@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":"Menu-C0roOCY7.js","names":[],"sources":["../../src/typescript/lib/component/container/MenuItem.ts","../../src/typescript/lib/component/container/MenuSeparator.ts","../../src/typescript/lib/overlay/Menu.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { BorderOptions } from \"~/primitive/Border.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time component-level options for {@link MenuItem}. Use this\n * for cosmetic/layout overrides on the item row; the menu data (text, action,\n * shortcut, submenu) still comes through {@link MenuItemConfig}.\n *\n * @category Components\n */\nexport interface MenuItemOptions extends ComponentOptions {\n text?: string;\n enabled?: boolean;\n focused?: boolean;\n}\n\n/**\n * Selects which CSS-variable family a `MenuItem` reads its colours from.\n *\n * `'menu-bar'` resolves to `--ts-ui-menu-bar-item-*` tokens; `'context-menu'`\n * resolves to `--ts-ui-context-menu-item-*` tokens. The [`Menu`](/api/overlay/classes/Menu) class chooses\n * the prefix based on its mode.\n */\nexport type MenuItemCSSVarPrefix = \"menu-bar\" | \"context-menu\";\n\n/**\n * Describes a leaf action item, submenu trigger, or separator row inside a [`Menu`](/api/overlay/classes/Menu).\n *\n * @remarks When `separator` is true all other fields are ignored; the item renders as\n * a thin horizontal rule. When `submenu` is set the item opens a child [`Menu`](/api/overlay/classes/Menu)\n * instead of calling `action`.\n *\n * @category Components\n */\nexport interface MenuItemConfig {\n /** Display label shown in the menu row. */\n text?: string;\n /** Called when the item is activated (click or Enter). Ignored when `submenu` is set. */\n action?: () => void;\n /** Defaults to `true`. Disabled items are dimmed and non-interactive. */\n enabled?: boolean;\n /** Keyboard shortcut hint displayed on the right (e.g. `\"Ctrl+S\"`). */\n shortcut?: string;\n /**\n * Icon displayed on the left (e.g. a Unicode character).\n *\n * @remarks Prefer `glyph` for crisp SVG output from the framework's glyph\n * registry; `icon` remains supported for callers that want to embed a raw\n * character.\n */\n icon?: string;\n /**\n * Registry glyph name displayed on the left of the item row. Takes\n * precedence over `icon` when both are provided.\n */\n glyph?: string;\n /**\n * Optional colour for the leading `glyph`, overriding the item's default\n * foreground (e.g. a severity tint). Any CSS colour string. Ignored when the\n * row has no `glyph`.\n */\n glyphColor?: string;\n /** When present this item opens a submenu rather than calling `action`. */\n submenu?: MenuConfig;\n /** When `true` the item renders as a separator; all other fields are ignored. */\n separator?: true;\n}\n\n/**\n * Describes one top-level entry in a [`MenuBar`](/api/component/menubar/classes/MenuBar).\n *\n * @category Components\n */\nexport interface MenuConfig {\n /** Label shown in the bar button (e.g. `\"File\"`). */\n label: string;\n /**\n * The items in the dropdown panel: either a fixed array, or a provider called\n * to produce them when the panel is built. A submenu panel is (re)built each\n * time it opens, so a provider on a submenu's `items` is re-invoked on every\n * open — letting the submenu reflect current state (e.g. an export chooser\n * whose labels track the active tab) without the parent menu rebuilding.\n */\n items: MenuItemConfig[] | (() => MenuItemConfig[]);\n /**\n * Optional registry glyph name displayed to the left of the bar button's\n * label (e.g. `\"file\"`, `\"eye\"`, `\"info-circle\"`). Omit for a text-only\n * menu button.\n */\n glyph?: string;\n}\n\n/**\n * A single row inside a [`Menu`](/api/overlay/classes/Menu) panel.\n *\n * Renders a four-zone layout: icon | text | shortcut | chevron. When\n * `config.separator` is true the item renders instead as a thin horizontal\n * rule and ignores all other config fields.\n *\n * Mouse hover triggers the submenu open callback after a 150 ms delay.\n * Keyboard focus is applied programmatically via `setFocused`.\n *\n * @category Components\n */\nclass MenuItem extends Component {\n\n /** Fixed pixel height for every non-separator menu item. */\n static readonly HEIGHT: number = 24;\n\n private static readonly SEPARATOR_HEIGHT: number = 9;\n /** Title left inset when the menu reserves no icon column. */\n static readonly TEXT_INSET: number = 8;\n /** Title left offset when the menu reserves an icon column. */\n static readonly ICON_ZONE: number = 24;\n /** Width reserved for a submenu chevron in the right zone. */\n static readonly CHEVRON_ZONE: number = 16;\n /** Padding after the right zone, at the panel's inner edge. */\n static readonly RIGHT_PAD: number = 8;\n /**\n * Gap between the title column and the shortcut/chevron right zone, so a\n * content-sized menu never butts the label against them.\n */\n static readonly TEXT_GAP: number = 10;\n\n private readonly _config: MenuItemConfig;\n private readonly _onActivate: () => void;\n private readonly _onOpenSubmenu: (item: MenuItem) => void;\n private readonly _cssVarPrefix: MenuItemCSSVarPrefix;\n\n private _iconText: Text | null = null;\n private _iconGlyph: Glyph | null = null;\n private _titleText: Text | null = null;\n private _shortcutText: Text | null = null;\n private _chevronText: Text | null = null;\n\n // Column geometry applied by the parent Menu so every item lines up: the\n // title's left offset and the shared title-column width the right zone begins\n // after. Null until the menu sets them (a standalone item falls back to its\n // own natural metrics).\n private _iconStart: number | null = null;\n private _titleColumn: number | null = null;\n\n private _submenuTimer: ReturnType<typeof setTimeout> | null = null;\n\n private readonly _onMouseOver: () => void;\n private readonly _onMouseOut: () => void;\n private readonly _onClick: () => void;\n\n /**\n * Constructs a MenuItem from a config descriptor.\n *\n * @param config - The item descriptor including text, action, shortcut, icon, and submenu.\n * @param onActivate - Called when this item is activated (click or Enter on a leaf item).\n * @param onOpenSubmenu - Called when this item's submenu should open (or close others when no submenu).\n * @param cssVarPrefix - Selects which CSS-variable family supplies disabled, hover, and shortcut colours. Defaults to `'menu-bar'`.\n */\n constructor(\n config: MenuItemConfig,\n onActivate: () => void,\n onOpenSubmenu: (item: MenuItem) => void,\n cssVarPrefix: MenuItemCSSVarPrefix = \"menu-bar\",\n options?: MenuItemOptions\n ) {\n // Child components are built first; options are applied via applyOptions at the constructor tail.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n this._config = config;\n this._onActivate = onActivate;\n this._onOpenSubmenu = onOpenSubmenu;\n this._cssVarPrefix = cssVarPrefix;\n\n if (config.separator) {\n this.setHeight(MenuItem.SEPARATOR_HEIGHT);\n this.setPreferredSize({ width: 0, height: MenuItem.SEPARATOR_HEIGHT });\n this.setBackgroundColor(\"transparent\");\n this.setElementCSSRule(\n \"borderTop\",\n `1px solid var(--ts-ui-${cssVarPrefix}-separator-color, rgb(220, 220, 220))`\n );\n this.setElementCSSRule(\"margin\", \"4px 0\");\n this.getAria().setRole(\"separator\");\n\n this._onMouseOver = () => {};\n this._onMouseOut = () => {};\n this._onClick = () => {};\n\n if (options) {\n this.applyOptions(options);\n }\n\n return;\n }\n\n const enabled = config.enabled !== false;\n\n this.setHeight(MenuItem.HEIGHT);\n this.setPreferredSize({ width: 0, height: MenuItem.HEIGHT });\n this.setBackgroundColor(\"transparent\");\n this.setElementCSSRule(\"fontSize\", \"var(--ts-ui-button-font-size, var(--ts-ui-font-size))\");\n this.setCursor(enabled ? \"pointer\" : \"default\");\n this.getAria().setRole(\"menuitem\");\n\n if (!enabled) {\n this.setForegroundColor(\n `var(--ts-ui-${cssVarPrefix}-item-disabled-color, rgb(170, 170, 170))`\n );\n this.getAria().setDisabled(true);\n }\n\n if (config.glyph) {\n this._iconGlyph = new Glyph(config.glyph);\n this._iconGlyph.setPointerEvents(\"none\");\n this._iconGlyph.setPreferredSize({ width: 16, height: 16 });\n\n if (config.glyphColor) {\n this._iconGlyph.setForegroundColor(config.glyphColor);\n }\n\n this.addComponent(this._iconGlyph);\n } else {\n this._iconText = new Text(config.icon ?? \"\");\n this._iconText.setPointerEvents(\"none\");\n this._iconText.setVisible(!!config.icon);\n this.addComponent(this._iconText);\n }\n\n this._titleText = new Text(config.text ?? \"\");\n this._titleText.setPointerEvents(\"none\");\n this._titleText.setWhiteSpace(\"nowrap\");\n this._titleText.setOverflow(\"hidden\");\n this._titleText.setTextOverflow(\"ellipsis\");\n this.addComponent(this._titleText);\n\n if (config.shortcut) {\n this._shortcutText = new Text(config.shortcut);\n this._shortcutText.setPointerEvents(\"none\");\n this._shortcutText.setTextAlign(\"left\");\n this._shortcutText.setForegroundColor(\n `var(--ts-ui-${cssVarPrefix}-item-shortcut-color, rgb(140, 140, 140))`\n );\n this.addComponent(this._shortcutText);\n }\n\n if (this.hasSubmenu()) {\n this._chevronText = new Text(\"▶\");\n this._chevronText.setPointerEvents(\"none\");\n this._chevronText.setTextAlign(\"center\");\n this._chevronText.setForegroundColor(\n `var(--ts-ui-${cssVarPrefix}-item-shortcut-color, rgb(140, 140, 140))`\n );\n this.addComponent(this._chevronText);\n this.getAria().setHasPopup(\"menu\");\n this.getAria().setExpanded(false);\n }\n\n this._onMouseOver = () => {\n this.setFocused(true);\n\n // A disabled item never opens its own submenu; it still signals the\n // parent (the else branch) so hovering it closes any sibling submenu.\n if (enabled && this.hasSubmenu()) {\n this._submenuTimer = setTimeout(() => {\n this._submenuTimer = null;\n this._onOpenSubmenu(this);\n }, 150);\n } else {\n this._onOpenSubmenu(this);\n }\n };\n\n this._onMouseOut = () => {\n this.setFocused(false);\n\n if (this._submenuTimer !== null) {\n clearTimeout(this._submenuTimer);\n this._submenuTimer = null;\n }\n };\n\n this._onClick = () => {\n if (enabled && !this.hasSubmenu()) {\n this._onActivate();\n }\n };\n\n Event.addListener(this, \"mouseover\", this._onMouseOver);\n Event.addListener(this, \"mouseout\", this._onMouseOut);\n Event.addListener(this, \"click\", this._onClick);\n\n if (options) {\n this.applyOptions(options);\n }\n\n this.updateLabelHeights();\n }\n\n /**\n * Applies a {@link MenuItemOptions} bag, dispatching menu item label,\n * enabled state, and focus highlight after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: MenuItemOptions): this {\n super.applyOptions(options);\n\n if (options.text !== undefined && this._titleText) {\n this._titleText.setText(options.text);\n }\n\n if (options.enabled !== undefined) {\n this.getAria().setDisabled(!options.enabled);\n this.setCursor(options.enabled ? \"pointer\" : \"default\");\n }\n\n if (options.focused !== undefined) {\n this.setFocused(options.focused);\n }\n\n return this;\n }\n\n /**\n * Returns `true` if this item has a child submenu.\n *\n * @returns Whether a `submenu` config was provided.\n */\n hasSubmenu(): boolean {\n return !!this._config.submenu;\n }\n\n /** True when the item reserves an icon column (has a glyph or icon slot). */\n hasIcon(): boolean {\n return !!this._config.icon || !!this._config.glyph;\n }\n\n /** The item's measured title width, feeding the menu's shared title column. */\n titleTextWidth(): number {\n return Math.ceil(this._titleText?.getPreferredSize()?.width ?? 0);\n }\n\n /** The item's measured shortcut width, or 0 when it has no shortcut. */\n shortcutTextWidth(): number {\n if (!this._config.shortcut || this._shortcutText === null) {\n return 0;\n }\n\n return Math.ceil(this._shortcutText.getPreferredSize()?.width ?? 0);\n }\n\n /**\n * Applies the menu-computed column geometry so every item lines up: the title\n * left offset (icon column, or the bare inset) and the shared title-column\n * width the shortcut/chevron right zone begins after.\n *\n * @param iconStart - The title's left offset in pixels.\n * @param titleColumn - The shared title-column width across the menu's items.\n */\n setColumns(iconStart: number, titleColumn: number): void {\n this._iconStart = iconStart;\n this._titleColumn = titleColumn;\n\n this.scheduleLayout();\n }\n\n /**\n * Returns the submenu config, or `null` if this item has no submenu.\n *\n * @returns The [`MenuConfig`](/api/component/container/interfaces/MenuConfig) for the child panel, or null.\n */\n getSubmenuConfig(): MenuConfig | null {\n return this._config.submenu ?? null;\n }\n\n /**\n * Applies or removes the keyboard-focus highlight background.\n *\n * @param focused - `true` to highlight, `false` to clear.\n */\n setFocused(focused: boolean): this {\n this.setBackgroundColor(\n focused\n ? `var(--ts-ui-${this._cssVarPrefix}-item-hover-bg, rgba(30, 100, 200, 0.12))`\n : \"transparent\"\n );\n\n return this;\n }\n\n /**\n * Returns `true` when this item was constructed with `separator: true`.\n *\n * @returns Whether this item is a non-interactive separator row.\n */\n isSeparator(): boolean {\n return !!this._config.separator;\n }\n\n /**\n * Returns `true` when the item is interactive (not disabled).\n *\n * @returns Whether `enabled` was not explicitly set to `false`.\n */\n isEnabled(): boolean {\n return this._config.enabled !== false;\n }\n\n /**\n * Activates this item as if the user clicked or pressed Enter.\n *\n * For submenu items, calls `onOpenSubmenu` immediately (skipping the hover delay).\n * For leaf items, calls `onActivate`. Does nothing for disabled or separator items.\n */\n activate(): void {\n if (this.isSeparator() || !this.isEnabled()) {\n return;\n }\n\n if (this.hasSubmenu()) {\n this._onOpenSubmenu(this);\n } else {\n this._onActivate();\n }\n }\n\n /**\n * Cancels any pending submenu timer, then defers to the base class for\n * the rest of teardown.\n */\n protected destructor(): void {\n if (this._submenuTimer !== null) {\n clearTimeout(this._submenuTimer);\n this._submenuTimer = null;\n }\n\n super.destructor();\n }\n\n /**\n * Returns the offset from the top of the menu item to the title text's baseline.\n *\n * @returns The baseline offset in pixels, or `null` when the title has no baseline\n * (e.g. for a separator row).\n */\n getBaseline(): number | null {\n if (this._titleText === null) {\n return null;\n }\n\n return this.wrapInnerBaseline(this._titleText.getBaseline());\n }\n\n /**\n * Re-pins the four labels' line boxes to the item's CONTENT height —\n * `MenuItem.HEIGHT` less this item's own vertical chrome. `centerInHeight`\n * pins a label's minimum height as well as its line box, so a label pinned to\n * the outer height cannot shrink into a bordered item's content box and is\n * clipped. Called from the constructor tail and whenever the border changes.\n */\n private updateLabelHeights(): void {\n const perimeter = this.getPerimeterSize();\n // Floor of 1: chrome at or past the row height leaves nothing to centre\n // in, and a zero or negative line-height is not a value CSS should see.\n // The label clips in that case either way — the floor only keeps the\n // written value sane.\n const height = Math.max(1, MenuItem.HEIGHT - perimeter.top - perimeter.bottom);\n\n // Optional chaining, not null checks: a separator has none of these, and a\n // border arriving through the options bag could in principle reach here\n // before the field initializers have run.\n this._iconText?.centerInHeight(height);\n this._titleText?.centerInHeight(height);\n this._shortcutText?.centerInHeight(height);\n this._chevronText?.centerInHeight(height);\n }\n\n /**\n * Applies a border, then re-pins the four labels' line boxes to the item's\n * new content height so a bordered item's labels shrink to fit instead of\n * overrunning the smaller content box.\n *\n * @param options - Border configuration, or a CSS `border` shorthand string.\n * @returns This component, for method chaining.\n */\n setBorder(options: BorderOptions | string): this {\n super.setBorder(options);\n this.updateLabelHeights();\n\n return this;\n }\n\n /**\n * Clears the border, then re-pins the four labels' line boxes back to the\n * item's now-larger content height.\n *\n * @returns This component, for method chaining.\n */\n clearBorder(): this {\n super.clearBorder();\n this.updateLabelHeights();\n\n return this;\n }\n\n /**\n * Positions the four label zones within the item's content box.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n if (this._config.separator) {\n return this;\n }\n\n const box = this.getContentBounds();\n\n if (!box) {\n return this;\n }\n\n // Heights come from the content box, not MenuItem.HEIGHT: the constant\n // is the item's outer height. With no border the two are the same\n // number. The texts' line boxes are pinned to match by\n // updateLabelHeights, so they fit this box rather than overrunning it.\n const H = box.height;\n const totalWidth = box.width;\n const hasShortcut = !!this._config.shortcut && this._shortcutText !== null;\n const hasSub = this.hasSubmenu();\n\n // The menu aligns every item into shared columns; a standalone item falls\n // back to its own icon offset and natural title width.\n const iconStart = this._iconStart ?? (this.hasIcon() ? MenuItem.ICON_ZONE : MenuItem.TEXT_INSET);\n const titleColumn = this._titleColumn ?? this.titleTextWidth();\n\n if (this._iconGlyph) {\n const size = this._iconGlyph.getPreferredSize() ?? { width: 16, height: 16 };\n const iconY = Math.max(0, Math.floor((H - size.height) / 2));\n\n this._iconGlyph.setX(box.x + 4);\n this._iconGlyph.setY(box.y + iconY);\n this._iconGlyph.setWidth(size.width);\n this._iconGlyph.setHeight(size.height);\n } else if (this._iconText) {\n this._iconText.setX(box.x + 4);\n this._iconText.setY(box.y);\n this._iconText.setWidth(20);\n this._iconText.setHeight(H);\n }\n\n // Title fills the shared title column, left-aligned from the icon offset.\n if (this._titleText) {\n this._titleText.setX(box.x + iconStart);\n this._titleText.setY(box.y);\n this._titleText.setWidth(titleColumn);\n this._titleText.setHeight(H);\n }\n\n // Chevron: right-justified at the panel's inner edge.\n if (hasSub && this._chevronText) {\n this._chevronText.setX(box.x + totalWidth - MenuItem.RIGHT_PAD - MenuItem.CHEVRON_ZONE);\n this._chevronText.setY(box.y);\n this._chevronText.setWidth(MenuItem.CHEVRON_ZONE);\n this._chevronText.setHeight(H);\n }\n\n // Shortcut: left-justified in the right zone, one gap past the title column.\n if (hasShortcut && this._shortcutText) {\n this._shortcutText.setX(box.x + iconStart + titleColumn + MenuItem.TEXT_GAP);\n this._shortcutText.setY(box.y);\n this._shortcutText.setWidth(this.shortcutTextWidth());\n this._shortcutText.setHeight(H);\n }\n\n return this;\n }\n}\n\nconst MenuItemCallable = callable(MenuItem);\ntype MenuItemCallable = MenuItem;\nexport {\n MenuItem as _MenuItem,\n MenuItemCallable as MenuItem\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport type { MenuItemCSSVarPrefix } from \"~/component/container/MenuItem.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuSeparator}.\n *\n * @category Components\n */\nexport interface MenuSeparatorOptions extends ComponentOptions {\n}\n\n/**\n * User-overridable default fill; a caller-supplied `backgroundColor` wins.\n */\nconst _defaultMenuSeparatorOptions: Partial<MenuSeparatorOptions> = {\n backgroundColor: \"transparent\",\n};\n\n/**\n * A horizontal separator rule used inside a [`Menu`](/api/overlay/classes/Menu) panel to visually group menu items.\n *\n * Renders as a thin border line. Width is set externally by `Menu.doLayout()`.\n *\n * @category Components\n */\nclass MenuSeparator extends Component<MenuSeparatorOptions> {\n\n /** Fixed pixel height of every separator. */\n static readonly HEIGHT: number = 9;\n\n /**\n * Constructs a MenuSeparator with a thin horizontal rule appearance.\n *\n * @param cssVarPrefix - Selects which CSS-variable family supplies the border colour. Defaults to `'menu-bar'`.\n * @param options - Optional construction options.\n * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(\n cssVarPrefix: MenuItemCSSVarPrefix = \"menu-bar\",\n options?: MenuSeparatorOptions,\n subclassDefaults?: Partial<MenuSeparatorOptions>,\n ) {\n super(options, { ..._defaultMenuSeparatorOptions, ...(subclassDefaults ?? {}) });\n\n this.setHeight(MenuSeparator.HEIGHT);\n this.setPreferredSize({ width: 0, height: MenuSeparator.HEIGHT });\n this.setElementCSSRule(\n \"borderTop\",\n `1px solid var(--ts-ui-${cssVarPrefix}-separator-color, rgb(220, 220, 220))`\n );\n this.setElementCSSRule(\"margin\", \"4px 0\");\n this.getAria().setRole(\"separator\");\n }\n}\n\nconst MenuSeparatorCallable = callable(MenuSeparator);\ntype MenuSeparatorCallable = MenuSeparator;\nexport {\n MenuSeparator as _MenuSeparator,\n MenuSeparatorCallable as MenuSeparator\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, Rect } from \"~/core/DOM.js\";\nimport { LayerManager, DismissableLayer, LayerDismissMode } from \"~/core/LayerManager.js\";\nimport { positionAligned, positionFlexibleAnchored } from \"~/core/OverlayPosition.js\";\nimport { fadeShow, fadeHideAndDetach } from \"~/core/AnimatedDropdown.js\";\nimport type { Animation } from \"~/core/Animation.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport type { Size } from \"~/primitive/Size.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { MenuItem, MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { MenuSeparator } from \"~/component/container/MenuSeparator.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Util } from \"~/core/Util.js\";\n\n/**\n * Pixel bounds a content-sized menu panel clamps to. Menus size to their widest\n * item (via the layout manager's preferred width); the floor keeps a short menu\n * from looking cramped, and the ceiling stops a very long label running off-screen\n * (the item's title ellipsizes past it).\n */\nconst MIN_MENU_WIDTH = 120;\nconst MAX_MENU_WIDTH = 360;\n\n/** Duration (ms) of the fade-in / fade-out transitions on every menu panel. */\nconst MENU_ANIM_DURATION_MS = 120;\n\n/** Pixels kept between a clamped menu and the viewport edge so the panel\n * border and shadow are never flush against the screen. Mirrors the small\n * inset used by other floating panels; purely cosmetic breathing room. */\nconst VIEWPORT_MARGIN = 4;\n\n/** A resolved rebuild-mode placement: the panel's top-left plus the height room at it. */\ninterface MenuPlacement { x: number; y: number; available: number; }\n\n/**\n * A zero-size rect at a cursor point. A cursor is a degenerate anchor: with\n * `left === right` and `top === bottom`, the adjacency and alignment flips\n * collapse to the same operation — grow down-right from the point, or end at it.\n * That is native context-menu behaviour, so a pointer needs no separate path.\n */\nfunction pointRect(x: number, y: number): Rect {\n return { x, y, left: x, top: y, right: x, bottom: y, width: 0, height: 0 };\n}\n\n/**\n * Resolves a rebuild-mode panel's placement against `anchorRect` at `size`.\n * Vertically the panel is size-flexible (it caps its height and scrolls), so it\n * grows below the anchor and flips to end at the anchor's top when the room below\n * is short. Horizontally it is fixed-size (a natural width, no horizontal scroll),\n * so its left edge aligns with the anchor's left and flips to align its right edge\n * with the anchor's right when the left alignment overflows.\n *\n * @param anchorRect - The trigger rect, or a zero-size rect at the cursor.\n * @param size - The panel's current width/height to place.\n * @param vp - The viewport size to flip/clamp within.\n * @returns The resolved top-left coordinate and the vertical room available there.\n */\nfunction resolvePlacement(anchorRect: Rect, size: Size, vp: Size): MenuPlacement {\n const v = positionFlexibleAnchored(anchorRect.top, anchorRect.bottom, size.height, vp.height, VIEWPORT_MARGIN);\n const x = positionAligned(anchorRect.left, anchorRect.right, size.width, vp.width, VIEWPORT_MARGIN);\n\n // `available` is the room on the side the panel actually landed on — never\n // re-derive it from `v.start`, which measures the wrong side for a flipped\n // panel and lets an over-tall menu grow back across the cursor.\n return { x, y: v.start, available: v.available };\n}\n\n/**\n * A floating menu panel that operates in one of two modes:\n *\n * - **Rebuild mode** (`new Menu()`): a right-click context menu. Items are passed per\n * `show(x, y, items)` call and disposed on the next show or hide.\n * - **Persistent mode** (`new Menu(items, onClose)`): a [`MenuBar`](/api/component/menubar/classes/MenuBar) dropdown. Items are\n * built once in the constructor and reused across `open()` / `close()` cycles.\n *\n * The two API surfaces are disjoint by mode:\n * `show()` / `hide()` / `setMenuWidth()` are valid only in rebuild mode;\n * `open()` / `close()` / focus and submenu methods are valid only in persistent mode.\n *\n * @example\n * ```typescript\n * // Rebuild mode — right-click context menu\n * const menu = new Menu();\n * Event.addListener(myComponent, 'contextmenu', (e: MouseEvent) => {\n * e.preventDefault();\n * menu.show(e.clientX, e.clientY, [\n * { text: 'Cut', action: () => cut() },\n * { separator: true },\n * { text: 'Paste', action: () => paste() },\n * ]);\n * });\n *\n * // Persistent mode — MenuBar dropdown\n * const panel = new Menu(\n * [{ text: 'Save', shortcut: 'Ctrl+S', action: () => save() }],\n * () => bar.closeMenu()\n * );\n * panel.open(buttonElement);\n * ```\n *\n * @category Components\n */\nclass Menu extends Component implements DismissableLayer {\n\n private readonly _persistent: boolean;\n // A persistent menu's items provider, kept so each open() re-resolves it —\n // null when the items were passed as a fixed array (built once, never rebuilt).\n private readonly _itemsProvider: (() => MenuItemConfig[]) | null;\n private readonly _onClose: (() => void) | null;\n // In both modes every entry is registered via `addComponent` (see\n // `showAnchored` / `buildPersistentItems`), so the `destructor()` override\n // below has no item disposal to do — the base class's recursive teardown\n // already disposes each item; the override exists only to cancel the\n // in-flight fades. (A prior override manually\n // re-disposed them here, guarded to persistent mode only via\n // `assertPersistentMode`. That guard was never reachable through ancestor\n // teardown — no `Menu` anywhere in the library is itself registered via\n // `addComponent` (`Table._columnContextMenu` / `TabBar._contextMenu` are\n // plain fields, never added as children), so an ancestor's `destructor()`\n // recursion can never reach a `Menu` at all. The loop was removed simply\n // because it was redundant, not because the guard made it unsafe.)\n private _menuItems: Array<MenuItem | MenuSeparator> = [];\n\n // In-flight fades, cancelled on teardown so their fallback timers cannot\n // fire against this menu's released element handle.\n private _fadeShowAnimation: Animation.CancelHandle | null = null;\n private _fadeHideAnimation: Animation.CancelHandle | null = null;\n private _focusedIndex: number = -1;\n private _openSubmenuPanel: Menu | null = null;\n private _openSubmenuItem: MenuItem | null = null;\n private _excludedEl: Handle | null = null;\n // An explicit rebuild-mode width set via setMenuWidth; null means size to\n // content (the widest item, clamped to [MIN_MENU_WIDTH, MAX_MENU_WIDTH]).\n private _menuWidth: number | null = null;\n private _rebuildOnClose: (() => void) | null = null;\n private _currentOpener: Handle | null = null;\n // Rebuild-mode: when true, each show() scrolls the menu to the bottom after\n // layout so the latest (bottom-most) items are visible on open.\n private _scrollToBottomOnShow: boolean = false;\n\n /**\n * Constructs a Menu. With no arguments, a rebuild-mode (right-click context)\n * menu whose items are supplied per `show()` call. With `items` (and `onClose`),\n * a persistent-mode ([`MenuBar`](/api/component/menubar/classes/MenuBar) dropdown)\n * menu whose items are built immediately and reused across `open()` / `close()`\n * cycles.\n *\n * A single optional-parameter signature, deliberately not an overload pair: the\n * `callable()` export's construct/call type is derived via TS `ConstructorParameters`,\n * which captures only the *last* overload — an overload pair would drop the\n * no-arg form and make `Menu()` a type error for consumers.\n *\n * @param items - The menu item configurations, or a provider called once now\n * to produce them. A submenu is rebuilt on each open, so a provider passed\n * for a submenu's items is re-invoked every time that submenu opens. Omit for\n * a rebuild-mode context menu.\n * @param onClose - Callback invoked when the panel should close (item activated or outside click).\n */\n constructor(items?: MenuItemConfig[] | (() => MenuItemConfig[]), onClose?: () => void) {\n super();\n\n this._persistent = items !== undefined;\n this._itemsProvider = typeof items === \"function\" ? items : null;\n this._onClose = onClose ?? null;\n\n const vbox = new VBox();\n\n vbox.setComponentSpacing(0);\n vbox.setStretching(true);\n\n this.setLayoutManager(vbox);\n\n if (this._persistent) {\n this.applyPersistentChrome();\n // Fixed array: build once now. A provider defers to open(), which\n // re-resolves it on every open (so the sole resolution is per-open).\n if (this._itemsProvider === null) {\n this.buildPersistentItems(items as MenuItemConfig[]);\n }\n } else {\n this.applyRebuildChrome();\n }\n }\n\n /**\n * Lays the menu's items into aligned columns and returns the panel width.\n * Every title shares one column sized to the widest title; the shortcut/chevron\n * right zone is the widest of the shortcut column and a submenu chevron, so\n * shortcuts left-justify in a column and chevrons right-justify at the edge.\n * The width is clamped to `[MIN_MENU_WIDTH, MAX_MENU_WIDTH]`; when the ceiling\n * bites, the title column shrinks and its titles ellipsize. Measured from the\n * items directly so a menu reused across shows re-measures its new content.\n *\n * @returns The clamped panel width in pixels.\n */\n private layOutColumns(): number {\n const items = this._menuItems.filter(\n (i): i is MenuItem => i instanceof MenuItem && !i.isSeparator()\n );\n\n const iconStart = items.some(i => i.hasIcon()) ? MenuItem.ICON_ZONE : MenuItem.TEXT_INSET;\n const maxTitle = items.reduce((m, i) => Math.max(m, i.titleTextWidth()), 0);\n const maxShortcut = items.reduce((m, i) => Math.max(m, i.shortcutTextWidth()), 0);\n const hasChevron = items.some(i => i.hasSubmenu());\n const rightZone = Math.max(maxShortcut, hasChevron ? MenuItem.CHEVRON_ZONE : 0);\n const rightReserve = rightZone > 0 ? MenuItem.TEXT_GAP + rightZone : 0;\n\n const natural = iconStart + maxTitle + rightReserve + MenuItem.RIGHT_PAD;\n const width = Math.min(MAX_MENU_WIDTH, Math.max(MIN_MENU_WIDTH, natural));\n\n // When the ceiling bites, the title column absorbs the shortfall (titles\n // ellipsize); otherwise it is the full widest-title width.\n const titleColumn = Math.min(maxTitle, width - iconStart - rightReserve - MenuItem.RIGHT_PAD);\n\n for (const item of items) {\n item.setColumns(iconStart, titleColumn);\n }\n\n return width;\n }\n\n /**\n * Shows the menu at the given viewport coordinates, replacing any previously\n * displayed items with the new list. **Rebuild-mode only.**\n *\n * The menu grows down-right from the cursor; when there is no room it flips\n * so its bottom / right edge ends at the cursor, never covering it. A menu\n * taller than the room on the side it lands on is capped there and scrolls.\n *\n * @param x - Horizontal viewport coordinate (e.g. `MouseEvent.clientX`).\n * @param y - Vertical viewport coordinate (e.g. `MouseEvent.clientY`).\n * @param configs - Ordered list of item descriptors to render.\n * @param onClose - Optional callback invoked once when the menu next closes\n * (item activated or dismissed by an outside click), letting the opener\n * revert an open-state affordance such as a rotated dropdown chevron.\n * @param excludeEl - Optional element whose subtree is exempt from the\n * outside-click-to-close check. Pass the trigger that opened the menu (e.g.\n * a [`SplitButton`](/api/component/button/classes/SplitButton) chevron) so a\n * pointerdown on it does not self-close the menu before the trigger's own\n * click can toggle it shut — mirroring [`MenuBar`](/api/component/menubar/classes/MenuBar)'s\n * dropdown-button exclusion.\n */\n show(x: number, y: number, configs: MenuItemConfig[], onClose?: () => void, excludeEl?: Handle | null): this {\n this.assertRebuildMode(\"show\");\n\n // The placement primitives only guarantee an on-screen result for an anchor\n // inside the viewport. A cursor from a real event always is; show() is public,\n // so pin it here rather than in showAnchored, which also takes real trigger\n // rects that may legitimately extend past a viewport edge.\n const vp = DOM.source.getViewportSize();\n\n return this.showAnchored(\n pointRect(Util.clamp(x, 0, vp.width), Util.clamp(y, 0, vp.height)),\n configs, onClose ?? null, excludeEl ?? null,\n );\n }\n\n /**\n * Rebuild-mode geometry-and-content core shared by {@link show} (pointer-anchored)\n * and {@link toggleFor} (rect-anchored). Both grow down-right from `anchorRect`\n * and flip per axis when the room runs short. Tears down and rebuilds the item\n * list, then resolves `anchorRect` to a placement and applies the resulting\n * position and height clamp.\n *\n * @param anchorRect - The cursor point (as a zero-size rect) or trigger rect to\n * place the panel against.\n * @param configs - Ordered list of item descriptors to render.\n * @param onClose - Callback invoked once when the menu next closes, or `null`.\n * @param excludeEl - Element whose subtree is exempt from the outside-click\n * check, or `null`.\n */\n private showAnchored(anchorRect: Rect, configs: MenuItemConfig[], onClose: (() => void) | null, excludeEl: Handle | null): this {\n this._rebuildOnClose = onClose;\n this._excludedEl = excludeEl;\n\n this._menuItems = [];\n this.disposeAllComponents();\n\n this.pauseLayout();\n\n for (const config of configs) {\n const item: MenuItem | MenuSeparator = config.separator === true\n ? new MenuSeparator(\"context-menu\")\n : new MenuItem(\n config,\n () => {\n config.action?.();\n this.hide();\n },\n (hoveredItem) => { this.handleItemOpenSubmenu(hoveredItem); },\n \"context-menu\"\n );\n\n this.addComponent(item);\n this._menuItems.push(item);\n }\n\n this.resumeLayout();\n\n const contentWidth = this.layOutColumns();\n const naturalWidth = this._menuWidth ?? contentWidth;\n\n this.setWidth(naturalWidth);\n\n // A reused menu still carries the previous show's max-height — set by\n // `applyViewportHeightClamp` to that show's available room. Because\n // `getPreferredSize` clamps to the component's own max, leaving it in\n // place would cap the new content at the old height, so a menu that\n // grew past its previous size would neither expand nor reserve a\n // scrollbar gutter on reopen. Clear it before measuring.\n this.setMaxSize({ width: Number.MAX_VALUE, height: Number.MAX_VALUE });\n\n const totalHeight = this.getPreferredSize()?.height ?? 0;\n\n const el = this.getElement(true)!;\n\n const vp = DOM.source.getViewportSize();\n\n // First pass at the natural width resolves the vertical room. Width does not\n // affect the vertical placement, so `available` stays correct after the\n // scrollbar-gutter widening below; only `x` needs the second pass.\n const available = resolvePlacement(anchorRect, { width: naturalWidth, height: totalHeight }, vp).available;\n\n // When the content is taller than the room on the side the menu lands on,\n // `applyViewportHeightClamp` caps the height and the `overflow-y: auto`\n // scrollbar engages. Reserve its width as a right inset — and widen the\n // content-sized panel to match — so items lay out beside the native\n // scrollbar instead of beneath it. `Component.getInnerSize` subtracts the\n // inset, so the VBox stretches items to `naturalWidth`, unchanged from the\n // no-scroll case.\n const gutter = totalHeight > available ? DOM.source.getScrollBarWidth() : 0;\n\n this.setInsets(new Insets(4, gutter, 4, 0));\n this.setWidth(naturalWidth + gutter);\n\n const placement = resolvePlacement(anchorRect, { width: this.getWidth(), height: totalHeight }, vp);\n\n this.setX(placement.x);\n this.setY(placement.y);\n this.applyViewportHeightClamp(placement.available, totalHeight);\n\n this.scheduleLayout();\n\n LayerManager.mount(el);\n\n this.setVisible(true);\n this.fadeIn();\n\n // Join the central layer tree: the manager owns the outside-pointerdown /\n // window-blur / Escape dismissal and stamps the z-index. Registering while\n // whatever opened the menu is topmost links it under that layer, so a\n // context menu raised over a modal dialog inherits the dialog band and\n // paints in front of it.\n LayerManager.register(this);\n this.setZIndex(LayerManager.getZIndex(this));\n\n // Optionally reveal the bottom of the list on open (e.g. a history menu\n // whose latest entries sit at the bottom). Flush the scheduled layout\n // first so the item heights are committed and `getMaxScrollTop` is\n // accurate; when nothing overflows the offset is 0 and this is a no-op.\n if (this._scrollToBottomOnShow) {\n this.flushLayout();\n this.setScrollTop(this.getMaxScrollTop());\n }\n\n return this;\n }\n\n /**\n * Opens or closes the menu for a given opener element. **Rebuild-mode only.**\n *\n * This is the toggle form of {@link show} for a left-click dropdown trigger\n * (e.g. a [`SplitButton`](/api/component/button/classes/SplitButton) chevron\n * or a [`ToolBar`](/api/component/menubar/classes/ToolBar) overflow button):\n * the opener is excluded from the outside-click dismissal (so its own\n * pointerdown does not self-close the menu) and remembered, so a second press\n * of the *same* opener closes the menu instead of reopening it. Pressing a\n * *different* opener while the menu is open switches it to that opener. The\n * menu opens below `anchorRect` and flips above it when the room below is\n * short, right-aligning to it when the left alignment overflows. Plain\n * {@link show} stays the right call for right-click context menus, which\n * should reposition — not close — on a repeat trigger; the remaining\n * distinction from `toggleFor` is the toggle identity, the opener exclusion,\n * and the empty-list suppression below.\n *\n * An empty `configs` opens nothing: the menu is not shown, `onClose` fires once\n * so the opener can revert an open-state affordance, and no opener state is\n * recorded. A repeat press of the *same* opener still closes an open menu — the\n * toggle-shut branch runs before the empty check.\n *\n * @param openerEl - The element that triggers the menu; excluded from the\n * outside-click check and used as the toggle identity.\n * @param anchorRect - The trigger's viewport rect (e.g.\n * `DOM.source.getViewportRect(button)`); the menu opens below it and flips\n * above it when the room below is short.\n * @param configs - Ordered list of item descriptors to render. An empty list\n * opens nothing (see above).\n * @param onClose - Optional callback invoked once when the menu next closes,\n * or when an empty `configs` suppresses the open.\n *\n * @returns This menu, for method chaining.\n */\n toggleFor(openerEl: Handle, anchorRect: Rect, configs: MenuItemConfig[], onClose?: () => void): this {\n this.assertRebuildMode(\"toggleFor\");\n\n // Same opener fired again while its menu is open: close it. Its pointerdown\n // was excluded from the dismissal above, so this click is the toggle-shut.\n // MUST stay ahead of the empty check below — a provider that has since gone\n // empty must still be able to close the panel it opened.\n if (this._currentOpener === openerEl) {\n this.hide();\n\n return this;\n }\n\n // Nothing to show: an empty panel is never useful, so open nothing. `onClose`\n // still fires so the opener reverts an optimistic open-state affordance (e.g.\n // SplitButton's spun-up chevron), and `_currentOpener` stays untouched, so the\n // next press of this opener is a fresh open rather than a stale toggle-shut.\n if (configs.length === 0) {\n onClose?.();\n\n return this;\n }\n\n // Closed, or open for a different opener: (re)show anchored for this one.\n this.showAnchored(anchorRect, configs, onClose ?? null, openerEl);\n this._currentOpener = openerEl;\n\n return this;\n }\n\n /**\n * Hides the menu and detaches it from the DOM. **Rebuild-mode only.**\n *\n * The instance remains alive and can be shown again by calling `show()`.\n */\n hide(): this {\n this.assertRebuildMode(\"hide\");\n\n this.closeOpenSubmenu();\n\n LayerManager.unregister(this);\n\n this.fadeOutAndDetach();\n\n // Forget the toggle opener so the next `toggleFor` for it opens rather\n // than seeing a stale match and closing an already-closed menu.\n this._currentOpener = null;\n\n // Fire the per-show close callback exactly once, clearing it first so a\n // later bare `hide()` (or re-show) can't re-invoke a stale opener's hook.\n const onClose = this._rebuildOnClose;\n this._rebuildOnClose = null;\n onClose?.();\n\n return this;\n }\n\n /**\n * Returns the current pixel width of the rebuild-mode menu panel (the\n * content-fit width, or an explicit override set via {@link setMenuWidth}).\n *\n * @returns The current panel width in pixels.\n */\n getMenuWidth(): number {\n return this.getWidth();\n }\n\n /**\n * Pins the rebuild-mode menu panel to an explicit width, overriding the\n * default content-fit sizing. **Rebuild-mode only.**\n *\n * @param width - Width in pixels.\n */\n setMenuWidth(width: number): this {\n this.assertRebuildMode(\"setMenuWidth\");\n\n this._menuWidth = width;\n\n return this;\n }\n\n /**\n * Controls whether the menu scrolls to the bottom of its item list each time\n * it is shown. **Rebuild-mode only.**\n *\n * Use this for a menu whose latest entries sit at the bottom (e.g. a\n * chronological notification history), so the most recent items are visible\n * on open rather than the user having to scroll down. A no-op when the list\n * fits without scrolling.\n *\n * @param value - `true` to scroll to the bottom on every show.\n *\n * @returns This menu, for method chaining.\n */\n setScrollToBottomOnShow(value: boolean): this {\n this.assertRebuildMode(\"setScrollToBottomOnShow\");\n\n this._scrollToBottomOnShow = value;\n\n return this;\n }\n\n /**\n * Opens this panel positioned below the anchor element (top-level) or to the\n * right of the parent panel (submenu). Appends to `document.documentElement`.\n * **Persistent-mode only.**\n *\n * @param anchorEl - The `HTMLElement` of the triggering button or menu item.\n * @param parentPanel - When set, positions the panel as a submenu of this parent.\n */\n open(anchorEl: Handle, parentPanel?: Menu): this {\n this.assertPersistentMode(\"open\");\n\n // A provider-sourced menu re-resolves its items on every open, so labels\n // and enabled state reflect current state each time it is shown (a submenu\n // is a fresh panel per open, but a reused top-level dropdown is not).\n if (this._itemsProvider) {\n this.rebuildPersistentItems(this._itemsProvider());\n }\n\n // Clear the previous open's max-height (set by `applyViewportHeightClamp`)\n // before measuring, so a provider-sourced dropdown that grew reflects its\n // new content height rather than being capped at the old available room.\n this.setMaxSize({ width: Number.MAX_VALUE, height: Number.MAX_VALUE });\n\n const totalHeight = this.getPreferredSize()?.height ?? (this._menuItems.length * MenuItem.HEIGHT + 8);\n const width = this.getWidth();\n\n const el = this.getElement(true)!;\n LayerManager.mount(el);\n\n const vp = DOM.source.getViewportSize();\n\n if (parentPanel) {\n const parentEl = parentPanel.getElement();\n const parentRect = parentEl\n ? DOM.source.getElementRect(parentEl)\n : { left: 0, right: 0, top: 0, bottom: 0 };\n const anchorRect = DOM.source.getElementRect(anchorEl);\n\n let x = parentRect.right;\n\n if (x + width > vp.width) {\n x = parentRect.left - width;\n }\n\n // A submenu grows down from the anchor item's top, and flips up\n // against that same top edge when there is more room above.\n const y = this.placeVertically(anchorRect.top, anchorRect.top, totalHeight, vp.height);\n\n this.setAutoCommitStyle(false);\n this.setX(Math.max(0, x));\n this.setY(Math.max(0, y));\n this.setAutoCommitStyle(true);\n } else {\n const anchorRect = DOM.source.getElementRect(anchorEl);\n\n let x = anchorRect.left;\n\n if (x + width > vp.width) {\n x = vp.width - width;\n }\n\n // A top-level dropdown grows down from the anchor's bottom, and\n // flips up against the anchor's top when there is more room above.\n const y = this.placeVertically(anchorRect.bottom, anchorRect.top, totalHeight, vp.height);\n\n this.setAutoCommitStyle(false);\n this.setX(Math.max(0, x));\n this.setY(Math.max(0, y));\n this.setAutoCommitStyle(true);\n }\n\n const anchorId = DOM.source.getId(anchorEl);\n\n if (anchorId) {\n this.getAria().setLabelledBy(anchorId);\n }\n\n this.setVisible(true);\n this.doLayout();\n this.fadeIn();\n\n // Join the central layer tree so the manager owns dismissal and the\n // z-stamp. A submenu opened via `open(item, this)` registers while its\n // parent menu is topmost, so it links under the parent — a pointerdown\n // inside the submenu counts as inside the parent (cross-portal\n // containment) and keeps the whole chain open.\n LayerManager.register(this);\n this.setZIndex(LayerManager.getZIndex(this));\n\n return this;\n }\n\n /**\n * Closes this panel and any open child submenus. Detaches from the DOM.\n * **Persistent-mode only.**\n */\n close(): this {\n this.assertPersistentMode(\"close\");\n\n this.closeOpenSubmenu();\n\n this.setFocusedIndex(-1);\n this.clearItemHighlights();\n\n LayerManager.unregister(this);\n\n this.fadeOutAndDetach();\n\n return this;\n }\n\n /**\n * Clears the hover highlight from every item. A click that activates an item\n * dismisses the menu, detaching the panel from the DOM under the pointer; the\n * browser fires no `mouseout` for an element removed under a stationary\n * pointer, so the hovered item's `setFocused(true)` is never undone by\n * `_onMouseOut`. `setFocusedIndex(-1)` only resets the keyboard-tracked item,\n * not one highlighted purely by hover. Persistent-mode menus reuse these item\n * elements across open/close, so without this sweep the stale highlight\n * reappears the next time the menu opens.\n */\n private clearItemHighlights(): void {\n for (const item of this._menuItems) {\n if (item instanceof MenuItem) {\n item.setFocused(false);\n }\n }\n }\n\n /**\n * Plays the standard menu-panel entrance fade. Cancels any in-flight\n * fade-out so the deferred detach skips removing the still-visible panel.\n */\n private fadeIn(): void {\n this._fadeShowAnimation?.cancel();\n this._fadeShowAnimation = fadeShow(this, { durationMs: MENU_ANIM_DURATION_MS });\n }\n\n /**\n * Plays the standard menu-panel exit fade, then hides and detaches the\n * panel from the DOM when the transition completes. A fresh `show()` /\n * `open()` during the fade cancels the deferred detach.\n */\n private fadeOutAndDetach(): void {\n this._fadeHideAnimation?.cancel();\n this._fadeHideAnimation = fadeHideAndDetach(this, { durationMs: MENU_ANIM_DURATION_MS });\n }\n\n /**\n * Cancels any in-flight fade, then defers to the base class. Cancelling\n * first keeps a fade's fallback timer from firing after `super.destructor()`\n * has released this menu's element handle.\n */\n protected destructor(): void {\n this._fadeShowAnimation?.cancel();\n this._fadeShowAnimation = null;\n this._fadeHideAnimation?.cancel();\n this._fadeHideAnimation = null;\n\n // A still-open submenu is a raw field, not a registered child (see the\n // class comment above _menuItems: no Menu anywhere in the library is\n // registered via addComponent), so the child recursion in\n // super.destructor() below can never reach it.\n this._openSubmenuPanel?.dispose();\n this._openSubmenuPanel = null;\n\n // Cancelling the fades above suppresses whatever fade-completion callback\n // would otherwise have unregistered this menu from the layer tree\n // (mirrors AnimatedDropdown.destructor()). Idempotent, so an\n // already-closed menu costs nothing.\n LayerManager.unregister(this);\n\n super.destructor();\n }\n\n /**\n * Moves keyboard focus to the item at the given index. Pass `-1` to clear focus.\n * **Persistent-mode only.**\n *\n * @param index - Zero-based item index, or `-1` to clear.\n */\n focusItem(index: number): this {\n this.assertPersistentMode(\"focusItem\");\n\n this.setFocusedIndex(index);\n\n return this;\n }\n\n /**\n * Moves focus to the next focusable item, wrapping around and skipping separators.\n * **Persistent-mode only.**\n */\n focusNext(): this {\n this.assertPersistentMode(\"focusNext\");\n\n let next = this._focusedIndex + 1;\n\n while (next < this._menuItems.length && this.isItemSeparator(next)) {\n next++;\n }\n\n if (next >= this._menuItems.length) {\n next = 0;\n\n while (next < this._menuItems.length && this.isItemSeparator(next)) {\n next++;\n }\n }\n\n this.setFocusedIndex(next);\n\n return this;\n }\n\n /**\n * Moves focus to the previous focusable item, wrapping around and skipping separators.\n * **Persistent-mode only.**\n */\n focusPrev(): this {\n this.assertPersistentMode(\"focusPrev\");\n\n let prev = this._focusedIndex - 1;\n\n while (prev >= 0 && this.isItemSeparator(prev)) {\n prev--;\n }\n\n if (prev < 0) {\n prev = this._menuItems.length - 1;\n\n while (prev >= 0 && this.isItemSeparator(prev)) {\n prev--;\n }\n }\n\n this.setFocusedIndex(prev);\n\n return this;\n }\n\n /**\n * Activates the currently focused item. No-op when no item is focused or the item\n * is disabled or a separator. **Persistent-mode only.**\n */\n activateFocused(): void {\n this.assertPersistentMode(\"activateFocused\");\n\n if (this._focusedIndex < 0 || this._focusedIndex >= this._menuItems.length) {\n return;\n }\n\n const item = this._menuItems[this._focusedIndex];\n\n if (item instanceof MenuItem && !item.isSeparator() && item.isEnabled()) {\n item.activate();\n }\n }\n\n /**\n * Returns the index of the currently focused item, or `-1` if no item is focused.\n * **Persistent-mode only.**\n *\n * @returns The focused item index, or -1.\n */\n getFocusedIndex(): number {\n this.assertPersistentMode(\"getFocusedIndex\");\n\n return this._focusedIndex;\n }\n\n /**\n * Sets an element whose subtree is excluded from the outside-click-to-close check.\n * **Persistent-mode only.**\n *\n * Used by [`MenuBar`](/api/component/menubar/classes/MenuBar) to prevent a pointerdown on its own buttons from closing the panel\n * before the button's click handler has a chance to toggle the menu.\n *\n * @param el - The element to exclude, or `null` to clear.\n */\n setExcludedElement(el: Handle | null): this {\n this.assertPersistentMode(\"setExcludedElement\");\n\n this._excludedEl = el;\n\n return this;\n }\n\n /**\n * Returns the element whose subtree is currently excluded from the\n * outside-click-to-close check, or `null` if none is set.\n *\n * @returns The excluded element, or null.\n */\n getExcludedElement(): Handle | null {\n return this._excludedEl;\n }\n\n /**\n * Applies the persistent-mode chrome (MenuBar dropdown CSS variables, aria role).\n */\n private applyPersistentChrome(): void {\n this.setBackgroundColor(\"var(--ts-ui-menu-bar-panel-bg, rgb(255, 255, 255))\");\n this.setInsets(new Insets(4, 0, 4, 0));\n this.setBorder({ border: \"1px solid var(--ts-ui-menu-bar-panel-border, rgb(200, 200, 200))\" });\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setShadow(\"var(--ts-ui-menu-bar-panel-shadow, 2px 4px 8px rgba(0, 0, 0, 0.15))\");\n this.getAria().setRole(\"menu\");\n this.setContain(\"layout\");\n\n this.enableVerticalScroll();\n }\n\n /**\n * Applies the rebuild-mode chrome (right-click context-menu CSS variables).\n */\n private applyRebuildChrome(): void {\n this.setVisible(false);\n this.setBackgroundColor(\"var(--ts-ui-context-menu-bg, rgb(255, 255, 255))\");\n this.setInsets(new Insets(4, 0, 4, 0));\n this.setBorder({ border: \"1px solid var(--ts-ui-context-menu-border, rgb(200, 200, 200))\" });\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setShadow(\"var(--ts-ui-context-menu-shadow, 2px 4px 8px rgba(0, 0, 0, 0.15))\");\n this.setContain(\"layout\");\n\n this.enableVerticalScroll();\n }\n\n /**\n * Wires the native vertical-scroll primitives so an over-tall menu scrolls\n * its items instead of overflowing the clamped panel height. This is the\n * `\"y\"` case of [`Panel.setAutoScroll`](/api/core/classes/Panel#setautoscroll)\n * replicated directly: `overflow-x: hidden` (so a `visible` x-axis does not\n * compute to `auto` and sprout a spurious horizontal scrollbar), `overflow-y:\n * auto`, and the layout manager's vertical overflow flag so the `VBox` lays\n * items out past the clamped inner height rather than compressing them.\n */\n private enableVerticalScroll(): void {\n this.setOverflowX(\"hidden\");\n this.setOverflowY(\"auto\");\n\n (this.getLayoutManager() as VBox).setOverflowing(false, true);\n }\n\n /**\n * Caps the menu's height to the vertical room available at its anchored\n * position, then applies the content height. Because `Menu` is a\n * `clampsToContentSize()` component, `setHeight` runs `contentHeight` through\n * `clampHeight`, which caps it at `availableHeight`; when the content fits no\n * clamp fires, and when it overflows the height is capped and the\n * `overflow-y: auto` scrollbar engages. Width is left unconstrained via the\n * `Number.MAX_VALUE` \"no constraint\" sentinel — horizontal scroll is a\n * non-goal.\n *\n * @param availableHeight - Vertical room (px) the menu may occupy at its anchor.\n * @param contentHeight - The unclamped preferred height (px) of the menu's items.\n */\n private applyViewportHeightClamp(availableHeight: number, contentHeight: number): void {\n this.setMaxSize({ width: Number.MAX_VALUE, height: Math.max(0, availableHeight) });\n this.setHeight(contentHeight);\n }\n\n /**\n * Resolves the persistent-mode panel's top coordinate and applies the\n * matching height clamp. The menu grows downward from `growTop` unless the\n * content overflows the room below *and* the room above the anchor is\n * larger, in which case it flips to grow upward from `anchorTop`. Delegates\n * the room/flip arithmetic to {@link positionFlexibleAnchored}, which measures\n * the room against the side the menu actually grows toward — never re-derived\n * from a clamped top, which would let a flipped, viewport-pinned menu grow\n * back down across its anchor.\n *\n * @param growTop - The viewport edge the menu grows downward from.\n * @param anchorTop - The anchor edge the menu flips up against.\n * @param totalHeight - The unclamped preferred height (px) of the menu's items.\n * @param viewportHeight - The current viewport height (px).\n * @returns The resolved top coordinate (px) for the panel.\n */\n private placeVertically(growTop: number, anchorTop: number, totalHeight: number, viewportHeight: number): number {\n const { start, available } = positionFlexibleAnchored(anchorTop, growTop, totalHeight, viewportHeight, VIEWPORT_MARGIN);\n\n this.applyViewportHeightClamp(available, totalHeight);\n\n return start;\n }\n\n /**\n * Tear down the current persistent items and rebuild them from `configs`.\n * Used by `open()` to re-resolve a provider-sourced dropdown each time it\n * shows, so its labels / enabled state track current state. Mirrors the\n * rebuild-mode `show()` teardown, then defers to {@link buildPersistentItems}.\n *\n * @param configs - The freshly-resolved item configurations.\n */\n private rebuildPersistentItems(configs: MenuItemConfig[]): void {\n this.closeOpenSubmenu();\n\n this._menuItems = [];\n this._focusedIndex = -1;\n this.disposeAllComponents();\n\n this.buildPersistentItems(configs);\n }\n\n /**\n * Builds the item list for persistent mode and sets the panel width.\n *\n * @param items - The item configs from the constructor.\n */\n private buildPersistentItems(items: MenuItemConfig[]): void {\n this.pauseLayout();\n\n for (const config of items) {\n const item = new MenuItem(\n config,\n () => { config.action?.(); this._onClose!(); },\n (hoveredItem) => { this.handleItemOpenSubmenu(hoveredItem); }\n );\n\n this.addComponent(item);\n this._menuItems.push(item);\n }\n\n this.resumeLayout();\n\n this.setWidth(this.layOutColumns());\n }\n\n /**\n * Updates the focused-item highlight, clearing the previous item and setting the new one.\n *\n * @param index - The new focused index, or -1 to clear.\n */\n private setFocusedIndex(index: number): void {\n if (this._focusedIndex >= 0 && this._focusedIndex < this._menuItems.length) {\n const prev = this._menuItems[this._focusedIndex];\n\n if (prev instanceof MenuItem) {\n prev.setFocused(false);\n }\n }\n\n this._focusedIndex = index;\n\n if (index >= 0 && index < this._menuItems.length) {\n const next = this._menuItems[index];\n\n if (next instanceof MenuItem) {\n next.setFocused(true);\n }\n }\n }\n\n /**\n * Returns `true` if the item at the given index should be skipped during focus traversal.\n *\n * @param index - Zero-based item index.\n * @returns Whether the item is a separator (or [`MenuSeparator`](/api/component/container/classes/MenuSeparator) instance).\n */\n private isItemSeparator(index: number): boolean {\n const item = this._menuItems[index];\n\n if (item instanceof MenuSeparator) {\n return true;\n }\n\n if (item instanceof MenuItem) {\n return item.isSeparator();\n }\n\n return false;\n }\n\n /**\n * Closes and forgets this menu's open child submenu, if any. Shared by the\n * teardown paths (`hide` / `close`) and by the submenu switch in\n * `handleItemOpenSubmenu` when a different item takes the child slot.\n */\n private closeOpenSubmenu(): void {\n if (this._openSubmenuPanel) {\n this._openSubmenuPanel.close();\n this._openSubmenuItem?.getAria().setExpanded(false);\n this._openSubmenuPanel = null;\n this._openSubmenuItem = null;\n }\n }\n\n /**\n * Closes the whole menu chain through the current mode's close path: the\n * persistent-mode `onClose` callback (a menubar dropdown), or `hide()` for a\n * rebuild-mode context menu — which also tears down any open child submenu.\n * Used by an activated submenu leaf and the outside-click / window-blur\n * dismissals, so a submenu selection closes the parent context menu too.\n */\n private dismissAll(): void {\n if (this._persistent) {\n this._onClose!();\n } else {\n this.hide();\n }\n }\n\n // ----- DismissableLayer -----\n\n /**\n * Returns the menu panel's root element for the central layer tree.\n *\n * @returns The menu's element, or null when not yet rendered.\n */\n getLayerElement(): Handle | null {\n return this.getElement() ?? null;\n }\n\n /**\n * Returns the dismiss mode the document-level handlers consult. A menu is\n * `\"click-outside\"`: dismissed by a `pointerdown` outside its layer subtree\n * (and its excluded trigger) and by a window blur, but never by a focus\n * move — exactly the pointerdown + window-blur behaviour the menu owned\n * before it joined the layer tree.\n *\n * @returns The layer dismiss mode.\n */\n getDismissMode(): LayerDismissMode {\n return \"click-outside\";\n }\n\n /**\n * Advisory close request from the manager (an outside `pointerdown`, a\n * window blur, or Escape). Routes through the mode-aware close chain: a\n * persistent (MenuBar) menu fires its `onClose`, a rebuild (context) menu\n * calls `hide()`; either way any open submenu is torn down.\n */\n requestClose(): void {\n this.dismissAll();\n }\n\n /**\n * Returns the trigger element excluded from the manager's outside-pointerdown\n * test — the context-menu opener passed to {@link show} / {@link toggleFor},\n * or the [`MenuBar`](/api/component/menubar/classes/MenuBar) button set via\n * {@link setExcludedElement} — so the gesture that opened the menu does not\n * immediately re-close it.\n *\n * @returns The excluded element, or null.\n */\n getAnchorElement(): Handle | null {\n return this._excludedEl;\n }\n\n /**\n * Returns the dropdown z-index band so an unrelated top-level menu stacks in\n * the dropdown family. A submenu — or a menu opened while another layer is\n * topmost — inherits its opener's band instead, so it rises above the\n * surface it was opened from.\n *\n * @returns The dropdown band base.\n */\n getBand(): number {\n return LayerManager.Band.Dropdown;\n }\n\n /**\n * Mirrors a manager-allocated z-index onto the element when the layer this\n * menu was opened inside is raised via the manager's bring-to-front path,\n * so an open menu never falls behind the surface it belongs to.\n *\n * @param zIndex - The fresh z-index assigned by the manager.\n */\n onZIndexChanged(zIndex: number): void {\n this.setZIndex(zIndex);\n }\n\n /**\n * Handles the open-submenu signal from a hovered or activated [`MenuItem`](/api/component/container/classes/MenuItem).\n *\n * Closes any existing child submenu when the item has no submenu (or is\n * disabled); opens or switches to the child panel when it does.\n *\n * @param item - The [`MenuItem`](/api/component/container/classes/MenuItem) that triggered the signal.\n */\n private handleItemOpenSubmenu(item: MenuItem): void {\n // A disabled item opens no submenu (even though it has one); like a plain\n // item, hovering it just closes any sibling submenu that is open.\n if (!item.hasSubmenu() || !item.isEnabled()) {\n this.closeOpenSubmenu();\n\n return;\n }\n\n if (this._openSubmenuItem === item) {\n return;\n }\n\n this.closeOpenSubmenu();\n\n const submenuPanel = new Menu(\n item.getSubmenuConfig()!.items,\n () => { this.dismissAll(); }\n );\n\n this._openSubmenuPanel = submenuPanel;\n this._openSubmenuItem = item;\n item.getAria().setExpanded(true);\n\n submenuPanel.setExcludedElement(this.getElement(true)!);\n submenuPanel.open(item.getElement(true)!, this);\n }\n\n /**\n * Throws if the method called is not valid in the current mode (rebuild-only check).\n *\n * @param method - Name of the method that was invoked, used in the error message.\n */\n private assertRebuildMode(method: string): void {\n if (this._persistent) {\n throw new Error(`Menu.${method}() is only valid in rebuild mode (constructed without arguments).`);\n }\n }\n\n /**\n * Throws if the method called is not valid in the current mode (persistent-only check).\n *\n * @param method - Name of the method that was invoked, used in the error message.\n */\n private assertPersistentMode(method: string): void {\n if (!this._persistent) {\n throw new Error(`Menu.${method}() is only valid in persistent mode (constructed with items and onClose).`);\n }\n }\n}\n\nconst MenuCallable = callable(Menu);\ntype MenuCallable = Menu;\nexport {\n Menu as _Menu,\n MenuCallable as Menu\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{S as e,h as t,t as n,v as r}from"./Component-w65rPK75.js";import{t as i}from"./Glyph-DeJF_zsR.js";import{t as a}from"./AbstractBooleanInput-CbW7ZrDT.js";import{r as o}from"./VBox-32fxpkDl.js";import{circle as s}from"./glyphs/solid/circle.es.js";i.register(s);var c={outline:`none`},RadioButton=class extends a{_ring;_dot;constructor(t,a,s){super({...a??{}},{...c,...s??{}}),this.setLayoutManager(new o),this._ring=new n,this._ring.setPreferredSize({width:16,height:16}),this._ring.setMinSize({width:16,height:16}),this._ring.setMaxSize({width:16,height:16}),this._ring.setSize({width:16,height:16}),this._ring.setBackgroundColor(`var(--ts-ui-radio-bg, var(--ts-ui-form-bg, rgb(255, 255, 255)))`),this._ring.setBorder(`1px solid var(--ts-ui-form-border, rgb(160, 160, 160))`),this._ring.setBorderRadius(`50%`),this._ring.setCursor(`pointer`),this._dot=new i(`circle`),this._dot.setForegroundColor(`var(--ts-ui-radio-dot-color, rgb(255, 255, 255))`),this._dot.setPreferredSize({width:8,height:8}),this._dot.setMaxSize({width:8,height:8}),this._dot.setX(3),this._dot.setY(3),this._dot.setOpacity(0),this._dot.setPointerEvents(`none`),r.isReducedMotion()||(this._dot.setTransition(`opacity 120ms ease-out`),this._ring.setTransition(`background-color 120ms ease-out, border-color 120ms ease-out`)),this._ring.addComponent(this._dot),super.addComponent(this._ring),this.getAria().setRole(`radio`),this.getAria().setTabIndex(0),this.getAria().setChecked(!1),e.addListener(this._ring,`click`,()=>this.activateFromPointer()),this.installKeyboard(),this._options.label===void 0&&this._options.text===void 0&&t!==void 0?this._options.label=t:this._options.label===void 0&&this._options.text!==void 0&&(this._options.label=this._options.text),this._options.value!==void 0&&this._options.selected===void 0&&(this._options.selected=this._options.value),this._options.selected!==void 0&&this.applySelected(this._options.selected),this._options.label!==void 0&&this.applyLabel(this._options.label),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.selected!==void 0&&(this._options.selected=e.selected),e.value!==void 0&&(this._options.value=e.value),e.label!==void 0&&(this._options.label=e.label),e.text!==void 0&&(this._options.text=e.text),e.radioName!==void 0&&(this._options.radioName=e.radioName),e.enabled!==void 0&&(this._options.enabled=e.enabled),e.readOnly!==void 0&&(this._options.readOnly=e.readOnly),this}activate(){this.isSelected()||(this.setSelected(!0),e.fireEvent(this,`change`))}getInteractiveSurface(){return this._ring}isSelected(){return this._options.selected??!1}setSelected(e){let t=!!e;return t===this.isSelected()?this:(this._options.selected=t,this.applySelected(t),this.notifyChange(t),this)}getValue(){return this.isSelected()}setValue(e){return this.setSelected(e)}setRadioName(e){return this._options.radioName=e,this}clearRadioName(){return this._options.radioName=void 0,this}getRadioName(){return this._options.radioName??null}on(t,n){return t===`action`?(e.addListener(this,`change`,n),this):super.on(t,n)}off(t,n){return t===`action`?(e.removeListener(this,`change`,n),this):super.off(t,n)}applySelected(e){this.getAria().setChecked(e),this._ring.setBackgroundColor(e?`var(--ts-ui-radio-bg-selected, rgb(30, 100, 200))`:`var(--ts-ui-radio-bg, var(--ts-ui-form-bg, rgb(255, 255, 255)))`),this._ring.setBorder(e?`1px solid var(--ts-ui-radio-bg-selected, rgb(30, 100, 200))`:`1px solid var(--ts-ui-form-border, rgb(160, 160, 160))`),this._dot.setOpacity(+!!e)}},l=t(RadioButton);export{l as t};
2
- //# sourceMappingURL=RadioButton-CCcIaL54.js.map
@@ -1,2 +0,0 @@
1
- import{t as e}from"./DOM-Ba8YRVel.js";import{S as t,h as n,t as r,v as i}from"./Component-w65rPK75.js";import{t as a}from"./ListenerBag-WPdKuuds.js";import{t as o}from"./Glyph-DeJF_zsR.js";var AutoRepeat=class{_initialDelay;_decay;_floor;_onTick;_handle=null;_delay;constructor(e){this._initialDelay=e.initialDelay,this._decay=e.decay,this._floor=e.floor,this._onTick=e.onTick,this._delay=e.initialDelay}start(){this._onTick(),this._delay=this._initialDelay,this.scheduleNext()}stop(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null),this._delay=this._initialDelay}isRunning(){return this._handle!==null}scheduleNext(){this._handle=setTimeout(()=>{this._onTick(),this._delay=Math.max(this._floor,this._delay*this._decay),this.scheduleNext()},this._delay)}};function scrollShadowRamp(e){return Math.max(0,Math.min(1,(e-1)/40))}function scrollShadowBoxShadow(){let e=`12px`;return`inset 0 ${e} ${e} -${e} var(--ts-ss-top, transparent),inset 0 -${e} ${e} -${e} var(--ts-ss-bottom, transparent),inset ${e} 0 ${e} -${e} var(--ts-ss-left, transparent),inset -${e} 0 ${e} -${e} var(--ts-ss-right, transparent)`}function scrollShadowEdgeValue(e){return e===0?null:`color-mix(in srgb, var(--ts-ui-scroll-shadow-color) ${e}%, transparent)`}var s=12,c=2,l=30,u=10,d=400,f=.75,p=40,m=40,ScrollArrowButton=class extends r{_glyph;_disabled=!1;_listeners=new a;_repeat;constructor(e){super(),this.setWidth(s),this.setHeight(s),this.setCursor(`default`),this.setBackgroundColor(`var(--ts-ui-scrollbar-arrow-bg, transparent)`),this.setForegroundColor(`var(--ts-ui-scrollbar-arrow-color, rgba(0, 0, 0, 0.55))`),i.isReducedMotion()||this.setTransition(`color 120ms ease-out`),this._glyph=new o(`unicode-arrow-`+e),this._glyph.setPreferredSize({width:s,height:s}),this._glyph.setFontSize(u),this._glyph.setPointerEvents(`none`),super.addComponent(this._glyph),this._repeat=new AutoRepeat({initialDelay:d,decay:f,floor:p,onTick:()=>this.emit(`tick`)}),t.addListener(this,`mousedown`,this._onMouseDown),t.addListener(this,`mouseover`,this._onMouseOver),t.addListener(this,`mouseout`,this._onMouseOut),t.addViewportListener(this,`mouseup`,this._onMouseUp),t.addViewportListener(this,`mouseleave`,this._onMouseUp)}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)}isDisabledState(){return this._disabled}setDisabledState(e){this._disabled!==e&&(this._disabled=e,e?(this._repeat.stop(),this.setForegroundColor(`var(--ts-ui-scrollbar-arrow-disabled-color, rgba(0, 0, 0, 0.18))`)):this.setForegroundColor(`var(--ts-ui-scrollbar-arrow-color, rgba(0, 0, 0, 0.55))`))}_onMouseDown=e=>(this._disabled||this._repeat.start(),{stop:!0,prevent:!0});_onMouseUp=()=>{this._repeat.isRunning()&&this._repeat.stop()};_onMouseOver=()=>{this._disabled||this.setBackgroundColor(`var(--ts-ui-scrollbar-arrow-hover-bg, rgba(0, 0, 0, 0.06))`)};_onMouseOut=()=>{this.setBackgroundColor(`var(--ts-ui-scrollbar-arrow-bg, transparent)`)}},h={backgroundColor:`var(--ts-ui-scrollbar-track, rgba(0, 0, 0, 0.04))`,touchAction:`none`},Scrollbar=class extends r{_orientation=`vertical`;_thumb;_viewportSize=0;_contentSize=0;_scrollPosition=0;_thumbSize=-1;_thumbPos=-1;_dragStartClient=0;_dragStartScroll=0;_listeners=new a;_arrowsEnabled=!0;_arrowStep=m;_arrowStart=null;_arrowEnd=null;constructor(e=`vertical`,n,i){super(n,{...h,...i??{}}),this._orientation=e,n?.arrowStep!==void 0&&(this._arrowStep=n.arrowStep),n?.arrowsEnabled!==void 0&&(this._arrowsEnabled=n.arrowsEnabled),this.setUserSelect(`none`),this.getAria().setHidden(!0),this.isVertical()?this.setWidth(s):this.setHeight(s),this._thumb=new r,this._thumb.setBackgroundColor(`var(--ts-ui-scrollbar-thumb, rgba(0, 0, 0, 0.35))`),this._thumb.setCursor(`default`),this.isVertical()?(this._thumb.setX(c),this._thumb.setY(0),this._thumb.setWidth(8)):(this._thumb.setX(0),this._thumb.setY(c),this._thumb.setHeight(8)),super.addComponent(this._thumb),this._arrowsEnabled&&this.buildArrows(),t.addListener(this._thumb,`mousedown`,this._onDragStart),t.addListener(this._thumb,`touchstart`,this._onDragStart),t.addListener(this._thumb,`mouseover`,this._onThumbMouseOver),t.addListener(this._thumb,`mouseout`,this._onThumbMouseOut),t.addListener(this,`mousedown`,this._onTrackClick),t.addListener(this,`touchstart`,this._onTrackClick),this.applyListeners(n?.listeners)}buildArrows(){let e=this.isVertical()?`up`:`left`,t=this.isVertical()?`down`:`right`;this._arrowStart=new ScrollArrowButton(e),this._arrowStart.setX(0),this._arrowStart.setY(0),this._arrowStart.on(`tick`,this._onArrowStartTick),this._arrowStart.setDisabledState(!0),super.addComponent(this._arrowStart),this._arrowEnd=new ScrollArrowButton(t),this._arrowEnd.setX(0),this._arrowEnd.setY(0),this._arrowEnd.on(`tick`,this._onArrowEndTick),super.addComponent(this._arrowEnd)}_onArrowStartTick=()=>this.onArrowTick(-1);_onArrowEndTick=()=>this.onArrowTick(1);onArrowTick(e){let t=Math.max(0,this._contentSize-this._viewportSize),n=Math.max(0,Math.min(t,this._scrollPosition+e*this._arrowStep));n!==this._scrollPosition&&this.emit(`scroll`,n)}disposeArrows(){this._arrowStart&&=(super.removeComponent(this._arrowStart),null),this._arrowEnd&&=(super.removeComponent(this._arrowEnd),null)}_onThumbMouseOver=()=>{this._thumb.setBackgroundColor(`var(--ts-ui-scrollbar-thumb-hover, rgba(0, 0, 0, 0.55))`)};_onThumbMouseOut=()=>{this._thumb.setBackgroundColor(`var(--ts-ui-scrollbar-thumb, rgba(0, 0, 0, 0.35))`)};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)}setMetrics(e,t,n){this._viewportSize=e,this._contentSize=t,this._scrollPosition=n;let r=t>e;if(this.setDisplayed(r),!r)return this;let i=this.getTrackLength();if(i<=0)return this;let a=e/t,o=Math.max(l,Math.floor(i*a));this._thumbSize!==o&&(this._thumbSize=o,this.setThumbSize(o));let u=Math.max(0,t-e),d=Math.max(0,i-o),f=u>0?Math.round(n/u*d):0;this._thumbPos!==f&&(this._thumbPos=f,this.setThumbPos(f));let p=this.axisBox();if(this.isVertical()?(this._thumb.setX(p.crossOrigin+c),this._thumb.setWidth(p.crossExtent-4)):(this._thumb.setY(p.crossOrigin+c),this._thumb.setHeight(p.crossExtent-4)),this._arrowsEnabled&&this._arrowStart&&this._arrowEnd){let e=p.origin+p.extent-s;this.isVertical()?(this._arrowStart.setX(p.crossOrigin),this._arrowStart.setY(p.origin),this._arrowStart.setWidth(p.crossExtent),this._arrowEnd.setX(p.crossOrigin),this._arrowEnd.setY(e),this._arrowEnd.setWidth(p.crossExtent)):(this._arrowStart.setY(p.crossOrigin),this._arrowStart.setX(p.origin),this._arrowStart.setHeight(p.crossExtent),this._arrowEnd.setY(p.crossOrigin),this._arrowEnd.setX(e),this._arrowEnd.setHeight(p.crossExtent)),this._arrowStart.setDisabledState(n<=0),this._arrowEnd.setDisabledState(n>=u)}return this}getTrackWidth(){return s}getOrientation(){return this._orientation}isArrowsEnabled(){return this._arrowsEnabled}setArrowsEnabled(e){return this._arrowsEnabled===e?this:(this._arrowsEnabled=e,e?this.buildArrows():this.disposeArrows(),this.setMetrics(this._viewportSize,this._contentSize,this._scrollPosition),this)}getArrowStep(){return this._arrowStep}setArrowStep(e){return this._arrowStep=e,this}isVertical(){return this._orientation===`vertical`}axisBox(){let e=this.getContentBounds()??{x:0,y:0,width:this.getWidth()||0,height:this.getHeight()||0};return this.isVertical()?{origin:e.y,extent:e.height,crossOrigin:e.x,crossExtent:e.width}:{origin:e.x,extent:e.width,crossOrigin:e.y,crossExtent:e.height}}getTrackLength(){let e=this._arrowsEnabled?24:0;return Math.max(0,this.axisBox().extent-e)}getTrackOrigin(){return this.axisBox().origin+(this._arrowsEnabled?s:0)}setThumbSize(e){this.isVertical()?this._thumb.setHeight(e):this._thumb.setWidth(e)}setThumbPos(e){let t=this.getTrackOrigin();this.isVertical()?this._thumb.setY(t+e):this._thumb.setX(t+e)}extractClientPrimary(e){let t=this.isVertical(),n=e;if(n.touches!==void 0){let e=n.touches.length>0?n.touches[0]:n.changedTouches[0];return e?t?e.clientY:e.clientX:0}let r=e;return t?r.clientY:r.clientX}_onDragStart=n=>{n.preventDefault(),this._dragStartClient=this.extractClientPrimary(n),this._dragStartScroll=this._scrollPosition,t.addViewportListener(this,`mousemove`,this._onDragMove),t.addViewportListener(this,`mouseup`,this._onDragEnd),t.addViewportListener(this,`touchmove`,this._onDragMove),t.addViewportListener(this,`touchend`,this._onDragEnd),t.addViewportListener(this,`touchcancel`,this._onDragEnd),e.sink.apply(e.source.getBody(),{style:{pointerEvents:`none`}})};_onDragMove=e=>{let t=this.getTrackLength(),n=Math.max(0,this._contentSize-this._viewportSize),r=Math.max(0,t-this._thumbSize);if(r<=0)return!0;let i=(this.extractClientPrimary(e)-this._dragStartClient)/r*n,a=Math.max(0,Math.min(n,this._dragStartScroll+i));return this.emit(`scroll`,a),!0};_onDragEnd=n=>(t.removeViewportListener(this,`mousemove`,this._onDragMove),t.removeViewportListener(this,`mouseup`,this._onDragEnd),t.removeViewportListener(this,`touchmove`,this._onDragMove),t.removeViewportListener(this,`touchend`,this._onDragEnd),t.removeViewportListener(this,`touchcancel`,this._onDragEnd),e.sink.apply(e.source.getBody(),{style:{pointerEvents:``}}),!0);_onTrackClick=t=>{t.preventDefault();let n=this.isVertical(),r,i=t;if(i.touches!==void 0){let t=i.touches.length>0?i.touches[0]:i.changedTouches[0],a=this.getElement();if(!t||!a)return;let o=e.source.getViewportRect(this),s=this.getBorderSize();r=n?t.clientY-o.top-s.top:t.clientX-o.left-s.left}else{let e=t;r=n?e.offsetY:e.offsetX}let a=this.axisBox(),o=this.getTrackOrigin(),c=a.origin+a.extent;if(this._arrowsEnabled&&(r<o||r>=c-s))return;let l=r-o<this._thumbPos+this._thumbSize/2?-1:1,u=Math.max(0,this._contentSize-this._viewportSize),d=Math.max(0,Math.min(u,this._scrollPosition+l*this._viewportSize));this.emit(`scroll`,d)}},g=n(Scrollbar);export{AutoRepeat as a,scrollShadowRamp as i,scrollShadowBoxShadow as n,scrollShadowEdgeValue as r,g as t};
2
- //# sourceMappingURL=Scrollbar-C3ZZyZ1e.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Scrollbar-C3ZZyZ1e.js","names":[],"sources":["../../src/typescript/lib/core/AutoRepeat.ts","../../src/typescript/lib/core/ScrollShadow.ts","../../src/typescript/lib/component/container/Scrollbar.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * Construction options for {@link AutoRepeat}.\n *\n * @category Core\n */\nexport interface AutoRepeatOptions {\n /** Delay before the first repeat tick, in ms. */\n initialDelay: number;\n /** Multiplier applied to the delay after each tick (`0 < decay <= 1`). */\n decay: number;\n /** Lower bound the decaying delay never drops below, in ms. */\n floor: number;\n /** Invoked once immediately on {@link AutoRepeat.start} and once per scheduled tick. */\n onTick: () => void;\n}\n\n/**\n * The press-and-hold *accelerating* auto-repeat state machine, extracted from the\n * spin button and scrollbar arrow so both drive one implementation.\n *\n * {@link start} fires `onTick` once immediately, then schedules repeats whose\n * interval starts at `initialDelay` and shrinks by `decay` after every tick,\n * never dropping below `floor`. {@link stop} cancels the schedule and resets the\n * interval, so a subsequent press begins again at `initialDelay`.\n *\n * It is a plain class (no DOM element, so not `callable()`-wrapped) and drives an\n * owner-supplied callback rather than emitting its own events — each host keeps\n * dispatching through its own event surface. `setTimeout` is a process timer, not\n * a DOM call, so it does not route through the DOM seam.\n *\n * @category Core\n */\nexport class AutoRepeat {\n\n private readonly _initialDelay: number;\n private readonly _decay: number;\n private readonly _floor: number;\n private readonly _onTick: () => void;\n private _handle: ReturnType<typeof setTimeout> | null = null;\n private _delay: number;\n\n /**\n * @param options - The repeat timing and the per-tick callback.\n */\n constructor(options: AutoRepeatOptions) {\n this._initialDelay = options.initialDelay;\n this._decay = options.decay;\n this._floor = options.floor;\n this._onTick = options.onTick;\n this._delay = options.initialDelay;\n }\n\n /**\n * Fires `onTick` immediately, then schedules accelerating repeats starting\n * at `initialDelay`.\n */\n start(): void {\n this._onTick();\n this._delay = this._initialDelay;\n this.scheduleNext();\n }\n\n /**\n * Cancels any pending schedule and resets the interval to `initialDelay`.\n */\n stop(): void {\n if (this._handle !== null) {\n clearTimeout(this._handle);\n this._handle = null;\n }\n\n this._delay = this._initialDelay;\n }\n\n /**\n * Returns whether a repeat schedule is currently armed.\n *\n * @returns `true` between {@link start} and {@link stop}.\n */\n isRunning(): boolean {\n return this._handle !== null;\n }\n\n /**\n * Schedules the next tick at the current interval, then decays the interval\n * (×`decay`, floored at `floor`) for the tick after it.\n */\n private scheduleNext(): void {\n this._handle = setTimeout(() => {\n this._onTick();\n this._delay = Math.max(this._floor, this._delay * this._decay);\n this.scheduleNext();\n }, this._delay);\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * Shared visual recipe for the position-aware scroll-edge shadows. Both scroll\n * systems paint the same fade — {@link Panel} over native `overflow` scrolling\n * and {@link VirtualScroller} over transform-based virtual lists — so the\n * geometry constants, the box-shadow recipe, and the per-edge colour ramp live\n * here as the single source of truth. Each owner keeps its own overlay element\n * and per-edge cache; only the drift-prone visual maths is shared.\n *\n * @category Core\n */\n\n/**\n * Reach in pixels of each scroll-edge shadow — used as the inset shadow's\n * offset, blur, and (negative) spread, so each edge's fade hugs its border and\n * dies out roughly this far inward.\n *\n * Fixed framework-side rather than themed, for the same reason the keyboard\n * focus indicator fixes its `2px` width (see `Theme.indicator.focus`): the\n * colour is the only part a theme needs to vary, and a constant keeps the\n * overlay's four-shadow geometry simple. `12px` reads as a soft edge cue\n * without masking a meaningful strip of content.\n */\nexport const SCROLL_SHADOW_EXTENT_PX = 12;\n\n/**\n * Distance in pixels over which an edge's shadow ramps from none to full as the\n * scroll position moves away from that edge's extreme. The strength is\n * `clamp(distanceFromExtreme / this, 0, 1)`, so the shadow fades in smoothly\n * just after leaving an edge and fades out as the opposite edge is approached,\n * instead of popping on/off at a single-pixel threshold. `40px` gives a visible\n * fade without staying faint through a meaningful amount of overflow.\n */\nexport const SCROLL_SHADOW_RAMP_PX = 40;\n\n/**\n * Ramps an edge's shadow in by how far the scroll position sits past that\n * edge's extreme. The `- 1` folds in a sub-pixel epsilon: within 1px of an\n * extreme the strength is 0, so a fractional scrollSize/clientSize mismatch\n * can't leave a phantom fade.\n *\n * @param distance - Pixels the scroll position is past the edge's extreme.\n * @returns The edge strength in the range 0–1.\n */\nexport function scrollShadowRamp(distance: number): number {\n return Math.max(0, Math.min(1, (distance - 1) / SCROLL_SHADOW_RAMP_PX));\n}\n\n/**\n * Builds the four-layer inset `box-shadow` recipe — one blurred shadow per edge,\n * each gated by a local custom property (`--ts-ss-top` / `-bottom` / `-left` /\n * `-right`) defaulting to `transparent`, so lighting an edge is a single\n * property flip rather than a shadow rebuild. The `-extent` spread keeps each\n * shadow hugging its own edge while the equal blur fades it inward over\n * `extent`px, so the edge reads as a soft cast shadow rather than the\n * hard-terminated band a `linear-gradient` would paint.\n *\n * @returns The `box-shadow` value.\n */\nexport function scrollShadowBoxShadow(): string {\n const extent = SCROLL_SHADOW_EXTENT_PX + \"px\";\n\n return (\n `inset 0 ${extent} ${extent} -${extent} var(--ts-ss-top, transparent),` +\n `inset 0 -${extent} ${extent} -${extent} var(--ts-ss-bottom, transparent),` +\n `inset ${extent} 0 ${extent} -${extent} var(--ts-ss-left, transparent),` +\n `inset -${extent} 0 ${extent} -${extent} var(--ts-ss-right, transparent)`\n );\n}\n\n/**\n * Value for one edge's shadow custom property: the theme shadow colour scaled\n * toward transparent by `percent`, or `null` at zero so the `box-shadow` layer\n * falls back to its `transparent` default.\n *\n * @param percent - The edge strength as a whole percentage (0–100).\n * @returns The `color-mix` value, or `null` when the edge is off.\n */\nexport function scrollShadowEdgeValue(percent: number): string | null {\n return percent === 0\n ? null\n : `color-mix(in srgb, var(--ts-ui-scroll-shadow-color) ${percent}%, transparent)`;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Animation } from \"~/core/Animation.js\";\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { AutoRepeat } from \"~/core/AutoRepeat.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport type { HorizontalSide } from \"~/primitive/Edge.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisOrientation } from \"~/primitive/Axis.js\";\n\n/**\n * String-literal union of the events emitted by {@link Scrollbar}.\n *\n * @category Components\n */\nexport type ScrollbarEvent = \"scroll\";\n\n/**\n * String-literal union of the events emitted by the file-local\n * `ScrollArrowButton`.\n */\ntype ScrollArrowEvent = \"tick\";\n\nconst TRACK_WIDTH = 12;\nconst THUMB_INSET = 2;\nconst THUMB_MIN_SIZE = 30;\n\n// Font size for the Unicode triangle character rendered in each arrow button.\n// The arrow box is TRACK_WIDTH (12) px square; the ambient 14 px theme font\n// produces a 16 px line-box that overflows the 12 px element and is clipped\n// at the bottom by `overflow: hidden`, leaving the visible triangle slid up\n// against the top edge. 10 px shrinks the line-box (= font-size × line-height\n// 1) below the element height so the character fits with even padding above\n// and below — matches the value [`AccordionIndicator`](./AccordionIndicator.ts)\n// uses for its `▶` chevron.\nconst ARROW_GLYPH_FONT_SIZE = 10;\n\n// Initial hold-repeat delay and acceleration parameters for ScrollArrowButton.\n// Mirrors SpinButton's scheduler so a long press on either widget produces the\n// same accelerating tick cadence — 400 ms first interval, multiplied by 0.75\n// each tick, floored at 40 ms.\nconst ARROW_REPEAT_INITIAL_MS = 400;\nconst ARROW_REPEAT_DECAY = 0.75;\nconst ARROW_REPEAT_FLOOR_MS = 40;\n\n// Default per-click scroll step in pixels for arrow buttons. Roughly two rows\n// at default font size in the Table; chosen as a fixed pixel value rather than\n// a derived fraction of viewport so the step size stays predictable as content\n// grows. Owners can override via `setArrowStep(px)`.\nconst DEFAULT_ARROW_STEP_PX = 40;\n\n// Crossfade duration for the arrow's enabled↔disabled colour swap. Matches the\n// 120 ms ease-out cadence Checkbox / Toggle / RadioButton use for their state\n// crossfades so the scrollbar's arrows read as the same UI vocabulary; short\n// enough to feel instant while softening the hard colour flip at each edge.\nconst ARROW_FADE_DURATION_MS = 120;\n\n/**\n * Callback type fired by {@link Scrollbar} when the user drags the thumb or\n * clicks the track. Receives the new scroll position in pixels.\n *\n * @category Components\n */\nexport type ScrollbarListener = (position: number) => void;\n\n/**\n * Construction-time options for {@link Scrollbar}.\n *\n * @category Components\n */\nexport interface ScrollbarOptions extends ComponentOptions {\n\n /**\n * When `true`, the scrollbar renders classic OS-style arrow buttons at\n * each end of the track (up/down for vertical, left/right for horizontal).\n * The buttons step the scroll position by {@link ScrollbarOptions.arrowStep}\n * pixels per click and accelerate while held. The matching arrow shows a\n * dimmed disabled-state colour when scroll is already at that edge.\n * Defaults to `true`; set `false` to suppress the arrows for a minimalist\n * look.\n */\n arrowsEnabled?: boolean;\n\n /**\n * Per-click scroll step in pixels for the arrow buttons. Ignored when\n * `arrowsEnabled` is `false`. Defaults to `40`.\n */\n arrowStep?: number;\n\n /**\n * Multi-event listener bag dispatched to {@link Scrollbar.on} at\n * construction time.\n */\n listeners?: {\n scroll?: ScrollbarListener;\n };\n}\n\n/**\n * Direction for a `ScrollArrowButton`. `\"up\"` / `\"down\"` go on the ends\n * of a vertical scrollbar; `\"left\"` / `\"right\"` on a horizontal one.\n */\ntype ArrowDirection = HorizontalSide | \"up\" | \"down\";\n\n/**\n * A press-and-hold arrow button rendered at one end of a {@link Scrollbar}'s\n * track when arrows are enabled. File-local — not exported from the container\n * barrel because it is a Scrollbar implementation detail.\n *\n * Mirrors the [`SpinButton`](/api/component/input/classes/SpinButton)\n * hold-repeat cadence: a click fires one tick, holding accelerates from a\n * 400 ms initial interval to a 40 ms floor, and release cancels the schedule.\n * When in the disabled state, mousedown is ignored and the glyph renders in\n * the dim colour token.\n */\nclass ScrollArrowButton extends Component {\n\n private _glyph: Glyph;\n private _disabled: boolean = false;\n private _listeners: ListenerBag<ScrollArrowEvent> = new ListenerBag<ScrollArrowEvent>();\n private _repeat: AutoRepeat;\n\n /**\n * Constructs a square TRACK_WIDTH × TRACK_WIDTH arrow button pointing in\n * the given direction.\n *\n * @param direction - One of `\"up\"`, `\"down\"`, `\"left\"`, `\"right\"`.\n */\n constructor(direction: ArrowDirection) {\n super();\n\n this.setWidth(TRACK_WIDTH);\n this.setHeight(TRACK_WIDTH);\n this.setCursor(\"default\");\n this.setBackgroundColor(\"var(--ts-ui-scrollbar-arrow-bg, transparent)\");\n this.setForegroundColor(\"var(--ts-ui-scrollbar-arrow-color, rgba(0, 0, 0, 0.55))\");\n\n // Fade the enabled↔disabled colour swap in setDisabledState instead of a hard\n // switch. Declared at construction (mirrors Checkbox's crossfade) so the initial\n // colour — the start arrow's dim state set by Scrollbar.buildArrows before first\n // paint — appears instantly, and only later at-edge toggles fade. Honours\n // prefers-reduced-motion.\n if (!Animation.isReducedMotion()) {\n this.setTransition(\"color \" + ARROW_FADE_DURATION_MS + \"ms ease-out\");\n }\n\n // Fill the full TRACK_WIDTH × TRACK_WIDTH button so `text-align: center`\n // + `line-height: 1` (Glyph char-mode defaults) centre the character\n // within that box. The explicit `fontSize` shrinks the inherited\n // 14 px size so the Unicode triangle's line-box (which scales with\n // font-size) fits inside the 12 px element box and isn't clipped at\n // the bottom by `overflow: hidden` — mirrors the same fix\n // AccordionIndicator applies to its `▶` chevron.\n this._glyph = new Glyph(\"unicode-arrow-\" + direction);\n this._glyph.setPreferredSize({ width: TRACK_WIDTH, height: TRACK_WIDTH });\n this._glyph.setFontSize(ARROW_GLYPH_FONT_SIZE);\n // The glyph fills the whole button, so without this a click's target is\n // the glyph element. The Event system routes `addListener` callbacks only\n // to the exact target id (no bubbling), so the arrow's own\n // mousedown/mouseover/mouseout handlers below would never fire. Making\n // the glyph non-interactive lets pointer events fall through to the arrow.\n this._glyph.setPointerEvents(\"none\");\n\n super.addComponent(this._glyph);\n\n this._repeat = new AutoRepeat({\n initialDelay: ARROW_REPEAT_INITIAL_MS,\n decay: ARROW_REPEAT_DECAY,\n floor: ARROW_REPEAT_FLOOR_MS,\n onTick: () => this.emit(\"tick\"),\n });\n\n Event.addListener(this, \"mousedown\", this._onMouseDown);\n Event.addListener(this, \"mouseover\", this._onMouseOver);\n Event.addListener(this, \"mouseout\", this._onMouseOut);\n Event.addViewportListener(this, \"mouseup\", this._onMouseUp);\n Event.addViewportListener(this, \"mouseleave\", this._onMouseUp);\n }\n\n /**\n * Registers a listener for one of this arrow button's events.\n *\n * @param event - `\"tick\"` fires on each logical tick (initial mousedown\n * plus every subsequent hold-repeat tick).\n * @param listener - The callback invoked when the event fires.\n *\n * @returns This arrow button, for method chaining.\n */\n on(event: \"tick\", listener: () => void): this;\n on(event: ScrollArrowEvent, 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 arrow button, for method chaining.\n */\n off(event: ScrollArrowEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with no arguments.\n *\n * @param event - The event to emit.\n */\n protected emit(event: \"tick\"): void;\n protected emit(event: ScrollArrowEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns whether this arrow is currently in the at-edge disabled state.\n *\n * @returns `true` when disabled, `false` otherwise.\n */\n isDisabledState(): boolean {\n return this._disabled;\n }\n\n /**\n * Toggles the disabled visual state and behaviour. Disabled arrows render\n * the glyph in the dim colour token and ignore mousedown events; any\n * in-flight hold-repeat schedule is cancelled.\n *\n * @param disabled - `true` to disable, `false` to enable.\n */\n setDisabledState(disabled: boolean): void {\n if (this._disabled === disabled) {\n return;\n }\n\n this._disabled = disabled;\n\n if (disabled) {\n this._repeat.stop();\n this.setForegroundColor(\"var(--ts-ui-scrollbar-arrow-disabled-color, rgba(0, 0, 0, 0.18))\");\n } else {\n this.setForegroundColor(\"var(--ts-ui-scrollbar-arrow-color, rgba(0, 0, 0, 0.55))\");\n }\n }\n\n /**\n * Handles mousedown on the button. Stops propagation so the parent\n * Scrollbar's track-click handler does not also fire, then (if not\n * disabled) fires the first tick and schedules accelerating repeats.\n *\n * @param _e - The mousedown event.\n * @returns `{ stop: true, prevent: true }`, consuming the press and suppressing the browser's default text selection.\n */\n private _onMouseDown = (_e: MouseEvent): Event.ListenerResult => {\n if (!this._disabled) {\n this._repeat.start();\n }\n\n return { stop: true, prevent: true };\n };\n\n /**\n * Cancels the hold-repeat schedule on mouseup or when the pointer leaves\n * the viewport.\n */\n private _onMouseUp = (): void => {\n if (!this._repeat.isRunning()) {\n return;\n }\n\n this._repeat.stop();\n };\n\n /**\n * Darkens the button background on hover, mirroring the thumb-hover idiom.\n */\n private _onMouseOver = (): void => {\n if (this._disabled) {\n return;\n }\n\n this.setBackgroundColor(\"var(--ts-ui-scrollbar-arrow-hover-bg, rgba(0, 0, 0, 0.06))\");\n };\n\n /**\n * Restores the resting background when the pointer leaves the button.\n */\n private _onMouseOut = (): void => {\n this.setBackgroundColor(\"var(--ts-ui-scrollbar-arrow-bg, transparent)\");\n };\n\n}\n\nconst _defaultScrollbarOptions: Partial<ScrollbarOptions> = {\n backgroundColor: \"var(--ts-ui-scrollbar-track, rgba(0, 0, 0, 0.04))\",\n touchAction: \"none\",\n};\n\n/**\n * A custom virtual scrollbar overlay.\n *\n * Designed for components that own their own scroll state (e.g. transform-based\n * virtual lists) and don't expose native browser scrolling. The owner pushes\n * viewport/content metrics in via {@link Scrollbar.setMetrics} and subscribes\n * to scroll position changes via {@link Scrollbar.on}.\n *\n * The thumb is dragged with the mouse; clicking the track above/beside the\n * thumb pages by one viewport. The scrollbar hides itself when content fits in\n * the viewport. Classic OS-style arrow buttons at each end of the track are\n * controlled by {@link ScrollbarOptions.arrowsEnabled} — enabled by default;\n * set it `false` for a minimalist look.\n *\n * Available in vertical (default) and horizontal orientations; the owner is\n * responsible for sizing the primary axis (height for vertical, width for\n * horizontal) and positioning the bar on the cross axis.\n *\n * @category Components\n */\nclass Scrollbar extends Component<ScrollbarOptions> {\n\n private _orientation : AxisOrientation = \"vertical\";\n private _thumb : Component;\n private _viewportSize : number = 0;\n private _contentSize : number = 0;\n private _scrollPosition : number = 0;\n // Sentinel `-1` rather than `0` so the first `setMetrics` call always\n // writes the thumb's size/position through to the DOM, even when the\n // computed values happen to be `0` (e.g. scroll position at top with\n // arrows enabled, where the constructor-time `setY(0)` would otherwise\n // never get corrected to the arrow-region origin offset).\n private _thumbSize : number = -1;\n private _thumbPos : number = -1;\n private _dragStartClient : number = 0;\n private _dragStartScroll : number = 0;\n private _listeners : ListenerBag<ScrollbarEvent> = new ListenerBag<ScrollbarEvent>();\n\n private _arrowsEnabled : boolean = true;\n private _arrowStep : number = DEFAULT_ARROW_STEP_PX;\n private _arrowStart : ScrollArrowButton | null = null;\n private _arrowEnd : ScrollArrowButton | null = null;\n\n /**\n * Constructs a Scrollbar.\n *\n * @param orientation - `\"vertical\"` (default) or `\"horizontal\"`.\n * @param options - Optional configuration bag. ComponentOptions fields are\n * forwarded to `super` so the standard cascade applies; the\n * arrow-specific fields are read here ahead of any DOM construction.\n * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(orientation: AxisOrientation = \"vertical\", options?: ScrollbarOptions, subclassDefaults?: Partial<ScrollbarOptions>) {\n super(options, { ..._defaultScrollbarOptions, ...(subclassDefaults ?? {}) });\n\n this._orientation = orientation;\n\n // Pull arrow configuration out of options ahead of any DOM work so the\n // thumb / arrow construction below sees the final flags. ComponentOptions\n // fields (visible, backgroundColor, etc.) are already applied by super.\n if (options?.arrowStep !== undefined) {\n this._arrowStep = options.arrowStep;\n }\n if (options?.arrowsEnabled !== undefined) {\n this._arrowsEnabled = options.arrowsEnabled;\n }\n\n this.setUserSelect(\"none\");\n\n // Purely decorative: the real scroll semantics live on the native scroll\n // region this widget mirrors (overlay Panel, VirtualScroller), so hide the\n // track / thumb / arrow subtree from assistive tech rather than have it\n // announce a stack of non-interactive divs. Matches Spacer / Glyph.\n this.getAria().setHidden(true);\n\n if (this.isVertical()) {\n this.setWidth(TRACK_WIDTH);\n } else {\n this.setHeight(TRACK_WIDTH);\n }\n\n this._thumb = new Component();\n this._thumb.setBackgroundColor(\"var(--ts-ui-scrollbar-thumb, rgba(0, 0, 0, 0.35))\");\n this._thumb.setCursor(\"default\");\n\n if (this.isVertical()) {\n this._thumb.setX(THUMB_INSET);\n this._thumb.setY(0);\n this._thumb.setWidth(TRACK_WIDTH - 2 * THUMB_INSET);\n } else {\n this._thumb.setX(0);\n this._thumb.setY(THUMB_INSET);\n this._thumb.setHeight(TRACK_WIDTH - 2 * THUMB_INSET);\n }\n\n super.addComponent(this._thumb);\n\n if (this._arrowsEnabled) {\n this.buildArrows();\n }\n\n Event.addListener(this._thumb, \"mousedown\", this._onDragStart);\n Event.addListener(this._thumb, \"touchstart\", this._onDragStart);\n Event.addListener(this._thumb, \"mouseover\", this._onThumbMouseOver);\n Event.addListener(this._thumb, \"mouseout\", this._onThumbMouseOut);\n Event.addListener(this, \"mousedown\", this._onTrackClick);\n Event.addListener(this, \"touchstart\", this._onTrackClick);\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Constructs and wires the two arrow buttons. Both are seeded at `(0, 0)`;\n * their real position and cross-axis extent are set in `setMetrics`, which\n * is the first point the bar's content box is known. Disabled-state for the\n * start arrow is pre-set since `_scrollPosition` defaults to 0 — without\n * this the start arrow renders enabled for one frame before `setMetrics`\n * corrects it.\n */\n private buildArrows(): void {\n const startDirection: ArrowDirection = this.isVertical() ? \"up\" : \"left\";\n const endDirection: ArrowDirection = this.isVertical() ? \"down\" : \"right\";\n\n this._arrowStart = new ScrollArrowButton(startDirection);\n this._arrowStart.setX(0);\n this._arrowStart.setY(0);\n this._arrowStart.on(\"tick\", this._onArrowStartTick);\n this._arrowStart.setDisabledState(true);\n super.addComponent(this._arrowStart);\n\n this._arrowEnd = new ScrollArrowButton(endDirection);\n // Both axes depend on the bar's content box, which is not resolved yet;\n // `setMetrics` writes the origin and the cross-axis extent.\n this._arrowEnd.setX(0);\n this._arrowEnd.setY(0);\n this._arrowEnd.on(\"tick\", this._onArrowEndTick);\n super.addComponent(this._arrowEnd);\n }\n\n /**\n * Bound forwarder for the start arrow's `\"tick\"` event. Kept as a class\n * field so {@link off}-style detachment would have a stable reference.\n */\n private _onArrowStartTick = (): void => this.onArrowTick(-1);\n\n /**\n * Bound forwarder for the end arrow's `\"tick\"` event.\n */\n private _onArrowEndTick = (): void => this.onArrowTick(+1);\n\n /**\n * Computes the next scroll position one `_arrowStep` away from the current\n * one, clamps it to the valid range, and fires scroll listeners if it\n * changed. The owner's listener call back into `setMetrics` next frame,\n * which refreshes `atStart` / `atEnd` and updates the arrow disabled state.\n *\n * @param direction - `-1` for the start arrow, `+1` for the end arrow.\n */\n private onArrowTick(direction: -1 | 1): void {\n const maxScroll = Math.max(0, this._contentSize - this._viewportSize);\n const newPosition = Math.max(0, Math.min(maxScroll, this._scrollPosition + direction * this._arrowStep));\n\n if (newPosition !== this._scrollPosition) {\n this.emit(\"scroll\", newPosition);\n }\n }\n\n /**\n * Tears down both arrow buttons. Called when arrows are toggled off at\n * runtime; safe when no arrows are currently mounted.\n */\n private disposeArrows(): void {\n if (this._arrowStart) {\n super.removeComponent(this._arrowStart);\n this._arrowStart = null;\n }\n if (this._arrowEnd) {\n super.removeComponent(this._arrowEnd);\n this._arrowEnd = null;\n }\n }\n\n /**\n * Darkens the thumb fill when the cursor moves over it.\n */\n private _onThumbMouseOver = (): void => {\n this._thumb.setBackgroundColor(\"var(--ts-ui-scrollbar-thumb-hover, rgba(0, 0, 0, 0.55))\");\n };\n\n /**\n * Restores the thumb's resting fill when the cursor leaves it.\n */\n private _onThumbMouseOut = (): void => {\n this._thumb.setBackgroundColor(\"var(--ts-ui-scrollbar-thumb, rgba(0, 0, 0, 0.35))\");\n };\n\n /**\n * Registers a listener for one of this scrollbar's events.\n *\n * @param event - `\"scroll\"` fires when the user changes the scroll\n * position by dragging the thumb, clicking the track, or pressing an\n * arrow button, receiving the new scroll position in pixels.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This scrollbar, for method chaining.\n */\n on(event: \"scroll\", listener: ScrollbarListener): this;\n on(event: ScrollbarEvent, 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 scrollbar, for method chaining.\n */\n off(event: ScrollbarEvent, 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: \"scroll\", position: number): void;\n protected emit(event: ScrollbarEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Pushes viewport/content metrics and the current scroll position into the\n * scrollbar. Recomputes the thumb's size, position, and cross-axis extent\n * against the content box, and hides the scrollbar if the content fits in\n * the viewport. When arrows are enabled, repositions both arrows against\n * the content box and refreshes the disabled-at-edge state on both.\n *\n * @param viewportSize - The visible window size in pixels along the scroll axis.\n * @param contentSize - The total scrollable content size in pixels along the scroll axis.\n * @param scrollPosition - The current scroll offset in pixels.\n */\n setMetrics(viewportSize: number, contentSize: number, scrollPosition: number): this {\n this._viewportSize = viewportSize;\n this._contentSize = contentSize;\n this._scrollPosition = scrollPosition;\n\n const overflow = contentSize > viewportSize;\n this.setDisplayed(overflow);\n\n if (!overflow) {\n return this;\n }\n\n const trackLength = this.getTrackLength();\n if (trackLength <= 0) {\n return this;\n }\n\n const ratio = viewportSize / contentSize;\n const newThumbSize = Math.max(THUMB_MIN_SIZE, Math.floor(trackLength * ratio));\n if (this._thumbSize !== newThumbSize) {\n this._thumbSize = newThumbSize;\n this.setThumbSize(newThumbSize);\n }\n\n const maxScroll = Math.max(0, contentSize - viewportSize);\n const maxThumb = Math.max(0, trackLength - newThumbSize);\n const newThumbPos = maxScroll > 0 ? Math.round((scrollPosition / maxScroll) * maxThumb) : 0;\n if (this._thumbPos !== newThumbPos) {\n this._thumbPos = newThumbPos;\n this.setThumbPos(newThumbPos);\n }\n\n const axis = this.axisBox();\n\n // The thumb's cross-axis extent narrows with a bordered/padded content\n // box; re-derived every call, mirroring the end arrow's write below.\n if (this.isVertical()) {\n this._thumb.setX(axis.crossOrigin + THUMB_INSET);\n this._thumb.setWidth(axis.crossExtent - 2 * THUMB_INSET);\n } else {\n this._thumb.setY(axis.crossOrigin + THUMB_INSET);\n this._thumb.setHeight(axis.crossExtent - 2 * THUMB_INSET);\n }\n\n if (this._arrowsEnabled && this._arrowStart && this._arrowEnd) {\n // Position both arrows against the content box — the start arrow\n // at its origin, the end arrow at (origin + extent - TRACK_WIDTH).\n const endPos = axis.origin + axis.extent - TRACK_WIDTH;\n\n // The cross-axis extent too, not just the origin: the arrows are\n // built as a rigid TRACK_WIDTH square, so a bordered bar would keep\n // them at their full width inside a narrower box and overrun the\n // side the border is on. The main axis stays TRACK_WIDTH — that is\n // the gutter the track math reserves at each end.\n if (this.isVertical()) {\n this._arrowStart.setX(axis.crossOrigin);\n this._arrowStart.setY(axis.origin);\n this._arrowStart.setWidth(axis.crossExtent);\n this._arrowEnd.setX(axis.crossOrigin);\n this._arrowEnd.setY(endPos);\n this._arrowEnd.setWidth(axis.crossExtent);\n } else {\n this._arrowStart.setY(axis.crossOrigin);\n this._arrowStart.setX(axis.origin);\n this._arrowStart.setHeight(axis.crossExtent);\n this._arrowEnd.setY(axis.crossOrigin);\n this._arrowEnd.setX(endPos);\n this._arrowEnd.setHeight(axis.crossExtent);\n }\n\n this._arrowStart.setDisabledState(scrollPosition <= 0);\n this._arrowEnd.setDisabledState(scrollPosition >= maxScroll);\n }\n\n return this;\n }\n\n /**\n * Returns the static track width in pixels (the cross-axis dimension).\n *\n * @returns The fixed track width.\n */\n getTrackWidth(): number {\n return TRACK_WIDTH;\n }\n\n /**\n * Returns the scrollbar's orientation.\n *\n * @returns `\"vertical\"` or `\"horizontal\"`.\n */\n getOrientation(): AxisOrientation {\n return this._orientation;\n }\n\n /**\n * Returns whether the arrow buttons at each end of the track are enabled.\n *\n * @returns `true` when arrows are rendered, `false` otherwise.\n */\n isArrowsEnabled(): boolean {\n return this._arrowsEnabled;\n }\n\n /**\n * Enables or disables the end-cap arrow buttons. Intended for\n * construction-time use via {@link ScrollbarOptions.arrowsEnabled};\n * runtime toggles are supported but tear down or build the arrow\n * components on the fly and re-run `setMetrics` against the cached\n * viewport / content / scroll-position triple so the thumb size / position\n * recompute against the new track length.\n *\n * @param enabled - `true` to render arrows, `false` to remove them.\n * @returns This scrollbar, for method chaining.\n */\n setArrowsEnabled(enabled: boolean): this {\n if (this._arrowsEnabled === enabled) {\n return this;\n }\n\n this._arrowsEnabled = enabled;\n\n if (enabled) {\n this.buildArrows();\n } else {\n this.disposeArrows();\n }\n\n this.setMetrics(this._viewportSize, this._contentSize, this._scrollPosition);\n\n return this;\n }\n\n /**\n * Returns the per-click scroll step in pixels used by the arrow buttons.\n *\n * @returns The cached arrow step.\n */\n getArrowStep(): number {\n return this._arrowStep;\n }\n\n /**\n * Sets the per-click scroll step in pixels used by the arrow buttons.\n * No-op when arrows are not enabled, except that the new value is cached\n * and applies on a subsequent {@link Scrollbar.setArrowsEnabled}`(true)`.\n *\n * @param px - The new step size in pixels.\n * @returns This scrollbar, for method chaining.\n */\n setArrowStep(px: number): this {\n this._arrowStep = px;\n\n return this;\n }\n\n /**\n * Returns true if this is a vertical scrollbar.\n *\n * @returns True for vertical, false for horizontal.\n */\n private isVertical(): boolean {\n return this._orientation === \"vertical\";\n }\n\n /**\n * The content box projected onto the scroll axis: `origin` and `extent` run\n * along it, `crossOrigin` and `crossExtent` across it. Falls back to the\n * outer box while the element does not exist yet.\n */\n private axisBox(): { origin: number; extent: number; crossOrigin: number; crossExtent: number } {\n const box = this.getContentBounds()\n ?? { x: 0, y: 0, width: this.getWidth() || 0, height: this.getHeight() || 0 };\n\n return this.isVertical()\n ? { origin: box.y, extent: box.height, crossOrigin: box.x, crossExtent: box.width }\n : { origin: box.x, extent: box.width, crossOrigin: box.y, crossExtent: box.height };\n }\n\n /**\n * Returns the length of the track along the scroll axis available for the\n * thumb to travel, measured in the content box. Subtracts the two arrow\n * regions when arrows are enabled so the thumb travel range stays inside\n * the track between them.\n */\n private getTrackLength(): number {\n const inset = this._arrowsEnabled ? 2 * TRACK_WIDTH : 0;\n\n return Math.max(0, this.axisBox().extent - inset);\n }\n\n /**\n * Returns the primary-axis offset, in the content box, where the track\n * region (thumb travel area) starts. The content-box origin, plus\n * `TRACK_WIDTH` when arrows are enabled (skipping the start arrow).\n */\n private getTrackOrigin(): number {\n const axis = this.axisBox();\n\n return axis.origin + (this._arrowsEnabled ? TRACK_WIDTH : 0);\n }\n\n /**\n * Sets the thumb's size along the scroll axis.\n *\n * @param size - The new thumb size in pixels.\n */\n private setThumbSize(size: number): void {\n if (this.isVertical()) {\n this._thumb.setHeight(size);\n } else {\n this._thumb.setWidth(size);\n }\n }\n\n /**\n * Sets the thumb's position along the scroll axis, offset by the track\n * origin so the thumb sits inside the track region between the arrows\n * when those are enabled.\n *\n * @param pos - The new thumb position in pixels, relative to the track region.\n */\n private setThumbPos(pos: number): void {\n const origin = this.getTrackOrigin();\n\n if (this.isVertical()) {\n this._thumb.setY(origin + pos);\n } else {\n this._thumb.setX(origin + pos);\n }\n }\n\n\n /**\n * Reads the client coordinate along the scroll axis from a mouse or touch\n * event, preferring the active touch on touchstart/touchmove and the\n * changed touch on touchend.\n *\n * @param e - A mouse or touch event.\n * @returns The primary-axis coordinate in viewport space.\n */\n private extractClientPrimary(e: MouseEvent | TouchEvent): number {\n const vertical = this.isVertical();\n const touchEvent = e as TouchEvent;\n\n if (touchEvent.touches !== undefined) {\n const t = touchEvent.touches.length > 0\n ? touchEvent.touches[0]\n : touchEvent.changedTouches[0];\n if (!t) {\n return 0;\n }\n return vertical ? t.clientY : t.clientX;\n }\n\n const mouse = e as MouseEvent;\n return vertical ? mouse.clientY : mouse.clientX;\n }\n\n /**\n * Captures the initial client coordinate and scroll position, then attaches\n * viewport listeners so the drag continues even when the pointer or finger\n * leaves the thumb.\n *\n * @param e - The mousedown or touchstart event on the thumb.\n */\n private _onDragStart = (e: MouseEvent | TouchEvent): void => {\n e.preventDefault();\n\n this._dragStartClient = this.extractClientPrimary(e);\n this._dragStartScroll = this._scrollPosition;\n\n Event.addViewportListener(this, \"mousemove\", this._onDragMove);\n Event.addViewportListener(this, \"mouseup\", this._onDragEnd);\n Event.addViewportListener(this, \"touchmove\", this._onDragMove);\n Event.addViewportListener(this, \"touchend\", this._onDragEnd);\n Event.addViewportListener(this, \"touchcancel\", this._onDragEnd);\n\n // Suppresses pointer events on document.body (not a Component) for the\n // duration of the drag so the cursor can't snag on other elements.\n DOM.sink.apply(DOM.source.getBody(), { style: { pointerEvents: \"none\" } });\n };\n\n /**\n * Translates the client-axis delta into a scroll-position delta proportional\n * to the content/track ratio, clamps it, and fires scroll listeners.\n *\n * @param e - The viewport mousemove or touchmove event during a drag.\n * @returns `true` while a thumb drag is in progress, consuming the move so nothing else tracks the pointer.\n */\n private _onDragMove = (e: MouseEvent | TouchEvent): Event.ListenerResult => {\n const trackLength = this.getTrackLength();\n const maxScroll = Math.max(0, this._contentSize - this._viewportSize);\n const maxThumb = Math.max(0, trackLength - this._thumbSize);\n\n if (maxThumb <= 0) {\n return true;\n }\n\n const delta = this.extractClientPrimary(e) - this._dragStartClient;\n const scrollDelta = (delta / maxThumb) * maxScroll;\n const newPosition = Math.max(0, Math.min(maxScroll, this._dragStartScroll + scrollDelta));\n\n this.emit(\"scroll\", newPosition);\n\n return true;\n };\n\n /**\n * Removes viewport listeners and restores body pointer events.\n *\n * @param _e - The mouseup/touchend/touchcancel event ending the drag.\n * @returns `true`, consuming the release that ends the thumb drag.\n */\n private _onDragEnd = (_e: Event): Event.ListenerResult => {\n Event.removeViewportListener(this, \"mousemove\", this._onDragMove);\n Event.removeViewportListener(this, \"mouseup\", this._onDragEnd);\n Event.removeViewportListener(this, \"touchmove\", this._onDragMove);\n Event.removeViewportListener(this, \"touchend\", this._onDragEnd);\n Event.removeViewportListener(this, \"touchcancel\", this._onDragEnd);\n\n // Restores pointer events on document.body (not a Component) once the drag ends.\n DOM.sink.apply(DOM.source.getBody(), { style: { pointerEvents: \"\" } });\n\n return true;\n };\n\n /**\n * Pages the scroll position by one viewport size when the user clicks or\n * taps the track outside the thumb. Events on the thumb itself are caught\n * by a separate listener and don't reach here. When arrows are enabled,\n * clicks that land inside an arrow region are ignored so the arrow\n * button's own handler is the sole authority on arrow-region clicks.\n *\n * @param e - The mousedown or touchstart event on the track.\n */\n private _onTrackClick = (e: MouseEvent | TouchEvent): void => {\n e.preventDefault();\n\n const vertical = this.isVertical();\n let click: number;\n\n const touchEvent = e as TouchEvent;\n if (touchEvent.touches !== undefined) {\n const t = touchEvent.touches.length > 0\n ? touchEvent.touches[0]\n : touchEvent.changedTouches[0];\n const el = this.getElement();\n if (!t || !el) {\n return;\n }\n // getViewportRect's top-left is the border box, while the mouse\n // branch's offsetX/offsetY below is padding-box-relative; subtract\n // the leading border side so both branches agree before being\n // compared against the content-box-relative axisBox() values.\n const rect = DOM.source.getViewportRect(this);\n const border = this.getBorderSize();\n click = vertical\n ? t.clientY - rect.top - border.top\n : t.clientX - rect.left - border.left;\n } else {\n const mouse = e as MouseEvent;\n click = vertical ? mouse.offsetY : mouse.offsetX;\n }\n\n // When arrows are enabled, ignore clicks that landed inside either\n // arrow region — those are handled by the arrow's own mousedown\n // listener (which also stops propagation, but the touchstart path\n // does not, so we double-check here).\n const axis = this.axisBox();\n const origin = this.getTrackOrigin();\n const outer = axis.origin + axis.extent;\n if (this._arrowsEnabled && (click < origin || click >= outer - TRACK_WIDTH)) {\n return;\n }\n\n // Subtract origin so the thumb-position comparison stays in the\n // track-relative coordinate space (matches `_thumbPos`).\n const trackClick = click - origin;\n const thumbCenter = this._thumbPos + this._thumbSize / 2;\n const direction = trackClick < thumbCenter ? -1 : 1;\n const maxScroll = Math.max(0, this._contentSize - this._viewportSize);\n const newPosition = Math.max(0, Math.min(maxScroll, this._scrollPosition + direction * this._viewportSize));\n\n this.emit(\"scroll\", newPosition);\n };\n}\n\nconst ScrollbarCallable = callable(Scrollbar);\ntype ScrollbarCallable = Scrollbar;\nexport {\n Scrollbar as _Scrollbar,\n ScrollbarCallable as Scrollbar\n};\n"],"mappings":"6LAkCA,IAAa,WAAb,KAAwB,CAEpB,cACA,OACA,OACA,QACA,QAAwD,KACxD,OAKA,YAAY,EAA4B,CACpC,KAAK,cAAgB,EAAQ,aAC7B,KAAK,OAAgB,EAAQ,MAC7B,KAAK,OAAgB,EAAQ,MAC7B,KAAK,QAAgB,EAAQ,OAC7B,KAAK,OAAgB,EAAQ,YACjC,CAMA,OAAc,CACV,KAAK,QAAQ,EACb,KAAK,OAAS,KAAK,cACnB,KAAK,aAAa,CACtB,CAKA,MAAa,CACL,KAAK,UAAY,OACjB,aAAa,KAAK,OAAO,EACzB,KAAK,QAAU,MAGnB,KAAK,OAAS,KAAK,aACvB,CAOA,WAAqB,CACjB,OAAO,KAAK,UAAY,IAC5B,CAMA,cAA6B,CACzB,KAAK,QAAU,eAAiB,CAC5B,KAAK,QAAQ,EACb,KAAK,OAAS,KAAK,IAAI,KAAK,OAAQ,KAAK,OAAS,KAAK,MAAM,EAC7D,KAAK,aAAa,CACtB,EAAG,KAAK,MAAM,CAClB,CACJ,ECnDA,SAAgB,iBAAiB,EAA0B,CACvD,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,GAAI,EAAW,GAAA,EAA0B,CAAC,CAC1E,CAaA,SAAgB,uBAAgC,CAC5C,IAAM,EAAS,OAEf,MACI,WAAW,EAAO,GAAG,EAAO,IAAI,EAAO,0CAC3B,EAAO,GAAG,EAAO,IAAI,EAAO,0CAC/B,EAAO,KAAK,EAAO,IAAI,EAAO,yCAC7B,EAAO,KAAK,EAAO,IAAI,EAAO,iCAEhD,CAUA,SAAgB,sBAAsB,EAAgC,CAClE,OAAO,IAAY,EACb,KACA,uDAAuD,EAAQ,gBACzE,CCzDA,IAAM,EAAiB,GACjB,EAAiB,EACjB,EAAiB,GAUjB,EAAwB,GAMxB,EAA0B,IAC1B,EAA0B,IAC1B,EAA0B,GAM1B,EAAwB,GAkExB,kBAAN,cAAgC,CAAU,CAEtC,OACA,UAA+D,GAC/D,WAA+D,IAAI,EACnE,QAQA,YAAY,EAA2B,CACnC,MAAM,EAEN,KAAK,SAAS,CAAW,EACzB,KAAK,UAAU,CAAW,EAC1B,KAAK,UAAU,SAAS,EACxB,KAAK,mBAAmB,8CAA8C,EACtE,KAAK,mBAAmB,yDAAyD,EAO5E,EAAU,gBAAgB,GAC3B,KAAK,cAAc,sBAAiD,EAUxE,KAAK,OAAS,IAAI,EAAM,iBAAmB,CAAS,EACpD,KAAK,OAAO,iBAAiB,CAAE,MAAO,EAAa,OAAQ,CAAY,CAAC,EACxE,KAAK,OAAO,YAAY,CAAqB,EAM7C,KAAK,OAAO,iBAAiB,MAAM,EAEnC,MAAM,aAAa,KAAK,MAAM,EAE9B,KAAK,QAAU,IAAI,WAAW,CAC1B,aAAc,EACd,MAAc,EACd,MAAc,EACd,WAAoB,KAAK,KAAK,MAAM,CACxC,CAAC,EAED,EAAM,YAAY,KAAM,YAAc,KAAK,YAAY,EACvD,EAAM,YAAY,KAAM,YAAc,KAAK,YAAY,EACvD,EAAM,YAAY,KAAM,WAAc,KAAK,WAAW,EACtD,EAAM,oBAAoB,KAAM,UAAc,KAAK,UAAU,EAC7D,EAAM,oBAAoB,KAAM,aAAc,KAAK,UAAU,CACjE,CAYA,GAAG,EAA0B,EAA0B,CAGnD,OAFA,KAAK,WAAW,IAAI,EAAO,CAAQ,EAE5B,IACX,CAWA,IAAI,EAAyB,EAA0B,CAGnD,OAFA,KAAK,WAAW,OAAO,EAAO,CAAQ,EAE/B,IACX,CAQA,KAAe,EAAyB,GAAG,EAA0B,CACjE,KAAK,WAAW,KAAK,EAAO,GAAG,CAAO,CAC1C,CAOA,iBAA2B,CACvB,OAAO,KAAK,SAChB,CASA,iBAAiB,EAAyB,CAClC,KAAK,YAAc,IAIvB,KAAK,UAAY,EAEb,GACA,KAAK,QAAQ,KAAK,EAClB,KAAK,mBAAmB,kEAAkE,GAE1F,KAAK,mBAAmB,yDAAyD,EAEzF,CAUA,aAAwB,IACf,KAAK,WACN,KAAK,QAAQ,MAAM,EAGhB,CAAE,KAAM,GAAM,QAAS,EAAK,GAOvC,eAAiC,CACxB,KAAK,QAAQ,UAAU,GAI5B,KAAK,QAAQ,KAAK,CACtB,EAKA,iBAAmC,CAC3B,KAAK,WAIT,KAAK,mBAAmB,4DAA4D,CACxF,EAKA,gBAAkC,CAC9B,KAAK,mBAAmB,8CAA8C,CAC1E,CAEJ,EAEM,EAAsD,CACxD,gBAAiB,oDACjB,YAAiB,MACrB,EAsBM,UAAN,cAAwB,CAA4B,CAEhD,aAAiD,WACjD,OACA,cAAsD,EACtD,aAAsD,EACtD,gBAAsD,EAMtD,WAAsD,GACtD,UAAsD,GACtD,iBAAsD,EACtD,iBAAsD,EACtD,WAAyD,IAAI,EAE7D,eAAsD,GACtD,WAAsD,EACtD,YAAsD,KACtD,UAAsD,KAatD,YAAY,EAA+B,WAAY,EAA4B,EAA8C,CAC7H,MAAM,EAAS,CAAE,GAAG,EAA0B,GAAI,GAAoB,CAAC,CAAG,CAAC,EAE3E,KAAK,aAAe,EAKhB,GAAS,YAAc,IAAA,KACvB,KAAK,WAAa,EAAQ,WAE1B,GAAS,gBAAkB,IAAA,KAC3B,KAAK,eAAiB,EAAQ,eAGlC,KAAK,cAAc,MAAM,EAMzB,KAAK,QAAQ,CAAC,CAAC,UAAU,EAAI,EAEzB,KAAK,WAAW,EAChB,KAAK,SAAS,CAAW,EAEzB,KAAK,UAAU,CAAW,EAG9B,KAAK,OAAS,IAAI,EAClB,KAAK,OAAO,mBAAmB,mDAAmD,EAClF,KAAK,OAAO,UAAU,SAAS,EAE3B,KAAK,WAAW,GAChB,KAAK,OAAO,KAAK,CAAW,EAC5B,KAAK,OAAO,KAAK,CAAC,EAClB,KAAK,OAAO,SAAS,CAA6B,IAElD,KAAK,OAAO,KAAK,CAAC,EAClB,KAAK,OAAO,KAAK,CAAW,EAC5B,KAAK,OAAO,UAAU,CAA6B,GAGvD,MAAM,aAAa,KAAK,MAAM,EAE1B,KAAK,gBACL,KAAK,YAAY,EAGrB,EAAM,YAAY,KAAK,OAAQ,YAAc,KAAK,YAAY,EAC9D,EAAM,YAAY,KAAK,OAAQ,aAAc,KAAK,YAAY,EAC9D,EAAM,YAAY,KAAK,OAAQ,YAAc,KAAK,iBAAiB,EACnE,EAAM,YAAY,KAAK,OAAQ,WAAc,KAAK,gBAAgB,EAClE,EAAM,YAAY,KAAM,YAAc,KAAK,aAAa,EACxD,EAAM,YAAY,KAAM,aAAc,KAAK,aAAa,EAExD,KAAK,eAAe,GAAS,SAAS,CAC1C,CAUA,aAA4B,CACxB,IAAM,EAAiC,KAAK,WAAW,EAAI,KAAS,OAC9D,EAAiC,KAAK,WAAW,EAAI,OAAS,QAEpE,KAAK,YAAc,IAAI,kBAAkB,CAAc,EACvD,KAAK,YAAY,KAAK,CAAC,EACvB,KAAK,YAAY,KAAK,CAAC,EACvB,KAAK,YAAY,GAAG,OAAQ,KAAK,iBAAiB,EAClD,KAAK,YAAY,iBAAiB,EAAI,EACtC,MAAM,aAAa,KAAK,WAAW,EAEnC,KAAK,UAAY,IAAI,kBAAkB,CAAY,EAGnD,KAAK,UAAU,KAAK,CAAC,EACrB,KAAK,UAAU,KAAK,CAAC,EACrB,KAAK,UAAU,GAAG,OAAQ,KAAK,eAAe,EAC9C,MAAM,aAAa,KAAK,SAAS,CACrC,CAMA,sBAAwC,KAAK,YAAY,EAAE,EAK3D,oBAAsC,KAAK,YAAY,CAAE,EAUzD,YAAoB,EAAyB,CACzC,IAAM,EAAc,KAAK,IAAI,EAAG,KAAK,aAAe,KAAK,aAAa,EAChE,EAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAW,KAAK,gBAAkB,EAAY,KAAK,UAAU,CAAC,EAEnG,IAAgB,KAAK,iBACrB,KAAK,KAAK,SAAU,CAAW,CAEvC,CAMA,eAA8B,CAC1B,AAEI,KAAK,eADL,MAAM,gBAAgB,KAAK,WAAW,EACnB,MAEvB,AAEI,KAAK,aADL,MAAM,gBAAgB,KAAK,SAAS,EACnB,KAEzB,CAKA,sBAAwC,CACpC,KAAK,OAAO,mBAAmB,yDAAyD,CAC5F,EAKA,qBAAuC,CACnC,KAAK,OAAO,mBAAmB,mDAAmD,CACtF,EAaA,GAAG,EAAwB,EAA0B,CAGjD,OAFA,KAAK,WAAW,IAAI,EAAO,CAAQ,EAE5B,IACX,CAWA,IAAI,EAAuB,EAA0B,CAGjD,OAFA,KAAK,WAAW,OAAO,EAAO,CAAQ,EAE/B,IACX,CAUA,KAAe,EAAuB,GAAG,EAA0B,CAC/D,KAAK,WAAW,KAAK,EAAO,GAAG,CAAO,CAC1C,CAaA,WAAW,EAAsB,EAAqB,EAA8B,CAChF,KAAK,cAAkB,EACvB,KAAK,aAAkB,EACvB,KAAK,gBAAkB,EAEvB,IAAM,EAAW,EAAc,EAG/B,GAFA,KAAK,aAAa,CAAQ,EAEtB,CAAC,EACD,OAAO,KAGX,IAAM,EAAc,KAAK,eAAe,EACxC,GAAI,GAAe,EACf,OAAO,KAGX,IAAM,EAAe,EAAe,EAC9B,EAAe,KAAK,IAAI,EAAgB,KAAK,MAAM,EAAc,CAAK,CAAC,EACzE,KAAK,aAAe,IACpB,KAAK,WAAa,EAClB,KAAK,aAAa,CAAY,GAGlC,IAAM,EAAY,KAAK,IAAI,EAAG,EAAc,CAAY,EAClD,EAAY,KAAK,IAAI,EAAG,EAAc,CAAY,EAClD,EAAc,EAAY,EAAI,KAAK,MAAO,EAAiB,EAAa,CAAQ,EAAI,EACtF,KAAK,YAAc,IACnB,KAAK,UAAY,EACjB,KAAK,YAAY,CAAW,GAGhC,IAAM,EAAO,KAAK,QAAQ,EAY1B,GARI,KAAK,WAAW,GAChB,KAAK,OAAO,KAAK,EAAK,YAAc,CAAW,EAC/C,KAAK,OAAO,SAAS,EAAK,YAAc,CAAe,IAEvD,KAAK,OAAO,KAAK,EAAK,YAAc,CAAW,EAC/C,KAAK,OAAO,UAAU,EAAK,YAAc,CAAe,GAGxD,KAAK,gBAAkB,KAAK,aAAe,KAAK,UAAW,CAG3D,IAAM,EAAS,EAAK,OAAS,EAAK,OAAS,EAOvC,KAAK,WAAW,GAChB,KAAK,YAAY,KAAK,EAAK,WAAW,EACtC,KAAK,YAAY,KAAK,EAAK,MAAM,EACjC,KAAK,YAAY,SAAS,EAAK,WAAW,EAC1C,KAAK,UAAU,KAAK,EAAK,WAAW,EACpC,KAAK,UAAU,KAAK,CAAM,EAC1B,KAAK,UAAU,SAAS,EAAK,WAAW,IAExC,KAAK,YAAY,KAAK,EAAK,WAAW,EACtC,KAAK,YAAY,KAAK,EAAK,MAAM,EACjC,KAAK,YAAY,UAAU,EAAK,WAAW,EAC3C,KAAK,UAAU,KAAK,EAAK,WAAW,EACpC,KAAK,UAAU,KAAK,CAAM,EAC1B,KAAK,UAAU,UAAU,EAAK,WAAW,GAG7C,KAAK,YAAY,iBAAiB,GAAkB,CAAC,EACrD,KAAK,UAAU,iBAAiB,GAAkB,CAAS,CAC/D,CAEA,OAAO,IACX,CAOA,eAAwB,CACpB,OAAO,CACX,CAOA,gBAAkC,CAC9B,OAAO,KAAK,YAChB,CAOA,iBAA2B,CACvB,OAAO,KAAK,cAChB,CAaA,iBAAiB,EAAwB,CAerC,OAdI,KAAK,iBAAmB,EACjB,MAGX,KAAK,eAAiB,EAElB,EACA,KAAK,YAAY,EAEjB,KAAK,cAAc,EAGvB,KAAK,WAAW,KAAK,cAAe,KAAK,aAAc,KAAK,eAAe,EAEpE,KACX,CAOA,cAAuB,CACnB,OAAO,KAAK,UAChB,CAUA,aAAa,EAAkB,CAG3B,MAFA,MAAK,WAAa,EAEX,IACX,CAOA,YAA8B,CAC1B,OAAO,KAAK,eAAiB,UACjC,CAOA,SAAgG,CAC5F,IAAM,EAAM,KAAK,iBAAiB,GACtB,CAAE,EAAG,EAAG,EAAG,EAAG,MAAO,KAAK,SAAS,GAAK,EAAG,OAAQ,KAAK,UAAU,GAAK,CAAE,EAErF,OAAO,KAAK,WAAW,EACjB,CAAE,OAAQ,EAAI,EAAG,OAAQ,EAAI,OAAQ,YAAa,EAAI,EAAG,YAAa,EAAI,KAAM,EAChF,CAAE,OAAQ,EAAI,EAAG,OAAQ,EAAI,MAAQ,YAAa,EAAI,EAAG,YAAa,EAAI,MAAO,CAC3F,CAQA,gBAAiC,CAC7B,IAAM,EAAQ,KAAK,eAAiB,GAAkB,EAEtD,OAAO,KAAK,IAAI,EAAG,KAAK,QAAQ,CAAC,CAAC,OAAS,CAAK,CACpD,CAOA,gBAAiC,CAG7B,OAFa,KAAK,QAEX,CAAA,CAAK,QAAU,KAAK,eAAiB,EAAc,EAC9D,CAOA,aAAqB,EAAoB,CACjC,KAAK,WAAW,EAChB,KAAK,OAAO,UAAU,CAAI,EAE1B,KAAK,OAAO,SAAS,CAAI,CAEjC,CASA,YAAoB,EAAmB,CACnC,IAAM,EAAS,KAAK,eAAe,EAE/B,KAAK,WAAW,EAChB,KAAK,OAAO,KAAK,EAAS,CAAG,EAE7B,KAAK,OAAO,KAAK,EAAS,CAAG,CAErC,CAWA,qBAA6B,EAAoC,CAC7D,IAAM,EAAa,KAAK,WAAW,EAC7B,EAAa,EAEnB,GAAI,EAAW,UAAY,IAAA,GAAW,CAClC,IAAM,EAAI,EAAW,QAAQ,OAAS,EAChC,EAAW,QAAQ,GACnB,EAAW,eAAe,GAIhC,OAHK,EAGE,EAAW,EAAE,QAAU,EAAE,QAFrB,CAGf,CAEA,IAAM,EAAQ,EACd,OAAO,EAAW,EAAM,QAAU,EAAM,OAC5C,CASA,aAAwB,GAAqC,CACzD,EAAE,eAAe,EAEjB,KAAK,iBAAmB,KAAK,qBAAqB,CAAC,EACnD,KAAK,iBAAmB,KAAK,gBAE7B,EAAM,oBAAoB,KAAM,YAAe,KAAK,WAAW,EAC/D,EAAM,oBAAoB,KAAM,UAAe,KAAK,UAAU,EAC9D,EAAM,oBAAoB,KAAM,YAAe,KAAK,WAAW,EAC/D,EAAM,oBAAoB,KAAM,WAAe,KAAK,UAAU,EAC9D,EAAM,oBAAoB,KAAM,cAAe,KAAK,UAAU,EAI9D,EAAI,KAAK,MAAM,EAAI,OAAO,QAAQ,EAAG,CAAE,MAAO,CAAE,cAAe,MAAO,CAAE,CAAC,CAC7E,EASA,YAAuB,GAAqD,CACxE,IAAM,EAAc,KAAK,eAAe,EAClC,EAAc,KAAK,IAAI,EAAG,KAAK,aAAe,KAAK,aAAa,EAChE,EAAc,KAAK,IAAI,EAAG,EAAc,KAAK,UAAU,EAE7D,GAAI,GAAY,EACZ,MAAO,GAIX,IAAM,GADc,KAAK,qBAAqB,CAAC,EAAI,KAAK,kBAC3B,EAAY,EACnC,EAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAW,KAAK,iBAAmB,CAAW,CAAC,EAIxF,OAFA,KAAK,KAAK,SAAU,CAAW,EAExB,EACX,EAQA,WAAsB,IAClB,EAAM,uBAAuB,KAAM,YAAe,KAAK,WAAW,EAClE,EAAM,uBAAuB,KAAM,UAAe,KAAK,UAAU,EACjE,EAAM,uBAAuB,KAAM,YAAe,KAAK,WAAW,EAClE,EAAM,uBAAuB,KAAM,WAAe,KAAK,UAAU,EACjE,EAAM,uBAAuB,KAAM,cAAe,KAAK,UAAU,EAGjE,EAAI,KAAK,MAAM,EAAI,OAAO,QAAQ,EAAG,CAAE,MAAO,CAAE,cAAe,EAAG,CAAE,CAAC,EAE9D,IAYX,cAAyB,GAAqC,CAC1D,EAAE,eAAe,EAEjB,IAAM,EAAW,KAAK,WAAW,EAC7B,EAEE,EAAa,EACnB,GAAI,EAAW,UAAY,IAAA,GAAW,CAClC,IAAM,EAAI,EAAW,QAAQ,OAAS,EAChC,EAAW,QAAQ,GACnB,EAAW,eAAe,GAC1B,EAAK,KAAK,WAAW,EAC3B,GAAI,CAAC,GAAK,CAAC,EACP,OAMJ,IAAM,EAAS,EAAI,OAAO,gBAAgB,IAAI,EACxC,EAAS,KAAK,cAAc,EAClC,EAAQ,EACF,EAAE,QAAU,EAAK,IAAM,EAAO,IAC9B,EAAE,QAAU,EAAK,KAAO,EAAO,IACzC,KAAO,CACH,IAAM,EAAQ,EACd,EAAQ,EAAW,EAAM,QAAU,EAAM,OAC7C,CAMA,IAAM,EAAS,KAAK,QAAQ,EACtB,EAAS,KAAK,eAAe,EAC7B,EAAS,EAAK,OAAS,EAAK,OAClC,GAAI,KAAK,iBAAmB,EAAQ,GAAU,GAAS,EAAQ,GAC3D,OAOJ,IAAM,EAFc,EAAQ,EACR,KAAK,UAAY,KAAK,WAAa,EACR,GAAK,EAC9C,EAAc,KAAK,IAAI,EAAG,KAAK,aAAe,KAAK,aAAa,EAChE,EAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAW,KAAK,gBAAkB,EAAY,KAAK,aAAa,CAAC,EAE1G,KAAK,KAAK,SAAU,CAAW,CACnC,CACJ,EAEM,EAAoB,EAAS,SAAS"}
@@ -1,2 +0,0 @@
1
- import{t as e}from"./DOM-Ba8YRVel.js";import{i as t,n}from"./Position-PBVBW1JS.js";import{S as r,h as i,r as a}from"./Component-w65rPK75.js";import{t as o}from"./Glyph-DeJF_zsR.js";import{t as s}from"./Button-T16zSZip.js";import{xmark as c}from"./glyphs/solid/xmark.es.js";var ToggleButton=class extends s{get selectedStyleRule(){return this._selectedStyleRule??=this.createStyleRule(`.selected:not(:hover)`)}constructor(e,t,n){super(e,void 0,n),this.selectedStyleRule.set(`boxShadow`,`var(--ts-ui-toggle-selected-shadow, 2px 2px 1px inset grey)`),this.selectedStyleRule.set(`backgroundColor`,`var(--ts-ui-toggle-selected-bg, rgb(200, 200, 200))`),this.selectedStyleRule.set(`backgroundImage`,`var(--ts-ui-toggle-selected-bg, none)`),r.addListener(this,`click`,()=>this.onAction()),t&&this.applyOptions(t),this.applyListeners(t?.listeners)}applyOptions(e){return super.applyOptions(e),e.selected!==void 0&&this.setSelected(e.selected),this.isFlat()&&(this.setSelectedShadow(`var(--ts-ui-button-flat-pressed-shadow, inset 1px 1px 3px rgba(0, 0, 0, 0.25))`),this.setSelectedBackgroundColor(`var(--ts-ui-button-flat-pressed-bg, rgba(0, 0, 0, 0.10))`)),this}on(e,t){return r.addListener(this,`change`,t),this}off(e,t){return r.removeListener(this,`change`,t),this}isSelected(){return this._options.selected??!1}setSelected(t){this._options.selected=t,this.getAria().setPressed(t);let n=this.getElement();return n&&e.sink.apply(n,{toggleClass:{selected:t}}),this}setSelectedBackgroundColor(e){return this.selectedStyleRule.set(`backgroundColor`,e),this}setSelectedBackgroundImage(e){return this.selectedStyleRule.set(`backgroundImage`,e),this}setSelectedShadow(e){return this.selectedStyleRule.set(`boxShadow`,e),this}setSelectedBorder(e){let n=typeof e==`string`?{border:e}:e;return this.selectedStyleRule.setMany(t(n)),this}setFlat(e){return super.setFlat(e),this.isFlat()?(this.setSelectedShadow(`var(--ts-ui-button-flat-pressed-shadow, inset 1px 1px 3px rgba(0, 0, 0, 0.25))`),this.setSelectedBackgroundColor(`var(--ts-ui-button-flat-pressed-bg, rgba(0, 0, 0, 0.10))`)):(this.setSelectedShadow(`var(--ts-ui-toggle-selected-shadow, 2px 2px 1px inset grey)`),this.setSelectedBackgroundColor(`var(--ts-ui-toggle-selected-bg, rgb(200, 200, 200))`)),this}onAction(){this.setSelected(!this.isSelected()),r.fireEvent(this,`change`)}render(){let t=super.render();return e.sink.apply(t,{toggleClass:{selected:this.isSelected()}}),t}},l=i(ToggleButton);o.register(c);var u={preferredSize:{width:16,height:16},insets:new n(0,0,0,0),foregroundColor:`var(--ts-ui-close-button-fg, #555)`},TabCloseButton=class extends s{constructor(e,t){super(void 0,e,{...u,glyph:`xmark`,...t??{}})}},d=i(TabCloseButton),f={backgroundColor:`var(--ts-ui-tab-button-bg, #b8b8c3)`,backgroundImage:`var(--ts-ui-tab-button-bg, #b8b8c3)`,border:{borderTop:`var(--ts-ui-tab-button-border-top, var(--ts-ui-tab-button-border, none))`,borderRight:`var(--ts-ui-tab-button-border-right, var(--ts-ui-tab-button-border, none))`,borderBottom:`var(--ts-ui-tab-button-border-bottom, var(--ts-ui-tab-button-border, none))`,borderLeft:`var(--ts-ui-tab-button-border-left, var(--ts-ui-tab-button-border, none))`},borderRadius:void 0,shadow:void 0,hoverBackgroundColor:`var(--ts-ui-tab-button-hover-bg, #c4c4cf)`,hoverBackgroundImage:`var(--ts-ui-tab-button-hover-bg, #c4c4cf)`,hoverShadow:`none`},TabButton=class extends l{_closeable=!1;_closeButton=null;constructor(e,t,n){super(e,void 0,{...f,...n??{}}),this._closeable=t?.closeable??!1,t&&this.applyOptions(t),this.applyTabStyling(t),t?.glyph!==void 0&&this.setGlyph(t.glyph),this._closeable&&this.buildCloseButton(),this.applyListeners(t?.listeners)}destructor(){this._closeButton?.dispose(),super.destructor()}applyTabStyling(e){this.setHoverBorder(e?.hoverBorder??{borderTop:`var(--ts-ui-tab-button-hover-border-top, var(--ts-ui-tab-button-hover-border, none))`,borderRight:`var(--ts-ui-tab-button-hover-border-right, var(--ts-ui-tab-button-hover-border, none))`,borderBottom:`var(--ts-ui-tab-button-hover-border-bottom, var(--ts-ui-tab-button-hover-border, none))`,borderLeft:`var(--ts-ui-tab-button-hover-border-left, var(--ts-ui-tab-button-hover-border, none))`}),this.setSelectedBackgroundColor(`var(--ts-ui-tab-button-selected-bg, rgb(255, 255, 255))`),this.setSelectedBackgroundImage(`var(--ts-ui-tab-button-selected-bg, rgb(255, 255, 255))`),this.setSelectedShadow(`none`),this.setSelectedBorder({borderTop:`var(--ts-ui-tab-button-selected-border-top, var(--ts-ui-tab-button-selected-border, none))`,borderRight:`var(--ts-ui-tab-button-selected-border-right, var(--ts-ui-tab-button-selected-border, none))`,borderBottom:`var(--ts-ui-tab-button-selected-border-bottom, var(--ts-ui-tab-button-selected-border, none))`,borderLeft:`var(--ts-ui-tab-button-selected-border-left, var(--ts-ui-tab-button-selected-border, none))`})}buildCloseButton(){let t=new d;t.setBackgroundColor(`transparent`),t.setBackgroundImage(`none`),t.setHoverBackgroundColor(`var(--ts-ui-tab-close-hover-bg, rgba(0, 0, 0, 0.12))`),t.setHoverBackgroundImage(`none`),t.setHoverShadow(`none`),t.setBorderRadius(`3px`),t.clearBorder(),t.clearShadow(),t.setZIndex(1);let n=a.getResolvedScale();t.setWidth(n.tabClose),t.setHeight(n.tabClose),t.pinGlyphSize(n.tabCloseGlyph),e.sink.appendChild(this.getElement(!0),t.getElement(!0)),this._closeButton=t}getCloseButton(){return this._closeButton}isCloseable(){return this._closeable}},p=i(TabButton);export{d as n,l as r,p as t};
2
- //# sourceMappingURL=TabButton-DNo4ZgV8.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TabButton-DNo4ZgV8.js","names":[],"sources":["../../src/typescript/lib/component/button/ToggleButton.ts","../../src/typescript/lib/component/button/TabCloseButton.ts","../../src/typescript/lib/component/button/TabButton.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { BorderOptions, borderToStyle } from \"~/primitive/Border.js\";\nimport { Button, ButtonOptions, ClickListener } from \"~/component/button/Button.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link ToggleButton}.\n *\n * @category Components\n */\nexport interface ToggleButtonOptions extends ButtonOptions {\n selected?: boolean;\n}\n\n/**\n * A toggle button component that switches between selected and unselected states on each click.\n *\n * Maintains a separate CSS rule for the `.selected` class to allow independent styling of\n * the active state, and fires a 'change' event whenever the selection state changes.\n *\n * @category Components\n */\nclass ToggleButton extends Button<ToggleButtonOptions> {\n\n // Lazy `.selected` rule. The slot is a fast-path cache for the wrapper\n // returned by Component's `createStyleRule` builder, which dedupes by\n // selector suffix — see Button's `_pressedStyleRule` for the full\n // explanation.\n private declare _selectedStyleRule?: StyleRule;\n private get selectedStyleRule(): StyleRule {\n return this._selectedStyleRule ??= this.createStyleRule(\".selected:not(:hover)\");\n }\n\n constructor(text: string, options?: ToggleButtonOptions, subclassDefaults?: Partial<ToggleButtonOptions>) {\n // Button is a children-build class: it builds its inner text/HBox row\n // in its constructor body. We forward the positional `text` to super\n // (no options), queue the selected-state styles into the lazy rule,\n // then dispatch the consumer options through `applyOptions` at the\n // tail so Button's own option-backed setters run after children exist.\n // `subclassDefaults` is forwarded regardless, since it only seeds the\n // `_defaultOptions` fallback bag — a separate, independent parameter\n // from `options` — so a subclass's own defaults (e.g. `TabButton`'s\n // tab fill) still layer in even though `options` waits.\n super(text, undefined, subclassDefaults);\n\n this.selectedStyleRule.set(\"boxShadow\", \"var(--ts-ui-toggle-selected-shadow, 2px 2px 1px inset grey)\");\n this.selectedStyleRule.set(\"backgroundColor\", \"var(--ts-ui-toggle-selected-bg, rgb(200, 200, 200))\");\n this.selectedStyleRule.set(\"backgroundImage\", \"var(--ts-ui-toggle-selected-bg, none)\");\n\n Event.addListener(this, \"click\", () => this.onAction());\n\n if (options) {\n this.applyOptions(options);\n }\n\n // Button's constructor skipped the listener bag (it wires only a plain\n // Button); wire it here, after super() and applyOptions, as the leaf.\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Applies a {@link ToggleButtonOptions} bag, dispatching the toggle's\n * `selected` state after inherited Button/Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: ToggleButtonOptions): this {\n super.applyOptions(options);\n\n if (options.selected !== undefined) {\n this.setSelected(options.selected);\n }\n\n // Button's flat branch runs in `applyChromeOptions` and re-points only\n // Button's own hover/pressed rules — it never touches this toggle's\n // `.selected` rule. Re-point it here so a `new ToggleButton(text,\n // { flat: true })` reads depressed when selected, mirroring `setFlat`.\n if (this.isFlat()) {\n this.setSelectedShadow(\"var(--ts-ui-button-flat-pressed-shadow, inset 1px 1px 3px rgba(0, 0, 0, 0.25))\");\n this.setSelectedBackgroundColor(\"var(--ts-ui-button-flat-pressed-bg, rgba(0, 0, 0, 0.10))\");\n }\n\n return this;\n }\n\n /**\n * Registers a listener for this toggle button's `\"action\"` event — fired\n * when the toggle state changes. Overrides the inherited\n * [`Button`](/api/component/button/classes/Button) `on` so `\"action\"`\n * routes to the DOM `\"change\"` event (the toggle) rather than the base\n * `\"click\"`. A typed semantic shorthand over {@link Event.addListener}.\n *\n * @param event - The event name. Only `\"action\"` is accepted.\n * @param listener - The callback to invoke when the toggle state changes.\n *\n * @returns This button, for method chaining.\n */\n on(event: \"action\", listener: ClickListener): this;\n on(_event: \"action\", listener: ClickListener): this {\n Event.addListener(this, \"change\", listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"action\"` listener. The exact\n * callback reference must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This button, for method chaining.\n */\n off(event: \"action\", listener: ClickListener): this;\n off(_event: \"action\", listener: ClickListener): this {\n Event.removeListener(this, \"change\", listener);\n\n return this;\n }\n\n /**\n * Returns whether the toggle button is currently in the selected state.\n *\n * @returns True if the button is currently selected.\n */\n isSelected(): boolean {\n return this._options.selected ?? false;\n }\n\n /**\n * Sets the selected state, toggles the 'selected' CSS class, and updates `aria-pressed`.\n *\n * @param value - True to select the button, false to deselect it.\n */\n setSelected(value: boolean) : this {\n this._options.selected = value;\n\n this.getAria().setPressed(value);\n\n let element = this.getElement();\n if (element) {\n DOM.sink.apply(element, { toggleClass: { selected: value } });\n }\n\n return this;\n }\n\n /**\n * Sets the background color for the `.selected` CSS rule, overriding the\n * default `--ts-ui-toggle-selected-bg` token for this instance.\n *\n * @param backgroundColor - A CSS color string.\n *\n * @returns This button, for method chaining.\n */\n setSelectedBackgroundColor(backgroundColor: string): this {\n this.selectedStyleRule.set(\"backgroundColor\", backgroundColor);\n\n return this;\n }\n\n /**\n * Sets the background image for the `.selected` CSS rule. Pairs with\n * {@link setSelectedBackgroundColor} so a plain colour drops out (invalid\n * as an image) and a gradient wins, matching the framework's background\n * colour/image auto-routing.\n *\n * @param backgroundImage - A CSS background-image string.\n *\n * @returns This button, for method chaining.\n */\n setSelectedBackgroundImage(backgroundImage: string): this {\n this.selectedStyleRule.set(\"backgroundImage\", backgroundImage);\n\n return this;\n }\n\n /**\n * Sets the box-shadow for the `.selected` CSS rule, overriding the default\n * `--ts-ui-toggle-selected-shadow` token for this instance.\n *\n * @param shadow - A CSS box-shadow string (e.g. `\"none\"`).\n *\n * @returns This button, for method chaining.\n */\n setSelectedShadow(shadow: string): this {\n this.selectedStyleRule.set(\"boxShadow\", shadow);\n\n return this;\n }\n\n /**\n * Sets the border for the `.selected` CSS rule, overriding the default\n * selected-state border for this instance. Accepts either a {@link BorderOptions}\n * bag or a CSS `border` shorthand string (sugar for `{ border: <string> }`,\n * e.g. `\"1px solid rgb(...)\"` or `\"none\"`).\n *\n * @param options - Border configuration, or a CSS `border` shorthand value.\n *\n * @returns This button, for method chaining.\n */\n setSelectedBorder(options: BorderOptions | string): this {\n const border = typeof options === \"string\" ? { border: options } : options;\n this.selectedStyleRule.setMany(borderToStyle(border));\n\n return this;\n }\n\n /**\n * Toggles the flat appearance, then re-points the `.selected:not(:hover)`\n * rule so a toggled-on flat button reads as depressed with the same sunken\n * treatment Button's flat `:active` state uses. Flattening points the\n * selected shadow/background at the `--ts-ui-button-flat-pressed-*` tokens;\n * un-flattening restores the default `--ts-ui-toggle-selected-*` tokens.\n *\n * @param value - The new flat state.\n *\n * @returns This button, for method chaining.\n */\n setFlat(value: boolean): this {\n super.setFlat(value);\n\n // Read the resolved state, not the requested `value`: `super.setFlat`\n // can refuse the flip (chromeless wins) or no-op on an unchanged value,\n // and the selected rule must track whatever flat state actually holds.\n if (this.isFlat()) {\n this.setSelectedShadow(\"var(--ts-ui-button-flat-pressed-shadow, inset 1px 1px 3px rgba(0, 0, 0, 0.25))\");\n this.setSelectedBackgroundColor(\"var(--ts-ui-button-flat-pressed-bg, rgba(0, 0, 0, 0.10))\");\n } else {\n this.setSelectedShadow(\"var(--ts-ui-toggle-selected-shadow, 2px 2px 1px inset grey)\");\n this.setSelectedBackgroundColor(\"var(--ts-ui-toggle-selected-bg, rgb(200, 200, 200))\");\n }\n\n return this;\n }\n\n /**\n * Toggles the selected state and fires a 'change' event when the button is clicked.\n */\n private onAction() {\n this.setSelected(!this.isSelected());\n\n Event.fireEvent(this, \"change\");\n }\n\n /**\n * Renders the button element and applies the 'selected' class if currently selected.\n *\n * @returns The created button element with the 'selected' class applied if appropriate.\n */\n render() {\n let element = super.render();\n DOM.sink.apply(element, { toggleClass: { selected: this.isSelected() } });\n return element;\n }\n}\n\nconst ToggleButtonCallable = callable(ToggleButton);\ntype ToggleButtonCallable = ToggleButton;\nexport {\n ToggleButton as _ToggleButton,\n ToggleButtonCallable as ToggleButton\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button, ButtonOptions } from \"~/component/button/Button.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { xmark } from \"~/glyphs/solid/xmark.js\";\n\nGlyph.register(xmark);\n\n/**\n * Construction-time options for {@link TabCloseButton}.\n *\n * @category Components\n */\nexport interface TabCloseButtonOptions extends ButtonOptions {\n}\n\n/**\n * User-overridable visual defaults forwarded to `super` via the options bag.\n * Layered on top of [`Button`](/api/component/button/classes/Button)'s own\n * defaults, then overlaid with consumer options before the seed `glyph`\n * pins the close icon.\n */\nconst _defaultTabCloseButtonOptions: Partial<TabCloseButtonOptions> = {\n preferredSize: { width: 16, height: 16 },\n insets: new Insets(0, 0, 0, 0),\n foregroundColor: \"var(--ts-ui-close-button-fg, #555)\",\n};\n\n/**\n * A compact close button displaying the `xmark` glyph, sized to sit flush inside a tab header.\n *\n * @category Components\n */\nclass TabCloseButton extends Button<TabCloseButtonOptions> {\n\n /**\n * Creates a TabCloseButton seeded with the `xmark` glyph and sized for use in a tab toolbar.\n */\n constructor(options?: TabCloseButtonOptions, subclassDefaults?: Partial<TabCloseButtonOptions>) {\n // The seed `glyph` is in the defaults bag — a caller-supplied\n // `options.glyph` still wins because Button resolves the effective\n // glyph as `options.glyph ?? _defaultOptions.glyph` at construction.\n super(undefined, options, {\n ..._defaultTabCloseButtonOptions,\n glyph: \"xmark\",\n ...(subclassDefaults ?? {}),\n });\n }\n}\n\nconst TabCloseButtonCallable = callable(TabCloseButton);\ntype TabCloseButtonCallable = TabCloseButton;\nexport {\n TabCloseButton as _TabCloseButton,\n TabCloseButtonCallable as TabCloseButton\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { ToggleButton, ToggleButtonOptions } from \"~/component/button/ToggleButton.js\";\nimport { TabCloseButton } from \"~/component/button/TabCloseButton.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link TabButton}.\n *\n * `text` (the label), `glyph`, and `selected` are inherited from\n * {@link ToggleButtonOptions}.\n *\n * @category Components\n */\nexport interface TabButtonOptions extends ToggleButtonOptions {\n /** When true, builds and overlays the close (✕) affordance. Default false. */\n closeable?: boolean;\n}\n\n/**\n * User-overridable default fill for the unselected state; a caller-supplied\n * `backgroundColor`/`backgroundImage` wins. Reaches `Button` through\n * `ToggleButton`'s `subclassDefaults` parameter (see `ToggleButton.ts`).\n */\nconst _defaultTabButtonOptions: Partial<TabButtonOptions> = {\n backgroundColor: \"var(--ts-ui-tab-button-bg, #b8b8c3)\",\n backgroundImage: \"var(--ts-ui-tab-button-bg, #b8b8c3)\",\n border: {\n borderTop: \"var(--ts-ui-tab-button-border-top, var(--ts-ui-tab-button-border, none))\",\n borderRight: \"var(--ts-ui-tab-button-border-right, var(--ts-ui-tab-button-border, none))\",\n borderBottom: \"var(--ts-ui-tab-button-border-bottom, var(--ts-ui-tab-button-border, none))\",\n borderLeft: \"var(--ts-ui-tab-button-border-left, var(--ts-ui-tab-button-border, none))\",\n },\n // Explicit `undefined` keys — not omitted — so these two win over Button's\n // own non-empty `_defaultButtonOptions.borderRadius`/`.shadow` in the\n // spread merge at ComponentDefaults.ts's resolveClassDefaults. Omitting\n // the keys would let Button's rounded/shadowed look leak through once the\n // unconditional `clearBorderRadius()`/`clearShadow()` calls below are\n // deleted.\n borderRadius: undefined,\n shadow: undefined,\n hoverBackgroundColor: \"var(--ts-ui-tab-button-hover-bg, #c4c4cf)\",\n hoverBackgroundImage: \"var(--ts-ui-tab-button-hover-bg, #c4c4cf)\",\n hoverShadow: \"none\",\n};\n\n/**\n * A tab-styled toggle button: a {@link ToggleButton} that paints its own\n * unselected/hover/selected fill from the `--ts-ui-tab-button-*` theme tokens\n * and optionally overlays a close (✕) affordance built from\n * {@link TabCloseButton}.\n *\n * `TabButton` is a [`TabBar`](/api/component/container/classes/TabBar)\n * collaborator, not a general-purpose button. It owns the per-tab styling and\n * close-button *construction* that the strip previously replayed inline for\n * every cell; the strip keeps every geometry concern — insets, writing mode,\n * min/max clamp, and the per-layout re-pin of the overlaid close button (which\n * is positioned via the exposed {@link getCloseButton}). It adds no selection\n * or roving-tab-index logic of its own — those slot through the inherited\n * `ToggleButton` surface.\n *\n * @category Components\n */\nclass TabButton extends ToggleButton {\n\n // Whether this tab carries a close affordance. Construction-time only (the\n // close button is built or not when the tab is created), so it is set once\n // in the constructor and never via an option-dispatched setter — hence a\n // plain field rather than a `declare`d cascade-written one.\n private _closeable: boolean = false;\n\n // The overlaid close button, or null when the tab is not closeable. Built\n // in the constructor and raw-appended onto this button's own element (the\n // standard overlay pattern); TabBar reads it to wire its \"action\" and to\n // position/re-pin it each layout.\n private _closeButton: TabCloseButton | null = null;\n\n /**\n * Builds a tab-styled toggle button with the given label, applying the\n * `--ts-ui-tab-button-*` fill/border/hover/selected styling and, when\n * `closeable` is set, building and overlaying the close affordance.\n *\n * @param text - The visible label for the tab.\n * @param options - Tab button options; `closeable` gates the close button.\n * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(text: string, options?: TabButtonOptions, subclassDefaults?: Partial<TabButtonOptions>) {\n // ToggleButton (via Button) builds its inner text/HBox content row in\n // its constructor body, so forward the positional `text` to super,\n // ... (options still isn't forwarded, only the defaults bag is); the\n // styling setters below then fire on a fully-built button.\n super(text, undefined, { ..._defaultTabButtonOptions, ...(subclassDefaults ?? {}) });\n\n this._closeable = options?.closeable ?? false;\n\n if (options) {\n this.applyOptions(options);\n }\n\n // Paint the tab styling AFTER applyOptions, not before: the cascade\n // re-applies Button's chrome defaults from _defaultButtonOptions (the\n // \"2px ridge\" border, the 4px border-radius, the button drop-shadow), so\n // tab styling applied earlier would be clobbered back to a rounded,\n // ridged, shadowed button. Running it last lets the flat tab tokens and\n // the cleared radius/shadow win — matching the legacy createBarEntry,\n // which styled a no-options ToggleButton with nothing applied afterward.\n this.applyTabStyling(options);\n\n // Button's constructor late-dispatches `setGlyph` from the options bag\n // once, before this subclass's `applyOptions` runs; a glyph passed to a\n // ToggleButton subclass is therefore only pure-written to `_options` and\n // never rendered. Dispatch it explicitly here, mirroring how TabBar\n // called `setGlyph(constraints.glyph)` after construction.\n if (options?.glyph !== undefined) {\n this.setGlyph(options.glyph);\n }\n\n if (this._closeable) {\n this.buildCloseButton();\n }\n\n // ToggleButton's constructor wired the listener bag from the options it\n // received — but `super(text, ...)` above passed none, so it wired nothing.\n // Wire the bag here, as the leaf, after super() and applyOptions, so a\n // `new TabButton(name, { listeners: { action } })` is not silently\n // dropped (ARCHITECTURE.md, Event handling).\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Disposes the overlaid close button, then runs the inherited teardown.\n * `_closeButton` is raw-appended onto this button's own element rather than\n * registered via `addComponent` (see `buildCloseButton`'s doc comment), so\n * `super.destructor()`'s child recursion cannot reach it.\n */\n protected destructor(): void {\n this._closeButton?.dispose();\n\n super.destructor();\n }\n\n /**\n * Paints the tab's selected states from the `--ts-ui-tab-button-*` theme\n * tokens. Runs after `applyOptions()` so these overrides win over\n * `Button`'s inherited chrome defaults — see the call site's comment.\n *\n * The resting fill, border, radius, shadow, and the three hover\n * colour/shadow fields all resolve from `_defaultTabButtonOptions` (top of\n * this file), which layers through `ToggleButton`'s `subclassDefaults`\n * forwarding — `Button`'s own chrome dispatch folds each of those\n * correctly, so nothing needs reasserting here. Only `hoverBorder` is\n * reasserted below: `getHoverBorder()` has no `_defaultOptions` fold (it\n * returns a private field), so a bag entry for it would be dead data.\n *\n * @param options - The options this `TabButton` was constructed with, so\n * the hover-border fold below still reads an explicit caller value.\n */\n private applyTabStyling(options?: TabButtonOptions): void {\n this.setHoverBorder(options?.hoverBorder ?? {\n borderTop: \"var(--ts-ui-tab-button-hover-border-top, var(--ts-ui-tab-button-hover-border, none))\",\n borderRight: \"var(--ts-ui-tab-button-hover-border-right, var(--ts-ui-tab-button-hover-border, none))\",\n borderBottom: \"var(--ts-ui-tab-button-hover-border-bottom, var(--ts-ui-tab-button-hover-border, none))\",\n borderLeft: \"var(--ts-ui-tab-button-hover-border-left, var(--ts-ui-tab-button-hover-border, none))\",\n });\n\n // Selected (active) state.\n this.setSelectedBackgroundColor(\"var(--ts-ui-tab-button-selected-bg, rgb(255, 255, 255))\");\n this.setSelectedBackgroundImage(\"var(--ts-ui-tab-button-selected-bg, rgb(255, 255, 255))\");\n this.setSelectedShadow(\"none\");\n this.setSelectedBorder({\n borderTop: \"var(--ts-ui-tab-button-selected-border-top, var(--ts-ui-tab-button-selected-border, none))\",\n borderRight: \"var(--ts-ui-tab-button-selected-border-right, var(--ts-ui-tab-button-selected-border, none))\",\n borderBottom: \"var(--ts-ui-tab-button-selected-border-bottom, var(--ts-ui-tab-button-selected-border, none))\",\n borderLeft: \"var(--ts-ui-tab-button-selected-border-left, var(--ts-ui-tab-button-selected-border, none))\",\n });\n }\n\n /**\n * Builds the close (✕) affordance and overlays its element on this button's\n * own element. Transparent so the tab's background shows through, with a\n * faint rounded hover tint giving the ✕ its affordance. The ✕ is overlaid\n * (raw-appended) rather than enrolled in a layout so it floats at the corner\n * of the tab; TabBar pins its precise position each layout.\n */\n private buildCloseButton(): void {\n const closeButton = new TabCloseButton();\n\n // Transparent so the tab's own background shows through; a faint rounded\n // tint on hover gives the ✕ its affordance. `--ts-ui-tab-close-hover-bg`\n // has no themeToVars entry, so the inline rgba fallback is the actual\n // source of the value — keep it verbatim.\n closeButton.setBackgroundColor(\"transparent\");\n closeButton.setBackgroundImage(\"none\");\n closeButton.setHoverBackgroundColor(\"var(--ts-ui-tab-close-hover-bg, rgba(0, 0, 0, 0.12))\");\n closeButton.setHoverBackgroundImage(\"none\");\n closeButton.setHoverShadow(\"none\");\n closeButton.setBorderRadius(\"3px\");\n closeButton.clearBorder();\n closeButton.clearShadow();\n closeButton.setZIndex(1);\n\n // Shrink the ✕ glyph to roughly half the close-button hit box, centred —\n // the button stays the click target while the mark reads lighter. Pin it\n // (Glyph.setPreferredSize locks min/max too) so the line-height sync\n // never re-tracks the glyph to the title line height; TabBar's\n // per-layout re-pin keeps it at the base-scaled size.\n // Size the box here, not only in TabBar's per-layout pass. The ✕ is\n // raw-appended rather than enrolled in a layout, so nothing else gives\n // it a width until that pass runs — and a Button whose own width is\n // still unresolved centres its content at 0,0 and keeps it there, which\n // is what left the ✕ jammed into the corner on every tab that existed\n // at the strip's first layout.\n const closeScale = ThemeManager.getResolvedScale();\n\n closeButton.setWidth(closeScale.tabClose);\n closeButton.setHeight(closeScale.tabClose);\n closeButton.pinGlyphSize(closeScale.tabCloseGlyph);\n\n // Overlay it on this button's own element rather than enrolling it in a\n // layout (which would stretch it over the whole tab); TabBar pins it to\n // the right edge each layout.\n DOM.sink.appendChild(this.getElement(true)!, closeButton.getElement(true)!);\n\n this._closeButton = closeButton;\n }\n\n /**\n * Returns the overlaid close button, or `null` when this tab is not\n * closeable. TabBar wires its `\"action\"` event and positions/re-pins it on\n * each layout pass.\n *\n * @returns The close button, or `null`.\n */\n getCloseButton(): TabCloseButton | null {\n return this._closeButton;\n }\n\n /**\n * Returns whether this tab carries a close affordance. Construction-time\n * only — there is no runtime toggle.\n *\n * @returns True when the tab was built closeable.\n */\n isCloseable(): boolean {\n return this._closeable;\n }\n}\n\nconst TabButtonCallable = callable(TabButton);\ntype TabButtonCallable = TabButton;\nexport {\n TabButton as _TabButton,\n TabButtonCallable as TabButton\n};\n"],"mappings":"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"}