@jimka/typescript-ui 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/dist/lib/{AbstractInput-B10DfLOf.js → AbstractInput-sRNPzWel.js} +2 -2
  2. package/dist/lib/{AbstractInput-B10DfLOf.js.map → AbstractInput-sRNPzWel.js.map} +1 -1
  3. package/dist/lib/Anchor-CwRhdP2J.js +2 -0
  4. package/dist/lib/Anchor-CwRhdP2J.js.map +1 -0
  5. package/dist/lib/AnimatedDropdown-H3Rh-YMZ.js +2 -0
  6. package/dist/lib/AnimatedDropdown-H3Rh-YMZ.js.map +1 -0
  7. package/dist/lib/Border-0DJFDiij.js +2 -0
  8. package/dist/lib/Border-0DJFDiij.js.map +1 -0
  9. package/dist/lib/Button-MpMIYBYJ.js +2 -0
  10. package/dist/lib/Button-MpMIYBYJ.js.map +1 -0
  11. package/dist/lib/{Card-CD3jxshU.js → Card-QYo27r5b.js} +2 -2
  12. package/dist/lib/{Card-CD3jxshU.js.map → Card-QYo27r5b.js.map} +1 -1
  13. package/dist/lib/Checkbox-D0JYK-wc.js +2 -0
  14. package/dist/lib/Checkbox-D0JYK-wc.js.map +1 -0
  15. package/dist/lib/CheckboxMenuRow-CesLhSnp.js +2 -0
  16. package/dist/lib/CheckboxMenuRow-CesLhSnp.js.map +1 -0
  17. package/dist/lib/{CodeEditor-BGlB232h.js → CodeEditor-N1FQodcF.js} +2 -2
  18. package/dist/lib/CodeEditor-N1FQodcF.js.map +1 -0
  19. package/dist/lib/ComboBox-Dx8Tk9yg.js +2 -0
  20. package/dist/lib/ComboBox-Dx8Tk9yg.js.map +1 -0
  21. package/dist/lib/Component-B0ShfPVi.js +2 -0
  22. package/dist/lib/Component-B0ShfPVi.js.map +1 -0
  23. package/dist/lib/Container-abY5Ww3W.js +2 -0
  24. package/dist/lib/{Container-C7RPLQEc.js.map → Container-abY5Ww3W.js.map} +1 -1
  25. package/dist/lib/{DOM-Ba8YRVel.js → DOM-C-Edb3tA.js} +2 -2
  26. package/dist/lib/DOM-C-Edb3tA.js.map +1 -0
  27. package/dist/lib/Dialog-cbA9V17e.js +2 -0
  28. package/dist/lib/Dialog-cbA9V17e.js.map +1 -0
  29. package/dist/lib/{FieldDecorator-HG65hEtk.js → FieldDecorator-BlYMfZ9L.js} +2 -2
  30. package/dist/lib/{FieldDecorator-HG65hEtk.js.map → FieldDecorator-BlYMfZ9L.js.map} +1 -1
  31. package/dist/lib/{Fit-Cg3dPyZg.js → Fit-BhlB-HAo.js} +2 -2
  32. package/dist/lib/{Fit-Cg3dPyZg.js.map → Fit-BhlB-HAo.js.map} +1 -1
  33. package/dist/lib/{FloatingPanel-D5buuVrQ.js → FloatingPanel-DkJXsZ2D.js} +2 -2
  34. package/dist/lib/{FloatingPanel-D5buuVrQ.js.map → FloatingPanel-DkJXsZ2D.js.map} +1 -1
  35. package/dist/lib/{Glyph-DeJF_zsR.js → Glyph-_VeBacS2.js} +2 -2
  36. package/dist/lib/{Glyph-DeJF_zsR.js.map → Glyph-_VeBacS2.js.map} +1 -1
  37. package/dist/lib/{Grid-BYBohJIA.js → Grid-Daiz7F5d.js} +2 -2
  38. package/dist/lib/Grid-Daiz7F5d.js.map +1 -0
  39. package/dist/lib/GridConstraints-C58LuTqM.js +2 -0
  40. package/dist/lib/{GridConstraints-Bts284FI.js.map → GridConstraints-C58LuTqM.js.map} +1 -1
  41. package/dist/lib/{Header-BIFJK9nR.js → Header-Dql3Zxc6.js} +2 -2
  42. package/dist/lib/{Header-BIFJK9nR.js.map → Header-Dql3Zxc6.js.map} +1 -1
  43. package/dist/lib/{IconText-Dsb1xS-E.js → IconText-PUjhTLlp.js} +2 -2
  44. package/dist/lib/{IconText-Dsb1xS-E.js.map → IconText-PUjhTLlp.js.map} +1 -1
  45. package/dist/lib/LayoutSerialization-CMxunLfV.js +2 -0
  46. package/dist/lib/LayoutSerialization-CMxunLfV.js.map +1 -0
  47. package/dist/lib/List-nx8220Fi.js +2 -0
  48. package/dist/lib/{List-BPd-IXZR.js.map → List-nx8220Fi.js.map} +1 -1
  49. package/dist/lib/MemoryStore-DhPoU-7X.js +2 -0
  50. package/dist/lib/MemoryStore-DhPoU-7X.js.map +1 -0
  51. package/dist/lib/Menu-Q0bpXXze.js +2 -0
  52. package/dist/lib/Menu-Q0bpXXze.js.map +1 -0
  53. package/dist/lib/{MenuButton-BR4KOFld.js → MenuButton-DCXi0tsP.js} +2 -2
  54. package/dist/lib/{MenuButton-BR4KOFld.js.map → MenuButton-DCXi0tsP.js.map} +1 -1
  55. package/dist/lib/OverlayFade-3pof2AJH.js +2 -0
  56. package/dist/lib/OverlayFade-3pof2AJH.js.map +1 -0
  57. package/dist/lib/{Panel-CpNQTLNF.js → Panel-Cy1pdlWO.js} +2 -2
  58. package/dist/lib/{Panel-CpNQTLNF.js.map → Panel-Cy1pdlWO.js.map} +1 -1
  59. package/dist/lib/{Position-PBVBW1JS.js → Position-BwtxouxK.js} +2 -2
  60. package/dist/lib/{Position-PBVBW1JS.js.map → Position-BwtxouxK.js.map} +1 -1
  61. package/dist/lib/{ProgressSpinner-lAsK26C-.js → ProgressSpinner-DK8xEQ9y.js} +2 -2
  62. package/dist/lib/{ProgressSpinner-lAsK26C-.js.map → ProgressSpinner-DK8xEQ9y.js.map} +1 -1
  63. package/dist/lib/RadioButton-BEwpqg1k.js +2 -0
  64. package/dist/lib/{RadioButton-CCcIaL54.js.map → RadioButton-BEwpqg1k.js.map} +1 -1
  65. package/dist/lib/RadioMenuRow-DP5qPyFR.js +2 -0
  66. package/dist/lib/RadioMenuRow-DP5qPyFR.js.map +1 -0
  67. package/dist/lib/{RovingTabIndex-C2Vzq0IX.js → RovingTabIndex-pHEv5oLD.js} +2 -2
  68. package/dist/lib/{RovingTabIndex-C2Vzq0IX.js.map → RovingTabIndex-pHEv5oLD.js.map} +1 -1
  69. package/dist/lib/Scrollbar-t8fFrUVQ.js +2 -0
  70. package/dist/lib/Scrollbar-t8fFrUVQ.js.map +1 -0
  71. package/dist/lib/{Slider-DZ9drUxn.js → Slider-CTbwR6HR.js} +2 -2
  72. package/dist/lib/{Slider-DZ9drUxn.js.map → Slider-CTbwR6HR.js.map} +1 -1
  73. package/dist/lib/{Spacer-SDhOMp2g.js → Spacer-srdN2d2b.js} +2 -2
  74. package/dist/lib/{Spacer-SDhOMp2g.js.map → Spacer-srdN2d2b.js.map} +1 -1
  75. package/dist/lib/TabButton-DLoMQzky.js +2 -0
  76. package/dist/lib/TabButton-DLoMQzky.js.map +1 -0
  77. package/dist/lib/TabCloseButton-CY7BGitY.js +2 -0
  78. package/dist/lib/TabCloseButton-CY7BGitY.js.map +1 -0
  79. package/dist/lib/{Text-CdHf3B-1.js → Text-CMKnK9uB.js} +2 -2
  80. package/dist/lib/{Text-CdHf3B-1.js.map → Text-CMKnK9uB.js.map} +1 -1
  81. package/dist/lib/{Tooltip-B_UF5bXD.js → Tooltip-CUOJOqQi.js} +3 -3
  82. package/dist/lib/Tooltip-CUOJOqQi.js.map +1 -0
  83. package/dist/lib/Tree-C64JNPWJ.js +2 -0
  84. package/dist/lib/Tree-C64JNPWJ.js.map +1 -0
  85. package/dist/lib/VBox-DfJPWXqc.js +2 -0
  86. package/dist/lib/VBox-DfJPWXqc.js.map +1 -0
  87. package/dist/lib/VirtualScroller-DTIn19o1.js +2 -0
  88. package/dist/lib/VirtualScroller-DTIn19o1.js.map +1 -0
  89. package/dist/lib/assets/StoreWorker-B5NwRM2I.js +2 -0
  90. package/dist/lib/assets/StoreWorker-B5NwRM2I.js.map +1 -0
  91. package/dist/lib/component/button.es.js +1 -1
  92. package/dist/lib/component/button.es.js.map +1 -1
  93. package/dist/lib/component/chart.es.js +1 -1
  94. package/dist/lib/component/container.es.js +1 -1
  95. package/dist/lib/component/container.es.js.map +1 -1
  96. package/dist/lib/component/diagram.es.js +1 -1
  97. package/dist/lib/component/diagram.es.js.map +1 -1
  98. package/dist/lib/component/display.es.js +1 -1
  99. package/dist/lib/component/display.es.js.map +1 -1
  100. package/dist/lib/component/editor.es.js +2 -2
  101. package/dist/lib/component/editor.es.js.map +1 -1
  102. package/dist/lib/component/input.es.js +1 -1
  103. package/dist/lib/component/input.es.js.map +1 -1
  104. package/dist/lib/component/list.es.js +1 -1
  105. package/dist/lib/component/list.es.js.map +1 -1
  106. package/dist/lib/component/menubar.es.js +1 -1
  107. package/dist/lib/component/menubar.es.js.map +1 -1
  108. package/dist/lib/component/table.es.js +6 -3
  109. package/dist/lib/component/table.es.js.map +1 -1
  110. package/dist/lib/component/tree.es.js +1 -1
  111. package/dist/lib/core.es.js +1 -1
  112. package/dist/lib/core.es.js.map +1 -1
  113. package/dist/lib/data.es.js +1 -1
  114. package/dist/lib/glyphs/solid/index.es.js +1 -1
  115. package/dist/lib/layout.es.js +1 -1
  116. package/dist/lib/layout.es.js.map +1 -1
  117. package/dist/lib/overlay.es.js +1 -1
  118. package/dist/lib/overlay.es.js.map +1 -1
  119. package/dist/lib/primitive.es.js +1 -1
  120. package/dist/lib/router.es.js +1 -1
  121. package/dist/lib/router.es.js.map +1 -1
  122. package/dist/lib/selectionsEqual-gMG7gCVI.js +2 -0
  123. package/dist/lib/selectionsEqual-gMG7gCVI.js.map +1 -0
  124. package/dist/lib/types/component/button/Button.d.ts +13 -0
  125. package/dist/lib/types/component/button/MenuButton.d.ts +1 -0
  126. package/dist/lib/types/component/button/PopupButton.d.ts +22 -0
  127. package/dist/lib/types/component/button/TabButton.d.ts +4 -0
  128. package/dist/lib/types/component/button/index.d.ts +2 -0
  129. package/dist/lib/types/component/container/CheckboxMenuRow.d.ts +33 -0
  130. package/dist/lib/types/component/container/CollapseButton.d.ts +11 -3
  131. package/dist/lib/types/component/container/MenuItem.d.ts +15 -5
  132. package/dist/lib/types/component/container/MenuRow.d.ts +24 -0
  133. package/dist/lib/types/component/container/MenuSeparator.d.ts +4 -2
  134. package/dist/lib/types/component/container/RadioMenuRow.d.ts +33 -0
  135. package/dist/lib/types/component/container/Scrollbar.d.ts +5 -0
  136. package/dist/lib/types/component/container/SplitGutter.d.ts +7 -2
  137. package/dist/lib/types/component/container/TabBar.d.ts +2 -0
  138. package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
  139. package/dist/lib/types/component/container/index.d.ts +6 -1
  140. package/dist/lib/types/component/display/WebGLCanvas.d.ts +1 -0
  141. package/dist/lib/types/component/input/AbstractCalendarDropdown.d.ts +2 -0
  142. package/dist/lib/types/component/input/AbstractPickerField.d.ts +3 -2
  143. package/dist/lib/types/component/list/AbstractSelectableList.d.ts +1 -1
  144. package/dist/lib/types/component/list/MultiSelectList.d.ts +2 -1
  145. package/dist/lib/types/component/shared/VirtualRowView.d.ts +2 -0
  146. package/dist/lib/types/component/table/Body.d.ts +22 -1
  147. package/dist/lib/types/component/table/Column.d.ts +3 -1
  148. package/dist/lib/types/component/table/ColumnConfig.d.ts +2 -0
  149. package/dist/lib/types/component/table/ColumnFilter.d.ts +27 -0
  150. package/dist/lib/types/component/table/Header.d.ts +30 -3
  151. package/dist/lib/types/component/table/Row.d.ts +4 -0
  152. package/dist/lib/types/component/table/Table.d.ts +29 -0
  153. package/dist/lib/types/component/table/TableExporter.d.ts +4 -3
  154. package/dist/lib/types/component/table/TreeBody.d.ts +2 -1
  155. package/dist/lib/types/component/table/cell/CellText.d.ts +9 -0
  156. package/dist/lib/types/component/table/cell/Filter.d.ts +54 -0
  157. package/dist/lib/types/component/table/cell/FilterClauseBadge.d.ts +21 -0
  158. package/dist/lib/types/component/table/cell/GroupSeparator.d.ts +9 -0
  159. package/dist/lib/types/component/table/cell/Header.d.ts +1 -0
  160. package/dist/lib/types/component/table/cell/editor/CellEditor.d.ts +1 -0
  161. package/dist/lib/types/component/table/cell/renderer/CellRenderer.d.ts +1 -0
  162. package/dist/lib/types/component/table/cell/renderer/Combo.d.ts +2 -0
  163. package/dist/lib/types/component/table/cell/renderer/Date.d.ts +2 -0
  164. package/dist/lib/types/component/table/cell/renderer/DateTime.d.ts +2 -0
  165. package/dist/lib/types/component/table/cell/renderer/Filter.d.ts +16 -0
  166. package/dist/lib/types/component/table/cell/renderer/Glyph.d.ts +1 -0
  167. package/dist/lib/types/component/table/cell/renderer/Link.d.ts +2 -0
  168. package/dist/lib/types/component/table/cell/renderer/Number.d.ts +3 -1
  169. package/dist/lib/types/component/table/cell/renderer/String.d.ts +2 -0
  170. package/dist/lib/types/component/table/cell/renderer/Time.d.ts +2 -0
  171. package/dist/lib/types/component/table/cell/renderer/TreeCell.d.ts +1 -0
  172. package/dist/lib/types/component/table/index.d.ts +6 -0
  173. package/dist/lib/types/component/tree/Tree.d.ts +17 -1
  174. package/dist/lib/types/component/tree/index.d.ts +1 -1
  175. package/dist/lib/types/core/AnimatedDropdown.d.ts +0 -9
  176. package/dist/lib/types/core/Body.d.ts +4 -0
  177. package/dist/lib/types/core/ClassStyleRules.d.ts +1 -0
  178. package/dist/lib/types/core/DOM.d.ts +10 -0
  179. package/dist/lib/types/core/Event.d.ts +11 -2
  180. package/dist/lib/types/core/OverlayFade.d.ts +12 -0
  181. package/dist/lib/types/core/OverlayPosition.d.ts +6 -0
  182. package/dist/lib/types/core/Theme.d.ts +8 -0
  183. package/dist/lib/types/core/index.d.ts +5 -3
  184. package/dist/lib/types/data/AbstractStore.d.ts +3 -0
  185. package/dist/lib/types/data/FilterDescriptor.d.ts +13 -0
  186. package/dist/lib/types/data/index.d.ts +2 -1
  187. package/dist/lib/types/data/temporalText.d.ts +2 -0
  188. package/dist/lib/types/layout/BoxLayout.d.ts +6 -1
  189. package/dist/lib/types/layout/Grid.d.ts +1 -0
  190. package/dist/lib/types/layout/HFlow.d.ts +1 -1
  191. package/dist/lib/types/layout/Split.d.ts +10 -1
  192. package/dist/lib/types/layout/Tab.d.ts +7 -1
  193. package/dist/lib/types/layout/VFlow.d.ts +1 -1
  194. package/dist/lib/types/layout/index.d.ts +1 -1
  195. package/dist/lib/types/overlay/Dock.d.ts +1 -0
  196. package/dist/lib/types/overlay/Menu.d.ts +1 -1
  197. package/dist/lib/types/overlay/Popover.d.ts +3 -0
  198. package/dist/lib/types/overlay/PopupPanel.d.ts +17 -0
  199. package/dist/lib/types/overlay/Window.d.ts +2 -0
  200. package/dist/lib/types/overlay/index.d.ts +2 -0
  201. package/dist/lib/types/router/RoutePattern.d.ts +7 -0
  202. package/dist/lib/types/router/Router.d.ts +6 -2
  203. package/dist/lib/types/router/index.d.ts +1 -1
  204. package/dist/lib/validation.es.js +1 -1
  205. package/llms.txt +4 -0
  206. package/package.json +1 -1
  207. package/dist/lib/AbstractBooleanInput-CbW7ZrDT.js +0 -2
  208. package/dist/lib/AbstractBooleanInput-CbW7ZrDT.js.map +0 -1
  209. package/dist/lib/Anchor-BsblS5it.js +0 -2
  210. package/dist/lib/Anchor-BsblS5it.js.map +0 -1
  211. package/dist/lib/AnimatedDropdown-kL47HPGP.js +0 -2
  212. package/dist/lib/AnimatedDropdown-kL47HPGP.js.map +0 -1
  213. package/dist/lib/Border-CNS6aEXB.js +0 -2
  214. package/dist/lib/Border-CNS6aEXB.js.map +0 -1
  215. package/dist/lib/Button-T16zSZip.js +0 -2
  216. package/dist/lib/Button-T16zSZip.js.map +0 -1
  217. package/dist/lib/CodeEditor-BGlB232h.js.map +0 -1
  218. package/dist/lib/ComboBox-C9FYMuLv.js +0 -2
  219. package/dist/lib/ComboBox-C9FYMuLv.js.map +0 -1
  220. package/dist/lib/Component-w65rPK75.js +0 -2
  221. package/dist/lib/Component-w65rPK75.js.map +0 -1
  222. package/dist/lib/Container-C7RPLQEc.js +0 -2
  223. package/dist/lib/DOM-Ba8YRVel.js.map +0 -1
  224. package/dist/lib/DragManager-UvPHSoRm.js +0 -2
  225. package/dist/lib/DragManager-UvPHSoRm.js.map +0 -1
  226. package/dist/lib/Grid-BYBohJIA.js.map +0 -1
  227. package/dist/lib/GridConstraints-Bts284FI.js +0 -2
  228. package/dist/lib/LayoutSerialization-ySzY1h2l.js +0 -2
  229. package/dist/lib/LayoutSerialization-ySzY1h2l.js.map +0 -1
  230. package/dist/lib/LayoutSizes-DKDK1Cle.js +0 -2
  231. package/dist/lib/LayoutSizes-DKDK1Cle.js.map +0 -1
  232. package/dist/lib/List-BPd-IXZR.js +0 -2
  233. package/dist/lib/MemoryStore-BgP7-Uzn.js +0 -2
  234. package/dist/lib/MemoryStore-BgP7-Uzn.js.map +0 -1
  235. package/dist/lib/Menu-C0roOCY7.js +0 -2
  236. package/dist/lib/Menu-C0roOCY7.js.map +0 -1
  237. package/dist/lib/RadioButton-CCcIaL54.js +0 -2
  238. package/dist/lib/Scrollbar-C3ZZyZ1e.js +0 -2
  239. package/dist/lib/Scrollbar-C3ZZyZ1e.js.map +0 -1
  240. package/dist/lib/TabButton-DNo4ZgV8.js +0 -2
  241. package/dist/lib/TabButton-DNo4ZgV8.js.map +0 -1
  242. package/dist/lib/Tooltip-B_UF5bXD.js.map +0 -1
  243. package/dist/lib/Tree-C7qLbIYH.js +0 -2
  244. package/dist/lib/Tree-C7qLbIYH.js.map +0 -1
  245. package/dist/lib/VBox-32fxpkDl.js +0 -2
  246. package/dist/lib/VBox-32fxpkDl.js.map +0 -1
  247. package/dist/lib/VirtualScroller-cvFNMS7U.js +0 -2
  248. package/dist/lib/VirtualScroller-cvFNMS7U.js.map +0 -1
  249. package/dist/lib/assets/StoreWorker-CgOBYoai.js +0 -2
  250. package/dist/lib/assets/StoreWorker-CgOBYoai.js.map +0 -1
  251. package/dist/lib/selectionsEqual-BrTxPMmK.js +0 -2
  252. package/dist/lib/selectionsEqual-BrTxPMmK.js.map +0 -1
@@ -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/CellGeometry.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/Header.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\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 from the container's\n * [`getIntrinsicColumnWidths`](/api/component/table/classes/Table#getIntrinsicColumnWidths) —\n * a per-type width policy, refined by any `minWidth` / `maxWidth` / `width` declared in the\n * column spec: compact types (`boolean`, `glyph`, `date`, `time`, `datetime`, `number`)\n * receive a type-derived width floored at the header text width; string and auto columns\n * either share the remaining space equally or, under `autoSizeColumns`, size to their\n * sampled content, clamped to their constraints either way. On container resize\n * `boolean` / `number` / `date` columns keep their width unchanged; every other column\n * (including `glyph`, `time`, and `datetime`) scales proportionally like a flexible\n * column, again clamped to their per-column constraints. Every per-column minimum\n * is read through [`getColumnMinWidth`](/api/component/table/classes/Table#getColumnMinWidth),\n * so a column never squeezes below the floor its type or spec implies.\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 * toward the container's available width, or toward a wider total a resize drag grew the\n * table to, whichever is larger, so a drag-widened table is not rescaled back down.\n */\n doLayout() {\n const container = <TableComponent>this.getContainer();\n\n if (!container) {\n return;\n }\n\n const containerSize = container.getInnerSize();\n\n // A table whose size has not been resolved yet reports NaN dimensions\n // rather than `null` — `getInnerSize` subtracts the perimeter from an\n // unset `_width`/`_height`. That happens whenever a layout is driven by\n // something other than the parent's sizing pass: a store load that\n // re-derives column widths fires one from the store event, which for a\n // generated table typically lands before the table has ever been sized.\n // Laying out against NaN would poison the width array — `availableWidth`\n // is NaN, and the slack arithmetic downstream compares NaN (every\n // comparison is false, so no early return catches it) — so the widths\n // are left alone and the next properly-sized pass derives them.\n if (!containerSize || !Number.isFinite(containerSize.width) || !Number.isFinite(containerSize.height)) {\n return;\n }\n\n const containerInsets = container.getContentInsets();\n const columns = container.getColumns();\n const columnCount = container.getColumns().length;\n const availableWidth = container.getAvailableColumnWidth();\n // A resize drag may have grown the table's total column width past\n // `availableWidth`; rescaling toward the target instead of the raw\n // available width is what keeps `rescaleWidths` from squeezing a\n // drag-widened table back to the container's width on the next pass.\n const targetWidth = Math.max(availableWidth, container.getColumnWidthTarget());\n\n let columnWidths = container.getColumnWidths();\n\n if (columnWidths.length !== columnCount) {\n columnWidths = this.initializeWidths(container, columns, availableWidth);\n container.setColumnWidths(columnWidths);\n } else {\n columnWidths = this.rescaleWidths(container, columns, columnWidths, targetWidth);\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\n // The band's bottom border is chrome outside the rows, so the\n // outer height is what the rows need PLUS the header's own\n // perimeter — the same \"children plus the container perimeter\"\n // sum every other manager's size report uses. Taking it out of a\n // row instead would make a header cell shorter than the body row\n // it heads, by an amount the theme's border width decides.\n const headerPerimeter = header.getPerimeterSize();\n const headerBandHeight = parentRowHeight + columnHeight\n + headerPerimeter.top + headerPerimeter.bottom;\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 // The rows are the header's own children, so their frame is the\n // header's content box, not the band: a row placed at the band's\n // origin and sized to the band starts inside the border and\n // overruns the far edge.\n const headerBox = header.getContentBounds()\n ?? { x: 0, y: 0, width: containerSize.width, height: headerBandHeight };\n\n // Rows stay at least as wide as the visible content box, and\n // wider when the columns overflow it, so cells off the right\n // edge can translate into view — because `Row`'s default\n // `overflow: hidden` would otherwise clip cells at the row's\n // right edge and prevent them from coming into view when the\n // inner rows translate left during a horizontal scroll.\n const columnSum = columnWidths.reduce((s, w) => s + w, 0);\n const innerRowW = Math.max(headerBox.width, columnSum);\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 its\n // cell pool is empty anyway — `header.renderColumnWindow`\n // below skips positioning it.\n const parentRow = header.getParentRow();\n parentRow.setAutoCommitStyle(false);\n parentRow.setX(headerBox.x);\n parentRow.setY(headerBox.y);\n parentRow.setWidth(innerRowW);\n parentRow.setHeight(parentRowHeight);\n parentRow.setAutoCommitStyle(true);\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(headerBox.x);\n columnRow.setY(headerBox.y + parentRowHeight);\n columnRow.setWidth(innerRowW);\n columnRow.setHeight(columnHeight);\n columnRow.setAutoCommitStyle(true);\n\n // Reconciles the header's rendered cells to the\n // horizontally-visible column range and positions every\n // rendered cell in both rows — the header-side counterpart\n // of `body.renderWindow` below.\n header.renderColumnWindow({\n columnWidths,\n viewportWidth: availableWidth,\n columnHeight,\n parentRowHeight,\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. Placed from `headerBox`, the same rectangle as the\n // rows above, since the cover is a raw `<div>` appended to the\n // header's element and so shares their containing block.\n const trackW = DOM.source.getScrollBarWidth();\n const cover = header.getScrollbarCover();\n DOM.sink.apply(cover, {\n style: {\n left: (headerBox.x + headerBox.width - trackW) + \"px\",\n top: headerBox.y + \"px\",\n width: trackW + \"px\",\n height: headerBox.height + \"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 // Same \"children plus perimeter\" sum as the header band above:\n // the footer's own top border is chrome outside the row, so the\n // outer height grows by it instead of the row shrinking inside it.\n const footerPerimeter = footer.getPerimeterSize();\n const footerBandHeight = columnHeight + footerPerimeter.top + footerPerimeter.bottom;\n\n footer.setAutoCommitStyle(false);\n footer.setX(containerInsets.getLeft());\n footer.setY(containerInsets.getTop() + containerSize.height - footerBandHeight);\n footer.setWidth(containerSize.width);\n footer.setHeight(footerBandHeight);\n footer.setAutoCommitStyle(true);\n\n // The footer's inner row is the footer's own child, so its cells\n // are sized from the footer's content box, not the band.\n const footerBox = footer.getContentBounds()\n ?? { x: 0, y: 0, width: containerSize.width, height: columnHeight };\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(footerBox.height);\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 the container's per-type width\n * policy and column constraints.\n *\n * Fixed-shape columns (boolean, glyph, date, time, datetime, number) get a\n * type-derived width clamped to any declared min/max. Flexible columns\n * (string, auto) with no definite width share the remaining space equally,\n * each clamped to its own min/max; a `string`/`auto` column with a definite\n * width — either declared or, under `autoSizeColumns`, sampled from content —\n * uses that width like a fixed-shape column instead of sharing space.\n *\n * @param container - The Table component whose columns are being sized.\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(container: TableComponent, columns: Column[], availableWidth: number): number[] {\n const intrinsic = container.getIntrinsicColumnWidths();\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 const widths = intrinsic.map((w, i) => {\n if (w !== null) {\n return w;\n }\n\n const col = columns[i];\n const min = container.getColumnMinWidth(col);\n const max = col.getMaxWidth() ?? UNBOUNDED;\n\n return Util.clamp(rawFlex, min, max);\n });\n\n return this.absorbSlackIntoGreedy(columns, widths, availableWidth);\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 container - The Table component whose columns are being sized.\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(container: TableComponent, 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 // On a table with more columns than fit — the generated-table case this\n // sizing exists for — the fixed-shape columns alone can already exceed\n // the viewport, leaving `newFlexTotal` negative. There is no space for\n // the flex columns to share, and rescaling by a negative ratio would\n // collapse every one of them to its floor on the first re-layout, with\n // no resize involved. Keep the derived widths and let the table scroll\n // horizontally, which is what a column too wide for the viewport means.\n if (prevFlexTotal <= 0 || newFlexTotal <= 0 || Math.abs(prevFlexTotal - newFlexTotal) <= 0.5) {\n return columnWidths;\n }\n\n const ratio = newFlexTotal / prevFlexTotal;\n\n const rescaled = columnWidths.map((w, i) => {\n if (isFixed[i]) {\n return w;\n }\n\n return this.clamp(w * ratio, columns[i], container);\n });\n\n return this.absorbSlackIntoGreedy(columns, rescaled, availableWidth);\n }\n\n /**\n * Adds any positive leftover width — space freed when a flexible column\n * clamped to its `maxWidth` — to the flexible columns that declare no\n * `maxWidth`, so an unbounded \"filler\" column grows to fill the table\n * instead of leaving dead space at the right edge. A no-op when the columns\n * already fill the width (the common case: no flexible column is capped) or\n * overflow it.\n *\n * @param columns - The visible resolved columns.\n * @param widths - The per-column widths computed so far.\n * @param availableWidth - Total available pixel width for columns.\n * @returns The widths with any positive slack handed to unbounded flexible columns.\n */\n private absorbSlackIntoGreedy(columns: Column[], widths: number[], availableWidth: number): number[] {\n const slack = availableWidth - widths.reduce((s, w) => s + w, 0);\n\n if (slack <= 0.5) {\n return widths;\n }\n\n const greedy: number[] = [];\n\n columns.forEach((col, i) => {\n const t = col.getField().getType();\n const isFlex = t !== 'boolean' && t !== 'number' && t !== 'date';\n\n if (isFlex && col.getMaxWidth() === undefined) {\n greedy.push(i);\n }\n });\n\n if (greedy.length === 0) {\n return widths;\n }\n\n const share = slack / greedy.length;\n const result = [...widths];\n\n for (const i of greedy) {\n result[i] += share;\n }\n\n return result;\n }\n\n /**\n * Clamps a width value to the `[minWidth, maxWidth]` range declared on a column,\n * using the container's type-derived floor as the default minimum and no upper\n * bound when unconstrained.\n *\n * @param width - The raw width to clamp.\n * @param column - The column whose constraints apply.\n * @param container - The Table component whose columns are being sized.\n * @returns The clamped width.\n */\n private clamp(width: number, column: Column, container: TableComponent): number {\n const min = container.getColumnMinWidth(column);\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 * The body is the only writer: its read-only resolution forwards the\n * OR of the static column flag, the spec-level row predicate, and the\n * per-cell predicate, and re-runs whenever a row rebinds or its column\n * window changes — so a read-only column scrolling into view arrives\n * read-only without a rebind. Application code should declare read-only\n * through 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, or\n * `null` to restore the theme default\n * (`var(--ts-ui-table-cell-bg, transparent)`).\n * @returns This cell, for method chaining.\n */\n setBaseBackground(color: string | null): this {\n this._baseBackground = color ?? 'var(--ts-ui-table-cell-bg, transparent)';\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()`), inside this cell's own content box.\n * For a plain renderer the offset is `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 from this cell's content box origin and shrinks its\n * width accordingly. Idempotent — when no offset is reserved this\n * is a single comparison and an early 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 // The Card layout already placed the editor inside this cell's content\n // box, so its y and height are right; only the left edge and the width\n // move here. The outer-width fallback preserves the previous behaviour\n // before the element exists.\n const box = this.getContentBounds()\n ?? { x: 0, y: 0, width: this.getWidth() ?? 0, height: 0 };\n\n const editorWidth = Math.max(0, box.width - contentX);\n\n editor.setAutoCommitStyle(false);\n editor.setX(box.x + 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 const changed = this._renderer !== renderer;\n\n this._renderer = renderer;\n this.getLayoutManager().setVisibleComponentId(renderer.getId());\n\n // Re-fit the newly visible renderer to the cell, mirroring startEdit /\n // stopEdit's Card swaps. Body.bindAndPositionRows skips a rebound\n // cell's own doLayout when its column geometry is unchanged (a pure\n // scroll) — safe for a fixed-type column, but a DynamicCell can swap\n // its active renderer on that same rebind. Without this the swapped-in\n // variant keeps its stale (zero) size and vanishes: the rotated value\n // column's number/date/boolean rows blank out on scroll. Guarded on an\n // actual variant change so stable-type rebinds keep that skip. (On the\n // cell's very first bind the cell is not yet sized, so this lays out to\n // zero — harmless, as Body's first geometry pass re-runs doLayout.)\n if (changed) {\n this.doLayout();\n }\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 * @remarks\n * An implementation that adds, removes or replaces a child component must\n * end by calling `this.doLayout()`, because the new child has no bounds\n * until something lays this renderer out and nothing else will: the table\n * skips the layout for a cell whose geometry has not moved, and rebinding a\n * row to new data does not move it. `GlyphRenderer` is the framework's own\n * example. An implementation that only writes text needs no layout — the\n * text does not auto-measure — which is the common case.\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 // Unlike a text renderer, this one answers a value change by replacing\n // a child component, and the replacement has no bounds until something\n // lays this renderer out. Nothing else will: the owning cell keeps its\n // geometry across a rebind, so the table's cell-geometry diff skips it.\n // Mirrors `Cell.setActiveRenderer`, which lays itself out whenever it\n // swaps the child the layout is fitted around.\n this.doLayout();\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 /**\n * Explicit starting width in pixels. Takes precedence over the type\n * policy and any sampled content, but is still clamped into the\n * column's `[minWidth, maxWidth]` envelope — an explicit `width`\n * narrower than the type's floor is raised to that floor.\n */\n width ?: number;\n /**\n * Longest value this column can hold, in characters (e.g. a\n * `varchar(60)` column passes `60`). For a `string`/`auto` column\n * under {@link ColumnSpec.autoSizeColumns} this is used only when\n * sampling the store yields no candidates; for a `number` column it\n * outranks the sample, since a declared digit budget is a better\n * answer than what fifty rows happen to hold.\n */\n maxContentLength ?: 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 * Overrides the header label for this column. Defaults to the field name.\n * Set to `''` to render a blank header (used by the rotated view's filler\n * column).\n */\n headerText ?: 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 * When `true`, `string` and `auto` columns are sized from a bounded\n * sample of the values the column holds, instead of staying flex\n * columns that share the leftover space. `boolean`, `glyph`, `date`,\n * `time`, `datetime`, and `number` columns are always sized from\n * their type, whether or not this flag is set. Defaults to `false`.\n */\n autoSizeColumns ?: 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` from the\n * content box's origin and places the delegate immediately to its right,\n * filling the remaining content 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 // No early return on a null box: `super.doLayout()` is the last\n // statement here, and skipping it would leave the children's bounds\n // uncommitted. The zero fallback preserves the previous\n // `getWidth() || 0` behaviour before the element exists.\n const box = this.getContentBounds() ?? { x: 0, y: 0, width: 0, height: 0 };\n const width = box.width;\n const height = box.height;\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(box.x + indent);\n this._toggle.setY(box.y + 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(box.x + delegateX);\n this._delegate.setY(box.y);\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 column in the body's current column window and binds each\n * cell's commit callback to the 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 _treeCell: Cell<any> | null = null;\n private _stripe: boolean = false;\n\n // All non-hidden fields, in display order — the full column list a\n // window is carved out of. Populated by `setColumnFields`.\n private _visibleFields: Field[] = [];\n private _columnConfigs: Map<string, ColumnConfig> = new Map();\n private _treeFieldName: string | undefined;\n // Visible-column index of slot 0. The rendered columns are always a\n // contiguous run, so slot `s` holds column `_windowFirst + s`.\n private _windowFirst: number = 0;\n // Cell key per rendered slot, index-aligned with `getComponents()`.\n private _cellKeys: string[] = [];\n // Field name per rendered slot, index-aligned with `getComponents()`.\n private _fieldNames: string[] = [];\n // Set by `setColumnFields` so the next `setColumnWindow` reconciles\n // even when the requested range happens to match the current one —\n // a column-set change (hide/show, config swap) can leave the range\n // unchanged while the cells behind it need to change.\n private _columnsDirty: 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 this.setColumnFields(this._model, hiddenColumns, columnConfigs, treeFieldName);\n }\n }\n\n /**\n * Returns the cell on the row's tree column. The host `TreeBody` reads\n * this to find each row's `TreeCellRenderer` for depth / toggle updates\n * and toggle-click routing.\n *\n * Returns `null` when the row was constructed without a `treeFieldName`,\n * and also whenever the tree column sits outside the row's current column\n * window — a row renders only its horizontally-visible columns, so a wide\n * table scrolled right has no tree cell to return. Callers must handle\n * `null` on every access rather than caching the result.\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 visible-column index of the first rendered cell —\n * `getComponents()[s]` renders visible column `getColumnWindowStart() + s`.\n *\n * @returns The visible-column index slot `0` currently renders.\n */\n getColumnWindowStart(): number {\n return this._windowFirst;\n }\n\n /**\n * Returns one field name per *rendered* slot, index-aligned with\n * `getComponents()`. Hidden columns are excluded.\n *\n * A row renders only the columns in its current column window, so this\n * describes that window rather than every visible column: slot `s` names\n * visible column {@link getColumnWindowStart} + `s`. Code holding a column\n * index must subtract that offset before indexing here, and code reading a\n * slot must add it to recover the column index.\n *\n * @returns The field names, one per rendered slot.\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 * Records the visible-field list, per-field configs and tree column\n * this row renders from. Builds no cells — {@link setColumnWindow}\n * owns cell construction, called separately once the host `Body`\n * knows which column range to render.\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 setColumnFields(\n model: AbstractModel,\n hiddenColumns: Set<string>,\n columnConfigs: Map<string, ColumnConfig>,\n treeFieldName?: string,\n ): this {\n this._model = model;\n\n this._visibleFields = model.getFields()\n .filter(f => !hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n\n this._columnConfigs = columnConfigs;\n this._treeFieldName = treeFieldName;\n this._columnsDirty = true;\n\n return this;\n }\n\n /**\n * Reconciles the rendered cells to exactly the visible columns\n * `[firstCol, lastCol]`. A column keeps its existing cell when that\n * cell already presents the same field; otherwise it recycles a cell\n * that just left the window and shares the same cell key, or\n * builds a fresh one. Cells that end up unclaimed are committed (if\n * editing), removed, and disposed.\n *\n * @param firstCol - The first visible-column index to render, inclusive.\n * @param lastCol - The last visible-column index to render, inclusive.\n *\n * @returns `true` when the rendered cell set changed.\n */\n setColumnWindow(firstCol: number, lastCol: number): boolean {\n // Clamp into this row's own visible-field range. A pooled row can be\n // asked to window against a range sized for a store the host `Body`\n // has already swapped to (`Table.bindView` calls `Body.setStore`,\n // which renders synchronously, before `setColumns` / `setColumnConfigs`\n // resync this row's `_visibleFields` to the new model) — the request\n // is stale for one pass, not invalid, so it is clamped rather than\n // trusted verbatim. The immediately-following `setColumns` /\n // `setColumnConfigs` call marks the row dirty and re-renders against\n // the caught-up field list.\n const maxCol = this._visibleFields.length - 1;\n\n lastCol = Math.min(lastCol, maxCol);\n\n if (firstCol > lastCol) {\n firstCol = lastCol + 1;\n }\n\n const currentLastCol = this._windowFirst + this.getComponents().length - 1;\n\n if (!this._columnsDirty && firstCol === this._windowFirst && lastCol === currentLastCol) {\n return false;\n }\n\n const cells = this.getComponents() as Cell<any>[];\n const byName = new Map<string, { cell: Cell<any>, key: string }>();\n\n for (let s = 0; s < cells.length; s++) {\n const fieldName = this._fieldNames[s];\n\n if (fieldName !== undefined) {\n byName.set(fieldName, { cell: cells[s], key: this._cellKeys[s] });\n }\n }\n\n const slotCount = lastCol - firstCol + 1;\n const assigned: (Cell<any> | undefined)[] = new Array(slotCount).fill(undefined);\n const retargeted = new Set<number>();\n\n // Pass 1 — keep a cell for its own field, if its key still matches.\n for (let col = firstCol; col <= lastCol; col++) {\n const field = this._visibleFields[col];\n const key = this.cellKeyFor(field);\n const entry = byName.get(field.getName());\n\n if (entry && entry.key === key) {\n assigned[col - firstCol] = entry.cell;\n byName.delete(field.getName());\n }\n }\n\n // Remaining leftovers, grouped by key, so pass 2 can recycle one.\n const free = new Map<string, Cell<any>[]>();\n\n for (const entry of byName.values()) {\n const pool = free.get(entry.key);\n\n if (pool) {\n pool.push(entry.cell);\n } else {\n free.set(entry.key, [entry.cell]);\n }\n }\n\n // Pass 2 — recycle a leftover with the same key, else build.\n for (let col = firstCol; col <= lastCol; col++) {\n const slot = col - firstCol;\n\n if (assigned[slot] !== undefined) {\n continue;\n }\n\n const field = this._visibleFields[col];\n const key = this.cellKeyFor(field);\n const pool = free.get(key);\n let cell: Cell<any>;\n\n if (pool && pool.length > 0) {\n cell = pool.pop()!;\n\n this.setLayoutConstraints(cell, { data: field });\n } else {\n cell = Row.createCellForField(field, this._columnConfigs);\n\n if (this._treeFieldName !== undefined && field.getName() === this._treeFieldName) {\n cell.wrapRenderer((delegate: CellRenderer<any>) => new TreeCellRenderer(delegate));\n }\n\n const builtCell = cell;\n cell.on(\"commit\", (newValue) => this.commitCellValue(builtCell, newValue));\n\n this.addComponent(cell, { data: field });\n }\n\n assigned[slot] = cell;\n retargeted.add(col);\n }\n\n // Pass 3 — per-column state that a shift can invalidate even for a\n // survivor: ARIA column index, group tint, and (for a retargeted\n // cell only) the bound value.\n for (let col = firstCol; col <= lastCol; col++) {\n const cell = assigned[col - firstCol]!;\n const field = this._visibleFields[col];\n\n cell.getAria().setColIndex(col + 1);\n cell.setBaseBackground(this._columnConfigs.get(field.getName())?.groupColor ?? null);\n\n if (retargeted.has(col)) {\n this.bindCell(cell, this._data, field.getName());\n }\n }\n\n // Discard whatever is still free. Commit an in-flight edit first so\n // user keystrokes land on the record (mirrors the blur-commits-edit\n // contract), then dispose so the cell's per-instance stylesheet rule\n // doesn't survive the discard.\n for (const pool of free.values()) {\n for (const cell of pool) {\n if (cell.isEditing()) {\n cell.commitEdit();\n }\n\n this.removeComponent(cell);\n cell.dispose();\n }\n }\n\n // Re-order children to the new visible-column order.\n //\n // The order comes from `assigned`, which this reconciler just built in\n // slot order — not from re-sorting on `Field.getOrder()`. A model that\n // declares no `order` returns the -1 sentinel for every field, so an\n // order-based comparison ties throughout, the sort is a stable no-op,\n // and a recycled cell keeps whatever index it already held. That\n // desynchronises `getComponents()` from `_fieldNames`, which is built\n // in column order below and documented to stay index-aligned with it.\n const slotOf = new Map(assigned.map((cell, slot) => [cell, slot]));\n\n this.sortComponents((c1, c2) =>\n (slotOf.get(c1 as Cell<any>) ?? 0) - (slotOf.get(c2 as Cell<any>) ?? 0));\n\n this._windowFirst = firstCol;\n this._fieldNames = [];\n this._cellKeys = [];\n this._treeCell = null;\n\n for (let col = firstCol; col <= lastCol; col++) {\n const field = this._visibleFields[col];\n const cell = assigned[col - firstCol]!;\n\n this._fieldNames.push(field.getName());\n this._cellKeys.push(this.cellKeyFor(field));\n\n if (this._treeFieldName !== undefined && field.getName() === this._treeFieldName) {\n this._treeCell = cell;\n }\n }\n\n this._columnsDirty = false;\n\n return true;\n }\n\n /**\n * Commits a cell's newly-entered value onto the row's bound record.\n * Wired once per cell, at construction, so a recycled cell resolves\n * its *current* field from the row's layout constraints at emit time\n * rather than closing over the field it was originally built for.\n *\n * @param cell - The cell that emitted `\"commit\"`.\n * @param value - The committed value.\n */\n private commitCellValue(cell: Cell<any>, value: unknown): void {\n const field = this.getLayoutConstraints(cell)?.data as Field | undefined;\n\n if (!field) {\n return;\n }\n\n if (this._data) {\n this._data.set(field.getName(), value);\n this._onCellCommit?.(this._data);\n }\n\n this.updateVisualState();\n }\n\n /**\n * Computes the cell-reuse key for `field`, per the precedence table in\n * the column-virtualization plan's Architecture Decisions: the tree\n * column and any column with a custom renderer, `cellType`, or `values`\n * config get a field-namespaced key (never shared); a `time`/`datetime`\n * column shares a key with every other column of the same type and\n * `showSeconds`; every other column shares a key with every other\n * column of the same field type.\n *\n * @param field - The field whose column key to compute.\n * @param config - The field's column config, if any.\n * @param isTreeColumn - Whether this field is the row's tree column.\n *\n * @returns The reuse key. Two columns with the same key may share a cell.\n */\n private static cellKey(field: Field, config: ColumnConfig | undefined, isTreeColumn: boolean): string {\n if (isTreeColumn) {\n return `tree:${field.getName()}`;\n }\n\n if (config?.renderer) {\n return `renderer:${field.getName()}`;\n }\n\n if (config?.cellType) {\n return `dynamic:${field.getName()}`;\n }\n\n if (config?.values && config.values.length > 0) {\n return `combo:${field.getName()}`;\n }\n\n const type = field.getType();\n\n if (type === 'time' || type === 'datetime') {\n return `${type}:${config?.showSeconds ?? false}`;\n }\n\n return type;\n }\n\n /**\n * Resolves `field`'s config and tree-column status from this row's\n * current state and forwards to `cellKey`.\n */\n private cellKeyFor(field: Field): string {\n const isTreeColumn = this._treeFieldName !== undefined && field.getName() === this._treeFieldName;\n\n return Row.cellKey(field, this._columnConfigs.get(field.getName()), isTreeColumn);\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\nconst _defaultSortPriorityBadgeOptions: Partial<SortPriorityBadgeOptions> = {\n backgroundColor: \"var(--ts-ui-sort-badge-bg, rgba(0,0,0,0.15))\",\n foregroundColor: \"var(--ts-ui-sort-badge-color, inherit)\",\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 * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(options?: SortPriorityBadgeOptions, subclassDefaults?: Partial<SortPriorityBadgeOptions>) {\n ensureSortBadgeClassRule();\n\n super({ tag: \"span\", ...(options ?? {}) }, { ..._defaultSortPriorityBadgeOptions, ...(subclassDefaults ?? {}) });\n\n this._priority ??= null;\n\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 * Re-targets this cell at another column's model field. Used by the\n * header's column-window reconciler when recycling a cell whose\n * column left the window for one entering it.\n *\n * @param name - The new field name this cell reports on sort and\n * context-menu events.\n * @returns This cell, for method chaining.\n */\n setFieldName(name: string): this {\n this._fieldName = name;\n\n return this;\n }\n\n /**\n * Returns the model field name this cell currently reports on sort\n * and context-menu events.\n *\n * @returns The current field name.\n */\n getFieldName(): string {\n return this._fieldName;\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 // Mounting or clearing the glyph rewrites the renderer's left inset,\n // and an inset only reaches the label through a layout pass — nothing\n // in `setInsets` schedules one. The cell lays itself out here rather\n // than leaving that to its caller, mirroring `Cell.setActiveRenderer`,\n // which re-fits itself for the same reason. The header's own scroll\n // path skips a cell whose geometry has not moved, so a caller-owned\n // relayout would leave the label sitting under the glyph on every\n // route that does not also resize the cell. (Called before the cell has\n // been sized — the reconciler mounts the glyph before the geometry pass\n // — this fits the renderer against an unset size, which the header's\n // geometry pass then overwrites.)\n this.doLayout();\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 // `dispose()` removes the element *and* deletes the glyph's\n // per-instance rules. Unmounting alone stranded them on the shared\n // sheet, so a header whose glyph tracks sort state leaked one set\n // per change.\n this._headerGlyphInstance.dispose();\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 * Replaces the base header label. The required marker and sort arrow are\n * re-composed onto the new label automatically, so neither is lost.\n * Re-applied on every header reconcile so a column config that overrides\n * the label — or clears the override — takes effect on a surviving cell,\n * mirroring {@link setRequired}.\n *\n * @param text - The new base label.\n * @returns This header cell, for method chaining.\n */\n setHeaderText(text: String): this {\n if (this._text === text) {\n return this;\n }\n\n this._text = text;\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 /**\n * Destroys the side-loaded overlay children before the inherited teardown\n * runs.\n *\n * The resize handle, the sort-priority badge and the header glyph are held\n * in private fields and mounted with a raw `appendChild` rather than\n * `addComponent` — deliberately, since registering them would let this\n * cell's `Card` layout treat them as non-visible siblings of the renderer\n * and hide them. The cost is that they never enter `_components`, so the\n * base destructor's recursion cannot reach them and their per-instance\n * rules would outlive the cell. The residue scales with column count, and\n * a larger stylesheet makes every later style recalculation dearer.\n */\n protected destructor(): void {\n // Loose checks: `_resizeHandle` and `_priorityBadge` are `declare`\n // fields, so they read as `undefined` if teardown lands before this\n // class's constructor body has run.\n this._resizeHandle?.dispose();\n this._priorityBadge?.dispose();\n this._headerGlyphInstance?.dispose();\n\n super.destructor();\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\";\n\n/** Geometry last written to one cell, kept so an identical rewrite can be skipped. */\ninterface CellGeometry {\n /** The cell's x offset within its row, in pixels. */\n x: number;\n /** The cell's width, in pixels. */\n w: number;\n /** The cell's height, in pixels. */\n h: number;\n}\n\n/**\n * Remembers the geometry last written to each table cell so a pass that would\n * rewrite the same values skips both the write and the `doLayout` behind it.\n *\n * Shared by the table body's row cells and the header's own two rows, which\n * position their cells identically: an x offset inside a row that is itself\n * translated by the scroll offset, at the row's height. Because the offsets are\n * content-absolute, a cell keeps its geometry for as long as it keeps its\n * column, and a horizontal scroll leaves most of them untouched.\n *\n * **Records are keyed on the cell, not on its position.** A column-window slide\n * renumbers the slots while the surviving cells stay on their own columns, so a\n * position-keyed cache would miss on every survivor and lay out the whole row\n * again — which is most of the work a slide could avoid.\n *\n * **Geometry is the only layout input this cache writes**, so anything else\n * that moves a cell's layout has to deal with it, by laying the cell out itself\n * or by calling {@link clear}. The writes that do, framework-side:\n *\n * - `Cell.setActiveRenderer` — a `DynamicCell` swapping the child the layout\n * is fitted around.\n * - `Cell.startEdit` / `detachEditor` — the same swap, for the editor\n * (`detachEditor` is private, reached from `commitEdit` and `cancelEdit`).\n * - `TreeCellRenderer.setTreeState` — a depth change moves the indent.\n * - `HeaderCell.setHeaderGlyph` — a glyph shifts the renderer's left inset.\n * - `GlyphRenderer.setValue` — replaces its child outright.\n * - A theme change, which rewrites the padding and border every cell is fitted\n * against; both consumers {@link clear} on it.\n *\n * `Cell.wrapRenderer` also replaces the visible child without laying out, and\n * is public — it is safe only because its one caller wraps a cell it has just\n * built, which has no record yet.\n *\n * **A value write is usually not one of them**, which is what makes the skip\n * worth having: a renderer that only writes text moves nothing, because cell\n * renderers run with `setAutoMeasure(false)`. A renderer whose `setValue` adds,\n * removes or replaces a child is the exception — see `GlyphRenderer` — and owes\n * itself the layout. That applies to consumer renderers installed through\n * `ColumnConfig.renderer` as much as to the framework's own.\n *\n * @internal\n */\nexport class CellGeometryCache {\n\n private _geometry: WeakMap<Component, CellGeometry> = new WeakMap();\n\n /**\n * Drops every record, so the next pass re-applies and re-lays-out every\n * cell even where its geometry is unchanged.\n */\n clear(): void {\n this._geometry = new WeakMap();\n }\n\n /**\n * Writes `x` / `width` / `height` to `cell` and lays it out, unless the\n * cell already holds those exact values.\n *\n * @param cell - The cell to position.\n * @param x - The cell's x offset within its row, in pixels.\n * @param w - The cell's width, in pixels.\n * @param h - The cell's height, in pixels.\n */\n apply(cell: Component, x: number, w: number, h: number): void {\n const previous = this._geometry.get(cell);\n\n if (previous && previous.x === x && previous.w === w && previous.h === h) {\n return;\n }\n\n cell.setAutoCommitStyle(false);\n cell.setX(x);\n cell.setY(0);\n cell.setWidth(w);\n cell.setHeight(h);\n cell.setAutoCommitStyle(true);\n\n // A geometry change needs a full layout pass so the cell's renderer\n // re-fits the new width.\n cell.doLayout();\n\n // Recorded only once the cell has an element. Without one the layout\n // above could not do its job: the layout manager fits the renderer\n // against `getInnerSize()`, which is element-gated, so an unrendered\n // cell keeps a full-width box around an unsized renderer. Recording\n // that would make every later pass at this same geometry skip the cell\n // and leave it that way. Reachable from the header, which renders no\n // cells until its first layout pass and so can be driven through\n // `renderColumnWindow` before the table is realized; the body's pooled\n // rows are realized as they are built, so there the guard never fires.\n //\n // What is recorded is what was requested rather than what the setters\n // committed, which agree because a table cell carries no min/max and\n // does not clamp to its content.\n if (cell.getElement()) {\n this._geometry.set(cell, { x: x, w: w, h: h });\n }\n }\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 // The inner field's max is unpinned too, matching StringEditor: the\n // field has to fill the cell, and a TextField otherwise pins its max to\n // its own one-line box.\n this._textField.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 * Disposes the lazily-created picker dropdown, then runs the inherited\n * teardown. `_dropdown` is a LayerManager-mounted overlay (AnimatedDropdown\n * subclass), never a registered child, so the base destructor's recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._dropdown?.dispose();\n\n super.destructor();\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 * Disposes the lazily-created picker dropdown, then runs the inherited\n * teardown. `_dropdown` is a LayerManager-mounted overlay (AnimatedDropdown\n * subclass), never a registered child, so the base destructor's recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._dropdown?.dispose();\n\n super.destructor();\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 * Disposes the lazily-created picker dropdown, then runs the inherited\n * teardown. `_dropdown` is a LayerManager-mounted overlay (AnimatedDropdown\n * subclass), never a registered child, so the base destructor's recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._dropdown?.dispose();\n\n super.destructor();\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 * Disposes every editor this pool has lazily constructed, releasing their\n * per-instance stylesheet rules. Called once, from `Body.destructor()`,\n * when the owning table is torn down — a shared editor is acquired into\n * `_editors` only on a real edit gesture (`Cell.startEdit` → `acquire`),\n * held there for the table's whole lifetime, and detached-but-not-disposed\n * on every edit end (`Cell.detachEditor`'s `removeComponent`, which keeps a\n * reusable editor alive across edits) — so nothing else ever reaches it.\n */\n dispose(): void {\n for (const editor of this._editors.values()) {\n editor.dispose();\n }\n\n this._editors.clear();\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 { CellGeometryCache } from \"~/component/table/CellGeometry.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 { selectionsEqual } from \"~/component/shared/selectionsEqual.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 { 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\n/** Number of off-screen columns to render to the left and right of the viewport. */\nconst COLUMN_BUFFER = 2;\n\n/**\n * The horizontally-visible column range plus the geometry every rendered\n * cell is placed from. `firstCol` / `lastCol` are inclusive visible-column\n * indices; `lastCol` is `-1` when there are no columns.\n *\n * @internal\n */\nexport interface ColumnWindow {\n firstCol: number;\n lastCol : number;\n /** Effective width per visible column, index-aligned with visible-column order. */\n widths : number[];\n /** Left offset per visible column — the running sum of `widths`. */\n lefts : number[];\n}\n\n/**\n * Computes the column window for a horizontal scroll offset and viewport width.\n *\n * Walks `widths` left to right, accumulating each column's left offset. A\n * column is raw-visible when its right edge is at or after `scrollX` AND its\n * left edge is at or before `scrollX + viewportWidth` — an inclusive\n * comparison, so a table with no known widths yet (`widths` full of zeros,\n * e.g. before the layout manager has run) degrades to \"every column\n * renders\" rather than \"no column renders\". The raw-visible run is widened\n * by {@link COLUMN_BUFFER} on each side and clamped to `[0, n-1]`.\n *\n * @param widths - Effective width per visible column, in display order.\n * @param scrollX - The current horizontal scroll offset in pixels.\n * @param viewportWidth - The body's visible width in pixels.\n *\n * @returns The column window: the rendered range plus the widths/lefts it was derived from.\n *\n * @internal\n */\nexport function computeColumnWindow(\n widths : number[],\n scrollX : number,\n viewportWidth: number,\n): ColumnWindow {\n const n = widths.length;\n const lefts = new Array<number>(n);\n let x = 0;\n\n for (let i = 0; i < n; i++) {\n lefts[i] = x;\n x += widths[i];\n }\n\n if (n === 0) {\n return { firstCol: 0, lastCol: -1, widths, lefts };\n }\n\n const viewportRight = scrollX + viewportWidth;\n\n let firstRawVisible = -1;\n let lastRawVisible = -1;\n\n for (let i = 0; i < n; i++) {\n const left = lefts[i];\n const right = left + widths[i];\n\n if (right >= scrollX && left <= viewportRight) {\n if (firstRawVisible === -1) {\n firstRawVisible = i;\n }\n\n lastRawVisible = i;\n }\n }\n\n if (firstRawVisible === -1) {\n // No column's span touches the viewport at all (e.g. scrolled past\n // the content); fall back to rendering nothing beyond the buffer\n // around the nearest edge so a window is still returned.\n firstRawVisible = 0;\n lastRawVisible = 0;\n }\n\n const firstCol = Math.max(0, firstRawVisible - COLUMN_BUFFER);\n const lastCol = Math.min(n - 1, lastRawVisible + COLUMN_BUFFER);\n\n return { firstCol, lastCol, widths, lefts };\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 : CellGeometryCache = new CellGeometryCache();\n private _lastBodyWidth : number = 0;\n private _lastColumnWidths: number[] = [];\n private _lastAriaRowCount: number = -1;\n private _rowHeight : number;\n // Last column window `bindAndPositionRows` applied. Framework-managed\n // bookkeeping (recomputed from `renderWindow` on every pass), so per\n // ARCHITECTURE.md's DOM-write rule this gets no `BodyOptions` field or\n // public setter.\n private _colWindow : ColumnWindow = { firstCol: 0, lastCol: -1, widths: [], lefts: [] };\n // Re-entrancy guard: a commit fired mid-render cascades back into\n // `renderWindow` / `syncPoolCells` through `store.notifyRecordChanged`;\n // this makes the nested call a no-op. See `commitEditsOutsideWindow`.\n private _reconciling : boolean = false;\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(() => this.onThemeReflow());\n }\n\n /**\n * Refreshes the derived row height before the shared re-bind pass, so the\n * rows this reflow re-renders are positioned against the new line box.\n */\n protected onThemeReflow(): void {\n this._rowHeight = this.computeRowHeight();\n\n super.onThemeReflow();\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, windows it to the current column window, and\n * wires each of its cells to the shared editor pool + horizontal\n * scroll-into-view handler. The base's `growRowPool` owns the append +\n * parallel-array bookkeeping.\n *\n * @returns The wired, un-appended pool row.\n */\n protected createPoolRow(): Row {\n const row = this.createRow();\n\n row.setColumnWindow(this._colWindow.firstCol, this._colWindow.lastCol);\n this.wireRowCells(row);\n\n return row;\n }\n\n /**\n * Wires every one of `row`'s cells to the shared editor pool and the\n * horizontal scroll-into-view handler. Called from {@link createPoolRow}\n * for a freshly-pooled row and from {@link bindAndPositionRows} whenever\n * a reconcile changes the rendered cell set — both `setEditorPool` and\n * `setScrollIntoViewHandler` are idempotent for a surviving cell.\n *\n * @param row - The pool row whose cells to wire.\n */\n private wireRowCells(row: Row): void {\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\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.setColumnFields}\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 the per-cell geometry\n * with it, so the next renderWindow re-applies positions and sizes for\n * every visible row and every cell in it.\n */\n protected invalidateGeom(): void {\n super.invalidateGeom();\n\n this._cellGeom.clear();\n }\n\n /**\n * Updates the set of hidden column field names, records the new\n * visible-field list on every pooled row, and 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`. Recording the fields instead\n * builds and removes nothing here: each row reconciles its own cell\n * set on the following render, matching every column that survives\n * by field name so its renderer, editor, theme listener, sort state\n * and group tint are preserved.\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 * Commits every open edit (the whole rendered cell set is about to be\n * discarded, so `keep` is `null`), then walks every pool row and records\n * the new `_hiddenColumns` + `_columnConfigs` it should render from.\n * Builds no cells itself — marking a row's column fields dirty makes its\n * next {@link Row.setColumnWindow} (from {@link bindAndPositionRows}, on\n * the next `renderWindow`) reconcile the cell set against the new column\n * list, exactly as it does for a scroll-driven window slide. Also\n * invalidates the per-slot geometry caches so that render re-positions\n * cells against the new column count.\n *\n * Guarded by `_reconciling`, mirroring `renderWindow`: the commit pass\n * can cascade back into this method through `store.notifyRecordChanged`.\n */\n private syncPoolCells(): void {\n if (this._reconciling) {\n return;\n }\n\n this._reconciling = true;\n\n try {\n this.commitEditsOutsideWindow(null);\n\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.setColumnFields(\n this._store.model,\n this._hiddenColumns,\n this._columnConfigs,\n treeFieldName,\n );\n\n this.wireRowCells(row);\n\n // The row's column count changed, so its own width may have.\n // The cells' records are keyed on the cell, so they stay valid\n // across the re-point `setColumnWindow` performs next — a cell\n // moved onto a column at the same x and width genuinely needs\n // no reposition. What it may need is a layout, when the new\n // column changes something the layout fits around; the writes\n // that do that lay the cell out themselves.\n this._rowGeom[i] = null;\n }\n } finally {\n this._reconciling = false;\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 // A pass the startup font gate deferred rendered no rows, so the\n // mirrors these events drive — the header translate and the pinned-side\n // body — would be moved to an offset nothing was laid out at.\n if (this.wasRenderDeferred()) {\n return;\n }\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 * Disposes the shared cell-editor pool, then runs the inherited teardown\n * (which disposes the row pool and the scroller — see\n * VirtualRowView.destructor()). `_editorPool`'s cached editors are held in\n * a private Map, never a registered child of this body, so the base\n * destructor's recursion cannot reach them.\n */\n protected destructor(): void {\n this._editorPool.dispose();\n\n super.destructor();\n }\n\n /**\n * Runs a render pass the startup font gate deferred, once a layout reaches\n * this body again.\n *\n * @returns This body, for method chaining.\n *\n * @remarks Unlike `Tree`, this body does not render its window on every\n * layout — its passes come from the parent table layout and from store\n * events. The parent layout usually gets there first, calling\n * `renderWindow` directly with the column widths, which resumes the pass on\n * its own; this override covers the case where it does not, so a body whose\n * table is already clean still picks its deferred pass back up.\n */\n doLayout(): this {\n super.doLayout();\n this.renderWindowIfDeferred();\n\n return this;\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 // Cache the caller's widths before anything can return early. They come\n // from the parent layout and from nowhere else, so a pass the startup\n // font gate defers below would otherwise replay against the zero-width\n // cache this view starts with. This also means an unmounted body now\n // records widths (and may invalidate its row geometry) where it used to\n // return untouched — harmless, since its pool is empty.\n this.updateColumnWidthCache(bodyWidth, columnWidths);\n\n // A commit fired mid-render (see `commitEditsOutsideWindow`) cascades\n // back into this method through `store.notifyRecordChanged`; the\n // nested call is dropped rather than queued, both guarded methods\n // commit before they read the state they render from, so the next\n // (outer) pass sees the up-to-date result. Above the two early\n // returns below so a nested call still caches any widths it carries\n // (`updateColumnWidthCache` already ran) — only the render itself is\n // dropped.\n if (this._reconciling) {\n return;\n }\n\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n\n if (this.deferRenderWhileFirstLayoutHeld()) {\n return;\n }\n\n this._reconciling = true;\n\n try {\n this.renderWindowPass();\n } finally {\n this._reconciling = false;\n }\n\n // Applies any scroll offset the startup font gate held back. Unlike\n // `Tree` there is no post-render work to redo alongside it: every caller\n // that refreshes this body's active descendant is a user gesture —\n // focus, click, key — and none of those can land inside the startup\n // hold, before a single row exists. Outside the guard above: applying a\n // held offset re-enters `renderWindow` through the scroller's onScroll\n // hook, and that nested pass has to run.\n this.finishResumedRender();\n }\n\n /**\n * The body of `renderWindow`, below its early returns — recomputes the\n * row AND column windows, rebinds changed rows from the pool, and hides\n * excess rows. Split out so the public `renderWindow` can wrap it in the\n * `_reconciling` re-entrancy guard.\n */\n private renderWindowPass(): void {\n const scroller = this._scroller!;\n let records = this.getVisibleRecords();\n let 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 // Loose-clamp scroll positions against the new content sizes before\n // reading them for the window calc.\n let 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 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 const effectiveWidths = Array.from({ length: fieldCount }, (_, i) => this._lastColumnWidths[i] ?? fallback);\n\n this._colWindow = computeColumnWindow(effectiveWidths, scroller.getScrollX(), this.getWidth() || 0);\n\n // An edit whose column is about to leave the window is committed\n // before any pool row is rebound — mirrors the precedent\n // `Row.setColumnWindow` sets for a column-set change: commit before\n // discarding the cell that holds it. A commit can change what a\n // filtered/sorted store returns, so the row count is re-read.\n if (this.commitEditsOutsideWindow(this._colWindow)) {\n records = this.getVisibleRecords();\n totalRows = records.length;\n totalHeight = totalRows * this._rowHeight;\n }\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 this.bindAndPositionRows(win.firstRow, win.windowSize, rowWidth, records, this._colWindow);\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 * Commits every open edit whose rendered column falls outside `keep`,\n * ahead of a column-window change that is about to discard the cell\n * holding it — mirrors the precedent {@link Row.setColumnWindow} sets on\n * a column-set change: commit before discarding. Guarded by the same\n * `_reconciling` flag `renderWindow` sets: `Cell.commitEdit` emits\n * `\"commit\"` while the cell still reports `isEditing()`, and that emit\n * cascades through `store.notifyRecordChanged` back into `renderWindow`.\n *\n * @param keep - The column window to preserve; every rendered slot\n * outside it is a candidate. `null` treats every rendered slot as\n * outside — used by {@link syncPoolCells}, which is about to discard\n * the whole cell set for a hide/show or config swap.\n *\n * @returns `true` when at least one open edit was committed.\n */\n private commitEditsOutsideWindow(keep: ColumnWindow | null): boolean {\n let committed = false;\n\n for (const row of this._rowPool) {\n const cells = row.getComponents() as Cell<any>[];\n const start = row.getColumnWindowStart();\n\n for (let slot = 0; slot < cells.length; slot++) {\n const colIndex = start + slot;\n const outside = keep === null || colIndex < keep.firstCol || colIndex > keep.lastCol;\n\n if (outside && cells[slot].isEditing()) {\n cells[slot].commitEdit();\n committed = true;\n }\n }\n }\n\n return committed;\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 records - The current store records (passed in so this helper doesn't re-query).\n * @param columns - The column window: the rendered `[firstCol, lastCol]`\n * range plus the widths/lefts every rendered cell is placed from.\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, records: ModelRecord[], columns: ColumnWindow): void {\n const rowHeight = this._rowHeight;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const windowChanged = row.setColumnWindow(columns.firstCol, columns.lastCol);\n\n if (windowChanged) {\n // Slot → column mapping changed, so newly-entered cells need\n // the editor pool + scroll-into-view handler wired. The\n // geometry records survive: they are keyed on the cell, so a\n // cell that kept its column is still correctly recorded even\n // though its slot moved.\n this.wireRowCells(row);\n }\n\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 }\n\n if (wasRebound || windowChanged) {\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 let x = columns.lefts[columns.firstCol] ?? 0;\n\n for (let slot = 0; slot < cells.length; slot++) {\n const colW = columns.widths[columns.firstCol + slot] ?? 0;\n\n this._cellGeom.apply(cells[slot], x, colW, rowHeight);\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 const before = new Set(this._selectedRecords);\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(before);\n\n // Determine which column was clicked and update focused cell.\n // `resolveClickedColumn` scans the rendered cells and so returns a\n // slot, not a column index — convert by adding the window start so\n // `_focusedColIndex` and the \"cellclick\" payload stay in\n // visible-column order regardless of what the row currently renders.\n const targetHandle = e.target === null ? null : DOM.source.intern(e.target);\n const cells = row.getComponents();\n const slot = resolveClickedColumn(cells, targetHandle);\n const columnIndex = slot >= 0 ? slot + row.getColumnWindowStart() : -1;\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()[slot];\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 const before = new Set(this._selectedRecords);\n\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(before);\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 const before = new Set(this._selectedRecords);\n\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(before);\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 when the selected set changes; `\"cellclick\"`\n * fires when a data cell is clicked, carrying the clicked record, the\n * column's field name and visible index, the cell value, the record's\n * row index in the visible-records view, and 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 /**\n * Fires `\"selection\"` with the current selection, unless it has the same\n * membership as `before` — in which case the gesture that ran did not\n * actually change what was selected, and the event is skipped.\n *\n * @param before - The selection as it stood immediately before the\n * mutating gesture ran.\n */\n private notifySelectionChange(before: ReadonlySet<ModelRecord>): void {\n if (selectionsEqual(before, this._selectedRecords)) {\n return;\n }\n\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 once per row whenever that row\n * rebinds *or* its column window changes — the latter so a\n * `readOnly` column scrolling into view arrives read-only without\n * waiting for a rebind.\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 a rebind or a window change) 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 this._header?.setFocusedColumn(this._anchorRecord ? this._focusedColIndex : null);\n\n if (!this._anchorRecord) {\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 return;\n }\n\n const row = this._rowPool[poolSlotIdx];\n const cells = row.getComponents();\n const slot = this._focusedColIndex - row.getColumnWindowStart();\n const cell = (slot >= 0 && slot < cells.length) ? cells[slot] : undefined;\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 row = this._rowPool[poolSlotIdx];\n const cells = row.getComponents();\n const slot = this._focusedColIndex - row.getColumnWindowStart();\n const cell = (slot >= 0 && slot < cells.length) ? cells[slot] : undefined;\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 // Bring the newly-focused column into the rendered window before\n // refreshing the focus ring / active descendant against it.\n this.scrollColumnIntoView(this._focusedColIndex);\n this.renderWindow();\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 // Same as the ArrowLeft/ArrowRight branch: make sure the focused\n // column is in the rendered window before resolving its slot.\n this.scrollColumnIntoView(this._focusedColIndex);\n this.renderWindow();\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 row = this._rowPool[poolSlotIdx];\n const cells = row.getComponents();\n const slot = this._focusedColIndex - row.getColumnWindowStart();\n const cell = (slot >= 0 && slot < cells.length) ? cells[slot] : undefined;\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 { 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 { computeColumnWindow } from \"~/component/table/Body.js\";\nimport { CellGeometryCache } from \"~/component/table/CellGeometry.js\";\nimport type { ColumnWindow } from \"~/component/table/Body.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 geometry the table layout supplies to the header on each pass. Cached\n * by {@link TableHeader.renderColumnWindow} so a scroll-driven pass can\n * re-run with no argument.\n *\n * @category Components\n */\nexport interface HeaderColumnGeometry {\n /** Width per visible column, in display order. May be shorter than the column list. */\n columnWidths : number[];\n /** The width the columns are windowed against — the table's available column width. */\n viewportWidth : number;\n /** Height of the column-header row, in pixels. */\n columnHeight : number;\n /** Height of the parent-header row, in pixels; `0` when it is collapsed. */\n parentRowHeight: number;\n}\n\n/**\n * The header section of a table, rendered as a `<thead>` element.\n *\n * Builds one {@link HeaderCell} per column in its current column window —\n * the horizontally-visible range plus a small buffer, mirroring the body's\n * own column virtualization — rather than one per visible field up front.\n * Each cell is wired with a sort-click callback (cycles asc → desc → clear), a\n * resize-drag callback (forwarded to the owner via the `\"columnresize\"`\n * event), and a context-menu callback (forwarded via the\n * `\"columncontextmenu\"` event); see {@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 // Non-hidden fields, in display order — the full column list the\n // rendered window is carved out of. Populated by `rebuildCells`.\n private _visibleFields: Field[] = [];\n // Visible-column index of slot 0. The rendered columns are always a\n // contiguous run, so slot `s` holds column `_windowFirst + s`.\n private _windowFirst : number = 0;\n private _scrollX : number = 0;\n private _focusedCol : number | null = null;\n // Set by `rebuildCells` so the next `renderColumnWindow` reconciles even\n // when the requested range happens to match the current one — a\n // column-set change can leave the range unchanged while the cells\n // behind it need to change.\n private _columnsDirty : boolean = true;\n private _geometry : HeaderColumnGeometry = { columnWidths: [], viewportWidth: 0, columnHeight: 0, parentRowHeight: 0 };\n // Geometry last written to each header cell, shared with the body's rows;\n // see `CellGeometryCache` for the invariant it rests on.\n private _cellGeom : CellGeometryCache = new CellGeometryCache();\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 // Drops the records so the next layout pass re-fits every cell against\n // the new theme; see `CellGeometryCache` for why a theme change needs\n // that and geometry alone cannot detect it.\n //\n // Re-rendering from inside this callback, as\n // `VirtualRowView.onThemeReflow` does for the body, would run too\n // early: each cell renderer holds its own theme subscription that\n // rewrites the insets the pass has to fit against, and those renderers\n // subscribe after this header does, so the re-render would fit against\n // the outgoing theme's padding.\n this.subscribeTheme(() => this._cellGeom.clear());\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 * Swaps the store whose sort state this header drives and displays.\n *\n * @param store - The new store to bind to this header.\n *\n * @remarks Internal wiring called by the owning {@link Table} when its\n * bound store or display mode changes. Not for consumer use.\n */\n setStore(store: AbstractStore): this {\n this._store = store;\n\n return this;\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, rebuilding the parent row\n * immediately and marking the column row's cells for reconciliation.\n *\n * The column row renders no cells until the next {@link renderColumnWindow},\n * which every caller reaches through the table's own layout pass.\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 rendered header cell components, in slot order. The column row\n * lives at child index 1 — the parent-header row sits at index 0.\n *\n * A slot maps to a visible-column index by adding\n * {@link getColumnWindowStart}: only the horizontally-visible column\n * window (plus a small buffer) is rendered, so this is no longer every\n * visible column.\n *\n * @returns An array of the rendered cell components from the column-header row.\n */\n getColumns() {\n return this.getComponents()[1].getComponents();\n }\n\n /**\n * Returns the visible-column index of the first rendered header cell.\n *\n * @returns The visible-column index slot `0` currently renders.\n */\n getColumnWindowStart(): number {\n return this._windowFirst;\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 * 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 height. Row-level widths, heights, and positions\n * are set independently by the table layout manager — `TableHeader` itself\n * just stores the total band height. The per-row assignments\n * live in `layout/Table.doLayout` because the split depends on\n * {@link hasParentRow} and on the header's own content box.\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 * Recomputes the visible-field list from the current model and hidden\n * set, marks the rendered cell set dirty, and re-syncs the sort\n * indicators. Builds no cells itself: the column row renders nothing\n * until the next {@link renderColumnWindow}, which reconciles against\n * the window the table layout hands it — mirroring `Body`, which\n * renders no rows until its first `renderWindow`.\n */\n private rebuildCells(): void {\n this._visibleFields = this.computeVisibleFields(this._model);\n this._columnsDirty = true;\n\n this.syncSortIndicators();\n }\n\n /**\n * Filters and orders `model`'s fields to the currently-visible set:\n * every field not in `_hiddenColumns`, sorted by {@link Field.getOrder}.\n * Called by {@link rebuildCells} to populate `_visibleFields`, the\n * single derivation `syncSortIndicators` and {@link reconcileColumnCells}\n * both read from instead of re-deriving it themselves.\n *\n * @param model - The model whose fields to filter and order.\n * @returns The visible fields, in display order.\n */\n private computeVisibleFields(model: AbstractModel): Field[] {\n return model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n }\n\n /**\n * Reconciles the column-row's rendered header cells to the\n * horizontally-visible column range `[firstCol, lastCol]`. A header\n * cell carries no per-kind identity — unlike the body's cell\n * reconciler — so any leftover cell can serve any entering column:\n *\n * 1. A column keeps the cell that already holds its field, matched by\n * field name.\n * 2. Every other column in the range recycles a leftover cell, or\n * builds a fresh one when none remains.\n * 3. Every per-column property (label, tooltip, glyph, group tint,\n * required marker, ARIA column index) is re-applied to every\n * rendered cell, whether or not it was re-targeted, so a recycled\n * cell never shows a trace of its previous column.\n *\n * Cells left over after the window is filled are removed and disposed.\n * Called from {@link renderColumnWindow}, which positions the returned\n * cells afterward.\n *\n * @param firstCol - The first visible-column index to render, inclusive.\n * @param lastCol - The last visible-column index to render, inclusive.\n * @returns `true` when the rendered cell set changed.\n */\n private reconcileColumnCells(firstCol: number, lastCol: number): boolean {\n const row = this.getComponents()[1] as Row;\n\n if (!this._columnsDirty\n && firstCol === this._windowFirst\n && lastCol === this._windowFirst + row.getComponents().length - 1) {\n return false;\n }\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 slotCount = lastCol - firstCol + 1;\n const assigned: (HeaderCell | undefined)[] = new Array(slotCount).fill(undefined);\n\n // Pass 1 — keep a cell for its own field.\n for (let col = firstCol; col <= lastCol; col++) {\n const name = this._visibleFields[col].getName();\n const cell = byName.get(name);\n\n if (cell) {\n assigned[col - firstCol] = cell;\n byName.delete(name);\n }\n }\n\n const free = Array.from(byName.values());\n\n // Pass 2 — recycle a leftover, else build.\n for (let col = firstCol; col <= lastCol; col++) {\n const slot = col - firstCol;\n\n if (assigned[slot] !== undefined) {\n continue;\n }\n\n const field = this._visibleFields[col];\n let cell = free.pop();\n\n if (cell) {\n row.setLayoutConstraints(cell, { data: field });\n } else {\n cell = new HeaderCell(field.getName(), field.getName(), null);\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 // columnIndexOf) at emit time, so a later hide/show/scroll\n // that shifts indices needs no re-wiring. Re-wiring a\n // surviving cell would stack duplicate listeners on its\n // ListenerBag — making a single drag emit `columnresize`\n // several times with mismatched indices, and a single header\n // click cycle the sort twice.\n this.wireCell(cell);\n }\n\n assigned[slot] = cell;\n }\n\n // Pass 3 — per-column state, re-applied to every rendered cell so a\n // recycled cell never shows a trace of its previous column.\n for (let col = firstCol; col <= lastCol; col++) {\n const cell = assigned[col - firstCol]!;\n const field = this._visibleFields[col];\n const column = columnMap.get(field.getName());\n\n cell.setFieldName(field.getName());\n cell.setHeaderText(column?.getHeaderText() ?? field.getName());\n\n const description = field.getDescription();\n\n if (cell.getTooltip() !== description) {\n cell.setTooltip(description);\n }\n\n const headerGlyph = column?.getHeaderGlyph() ?? null;\n\n if (cell.getHeaderGlyph() !== headerGlyph) {\n cell.setHeaderGlyph(headerGlyph);\n }\n\n cell.setBaseBackground(column?.getGroupColor() ?? null);\n cell.setRequired(column?.isRequired() ?? false);\n cell.getAria().setColIndex(col + 1);\n }\n\n // Discard what is left over.\n for (const cell of free) {\n row.removeComponent(cell);\n cell.dispose();\n }\n\n // Re-order children to the window's display order so sibling\n // iteration (e.g. `syncSortIndicators`) matches slot order.\n //\n // The order comes from `assigned`, which this reconciler just built in\n // slot order — not from re-sorting on `Field.getOrder()`. A model that\n // declares no `order` returns the -1 sentinel for every field, so an\n // order-based comparison ties throughout, the sort is a stable no-op,\n // and a recycled cell keeps whatever index it already held. That\n // desynchronises slot from column: `getColumns()[s]` stops being the\n // cell for column `_windowFirst + s`, which silently misplaces\n // geometry, resize indices, sort arrows and the focus underline.\n const slotOf = new Map(assigned.map((cell, slot) => [cell, slot]));\n\n row.sortComponents((c1, c2) =>\n (slotOf.get(c1 as HeaderCell) ?? 0) - (slotOf.get(c2 as HeaderCell) ?? 0));\n\n this._windowFirst = firstCol;\n this._columnsDirty = false;\n\n return true;\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.disposeAllComponents();\n\n if (!this.hasParentRow()) {\n return;\n }\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 columnIndexOf} when the event\n * fires, rather than capturing an index at wiring time. This keeps the\n * index correct after a hide/show/reorder/scroll shuffles the columns\n * without re-wiring — re-wiring would stack duplicate listeners on the\n * surviving 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.columnIndexOf(cell), clientX));\n cell.on(\"resizedrag\", (clientX) => this.emit(\"columnresize\", this.columnIndexOf(cell), clientX));\n cell.on(\"contextmenu\", (fieldName, x, y) => this.emit(\"columncontextmenu\", fieldName, x, y));\n }\n\n /**\n * Converts a rendered cell to its visible-column index — the slot it\n * occupies in the column row plus the window's start offset.\n *\n * @param cell - The header cell to resolve.\n * @returns The visible-column index, or `-1` when the cell is not currently rendered.\n */\n private columnIndexOf(cell: HeaderCell): number {\n const slot = this.getColumns().indexOf(cell);\n\n return slot === -1 ? -1 : this._windowFirst + slot;\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 rendered 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 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, slot) => {\n const field = this._visibleFields[this._windowFirst + slot];\n const entry = field ? fieldToSorter.get(field.getName()) : undefined;\n\n if (entry) {\n cell.setSortState(entry.dir, showPriority ? entry.priority : null);\n } else {\n cell.clearSortState();\n }\n });\n }\n\n /**\n * Reconciles the rendered header cells to the horizontally-visible column\n * range and positions every rendered cell in both rows.\n *\n * @param geometry - Replaces the cached geometry when supplied; the cached\n * value is reused when omitted.\n * @returns This header, for method chaining.\n */\n renderColumnWindow(geometry?: HeaderColumnGeometry): this {\n if (geometry) {\n this._geometry = geometry;\n }\n\n const g = this._geometry;\n const widths = this._visibleFields.map((_, i) => g.columnWidths[i] ?? 0);\n const win = computeColumnWindow(widths, this._scrollX, g.viewportWidth);\n const changed = this.reconcileColumnCells(win.firstCol, win.lastCol);\n\n if (changed) {\n this.syncSortIndicators();\n this.applyFocusedColumn();\n }\n\n this.positionColumnCells(win, g.columnHeight);\n this.positionParentCells(win, g.parentRowHeight);\n\n return this;\n }\n\n /**\n * Positions every rendered column-row cell from the window's `lefts`\n * and `widths` arrays — the same geometry {@link reconcileColumnCells}\n * just reconciled the cell set against.\n *\n * @param win - The column window computed by {@link renderColumnWindow}.\n * @param columnHeight - The column row's height in pixels.\n */\n private positionColumnCells(win: ColumnWindow, columnHeight: number): void {\n const cells = this.getColumns();\n\n for (let slot = 0; slot < cells.length; slot++) {\n const col = win.firstCol + slot;\n\n this._cellGeom.apply(cells[slot], win.lefts[col] ?? 0, win.widths[col] ?? 0, columnHeight);\n }\n }\n\n /**\n * Positions every parent-row cell from the window's `lefts` / `widths`\n * arrays in constant time: a cell's x is the left offset of its\n * `spanFrom` column, and its width is the sum of every column across\n * its span.\n *\n * @param win - The column window computed by {@link renderColumnWindow}.\n * @param parentRowHeight - The parent row's height in pixels; `0` when collapsed.\n */\n private positionParentCells(win: ColumnWindow, parentRowHeight: number): void {\n const row = this.getParentRow();\n\n for (const cell of row.getComponents()) {\n const lc = row.getLayoutConstraints(cell);\n const span = lc?.data as { spanFrom: number, spanTo: number } | undefined;\n const from = span?.spanFrom ?? 0;\n const to = span?.spanTo ?? 0;\n const x = win.lefts[from] ?? 0;\n const w = (win.lefts[to] ?? 0) + (win.widths[to] ?? 0) - x;\n\n this._cellGeom.apply(cell, x, w, parentRowHeight);\n }\n }\n\n /**\n * Mirrors the body's horizontal scroll offset onto the header's two inner\n * rows and re-renders the column window.\n *\n * Translates the two inner rows (parent row + column row) rather than the\n * header element itself — the header band stays pinned to the viewport\n * width so its background covers the vertical-scrollbar reserve band on\n * the right edge, and only the cells inside scroll with the body.\n *\n * @param scrollLeft - The new horizontal scroll offset, in pixels.\n * @returns This header, for method chaining.\n */\n setScrollX(scrollLeft: number): this {\n if (scrollLeft === this._scrollX) {\n return this;\n }\n\n this._scrollX = scrollLeft;\n\n this.getParentRow().setTranslate(-scrollLeft, 0);\n this.getComponents()[1].setTranslate(-scrollLeft, 0);\n\n this.renderColumnWindow();\n\n return this;\n }\n\n /**\n * Returns the horizontal scroll offset last applied.\n *\n * @returns The scroll offset in pixels.\n */\n getScrollX(): number {\n return this._scrollX;\n }\n\n /**\n * Paints the column-focus underline on the rendered cell for `colIndex`\n * and clears it everywhere else. `null` clears every cell.\n *\n * @param colIndex - The visible-column index to focus, or `null` to clear.\n * @returns This header, for method chaining.\n */\n setFocusedColumn(colIndex: number | null): this {\n this._focusedCol = colIndex;\n\n this.applyFocusedColumn();\n\n return this;\n }\n\n /**\n * Re-applies `_focusedCol` to the currently-rendered cells. Called\n * directly from {@link setFocusedColumn} and again at the end of every\n * reconcile that changed the rendered set, so a cell recycled into the\n * focused column picks up the underline without waiting for the next\n * explicit `setFocusedColumn` call.\n */\n private applyFocusedColumn(): void {\n const cells = this.getColumns() as HeaderCell[];\n\n for (let slot = 0; slot < cells.length; slot++) {\n cells[slot].setColumnFocused(this._windowFirst + slot === this._focusedCol);\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 { 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 // The row is a child, so it already starts inside the footer's border;\n // handing it the outer width would push it that far past the far edge.\n // Before the element exists there is no border to subtract, so the\n // argument is the whole box.\n this.getComponents()[0].setWidth(this.getContentBounds()?.width ?? 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 // The footer gives itself a 1px top border in its constructor, so the\n // row was a pixel taller than the band's content box and its bottom\n // pixel fell under `overflow: hidden`. Nothing displays a footer today\n // — there is no footer-visibility setter — so this corrects the\n // arithmetic rather than any rendered pixel.\n this.getComponents()[0].setHeight(this.getContentBounds()?.height ?? 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 _width : number | undefined;\n private _maxContentLength: number | undefined;\n private _hidden : boolean;\n private _unhideable : boolean;\n private _readOnly : boolean;\n private _headerGlyph : string | null;\n private _headerText : 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._width = config?.width;\n this._maxContentLength = config?.maxContentLength;\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._headerText = config?.headerText ?? 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 the explicit starting width in pixels declared in the spec.\n *\n * @returns The declared width, or `undefined` if unset.\n */\n getWidth(): number | undefined {\n return this._width;\n }\n\n /**\n * Returns the longest value this column can hold, in characters, as\n * declared in the spec.\n *\n * @returns The declared character budget, or `undefined` if unset.\n */\n getMaxContentLength(): number | undefined {\n return this._maxContentLength;\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 * Returns the configured header-label override for this column.\n *\n * @returns The override text, or `null` when unset (the header falls back to\n * the field name).\n */\n getHeaderText(): string | null {\n return this._headerText;\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 * @internal\n */\n 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 { MemoryStore } from \"~/data/MemoryStore.js\";\nimport { Model } from \"~/data/Model.js\";\nimport type { Field } from \"~/data/Field.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 { CellType, ColumnConfig, ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport { ColumnSpec, normalizeComboOptions } 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\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { chainRoom, distributeDragChain, DRAG_DISTRIBUTION_EPSILON } from \"~/core/DragChain.js\";\n\n/** Events emitted by {@link Table}. */\nexport type TableEvent = \"selection\" | \"cellclick\";\n\n/** Which presentation a Table renders: record-per-row, or one record as key/value rows. */\nexport type TableDisplayMode = \"normal\" | \"rotated\";\n\n// The model backing the rotated key/value projection: one record per source\n// field, `field` holding the field name and `value` holding that field's value\n// on the displayed record. `value` is declared `'auto'` because the projection\n// carries every source field's native type (string, number, boolean, Date, …)\n// in the same column. `filler` is a blank, unbounded spacer column that soaks\n// up leftover table width so `field` / `value` stay compact on a wide table.\nconst ROTATED_MODEL = new Model([\n { name: 'field', type: 'string', order: 0 },\n { name: 'value', type: 'auto', order: 1 },\n { name: 'filler', type: 'string', order: 2 }, // blank spacer column; absorbs leftover width\n]);\n\n// Column-width policy constants. See plans/implemented/table-generated-column-widths.md\n// for the derivation of each value.\nconst MIN_COLUMN_WIDTH_PX = 30; // Absolute floor. No policy minimum ever falls below it.\nconst CHECKBOX_WIDTH_PX = 16; // The checkbox box edge (Checkbox.ts).\nconst GLYPH_WIDTH_PX = 20; // One icon-font glyph square at the default body font, plus slack.\nconst MIN_NUMBER_DIGITS = 4; // Digits a number column must always fit.\nconst DEFAULT_NUMBER_DIGITS = 8; // Digits assumed for a number column with neither a sample nor a hint.\nconst MIN_STRING_CHARS = 8; // Characters a string column must always fit.\nconst STRING_WIDTH_PX = 100; // Starting width for a flex column that needs a concrete number.\nconst HEADER_CHROME_PX = 21; // 4px cell padding + 5px resize-handle gutter + 12px sort indicator.\nconst CELL_CHROME_PX = 6; // 4px cell padding + 2px so the last glyph isn't flush against the border.\nconst AUTO_WIDTH_CAP_PX = 400; // Ceiling for a measured width when the column declares no maxWidth.\nconst SAMPLE_ROWS = 50; // Records read per derivation.\nconst WIDEST_CANDIDATES = 3; // Strings measured per sampled column (the longest by character count).\nconst HINT_SAMPLE_MAX_CHARS = 60; // Cap on the maxContentLength probe string.\n// Formatted to measure the width of a date/time/datetime column; a fixed\n// instant so the measurement is deterministic across runs.\nconst REFERENCE_DATE = new Date(2000, 11, 31, 23, 59, 59);\n\n// Sub-pixel tolerance below which a resize-drag-grown column total is treated\n// as equal to the available width (`_columnWidthTarget` collapses to 0\n// instead of recording a target the layout would treat as already-grown).\n// The same 0.5 the file's existing width comparisons use (see setColumnVisible).\nconst WIDTH_TARGET_EPSILON_PX = 0.5;\n\n/**\n * The floor and starting width a column's field type implies, before any\n * declared `minWidth`/`maxWidth`/`width` override is applied.\n */\ninterface WidthPolicy {\n min: number;\n /** `null` = flex: no definite width, share the leftover space. */\n preferred: number | null;\n}\n\n/** Cached reference measurements consulted by the cheap {@link Table.getColumnMinWidth} path. */\ninterface WidthReferences {\n /** Width of the widest digit glyph, \"0\" through \"9\". */\n digitPx: number;\n /** Width of `REFERENCE_DATE` formatted, keyed by `${type}:${showSeconds}`. */\n datePx: Map<string, number>;\n}\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 * {@link setDisplayMode | Rotated mode} (`\"normal\"` | `\"rotated\"`) swaps the\n * presentation to psql `\\x`-style key/value rows: one `field`/`value` row per\n * source field of the selected record, built from a two-field\n * [`Model`](/api/data/classes/Model) projection and driven through the same\n * header and body — including per-field cell variants via\n * {@link ColumnConfig}'s `cellType` / `cellValues`. The projection is\n * read-only; see {@link setDisplayMode} for details.\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 // Index of the column whose right edge is being dragged; `null` when no\n // drag is live.\n private _dragEdgeIndex : number | null = null;\n // Pointer x consumed so far — advanced only by applied travel (see\n // `onColumnResize`), never the raw `clientX`.\n private _dragLastClientX : number = 0;\n // The total column width a resize drag grew the table to, or `0` when the\n // table sits at its available width. Backing field for `getColumnWidthTarget`.\n private _columnWidthTarget: 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 private _displayMode : TableDisplayMode = \"normal\";\n private _rotatedRecord : ModelRecord | null = null;\n private _rotatedStore : MemoryStore | null = null;\n private _rotatedColumns : Column[] = [];\n private _rotatedConfigs : Map<string, ColumnConfig> = new Map();\n private _rotatedFieldByName: Map<string, Field> = new Map();\n private _sourceRefresh : (() => void) | null = null;\n private _suppressSelectionForward: boolean = false;\n private _autoWidthsSampled: boolean = false;\n private _widthRefs : WidthReferences | null = null;\n // Longest sampled candidate strings from the last content derivation,\n // keyed by field name. Populated by `collectCandidates`; consulted by\n // `sampledDigits` and `resolveContentCandidates`. A plain cache read —\n // never re-scans the store — so `getColumnMinWidth` can consult it\n // without violating its \"never samples the store\" contract.\n private _sampledCandidates: Map<string, string[]> = new Map();\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.bindSourceStore(store);\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.\n this._body.on(\"horizontalscroll\", scrollLeft => {\n this._header.setScrollX(scrollLeft);\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. Suppressed during a bindView re-bind (whose\n // transient `_body.selectRecord(null)` would otherwise leak a spurious\n // empty selection) and while rotated, where selection is driven by\n // `_rotatedRecord` instead of the body's own selection state.\n this._body.on(\"selection\", records => {\n if (this._suppressSelectionForward || this._displayMode === \"rotated\") {\n return;\n }\n\n this.emit(\"selection\", records);\n });\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 columns this table currently renders: the two-column\n * `field`/`value` projection while {@link getDisplayMode} is `\"rotated\"`,\n * otherwise the resolved, visible source columns in field display order.\n *\n * @returns The active {@link Column} instances in display order.\n */\n getColumns(): Column[] {\n return this._displayMode === \"rotated\" ? this._rotatedColumns : this.getSourceColumns();\n }\n\n /**\n * Returns the resolved, visible source columns in display order,\n * regardless of the active display mode.\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 source {@link Column} instances in field display order.\n */\n private getSourceColumns(): Column[] {\n const effective = this.getEffectiveHiddenSet();\n\n return this._resolvedColumns.filter(c => !effective.has(c.getField().getName()));\n }\n\n /**\n * Returns the active display mode.\n *\n * @returns `\"normal\"` (one row per record) or `\"rotated\"` (one selected\n * record shown as key/value rows). Defaults to `\"normal\"`.\n */\n getDisplayMode(): TableDisplayMode {\n return this._displayMode;\n }\n\n /**\n * Switches between the normal (record-per-row) and rotated (key/value)\n * presentations. No-op when already in `mode`.\n *\n * Entering `\"rotated\"` adopts the currently selected record as the\n * displayed record — falling back to the store's first record, then to\n * nothing — and re-points the header and body at a two-column `field`/\n * `value` projection built from it; see the [`Table` rotated record\n * view](/components/Table#rotated-record-view) docs. Returning to\n * `\"normal\"` restores the source columns and re-selects the record that\n * was displayed while rotated. The projection is read-only; sorting it\n * reorders the field rows without touching the source store's own sort.\n *\n * @param mode - The display mode to switch to.\n * @returns This table, for method chaining.\n */\n setDisplayMode(mode: TableDisplayMode): this {\n if (mode === this._displayMode) {\n return this;\n }\n\n this._displayMode = mode;\n\n if (mode === \"rotated\") {\n this._rotatedRecord = this._body.getSelectedRecord() ?? this._store.getRecords()[0] ?? null;\n\n const rotatedStore = this.ensureRotatedStore();\n\n this.rebuildRotatedStore();\n this.bindView(rotatedStore, this._rotatedColumns, this._rotatedConfigs, new Set(), () => true);\n this.emit(\"selection\", this._rotatedRecord ? [this._rotatedRecord] : []);\n } else {\n this.bindView(this._store, this.getSourceColumns(), this._columnConfigs, this.getEffectiveHiddenSet(), this._spec?.rowReadOnly ?? null);\n this._body.selectRecord(this._rotatedRecord);\n }\n\n return this;\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. Leaves rotated mode first — the\n * projection is built from the outgoing store, and would sort/select the\n * wrong store otherwise. Ends with a layout pass, so the header's new\n * columns render immediately — matching `setColumnVisible` / `resetColumns`\n * / `bindView`, which each end in a layout too.\n *\n * @param store - The new store to bind to the table.\n */\n setStore(store: AbstractStore): this {\n this.setDisplayMode(\"normal\");\n\n this._header.setStore(store);\n this.unbindSourceStore(this._store);\n\n this._store = store;\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n this._autoWidthsSampled = false;\n this._widthRefs = null;\n this._sampledCandidates = new Map();\n this._resolvedColumns = Column.resolve(store.model.getFields(), this._spec);\n\n this.bindSourceStore(store);\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 // Matches setColumnVisible / resetColumns / bindView, which each end\n // in a layout too: the header now renders no cells until its first\n // renderColumnWindow, so without this a store swap on an\n // already-sized table would leave the header blank until something\n // else happened to trigger a layout. A no-op on an unsized table —\n // layout/Table.doLayout returns early when the container size isn't finite.\n this.doLayout();\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 * The pixel width the columns are laid out against: the table's inner\n * width less the reserved vertical-scrollbar track. Shared by the\n * column-resize drag handler and the layout manager so both derive the\n * same number.\n *\n * @returns The available column width in pixels, or `0` before first render.\n */\n getAvailableColumnWidth(): number {\n const innerSize = this.getInnerSize();\n\n return innerSize ? innerSize.width - DOM.source.getScrollBarWidth() : 0;\n }\n\n /**\n * The total column width a resize drag grew the table to, or `0` when the\n * table sits at its available width. Called by the layout manager so a\n * drag-widened total survives the next layout pass instead of being\n * rescaled back to the container's width.\n *\n * @returns The drag-grown column width target in pixels, or `0`.\n */\n getColumnWidthTarget(): number {\n return this._columnWidthTarget;\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 * A no-op while {@link getDisplayMode} is `\"rotated\"` — the projection's\n * `field` and `value` columns are always both shown.\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 (this._displayMode === \"rotated\") {\n return this;\n }\n\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 // The visible column set just changed; see the matching comment in\n // `resetColumns` / `bindView` for why `_widthRefs` must be cleared\n // whenever it does.\n this._widthRefs = null;\n\n const newVisibleColumns = this.getColumns();\n let intrinsic: Array<number | null> | null = null;\n\n const rawWidths = newVisibleColumns.map((col, i) => {\n const saved = this._savedColumnWidths.get(col.getField().getName());\n\n if (saved !== undefined) {\n return saved;\n }\n\n intrinsic ??= this.getIntrinsicColumnWidths();\n\n return intrinsic[i] ?? this.flexColumnWidth(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 // Keep a grown table's target in step with the new column set: showing a\n // column trims the others back to the same total, hiding one frees its width.\n if (this._columnWidthTarget > 0) {\n this._columnWidthTarget = this._columnWidths.reduce((s, w) => s + w, 0);\n }\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. While\n * rotated, the new record becomes the displayed record instead of\n * scrolling (there is no row to scroll to — see {@link selectRecord}).\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\n if (this._displayMode === \"rotated\") {\n this.selectRecord(record);\n\n return record;\n }\n\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. While rotated,\n * removes the displayed record; the source-store listener then re-targets\n * the view to the store's next remaining record.\n */\n removeSelectedRow(): this {\n if (this._displayMode === \"rotated\") {\n if (this._rotatedRecord) {\n this._store.remove(this._rotatedRecord);\n }\n\n return this;\n }\n\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 * While {@link getDisplayMode} is `\"rotated\"`, this re-targets the\n * projection: `record` becomes the displayed record and its field/value\n * rows are rebuilt, instead of moving the normal-mode row selection.\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 (this._displayMode === \"rotated\") {\n if (record === this._rotatedRecord) {\n return this;\n }\n\n this._rotatedRecord = record;\n this.rebuildRotatedStore();\n this.emit(\"selection\", record ? [record] : []);\n\n return this;\n }\n\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 * While {@link getDisplayMode} is `\"rotated\"`, returns the displayed\n * source record — never a projection (`field`/`value`) record.\n *\n * @returns The selected {@link ModelRecord}, or null.\n */\n getSelectedRecord(): ModelRecord | null {\n if (this._displayMode === \"rotated\") {\n return this._rotatedRecord;\n }\n\n return this._body.getSelectedRecord();\n }\n\n /**\n * Returns all currently selected records. While {@link getDisplayMode}\n * is `\"rotated\"`, returns the single displayed source record (or an\n * empty array when none is displayed) — never projection records.\n *\n * @returns An array of selected {@link ModelRecord} instances.\n */\n getSelectedRecords(): ModelRecord[] {\n if (this._displayMode === \"rotated\") {\n return this._rotatedRecord ? [this._rotatedRecord] : [];\n }\n\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 * 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, this.getColumnMinWidth(columns[i]));\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] - this.getColumnMinWidth(col)\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 * Subscribes to the source store's mutation events so a rotated view\n * tracks the record it displays. Stores the callback in `_sourceRefresh`\n * so {@link unbindSourceStore} can remove exactly this registration later.\n *\n * @param store - The source store to subscribe to.\n */\n private bindSourceStore(store: AbstractStore): void {\n const refresh = () => this.onSourceStoreChange();\n\n this._sourceRefresh = refresh;\n\n store.on('load', refresh);\n store.on('add', refresh);\n store.on('remove', refresh);\n store.on('datachange', refresh);\n }\n\n /**\n * Unsubscribes the callback installed by {@link bindSourceStore} from `store`.\n *\n * @param store - The store to unsubscribe from.\n */\n private unbindSourceStore(store: AbstractStore): void {\n if (!this._sourceRefresh) {\n return;\n }\n\n (['load', 'add', 'remove', 'datachange'] as const).forEach(e =>\n store.off(e, this._sourceRefresh!)\n );\n }\n\n /**\n * Keeps the rotated projection in sync with the source store. Also the\n * hook for the one-shot auto-size re-derive (see\n * {@link maybeResampleColumnWidths}), which runs regardless of display\n * mode. Outside rotated mode this is otherwise a no-op. Re-targets\n * `_rotatedRecord` to the store's first record (firing `\"selection\"`)\n * when the displayed record no longer exists in the store — e.g. it was\n * removed, or the store was reloaded — then rebuilds the projection\n * against the (possibly new) record.\n */\n private onSourceStoreChange(): void {\n this.maybeResampleColumnWidths();\n\n if (this._displayMode !== \"rotated\") {\n return;\n }\n\n const records = this._store.getRecords();\n const stillPresent = this._rotatedRecord !== null && records.includes(this._rotatedRecord);\n\n if (!stillPresent) {\n const before = this._rotatedRecord;\n this._rotatedRecord = records[0] ?? null;\n\n if (this._rotatedRecord !== before) {\n this.emit(\"selection\", this._rotatedRecord ? [this._rotatedRecord] : []);\n }\n }\n\n this.rebuildRotatedStore();\n }\n\n /**\n * Returns the lazily-built projection store, constructing it (and the\n * two resolved `field`/`value` columns and their configs) on first use.\n *\n * @returns The projection {@link MemoryStore}, reused across mode switches.\n */\n private ensureRotatedStore(): MemoryStore {\n if (this._rotatedStore) {\n return this._rotatedStore;\n }\n\n const spec: ColumnSpec = {\n columns: [\n { field: 'field', minWidth: 80, maxWidth: 200, unhideable: true },\n {\n field: 'value',\n minWidth: 120,\n maxWidth: 360,\n unhideable: true,\n cellType: (r) => this.rotatedCellType(r),\n cellValues: (r) => this.rotatedCellValues(r),\n },\n { field: 'filler', headerText: '', minWidth: 0, unhideable: true },\n ],\n rowReadOnly: () => true,\n };\n\n this._rotatedStore = new MemoryStore(ROTATED_MODEL, []);\n this._rotatedColumns = Column.resolve(ROTATED_MODEL.getFields(), spec);\n this._rotatedConfigs = this.buildColumnConfigs(spec);\n\n return this._rotatedStore;\n }\n\n /**\n * Rebuilds the projection store's records from `_rotatedRecord` — one row\n * per visible source field — and refreshes the `_rotatedFieldByName` map\n * used by {@link rotatedCellType} / {@link rotatedCellValues} to resolve\n * each row's source field. Resolution is by the row's own `field` value\n * (not by index), so it stays correct after the projection is sorted by\n * clicking a header.\n */\n private rebuildRotatedStore(): void {\n const store = this.ensureRotatedStore();\n const fields = this.getSourceColumns().map(c => c.getField());\n const record = this._rotatedRecord;\n\n // Refresh the field lookup BEFORE loadData. loadData fires the store's\n // `load` event synchronously, and while the body is already bound to\n // the projection store (i.e. switching the displayed record) that\n // reraises straight into rotatedCellType / rotatedCellValues to rebind\n // each value cell. Building the lookup afterwards left that first\n // render resolving every value cell against a stale map: cellType fell\n // back to the column's `auto` type, the string renderer was swapped in\n // unlaid-out, and the value column stayed blank until the next scroll.\n this._rotatedFieldByName = new Map(fields.map(f => [f.getName(), f]));\n\n store.loadData(record\n ? fields.map(f => ({ field: f.getName(), value: record.get(f.getName()) }))\n : []);\n }\n\n /**\n * Per-row cell-variant resolver for the rotated `value` column\n * (`ColumnConfig.cellType`): a row whose source field declares `values`\n * in the table's own spec renders as a combo, otherwise it renders as\n * its source field's own type. O(1) and pure, as the `cellType` contract\n * requires.\n *\n * @param record - The projection record for one field/value row.\n * @returns The cell variant to render, or `null` if the row's source\n * field cannot be resolved (should not happen for a row this table built).\n */\n private rotatedCellType(record: ModelRecord): CellType | null {\n const field = this._rotatedFieldByName.get(record.get('field') as string);\n\n if (!field) {\n return null;\n }\n\n const values = this._columnConfigs.get(field.getName())?.values;\n\n return (values && values.length > 0) ? 'combo' : field.getType();\n }\n\n /**\n * Per-row combo-option resolver for the rotated `value` column\n * (`ColumnConfig.cellValues`), consulted only for rows where\n * {@link rotatedCellType} resolves to `'combo'`. O(1) and pure, as the\n * `cellValues` contract requires.\n *\n * @param record - The projection record for one field/value row.\n * @returns The source field's declared `values`, or `undefined`.\n */\n private rotatedCellValues(record: ModelRecord): Array<ComboOption | string> | undefined {\n const field = this._rotatedFieldByName.get(record.get('field') as string);\n\n return field ? this._columnConfigs.get(field.getName())?.values : undefined;\n }\n\n /**\n * Re-points the header and body at `store` / `columns`, the shared\n * sequence behind both {@link setDisplayMode} and {@link setStore}.\n *\n * @param store - The store the header and body should render.\n * @param columns - The resolved columns to display.\n * @param configs - The column-config map (drives `cellType` / `cellValues` / etc).\n * @param hidden - The set of field names to hide.\n * @param rowReadOnly - The row-level read-only predicate, or `null`.\n *\n * @remarks `Body.setStore` re-renders with pool rows whose cells still\n * match the outgoing model; `setColumns` (called after `setStore`) is\n * what re-syncs those cells, so the order matters. `_body.selectRecord(null)`\n * transiently fires the body's own `\"selection\"` — `_suppressSelectionForward`\n * gates the Table-level forwarder for the duration of the re-bind so that\n * transient clear never reaches consumers. Clearing `_columnWidths` /\n * `_savedColumnWidths` is what makes the layout manager re-initialise\n * widths for the new column count on the next `doLayout`.\n *\n * `setStore` is called again at the end (re-assigning the same store) to\n * force a second full rebind after the column/config/hidden/read-only\n * state has settled: `Body`'s own per-slot metadata (read-only tint,\n * required-empty, ARIA) is only re-applied on a slot whose bound index\n * changes, and the first `setStore` call already consumed that \"changed\"\n * signal — on the model swap alone, before `setColumnConfigs` /\n * `setColumns` have synced the pool's cells to the new column shape. A\n * second `setStore` re-triggers the same store-change rebind, this time\n * over the fully-synced cells, so e.g. a rotated column's freshly-built\n * `DynamicCell` actually receives `setReadOnly(true)`.\n */\n private bindView(\n store: AbstractStore,\n columns: Column[],\n configs: Map<string, ColumnConfig>,\n hidden: Set<string>,\n rowReadOnly: ((record: ModelRecord) => boolean) | null,\n ): void {\n this._suppressSelectionForward = true;\n\n this._header.setStore(store);\n this._header.setModel(store.model);\n this._header.setColumns(columns);\n this._header.setHiddenColumns(hidden);\n\n this._body.selectRecord(null);\n this._body.setStore(store);\n this._body.setColumnConfigs(configs);\n this._body.setColumns(columns);\n this._body.setHiddenColumns(hidden);\n this._body.setRowReadOnly(rowReadOnly);\n this._body.setStore(store);\n\n this._suppressSelectionForward = false;\n\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n // The visible column set just changed shape (source columns <->\n // rotated field/value/filler), and `_widthRefs`'s date/time/datetime\n // reference entries are derived from that set (`dateReferenceKeys`\n // walks `getColumns()`), so a cache built under the old mode would\n // answer for temporal types the new mode doesn't have — or miss ones\n // it does.\n this._widthRefs = null;\n\n this.getAria().setColCount(this.getColumns().length);\n this.doLayout();\n }\n\n /**\n * Disposes the column context menu, then runs the inherited teardown.\n * `_columnContextMenu` is a LayerManager-mounted panel, never a\n * registered child (see Menu.ts's class comment), so\n * `super.destructor()`'s child recursion cannot reach it.\n */\n protected destructor(): void {\n this._columnContextMenu.dispose();\n\n super.destructor();\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). While rotated,\n * shows only the export entries (when enabled) — there are no per-column\n * show/hide toggles for a two-column projection.\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 if (this._displayMode === \"rotated\") {\n if (!this._exportMenuEnabled) {\n return;\n }\n\n this._columnContextMenu.show(x, y, [\n { text: 'Export as CSV', action: () => this.exportCSV() },\n { text: 'Export as JSON', action: () => this.exportJSON() },\n ]);\n\n return;\n }\n\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. Mode-independent:\n * always exports the source table's records and columns, never the\n * rotated field/value projection.\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. Mode-independent:\n * always exports the source table's records and columns, never the\n * rotated field/value projection.\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.getSourceColumns();\n }\n\n /**\n * Captures the dragged edge when a column-resize drag begins: the index of\n * the column whose right edge is being dragged and the pointer's starting\n * `clientX`. {@link onColumnResize} distributes each subsequent frame's\n * travel from this state, nearest-first, across the columns fanning\n * outward from the edge.\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 if (colIndex < 0 || colIndex >= this._columnWidths.length) {\n return;\n }\n\n this._dragEdgeIndex = colIndex;\n this._dragLastClientX = clientX;\n }\n\n /**\n * Handles a column resize drag: the dragged edge splits the visible\n * columns into a left chain `[colIndex, colIndex - 1, …, 0]` and a right\n * chain `[colIndex + 1, …, n - 1]`, each ordered nearest-first. This\n * frame's pointer travel is applied on top of the live widths — the\n * nearest column in the direction of travel absorbs it first, spilling to\n * the next only once it hits its `minWidth`/`maxWidth`.\n *\n * Scavenging from the right chain only happens while the columns still fit\n * the viewport. Moving right, the right chain gives up width first; once it\n * is exhausted, further travel grows the table's total column width instead\n * of stalling, and the table starts to scroll horizontally. From that point\n * on the right chain is left alone entirely — a table already wider than its\n * viewport grows and shrinks as a whole, so the dragged edge only moves the\n * columns left of it. Moving left, that accrued growth is given back first\n * — the total never falls below {@link getAvailableColumnWidth} — and only\n * travel past it, once the table fits again, regrows the right chain. The\n * grown-or-not total is recorded via `_columnWidthTarget` so the layout\n * manager preserves it instead of rescaling it away.\n *\n * The tracked pointer position advances only by the travel actually\n * applied, not the raw `clientX`. When every chain is exhausted the\n * applied travel is zero, so travel past the limit accrues a dead zone the\n * pointer must retrace before the edge moves again — keeping the cursor\n * glued to the handle on reversal instead of the handle jumping to meet a\n * far-off cursor.\n *\n * The pass is queued onto the animation-frame layout queue rather than run\n * synchronously, so every move dispatched within one frame collapses into a\n * single pass. No pass is needed between moves — the drag arithmetic reads\n * only state a layout pass does not produce.\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 private onColumnResize(colIndex: number, clientX: number): void {\n if (this._dragEdgeIndex === null || colIndex !== this._dragEdgeIndex) {\n return;\n }\n\n const widths = this._columnWidths;\n const columns = this.getColumns();\n const mins = columns.map(col => col.getMinWidth() ?? MIN_COLUMN_WIDTH_PX);\n const maxs = columns.map(col => col.getMaxWidth() ?? Number.POSITIVE_INFINITY);\n\n // Nearest-first chains fanning out from the dragged edge.\n const left : number[] = [];\n const right: number[] = [];\n\n for (let i = colIndex; i >= 0; i--) {\n left.push(i);\n }\n\n for (let i = colIndex + 1; i < widths.length; i++) {\n right.push(i);\n }\n\n const frameDelta = clientX - this._dragLastClientX;\n const sign = frameDelta >= 0 ? 1 : -1;\n const available = this.getAvailableColumnWidth();\n const total = widths.reduce((s, w) => s + w, 0);\n // Growth already accrued, which a leftward drag gives back before the right\n // chain grows. Never negative: the total is floored at `available`.\n const growth = Math.max(0, total - available);\n\n const delta = sign > 0\n ? Math.min(frameDelta, chainRoom(left, widths, 1, mins, maxs))\n : Math.min(-frameDelta, chainRoom(left, widths, -1, mins, maxs), chainRoom(right, widths, 1, mins, maxs) + growth);\n\n if (delta <= DRAG_DISTRIBUTION_EPSILON) {\n return; // dead zone — the tracked pointer deliberately stays put\n }\n\n // Rightward: the right chain absorbs everything it can, the rest grows the\n // total — unless the table already overflows, in which case nothing is\n // scavenged and the whole travel grows the total. Leftward: the accrued\n // growth is given back first, and only travel past it (the table fits\n // again) is absorbed by the right chain.\n const absorbed = sign > 0\n ? (growth > WIDTH_TARGET_EPSILON_PX ? 0 : delta)\n : delta - Math.min(delta, growth);\n const out = widths.slice();\n\n distributeDragChain(left, widths, delta, sign, mins, maxs, out);\n distributeDragChain(right, widths, absorbed, -sign, mins, maxs, out);\n\n this._dragLastClientX += sign * delta;\n this._columnWidths = out;\n\n const newTotal = out.reduce((s, w) => s + w, 0);\n\n this._columnWidthTarget = newTotal > available + WIDTH_TARGET_EPSILON_PX ? newTotal : 0;\n\n this.scheduleLayout();\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._columnWidthTarget = 0;\n // The visible column set may change shape (a spec-hidden temporal\n // column reappears, or vice versa), and `_widthRefs`'s date/time/\n // datetime entries are derived from `dateReferenceKeys`' walk of the\n // visible columns — see the same clear in `bindView`.\n this._widthRefs = null;\n\n const columns = this.getColumns();\n const intrinsic = this.getIntrinsicColumnWidths();\n\n this._columnWidths = columns.map((col, i) => intrinsic[i] ?? this.flexColumnWidth(col));\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n this.doLayout();\n }\n\n /**\n * Returns whether `string`/`auto` columns are sized from sampled cell\n * values. Always `false` in rotated mode — the projection's `field` /\n * `value` / `filler` columns carry hand-tuned min/max widths already,\n * and auto-sizing them is out of scope (see the plan's Non-Goals).\n *\n * @returns `true` when the spec declares `autoSizeColumns: true` AND\n * the table is in normal (non-rotated) display mode.\n */\n isAutoSizeColumns(): boolean {\n return (this._spec?.autoSizeColumns ?? false) && this._displayMode !== \"rotated\";\n }\n\n /**\n * Returns the column's declared `minWidth`, or the floor its field type\n * implies. Cheap enough for the drag path — reads cached reference\n * measurements and a cached sample-candidate map, never the store or\n * the DOM.\n *\n * @param column - The column to compute a minimum width for.\n * @returns The minimum width in pixels.\n */\n getColumnMinWidth(column: Column): number {\n return column.getMinWidth() ?? this.columnWidthPolicy(column, 0, null).min;\n }\n\n /**\n * Computes one starting width per visible column, in display order,\n * from each column's field-type policy, any declared `width` /\n * `minWidth` / `maxWidth`, and — for `string`/`auto` columns under\n * {@link isAutoSizeColumns} — a bounded sample of the store.\n *\n * @returns One entry per visible column. A number is a definite\n * starting width; `null` means \"no definite width — share the\n * remaining space\", which only happens for a `string`/`auto` column\n * with no auto-sized content to measure.\n */\n getIntrinsicColumnWidths(): Array<number | null> {\n const columns = this.getColumns();\n const headerPx = this.measureHeaders(columns);\n const contentPx = this.measureContent(columns);\n\n return columns.map((col, i) => {\n const policy = this.columnWidthPolicy(col, headerPx[i], contentPx[i]);\n const raw = col.getWidth() ?? policy.preferred;\n\n if (raw === null) {\n return null;\n }\n\n return this.clampColumnWidth(raw, col, policy);\n });\n }\n\n /**\n * Clamps a raw width into a column's `[minWidth, maxWidth]` envelope,\n * falling back to the policy floor and {@link AUTO_WIDTH_CAP_PX} when\n * the column declares no explicit bound.\n */\n private clampColumnWidth(w: number, col: Column, policy: WidthPolicy): number {\n return Util.clamp(w, col.getMinWidth() ?? policy.min, col.getMaxWidth() ?? AUTO_WIDTH_CAP_PX);\n }\n\n /**\n * Returns the width, in pixels, one column should start at when it\n * cannot share leftover space with its siblings — the fallback used by\n * {@link setColumnVisible} and {@link resetColumns} for a flex column\n * whose `getIntrinsicColumnWidths` entry is `null`. Measures only this\n * one column's header, so it is the one exception to the\n * three-batched-calls derivation budget; it runs only on that fallback\n * path.\n *\n * @param col - The flex column to compute a standalone width for.\n */\n private flexColumnWidth(col: Column): number {\n const headerPx = this.measureHeaders([col])[0];\n const policy = this.columnWidthPolicy(col, headerPx, null);\n\n return this.clampColumnWidth(Math.max(STRING_WIDTH_PX, headerPx), col, policy);\n }\n\n /**\n * Returns the header text width in pixels, plus {@link HEADER_CHROME_PX},\n * for each column — one batched measurement under the header font.\n */\n private measureHeaders(columns: Column[]): number[] {\n const texts = columns.map(col => col.getHeaderText() ?? col.getField().getName());\n const widths = Util.measureTextWidths(texts, {\n fontSize : \"var(--ts-ui-table-header-font-size, var(--ts-ui-font-size))\",\n fontWeight: \"bold\",\n });\n\n return widths.map(w => w + HEADER_CHROME_PX);\n }\n\n /**\n * Returns the sampled content width in pixels for each column — `null`\n * for every column the policy does not need content for. The only part\n * of the derivation that reads the store (via {@link collectCandidates}).\n * A single batched measurement covers every `string`/`auto` column with\n * something to measure.\n */\n private measureContent(columns: Column[]): Array<number | null> {\n this.collectCandidates(columns);\n\n const measurable = columns.map(col => this.resolveContentCandidates(col));\n\n const toMeasure: string[] = [];\n const owner: number[] = [];\n\n measurable.forEach((candidates, i) => {\n candidates?.forEach(text => {\n owner.push(i);\n toMeasure.push(text);\n });\n });\n\n const widths = Util.measureTextWidths(toMeasure);\n const contentPx: Array<number | null> = columns.map(() => null);\n\n widths.forEach((w, k) => {\n const i = owner[k];\n\n contentPx[i] = Math.max(contentPx[i] ?? 0, w);\n });\n\n return contentPx;\n }\n\n /**\n * Resolves the strings to measure for one `string`/`auto` column,\n * falling back from sampled candidates to `values` option labels, to a\n * `maxContentLength` probe string, in that order. Returns `null` for\n * every other field type, for every `string`/`auto` column when\n * {@link isAutoSizeColumns} is `false` (the whole chain is gated by the\n * flag, not just the store sample), and for a `string`/`auto` column\n * under auto-size with nothing to measure (the column stays flex).\n */\n private resolveContentCandidates(col: Column): string[] | null {\n const type = col.getField().getType();\n\n if (type !== \"string\" && type !== \"auto\") {\n return null;\n }\n\n // autoSizeColumns gates the whole fallback chain below, not just the\n // store sample: with the flag off a string/auto column stays flex,\n // exactly as before this feature, even when it declares `values` or\n // `maxContentLength`.\n if (!this.isAutoSizeColumns()) {\n return null;\n }\n\n const sampled = this._sampledCandidates.get(col.getField().getName());\n\n if (sampled && sampled.length > 0) {\n return sampled;\n }\n\n const config = this._columnConfigs.get(col.getField().getName());\n\n if (config?.values && config.values.length > 0) {\n return normalizeComboOptions(config.values)\n .map(o => o.label)\n .sort((a, b) => b.length - a.length)\n .slice(0, WIDEST_CANDIDATES);\n }\n\n const maxLen = col.getMaxContentLength();\n\n if (maxLen !== undefined) {\n return [\"0\".repeat(Math.min(maxLen, HINT_SAMPLE_MAX_CHARS))];\n }\n\n return null;\n }\n\n /**\n * Reads at most {@link SAMPLE_ROWS} records by index and keeps, per\n * column that needs content (see {@link samplesRecordText}), the\n * {@link WIDEST_CANDIDATES} longest distinct formatted values. Stores\n * the result in `_sampledCandidates`, keyed by field name, for\n * {@link resolveContentCandidates} and {@link sampledDigits} to consult.\n * Reads by `store.getAt` rather than `store.getRecords()`, which\n * copies the entire filtered view.\n */\n private collectCandidates(columns: Column[]): void {\n const wanted = columns.map(col => this.samplesRecordText(col));\n const rows = Math.min(SAMPLE_ROWS, this._store.getCount());\n const best = new Map<string, string[]>();\n\n for (let r = 0; r < rows; r++) {\n const record = this._store.getAt(r)!;\n\n columns.forEach((col, i) => {\n if (!wanted[i]) {\n return;\n }\n\n const name = col.getField().getName();\n const raw = record.get(name);\n const text = String(TableExporter.formatValue(col, raw, this._columnConfigs) ?? \"\");\n const list = best.get(name) ?? [];\n\n this.keepLongest(list, text);\n best.set(name, list);\n });\n }\n\n if (rows > 0) {\n this._autoWidthsSampled = true;\n }\n\n this._sampledCandidates = best;\n }\n\n /**\n * Keeps `list` to at most {@link WIDEST_CANDIDATES} entries, longest\n * first, dropping an exact duplicate of a string already held.\n */\n private keepLongest(list: string[], text: string): void {\n if (list.includes(text)) {\n return;\n }\n\n list.push(text);\n list.sort((a, b) => b.length - a.length);\n\n if (list.length > WIDEST_CANDIDATES) {\n list.length = WIDEST_CANDIDATES;\n }\n }\n\n /**\n * Returns whether `collectCandidates` should read this column's values:\n * always for `number` (its sample feeds a digit count, not a\n * measurement), and for `string`/`auto` only under\n * {@link isAutoSizeColumns}. A column with a `renderer` is never\n * sampled — its rendered text is not derived from the raw value. Nor is\n * a column that declares `values`: it uses its option labels instead of\n * the store, never as well as it.\n */\n private samplesRecordText(col: Column): boolean {\n const config = this._columnConfigs.get(col.getField().getName());\n\n if (config?.renderer) {\n return false;\n }\n\n if (config?.values && config.values.length > 0) {\n return false;\n }\n\n const type = col.getField().getType();\n\n if (type === \"number\") {\n return true;\n }\n\n return (type === \"string\" || type === \"auto\") && this.isAutoSizeColumns();\n }\n\n /**\n * Returns the longest `String(value).length` seen for `col` during the\n * last {@link collectCandidates} pass, or `null` when the column was\n * not sampled. A length scan over the cached candidates, never a\n * measurement and never a store read.\n */\n private sampledDigits(col: Column): number | null {\n const candidates = this._sampledCandidates.get(col.getField().getName());\n\n if (!candidates || candidates.length === 0) {\n return null;\n }\n\n return Math.max(...candidates.map(s => s.length));\n }\n\n /** Reads `showSeconds` from this column's config, defaulting to `false`. */\n private showsSeconds(col: Column): boolean {\n return this._columnConfigs.get(col.getField().getName())?.showSeconds ?? false;\n }\n\n /**\n * Returns the per-type width policy for `col`: a `{min, preferred}`\n * pair derived from its field type, cached reference measurements, and\n * — for `number`/`string`/`auto` — sampled content. `headerPx` and\n * `contentPx` may be passed as `0`/`null` (as {@link getColumnMinWidth}\n * does) because no branch's `min` depends on either.\n */\n private columnWidthPolicy(col: Column, headerPx: number, contentPx: number | null): WidthPolicy {\n const refs = this.ensureWidthReferences();\n const type = col.getField().getType();\n\n switch (type) {\n case \"boolean\": {\n const min = CHECKBOX_WIDTH_PX + CELL_CHROME_PX;\n\n return { min, preferred: Math.max(min, headerPx) };\n }\n\n case \"glyph\": {\n const min = GLYPH_WIDTH_PX + CELL_CHROME_PX;\n\n return { min, preferred: Math.max(min, headerPx) };\n }\n\n case \"date\":\n case \"time\":\n case \"datetime\": {\n const key = `${type}:${this.showsSeconds(col)}`;\n const min = (refs.datePx.get(key) ?? 0) + CELL_CHROME_PX;\n\n return { min, preferred: Math.max(min, headerPx) };\n }\n\n case \"number\": {\n const min = refs.digitPx * MIN_NUMBER_DIGITS + CELL_CHROME_PX;\n const digits = col.getMaxContentLength() ?? this.sampledDigits(col) ?? DEFAULT_NUMBER_DIGITS;\n\n return { min, preferred: Math.max(min, refs.digitPx * digits + CELL_CHROME_PX, headerPx) };\n }\n\n default: { // \"string\" and \"auto\"\n const min = Math.max(MIN_COLUMN_WIDTH_PX, refs.digitPx * MIN_STRING_CHARS + CELL_CHROME_PX);\n\n if (contentPx === null) {\n return { min, preferred: null }; // auto-size off, or nothing to measure — flex column\n }\n\n return { min, preferred: Math.max(min, contentPx, headerPx) };\n }\n }\n }\n\n /**\n * Returns the cached width-reference bundle, measuring it once (in one\n * batched call) the first time anything asks. `dateReferenceKeys` scans\n * only the currently visible columns, so this is cleared everywhere\n * that set can change: `setStore`, `maybeResampleColumnWidths`,\n * `bindView` (a display-mode switch), `setColumnVisible`, and\n * `resetColumns`.\n */\n private ensureWidthReferences(): WidthReferences {\n if (this._widthRefs) {\n return this._widthRefs;\n }\n\n const digits = [\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"];\n const keys = this.dateReferenceKeys();\n const widths = Util.measureTextWidths([...digits, ...keys.map(k => k.text)]);\n\n this._widthRefs = {\n digitPx: Math.max(...widths.slice(0, digits.length)),\n datePx : new Map(keys.map((k, i) => [k.key, widths[digits.length + i]])),\n };\n\n return this._widthRefs;\n }\n\n /**\n * Walks the visible columns and builds one `{key, text}` reference pair\n * per distinct `(temporal type, showSeconds)` combination in use,\n * formatting {@link REFERENCE_DATE} the same way the matching cell\n * renderer would (via `TableExporter.formatValue`).\n */\n private dateReferenceKeys(): Array<{ key: string; text: string }> {\n const seen = new Map<string, string>();\n\n for (const col of this.getColumns()) {\n const type = col.getField().getType();\n\n if (type !== \"date\" && type !== \"time\" && type !== \"datetime\") {\n continue;\n }\n\n const key = `${type}:${this.showsSeconds(col)}`;\n\n if (!seen.has(key)) {\n seen.set(key, String(TableExporter.formatValue(col, REFERENCE_DATE, this._columnConfigs) ?? \"\"));\n }\n }\n\n return Array.from(seen, ([key, text]) => ({ key, text }));\n }\n\n /**\n * Re-derives column widths once, the first time the source store's\n * `'load'` / `'add'` / `'remove'` / `'datachange'` events find records —\n * so a table built before its data arrives sizes itself against the\n * data once it shows up. A no-op when auto-size is off, when the store\n * is still empty, or after the first successful derivation for this\n * store (see {@link Table.setStore}, which resets the guard).\n */\n private maybeResampleColumnWidths(): void {\n if (!this.isAutoSizeColumns() || this._autoWidthsSampled || this._store.getCount() === 0) {\n return;\n }\n\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n this._widthRefs = null;\n\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 type { ColumnSpec } from \"~/component/table/ColumnConfig.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 /**\n * Constructs a TablePanel bound to the given store.\n *\n * @param store - The data store to bind to the managed table.\n * @param spec - Optional column spec, forwarded to the underlying\n * {@link Table} unchanged; omit to auto-generate all columns.\n */\n constructor(store: AbstractStore, spec?: ColumnSpec) {\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, spec);\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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"}
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/CellText.ts","../../../src/typescript/lib/component/table/cell/Dynamic.ts","../../../src/typescript/lib/component/table/cell/Default.ts","../../../src/typescript/lib/component/table/cell/GroupSeparator.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/cell/renderer/Filter.ts","../../../src/typescript/lib/component/table/cell/FilterClauseBadge.ts","../../../src/typescript/lib/component/table/ColumnFilter.ts","../../../src/typescript/lib/component/table/cell/Filter.ts","../../../src/typescript/lib/component/table/CellGeometry.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/Header.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 { ThemeManager } from \"~/core/Theme.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { TRACK_WIDTH } from \"~/component/container/Scrollbar.js\";\n\n/**\n * Construction-time options for the {@link Table} layout manager.\n *\n * @category Layouts\n */\ninterface TableLayoutOptions extends LayoutManagerOptions {\n}\n\n// The filter row's height is the filter input's own single-line box, not an\n// offset from `columnHeight`: the input's chrome (TextField's default 3px\n// top+bottom padding, `_defaultTextFieldOptions.padding`; zero border, since\n// FilterCellRenderer zeroes the input's border) is independent of the\n// table's own `theme.table.cell.padding`, which sizes columnHeight instead —\n// a non-default cell padding would make an offset-from-columnHeight formula\n// wrong in either direction.\nconst FILTER_INPUT_PADDING = new Insets(3, 3, 3, 3);\nconst NO_INSETS = new Insets(0, 0, 0, 0);\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 from the container's\n * [`getIntrinsicColumnWidths`](/api/component/table/classes/Table#getIntrinsicColumnWidths) —\n * a per-type width policy, refined by any `minWidth` / `maxWidth` / `width` declared in the\n * column spec: compact types (`boolean`, `glyph`, `date`, `time`, `datetime`, `number`)\n * receive a type-derived width floored at the header text width; string and auto columns\n * either share the remaining space equally or, under `autoSizeColumns`, size to their\n * sampled content, clamped to their constraints either way. On container resize\n * `boolean` / `number` / `date` columns keep their width unchanged; every other column\n * (including `glyph`, `time`, and `datetime`) scales proportionally like a flexible\n * column, again clamped to their per-column constraints. Every per-column minimum\n * is read through [`getColumnMinWidth`](/api/component/table/classes/Table#getColumnMinWidth),\n * so a column never squeezes below the floor its type or spec implies.\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 * toward the container's available width, or toward a wider total a resize drag grew the\n * table to, whichever is larger, so a drag-widened table is not rescaled back down.\n */\n doLayout() {\n const container = <TableComponent>this.getContainer();\n\n if (!container) {\n return;\n }\n\n const containerSize = container.getInnerSize();\n\n // A table whose size has not been resolved yet reports NaN dimensions\n // rather than `null` — `getInnerSize` subtracts the perimeter from an\n // unset `_width`/`_height`. That happens whenever a layout is driven by\n // something other than the parent's sizing pass: a store load that\n // re-derives column widths fires one from the store event, which for a\n // generated table typically lands before the table has ever been sized.\n // Laying out against NaN would poison the width array — `availableWidth`\n // is NaN, and the slack arithmetic downstream compares NaN (every\n // comparison is false, so no early return catches it) — so the widths\n // are left alone and the next properly-sized pass derives them.\n if (!containerSize || !Number.isFinite(containerSize.width) || !Number.isFinite(containerSize.height)) {\n return;\n }\n\n const containerInsets = container.getContentInsets();\n const columns = container.getColumns();\n const columnCount = container.getColumns().length;\n const availableWidth = container.getAvailableColumnWidth();\n // A resize drag may have grown the table's total column width past\n // `availableWidth`; rescaling toward the target instead of the raw\n // available width is what keeps `rescaleWidths` from squeezing a\n // drag-widened table back to the container's width on the next pass.\n const targetWidth = Math.max(availableWidth, container.getColumnWidthTarget());\n\n let columnWidths = container.getColumnWidths();\n\n if (columnWidths.length !== columnCount) {\n columnWidths = this.initializeWidths(container, columns, availableWidth);\n container.setColumnWidths(columnWidths);\n } else {\n columnWidths = this.rescaleWidths(container, columns, columnWidths, targetWidth);\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\n // Same \"does this row apply\" gate as the parent row, but the\n // height itself is NOT columnHeight-derived — the filter row\n // holds a TextField, whose own chrome differs from the column\n // row's plain text. See FILTER_INPUT_PADDING's comment.\n const filterRowHeight = header.hasFilterRow()\n ? Util.singleLineBoxHeight(NO_INSETS, FILTER_INPUT_PADDING, { top: 0, bottom: 0 })\n : 0;\n\n // The band's bottom border is chrome outside the rows, so the\n // outer height is what the rows need PLUS the header's own\n // perimeter — the same \"children plus the container perimeter\"\n // sum every other manager's size report uses. Taking it out of a\n // row instead would make a header cell shorter than the body row\n // it heads, by an amount the theme's border width decides.\n const headerPerimeter = header.getPerimeterSize();\n const headerBandHeight = parentRowHeight + columnHeight + filterRowHeight\n + headerPerimeter.top + headerPerimeter.bottom;\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 // The rows are the header's own children, so their frame is the\n // header's content box, not the band: a row placed at the band's\n // origin and sized to the band starts inside the border and\n // overruns the far edge.\n const headerBox = header.getContentBounds()\n ?? { x: 0, y: 0, width: containerSize.width, height: headerBandHeight };\n\n // Rows stay at least as wide as the visible content box, and\n // wider when the columns overflow it, so cells off the right\n // edge can translate into view — because `Row`'s default\n // `overflow: hidden` would otherwise clip cells at the row's\n // right edge and prevent them from coming into view when the\n // inner rows translate left during a horizontal scroll.\n const columnSum = columnWidths.reduce((s, w) => s + w, 0);\n const innerRowW = Math.max(headerBox.width, columnSum);\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 its\n // cell pool is empty anyway — `header.renderColumnWindow`\n // below skips positioning it.\n const parentRow = header.getParentRow();\n parentRow.setAutoCommitStyle(false);\n parentRow.setX(headerBox.x);\n parentRow.setY(headerBox.y);\n parentRow.setWidth(innerRowW);\n parentRow.setHeight(parentRowHeight);\n parentRow.setAutoCommitStyle(true);\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(headerBox.x);\n columnRow.setY(headerBox.y + parentRowHeight);\n columnRow.setWidth(innerRowW);\n columnRow.setHeight(columnHeight);\n columnRow.setAutoCommitStyle(true);\n\n // Filter row sits beneath the column row, collapsing to zero\n // height (and zero cells, via `header.hasFilterRow()`) exactly\n // like the parent row above when it has nothing to show.\n const filterRow = header.getFilterRow();\n filterRow.setAutoCommitStyle(false);\n filterRow.setX(headerBox.x);\n filterRow.setY(headerBox.y + parentRowHeight + columnHeight);\n filterRow.setWidth(innerRowW);\n filterRow.setHeight(filterRowHeight);\n filterRow.setAutoCommitStyle(true);\n\n // Reconciles the header's rendered cells to the\n // horizontally-visible column range and positions every\n // rendered cell in all three rows — the header-side counterpart\n // of `body.renderWindow` below.\n header.renderColumnWindow({\n columnWidths,\n viewportWidth: availableWidth,\n columnHeight,\n parentRowHeight,\n filterRowHeight,\n });\n\n // Positions the column-menu button over the vertical-scrollbar\n // reservation band at the header's right edge, spanning it the\n // full band width and height (top to bottom, parent-header row\n // included when one is present) — the button carries the\n // header's own background and a divider (see the `TableHeader`\n // constructor), so it alone keeps cells scrolled horizontally\n // appearing to clip at the trackW boundary and the band visually\n // continuous with the header's gradient. Placed from `headerBox`,\n // the same rectangle the rows above are positioned against.\n //\n // Both axes are pinned via `setPreferredSize` rather than left to\n // the button's own glyph-derived size: `Button.setPreferredSize`\n // permanently opts it out of its own auto-sizing pipeline (see\n // its doc comment), which is what keeps this size from being\n // undone the next time `Absolute` (`TableHeader`'s own layout\n // manager) re-commits every child at `preferredSize ?? size` on\n // a header-level layout.\n //\n // `trackW` is the custom `Scrollbar`'s fixed track width, not the\n // native probe — the band must match what `Body`'s\n // `VirtualScroller` actually renders its own `Scrollbar` at.\n const trackW = TRACK_WIDTH;\n const menuButton = header.getMenuButton();\n const buttonSize = { width: trackW, height: headerBox.height };\n\n menuButton.setPreferredSize(buttonSize);\n menuButton.setAutoCommitStyle(false);\n menuButton.setX(headerBox.x + headerBox.width - trackW);\n menuButton.setY(headerBox.y);\n menuButton.setWidth(buttonSize.width);\n menuButton.setHeight(buttonSize.height);\n // `Absolute.doLayout`'s own `commitBounds` is what normally\n // cascades a freshly-positioned child into its own `doLayout()`\n // (see `LayoutManager.commitBounds`); this button is instead\n // committed via raw setters, mirroring how the header/body/\n // footer/rows above are positioned, so that cascade has to be\n // called explicitly here. Without it, the button's own `Fit`\n // layout never runs and its glyph is never actually placed —\n // present in the DOM, sized, but with no committed position, so\n // nothing paints.\n menuButton.doLayout();\n menuButton.setAutoCommitStyle(true);\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 // Same \"children plus perimeter\" sum as the header band above:\n // the footer's own top border is chrome outside the row, so the\n // outer height grows by it instead of the row shrinking inside it.\n const footerPerimeter = footer.getPerimeterSize();\n const footerBandHeight = columnHeight + footerPerimeter.top + footerPerimeter.bottom;\n\n footer.setAutoCommitStyle(false);\n footer.setX(containerInsets.getLeft());\n footer.setY(containerInsets.getTop() + containerSize.height - footerBandHeight);\n footer.setWidth(containerSize.width);\n footer.setHeight(footerBandHeight);\n footer.setAutoCommitStyle(true);\n\n // The footer's inner row is the footer's own child, so its cells\n // are sized from the footer's content box, not the band.\n const footerBox = footer.getContentBounds()\n ?? { x: 0, y: 0, width: containerSize.width, height: columnHeight };\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(footerBox.height);\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 the container's per-type width\n * policy and column constraints.\n *\n * Fixed-shape columns (boolean, glyph, date, time, datetime, number) get a\n * type-derived width clamped to any declared min/max. Flexible columns\n * (string, auto) with no definite width share the remaining space equally,\n * each clamped to its own min/max; a `string`/`auto` column with a definite\n * width — either declared or, under `autoSizeColumns`, sampled from content —\n * uses that width like a fixed-shape column instead of sharing space.\n *\n * @param container - The Table component whose columns are being sized.\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(container: TableComponent, columns: Column[], availableWidth: number): number[] {\n const intrinsic = container.getIntrinsicColumnWidths();\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 const widths = intrinsic.map((w, i) => {\n if (w !== null) {\n return w;\n }\n\n const col = columns[i];\n const min = container.getColumnMinWidth(col);\n const max = col.getMaxWidth() ?? UNBOUNDED;\n\n return Util.clamp(rawFlex, min, max);\n });\n\n return this.absorbSlackIntoGreedy(columns, widths, availableWidth);\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 container - The Table component whose columns are being sized.\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(container: TableComponent, 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 // On a table with more columns than fit — the generated-table case this\n // sizing exists for — the fixed-shape columns alone can already exceed\n // the viewport, leaving `newFlexTotal` negative. There is no space for\n // the flex columns to share, and rescaling by a negative ratio would\n // collapse every one of them to its floor on the first re-layout, with\n // no resize involved. Keep the derived widths and let the table scroll\n // horizontally, which is what a column too wide for the viewport means.\n if (prevFlexTotal <= 0 || newFlexTotal <= 0 || Math.abs(prevFlexTotal - newFlexTotal) <= 0.5) {\n return columnWidths;\n }\n\n const ratio = newFlexTotal / prevFlexTotal;\n\n const rescaled = columnWidths.map((w, i) => {\n if (isFixed[i]) {\n return w;\n }\n\n return this.clamp(w * ratio, columns[i], container);\n });\n\n return this.absorbSlackIntoGreedy(columns, rescaled, availableWidth);\n }\n\n /**\n * Adds any positive leftover width — space freed when a flexible column\n * clamped to its `maxWidth` — to the flexible columns that declare no\n * `maxWidth`, so an unbounded \"filler\" column grows to fill the table\n * instead of leaving dead space at the right edge. A no-op when the columns\n * already fill the width (the common case: no flexible column is capped) or\n * overflow it.\n *\n * @param columns - The visible resolved columns.\n * @param widths - The per-column widths computed so far.\n * @param availableWidth - Total available pixel width for columns.\n * @returns The widths with any positive slack handed to unbounded flexible columns.\n */\n private absorbSlackIntoGreedy(columns: Column[], widths: number[], availableWidth: number): number[] {\n const slack = availableWidth - widths.reduce((s, w) => s + w, 0);\n\n if (slack <= 0.5) {\n return widths;\n }\n\n const greedy: number[] = [];\n\n columns.forEach((col, i) => {\n const t = col.getField().getType();\n const isFlex = t !== 'boolean' && t !== 'number' && t !== 'date';\n\n if (isFlex && col.getMaxWidth() === undefined) {\n greedy.push(i);\n }\n });\n\n if (greedy.length === 0) {\n return widths;\n }\n\n const share = slack / greedy.length;\n const result = [...widths];\n\n for (const i of greedy) {\n result[i] += share;\n }\n\n return result;\n }\n\n /**\n * Clamps a width value to the `[minWidth, maxWidth]` range declared on a column,\n * using the container's type-derived floor as the default minimum and no upper\n * bound when unconstrained.\n *\n * @param width - The raw width to clamp.\n * @param column - The column whose constraints apply.\n * @param container - The Table component whose columns are being sized.\n * @returns The clamped width.\n */\n private clamp(width: number, column: Column, container: TableComponent): number {\n const min = container.getColumnMinWidth(column);\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\n /**\n * Mirrors {@link CellRenderer.getDisplayText} 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 `\"\"`; editors do not currently report display text of\n * their own.\n *\n * @returns Always `\"\"`.\n */\n getDisplayText(): string {\n return \"\";\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 * The body is the only writer: its read-only resolution forwards the\n * OR of the static column flag, the spec-level row predicate, and the\n * per-cell predicate, and re-runs whenever a row rebinds or its column\n * window changes — so a read-only column scrolling into view arrives\n * read-only without a rebind. Application code should declare read-only\n * through 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, or\n * `null` to restore the theme default\n * (`var(--ts-ui-table-cell-bg, transparent)`).\n * @returns This cell, for method chaining.\n */\n setBaseBackground(color: string | null): this {\n this._baseBackground = color ?? 'var(--ts-ui-table-cell-bg, transparent)';\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()`), inside this cell's own content box.\n * For a plain renderer the offset is `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 from this cell's content box origin and shrinks its\n * width accordingly. Idempotent — when no offset is reserved this\n * is a single comparison and an early 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 // The Card layout already placed the editor inside this cell's content\n // box, so its y and height are right; only the left edge and the width\n // move here. The outer-width fallback preserves the previous behaviour\n // before the element exists.\n const box = this.getContentBounds()\n ?? { x: 0, y: 0, width: this.getWidth() ?? 0, height: 0 };\n\n const editorWidth = Math.max(0, box.width - contentX);\n\n editor.setAutoCommitStyle(false);\n editor.setX(box.x + 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 const changed = this._renderer !== renderer;\n\n this._renderer = renderer;\n this.getLayoutManager().setVisibleComponentId(renderer.getId());\n\n // Re-fit the newly visible renderer to the cell, mirroring startEdit /\n // stopEdit's Card swaps. Body.bindAndPositionRows skips a rebound\n // cell's own doLayout when its column geometry is unchanged (a pure\n // scroll) — safe for a fixed-type column, but a DynamicCell can swap\n // its active renderer on that same rebind. Without this the swapped-in\n // variant keeps its stale (zero) size and vanishes: the rotated value\n // column's number/date/boolean rows blank out on scroll. Guarded on an\n // actual variant change so stable-type rebinds keep that skip. (On the\n // cell's very first bind the cell is not yet sized, so this lays out to\n // zero — harmless, as Body's first geometry pass re-runs doLayout.)\n if (changed) {\n this.doLayout();\n }\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 * @remarks\n * An implementation that adds, removes or replaces a child component must\n * end by calling `this.doLayout()`, because the new child has no bounds\n * until something lays this renderer out and nothing else will: the table\n * skips the layout for a cell whose geometry has not moved, and rebinding a\n * row to new data does not move it. `GlyphRenderer` is the framework's own\n * example. An implementation that only writes text needs no layout — the\n * text does not auto-measure — which is the common case.\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 * Returns the exact text this cell shows the user — the string the\n * cell's own `Text` child was last given, computed from state cached in\n * {@link setValue} and never read from the DOM. Safe to call on a\n * renderer that was constructed, fed a value, and never rendered.\n *\n * Defaults to `\"\"`. Every built-in renderer overrides this to return\n * its actual displayed text; the default is what a consumer-authored\n * `CellRenderer` subclass answers until it opts in, and the honest\n * answer for a renderer that draws no text at all (e.g. a chart or\n * badge renderer).\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return \"\";\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 private _display: string = \"\";\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 // The renderer is the element the pointer hits (the Text stays\n // pointer-events: none), so it must opt in for a selection to be\n // able to start here. The Text needs its own opt-in too — its\n // element carries the framework's user-select: none in its own\n // right, and a descendant's declaration is not inherited from an\n // ancestor's.\n this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n // Mirrors the user-select opt-in above: the renderer is the element the\n // pointer hits, so its own cursor is what the browser shows on hover.\n this.setCursor(\"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._display = this._value === null ? \"\" : String(this._value);\n this._text.setText(this._display);\n\n return this;\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Text}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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 via a {@link Text}, right-aligned by default. Caches\n * the last value passed to {@link setValue} so {@link getValue} returns the\n * exact `Number | null` that was rendered — never the result of re-parsing\n * the 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 private _display: string = \"\";\n\n /**\n * @param align - The text alignment to render with. Defaults to\n * `\"right\"`, the convention for a homogeneous numeric column;\n * {@link DynamicCell} passes `\"left\"` instead, since it renders a\n * number row alongside left-aligned rows of other types in the same\n * column.\n */\n constructor(align: \"left\" | \"right\" = \"right\") {\n super();\n\n this._text.setPointerEvents(\"none\");\n this._text.setTextAlign(align);\n this._text.setText(\"\");\n this._text.setAutoMeasure(false);\n\n this.addComponent(this._text);\n\n this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n this.setCursor(\"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._display = this._value === null ? \"\" : String(this._value);\n this._text.setText(this._display);\n\n return this;\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Text}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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\";\nimport { temporalDisplayText } from \"~/data/temporalText.js\";\n\n/**\n * A read-only renderer for date cell values.\n *\n * Displays the value via a {@link Text} formatted by the shared temporal\n * display-text formatter, so its text stays in step with what a `contains` /\n * `startsWith` / `endsWith` filter over this column matches against.\n */\nclass DateRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n private _display: string = \"\";\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 this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n this.setCursor(\"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._display = value ? temporalDisplayText('date', false, value) : \"\";\n this._text.setText(this._display);\n\n return this;\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Text}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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\";\nimport { temporalDisplayText } from \"~/data/temporalText.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 by the\n * shared temporal display-text formatter, so its text stays in step with\n * what a `contains` / `startsWith` / `endsWith` filter over this column\n * matches against.\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 private _display: string = \"\";\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 this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n this.setCursor(\"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._display = value ? temporalDisplayText('time', this._showSeconds, value) : \"\";\n this._text.setText(this._display);\n\n return this;\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Text}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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\";\nimport { temporalDisplayText } from \"~/data/temporalText.js\";\n\n/**\n * A read-only renderer for date-time cell values.\n *\n * Displays a `Date` via a {@link Text} formatted by the shared temporal\n * display-text formatter, so its text stays in step with what a `contains` /\n * `startsWith` / `endsWith` filter over this column matches against.\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 private _display: string = \"\";\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 this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n this.setCursor(\"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._display = value ? temporalDisplayText('datetime', this._showSeconds, value) : \"\";\n this._text.setText(this._display);\n\n return this;\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Text}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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 // Unlike a text renderer, this one answers a value change by replacing\n // a child component, and the replacement has no bounds until something\n // lays this renderer out. Nothing else will: the owning cell keeps its\n // geometry across a rebind, so the table's cell-geometry diff skips it.\n // Mirrors `Cell.setActiveRenderer`, which lays itself out whenever it\n // swaps the child the layout is fitted around.\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns the registry glyph name currently displayed, or `\"\"` when the\n * cell is empty. Unlike the text renderers there is no `Text` child to\n * mirror — the registry name is the cell's only textual handle, and it\n * is what CSV export writes today.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._value === null ? \"\" : String(this._value);\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 /**\n * Explicit starting width in pixels. Takes precedence over the type\n * policy and any sampled content, but is still clamped into the\n * column's `[minWidth, maxWidth]` envelope — an explicit `width`\n * narrower than the type's floor is raised to that floor.\n */\n width ?: number;\n /**\n * Longest value this column can hold, in characters (e.g. a\n * `varchar(60)` column passes `60`). For a `string`/`auto` column\n * under {@link ColumnSpec.autoSizeColumns} this is used only when\n * sampling the store yields no candidates; for a `number` column it\n * outranks the sample, since a declared digit budget is a better\n * answer than what fifty rows happen to hold.\n */\n maxContentLength ?: 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 * When `false`, this column gets no filter input in the header's filter\n * row. Resolution: `ColumnConfig.filterable` wins when set; otherwise\n * falls back to {@link ColumnSpec.filterable}; otherwise `true`.\n * The row itself only ever appears when {@link Table.setFilterRowVisible}\n * (or the header's context-menu **Filter** entry) is also on.\n */\n filterable ?: 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 * Overrides the header label for this column. Defaults to the field name.\n * Set to `''` to render a blank header (used by the rotated view's filler\n * column).\n */\n headerText ?: 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 * When `true`, `string` and `auto` columns are sized from a bounded\n * sample of the values the column holds, instead of staying flex\n * columns that share the leftover space. `boolean`, `glyph`, `date`,\n * `time`, `datetime`, and `number` columns are always sized from\n * their type, whether or not this flag is set. Defaults to `false`.\n */\n autoSizeColumns ?: boolean;\n /**\n * Table-wide default for {@link ColumnConfig.filterable}. A column's own\n * `filterable` still wins when set. Defaults to `true`.\n */\n filterable ?: 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 private _display: string = \"\";\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 this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n this.setCursor(\"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._display = \"\";\n } else {\n const key = String(this._value);\n\n this._display = this._map.get(key) ?? key;\n }\n\n this._text.setText(this._display);\n\n return this;\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Text}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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 { 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, ComboOption } from \"~/component/table/ColumnConfig.js\";\n\n/**\n * Builds the renderer that draws a cell of this variant. The shared factory\n * behind {@link DynamicCell.buildRenderer} and {@link CellTextResolver} — one\n * authority for \"which renderer draws this variant\".\n *\n * @param type - The built-in variant to construct a renderer for.\n * @param showSeconds - Forwarded to `time`/`datetime` renderers.\n * @param numberAlign - Text alignment for a `number` renderer. Defaults to\n * `\"right\"`; {@link DynamicCell} passes `\"left\"` explicitly, since its\n * number row sits alongside left-aligned rows of other types in the same\n * column.\n * @returns A freshly constructed renderer for `type`. `glyph` and any\n * unrecognised variant fall back to a {@link StringRenderer}.\n *\n * @internal\n */\nexport function buildCellRenderer(\n type: CellType,\n showSeconds: boolean,\n numberAlign: \"left\" | \"right\" = \"right\",\n): CellRenderer<any> {\n switch (type) {\n case 'number':\n return new NumberRenderer(numberAlign);\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 * Owner-held pool of unmounted renderers used to format values off-screen —\n * the small pool {@link Table} and {@link TableHeader} format non-cell values\n * (export, filter-build, quick search) through. Mirrors\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool): a keyed\n * cache of at most one renderer per variant, lazily built, disposed by its\n * owner.\n *\n * @category Components\n * @internal\n */\nexport class CellTextResolver {\n\n private _renderers : Map<string, CellRenderer<any>> = new Map();\n private _comboOptions : Array<ComboOption | string> | null = null;\n\n /**\n * Formats `value` the way the matching cell renderer would display it.\n *\n * @param type - The variant to format against.\n * @param showSeconds - Forwarded to a `time`/`datetime` variant's renderer.\n * @param values - The combo option list; consulted (and cached) only when\n * `type` is `'combo'`.\n * @param value - The raw value to format.\n * @returns The display text {@link CellRenderer.getDisplayText} would\n * report for this value.\n */\n text(\n type: CellType,\n showSeconds: boolean,\n values: Array<ComboOption | string> | undefined,\n value: any,\n ): string {\n const key = showSeconds ? `${type}:seconds` : type;\n let renderer = this._renderers.get(key);\n\n if (!renderer) {\n renderer = buildCellRenderer(type, showSeconds);\n // Never parented, never rendered: keep its Text writes from\n // enqueuing a layout pass for a component nothing will lay out.\n renderer.pauseLayout();\n this._renderers.set(key, renderer);\n }\n\n if (type === 'combo' && values && values !== this._comboOptions) {\n (renderer as ComboRenderer).setOptions(values);\n this._comboOptions = values;\n }\n\n renderer.setValue(value);\n\n return renderer.getDisplayText();\n }\n\n /**\n * Disposes every renderer this resolver has lazily constructed.\n */\n dispose(): void {\n for (const renderer of this._renderers.values()) {\n renderer.dispose();\n }\n\n this._renderers.clear();\n this._comboOptions = null;\n }\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 { ComboRenderer } from \"~/component/table/cell/renderer/Combo.js\";\nimport { buildCellRenderer } from \"~/component/table/cell/CellText.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 // Left-aligned: unlike a homogeneous number column, a DynamicCell\n // number row sits in a column that also shows left-aligned\n // string/date/combo rows, and a lone right-aligned row read as\n // jarring there.\n return buildCellRenderer(type, showSeconds, \"left\");\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 { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A non-interactive body row that labels a contiguous run of a rotated\n * table's field/value rows sharing the same {@link Column.getGroup} name.\n * Constructed once per visible group run by `Table` (private\n * `rebuildRotatedStore` / `computeGroupRuns`) and mounted by `Row.renderSeparator`\n * in place of the row's usual three field/value/filler cells; the whole row\n * is sized to `rowWidth` by `Body.bindAndPositionRows`, so this cell spans it\n * directly rather than via the `spanFrom`/`spanTo` column-summing\n * {@link ParentHeaderCell} uses.\n *\n * @category Components\n */\nclass GroupSeparatorCell extends DefaultCell {\n\n private _color: string | null;\n\n /**\n * Constructs a separator cell for one group run.\n *\n * @param text - The group label to display.\n * @param color - Optional CSS color string for the cell's background;\n * `null` renders a transparent background with just the top divider.\n */\n constructor(text: string, color: string | null) {\n super(\"td\");\n\n this._color = color;\n\n const renderer = this.getRenderer();\n renderer.getText().setFontWeight(\"bold\");\n renderer.getText().setText(text);\n renderer.setUserSelect(\"none\");\n renderer.getText().setUserSelect(\"none\");\n renderer.setCursor(\"default\");\n\n this.setBackgroundColor(color ?? \"transparent\");\n\n // A body row has no inherited header-band surface (unlike\n // ParentHeaderCell, which relies on the Header's own gradient), so\n // an uncolored separator still needs its own boundary — a single\n // top divider in the same token the header's own dividers use.\n this.setShadow(\"inset 0 1px 0 0 var(--ts-ui-table-header-border, rgba(0, 0, 0, 0.2))\");\n }\n\n /**\n * Returns the optional background color this separator adopts.\n *\n * @returns The CSS color string, or `null` when the separator shows\n * only its top divider.\n */\n getColor(): string | null {\n return this._color;\n }\n}\n\nconst GroupSeparatorCellCallable = callable(GroupSeparatorCell);\ntype GroupSeparatorCellCallable = GroupSeparatorCell;\nexport {\n GroupSeparatorCell as _GroupSeparatorCell,\n GroupSeparatorCellCallable as GroupSeparatorCell\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 * Returns the delegate's display text — the toggle and indent columns\n * carry no text of their own.\n *\n * @returns The delegate's current display text.\n */\n getDisplayText(): string {\n return this._delegate.getDisplayText();\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` from the\n * content box's origin and places the delegate immediately to its right,\n * filling the remaining content 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 // No early return on a null box: `super.doLayout()` is the last\n // statement here, and skipping it would leave the children's bounds\n // uncommitted. The zero fallback preserves the previous\n // `getWidth() || 0` behaviour before the element exists.\n const box = this.getContentBounds() ?? { x: 0, y: 0, width: 0, height: 0 };\n const width = box.width;\n const height = box.height;\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(box.x + indent);\n this._toggle.setY(box.y + 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(box.x + delegateX);\n this._delegate.setY(box.y);\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 { GroupSeparatorCell } from \"~/component/table/cell/GroupSeparator.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, DEFAULT_INDENT_PX } from \"~/component/table/cell/renderer/TreeCell.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Insets } from \"~/primitive/Insets.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 column in the body's current column window and binds each\n * cell's commit callback to the 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 _treeCell: Cell<any> | null = null;\n private _stripe: boolean = false;\n\n // All non-hidden fields, in display order — the full column list a\n // window is carved out of. Populated by `setColumnFields`.\n private _visibleFields: Field[] = [];\n private _columnConfigs: Map<string, ColumnConfig> = new Map();\n private _treeFieldName: string | undefined;\n // Visible-column index of slot 0. The rendered columns are always a\n // contiguous run, so slot `s` holds column `_windowFirst + s`.\n private _windowFirst: number = 0;\n // Cell key per rendered slot, index-aligned with `getComponents()`.\n private _cellKeys: string[] = [];\n // Field name per rendered slot, index-aligned with `getComponents()`.\n private _fieldNames: string[] = [];\n // Set by `setColumnFields` so the next `setColumnWindow` reconciles\n // even when the requested range happens to match the current one —\n // a column-set change (hide/show, config swap) can leave the range\n // unchanged while the cells behind it need to change.\n private _columnsDirty: boolean = false;\n // Set by `renderSeparator` and cleared by `setColumnWindow`'s guard —\n // this row currently shows a single group-separator cell instead of the\n // usual per-column cells (rotated-mode group runs only). Not for\n // consumer use.\n private _separatorMode: 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 this.setColumnFields(this._model, hiddenColumns, columnConfigs, treeFieldName);\n }\n }\n\n /**\n * Returns the cell on the row's tree column. The host `TreeBody` reads\n * this to find each row's `TreeCellRenderer` for depth / toggle updates\n * and toggle-click routing.\n *\n * Returns `null` when the row was constructed without a `treeFieldName`,\n * and also whenever the tree column sits outside the row's current column\n * window — a row renders only its horizontally-visible columns, so a wide\n * table scrolled right has no tree cell to return. Callers must handle\n * `null` on every access rather than caching the result.\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 visible-column index of the first rendered cell —\n * `getComponents()[s]` renders visible column `getColumnWindowStart() + s`.\n *\n * @returns The visible-column index slot `0` currently renders.\n */\n getColumnWindowStart(): number {\n return this._windowFirst;\n }\n\n /**\n * Returns one field name per *rendered* slot, index-aligned with\n * `getComponents()`. Hidden columns are excluded.\n *\n * A row renders only the columns in its current column window, so this\n * describes that window rather than every visible column: slot `s` names\n * visible column {@link getColumnWindowStart} + `s`. Code holding a column\n * index must subtract that offset before indexing here, and code reading a\n * slot must add it to recover the column index.\n *\n * @returns The field names, one per rendered slot.\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 whether this row currently renders a single group-separator\n * cell (see {@link renderSeparator}) instead of its usual per-column cells.\n *\n * @returns `true` while in separator mode.\n *\n * @remarks Not for consumer use — internal wiring consulted by `Body`\n * (`bindAndPositionRows`, `onRowClick`) so a separator row is skipped by\n * click selection and keyboard row navigation.\n */\n isSeparator(): boolean {\n return this._separatorMode;\n }\n\n /**\n * Indents this row's `field`-name cell by `DEFAULT_INDENT_PX` (from\n * `TreeCellRenderer` — the same per-level indent Tree uses) so a\n * rotated-mode group's member rows read as visually nested under their\n * {@link GroupSeparatorCell}, or restores it flush-left. A no-op when\n * this row has no `field` cell in its current window (outside rotated\n * mode, or a separator row).\n *\n * @param indented - `true` to indent, `false` to restore flush-left.\n *\n * @remarks Not for consumer use — called by `Body.bindAndPositionRows`\n * via the `Table`-supplied indent predicate.\n */\n setFieldIndent(indented: boolean): void {\n const slot = this._fieldNames.indexOf('field');\n\n if (slot === -1) {\n return;\n }\n\n const cell = this.getComponents()[slot] as Cell<any>;\n\n cell.setInsets(new Insets(0, 0, 0, indented ? DEFAULT_INDENT_PX : 0));\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 * Records the visible-field list, per-field configs and tree column\n * this row renders from. Builds no cells — {@link setColumnWindow}\n * owns cell construction, called separately once the host `Body`\n * knows which column range to render.\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 setColumnFields(\n model: AbstractModel,\n hiddenColumns: Set<string>,\n columnConfigs: Map<string, ColumnConfig>,\n treeFieldName?: string,\n ): this {\n this._model = model;\n\n this._visibleFields = model.getFields()\n .filter(f => !hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n\n this._columnConfigs = columnConfigs;\n this._treeFieldName = treeFieldName;\n this._columnsDirty = true;\n\n return this;\n }\n\n /**\n * Discards this row's current cells and mounts a single\n * {@link GroupSeparatorCell} spanning the whole row, labeling a\n * contiguous run of a rotated table's grouped field/value rows. The\n * row's own ARIA role switches to `'separator'` (from the default\n * `'row'`); {@link setColumnWindow}'s guard reverses both the moment\n * this pooled slot is asked to render a real field row again.\n *\n * @param label - The group label to display.\n * @param color - Optional CSS color string for the separator's\n * background; `null` shows only its top divider.\n *\n * @remarks Not for consumer use — called by `Body.bindAndPositionRows`\n * via the `Table`-supplied separator predicate.\n */\n renderSeparator(label: string, color: string | null): void {\n this.disposeAllComponents();\n this.addComponent(new GroupSeparatorCell(label, color));\n\n this._separatorMode = true;\n this._windowFirst = 0;\n this._fieldNames = [];\n this._cellKeys = [];\n this._treeCell = null;\n this._columnsDirty = true; // forces the next setColumnWindow to rebuild fully\n\n this.getAria().setRole(\"separator\");\n }\n\n /**\n * Reconciles the rendered cells to exactly the visible columns\n * `[firstCol, lastCol]`. A column keeps its existing cell when that\n * cell already presents the same field; otherwise it recycles a cell\n * that just left the window and shares the same cell key, or\n * builds a fresh one. Cells that end up unclaimed are committed (if\n * editing), removed, and disposed.\n *\n * @param firstCol - The first visible-column index to render, inclusive.\n * @param lastCol - The last visible-column index to render, inclusive.\n *\n * @returns `true` when the rendered cell set changed.\n */\n setColumnWindow(firstCol: number, lastCol: number): boolean {\n if (this._separatorMode) {\n this.disposeAllComponents();\n\n this._separatorMode = false;\n this._columnsDirty = true;\n this.getAria().setRole(\"row\");\n }\n\n // Clamp into this row's own visible-field range. A pooled row can be\n // asked to window against a range sized for a store the host `Body`\n // has already swapped to (`Table.bindView` calls `Body.setStore`,\n // which renders synchronously, before `setColumns` / `setColumnConfigs`\n // resync this row's `_visibleFields` to the new model) — the request\n // is stale for one pass, not invalid, so it is clamped rather than\n // trusted verbatim. The immediately-following `setColumns` /\n // `setColumnConfigs` call marks the row dirty and re-renders against\n // the caught-up field list.\n const maxCol = this._visibleFields.length - 1;\n\n lastCol = Math.min(lastCol, maxCol);\n\n if (firstCol > lastCol) {\n firstCol = lastCol + 1;\n }\n\n const currentLastCol = this._windowFirst + this.getComponents().length - 1;\n\n if (!this._columnsDirty && firstCol === this._windowFirst && lastCol === currentLastCol) {\n return false;\n }\n\n const cells = this.getComponents() as Cell<any>[];\n const byName = new Map<string, { cell: Cell<any>, key: string }>();\n\n for (let s = 0; s < cells.length; s++) {\n const fieldName = this._fieldNames[s];\n\n if (fieldName !== undefined) {\n byName.set(fieldName, { cell: cells[s], key: this._cellKeys[s] });\n }\n }\n\n const slotCount = lastCol - firstCol + 1;\n const assigned: (Cell<any> | undefined)[] = new Array(slotCount).fill(undefined);\n const retargeted = new Set<number>();\n\n // Pass 1 — keep a cell for its own field, if its key still matches.\n for (let col = firstCol; col <= lastCol; col++) {\n const field = this._visibleFields[col];\n const key = this.cellKeyFor(field);\n const entry = byName.get(field.getName());\n\n if (entry && entry.key === key) {\n assigned[col - firstCol] = entry.cell;\n byName.delete(field.getName());\n }\n }\n\n // Remaining leftovers, grouped by key, so pass 2 can recycle one.\n const free = new Map<string, Cell<any>[]>();\n\n for (const entry of byName.values()) {\n const pool = free.get(entry.key);\n\n if (pool) {\n pool.push(entry.cell);\n } else {\n free.set(entry.key, [entry.cell]);\n }\n }\n\n // Pass 2 — recycle a leftover with the same key, else build.\n for (let col = firstCol; col <= lastCol; col++) {\n const slot = col - firstCol;\n\n if (assigned[slot] !== undefined) {\n continue;\n }\n\n const field = this._visibleFields[col];\n const key = this.cellKeyFor(field);\n const pool = free.get(key);\n let cell: Cell<any>;\n\n if (pool && pool.length > 0) {\n cell = pool.pop()!;\n\n this.setLayoutConstraints(cell, { data: field });\n } else {\n cell = Row.createCellForField(field, this._columnConfigs);\n\n if (this._treeFieldName !== undefined && field.getName() === this._treeFieldName) {\n cell.wrapRenderer((delegate: CellRenderer<any>) => new TreeCellRenderer(delegate));\n }\n\n const builtCell = cell;\n cell.on(\"commit\", (newValue) => this.commitCellValue(builtCell, newValue));\n\n this.addComponent(cell, { data: field });\n }\n\n assigned[slot] = cell;\n retargeted.add(col);\n }\n\n // Pass 3 — per-column state that a shift can invalidate even for a\n // survivor: ARIA column index, group tint, and (for a retargeted\n // cell only) the bound value.\n for (let col = firstCol; col <= lastCol; col++) {\n const cell = assigned[col - firstCol]!;\n const field = this._visibleFields[col];\n\n cell.getAria().setColIndex(col + 1);\n cell.setBaseBackground(this._columnConfigs.get(field.getName())?.groupColor ?? null);\n\n if (retargeted.has(col)) {\n this.bindCell(cell, this._data, field.getName());\n }\n }\n\n // Discard whatever is still free. Commit an in-flight edit first so\n // user keystrokes land on the record (mirrors the blur-commits-edit\n // contract), then dispose so the cell's per-instance stylesheet rule\n // doesn't survive the discard.\n for (const pool of free.values()) {\n for (const cell of pool) {\n if (cell.isEditing()) {\n cell.commitEdit();\n }\n\n this.removeComponent(cell);\n cell.dispose();\n }\n }\n\n // Re-order children to the new visible-column order.\n //\n // The order comes from `assigned`, which this reconciler just built in\n // slot order — not from re-sorting on `Field.getOrder()`. A model that\n // declares no `order` returns the -1 sentinel for every field, so an\n // order-based comparison ties throughout, the sort is a stable no-op,\n // and a recycled cell keeps whatever index it already held. That\n // desynchronises `getComponents()` from `_fieldNames`, which is built\n // in column order below and documented to stay index-aligned with it.\n const slotOf = new Map(assigned.map((cell, slot) => [cell, slot]));\n\n this.sortComponents((c1, c2) =>\n (slotOf.get(c1 as Cell<any>) ?? 0) - (slotOf.get(c2 as Cell<any>) ?? 0));\n\n this._windowFirst = firstCol;\n this._fieldNames = [];\n this._cellKeys = [];\n this._treeCell = null;\n\n for (let col = firstCol; col <= lastCol; col++) {\n const field = this._visibleFields[col];\n const cell = assigned[col - firstCol]!;\n\n this._fieldNames.push(field.getName());\n this._cellKeys.push(this.cellKeyFor(field));\n\n if (this._treeFieldName !== undefined && field.getName() === this._treeFieldName) {\n this._treeCell = cell;\n }\n }\n\n this._columnsDirty = false;\n\n return true;\n }\n\n /**\n * Commits a cell's newly-entered value onto the row's bound record.\n * Wired once per cell, at construction, so a recycled cell resolves\n * its *current* field from the row's layout constraints at emit time\n * rather than closing over the field it was originally built for.\n *\n * @param cell - The cell that emitted `\"commit\"`.\n * @param value - The committed value.\n */\n private commitCellValue(cell: Cell<any>, value: unknown): void {\n const field = this.getLayoutConstraints(cell)?.data as Field | undefined;\n\n if (!field) {\n return;\n }\n\n if (this._data) {\n this._data.set(field.getName(), value);\n this._onCellCommit?.(this._data);\n }\n\n this.updateVisualState();\n }\n\n /**\n * Computes the cell-reuse key for `field`, per the precedence table in\n * the column-virtualization plan's Architecture Decisions: the tree\n * column and any column with a custom renderer, `cellType`, or `values`\n * config get a field-namespaced key (never shared); a `time`/`datetime`\n * column shares a key with every other column of the same type and\n * `showSeconds`; every other column shares a key with every other\n * column of the same field type.\n *\n * @param field - The field whose column key to compute.\n * @param config - The field's column config, if any.\n * @param isTreeColumn - Whether this field is the row's tree column.\n *\n * @returns The reuse key. Two columns with the same key may share a cell.\n */\n private static cellKey(field: Field, config: ColumnConfig | undefined, isTreeColumn: boolean): string {\n if (isTreeColumn) {\n return `tree:${field.getName()}`;\n }\n\n if (config?.renderer) {\n return `renderer:${field.getName()}`;\n }\n\n if (config?.cellType) {\n return `dynamic:${field.getName()}`;\n }\n\n if (config?.values && config.values.length > 0) {\n return `combo:${field.getName()}`;\n }\n\n const type = field.getType();\n\n if (type === 'time' || type === 'datetime') {\n return `${type}:${config?.showSeconds ?? false}`;\n }\n\n return type;\n }\n\n /**\n * Resolves `field`'s config and tree-column status from this row's\n * current state and forwards to `cellKey`.\n */\n private cellKeyFor(field: Field): string {\n const isTreeColumn = this._treeFieldName !== undefined && field.getName() === this._treeFieldName;\n\n return Row.cellKey(field, this._columnConfigs.get(field.getName()), isTreeColumn);\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 * @param e - The mousedown event. Only ever a primary-button press — the\n * default `button: \"primary\"` registration filters the rest.\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\nconst _defaultSortPriorityBadgeOptions: Partial<SortPriorityBadgeOptions> = {\n backgroundColor: \"var(--ts-ui-sort-badge-bg, rgba(0,0,0,0.15))\",\n foregroundColor: \"var(--ts-ui-sort-badge-color, inherit)\",\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 * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(options?: SortPriorityBadgeOptions, subclassDefaults?: Partial<SortPriorityBadgeOptions>) {\n ensureSortBadgeClassRule();\n\n super({ tag: \"span\", ...(options ?? {}) }, { ..._defaultSortPriorityBadgeOptions, ...(subclassDefaults ?? {}) });\n\n this._priority ??= null;\n\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 // A column title is chrome, not data, so it stays unselectable even\n // though StringRenderer (which DefaultCell built this cell's\n // renderer from) now opts in.\n renderer.setUserSelect(\"none\");\n renderer.getText().setUserSelect(\"none\");\n // StringRenderer's constructor now sets cursor: \"text\" unconditionally\n // (it does not know it is being reused as a header renderer here), so\n // this restates the framework default the same way the user-select\n // opt-out above does.\n renderer.setCursor(\"default\");\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', { prevent: true, handler: this.onContextMenu });\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 * Re-targets this cell at another column's model field. Used by the\n * header's column-window reconciler when recycling a cell whose\n * column left the window for one entering it.\n *\n * @param name - The new field name this cell reports on sort and\n * context-menu events.\n * @returns This cell, for method chaining.\n */\n setFieldName(name: string): this {\n this._fieldName = name;\n\n return this;\n }\n\n /**\n * Returns the model field name this cell currently reports on sort\n * and context-menu events.\n *\n * @returns The current field name.\n */\n getFieldName(): string {\n return this._fieldName;\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 // Mounting or clearing the glyph rewrites the renderer's left inset,\n // and an inset only reaches the label through a layout pass — nothing\n // in `setInsets` schedules one. The cell lays itself out here rather\n // than leaving that to its caller, mirroring `Cell.setActiveRenderer`,\n // which re-fits itself for the same reason. The header's own scroll\n // path skips a cell whose geometry has not moved, so a caller-owned\n // relayout would leave the label sitting under the glyph on every\n // route that does not also resize the cell. (Called before the cell has\n // been sized — the reconciler mounts the glyph before the geometry pass\n // — this fits the renderer against an unset size, which the header's\n // geometry pass then overwrites.)\n this.doLayout();\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 // `dispose()` removes the element *and* deletes the glyph's\n // per-instance rules. Unmounting alone stranded them on the shared\n // sheet, so a header whose glyph tracks sort state leaked one set\n // per change.\n this._headerGlyphInstance.dispose();\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 * Replaces the base header label. The required marker and sort arrow are\n * re-composed onto the new label automatically, so neither is lost.\n * Re-applied on every header reconcile so a column config that overrides\n * the label — or clears the override — takes effect on a surviving cell,\n * mirroring {@link setRequired}.\n *\n * @param text - The new base label.\n * @returns This header cell, for method chaining.\n */\n setHeaderText(text: String): this {\n if (this._text === text) {\n return this;\n }\n\n this._text = text;\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 /**\n * Forwards a right-click on the header cell (or any descendant) to the\n * registered `\"contextmenu\"` callback with the viewport coordinates.\n * `preventDefault` is applied by the registration's `prevent: true` floor.\n *\n * @param e - The contextmenu event.\n */\n private onContextMenu(e: MouseEvent): void {\n this.emit(\"contextmenu\", this._fieldName, e.clientX, e.clientY);\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 /**\n * Destroys the side-loaded overlay children before the inherited teardown\n * runs.\n *\n * The resize handle, the sort-priority badge and the header glyph are held\n * in private fields and mounted with a raw `appendChild` rather than\n * `addComponent` — deliberately, since registering them would let this\n * cell's `Card` layout treat them as non-visible siblings of the renderer\n * and hide them. The cost is that they never enter `_components`, so the\n * base destructor's recursion cannot reach them and their per-instance\n * rules would outlive the cell. The residue scales with column count, and\n * a larger stylesheet makes every later style recalculation dearer.\n */\n protected destructor(): void {\n // Loose checks: `_resizeHandle` and `_priorityBadge` are `declare`\n // fields, so they read as `undefined` if teardown lands before this\n // class's constructor body has run.\n this._resizeHandle?.dispose();\n this._priorityBadge?.dispose();\n this._headerGlyphInstance?.dispose();\n\n super.destructor();\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 renderer.setUserSelect(\"none\");\n renderer.getText().setUserSelect(\"none\");\n renderer.setCursor(\"default\");\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\", { prevent: true, handler: 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 * `preventDefault` is applied by the registration's `prevent: true` floor.\n */\n private onContextMenu(e: MouseEvent): void {\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 { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { TextField } from \"~/component/input/TextField.js\";\nimport { MenuButton } from \"~/component/button/MenuButton.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * The filter row's operator-picker button. A column carrying 2+ clauses\n * substitutes the clauses popover for the default operator menu on click —\n * see {@link FilterCellRenderer.setMenuOpenPredicate}, which forwards to\n * {@link setMenuOpenPredicate} here. Not exported: `FilterCell` reaches this\n * behaviour only through `FilterCellRenderer`'s forwarding method, so the\n * predicate mechanism stays this file's own implementation detail rather\n * than a new public surface on {@link MenuButton}.\n */\nclass FilterOperatorButton extends MenuButton {\n\n private _menuOpenPredicate: (() => boolean) | null = null;\n\n /**\n * Sets the predicate consulted by {@link shouldToggleMenu}, re-evaluated\n * on every click. `null` (the default) always allows the menu to open.\n *\n * @param predicate - Returns `true` to allow the default operator menu to\n * open for the next click, `false` to veto it.\n */\n setMenuOpenPredicate(predicate: (() => boolean) | null): this {\n this._menuOpenPredicate = predicate;\n\n return this;\n }\n\n protected shouldToggleMenu(): boolean {\n return this._menuOpenPredicate ? this._menuOpenPredicate() : true;\n }\n}\n\n/**\n * Hosts a filter column's two interactive controls — a text input and an\n * operator-picker button — side by side in an `HBox`.\n *\n * `itemAlign: \"stretch\"` sizes both children to the row's height. The\n * operator button carries no explicit `preferredSize` — pinning one would\n * disable Button's own content-derived sizing (see `Button.getPreferredSize`),\n * which is what keeps the button no wider than its glyph needs. `flat` +\n * `compact` additionally give it the tightest glyph-only inset perimeter.\n * `showText: false` keeps the button glyph-only on its face while its title —\n * set by {@link FilterCell} to the current operator's label — still drives\n * the hover tooltip and accessible name (`Button.setText`'s documented\n * `showText:false` behaviour).\n *\n * @category Components\n */\nclass FilterCellRenderer extends CellRenderer<string | null> {\n\n private _input: TextField = new TextField();\n private _operatorButton: FilterOperatorButton = new FilterOperatorButton();\n\n constructor() {\n super();\n\n this.setLayoutManager(new HBox({ spacing: 2, itemAlign: \"stretch\", mode: \"preferred\" }));\n\n this._input.setBorder({ border: \"0px solid transparent\" });\n // Transparent, not a second copy of the tint: `FilterCell` already\n // paints `--ts-ui-table-filter-row-bg` behind this renderer, and that\n // color is translucent. Painting it again here would stack two\n // translucent layers and read visibly stronger than the cell itself.\n // Letting the cell's single layer show through instead is exactly\n // how the operator button matches it too — `setFlat(true)` below\n // leaves its own resting background transparent.\n this._input.setBackgroundColor(\"transparent\");\n\n this._operatorButton.setFlat(true);\n this._operatorButton.setCompact(true);\n this._operatorButton.setShowText(false);\n // The description line composes into the button's hover tooltip\n // (Button._rebuildTooltip) without ever rendering on the glyph-only\n // face — FilterCell writes it once a column carries 2+ clauses, so\n // hovering the button states which conditions are active.\n this._operatorButton.setShowDescription(false);\n\n this.addComponent(this._input, { weight: 1 });\n this.addComponent(this._operatorButton);\n }\n\n /**\n * Returns the text input hosting the filter's typed value.\n *\n * @returns The {@link TextField} child.\n */\n getInput(): TextField {\n return this._input;\n }\n\n /**\n * Returns the operator-picker button.\n *\n * @returns The {@link MenuButton} child.\n */\n getOperatorButton(): MenuButton {\n return this._operatorButton;\n }\n\n /**\n * @internal Framework wiring between `FilterCell` and its operator\n * button; application code does not call this. Sets the predicate\n * consulted before the button's click opens its default dropdown —\n * `false` vetoes that default open, leaving the button's `\"action\"`\n * event as the only signal for that click, which `FilterCell` uses to\n * open the clauses popover instead once a column carries 2+ clauses.\n * Forwards to the operator button's own concrete type, which is not\n * otherwise exposed by `getOperatorButton`'s `MenuButton`-typed return.\n *\n * @param predicate - Returns `true` to allow the default menu to open for\n * the next click, `false` to veto it. `null` always allows it.\n */\n setMenuOpenPredicate(predicate: (() => boolean) | null): this {\n this._operatorButton.setMenuOpenPredicate(predicate);\n\n return this;\n }\n\n /**\n * Returns the text input's current value, or `null` when empty.\n *\n * @returns The current text, or `null`.\n */\n getValue(): string | null {\n const value = this._input.getValue();\n\n return value === \"\" ? null : value;\n }\n\n /**\n * Writes the text input's value. `null` renders as an empty input.\n *\n * @param value - The value to display, or `null` to clear the input.\n */\n setValue(value: string | null): void {\n this._input.setValue(value ?? \"\");\n }\n}\n\nconst FilterCellRendererCallable = callable(FilterCellRenderer);\ntype FilterCellRendererCallable = FilterCellRenderer;\nexport {\n FilterCellRenderer as _FilterCellRenderer,\n FilterCellRendererCallable as FilterCellRenderer\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 FilterClauseBadge}.\n *\n * @category Components\n */\ninterface FilterClauseBadgeOptions extends ComponentOptions {\n count?: number | null;\n}\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.FilterClauseBadge` 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) — mirrors {@link SortPriorityBadge}'s own class rule.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureFilterClauseBadgeClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"FilterClauseBadge\",\n styles: {\n position: \"absolute\",\n top: \"2px\",\n right: \"8px\",\n fontSize: \"var(--ts-ui-filter-clause-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\nconst _defaultFilterClauseBadgeOptions: Partial<FilterClauseBadgeOptions> = {\n backgroundColor: \"var(--ts-ui-filter-clause-badge-bg, rgba(0,0,0,0.15))\",\n foregroundColor: \"var(--ts-ui-filter-clause-badge-color, inherit)\",\n};\n\n/**\n * A small numeric badge anchored to the top-right corner of a table filter\n * cell, used to surface the count of AND-combined conditions held by that\n * column's {@link FilterCell} once there are 2 or more.\n *\n * Lives as a side-loaded overlay on the cell's `<th>` (its `position:absolute`\n * plus the host cell's [`Card`](/api/layout/classes/Card) layout keep it out\n * of the cell renderer's flow), mirroring\n * [`SortPriorityBadge`](/api/component/table/classes/SortPriorityBadge)'s own\n * placement. The badge stays hidden for a count of `null`, `0`, and `1` — a\n * column with zero or one condition renders exactly as it did before this\n * badge existed.\n *\n * A dedicated class rather than a second use of `SortPriorityBadge`: its CSS\n * custom properties are named `--ts-ui-filter-clause-badge-bg` /\n * `--ts-ui-filter-clause-badge-color`, an independent token pair from the\n * multi-sort priority badge's own, so re-theming one badge never silently\n * re-themes the other.\n *\n * @category Components\n */\nclass FilterClauseBadge extends Component<FilterClauseBadgeOptions> {\n\n declare private _count: number | null;\n\n /**\n * The accessible description last written via\n * {@link setAccessibleDescription}, or `null` when none is set.\n */\n private _description: string | null = null;\n\n /**\n * Constructs a filter-clause-count badge. The badge starts hidden until\n * {@link setCount} writes a count of 2 or greater.\n *\n * @param options - Optional configuration bag (initial count plus common\n * Component fields).\n * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(options?: FilterClauseBadgeOptions, subclassDefaults?: Partial<FilterClauseBadgeOptions>) {\n ensureFilterClauseBadgeClassRule();\n\n super({ tag: \"span\", ...(options ?? {}) }, { ..._defaultFilterClauseBadgeOptions, ...(subclassDefaults ?? {}) });\n\n this._count ??= null;\n\n this.setVisible(this._shouldShow(this._count));\n }\n\n /**\n * Applies a {@link FilterClauseBadgeOptions} bag. The count 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: FilterClauseBadgeOptions): this {\n super.applyOptions(options);\n\n if (options.count !== undefined) {\n this._count = options.count;\n }\n\n return this;\n }\n\n /**\n * Renders the root span and flushes the cached count into `textContent`\n * 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._countText() });\n\n return element;\n }\n\n /**\n * Returns the count last written via {@link setCount}, or `null`.\n *\n * @returns The current count, or null when none has been set.\n */\n getCount(): number | null {\n return this._count;\n }\n\n /**\n * Returns the accessible description last written via\n * {@link setAccessibleDescription}, or `null`.\n *\n * @returns The current description, or null when none has been set.\n */\n getAccessibleDescription(): string | null {\n return this._description;\n }\n\n /**\n * Sets (or clears) `aria-label`, so a column carrying several\n * AND-combined conditions states what those conditions actually are —\n * the numeric count alone reports *how many* apply, not *which*.\n *\n * @remarks\n * Deliberately **not** a mouse-hover {@link Tooltip}: the badge's class\n * rule sets `pointer-events: none` (so a click landing in its corner\n * still reaches the operator button underneath), which also makes the\n * badge unable to ever receive the `mouseover`/`mouseout` events a\n * `Tooltip` attachment depends on. The equivalent hover text lives on\n * `FilterCell`'s operator button instead (`Button.setDescription`),\n * which sits right beside the badge and — unlike the badge — already\n * accepts pointer events.\n *\n * @param text - The description text, or `null` to remove it.\n * @returns This component, for method chaining.\n */\n setAccessibleDescription(text: string | null): this {\n this._description = text;\n\n if (text) {\n this.getAria().setLabel(text);\n } else {\n this.getAria().clearLabel();\n }\n\n return this;\n }\n\n /**\n * Sets the clause count. Counts below 2 collapse to hidden because a\n * column with zero or one condition needs no numeric badge.\n *\n * @param value - The clause count, or `null` to clear.\n * @returns This component, for method chaining.\n */\n setCount(value: number | null): this {\n this._count = value;\n\n const element = this.getElement();\n if (element) {\n DOM.sink.apply(element, { text: this._countText() });\n }\n\n this.setVisible(this._shouldShow(value));\n\n return this;\n }\n\n /**\n * Returns the count rendered as a string, or the empty string when the\n * badge should not display text.\n *\n * @returns The text content for the current count value.\n */\n private _countText(): string {\n return this._shouldShow(this._count) ? String(this._count) : \"\";\n }\n\n /**\n * Returns `true` when the given count should make the badge visible.\n *\n * @param value - The count value to evaluate.\n * @returns True if a badge with this count should display.\n */\n private _shouldShow(value: number | null): boolean {\n return value != null && value >= 2;\n }\n}\n\nconst FilterClauseBadgeCallable = callable(FilterClauseBadge);\ntype FilterClauseBadgeCallable = FilterClauseBadge;\nexport {\n FilterClauseBadge as _FilterClauseBadge,\n FilterClauseBadgeCallable as FilterClauseBadge\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { FieldType } from \"~/data/Field.js\";\nimport type { FilterDescriptor } from \"~/data/FilterDescriptor.js\";\nimport { normalizeComboOptions } from \"~/component/table/ColumnConfig.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport type { CellTextResolver } from \"~/component/table/cell/CellText.js\";\n\n/**\n * The comparison a column's filter cell can offer. Which subset applies to a\n * given column is decided by {@link columnFilterOperators}, keyed on the\n * column's {@link FieldType}.\n *\n * @category Components\n */\nexport type ColumnFilterOperator =\n | 'contains' | 'startsWith' | 'endsWith'\n | 'eq' | 'neq'\n | 'gt' | 'gte' | 'lt' | 'lte'\n | 'isEmpty' | 'isNotEmpty';\n\n/**\n * One operator + raw text pair — a single condition within a column's filter\n * clause list. Ignored by `isEmpty` / `isNotEmpty`'s `text`, same as before\n * this type was split out of {@link ColumnFilterState}.\n *\n * @category Components\n */\nexport interface ColumnFilterClause {\n /** The comparison selected for this clause. */\n operator: ColumnFilterOperator;\n /** The raw text typed for this clause; ignored by `isEmpty` / `isNotEmpty`. */\n text: string;\n}\n\n/**\n * The list of clauses a filter cell currently holds, before it is turned\n * into a {@link FilterDescriptor} by {@link buildColumnFilter}. Always at\n * least one entry; two or more are combined with AND.\n *\n * @category Components\n */\nexport interface ColumnFilterState {\n /** Always at least one entry. Combined with AND when there is more than one. */\n clauses: ColumnFilterClause[];\n}\n\n/**\n * What a column contributes to its own filter build — the field-type switch\n * {@link buildColumnFilter} parses `state.text` against, plus the two extra\n * signals a combo or temporal column resolves display text with.\n *\n * @category Components\n */\nexport interface ColumnFilterTarget {\n /** The field's declared type. */\n type: FieldType;\n /** The column's declared combo options, when it renders as a combo cell. */\n values ?: Array<ComboOption | string>;\n /** Whether a `time`/`datetime` column's renderer includes seconds. */\n showSeconds ?: boolean;\n}\n\n/** Operators offered for a `string` / `auto` / `glyph` column, in menu order. */\nconst STRING_OPERATORS: ColumnFilterOperator[] =\n ['contains', 'startsWith', 'endsWith', 'eq', 'neq', 'isEmpty', 'isNotEmpty'];\n\n/** Operators offered for a `number` column, in menu order. */\nconst ORDERED_OPERATORS: ColumnFilterOperator[] =\n ['contains', 'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'isEmpty', 'isNotEmpty'];\n\n/** Operators offered for a `date` / `time` / `datetime` column, in menu order. */\nconst TEMPORAL_OPERATORS: ColumnFilterOperator[] =\n ['contains', 'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'startsWith', 'endsWith', 'isEmpty', 'isNotEmpty'];\n\n/** Operators offered for a `boolean` column, in menu order. */\nconst BOOLEAN_OPERATORS: ColumnFilterOperator[] = ['eq', 'neq', 'isEmpty', 'isNotEmpty'];\n\n/** Menu label shown for each operator. */\nconst OPERATOR_LABELS: Record<ColumnFilterOperator, string> = {\n contains: 'Contains',\n startsWith: 'Starts with',\n endsWith: 'Ends with',\n eq: 'Equals',\n neq: 'Not equals',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n isEmpty: 'Is empty',\n isNotEmpty: 'Is not empty',\n};\n\n/**\n * Registry glyph name shown on the operator button's face — see\n * `packages/lib/src/typescript/lib/glyphs/solid/` for each icon's source.\n */\nconst OPERATOR_GLYPHS: Record<ColumnFilterOperator, string> = {\n contains: 'magnifying-glass',\n startsWith: 'align-left',\n endsWith: 'align-right',\n eq: 'equals',\n neq: 'not-equal',\n gt: 'greater-than',\n gte: 'greater-than-equal',\n lt: 'less-than',\n lte: 'less-than-equal',\n isEmpty: 'ban',\n isNotEmpty: 'circle-check',\n};\n\n/**\n * Returns the operators offered for a field type, in menu order. The first\n * entry is that type's default operator.\n *\n * @param type - The column's field type.\n * @returns The operators to offer, in display order.\n */\nexport function columnFilterOperators(type: FieldType): ColumnFilterOperator[] {\n switch (type) {\n case 'string':\n case 'auto':\n case 'glyph':\n return STRING_OPERATORS;\n\n case 'boolean':\n return BOOLEAN_OPERATORS;\n\n case 'number':\n return ORDERED_OPERATORS;\n\n case 'date':\n case 'time':\n case 'datetime':\n return TEMPORAL_OPERATORS;\n }\n}\n\n/**\n * Returns the menu label for an operator, e.g. `'Starts with'`.\n *\n * @param operator - The operator to label.\n * @returns The display label.\n */\nexport function columnFilterOperatorLabel(operator: ColumnFilterOperator): string {\n return OPERATOR_LABELS[operator];\n}\n\n/**\n * Returns the registry glyph name shown on the operator button's face, e.g.\n * `'align-left'` for `startsWith`.\n *\n * @param operator - The operator to represent.\n * @returns The button-face glyph name.\n */\nexport function columnFilterOperatorGlyph(operator: ColumnFilterOperator): string {\n return OPERATOR_GLYPHS[operator];\n}\n\n/**\n * Returns whether an operator reads the text input. `false` for `isEmpty` /\n * `isNotEmpty`, which apply unconditionally and ignore any typed text.\n *\n * @param operator - The operator to check.\n * @returns `true` for every operator except `isEmpty` / `isNotEmpty`.\n */\nexport function columnFilterTakesOperand(operator: ColumnFilterOperator): boolean {\n return operator !== 'isEmpty' && operator !== 'isNotEmpty';\n}\n\n/**\n * Returns whether `clause` would actually contribute a filter once built —\n * `true` for `isEmpty` / `isNotEmpty` (which need no text and always apply),\n * or any other operator with non-blank `text`. The single rule behind both\n * {@link buildColumnFilter}'s own per-clause null-exclusion and\n * {@link effectiveClauseCount}'s count, so a caller displaying \"how many\n * conditions are active\" can never drift out of sync with what the store\n * actually filters on.\n *\n * @param clause - The clause to check.\n * @returns `true` when the clause would contribute a filter.\n */\nexport function isClauseEffective(clause: ColumnFilterClause): boolean {\n return !columnFilterTakesOperand(clause.operator) || clause.text !== '';\n}\n\n/**\n * Returns how many clauses in `clauses` would actually contribute a filter —\n * see {@link isClauseEffective}. Used by a column's clause-count badge so a\n * still-blank \"Add condition…\" row (added but not yet typed into) never\n * inflates the displayed count beyond what {@link buildColumnFilter} applies.\n *\n * @param clauses - The clause list to count.\n * @returns The number of effective clauses.\n */\nexport function effectiveClauseCount(clauses: ColumnFilterClause[]): number {\n return clauses.filter(isClauseEffective).length;\n}\n\n/** The characters a numeric filter operand can be built from. */\nconst NUMERIC_FILTER_KEY = /^[0-9.-]$/;\n\n/**\n * Whether `target` declares a combo column — one whose filter text is matched\n * against option labels rather than the stored value. The single rule behind\n * both {@link buildClauseFilter}'s combo routing and\n * {@link columnFilterTakesNumericOperand}, so the two can never drift apart.\n *\n * @param target - The column's type plus its `values` / `showSeconds`.\n * @returns `true` when `target` declares one or more combo options.\n */\nfunction isComboTarget(target: ColumnFilterTarget): boolean {\n return target.values !== undefined && target.values.length > 0;\n}\n\n/**\n * Whether a column's filter operand has to be a number — `true` for a\n * `number` column that is not a combo column (a combo column matches typed\n * text against option labels, which are text no matter the field's type).\n *\n * @param target - The column's type plus its `values` / `showSeconds`.\n * @returns `true` when the column's filter operand is parsed as a number.\n * @internal\n */\nexport function columnFilterTakesNumericOperand(target: ColumnFilterTarget): boolean {\n return target.type === 'number' && !isComboTarget(target);\n}\n\n/**\n * Whether a `KeyboardEvent.key` may be typed into a numeric filter input.\n * Allowed when it is not exactly one character (every editing and\n * navigation key reports a multi-character `key`) or when it is one of\n * `0`-`9`, `-`, `.`.\n *\n * @param key - The `KeyboardEvent.key` to check.\n * @returns `true` when the key may be typed into a numeric filter input.\n * @internal\n */\nexport function columnFilterAcceptsNumericKey(key: string): boolean {\n return key.length !== 1 || NUMERIC_FILTER_KEY.test(key);\n}\n\n/**\n * Parses `text` as a time of day — `HH:MM[:SS]` with an optional trailing\n * `AM`/`PM` — anchored to 1970-01-01 local, mirroring the normalisation\n * [`TimeEditor`](/api/component/table/classes/TimeEditor) commits and\n * documents.\n *\n * @param text - The raw text to parse.\n * @returns The anchored `Date`, or `null` when `text` does not match the\n * `HH:MM[:SS] [AM|PM]` shape.\n */\nfunction parseTimeOfDay(text: string): Date | null {\n const match = text.trim().match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?\\s*(am|pm)?$/i);\n\n if (!match) {\n return null;\n }\n\n let hours = Number(match[1]);\n const minutes = Number(match[2]);\n const seconds = match[3] ? Number(match[3]) : 0;\n const meridiem = match[4]?.toLowerCase();\n\n if (minutes > 59 || seconds > 59) {\n return null;\n }\n\n if (meridiem) {\n if (hours < 1 || hours > 12) {\n return null;\n }\n\n hours = meridiem === 'am' ? (hours === 12 ? 0 : hours) : (hours === 12 ? 12 : hours + 12);\n } else if (hours > 23) {\n return null;\n }\n\n return new Date(1970, 0, 1, hours, minutes, seconds);\n}\n\n/**\n * Parses `text` into the operand type `type` expects for a relational or\n * equality comparison.\n *\n * @param type - The column's field type.\n * @param text - The raw text typed into the filter input.\n * @returns The parsed operand, or `null` when `text` does not parse to `type`.\n */\nfunction parseOperand(type: FieldType, text: string): string | number | boolean | Date | null {\n switch (type) {\n case 'number': {\n const n = Number(text);\n\n return Number.isNaN(n) ? null : n;\n }\n\n case 'boolean': {\n const lower = text.toLowerCase();\n\n if (['true', '1', 'yes'].includes(lower)) return true;\n if (['false', '0', 'no'].includes(lower)) return false;\n\n return null;\n }\n\n case 'time': {\n const anchored = parseTimeOfDay(text);\n\n if (anchored) {\n return anchored;\n }\n\n const d = new Date(text);\n\n return Number.isNaN(d.getTime()) ? null : d;\n }\n\n case 'date':\n case 'datetime': {\n const d = new Date(text);\n\n return Number.isNaN(d.getTime()) ? null : d;\n }\n\n case 'string':\n case 'auto':\n case 'glyph':\n default:\n return text;\n }\n}\n\n/**\n * The half-open `[lo, hi)` span of instants a column of this shape renders\n * identically — a `date` cell hides the time of day, and a `time`/`datetime`\n * cell without `showSeconds` hides the seconds. Calendar arithmetic (never\n * `+ 86_400_000`), so the `date` case survives a DST boundary.\n *\n * @param type - The temporal field type.\n * @param showSeconds - Whether the matching renderer includes seconds.\n * @param operand - The parsed operand equality is being built around.\n * @returns The inclusive lower bound and exclusive upper bound of the span.\n */\nfunction displayBucket(type: 'date' | 'time' | 'datetime', showSeconds: boolean, operand: Date): { lo: Date; hi: Date } {\n if (type === 'date') {\n const lo = new Date(operand.getFullYear(), operand.getMonth(), operand.getDate());\n const hi = new Date(operand.getFullYear(), operand.getMonth(), operand.getDate() + 1);\n\n return { lo, hi };\n }\n\n const lo = new Date(operand.getFullYear(), operand.getMonth(), operand.getDate(),\n operand.getHours(), operand.getMinutes(), showSeconds ? operand.getSeconds() : 0);\n const hi = new Date(lo.getTime() + (showSeconds ? 1_000 : 60_000));\n\n return { lo, hi };\n}\n\n/**\n * Resolves a combo column's `contains` / `startsWith` / `endsWith` / `eq` /\n * `neq` filter: runs every declared option through `display`, keeps the\n * ones whose rendered label matches `text`, and emits the matching raw\n * values as an `in` set (`neq` wraps it in `not`). Substring operators are\n * case-insensitive; `eq` / `neq` are exact — the same case rule the data\n * layer's own `contains` / `eq` evaluation follows.\n *\n * @param field - The model field name this filter targets.\n * @param values - The column's declared combo options.\n * @param operator - The selected operator.\n * @param text - The raw typed text.\n * @param display - Resolver used to render each option's label.\n * @returns The built `in` / `not`-`in` descriptor. Zero matches still\n * returns `in []` (matches no row), never `null`.\n */\nfunction buildComboFilter(\n field: string,\n values: Array<ComboOption | string>,\n operator: 'contains' | 'startsWith' | 'endsWith' | 'eq' | 'neq',\n text: string,\n display: CellTextResolver,\n): FilterDescriptor {\n const substring = operator === 'contains' || operator === 'startsWith' || operator === 'endsWith';\n const needle = substring ? text.toLowerCase() : text;\n\n const matches = normalizeComboOptions(values).filter(option => {\n const label = display.text('combo', false, values, option.value);\n const haystack = substring ? label.toLowerCase() : label;\n\n switch (operator) {\n case 'contains': return haystack.includes(needle);\n case 'startsWith': return haystack.startsWith(needle);\n case 'endsWith': return haystack.endsWith(needle);\n case 'eq':\n case 'neq': return haystack === needle;\n }\n }).map(option => option.value);\n\n const inDescriptor: FilterDescriptor = { type: 'in', field, values: matches };\n\n return operator === 'neq' ? { type: 'not', filter: inDescriptor } : inDescriptor;\n}\n\n/**\n * Builds the {@link FilterDescriptor} for one clause of a column's filter\n * state, or `null` when the clause produces no filter — blank text on an\n * operator that takes an operand, or text that fails to parse to\n * `target.type` (so a half-typed number never blanks the table).\n *\n * `isEmpty` / `isNotEmpty` ignore `clause.text` entirely and reuse the\n * existing `in` / `not` descriptor algebra rather than becoming new\n * descriptor variants. A combo column (`target.values` non-empty) resolves\n * `contains` / `startsWith` / `endsWith` / `eq` / `neq` against its option\n * **labels** instead of the stored value, emitting the matching raw values as\n * an `in` set (`neq` wraps it in `not`). A `date` / `time` / `datetime`\n * column's `eq` / `neq` matches every instant that **displays** the same as\n * the typed text, not the exact instant — built as the half-open range of\n * instants that renders identically (`and(gte, lt)`, `neq` wraps it in\n * `not`). That same column's `contains` / `startsWith` / `endsWith`\n * descriptor carries the column's display hint (`temporal: { type,\n * showSeconds }`), so the operator matches the field's displayed text rather\n * than its raw `Date` form. Every other operator keeps comparing a parsed\n * operand against the raw stored value.\n *\n * @param field - The model field name this filter targets.\n * @param target - The column's type plus its `values` / `showSeconds`.\n * @param clause - The single operator + text pair to build.\n * @param display - Resolver used to render a combo option's label or a\n * temporal value's displayed text.\n * @returns The built descriptor, or `null` when the clause produces no filter.\n */\nfunction buildClauseFilter(\n field: string,\n target: ColumnFilterTarget,\n clause: ColumnFilterClause,\n display: CellTextResolver,\n): FilterDescriptor | null {\n const { operator, text } = clause;\n\n if (operator === 'isEmpty') {\n return { type: 'in', field, values: [null, undefined, ''] };\n }\n\n if (operator === 'isNotEmpty') {\n return { type: 'not', filter: { type: 'in', field, values: [null, undefined, ''] } };\n }\n\n // Every other operator needs an operand, so `isClauseEffective` reduces\n // to the same `text === ''` check this line replaced — routed through\n // the shared helper so this null-exclusion and `effectiveClauseCount`'s\n // own counting rule can never drift apart.\n if (!isClauseEffective(clause)) {\n return null;\n }\n\n if (isComboTarget(target)\n && (operator === 'contains' || operator === 'startsWith' || operator === 'endsWith'\n || operator === 'eq' || operator === 'neq')) {\n return buildComboFilter(field, target.values!, operator, text, display);\n }\n\n if ((target.type === 'date' || target.type === 'time' || target.type === 'datetime')\n && (operator === 'eq' || operator === 'neq')) {\n const operand = parseOperand(target.type, text);\n\n if (operand === null || !(operand instanceof Date)) {\n return null;\n }\n\n const { lo, hi } = displayBucket(target.type, target.showSeconds ?? false, operand);\n const andDescriptor: FilterDescriptor = {\n type: 'and',\n filters: [{ type: 'gte', field, value: lo }, { type: 'lt', field, value: hi }],\n };\n\n return operator === 'neq' ? { type: 'not', filter: andDescriptor } : andDescriptor;\n }\n\n if (operator === 'contains' || operator === 'startsWith' || operator === 'endsWith') {\n if (target.type === 'date' || target.type === 'time' || target.type === 'datetime') {\n return {\n type: operator,\n field,\n value: text,\n temporal: { type: target.type, showSeconds: target.showSeconds ?? false },\n };\n }\n\n return { type: operator, field, value: text };\n }\n\n const value = parseOperand(target.type, text);\n\n if (value === null) {\n return null;\n }\n\n // gt/gte/lt/lte never see a boolean operand in practice — no field type\n // offers both an ordering operator and 'boolean' parsing — but the\n // parsed union includes it, so a targeted cast closes the gap `switch`\n // narrowing can't for a shared local.\n const ordered = value as number | string | Date;\n\n switch (operator) {\n case 'eq': return { type: 'eq', field, value };\n case 'neq': return { type: 'neq', field, value };\n case 'gt': return { type: 'gt', field, value: ordered };\n case 'gte': return { type: 'gte', field, value: ordered };\n case 'lt': return { type: 'lt', field, value: ordered };\n case 'lte': return { type: 'lte', field, value: ordered };\n }\n}\n\n/**\n * Builds the {@link FilterDescriptor} for one column's current filter state,\n * or `null` when every clause produces no filter.\n *\n * Each clause is resolved independently, using the same per-clause logic\n * this function used before it supported more than one clause (blank text,\n * or text that fails to parse, drops that clause without touching the\n * others). The survivors are then combined: zero survivors builds `null`\n * (the existing blank-input case); exactly one survivor is returned\n * directly, with no `and` wrapper, so a column carrying its original single\n * clause builds byte-identical output to before this function supported\n * multiple clauses; two or more survivors are combined into\n * `{ type: 'and', filters: [...] }`, in clause order.\n *\n * @param field - The model field name this filter targets.\n * @param target - The column's type plus its `values` / `showSeconds`.\n * @param state - The column's current clause list.\n * @param display - Resolver used to render a combo option's label or a\n * temporal value's displayed text.\n * @returns The built descriptor, or `null` when the state produces no filter.\n */\nexport function buildColumnFilter(\n field: string,\n target: ColumnFilterTarget,\n state: ColumnFilterState,\n display: CellTextResolver,\n): FilterDescriptor | null {\n const survivors = state.clauses\n .map(clause => buildClauseFilter(field, target, clause, display))\n .filter((descriptor): descriptor is FilterDescriptor => descriptor !== null);\n\n if (survivors.length === 0) {\n return null;\n }\n\n if (survivors.length === 1) {\n return survivors[0];\n }\n\n return { type: 'and', filters: survivors };\n}\n\n/**\n * Compares two {@link ColumnFilterState}s for equality — same clause count,\n * same operator and text at every index, in order. Used to suppress a\n * redundant debounce reschedule when a repeat keystroke reports state\n * unchanged from what is already cached.\n *\n * @param a - The first state to compare.\n * @param b - The second state to compare.\n * @returns `true` when both states hold the same clauses in the same order.\n */\nexport function columnFilterStatesEqual(a: ColumnFilterState, b: ColumnFilterState): boolean {\n if (a.clauses.length !== b.clauses.length) {\n return false;\n }\n\n return a.clauses.every((c, i) => c.operator === b.clauses[i].operator && c.text === b.clauses[i].text);\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell, CellEvent } from \"~/component/table/cell/Cell.js\";\nimport { FilterCellRenderer } from \"~/component/table/cell/renderer/Filter.js\";\nimport { FilterClauseBadge } from \"~/component/table/cell/FilterClauseBadge.js\";\nimport {\n columnFilterOperatorLabel,\n columnFilterOperatorGlyph,\n columnFilterTakesOperand,\n columnFilterAcceptsNumericKey,\n isClauseEffective,\n effectiveClauseCount,\n} from \"~/component/table/ColumnFilter.js\";\nimport type { ColumnFilterOperator, ColumnFilterClause, ColumnFilterState } from \"~/component/table/ColumnFilter.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Popover } from \"~/overlay/Popover.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { MenuButton } from \"~/component/button/MenuButton.js\";\nimport { TabCloseButton } from \"~/component/button/TabCloseButton.js\";\nimport { TextField } from \"~/component/input/TextField.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { magnifying_glass } from \"~/glyphs/solid/magnifying_glass.js\";\nimport { align_left } from \"~/glyphs/solid/align_left.js\";\nimport { align_right } from \"~/glyphs/solid/align_right.js\";\nimport { equals } from \"~/glyphs/solid/equals.js\";\nimport { not_equal } from \"~/glyphs/solid/not_equal.js\";\nimport { greater_than } from \"~/glyphs/solid/greater_than.js\";\nimport { greater_than_equal } from \"~/glyphs/solid/greater_than_equal.js\";\nimport { less_than } from \"~/glyphs/solid/less_than.js\";\nimport { less_than_equal } from \"~/glyphs/solid/less_than_equal.js\";\nimport { ban } from \"~/glyphs/solid/ban.js\";\nimport { circle_check } from \"~/glyphs/solid/circle_check.js\";\nimport { plus } from \"~/glyphs/solid/plus.js\";\n\nGlyph.register(\n magnifying_glass, align_left, align_right, equals, not_equal,\n greater_than, greater_than_equal, less_than, less_than_equal,\n ban, circle_check, plus,\n);\n\n/**\n * String-literal union of the events emitted by {@link FilterCell}. Extends\n * the inherited `CellEvent` union with the filter-specific event.\n *\n * @category Components\n */\nexport type FilterCellEvent = CellEvent | \"filterchange\";\n\n/**\n * Placeholder shown on a clause's text field while it is disabled because\n * the selected operator ignores text ({@link columnFilterTakesOperand} is\n * `false` for `isEmpty` / `isNotEmpty`) — otherwise a disabled field with no\n * explanation just looks broken.\n */\nconst NO_OPERAND_PLACEHOLDER = \"No value needed\";\n\n/**\n * A header-band cell hosting one column's filter input and operator picker,\n * plus (once a second condition is added) a corner badge and a popover\n * listing every AND-combined condition.\n *\n * Stateless across a column-window recycle: {@link TableHeader} owns the\n * per-column {@link ColumnFilterState} map and re-applies it via\n * {@link FilterCell.setFilterState} on every reconcile, mirroring how\n * {@link HeaderCell} re-applies sort state to a recycled cell.\n *\n * @category Components\n */\nclass FilterCell extends Cell<string | null> {\n\n private _fieldName: string;\n private _operators: ColumnFilterOperator[] = [];\n // Whether this cell's filter inputs are currently restricted to\n // characters that can appear in a number — see `setNumericOnly` and\n // `numericKeyDisposition`. Consulted at keydown time (never cached onto\n // the DOM), so a cell recycled onto a non-numeric column never carries a\n // stale restriction.\n private _numericOnly: boolean = false;\n // The full clause list; always at least one entry. `_clauses[0]` is the\n // single source of truth for the always-visible inline input + operator\n // button — every path that changes clause 0 writes into `_clauses[0]`\n // first, then calls `fireFilterChange`. `_clauses[1..]` exist only\n // inside the lazily-created `_clausesPopover`.\n private _clauses: ColumnFilterClause[] = [{ operator: 'contains', text: '' }];\n // `declare`d (no initializer) so the constructor-body assignment below\n // is the only construction, mirroring HeaderCell's `_priorityBadge`\n // field — see that class's destructor comment for why.\n declare private _badge: FilterClauseBadge;\n // Lazily created on first \"Add condition…\", mirroring MenuButton's own\n // lazy `_menu`.\n private _clausesPopover: Popover | null = null;\n // Cached for the popover's title; setColumnLabel already receives it.\n private _columnLabel: string = '';\n\n /**\n * @param fieldName - The model field this cell's filter targets.\n * @param operators - The operators to offer; an empty array renders the\n * cell blank (see {@link setOperators}).\n */\n constructor(fieldName: string, operators: ColumnFilterOperator[]) {\n super(\"th\", new FilterCellRenderer());\n\n this._fieldName = fieldName;\n this._badge = new FilterClauseBadge();\n\n this.getAria().setRole(\"columnheader\");\n // Tints the whole cell — not just the input/button inside it — so\n // the filter row reads as one continuous band with no untinted gaps\n // at the cell edges or between the two controls.\n this.setBackgroundColor(\"var(--ts-ui-table-filter-row-bg, rgba(255, 200, 0, 0.2))\");\n\n // Inter-column divider, in the same resize-handle gray that separates\n // the column header row above — see ParentHeaderCell's own identical\n // write for why this is `setShadow`, not `setBorder`: `Cell`'s\n // theme-change listener re-runs `setBorder('var(--ts-ui-table-cell-\n // border, none)')` on every theme toggle and would wipe a\n // border-based divider.\n this.setShadow(\"inset -1px 0 0 0 var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\");\n\n const renderer = this.filterRenderer();\n\n renderer.getInput().on(\"change\", () => {\n this._clauses[0].text = renderer.getValue() ?? '';\n this.syncBadge();\n this.fireFilterChange(false);\n });\n renderer.getInput().on(\"keydown\", (e: KeyboardEvent) => this.onInputKeyDown(e));\n\n // Provider form, so the checkmark tracks the current operator on every open.\n // `checked` renders the checkmark in its own leading column (left of the\n // glyph), so a checked and an unchecked row's glyph and label still align.\n // The trailing separator + \"Add condition…\" entry always appends one blank\n // clause and opens the clauses popover, whether the column currently has\n // 1 clause or 5 — one rule, not one that branches on the current count.\n renderer.getOperatorButton().setMenuItems(() => [\n ...this._operators.map(op => ({\n text: columnFilterOperatorLabel(op),\n glyph: columnFilterOperatorGlyph(op),\n checked: op === this._clauses[0].operator,\n action: () => this.selectOperator(op),\n })),\n { separator: true as const },\n { text: 'Add condition…', glyph: 'plus', action: () => this.addConditionAndOpenPopover() },\n ]);\n\n // A column with 2+ clauses substitutes the clauses popover for the\n // default operator menu on click — the menu only makes sense as a\n // single-clause editor (it names one operator's worth of checkmarks),\n // and a returning user with several conditions wants straight back\n // into the popover that lists them, not a menu for clause 0 alone.\n // The predicate is re-evaluated on every click, so it always reflects\n // the current clause count without needing to be re-armed elsewhere.\n renderer.setMenuOpenPredicate(() => this._clauses.length < 2);\n renderer.getOperatorButton().on(\"action\", () => this.onOperatorButtonClick());\n\n this.setOperators(operators);\n }\n\n /** Returns this cell's renderer, narrowed to its concrete type. */\n private filterRenderer(): FilterCellRenderer {\n return this.getRenderer() as FilterCellRenderer;\n }\n\n /**\n * Writes the operator button's glyph face and its title — the title stays\n * off the visible face (`FilterCellRenderer` sets `showText: false`) but\n * still drives the button's hover tooltip and accessible name, so\n * hovering the button reports which mode the filter is currently in.\n *\n * @param op - The operator to represent.\n */\n private applyOperatorFace(op: ColumnFilterOperator): void {\n const button = this.filterRenderer().getOperatorButton();\n\n button.setGlyph(columnFilterOperatorGlyph(op));\n button.setText(columnFilterOperatorLabel(op));\n }\n\n /**\n * Enables or disables a clause's text field for whether `op` reads it\n * ({@link columnFilterTakesOperand}), and mirrors that in the field's\n * placeholder so a disabled field explains itself instead of just going\n * grey with no indication why it won't take input. Applied to both the\n * always-visible inline input and each popover row's own field — every\n * `setEnabled` call gated on `columnFilterTakesOperand` routes through\n * here so the two can never drift apart.\n *\n * A placeholder rather than a hover tooltip: `setEnabled(false)` writes\n * the input's native `disabled` attribute, and browsers do not reliably\n * dispatch hover events to a disabled form control, so a tooltip\n * attached to one is not a dependable way to explain it.\n *\n * @param field - The text field to update.\n * @param op - The operator now selected for that field's clause.\n */\n private applyOperandAvailability(field: TextField, op: ColumnFilterOperator): void {\n const takesText = columnFilterTakesOperand(op);\n\n field.setEnabled(takesText);\n\n if (takesText) {\n field.clearPlaceholder();\n } else {\n field.setPlaceholder(NO_OPERAND_PLACEHOLDER);\n }\n }\n\n /**\n * Re-targets this cell at another column's model field. Used by the\n * header's filter-row reconciler when recycling a cell whose column\n * left the window for one entering it. Closes an open clauses popover\n * only when the incoming name is a true recycle onto a different\n * field — not on every resync pass, since the header re-applies\n * `setFieldName` unconditionally on every reconcile.\n *\n * @param name - The new field name this cell reports on `\"filterchange\"`.\n * @returns This cell, for method chaining.\n */\n setFieldName(name: string): this {\n if (name !== this._fieldName) {\n this._clausesPopover?.hide();\n }\n\n this._fieldName = name;\n\n return this;\n }\n\n /**\n * Returns the model field name this cell currently reports on\n * `\"filterchange\"`.\n *\n * @returns The current field name.\n */\n getFieldName(): string {\n return this._fieldName;\n }\n\n /**\n * Replaces the offered operators. Collapses to a single blank clause on\n * `operators[0]` — the header's reconciler always calls\n * {@link setFilterState} with the authoritative cached state (or the\n * blank default) immediately after this, in the same synchronous pass,\n * so whatever this leaves in the clause list is overwritten before the\n * next paint.\n *\n * An **empty array** marks the column non-filterable: the cell renders\n * blank (no input, no operator button) and emits nothing. A non-empty\n * array shows both controls again.\n *\n * @param operators - The operators to offer, in menu order.\n * @returns This cell, for method chaining.\n */\n setOperators(operators: ColumnFilterOperator[]): this {\n this._operators = operators;\n\n const renderer = this.filterRenderer();\n const blank = operators.length === 0;\n\n renderer.getInput().setDisplayed(!blank);\n renderer.getOperatorButton().setDisplayed(!blank);\n\n if (blank) {\n // A cell recycled from a multi-condition column onto a\n // `filterable: false` one has no operators to fall back to, but\n // must still drop its stale clause list and hide the badge —\n // otherwise a badge showing a leftover count of 2+ would linger\n // over a cell whose input and operator button just went hidden,\n // with no controls left to explain it.\n this._clauses = [{ operator: 'contains', text: '' }];\n this._clausesPopover?.hide();\n this.syncBadge();\n\n return this;\n }\n\n this._clauses = [{ operator: operators[0], text: '' }];\n this.applyOperatorFace(operators[0]);\n this.applyOperandAvailability(renderer.getInput(), operators[0]);\n this.syncBadge();\n\n return this;\n }\n\n /**\n * Restricts this cell's filter inputs to characters that can appear in a\n * number, or lifts that restriction.\n *\n * @param numeric - `true` to restrict the inline input and every\n * popover clause row's field to digits, `-`, and `.`; `false` to\n * accept any text.\n * @returns This cell, for method chaining.\n */\n setNumericOnly(numeric: boolean): this {\n this._numericOnly = numeric;\n\n return this;\n }\n\n /**\n * Sets the accessible name of the text input to `\"Filter \" + label` —\n * `label` is the column's header text. Also cached for the clauses\n * popover's title.\n *\n * @param label - The column's header label.\n * @returns This cell, for method chaining.\n */\n setColumnLabel(label: string): this {\n this._columnLabel = label;\n this.filterRenderer().getInput().getAria().setLabel(\"Filter \" + label);\n\n return this;\n }\n\n /**\n * Writes the full clause list without emitting `\"filterchange\"`. Used by\n * the header to re-apply cached state to a recycled cell. Clones the\n * incoming clauses so a later popover mutation (`push`/`splice` on this\n * cell's own `_clauses`) never aliases — and silently corrupts — the\n * header's cached entry.\n *\n * @param state - The clause list to display.\n * @returns This cell, for method chaining.\n */\n setFilterState(state: ColumnFilterState): this {\n this._clauses = state.clauses.map(c => ({ ...c }));\n\n const renderer = this.filterRenderer();\n\n this.applyOperatorFace(this._clauses[0].operator);\n this.applyOperandAvailability(renderer.getInput(), this._clauses[0].operator);\n renderer.setValue(this._clauses[0].text === \"\" ? null : this._clauses[0].text);\n this.syncBadge();\n\n // Mirrors selectOperator's self-layout: a cell recycled onto a new\n // column at unchanged geometry (same x/width/height) has its\n // doLayout skipped by the header's geometry-diff cache, and an\n // operator change moves this cell's own layout (enabled/disabled\n // input) without moving that geometry. See CellGeometry.ts's writer\n // list.\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns the full clause list currently held, read from the internal\n * cache — `_clauses[0].text` is kept current on every change/Enter/\n * Escape rather than re-read from the DOM at return time.\n *\n * @returns The current filter state.\n */\n getFilterState(): ColumnFilterState {\n return { clauses: this._clauses.map(c => ({ ...c })) };\n }\n\n /**\n * Selects a new operator for clause 0 from the inline operator dropdown:\n * updates the button face, enables/disables the text input (clearing it\n * when the new operator ignores text), lays the cell out — mirroring\n * {@link HeaderCell.setHeaderGlyph}'s self-layout, since the geometry\n * cache does not know an operator change moved layout without moving\n * geometry — then emits `\"filterchange\"` immediately.\n *\n * @param op - The newly selected operator.\n */\n private selectOperator(op: ColumnFilterOperator): void {\n this._clauses[0].operator = op;\n\n const renderer = this.filterRenderer();\n const takesText = columnFilterTakesOperand(op);\n\n this.applyOperatorFace(op);\n this.applyOperandAvailability(renderer.getInput(), op);\n\n if (!takesText) {\n renderer.setValue(null);\n this._clauses[0].text = '';\n }\n\n this.doLayout();\n this.syncBadge();\n\n this.fireFilterChange(true);\n }\n\n /**\n * The keydown disposition for this cell's filter inputs. Returns\n * `{ prevent: true }` for a keystroke that would type a character a\n * number cannot contain into a numeric column's field, and `false` for\n * everything else — every key on a non-numeric column, every editing\n * and navigation key (whose `key` is a multi-character name), and every\n * keystroke with a modifier held, which is a shortcut rather than\n * typing.\n *\n * @param e - The keydown event.\n * @returns `{ prevent: true }` to refuse the keystroke, `false` to\n * allow it.\n */\n private numericKeyDisposition(e: KeyboardEvent): Event.ListenerResult {\n if (!this._numericOnly || e.ctrlKey || e.metaKey || e.altKey) {\n return false;\n }\n\n return columnFilterAcceptsNumericKey(e.key) ? false : { prevent: true };\n }\n\n /**\n * Handles a keydown on the text input: first the numeric-restriction\n * gate ({@link numericKeyDisposition}), which — when it refuses the\n * keystroke — returns immediately without reaching the handling below.\n * Otherwise, Enter applies the current text immediately (the preceding\n * `\"change\"` event already updated `_clauses[0].text`); Escape clears\n * the text and applies that immediately too.\n *\n * @param e - The keydown event.\n * @returns The gate's refusal, when the keystroke is refused; `false`\n * otherwise.\n */\n private onInputKeyDown(e: KeyboardEvent): Event.ListenerResult {\n const refusal = this.numericKeyDisposition(e);\n\n if (refusal) {\n return refusal;\n }\n\n if (e.key === \"Enter\") {\n this.fireFilterChange(true);\n } else if (e.key === \"Escape\") {\n this._clauses[0].text = '';\n this.filterRenderer().setValue(null);\n this.syncBadge();\n this.fireFilterChange(true);\n }\n\n return false;\n }\n\n /**\n * Handles every click on the operator button. A column with 0 or 1\n * clauses has already had its default operator menu opened by\n * {@link MenuButton}'s own internal click wiring by the time this runs —\n * the `setMenuOpenPredicate` guard registered in the constructor let that\n * toggle through, so this is a no-op for that case. A column with 2+\n * clauses had that internal toggle vetoed instead, so this opens the\n * clauses popover directly, which is the only thing that click should do.\n */\n private onOperatorButtonClick(): void {\n if (this._clauses.length >= 2) {\n this.openClausesPopover();\n }\n }\n\n /**\n * Appends one blank clause (on `operators[0]`) and opens the clauses\n * popover. The single rule behind the operator menu's trailing\n * \"Add condition…\" entry, whether the column currently has 1 clause or 5.\n */\n private addConditionAndOpenPopover(): void {\n this._clauses.push({ operator: this._operators[0], text: '' });\n this.openClausesPopover();\n this.fireFilterChange(true);\n }\n\n /**\n * Lazily creates the clauses popover on first use, mirroring\n * {@link MenuButton}'s own lazy `_menu ??= new Menu()`.\n *\n * @returns The (possibly just-created) popover.\n */\n private ensureClausesPopover(): Popover {\n if (!this._clausesPopover) {\n this._clausesPopover = new Popover({\n placement: 'auto',\n dismissOn: 'click-outside',\n // Fires once whenever the popover closes, whether via \"Done\"\n // or a click-outside dismissal (the manager's `requestClose`\n // routes through the same `hide()`) — the only path that can\n // catch a click-outside close, since Popover exposes no other\n // close event.\n onClose: () => this.pruneBlankClauses(),\n });\n this._clausesPopover.addAction('Done', () => this._clausesPopover!.hide());\n }\n\n return this._clausesPopover;\n }\n\n /**\n * Drops every clause at index ≥ 1 that is not {@link isClauseEffective} —\n * added via \"Add condition…\" / \"Add condition\" but never given text (or\n * an operator that needs none). Run once whenever the clauses popover\n * closes ({@link ensureClausesPopover}'s `onClose`), so a blank row\n * doesn't linger in `_clauses` forever and reappear on the next\n * {@link openClausesPopover} → {@link buildClausesBody} redraw.\n *\n * Never calls `syncBadge` or `fireFilterChange`: a pruned clause was by\n * definition not effective, so it was never counted in the badge or\n * included in the built filter ({@link buildColumnFilter} /\n * `buildClauseFilter` already exclude a non-effective clause) — pruning\n * it changes nothing either observes. `_clauses[0]` is always kept\n * unconditionally; it is the always-visible inline clause and carries no\n * remove control (see {@link buildClauseRow}), so it is never a pruning\n * candidate regardless of whether it is itself effective.\n */\n private pruneBlankClauses(): void {\n this._clauses = [this._clauses[0], ...this._clauses.slice(1).filter(isClauseEffective)];\n }\n\n /**\n * Opens the clauses popover anchored under the operator button, rebuilt\n * fresh from the current clause list — the same \"rebuild fresh on every\n * open\" rule {@link MenuButton}'s provider form already follows.\n */\n private openClausesPopover(): void {\n const popover = this.ensureClausesPopover();\n\n popover.setTitle(this._columnLabel + ' filter conditions');\n popover.setBody(this.buildClausesBody());\n popover.attachToComponent(this.filterRenderer().getOperatorButton());\n popover.show();\n\n this.syncBadge();\n }\n\n /**\n * Rebuilds the popover's body in place from the current clause list,\n * without re-showing or re-anchoring it. Called after any mutation made\n * from inside the popover itself (add / remove / operator pick).\n */\n private refreshClausesPopoverBody(): void {\n this._clausesPopover?.setBody(this.buildClausesBody());\n }\n\n /**\n * Builds the popover body: one {@link buildClauseRow} per current\n * clause, then a plain \"Add condition\" button that appends another\n * blank clause and re-renders the row list in place.\n *\n * @returns A freshly built, `VBox`-laid body component.\n */\n private buildClausesBody(): Component {\n const body = new Component({ layoutManager: new VBox({ spacing: 4 }) });\n\n this._clauses.forEach((_, index) => body.addComponent(this.buildClauseRow(index)));\n\n const addButton = new Button('Add condition', { glyph: 'plus' });\n\n addButton.on(\"action\", () => this.onAddConditionButtonClick());\n body.addComponent(addButton);\n\n return body;\n }\n\n /**\n * Builds one popover row: an operator-picker `MenuButton` and a\n * `TextField`, both scoped to `_clauses[index]`, plus — for every row\n * but the first — a {@link TabCloseButton} that removes the clause. Row\n * 0 carries no remove control, for the same reason the inline single\n * clause has never been \"removable,\" only clearable.\n *\n * @param index - The clause this row edits.\n * @returns The built row, an `HBox`-laid component.\n */\n private buildClauseRow(index: number): Component {\n const clause = this._clauses[index];\n\n const opButton = new MenuButton();\n\n // Not flat: this row sits beside the popover's non-flat \"Add\n // condition\" Button, and `setFlat(true)`'s stripped border/\n // background/shadow chrome doesn't read as clickable next to it.\n // Compact still wins over flat for the inset/sizing calculation\n // (`Button._resolveInsets`), so this doesn't change the button's\n // size or padding — only restores its bordered/background chrome.\n opButton.setCompact(true);\n opButton.setShowText(false);\n opButton.setGlyph(columnFilterOperatorGlyph(clause.operator));\n opButton.setText(columnFilterOperatorLabel(clause.operator));\n opButton.setMenuItems(() => this._operators.map(op => ({\n text: columnFilterOperatorLabel(op),\n glyph: columnFilterOperatorGlyph(op),\n checked: op === this._clauses[index].operator,\n action: () => this.selectRowOperator(index, op),\n })));\n\n const field = new TextField();\n\n field.setValue(clause.text);\n this.applyOperandAvailability(field, clause.operator);\n field.on(\"change\", () => this.onRowTextChange(index, field.getValue()));\n field.on(\"keydown\", (e: KeyboardEvent) => this.numericKeyDisposition(e));\n\n const row = new Component({ layoutManager: new HBox({ spacing: 4, itemAlign: \"stretch\" }) });\n\n row.addComponent(opButton);\n row.addComponent(field, { weight: 1 });\n\n if (index > 0) {\n const removeButton = new TabCloseButton();\n\n removeButton.on(\"action\", () => this.removeClause(index));\n row.addComponent(removeButton);\n }\n\n return row;\n }\n\n /**\n * Applies an operator picked from a popover row's own operator button,\n * scoped to `_clauses[index]` — the same checkable-item mechanics as the\n * inline {@link selectOperator}. Row 0 additionally re-applies the\n * change to the always-visible inline controls, since the popover's row\n * 0 edits the same clause the inline row displays.\n *\n * @param index - The clause index the row represents.\n * @param op - The newly selected operator.\n */\n private selectRowOperator(index: number, op: ColumnFilterOperator): void {\n const clause = this._clauses[index];\n const takesText = columnFilterTakesOperand(op);\n\n clause.operator = op;\n\n if (!takesText) {\n clause.text = '';\n }\n\n if (index === 0) {\n this.applyOperatorFace(op);\n this.applyOperandAvailability(this.filterRenderer().getInput(), op);\n\n if (!takesText) {\n this.filterRenderer().setValue(null);\n }\n\n this.doLayout();\n }\n\n this.refreshClausesPopoverBody();\n this.syncBadge();\n this.fireFilterChange(true);\n }\n\n /**\n * Applies a popover row's text-field edit to `_clauses[index]`, debounced\n * exactly like the inline input. Row 0 additionally re-applies the\n * change to the always-visible inline input, for the same reason\n * {@link selectRowOperator} does.\n *\n * @param index - The clause index the row represents.\n * @param text - The row's current text-field value.\n */\n private onRowTextChange(index: number, text: string): void {\n this._clauses[index].text = text;\n\n if (index === 0) {\n this.filterRenderer().setValue(text === \"\" ? null : text);\n }\n\n this.syncBadge();\n this.fireFilterChange(false);\n }\n\n /**\n * Handles the popover's own \"Add condition\" button: appends a blank\n * clause, refreshes the badge and the row list, and applies immediately.\n */\n private onAddConditionButtonClick(): void {\n this._clauses.push({ operator: this._operators[0], text: '' });\n\n this.syncBadge();\n this.refreshClausesPopoverBody();\n this.fireFilterChange(true);\n }\n\n /**\n * Removes one clause (never index 0, which carries no remove control),\n * refreshes the badge and the row list, and applies immediately.\n *\n * @param index - The clause to remove.\n */\n private removeClause(index: number): void {\n this._clauses.splice(index, 1);\n\n this.syncBadge();\n this.refreshClausesPopoverBody();\n this.fireFilterChange(true);\n }\n\n /**\n * Shows the corner clause-count badge once there are 2 or more\n * *effective* clauses ({@link effectiveClauseCount} — the same\n * would-this-contribute-a-filter rule {@link buildColumnFilter} builds\n * with), hides it otherwise — mirroring {@link SortPriorityBadge}'s own\n * hidden-below-2 threshold. A clause added via \"Add condition…\" but not\n * yet typed into (blank text, on an operator that needs one) is not\n * effective, so it is never counted: the badge always reports how many\n * conditions are actually applied to the store, never the raw row count\n * a still-blank popover row would otherwise inflate it to. Also keeps\n * the badge's accessible description and the operator button's\n * hover-tooltip description in sync: the numeric badge alone reports\n * *how many* conditions are active, not *which* — hovering the operator\n * button (or, for assistive tech, the badge's `aria-label`) states them\n * in words. Called at the end of every mutation that can change the\n * clause count *or* an existing clause's operator/text (so the\n * description never goes stale while a clause is being edited), and\n * after {@link setFilterState} rehydrates the clause list.\n */\n private syncBadge(): void {\n const count = effectiveClauseCount(this._clauses);\n const multi = count >= 2;\n const description = multi ? this.describeClauses() : null;\n const opButton = this.filterRenderer().getOperatorButton();\n\n this._badge.setCount(multi ? count : null);\n this._badge.setAccessibleDescription(description);\n\n // Single source of truth for the operator button's tooltip *title*\n // (the hover tooltip's first line): `applyOperatorFace` writes a\n // single-operator title unconditionally from clause 0's operator\n // alone, which would misrepresent a 2+-condition column as just its\n // first clause. Written unconditionally here — even when `multi` is\n // `false` — because `removeClause` can drop the effective count from\n // 2 back to 1 without going through `applyOperatorFace` at all, so\n // this is the only path guaranteed to run on every count change and\n // restore the single-operator title.\n opButton.setText(multi ? `${count} conditions` : columnFilterOperatorLabel(this._clauses[0].operator));\n\n if (description) {\n opButton.setDescription(description);\n } else {\n opButton.clearDescription();\n }\n }\n\n /**\n * Composes every *effective* current clause ({@link isClauseEffective})\n * into one human-readable line, in clause order, joined by `\" AND \"` —\n * e.g. `age At least \"18\" AND age At most \"65\"`. A still-blank clause is\n * skipped, so the composed line never names a condition the badge's own\n * count doesn't include. Used as both the badge's accessible description\n * and the operator button's hover-tooltip description once a column\n * carries 2+ effective clauses.\n *\n * @returns The composed description.\n */\n private describeClauses(): string {\n const label = this._columnLabel || this._fieldName;\n\n return this._clauses\n .filter(isClauseEffective)\n .map(c => columnFilterTakesOperand(c.operator)\n ? `${label} ${columnFilterOperatorLabel(c.operator)} \"${c.text}\"`\n : `${label} ${columnFilterOperatorLabel(c.operator)}`)\n .join(' AND ');\n }\n\n /**\n * Emits `\"filterchange\"` with this cell's current field name and state.\n *\n * @param immediate - `true` when the change should apply without the\n * header's keystroke debounce (operator pick, Enter, Escape).\n */\n private fireFilterChange(immediate: boolean): void {\n this.emit(\"filterchange\", this._fieldName, this.getFilterState(), immediate);\n }\n\n /**\n * Mounts the side-loaded clause-count badge. Mirrors\n * {@link HeaderCell.init}'s own side-loaded overlays: a raw `appendChild`\n * (not `addComponent`) keeps this cell's `Card` layout from treating the\n * badge as a second \"visible child\" of the renderer.\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 if (!el) return this;\n\n DOM.sink.appendChild(el, this._badge.getElement(true)!);\n\n return this;\n }\n\n /**\n * Registers a listener for one of this cell's events.\n *\n * @param event - `\"commit\"` / `\"editend\"` inherit from {@link Cell} (unused\n * on this non-editing cell, kept for inheritance compatibility);\n * `\"filterchange\"` fires when the typed text or the selected operator\n * changes, receiving the field name, the new state, and whether the\n * change should apply immediately (bypassing the header's debounce).\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: \"filterchange\", listener: (fieldName: string, state: ColumnFilterState, immediate: boolean) => void): this;\n on(event: FilterCellEvent, 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: FilterCellEvent, 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: \"filterchange\", fieldName: string, state: ColumnFilterState, immediate: boolean): void;\n protected emit(event: FilterCellEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Destroys the side-loaded badge and disposes the lazily-created clauses\n * popover before the inherited teardown runs.\n */\n protected destructor(): void {\n // `_badge` is a `declare` field (see cell/Header.ts:586-595's own\n // comment on why), so it can read as `undefined` if teardown lands\n // before the constructor body ran.\n this._badge?.dispose();\n this._clausesPopover?.dispose();\n\n super.destructor();\n }\n}\n\nconst FilterCellCallable = callable(FilterCell);\ntype FilterCellCallable = FilterCell;\nexport {\n FilterCell as _FilterCell,\n FilterCellCallable as FilterCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\n\n/** Geometry last written to one cell, kept so an identical rewrite can be skipped. */\ninterface CellGeometry {\n /** The cell's x offset within its row, in pixels. */\n x: number;\n /** The cell's width, in pixels. */\n w: number;\n /** The cell's height, in pixels. */\n h: number;\n}\n\n/**\n * Remembers the geometry last written to each table cell so a pass that would\n * rewrite the same values skips both the write and the `doLayout` behind it.\n *\n * Shared by the table body's row cells and the header's own two rows, which\n * position their cells identically: an x offset inside a row that is itself\n * translated by the scroll offset, at the row's height. Because the offsets are\n * content-absolute, a cell keeps its geometry for as long as it keeps its\n * column, and a horizontal scroll leaves most of them untouched.\n *\n * **Records are keyed on the cell, not on its position.** A column-window slide\n * renumbers the slots while the surviving cells stay on their own columns, so a\n * position-keyed cache would miss on every survivor and lay out the whole row\n * again — which is most of the work a slide could avoid.\n *\n * **Geometry is the only layout input this cache writes**, so anything else\n * that moves a cell's layout has to deal with it, by laying the cell out itself\n * or by calling {@link clear}. The writes that do, framework-side:\n *\n * - `Cell.setActiveRenderer` — a `DynamicCell` swapping the child the layout\n * is fitted around.\n * - `Cell.startEdit` / `detachEditor` — the same swap, for the editor\n * (`detachEditor` is private, reached from `commitEdit` and `cancelEdit`).\n * - `TreeCellRenderer.setTreeState` — a depth change moves the indent.\n * - `HeaderCell.setHeaderGlyph` — a glyph shifts the renderer's left inset.\n * - `FilterCell.selectOperator` / `setFilterState` — an operator change\n * enables/disables the text input, which moves layout without moving\n * geometry.\n * - `GlyphRenderer.setValue` — replaces its child outright.\n * - A theme change, which rewrites the padding and border every cell is fitted\n * against; both consumers {@link clear} on it.\n *\n * `Cell.wrapRenderer` also replaces the visible child without laying out, and\n * is public — it is safe only because its one caller wraps a cell it has just\n * built, which has no record yet.\n *\n * **A value write is usually not one of them**, which is what makes the skip\n * worth having: a renderer that only writes text moves nothing, because cell\n * renderers run with `setAutoMeasure(false)`. A renderer whose `setValue` adds,\n * removes or replaces a child is the exception — see `GlyphRenderer` — and owes\n * itself the layout. That applies to consumer renderers installed through\n * `ColumnConfig.renderer` as much as to the framework's own.\n *\n * @internal\n */\nexport class CellGeometryCache {\n\n private _geometry: WeakMap<Component, CellGeometry> = new WeakMap();\n\n /**\n * Drops every record, so the next pass re-applies and re-lays-out every\n * cell even where its geometry is unchanged.\n */\n clear(): void {\n this._geometry = new WeakMap();\n }\n\n /**\n * Writes `x` / `width` / `height` to `cell` and lays it out, unless the\n * cell already holds those exact values.\n *\n * @param cell - The cell to position.\n * @param x - The cell's x offset within its row, in pixels.\n * @param w - The cell's width, in pixels.\n * @param h - The cell's height, in pixels.\n */\n apply(cell: Component, x: number, w: number, h: number): void {\n const previous = this._geometry.get(cell);\n\n if (previous && previous.x === x && previous.w === w && previous.h === h) {\n return;\n }\n\n cell.setAutoCommitStyle(false);\n cell.setX(x);\n cell.setY(0);\n cell.setWidth(w);\n cell.setHeight(h);\n cell.setAutoCommitStyle(true);\n\n // A geometry change needs a full layout pass so the cell's renderer\n // re-fits the new width.\n cell.doLayout();\n\n // Recorded only once the cell has an element. Without one the layout\n // above could not do its job: the layout manager fits the renderer\n // against `getInnerSize()`, which is element-gated, so an unrendered\n // cell keeps a full-width box around an unsized renderer. Recording\n // that would make every later pass at this same geometry skip the cell\n // and leave it that way. Reachable from the header, which renders no\n // cells until its first layout pass and so can be driven through\n // `renderColumnWindow` before the table is realized; the body's pooled\n // rows are realized as they are built, so there the guard never fires.\n //\n // What is recorded is what was requested rather than what the setters\n // committed, which agree because a table cell carries no min/max and\n // does not clamp to its content.\n if (cell.getElement()) {\n this._geometry.set(cell, { x: x, w: w, h: h });\n }\n }\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 // The inner field's max is unpinned too, matching StringEditor: the\n // field has to fill the cell, and a TextField otherwise pins its max to\n // its own one-line box.\n this._textField.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 * Disposes the lazily-created picker dropdown, then runs the inherited\n * teardown. `_dropdown` is a LayerManager-mounted overlay (AnimatedDropdown\n * subclass), never a registered child, so the base destructor's recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._dropdown?.dispose();\n\n super.destructor();\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 * Disposes the lazily-created picker dropdown, then runs the inherited\n * teardown. `_dropdown` is a LayerManager-mounted overlay (AnimatedDropdown\n * subclass), never a registered child, so the base destructor's recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._dropdown?.dispose();\n\n super.destructor();\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 * Disposes the lazily-created picker dropdown, then runs the inherited\n * teardown. `_dropdown` is a LayerManager-mounted overlay (AnimatedDropdown\n * subclass), never a registered child, so the base destructor's recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._dropdown?.dispose();\n\n super.destructor();\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 * Disposes every editor this pool has lazily constructed, releasing their\n * per-instance stylesheet rules. Called once, from `Body.destructor()`,\n * when the owning table is torn down — a shared editor is acquired into\n * `_editors` only on a real edit gesture (`Cell.startEdit` → `acquire`),\n * held there for the table's whole lifetime, and detached-but-not-disposed\n * on every edit end (`Cell.detachEditor`'s `removeComponent`, which keeps a\n * reusable editor alive across edits) — so nothing else ever reaches it.\n */\n dispose(): void {\n for (const editor of this._editors.values()) {\n editor.dispose();\n }\n\n this._editors.clear();\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 { CellGeometryCache } from \"~/component/table/CellGeometry.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 { selectionsEqual } from \"~/component/shared/selectionsEqual.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 { 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\n/** Number of off-screen columns to render to the left and right of the viewport. */\nconst COLUMN_BUFFER = 2;\n\n/**\n * The horizontally-visible column range plus the geometry every rendered\n * cell is placed from. `firstCol` / `lastCol` are inclusive visible-column\n * indices; `lastCol` is `-1` when there are no columns.\n *\n * @internal\n */\nexport interface ColumnWindow {\n firstCol: number;\n lastCol : number;\n /** Effective width per visible column, index-aligned with visible-column order. */\n widths : number[];\n /** Left offset per visible column — the running sum of `widths`. */\n lefts : number[];\n}\n\n/**\n * Computes the column window for a horizontal scroll offset and viewport width.\n *\n * Walks `widths` left to right, accumulating each column's left offset. A\n * column is raw-visible when its right edge is at or after `scrollX` AND its\n * left edge is at or before `scrollX + viewportWidth` — an inclusive\n * comparison, so a table with no known widths yet (`widths` full of zeros,\n * e.g. before the layout manager has run) degrades to \"every column\n * renders\" rather than \"no column renders\". The raw-visible run is widened\n * by {@link COLUMN_BUFFER} on each side and clamped to `[0, n-1]`.\n *\n * @param widths - Effective width per visible column, in display order.\n * @param scrollX - The current horizontal scroll offset in pixels.\n * @param viewportWidth - The body's visible width in pixels.\n *\n * @returns The column window: the rendered range plus the widths/lefts it was derived from.\n *\n * @internal\n */\nexport function computeColumnWindow(\n widths : number[],\n scrollX : number,\n viewportWidth: number,\n): ColumnWindow {\n const n = widths.length;\n const lefts = new Array<number>(n);\n let x = 0;\n\n for (let i = 0; i < n; i++) {\n lefts[i] = x;\n x += widths[i];\n }\n\n if (n === 0) {\n return { firstCol: 0, lastCol: -1, widths, lefts };\n }\n\n const viewportRight = scrollX + viewportWidth;\n\n let firstRawVisible = -1;\n let lastRawVisible = -1;\n\n for (let i = 0; i < n; i++) {\n const left = lefts[i];\n const right = left + widths[i];\n\n if (right >= scrollX && left <= viewportRight) {\n if (firstRawVisible === -1) {\n firstRawVisible = i;\n }\n\n lastRawVisible = i;\n }\n }\n\n if (firstRawVisible === -1) {\n // No column's span touches the viewport at all (e.g. scrolled past\n // the content); fall back to rendering nothing beyond the buffer\n // around the nearest edge so a window is still returned.\n firstRawVisible = 0;\n lastRawVisible = 0;\n }\n\n const firstCol = Math.max(0, firstRawVisible - COLUMN_BUFFER);\n const lastCol = Math.min(n - 1, lastRawVisible + COLUMN_BUFFER);\n\n return { firstCol, lastCol, widths, lefts };\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 * Builds a tab/newline-formatted clipboard payload from `rows`, spanning\n * `[startRow, startCol]` to `[endRow, endCol]` inclusive in row-major order.\n * `startOffset`/`endOffset` trim the first/last cell to the selected\n * characters (`null` leaves that cell's text whole); every cell strictly\n * between the two boundaries contributes its full text.\n *\n * @param rows - The copy grid's cell text, row-major.\n * @param startRow - The selection start's row index.\n * @param startCol - The selection start's column index.\n * @param startOffset - Character offset into the start cell's text, or `null` for the whole cell.\n * @param endRow - The selection end's row index.\n * @param endCol - The selection end's column index.\n * @param endOffset - Character offset into the end cell's text, or `null` for the whole cell.\n *\n * @returns The tab-separated, newline-separated clipboard text.\n *\n * @internal\n */\nexport function buildTsv(\n rows: string[][],\n startRow: number, startCol: number, startOffset: number | null,\n endRow: number, endCol: number, endOffset: number | null,\n): string {\n const lines: string[] = [];\n\n for (let r = startRow; r <= endRow; r++) {\n const row = rows[r];\n const colFrom = r === startRow ? startCol : 0;\n const colTo = r === endRow ? endCol : row.length - 1;\n const parts: string[] = [];\n\n for (let c = colFrom; c <= colTo; c++) {\n const isStart = r === startRow && c === startCol;\n const isEnd = r === endRow && c === endCol;\n const text = row[c];\n const from = isStart && startOffset !== null ? startOffset : 0;\n const to = isEnd && endOffset !== null ? endOffset : text.length;\n\n parts.push(escapeTsvField(text.slice(from, to)));\n }\n\n lines.push(parts.join(\"\\t\"));\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction escapeTsvField(value: string): string {\n if (value.includes(\"\\t\") || value.includes(\"\\n\") || value.includes(\"\\\"\")) {\n return \"\\\"\" + value.replace(/\"/g, \"\\\"\\\"\") + \"\\\"\";\n }\n\n return value;\n}\n\n/**\n * Finds the row-major grid position of the rendered cell containing\n * `target`, or `null` when no cell in `grid` contains it.\n *\n * @param grid - The copy grid's cell elements, row-major.\n * @param target - The selection boundary's container handle.\n *\n * @returns The matching `{ row, col }`, or `null`.\n *\n * @internal\n */\nexport function locateCellInGrid(\n grid: Array<Array<{ element: Handle }>>,\n target: Handle,\n): { row: number, col: number } | null {\n for (let r = 0; r < grid.length; r++) {\n for (let c = 0; c < grid[r].length; c++) {\n if (DOM.source.contains(grid[r][c].element, target)) {\n return { row: r, col: c };\n }\n }\n }\n\n return null;\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 _rowVisible : ((record: ModelRecord) => boolean) | null = null;\n private _rowSeparator : ((record: ModelRecord) => { label: string, color: string | null } | null) | null = null;\n private _rowIndented : ((record: ModelRecord) => boolean) | null = null;\n private _cellGeom : CellGeometryCache = new CellGeometryCache();\n private _lastBodyWidth : number = 0;\n private _lastColumnWidths: number[] = [];\n private _lastAriaRowCount: number = -1;\n private _rowHeight : number;\n // Last column window `bindAndPositionRows` applied. Framework-managed\n // bookkeeping (recomputed from `renderWindow` on every pass), so per\n // ARCHITECTURE.md's DOM-write rule this gets no `BodyOptions` field or\n // public setter.\n private _colWindow : ColumnWindow = { firstCol: 0, lastCol: -1, widths: [], lefts: [] };\n // Re-entrancy guard: a commit fired mid-render cascades back into\n // `renderWindow` / `syncPoolCells` through `store.notifyRecordChanged`;\n // this makes the nested call a no-op. See `commitEditsOutsideWindow`.\n private _reconciling : boolean = false;\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(() => this.onThemeReflow());\n }\n\n /**\n * Refreshes the derived row height before the shared re-bind pass, so the\n * rows this reflow re-renders are positioned against the new line box.\n */\n protected onThemeReflow(): void {\n this._rowHeight = this.computeRowHeight();\n\n super.onThemeReflow();\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, windows it to the current column window, and\n * wires each of its cells to the shared editor pool + horizontal\n * scroll-into-view handler. The base's `growRowPool` owns the append +\n * parallel-array bookkeeping.\n *\n * @returns The wired, un-appended pool row.\n */\n protected createPoolRow(): Row {\n const row = this.createRow();\n\n row.setColumnWindow(this._colWindow.firstCol, this._colWindow.lastCol);\n this.wireRowCells(row);\n\n return row;\n }\n\n /**\n * Wires every one of `row`'s cells to the shared editor pool and the\n * horizontal scroll-into-view handler. Called from {@link createPoolRow}\n * for a freshly-pooled row and from {@link bindAndPositionRows} whenever\n * a reconcile changes the rendered cell set — both `setEditorPool` and\n * `setScrollIntoViewHandler` are idempotent for a surviving cell.\n *\n * @param row - The pool row whose cells to wire.\n */\n private wireRowCells(row: Row): void {\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\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), further filtered through {@link setRowVisible}'s\n * predicate when one is active.\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, and does not\n * consult `_rowVisible` — see {@link setRowVisible}. 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 const records = this._store.getRecords();\n\n return this._rowVisible ? records.filter(this._rowVisible) : records;\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.setColumnFields}\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 the per-cell geometry\n * with it, so the next renderWindow re-applies positions and sizes for\n * every visible row and every cell in it.\n */\n protected invalidateGeom(): void {\n super.invalidateGeom();\n\n this._cellGeom.clear();\n }\n\n /**\n * Updates the set of hidden column field names, records the new\n * visible-field list on every pooled row, and 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`. Recording the fields instead\n * builds and removes nothing here: each row reconciles its own cell\n * set on the following render, matching every column that survives\n * by field name so its renderer, editor, theme listener, sort state\n * and group tint are preserved.\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 * Sets the predicate that marks a record as a group-separator row for\n * rotated mode, forwarded from `Table`'s internal view-binding step.\n * Cleared by passing `null`. That step is already followed by\n * `setStore` / `doLayout`, so — unlike {@link setRowVisible} — this\n * setter forces no render of its own.\n *\n * @param predicate - Returns the separator's label/color for a\n * separator record, or `null` for an ordinary field/value record.\n * Called on every rebind; must be O(1) and pure.\n * @returns This body, for method chaining.\n *\n * @remarks Internal wiring called by {@link Table} — not for\n * consumer use.\n */\n setRowSeparator(predicate: ((record: ModelRecord) => { label: string, color: string | null } | null) | null): this {\n this._rowSeparator = predicate;\n\n return this;\n }\n\n /**\n * Sets the predicate that marks a record as a rotated-mode group\n * member, forwarded from `Table`'s internal view-binding step. Cleared\n * by passing `null`. Mirrors {@link setRowSeparator}'s shape and, like\n * it, forces no render of its own.\n *\n * @param predicate - Returns `true` when the record's `field`-name cell\n * should render indented (see {@link Row.setFieldIndent}). Called on\n * every rebind; must be O(1) and pure.\n * @returns This body, for method chaining.\n *\n * @remarks Internal wiring called by {@link Table} — not for\n * consumer use.\n */\n setRowIndented(predicate: ((record: ModelRecord) => boolean) | null): this {\n this._rowIndented = predicate;\n\n return this;\n }\n\n /**\n * Sets a live predicate that hides non-matching rows without touching\n * the store — the display-only filter behind {@link Table.setRowVisible}.\n * Cleared by passing `null`.\n *\n * @param predicate - Returns `true` to keep the record's row rendered.\n * Called for every loaded record on every render pass; must be O(1)\n * and pure, the same contract {@link ColumnSpec.rowReadOnly} follows.\n * @returns This body, for method chaining.\n *\n * @remarks Internal wiring called by {@link Table} — not for consumer\n * use; consumers call {@link Table.setRowVisible}. Unlike\n * {@link setRowReadOnly}, changing the predicate can shrink or grow the\n * visible-records list itself, so a pool slot's cached data index can no\n * longer be trusted to still name the same record — invalidating the\n * bindings and forcing a render is required, not optional. Has no\n * effect on `TreeBody`, whose depth-flattened visible-records override\n * never consults `_rowVisible` — see the `TreeTable` docs non-goal.\n */\n setRowVisible(predicate: ((record: ModelRecord) => boolean) | null): this {\n this._rowVisible = predicate;\n this.invalidateRowBindings();\n this.renderWindow();\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 * Commits every open edit (the whole rendered cell set is about to be\n * discarded, so `keep` is `null`), then walks every pool row and records\n * the new `_hiddenColumns` + `_columnConfigs` it should render from.\n * Builds no cells itself — marking a row's column fields dirty makes its\n * next {@link Row.setColumnWindow} (from {@link bindAndPositionRows}, on\n * the next `renderWindow`) reconcile the cell set against the new column\n * list, exactly as it does for a scroll-driven window slide. Also\n * invalidates the per-slot geometry caches so that render re-positions\n * cells against the new column count.\n *\n * Guarded by `_reconciling`, mirroring `renderWindow`: the commit pass\n * can cascade back into this method through `store.notifyRecordChanged`.\n */\n private syncPoolCells(): void {\n if (this._reconciling) {\n return;\n }\n\n this._reconciling = true;\n\n try {\n this.commitEditsOutsideWindow(null);\n\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.setColumnFields(\n this._store.model,\n this._hiddenColumns,\n this._columnConfigs,\n treeFieldName,\n );\n\n this.wireRowCells(row);\n\n // The row's column count changed, so its own width may have.\n // The cells' records are keyed on the cell, so they stay valid\n // across the re-point `setColumnWindow` performs next — a cell\n // moved onto a column at the same x and width genuinely needs\n // no reposition. What it may need is a layout, when the new\n // column changes something the layout fits around; the writes\n // that do that lay the cell out themselves.\n this._rowGeom[i] = null;\n }\n } finally {\n this._reconciling = false;\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 // A native `copy` targets wherever the browser's selection actually\n // starts (a cell's own element or its Text descendant), not Body's\n // own element — an exact-target listener here would never fire, the\n // same reason clicks above are routed as a subtree listener.\n Event.addSubtreeListener(this, \"copy\", this.onCopy);\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 // A pass the startup font gate deferred rendered no rows, so the\n // mirrors these events drive — the header translate and the pinned-side\n // body — would be moved to an offset nothing was laid out at.\n if (this.wasRenderDeferred()) {\n return;\n }\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 * Disposes the shared cell-editor pool, then runs the inherited teardown\n * (which disposes the row pool and the scroller — see\n * VirtualRowView.destructor()). `_editorPool`'s cached editors are held in\n * a private Map, never a registered child of this body, so the base\n * destructor's recursion cannot reach them.\n */\n protected destructor(): void {\n this._editorPool.dispose();\n\n super.destructor();\n }\n\n /**\n * Runs a render pass the startup font gate deferred, once a layout reaches\n * this body again.\n *\n * @returns This body, for method chaining.\n *\n * @remarks Unlike `Tree`, this body does not render its window on every\n * layout — its passes come from the parent table layout and from store\n * events. The parent layout usually gets there first, calling\n * `renderWindow` directly with the column widths, which resumes the pass on\n * its own; this override covers the case where it does not, so a body whose\n * table is already clean still picks its deferred pass back up.\n */\n doLayout(): this {\n super.doLayout();\n this.renderWindowIfDeferred();\n\n return this;\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 // Cache the caller's widths before anything can return early. They come\n // from the parent layout and from nowhere else, so a pass the startup\n // font gate defers below would otherwise replay against the zero-width\n // cache this view starts with. This also means an unmounted body now\n // records widths (and may invalidate its row geometry) where it used to\n // return untouched — harmless, since its pool is empty.\n this.updateColumnWidthCache(bodyWidth, columnWidths);\n\n // A commit fired mid-render (see `commitEditsOutsideWindow`) cascades\n // back into this method through `store.notifyRecordChanged`; the\n // nested call is dropped rather than queued, both guarded methods\n // commit before they read the state they render from, so the next\n // (outer) pass sees the up-to-date result. Above the two early\n // returns below so a nested call still caches any widths it carries\n // (`updateColumnWidthCache` already ran) — only the render itself is\n // dropped.\n if (this._reconciling) {\n return;\n }\n\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n\n if (this.deferRenderWhileFirstLayoutHeld()) {\n return;\n }\n\n this._reconciling = true;\n\n try {\n this.renderWindowPass();\n } finally {\n this._reconciling = false;\n }\n\n // Applies any scroll offset the startup font gate held back. Unlike\n // `Tree` there is no post-render work to redo alongside it: every caller\n // that refreshes this body's active descendant is a user gesture —\n // focus, click, key — and none of those can land inside the startup\n // hold, before a single row exists. Outside the guard above: applying a\n // held offset re-enters `renderWindow` through the scroller's onScroll\n // hook, and that nested pass has to run.\n this.finishResumedRender();\n }\n\n /**\n * The body of `renderWindow`, below its early returns — recomputes the\n * row AND column windows, rebinds changed rows from the pool, and hides\n * excess rows. Split out so the public `renderWindow` can wrap it in the\n * `_reconciling` re-entrancy guard.\n */\n private renderWindowPass(): void {\n const scroller = this._scroller!;\n let records = this.getVisibleRecords();\n let 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 // Loose-clamp scroll positions against the new content sizes before\n // reading them for the window calc.\n let 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 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 const effectiveWidths = Array.from({ length: fieldCount }, (_, i) => this._lastColumnWidths[i] ?? fallback);\n\n this._colWindow = computeColumnWindow(effectiveWidths, scroller.getScrollX(), this.getWidth() || 0);\n\n // An edit whose column is about to leave the window is committed\n // before any pool row is rebound — mirrors the precedent\n // `Row.setColumnWindow` sets for a column-set change: commit before\n // discarding the cell that holds it. A commit can change what a\n // filtered/sorted store returns, so the row count is re-read.\n if (this.commitEditsOutsideWindow(this._colWindow)) {\n records = this.getVisibleRecords();\n totalRows = records.length;\n totalHeight = totalRows * this._rowHeight;\n }\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 this.bindAndPositionRows(win.firstRow, win.windowSize, rowWidth, records, this._colWindow);\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 * Commits every open edit whose rendered column falls outside `keep`,\n * ahead of a column-window change that is about to discard the cell\n * holding it — mirrors the precedent {@link Row.setColumnWindow} sets on\n * a column-set change: commit before discarding. Guarded by the same\n * `_reconciling` flag `renderWindow` sets: `Cell.commitEdit` emits\n * `\"commit\"` while the cell still reports `isEditing()`, and that emit\n * cascades through `store.notifyRecordChanged` back into `renderWindow`.\n *\n * @param keep - The column window to preserve; every rendered slot\n * outside it is a candidate. `null` treats every rendered slot as\n * outside — used by {@link syncPoolCells}, which is about to discard\n * the whole cell set for a hide/show or config swap.\n *\n * @returns `true` when at least one open edit was committed.\n */\n private commitEditsOutsideWindow(keep: ColumnWindow | null): boolean {\n let committed = false;\n\n for (const row of this._rowPool) {\n const cells = row.getComponents() as Cell<any>[];\n const start = row.getColumnWindowStart();\n\n for (let slot = 0; slot < cells.length; slot++) {\n const colIndex = start + slot;\n const outside = keep === null || colIndex < keep.firstCol || colIndex > keep.lastCol;\n\n if (outside && cells[slot].isEditing()) {\n cells[slot].commitEdit();\n committed = true;\n }\n }\n }\n\n return committed;\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 records - The current store records (passed in so this helper doesn't re-query).\n * @param columns - The column window: the rendered `[firstCol, lastCol]`\n * range plus the widths/lefts every rendered cell is placed from.\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, records: ModelRecord[], columns: ColumnWindow): void {\n const rowHeight = this._rowHeight;\n\n this.alignPoolWindow(firstRow);\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const separator = this._rowSeparator?.(records[dataIndex]) ?? null;\n\n if (separator) {\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound || !row.isSeparator()) {\n row.renderSeparator(separator.label, separator.color);\n this._boundIndices[i] = dataIndex;\n this.computeRowAria(row, dataIndex);\n }\n\n this.positionRow(i, dataIndex * rowHeight, rowWidth);\n this._cellGeom.apply(row.getComponents()[0], 0, rowWidth, rowHeight);\n\n continue;\n }\n\n const windowChanged = row.setColumnWindow(columns.firstCol, columns.lastCol);\n\n if (windowChanged) {\n // Slot → column mapping changed, so newly-entered cells need\n // the editor pool + scroll-into-view handler wired. The\n // geometry records survive: they are keyed on the cell, so a\n // cell that kept its column is still correctly recorded even\n // though its slot moved.\n this.wireRowCells(row);\n }\n\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 }\n\n if (wasRebound || windowChanged) {\n this.applyReadOnlyState(row, records[dataIndex]);\n row.setFieldIndent(this._rowIndented?.(records[dataIndex]) ?? false);\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 let x = columns.lefts[columns.firstCol] ?? 0;\n\n for (let slot = 0; slot < cells.length; slot++) {\n const colW = columns.widths[columns.firstCol + slot] ?? 0;\n\n this._cellGeom.apply(cells[slot], x, colW, rowHeight);\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 if (row.isSeparator()) {\n return;\n }\n\n const record = row.getData() ?? null;\n if (!record) return;\n\n const records = this.getVisibleRecords();\n const before = new Set(this._selectedRecords);\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(before);\n\n // Determine which column was clicked and update focused cell.\n // `resolveClickedColumn` scans the rendered cells and so returns a\n // slot, not a column index — convert by adding the window start so\n // `_focusedColIndex` and the \"cellclick\" payload stay in\n // visible-column order regardless of what the row currently renders.\n const targetHandle = e.target === null ? null : DOM.source.intern(e.target);\n const cells = row.getComponents();\n const slot = resolveClickedColumn(cells, targetHandle);\n const columnIndex = slot >= 0 ? slot + row.getColumnWindowStart() : -1;\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()[slot];\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 * Handles the native `copy` event, replacing the browser's default raw\n * concatenated-text payload with a tab/newline-formatted grid built from\n * the current selection.\n *\n * @param e - The native clipboard event.\n *\n * @returns `{ prevent: true }` when a payload was written, so the\n * dispatcher suppresses the browser's default copy; `undefined`\n * (leaving the default behaviour intact) when there is nothing to copy.\n */\n private onCopy(e: ClipboardEvent): Event.ListenerResult {\n const text = this.buildSelectionText();\n\n if (text === null) {\n return;\n }\n\n e.clipboardData?.setData(\"text/plain\", text);\n\n return { prevent: true };\n }\n\n /**\n * Resolves the current browser selection against the rendered cell grid\n * and formats it as tab/newline-separated text.\n *\n * @returns The formatted clipboard text, or `null` when there is no\n * selection or it does not resolve to any rendered cell.\n */\n private buildSelectionText(): string | null {\n const range = DOM.source.getDocumentSelection();\n if (!range) {\n return null;\n }\n\n const grid = this.renderedCellGrid();\n let start = locateCellInGrid(grid, range.startContainer);\n let end = locateCellInGrid(grid, range.endContainer);\n\n if (!start || !end) {\n return null;\n }\n\n let startOffset = range.startOffset;\n let endOffset = range.endOffset;\n\n // A pool row's element is appended to the DOM once, in creation\n // order, and never physically reordered afterwards — recycling\n // changes which data index a slot displays (see renderedCellGrid's\n // own remarks) but not the element's position in the DOM tree. The\n // browser normalises `startContainer`/`endContainer` to that fixed\n // DOM position, which is unrelated to the row-major order this\n // grid is already built in, so the \"start\" the browser reports can\n // resolve to a later grid position than \"end\"; swap so the walk\n // below always proceeds forward through the grid's row-major order.\n if (start.row > end.row || (start.row === end.row && start.col > end.col)) {\n [start, end] = [end, start];\n [startOffset, endOffset] = [endOffset, startOffset];\n }\n\n return buildTsv(\n grid.map(row => row.map(cell => cell.text)),\n start.row, start.col, startOffset,\n end.row, end.col, endOffset,\n );\n }\n\n /**\n * The copy grid: every currently-displayed, non-separator pool row's\n * cells, in row-major order.\n *\n * @remarks `bindAndPositionRows` always sets `_boundIndices[i]` to\n * `firstRow + i` for a displayed slot, so iterating `_rowPool` by array\n * index already yields row-major (data-index) order — no sort is\n * needed here. What *isn't* row-major is the DOM: a pool row's element\n * is appended once, in creation order, and never physically reordered\n * as the pool recycles, so a native `Range`'s start/end containers\n * (which the browser orders by DOM position) can disagree with this\n * grid's order — `buildSelectionText`'s swap guard is what corrects\n * for that, not this method. A row's own cells need no such\n * correction either way: `Row.setColumnWindow`'s `sortComponents` call\n * keeps `getComponents()` in visible-column order already (the same\n * array `resolveClickedColumn` relies on).\n *\n * @returns The rendered cell grid, row-major.\n */\n private renderedCellGrid(): Array<Array<{ text: string, element: Handle }>> {\n const grid: Array<Array<{ text: string, element: Handle }>> = [];\n\n for (let i = 0; i < this._rowPool.length; i++) {\n if (!this._rowDisplayed[i]) {\n continue;\n }\n\n const row = this._rowPool[i];\n if (row.isSeparator()) {\n continue;\n }\n\n const cells = row.getComponents() as Cell<any>[];\n\n grid.push(cells.map(cell => ({\n text: cell.getRenderer().getDisplayText(),\n element: cell.getElement()!,\n })));\n }\n\n return grid;\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 const before = new Set(this._selectedRecords);\n\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(before);\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 const before = new Set(this._selectedRecords);\n\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(before);\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 when the selected set changes; `\"cellclick\"`\n * fires when a data cell is clicked, carrying the clicked record, the\n * column's field name and visible index, the cell value, the record's\n * row index in the visible-records view, and 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 /**\n * Fires `\"selection\"` with the current selection, unless it has the same\n * membership as `before` — in which case the gesture that ran did not\n * actually change what was selected, and the event is skipped.\n *\n * @param before - The selection as it stood immediately before the\n * mutating gesture ran.\n */\n private notifySelectionChange(before: ReadonlySet<ModelRecord>): void {\n if (selectionsEqual(before, this._selectedRecords)) {\n return;\n }\n\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 once per row whenever that row\n * rebinds *or* its column window changes — the latter so a\n * `readOnly` column scrolling into view arrives read-only without\n * waiting for a rebind.\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 a rebind or a window change) 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 this._header?.setFocusedColumn(this._anchorRecord ? this._focusedColIndex : null);\n\n if (!this._anchorRecord) {\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 return;\n }\n\n const row = this._rowPool[poolSlotIdx];\n const cells = row.getComponents();\n const slot = this._focusedColIndex - row.getColumnWindowStart();\n const cell = (slot >= 0 && slot < cells.length) ? cells[slot] : undefined;\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 row = this._rowPool[poolSlotIdx];\n const cells = row.getComponents();\n const slot = this._focusedColIndex - row.getColumnWindowStart();\n const cell = (slot >= 0 && slot < cells.length) ? cells[slot] : undefined;\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 * Steps `index` in `direction` past every separator record (per\n * {@link setRowSeparator}'s predicate), returning the nearest real-row\n * index. Only reachable when a separator predicate is active.\n *\n * A separator is always immediately followed by at least one real row\n * (a group run is never empty) and the projection's last row can never\n * be a separator, so a forward (`+1`) search always terminates without\n * needing the fallback below. Only a backward (`-1`) search can run off\n * the array — when a group sits at the very first row — in which case\n * the fallback retries forward from the original index so the anchor\n * still lands on a real row instead of the clamp re-selecting the\n * separator it just walked off.\n *\n * @param records - The current visible-records list.\n * @param index - The candidate index to start from.\n * @param direction - The direction the caller's key already moves:\n * `1` for ArrowDown/PageDown/Home, `-1` for ArrowUp/PageUp/End.\n *\n * @returns The nearest index in `records` that is not a separator.\n */\n private skipSeparators(records: ModelRecord[], index: number, direction: 1 | -1): number {\n let i = index;\n\n while (i >= 0 && i < records.length && this._rowSeparator?.(records[i])) {\n i += direction;\n }\n\n if (i < 0 || i >= records.length) {\n i = index;\n\n while (i < records.length && this._rowSeparator?.(records[i])) {\n i++;\n }\n }\n\n return Math.max(0, Math.min(i, records.length - 1));\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): Event.ListenerResult {\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 // 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 // Bring the newly-focused column into the rendered window before\n // refreshing the focus ring / active descendant against it.\n this.scrollColumnIntoView(this._focusedColIndex);\n this.renderWindow();\n\n this._updateActiveDescendant();\n this._updateFocusStyle();\n\n return { prevent: true };\n }\n\n // Enter/Space — start editing the focused cell\n if (e.key === 'Enter' || e.key === ' ') {\n if (!this._anchorRecord) {\n return { prevent: true };\n }\n\n // Same as the ArrowLeft/ArrowRight branch: make sure the focused\n // column is in the rendered window before resolving its slot.\n this.scrollColumnIntoView(this._focusedColIndex);\n this.renderWindow();\n\n const anchorIdx = records.indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n return { prevent: true };\n }\n\n const row = this._rowPool[poolSlotIdx];\n const cells = row.getComponents();\n const slot = this._focusedColIndex - row.getColumnWindowStart();\n const cell = (slot >= 0 && slot < cells.length) ? cells[slot] : undefined;\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 { prevent: true };\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 let direction: 1 | -1;\n\n if (e.key === 'ArrowDown') {\n newIdx = currentIdx < 0 ? 0 : Math.min(currentIdx + 1, records.length - 1);\n direction = 1;\n } else if (e.key === 'ArrowUp') {\n newIdx = currentIdx < 0 ? 0 : Math.max(currentIdx - 1, 0);\n direction = -1;\n } else if (e.key === 'PageDown') {\n newIdx = currentIdx < 0 ? 0 : Math.min(currentIdx + pageSize, records.length - 1);\n direction = 1;\n } else if (e.key === 'PageUp') {\n newIdx = currentIdx < 0 ? 0 : Math.max(currentIdx - pageSize, 0);\n direction = -1;\n } else if (e.key === 'Home') {\n newIdx = 0;\n direction = 1;\n } else {\n newIdx = records.length - 1;\n direction = -1;\n }\n\n if (this._rowSeparator) {\n newIdx = this.skipSeparators(records, newIdx, direction);\n }\n\n const newAnchor = records[newIdx];\n\n this.selectRecord(newAnchor);\n this.scrollRecordIntoView(newAnchor);\n this.renderWindow();\n this._updateActiveDescendant();\n\n return { prevent: true };\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 { DOM } 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 { FilterCell } from \"~/component/table/cell/Filter.js\";\nimport { computeColumnWindow } from \"~/component/table/Body.js\";\nimport { CellGeometryCache } from \"~/component/table/CellGeometry.js\";\nimport type { ColumnWindow } from \"~/component/table/Body.js\";\nimport { columnFilterOperators, buildColumnFilter, columnFilterStatesEqual, columnFilterTakesNumericOperand } from \"~/component/table/ColumnFilter.js\";\nimport type { ColumnFilterState, ColumnFilterTarget } from \"~/component/table/ColumnFilter.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { CellTextResolver } from \"~/component/table/cell/CellText.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { ellipsis_v } from \"~/glyphs/solid/ellipsis_v.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { TRACK_WIDTH } from \"~/component/container/Scrollbar.js\";\n\n// Register the column-menu button's glyph eagerly at module load — same\n// pattern as ToolBar registering its overflow chevron — so the button always\n// resolves its glyph without the consumer pre-registering it.\nGlyph.register(ellipsis_v);\n\n/** Glyph the column-menu button renders — matches `ToolBar`'s overflow trigger. */\nconst MENU_BUTTON_GLYPH = \"ellipsis-v\";\n\n/** Accessible name / tooltip for the column-menu button. */\nconst MENU_BUTTON_LABEL = \"Column options\";\n\n// A flat, compact, glyph-only `Button` reserves `glyph + MENU_BUTTON_CHROME_PX`\n// per axis around the glyph — 2px of compact insets on each side. The\n// button's own border is cleared in the constructor (`clearBorder()` and its\n// hover/pressed twins), so it contributes no width here. The button fills\n// the vertical-scrollbar reservation band exactly (see the constructor,\n// which pins the glyph to `TRACK_WIDTH - MENU_BUTTON_CHROME_PX`), so this is\n// the fixed per-side overhead subtracted from that fixed track width.\nconst MENU_BUTTON_CHROME_PX = 4;\n\n// Needs to beat the header's inner rows, which are Components at `z-index:\n// 0` (an implicit stacking context) — a plain sibling with `z-index: auto`\n// paints BENEATH them.\nconst MENU_BUTTON_Z_INDEX = 1;\n\n/**\n * Debounce (ms) between a filter-cell keystroke and the store write it\n * schedules. Matches `AutoCompleteField`'s default keystroke debounce.\n * Picking an operator, pressing Enter, or pressing Escape bypass this and\n * apply immediately.\n */\nconst COLUMN_FILTER_DEBOUNCE_MS = 200;\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 geometry the table layout supplies to the header on each pass. Cached\n * by {@link TableHeader.renderColumnWindow} so a scroll-driven pass can\n * re-run with no argument.\n *\n * @category Components\n */\nexport interface HeaderColumnGeometry {\n /** Width per visible column, in display order. May be shorter than the column list. */\n columnWidths : number[];\n /** The width the columns are windowed against — the table's available column width. */\n viewportWidth : number;\n /** Height of the column-header row, in pixels. */\n columnHeight : number;\n /** Height of the parent-header row, in pixels; `0` when it is collapsed. */\n parentRowHeight: number;\n /** Height of the filter row, in pixels; `0` when it is collapsed. */\n filterRowHeight: number;\n}\n\n/**\n * The header section of a table, rendered as a `<thead>` element.\n *\n * Builds one {@link HeaderCell} per column in its current column window —\n * the horizontally-visible range plus a small buffer, mirroring the body's\n * own column virtualization — rather than one per visible field up front.\n * Each cell is wired with a sort-click callback (cycles asc → desc → clear), a\n * resize-drag callback (forwarded to the owner via the `\"columnresize\"`\n * event), and a context-menu callback (forwarded via the\n * `\"columncontextmenu\"` event); see {@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 _menuButton: Button;\n private _boundOnMenuButtonAction: () => void = () => this.onMenuButtonAction();\n\n // Non-hidden fields, in display order — the full column list the\n // rendered window is carved out of. Populated by `rebuildCells`.\n private _visibleFields: Field[] = [];\n // Visible-column index of slot 0. The rendered columns are always a\n // contiguous run, so slot `s` holds column `_windowFirst + s`.\n private _windowFirst : number = 0;\n private _scrollX : number = 0;\n private _focusedCol : number | null = null;\n // Set by `rebuildCells` so the next `renderColumnWindow` reconciles even\n // when the requested range happens to match the current one — a\n // column-set change can leave the range unchanged while the cells\n // behind it need to change.\n private _columnsDirty : boolean = true;\n private _geometry : HeaderColumnGeometry = { columnWidths: [], viewportWidth: 0, columnHeight: 0, parentRowHeight: 0, filterRowHeight: 0 };\n // Geometry last written to each header cell, shared with the body's rows;\n // see `CellGeometryCache` for the invariant it rests on.\n private _cellGeom : CellGeometryCache = new CellGeometryCache();\n\n // Filter-row state. `_filterStates` is keyed per store (rather than held\n // flat) so a round trip through rotated mode — which re-points this\n // header at the projection store and back — restores the source store's\n // filter text unchanged instead of showing one store's text over\n // another's columns. See `filterState()`.\n private _filterRowVisible : boolean = false;\n private _filterStates : WeakMap<AbstractStore, Map<string, ColumnFilterState>> = new WeakMap();\n private _filterCellsDirty : boolean = true;\n private _filterWindowFirst : number = 0;\n // The field whose write is still pending the debounce timer, or that a\n // caller's `immediate` request is about to flush. Lives here rather than\n // on the cell because a horizontal scroll can recycle the cell onto a\n // different column while the write is still in flight.\n private _pendingFilterField: string | null = null;\n private _filterTimer : ReturnType<typeof setTimeout> | null = null;\n private _boundOnStoreFilterChange: () => void = () => this.onStoreFilterChange();\n\n // Per-field column config, supplying `values` / `showSeconds` to\n // `filterTarget`. Kept alongside a pooled resolver so a combo/temporal\n // filter build can resolve display text without mounting a renderer.\n private _columnConfigs: Map<string, ColumnConfig> = new Map();\n private _cellText : CellTextResolver = new CellTextResolver();\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 this._store.on('filterchange', this._boundOnStoreFilterChange);\n\n // Three `Row` children — parent row at index 0, column row at index 1,\n // filter row at index 2. The parent row collapses to zero height when\n // no visible column declares a group; existing no-group tables remain\n // byte-identical at runtime because `rebuildParentCells` produces no\n // cells in that case and the layout manager zeroes the parent-row\n // height. The filter row collapses the same way — via `hasFilterRow`\n // — until a caller opts in through `setFilterRowVisible`.\n const parentRow = new Row();\n const row = new Row();\n const filterRow = new Row();\n this.addRow(parentRow);\n this.addRow(row);\n this.addRow(filterRow);\n\n // A plain, non-`Row` child — appended via `super.addComponent` because\n // this class's own `addComponent` is narrowed to `Row` — and appended\n // last so the fixed indices `getParentRow()` (0), `getColumns()` (1),\n // and `getFilterRow()` (2) keep resolving to the three rows above.\n //\n // This button fully replaces what used to be a separate, non-\n // interactive scrollbar-reservation cover: it carries the header's\n // own background/gradient and a left divider matching the column-cell\n // border, so cells translated horizontally still appear to clip at\n // the reserved band's boundary. Its glyph is pinned to exactly fill\n // that band — `TRACK_WIDTH` is a fixed compile-time constant, so this\n // costs nothing at runtime.\n const glyphPx = Math.max(1, TRACK_WIDTH - MENU_BUTTON_CHROME_PX);\n\n this._menuButton = new Button({\n glyph: MENU_BUTTON_GLYPH,\n text: MENU_BUTTON_LABEL,\n showText: false,\n flat: true,\n compact: true,\n zIndex: MENU_BUTTON_Z_INDEX,\n listeners: { action: this._boundOnMenuButtonAction },\n });\n this._menuButton.pinGlyphSize(glyphPx);\n this._menuButton.setBackgroundColor(TABLE_HEADER_BG);\n this._menuButton.setBackgroundImage(TABLE_HEADER_BG);\n // Flat chrome's hover/pressed treatment (installed by the `super()`\n // cascade above) overrides `background-color` with a translucent\n // black tint, meant to composite against an ambient page background\n // showing through a transparent resting state. This button's resting\n // background is opaque instead (the masking colour above), so swap in\n // the *non-flat* hover/pressed tokens for the usual darker-on-press\n // look. Depending on the theme, those tokens can resolve to either a\n // plain colour or a gradient — the same ambiguity `TABLE_HEADER_BG`\n // has above — so, as above, write each token to both\n // `background-color` and `background-image`: whichever is invalid\n // for the resolved value drops out silently, leaving exactly one\n // opaque paint layer instead of the transparent gap a colour-only\n // write leaves when the token turns out to be a gradient.\n const hoverBg = \"var(--ts-ui-button-hover-bg, rgb(252, 252, 252))\";\n const pressedBg = \"var(--ts-ui-button-pressed-bg, rgb(200, 200, 200))\";\n\n this._menuButton.setHoverBackgroundColor(hoverBg);\n this._menuButton.setHoverBackgroundImage(hoverBg);\n this._menuButton.setPressedBackgroundColor(pressedBg);\n this._menuButton.setPressedBackgroundImage(pressedBg);\n // Flat chrome (installed by the `super()` cascade above) reserves a\n // 1px transparent border on every side, at rest and on hover/pressed,\n // so its own border-colour swap doesn't nudge the glyph. An `inset`\n // box-shadow always clips to the padding edge — *inside* the border —\n // regardless of the border's colour, so that reservation would leave\n // the divider below 1px short on every edge it touches (top, bottom,\n // and the left edge it's meant to sit flush against). The hover/\n // pressed background swap above already carries the interaction\n // feedback flat chrome's border swap was for, so clear the border in\n // every state rather than work around the clip: with no border\n // reserved, the padding edge coincides with the button's true edges\n // and the shadow below reaches them exactly.\n this._menuButton.clearBorder();\n this._menuButton.clearHoverBorder();\n this._menuButton.clearPressedBorder();\n // `ParentHeaderCell` uses the same inset-shadow technique for its own\n // dividers.\n this._menuButton.setShadow(\"inset 1px 0 0 0 var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\");\n this._menuButton.getAria().setHasPopup(\"menu\");\n super.addComponent(this._menuButton);\n\n this.rebuildCells();\n this.rebuildParentCells();\n\n // Drops the records so the next layout pass re-fits every cell against\n // the new theme; see `CellGeometryCache` for why a theme change needs\n // that and geometry alone cannot detect it.\n //\n // Re-rendering from inside this callback, as\n // `VirtualRowView.onThemeReflow` does for the body, would run too\n // early: each cell renderer holds its own theme subscription that\n // rewrites the insets the pass has to fit against, and those renderers\n // subscribe after this header does, so the re-render would fit against\n // the outgoing theme's padding.\n this.subscribeTheme(() => this._cellGeom.clear());\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 * Swaps the store whose sort state this header drives and displays.\n *\n * @param store - The new store to bind to this header.\n *\n * @remarks Internal wiring called by the owning {@link Table} when its\n * bound store or display mode changes. Not for consumer use.\n */\n setStore(store: AbstractStore): this {\n this._store.off('filterchange', this._boundOnStoreFilterChange);\n this._store = store;\n this._store.on('filterchange', this._boundOnStoreFilterChange);\n\n return this;\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, rebuilding the parent row\n * immediately and marking the column row's cells for reconciliation.\n *\n * The column row renders no cells until the next {@link renderColumnWindow},\n * which every caller reaches through the table's own layout pass.\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 * Supplies the per-field column-config map, consulted internally when\n * building a column's filter descriptor. Mirrors\n * [`Body.setColumnConfigs`](/api/component/table/classes/Body#setcolumnconfigs).\n *\n * @param configs - The new column-config map keyed by field name.\n * @returns This header, for method chaining.\n */\n setColumnConfigs(configs: Map<string, ColumnConfig>): this {\n this._columnConfigs = configs;\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, or on an activation\n * of {@link getMenuButton}'s column-menu button, receiving the field name\n * (empty string when the click landed on a parent-header cell or on the\n * menu button) and 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 rendered header cell components, in slot order. The column row\n * lives at child index 1 — the parent-header row sits at index 0.\n *\n * A slot maps to a visible-column index by adding\n * {@link getColumnWindowStart}: only the horizontally-visible column\n * window (plus a small buffer) is rendered, so this is no longer every\n * visible column.\n *\n * @returns An array of the rendered cell components from the column-header row.\n */\n getColumns() {\n return this.getComponents()[1].getComponents();\n }\n\n /**\n * Returns the visible-column index of the first rendered header cell.\n *\n * @returns The visible-column index slot `0` currently renders.\n */\n getColumnWindowStart(): number {\n return this._windowFirst;\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 filter row hosting one {@link FilterCell} per\n * horizontally-visible column. Always present, even when collapsed — in\n * that case the row has zero cells and the layout manager collapses it\n * to zero height.\n *\n * @returns The filter row.\n */\n getFilterRow(): Row {\n return this.getComponents()[2] as Row;\n }\n\n /**\n * Returns `true` when the table-level filter-row toggle\n * ({@link setFilterRowVisible}) is on **and** at least one visible\n * column is filterable — i.e. the filter row is rendered with non-zero\n * height. Unlike {@link hasParentRow}, a filterable column alone is not\n * enough: the toggle must also be on, so a table with `filterable`\n * columns renders exactly as it does today until a caller opts in.\n *\n * @returns `true` when the filter row should be laid out, `false` when\n * it should collapse.\n */\n hasFilterRow(): boolean {\n return this._filterRowVisible\n && this._columns\n .filter(c => !this._hiddenColumns.has(c.getField().getName()))\n .some(c => c.isFilterable());\n }\n\n /**\n * Shows or hides the filter row. Pushed down from\n * {@link Table.setFilterRowVisible}; idempotent when `visible` already\n * matches the current state. Hiding also clears every filter the row\n * itself applied, so a hidden row never leaves an invisible predicate\n * narrowing the view.\n *\n * @param visible - `true` to show the filter row, `false` to hide it.\n * @returns This header, for method chaining.\n */\n setFilterRowVisible(visible: boolean): this {\n if (visible === this._filterRowVisible) {\n return this;\n }\n\n this._filterRowVisible = visible;\n this._filterCellsDirty = true;\n\n if (!visible) {\n this.clearFilterRowState();\n }\n\n return this;\n }\n\n /**\n * Cancels any in-flight debounced write and removes every descriptor the\n * filter row itself applied to the store, discarding the row's cached\n * clause-list state along with it. Called when the row is hidden, so a\n * column's filter never keeps narrowing the view once there is no\n * longer any control showing — or letting the user change — its\n * criteria.\n *\n * Clears every column in a single {@link AbstractStore.setFilters} call\n * rather than looping {@link AbstractStore.setFilter} once per column —\n * a consuming store with `pageSize` set (or `remoteFilter` enabled)\n * reloads once per `setFilter` call, so an N-column loop would fire N\n * sequential reloads for what is one user action.\n */\n private clearFilterRowState(): void {\n if (this._filterTimer !== null) {\n clearTimeout(this._filterTimer);\n this._filterTimer = null;\n }\n\n this._pendingFilterField = null;\n\n const states = this.filterState();\n const entries: Array<[string, null]> = [];\n\n for (const fieldName of [...states.keys()]) {\n states.delete(fieldName);\n\n if (this._store.getFilter(fieldName) !== null) {\n entries.push([fieldName, null]);\n }\n }\n\n if (entries.length > 0) {\n void this._store.setFilters(entries);\n }\n }\n\n /**\n * Returns the button that opens the column context menu — the same menu\n * a right-click on a header cell opens. Sits in the vertical-scrollbar\n * reservation band at the header's right edge; positioned by the table\n * layout.\n *\n * @returns The column-menu button.\n */\n getMenuButton(): Button {\n return this._menuButton;\n }\n\n /**\n * Handles a click (or keyboard activation) of the column-menu button:\n * emits `\"columncontextmenu\"` with an empty field name — matching a\n * right-click on a parent-header cell — anchored to the button's own\n * viewport rect rather than the click coordinates. A keyboard-activated\n * click reports `clientX`/`clientY` of `0`, which would open the menu in\n * the viewport's top-left corner; the button's own rect is correct for\n * both mouse and keyboard activation.\n */\n private onMenuButtonAction(): void {\n const rect = DOM.source.getViewportRect(this._menuButton);\n\n this.emit(\"columncontextmenu\", \"\", rect.left, rect.bottom);\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 height. Row-level widths, heights, and positions\n * are set independently by the table layout manager — `TableHeader` itself\n * just stores the total band height. The per-row assignments\n * live in `layout/Table.doLayout` because the split depends on\n * {@link hasParentRow} and on the header's own content box.\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 * Recomputes the visible-field list from the current model and hidden\n * set, marks the rendered cell set dirty, and re-syncs the sort\n * indicators. Builds no cells itself: the column row renders nothing\n * until the next {@link renderColumnWindow}, which reconciles against\n * the window the table layout hands it — mirroring `Body`, which\n * renders no rows until its first `renderWindow`.\n */\n private rebuildCells(): void {\n this._visibleFields = this.computeVisibleFields(this._model);\n this._columnsDirty = true;\n this._filterCellsDirty = true;\n\n this.syncSortIndicators();\n }\n\n /**\n * Filters and orders `model`'s fields to the currently-visible set:\n * every field not in `_hiddenColumns`, sorted by {@link Field.getOrder}.\n * Called by {@link rebuildCells} to populate `_visibleFields`, the\n * single derivation `syncSortIndicators` and {@link reconcileColumnCells}\n * both read from instead of re-deriving it themselves.\n *\n * @param model - The model whose fields to filter and order.\n * @returns The visible fields, in display order.\n */\n private computeVisibleFields(model: AbstractModel): Field[] {\n return model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n }\n\n /**\n * Reconciles the column-row's rendered header cells to the\n * horizontally-visible column range `[firstCol, lastCol]`. A header\n * cell carries no per-kind identity — unlike the body's cell\n * reconciler — so any leftover cell can serve any entering column:\n *\n * 1. A column keeps the cell that already holds its field, matched by\n * field name.\n * 2. Every other column in the range recycles a leftover cell, or\n * builds a fresh one when none remains.\n * 3. Every per-column property (label, tooltip, glyph, group tint,\n * required marker, ARIA column index) is re-applied to every\n * rendered cell, whether or not it was re-targeted, so a recycled\n * cell never shows a trace of its previous column.\n *\n * Cells left over after the window is filled are removed and disposed.\n * Called from {@link renderColumnWindow}, which positions the returned\n * cells afterward.\n *\n * @param firstCol - The first visible-column index to render, inclusive.\n * @param lastCol - The last visible-column index to render, inclusive.\n * @returns `true` when the rendered cell set changed.\n */\n private reconcileColumnCells(firstCol: number, lastCol: number): boolean {\n const row = this.getComponents()[1] as Row;\n\n if (!this._columnsDirty\n && firstCol === this._windowFirst\n && lastCol === this._windowFirst + row.getComponents().length - 1) {\n return false;\n }\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 slotCount = lastCol - firstCol + 1;\n const assigned: (HeaderCell | undefined)[] = new Array(slotCount).fill(undefined);\n\n // Pass 1 — keep a cell for its own field.\n for (let col = firstCol; col <= lastCol; col++) {\n const name = this._visibleFields[col].getName();\n const cell = byName.get(name);\n\n if (cell) {\n assigned[col - firstCol] = cell;\n byName.delete(name);\n }\n }\n\n const free = Array.from(byName.values());\n\n // Pass 2 — recycle a leftover, else build.\n for (let col = firstCol; col <= lastCol; col++) {\n const slot = col - firstCol;\n\n if (assigned[slot] !== undefined) {\n continue;\n }\n\n const field = this._visibleFields[col];\n let cell = free.pop();\n\n if (cell) {\n row.setLayoutConstraints(cell, { data: field });\n } else {\n cell = new HeaderCell(field.getName(), field.getName(), null);\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 // columnIndexOf) at emit time, so a later hide/show/scroll\n // that shifts indices needs no re-wiring. Re-wiring a\n // surviving cell would stack duplicate listeners on its\n // ListenerBag — making a single drag emit `columnresize`\n // several times with mismatched indices, and a single header\n // click cycle the sort twice.\n this.wireCell(cell);\n }\n\n assigned[slot] = cell;\n }\n\n // Pass 3 — per-column state, re-applied to every rendered cell so a\n // recycled cell never shows a trace of its previous column.\n for (let col = firstCol; col <= lastCol; col++) {\n const cell = assigned[col - firstCol]!;\n const field = this._visibleFields[col];\n const column = columnMap.get(field.getName());\n\n cell.setFieldName(field.getName());\n cell.setHeaderText(column?.getHeaderText() ?? field.getName());\n\n const description = field.getDescription();\n\n if (cell.getTooltip() !== description) {\n cell.setTooltip(description);\n }\n\n const headerGlyph = column?.getHeaderGlyph() ?? null;\n\n if (cell.getHeaderGlyph() !== headerGlyph) {\n cell.setHeaderGlyph(headerGlyph);\n }\n\n cell.setBaseBackground(column?.getGroupColor() ?? null);\n cell.setRequired(column?.isRequired() ?? false);\n cell.getAria().setColIndex(col + 1);\n }\n\n // Discard what is left over.\n for (const cell of free) {\n row.removeComponent(cell);\n cell.dispose();\n }\n\n // Re-order children to the window's display order so sibling\n // iteration (e.g. `syncSortIndicators`) matches slot order.\n //\n // The order comes from `assigned`, which this reconciler just built in\n // slot order — not from re-sorting on `Field.getOrder()`. A model that\n // declares no `order` returns the -1 sentinel for every field, so an\n // order-based comparison ties throughout, the sort is a stable no-op,\n // and a recycled cell keeps whatever index it already held. That\n // desynchronises slot from column: `getColumns()[s]` stops being the\n // cell for column `_windowFirst + s`, which silently misplaces\n // geometry, resize indices, sort arrows and the focus underline.\n const slotOf = new Map(assigned.map((cell, slot) => [cell, slot]));\n\n row.sortComponents((c1, c2) =>\n (slotOf.get(c1 as HeaderCell) ?? 0) - (slotOf.get(c2 as HeaderCell) ?? 0));\n\n this._windowFirst = firstCol;\n this._columnsDirty = false;\n\n return true;\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. A run of adjacent ungrouped columns\n * shares one blank spanning cell so the parent row's surface stays\n * 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.disposeAllComponents();\n\n if (!this.hasParentRow()) {\n return;\n }\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 // A run continues when both sides share the same group key —\n // including two adjacent ungrouped columns, which share `null`\n // and so merge into one blank spanning cell.\n const runContinues = 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 columnIndexOf} when the event\n * fires, rather than capturing an index at wiring time. This keeps the\n * index correct after a hide/show/reorder/scroll shuffles the columns\n * without re-wiring — re-wiring would stack duplicate listeners on the\n * surviving 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.columnIndexOf(cell), clientX));\n cell.on(\"resizedrag\", (clientX) => this.emit(\"columnresize\", this.columnIndexOf(cell), clientX));\n cell.on(\"contextmenu\", (fieldName, x, y) => this.emit(\"columncontextmenu\", fieldName, x, y));\n }\n\n /**\n * Converts a rendered cell to its visible-column index — the slot it\n * occupies in the column row plus the window's start offset.\n *\n * @param cell - The header cell to resolve.\n * @returns The visible-column index, or `-1` when the cell is not currently rendered.\n */\n private columnIndexOf(cell: HeaderCell): number {\n const slot = this.getColumns().indexOf(cell);\n\n return slot === -1 ? -1 : this._windowFirst + slot;\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 rendered 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 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, slot) => {\n const field = this._visibleFields[this._windowFirst + slot];\n const entry = field ? fieldToSorter.get(field.getName()) : undefined;\n\n if (entry) {\n cell.setSortState(entry.dir, showPriority ? entry.priority : null);\n } else {\n cell.clearSortState();\n }\n });\n }\n\n /**\n * Returns (creating on first use) the `{ operator, text }` map for the\n * currently-bound store. Keyed per store, rather than held flat, so a\n * round trip through rotated mode — which re-points this header at the\n * projection store and back — restores the source store's filter row\n * unchanged instead of showing one store's text over another's columns.\n *\n * @returns The current store's field-name-to-state map.\n */\n private filterState(): Map<string, ColumnFilterState> {\n let map = this._filterStates.get(this._store);\n\n if (!map) {\n map = new Map();\n this._filterStates.set(this._store, map);\n }\n\n return map;\n }\n\n /**\n * Handles a `\"filterchange\"` event from a rendered {@link FilterCell}:\n * caches the new state, flushes any other field's pending write first\n * (a horizontal scroll can retarget the pending field's timer before it\n * fires, but a *different* field changing while one is pending still\n * needs the earlier one applied), then schedules — or, when `immediate`,\n * applies — this field's write.\n *\n * @param fieldName - The field the change targets.\n * @param state - The cell's new operator + text.\n * @param immediate - `true` to bypass the debounce (operator pick, Enter, Escape).\n */\n private onFilterCellChange(fieldName: string, state: ColumnFilterState, immediate: boolean): void {\n const cached = this.filterState().get(fieldName);\n const unchanged = !!cached && columnFilterStatesEqual(cached, state);\n\n // A repeat keystroke reporting the same state is dropped so it\n // doesn't reschedule the debounce timer for no reason — but an\n // `immediate` request (operator pick, Enter, Escape) must still\n // flush even when the state matches what a still-pending debounced\n // keystroke already cached; otherwise pressing Enter right after\n // typing would silently do nothing, leaving the write to land only\n // when the original timer eventually fires.\n if (unchanged && !immediate) {\n return;\n }\n\n this.filterState().set(fieldName, state);\n\n if (this._pendingFilterField !== null && this._pendingFilterField !== fieldName) {\n this.applyPendingFilter();\n }\n\n if (this._filterTimer !== null) {\n clearTimeout(this._filterTimer);\n this._filterTimer = null;\n }\n\n this._pendingFilterField = fieldName;\n\n if (immediate) {\n this.applyPendingFilter();\n } else {\n this._filterTimer = setTimeout(() => this.applyPendingFilter(), COLUMN_FILTER_DEBOUNCE_MS);\n }\n }\n\n /**\n * Resolves the {@link ColumnFilterTarget} {@link buildColumnFilter} needs\n * for `fieldName` — the field's declared type plus its config's `values`\n * / `showSeconds`.\n *\n * @param fieldName - The field to resolve a filter target for.\n * @returns The resolved target, or `null` when the field is unknown to\n * the currently-bound model.\n */\n private filterTarget(fieldName: string): ColumnFilterTarget | null {\n const field = this._model.getField(fieldName);\n\n if (!field) {\n return null;\n }\n\n const config = this._columnConfigs.get(fieldName);\n\n return { type: field.getType(), values: config?.values, showSeconds: config?.showSeconds };\n }\n\n /**\n * Clears the pending timer and writes the pending field's cached state to\n * the store via {@link buildColumnFilter}, converting a blank or\n * unparseable state to `null` (removing the field's filter) rather than\n * writing a broken descriptor.\n */\n private applyPendingFilter(): void {\n if (this._filterTimer !== null) {\n clearTimeout(this._filterTimer);\n this._filterTimer = null;\n }\n\n const fieldName = this._pendingFilterField;\n\n if (fieldName === null) {\n return;\n }\n\n this._pendingFilterField = null;\n\n const state = this.filterState().get(fieldName);\n const target = this.filterTarget(fieldName);\n\n if (!state || !target) {\n return;\n }\n\n void this._store.setFilter(fieldName, buildColumnFilter(fieldName, target, state, this._cellText));\n }\n\n /**\n * Registered on the store's `'filterchange'` event so an external\n * mutation — `store.clearFilter()`, or a programmatic `setFilter` —\n * re-syncs the filter row instead of leaving it showing stale text over\n * unfiltered data.\n *\n * Deliberately only *drops* a cached entry once its column no longer\n * has a matching filter in the store; it never reconstructs text from a\n * descriptor (a temporal descriptor holds a `Date`, and formatting it\n * back would rewrite what the user typed). The field with a debounced\n * write still in flight is skipped, since its descriptor has not been\n * written yet.\n */\n private onStoreFilterChange(): void {\n const states = this.filterState();\n\n for (const [fieldName, state] of states) {\n if (fieldName === this._pendingFilterField) {\n continue;\n }\n\n const target = this.filterTarget(fieldName);\n\n if (!target) {\n continue;\n }\n\n const built = buildColumnFilter(fieldName, target, state, this._cellText);\n\n if (built !== null && this._store.getFilter(fieldName) === null) {\n states.delete(fieldName);\n }\n }\n\n this._filterCellsDirty = true;\n this.renderColumnWindow();\n }\n\n /**\n * Wires the `\"filterchange\"` callback for one filter cell. Called\n * exactly once per cell, at creation — mirroring {@link wireCell}.\n *\n * @param cell - The filter cell whose listener is being attached.\n */\n private wireFilterCell(cell: FilterCell): void {\n cell.on(\"filterchange\", (fieldName, state, immediate) => this.onFilterCellChange(fieldName, state, immediate));\n }\n\n /**\n * Reconciles the filter row's rendered cells to the horizontally-visible\n * column range `[firstCol, lastCol]`, mirroring\n * {@link reconcileColumnCells}'s three-pass recycle algorithm and\n * tracked by its own {@link _filterCellsDirty} flag and\n * {@link _filterWindowFirst} offset. A column that is not filterable\n * still gets a cell, so the row stays column-aligned; an empty operator\n * list is what renders that cell blank.\n *\n * When {@link hasFilterRow} is `false` every cell is disposed and the\n * row stays empty, mirroring {@link rebuildParentCells}'s own\n * disabled-state handling.\n *\n * @param firstCol - The first visible-column index to render, inclusive.\n * @param lastCol - The last visible-column index to render, inclusive.\n * @returns `true` when the rendered cell set changed.\n */\n private reconcileFilterCells(firstCol: number, lastCol: number): boolean {\n const row = this.getFilterRow();\n\n if (!this.hasFilterRow()) {\n if (row.getComponents().length === 0 && !this._filterCellsDirty) {\n return false;\n }\n\n for (const cell of row.getComponents().slice() as FilterCell[]) {\n row.removeComponent(cell);\n cell.dispose();\n }\n\n this._filterCellsDirty = false;\n\n return true;\n }\n\n if (!this._filterCellsDirty\n && firstCol === this._filterWindowFirst\n && lastCol === this._filterWindowFirst + row.getComponents().length - 1) {\n return false;\n }\n\n const columnMap = new Map(this._columns.map(c => [c.getField().getName(), c]));\n const existing = row.getComponents().slice() as FilterCell[];\n const byName = new Map<string, FilterCell>();\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 slotCount = lastCol - firstCol + 1;\n const assigned: (FilterCell | undefined)[] = new Array(slotCount).fill(undefined);\n\n // Pass 1 — keep a cell for its own field.\n for (let col = firstCol; col <= lastCol; col++) {\n const name = this._visibleFields[col].getName();\n const cell = byName.get(name);\n\n if (cell) {\n assigned[col - firstCol] = cell;\n byName.delete(name);\n }\n }\n\n const free = Array.from(byName.values());\n\n // Pass 2 — recycle a leftover, else build.\n for (let col = firstCol; col <= lastCol; col++) {\n const slot = col - firstCol;\n\n if (assigned[slot] !== undefined) {\n continue;\n }\n\n const field = this._visibleFields[col];\n const column = columnMap.get(field.getName());\n const operators = column?.isFilterable() ? columnFilterOperators(field.getType()) : [];\n let cell = free.pop();\n\n if (cell) {\n row.setLayoutConstraints(cell, { data: field });\n } else {\n cell = new FilterCell(field.getName(), operators);\n\n row.addComponent(cell, { data: field });\n\n this.wireFilterCell(cell);\n }\n\n assigned[slot] = cell;\n }\n\n // Pass 3 — per-column state, re-applied to every rendered cell so a\n // recycled cell never shows a trace of its previous column.\n for (let col = firstCol; col <= lastCol; col++) {\n const cell = assigned[col - firstCol]!;\n const field = this._visibleFields[col];\n const column = columnMap.get(field.getName());\n const operators = column?.isFilterable() ? columnFilterOperators(field.getType()) : [];\n\n cell.setFieldName(field.getName());\n cell.setColumnLabel(column?.getHeaderText() ?? field.getName());\n cell.setOperators(operators);\n\n const target = this.filterTarget(field.getName());\n\n cell.setNumericOnly(target !== null && columnFilterTakesNumericOperand(target));\n cell.getAria().setColIndex(col + 1);\n\n if (operators.length > 0) {\n cell.setFilterState(this.filterState().get(field.getName())\n ?? { clauses: [{ operator: operators[0], text: '' }] });\n }\n }\n\n // Discard what is left over.\n for (const cell of free) {\n row.removeComponent(cell);\n cell.dispose();\n }\n\n // Re-order to slot order, mirroring `reconcileColumnCells`.\n const slotOf = new Map(assigned.map((cell, slot) => [cell, slot]));\n\n row.sortComponents((c1, c2) =>\n (slotOf.get(c1 as FilterCell) ?? 0) - (slotOf.get(c2 as FilterCell) ?? 0));\n\n this._filterWindowFirst = firstCol;\n this._filterCellsDirty = false;\n\n return true;\n }\n\n /**\n * Reconciles the rendered header cells to the horizontally-visible column\n * range and positions every rendered cell in all three rows.\n *\n * @param geometry - Replaces the cached geometry when supplied; the cached\n * value is reused when omitted.\n * @returns This header, for method chaining.\n */\n renderColumnWindow(geometry?: HeaderColumnGeometry): this {\n if (geometry) {\n this._geometry = geometry;\n }\n\n const g = this._geometry;\n const widths = this._visibleFields.map((_, i) => g.columnWidths[i] ?? 0);\n const win = computeColumnWindow(widths, this._scrollX, g.viewportWidth);\n const changed = this.reconcileColumnCells(win.firstCol, win.lastCol);\n\n if (changed) {\n this.syncSortIndicators();\n this.applyFocusedColumn();\n }\n\n this.positionColumnCells(win, g.columnHeight);\n this.positionParentCells(win, g.parentRowHeight);\n\n this.reconcileFilterCells(win.firstCol, win.lastCol);\n this.positionFilterCells(win, g.filterRowHeight);\n\n return this;\n }\n\n /**\n * Positions every rendered filter-row cell from the window's `lefts` /\n * `widths` arrays, mirroring {@link positionColumnCells} — the filter\n * row is windowed exactly like the column row, one cell per visible\n * column.\n *\n * @param win - The column window computed by {@link renderColumnWindow}.\n * @param filterRowHeight - The filter row's height in pixels; `0` when collapsed.\n */\n private positionFilterCells(win: ColumnWindow, filterRowHeight: number): void {\n const cells = this.getFilterRow().getComponents();\n\n for (let slot = 0; slot < cells.length; slot++) {\n const col = win.firstCol + slot;\n\n this._cellGeom.apply(cells[slot], win.lefts[col] ?? 0, win.widths[col] ?? 0, filterRowHeight);\n }\n }\n\n /**\n * Positions every rendered column-row cell from the window's `lefts`\n * and `widths` arrays — the same geometry {@link reconcileColumnCells}\n * just reconciled the cell set against.\n *\n * @param win - The column window computed by {@link renderColumnWindow}.\n * @param columnHeight - The column row's height in pixels.\n */\n private positionColumnCells(win: ColumnWindow, columnHeight: number): void {\n const cells = this.getColumns();\n\n for (let slot = 0; slot < cells.length; slot++) {\n const col = win.firstCol + slot;\n\n this._cellGeom.apply(cells[slot], win.lefts[col] ?? 0, win.widths[col] ?? 0, columnHeight);\n }\n }\n\n /**\n * Positions every parent-row cell from the window's `lefts` / `widths`\n * arrays in constant time: a cell's x is the left offset of its\n * `spanFrom` column, and its width is the sum of every column across\n * its span.\n *\n * @param win - The column window computed by {@link renderColumnWindow}.\n * @param parentRowHeight - The parent row's height in pixels; `0` when collapsed.\n */\n private positionParentCells(win: ColumnWindow, parentRowHeight: number): void {\n const row = this.getParentRow();\n\n for (const cell of row.getComponents()) {\n const lc = row.getLayoutConstraints(cell);\n const span = lc?.data as { spanFrom: number, spanTo: number } | undefined;\n const from = span?.spanFrom ?? 0;\n const to = span?.spanTo ?? 0;\n const x = win.lefts[from] ?? 0;\n const w = (win.lefts[to] ?? 0) + (win.widths[to] ?? 0) - x;\n\n this._cellGeom.apply(cell, x, w, parentRowHeight);\n }\n }\n\n /**\n * Mirrors the body's horizontal scroll offset onto the header's three\n * inner rows and re-renders the column window.\n *\n * Translates the three inner rows (parent row + column row + filter row)\n * rather than the header element itself — the header band stays pinned\n * to the viewport width so its background covers the vertical-scrollbar\n * reserve band on the right edge, and only the cells inside scroll with\n * the body.\n *\n * @param scrollLeft - The new horizontal scroll offset, in pixels.\n * @returns This header, for method chaining.\n */\n setScrollX(scrollLeft: number): this {\n if (scrollLeft === this._scrollX) {\n return this;\n }\n\n this._scrollX = scrollLeft;\n\n this.getParentRow().setTranslate(-scrollLeft, 0);\n this.getComponents()[1].setTranslate(-scrollLeft, 0);\n this.getFilterRow().setTranslate(-scrollLeft, 0);\n\n this.renderColumnWindow();\n\n return this;\n }\n\n /**\n * Returns the horizontal scroll offset last applied.\n *\n * @returns The scroll offset in pixels.\n */\n getScrollX(): number {\n return this._scrollX;\n }\n\n /**\n * Paints the column-focus underline on the rendered cell for `colIndex`\n * and clears it everywhere else. `null` clears every cell.\n *\n * @param colIndex - The visible-column index to focus, or `null` to clear.\n * @returns This header, for method chaining.\n */\n setFocusedColumn(colIndex: number | null): this {\n this._focusedCol = colIndex;\n\n this.applyFocusedColumn();\n\n return this;\n }\n\n /**\n * Re-applies `_focusedCol` to the currently-rendered cells. Called\n * directly from {@link setFocusedColumn} and again at the end of every\n * reconcile that changed the rendered set, so a cell recycled into the\n * focused column picks up the underline without waiting for the next\n * explicit `setFocusedColumn` call.\n */\n private applyFocusedColumn(): void {\n const cells = this.getColumns() as HeaderCell[];\n\n for (let slot = 0; slot < cells.length; slot++) {\n cells[slot].setColumnFocused(this._windowFirst + slot === this._focusedCol);\n }\n }\n\n /**\n * Clears the pending filter-write timer and detaches this header's store\n * listener before the inherited teardown runs.\n */\n protected destructor(): void {\n if (this._filterTimer !== null) {\n clearTimeout(this._filterTimer);\n this._filterTimer = null;\n }\n\n this._store.off('filterchange', this._boundOnStoreFilterChange);\n this._cellText.dispose();\n\n super.destructor();\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 { 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 // The row is a child, so it already starts inside the footer's border;\n // handing it the outer width would push it that far past the far edge.\n // Before the element exists there is no border to subtract, so the\n // argument is the whole box.\n this.getComponents()[0].setWidth(this.getContentBounds()?.width ?? 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 // The footer gives itself a 1px top border in its constructor, so the\n // row was a pixel taller than the band's content box and its bottom\n // pixel fell under `overflow: hidden`. Nothing displays a footer today\n // — there is no footer-visibility setter — so this corrects the\n // arithmetic rather than any rendered pixel.\n this.getComponents()[0].setHeight(this.getContentBounds()?.height ?? 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 _width : number | undefined;\n private _maxContentLength: number | undefined;\n private _hidden : boolean;\n private _unhideable : boolean;\n private _readOnly : boolean;\n private _headerGlyph : string | null;\n private _headerText : string | null;\n private _group : string | null;\n private _groupColor : string | null;\n private _required : boolean;\n private _filterable : 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\n * absent / false, except `filterable`, which defaults to `true`.\n * @param specFilterable - Table-wide {@link ColumnSpec.filterable} default,\n * consulted when `config.filterable` is unset.\n */\n constructor(field: Field, config?: ColumnConfig, specFilterable?: boolean) {\n this._field = field;\n this._minWidth = config?.minWidth;\n this._maxWidth = config?.maxWidth;\n this._width = config?.width;\n this._maxContentLength = config?.maxContentLength;\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._headerText = config?.headerText ?? null;\n this._group = config?.group ?? null;\n this._groupColor = config?.groupColor ?? null;\n this._required = config?.required ?? false;\n this._filterable = config?.filterable ?? specFilterable ?? true;\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 the explicit starting width in pixels declared in the spec.\n *\n * @returns The declared width, or `undefined` if unset.\n */\n getWidth(): number | undefined {\n return this._width;\n }\n\n /**\n * Returns the longest value this column can hold, in characters, as\n * declared in the spec.\n *\n * @returns The declared character budget, or `undefined` if unset.\n */\n getMaxContentLength(): number | undefined {\n return this._maxContentLength;\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 * Returns the configured header-label override for this column.\n *\n * @returns The override text, or `null` when unset (the header falls back to\n * the field name).\n */\n getHeaderText(): string | null {\n return this._headerText;\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 * Returns whether this column gets a filter input in the header's\n * filter row, once the row itself is shown via\n * {@link Table.setFilterRowVisible}. Filterable by default.\n *\n * @returns `false` only when the spec declared `filterable: false` for\n * this column, or left it unset while {@link ColumnSpec.filterable} is\n * `false`.\n */\n isFilterable(): boolean {\n return this._filterable;\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()), spec.filterable));\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()), spec.filterable));\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 type { CellTextResolver } from \"~/component/table/cell/CellText.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 * Combo, date, time, and datetime values are formatted the same way the\n * matching cell renderer displays them, 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 `values` / `showSeconds`).\n * @param display - Resolver used to format combo/temporal values.\n * @param options - Optional export options.\n */\n static exportCSV(\n columns : Column[],\n records : ModelRecord[],\n columnConfigs: Map<string, ColumnConfig>,\n display : CellTextResolver,\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, display)))\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 `values` / `showSeconds`).\n * @param display - Resolver used to format combo/temporal values.\n * @param options - Optional export options.\n */\n static exportJSON(\n columns : Column[],\n records : ModelRecord[],\n columnConfigs: Map<string, ColumnConfig>,\n display : CellTextResolver,\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, display)\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 * Only two shapes are reformatted — everything else, including `null` /\n * `undefined`, a plain `number` / `string` / `boolean`, and a `Date` on a\n * non-temporal column, passes through unchanged so JSON export keeps its\n * types and a boolean column keeps exporting `true` / `false`.\n *\n * @param column - The column whose field type / config drives the formatting choice.\n * @param value - The raw value read from the record.\n * @param columnConfigs - Per-field config map; consulted for `values` and `showSeconds`.\n * @param display - Resolver used to format the combo/temporal shapes.\n * @returns The formatted value, or the original value when no formatting applies.\n *\n * @internal\n */\n static formatValue(column: Column, value: any, columnConfigs: Map<string, ColumnConfig>, display: CellTextResolver): any {\n if (value == null) {\n return value;\n }\n\n const config = columnConfigs.get(column.getField().getName());\n\n if (config?.values && config.values.length > 0) {\n return display.text('combo', false, config.values, value);\n }\n\n if (!(value instanceof Date)) {\n return value;\n }\n\n const type = column.getField().getType();\n\n if (type !== 'date' && type !== 'time' && type !== 'datetime') {\n return value;\n }\n\n return display.text(type, config?.showSeconds ?? false, undefined, value);\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 type { StoreUpdateEvent } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { MemoryStore } from \"~/data/MemoryStore.js\";\nimport { Model } from \"~/data/Model.js\";\nimport type { Field } from \"~/data/Field.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { Dialog, DialogButtons } from \"~/overlay/Dialog.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { CheckboxMenuRow } from \"~/component/container/CheckboxMenuRow.js\";\nimport { TRACK_WIDTH } from \"~/component/container/Scrollbar.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { table_columns } from \"~/glyphs/solid/table_columns.js\";\nimport { undo } from \"~/glyphs/solid/undo.js\";\nimport { file_csv } from \"~/glyphs/solid/file_csv.js\";\nimport { file_code } from \"~/glyphs/solid/file_code.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport type { CellType, ColumnConfig, ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport { ColumnSpec, normalizeComboOptions } from \"~/component/table/ColumnConfig.js\";\nimport { columnFilterOperators } from \"~/component/table/ColumnFilter.js\";\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { TableExporter, ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { CellTextResolver } from \"~/component/table/cell/CellText.js\";\nimport { DEFAULT_INDENT_PX } from \"~/component/table/cell/renderer/TreeCell.js\";\nimport { Checkbox } from \"~/component/input/Checkbox.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { chainRoom, distributeDragChain, DRAG_DISTRIBUTION_EPSILON } from \"~/core/DragChain.js\";\n\n// Register the column context menu's item glyphs eagerly at module load —\n// same pattern as PaginationBar's nav glyphs — so a consumer never has to\n// pre-register them before the menu can open.\nGlyph.register(table_columns, undo, file_csv, file_code);\n\n/** Events emitted by {@link Table}. */\nexport type TableEvent = \"selection\" | \"cellclick\";\n\n/** Which presentation a Table renders: record-per-row, or one record as key/value rows. */\nexport type TableDisplayMode = \"normal\" | \"rotated\";\n\n// The model backing the rotated key/value projection: one record per source\n// field, `field` holding the field name and `value` holding that field's value\n// on the displayed record. `value` is declared `'auto'` because the projection\n// carries every source field's native type (string, number, boolean, Date, …)\n// in the same column. `filler` is a blank, unbounded spacer column that soaks\n// up leftover table width so `field` / `value` stay compact on a wide table.\nconst ROTATED_MODEL = new Model([\n { name: 'field', type: 'string', order: 0 },\n { name: 'value', type: 'auto', order: 1 },\n { name: 'filler', type: 'string', order: 2 }, // blank spacer column; absorbs leftover width\n]);\n\n// Column-width policy constants. See plans/implemented/table-generated-column-widths.md\n// for the derivation of each value.\nconst MIN_COLUMN_WIDTH_PX = 30; // Absolute floor. No policy minimum ever falls below it.\nconst CHECKBOX_WIDTH_PX = 16; // The checkbox box edge (Checkbox.ts).\nconst GLYPH_WIDTH_PX = 20; // One icon-font glyph square at the default body font, plus slack.\nconst MIN_NUMBER_DIGITS = 4; // Digits a number column must always fit.\nconst DEFAULT_NUMBER_DIGITS = 8; // Digits assumed for a number column with neither a sample nor a hint.\nconst MIN_STRING_CHARS = 8; // Characters a string column must always fit.\nconst STRING_WIDTH_PX = 100; // Starting width for a flex column that needs a concrete number.\nconst HEADER_CHROME_PX = 21; // 4px cell padding + 5px resize-handle gutter + 12px sort indicator.\nconst CELL_CHROME_PX = 6; // 4px cell padding + 2px so the last glyph isn't flush against the border.\nconst AUTO_WIDTH_CAP_PX = 400; // Ceiling for a measured width when the column declares no maxWidth.\nconst SAMPLE_ROWS = 50; // Records read per derivation.\nconst WIDEST_CANDIDATES = 3; // Strings measured per sampled column (the longest by character count).\nconst HINT_SAMPLE_MAX_CHARS = 60; // Cap on the maxContentLength probe string.\n// Formatted to measure the width of a date/time/datetime column; a fixed\n// instant so the measurement is deterministic across runs.\nconst REFERENCE_DATE = new Date(2000, 11, 31, 23, 59, 59);\n\n// Sub-pixel tolerance below which a resize-drag-grown column total is treated\n// as equal to the available width (`_columnWidthTarget` collapses to 0\n// instead of recording a target the layout would treat as already-grown).\n// The same 0.5 the file's existing width comparisons use (see setColumnVisible).\nconst WIDTH_TARGET_EPSILON_PX = 0.5;\n\n// Column show/hide entries. At or below the threshold they live in a submenu of\n// the header context menu; past it a leaf row opens a modal dialog instead,\n// which lists far more rows comfortably and scrolls.\nconst COLUMN_MENU_DIALOG_THRESHOLD = 20; // Resolved columns above which the dialog replaces the submenu.\nconst COLUMN_DIALOG_MAX_PER_COLUMN = 15; // Checkboxes per dialog column before wrapping to another.\nconst COLUMN_DIALOG_COLUMN_GAP_PX = 24; // Horizontal gap between dialog columns.\nconst COLUMN_DIALOG_INSET_PX = 16; // Body inset, matching the padding Dialog gives its own message text.\nconst COLUMN_DIALOG_GROUP_INDENT_PX = 16; // One nesting level, matching the submenu's group indent.\n\n/**\n * The floor and starting width a column's field type implies, before any\n * declared `minWidth`/`maxWidth`/`width` override is applied.\n */\ninterface WidthPolicy {\n min: number;\n /** `null` = flex: no definite width, share the leftover space. */\n preferred: number | null;\n}\n\n/** Cached reference measurements consulted by the cheap {@link Table.getColumnMinWidth} path. */\ninterface WidthReferences {\n /** Width of the widest digit glyph, \"0\" through \"9\". */\n digitPx: number;\n /**\n * Widest digit-substituted variant of `REFERENCE_DATE` formatted, keyed\n * by `${type}:${showSeconds}` — guards against a non-tabular font\n * rendering some digit wider than `REFERENCE_DATE`'s own digits.\n */\n datePx: Map<string, number>;\n}\n\n/** The state behind one active {@link Table.setQuickSearch} call. */\ninterface QuickSearchState {\n /** Trimmed, lower-cased search text. Never `''` — a blank search is stored as `null` instead. */\n needle: string;\n /** The `fields` argument exactly as passed, or `null` for the default column scope. */\n requested: readonly string[] | null;\n /** The field names actually searched, derived from `requested`. */\n fields: string[];\n /**\n * Per-record searchable text: every searched field's cell text, lower-cased and\n * joined with `\\n`. Built the first time a record is tested and reused on every\n * later render pass — `Body.getVisibleRecords()` re-runs the predicate over the\n * whole store on every frame, so formatting per pass is not affordable.\n */\n cache: WeakMap<ModelRecord, string>;\n}\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 * {@link setDisplayMode | Rotated mode} (`\"normal\"` | `\"rotated\"`) swaps the\n * presentation to psql `\\x`-style key/value rows: one `field`/`value` row per\n * source field of the selected record, built from a two-field\n * [`Model`](/api/data/classes/Model) projection and driven through the same\n * header and body — including per-field cell variants via\n * {@link ColumnConfig}'s `cellType` / `cellValues`. The projection is\n * read-only; see {@link setDisplayMode} for details.\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 // The open column dialog, or null. A LayerManager-mounted overlay, never a\n // registered child, so `destructor()` disposes it explicitly.\n private _columnDialog : Dialog | null = null;\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 // Index of the column whose right edge is being dragged; `null` when no\n // drag is live.\n private _dragEdgeIndex : number | null = null;\n // Pointer x consumed so far — advanced only by applied travel (see\n // `onColumnResize`), never the raw `clientX`.\n private _dragLastClientX : number = 0;\n // The total column width a resize drag grew the table to, or `0` when the\n // table sits at its available width. Backing field for `getColumnWidthTarget`.\n private _columnWidthTarget: number = 0;\n private _savedColumnWidths: Map<string, number> = new Map();\n private _columnConfigs : Map<string, ColumnConfig> = new Map();\n private _exportMenuEnabled: boolean = true;\n private _filterRowVisible : boolean = false;\n private _listeners : ListenerBag<TableEvent> = new ListenerBag<TableEvent>();\n private _displayMode : TableDisplayMode = \"normal\";\n private _rotatedRecord : ModelRecord | null = null;\n private _rowVisible : ((record: ModelRecord) => boolean) | null = null;\n private _quickSearch : QuickSearchState | null = null;\n private _rotatedStore : MemoryStore | null = null;\n private _rotatedColumns : Column[] = [];\n private _rotatedConfigs : Map<string, ColumnConfig> = new Map();\n private _rotatedFieldByName: Map<string, Field> = new Map();\n // Identity map from a projection record just loaded into `_rotatedStore`\n // to the group-separator label/color it represents — populated by\n // `rebuildRotatedStore`, consulted by `Body` (via the `rowSeparator`\n // predicate `bindView` forwards) to render that record as a\n // `GroupSeparatorCell` row instead of an ordinary field/value row.\n // Keyed by object identity (not by the record's `field` value) so a\n // real field named the same as a group can never false-match.\n private _rotatedSeparatorRecords: Map<ModelRecord, { label: string, color: string | null }> = new Map();\n // Identity set of every projection record whose underlying source\n // column has a non-null `getGroup()` — populated by\n // `rebuildRotatedStore` alongside `_rotatedSeparatorRecords`,\n // suppressed the same way while sorted, and consulted by `Body` (via\n // the `rowIndented` predicate `bindView` forwards) to indent that\n // row's `field`-name cell, visually nesting it under its group's\n // separator.\n private _rotatedIndentedRecords: Set<ModelRecord> = new Set();\n private _sourceRefresh : (() => void) | null = null;\n private _sourceUpdate : ((event: StoreUpdateEvent) => void) | null = null;\n private _suppressSelectionForward: boolean = false;\n private _autoWidthsSampled: boolean = false;\n private _widthRefs : WidthReferences | null = null;\n // Longest sampled candidate strings from the last content derivation,\n // keyed by field name. Populated by `collectCandidates`; consulted by\n // `sampledDigits` and `resolveContentCandidates`. A plain cache read —\n // never re-scans the store — so `getColumnMinWidth` can consult it\n // without violating its \"never samples the store\" contract.\n private _sampledCandidates: Map<string, string[]> = new Map();\n // Owner-held pool of unmounted renderers this table formats non-cell\n // values through (export, quick search, width sampling). Disposed in\n // `destructor`; see `CellText.ts` — mirrors `CellEditorPool`.\n private _cellText : CellTextResolver = new CellTextResolver();\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.bindSourceStore(store);\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 this._header.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.\n this._body.on(\"horizontalscroll\", scrollLeft => {\n this._header.setScrollX(scrollLeft);\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. Suppressed during a bindView re-bind (whose\n // transient `_body.selectRecord(null)` would otherwise leak a spurious\n // empty selection) and while rotated, where selection is driven by\n // `_rotatedRecord` instead of the body's own selection state.\n this._body.on(\"selection\", records => {\n if (this._suppressSelectionForward || this._displayMode === \"rotated\") {\n return;\n }\n\n this.emit(\"selection\", records);\n });\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 columns this table currently renders: the two-column\n * `field`/`value` projection while {@link getDisplayMode} is `\"rotated\"`,\n * otherwise the resolved, visible source columns in field display order.\n *\n * @returns The active {@link Column} instances in display order.\n */\n getColumns(): Column[] {\n return this._displayMode === \"rotated\" ? this._rotatedColumns : this.getSourceColumns();\n }\n\n /**\n * Returns the resolved, visible source columns in display order,\n * regardless of the active display mode.\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 source {@link Column} instances in field display order.\n */\n private getSourceColumns(): Column[] {\n const effective = this.getEffectiveHiddenSet();\n\n return this._resolvedColumns.filter(c => !effective.has(c.getField().getName()));\n }\n\n /**\n * Returns the active display mode.\n *\n * @returns `\"normal\"` (one row per record) or `\"rotated\"` (one selected\n * record shown as key/value rows). Defaults to `\"normal\"`.\n */\n getDisplayMode(): TableDisplayMode {\n return this._displayMode;\n }\n\n /**\n * Switches between the normal (record-per-row) and rotated (key/value)\n * presentations. No-op when already in `mode`.\n *\n * Entering `\"rotated\"` adopts the currently selected record as the\n * displayed record — falling back to the store's first record, then to\n * nothing — and re-points the header and body at a two-column `field`/\n * `value` projection built from it; see the [`Table` rotated record\n * view](/components/Table#rotated-record-view) docs. Returning to\n * `\"normal\"` restores the source columns and re-selects the record that\n * was displayed while rotated. The projection is read-only; sorting it\n * reorders the field rows without touching the source store's own sort.\n *\n * @param mode - The display mode to switch to.\n * @returns This table, for method chaining.\n */\n setDisplayMode(mode: TableDisplayMode): this {\n if (mode === this._displayMode) {\n return this;\n }\n\n this._displayMode = mode;\n\n if (mode === \"rotated\") {\n this._rotatedRecord = this._body.getSelectedRecord() ?? this._store.getRecords()[0] ?? null;\n\n const rotatedStore = this.ensureRotatedStore();\n\n this.rebuildRotatedStore();\n this.bindView(rotatedStore, this._rotatedColumns, this._rotatedConfigs, new Set(), () => true, null,\n (record) => this._rotatedSeparatorRecords.get(record) ?? null,\n (record) => this._rotatedIndentedRecords.has(record));\n this.emit(\"selection\", this._rotatedRecord ? [this._rotatedRecord] : []);\n } else {\n this.bindView(this._store, this.getSourceColumns(), this._columnConfigs, this.getEffectiveHiddenSet(), this._spec?.rowReadOnly ?? null, this.composeRowVisible(), null, null);\n this._body.selectRecord(this._rotatedRecord);\n }\n\n return this;\n }\n\n /**\n * Sets a live predicate that hides non-matching rows without touching\n * the store — a client-side quick search over an already-loaded grid.\n * Cleared by passing `null`.\n *\n * Display-only: never touches {@link getStore}'s records,\n * {@link getSelectedRecords}, or any pending edit. The predicate is\n * re-applied automatically on every trigger that already re-renders the\n * body — scrolling, a store `'datachange'` / `'add'` / `'remove'` /\n * `'load'`, or a column show/hide — so calling this again is only\n * needed when the predicate itself changes.\n *\n * Neutralized while {@link getDisplayMode} is `\"rotated\"`: the\n * projection's rows are one per source field of a single displayed\n * record, not one per source record, so a predicate written against\n * source records cannot apply there. The last predicate set still\n * takes effect immediately on returning to `\"normal\"`, even if it was\n * set while rotated. Inherited by `TreeTable` as a documented no-op —\n * see the `TreeTable` docs non-goal.\n *\n * Composes with {@link setQuickSearch} via AND — a row renders only when\n * both agree — and setting one never clears the other.\n *\n * @param predicate - Returns `true` to keep the record's row rendered.\n * Called for every loaded record on every render pass; must be O(1)\n * and pure.\n * @returns This table, for method chaining.\n */\n setRowVisible(predicate: ((record: ModelRecord) => boolean) | null): this {\n this._rowVisible = predicate;\n this.applyRowVisible();\n\n return this;\n }\n\n /**\n * Hides every row whose displayed cell text does not contain `text`, matched\n * case-insensitively — a client-side quick search over an already-loaded\n * grid. Cleared by passing `null`, `''`, or blank text.\n *\n * Each searched field is resolved through {@link getCellText}, so the match\n * runs against what the cell actually shows — a combo column's option\n * label, a formatted date/time/datetime — not the raw stored value.\n *\n * With no `fields` argument the searched columns default to every resolved\n * column (including a hidden one) whose filter row would offer a\n * **Contains** operator: a `boolean` column is excluded, since its cell is\n * a checkbox with no text to match, and a column with `filterable: false`\n * is excluded, matching the filter row's own opt-out. Passing `fields`\n * overrides that default and searches exactly the named fields, verbatim —\n * even one whose column is `filterable: false` or offers no Contains\n * operator; an empty array searches nothing, so no row matches.\n *\n * Display-only: never touches {@link getStore}'s records,\n * {@link getSelectedRecords}, or any pending edit. Composes with\n * {@link setRowVisible} via AND — a row renders only when both agree — and\n * setting one never clears the other. Neutralized while\n * {@link getDisplayMode} is `\"rotated\"` and resumes on return to\n * `\"normal\"`, and is inherited by `TreeTable` as a documented no-op — both\n * for the same reasons {@link setRowVisible} is.\n *\n * Each record's searchable text is captured the first time that record is\n * tested against the current search, and reused on every later render\n * pass rather than rebuilt per frame. That cached text is refreshed when\n * the store reports the record changed (an in-grid edit), but a batch\n * committed through the store's own edit-batch API reports no per-record\n * identity, so those records keep the text they were cached with until\n * `setQuickSearch` is called again. Calling `setQuickSearch` always\n * rebuilds the cache from scratch, even for the same text.\n *\n * @param text - The search text, or `null`/blank to clear the search.\n * @param fields - Field names to search, verbatim. Omit for the default\n * column scope described above.\n * @returns This table, for method chaining.\n */\n setQuickSearch(text: string | null, fields?: readonly string[] | null): this {\n const needle = (text ?? '').trim().toLowerCase();\n\n this._quickSearch = needle === '' ? null : {\n needle,\n requested: fields ?? null,\n fields: this.resolveSearchFields(fields ?? null),\n cache: new WeakMap(),\n };\n\n this.applyRowVisible();\n\n return this;\n }\n\n /**\n * Derives the field names {@link setQuickSearch} searches: `requested`\n * verbatim when given, otherwise every resolved column whose filter row\n * would offer a Contains operator.\n *\n * @param requested - The `fields` argument passed to `setQuickSearch`, or `null`.\n * @returns The field names to search.\n */\n private resolveSearchFields(requested: readonly string[] | null): string[] {\n if (requested) {\n return requested.slice();\n }\n\n return this._resolvedColumns\n .filter(c => c.isFilterable() && columnFilterOperators(c.getField().getType()).includes('contains'))\n .map(c => c.getField().getName());\n }\n\n /**\n * Tests one record against an active quick search, consulting (and\n * populating) its cached searchable text.\n *\n * @param search - The active quick-search state.\n * @param record - The record to test.\n * @returns `true` when the record's cached text contains the search needle.\n */\n private quickSearchMatches(search: QuickSearchState, record: ModelRecord): boolean {\n let text = search.cache.get(record);\n\n if (text === undefined) {\n text = search.fields.map(f => this.getCellText(f, record).toLowerCase()).join('\\n');\n\n search.cache.set(record, text);\n }\n\n return text.includes(search.needle);\n }\n\n /**\n * Combines the active quick search and the consumer's own row-visible\n * predicate into the single predicate `Body` sees, via AND.\n *\n * @returns The composed predicate, or `null` when neither is active.\n */\n private composeRowVisible(): ((record: ModelRecord) => boolean) | null {\n const search = this._quickSearch;\n const custom = this._rowVisible;\n\n if (!search) {\n return custom;\n }\n\n const matches = (record: ModelRecord) => this.quickSearchMatches(search, record);\n\n return custom ? (record: ModelRecord) => matches(record) && custom(record) : matches;\n }\n\n /**\n * Pushes the composed row-visible predicate to `Body`, when not rotated.\n */\n private applyRowVisible(): void {\n if (this._displayMode === \"normal\") {\n this._body.setRowVisible(this.composeRowVisible());\n }\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. Leaves rotated mode first — the\n * projection is built from the outgoing store, and would sort/select the\n * wrong store otherwise. Ends with a layout pass, so the header's new\n * columns render immediately — matching `setColumnVisible` / `resetColumns`\n * / `bindView`, which each end in a layout too.\n *\n * @param store - The new store to bind to the table.\n */\n setStore(store: AbstractStore): this {\n this.setDisplayMode(\"normal\");\n\n this._header.setStore(store);\n this.unbindSourceStore(this._store);\n\n this._store = store;\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n this._autoWidthsSampled = false;\n this._widthRefs = null;\n this._sampledCandidates = new Map();\n this._resolvedColumns = Column.resolve(store.model.getFields(), this._spec);\n\n this.bindSourceStore(store);\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.refreshQuickSearch();\n this.getAria().setColCount(this.getColumns().length);\n\n // Matches setColumnVisible / resetColumns / bindView, which each end\n // in a layout too: the header now renders no cells until its first\n // renderColumnWindow, so without this a store swap on an\n // already-sized table would leave the header blank until something\n // else happened to trigger a layout. A no-op on an unsized table —\n // layout/Table.doLayout returns early when the container size isn't finite.\n this.doLayout();\n\n return this;\n }\n\n /**\n * Re-derives the active quick search's field list against the current\n * `_resolvedColumns` and rebuilds its cache from scratch. A no-op when no\n * search is active. Called by {@link setStore}: a search built before a\n * store swap holds field names from the old model, which a different\n * model's columns may no longer have.\n */\n private refreshQuickSearch(): void {\n if (!this._quickSearch) {\n return;\n }\n\n this._quickSearch = {\n needle: this._quickSearch.needle,\n requested: this._quickSearch.requested,\n fields: this.resolveSearchFields(this._quickSearch.requested),\n cache: new WeakMap(),\n };\n\n this.applyRowVisible();\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 * The pixel width the columns are laid out against: the table's inner\n * width less the custom vertical `Scrollbar`'s fixed track width. Shared\n * by the column-resize drag handler and the layout manager so both\n * derive the same number.\n *\n * @returns The available column width in pixels, or `0` before first render.\n */\n getAvailableColumnWidth(): number {\n const innerSize = this.getInnerSize();\n\n return innerSize ? innerSize.width - TRACK_WIDTH : 0;\n }\n\n /**\n * The total column width a resize drag grew the table to, or `0` when the\n * table sits at its available width. Called by the layout manager so a\n * drag-widened total survives the next layout pass instead of being\n * rescaled back to the container's width.\n *\n * @returns The drag-grown column width target in pixels, or `0`.\n */\n getColumnWidthTarget(): number {\n return this._columnWidthTarget;\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 * A no-op while {@link getDisplayMode} is `\"rotated\"` — the projection's\n * `field` and `value` columns are always both shown.\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 (this._displayMode === \"rotated\") {\n return this;\n }\n\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 // The visible column set just changed; see the matching comment in\n // `resetColumns` / `bindView` for why `_widthRefs` must be cleared\n // whenever it does.\n this._widthRefs = null;\n\n const newVisibleColumns = this.getColumns();\n let intrinsic: Array<number | null> | null = null;\n\n const rawWidths = newVisibleColumns.map((col, i) => {\n const saved = this._savedColumnWidths.get(col.getField().getName());\n\n if (saved !== undefined) {\n return saved;\n }\n\n intrinsic ??= this.getIntrinsicColumnWidths();\n\n return intrinsic[i] ?? this.flexColumnWidth(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 // Keep a grown table's target in step with the new column set: showing a\n // column trims the others back to the same total, hiding one frees its width.\n if (this._columnWidthTarget > 0) {\n this._columnWidthTarget = this._columnWidths.reduce((s, w) => s + w, 0);\n }\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 whether the header's filter row is currently shown.\n *\n * @returns `true` when the filter row is visible.\n */\n isFilterRowVisible(): boolean {\n return this._filterRowVisible;\n }\n\n /**\n * Shows or hides the header's filter row. Also reachable from the\n * header's right-click context menu via its checkbox-style **Filter**\n * entry. Idempotent when `visible` already matches the current state.\n * Hiding the row also clears every filter it applied — a column filter\n * is only ever active while its control is visible and editable.\n *\n * @param visible - `true` to show the filter row, `false` to hide it.\n * @returns This table, for method chaining.\n */\n setFilterRowVisible(visible: boolean): this {\n if (visible === this._filterRowVisible) {\n return this;\n }\n\n this._filterRowVisible = visible;\n this._header.setFilterRowVisible(visible);\n this.doLayout();\n\n return this;\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. While\n * rotated, the new record becomes the displayed record instead of\n * scrolling (there is no row to scroll to — see {@link selectRecord}).\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\n if (this._displayMode === \"rotated\") {\n this.selectRecord(record);\n\n return record;\n }\n\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. While rotated,\n * removes the displayed record; the source-store listener then re-targets\n * the view to the store's next remaining record.\n */\n removeSelectedRow(): this {\n if (this._displayMode === \"rotated\") {\n if (this._rotatedRecord) {\n this._store.remove(this._rotatedRecord);\n }\n\n return this;\n }\n\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 * While {@link getDisplayMode} is `\"rotated\"`, this re-targets the\n * projection: `record` becomes the displayed record and its field/value\n * rows are rebuilt, instead of moving the normal-mode row selection.\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 (this._displayMode === \"rotated\") {\n if (record === this._rotatedRecord) {\n return this;\n }\n\n this._rotatedRecord = record;\n this.rebuildRotatedStore();\n\n // The new record's field/value content may need a different width\n // than the old one's; re-derive from getIntrinsicColumnWidths\n // instead of leaving the next doLayout rescale the stale widths.\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n this.doLayout();\n\n this.emit(\"selection\", record ? [record] : []);\n\n return this;\n }\n\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 * While {@link getDisplayMode} is `\"rotated\"`, returns the displayed\n * source record — never a projection (`field`/`value`) record.\n *\n * @returns The selected {@link ModelRecord}, or null.\n */\n getSelectedRecord(): ModelRecord | null {\n if (this._displayMode === \"rotated\") {\n return this._rotatedRecord;\n }\n\n return this._body.getSelectedRecord();\n }\n\n /**\n * Returns all currently selected records. While {@link getDisplayMode}\n * is `\"rotated\"`, returns the single displayed source record (or an\n * empty array when none is displayed) — never projection records.\n *\n * @returns An array of selected {@link ModelRecord} instances.\n */\n getSelectedRecords(): ModelRecord[] {\n if (this._displayMode === \"rotated\") {\n return this._rotatedRecord ? [this._rotatedRecord] : [];\n }\n\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 * 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, this.getColumnMinWidth(columns[i]));\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] - this.getColumnMinWidth(col)\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 * Subscribes to the source store's mutation events so a rotated view\n * tracks the record it displays, and so an active quick search's cached\n * text for one edited record can be dropped without rebuilding the\n * whole cache. Stores the callbacks in `_sourceRefresh` / `_sourceUpdate`\n * so {@link unbindSourceStore} can remove exactly these registrations later.\n *\n * @param store - The source store to subscribe to.\n */\n private bindSourceStore(store: AbstractStore): void {\n const refresh = () => this.onSourceStoreChange();\n\n this._sourceRefresh = refresh;\n\n store.on('load', refresh);\n store.on('add', refresh);\n store.on('remove', refresh);\n store.on('datachange', refresh);\n\n const invalidate = (event: StoreUpdateEvent) => this.onSourceRecordUpdate(event);\n\n this._sourceUpdate = invalidate;\n store.on('update', invalidate);\n }\n\n /**\n * Unsubscribes the callbacks installed by {@link bindSourceStore} from `store`.\n *\n * @param store - The store to unsubscribe from.\n */\n private unbindSourceStore(store: AbstractStore): void {\n if (!this._sourceRefresh) {\n return;\n }\n\n (['load', 'add', 'remove', 'datachange'] as const).forEach(e =>\n store.off(e, this._sourceRefresh!)\n );\n\n if (this._sourceUpdate) {\n store.off('update', this._sourceUpdate);\n }\n }\n\n /**\n * Keeps the rotated projection in sync with the source store. Also the\n * hook for the one-shot auto-size re-derive (see\n * {@link maybeResampleColumnWidths}), which runs regardless of display\n * mode. Outside rotated mode this is otherwise a no-op. Re-targets\n * `_rotatedRecord` to the store's first record (firing `\"selection\"`)\n * when the displayed record no longer exists in the store — e.g. it was\n * removed, or the store was reloaded — then rebuilds the projection\n * against the (possibly new) record.\n */\n private onSourceStoreChange(): void {\n this.maybeResampleColumnWidths();\n\n if (this._displayMode !== \"rotated\") {\n return;\n }\n\n const records = this._store.getRecords();\n const stillPresent = this._rotatedRecord !== null && records.includes(this._rotatedRecord);\n\n if (!stillPresent) {\n const before = this._rotatedRecord;\n this._rotatedRecord = records[0] ?? null;\n\n if (this._rotatedRecord !== before) {\n this.emit(\"selection\", this._rotatedRecord ? [this._rotatedRecord] : []);\n }\n }\n\n this.rebuildRotatedStore();\n\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n this.doLayout();\n }\n\n /**\n * Drops one record's cached quick-search text so it is re-tested against\n * fresh text on the next render pass. A no-op when no search is active.\n *\n * @param event - The `'update'` event payload; only `record` is read.\n */\n private onSourceRecordUpdate(event: StoreUpdateEvent): void {\n this._quickSearch?.cache.delete(event.record);\n }\n\n /**\n * Returns the lazily-built projection store, constructing it (and the\n * two resolved `field`/`value` columns and their configs) on first use.\n *\n * @returns The projection {@link MemoryStore}, reused across mode switches.\n */\n private ensureRotatedStore(): MemoryStore {\n if (this._rotatedStore) {\n return this._rotatedStore;\n }\n\n const spec: ColumnSpec = {\n columns: [\n { field: 'field', minWidth: 80, maxWidth: 200, unhideable: true },\n {\n field: 'value',\n minWidth: 120,\n maxWidth: 360,\n unhideable: true,\n cellType: (r) => this.rotatedCellType(r),\n cellValues: (r) => this.rotatedCellValues(r),\n },\n { field: 'filler', headerText: '', minWidth: 0, unhideable: true },\n ],\n rowReadOnly: () => true,\n // The projection has no per-column filterable field to filter on\n // (see `hasFilterRow`'s doc) — filterable now defaults to `true`,\n // so this must say so explicitly rather than relying on the\n // library-wide default.\n filterable: false,\n };\n\n this._rotatedStore = new MemoryStore(ROTATED_MODEL, []);\n // Group separators are dropped from `rebuildRotatedStore`'s next\n // rebuild whenever the projection is sorted, and restored once the\n // sort is cleared — both `sort()` and `clearSort()` emit this event.\n // `store.loadData` (called from inside `rebuildRotatedStore` itself)\n // fires `'load'`, never `'sortchange'`, so this cannot re-enter.\n this._rotatedStore.on('sortchange', () => this.rebuildRotatedStore());\n this._rotatedColumns = Column.resolve(ROTATED_MODEL.getFields(), spec);\n this._rotatedConfigs = this.buildColumnConfigs(spec);\n\n return this._rotatedStore;\n }\n\n /**\n * Rebuilds the projection store's records from `_rotatedRecord` — one row\n * per visible source field, plus one group-separator row immediately\n * before each contiguous grouped run (per {@link computeGroupRuns}) while\n * the projection is unsorted — and refreshes the `_rotatedFieldByName` map\n * used by {@link rotatedCellType} / {@link rotatedCellValues} to resolve\n * each row's source field. Resolution is by the row's own `field` value\n * (not by index), so it stays correct after the projection is sorted by\n * clicking a header. Also refreshes `_rotatedSeparatorRecords`, the\n * identity map `Body` consults (via the `rowSeparator` predicate\n * {@link bindView} forwards) to tell a separator record from a real\n * one, and `_rotatedIndentedRecords`, the identity set `Body` consults\n * (via the `rowIndented` predicate) to indent a group member's\n * `field`-name cell.\n */\n private rebuildRotatedStore(): void {\n const store = this.ensureRotatedStore();\n const columns = this.getSourceColumns();\n const record = this._rotatedRecord;\n\n // Refresh the field lookup BEFORE loadData. loadData fires the store's\n // `load` event synchronously, and while the body is already bound to\n // the projection store (i.e. switching the displayed record) that\n // reraises straight into rotatedCellType / rotatedCellValues to rebind\n // each value cell. Building the lookup afterwards left that first\n // render resolving every value cell against a stale map: cellType fell\n // back to the column's `auto` type, the string renderer was swapped in\n // unlaid-out, and the value column stayed blank until the next scroll.\n this._rotatedFieldByName = new Map(columns.map(c => [c.getField().getName(), c.getField()]));\n\n const rows: Array<{ field: string, value: unknown }> = [];\n const separatorInfo: Array<{ label: string, color: string | null } | null> = [];\n const indentInfo: boolean[] = [];\n\n if (record) {\n // Separators would scatter away from the group they label once\n // the projection is sorted (a plain store sort has no notion of\n // group adjacency) — suppressed entirely while a sort is active,\n // and restored by the `'sortchange'` listener `ensureRotatedStore`\n // wires once `sort()` / `clearSort()` rebuild this from scratch.\n // The member-row indent is suppressed the same way and for the\n // same reason — nesting rows under a label that no longer sits\n // next to them would misrepresent the grouping.\n const unsorted = store.getActiveSorters().length === 0;\n const runs = unsorted\n ? this.computeGroupRuns(columns)\n : new Map<number, { label: string, color: string | null }>();\n\n for (let i = 0; i < columns.length; i++) {\n const run = runs.get(i);\n\n if (run) {\n rows.push({ field: run.label, value: null });\n separatorInfo.push(run);\n indentInfo.push(false);\n }\n\n const field = columns[i].getField();\n\n rows.push({ field: field.getName(), value: record.get(field.getName()) });\n separatorInfo.push(null);\n indentInfo.push(unsorted && columns[i].getGroup() !== null);\n }\n }\n\n store.loadData(rows);\n\n this._rotatedSeparatorRecords = new Map();\n this._rotatedIndentedRecords = new Set();\n\n store.getRecords().forEach((r, i) => {\n const info = separatorInfo[i];\n\n if (info) {\n this._rotatedSeparatorRecords.set(r, info);\n }\n\n if (indentInfo[i]) {\n this._rotatedIndentedRecords.add(r);\n }\n });\n }\n\n /**\n * Finds each contiguous run of visible source columns sharing the same\n * non-null {@link Column.getGroup} name, mirroring the adjacency rule\n * {@link TableHeader}'s private `rebuildParentCells` uses for the\n * parent-header band: non-adjacent columns sharing a group name are two\n * separate runs, and a run's color is the first non-null\n * {@link Column.getGroupColor} encountered, not the last. Unlike\n * `rebuildParentCells`, which also emits a blank spanning cell for every\n * *ungrouped* run so the parent-header band has no gap, this emits\n * nothing for an ungrouped run — a rotated body row has no such\n * continuity requirement.\n *\n * @param columns - The visible source columns to scan, in display order\n * (already sorted by field order — see {@link getSourceColumns}).\n *\n * @returns A map from a run's starting index into `columns` to its\n * label/color.\n */\n private computeGroupRuns(columns: Column[]): Map<number, { label: string, color: string | null }> {\n const runs = new Map<number, { label: string, color: string | null }>();\n\n if (columns.length === 0) {\n return runs;\n }\n\n let runStart = 0;\n let runKey = columns[0].getGroup();\n let runColor = columns[0].getGroupColor();\n\n const flush = (): void => {\n if (runKey !== null) {\n runs.set(runStart, { label: runKey, color: runColor });\n }\n };\n\n for (let i = 1; i < columns.length; i++) {\n const nextKey = columns[i].getGroup();\n const runContinues = runKey !== null && nextKey === runKey;\n\n if (!runContinues) {\n flush();\n runStart = i;\n runKey = nextKey;\n runColor = columns[i].getGroupColor();\n } else if (runColor === null && columns[i].getGroupColor() !== null) {\n runColor = columns[i].getGroupColor();\n }\n }\n\n flush();\n\n return runs;\n }\n\n /**\n * Per-row cell-variant resolver for the rotated `value` column\n * (`ColumnConfig.cellType`): a row whose source field declares `values`\n * in the table's own spec renders as a combo, otherwise it renders as\n * its source field's own type. O(1) and pure, as the `cellType` contract\n * requires.\n *\n * @param record - The projection record for one field/value row.\n * @returns The cell variant to render, or `null` if the row's source\n * field cannot be resolved (should not happen for a row this table built).\n */\n private rotatedCellType(record: ModelRecord): CellType | null {\n const field = this._rotatedFieldByName.get(record.get('field') as string);\n\n if (!field) {\n return null;\n }\n\n const values = this._columnConfigs.get(field.getName())?.values;\n\n return (values && values.length > 0) ? 'combo' : field.getType();\n }\n\n /**\n * Per-row combo-option resolver for the rotated `value` column\n * (`ColumnConfig.cellValues`), consulted only for rows where\n * {@link rotatedCellType} resolves to `'combo'`. O(1) and pure, as the\n * `cellValues` contract requires.\n *\n * @param record - The projection record for one field/value row.\n * @returns The source field's declared `values`, or `undefined`.\n */\n private rotatedCellValues(record: ModelRecord): Array<ComboOption | string> | undefined {\n const field = this._rotatedFieldByName.get(record.get('field') as string);\n\n return field ? this._columnConfigs.get(field.getName())?.values : undefined;\n }\n\n /**\n * Re-points the header and body at `store` / `columns`, the shared\n * sequence behind both {@link setDisplayMode} and {@link setStore}.\n *\n * @param store - The store the header and body should render.\n * @param columns - The resolved columns to display.\n * @param configs - The column-config map (drives `cellType` / `cellValues` / etc).\n * @param hidden - The set of field names to hide.\n * @param rowReadOnly - The row-level read-only predicate, or `null`.\n * @param rowVisible - The row-visibility predicate, or `null`. Passed\n * `null` on the rotated call site — a predicate written against\n * source records cannot apply to the field/value projection.\n * @param rowSeparator - The group-separator predicate, or `null`.\n * Passed `null` on the normal-mode call site — no separator concept\n * exists outside the rotated projection.\n * @param rowIndented - The group-member indent predicate, or `null`.\n * Passed `null` on the normal-mode call site, for the same reason\n * as `rowSeparator`.\n *\n * @remarks `Body.setStore` re-renders with pool rows whose cells still\n * match the outgoing model; `setColumns` (called after `setStore`) is\n * what re-syncs those cells, so the order matters. `_body.selectRecord(null)`\n * transiently fires the body's own `\"selection\"` — `_suppressSelectionForward`\n * gates the Table-level forwarder for the duration of the re-bind so that\n * transient clear never reaches consumers. Clearing `_columnWidths` /\n * `_savedColumnWidths` is what makes the layout manager re-initialise\n * widths for the new column count on the next `doLayout`.\n *\n * `setStore` is called again at the end (re-assigning the same store) to\n * force a second full rebind after the column/config/hidden/read-only\n * state has settled: `Body`'s own per-slot metadata (read-only tint,\n * required-empty, ARIA) is only re-applied on a slot whose bound index\n * changes, and the first `setStore` call already consumed that \"changed\"\n * signal — on the model swap alone, before `setColumnConfigs` /\n * `setColumns` have synced the pool's cells to the new column shape. A\n * second `setStore` re-triggers the same store-change rebind, this time\n * over the fully-synced cells, so e.g. a rotated column's freshly-built\n * `DynamicCell` actually receives `setReadOnly(true)`.\n */\n private bindView(\n store: AbstractStore,\n columns: Column[],\n configs: Map<string, ColumnConfig>,\n hidden: Set<string>,\n rowReadOnly: ((record: ModelRecord) => boolean) | null,\n rowVisible: ((record: ModelRecord) => boolean) | null,\n rowSeparator: ((record: ModelRecord) => { label: string, color: string | null } | null) | null,\n rowIndented: ((record: ModelRecord) => boolean) | null,\n ): void {\n this._suppressSelectionForward = true;\n\n this._header.setStore(store);\n this._header.setModel(store.model);\n this._header.setColumns(columns);\n this._header.setHiddenColumns(hidden);\n\n this._body.selectRecord(null);\n this._body.setStore(store);\n this._body.setColumnConfigs(configs);\n this._header.setColumnConfigs(configs);\n this._body.setColumns(columns);\n this._body.setHiddenColumns(hidden);\n this._body.setRowReadOnly(rowReadOnly);\n this._body.setRowVisible(rowVisible);\n this._body.setRowSeparator(rowSeparator);\n this._body.setRowIndented(rowIndented);\n this._body.setStore(store);\n\n this._suppressSelectionForward = false;\n\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n // The visible column set just changed shape (source columns <->\n // rotated field/value/filler), and `_widthRefs`'s date/time/datetime\n // reference entries are derived from that set (`dateReferenceKeys`\n // walks `getColumns()`), so a cache built under the old mode would\n // answer for temporal types the new mode doesn't have — or miss ones\n // it does.\n this._widthRefs = null;\n\n this.getAria().setColCount(this.getColumns().length);\n this.doLayout();\n }\n\n /**\n * Disposes the column context menu and the column dialog (if open), then\n * runs the inherited teardown. `_columnContextMenu` and `_columnDialog`\n * are both LayerManager-mounted panels, never registered children (see\n * Menu.ts's class comment), so `super.destructor()`'s child recursion\n * cannot reach either.\n */\n protected destructor(): void {\n this._columnDialog?.dispose();\n this._columnContextMenu.dispose();\n this._cellText.dispose();\n\n super.destructor();\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). While rotated,\n * shows only the export entries (when enabled) — there are no per-column\n * show/hide toggles for a two-column projection.\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 if (this._displayMode === \"rotated\") {\n if (!this._exportMenuEnabled) {\n return;\n }\n\n this._columnContextMenu.show(x, y, [\n { text: 'Export as CSV', glyph: 'file-csv', action: () => this.exportCSV() },\n { text: 'Export as JSON', glyph: 'file-code', action: () => this.exportJSON() },\n ]);\n\n return;\n }\n\n const columns = this.columnsInMenuOrder();\n const items: MenuItemConfig[] = [];\n\n if (columns.length > 0) {\n items.push(\n columns.length > COLUMN_MENU_DIALOG_THRESHOLD\n ? { text: 'Show/hide columns', glyph: 'table-columns', action: () => this.showColumnDialog() }\n : {\n text: 'Show/hide columns',\n glyph: 'table-columns',\n submenu: { label: 'Show/hide columns', items: this.buildColumnMenuItems(columns) },\n }\n );\n }\n\n items.push(\n { separator: true },\n { text: 'Reset columns', glyph: 'undo', action: () => this.resetColumns() }\n );\n\n if (this._resolvedColumns.some(c => c.isFilterable())) {\n items.push(\n { separator: true },\n {\n row: () => {\n const row = new CheckboxMenuRow({ text: 'Filter', checked: this._filterRowVisible });\n\n row.on('action', () => { this.setFilterRowVisible(row.isChecked()); });\n\n return row;\n },\n },\n );\n }\n\n if (this._exportMenuEnabled) {\n items.push(\n { separator: true },\n { text: 'Export as CSV', glyph: 'file-csv', action: () => this.exportCSV() },\n { text: 'Export as JSON', glyph: 'file-code', action: () => this.exportJSON() }\n );\n }\n\n this._columnContextMenu.show(x, y, items);\n }\n\n /**\n * Resolved columns in display order — the order the header and body\n * render them in, and the order both the show/hide submenu and dialog\n * list them in.\n *\n * @returns Resolved columns sorted by {@link Field.getOrder}.\n */\n private columnsInMenuOrder(): Column[] {\n return this._resolvedColumns\n .slice()\n .sort((a, b) => a.getField().getOrder() - b.getField().getOrder());\n }\n\n /**\n * Builds the show/hide submenu's item list: one checkable row per\n * `columns` entry, with a disabled section-header row and a preceding\n * separator at each group boundary.\n *\n * @param columns - Resolved columns in display order, from {@link columnsInMenuOrder}.\n * @returns The submenu's `MenuItemConfig` array.\n */\n private buildColumnMenuItems(columns: Column[]): MenuItemConfig[] {\n const items: MenuItemConfig[] = [];\n let lastGroup: string | null | undefined = undefined;\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 for (const col of columns) {\n const fieldName = col.getField().getName();\n const visible = !this._hiddenColumns.has(fieldName);\n const group = col.getGroup();\n\n if (group !== lastGroup) {\n if (items.length > 0) {\n items.push({ separator: true });\n }\n\n if (group !== null) {\n items.push({ text: group, enabled: false });\n }\n }\n\n const label = (group !== null ? GROUP_INDENT : \"\") + fieldName;\n const enabled = !col.isUnhideable();\n\n items.push({\n row: () => {\n const row = new CheckboxMenuRow({ text: label, checked: visible, enabled });\n\n row.on('action', () => { this.setColumnVisible(fieldName, row.isChecked()); });\n\n return row;\n },\n });\n\n lastGroup = group;\n }\n\n return items;\n }\n\n /**\n * Opens the show/hide-columns dialog: a checkbox per resolved column,\n * staged in a local copy of `_hiddenColumns` and only written back to the\n * table on Apply. Cancel, the title-bar ×, Escape, and a dispose-while-open\n * all resolve without writing anything.\n */\n private showColumnDialog(): void {\n const columns = this.columnsInMenuOrder();\n const snapshot = new Set(this._hiddenColumns);\n const staged = new Set(snapshot);\n const body = this.buildColumnDialogBody(columns, staged);\n\n // Sized from the body's own measured content — Dialog's width is fixed at\n // construction and never re-derived from content (unlike height, see\n // resizeToContent) — so a tight width here is the only way to avoid a\n // dialog wider than the checkbox columns actually need.\n const width = Math.ceil(body.getPreferredSize()?.width ?? 0);\n\n const dialog = new Dialog({\n title: 'Show/hide columns',\n contentComponent: body,\n width,\n buttons: [\n DialogButtons.Cancel,\n { ...DialogButtons.Confirm, text: 'Apply', primary: true },\n ],\n });\n\n this._columnDialog = dialog;\n\n void dialog.show().then(result => {\n this._columnDialog = null;\n\n if (result !== 'confirm') {\n return;\n }\n\n for (const col of columns) {\n const fieldName = col.getField().getName();\n\n if (staged.has(fieldName) !== snapshot.has(fieldName)) {\n this.setColumnVisible(fieldName, !staged.has(fieldName));\n }\n }\n });\n }\n\n /**\n * The number of side-by-side checkbox columns the show/hide dialog uses\n * for `checkboxCount` resolved columns, capped at\n * {@link COLUMN_DIALOG_MAX_PER_COLUMN} rows per column.\n *\n * @param checkboxCount - Total resolved columns to lay out.\n * @returns The column count, at least `1`.\n */\n private dialogColumnCount(checkboxCount: number): number {\n return Math.max(1, Math.ceil(checkboxCount / COLUMN_DIALOG_MAX_PER_COLUMN));\n }\n\n /**\n * Splits `columns` into {@link dialogColumnCount} slices, in display\n * order, as evenly sized as possible — e.g. 22 columns over 2 dialog\n * columns is `[11, 11]`, not `[15, 7]`.\n *\n * @param columns - Resolved columns in display order, from {@link columnsInMenuOrder}.\n * @returns One slice of `columns` per dialog column.\n */\n private splitIntoDialogColumns(columns: Column[]): Column[][] {\n const numColumns = this.dialogColumnCount(columns.length);\n const base = Math.floor(columns.length / numColumns);\n const remainder = columns.length % numColumns;\n\n const slices: Column[][] = [];\n let cursor = 0;\n\n for (let i = 0; i < numColumns; i++) {\n const size = base + (i < remainder ? 1 : 0);\n\n slices.push(columns.slice(cursor, cursor + size));\n cursor += size;\n }\n\n return slices;\n }\n\n /**\n * Builds the show/hide-columns dialog body: {@link splitIntoDialogColumns}'\n * slices laid out side by side, each built by {@link buildColumnDialogColumn}.\n *\n * @param columns - Resolved columns in display order, from {@link columnsInMenuOrder}.\n * @param staged - The dialog's local copy of `_hiddenColumns`, mutated as the user toggles checkboxes.\n * @returns The dialog's `contentComponent`.\n */\n private buildColumnDialogBody(columns: Column[], staged: Set<string>): Component {\n const columnComponents = this.splitIntoDialogColumns(columns)\n .map(slice => this.buildColumnDialogColumn(slice, staged));\n\n return new Component({\n // The dialog is sized to this body's own preferred width (see\n // showColumnDialog), so `justify: \"center\"` is normally a no-op — it\n // only centers the columns when Dialog's own MIN_DIALOG_WIDTH floor\n // (320px) leaves leftover space, e.g. a couple of short-named columns.\n layoutManager: new HBox({ itemAlign: \"start\", justify: \"center\", spacing: COLUMN_DIALOG_COLUMN_GAP_PX }),\n insets: new Insets(COLUMN_DIALOG_INSET_PX, COLUMN_DIALOG_INSET_PX, COLUMN_DIALOG_INSET_PX, COLUMN_DIALOG_INSET_PX),\n components: columnComponents,\n });\n }\n\n /**\n * Builds one column of the show/hide-columns dialog: one `Checkbox` per\n * `columns` entry, indented and grouped under a bold `Text` header for\n * each grouped run — a group split across dialog columns repeats its\n * header at the top of the next one. Every checkbox's `change` listener\n * mutates `staged` only — nothing here writes to the table.\n *\n * @param columns - The slice of resolved columns this dialog column lists.\n * @param staged - The dialog's local copy of `_hiddenColumns`, mutated as the user toggles checkboxes.\n * @returns A single dialog column, ready to sit beside its siblings.\n */\n private buildColumnDialogColumn(columns: Column[], staged: Set<string>): Component {\n const rows: Component[] = [];\n let lastGroup: string | null | undefined = undefined;\n\n for (const col of columns) {\n const fieldName = col.getField().getName();\n const group = col.getGroup();\n\n if (group !== lastGroup && group !== null) {\n const groupHeader = new Text(group, { fontWeight: \"bold\" });\n\n rows.push(groupHeader);\n }\n\n rows.push(new Checkbox({\n label: fieldName,\n selected: !staged.has(fieldName),\n enabled: !col.isUnhideable(),\n insets: group !== null\n ? new Insets(0, 0, 0, COLUMN_DIALOG_GROUP_INDENT_PX)\n : undefined,\n listeners: {\n change: (on: boolean) => {\n if (on) {\n staged.delete(fieldName);\n } else {\n staged.add(fieldName);\n }\n },\n },\n }));\n\n lastGroup = group;\n }\n\n return new Component({\n layoutManager: new VBox({ itemAlign: \"stretch\" }),\n components: rows,\n });\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. Mode-independent:\n * always exports the source table's records and columns, never the\n * rotated field/value projection.\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, this._cellText, options);\n }\n\n /**\n * Triggers a JSON download of the current store view. Mode-independent:\n * always exports the source table's records and columns, never the\n * rotated field/value projection.\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, this._cellText, 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.getSourceColumns();\n }\n\n /**\n * Returns the exact text a cell shows for `field` on `record` — the same\n * string CSV/JSON export and the column filter row resolve a combo label\n * or a formatted date/time/datetime through. Resolved against every\n * resolved column, including a hidden one, so a quick search built on\n * this method still matches a column the user has toggled off.\n *\n * @param field - The model field name to read and format.\n * @param record - The record to read the field from.\n * @returns The cell's display text, or `''` when `field` names no\n * resolved column or the record's value is `null`/`undefined`.\n */\n getCellText(field: string, record: ModelRecord): string {\n const col = this._resolvedColumns.find(c => c.getField().getName() === field);\n\n if (!col) {\n return '';\n }\n\n return String(TableExporter.formatValue(col, record.get(field), this._columnConfigs, this._cellText) ?? '');\n }\n\n /**\n * Captures the dragged edge when a column-resize drag begins: the index of\n * the column whose right edge is being dragged and the pointer's starting\n * `clientX`. {@link onColumnResize} distributes each subsequent frame's\n * travel from this state, nearest-first, across the columns fanning\n * outward from the edge.\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 if (colIndex < 0 || colIndex >= this._columnWidths.length) {\n return;\n }\n\n this._dragEdgeIndex = colIndex;\n this._dragLastClientX = clientX;\n }\n\n /**\n * Handles a column resize drag: the dragged edge splits the visible\n * columns into a left chain `[colIndex, colIndex - 1, …, 0]` and a right\n * chain `[colIndex + 1, …, n - 1]`, each ordered nearest-first. This\n * frame's pointer travel is applied on top of the live widths — the\n * nearest column in the direction of travel absorbs it first, spilling to\n * the next only once it hits its `minWidth`/`maxWidth`.\n *\n * Scavenging from the right chain only happens while the columns still fit\n * the viewport. Moving right, the right chain gives up width first; once it\n * is exhausted, further travel grows the table's total column width instead\n * of stalling, and the table starts to scroll horizontally. From that point\n * on the right chain is left alone entirely — a table already wider than its\n * viewport grows and shrinks as a whole, so the dragged edge only moves the\n * columns left of it. Moving left, that accrued growth is given back first\n * — the total never falls below {@link getAvailableColumnWidth} — and only\n * travel past it, once the table fits again, regrows the right chain. The\n * grown-or-not total is recorded via `_columnWidthTarget` so the layout\n * manager preserves it instead of rescaling it away.\n *\n * The tracked pointer position advances only by the travel actually\n * applied, not the raw `clientX`. When every chain is exhausted the\n * applied travel is zero, so travel past the limit accrues a dead zone the\n * pointer must retrace before the edge moves again — keeping the cursor\n * glued to the handle on reversal instead of the handle jumping to meet a\n * far-off cursor.\n *\n * The pass is queued onto the animation-frame layout queue rather than run\n * synchronously, so every move dispatched within one frame collapses into a\n * single pass. No pass is needed between moves — the drag arithmetic reads\n * only state a layout pass does not produce.\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 private onColumnResize(colIndex: number, clientX: number): void {\n if (this._dragEdgeIndex === null || colIndex !== this._dragEdgeIndex) {\n return;\n }\n\n const widths = this._columnWidths;\n const columns = this.getColumns();\n const mins = columns.map(col => col.getMinWidth() ?? MIN_COLUMN_WIDTH_PX);\n const maxs = columns.map(col => col.getMaxWidth() ?? Number.POSITIVE_INFINITY);\n\n // Nearest-first chains fanning out from the dragged edge.\n const left : number[] = [];\n const right: number[] = [];\n\n for (let i = colIndex; i >= 0; i--) {\n left.push(i);\n }\n\n for (let i = colIndex + 1; i < widths.length; i++) {\n right.push(i);\n }\n\n const frameDelta = clientX - this._dragLastClientX;\n const sign = frameDelta >= 0 ? 1 : -1;\n const available = this.getAvailableColumnWidth();\n const total = widths.reduce((s, w) => s + w, 0);\n // Growth already accrued, which a leftward drag gives back before the right\n // chain grows. Never negative: the total is floored at `available`.\n const growth = Math.max(0, total - available);\n\n const delta = sign > 0\n ? Math.min(frameDelta, chainRoom(left, widths, 1, mins, maxs))\n : Math.min(-frameDelta, chainRoom(left, widths, -1, mins, maxs), chainRoom(right, widths, 1, mins, maxs) + growth);\n\n if (delta <= DRAG_DISTRIBUTION_EPSILON) {\n return; // dead zone — the tracked pointer deliberately stays put\n }\n\n // Rightward: the right chain absorbs everything it can, the rest grows the\n // total — unless the table already overflows, in which case nothing is\n // scavenged and the whole travel grows the total. Leftward: the accrued\n // growth is given back first, and only travel past it (the table fits\n // again) is absorbed by the right chain.\n const absorbed = sign > 0\n ? (growth > WIDTH_TARGET_EPSILON_PX ? 0 : delta)\n : delta - Math.min(delta, growth);\n const out = widths.slice();\n\n distributeDragChain(left, widths, delta, sign, mins, maxs, out);\n distributeDragChain(right, widths, absorbed, -sign, mins, maxs, out);\n\n this._dragLastClientX += sign * delta;\n this._columnWidths = out;\n\n const newTotal = out.reduce((s, w) => s + w, 0);\n\n this._columnWidthTarget = newTotal > available + WIDTH_TARGET_EPSILON_PX ? newTotal : 0;\n\n this.scheduleLayout();\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._columnWidthTarget = 0;\n // The visible column set may change shape (a spec-hidden temporal\n // column reappears, or vice versa), and `_widthRefs`'s date/time/\n // datetime entries are derived from `dateReferenceKeys`' walk of the\n // visible columns — see the same clear in `bindView`.\n this._widthRefs = null;\n\n const columns = this.getColumns();\n const intrinsic = this.getIntrinsicColumnWidths();\n\n this._columnWidths = columns.map((col, i) => intrinsic[i] ?? this.flexColumnWidth(col));\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n this.doLayout();\n }\n\n /**\n * Returns whether `string`/`auto` columns are sized from a sample of\n * the source store's cell values — the normal-mode auto-sizing opt-in.\n * Always `false` in rotated mode: the projection's `field`/`value`\n * columns are always sized from the displayed record's content,\n * independent of this flag; `filler` stays flex either way.\n *\n * @returns `true` when the spec declares `autoSizeColumns: true` AND\n * the table is in normal (non-rotated) display mode.\n */\n isAutoSizeColumns(): boolean {\n return (this._spec?.autoSizeColumns ?? false) && this._displayMode !== \"rotated\";\n }\n\n /**\n * Returns the column's declared `minWidth`, or the floor its field type\n * implies. Cheap enough for the drag path — reads cached reference\n * measurements and a cached sample-candidate map, never the store or\n * the DOM.\n *\n * @param column - The column to compute a minimum width for.\n * @returns The minimum width in pixels.\n */\n getColumnMinWidth(column: Column): number {\n return column.getMinWidth() ?? this.columnWidthPolicy(column, 0, null).min;\n }\n\n /**\n * Computes one starting width per visible column, in display order,\n * from each column's field-type policy, any declared `width` /\n * `minWidth` / `maxWidth`, and content — for `string`/`auto` columns\n * under {@link isAutoSizeColumns}, a bounded sample of the store; for\n * the rotated `field`/`value` columns, the currently displayed record.\n *\n * @returns One entry per visible column. A number is a definite\n * starting width; `null` means \"no definite width — share the\n * remaining space\", which only happens for a `string`/`auto` column\n * with no auto-sized content to measure.\n */\n getIntrinsicColumnWidths(): Array<number | null> {\n const columns = this.getColumns();\n const headerPx = this.measureHeaders(columns);\n const contentPx = this.measureContent(columns);\n\n return columns.map((col, i) => {\n const policy = this.columnWidthPolicy(col, headerPx[i], contentPx[i]);\n const raw = col.getWidth() ?? policy.preferred;\n\n if (raw === null) {\n return null;\n }\n\n return this.clampColumnWidth(raw, col, policy);\n });\n }\n\n /**\n * Clamps a raw width into a column's `[minWidth, maxWidth]` envelope,\n * falling back to the policy floor and {@link AUTO_WIDTH_CAP_PX} when\n * the column declares no explicit bound.\n */\n private clampColumnWidth(w: number, col: Column, policy: WidthPolicy): number {\n return Util.clamp(w, col.getMinWidth() ?? policy.min, col.getMaxWidth() ?? AUTO_WIDTH_CAP_PX);\n }\n\n /**\n * Returns the width, in pixels, one column should start at when it\n * cannot share leftover space with its siblings — the fallback used by\n * {@link setColumnVisible} and {@link resetColumns} for a flex column\n * whose `getIntrinsicColumnWidths` entry is `null`. Measures only this\n * one column's header, so it is the one exception to the\n * three-batched-calls derivation budget; it runs only on that fallback\n * path.\n *\n * @param col - The flex column to compute a standalone width for.\n */\n private flexColumnWidth(col: Column): number {\n const headerPx = this.measureHeaders([col])[0];\n const policy = this.columnWidthPolicy(col, headerPx, null);\n\n return this.clampColumnWidth(Math.max(STRING_WIDTH_PX, headerPx), col, policy);\n }\n\n /**\n * Returns the header text width in pixels, plus {@link HEADER_CHROME_PX},\n * for each column — one batched measurement under the header font.\n */\n private measureHeaders(columns: Column[]): number[] {\n const texts = columns.map(col => col.getHeaderText() ?? col.getField().getName());\n const widths = Util.measureTextWidths(texts, {\n fontSize : \"var(--ts-ui-table-header-font-size, var(--ts-ui-font-size))\",\n fontWeight: \"bold\",\n });\n\n return widths.map(w => w + HEADER_CHROME_PX);\n }\n\n /**\n * Returns the sampled content width in pixels for each column — `null`\n * for every column the policy does not need content for. The only part\n * of the derivation that reads the store (via {@link collectCandidates}).\n * A single batched measurement covers every `string`/`auto` column with\n * something to measure.\n */\n private measureContent(columns: Column[]): Array<number | null> {\n this.collectCandidates(columns);\n\n const measurable = columns.map(col => this.resolveContentCandidates(col));\n\n const toMeasure: string[] = [];\n const owner: number[] = [];\n\n measurable.forEach((candidates, i) => {\n candidates?.forEach(text => {\n owner.push(i);\n toMeasure.push(text);\n });\n });\n\n const widths = Util.measureTextWidths(toMeasure);\n const contentPx: Array<number | null> = columns.map(() => null);\n\n widths.forEach((w, k) => {\n const i = owner[k];\n\n contentPx[i] = Math.max(contentPx[i] ?? 0, w);\n });\n\n return contentPx;\n }\n\n /**\n * Resolves the strings to measure for one `string`/`auto` column. In\n * rotated mode, delegates to {@link resolveRotatedContentCandidates}\n * unconditionally — {@link isAutoSizeColumns} does not apply to the\n * rotated projection. Otherwise falls back from sampled candidates to\n * `values` option labels, to a `maxContentLength` probe string, in that\n * order, gated by {@link isAutoSizeColumns}. Returns `null` for every\n * other field type, for every `string`/`auto` column when\n * {@link isAutoSizeColumns} is `false` in normal mode (the rest of the\n * normal-mode chain is gated by the flag, not just the store sample),\n * and for a `string`/`auto` column under auto-size with nothing to\n * measure (the column stays flex).\n */\n private resolveContentCandidates(col: Column): string[] | null {\n const type = col.getField().getType();\n\n if (type !== \"string\" && type !== \"auto\") {\n return null;\n }\n\n if (this._displayMode === \"rotated\") {\n return this.resolveRotatedContentCandidates(col);\n }\n\n // autoSizeColumns gates the rest of this normal-mode fallback chain,\n // not just the store sample: with the flag off a string/auto column\n // stays flex, exactly as before this feature, even when it declares\n // `values` or `maxContentLength`. Rotated mode is handled above,\n // unconditionally.\n if (!this.isAutoSizeColumns()) {\n return null;\n }\n\n const sampled = this._sampledCandidates.get(col.getField().getName());\n\n if (sampled && sampled.length > 0) {\n return sampled;\n }\n\n const config = this._columnConfigs.get(col.getField().getName());\n\n if (config?.values && config.values.length > 0) {\n return normalizeComboOptions(config.values)\n .map(o => o.label)\n .sort((a, b) => b.length - a.length)\n .slice(0, WIDEST_CANDIDATES);\n }\n\n const maxLen = col.getMaxContentLength();\n\n if (maxLen !== undefined) {\n return [\"0\".repeat(Math.min(maxLen, HINT_SAMPLE_MAX_CHARS))];\n }\n\n return null;\n }\n\n /**\n * Content candidates for the rotated `field`/`value` columns, measured\n * from the currently displayed record instead of a store sample — the\n * rotated store holds at most one row per visible source field, so every\n * row is used exactly, not a sampled subset.\n *\n * @param col - The rotated `field`, `value`, or `filler` column.\n * @returns Up to `WIDEST_CANDIDATES` distinct candidate strings, longest\n * first, or `null` for `filler` (stays flex, absorbing the leftover\n * width) or when there is nothing to measure (no displayed record).\n */\n private resolveRotatedContentCandidates(col: Column): string[] | null {\n const name = col.getField().getName();\n\n if (name !== 'field' && name !== 'value') {\n return null;\n }\n\n const records = this.ensureRotatedStore().getRecords();\n const sourceColumns = name === 'value'\n ? new Map(this.getSourceColumns().map(c => [c.getField().getName(), c]))\n : null;\n const list: string[] = [];\n\n for (const record of records) {\n const text = name === 'field'\n ? String(record.get('field') ?? '')\n : this.formatRotatedValueText(record, sourceColumns!);\n\n if (text !== null) {\n this.keepLongest(list, text);\n }\n }\n\n return list.length > 0 ? list : null;\n }\n\n /**\n * Formats one rotated `value` row's text the way its own resolved\n * `DynamicCell` renderer actually displays it — mirrors\n * {@link DynamicCell.bindRecord}'s variant resolution so the measured\n * string matches the rendered one.\n *\n * @param record - One projection record (`field`/`value` pair).\n * @param sourceColumns - Visible source columns keyed by field name.\n * @returns The display text, or `null` for `boolean`/`glyph` rows (a\n * fixed-size control, not measurable text) or an unresolvable field.\n */\n private formatRotatedValueText(record: ModelRecord, sourceColumns: Map<string, Column>): string | null {\n const cellType = this.rotatedCellType(record);\n\n if (cellType === 'boolean' || cellType === 'glyph') {\n return null;\n }\n\n if (cellType === 'combo') {\n const labelByValue = new Map(normalizeComboOptions(this.rotatedCellValues(record) ?? []).map(o => [o.value, o.label]));\n const value = String(record.get('value') ?? '');\n\n return labelByValue.get(value) ?? value; // mirrors ComboRenderer.setValue\n }\n\n const sourceColumn = sourceColumns.get(record.get('field') as string);\n\n if (!sourceColumn) {\n return null;\n }\n\n // Empty configs map, not this._columnConfigs: the rotated `value`\n // DynamicCell's own config never threads `showSeconds` through\n // (ensureRotatedStore's spec doesn't set it on the `value` column), so\n // every time/datetime row renders without seconds today regardless of\n // the source column's own setting. Matching that here keeps the\n // measured text the same length as what actually renders.\n return String(TableExporter.formatValue(sourceColumn, record.get('value'), new Map(), this._cellText) ?? '');\n }\n\n /**\n * Reads at most {@link SAMPLE_ROWS} records by index and keeps, per\n * column that needs content (see {@link samplesRecordText}), the\n * {@link WIDEST_CANDIDATES} longest distinct formatted values. Stores\n * the result in `_sampledCandidates`, keyed by field name, for\n * {@link resolveContentCandidates} and {@link sampledDigits} to consult.\n * Reads by `store.getAt` rather than `store.getRecords()`, which\n * copies the entire filtered view.\n */\n private collectCandidates(columns: Column[]): void {\n const wanted = columns.map(col => this.samplesRecordText(col));\n const rows = Math.min(SAMPLE_ROWS, this._store.getCount());\n const best = new Map<string, string[]>();\n\n for (let r = 0; r < rows; r++) {\n const record = this._store.getAt(r)!;\n\n columns.forEach((col, i) => {\n if (!wanted[i]) {\n return;\n }\n\n const name = col.getField().getName();\n const raw = record.get(name);\n const text = String(TableExporter.formatValue(col, raw, this._columnConfigs, this._cellText) ?? \"\");\n const list = best.get(name) ?? [];\n\n this.keepLongest(list, text);\n best.set(name, list);\n });\n }\n\n if (rows > 0) {\n this._autoWidthsSampled = true;\n }\n\n this._sampledCandidates = best;\n }\n\n /**\n * Keeps `list` to at most {@link WIDEST_CANDIDATES} entries, longest\n * first, dropping an exact duplicate of a string already held.\n */\n private keepLongest(list: string[], text: string): void {\n if (list.includes(text)) {\n return;\n }\n\n list.push(text);\n list.sort((a, b) => b.length - a.length);\n\n if (list.length > WIDEST_CANDIDATES) {\n list.length = WIDEST_CANDIDATES;\n }\n }\n\n /**\n * Returns whether `collectCandidates` should read this column's values:\n * always for `number` (its sample feeds a digit count, not a\n * measurement), and for `string`/`auto` only under\n * {@link isAutoSizeColumns}. A column with a `renderer` is never\n * sampled — its rendered text is not derived from the raw value. Nor is\n * a column that declares `values`: it uses its option labels instead of\n * the store, never as well as it.\n */\n private samplesRecordText(col: Column): boolean {\n const config = this._columnConfigs.get(col.getField().getName());\n\n if (config?.renderer) {\n return false;\n }\n\n if (config?.values && config.values.length > 0) {\n return false;\n }\n\n const type = col.getField().getType();\n\n if (type === \"number\") {\n return true;\n }\n\n return (type === \"string\" || type === \"auto\") && this.isAutoSizeColumns();\n }\n\n /**\n * Returns the longest `String(value).length` seen for `col` during the\n * last {@link collectCandidates} pass, or `null` when the column was\n * not sampled. A length scan over the cached candidates, never a\n * measurement and never a store read.\n */\n private sampledDigits(col: Column): number | null {\n const candidates = this._sampledCandidates.get(col.getField().getName());\n\n if (!candidates || candidates.length === 0) {\n return null;\n }\n\n return Math.max(...candidates.map(s => s.length));\n }\n\n /** Reads `showSeconds` from this column's config, defaulting to `false`. */\n private showsSeconds(col: Column): boolean {\n return this._columnConfigs.get(col.getField().getName())?.showSeconds ?? false;\n }\n\n /**\n * Returns the per-type width policy for `col`: a `{min, preferred}`\n * pair derived from its field type, cached reference measurements, and\n * — for `number`/`string`/`auto` — sampled content. `headerPx` and\n * `contentPx` may be passed as `0`/`null` (as {@link getColumnMinWidth}\n * does) because no branch's `min` depends on either.\n */\n private columnWidthPolicy(col: Column, headerPx: number, contentPx: number | null): WidthPolicy {\n const refs = this.ensureWidthReferences();\n const type = col.getField().getType();\n\n switch (type) {\n case \"boolean\": {\n const min = CHECKBOX_WIDTH_PX + CELL_CHROME_PX;\n\n return { min, preferred: Math.max(min, headerPx) };\n }\n\n case \"glyph\": {\n const min = GLYPH_WIDTH_PX + CELL_CHROME_PX;\n\n return { min, preferred: Math.max(min, headerPx) };\n }\n\n case \"date\":\n case \"time\":\n case \"datetime\": {\n const key = `${type}:${this.showsSeconds(col)}`;\n const min = (refs.datePx.get(key) ?? 0) + CELL_CHROME_PX;\n\n return { min, preferred: Math.max(min, headerPx) };\n }\n\n case \"number\": {\n const min = refs.digitPx * MIN_NUMBER_DIGITS + CELL_CHROME_PX;\n const digits = col.getMaxContentLength() ?? this.sampledDigits(col) ?? DEFAULT_NUMBER_DIGITS;\n\n return { min, preferred: Math.max(min, refs.digitPx * digits + CELL_CHROME_PX, headerPx) };\n }\n\n default: { // \"string\" and \"auto\"\n const min = Math.max(MIN_COLUMN_WIDTH_PX, refs.digitPx * MIN_STRING_CHARS + CELL_CHROME_PX);\n\n if (contentPx === null) {\n return { min, preferred: null }; // auto-size off, or nothing to measure — flex column\n }\n\n // A rotated group member's `field`-name cell renders\n // indented by DEFAULT_INDENT_PX (see `Row.setFieldIndent`);\n // reserve that space here too, or an indented row's text\n // would overflow a column sized purely from raw text width.\n // Reserved whenever ANY row is currently indented, not\n // per-candidate — `_rotatedIndentedRecords` is empty\n // whenever the projection is sorted or has no groups, so\n // this adds nothing then.\n const indent = this._displayMode === \"rotated\"\n && col.getField().getName() === \"field\"\n && this._rotatedIndentedRecords.size > 0\n ? DEFAULT_INDENT_PX\n : 0;\n\n return { min, preferred: Math.max(min, contentPx + CELL_CHROME_PX + indent, headerPx) };\n }\n }\n }\n\n /**\n * Returns the cached width-reference bundle, measuring it once (in one\n * batched call) the first time anything asks. `dateReferenceKeys` scans\n * only the currently visible columns, so this is cleared everywhere\n * that set can change: `setStore`, `maybeResampleColumnWidths`,\n * `bindView` (a display-mode switch), `setColumnVisible`, and\n * `resetColumns`.\n */\n private ensureWidthReferences(): WidthReferences {\n if (this._widthRefs) {\n return this._widthRefs;\n }\n\n const digits = [\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"];\n const keys = this.dateReferenceKeys();\n const widths = Util.measureTextWidths([...digits, ...keys.flatMap(k => k.texts)]);\n\n const datePx = new Map<string, number>();\n let offset = digits.length;\n\n for (const k of keys) {\n datePx.set(k.key, Math.max(...widths.slice(offset, offset + k.texts.length)));\n offset += k.texts.length;\n }\n\n this._widthRefs = {\n digitPx: Math.max(...widths.slice(0, digits.length)),\n datePx,\n };\n\n return this._widthRefs;\n }\n\n /**\n * Walks the visible columns and builds one `{key, texts}` reference pair\n * per distinct `(temporal type, showSeconds)` combination in use,\n * formatting {@link REFERENCE_DATE} the same way the matching cell\n * renderer would (via `TableExporter.formatValue`), then widening the\n * probe with every digit position substituted by each of 0-9 in turn —\n * guards against a non-tabular font rendering some other digit wider\n * than `REFERENCE_DATE`'s own digits, mirroring the per-digit-max\n * defense `digitPx` already applies to `number` columns. A no-op (all\n * variants equal) under a tabular font.\n */\n private dateReferenceKeys(): Array<{ key: string; texts: string[] }> {\n const digitChars = [\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"];\n const seen = new Map<string, string[]>();\n\n for (const col of this.getColumns()) {\n const type = col.getField().getType();\n\n if (type !== \"date\" && type !== \"time\" && type !== \"datetime\") {\n continue;\n }\n\n const key = `${type}:${this.showsSeconds(col)}`;\n\n if (!seen.has(key)) {\n const base = String(TableExporter.formatValue(col, REFERENCE_DATE, this._columnConfigs, this._cellText) ?? \"\");\n\n seen.set(key, [base, ...digitChars.map(d => base.replace(/\\d/g, d))]);\n }\n }\n\n return Array.from(seen, ([key, texts]) => ({ key, texts }));\n }\n\n /**\n * Re-derives column widths once, the first time the source store's\n * `'load'` / `'add'` / `'remove'` / `'datachange'` events find records —\n * so a table built before its data arrives sizes itself against the\n * data once it shows up. A no-op when auto-size is off, when the store\n * is still empty, or after the first successful derivation for this\n * store (see {@link Table.setStore}, which resets the guard).\n */\n private maybeResampleColumnWidths(): void {\n if (!this.isAutoSizeColumns() || this._autoWidthsSampled || this._store.getCount() === 0) {\n return;\n }\n\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._columnWidthTarget = 0;\n this._widthRefs = null;\n\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 type { ColumnSpec } from \"~/component/table/ColumnConfig.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 /**\n * Constructs a TablePanel bound to the given store.\n *\n * @param store - The data store to bind to the managed table.\n * @param spec - Optional column spec, forwarded to the underlying\n * {@link Table} unchanged; omit to auto-generate all columns.\n */\n constructor(store: AbstractStore, spec?: ColumnSpec) {\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, spec);\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 { Event } from \"~/core/Event.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 * @remarks Does not consult `_rowVisible` — `Table.setRowVisible`'s\n * predicate is a documented no-op on `TreeTable` (see the `TreeTable`\n * docs non-goal), because a flat per-record filter cannot decide what\n * happens to a hidden parent's children without the parent/child index\n * this method has no access to.\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): Event.ListenerResult {\n if (e.key !== \"ArrowRight\" && e.key !== \"ArrowLeft\") {\n return super.onKeyDown(e);\n }\n\n const anchor = this.getSelectedRecord();\n\n if (!anchor) {\n return super.onKeyDown(e);\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 return { prevent: true };\n }\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 return { prevent: true };\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 private _display: string = \"\";\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 this.setUserSelect(\"text\");\n this._text.setUserSelect(\"text\");\n this.setCursor(\"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._display = this._value === null ? \"\" : String(this._value);\n this._text.setText(this._display);\n }\n\n /**\n * Returns the exact text last rendered — the same string {@link setValue}\n * pushed into the child {@link Link}. Computed from cached state, never\n * the DOM.\n *\n * @returns The cell's current display text.\n */\n getDisplayText(): string {\n return this._display;\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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"}