@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 +0,0 @@
1
- {"version":3,"file":"Tooltip-B_UF5bXD.js","names":[],"sources":["../../src/typescript/lib/core/LayerManager.ts","../../src/typescript/lib/core/OverlayPosition.ts","../../src/typescript/lib/overlay/Tooltip.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * How a layer responds to an outside interaction.\n *\n * - `\"click-outside\"`: dismissed when a `pointerdown` lands outside the\n * layer's subtree (and its anchor); ignores focus moves.\n * - `\"blur\"`: dismissed when either a `pointerdown` or a focus move lands\n * outside the layer's subtree (and its anchor).\n * - `\"manual\"`: never dismissed by the document-level handlers; the host\n * drives its own teardown.\n * - `\"modal\"`: never dismissed by an outside `pointerdown` or focus move\n * (the layer captures the interaction); only Escape, routed through the\n * keydown handler, asks it to close.\n *\n * @category Core\n */\nexport type LayerDismissMode = \"click-outside\" | \"blur\" | \"manual\" | \"modal\";\n\n/**\n * A portaled overlay surface that participates in the runtime layer tree.\n * Implemented by [`AnimatedDropdown`](/api/core/classes/AnimatedDropdown),\n * [`Popover`](/api/overlay/classes/Popover),\n * [`Dialog`](/api/overlay/classes/Dialog), and\n * [`Window`](/api/overlay/classes/Window).\n *\n * A surface registers itself with {@link LayerManager.register} from its\n * show path and unregisters with {@link LayerManager.unregister} from its\n * hide path. The manager then owns the z-index stamp, the activation\n * marking, and the dismissal decision — calling {@link DismissableLayer.requestClose}\n * (advisory) when an outside interaction meets the layer's\n * {@link LayerDismissMode}.\n *\n * @category Core\n */\nexport interface DismissableLayer {\n /** The layer's root element (already mounted on `documentElement`). */\n getLayerElement(): Handle | null;\n\n /** Dismiss policy consulted by the document-level interaction handlers. */\n getDismissMode(): LayerDismissMode;\n\n /** Advisory request to close; the surface runs its own teardown + unregister. */\n requestClose(): void;\n\n /**\n * Optional anchor element excluded from \"outside\" tests so the trigger\n * click that opened the layer does not immediately re-close it.\n */\n getAnchorElement?(): Handle | null;\n\n /**\n * Optional activation hook. The manager calls it with `true` when this\n * layer becomes the active (topmost containing) layer for a pointer /\n * focus interaction and `false` when it is superseded. Surfaces that\n * carry an active-state affordance (e.g. a window title bar) map it to\n * their own toggle; surfaces without one omit the method.\n */\n onActivate?(active: boolean): void;\n\n /**\n * Optional re-stamp hook. The manager calls it with a fresh z-index when\n * {@link LayerManager.bringToFront} re-allocates the layer (or one of its\n * ancestors) while it is already shown, so the surface can mirror the new\n * value onto its element through its own typed `setZIndex`. Surfaces that\n * only read z once in their show path (and never get raised afterwards)\n * may omit it.\n */\n onZIndexChanged?(zIndex: number): void;\n\n /**\n * Optional z-index band hint. Returns one of {@link LayerManager.Band}'s\n * values so an unrelated peer layer stacks in its surface family's band\n * (Window < Popover < dropdown < Dialog). Omitted surfaces default to the\n * dropdown band. A nested layer ignores its own band and inherits its\n * opener's, so this only matters for top-level (unparented) registrations.\n */\n getBand?(): number;\n\n /**\n * Optional top-level hint. Returns `true` for a surface that is an\n * independent peer rather than a layer opened from another — a window is\n * not a child of whatever happened to be topmost when it appeared. Such a\n * layer registers as a tree root, so raising it does not drag an unrelated\n * peer up with it (see {@link LayerManager.bringToFront}). Omitted surfaces\n * register under the current topmost layer (the opened-from edge).\n */\n isLayerRoot?(): boolean;\n}\n\n/**\n * Node in the runtime layer tree. Generalizes the per-host `LayerEntry`\n * that `AnimatedDropdown` previously kept module-private: `children` are the\n * layers opened while this one was topmost, `band` is the z-index band base\n * the layer was assigned (inherited from its opener), and `zIndex` is the\n * band + counter stamp the manager assigned at register time.\n */\ninterface LayerNode {\n layer: DismissableLayer;\n parent: LayerNode | null;\n children: LayerNode[];\n band: number;\n zIndex: number;\n}\n\n// Z-index bands. Plain module constants because z-index is not themed\n// anywhere today (Theme.ts carries no z-index tokens; the values were inline\n// per class). The bands reconcile the four historical bases into one\n// ascending allocator and preserve relative order between unrelated peers:\n// Window 9000 < Popover 9800 < dropdowns 10000 < Dialog 11000.\n// A nested child inherits its opener's band but always lands above it because\n// it registers later and so draws a higher counter. The 200-1000 gap between\n// bands leaves headroom for the monotonic `_zCounter`; it is not reset, on the\n// assumption a single session opens far fewer than 200 unrelated layers in the\n// same band before a reload — the historical inline values made the same bet.\nconst Z_BAND_WINDOW: number = 9000;\nconst Z_BAND_POPOVER: number = 9800;\nconst Z_BAND_DROPDOWN: number = 10000;\nconst Z_BAND_DIALOG: number = 11000;\n// Above every managed layer: a tooltip is a transient, non-interactive\n// affordance that must float over even a modal Dialog and its backdrop. Not a\n// registered layer band — exposed so the Tooltip singleton stamps itself here\n// rather than carrying a magic number that could fall below the Dialog band.\nconst Z_BAND_TOOLTIP: number = 12000;\n\n/**\n * Sentinel `Component` used as the registration key for the manager's three\n * document-level listeners. `Event.addViewportListener` binds a listener to a\n * `Component`, but the manager's handlers are module-global (one per type, not\n * per layer), so a single stable sentinel owns them — mirroring `Window`'s\n * `_viewportListenerOwner`. The install/teardown lifecycle keys off the tree\n * being non-empty rather than any individual layer.\n */\nconst _listenerOwner: Component = new Component();\n\n/**\n * Central registry and document-level interaction broker for portaled overlay\n * surfaces. Owns one runtime layer tree (push-on-register /\n * link-under-current-topmost / pop-on-unregister), one z-index allocator with\n * reserved bands, and a single set of document-level `pointerdown` / `focusin`\n * / `keydown` listeners installed on the first register and removed on the\n * last unregister.\n *\n * The relationship the tree captures — the runtime \"opened-from\" edge (which\n * layer was topmost when this one opened) — is distinct from the static\n * component hierarchy and changes per activation, so it lives here rather than\n * in the component tree. Containment queries hop across portaled descendant\n * layers via {@link LayerManager.containsAcrossLayers}, which is the single\n * place cross-portal containment is reasoned about.\n *\n * @category Core\n */\nexport namespace LayerManager {\n\n // LIFO stack of open layers in register order; the last entry is topmost.\n const _stack: LayerNode[] = [];\n\n // Membership + node lookup keyed by the layer instance, generalizing the\n // per-host `_entryByLayer` WeakMap.\n const _nodeByLayer: WeakMap<DismissableLayer, LayerNode> = new WeakMap();\n\n // Ascending per-register counter; combined with a band base to stamp z.\n let _zCounter: number = 0;\n\n // The layer currently marked active (received the last `onActivate(true)`).\n let _activeLayer: DismissableLayer | null = null;\n\n let _listenersInstalled: boolean = false;\n\n /**\n * The four z-index bands a surface returns from\n * {@link DismissableLayer.getBand}. Exposed so each surface can tag itself\n * without re-declaring the constants. Reconciles the historical inline\n * bases (Window 9000, Popover 9998, dropdowns 10050, Dialog 10101) into\n * one ascending allocator.\n */\n export const Band = {\n Window: Z_BAND_WINDOW,\n Popover: Z_BAND_POPOVER,\n Dropdown: Z_BAND_DROPDOWN,\n Dialog: Z_BAND_DIALOG,\n Tooltip: Z_BAND_TOOLTIP,\n } as const;\n\n /**\n * Picks the z-index band for a layer. A nested layer inherits its\n * opener's band so it stays in the same stacking neighbourhood (and rises\n * above the opener because it registers later); an unrelated peer uses its\n * own surface-type band from {@link DismissableLayer.getBand}.\n */\n function bandFor(parent: LayerNode | null, ownBand: number): number {\n return parent ? parent.band : ownBand;\n }\n\n /**\n * Pushes `layer` as a child of the current topmost layer — unless it\n * declares itself a top-level peer via\n * {@link DismissableLayer.isLayerRoot}, in which case it registers as a\n * tree root — assigns its band-based z-index, and installs the\n * document-level listeners on the first call. A duplicate register (e.g. a\n * `showAnimated` that cancels an in-flight fade-out) is a no-op so the tree\n * never double-pushes.\n *\n * @param layer - The surface entering the layer tree.\n */\n export function register(layer: DismissableLayer): void {\n if (_nodeByLayer.has(layer)) {\n return;\n }\n\n const topmost = _stack.length > 0 ? _stack[_stack.length - 1] : null;\n const parent = layer.isLayerRoot?.() ? null : topmost;\n const band = bandFor(parent, layer.getBand?.() ?? Z_BAND_DROPDOWN);\n const zIndex = band + (++_zCounter);\n\n const node: LayerNode = { layer, parent, children: [], band, zIndex };\n\n _nodeByLayer.set(layer, node);\n _stack.push(node);\n\n if (parent) {\n parent.children.push(node);\n }\n\n if (!_listenersInstalled) {\n installListeners();\n }\n }\n\n /**\n * Appends a portaled overlay element to `document.documentElement` if it is\n * not already contained there. The single home for the \"portal mount\"\n * idiom every floating surface used to inline. Idempotent — a surface that\n * re-shows after detach re-appends, and one already mounted is left alone.\n * Does **not** register the layer; surfaces still call {@link register}\n * explicitly.\n *\n * @param el - The overlay element to portal onto the document root.\n */\n export function mount(el: Handle): void {\n if (!DOM.source.contains(DOM.source.getDocumentElement(), el)) {\n DOM.sink.appendChild(DOM.source.getDocumentElement(), el);\n }\n }\n\n /**\n * Pops `layer` from the tree (and unlinks it from its parent's child\n * list), clears it as active if it was, and removes the document-level\n * listeners on the last call.\n *\n * @param layer - The surface leaving the layer tree.\n */\n export function unregister(layer: DismissableLayer): void {\n const node = _nodeByLayer.get(layer);\n\n if (!node) {\n return;\n }\n\n _nodeByLayer.delete(layer);\n\n const idx = _stack.indexOf(node);\n\n if (idx >= 0) {\n _stack.splice(idx, 1);\n }\n\n if (node.parent) {\n const ci = node.parent.children.indexOf(node);\n\n if (ci >= 0) {\n node.parent.children.splice(ci, 1);\n }\n }\n\n if (_activeLayer === layer) {\n _activeLayer = null;\n }\n\n if (_stack.length === 0 && _listenersInstalled) {\n uninstallListeners();\n }\n }\n\n /**\n * Returns true when `node` is inside `layer`'s own element OR inside any\n * descendant layer's element. This is the only cross-portal containment\n * hop in the framework: a `pointerdown` or focus move that lands in a\n * child layer (e.g. a dropdown opened from inside a popover) counts as\n * inside the ancestor so the ancestor stays open.\n *\n * @param layer - The layer whose subtree is tested.\n * @param node - The DOM node receiving the interaction.\n * @returns True when `node` is inside `layer` or any descendant layer.\n */\n export function containsAcrossLayers(layer: DismissableLayer, node: Handle | null): boolean {\n if (!node) {\n return false;\n }\n\n const layerNode = _nodeByLayer.get(layer);\n\n if (!layerNode) {\n const el = layer.getLayerElement();\n\n return el ? DOM.source.contains(el, node) : false;\n }\n\n return nodeContains(layerNode, node);\n }\n\n /**\n * Returns the topmost currently-registered layer, or null when none is\n * open.\n *\n * @returns The topmost open layer, or null.\n */\n export function getTopLayer(): DismissableLayer | null {\n return _stack.length > 0 ? _stack[_stack.length - 1].layer : null;\n }\n\n /**\n * Re-stamps `layer` (and its descendant layers) with fresh top-of-band\n * z-indices and marks it active. Used by surfaces that raise on click\n * (e.g. a window brought to front), so the raised layer — and anything it\n * opened — jumps above its band peers without disturbing other bands.\n *\n * @param layer - The layer to raise and activate.\n */\n export function bringToFront(layer: DismissableLayer): void {\n const node = _nodeByLayer.get(layer);\n\n if (!node) {\n return;\n }\n\n restampSubtree(node);\n markActive(layer);\n }\n\n /**\n * Returns the z-index currently assigned to `layer`, for surfaces that\n * mirror it elsewhere. Falls back to the dropdown band when the layer is\n * not registered.\n *\n * @param layer - The layer whose stamp to read.\n * @returns The assigned z-index.\n */\n export function getZIndex(layer: DismissableLayer): number {\n const node = _nodeByLayer.get(layer);\n\n return node ? node.zIndex : Z_BAND_DROPDOWN;\n }\n\n /**\n * Re-stamps `node`'s subtree from a fresh top-of-band counter run so the\n * raised layer and its descendants ascend together, preserving their\n * relative order. Each re-stamped layer is notified via\n * {@link DismissableLayer.onZIndexChanged} so it can mirror the new value\n * through its own typed `setZIndex` (the manager never writes the DOM).\n */\n function restampSubtree(node: LayerNode): void {\n const walk = (n: LayerNode): void => {\n n.zIndex = n.band + (++_zCounter);\n n.layer.onZIndexChanged?.(n.zIndex);\n\n for (const child of n.children) {\n walk(child);\n }\n };\n\n walk(node);\n }\n\n /** True when `node` (or a descendant layer) contains `target`. */\n function nodeContains(node: LayerNode, target: Handle): boolean {\n const el = node.layer.getLayerElement();\n\n if (el && DOM.source.contains(el, target)) {\n return true;\n }\n\n for (const child of node.children) {\n if (nodeContains(child, target)) {\n return true;\n }\n }\n\n return false;\n }\n\n /** Marks `layer` active and deactivates the previously-active layer. */\n function markActive(layer: DismissableLayer): void {\n if (_activeLayer === layer) {\n return;\n }\n\n deactivateActive();\n\n _activeLayer = layer;\n\n if (layer.onActivate) {\n layer.onActivate(true);\n }\n }\n\n /** Deactivates the currently-active layer, if any. */\n function deactivateActive(): void {\n if (_activeLayer && _activeLayer.onActivate) {\n _activeLayer.onActivate(false);\n }\n\n _activeLayer = null;\n }\n\n /**\n * Walks up the \"opened-from\" chain from `node` and returns the nearest\n * ancestor-or-self layer that carries an activation affordance\n * ({@link DismissableLayer.onActivate}), or `null` if none does. A layer\n * opened inside a window resolves to that window, so the window stays\n * active while its descendant holds focus — interacting with a child layer\n * must not darken the title bar of the window that owns it.\n */\n function activatableAncestor(node: LayerNode): DismissableLayer | null {\n let cur: LayerNode | null = node;\n\n while (cur) {\n if (cur.layer.onActivate) {\n return cur.layer;\n }\n\n cur = cur.parent;\n }\n\n return null;\n }\n\n /**\n * Walks the stack top-down for a pointer / focus interaction at `target`,\n * dismissing each topmost layer whose dismiss mode and containment test\n * say the interaction landed outside it, and marking the topmost\n * containing layer active. `focusOnly` is true for the `focusin` pass, so\n * only `\"blur\"` layers act on it (a `\"click-outside\"` layer ignores focus\n * moves).\n */\n function handleOutside(target: Handle | null, focusOnly: boolean): void {\n // Snapshot top-down so requestClose-driven unregisters don't disturb\n // the walk; re-read containment against the live tree each step.\n const snapshot = _stack.slice().reverse();\n let activated = false;\n let landedInside = false;\n\n for (const node of snapshot) {\n const mode = node.layer.getDismissMode();\n\n if (containsAcrossLayers(node.layer, target)) {\n // Activation belongs to the nearest activatable ancestor (the\n // window a child layer was opened from), not the innermost\n // containing layer; fall back to the layer itself when nothing\n // up the chain carries an activation affordance, preserving the\n // prior behaviour for standalone layers.\n markActive(activatableAncestor(node) ?? node.layer);\n activated = true;\n landedInside = true;\n\n break;\n }\n\n const anchor = node.layer.getAnchorElement?.();\n\n if (anchor && target && DOM.source.contains(anchor, target)) {\n landedInside = true;\n\n break;\n }\n\n const dismissable = mode === \"click-outside\" || mode === \"blur\";\n const acts = focusOnly ? mode === \"blur\" : dismissable;\n\n if (mode === \"manual\" || mode === \"modal\" || !acts) {\n // Modal captures the interaction: an outside pointer/focus on a\n // modal must not fall through to dismiss layers beneath it.\n if (mode === \"modal\") {\n landedInside = true;\n\n break;\n }\n\n continue;\n }\n\n node.layer.requestClose();\n }\n\n // An interaction that landed outside every layer's subtree (and any\n // anchor / modal capture) deactivates the active layer — e.g. an\n // empty-viewport click that should drop a window's active title-bar\n // highlight. A focus move to a non-layer element (focusOnly) does not\n // deactivate, so tabbing through page chrome doesn't flicker the\n // active window.\n if (!activated && !landedInside && !focusOnly) {\n deactivateActive();\n }\n }\n\n /** Document `pointerdown` handler — see {@link handleOutside}. */\n function onPointerDown(e: PointerEvent): void {\n handleOutside(e.target === null ? null : DOM.source.intern(e.target), false);\n }\n\n /**\n * Window `blur` handler. When the whole browser window loses focus (the user\n * clicks another application or alt-tabs away) no in-page `pointerdown` or\n * `focusin` fires, so an open layer would otherwise stay up. Treat it as an\n * interaction landing outside every layer, dismissing each `\"click-outside\"`\n * / `\"blur\"` layer while a `\"modal\"` shields those beneath it and `\"manual\"`\n * layers (windows) are left open.\n *\n * @remarks Viewport listeners are capture-phase, so element blurs from within\n * the page surface here too; only a genuine window blur (`target` is the\n * window itself) should dismiss layers.\n */\n function onWindowBlur(e: FocusEvent): void {\n if (!DOM.source.isWindow(e.target === null ? null : DOM.source.intern(e.target))) {\n return;\n }\n\n handleOutside(null, false);\n }\n\n /** Document `focusin` handler — only `\"blur\"` layers act on it. */\n function onFocusIn(e: FocusEvent): void {\n handleOutside(e.target === null ? null : DOM.source.intern(e.target), true);\n }\n\n /**\n * Document `keydown` handler — Escape asks the topmost non-manual layer to close.\n *\n * @param e - The keydown event.\n * @returns `true` when Escape actually closed a layer; nothing otherwise, so an\n * Escape with no dismissible layer open keeps propagating.\n */\n function onKeyDown(e: KeyboardEvent): Event.ListenerResult {\n if (e.key !== \"Escape\") {\n return;\n }\n\n for (let i = _stack.length - 1; i >= 0; i--) {\n const mode = _stack[i].layer.getDismissMode();\n\n if (mode === \"manual\") {\n continue;\n }\n\n _stack[i].layer.requestClose();\n\n return true;\n }\n\n return;\n }\n\n /** Installs the three document-level listeners against the sentinel owner. */\n function installListeners(): void {\n Event.addViewportListener(_listenerOwner, \"pointerdown\", onPointerDown);\n Event.addViewportListener(_listenerOwner, \"focusin\", onFocusIn);\n Event.addViewportListener(_listenerOwner, \"keydown\", onKeyDown);\n Event.addViewportListener(_listenerOwner, \"blur\", onWindowBlur);\n\n _listenersInstalled = true;\n }\n\n /** Removes the three document-level listeners. */\n function uninstallListeners(): void {\n Event.removeViewportListener(_listenerOwner, \"pointerdown\", onPointerDown);\n Event.removeViewportListener(_listenerOwner, \"focusin\", onFocusIn);\n Event.removeViewportListener(_listenerOwner, \"keydown\", onKeyDown);\n Event.removeViewportListener(_listenerOwner, \"blur\", onWindowBlur);\n\n _listenersInstalled = false;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Util } from \"~/core/Util.js\";\nimport type { Rect } from \"~/core/DOM.js\";\nimport type { Size } from \"~/primitive/Size.js\";\n\n/**\n * The primary growth axis for an anchored overlay.\n *\n * @category Core\n */\nexport type AnchorAxis = \"vertical\" | \"horizontal\";\n\n/**\n * Options controlling how {@link positionAnchored} places an element against an\n * anchor rect.\n *\n * @category Core\n */\nexport interface AnchorOptions {\n /** Primary growth axis: `\"vertical\"` grows below/above, `\"horizontal\"` right/left. */\n axis: AnchorAxis;\n /** Gap in px between the anchor edge and the element on the primary axis. Default 0. */\n gap?: number;\n /** Viewport-edge margin in px kept on the cross axis. Default 0. */\n margin?: number;\n}\n\n/**\n * Chooses a top-left coordinate on the primary axis. Prefers growing past the\n * anchor's far edge (`farEdge + gap`); flips to the near edge\n * (`nearEdge - extent - gap`) only when the far side lacks room AND the near\n * side has it. When neither side fits, saturates on-screen toward the side with\n * more room — pinned to the viewport's far end when the far side is roomier,\n * otherwise to `0` — so the element never overflows off-screen.\n *\n * @param nearEdge - The anchor's near edge (top / left) on this axis.\n * @param farEdge - The anchor's far edge (bottom / right) on this axis.\n * @param extent - The element's size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param gap - Gap in px kept between the anchor edge and the element.\n * @returns The chosen top-left coordinate on this axis.\n *\n * @category Core\n */\nexport function positionAdjacent(nearEdge: number, farEdge: number, extent: number, viewportExtent: number, gap: number): number {\n const farStart = farEdge + gap;\n const spaceFar = viewportExtent - farStart;\n const spaceNear = nearEdge - gap;\n\n if (extent <= spaceFar) {\n return farStart;\n }\n\n if (extent <= spaceNear) {\n return nearEdge - extent - gap;\n }\n\n // Neither side fits: pin to whichever side has more room, saturated so the\n // element stays on-screen (mirrors placeAnchored's spaceBelow/spaceAbove\n // fallback, axis-agnostic).\n if (spaceFar >= spaceNear) {\n return Math.max(0, viewportExtent - extent);\n }\n\n return 0;\n}\n\n/**\n * Chooses a top-left coordinate on the cross axis for a **fixed-size** element\n * that ALIGNS WITH (and overlaps) its anchor rather than sitting beside it.\n * Prefers the element's near edge flush with the anchor's near edge\n * (`nearEdge`); flips to the element's FAR edge flush with the anchor's far\n * edge (`farEdge - extent`) only when the near alignment overflows the\n * viewport. This is alignment, not adjacency — the element overlaps the\n * anchor either way; only which pair of edges is made flush changes. It is\n * fixed-size and returns no `available` (contrast {@link\n * positionFlexibleAnchored}); `margin` binds on **this** axis. When neither\n * alignment fits on-screen (an anchor whose own far edge is off-viewport, or\n * an element wider than the viewport), falls back to {@link clampAxis} — the\n * right answer when no placement fits, not a flip substitute.\n *\n * @param nearEdge - The anchor's near edge (left / top) on this axis.\n * @param farEdge - The anchor's far edge (right / bottom) on this axis.\n * @param extent - The element's fixed size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param margin - Viewport-edge margin in px kept on this axis.\n * @returns The chosen top-left coordinate on this axis.\n *\n * @category Core\n */\nexport function positionAligned(\n nearEdge: number, farEdge: number, extent: number, viewportExtent: number, margin: number,\n): number {\n // Preferred: the element's near edge aligns with the anchor's near edge.\n if (nearEdge >= margin && nearEdge + extent <= viewportExtent - margin) {\n return nearEdge;\n }\n\n // Flipped: the element's FAR edge aligns with the anchor's far edge. Note this\n // is alignment, not adjacency — the element overlaps the anchor either way; only\n // which pair of edges is made flush changes.\n const farStart = farEdge - extent;\n\n if (farStart >= margin && farStart + extent <= viewportExtent - margin) {\n return farStart;\n }\n\n // Neither alignment fits on-screen (an anchor whose own far edge is off-viewport,\n // or an element wider than the viewport): fall back to the clamp. Clamping is the\n // right answer when NO placement fits — it is only wrong as a substitute for a flip.\n return clampAxis(nearEdge, extent, viewportExtent, margin);\n}\n\n/**\n * Clamps a top-left coordinate into `[margin, extent - size - margin]`, pinning\n * to `margin` (top-aligned) when the element is larger than the available span\n * so the coordinate never goes below `margin` and off-screen. The upper bound is\n * floored to `margin` before clamping, because `Util.clamp` resolves a `min >\n * max` range to the max — the opposite of the `Math.max(margin, Math.min(...))`\n * this replaces; flooring keeps `min <= max`, so an over-large element pins to\n * `margin` and the caller's height-cap / scroll carries the overflow.\n *\n * @param value - The proposed coordinate.\n * @param size - The element's extent on this axis.\n * @param extent - The viewport's extent on this axis.\n * @param margin - Viewport-edge margin kept on this axis.\n * @returns The clamped coordinate.\n */\nfunction clampAxis(value: number, size: number, extent: number, margin: number): number {\n return Util.clamp(value, margin, Math.max(margin, extent - size - margin));\n}\n\n/**\n * The chosen coordinate for a size-flexible anchored element, plus the room\n * available on the side it landed on.\n *\n * @category Core\n */\nexport interface FlexiblePlacement {\n /** Top-left coordinate on the primary axis. */\n start: number;\n /** Room (px) on the side actually chosen — the caller's height/width cap. */\n available: number;\n}\n\n/**\n * Chooses a top-left coordinate on the primary axis for a **size-flexible**\n * element — one whose extent may be capped and the overflow scrolled, rather\n * than being placed at a fixed size. Grows from `farEdge`; flips to end at\n * `nearEdge` only when the content overflows the far room **and** the near\n * side is roomier. Unlike {@link positionAdjacent}, the far-fits check is against the\n * *room*, not the raw `extent` — so a flip that still doesn't fully fit on\n * the near side still flips (and clamps) rather than falling back to filling\n * the viewport over the anchor. The returned `available` is the room on the\n * chosen side, so a caller that caps the extent measures the correct side —\n * re-deriving it from `start` would measure the wrong side for a flipped\n * element. `viewportMargin` binds on **this** (primary) axis, unlike\n * {@link positionAnchored}'s cross-axis-only `margin`. Always flush against\n * the anchor edge — no `gap` parameter, because every current caller sits\n * flush.\n *\n * @param nearEdge - The anchor's near edge (top / left) on this axis — the\n * edge a flipped element's far edge meets.\n * @param farEdge - The anchor's far edge (bottom / right) on this axis — the\n * edge an unflipped element grows from.\n * @param extent - The element's unclamped preferred size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param viewportMargin - Viewport-edge margin in px kept on this axis.\n * @returns The chosen coordinate and the room available at it.\n *\n * @category Core\n */\nexport function positionFlexibleAnchored(\n nearEdge: number,\n farEdge: number,\n extent: number,\n viewportExtent: number,\n viewportMargin: number,\n): FlexiblePlacement {\n const roomFar = viewportExtent - farEdge - viewportMargin;\n const roomNear = nearEdge - viewportMargin;\n\n if (extent <= roomFar || roomFar >= roomNear) {\n return { start: farEdge, available: roomFar };\n }\n\n return { start: nearEdge - Math.min(extent, roomNear), available: roomNear };\n}\n\n/**\n * Places an element of `size` against `anchorRect` inside `viewport`. On the\n * primary axis it grows past the anchor's far edge (below / right), flipping to\n * the near edge (above / left) only when the far side lacks room AND the near\n * side has more; on the cross axis it aligns to the anchor's near edge,\n * flipping to align with the anchor's far edge when the near alignment\n * overflows, and clamping only when neither alignment fits. Pure — all\n * viewport reads are supplied by the caller, so it is directly unit-testable\n * with no DOM.\n *\n * @param anchorRect - The anchor element's bounding rect.\n * @param size - The element's width/height to place.\n * @param viewport - The viewport size to clamp/flip within.\n * @param opts - Axis, gap, and cross-axis margin.\n * @returns The resolved top-left `{ x, y }` for the element.\n *\n * @category Core\n */\nexport function positionAnchored(anchorRect: Rect, size: Size, viewport: Size, opts: AnchorOptions): { x: number; y: number } {\n const gap = opts.gap ?? 0;\n const margin = opts.margin ?? 0;\n\n if (opts.axis === \"vertical\") {\n const y = positionAdjacent(anchorRect.top, anchorRect.bottom, size.height, viewport.height, gap);\n const x = positionAligned(anchorRect.left, anchorRect.right, size.width, viewport.width, margin);\n\n return { x, y };\n }\n\n const x = positionAdjacent(anchorRect.left, anchorRect.right, size.width, viewport.width, gap);\n const y = positionAligned(anchorRect.top, anchorRect.bottom, size.height, viewport.height, margin);\n\n return { x, y };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM, type Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { LayerManager } from \"~/core/LayerManager.js\";\nimport { positionAdjacent } from \"~/core/OverlayPosition.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { Animation } from \"~/core/Animation.js\";\nimport { Text } from \"~/component/input/Text.js\";\n\nconst TOOLTIP_ANIM_DURATION_MS: number = 100;\n\n/**\n * Optional color overrides for a tooltip attachment.\n *\n * When provided to {@link Tooltip.attach}, these replace the default theme variables\n * for the duration that the tooltip is shown for that component.\n *\n * @category Core\n */\nexport interface TooltipColors {\n /** CSS color for the tooltip background. */\n background?: string;\n /** CSS color for the tooltip text. */\n color?: string;\n /** CSS color for the tooltip border. */\n border?: string;\n}\n\n/** Internal record of a component's tooltip attachment. */\ninterface TooltipAttachment {\n text : string;\n colors : TooltipColors | undefined;\n mouseoverFn : (e: MouseEvent) => void;\n mousemoveFn : (e: MouseEvent) => void;\n mouseoutFn : () => void;\n mousedownFn : () => void;\n}\n\n/** Internal record of a raw-element tooltip attachment. */\ninterface ElementTooltipAttachment {\n text : string;\n mouseoverFn : (e: MouseEvent) => void;\n mousemoveFn : (e: MouseEvent) => void;\n mouseoutFn : () => void;\n mousedownFn : () => void;\n showTimer : ReturnType<typeof setTimeout> | null;\n lastX : number;\n lastY : number;\n}\n\n/**\n * A singleton floating tooltip that appears near the cursor after a short delay.\n *\n * Use `Tooltip.attach(component, text)` to wire hover listeners onto any component.\n * For manual control use `Tooltip.show(text, x, y)` and `Tooltip.hide()` directly.\n *\n * @example\n * ```typescript\n * Tooltip.attach(myButton, 'Save the document');\n * ```\n *\n * @category Core\n */\n// Intentional exception to the `callable()`-wrap rule: `Tooltip` has a private\n// constructor and is used only through its static methods (`show` / `attach` /\n// …), so there is no public `new Tooltip()` / `Tooltip(...)` surface to wrap.\n// The singleton is exported directly (see `overlay/index.ts`).\nexport class Tooltip extends Component {\n\n // In-flight fades, cancelled on teardown so their fallback timers cannot\n // fire against this tooltip's released element handle. Instance fields\n // written through the singleton `inst` in the static show / hide below.\n private _showAnimation: Animation.CancelHandle | null = null;\n private _hideAnimation: Animation.CancelHandle | null = null;\n\n private static instance: Tooltip | null = null;\n private static showTimer: ReturnType<typeof setTimeout> | null = null;\n private static attachments: Map<string, TooltipAttachment> = new Map();\n private static elementAttachments: Map<Handle, ElementTooltipAttachment> = new Map();\n private static activeElement: Handle | null = null;\n\n // True while the anchor-watch viewport listener is installed (only while a\n // tooltip is shown). See _onAnchorWatch.\n private static watching: boolean = false;\n\n /**\n * Hides the tooltip when its anchor element has left the DOM. The browser\n * fires no `mouseout` when an element is removed under a stationary pointer,\n * so a tooltip whose anchor vanishes would otherwise linger; this runs on\n * pointer movement while a tooltip is shown and dismisses it once the anchor\n * is no longer connected.\n */\n private static readonly _onAnchorWatch = (): void => {\n if (Tooltip.activeElement !== null && !DOM.source.isConnected(Tooltip.activeElement)) {\n Tooltip.hide();\n }\n };\n\n // Set true while a fade-out is in flight; reset to false when the fade-out\n // completes (so the deferred removeElement fires) or when a fresh `show()`\n // re-displays the tooltip mid-fade (the deferred removeElement is then\n // skipped because the tooltip is back on screen).\n private static dismissing: boolean = false;\n\n private static readonly H_PADDING: number = 16;\n private static readonly V_PADDING: number = 8;\n private static readonly MAX_WIDTH: number = 300;\n private static readonly ITEM_HEIGHT: number = 20;\n\n // Clearance kept free around the pointer *hotspot* (the arrow's tip, the\n // hand's fingertip) — the coordinate `show` is handed. The cursor glyph\n // hangs down and to the right of its hotspot, so this box is deliberately\n // NOT centred on it: nothing sits above the tip, and only a few px reach\n // left of it. A box symmetric about the hotspot would push a flipped\n // tooltip visibly further from the cursor than an unflipped one, since the\n // same gap spans glyph on one side and empty space on the other.\n //\n // These are clearance, not glyph metrics: DOWN is shorter than the ~24px\n // glyph on purpose, so an unflipped tooltip keeps sitting alongside the\n // pointer's lower half (where it tapers and hides nothing) rather than\n // being pushed clear of it. No API reports the live cursor's size or\n // hotspot, so the numbers approximate the platform defaults.\n private static readonly CURSOR_LEFT: number = 4;\n private static readonly CURSOR_RIGHT: number = 12;\n private static readonly CURSOR_UP: number = 0;\n private static readonly CURSOR_DOWN: number = 12;\n private static readonly CURSOR_GAP: number = 2;\n\n private _text: Text;\n\n // Number of `\\n`-separated lines in the text currently shown. Cached by\n // `show()` (which knows the raw string) and read by `doLayout()` (which\n // only sees the committed size) so the label's height tracks the line\n // count. 1 for a single-line tooltip — the unchanged legacy case.\n private _lineCount: number = 1;\n\n // Resolved per-line pixel height of the tooltip text, set by `show()` from\n // the live additive line box and read by `doLayout()`. Replaces the\n // fixed `ITEM_HEIGHT` multiplier, which over-allocated ~3px/line and left\n // a trailing empty line on multi-line tooltips.\n private _perLine: number = Tooltip.ITEM_HEIGHT;\n\n /** Private — use the static methods; only one instance is ever created. */\n private constructor() {\n super();\n\n this.setVisible(false);\n // Above every managed layer (incl. a modal Dialog and its backdrop) so a\n // hover tooltip is never occluded.\n this.setZIndex(LayerManager.Band.Tooltip);\n this.setBackgroundColor(\"var(--ts-ui-tooltip-bg, rgb(255, 255, 240))\");\n this.setForegroundColor(\"var(--ts-ui-tooltip-color, rgb(0, 0, 0))\");\n this.setBorder({ border: \"1px solid var(--ts-ui-tooltip-border, rgb(180, 180, 100))\" });\n this.setShadow(\"var(--ts-ui-tooltip-shadow, 1px 2px 4px rgba(0, 0, 0, 0.2))\");\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setPointerEvents(\"none\");\n // Top-level overlay, dynamic size — layout+paint containment scopes reflow without\n // committing to a fixed size.\n this.setContain(\"layout paint\");\n\n this._text = new Text();\n this._text.setPointerEvents(\"none\");\n // `pre-wrap` preserves explicit `\\n` breaks as real lines *and* wraps\n // a line that overflows the fixed width — `nowrap` would collapse the\n // breaks onto one line. A single-line tooltip narrower than MAX_WIDTH\n // renders identically to the old `nowrap` behaviour.\n this._text.setWhiteSpace(\"pre-wrap\");\n this.addComponent(this._text);\n }\n\n /**\n * Returns the single shared `Tooltip` instance, creating it on first call.\n *\n * @returns The singleton `Tooltip` component.\n */\n private static getInstance(): Tooltip {\n if (!Tooltip.instance) {\n Tooltip.instance = new Tooltip();\n }\n\n return Tooltip.instance;\n }\n\n /**\n * Immediately shows the tooltip with the given text near the specified coordinates.\n *\n * The tooltip sits just past the cursor on each axis — down and to the right —\n * flipping to sit just before it when there is no room past, so it never covers\n * the cursor it's hinting at. The clearance is measured from a small box around\n * the pointer hotspot rather than from the hotspot itself, so a flipped tooltip\n * hugs the cursor as closely as an unflipped one. A tooltip taller than roughly\n * half the viewport (an unusually long multi-line label) can still saturate\n * on-screen rather than flipping cleanly, since its height is uncapped and never\n * scrolls; this is a known limitation, not a bug to fix here.\n *\n * @param text - The string to display inside the tooltip.\n * @param x - Horizontal viewport coordinate for the tooltip origin.\n * @param y - Vertical viewport coordinate for the tooltip origin.\n */\n static show(text: string, x: number, y: number): void {\n if (Tooltip.showTimer !== null) {\n clearTimeout(Tooltip.showTimer);\n Tooltip.showTimer = null;\n }\n\n const inst = Tooltip.getInstance();\n\n // Whether the tooltip is already on screen (and not mid-dismiss). A\n // caller that shows on every mousemove — a chart hover — reaches `show`\n // dozens of times a second; replaying the entrance fade each time would\n // reset opacity to 0 before the 100 ms fade completes, flickering the\n // tooltip. When already shown we skip the fade and just reposition/retext.\n const alreadyShown = inst.isVisible() && !Tooltip.dismissing;\n\n inst._text.setText(text);\n\n // Size width to the widest line and height to the line count so a\n // `\\n`-containing string renders as a multi-line block. A string with\n // no `\\n` yields a single line and the original single-line sizing.\n const lines = text.split(\"\\n\");\n const widestLine = lines.reduce((max, line) => Math.max(max, Util.measureTextWidth(line)), 0);\n\n inst._perLine = inst._perLineHeight();\n\n // The label is laid out inside the content box, so the tooltip's own\n // border has to be added on top of the text box it needs — otherwise\n // the border eats into the measured width and the last word wraps.\n const perimeter = inst.getPerimeterSize();\n const chromeW = perimeter.left + perimeter.right;\n const chromeH = perimeter.top + perimeter.bottom;\n\n // No minimum width — the tooltip hugs its content (widest line plus\n // horizontal padding), capped at MAX_WIDTH. A min-width floor would\n // pad short labels out to a fixed box wider than their text. MAX_WIDTH\n // stays an outer cap.\n const tooltipWidth = Math.min(Tooltip.MAX_WIDTH, widestLine + Tooltip.H_PADDING + chromeW);\n\n // When the widest line is wider than the cap, the label soft-wraps at the\n // available text width, so the visual line count exceeds the `\\n`-split\n // count. Measure the wrapped height at that width and derive the real line\n // count; an uncapped tooltip never wraps, so the split count stands.\n if (widestLine + Tooltip.H_PADDING + chromeW > Tooltip.MAX_WIDTH) {\n const availTextWidth = tooltipWidth - Tooltip.H_PADDING - chromeW;\n const wrappedHeight = DOM.source.measureText(text, { maxWidth: availTextWidth }).height;\n inst._lineCount = Math.max(lines.length, Math.round(wrappedHeight / inst._perLine));\n } else {\n inst._lineCount = lines.length;\n }\n\n // Height hugs the text: one resolved line height per line plus padding.\n // Floor the single-line case to the legacy ITEM_HEIGHT box so plain\n // one-line tooltips stay pixel-stable; only multi-line tooltips switch\n // to the tighter per-line height that removes the trailing gap.\n let tooltipHeight = inst._lineCount * inst._perLine + Tooltip.V_PADDING;\n if (inst._lineCount === 1) {\n tooltipHeight = Math.max(tooltipHeight, Tooltip.ITEM_HEIGHT + Tooltip.V_PADDING);\n }\n\n // Added after the single-line floor so the floor keeps meaning \"one\n // line of text\" rather than \"one line of text minus the border\".\n tooltipHeight += chromeH;\n\n inst.setWidth(tooltipWidth);\n inst.setHeight(tooltipHeight);\n\n const vp = DOM.source.getViewportSize();\n\n // Clamp the cursor into the viewport first: the flip primitive only guarantees\n // an on-screen result for an in-viewport anchor, and show() is public.\n const cx = Util.clamp(x, 0, vp.width);\n const cy = Util.clamp(y, 0, vp.height);\n\n // Sit past the cursor's clearance box, flipping to sit *before* it when there\n // is no room past — so the tooltip can never land under the cursor. The box\n // (not the bare hotspot) is the anchor rect, which is what lets the two sides\n // differ: the primitive applies one `gap` to whichever edge it picks, and the\n // asymmetry the cursor actually has lives in the edges. No viewport margin,\n // since the tooltip may sit flush against an edge.\n inst.setX(positionAdjacent(\n cx - Tooltip.CURSOR_LEFT, cx + Tooltip.CURSOR_RIGHT, tooltipWidth, vp.width, Tooltip.CURSOR_GAP,\n ));\n inst.setY(positionAdjacent(\n cy - Tooltip.CURSOR_UP, cy + Tooltip.CURSOR_DOWN, tooltipHeight, vp.height, Tooltip.CURSOR_GAP,\n ));\n\n const el = inst.getElement(true)!;\n\n inst.scheduleLayout();\n\n LayerManager.mount(el);\n\n inst.setVisible(true);\n\n // Watch for the anchor leaving the DOM while the tooltip is up (a removed\n // element fires no mouseout), so an orphaned tooltip dismisses on the next\n // pointer move instead of lingering.\n if (!Tooltip.watching) {\n Event.addViewportListener(inst, \"mousemove\", Tooltip._onAnchorWatch);\n Tooltip.watching = true;\n }\n\n // Cancel a pending fade-out's removeElement so a fresh show during the\n // outgoing transition keeps the element in the DOM.\n Tooltip.dismissing = false;\n\n if (alreadyShown) {\n // Already visible — hold at full opacity; the position/size/text\n // updates above are all this repeat show needs. No entrance fade.\n DOM.sink.apply(el, { style: { opacity: \"1\" } });\n\n return;\n }\n\n inst._showAnimation?.cancel();\n inst._showAnimation = Animation.play(el, {\n from: { opacity: \"0\" },\n to: { opacity: \"1\" },\n durationMs: TOOLTIP_ANIM_DURATION_MS,\n properties: [\"opacity\"],\n });\n }\n\n /**\n * Hides and detaches the tooltip.\n *\n * Any pending show timer is also cancelled.\n */\n static hide(): void {\n if (Tooltip.showTimer !== null) {\n clearTimeout(Tooltip.showTimer);\n Tooltip.showTimer = null;\n }\n\n const inst = Tooltip.getInstance();\n const el = inst.getElement();\n\n // The tooltip is going away; stop watching the (now-irrelevant) anchor\n // and forget it.\n if (Tooltip.watching) {\n Event.removeViewportListener(inst, \"mousemove\", Tooltip._onAnchorWatch);\n Tooltip.watching = false;\n }\n Tooltip.activeElement = null;\n\n if (!el) {\n return;\n }\n\n Tooltip.dismissing = true;\n\n inst._hideAnimation?.cancel();\n inst._hideAnimation = Animation.play(el, {\n to: { opacity: \"0\" },\n durationMs: TOOLTIP_ANIM_DURATION_MS,\n properties: [\"opacity\"],\n onComplete: () => {\n if (!Tooltip.dismissing) {\n return;\n }\n Tooltip.dismissing = false;\n inst.setVisible(false);\n inst.removeElement();\n },\n });\n }\n\n /**\n * Wires `mouseover` and `mouseout` listeners onto `component` so the tooltip\n * shows automatically after a 500 ms hover delay.\n *\n * @remarks Uses `mouseover`/`mouseout` rather than `mouseenter`/`mouseleave`\n * because the Event system routes through a window-level capture handler, and\n * the non-bubbling enter/leave events are not reliably seen there in Chrome.\n *\n * If `colors` is provided the tooltip uses those colors instead of the default\n * theme variables while it is showing for this component.\n *\n * Calling `attach` on a component that already has an attachment replaces it.\n *\n * @param component - The component to attach hover behaviour to.\n * @param text - The tooltip text to display.\n * @param colors - Optional color overrides applied while this tooltip is visible.\n */\n static attach(component: Component, text: string, colors?: TooltipColors): void {\n Tooltip.detach(component);\n\n let cursorX = 0;\n let cursorY = 0;\n\n const mouseoverFn = (e: MouseEvent) => {\n if (Tooltip.showTimer !== null) {\n return;\n }\n\n cursorX = e.clientX;\n cursorY = e.clientY;\n\n Tooltip.showTimer = setTimeout(() => {\n Tooltip._applyColors(colors);\n // Record the anchor so the anchor-watch dismisses the tooltip if\n // this component is later removed from the DOM.\n Tooltip.activeElement = component.getElement() ?? null;\n Tooltip.show(text, cursorX, cursorY);\n Tooltip.showTimer = null;\n }, 500);\n };\n\n const mousemoveFn = (e: MouseEvent) => {\n cursorX = e.clientX;\n cursorY = e.clientY;\n };\n\n const mouseoutFn = () => {\n Tooltip.hide();\n };\n\n // Acting on the component (a click, or the start of a drag) dismisses\n // its tooltip: once the pointer is pressed the hover hint has served its\n // purpose and would only obscure the result of the action. `hide` also\n // cancels a still-pending show timer, so a press during the hover delay\n // suppresses the tooltip entirely. It stays hidden until the pointer\n // leaves and re-enters, since no fresh `mouseover` fires under a\n // stationary cursor.\n const mousedownFn = () => {\n Tooltip.hide();\n };\n\n Event.addListener(component, \"mouseover\", mouseoverFn);\n Event.addListener(component, \"mousemove\", mousemoveFn);\n Event.addListener(component, \"mouseout\", mouseoutFn);\n Event.addListener(component, \"mousedown\", mousedownFn);\n\n Tooltip.attachments.set(component.getId(), {\n text, colors, mouseoverFn, mousemoveFn, mouseoutFn, mousedownFn,\n });\n }\n\n /**\n * Removes the tooltip attachment from a component, cancelling any pending show\n * and hiding the tooltip if it is currently visible for this component.\n *\n * @param component - The component whose attachment should be removed.\n */\n static detach(component: Component): void {\n const id = component.getId();\n const att = Tooltip.attachments.get(id);\n\n if (!att) {\n return;\n }\n\n Event.removeListener(component, \"mouseover\", att.mouseoverFn);\n Event.removeListener(component, \"mousemove\", att.mousemoveFn);\n Event.removeListener(component, \"mouseout\", att.mouseoutFn);\n Event.removeListener(component, \"mousedown\", att.mousedownFn);\n\n Tooltip.attachments.delete(id);\n Tooltip.hide();\n }\n\n /**\n * Applies optional color overrides to the singleton tooltip instance before it is shown.\n * Resets to the default theme variables when no overrides are provided.\n *\n * @param colors - The color overrides to apply, or `undefined` to use defaults.\n */\n private static _applyColors(colors?: TooltipColors): void {\n const inst = Tooltip.getInstance();\n\n inst.setBackgroundColor(\n colors?.background ?? 'var(--ts-ui-tooltip-bg, rgb(255, 255, 240))'\n );\n inst.setForegroundColor(\n colors?.color ?? 'var(--ts-ui-tooltip-color, rgb(0, 0, 0))'\n );\n inst.setBorder({ border: `1px solid ${colors?.border ?? 'var(--ts-ui-tooltip-border, rgb(180, 180, 100))'}` });\n }\n\n /**\n * Wires native `mouseover`, `mousemove`, and `mouseout` listeners directly onto\n * a raw DOM element so the tooltip shows after a 500 ms hover delay.\n *\n * @remarks Use this instead of {@link attach} when the element contains child nodes\n * that would receive the event as `evnt.target` — the Event system's component\n * listener only matches the exact target id, so `attach` would miss those cases.\n *\n * Calling `attachToElement` on an element that already has an attachment\n * replaces it — the previous listeners are removed and a fresh set is\n * installed against the new text. If the tooltip is currently visible for\n * this element when the call happens, it re-renders immediately at the\n * last known cursor position so the visible text updates without a\n * re-hover.\n *\n * @param element - The element handle to attach hover behaviour to.\n * @param text - The tooltip text to display.\n */\n static attachToElement(element: Handle, text: string): void {\n const previous = Tooltip.elementAttachments.get(element);\n const wasActive = Tooltip.activeElement === element;\n\n // Carry the previous binding's last cursor coords so the mid-hover\n // repaint below has real values instead of (0, 0) — detachElement\n // deletes the WeakMap entry, so the read has to happen first.\n const carryX = previous ? previous.lastX : 0;\n const carryY = previous ? previous.lastY : 0;\n\n Tooltip.detachElement(element);\n\n const att: ElementTooltipAttachment = {\n text,\n showTimer : null,\n lastX : carryX,\n lastY : carryY,\n mouseoverFn: function onTooltipMouseOver(e: MouseEvent): void {\n if (att.showTimer !== null) {\n return;\n }\n\n att.lastX = e.clientX;\n att.lastY = e.clientY;\n\n att.showTimer = setTimeout(function onTooltipShowTimer(): void {\n Tooltip.activeElement = element;\n Tooltip.show(att.text, att.lastX, att.lastY);\n att.showTimer = null;\n }, 500);\n },\n mousemoveFn: function onTooltipMouseMove(e: MouseEvent): void {\n att.lastX = e.clientX;\n att.lastY = e.clientY;\n },\n mouseoutFn: function onTooltipMouseOut(): void {\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n if (Tooltip.activeElement === element) {\n Tooltip.activeElement = null;\n }\n\n Tooltip.hide();\n },\n // Pressing the element (click or drag start) dismisses its tooltip\n // and cancels any pending show — see the `attach` counterpart.\n mousedownFn: function onTooltipMouseDown(): void {\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n Tooltip.hide();\n },\n };\n\n DOM.sink.addListener(element, \"mouseover\", att.mouseoverFn);\n DOM.sink.addListener(element, \"mousemove\", att.mousemoveFn);\n DOM.sink.addListener(element, \"mouseout\", att.mouseoutFn);\n DOM.sink.addListener(element, \"mousedown\", att.mousedownFn);\n\n Tooltip.elementAttachments.set(element, att);\n\n // Mid-hover update: if the previous attachment was the active tooltip\n // target, repaint the visible tooltip with the new text at the last\n // known cursor coords so the swap is seen without a re-hover.\n if (wasActive) {\n Tooltip.activeElement = element;\n Tooltip.show(text, att.lastX, att.lastY);\n }\n }\n\n /**\n * Removes an `attachToElement` binding installed on an element handle.\n *\n * Detaches the three hover listeners, cancels any pending show timer, and\n * clears the active-element tracker if it pointed at this element.\n * Idempotent — calling on an unattached element is a no-op.\n *\n * @param element - The element handle whose attachment should be removed.\n */\n static detachElement(element: Handle): void {\n const att = Tooltip.elementAttachments.get(element);\n\n if (!att) {\n return;\n }\n\n DOM.sink.removeListener(element, \"mouseover\", att.mouseoverFn);\n DOM.sink.removeListener(element, \"mousemove\", att.mousemoveFn);\n DOM.sink.removeListener(element, \"mouseout\", att.mouseoutFn);\n DOM.sink.removeListener(element, \"mousedown\", att.mousedownFn);\n\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n Tooltip.elementAttachments.delete(element);\n\n if (Tooltip.activeElement === element) {\n Tooltip.activeElement = null;\n }\n }\n\n /**\n * Resolves the tooltip text's rendered per-line height in pixels from the\n * live additive line box, measured at hover time. Matches the `_text` label\n * (a default `Text`: 14px font, theme leading) so the box hugs the text\n * vertically with no trailing gap, and tracks theme/font changes on the next\n * hover.\n *\n * @returns The per-line height in pixels, ceiled to a whole pixel.\n *\n * @remarks `measureTextMetrics` already defaults `lineHeight` to\n * `calc(1em + var(--ts-ui-line-padding, 2px))`, so the line box is left to\n * that default rather than passed explicitly.\n */\n private _perLineHeight(): number {\n const { height } = DOM.source.measureText(\"X\");\n\n return Math.ceil(height);\n }\n\n /**\n * Positions the label to fill the tooltip's content box with uniform\n * padding, so the label does not overhang the tooltip's border.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n const box = this.getContentBounds();\n\n if (!box) {\n return this;\n }\n\n this._text.setX(box.x + Tooltip.H_PADDING / 2);\n this._text.setY(box.y + Tooltip.V_PADDING / 2);\n this._text.setWidth(Math.max(0, box.width - Tooltip.H_PADDING));\n this._text.setHeight(this._lineCount * this._perLine);\n\n return this;\n }\n\n /**\n * Cancels any in-flight fade, then defers to the base class. Cancelling\n * first keeps their fallback timers from firing after `super.destructor()`\n * has released this tooltip's element handle.\n */\n protected destructor(): void {\n this._showAnimation?.cancel();\n this._showAnimation = null;\n this._hideAnimation?.cancel();\n this._hideAnimation = null;\n\n super.destructor();\n }\n}\n"],"mappings":"kLAwHA,IAAM,EAA0B,IAC1B,EAA0B,KAC1B,EAA0B,IAC1B,EAA0B,KAK1B,EAA0B,KAU1B,EAA4B,IAAI,EAmBrB,GAAV,SAAA,EAAA,CAGH,IAAM,EAAsB,CAAC,EAIvB,EAAqD,IAAI,QAG3D,EAAoB,EAGpB,EAAwC,KAExC,EAA+B,GAStB,EAAA,KAAO,CAChB,OAAU,EACV,QAAU,EACV,SAAU,EACV,OAAU,EACV,QAAU,CACd,EAQA,SAAS,QAAQ,EAA0B,EAAyB,CAChE,OAAO,EAAS,EAAO,KAAO,CAClC,CAaO,SAAS,SAAS,EAA+B,CACpD,GAAI,EAAa,IAAI,CAAK,EACtB,OAGJ,IAAM,EAAU,EAAO,OAAS,EAAI,EAAO,EAAO,OAAS,GAAK,KAC1D,EAAU,EAAM,cAAc,EAAI,KAAO,EACzC,EAAU,QAAQ,EAAQ,EAAM,UAAU,GAAK,CAAe,EAG9D,EAAkB,CAAE,QAAO,SAAQ,SAAU,CAAC,EAAG,OAAM,OAF9C,GAAQ,EAAE,CAE2C,EAEpE,EAAa,IAAI,EAAO,CAAI,EAC5B,EAAO,KAAK,CAAI,EAEZ,GACA,EAAO,SAAS,KAAK,CAAI,EAGxB,GACD,iBAAiB,CAEzB,qBAYO,SAAS,MAAM,EAAkB,CAC/B,EAAI,OAAO,SAAS,EAAI,OAAO,mBAAmB,EAAG,CAAE,GACxD,EAAI,KAAK,YAAY,EAAI,OAAO,mBAAmB,EAAG,CAAE,CAEhE,eASO,SAAS,WAAW,EAA+B,CACtD,IAAM,EAAO,EAAa,IAAI,CAAK,EAEnC,GAAI,CAAC,EACD,OAGJ,EAAa,OAAO,CAAK,EAEzB,IAAM,EAAM,EAAO,QAAQ,CAAI,EAM/B,GAJI,GAAO,GACP,EAAO,OAAO,EAAK,CAAC,EAGpB,EAAK,OAAQ,CACb,IAAM,EAAK,EAAK,OAAO,SAAS,QAAQ,CAAI,EAExC,GAAM,GACN,EAAK,OAAO,SAAS,OAAO,EAAI,CAAC,CAEzC,CAEI,IAAiB,IACjB,EAAe,MAGf,EAAO,SAAW,GAAK,GACvB,mBAAmB,CAE3B,yBAaO,SAAS,qBAAqB,EAAyB,EAA8B,CACxF,GAAI,CAAC,EACD,MAAO,GAGX,IAAM,EAAY,EAAa,IAAI,CAAK,EAExC,GAAI,CAAC,EAAW,CACZ,IAAM,EAAK,EAAM,gBAAgB,EAEjC,OAAO,EAAK,EAAI,OAAO,SAAS,EAAI,CAAI,EAAI,EAChD,CAEA,OAAO,aAAa,EAAW,CAAI,CACvC,6CAQO,SAAS,aAAuC,CACnD,OAAO,EAAO,OAAS,EAAI,EAAO,EAAO,OAAS,EAAE,CAAC,MAAQ,IACjE,2BAUO,SAAS,aAAa,EAA+B,CACxD,IAAM,EAAO,EAAa,IAAI,CAAK,EAE9B,IAIL,eAAe,CAAI,EACnB,WAAW,CAAK,EACpB,6BAUO,SAAS,UAAU,EAAiC,CACvD,IAAM,EAAO,EAAa,IAAI,CAAK,EAEnC,OAAO,EAAO,EAAK,OAAS,CAChC,uBASA,SAAS,eAAe,EAAuB,CAC3C,IAAM,KAAQ,GAAuB,CACjC,EAAE,OAAS,EAAE,MAAQ,EAAE,EACvB,EAAE,MAAM,kBAAkB,EAAE,MAAM,EAElC,IAAK,IAAM,KAAS,EAAE,SAClB,KAAK,CAAK,CAElB,EAEA,KAAK,CAAI,CACb,CAGA,SAAS,aAAa,EAAiB,EAAyB,CAC5D,IAAM,EAAK,EAAK,MAAM,gBAAgB,EAEtC,GAAI,GAAM,EAAI,OAAO,SAAS,EAAI,CAAM,EACpC,MAAO,GAGX,IAAK,IAAM,KAAS,EAAK,SACrB,GAAI,aAAa,EAAO,CAAM,EAC1B,MAAO,GAIf,MAAO,EACX,CAGA,SAAS,WAAW,EAA+B,CAC3C,IAAiB,IAIrB,iBAAiB,EAEjB,EAAe,EAEX,EAAM,YACN,EAAM,WAAW,EAAI,EAE7B,CAGA,SAAS,kBAAyB,CAC1B,GAAgB,EAAa,YAC7B,EAAa,WAAW,EAAK,EAGjC,EAAe,IACnB,CAUA,SAAS,oBAAoB,EAA0C,CACnE,IAAI,EAAwB,EAE5B,KAAO,GAAK,CACR,GAAI,EAAI,MAAM,WACV,OAAO,EAAI,MAGf,EAAM,EAAI,MACd,CAEA,OAAO,IACX,CAUA,SAAS,cAAc,EAAuB,EAA0B,CAGpE,IAAM,EAAY,EAAO,MAAM,CAAC,CAAC,QAAQ,EACrC,EAAc,GACd,EAAe,GAEnB,IAAK,IAAM,KAAQ,EAAU,CACzB,IAAM,EAAO,EAAK,MAAM,eAAe,EAEvC,GAAI,qBAAqB,EAAK,MAAO,CAAM,EAAG,CAM1C,WAAW,oBAAoB,CAAI,GAAK,EAAK,KAAK,EAClD,EAAe,GACf,EAAe,GAEf,KACJ,CAEA,IAAM,EAAS,EAAK,MAAM,mBAAmB,EAE7C,GAAI,GAAU,GAAU,EAAI,OAAO,SAAS,EAAQ,CAAM,EAAG,CACzD,EAAe,GAEf,KACJ,CAKA,GAAI,IAAS,UAAY,IAAS,SAAW,EAFzB,EAAY,IAAS,OADrB,IAAS,iBAAmB,IAAS,QAGL,CAGhD,GAAI,IAAS,QAAS,CAClB,EAAe,GAEf,KACJ,CAEA,QACJ,CAEA,EAAK,MAAM,aAAa,CAC5B,CAQI,CAAC,GAAa,CAAC,GAAgB,CAAC,GAChC,iBAAiB,CAEzB,CAGA,SAAS,cAAc,EAAuB,CAC1C,cAAc,EAAE,SAAW,KAAO,KAAO,EAAI,OAAO,OAAO,EAAE,MAAM,EAAG,EAAK,CAC/E,CAcA,SAAS,aAAa,EAAqB,CAClC,EAAI,OAAO,SAAS,EAAE,SAAW,KAAO,KAAO,EAAI,OAAO,OAAO,EAAE,MAAM,CAAC,GAI/E,cAAc,KAAM,EAAK,CAC7B,CAGA,SAAS,UAAU,EAAqB,CACpC,cAAc,EAAE,SAAW,KAAO,KAAO,EAAI,OAAO,OAAO,EAAE,MAAM,EAAG,EAAI,CAC9E,CASA,SAAS,UAAU,EAAwC,CACnD,KAAE,MAAQ,SAId,KAAK,IAAI,EAAI,EAAO,OAAS,EAAG,GAAK,EAAG,IACvB,KAAO,EAAE,CAAC,MAAM,eAEzB,IAAS,SAMb,OAFA,EAAO,EAAE,CAAC,MAAM,aAAa,EAEtB,EACX,CAGJ,CAGA,SAAS,kBAAyB,CAC9B,EAAM,oBAAoB,EAAgB,cAAe,aAAa,EACtE,EAAM,oBAAoB,EAAgB,UAAe,SAAS,EAClE,EAAM,oBAAoB,EAAgB,UAAe,SAAS,EAClE,EAAM,oBAAoB,EAAgB,OAAe,YAAY,EAErE,EAAsB,EAC1B,CAGA,SAAS,oBAA2B,CAChC,EAAM,uBAAuB,EAAgB,cAAe,aAAa,EACzE,EAAM,uBAAuB,EAAgB,UAAe,SAAS,EACrE,EAAM,uBAAuB,EAAgB,UAAe,SAAS,EACrE,EAAM,uBAAuB,EAAgB,OAAe,YAAY,EAExE,EAAsB,EAC1B,CACH,EAAA,CAAA,AAAA,IAAA,CAAA,CAAD,EC7hBA,SAAgB,iBAAiB,EAAkB,EAAiB,EAAgB,EAAwB,EAAqB,CAC7H,IAAM,EAAY,EAAU,EACtB,EAAY,EAAiB,EAC7B,EAAY,EAAW,EAiB7B,OAfI,GAAU,EACH,EAGP,GAAU,EACH,EAAW,EAAS,EAM3B,GAAY,EACL,KAAK,IAAI,EAAG,EAAiB,CAAM,EAGvC,CACX,CAyBA,SAAgB,gBACZ,EAAkB,EAAiB,EAAgB,EAAwB,EACrE,CAEN,GAAI,GAAY,GAAU,EAAW,GAAU,EAAiB,EAC5D,OAAO,EAMX,IAAM,EAAW,EAAU,EAS3B,OAPI,GAAY,GAAU,EAAW,GAAU,EAAiB,EACrD,EAMJ,UAAU,EAAU,EAAQ,EAAgB,CAAM,CAC7D,CAiBA,SAAS,UAAU,EAAe,EAAc,EAAgB,EAAwB,CACpF,OAAO,EAAK,MAAM,EAAO,EAAQ,KAAK,IAAI,EAAQ,EAAS,EAAO,CAAM,CAAC,CAC7E,CA0CA,SAAgB,yBACZ,EACA,EACA,EACA,EACA,EACiB,CACjB,IAAM,EAAW,EAAiB,EAAU,EACtC,EAAW,EAAW,EAM5B,OAJI,GAAU,GAAW,GAAW,EACzB,CAAE,MAAO,EAAS,UAAW,CAAQ,EAGzC,CAAE,MAAO,EAAW,KAAK,IAAI,EAAQ,CAAQ,EAAG,UAAW,CAAS,CAC/E,CAoBA,SAAgB,iBAAiB,EAAkB,EAAY,EAAgB,EAA+C,CAC1H,IAAM,EAAS,EAAK,KAAU,EACxB,EAAS,EAAK,QAAU,EAE9B,GAAI,EAAK,OAAS,WAAY,CAC1B,IAAM,EAAI,iBAAiB,EAAW,IAAK,EAAW,OAAQ,EAAK,OAAQ,EAAS,OAAQ,CAAG,EAG/F,MAAO,CAAE,EAFC,gBAAgB,EAAW,KAAM,EAAW,MAAO,EAAK,MAAO,EAAS,MAAO,CAEhF,EAAG,GAAE,CAClB,CAKA,MAAO,CAAE,EAHC,iBAAiB,EAAW,KAAM,EAAW,MAAO,EAAK,MAAO,EAAS,MAAO,CAGjF,EAAG,EAFF,gBAAgB,EAAW,IAAK,EAAW,OAAQ,EAAK,OAAQ,EAAS,OAAQ,CAE/E,CAAE,CAClB,CCpNA,IAAM,EAAmC,IA0D5B,EAAb,MAAa,gBAAgB,CAAU,CAKnC,eAAwD,KACxD,eAAwD,KAExD,OAAe,SAA2B,KAC1C,OAAe,UAAkD,KACjE,OAAe,YAA8C,IAAI,IACjE,OAAe,mBAA4D,IAAI,IAC/E,OAAe,cAA+B,KAI9C,OAAe,SAAoB,GASnC,OAAwB,mBAA6B,CAC7C,QAAQ,gBAAkB,MAAQ,CAAC,EAAI,OAAO,YAAY,QAAQ,aAAa,GAC/E,QAAQ,KAAK,CAErB,EAMA,OAAe,WAAsB,GAErC,OAAwB,UAAoB,GAC5C,OAAwB,UAAoB,EAC5C,OAAwB,UAAoB,IAC5C,OAAwB,YAAsB,GAe9C,OAAwB,YAAsB,EAC9C,OAAwB,aAAuB,GAC/C,OAAwB,UAAoB,EAC5C,OAAwB,YAAsB,GAC9C,OAAwB,WAAqB,EAE7C,MAMA,WAA6B,EAM7B,SAA2B,QAAQ,YAGnC,aAAsB,CAClB,MAAM,EAEN,KAAK,WAAW,EAAK,EAGrB,KAAK,UAAU,EAAa,KAAK,OAAO,EACxC,KAAK,mBAAmB,6CAA6C,EACrE,KAAK,mBAAmB,0CAA0C,EAClE,KAAK,UAAU,CAAE,OAAQ,2DAA4D,CAAC,EACtF,KAAK,UAAU,6DAA6D,EAC5E,KAAK,gBAAgB,iCAAiC,EACtD,KAAK,iBAAiB,MAAM,EAG5B,KAAK,WAAW,cAAc,EAE9B,KAAK,MAAQ,IAAI,EACjB,KAAK,MAAM,iBAAiB,MAAM,EAKlC,KAAK,MAAM,cAAc,UAAU,EACnC,KAAK,aAAa,KAAK,KAAK,CAChC,CAOA,OAAe,aAAuB,CAKlC,MAJA,CACI,QAAQ,WAAW,IAAI,QAGpB,QAAQ,QACnB,CAkBA,OAAO,KAAK,EAAc,EAAW,EAAiB,CAC9C,QAAQ,YAAc,OACtB,aAAa,QAAQ,SAAS,EAC9B,QAAQ,UAAY,MAGxB,IAAM,EAAO,QAAQ,YAAY,EAO3B,EAAe,EAAK,UAAU,GAAK,CAAC,QAAQ,WAElD,EAAK,MAAM,QAAQ,CAAI,EAKvB,IAAM,EAAa,EAAK,MAAM;CAAI,EAC5B,EAAa,EAAM,QAAQ,EAAK,IAAS,KAAK,IAAI,EAAK,EAAK,iBAAiB,CAAI,CAAC,EAAG,CAAC,EAE5F,EAAK,SAAW,EAAK,eAAe,EAKpC,IAAM,EAAY,EAAK,iBAAiB,EAClC,EAAY,EAAU,KAAO,EAAU,MACvC,EAAY,EAAU,IAAO,EAAU,OAMvC,EAAgB,KAAK,IAAI,QAAQ,UAAW,EAAa,QAAQ,UAAY,CAAO,EAM1F,GAAI,EAAa,QAAQ,UAAY,EAAU,QAAQ,UAAW,CAC9D,IAAM,EAAiB,EAAe,QAAQ,UAAY,EACpD,EAAiB,EAAI,OAAO,YAAY,EAAM,CAAE,SAAU,CAAe,CAAC,CAAC,CAAC,OAClF,EAAK,WAAkB,KAAK,IAAI,EAAM,OAAQ,KAAK,MAAM,EAAgB,EAAK,QAAQ,CAAC,CAC3F,KACI,GAAK,WAAa,EAAM,OAO5B,IAAI,EAAgB,EAAK,WAAa,EAAK,SAAW,QAAQ,UAC1D,EAAK,aAAe,IACpB,EAAgB,KAAK,IAAI,EAAe,QAAQ,YAAc,QAAQ,SAAS,GAKnF,GAAiB,EAEjB,EAAK,SAAS,CAAY,EAC1B,EAAK,UAAU,CAAa,EAE5B,IAAM,EAAK,EAAI,OAAO,gBAAgB,EAIhC,EAAK,EAAK,MAAM,EAAG,EAAG,EAAG,KAAK,EAC9B,EAAK,EAAK,MAAM,EAAG,EAAG,EAAG,MAAM,EAQrC,EAAK,KAAK,iBACN,EAAK,QAAQ,YAAa,EAAK,QAAQ,aAAc,EAAe,EAAG,MAAQ,QAAQ,UAC3F,CAAC,EACD,EAAK,KAAK,iBACN,EAAK,QAAQ,UAAa,EAAK,QAAQ,YAAc,EAAe,EAAG,OAAQ,QAAQ,UAC3F,CAAC,EAED,IAAM,EAAK,EAAK,WAAW,EAAI,EAoB/B,GAlBA,EAAK,eAAe,EAEpB,EAAa,MAAM,CAAE,EAErB,EAAK,WAAW,EAAI,EAKpB,AAEI,QAAQ,YADR,EAAM,oBAAoB,EAAM,YAAa,QAAQ,cAAc,EAChD,IAKvB,QAAQ,WAAa,GAEjB,EAAc,CAGd,EAAI,KAAK,MAAM,EAAI,CAAE,MAAO,CAAE,QAAS,GAAI,CAAE,CAAC,EAE9C,MACJ,CAEA,EAAK,gBAAgB,OAAO,EAC5B,EAAK,eAAiB,EAAU,KAAK,EAAI,CACrC,KAAY,CAAE,QAAS,GAAI,EAC3B,GAAY,CAAE,QAAS,GAAI,EAC3B,WAAY,EACZ,WAAY,CAAC,SAAS,CAC1B,CAAC,CACL,CAOA,OAAO,MAAa,CACZ,QAAQ,YAAc,OACtB,aAAa,QAAQ,SAAS,EAC9B,QAAQ,UAAY,MAGxB,IAAM,EAAO,QAAQ,YAAY,EAC3B,EAAO,EAAK,WAAW,EAI7B,AAEI,QAAQ,YADR,EAAM,uBAAuB,EAAM,YAAa,QAAQ,cAAc,EACnD,IAEvB,QAAQ,cAAgB,KAEnB,IAIL,QAAQ,WAAa,GAErB,EAAK,gBAAgB,OAAO,EAC5B,EAAK,eAAiB,EAAU,KAAK,EAAI,CACrC,GAAY,CAAE,QAAS,GAAI,EAC3B,WAAY,EACZ,WAAY,CAAC,SAAS,EACtB,eAAkB,CACT,QAAQ,aAGb,QAAQ,WAAa,GACrB,EAAK,WAAW,EAAK,EACrB,EAAK,cAAc,EACvB,CACJ,CAAC,EACL,CAmBA,OAAO,OAAO,EAAsB,EAAc,EAA8B,CAC5E,QAAQ,OAAO,CAAS,EAExB,IAAI,EAAU,EACV,EAAU,EAER,YAAe,GAAkB,CAC/B,QAAQ,YAAc,OAI1B,EAAU,EAAE,QACZ,EAAU,EAAE,QAEZ,QAAQ,UAAY,eAAiB,CACjC,QAAQ,aAAa,CAAM,EAG3B,QAAQ,cAAgB,EAAU,WAAW,GAAK,KAClD,QAAQ,KAAK,EAAM,EAAS,CAAO,EACnC,QAAQ,UAAY,IACxB,EAAG,GAAG,EACV,EAEM,YAAe,GAAkB,CACnC,EAAU,EAAE,QACZ,EAAU,EAAE,OAChB,EAEM,eAAmB,CACrB,QAAQ,KAAK,CACjB,EASM,gBAAoB,CACtB,QAAQ,KAAK,CACjB,EAEA,EAAM,YAAY,EAAW,YAAa,WAAW,EACrD,EAAM,YAAY,EAAW,YAAa,WAAW,EACrD,EAAM,YAAY,EAAW,WAAY,UAAU,EACnD,EAAM,YAAY,EAAW,YAAa,WAAW,EAErD,QAAQ,YAAY,IAAI,EAAU,MAAM,EAAG,CACvC,OAAM,SAAQ,YAAa,YAAa,WAAY,WACxD,CAAC,CACL,CAQA,OAAO,OAAO,EAA4B,CACtC,IAAM,EAAM,EAAU,MAAM,EACtB,EAAM,QAAQ,YAAY,IAAI,CAAE,EAEjC,IAIL,EAAM,eAAe,EAAW,YAAa,EAAI,WAAW,EAC5D,EAAM,eAAe,EAAW,YAAa,EAAI,WAAW,EAC5D,EAAM,eAAe,EAAW,WAAa,EAAI,UAAU,EAC3D,EAAM,eAAe,EAAW,YAAa,EAAI,WAAW,EAE5D,QAAQ,YAAY,OAAO,CAAE,EAC7B,QAAQ,KAAK,EACjB,CAQA,OAAe,aAAa,EAA8B,CACtD,IAAM,EAAO,QAAQ,YAAY,EAEjC,EAAK,mBACD,GAAQ,YAAc,6CAC1B,EACA,EAAK,mBACD,GAAQ,OAAS,0CACrB,EACA,EAAK,UAAU,CAAE,OAAQ,aAAa,GAAQ,QAAU,mDAAoD,CAAC,CACjH,CAoBA,OAAO,gBAAgB,EAAiB,EAAoB,CACxD,IAAM,EAAY,QAAQ,mBAAmB,IAAI,CAAO,EAClD,EAAY,QAAQ,gBAAkB,EAKtC,EAAS,EAAW,EAAS,MAAQ,EACrC,EAAS,EAAW,EAAS,MAAQ,EAE3C,QAAQ,cAAc,CAAO,EAE7B,IAAM,EAAgC,CAClC,OACA,UAAa,KACb,MAAa,EACb,MAAa,EACb,YAAa,SAAS,mBAAmB,EAAqB,CACtD,EAAI,YAAc,OAItB,EAAI,MAAQ,EAAE,QACd,EAAI,MAAQ,EAAE,QAEd,EAAI,UAAY,WAAW,SAAS,oBAA2B,CAC3D,QAAQ,cAAgB,EACxB,QAAQ,KAAK,EAAI,KAAM,EAAI,MAAO,EAAI,KAAK,EAC3C,EAAI,UAAY,IACpB,EAAG,GAAG,EACV,EACA,YAAa,SAAS,mBAAmB,EAAqB,CAC1D,EAAI,MAAQ,EAAE,QACd,EAAI,MAAQ,EAAE,OAClB,EACA,WAAY,SAAS,mBAA0B,CACvC,EAAI,YAAc,OAClB,aAAa,EAAI,SAAS,EAC1B,EAAI,UAAY,MAGhB,QAAQ,gBAAkB,IAC1B,QAAQ,cAAgB,MAG5B,QAAQ,KAAK,CACjB,EAGA,YAAa,SAAS,oBAA2B,CACzC,EAAI,YAAc,OAClB,aAAa,EAAI,SAAS,EAC1B,EAAI,UAAY,MAGpB,QAAQ,KAAK,CACjB,CACJ,EAEA,EAAI,KAAK,YAAY,EAAS,YAAa,EAAI,WAAW,EAC1D,EAAI,KAAK,YAAY,EAAS,YAAa,EAAI,WAAW,EAC1D,EAAI,KAAK,YAAY,EAAS,WAAa,EAAI,UAAU,EACzD,EAAI,KAAK,YAAY,EAAS,YAAa,EAAI,WAAW,EAE1D,QAAQ,mBAAmB,IAAI,EAAS,CAAG,EAKvC,IACA,QAAQ,cAAgB,EACxB,QAAQ,KAAK,EAAM,EAAI,MAAO,EAAI,KAAK,EAE/C,CAWA,OAAO,cAAc,EAAuB,CACxC,IAAM,EAAM,QAAQ,mBAAmB,IAAI,CAAO,EAE7C,IAIL,EAAI,KAAK,eAAe,EAAS,YAAa,EAAI,WAAW,EAC7D,EAAI,KAAK,eAAe,EAAS,YAAa,EAAI,WAAW,EAC7D,EAAI,KAAK,eAAe,EAAS,WAAa,EAAI,UAAU,EAC5D,EAAI,KAAK,eAAe,EAAS,YAAa,EAAI,WAAW,EAEzD,EAAI,YAAc,OAClB,aAAa,EAAI,SAAS,EAC1B,EAAI,UAAY,MAGpB,QAAQ,mBAAmB,OAAO,CAAO,EAErC,QAAQ,gBAAkB,IAC1B,QAAQ,cAAgB,MAEhC,CAeA,gBAAiC,CAC7B,GAAM,CAAE,UAAW,EAAI,OAAO,YAAY,GAAG,EAE7C,OAAO,KAAK,KAAK,CAAM,CAC3B,CAQA,UAAiB,CACb,MAAM,SAAS,EAEf,IAAM,EAAM,KAAK,iBAAiB,EAWlC,OATK,GAIL,KAAK,MAAM,KAAK,EAAI,EAAI,QAAQ,UAAY,CAAC,EAC7C,KAAK,MAAM,KAAK,EAAI,EAAI,QAAQ,UAAY,CAAC,EAC7C,KAAK,MAAM,SAAS,KAAK,IAAI,EAAG,EAAI,MAAQ,QAAQ,SAAS,CAAC,EAC9D,KAAK,MAAM,UAAU,KAAK,WAAa,KAAK,QAAQ,EAE7C,MARI,IASf,CAOA,YAA6B,CACzB,KAAK,gBAAgB,OAAO,EAC5B,KAAK,eAAiB,KACtB,KAAK,gBAAgB,OAAO,EAC5B,KAAK,eAAiB,KAEtB,MAAM,WAAW,CACrB,CACJ"}
@@ -1,2 +0,0 @@
1
- import{t as e}from"./DOM-Ba8YRVel.js";import{S as t,h as n,t as r}from"./Component-w65rPK75.js";import{t as i}from"./ListenerBag-WPdKuuds.js";import{t as a}from"./Glyph-DeJF_zsR.js";import{t as o}from"./Text-CdHf3B-1.js";import{t as s}from"./ProgressSpinner-lAsK26C-.js";import{caret_down as c}from"./glyphs/solid/caret_down.es.js";import{n as l,t as u}from"./selectionsEqual-BrTxPMmK.js";import{caret_right as d}from"./glyphs/solid/caret_right.es.js";var TreeNodeRenderer=class extends r{constructor(){super()}},LabelTreeNodeRenderer=class extends TreeNodeRenderer{_label;constructor(){super(),this.clearInsets(),this._label=new o(void 0,{truncate:!0}),this._label.clearInsets(),this._label.setAutoMeasure(!1)}getLabel(){return this._label}update(e){this._label.setText(e.node.label),this._label.measure()}getContentWidth(){return this._label.getPreferredSize()?.width??0}layoutChildren(e,t){let n=this.getContentBounds()??{x:0,y:0,height:t},r=Math.min(this.getContentWidth(),e);this._label.setAutoCommitStyle(!1),this._label.setX(n.x),this._label.setY(n.y),this._label.setWidth(r),this._label.setHeight(n.height),this._label.setLineHeight(n.height),this._label.setAutoCommitStyle(!0)}init(t){super.init(t);let n=t||this.getElement();return n&&e.sink.appendChild(n,this._label.getElement(!0)),this}};a.register(c,d);var TreeRow=class extends r{_toggle=null;_spinner=null;_renderer;_node=null;_depth=0;constructor(e=()=>new LabelTreeNodeRenderer){super(),this.getAria().setRole(`treeitem`),this._renderer=e()}getToggle(){return this._toggle}getRenderer(){return this._renderer}setRenderer(t){let n=this.getElement();if(n){let r=this._renderer.getElement();r&&e.source.getParentNode(r)===n&&e.sink.removeChild(n,r),e.sink.appendChild(n,t.getElement(!0))}return this._renderer=t,this}getNode(){return this._node}getDepth(){return this._depth}setRowData(t,n,r,i,o,c,l,u){if(this._node=t,this._depth=n,this._toggle){let t=this.getElement();t&&e.sink.removeChild(t,this._toggle.getElement(!0)),this._toggle=null}if(this._spinner){let t=this.getElement();t&&e.sink.removeChild(t,this._spinner.getElement(!0)),this._spinner=null}if(u){let t=new s;this._spinner=t;let n=this.getElement();n&&e.sink.appendChild(n,t.getElement(!0))}else if(r){let t=new a(i?`caret-down`:`caret-right`);t.setCursor(`pointer`),t.clearInsets(),t.getAria().setHidden(!0),this._toggle=t;let n=this.getElement();n&&e.sink.appendChild(n,t.getElement(!0))}return this._renderer.update({node:t,depth:n,expanded:i,selected:l,hasChildren:r}),this.getAria().setLevel(n+1),this.getAria().setExpanded(r?i:null),this.getAria().setSetSize(o),this.getAria().setPosInSet(c),this}layoutChildren(e,t){let n=this.getContentBounds()??{x:0,y:0,width:this.getWidth()||0,height:e},r=n.x+this._depth*t;if(this._toggle){let e=this._toggle.getPreferredSize()?.height??n.height;this._toggle.setAutoCommitStyle(!1),this._toggle.setX(r),this._toggle.setY(n.y+Math.max(0,(n.height-e)/2)),this._toggle.setWidth(20),this._toggle.setHeight(e),this._toggle.setAutoCommitStyle(!0)}this._spinner&&(this._spinner.setAutoCommitStyle(!1),this._spinner.setX(r),this._spinner.setY(n.y),this._spinner.setWidth(20),this._spinner.setHeight(n.height),this._spinner.setAutoCommitStyle(!0),this._spinner.doLayout());let i=r+20,a=Math.max(0,n.x+n.width-i);this._renderer.setAutoCommitStyle(!1),this._renderer.setX(i),this._renderer.setY(n.y),this._renderer.setWidth(a),this._renderer.setHeight(n.height),this._renderer.setAutoCommitStyle(!0),this._renderer.layoutChildren(a,n.height)}getContentWidth(e){let t=this._depth*e,n=this._renderer.getContentWidth();return t+20+n}init(t){super.init(t);let n=t||this.getElement();return n&&e.sink.appendChild(n,this._renderer.getElement(!0)),this}},f=n(TreeRow),p=16,m=24,h=200,g=`var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))`,_={backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,overflow:`hidden`,maxSize:{width:2**53-1,height:2**53-1},rowOverflow:`scroll`},Tree=class extends l{_nodes=[];_expandedNodes=new Set;_flatRows=[];_lastRowWidth=0;_maxContentWidth=0;_selectedNodes=new Set;_loadingNodes=new Set;_loadedNodes=new Set;_anchorNode=null;_focusNode=null;_listeners=new i;_rendererFactory=()=>new LabelTreeNodeRenderer;constructor(e,t){super(e,{..._,...t??{}}),this.getAria().setRole(`tree`),this.getAria().setTabIndex(0),this.getAria().setMultiselectable(!0),this.subscribeTheme(()=>this.onThemeReflow()),this.applyListeners(e?.listeners)}applyOptions(e){return super.applyOptions(e),e.rowOverflow!==void 0&&this.setRowOverflow(e.rowOverflow),this}getRowOverflow(){return this._options.rowOverflow??this._defaultOptions.rowOverflow??`scroll`}setRowOverflow(e){return this._options.rowOverflow=e,this}getPreferredSize(){return this.getPreferredSizeConstraint()===null?{width:h,height:this._flatRows.length*this.getRowHeight()}:super.getPreferredSize()}setNodes(e){return this._nodes=e,this._expandedNodes.clear(),this._selectedNodes.clear(),this._loadingNodes.clear(),this._loadedNodes.clear(),this._anchorNode=null,this._focusNode=null,this._flatten(),this.getElement()&&(this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()),this}expandAll(){let addExpandable=e=>{for(let t of e)t.children&&t.children.length>0&&(this._expandedNodes.add(t),addExpandable(t.children))};return addExpandable(this._nodes),this._reflattenAndRender(),this}getRowHeight(){return m}createPoolRow(){return new f(this._rendererFactory)}getNodes(){return this._nodes}getSelectedNode(){return this._anchorNode}getSelectedNodes(){return Array.from(this._selectedNodes)}selectNode(e){let t=this._flatRows.findIndex(t=>t.node===e);return t<0?this:(this._selectedNodes.clear(),this._selectedNodes.add(e),this._anchorNode=e,this._focusNode=e,this._updateSelectionStyle(),this._scrollIntoView(t),this.renderWindow(),this._updateActiveDescendant(),this)}async revealByPredicate(e){let t=await this._findPath(this._nodes,e,[]);if(t===null)return null;for(let e=0;e<t.length-1;e++)this._expandedNodes.add(t[e]);this._reflattenAndRender();let n=t[t.length-1],r=this._flatRows.findIndex(e=>e.node===n);return r>=0&&this._scrollIntoView(r),n}async _findPath(e,t,n){for(let r of e){let e=[...n,r];if(t(r.data,r))return e;let i=await this._ensureChildrenLoaded(r);if(i.length>0){let n=await this._findPath(i,t,e);if(n!==null)return n}}return null}async _ensureChildrenLoaded(e){if(e.children&&e.children.length>0)return e.children;if(e.loadChildren!==void 0&&!this._loadedNodes.has(e))try{let t=await e.loadChildren();return e.children=t,this._loadedNodes.add(e),t}catch{return[]}return e.children??[]}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}setRendererFactory(e){this._rendererFactory=e;for(let t of this._rowPool)t.setRenderer(e());return this._boundIndices.fill(-1),this.invalidateGeom(),this.getElement()&&this.renderWindow(),this}getRendererFactory(){return this._rendererFactory}_isExpandable(e){return!!(e.children&&e.children.length>0)||e.hasChildren===!0}_flatten(){this._flatRows=[],this._maxContentWidth=0;let recurse=(e,t)=>{let n=e.length;for(let r=0;r<e.length;r++){let i=e[r];this._flatRows.push({node:i,depth:t,siblingCount:n,posInSet:r+1}),this._isExpandable(i)&&this._expandedNodes.has(i)&&i.children&&recurse(i.children,t+1)}};recurse(this._nodes,0)}_reflattenAndRender(){this._flatten(),this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()}expandNode(e){return this._expandedNodes.has(e)||this._onToggle(e),this}_onToggle(e){if(this._expandedNodes.has(e)){this._expandedNodes.delete(e),this._reflattenAndRender();return}if(!(e.loadChildren!==void 0&&!this._loadedNodes.has(e)&&!(e.children&&e.children.length))){this._expandedNodes.add(e),this._reflattenAndRender();return}this._loadingNodes.has(e)||this._loadAndExpand(e)}async _loadAndExpand(e){this._loadingNodes.add(e),this._reflattenAndRender();try{let t=await e.loadChildren();if(!this._loadingNodes.has(e))return;e.children=t,this._loadedNodes.add(e),this._expandedNodes.add(e)}catch(t){if(!this._loadingNodes.has(e))return;this.emit(`loaderror`,e,t)}finally{this._loadingNodes.delete(e),this._reflattenAndRender()}}_notifySelectionChange(e){u(e,this._selectedNodes)||this.emit(`selection`,this.getSelectedNodes())}_rangeSelect(e,t){let n=Math.min(e,t),r=Math.max(e,t);this._selectedNodes.clear();for(let e=n;e<=r;e++)this._selectedNodes.add(this._flatRows[e].node)}_selectAtIndex(e){if(e<0||e>=this._flatRows.length)return;let t=this._flatRows[e].node,n=new Set(this._selectedNodes);this._selectedNodes.clear(),this._selectedNodes.add(t),this._anchorNode=t,this._focusNode=t,this._updateSelectionStyle(),this._scrollIntoView(e),this.renderWindow(),this._updateActiveDescendant(),this._notifySelectionChange(n)}_extendSelectionTo(e){if(e<0||e>=this._flatRows.length)return;let t=new Set(this._selectedNodes),n=this._anchorNode?this._flatRows.findIndex(e=>e.node===this._anchorNode):e;this._rangeSelect(n<0?e:n,e),this._focusNode=this._flatRows[e].node,this._updateSelectionStyle(),this._scrollIntoView(e),this.renderWindow(),this._updateActiveDescendant(),this._notifySelectionChange(t)}_scrollIntoView(e){this.scrollRowIntoView(e)}_onKeyDown(e){if(!new Set([`ArrowDown`,`ArrowUp`,`ArrowLeft`,`ArrowRight`,`Home`,`End`]).has(e.key))return;let t=this._flatRows;if(t.length===0)return;e.preventDefault();let n=this._focusNode?t.findIndex(e=>e.node===this._focusNode):-1;if(e.key===`ArrowDown`){let r=n<0?0:Math.min(n+1,t.length-1);e.shiftKey?this._extendSelectionTo(r):this._selectAtIndex(r)}else if(e.key===`ArrowUp`){let t=n<0?0:Math.max(n-1,0);e.shiftKey?this._extendSelectionTo(t):this._selectAtIndex(t)}else if(e.key===`Home`)e.shiftKey?this._extendSelectionTo(0):this._selectAtIndex(0);else if(e.key===`End`){let n=t.length-1;e.shiftKey?this._extendSelectionTo(n):this._selectAtIndex(n)}else if(e.key===`ArrowRight`){if(n<0){this._selectAtIndex(0);return}let{node:e}=t[n];if(!this._isExpandable(e))return;this._expandedNodes.has(e)?this._selectAtIndex(n+1):this._onToggle(e)}else if(e.key===`ArrowLeft`){if(n<0)return;let{node:e,depth:r}=t[n];if(this._isExpandable(e)&&this._expandedNodes.has(e)){this._onToggle(e);let t=this._flatRows.findIndex(t=>t.node===e);t>=0&&this._scrollIntoView(t)}else if(r>0){for(let e=n-1;e>=0;e--)if(t[e].depth===r-1){this._selectAtIndex(e);break}}}}_handleClick(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getToggle();if(a){let t=a.getElement();if(t&&(n===t||e.source.contains(t,n))){this._onToggle(i);return}}let o=r.getElement();if(!o||!e.source.contains(o,n)&&n!==o)continue;let s=this._flatRows.findIndex(e=>e.node===i);if(t.shiftKey&&this._anchorNode)this._extendSelectionTo(s);else if(t.ctrlKey||t.metaKey){let e=new Set(this._selectedNodes);this._selectedNodes.has(i)?this._selectedNodes.delete(i):this._selectedNodes.add(i),this._anchorNode=i,this._focusNode=i,this._updateSelectionStyle(),this._notifySelectionChange(e)}else this._selectAtIndex(s);return}}_handleContextMenu(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getElement();if(!(!a||!e.source.contains(a,n)&&n!==a)){t.preventDefault(),this.emit(`contextmenu`,i,t);return}}}_handleDblClick(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getElement();if(!a||!e.source.contains(a,n)&&n!==a)continue;t.preventDefault(),this.emit(`dblclick`,i);let o=r.getToggle(),s=o?o.getElement():void 0;!(s&&(n===s||e.source.contains(s,n)))&&this._isExpandable(i)&&this._onToggle(i);return}}_updateSelectionStyle(){for(let t of this._rowPool){let n=t.getElement();if(!n)continue;let r=t.getNode(),i=r!==null&&this._selectedNodes.has(r),a=r!==null&&r===this._focusNode;i?e.sink.apply(n,{style:{"background-color":g}}):e.sink.apply(n,{style:{"background-color":null}}),a?e.sink.apply(n,{style:{outline:`2px solid var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))`,"outline-offset":`-2px`}}):e.sink.apply(n,{style:{outline:null,"outline-offset":null}}),t.getAria().setSelected(i)}}_updateActiveDescendant(){if(!this._focusNode){this.getAria().setActiveDescendant(``);return}for(let e of this._rowPool)if(e.getNode()===this._focusNode){this.getAria().setActiveDescendant(e.getId());return}this.getAria().setActiveDescendant(``)}onThemeReflow(){this._maxContentWidth=0,super.onThemeReflow()}renderWindow(){if(!this.getElement()||!this._scroller||this.deferRenderWhileFirstLayoutHeld())return;let e=this._scroller,t=this._flatRows.length,n=t*m;e.clampToContent(this._lastRowWidth,n);let r=this.getHeight()||0,i=this.computeVisibleWindow(e.getScrollY(),r,t),a=this.computePoolTarget(i.windowSize,r,t);this.growRowPool(a);let{reboundFlags:o,maxContentWidth:s}=this._bindAndMeasure(i.firstRow,i.windowSize);s>this._maxContentWidth&&(this._maxContentWidth=s);let c=this.getRowOverflow()===`clip`?e.getViewportWidth():Math.max(e.getViewportWidth(),this._maxContentWidth);c!==this._lastRowWidth&&(this._lastRowWidth=c,this.invalidateGeom()),this._positionRows(i.firstRow,i.windowSize,c,o),this.hideExcessPoolRows(i.windowSize),this._updateSelectionStyle(),e.layoutScrollbars(c,n),this.finishResumedRender()&&this._updateActiveDescendant()}_bindAndMeasure(e,t){let n=Array(t),r=0;for(let i=0;i<t;i++){let t=this._rowPool[i],a=e+i,o=this._flatRows[a],s=this._isExpandable(o.node),c=this._expandedNodes.has(o.node),l=this._loadingNodes.has(o.node),u=this._boundIndices[i]!==a;if(u){let e=this._selectedNodes.has(o.node);t.setRowData(o.node,o.depth,s,c,o.siblingCount,o.posInSet,e,l),this._boundIndices[i]=a}n[i]=u;let d=t.getContentWidth(p);d>r&&(r=d)}return{reboundFlags:n,maxContentWidth:r}}_positionRows(e,t,n,r){for(let i=0;i<t;i++){let t=this._rowPool[i],a=e+i,o=r[i],s=this.positionRow(i,a*m,n);(o||s)&&t.layoutChildren(m,p)}}init(e){super.init(e);let n=e||this.getElement();return n?(this.initScroller(n),t.addSubtreeListener(this,`click`,this._handleClick),t.addSubtreeListener(this,`contextmenu`,this._handleContextMenu),t.addSubtreeListener(this,`dblclick`,this._handleDblClick),t.addListener(this,`keydown`,this._onKeyDown),this.renderWindow(),this):this}doLayout(){return this.isLayoutPaused()?this:(super.doLayout(),this.renderWindowIfDeferred()||this.renderWindow(),this)}},v=n(Tree);export{LabelTreeNodeRenderer as n,TreeNodeRenderer as r,v as t};
2
- //# sourceMappingURL=Tree-C7qLbIYH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Tree-C7qLbIYH.js","names":[],"sources":["../../src/typescript/lib/component/tree/TreeNodeRenderer.ts","../../src/typescript/lib/component/tree/renderer/Label.ts","../../src/typescript/lib/component/tree/TreeRow.ts","../../src/typescript/lib/component/tree/Tree.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * Abstract base class for tree node renderers.\n *\n * @remarks\n * A `TreeNodeRenderer` controls the content area of a single\n * [`TreeRow`](/api/component/tree/classes/TreeRow) — that is, everything to\n * the right of the expand/collapse toggle. The owning [`Tree`](/api/component/tree/classes/Tree)\n * holds a renderer factory; each pool slot calls that factory once at\n * construction and rebinds via {@link update} when the slot is remapped to a\n * different node or its selection/expansion state changes.\n *\n * The toggle remains under `TreeRow`'s control because click detection routes\n * through it; renderers never see toggle clicks.\n *\n * Subclasses must implement {@link update}, {@link getContentWidth}, and\n * {@link layoutChildren}. See [`LabelTreeNodeRenderer`](/api/component/tree/classes/LabelTreeNodeRenderer)\n * for the canonical default.\n *\n * @category Components\n */\nexport abstract class TreeNodeRenderer extends Component {\n\n constructor() {\n super();\n }\n\n /**\n * Rebinds this renderer to new node data.\n *\n * @param context - The bound-node state for this render pass.\n *\n * @remarks\n * Called every time the owning pool slot is mapped to a different node, or\n * when an expansion-state change forces a full re-bind. Plain selection\n * style changes (background highlight, focus ring) are handled by the\n * `Tree` directly on the row element and do not flow through `update()`.\n */\n abstract update(context: TreeNodeRenderContext): void;\n\n /**\n * Returns the natural pixel width this renderer would render at given the\n * current bound node.\n *\n * @returns Content width in pixels.\n *\n * @remarks\n * Used by [`Tree`](/api/component/tree/classes/Tree) to compute the\n * horizontal scroll extent. Renderers that auto-measure their children\n * should return the sum of those measured widths.\n */\n abstract getContentWidth(): number;\n\n /**\n * Positions this renderer's internal children inside its content box.\n *\n * @param width - The horizontal extent of the renderer in pixels.\n * @param height - The vertical extent of the renderer in pixels.\n *\n * @remarks\n * Called by `TreeRow.layoutChildren` after the renderer's own size has\n * been set via `setX`/`setY`/`setWidth`/`setHeight`.\n *\n * The `width` and `height` arguments are this renderer's **outer** box, so\n * an implementation must not place children against them directly: take the\n * rectangle from [`getContentBounds()`](/api/core/classes/Component), which\n * subtracts any border and padding the renderer carries, and fall back to\n * the arguments only when it returns `null` (no element yet). A child's\n * containing block is already this renderer's padding box, so one placed\n * at `(0, 0)` sized to `width` / `height` starts inside the border and\n * overruns the opposite edge by its width, where the `overflow: hidden`\n * every component carries clips it.\n */\n abstract layoutChildren(width: number, height: number): void;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * The default [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) —\n * a single `<span>` carrying the node's label.\n *\n * @remarks\n * Used as the implicit factory by [`Tree`](/api/component/tree/classes/Tree)\n * when no custom renderer is set. The internal `Text` runs with\n * `autoMeasure(false)`; an explicit `measure()` call after each `setText`\n * caches the natural width for `getContentWidth()` to read on the next layout\n * pass.\n *\n * @example\n * ```typescript\n * tree.setRendererFactory(() => new LabelTreeNodeRenderer());\n * ```\n *\n * @category Components\n */\nexport class LabelTreeNodeRenderer extends TreeNodeRenderer {\n\n private _label: Text;\n\n /**\n * Constructs a label renderer with an empty text node. The label is\n * populated on the first {@link update} call.\n */\n constructor() {\n super();\n this.clearInsets();\n\n this._label = new Text(undefined, { truncate: true });\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Returns the underlying label component so consumers can tweak font /\n * colour properties at construction time.\n *\n * @returns The internal [`Text`](/api/component/input/classes/Text) instance.\n */\n getLabel(): Text {\n return this._label;\n }\n\n /**\n * Updates the rendered label text and re-measures.\n *\n * @param context - The bound-node state for this render pass.\n */\n update(context: TreeNodeRenderContext): void {\n this._label.setText(context.node.label);\n this._label.measure();\n }\n\n /**\n * Returns the cached natural width of the rendered label.\n *\n * @returns Label width in pixels.\n */\n getContentWidth(): number {\n return this._label.getPreferredSize()?.width ?? 0;\n }\n\n /**\n * Sizes the label to fill the renderer's allocated box and centres its\n * line-box vertically by matching `line-height` to the row height.\n *\n * The label goes inside this renderer's content box, so a border or padding\n * on the renderer shrinks it rather than being painted over.\n *\n * @param width - The row's available width in pixels. The label clamps to\n * this when it is narrower than the label's own natural width — its box\n * is otherwise always exactly as wide as its content (`getContentWidth`),\n * so `text-overflow: ellipsis` would never actually have room to trigger.\n * In `Tree`'s default `rowOverflow: \"scroll\"` mode this is a no-op: rows\n * already grow to fit the widest label (`Tree`'s `_maxContentWidth`), so\n * `width` is never narrower than `getContentWidth()` there. It only\n * clamps when `rowOverflow: \"clip\"` caps the row at the viewport width.\n * @param height - The vertical extent of the row in pixels. Used only while\n * the renderer has no element yet and the content box is unavailable.\n */\n layoutChildren(width: number, height: number): void {\n const box = this.getContentBounds() ?? { x: 0, y: 0, height };\n const labelWidth = Math.min(this.getContentWidth(), width);\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(box.x);\n this._label.setY(box.y);\n this._label.setWidth(labelWidth);\n this._label.setHeight(box.height);\n this._label.setLineHeight(box.height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element to the renderer's DOM element.\n *\n * @param element - Optional element passed by the rendering pipeline; 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 DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\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 { Glyph } from \"~/component/display/Glyph.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.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 icon. */\nexport const TOGGLE_WIDTH = 20;\n\n/**\n * A single visible row in the {@link Tree} virtual-scroll pool.\n *\n * Each pool slot holds one {@link TreeNode} at a time. The {@link Tree} calls\n * {@link setRowData} to rebind a slot to a different node and\n * {@link layoutChildren} to reposition the toggle and renderer after the row's\n * own dimensions are updated.\n *\n * @remarks\n * The toggle ([`Glyph`](/api/component/display/classes/Glyph)) and content\n * renderer are appended directly to the row's DOM element in `init()` rather\n * than via `addComponent`, so their preferred-size change notifications do not\n * propagate up to the Tree and trigger unnecessary layout passes. Leaf rows\n * have no toggle; non-leaf rows swap in a fresh `caret-down` / `caret-right`\n * glyph on each state change rather than mutating a single character. The row\n * content area (everything to the right of the toggle) is owned by a\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) supplied\n * via the constructor factory.\n */\nclass TreeRow extends Component {\n\n private _toggle: Glyph | null = null;\n private _spinner: ProgressSpinner | null = null;\n private _renderer: TreeNodeRenderer;\n private _node: TreeNode | null = null;\n private _depth: number = 0;\n\n constructor(rendererFactory: () => TreeNodeRenderer = () => new LabelTreeNodeRenderer()) {\n super();\n\n this.getAria().setRole(\"treeitem\");\n\n this._renderer = rendererFactory();\n }\n\n /**\n * Returns the toggle icon glyph, or null when the bound node is a leaf.\n *\n * @returns The toggle [`Glyph`](/api/component/display/classes/Glyph) instance, or null.\n */\n getToggle(): Glyph | null {\n return this._toggle;\n }\n\n /**\n * Returns the renderer currently bound to this pool row.\n *\n * @returns The active [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n */\n getRenderer(): TreeNodeRenderer {\n return this._renderer;\n }\n\n /**\n * Replaces this row's content renderer, swapping the underlying DOM\n * element in place.\n *\n * @param renderer - The new renderer instance to install.\n * @returns This row, for method chaining.\n *\n * @remarks\n * The new renderer's element is appended to the row immediately. The\n * caller is expected to follow up with `setRowData` (typically via the\n * Tree's next render pass) so the renderer receives an `update()` before\n * being laid out.\n */\n setRenderer(renderer: TreeNodeRenderer): this {\n const el = this.getElement();\n\n if (el) {\n const oldEl = this._renderer.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n\n DOM.sink.appendChild(el, renderer.getElement(true)!);\n }\n\n this._renderer = renderer;\n\n return this;\n }\n\n /**\n * Returns the tree node currently bound to this pool row.\n *\n * @returns The bound {@link TreeNode}, or null if the slot is unbound.\n */\n getNode(): TreeNode | null {\n return this._node;\n }\n\n /**\n * Returns the depth of the currently bound node.\n *\n * @returns The zero-based depth level.\n */\n getDepth(): number {\n return this._depth;\n }\n\n /**\n * Binds this pool slot to a new node, updating the toggle icon, content\n * renderer, and ARIA positional attributes.\n *\n * @param node - The tree node to display.\n * @param depth - The zero-based nesting depth (controls indentation).\n * @param hasChildren - Whether the node has child nodes (determines toggle visibility).\n * @param expanded - Whether the node is currently expanded.\n * @param siblingCount - Total number of siblings at this level under the same parent.\n * @param posInSet - 1-based position of this node among its siblings.\n * @param selected - Whether the node is currently selected.\n * @param loading - Whether this node's lazy children are currently loading;\n * when true a spinner replaces the toggle caret.\n */\n setRowData(node: TreeNode, depth: number, hasChildren: boolean, expanded: boolean, siblingCount: number, posInSet: number, selected: boolean, loading: boolean): this {\n this._node = node;\n this._depth = depth;\n\n if (this._toggle) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._toggle.getElement(true)!);\n }\n this._toggle = null;\n }\n\n if (this._spinner) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._spinner.getElement(true)!);\n }\n this._spinner = null;\n }\n\n if (loading) {\n // No explicit size: the spinner tracks the theme font-size so it\n // reads as the same visual weight as the caret glyph it replaces,\n // and `layoutChildren` fits it into the TOGGLE_WIDTH box.\n const spinner = new ProgressSpinner();\n this._spinner = spinner;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, spinner.getElement(true)!);\n }\n } else if (hasChildren) {\n const toggle = new Glyph(expanded ? \"caret-down\" : \"caret-right\");\n toggle.setCursor(\"pointer\");\n toggle.clearInsets();\n toggle.getAria().setHidden(true);\n this._toggle = toggle;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, toggle.getElement(true)!);\n }\n }\n\n this._renderer.update({ node, depth, expanded, selected, hasChildren });\n\n this.getAria().setLevel(depth + 1);\n this.getAria().setExpanded(hasChildren ? expanded : null);\n this.getAria().setSetSize(siblingCount);\n this.getAria().setPosInSet(posInSet);\n\n return this;\n }\n\n /**\n * Positions the toggle and renderer sub-components inside the row's content\n * box — the rectangle {@link Component.getContentBounds} returns, which a\n * border or padding on the row shrinks. Placing them against the row's outer\n * box instead makes the renderer overflow on both axes, and the row's\n * `overflow: hidden` clips it.\n *\n * @param rowHeight - The current height of this row in pixels. Used only\n * while the row has no element yet and the content box is unavailable;\n * otherwise the children's height comes from the content box, which is\n * this same height less the row's own border and padding.\n * @param indentPx - Pixels of indentation per depth level.\n */\n layoutChildren(rowHeight: number, indentPx: number): void {\n const box = this.getContentBounds()\n ?? { x: 0, y: 0, width: this.getWidth() || 0, height: rowHeight };\n\n const indent = box.x + this._depth * 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 ?? box.height;\n this._toggle.setAutoCommitStyle(false);\n this._toggle.setX(indent);\n this._toggle.setY(box.y + Math.max(0, (box.height - glyphHeight) / 2));\n this._toggle.setWidth(TOGGLE_WIDTH);\n this._toggle.setHeight(glyphHeight);\n this._toggle.setAutoCommitStyle(true);\n }\n\n if (this._spinner) {\n this._spinner.setAutoCommitStyle(false);\n this._spinner.setX(indent);\n this._spinner.setY(box.y);\n this._spinner.setWidth(TOGGLE_WIDTH);\n this._spinner.setHeight(box.height);\n this._spinner.setAutoCommitStyle(true);\n\n // The spinner owns its inner arc, which only positions itself when\n // its own doLayout runs; the row appends it directly rather than via\n // addComponent, so drive that layout here once the box is sized.\n this._spinner.doLayout();\n }\n\n const labelX = indent + TOGGLE_WIDTH;\n const labelBoxW = Math.max(0, box.x + box.width - labelX);\n\n this._renderer.setAutoCommitStyle(false);\n this._renderer.setX(labelX);\n this._renderer.setY(box.y);\n this._renderer.setWidth(labelBoxW);\n this._renderer.setHeight(box.height);\n this._renderer.setAutoCommitStyle(true);\n\n this._renderer.layoutChildren(labelBoxW, box.height);\n }\n\n /**\n * Returns the natural pixel width needed to display this row's full content\n * (indent + toggle + renderer content) without horizontal clipping.\n *\n * @param indentPx - Pixels of indentation per depth level.\n */\n getContentWidth(indentPx: number): number {\n const indent = this._depth * indentPx;\n const contentW = this._renderer.getContentWidth();\n\n return indent + TOGGLE_WIDTH + contentW;\n }\n\n /**\n * Appends the renderer sub-component element to the row's DOM element.\n * The toggle glyph (if any) is appended on demand by `setRowData`.\n *\n * @param element - Optional element passed by the rendering pipeline; 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 DOM.sink.appendChild(el, this._renderer.getElement(true)!);\n\n return this;\n }\n}\n\nconst TreeRowCallable = callable(TreeRow);\ntype TreeRowCallable = TreeRow;\nexport {\n TreeRow as _TreeRow,\n TreeRowCallable as TreeRow\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport type { Size } from \"~/primitive/Size.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { VirtualRowView } from \"~/component/shared/VirtualRowView.js\";\nimport { selectionsEqual } from \"~/component/shared/selectionsEqual.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeRow } from \"~/component/tree/TreeRow.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link Tree}.\n *\n * @category Components\n */\nexport type TreeEvent = \"selection\" | \"loaderror\" | \"contextmenu\" | \"dblclick\";\n\n/**\n * How a row wider than the viewport is handled.\n *\n * - `\"scroll\"` (the default) — every row grows to the widest label seen so\n * far and the horizontal scrollbar reveals the rest. Appropriate when a\n * label's full text matters (e.g. a file path).\n * - `\"clip\"` — every row caps at the viewport width and a row-node renderer\n * that honours it (e.g. {@link LabelTreeNodeRenderer}) truncates with an\n * ellipsis instead. Appropriate for a TOC-style outline, where reading a\n * label matters more than its exact text and scrolling sideways to read\n * one would be worse than truncating it.\n *\n * @category Components\n */\nexport type TreeRowOverflow = \"scroll\" | \"clip\";\n\n/** Pixels of indentation added per depth level. */\nconst INDENT_PX = 16;\n\n/** Fixed height in pixels for every visible row. */\nconst ROW_HEIGHT = 24;\n\n/** Default preferred width when the caller sets no explicit `preferredSize`. */\nconst DEFAULT_PREFERRED_WIDTH = 200;\n\n/** CSS background applied to the selected row. */\nconst SELECTED_BG = \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\";\n\n/**\n * A pre-flattened entry in the visible subtree used by the virtual scroll.\n */\ninterface FlatRow {\n node: TreeNode;\n depth: number;\n siblingCount: number;\n posInSet: number;\n}\n\n/**\n * Construction-time options for {@link Tree}. Root nodes are set via\n * `setNodes()` and the renderer factory via `setRendererFactory()`; this\n * interface only carries the event-listener bag and the inherited\n * {@link ComponentOptions}.\n *\n * @category Components\n */\nexport interface TreeOptions extends ComponentOptions {\n /** How a row wider than the viewport is handled. Default `\"scroll\"`. See {@link TreeRowOverflow}. */\n rowOverflow?: TreeRowOverflow;\n\n /**\n * Multi-event listener bag dispatched to {@link Tree.on} at\n * construction time.\n */\n listeners?: {\n selection?: (nodes: TreeNode[]) => void;\n loaderror?: (node: TreeNode, error: unknown) => void;\n contextmenu?: (node: TreeNode, event: MouseEvent) => void;\n dblclick?: (node: TreeNode) => void;\n };\n}\n\n/** User-overridable default fill; a caller-supplied `backgroundColor` wins. */\nconst _defaultTreeOptions: Partial<TreeOptions> = {\n backgroundColor: \"var(--ts-ui-input-bg, rgb(255, 255, 255))\",\n overflow: \"hidden\",\n maxSize: { width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER },\n rowOverflow: \"scroll\",\n};\n\n/**\n * A hierarchical data view with collapsible nodes and virtual scrolling.\n *\n * Pass root nodes via {@link Tree.setNodes}. The tree flattens the currently\n * visible subtree into a single scrollable list and recycles a fixed pool of\n * internal row components — rebinding rows only when their data index changes,\n * mirroring the approach used in `table/Body.ts`.\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 * @example\n * ```typescript\n * const tree = new Tree();\n * tree.setNodes([\n * { label: \"Fruits\", children: [\n * { label: \"Apple\" },\n * { label: \"Banana\" },\n * ]},\n * { label: \"Vegetables\" },\n * ]);\n * container.addComponent(tree);\n * ```\n *\n * @category Components\n */\nclass Tree extends VirtualRowView<TreeRow, TreeOptions> {\n\n private _nodes : TreeNode[] = [];\n private _expandedNodes : Set<TreeNode> = new Set();\n private _flatRows : FlatRow[] = [];\n private _lastRowWidth : number = 0;\n // Widest row content measured so far across the current flattened set. Only\n // the *visible* window is measured each frame, so tracking a running maximum\n // keeps the horizontal content width — and thus the H scrollbar — stable as\n // different-width rows scroll through the window, instead of jittering with\n // whatever happens to be on screen. Reset in `_flatten` when the row set is\n // rebuilt, so a narrower dataset (or a collapsed wide branch) re-derives it.\n private _maxContentWidth : number = 0;\n private _selectedNodes : Set<TreeNode> = new Set();\n private _loadingNodes : Set<TreeNode> = new Set();\n private _loadedNodes : Set<TreeNode> = new Set();\n private _anchorNode : TreeNode | null = null;\n private _focusNode : TreeNode | null = null;\n private _listeners : ListenerBag<TreeEvent> = new ListenerBag<TreeEvent>();\n private _rendererFactory : () => TreeNodeRenderer = () => new LabelTreeNodeRenderer();\n\n constructor(options?: TreeOptions, subclassDefaults?: Partial<TreeOptions>) {\n super(options, { ..._defaultTreeOptions, ...(subclassDefaults ?? {}) });\n\n this.getAria().setRole(\"tree\");\n this.getAria().setTabIndex(0);\n this.getAria().setMultiselectable(true);\n\n this.subscribeTheme(() => this.onThemeReflow());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Dispatches {@link TreeOptions.rowOverflow}; every other option is\n * inherited from {@link Component}.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This tree, for method chaining.\n */\n protected applyOptions(options: TreeOptions): this {\n super.applyOptions(options);\n\n if (options.rowOverflow !== undefined) {\n this.setRowOverflow(options.rowOverflow);\n }\n\n return this;\n }\n\n /**\n * How a row wider than the viewport is handled.\n *\n * @returns The cached {@link TreeOptions.rowOverflow}, or the class default when never set.\n */\n getRowOverflow(): TreeRowOverflow {\n return this._options.rowOverflow ?? this._defaultOptions.rowOverflow ?? \"scroll\";\n }\n\n /**\n * Sets how a row wider than the viewport is handled.\n *\n * @param rowOverflow - `\"scroll\"` grows rows to fit the widest label and\n * scrolls; `\"clip\"` caps rows at the viewport width and truncates. See\n * {@link TreeRowOverflow}.\n * @returns This tree, for method chaining.\n */\n setRowOverflow(rowOverflow: TreeRowOverflow): this {\n this._options.rowOverflow = rowOverflow;\n\n return this;\n }\n\n /**\n * Reports a height derived from the current flattened row count as the\n * preferred height when the caller has set no explicit `preferredSize`,\n * mirroring `Markdown.getPreferredSize`'s own measured-content override —\n * so a `Tree` sized by its preferred size (e.g. a corner-pinned `Anchor`\n * child, not stretched) grows and shrinks with its actual content instead\n * of reporting a fixed guess. Width keeps the class default; an explicit\n * `preferredSize` constraint wins outright.\n *\n * @returns The preferred `{width, height}`.\n */\n getPreferredSize(): Size | null {\n if (this.getPreferredSizeConstraint() !== null) {\n return super.getPreferredSize();\n }\n\n return { width: DEFAULT_PREFERRED_WIDTH, height: this._flatRows.length * this.getRowHeight() };\n }\n\n /**\n * Replaces the root nodes, collapses all nodes, clears selection, and re-renders.\n *\n * @param nodes - The new array of root {@link TreeNode} objects.\n */\n setNodes(nodes: TreeNode[]): this {\n this._nodes = nodes;\n this._expandedNodes.clear();\n this._selectedNodes.clear();\n this._loadingNodes.clear();\n this._loadedNodes.clear();\n this._anchorNode = null;\n this._focusNode = null;\n this._flatten();\n\n if (this.getElement()) {\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Expands every node that has (already-loaded) children, so the whole tree\n * is flattened and visible. Does not load lazy branches and does not change\n * the selection or emit any event.\n *\n * @returns This tree, for method chaining.\n */\n expandAll(): this {\n const addExpandable = (nodes: TreeNode[]): void => {\n for (const node of nodes) {\n if (node.children && node.children.length > 0) {\n this._expandedNodes.add(node);\n addExpandable(node.children);\n }\n }\n };\n\n addExpandable(this._nodes);\n this._reflattenAndRender();\n\n return this;\n }\n\n /** Returns the fixed row height every tree row is laid out at. */\n protected getRowHeight(): number {\n return ROW_HEIGHT;\n }\n\n /**\n * Constructs one pool row backed by the current renderer factory. The\n * base's `growRowPool` owns the append + parallel-array bookkeeping.\n *\n * @returns The new, un-appended pool row.\n */\n protected createPoolRow(): TreeRow {\n return new TreeRow(this._rendererFactory);\n }\n\n /**\n * Returns the root nodes currently displayed by the tree.\n *\n * @returns The array of root {@link TreeNode} objects.\n */\n getNodes(): TreeNode[] {\n return this._nodes;\n }\n\n /**\n * Returns the anchor node of the current selection (the last node selected by a plain click or\n * arrow key without Shift), or null if there is no selection.\n *\n * @returns The anchor {@link TreeNode}, or null.\n */\n getSelectedNode(): TreeNode | null {\n return this._anchorNode;\n }\n\n /**\n * Returns all currently selected nodes in no guaranteed order.\n *\n * @returns An array of selected {@link TreeNode} instances.\n */\n getSelectedNodes(): TreeNode[] {\n return Array.from(this._selectedNodes);\n }\n\n /**\n * Programmatically selects a single node, replacing any existing selection\n * and scrolling it into view.\n *\n * Unlike a user click this does **not** emit the `\"selection\"` event: it is a\n * state setter for syncing the tree to an external source of truth (e.g. the\n * active dock tab), so it must not re-trigger selection-driven side effects.\n * No-op when the node is not in the currently visible (flattened) set — e.g.\n * an ancestor is collapsed.\n *\n * @param node - A node currently held by this tree.\n *\n * @returns This tree, for method chaining.\n */\n selectNode(node: TreeNode): this {\n const index = this._flatRows.findIndex(r => r.node === node);\n if (index < 0) {\n return this;\n }\n\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this._anchorNode = node;\n this._focusNode = node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n\n return this;\n }\n\n /**\n * Finds the first node whose payload satisfies `predicate`, expanding every\n * ancestor on the path (loading lazy branches as needed) so the node becomes\n * visible, then scrolls it into view. Returns the node, or `null` when no\n * node matches.\n *\n * Unlike {@link selectNode} — which no-ops when the target sits under a\n * collapsed or not-yet-loaded ancestor — this walks the whole tree,\n * awaiting each lazy branch's `loadChildren` on the way down, so it can\n * reveal a node the user has never manually expanded to. Because it may\n * load every branch it visits, it is O(nodes) and issues one `loadChildren`\n * per unloaded branch it descends; call it for a deliberate \"jump to this\n * object\" action, not on a hot path.\n *\n * Revealing is not selecting: this method does not change the selection or\n * emit `\"selection\"`. Select the returned node with {@link selectNode} if the\n * reveal should also highlight it.\n *\n * The search is depth-first in root-then-child order and stops at the first\n * match. A lazy branch whose `loadChildren` rejects is skipped (its subtree\n * is treated as empty) and the walk continues; the failure is not surfaced\n * here (no `\"loaderror\"` is emitted for a reveal-driven load).\n *\n * @param predicate - Tested against each node's `data` payload (and the node\n * itself); return `true` for the node to reveal.\n *\n * @returns A promise resolving to the revealed {@link TreeNode}, or `null`.\n */\n async revealByPredicate(predicate: (data: unknown, node: TreeNode) => boolean): Promise<TreeNode | null> {\n const path = await this._findPath(this._nodes, predicate, []);\n if (path === null) {\n return null;\n }\n\n // Expand every ancestor (all but the target itself) so the target's row\n // enters the flattened set; the target's own children stay as they are.\n for (let i = 0; i < path.length - 1; i++) {\n this._expandedNodes.add(path[i]);\n }\n this._reflattenAndRender();\n\n const target = path[path.length - 1];\n const index = this._flatRows.findIndex(r => r.node === target);\n if (index >= 0) {\n this._scrollIntoView(index);\n }\n\n return target;\n }\n\n /**\n * Depth-first search for the first node satisfying `predicate`, returning\n * the full root-to-match path (inclusive) or `null`. Awaits and caches each\n * visited branch's lazy children so the walk can descend into unloaded\n * subtrees.\n *\n * @param nodes - The sibling nodes to search at this level.\n * @param predicate - The match test (see {@link revealByPredicate}).\n * @param prefix - The ancestor path leading to `nodes`.\n *\n * @returns The path from a root to the matching node, or `null`.\n */\n private async _findPath(\n nodes: TreeNode[],\n predicate: (data: unknown, node: TreeNode) => boolean,\n prefix: TreeNode[],\n ): Promise<TreeNode[] | null> {\n for (const node of nodes) {\n const here = [...prefix, node];\n\n if (predicate(node.data, node)) {\n return here;\n }\n\n const children = await this._ensureChildrenLoaded(node);\n if (children.length > 0) {\n const found = await this._findPath(children, predicate, here);\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n }\n\n /**\n * Returns a node's children, loading and caching them from `loadChildren`\n * first when the node is lazy and unloaded. Mirrors {@link _loadAndExpand}'s\n * cache writes (`children` + `_loadedNodes`) but does not expand the node —\n * a reveal only expands the ancestors on the path to its match. A rejected\n * load is swallowed and treated as an empty subtree.\n *\n * @param node - The node whose children are needed.\n *\n * @returns The node's children (possibly empty).\n */\n private async _ensureChildrenLoaded(node: TreeNode): Promise<TreeNode[]> {\n if (node.children && node.children.length > 0) {\n return node.children;\n }\n\n if (node.loadChildren !== undefined && !this._loadedNodes.has(node)) {\n try {\n const children = await node.loadChildren();\n\n node.children = children;\n this._loadedNodes.add(node);\n\n return children;\n } catch {\n return [];\n }\n }\n\n return node.children ?? [];\n }\n\n /**\n * Registers a listener for one of this tree's events.\n *\n * @param event - `\"selection\"` fires whenever the selection changes,\n * receiving the full array of selected {@link TreeNode} instances.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"selection\", listener: (nodes: TreeNode[]) => void): this;\n\n /**\n * Registers a listener for a lazy node's failed child load.\n *\n * @param event - `\"loaderror\"` fires when a node's `loadChildren` rejects;\n * the node stays collapsed and unloaded so toggling again retries.\n * @param listener - Receives the {@link TreeNode} whose load failed and the\n * rejection reason.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"loaderror\", listener: (node: TreeNode, error: unknown) => void): this;\n\n /**\n * Registers a listener for a right-click on a node.\n *\n * @param event - `\"contextmenu\"` fires when a node row is right-clicked,\n * receiving the {@link TreeNode} under the cursor and the originating\n * `MouseEvent` (its `clientX`/`clientY` position a context menu). The\n * default browser menu is suppressed and the tree's selection is left\n * unchanged, so a right-click never opens a node the way a left-click does.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"contextmenu\", listener: (node: TreeNode, event: MouseEvent) => void): this;\n\n /**\n * Registers a listener for a double-click (activation) on a node.\n *\n * @param event - `\"dblclick\"` fires when a node row is double-clicked,\n * receiving the {@link TreeNode} under the cursor. The first click of the\n * pair still emits `\"selection\"` as usual, so `\"dblclick\"` is an\n * activation signal layered on top of selection — consumers typically use\n * `\"selection\"` to preview a node and `\"dblclick\"` to open it.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"dblclick\", listener: (node: TreeNode) => void): this;\n on(event: TreeEvent, 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 tree, for method chaining.\n */\n off(event: TreeEvent, 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: \"selection\", nodes: TreeNode[]): void;\n protected emit(event: \"loaderror\", node: TreeNode, error: unknown): void;\n protected emit(event: \"contextmenu\", node: TreeNode, event_: MouseEvent): void;\n protected emit(event: \"dblclick\", node: TreeNode): void;\n protected emit(event: TreeEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the renderer factory used to create one\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer)\n * instance per pool slot.\n *\n * @param factory - Zero-argument function returning a new\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n * Defaults to `() => new LabelTreeNodeRenderer()`.\n * @returns This tree, for method chaining.\n *\n * @remarks\n * If pool rows already exist, each one's renderer is replaced in place via\n * `TreeRow.setRenderer(factory())`. The DOM pool itself is preserved.\n * Bindings are cleared so the next render pass rebinds and updates the\n * new renderers.\n */\n setRendererFactory(factory: () => TreeNodeRenderer): this {\n this._rendererFactory = factory;\n\n for (const row of this._rowPool) {\n row.setRenderer(factory());\n }\n\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n\n if (this.getElement()) {\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Returns the renderer factory currently in use.\n *\n * @returns The zero-argument factory function.\n */\n getRendererFactory(): () => TreeNodeRenderer {\n return this._rendererFactory;\n }\n\n /**\n * Reports whether a node renders an expand/collapse caret.\n *\n * @param node - The node to test.\n * @returns True when the node already carries children, or is a lazy node\n * declared with `hasChildren: true`.\n *\n * @remarks\n * Centralising the predicate keeps the caret, ARIA, and flatten decisions\n * from drifting apart. An eager node with an empty `children` array is not\n * expandable; a lazy node is expandable before its children exist.\n */\n private _isExpandable(node: TreeNode): boolean {\n return !!(node.children && node.children.length > 0) || node.hasChildren === true;\n }\n\n /**\n * Rebuilds the flat visible-row list from the current root nodes and expanded set,\n * computing `siblingCount` and `posInSet` (1-based) for each entry.\n */\n private _flatten(): void {\n this._flatRows = [];\n\n // The visible set is changing, so the widest-row running maximum no\n // longer describes it — reset it and let the next render pass re-derive\n // the content width from the new rows (a collapsed wide branch or a\n // narrower dataset must be able to shrink the horizontal extent).\n this._maxContentWidth = 0;\n\n const recurse = (nodes: TreeNode[], depth: number): void => {\n const siblingCount = nodes.length;\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n this._flatRows.push({ node, depth, siblingCount, posInSet: i + 1 });\n\n if (this._isExpandable(node) && this._expandedNodes.has(node) && node.children) {\n recurse(node.children, depth + 1);\n }\n }\n };\n\n recurse(this._nodes, 0);\n }\n\n /**\n * Re-flattens the visible subtree and forces a full rebind of the pool so\n * every row reflects the current expanded/loading state.\n *\n * @remarks\n * Called from the collapse path, the synchronous expand path, and the async\n * load path — anywhere the flattened-row set or a node's loading affordance\n * changes.\n */\n private _reflattenAndRender(): void {\n this._flatten();\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n /**\n * Expands `node` if it is not already expanded — the same commit path as\n * clicking its collapsed caret (loading its children first when it is an\n * unloaded lazy node), without requiring a prior click. No-ops if `node`\n * is already expanded. Useful for defaulting specific sections open after\n * {@link setNodes}, which always starts every node collapsed.\n *\n * @param node - The node to expand.\n *\n * @returns This tree, for method chaining.\n */\n expandNode(node: TreeNode): this {\n if (!this._expandedNodes.has(node)) {\n this._onToggle(node);\n }\n\n return this;\n }\n\n /**\n * Toggles the expanded state of a node, re-flattens, and re-renders.\n *\n * @param node - The node whose expanded state should be toggled.\n *\n * @remarks\n * Collapse and already-resolved expansion commit synchronously. A lazy node\n * declared with `loadChildren` that has not loaded yet defers its expansion\n * to {@link _loadAndExpand}, which only commits once the loader resolves.\n */\n private _onToggle(node: TreeNode): void {\n if (this._expandedNodes.has(node)) {\n this._expandedNodes.delete(node);\n this._reflattenAndRender();\n\n return;\n }\n\n const needsLoad = node.loadChildren !== undefined\n && !this._loadedNodes.has(node)\n && !(node.children && node.children.length);\n\n if (!needsLoad) {\n this._expandedNodes.add(node);\n this._reflattenAndRender();\n\n return;\n }\n\n if (this._loadingNodes.has(node)) {\n return;\n }\n\n void this._loadAndExpand(node);\n }\n\n /**\n * Loads a lazy node's children, then commits its expansion.\n *\n * @param node - The lazy node to load and expand.\n *\n * @remarks\n * Marks the node loading (driving its spinner affordance), awaits\n * `loadChildren`, and on success writes `children`, records the node as\n * loaded and expanded. A rejection emits `\"loaderror\"` and leaves the node\n * collapsed and unloaded so toggling again retries. An empty resolved array\n * is treated as success: the node renders as an expanded, empty parent.\n *\n * If `setNodes` swaps the dataset while the loader is in flight, it clears\n * `_loadingNodes`, so a still-present membership check after the await tells\n * us the node is still live; an orphaned resolve commits nothing.\n */\n private async _loadAndExpand(node: TreeNode): Promise<void> {\n this._loadingNodes.add(node);\n this._reflattenAndRender();\n\n try {\n const children = await node.loadChildren!();\n\n if (!this._loadingNodes.has(node)) {\n return;\n }\n\n node.children = children;\n this._loadedNodes.add(node);\n this._expandedNodes.add(node);\n } catch (error) {\n if (!this._loadingNodes.has(node)) {\n return;\n }\n\n this.emit(\"loaderror\", node, error);\n } finally {\n this._loadingNodes.delete(node);\n this._reflattenAndRender();\n }\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<TreeNode>): void {\n if (selectionsEqual(before, this._selectedNodes)) {\n return;\n }\n\n this.emit(\"selection\", this.getSelectedNodes());\n }\n\n /**\n * Fills `_selectedNodes` with every node in `_flatRows` between `anchorIdx` and `focusIdx` (inclusive).\n *\n * @param anchorIdx - One end of the range.\n * @param focusIdx - The other end of the range (may be less than `anchorIdx`).\n */\n private _rangeSelect(anchorIdx: number, focusIdx: number): void {\n const lo = Math.min(anchorIdx, focusIdx);\n const hi = Math.max(anchorIdx, focusIdx);\n\n this._selectedNodes.clear();\n\n for (let i = lo; i <= hi; i++) {\n this._selectedNodes.add(this._flatRows[i].node);\n }\n }\n\n /**\n * Replaces the selection with the single node at `index`, resets the anchor and focus, and scrolls it into view.\n *\n * @param index - Zero-based index into `_flatRows`.\n */\n private _selectAtIndex(index: number): void {\n if (index < 0 || index >= this._flatRows.length) {\n return;\n }\n\n const node = this._flatRows[index].node;\n const before = new Set(this._selectedNodes);\n\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this._anchorNode = node;\n this._focusNode = node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n this._notifySelectionChange(before);\n }\n\n /**\n * Extends the range selection from `_anchorNode` to `index`, moving only the focus end.\n *\n * @param index - Zero-based index into `_flatRows` for the new focus position.\n */\n private _extendSelectionTo(index: number): void {\n if (index < 0 || index >= this._flatRows.length) {\n return;\n }\n\n const before = new Set(this._selectedNodes);\n\n const anchorIdx = this._anchorNode\n ? this._flatRows.findIndex(r => r.node === this._anchorNode)\n : index;\n\n this._rangeSelect(anchorIdx < 0 ? index : anchorIdx, index);\n this._focusNode = this._flatRows[index].node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n this._notifySelectionChange(before);\n }\n\n /**\n * Scrolls the tree element so the row at `index` is fully visible, without\n * moving if it already is. Delegates to the shared base implementation.\n *\n * @param index - Zero-based index into `_flatRows`.\n */\n private _scrollIntoView(index: number): void {\n this.scrollRowIntoView(index);\n }\n\n /**\n * Handles keyboard navigation for the tree widget.\n *\n * @remarks\n * Implements the WAI-ARIA tree keyboard interaction pattern:\n * - `ArrowDown` / `ArrowUp` — move to next/previous visible row (plain) or extend range (Shift)\n * - `ArrowRight` — expand a collapsed node, or move into an already-expanded node's first child\n * - `ArrowLeft` — collapse an expanded node, or move to its parent\n * - `Home` / `End` — jump to first/last visible row (plain) or extend range to edge (Shift)\n *\n * @param e - The keyboard event fired on the tree element.\n */\n private _onKeyDown(e: KeyboardEvent): void {\n const navigable = new Set(['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']);\n if (!navigable.has(e.key)) {\n return;\n }\n\n const flatRows = this._flatRows;\n if (flatRows.length === 0) {\n return;\n }\n\n e.preventDefault();\n\n // For Shift+Arrow the moving end is the focus node, not the anchor\n const focusIdx = this._focusNode\n ? flatRows.findIndex(r => r.node === this._focusNode)\n : -1;\n\n if (e.key === 'ArrowDown') {\n const next = focusIdx < 0 ? 0 : Math.min(focusIdx + 1, flatRows.length - 1);\n e.shiftKey ? this._extendSelectionTo(next) : this._selectAtIndex(next);\n\n } else if (e.key === 'ArrowUp') {\n const prev = focusIdx < 0 ? 0 : Math.max(focusIdx - 1, 0);\n e.shiftKey ? this._extendSelectionTo(prev) : this._selectAtIndex(prev);\n\n } else if (e.key === 'Home') {\n e.shiftKey ? this._extendSelectionTo(0) : this._selectAtIndex(0);\n\n } else if (e.key === 'End') {\n const last = flatRows.length - 1;\n e.shiftKey ? this._extendSelectionTo(last) : this._selectAtIndex(last);\n\n } else if (e.key === 'ArrowRight') {\n if (focusIdx < 0) {\n this._selectAtIndex(0);\n return;\n }\n\n const { node } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (!hasChildren) {\n return;\n }\n\n if (this._expandedNodes.has(node)) {\n // Already expanded — move focus into the first child\n this._selectAtIndex(focusIdx + 1);\n } else {\n // Collapsed — expand without moving focus\n this._onToggle(node);\n }\n\n } else if (e.key === 'ArrowLeft') {\n if (focusIdx < 0) {\n return;\n }\n\n const { node, depth } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (hasChildren && this._expandedNodes.has(node)) {\n // Expanded — collapse and stay on the same node\n this._onToggle(node);\n const newIdx = this._flatRows.findIndex(r => r.node === node);\n if (newIdx >= 0) {\n this._scrollIntoView(newIdx);\n }\n } else if (depth > 0) {\n // Collapsed or leaf — move to parent (nearest ancestor with depth - 1)\n for (let i = focusIdx - 1; i >= 0; i--) {\n if (flatRows[i].depth === depth - 1) {\n this._selectAtIndex(i);\n break;\n }\n }\n }\n }\n }\n\n /**\n * Dispatches clicks to either toggle expand/collapse or select a row.\n *\n * @remarks\n * Supports three selection modes via modifier keys:\n * - Plain click: replace selection with the clicked node\n * - Ctrl/Cmd+click: toggle the clicked node without disturbing others\n * - Shift+click: range-select from the anchor to the clicked node\n *\n * @param e - The click event whose target is inside the tree's DOM subtree.\n */\n private _handleClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const toggle = row.getToggle();\n if (toggle) {\n const toggleEl = toggle.getElement();\n if (toggleEl && (target === toggleEl || DOM.source.contains(toggleEl, target))) {\n this._onToggle(node);\n return;\n }\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n const clickedIdx = this._flatRows.findIndex(r => r.node === node);\n\n if (e.shiftKey && this._anchorNode) {\n this._extendSelectionTo(clickedIdx);\n } else if (e.ctrlKey || e.metaKey) {\n const before = new Set(this._selectedNodes);\n\n if (this._selectedNodes.has(node)) {\n this._selectedNodes.delete(node);\n } else {\n this._selectedNodes.add(node);\n }\n\n this._anchorNode = node;\n this._focusNode = node;\n this._updateSelectionStyle();\n this._notifySelectionChange(before);\n } else {\n this._selectAtIndex(clickedIdx);\n }\n\n return;\n }\n }\n\n /**\n * Resolves the node under a right-click and emits the `\"contextmenu\"` event.\n *\n * @remarks\n * Mirrors {@link _handleClick}'s row-matching but deliberately does not change\n * the selection: a right-click positions a context menu over a node without\n * triggering the selection-driven side effects (e.g. opening the node) that a\n * left-click would. When a node row is hit the browser's native menu is\n * suppressed via `preventDefault` and `\"contextmenu\"` fires with the node and\n * the originating event; a right-click on empty space is left to the browser.\n *\n * @param e - The contextmenu event whose target is inside the tree's subtree.\n */\n private _handleContextMenu(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n e.preventDefault();\n this.emit(\"contextmenu\", node, e);\n\n return;\n }\n }\n\n /**\n * Resolves the node under a double-click and emits the `\"dblclick\"` event.\n *\n * @remarks\n * Mirrors {@link _handleContextMenu}'s row-matching. The first click of the\n * pair has already run through {@link _handleClick} and set the selection, so\n * this only layers an activation signal on top — it does not re-select.\n * `preventDefault` suppresses the browser's double-click text selection of the\n * row label.\n *\n * @param e - The dblclick event whose target is inside the tree's subtree.\n */\n private _handleDblClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n e.preventDefault();\n this.emit(\"dblclick\", node);\n\n // Double-clicking a parent row toggles its expansion, the\n // file-explorer convention. A double-click on the toggle caret is\n // skipped: `_handleClick` already fired `_onToggle` on each of the\n // two clicks, so its net state is unchanged and toggling again here\n // would flip it a third time. Leaf nodes have nothing to expand, so\n // their `\"dblclick\"` stays a pure activation signal.\n const toggle = row.getToggle();\n const toggleEl = toggle ? toggle.getElement() : undefined;\n const onToggle = !!toggleEl\n && (target === toggleEl || DOM.source.contains(toggleEl, target));\n\n if (!onToggle && this._isExpandable(node)) {\n this._onToggle(node);\n }\n\n return;\n }\n }\n\n /**\n * Applies or removes the selection highlight, focus ring, and aria-selected on all bound pool rows.\n */\n private _updateSelectionStyle(): void {\n for (const row of this._rowPool) {\n const rowEl = row.getElement();\n\n if (!rowEl) {\n continue;\n }\n\n const node = row.getNode();\n const isSelected = node !== null && this._selectedNodes.has(node!);\n const isFocused = node !== null && node === this._focusNode;\n\n // Per-node ephemeral selection/focus styling on a pooled row re-bound\n // to a different node on every render. Routing this through cached\n // Component setters would persist it into _options and replay it onto\n // the next node bound to this reused row, so write/remove the inline\n // styles directly instead.\n if (isSelected) {\n DOM.sink.apply(rowEl, { style: { \"background-color\": SELECTED_BG } });\n } else {\n DOM.sink.apply(rowEl, { style: { \"background-color\": null } });\n }\n\n if (isFocused) {\n DOM.sink.apply(rowEl, { style: { \"outline\": \"2px solid var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))\", \"outline-offset\": \"-2px\" } });\n } else {\n DOM.sink.apply(rowEl, { style: { \"outline\": null, \"outline-offset\": null } });\n }\n\n row.getAria().setSelected(isSelected);\n }\n }\n\n /**\n * Sets `aria-activedescendant` on the tree container to the pool row bound to `_focusNode`.\n *\n * @remarks Must be called after `renderWindow()` so the pool slot is guaranteed in the DOM.\n */\n private _updateActiveDescendant(): void {\n if (!this._focusNode) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n for (const row of this._rowPool) {\n if (row.getNode() === this._focusNode) {\n this.getAria().setActiveDescendant(row.getId());\n\n return;\n }\n }\n\n this.getAria().setActiveDescendant(\"\");\n }\n\n /**\n * Drops the running content-width maximum before the shared re-bind pass.\n *\n * @remarks The maximum is deliberately monotonic within a flattened set so\n * the horizontal scrollbar does not jitter as rows scroll in and out (it is\n * otherwise reset only in `_flatten`). A metrics reflow invalidates the\n * measurements it was accumulated from, so keeping it would pin the content\n * width to the old font — leaving a phantom scroll range when the new face\n * is narrower, and never widening past the stale value.\n */\n protected onThemeReflow(): void {\n this._maxContentWidth = 0;\n\n super.onThemeReflow();\n }\n\n /**\n * Recomputes the visible row window, rebinds changed pool slots, and hides excess rows.\n */\n protected renderWindow(): void {\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n\n if (this.deferRenderWhileFirstLayoutHeld()) {\n return;\n }\n\n const scroller = this._scroller;\n\n const totalRows = this._flatRows.length;\n const totalHeight = totalRows * ROW_HEIGHT;\n\n // Loose clamp using the last-known content width (the actual contentW\n // for this frame is computed below from the first row-bind pass).\n scroller.clampToContent(this._lastRowWidth, totalHeight);\n\n const visibleHeight = this.getHeight() || 0;\n const win = this.computeVisibleWindow(scroller.getScrollY(), visibleHeight, totalRows);\n\n const poolTarget = this.computePoolTarget(win.windowSize, visibleHeight, totalRows);\n this.growRowPool(poolTarget);\n\n const { reboundFlags, maxContentWidth } =\n this._bindAndMeasure(win.firstRow, win.windowSize);\n\n // Only the visible window was measured, so fold the widest row seen this\n // frame into the running maximum for the current flattened set (reset in\n // `_flatten`). The content width then only grows as wider rows are\n // discovered and never shrinks when they scroll away, so the horizontal\n // scrollbar stays stable instead of jittering with the visible rows.\n if (maxContentWidth > this._maxContentWidth) {\n this._maxContentWidth = maxContentWidth;\n }\n\n // Fill to the effective viewport width (owner width minus the vertical\n // scrollbar's reservation when that bar is visible), not the raw owner\n // width. Sizing rows to the full width would push their trailing edge\n // under the vertical bar, and handing that full width to\n // `layoutScrollbars` as the content width forces a spurious horizontal\n // bar for the reserved band. `clampToContent` above has already refreshed\n // the scroller's content metrics this pass.\n //\n // `rowOverflow: \"clip\"` never grows past the viewport at all — every\n // row (and so every renderer) is capped there instead, trading the\n // horizontal scrollbar for truncation (see `LabelTreeNodeRenderer`).\n const rowWidth = this.getRowOverflow() === \"clip\"\n ? scroller.getViewportWidth()\n : Math.max(scroller.getViewportWidth(), this._maxContentWidth);\n if (rowWidth !== this._lastRowWidth) {\n this._lastRowWidth = rowWidth;\n this.invalidateGeom();\n }\n\n this._positionRows(win.firstRow, win.windowSize, rowWidth, reboundFlags);\n this.hideExcessPoolRows(win.windowSize);\n\n this._updateSelectionStyle();\n\n scroller.layoutScrollbars(rowWidth, totalHeight);\n\n // A pass the startup font gate deferred skipped whatever its caller did\n // after asking for it — including the active-descendant refresh, which\n // could not name a row that did not exist yet. Redo it now the rows do.\n if (this.finishResumedRender()) {\n this._updateActiveDescendant();\n }\n }\n\n /**\n * First pass: binds visible pool slots to their flattened rows (when\n * rebound) and computes the maximum content width across visible rows so\n * the second pass knows how wide each row should be.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @returns Per-slot rebind flags (parallel to the window) and the widest\n * content width seen, both consumed by {@link _positionRows}.\n */\n private _bindAndMeasure(firstRow: number, windowSize: number): { reboundFlags: boolean[], maxContentWidth: number } {\n const reboundFlags: boolean[] = new Array(windowSize);\n let maxContentWidth = 0;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const flatRow = this._flatRows[dataIndex];\n const hasChildren = this._isExpandable(flatRow.node);\n const expanded = this._expandedNodes.has(flatRow.node);\n const loading = this._loadingNodes.has(flatRow.node);\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound) {\n const selected = this._selectedNodes.has(flatRow.node);\n row.setRowData(flatRow.node, flatRow.depth, hasChildren, expanded, flatRow.siblingCount, flatRow.posInSet, selected, loading);\n this._boundIndices[i] = dataIndex;\n }\n\n reboundFlags[i] = wasRebound;\n\n const cw = row.getContentWidth(INDENT_PX);\n if (cw > maxContentWidth) {\n maxContentWidth = cw;\n }\n }\n\n return { reboundFlags, maxContentWidth };\n }\n\n /**\n * Second pass: positions visible rows at `dataIndex * ROW_HEIGHT`, sizes\n * them to `rowWidth`, marks them displayed, and re-lays out their children\n * only when the row was rebound or its geometry 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 reboundFlags - The per-slot rebind flags produced by {@link _bindAndMeasure}.\n */\n private _positionRows(firstRow: number, windowSize: number, rowWidth: number, reboundFlags: boolean[]): void {\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const wasRebound = reboundFlags[i];\n\n const geomChanged = this.positionRow(i, dataIndex * ROW_HEIGHT, rowWidth);\n\n if (wasRebound || geomChanged) {\n row.layoutChildren(ROW_HEIGHT, INDENT_PX);\n }\n }\n }\n\n /**\n * Constructs the `VirtualScroller` and wires click and keyboard listeners.\n *\n * @param element - Optional element passed by the rendering pipeline; 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.addSubtreeListener(this, \"click\", this._handleClick);\n Event.addSubtreeListener(this, \"contextmenu\", this._handleContextMenu);\n Event.addSubtreeListener(this, \"dblclick\", this._handleDblClick);\n Event.addListener(this, \"keydown\", this._onKeyDown);\n\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Calls the layout manager and then refreshes the virtual-scroll viewport.\n *\n * @returns This component, for method chaining.\n *\n * @remarks\n * Overrides [`Component.doLayout`](/api/core/classes/Component#doLayout) so that layout-manager-driven size changes\n * (e.g. from a parent Tab or Split) automatically update the rendered row window.\n */\n doLayout(): this {\n if (this.isLayoutPaused()) {\n return this;\n }\n\n super.doLayout();\n\n // A pass the startup font gate deferred already renders the window, so\n // running the unconditional one as well would render twice on the frame\n // the gate opens.\n if (!this.renderWindowIfDeferred()) {\n this.renderWindow();\n }\n\n return this;\n }\n}\n\nconst TreeCallable = callable(Tree);\ntype TreeCallable = Tree;\nexport {\n Tree as _Tree,\n TreeCallable as Tree\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{a as e,o as t}from"./Position-PBVBW1JS.js";import{f as n,h as r,m as i,p as a}from"./Component-w65rPK75.js";var BoxLayout=class extends n{_spacing=5;_stretching=!1;_mode=`preferred`;_overflowSizing=`preferred`;_justify=`start`;constructor(e){super(),e&&this.applyOptions(e)}applyOptions(e){super.applyOptions(e),e.mode!==void 0&&this.setMode(e.mode),e.spacing!==void 0&&this.setComponentSpacing(e.spacing),e.stretching!==void 0&&this.setStretching(e.stretching),e.overflowSizing!==void 0&&this.setOverflowSizing(e.overflowSizing),e.justify!==void 0&&this.setJustify(e.justify)}getComponentSpacing(){return this._spacing||0}setComponentSpacing(e){return this._spacing=e||0,this}isStretching(){return this._stretching||!1}setStretching(e){return this._stretching=!!e,this}getMode(){return this._mode}setMode(e){return this._mode=e,this}getOverflowSizing(){return this._overflowSizing}setOverflowSizing(e){return this._overflowSizing=e,this}getJustify(){return this._justify}setJustify(e){return this._justify=e,this}aggregateMaxSize(n){let r=this.getContainer();if(!r)return null;let i=r.getPerimeterSize(),a=r.getLaidOutComponents(),o=n?i.left+i.right:i.top+i.bottom,s=n?i.top+i.bottom:i.left+i.right,c=o,l=0,u=!1,d=!1,f=0;for(let e of a){let r=e.getMaxSize();if(!r){u=!0,d=!0;continue}let i=n?r.width:r.height,a=n?r.height:r.width;t(i)?u=!0:this._mode===`equal`?f=Math.max(f,i):c+=i,t(a)?d=!0:l=Math.max(l,a)}this._mode===`equal`?c+=a.length*f+this._spacing*Math.max(0,a.length-1):c+=this._spacing*Math.max(0,a.length-1),l+=s;let p=u?e:c,m=d?e:l;return{width:n?p:m,height:n?m:p}}computeShrink(e,t,n,r){if(e<=n||r)return{remaining:Math.max(0,n-e),shrinkRatio:0};let i=e-n,a=e-t;return{remaining:0,shrinkRatio:a>0?Math.min(1,i/a):1}}justifyOffsets(e,t,n){let r=Math.max(0,t-e);return r===0||this._justify===`start`?{lead:0,gap:0}:this._justify===`center`?{lead:r/2,gap:0}:this._justify===`end`?{lead:r,gap:0}:this._justify===`between`?{lead:0,gap:n>1?r/(n-1):0}:{lead:n>0?r/(2*n):0,gap:n>0?r/n:0}}crossPlacement(e,t,n,r,a){let o=this.getLayoutConstraints(e),s=o?.fill??null,c=o?.anchor??null;if(a?s===i.VERTICAL||s===i.BOTH:s===i.HORIZONTAL||s===i.BOTH)return{offset:t,extent:n};let l=this.crossAnchorEdge(c,a);if(l===null)return null;let u=Math.min(r,n);return{offset:l===`lead`?t:t+(n-u),extent:u}}crossAnchorEdge(e,t){return e===null?null:t?e===a.NORTH||e===a.NORTHWEST||e===a.NORTHEAST?`lead`:e===a.SOUTH||e===a.SOUTHWEST||e===a.SOUTHEAST?`trail`:null:e===a.WEST||e===a.NORTHWEST||e===a.SOUTHWEST?`lead`:e===a.EAST||e===a.NORTHEAST||e===a.SOUTHEAST?`trail`:null}},HBox=class extends BoxLayout{_defaultComponentWidth=100;getContentBaseline(){if(this.isStretching())return null;let e=this.getContainer();if(!e)return null;let t=[],n=[];for(let r of e.getLaidOutComponents()){let e=r.getPreferredSize();t.push(e?e.height:0),n.push(r.getBaseline())}return this.computeRowMetrics(t,n).rowAscent}getPreferredSize(){let e=this.getContainer();if(!e)return null;let t=e.getPerimeterSize(),n=e.getLaidOutComponents(),r=t.left+t.right,i=[],a=[];if(this._mode===`equal`){let e=0;for(let t in n){let r=n[t],o=r.getPreferredSize();o&&(e=Math.max(e,o.width),i.push(o.height),a.push(r.getBaseline()))}r+=n.length*(e+this._spacing)-this._spacing}else{for(let e in n){let t=n[e],o=t.getPreferredSize();o&&(r+=o.width,i.push(o.height),a.push(t.getBaseline()))}r+=this._spacing*(n.length-1)}let o=this.computeRowHeight(i,a);return o+=t.top+t.bottom,{width:r,height:o}}getMinSize(){let e=this.getContainer();if(!e)return null;let t=e.getPerimeterSize(),n=e.getLaidOutComponents(),r=t.left+t.right,i=[],a=[];if(this._mode===`equal`){let e=0;for(let t in n){let r=n[t],o=r.getMinSize();o&&(e=Math.max(e,o.width),i.push(o.height),a.push(r.getBaseline()))}r+=n.length*(e+this._spacing)-this._spacing}else{for(let e in n){let t=n[e],o=t.getMinSize();o&&(r+=o.width,i.push(o.height),a.push(t.getBaseline()))}r+=this._spacing*(n.length-1)}let o=this.computeRowHeight(i,a);return o+=t.top+t.bottom,{width:r,height:o}}getMaxSize(){return this.aggregateMaxSize(!0)}computeTotalMinSize(){let e=this.getContainer();if(!e)return{width:0,height:0};let t=e.getLaidOutComponents();if(t.length===0)return{width:0,height:0};let n=0;if(this._mode===`equal`){let e=0;for(let r of t){let t=r.getMinSize();t&&(e=Math.max(e,t.width),n=Math.max(n,t.height))}return{width:t.length*(e+this._spacing)-this._spacing,height:n}}let r=this._spacing*(t.length-1);for(let e of t){let t=e.getMinSize();t&&(r+=t.width,n=Math.max(n,t.height))}return{width:r,height:n}}doLayout(){let e=this.getContainer();if(!e)return;let t=e.getInnerSize();if(!t)return;let n=e.getLaidOutComponents(),r=e.getContentInsets(),i=this.getComponentSpacing(),a=this.inflateForOverflow(t),o;o=this._mode===`equal`?this.layoutEqualMode(n,t,a,r,i):this.layoutPreferredMode(n,a,r,i),this.commitPlacements(o),this.reserveContentFrame()}layoutEqualMode(e,t,n,r,a){let o=this.computeEqualCellWidth(e,t.width,a);if(this.isStretching()){let t=r.getTop(),s=r.getLeft(),c=[];for(let r of e)c.push({component:r,...this.resolveBounds(r,s,t,o,n.height,i.BOTH)}),s+=o+a;return c}let s=[],c=[];for(let t of e){let e=t.getPreferredSize();s.push(e?e.height:0),c.push(t.getBaseline())}let{rowAscent:l,rowDescent:u}=this.computeRowMetrics(s,c),d=r.getTop(),f=n.height,p=r.getLeft(),m=[];for(let t=0;t<e.length;t+=1){let n=e[t],h=this.crossPlacement(n,d,f,s[t],!0);if(h)m.push({component:n,...this.resolveBounds(n,p,h.offset,o,h.extent,i.BOTH)});else{let e=this.rowChildY(r.getTop(),s[t],c[t],l,u);m.push({component:n,...this.resolveBounds(n,p,e,o,s[t],i.BOTH)})}p+=o+a}return m}computeEqualCellWidth(e,t,n){let r=0,i=0;for(let t of e){let e=t.getMinSize();e&&(r=Math.max(r,e.width));let n=t.getPreferredSize();n&&(i=Math.max(i,n.width))}let a=(t-n*(e.length-1))/e.length;return a>=r?a:this.isOverflowingX()&&this._overflowSizing===`preferred`?Math.max(r,i):r}layoutPreferredMode(e,t,n,r){let{totalWeight:a,fixedPreferred:o,fixedMin:s}=this.measureFixedWidths(e,r),{remaining:c,shrinkRatio:l}=this.computeShrink(o,s,t.width,this.isOverflowingX()),u=[],d=[],f=[];for(let n of e){let e=this.getLayoutConstraints(n)?.weight??0,r=n.getPreferredSize(),i=n.getMinSize(),o=n.getMaxSize();u.push(this.resolveChildWidth(r,i,o,e,a,c,l));let s;s=!r||this.isStretching()?t.height:Math.min(r.height,t.height),o&&(s=Math.min(s,o.height)),d.push(s),f.push(n.getBaseline())}let{rowAscent:p,rowDescent:m}=this.isStretching()?{rowAscent:null,rowDescent:0}:this.computeRowMetrics(d,f),h=0,g=0;if(a===0){let i=r*(e.length-1);for(let e of u)i+=e;let a=t.width-(n.getLeft()+n.getRight());({lead:h,gap:g}=this.justifyOffsets(i,a,e.length))}let _=n.getTop(),v=t.height-(n.getTop()+n.getBottom()),y=n.getLeft()+h,b=[];for(let t=0;t<e.length;t+=1){let a=e[t],o=a.getPreferredSize(),s=a.getMaxSize(),c=o?Math.min(o.height,v):v;s&&(c=Math.min(c,s.height));let l=this.crossPlacement(a,_,v,c,!0);if(l)b.push({component:a,...this.resolveBounds(a,y,l.offset,u[t],l.extent,i.BOTH)});else{let e=this.rowChildY(n.getTop(),d[t],f[t],p,m);b.push({component:a,...this.resolveBounds(a,y,e,u[t],d[t],i.BOTH)})}y+=u[t],y+=r+g}return b}measureFixedWidths(e,t){let n=0,r=t*(e.length-1),i=t*(e.length-1);for(let t of e){let e=this.getLayoutConstraints(t)?.weight??0;if(e>0)n+=e;else{let e=t.getPreferredSize(),n=t.getMinSize();r+=this.preferredChildWidth(e,n),i+=n?n.width:0}}return{totalWeight:n,fixedPreferred:r,fixedMin:i}}preferredChildWidth(e,t){let n=(e?e.width:void 0)??(t&&t.width>0?t.width:void 0)??this._defaultComponentWidth;return t?Math.max(n,t.width):n}resolveChildWidth(e,t,n,r,i,a,o){let s;if(r>0&&i>0)s=r/i*a;else{let n=this.preferredChildWidth(e,t);s=n-o*(n-(t?t.width:0))}return n&&(s=Math.min(s,n.width)),t&&(s=Math.max(s,t.width)),s}rowChildY(e,t,n,r,i){return r===null?e:n===null?e+this.nullChildY(t,r,i):e+(r-n)}},o=r(HBox),VBox=class extends BoxLayout{_defaultComponentHeight=100;getContentBaseline(){let e=this.getContainer();if(!e)return null;let t=e.getLaidOutComponents();return t.length===0?null:t[0].getBaseline()}getPreferredSize(){let n=this.getContainer();if(!n)return null;let r=n.getPerimeterSize(),i=n.getLaidOutComponents();if(this._mode===`equal`){let e=0,t=0;for(let n in i){let r=i[n].getPreferredSize();r&&(e=Math.max(e,r.width),t=Math.max(t,r.height))}return{width:e+r.left+r.right,height:i.length*(t+this._spacing)-this._spacing+r.top+r.bottom}}let a=e,o=r.top+r.bottom;for(let e in i){let n=i[e].getPreferredSize();n&&(a=t(a)?Math.min(a,n.width):Math.max(a,n.width),o+=n.height)}return a+=r.left+r.right,o+=this._spacing*(i.length-1),{width:a,height:o}}getMinSize(){let e=this.getContainer();if(!e)return null;let t=e.getPerimeterSize(),n=e.getLaidOutComponents();if(this._mode===`equal`){let e=0,r=0;for(let t in n){let i=n[t].getMinSize();i&&(e=Math.max(e,i.width),r=Math.max(r,i.height))}return{width:e+t.left+t.right,height:n.length*(r+this._spacing)-this._spacing+t.top+t.bottom}}let r=0,i=t.top+t.bottom;for(let e in n){let t=n[e].getMinSize();t&&(r=Math.max(r,t.width),i+=t.height)}return r+=t.left+t.right,i+=this._spacing*(n.length-1),{width:r,height:i}}getMaxSize(){return this.aggregateMaxSize(!1)}computeTotalMinSize(){let e=this.getContainer();if(!e)return{width:0,height:0};let t=e.getLaidOutComponents();if(t.length===0)return{width:0,height:0};let n=0;if(this._mode===`equal`){let e=0;for(let r of t){let t=r.getMinSize();t&&(n=Math.max(n,t.width),e=Math.max(e,t.height))}return{width:n,height:t.length*(e+this._spacing)-this._spacing}}let r=this._spacing*(t.length-1);for(let e of t){let t=e.getMinSize();t&&(n=Math.max(n,t.width),r+=t.height)}return{width:n,height:r}}doLayout(){let e=this.getContainer();if(!e)return;let t=e.getInnerSize();if(!t)return;let n=e.getLaidOutComponents(),r=e.getContentInsets(),i=this.getComponentSpacing(),a=this.inflateForOverflow(t),o;o=this._mode===`equal`?this.layoutEqualMode(n,t,a,r,i):this.layoutPreferredMode(n,a,r,i),this.commitPlacements(o),this.reserveContentFrame()}layoutEqualMode(e,t,n,r,a){let o=this.computeEqualCellHeight(e,t.height,a),s=r.getLeft(),c=r.getTop();if(this.isStretching()){let t=n.width,r=[];for(let n of e)r.push({component:n,...this.resolveBounds(n,s,c,t,o,i.BOTH)}),c+=o+a;return r}let l=r.getLeft(),u=n.width,d=[];for(let t of e){let e=t.getPreferredSize(),n=e?e.width:0,r=this.crossPlacement(t,l,u,n,!1);r?d.push({component:t,...this.resolveBounds(t,r.offset,c,r.extent,o,i.BOTH)}):d.push({component:t,...this.resolveBounds(t,s,c,n,o,i.BOTH)}),c+=o+a}return d}computeEqualCellHeight(e,t,n){let r=0,i=0;for(let t of e){let e=t.getMinSize();e&&(r=Math.max(r,e.height));let n=t.getPreferredSize();n&&(i=Math.max(i,n.height))}let a=(t-n*(e.length-1))/e.length;return a>=r?a:this.isOverflowingY()&&this._overflowSizing===`preferred`?Math.max(r,i):r}layoutPreferredMode(e,t,n,r){let{totalWeight:a,fixedPreferred:o,fixedMin:s}=this.measureFixedHeights(e,r),{remaining:c,shrinkRatio:l}=this.computeShrink(o,s,t.height,this.isOverflowingY()),u=[];for(let t of e){let e=this.getLayoutConstraints(t)?.weight??0,n=t.getPreferredSize(),r=t.getMinSize(),i=t.getMaxSize();u.push(this.resolveChildHeight(n,r,i,e,a,c,l))}let d=0,f=0;if(a===0){let i=r*(e.length-1);for(let e of u)i+=e;let a=t.height-(n.getTop()+n.getBottom());({lead:d,gap:f}=this.justifyOffsets(i,a,e.length))}let p=n.getLeft(),m=t.width-(n.getLeft()+n.getRight()),h=n.getLeft(),g=n.getTop()+d,_=[];for(let n=0;n<e.length;n+=1){let a=e[n],o=a.getPreferredSize(),s=a.getMaxSize(),c;c=!o||this.isStretching()?t.width:Math.min(o.width,t.width),s&&(c=Math.min(c,s.width));let l=o?Math.min(o.width,m):m;s&&(l=Math.min(l,s.width));let d=this.crossPlacement(a,p,m,l,!1);d?_.push({component:a,...this.resolveBounds(a,d.offset,g,d.extent,u[n],i.BOTH)}):_.push({component:a,...this.resolveBounds(a,h,g,c,u[n],i.BOTH)}),g+=u[n],g+=r+f}return _}measureFixedHeights(e,t){let n=0,r=t*(e.length-1),i=t*(e.length-1);for(let t of e){let e=this.getLayoutConstraints(t)?.weight??0;if(e>0)n+=e;else{let e=t.getPreferredSize(),n=t.getMinSize();r+=this.preferredChildHeight(e,n),i+=n?n.height:0}}return{totalWeight:n,fixedPreferred:r,fixedMin:i}}preferredChildHeight(e,t){let n=(e?e.height:void 0)??(t&&t.height>0?t.height:void 0)??this._defaultComponentHeight;return t?Math.max(n,t.height):n}resolveChildHeight(e,t,n,r,i,a,o){let s;if(r>0&&i>0)s=r/i*a;else{let n=this.preferredChildHeight(e,t);s=n-o*(n-(t?t.height:0))}return n&&(s=Math.min(s,n.height)),t&&(s=Math.max(s,t.height)),s}},s=r(VBox);export{BoxLayout as i,s as n,o as r,VBox as t};
2
- //# sourceMappingURL=VBox-32fxpkDl.js.map