@jimka/typescript-ui 0.2.0 → 0.4.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 (251) hide show
  1. package/README.md +4 -0
  2. package/THIRD-PARTY-NOTICES.md +7 -3
  3. package/dist/lib/{AbstractBooleanInput-BGesvIZy.js → AbstractBooleanInput-DSwKBAic.js} +2 -2
  4. package/dist/lib/{AbstractBooleanInput-BGesvIZy.js.map → AbstractBooleanInput-DSwKBAic.js.map} +1 -1
  5. package/dist/lib/{AbstractInput-BxOtFrER.js → AbstractInput-BmWGJTs1.js} +2 -2
  6. package/dist/lib/{AbstractInput-BxOtFrER.js.map → AbstractInput-BmWGJTs1.js.map} +1 -1
  7. package/dist/lib/AnchorConstraints-CVrMP9rz.js +2 -0
  8. package/dist/lib/AnchorConstraints-CVrMP9rz.js.map +1 -0
  9. package/dist/lib/{AnimatedDropdown-CZQEqQi-.js → AnimatedDropdown-DKas40B-.js} +2 -2
  10. package/dist/lib/{AnimatedDropdown-CZQEqQi-.js.map → AnimatedDropdown-DKas40B-.js.map} +1 -1
  11. package/dist/lib/Border-Bz29z-_7.js +2 -0
  12. package/dist/lib/Border-Bz29z-_7.js.map +1 -0
  13. package/dist/lib/Button-bjpWruNc.js +2 -0
  14. package/dist/lib/{Button-U1rsP4v8.js.map → Button-bjpWruNc.js.map} +1 -1
  15. package/dist/lib/{Card-BVwOS8AG.js → Card-BdfbvOD_.js} +2 -2
  16. package/dist/lib/{Card-BVwOS8AG.js.map → Card-BdfbvOD_.js.map} +1 -1
  17. package/dist/lib/ComboBox-CBw2VLLN.js +2 -0
  18. package/dist/lib/ComboBox-CBw2VLLN.js.map +1 -0
  19. package/dist/lib/Component-DHqrPwAw.js +2 -0
  20. package/dist/lib/Component-DHqrPwAw.js.map +1 -0
  21. package/dist/lib/Container-CAQpj4yb.js +2 -0
  22. package/dist/lib/{Container-D5H8VxJK.js.map → Container-CAQpj4yb.js.map} +1 -1
  23. package/dist/lib/DOM-67StIm5G.js +2 -0
  24. package/dist/lib/DOM-67StIm5G.js.map +1 -0
  25. package/dist/lib/DragChain-CYGuT7OA.js +2 -0
  26. package/dist/lib/DragChain-CYGuT7OA.js.map +1 -0
  27. package/dist/lib/DragManager-CHWCe3-8.js +2 -0
  28. package/dist/lib/DragManager-CHWCe3-8.js.map +1 -0
  29. package/dist/lib/{FieldDecorator-CJs9gwqH.js → FieldDecorator-CgAGcqCk.js} +2 -2
  30. package/dist/lib/{FieldDecorator-CJs9gwqH.js.map → FieldDecorator-CgAGcqCk.js.map} +1 -1
  31. package/dist/lib/{Fit-DjRsiwlg.js → Fit-DIYt2aQB.js} +2 -2
  32. package/dist/lib/{Fit-DjRsiwlg.js.map → Fit-DIYt2aQB.js.map} +1 -1
  33. package/dist/lib/Glyph-CeAIWyPB.js +2 -0
  34. package/dist/lib/Glyph-CeAIWyPB.js.map +1 -0
  35. package/dist/lib/{Grid-DdL9TVeJ.js → Grid-ViA0joow.js} +2 -2
  36. package/dist/lib/{Grid-DdL9TVeJ.js.map → Grid-ViA0joow.js.map} +1 -1
  37. package/dist/lib/GridConstraints-B1rxCw9B.js +2 -0
  38. package/dist/lib/GridConstraints-B1rxCw9B.js.map +1 -0
  39. package/dist/lib/Header-mKaqgfat.js +2 -0
  40. package/dist/lib/Header-mKaqgfat.js.map +1 -0
  41. package/dist/lib/IconText-BqwXA82-.js +2 -0
  42. package/dist/lib/IconText-BqwXA82-.js.map +1 -0
  43. package/dist/lib/{LayoutSerialization-BT66TY83.js → LayoutSerialization-D82UtnDD.js} +2 -2
  44. package/dist/lib/{LayoutSerialization-BT66TY83.js.map → LayoutSerialization-D82UtnDD.js.map} +1 -1
  45. package/dist/lib/LayoutSizes-DG9aW9k6.js +2 -0
  46. package/dist/lib/LayoutSizes-DG9aW9k6.js.map +1 -0
  47. package/dist/lib/List-CUntS8ux.js +2 -0
  48. package/dist/lib/List-CUntS8ux.js.map +1 -0
  49. package/dist/lib/MemoryStore-DT6iWWco.js +2 -0
  50. package/dist/lib/MemoryStore-DT6iWWco.js.map +1 -0
  51. package/dist/lib/Menu-B_-SQj72.js +2 -0
  52. package/dist/lib/Menu-B_-SQj72.js.map +1 -0
  53. package/dist/lib/{MenuButton-VJ5uC3c1.js → MenuButton-C79l8Ysf.js} +2 -2
  54. package/dist/lib/{MenuButton-VJ5uC3c1.js.map → MenuButton-C79l8Ysf.js.map} +1 -1
  55. package/dist/lib/{Panel-DzWdYwq1.js → Panel-D2X33qR6.js} +2 -2
  56. package/dist/lib/{Panel-DzWdYwq1.js.map → Panel-D2X33qR6.js.map} +1 -1
  57. package/dist/lib/Position-Ck_KD4cR.js +2 -0
  58. package/dist/lib/Position-Ck_KD4cR.js.map +1 -0
  59. package/dist/lib/{ProgressSpinner-zJKtGyNJ.js → ProgressSpinner-DCyq02pd.js} +2 -2
  60. package/dist/lib/{ProgressSpinner-zJKtGyNJ.js.map → ProgressSpinner-DCyq02pd.js.map} +1 -1
  61. package/dist/lib/{RadioButton-CYSSv4nV.js → RadioButton-BJIAQkII.js} +2 -2
  62. package/dist/lib/{RadioButton-CYSSv4nV.js.map → RadioButton-BJIAQkII.js.map} +1 -1
  63. package/dist/lib/{RovingTabIndex-BSZ31Vp5.js → RovingTabIndex-DFJyoveq.js} +2 -2
  64. package/dist/lib/{RovingTabIndex-BSZ31Vp5.js.map → RovingTabIndex-DFJyoveq.js.map} +1 -1
  65. package/dist/lib/Scrollbar-CSFK-Y57.js +2 -0
  66. package/dist/lib/Scrollbar-CSFK-Y57.js.map +1 -0
  67. package/dist/lib/Slider-C-kQQYCj.js +2 -0
  68. package/dist/lib/Slider-C-kQQYCj.js.map +1 -0
  69. package/dist/lib/{Spacer-D9NGW4QG.js → Spacer-D159iJy9.js} +2 -2
  70. package/dist/lib/{Spacer-D9NGW4QG.js.map → Spacer-D159iJy9.js.map} +1 -1
  71. package/dist/lib/TabButton-CHkXBgCj.js +2 -0
  72. package/dist/lib/TabButton-CHkXBgCj.js.map +1 -0
  73. package/dist/lib/Text-D5pg7taZ.js +2 -0
  74. package/dist/lib/Text-D5pg7taZ.js.map +1 -0
  75. package/dist/lib/Tooltip-DzK6LxUt.js +3 -0
  76. package/dist/lib/{Tooltip-tkXDk5Bo.js.map → Tooltip-DzK6LxUt.js.map} +1 -1
  77. package/dist/lib/VBox-BNegDPs_.js +2 -0
  78. package/dist/lib/VBox-BNegDPs_.js.map +1 -0
  79. package/dist/lib/VirtualScroller-zQr-vKrO.js +2 -0
  80. package/dist/lib/VirtualScroller-zQr-vKrO.js.map +1 -0
  81. package/dist/lib/component/button.es.js +1 -1
  82. package/dist/lib/component/chart.es.js +1 -1
  83. package/dist/lib/component/chart.es.js.map +1 -1
  84. package/dist/lib/component/container.es.js +1 -1
  85. package/dist/lib/component/container.es.js.map +1 -1
  86. package/dist/lib/component/diagram.es.js +1 -1
  87. package/dist/lib/component/diagram.es.js.map +1 -1
  88. package/dist/lib/component/display.es.js +1 -1
  89. package/dist/lib/component/display.es.js.map +1 -1
  90. package/dist/lib/component/editor.es.js +3 -3
  91. package/dist/lib/component/editor.es.js.map +1 -1
  92. package/dist/lib/component/input.es.js +1 -1
  93. package/dist/lib/component/input.es.js.map +1 -1
  94. package/dist/lib/component/list.es.js +1 -1
  95. package/dist/lib/component/list.es.js.map +1 -1
  96. package/dist/lib/component/menubar.es.js +1 -1
  97. package/dist/lib/component/menubar.es.js.map +1 -1
  98. package/dist/lib/component/table.es.js +2 -2
  99. package/dist/lib/component/table.es.js.map +1 -1
  100. package/dist/lib/component/tree.es.js +1 -1
  101. package/dist/lib/component/tree.es.js.map +1 -1
  102. package/dist/lib/core.es.js +1 -1
  103. package/dist/lib/core.es.js.map +1 -1
  104. package/dist/lib/data.es.js +1 -1
  105. package/dist/lib/data.es.js.map +1 -1
  106. package/dist/lib/glyphs/solid/index.es.js +1 -1
  107. package/dist/lib/layout.es.js +1 -1
  108. package/dist/lib/layout.es.js.map +1 -1
  109. package/dist/lib/overlay.es.js +1 -1
  110. package/dist/lib/overlay.es.js.map +1 -1
  111. package/dist/lib/primitive.es.js +1 -1
  112. package/dist/lib/router.es.js +1 -1
  113. package/dist/lib/router.es.js.map +1 -1
  114. package/dist/lib/selectionsEqual-4r5R7pTK.js +2 -0
  115. package/dist/lib/selectionsEqual-4r5R7pTK.js.map +1 -0
  116. package/dist/lib/types/component/button/TabCloseButton.d.ts +1 -1
  117. package/dist/lib/types/component/container/CollapseButton.d.ts +1 -1
  118. package/dist/lib/types/component/container/DialogBackdrop.d.ts +1 -1
  119. package/dist/lib/types/component/container/MenuItem.d.ts +4 -0
  120. package/dist/lib/types/component/container/MenuSeparator.d.ts +1 -1
  121. package/dist/lib/types/component/container/Scrollbar.d.ts +1 -0
  122. package/dist/lib/types/component/container/SplitGutter.d.ts +2 -2
  123. package/dist/lib/types/component/container/StatusBar.d.ts +1 -1
  124. package/dist/lib/types/component/container/VirtualScroller.d.ts +1 -0
  125. package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
  126. package/dist/lib/types/component/diagram/DiagramEdgeLayer.d.ts +14 -1
  127. package/dist/lib/types/component/diagram/DiagramModel.d.ts +1 -0
  128. package/dist/lib/types/component/diagram/DiagramNode.d.ts +5 -1
  129. package/dist/lib/types/component/diagram/DiagramView.d.ts +76 -5
  130. package/dist/lib/types/component/diagram/ElkLayoutEngine.d.ts +14 -1
  131. package/dist/lib/types/component/diagram/index.d.ts +2 -2
  132. package/dist/lib/types/component/display/Canvas.d.ts +8 -2
  133. package/dist/lib/types/component/display/Glyph.d.ts +2 -1
  134. package/dist/lib/types/component/display/Header.d.ts +1 -1
  135. package/dist/lib/types/component/display/IconLabel.d.ts +1 -1
  136. package/dist/lib/types/component/display/IconText.d.ts +1 -1
  137. package/dist/lib/types/component/display/Image.d.ts +1 -1
  138. package/dist/lib/types/component/display/Video.d.ts +1 -1
  139. package/dist/lib/types/component/display/WebGLCanvas.d.ts +8 -2
  140. package/dist/lib/types/component/editor/CodeEditor.d.ts +4 -3
  141. package/dist/lib/types/component/input/AbstractPickerField.d.ts +1 -1
  142. package/dist/lib/types/component/input/AutoCompleteDropdown.d.ts +1 -1
  143. package/dist/lib/types/component/input/Link.d.ts +1 -1
  144. package/dist/lib/types/component/input/NumberSpinner.d.ts +1 -1
  145. package/dist/lib/types/component/input/PasswordField.d.ts +1 -1
  146. package/dist/lib/types/component/input/SpinButton.d.ts +1 -1
  147. package/dist/lib/types/component/input/Text.d.ts +3 -1
  148. package/dist/lib/types/component/input/TextArea.d.ts +1 -1
  149. package/dist/lib/types/component/input/TextField.d.ts +3 -1
  150. package/dist/lib/types/component/input/UsernameField.d.ts +1 -1
  151. package/dist/lib/types/component/list/AbstractMarkerList.d.ts +9 -1
  152. package/dist/lib/types/component/list/BulletedList.d.ts +1 -0
  153. package/dist/lib/types/component/list/ListItem.d.ts +10 -4
  154. package/dist/lib/types/component/list/NumberedList.d.ts +1 -0
  155. package/dist/lib/types/component/menubar/MenuBarButton.d.ts +1 -1
  156. package/dist/lib/types/component/menubar/ToolBar.d.ts +1 -1
  157. package/dist/lib/types/component/menubar/ToolBarSeparator.d.ts +1 -1
  158. package/dist/lib/types/component/shared/VirtualRowView.d.ts +13 -1
  159. package/dist/lib/types/component/shared/selectionsEqual.d.ts +1 -0
  160. package/dist/lib/types/component/table/Body.d.ts +15 -2
  161. package/dist/lib/types/component/table/CellGeometry.d.ts +6 -0
  162. package/dist/lib/types/component/table/Column.d.ts +6 -0
  163. package/dist/lib/types/component/table/ColumnConfig.d.ts +4 -0
  164. package/dist/lib/types/component/table/Header.d.ts +25 -2
  165. package/dist/lib/types/component/table/Row.d.ts +13 -2
  166. package/dist/lib/types/component/table/Table.d.ts +45 -4
  167. package/dist/lib/types/component/table/TableExporter.d.ts +1 -1
  168. package/dist/lib/types/component/table/TablePanel.d.ts +2 -1
  169. package/dist/lib/types/component/table/cell/Cell.d.ts +1 -1
  170. package/dist/lib/types/component/table/cell/Header.d.ts +4 -0
  171. package/dist/lib/types/component/table/index.d.ts +2 -2
  172. package/dist/lib/types/component/tree/Tree.d.ts +2 -0
  173. package/dist/lib/types/core/Aria.d.ts +1 -3
  174. package/dist/lib/types/core/Body.d.ts +9 -2
  175. package/dist/lib/types/core/ClassStyleRules.d.ts +17 -0
  176. package/dist/lib/types/core/Component.d.ts +16 -2
  177. package/dist/lib/types/core/ComponentDefaults.d.ts +1 -0
  178. package/dist/lib/types/core/DOM.d.ts +15 -2
  179. package/dist/lib/types/core/DragChain.d.ts +3 -0
  180. package/dist/lib/types/core/ElementAttributes.d.ts +15 -0
  181. package/dist/lib/types/core/Favicon.d.ts +7 -0
  182. package/dist/lib/types/core/FirstLayoutGate.d.ts +5 -0
  183. package/dist/lib/types/core/PendingTransitions.d.ts +4 -0
  184. package/dist/lib/types/core/StyleTarget.d.ts +1 -0
  185. package/dist/lib/types/core/Theme.d.ts +1 -0
  186. package/dist/lib/types/core/Util.d.ts +3 -0
  187. package/dist/lib/types/core/index.d.ts +3 -0
  188. package/dist/lib/types/layout/Accordion.d.ts +0 -1
  189. package/dist/lib/types/layout/Border.d.ts +1 -0
  190. package/dist/lib/types/layout/FlowLayout.d.ts +4 -0
  191. package/dist/lib/types/layout/HFlow.d.ts +3 -0
  192. package/dist/lib/types/layout/Tab.d.ts +4 -1
  193. package/dist/lib/types/layout/Table.d.ts +1 -0
  194. package/dist/lib/types/layout/VFlow.d.ts +1 -0
  195. package/dist/lib/types/overlay/AbstractWindow.d.ts +2 -1
  196. package/dist/lib/types/overlay/Dock.d.ts +1 -0
  197. package/dist/lib/types/overlay/DropZoneOverlay.d.ts +1 -0
  198. package/dist/lib/types/overlay/Popover.d.ts +1 -1
  199. package/dist/lib/types/router/RoutePattern.d.ts +7 -0
  200. package/dist/lib/types/router/Router.d.ts +13 -2
  201. package/dist/lib/types/router/index.d.ts +1 -1
  202. package/dist/lib/validation.es.js +1 -1
  203. package/llms.txt +2 -1
  204. package/package.json +5 -9
  205. package/dist/lib/Border-BgaZkc1R.js +0 -2
  206. package/dist/lib/Border-BgaZkc1R.js.map +0 -1
  207. package/dist/lib/BoxLayout-DrKGfsup.js +0 -2
  208. package/dist/lib/BoxLayout-DrKGfsup.js.map +0 -1
  209. package/dist/lib/Button-U1rsP4v8.js +0 -2
  210. package/dist/lib/ComboBox-B9EDPu6T.js +0 -2
  211. package/dist/lib/ComboBox-B9EDPu6T.js.map +0 -1
  212. package/dist/lib/Component-DXf5a3g0.js +0 -2
  213. package/dist/lib/Component-DXf5a3g0.js.map +0 -1
  214. package/dist/lib/Container-D5H8VxJK.js +0 -2
  215. package/dist/lib/DOM-CGn7xyTw.js +0 -2
  216. package/dist/lib/DOM-CGn7xyTw.js.map +0 -1
  217. package/dist/lib/DragManager-CI4mZQFy.js +0 -2
  218. package/dist/lib/DragManager-CI4mZQFy.js.map +0 -1
  219. package/dist/lib/Glyph-KEf4nIv2.js +0 -2
  220. package/dist/lib/Glyph-KEf4nIv2.js.map +0 -1
  221. package/dist/lib/GridConstraints-DkLueyrf.js +0 -2
  222. package/dist/lib/GridConstraints-DkLueyrf.js.map +0 -1
  223. package/dist/lib/HBox-Ce0GTCJP.js +0 -2
  224. package/dist/lib/HBox-Ce0GTCJP.js.map +0 -1
  225. package/dist/lib/Header-BQIVpBte.js +0 -2
  226. package/dist/lib/Header-BQIVpBte.js.map +0 -1
  227. package/dist/lib/IconText-Bo4Qcnot.js +0 -2
  228. package/dist/lib/IconText-Bo4Qcnot.js.map +0 -1
  229. package/dist/lib/LayoutSizes-DU3qSq0w.js +0 -2
  230. package/dist/lib/LayoutSizes-DU3qSq0w.js.map +0 -1
  231. package/dist/lib/List-DtTVTyWW.js +0 -2
  232. package/dist/lib/List-DtTVTyWW.js.map +0 -1
  233. package/dist/lib/Menu-Dz4L7EKJ.js +0 -2
  234. package/dist/lib/Menu-Dz4L7EKJ.js.map +0 -1
  235. package/dist/lib/Position-A39T7-_1.js +0 -2
  236. package/dist/lib/Position-A39T7-_1.js.map +0 -1
  237. package/dist/lib/Scrollbar-BhtOjqkG.js +0 -2
  238. package/dist/lib/Scrollbar-BhtOjqkG.js.map +0 -1
  239. package/dist/lib/Slider-CdXm7E64.js +0 -2
  240. package/dist/lib/Slider-CdXm7E64.js.map +0 -1
  241. package/dist/lib/TabButton-CThevdY_.js +0 -2
  242. package/dist/lib/TabButton-CThevdY_.js.map +0 -1
  243. package/dist/lib/Text-gz6qjFfP.js +0 -2
  244. package/dist/lib/Text-gz6qjFfP.js.map +0 -1
  245. package/dist/lib/Tooltip-tkXDk5Bo.js +0 -3
  246. package/dist/lib/VBox-EIqw85Rh.js +0 -2
  247. package/dist/lib/VBox-EIqw85Rh.js.map +0 -1
  248. package/dist/lib/VirtualRowView-BxpHQ_u8.js +0 -2
  249. package/dist/lib/VirtualRowView-BxpHQ_u8.js.map +0 -1
  250. package/dist/lib/VirtualScroller-DwEEJE1_.js +0 -2
  251. package/dist/lib/VirtualScroller-DwEEJE1_.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Button-U1rsP4v8.js","names":[],"sources":["../../src/typescript/lib/component/button/Button.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 { Fit } from \"~/layout/Fit.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { FillType } from \"~/layout/FillType.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { BorderOptions, borderToStyle } from \"~/primitive/Border.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Size } from \"~/primitive/Size.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Util } from \"~/core/Util.js\";\n\n/**\n * String-literal union of the events emitted by {@link Button}. A typed\n * shorthand over [`Event.addListener`](/api/core/classes/Component) /\n * [`Event.removeListener`](/api/core/classes/Component) — Button does not\n * own a [`ListenerBag`](/api/core/classes/ListenerBag); the DOM `\"click\"`\n * event is dispatched through the framework's window-level capture handler.\n *\n * @category Components\n */\nexport type ButtonEvent = \"action\";\n\n/**\n * The handler shape consumers register for the `\"click\"` button event.\n *\n * @category Components\n */\nexport type ClickListener = (event: MouseEvent) => Event.ListenerResult;\n\n/**\n * Construction-time options for {@link Button}.\n *\n * @category Components\n */\nexport interface ButtonOptions extends ComponentOptions {\n text?: string;\n\n /**\n * Optional subtitle rendered on a second line *below* the title, in a\n * smaller, dimmer style. Dispatched late (mirroring `text`) so it lands\n * once the content row's children exist. Drives the hover tooltip\n * alongside `text` — see [`setDescription`](/api/component/button/classes/Button#setdescription).\n */\n description?: string;\n\n /**\n * When a leading glyph *and* a description are both present, controls\n * where the description aligns. `true` (default) spans the description\n * full-width *below* the glyph+title row, its left edge under the glyph.\n * `false` indents it under the title text, beside the glyph. No visible\n * effect without both a glyph and a description, or when `showDescription`\n * is `false`. Runtime counterpart: `setDescriptionUnderGlyph`.\n */\n descriptionUnderGlyph?: boolean;\n\n /**\n * When `false`, the description is *not* rendered on the button face — the\n * button shows only its glyph and title — but it still appears in the\n * hover tooltip (`{title}\\n\\n{description}`). Default `true`. The text is\n * always stored; only its on-button render is suppressed. No effect\n * without a description. Runtime counterpart: `setShowDescription`.\n */\n showDescription?: boolean;\n\n /**\n * When `false`, the title is *not* rendered on the button face — the\n * button shows only its glyph — but the title still drives the hover\n * tooltip (`{title}\\n\\n{description}`) and is reflected into `aria-label`\n * as the accessible name. Default `true`. The title is always stored; only\n * its on-button render is suppressed. Pairs with `showDescription` to give\n * a glyph-only button a tooltip + accessible name without an on-face label.\n * Runtime counterpart: `setShowText`.\n */\n showText?: boolean;\n glyph?: string;\n\n /**\n * Tints the leading glyph independently of the button's `foregroundColor`,\n * which colours the whole face (title *and* glyph). Sets the glyph\n * element's own `color`, picked up by its `currentColor` SVG fill — so a\n * button can render, say, a green glyph beside default-coloured text.\n * Survives a later `setGlyph`. Runtime counterpart: `setGlyphColor`.\n */\n glyphColor?: string;\n\n /**\n * Tints the description subtitle independently of the button's\n * `foregroundColor`, overriding the default dim description colour.\n * Runtime counterpart: `setDescriptionColor`.\n */\n descriptionColor?: string;\n\n enabled?: boolean;\n pressedBackgroundColor?: string;\n pressedBackgroundImage?: string;\n pressedForegroundColor?: string;\n pressedBorder?: BorderOptions | string;\n pressedBorderRadius?: string;\n pressedShadow?: string;\n hoverBackgroundColor?: string;\n hoverBackgroundImage?: string;\n hoverForegroundColor?: string;\n hoverBorder?: BorderOptions | string;\n hoverBorderRadius?: string;\n hoverShadow?: string;\n\n /**\n * Suppresses the framework's visual-chrome defaults — `border`,\n * `borderRadius`, `shadow`, `backgroundImage`, the twelve `pressedX` /\n * `hoverX` fields, and the UA `<button>` background. Use for buttons\n * that want only the cursor, color, and inset behaviour of `Button`\n * without the ridge border, drop shadow, and gradient background.\n * Runtime-toggle counterpart is `setChromeless`; read with\n * `isChromeless`. Used by [`PickerButton`](/api/component/input/classes/PickerButton)\n * and [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton).\n *\n * `applyOptions({ chromeless: true })` on a previously-chromeful button\n * writes the flag pure into `_options` and gates future chrome\n * dispatches, but does not clear the chrome already on the element —\n * callers wanting a runtime flip should call `setChromeless(true)`\n * directly.\n */\n chromeless?: boolean;\n\n /**\n * Classical \"flat\" appearance: no resting border/shadow/gradient; a light\n * frame + fill on `:hover:not(:active)` and a sunken inset frame on\n * `:active`. Mutually exclusive with `chromeless` (chromeless wins).\n * Runtime counterpart `setFlat`; read with `isFlat`.\n */\n flat?: boolean;\n\n /**\n * Compact rendering: tighter symmetric insets so the button reads denser\n * (e.g. packed into a [`ToolBar`](/api/component/menubar/classes/ToolBar)).\n * Text buttons go `(5,10,5,10)` →\n * `(2,6,2,6)`; glyph-only buttons collapse to a `(2,2,2,2)` square.\n * Defaults to `false`. Runtime counterpart `setCompact`; read with\n * `isCompact`.\n */\n compact?: boolean;\n\n /**\n * Anchor for the inner content row (glyph + label) within Button's outer\n * `Fit` layout. Defaults to {@link AnchorType.CENTER}. Pass\n * {@link AnchorType.WEST} for left-anchored menubar-style buttons.\n */\n anchor?: AnchorType;\n\n /**\n * Fill mode for the inner content row within Button's outer `Fit`\n * layout. Defaults to {@link FillType.NONE} (content sits at preferred\n * size, anchor decides displacement). `BOTH` stretches it to fill.\n */\n fill?: FillType;\n\n /**\n * Construction-time listener bag — the declarative form of `on()`. The\n * `action` entry is wired as if `on(\"action\", fn)` had been called (it\n * fires on the underlying DOM `click`).\n */\n listeners?: { action?: ClickListener };\n}\n\n/**\n * Default button inset perimeter — generous `5/10` (vertical/horizontal) so a\n * raised button reads as a comfortable click target. The asymmetric horizontal\n * padding gives the label breathing room either side of its centred glyph.\n */\nconst BUTTON_DEFAULT_INSETS: Insets = new Insets(5, 10, 5, 10);\n\n/**\n * Compact inset perimeter for a text-bearing button — `2/6`. Tightens the\n * generous default so the button reads denser (e.g. packed into a `ToolBar`)\n * while keeping enough horizontal padding that the label doesn't touch the\n * frame. Driven by {@link Button.setCompact}.\n */\nconst BUTTON_COMPACT_INSETS_TEXT: Insets = new Insets(2, 6, 2, 6);\n\n/**\n * Compact inset perimeter for a glyph-only button — a tight `2/2` square so a\n * toolbar icon collapses to its glyph with minimal margin. Driven by\n * {@link Button.setCompact}; supersedes the non-compact flat square below.\n */\nconst BUTTON_COMPACT_INSETS_GLYPH: Insets = new Insets(2, 2, 2, 2);\n\n/**\n * Flat glyph-only square inset — `4/4`. A non-compact flat glyph button (a\n * toolbar icon on a non-compact bar) collapses its asymmetric `5/10` default\n * to this square so it reads as a tight icon without the full compact tightening.\n */\nconst BUTTON_FLAT_GLYPH_INSETS: Insets = new Insets(4, 4, 4, 4);\n\n/**\n * Resting background the chromeful path installs as a colour (mirroring the\n * `--ts-ui-button-bg` image `super.applyChromeOptions` paints). Shared between\n * `applyChromeOptions` and `_applyFlatChrome` so the flat path can recognise\n * this framework default in `_options.backgroundColor` and tell it apart from a\n * colour the consumer set — the flat appearance is transparent at rest, so it\n * must clear the former but preserve the latter.\n */\nconst BUTTON_RESTING_BACKGROUND: string = \"var(--ts-ui-button-bg, transparent)\";\n\n/**\n * User-overridable visual defaults forwarded to `super` via the options bag.\n * The cascade in `Component`'s constructor dispatches each setter once with\n * the final value, so any field the caller supplied wins. Includes the\n * `pressedX` and `hoverX` defaults because both `pressedStyleRule` and\n * `hoverStyleRule` are lazy getters — those setters are safe to fire during\n * the super cascade and queue their writes until the rule materialises.\n */\nconst _defaultButtonOptions: Partial<ButtonOptions> = {\n tag: \"button\",\n cursor: \"pointer\",\n foregroundColor: \"var(--ts-ui-text-color, black)\",\n border: \"2px ridge var(--ts-ui-button-border, rgb(200, 200, 200))\",\n borderRadius: \"var(--ts-ui-border-radius, 4px)\",\n shadow: \"var(--ts-ui-button-shadow, 1px 2px 5px 0 rgba(0, 0, 0, 0.2))\",\n backgroundImage: \"var(--ts-ui-button-bg, linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200)))\",\n insets: BUTTON_DEFAULT_INSETS,\n anchor: AnchorType.CENTER,\n fill: FillType.NONE,\n pressedForegroundColor: \"var(--ts-ui-button-pressed-fg, rgb(150, 150, 150))\",\n pressedBackgroundColor: \"var(--ts-ui-button-pressed-bg, rgb(200, 200, 200))\",\n pressedBackgroundImage: \"var(--ts-ui-button-pressed-bg, none)\",\n pressedShadow: \"var(--ts-ui-button-pressed-shadow, 1px 2px 5px 0 rgba(0, 0, 0, 0.2) inset)\",\n hoverBackgroundColor: \"var(--ts-ui-button-hover-bg, rgb(252, 252, 252))\",\n hoverBackgroundImage: \"var(--ts-ui-button-hover-bg, none)\",\n hoverShadow: \"var(--ts-ui-button-hover-shadow, 1px 3px 6px 0 rgba(0, 0, 0, 0.25))\",\n};\n\n/**\n * A push button component with a text label and configurable pressed-state appearance.\n *\n * Maintains separate CSS rules for the normal and `:active` states, allowing\n * independent control of border, shadow, background, and foreground color when pressed.\n *\n * @example\n * ```typescript\n * import { Event } from '@jimka/typescript-ui/core';\n * import { Button } from '@jimka/typescript-ui/component/button';\n *\n * const button = new Button('Save');\n * Event.addListener(button, 'click', () => save());\n * panel.addComponent(button);\n * ```\n *\n * @category Components\n */\nclass Button<TOptions extends ButtonOptions = ButtonOptions> extends Component<TOptions> {\n\n private _text!: Text;\n /**\n * The button's content-row container. Holds the optional leading\n * [`Glyph`](/api/component/display/classes/Glyph) plus the `_text` label,\n * laid out by an `HBox`. Exposed as `protected` so subclasses can\n * re-anchor (`removeComponent` + `addComponent`) or rebuild the row\n * without having to opt into `customLayout: true`. Treat as part of the\n * subclass contract — future restructuring of Button's content row\n * needs to keep this field's identity and shape stable.\n */\n protected _content!: Component;\n\n /**\n * The vertical title/subtitle stack nested inside `_content`'s HBox. Holds\n * `[_text, _description?]` laid out by a `VBox`, so the optional\n * description renders on a line below the title while the leading glyph\n * stays beside the whole stack. Private — unlike `_content` this is an\n * internal detail of how the label row stacks and is not part of the\n * subclass re-anchor contract.\n */\n private _titleColumn!: Component;\n\n /**\n * The subtitle label, created lazily on the first {@link setDescription}\n * call (mirroring `_glyph`'s lazy creation) so a button with no\n * description never reserves a second line. `null` until then. Stays\n * alive (detached, not nulled) when `showDescription` is `false` so the\n * tooltip can still read its text.\n */\n private _description: Text | null = null;\n\n /**\n * When `true`, {@link _rebuildTooltip} detaches the hover tooltip instead of\n * attaching it, regardless of the title/description text. Lets a subclass\n * silence the tooltip while the button is showing its own popup — e.g. a\n * [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton) whose\n * dropdown is open, where the tooltip would otherwise float over the menu.\n * Toggled via {@link setTooltipSuppressed}.\n */\n private _tooltipSuppressed: boolean = false;\n\n /**\n * Lazy containers used only by the \"under glyph\" (full-width) description\n * topology: `_outerColumn` (VBox) holds `[_innerRow, _description]` and\n * `_innerRow` (HBox) holds `[glyph?, _text]`. Created on first entry into\n * that mode so a button with no description (or in under-title mode) never\n * allocates them — keeping the minimal tree the subclasses depend on.\n * Private; not part of the subclass re-anchor contract.\n */\n private _outerColumn: Component | null = null;\n private _innerRow: Component | null = null;\n\n private _glyph: Glyph | null = null;\n\n /**\n * The square pixel size {@link _syncGlyphSize} last wrote to `_glyph`'s\n * preferred size. Acts as the per-glyph opt-out guard: on a re-sync (e.g.\n * theme toggle) the glyph is only re-matched to the title line height when\n * its current preferred size still equals this value — so a subclass /\n * consumer that pinned its own glyph size (SpinButton, Tab close-glyph)\n * is left untouched. `null` until the framework first sizes the glyph.\n *\n * This size-difference guard is unreliable on its own: a consumer that\n * pins the glyph *before* the first successful sync (while this is still\n * `null` — e.g. before the button is attached and the line height\n * resolves) is not protected, so the first post-attach re-sync clobbers\n * the pin. {@link _glyphSizePinned} is the authoritative opt-out.\n */\n private _glyphSyncedSize: number | null = null;\n\n /**\n * Set by {@link pinGlyphSize} when a consumer fixes the leading glyph to a\n * specific size. While `true`, {@link _syncGlyphSize} skips the glyph\n * entirely, so a theme change never re-tracks it to the title line height.\n * This is the reliable opt-out (the {@link _glyphSyncedSize} size-diff guard\n * has a null-window hole — see there).\n */\n private _glyphSizePinned: boolean = false;\n\n /**\n * Flipped to `true` the first time a consumer calls `setPreferredSize`\n * (directly or via the options bag). When set, the native auto-sizing\n * pipeline (see `recomputePreferredSize`) no-ops so the consumer's\n * explicit intent wins permanently. There's no public surface to\n * re-enable auto-sizing — a future plan can add a `clearPreferredSize`\n * method that resets this flag and re-fires the recompute.\n *\n * `declare` rather than `= false` so the class-field super-cascade trap\n * doesn't clobber the value when `Component.applyOptions` dispatches\n * `setPreferredSize` during the super-time cascade (an `= false`\n * initializer runs *after* super returns and would silently revert the\n * setter's `_consumerSetPreferredSize = true` write — letting the\n * end-of-constructor `recomputePreferredSize` overwrite the consumer's\n * preferred size with the content-derived value). The early-return\n * check at the top of `recomputePreferredSize` treats `undefined` as\n * falsy, so the no-cascade-write case still auto-sizes correctly.\n */\n private declare _consumerSetPreferredSize?: boolean;\n\n /**\n * Bound theme-change handler. The auto-sizing pipeline reads font-size\n * and glyph metrics that can shift with the active theme, so the recompute\n * re-fires whenever the theme cascade flips. `_rebuildContentRow` runs\n * first so the single-line optical inset re-reads the freshly-invalidated\n * `Util.opticalCenterOffset()` — `recomputePreferredSize` alone does not\n * touch the content row. Held on the instance so a future `dispose` path\n * can unregister it.\n */\n private readonly _onThemeChange: () => void = () => {\n this._rebuildContentRow();\n this.recomputePreferredSize();\n };\n\n // Lazy `:active` rule. The slot is just a fast-path cache — the\n // `createStyleRule` builder on Component dedupes by selector suffix, so\n // even if the slot is reset between calls (e.g. by TypeScript class-field\n // init after super returns), the next access still returns the same\n // wrapper that the super-cascade allocated. `declare` keeps the slot off\n // the runtime class so the fast-path doesn't pay an unnecessary Map\n // lookup after construction.\n private declare _pressedStyleRule?: StyleRule;\n private get pressedStyleRule(): StyleRule {\n return this._pressedStyleRule ??= this.createStyleRule(\":active\");\n }\n private _pressedBorder: BorderOptions | null = null;\n\n // Lazy `:hover:not(:active)` rule. The `:not(:active)` guard makes the\n // cascade unambiguous regardless of source order — the moment the\n // pointer goes down, `:active` matches and `:hover:not(:active)` stops\n // matching, so the pressed treatment always wins.\n private declare _hoverStyleRule?: StyleRule;\n private get hoverStyleRule(): StyleRule {\n return this._hoverStyleRule ??= this.createStyleRule(\":hover:not(:active)\");\n }\n private _hoverBorder: BorderOptions | null = null;\n\n /**\n * Cached `flat` appearance flag. `declare` rather than initialized because\n * `setFlat` can fire during the super-time cascade via `applyChromeOptions`\n * (the flat branch installs hover/pressed treatments through the lazy\n * style-rule getters), and an `= false` initializer would run after super\n * returns and silently clobber the cascaded value.\n */\n private declare _flat?: boolean;\n\n /**\n * Cached `compact` flag. `declare` rather than initialized because\n * `setCompact` can fire during the super-time cascade via `applyOptions`,\n * and an `= false` initializer would run after super returns and silently\n * clobber the cascaded value (same trap as `_flat` above). The inset\n * resolution it drives is deferred to `_resolveInsets`, which guards on the\n * content row existing.\n */\n private declare _compact?: boolean;\n\n private _enabledCursor: string = \"pointer\";\n\n /**\n * Constructs a Button. `text` (positional or via options) and `glyph` are\n * both optional — an empty Button renders as a chrome-shaped placeholder\n * whose label / glyph can be filled in later via `setText` / `setGlyph`.\n *\n * @example\n * ```typescript\n * new Button('Save');\n * new Button({ glyph: 'times' });\n * new Button('Save', { glyph: 'check-circle' });\n * ```\n */\n constructor(text?: string, options?: ButtonOptions, subclassDefaults?: Partial<ButtonOptions>);\n constructor(options: ButtonOptions);\n constructor(\n textOrOptions?: string | ButtonOptions,\n options?: ButtonOptions,\n subclassDefaults?: Partial<ButtonOptions>,\n ) {\n // Normalise the overload: a non-string first argument is the options bag.\n let text: string | undefined;\n if (typeof textOrOptions === \"string\") {\n text = textOrOptions;\n } else if (textOrOptions !== undefined) {\n options = textOrOptions;\n }\n\n // Hand defaults to Component via the second super arg so they land in\n // `_defaultOptions` and survive subsequent `applyOptions` re-merges.\n // Subclass defaults (forwarded by callers via the third constructor\n // arg) layer on top so the deepest class's overrides win.\n super(\n options as TOptions,\n { ..._defaultButtonOptions, ...(subclassDefaults ?? {}) } as Partial<TOptions>,\n );\n\n // Structural state — can't go through the bag because consumers must\n // not be able to override it.\n this.setLayoutManager(new Fit());\n\n // Build the text/glyph content row. The title (and optional subtitle)\n // live in a vertical `_titleColumn` so the description stacks below the\n // title; the glyph sits beside the whole column in the outer HBox.\n this._text = new Text();\n this._titleColumn = new Component();\n this._titleColumn.setLayoutManager(new VBox({ spacing: 0 }));\n this._titleColumn.setInsets(new Insets(0, 0, 0, 0));\n this._titleColumn.setPointerEvents(\"none\");\n this._titleColumn.addComponent(this._text);\n\n this._content = new Component();\n this._content.setLayoutManager(new HBox({ spacing: 2 }));\n this._content.setInsets(new Insets(0, 0, 0, 0));\n this._content.setPointerEvents(\"none\");\n this._content.addComponent(this._titleColumn);\n\n this._text.setPointerEvents(\"none\");\n this._text.setTextAlign(\"center\");\n this._text.setFontWeight(\"bold\");\n this._text.setFontSize(\"--ts-ui-button-font-size\");\n\n this.addComponent(this._content, {\n fill: this.getFill(),\n anchor: this.getAnchor(),\n });\n\n // Late-built state: applyOptions wrote any caller `text`/`glyph`/\n // `description` into `_options` pure (no setter dispatch) because\n // `this.text`/`_content` didn't exist yet. Dispatch the effective value\n // now that children are wired up, folding a class default (e.g.\n // TabCloseButton's `glyph: \"xmark\"`) that raw dispatch left in\n // `_defaultOptions` rather than `_options`.\n const effectiveText = this._options.text ?? this._defaultOptions.text ?? text;\n if (effectiveText !== undefined) {\n this.setText(effectiveText);\n }\n const effectiveGlyph = this._options.glyph ?? this._defaultOptions.glyph;\n if (effectiveGlyph !== undefined) {\n this.setGlyph(effectiveGlyph);\n }\n const effectiveDescription = this._options.description ?? this._defaultOptions.description;\n if (effectiveDescription !== undefined) {\n this.setDescription(effectiveDescription);\n }\n\n // Construction-time flat / compact: the flat and compact setters ran\n // during the super cascade before `_glyph` / `_text` existed, so\n // `_resolveInsets` no-op'd. Re-evaluate now the content row is built so\n // the icon-square / compact insets land.\n if (this._flat || this._compact) {\n this._resolveInsets();\n }\n\n // Initial auto-sized preferred-size pass. No-ops when the consumer\n // already supplied `preferredSize` (the override of `setPreferredSize`\n // below flips `_consumerSetPreferredSize`).\n this.recomputePreferredSize();\n\n // Re-fire the auto-sized recompute on theme changes so any\n // font-size / glyph-metric shifts cascade into the button's preferred\n // size without explicit consumer prodding.\n this.subscribeTheme(this._onThemeChange);\n\n // Wire the listener bag — but only when this IS a plain Button.\n // Subclasses wire their own bag from their constructor body after their\n // `super()` returns, because a subclass event (e.g. SpinButton's\n // `tick`) may live in a `ListenerBag` that does not exist yet during\n // this base constructor. The check is instance-identity, not\n // `new.target`: construction routes through the `callable()` Proxy,\n // which has no `construct` trap, so `new.target` is the proxy rather\n // than this class — but the instance's prototype still resolves to the\n // raw class prototype, so a plain Button matches and any subclass does not.\n if (Object.getPrototypeOf(this) === Button.prototype) {\n this.applyListeners(options?.listeners);\n }\n }\n\n /**\n * Returns the content anchor used inside the button's `Fit` layout — the\n * caller value, else the class default ({@link AnchorType.CENTER}).\n *\n * @returns The resolved anchor.\n */\n getAnchor(): AnchorType {\n return (this._options.anchor ?? this._defaultOptions.anchor) as AnchorType;\n }\n\n /**\n * Returns the content fill used inside the button's `Fit` layout — the\n * caller value, else the class default ({@link FillType.NONE}).\n *\n * @returns The resolved fill.\n */\n getFill(): FillType {\n return (this._options.fill ?? this._defaultOptions.fill) as FillType;\n }\n\n /**\n * Applies a {@link ButtonOptions} bag. Inherited Component fields cascade\n * through `super.applyOptions`; pressed-state, hover-state, and `enabled`\n * fields cascade through their own setters (the lazy `pressedStyleRule`\n * and `hoverStyleRule` getters make them safe to fire during the\n * super-time cascade). `text`, `description`, and `glyph` are written pure\n * into `_options` during the super-time cascade (before the content row\n * exists) and dispatched from the constructor body once children exist —\n * but a *post-construction* call (a subclass's tail `applyOptions`, or a\n * runtime re-apply) dispatches the setters directly, so an option supplied\n * after the constructor's late dispatch still renders. The\n * `descriptionUnderGlyph` / `showDescription` flags are always pure-written\n * and consumed live by the content-row rebuild those dispatches trigger.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: TOptions): this {\n // Button structurally owns a per-instance Fit layout, installed\n // imperatively in the constructor because a fresh manager can't live in\n // the shared `_defaultOptions` bag. `super.applyOptions` re-dispatches\n // `_defaultOptions.layoutManager` (always an `Absolute`) on every call,\n // so a subclass that hands its consumer options to a tail `applyOptions`\n // (ToggleButton / TabButton: `if (options) this.applyOptions(options)`)\n // would clobber that Fit — and an Absolute layout never positions the\n // content row, collapsing the label into a corner. Capture the manager\n // and restore it when the cascade re-defaulted it away and the consumer\n // did not explicitly request a different one (ButtonOptions omits\n // `layoutManager`, so that override path is intentionally unreachable).\n const structuralLayout = this.getLayoutManager();\n\n super.applyOptions(options);\n\n if (options.layoutManager === undefined && this.getLayoutManager() !== structuralLayout) {\n this.setLayoutManager(structuralLayout);\n }\n\n // Content-row flags first (pure writes): a dispatched\n // setText/setGlyph/setDescription below reads them (via `_isShowText()`\n // and the content-row rebuild). They ride the options bag, not a class\n // field, so the super-cascade class-field trap does not apply.\n if (options.descriptionUnderGlyph !== undefined) this._options.descriptionUnderGlyph = options.descriptionUnderGlyph;\n if (options.showDescription !== undefined) this._options.showDescription = options.showDescription;\n if (options.showText !== undefined) this._options.showText = options.showText;\n\n // text / glyph / description. Before the content row exists — the\n // super-time cascade, where `_text` is unassigned — write pure and let\n // the constructor body dispatch the setters once children are wired.\n // After construction the row is built, so dispatch the setters to\n // rebuild the content: a subclass that forwards only `text` to super and\n // hands its bag to a tail `applyOptions` (ToggleButton / TabButton) has\n // already missed the constructor dispatch, so a pure write would record\n // the glyph/description but never render it. Instance-identity guard\n // (`_text`), so raw dispatch loses no `_defaultOptions` default (folded\n // by the constructor's own late dispatch for the cascade path).\n const contentBuilt = this._text !== undefined;\n\n // Glyph / description tints are stored before the glyph / description\n // dispatch below, so a `setGlyph` / `setDescription` fired here (or by the\n // constructor's late dispatch) re-reads and re-applies them to the fresh\n // element. A post-construction call with only the colour (the element\n // already built) tints it directly via the setter.\n if (options.glyphColor !== undefined) {\n if (contentBuilt) this.setGlyphColor(options.glyphColor); else this._options.glyphColor = options.glyphColor;\n }\n\n if (options.descriptionColor !== undefined) {\n if (contentBuilt) this.setDescriptionColor(options.descriptionColor); else this._options.descriptionColor = options.descriptionColor;\n }\n\n if (options.text !== undefined) {\n if (contentBuilt) this.setText(options.text); else this._options.text = options.text;\n }\n\n if (options.description !== undefined) {\n if (contentBuilt) this.setDescription(options.description); else this._options.description = options.description;\n }\n\n if (options.glyph !== undefined) {\n if (contentBuilt) this.setGlyph(options.glyph); else this._options.glyph = options.glyph;\n }\n\n if (options.enabled !== undefined) this.setEnabled(options.enabled);\n\n // chromeless / anchor / fill are pure writes — no setter dispatch.\n // Runtime flag flips for chromeless go through setChromeless(), which\n // also reconciles the DOM via clearChrome / restoreChrome. anchor and\n // fill are consumed by the constructor body when adding `_content` to\n // the outer Fit layout; later applyOptions calls don't reanchor.\n if (options.chromeless !== undefined) this._options.chromeless = options.chromeless;\n if (options.flat !== undefined) this._options.flat = options.flat;\n // Dispatched (not pure-written like `flat`) because it must resolve the\n // inset perimeter; `_resolveInsets` guards on the content row so the\n // cascade-time call before `_text` exists is a safe no-op.\n if (options.compact !== undefined) this.setCompact(options.compact);\n if (options.anchor !== undefined) this._options.anchor = options.anchor;\n if (options.fill !== undefined) this._options.fill = options.fill;\n\n return this;\n }\n\n /**\n * Gates Component's chrome dispatch on the `chromeless` flag and, when\n * the flag is off, extends it with Button's twelve `pressedX` / `hoverX`\n * chrome fields. Reads the flag from the runtime cache first so a flag\n * previously written (by an earlier `applyOptions` or `setChromeless`)\n * keeps gating future re-applies that omit `chromeless`.\n *\n * @param options - The raw caller options bag passed by {@link applyOptions}.\n */\n protected override applyChromeOptions(options: TOptions): void {\n const chromeless = (options.chromeless ?? this.isChromeless()) === true;\n if (chromeless) {\n // The chrome group's class defaults are dispatched only by\n // `super.applyChromeOptions` (skipped here for chromeless), so a\n // fresh chromeless button never receives them. The explicit\n // `undefined` writes still matter on a re-apply (e.g. `setChromeless`\n // after a chromeful pass): they clear a previously-written value so\n // the chrome getters report `null` and `applyStyle` skips the\n // property.\n //\n // Border goes through the private `_border` field, so `clearBorder`\n // clears it to a `none` border that overrides the UA `<button>` ridge.\n //\n // Finally, the UA `<button>` element has a non-transparent\n // background-color; set transparent unless the caller specified\n // their own backgroundColor.\n this.clearBorder();\n this._options.borderRadius = undefined;\n this._options.shadow = undefined;\n this._options.backgroundImage = undefined;\n if (this.getBackgroundColor() === null) {\n this._options.backgroundColor = \"transparent\";\n }\n return;\n }\n\n super.applyChromeOptions(options);\n\n // Chromeful resting background: `super` applied the `--ts-ui-button-bg`\n // token as a background *image* (for gradient tokens); also apply it as\n // a background *colour* so a flat-colour token (e.g. the Modern theme)\n // renders too — whichever channel is valid paints, the other is dropped.\n // Without the colour channel a flat-colour token was invalid as an image\n // and the button fell back to the UA `<button>` background. Skipped when\n // the caller set their own backgroundColor, and when flat (flat is\n // transparent at rest — see `_applyFlatChrome`).\n const isFlat = (this._options.flat ?? options.flat) === true;\n\n if (!isFlat && options.backgroundColor === undefined) {\n this.setBackgroundColor(BUTTON_RESTING_BACKGROUND);\n }\n\n // The pressed/hover colour + shadow + image fields carry class defaults,\n // so dispatch the caller value or the class default. The pressed/hover\n // border fields are not defaulted, so they stay caller-gated.\n this.setPressedForegroundColor(options.pressedForegroundColor ?? this.getPressedForegroundColor()!);\n this.setPressedBackgroundColor(options.pressedBackgroundColor ?? this.getPressedBackgroundColor()!);\n this.setPressedBackgroundImage(options.pressedBackgroundImage ?? this.getPressedBackgroundImage()!);\n this.setPressedShadow (options.pressedShadow ?? this.getPressedShadow()!);\n if (options.pressedBorder !== undefined) this.setPressedBorder (options.pressedBorder);\n if (options.pressedBorderRadius !== undefined) this.setPressedBorderRadius(options.pressedBorderRadius);\n\n if (options.hoverForegroundColor !== undefined) this.setHoverForegroundColor(options.hoverForegroundColor);\n this.setHoverBackgroundColor(options.hoverBackgroundColor ?? this.getHoverBackgroundColor()!);\n this.setHoverBackgroundImage(options.hoverBackgroundImage ?? this.getHoverBackgroundImage()!);\n this.setHoverShadow (options.hoverShadow ?? this.getHoverShadow()!);\n if (options.hoverBorder !== undefined) this.setHoverBorder (options.hoverBorder);\n if (options.hoverBorderRadius !== undefined) this.setHoverBorderRadius(options.hoverBorderRadius);\n\n // Flat is the third appearance mode: it runs in the chromeful `else`\n // path (chromeless already returned above) and re-points the resting\n // chrome + the raised `pressedX`/`hoverX` treatments just dispatched to\n // the flat tokens. Reading the runtime cache first keeps a previously\n // written flag gating future re-applies that omit `flat`.\n if ((this._options.flat ?? options.flat) === true) {\n this._flat = true;\n this._applyFlatChrome();\n }\n }\n\n /**\n * Sets the button's title text. The single entry point for mutating the\n * title — there is no public accessor for the inner label, so every title\n * change routes through here, keeping the auto-sized preferred size and\n * the hover tooltip (composed from title + description) in sync.\n *\n * @param text - The new title string.\n *\n * @returns This component, for method chaining.\n */\n setText(text: string): this {\n this._options.text = text;\n\n // `_text` is the on-face renderer. The title string lives in\n // `_options.text`; `_text` shows it only when `showText` is true, and\n // renders blank when hidden so the button stays metrically a glyph-only\n // button while the tooltip and accessible name still carry the title.\n this._text.setText(this._isShowText() ? text : \"\");\n this.recomputePreferredSize();\n this._rebuildTooltip();\n this._reflectAccessibleName();\n\n return this;\n }\n\n /**\n * Returns the button's current title text, or `\"\"` when no label is set.\n * A read-only companion to {@link setText} — the inner label has no other\n * public accessor; the [`ToolBar`](/api/component/menubar/classes/ToolBar)\n * overflow menu reads it to label each dropdown row.\n *\n * @returns The current title string, or `\"\"` if none is set.\n */\n getText(): string {\n return this._options.text ?? \"\";\n }\n\n /**\n * Applies a writing mode to the button and propagates it to the inner label\n * (and description) so their measured preferred size reflects the rotated\n * text run — a label-only `getWritingMode` would otherwise stay horizontal\n * even while inheriting the vertical mode through CSS, and report the wrong\n * extents. Re-syncs the auto-sized preferred size after the labels swap.\n *\n * @param value - A CSS `writing-mode` value (e.g. `vertical-rl`).\n *\n * @returns This component, for method chaining.\n */\n setWritingMode(value: string): this {\n const previousOrientation = this._contentOrientation();\n\n super.setWritingMode(value);\n\n // `writingMode` is a Component option, so this can fire from\n // `applyOptions` during `super()` — before the constructor assigns\n // `_text`. Forward (and resize) only once the labels exist; the Tab sets\n // the mode post-construction, so its labels are always present.\n if (this._text) {\n this._text.setWritingMode(value);\n this._description?.setWritingMode(value);\n\n // Re-lay the content row when the reading direction flips, but only\n // when a glyph or description is present — a lone label is a single\n // child whose container axis is irrelevant, so plain-text tabs keep\n // their exact prior layout.\n if (this._contentOrientation() !== previousOrientation && (this._glyph || this._description)) {\n this._rebuildContentRow();\n }\n\n this.recomputePreferredSize();\n }\n\n return this;\n }\n\n /**\n * Justifies the button's title (and leading glyph) within a button wider\n * than its content. Forwards the value to the inner\n * [`Text`](/api/component/input/classes/Text)'s CSS `text-align` and\n * re-anchors the whole content row in the Fit layout — `\"left\"` / `\"right\"`\n * track the start / end of the label's reading direction, so they map to the\n * vertical edges on a rotated (west/east) label. `\"center\"` is the default.\n *\n * @param align - A CSS `text-align` value (`\"left\"`, `\"center\"`, `\"right\"`).\n *\n * @returns This component, for method chaining.\n */\n setTextAlign(align: string): this {\n // `setTextAlign` can be driven post-construction (the Tab strip sets it\n // each layout pass), but a future option-bag dispatch could fire from\n // `applyOptions` during `super()` — before the constructor assigns\n // `_text`. Guard like `setWritingMode` does.\n if (this._text) {\n this._text.setTextAlign(align);\n\n // CSS text-align only nudges glyphs inside the label's own\n // content-hugging box; to justify the whole glyph+label block\n // within a button wider than its content, re-anchor the content row\n // in the Fit layout along the (possibly rotated) reading axis.\n const constraints = this.getLayoutConstraints(this._content);\n\n if (constraints) {\n constraints.anchor = this._anchorForTextAlign(align);\n this.scheduleLayout();\n }\n }\n\n return this;\n }\n\n /**\n * Maps a `text-align` value to the {@link AnchorType} that positions the\n * content row at the matching edge of a button wider than its content.\n * Accepts both physical (`\"left\"` / `\"right\"`) and flow-relative (`\"start\"`\n * / `\"end\"`) values; either way they track the start / end of the label's\n * reading direction, so on a rotated label they map to the vertical edges\n * (`NORTH` / `SOUTH`) — reversed between clockwise and counter-clockwise.\n *\n * @param align - A `text-align` value (`\"left\"`/`\"start\"`, `\"center\"`, `\"right\"`/`\"end\"`).\n *\n * @returns The anchor that justifies the content row accordingly.\n */\n private _anchorForTextAlign(align: string): AnchorType {\n const leading = align === \"left\" || align === \"start\";\n const trailing = align === \"right\" || align === \"end\";\n const orient = this._contentOrientation();\n\n if (orient === \"cw\") {\n // Reads top→bottom: leading is the top edge, trailing the bottom.\n return leading ? AnchorType.NORTH : trailing ? AnchorType.SOUTH : AnchorType.CENTER;\n }\n\n if (orient === \"ccw\") {\n // Reads bottom→top: leading is the bottom edge, trailing the top.\n return leading ? AnchorType.SOUTH : trailing ? AnchorType.NORTH : AnchorType.CENTER;\n }\n\n return leading ? AnchorType.WEST : trailing ? AnchorType.EAST : AnchorType.CENTER;\n }\n\n /**\n * Clears the writing mode from the button and its inner label (and\n * description), restoring horizontal text measurement.\n *\n * @returns This component, for method chaining.\n */\n clearWritingMode(): this {\n const previousOrientation = this._contentOrientation();\n\n super.clearWritingMode();\n\n if (this._text) {\n this._text.clearWritingMode();\n this._description?.clearWritingMode();\n\n // Restoring horizontal flow flips the reading direction back; re-lay\n // the content row for glyph/description buttons (see setWritingMode).\n if (this._contentOrientation() !== previousOrientation && (this._glyph || this._description)) {\n this._rebuildContentRow();\n }\n\n this.recomputePreferredSize();\n }\n\n return this;\n }\n\n /**\n * Sets the button's subtitle, shown on a line below the title in a\n * smaller, dimmer style. The subtitle label is created lazily on the\n * first call (mirroring {@link setGlyph}) so a button with no description\n * never reserves a second line. Re-syncs the auto-sized preferred size\n * and the hover tooltip.\n *\n * @param text - The subtitle string.\n *\n * @returns This component, for method chaining.\n */\n setDescription(text: string): this {\n if (!this._description) {\n this._description = new Text();\n this._description.setPointerEvents(\"none\");\n this._description.setTextAlign(\"center\");\n this._description.setFontSize(\"--ts-ui-button-description-font-size\");\n this._description.setFontWeight(\"var(--ts-ui-button-description-weight, normal)\");\n this._description.setForegroundColor(\n this._options.descriptionColor ?? \"var(--ts-ui-button-description-fg, rgb(110, 110, 110))\"\n );\n }\n\n // The instance is created above unconditionally so `_rebuildTooltip`\n // can always read it; `_rebuildContentRow` decides whether it is\n // actually parented onto the button face (gated by `showDescription`)\n // and in which topology (gated by `descriptionUnderGlyph`).\n this._description.setText(text);\n this._rebuildContentRow();\n this._rebuildTooltip();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns the subtitle Text child, or `null` when no description has been\n * set (mirroring {@link getGlyph}). Read-only access — the only way to\n * change the subtitle string is {@link setDescription}, so the tooltip\n * stays authoritative.\n *\n * @returns The description [`Text`](/api/component/input/classes/Text) instance, or null.\n */\n getDescription(): Text | null {\n return this._description;\n }\n\n /**\n * Removes the subtitle line, if one is present, and re-syncs the\n * auto-sized preferred size and the hover tooltip.\n *\n * @returns This component, for method chaining.\n */\n clearDescription(): this {\n this._description = null;\n\n this._rebuildContentRow();\n this._rebuildTooltip();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Recomposes the hover tooltip from the current title and description and\n * (re)attaches it. The tooltip body is `{title}\\n\\n{description}` when a\n * description exists, the title alone when it doesn't, and the description\n * alone when there is no title. Detaches entirely when both are empty.\n * Routed through [`Tooltip`](/api/overlay/classes/Tooltip), which renders the\n * `\\n` breaks across multiple lines.\n */\n private _rebuildTooltip(): void {\n // A suppressed tooltip stays detached whatever the text — the button is\n // showing its own popup and a hover hint would occlude it.\n if (this._tooltipSuppressed) {\n Tooltip.detach(this);\n\n return;\n }\n\n const title = this._options.text ?? \"\";\n const desc = this._description?.getText().valueOf() ?? \"\";\n\n let str: string;\n if (title && desc) {\n str = `${title}\\n\\n${desc}`;\n } else {\n str = title || desc;\n }\n\n if (str) {\n Tooltip.attach(this, str);\n } else {\n Tooltip.detach(this);\n }\n }\n\n /**\n * Suppresses or restores this button's hover tooltip. While suppressed the\n * tooltip is detached (and any visible/pending one dismissed) and stays off\n * across `setText`/`setDescription` rebuilds; restoring re-composes it from\n * the current title and description. Intended for a subclass that opens its\n * own popup — see {@link _tooltipSuppressed}.\n *\n * @param value - `true` to detach the tooltip, `false` to restore it.\n *\n * @returns This button, for method chaining.\n */\n protected setTooltipSuppressed(value: boolean): this {\n if (value === this._tooltipSuppressed) {\n return this;\n }\n\n this._tooltipSuppressed = value;\n\n this._rebuildTooltip();\n\n return this;\n }\n\n /**\n * Reflects the title into the accessible name exactly when it is hidden from\n * the button face (`showText:false`). A glyph-only face renders the title\n * blank, so without this the button would have no accessible name; the\n * stored title (`_options.text`) is mirrored into `aria-label`. When the\n * title is visible the rendered text already supplies the accessible name,\n * so any previously reflected label is cleared (e.g. after `setShowText(true)`).\n */\n private _reflectAccessibleName(): void {\n const title = this._options.text ?? \"\";\n\n if (!this._isShowText() && title !== \"\") {\n this.getAria().setLabel(title);\n } else {\n this.getAria().clearLabel();\n }\n }\n\n /**\n * Resolves the `descriptionUnderGlyph` flag with its `true` default — the\n * single place the default lives, read by {@link _rebuildContentRow}.\n *\n * @returns Whether the description aligns full-width under the glyph.\n */\n private _isDescriptionUnderGlyph(): boolean {\n return this._options.descriptionUnderGlyph ?? true;\n }\n\n /**\n * Resolves the `showDescription` flag with its `true` default.\n *\n * @returns Whether the description is rendered on the button face.\n */\n private _isShowDescription(): boolean {\n return this._options.showDescription ?? true;\n }\n\n /**\n * Resolves the `showText` flag with its `true` default.\n *\n * @returns Whether the title is rendered on the button face.\n */\n private _isShowText(): boolean {\n return this._options.showText ?? true;\n }\n\n /**\n * Classifies the active writing mode into the orientation the content row\n * must follow. The Tab strip rotates west/east labels with `sideways-rl`\n * (clockwise, reads top→bottom) and `sideways-lr` (counter-clockwise, reads\n * bottom→top); every other value — including none — lays out horizontally.\n *\n * @returns `\"cw\"`, `\"ccw\"`, or `\"horizontal\"`.\n */\n private _contentOrientation(): \"horizontal\" | \"cw\" | \"ccw\" {\n const wm = this.getWritingMode();\n\n if (wm === \"sideways-rl\") {\n return \"cw\";\n }\n\n if (wm === \"sideways-lr\") {\n return \"ccw\";\n }\n\n return \"horizontal\";\n }\n\n /**\n * Swaps `container`'s box layout to the given axis and spacing so a content\n * row follows the label's reading direction (`\"h\"` → `HBox`, `\"v\"` → `VBox`).\n *\n * @param container - The content container to re-lay out.\n * @param axis - `\"h\"` for a horizontal row, `\"v\"` for a vertical stack.\n * @param spacing - Inter-child spacing in px.\n */\n private _orientBox(container: Component, axis: \"h\" | \"v\", spacing: number): void {\n container.setLayoutManager(axis === \"v\" ? new VBox({ spacing }) : new HBox({ spacing }));\n }\n\n /**\n * Adds `children` to `container` in reading order, reversing when the axis\n * runs against the document order (a counter-clockwise inline run or a\n * clockwise block run), so the leading glyph / title stays first in the\n * rotated flow.\n *\n * @param container - The container to populate.\n * @param children - The children in their logical (unrotated) order.\n * @param reversed - Whether to add them in reverse.\n */\n private _addContentChildren(container: Component, children: Component[], reversed: boolean): void {\n const ordered = reversed ? [...children].reverse() : children;\n\n for (const child of ordered) {\n container.addComponent(child);\n }\n }\n\n /**\n * Rebuilds `_content`'s child tree from the current `_glyph` / `_text` /\n * `_description` instances and the `descriptionUnderGlyph` /\n * `showDescription` flags. The single point that (re)parents those shared\n * children, so the topology stays consistent across every glyph /\n * description / flag mutation. Never recreates `_content` itself, so its\n * identity and HBox layout — the documented subclass re-anchor seam — are\n * preserved.\n *\n * Idempotent: it empties every content container up front\n * ({@link Component.removeAllComponents} fully detaches each child), then\n * re-adds from the current field state, so it is safe to call any number\n * of times and after a field has been nulled.\n *\n * Two topologies:\n * - **under glyph** (`descriptionUnderGlyph` true, glyph + visible\n * description): `_content[ _outerColumn( _innerRow[glyph, _text], _description ) ]`\n * — the description spans full width below the glyph+title row.\n * - **under title** (the default fallback, also used whenever there is no\n * glyph, no description, or `showDescription` is false):\n * `_content[ glyph?, _titleColumn[ _text, _description? ] ]`.\n *\n * When `showDescription` is false the description is treated as absent for\n * layout — the minimal `[glyph?, _titleColumn[_text]]` tree is built and\n * `_description` is left detached but alive (so {@link _rebuildTooltip}\n * can still read it).\n */\n private _rebuildContentRow(): void {\n // Disassemble: empty every container so all shared children are\n // parentless and re-addable (add/insertComponent throw on a child that\n // still has a parent). Emptying wholesale also drops any outgoing glyph\n // / description whose field was reassigned or nulled by the caller.\n this._content.removeAllComponents();\n this._titleColumn.removeAllComponents();\n this._innerRow?.removeAllComponents();\n this._outerColumn?.removeAllComponents();\n\n // A hidden description is treated as absent for rendering; the instance\n // stays alive for the tooltip.\n const renderDesc = this._description !== null && this._isShowDescription();\n\n // A hidden title (`showText:false`) is rendered blank rather than\n // unparented: `_text` keeps its place in the no-description topology so\n // its line box still drives the button height (exactly like a genuinely\n // empty label), and the title string lives in `_options.text` to feed\n // the tooltip and the reflected `aria-label`. In the description\n // topologies the description drives the height, so a hidden title is\n // dropped outright to avoid reserving a blank title line.\n const renderText = this._isShowText();\n\n // The label's writing mode rotates both the inline (glyph→text) and the\n // block (title→description) reading directions; the content boxes have\n // to follow it, or a leading glyph lands beside vertical text instead of\n // before it. The inline run becomes a VBox and the block stack an HBox.\n // `sideways-rl` (cw) reads top→bottom with blocks running right→left;\n // `sideways-lr` (ccw) reads bottom→top with blocks running left→right —\n // hence the opposite reversals on the two axes.\n const orient = this._contentOrientation();\n const vertical = orient !== \"horizontal\";\n\n const inlineAxis: \"h\" | \"v\" = vertical ? \"v\" : \"h\";\n const blockAxis: \"h\" | \"v\" = vertical ? \"h\" : \"v\";\n const inlineReversed = orient === \"ccw\";\n const blockReversed = orient === \"cw\";\n\n if (renderDesc && this._glyph && this._isDescriptionUnderGlyph()) {\n if (!this._innerRow) {\n this._innerRow = new Component();\n this._innerRow.setInsets(new Insets(0, 0, 0, 0));\n this._innerRow.setPointerEvents(\"none\");\n }\n if (!this._outerColumn) {\n this._outerColumn = new Component();\n this._outerColumn.setInsets(new Insets(0, 0, 0, 0));\n this._outerColumn.setPointerEvents(\"none\");\n }\n\n this._orientBox(this._content, inlineAxis, 2);\n this._orientBox(this._innerRow, inlineAxis, 2);\n this._orientBox(this._outerColumn, blockAxis, 0);\n\n this._addContentChildren(this._innerRow, renderText ? [this._glyph, this._text] : [this._glyph], inlineReversed);\n this._addContentChildren(this._outerColumn, [this._innerRow, this._description!], blockReversed);\n this._content.addComponent(this._outerColumn);\n } else if (renderDesc) {\n // Title + description stack in the column so the description trails\n // the title; the glyph (if any) leads the whole stack on the inline\n // axis.\n this._orientBox(this._content, inlineAxis, 2);\n this._orientBox(this._titleColumn, blockAxis, 0);\n\n this._addContentChildren(this._titleColumn, renderText ? [this._text, this._description!] : [this._description!], blockReversed);\n\n const inlineChildren = this._glyph ? [this._glyph, this._titleColumn] : [this._titleColumn];\n this._addContentChildren(this._content, inlineChildren, inlineReversed);\n } else {\n // No description: place the title directly beside (or before) the\n // glyph rather than wrapping it in `_titleColumn`. The column reports\n // a null baseline (a plain VBox-backed Component), which would hide\n // the title's baseline from `_content` and leave the glyph centred;\n // added directly, the title's baseline is visible so the glyph drops\n // onto the text baseline — matching the description topology.\n // A glyph-only button (empty label) drops the inline spacing to 0 so\n // the glyph sits flush at the box centre: the default spacing between\n // the glyph and a zero-width label pads one side and pushes the glyph\n // off centre (the visible \"hugs the left edge\" on icon buttons). The\n // empty `_text` stays in the row so its line box still drives the\n // button height — dropping it shrinks the button vertically. A hidden\n // title renders blank (`_text.getText() === \"\"`), so it takes this\n // same glyph-only path: still parented to drive the height, spacing\n // collapsed, no title shown.\n const hasText = this._text.getText().valueOf() !== \"\";\n this._orientBox(this._content, inlineAxis, this._glyph && !hasText ? 0 : 2);\n\n const inlineChildren = this._glyph ? [this._glyph, this._text] : [this._text];\n this._addContentChildren(this._content, inlineChildren, inlineReversed);\n }\n\n // Optically centre a single line of label text. The Fit/anchor centring\n // centres the line box geometrically, but a label's ink occupies only\n // cap-top→baseline, so the empty descender band makes the text read as\n // too high. A top inset of `Util.opticalCenterOffset()` nudges it down\n // onto its optical centre. Applies only to a non-empty single-line\n // horizontal label: a two-line title+description block centres as a\n // block (offset 0), a glyph-only button (empty `_text`) is already\n // box-centred, and a rotated label has no horizontal descender band.\n const opticalOffset = (!vertical && !renderDesc && this._text.getText().valueOf() !== \"\") ? Util.opticalCenterOffset() : 0;\n\n this._content.setInsets(new Insets(opticalOffset, 0, 0, 0));\n\n this._reflectAccessibleName();\n\n this._afterRebuildContentRow();\n }\n\n /**\n * Hook invoked at the end of {@link _rebuildContentRow}, after the glyph /\n * title / description children have been re-parented onto the content row.\n * The base implementation does nothing; subclasses that attach their own\n * persistent child to `_content` (e.g. [`SplitButton`](/api/component/button/classes/SplitButton)'s\n * trailing chevron) override it to re-append that child, since the rebuild\n * empties the row wholesale via `removeAllComponents` and would otherwise\n * orphan it on a `setGlyph` / `setDescription` / writing-mode change.\n */\n protected _afterRebuildContentRow(): void {\n }\n\n /**\n * Sizes the leading glyph's box so its height matches the title's rendered\n * line-box height (the line of text it sits beside), instead of the\n * Glyph's static 16×16 default. The lever is `setPreferredSize`, not\n * `setFontSize` — a font-size token does not resize an SVG glyph's box.\n *\n * Theme-reactive: it reads `_text.getLineHeight()` (the button title's\n * additive line box, button font-size + `--ts-ui-line-padding`) and runs\n * from {@link recomputePreferredSize},\n * which already re-fires on every theme change — so the glyph re-tracks the\n * title line height without an extra listener. Bails when the line height\n * isn't resolved yet (pre-measure) so it never writes a 0/NaN size.\n *\n * Opt-out guard: it only overwrites a glyph size it itself last wrote\n * (tracked in `_glyphSyncedSize`). A subclass / consumer that pinned its\n * own glyph size (SpinButton's chevron, Tab's close ✕) leaves the current\n * size different from `_glyphSyncedSize`, so this method skips it and the\n * explicit size survives later re-syncs.\n */\n private _syncGlyphSize(): void {\n const glyph = this._glyph;\n if (!glyph) {\n return;\n }\n\n // A consumer-pinned glyph keeps its fixed size across theme changes.\n if (this._glyphSizePinned) {\n return;\n }\n\n const lineHeight = this._text.getLineHeight();\n if (lineHeight === null) {\n return;\n }\n\n const px = Math.round(lineHeight);\n\n const current = glyph.getPreferredSize();\n const ours = this._glyphSyncedSize;\n if (current && ours !== null && (current.width !== ours || current.height !== ours)) {\n return;\n }\n\n glyph.setPreferredSize({ width: px, height: px });\n this._glyphSyncedSize = px;\n }\n\n /**\n * Sets or clears an optional leading [`Glyph`](/api/component/display/classes/Glyph) shown alongside the button's text.\n *\n * @param name - Registry glyph name to display, or `null` to clear an existing glyph.\n *\n * @returns This component, for method chaining.\n *\n * @remarks\n * The glyph leads the content row. Its box is auto-sized to match the\n * title's line height; a consumer can override by sizing the returned glyph\n * explicitly via `getGlyph().setPreferredSize(...)`. Empty text combined\n * with `setGlyph(name)` therefore renders as a glyph-only button with no\n * visual artifacts at the default 0px spacing.\n */\n setGlyph(name: string): this {\n const glyph = new Glyph(name);\n glyph.setPointerEvents(\"none\");\n\n // Reassign before the rebuild; the rebuild empties the content\n // containers, detaching any previous glyph so it is dropped cleanly.\n this._glyph = glyph;\n // A fresh glyph hasn't been line-height-synced yet — clear the guard\n // so `_syncGlyphSize` (via recomputePreferredSize) sizes it.\n this._glyphSyncedSize = null;\n\n // Re-apply an instance glyph tint so it survives a glyph swap.\n if (this._options.glyphColor !== undefined) {\n glyph.setForegroundColor(this._options.glyphColor);\n }\n\n this._rebuildContentRow();\n\n // The content row's preferred size shifted — re-sync the button's\n // auto-derived preferred size (also sizes the glyph) unless the\n // consumer has pinned it.\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Removes the leading glyph from the button, if one is present.\n *\n * @returns This component, for method chaining.\n */\n clearGlyph(): this {\n this._glyph = null;\n this._glyphSyncedSize = null;\n\n this._rebuildContentRow();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns the current leading glyph component, or null if none is set.\n *\n * @returns The [`Glyph`](/api/component/display/classes/Glyph) instance, or null.\n */\n getGlyph(): Glyph | null {\n return this._glyph;\n }\n\n /**\n * Tints the leading glyph independently of the button's `foregroundColor`.\n * `foregroundColor` sets `color` on the whole button, which both the title\n * and the glyph (via its `currentColor` SVG fill) inherit; this sets `color`\n * on the glyph element alone, so the glyph can be tinted while the title\n * keeps the button's own colour. Stored so a later {@link setGlyph} re-applies\n * it. A no-op on the element until a glyph exists; the colour still takes\n * effect once one is set.\n *\n * @param color - A CSS color string.\n *\n * @returns This component, for method chaining.\n */\n setGlyphColor(color: string): this {\n this._options.glyphColor = color;\n this._glyph?.setForegroundColor(color);\n\n return this;\n }\n\n /**\n * Tints the description subtitle independently of the button's\n * `foregroundColor`, overriding the default dim description colour. Stored so\n * a lazily-created description ({@link setDescription}) picks it up. A no-op on\n * the element until a description exists.\n *\n * @param color - A CSS color string.\n *\n * @returns This component, for method chaining.\n */\n setDescriptionColor(color: string): this {\n this._options.descriptionColor = color;\n this._description?.setForegroundColor(color);\n\n return this;\n }\n\n /**\n * Pins the leading glyph to a fixed square size and opts it out of the\n * theme-reactive line-height sync. Use for compact icon buttons whose glyph\n * must stay a constant size regardless of the active theme's font metrics —\n * a tab's close ✕, a spinner chevron. A plain\n * `getGlyph().setPreferredSize(...)` is unreliable here: if it runs before\n * the button is attached (line height unresolved), the first theme change\n * re-tracks the glyph to the title line height and grows it. This sets the\n * authoritative `_glyphSizePinned` opt-out so the sync skips the glyph.\n *\n * @param px - The square glyph size in pixels.\n *\n * @returns This button, for chaining.\n */\n pinGlyphSize(px: number): this {\n this._glyphSizePinned = true;\n\n this._glyph?.setPreferredSize({ width: px, height: px });\n\n return this;\n }\n\n /**\n * Returns the offset from the top of the button to the label's text baseline.\n *\n * @returns The baseline offset in pixels, or `null` when the label has no baseline.\n */\n getBaseline(): number | null {\n return this.wrapInnerBaseline(this._text.getBaseline());\n }\n\n /**\n * Registers a listener for this button's `\"action\"` event — fired on\n * click. A typed semantic shorthand over {@link Event.addListener}\n * (the underlying DOM event is `\"click\"`); `\"action\"` is currently the\n * only allowed event name.\n *\n * @param event - The event name. Only `\"action\"` is accepted.\n * @param listener - The callback to invoke when the button is actioned.\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, \"click\", listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"action\"` listener. A typed shorthand\n * over {@link Event.removeListener}; the exact callback reference must\n * 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, \"click\", listener);\n\n return this;\n }\n\n /**\n * Programmatically actions the button, as if the user had clicked it:\n * fires this button's own `\"click\"` event so every registered\n * {@link on | `\"action\"`} handler runs. Provided so other components can\n * drive a button through its named surface rather than synthesising its\n * DOM event from the outside — e.g. [`ToolBar`](/api/component/menubar/classes/ToolBar)\n * replays an overflowed button's action from its dropdown. A\n * [`ToggleButton`](/api/component/button/classes/ToggleButton)\n * inherits this and toggles, because its internal toggle is wired to the\n * same `\"click\"`.\n *\n * @returns This button, for method chaining.\n */\n click(): this {\n Event.fireEvent(this, \"click\");\n\n return this;\n }\n\n /**\n * Registers a `pointerdown` event listener on this button. The named\n * surface lets external consumers (e.g.\n * [`AbstractPickerField`](/api/component/input/classes/AbstractPickerField))\n * route through the component rather than reaching for\n * `Event.addListener(button, \"pointerdown\", ...)` directly, preserving\n * the framework's named-listener contract.\n *\n * @param listener - Called with the originating PointerEvent.\n *\n * @returns This component, for method chaining.\n */\n addPointerDownListener(listener: Event.Listener): this {\n Event.addListener(this, \"pointerdown\", listener);\n\n return this;\n }\n\n /**\n * Returns whether this button is currently in `chromeless` mode (no\n * border / shadow / gradient / pressed-hover treatments).\n *\n * @returns True when chrome dispatches are gated off.\n */\n isChromeless(): boolean {\n return this._options.chromeless ?? this._defaultOptions.chromeless ?? false;\n }\n\n /**\n * Toggles the `chromeless` flag and reconciles the DOM. When flipping\n * to `true`, clears every chrome property currently on the element\n * before recording the flag. When flipping to `false`, restores the\n * chromeful defaults from `_defaultOptions` (which retains both\n * Button's base defaults and any subclass chrome layered in at\n * construction, so the round-trip is loss-free for both sources).\n *\n * @param value - The new chromeless state.\n *\n * @returns This component, for method chaining.\n *\n * @remarks Consumer-supplied chrome that came in via the caller's\n * `options` bag (rather than the subclass's `_defaultOptions`) is not\n * recovered by `setChromeless(false)` — only the defaults round-trip.\n */\n setChromeless(value: boolean): this {\n if (this.isChromeless() === value) {\n return this;\n }\n\n if (value) {\n // Clear the DOM before flipping the flag. The clear* setters\n // are not gated today, but the ordering keeps the intent\n // self-evident if a future change does gate them.\n this._clearChrome();\n this._options.chromeless = true;\n\n // Chromeless wins over flat: `_clearChrome` already stripped the\n // flat hover/pressed treatments, so drop the flat flag too to keep\n // `isFlat()` honest.\n this._flat = false;\n this._options.flat = false;\n } else {\n // Flip the flag first so the clear/set side-effects on the\n // restore path are not intercepted by anything that might\n // later gate them.\n this._options.chromeless = false;\n this._restoreChrome();\n }\n\n return this;\n }\n\n /**\n * Clears every chrome property the framework defaults touch. Pressed\n * and hover rules are only touched when their lazy backing slots have\n * already been allocated — calling the `clearX` setters when nothing\n * was ever installed would touch the lazy getters and acquire empty\n * orphan rules.\n */\n private _clearChrome(): void {\n this.clearBorder();\n this.clearBorderRadius();\n this.clearShadow();\n this.clearBackgroundImage();\n\n if (this._pressedStyleRule !== undefined) {\n this.clearPressedBackgroundColor();\n this.clearPressedBackgroundImage();\n this.clearPressedForegroundColor();\n this.clearPressedShadow();\n this.clearPressedBorderRadius();\n this.clearPressedBorder();\n }\n\n if (this._hoverStyleRule !== undefined) {\n this.clearHoverBackgroundColor();\n this.clearHoverBackgroundImage();\n this.clearHoverForegroundColor();\n this.clearHoverShadow();\n this.clearHoverBorderRadius();\n this.clearHoverBorder();\n }\n }\n\n /**\n * Re-applies chrome from `_defaultOptions`, which carries both Button's\n * base defaults and any subclass chrome layered in at construction via\n * the third constructor arg. Consumer-supplied chrome (from the\n * caller's options bag, not `_defaultOptions`) is not recovered here.\n */\n private _restoreChrome(): void {\n const d = this._defaultOptions as ButtonOptions;\n\n // Re-apply the chromeful resting background `_applyFlatChrome` cleared.\n // Flat set the sentinel \"transparent\" only when neither the consumer nor\n // a subclass default pinned a colour (otherwise that colour was\n // preserved through flat and needs no restore); swap that sentinel back\n // for the framework token so the un-flattened button regains its fill.\n if (d.backgroundColor === undefined && this._options.backgroundColor === \"transparent\") {\n this.setBackgroundColor(BUTTON_RESTING_BACKGROUND);\n }\n\n if (d.border !== undefined) this.setBorder(d.border);\n if (d.borderRadius !== undefined) this.setBorderRadius(d.borderRadius);\n if (d.shadow !== undefined) this.setShadow(d.shadow);\n if (d.backgroundImage !== undefined) this.setBackgroundImage(d.backgroundImage);\n\n if (d.pressedForegroundColor !== undefined) this.setPressedForegroundColor(d.pressedForegroundColor);\n if (d.pressedBackgroundColor !== undefined) this.setPressedBackgroundColor(d.pressedBackgroundColor);\n if (d.pressedBackgroundImage !== undefined) this.setPressedBackgroundImage(d.pressedBackgroundImage);\n if (d.pressedShadow !== undefined) this.setPressedShadow (d.pressedShadow);\n if (d.pressedBorder !== undefined) this.setPressedBorder (d.pressedBorder);\n else this.clearPressedBorder ();\n if (d.pressedBorderRadius !== undefined) this.setPressedBorderRadius (d.pressedBorderRadius);\n\n if (d.hoverForegroundColor !== undefined) this.setHoverForegroundColor (d.hoverForegroundColor);\n if (d.hoverBackgroundColor !== undefined) this.setHoverBackgroundColor (d.hoverBackgroundColor);\n if (d.hoverBackgroundImage !== undefined) this.setHoverBackgroundImage (d.hoverBackgroundImage);\n if (d.hoverShadow !== undefined) this.setHoverShadow (d.hoverShadow);\n if (d.hoverBorder !== undefined) this.setHoverBorder (d.hoverBorder);\n else this.clearHoverBorder ();\n if (d.hoverBorderRadius !== undefined) this.setHoverBorderRadius (d.hoverBorderRadius);\n }\n\n /**\n * Returns whether this button is currently in `flat` mode (no resting\n * border / shadow / gradient; a light frame on hover and a sunken inset\n * frame on press).\n *\n * @returns True when the flat appearance is applied.\n */\n isFlat(): boolean {\n return this._flat ?? false;\n }\n\n /**\n * Toggles the classical `flat` appearance and reconciles the DOM. When\n * flipping to `true`, suppresses the resting frame and installs the flat\n * hover / pressed treatments from the `--ts-ui-button-flat-*` tokens; when\n * flipping to `false`, restores the raised chrome from `_defaultOptions`\n * via the same round-trip `setChromeless(false)` uses. Mutually exclusive\n * with `chromeless` (chromeless wins): `setFlat(true)` no-ops with a\n * dev-time warning when the button is chromeless.\n *\n * @param value - The new flat state.\n *\n * @returns This component, for method chaining.\n *\n * @remarks Consumer-supplied chrome that came in via the caller's\n * `options` bag (rather than the subclass's `_defaultOptions`) is not\n * recovered by `setFlat(false)` — only the defaults round-trip, matching\n * the documented `setChromeless(false)` tradeoff.\n */\n setFlat(value: boolean): this {\n if (value && this.isChromeless()) {\n console.warn(\"Button #\" + this.getId() + \": setFlat(true) ignored — flat and chromeless are mutually exclusive, chromeless wins.\");\n\n return this;\n }\n\n if ((this._flat ?? false) === value) {\n return this;\n }\n\n if (value) {\n this._flat = true;\n this._options.flat = true;\n this._applyFlatChrome();\n } else {\n this._flat = false;\n this._options.flat = false;\n this._restoreChrome();\n // `_restoreChrome` recovers borders/shadows but not insets, so a\n // flat glyph-only button un-flattened here would keep its tight\n // square. Re-resolve so it falls back to the default (or compact)\n // perimeter for its new state.\n this._resolveInsets();\n }\n\n return this;\n }\n\n /**\n * Returns whether this button is currently in `compact` mode (tighter\n * symmetric insets).\n *\n * @returns True when compact rendering is applied.\n */\n isCompact(): boolean {\n return this._compact ?? false;\n }\n\n /**\n * Toggles compact rendering. When `true`, the button's inset perimeter\n * tightens — text buttons to `(2,6,2,6)`, glyph-only buttons to a\n * `(2,2,2,2)` square — so the button reads denser (e.g. packed into a\n * [`ToolBar`](/api/component/menubar/classes/ToolBar)); `false` restores the\n * default `(5,10,5,10)` (or the flat glyph square, if flat). Orthogonal to\n * `flat`: a button can be compact, flat, both, or neither, and compact\n * insets win over the flat square when both apply.\n *\n * @param value - The new compact state.\n *\n * @returns This component, for method chaining.\n */\n setCompact(value: boolean): this {\n if ((this._compact ?? false) === value) {\n return this;\n }\n\n this._compact = value;\n this._options.compact = value;\n this._resolveInsets();\n\n return this;\n }\n\n /**\n * Suppresses the resting frame (border / radius / shadow / gradient) the\n * same way the chromeless branch does, masks the inherited raised\n * `pressedX` / `hoverX` defaults so a re-apply can't leak them, then\n * installs the flat hover and sunken-pressed treatments sourced from the\n * `--ts-ui-button-flat-*` tokens through the existing lazy style-rule\n * setters. Glyph-only buttons (a glyph with an empty label) tighten to a\n * compact square inset so they read as toolbar icon buttons.\n */\n private _applyFlatChrome(): void {\n // Reserve the 1px frame at rest with a transparent border (rather than\n // clearing it like the chromeless branch). Under `box-sizing: border-box`\n // a border consumes content-box space, so a border that only appears on\n // `:hover` / `:active` would nudge the centred label by 1px. Reserving it\n // transparent keeps the geometry identical across states — the hover /\n // pressed rules below only swap the border *colour*. Matches the 1px\n // width of the `--ts-ui-button-flat-{hover,pressed}-border` tokens;\n // widen this in lockstep if those ever exceed 1px.\n this.setBorder(\"1px solid transparent\");\n\n // Clear (not just mask) the resting radius, shadow, and background image:\n // the chromeful `super.applyChromeOptions` already wrote these defaults\n // (`--ts-ui-border-radius`, `--ts-ui-button-shadow`, `--ts-ui-button-bg`)\n // into the rules, so nulling only the options would leave them painting\n // behind a \"flat\" button — a drop shadow ringing the button and rounded\n // corners.\n this.clearBorderRadius();\n this.clearShadow();\n this.clearBackgroundImage();\n\n // Drop the resting background *colour* too — flat is transparent at\n // rest. The chromeful path wrote `BUTTON_RESTING_BACKGROUND` into the\n // rule via `setBackgroundColor` (see `applyChromeOptions`), which also\n // stored it in `_options.backgroundColor`. A runtime `setFlat(true)` on\n // an already-rendered chromeful button therefore sees that token here,\n // not `undefined`; recognising it lets us override the rule to\n // transparent while still preserving a genuine consumer-set colour (the\n // old field-only write left the token painting). `_restoreChrome`\n // re-applies the token on `setFlat(false)`.\n const restingBackground = this.getBackgroundColor();\n\n if (restingBackground === undefined || restingBackground === BUTTON_RESTING_BACKGROUND) {\n this.setBackgroundColor(\"transparent\");\n }\n\n // Clear the inherited raised pressed/hover background images and the\n // raised hover shadow (written by the chromeful path above) so they\n // don't paint under the flat hover / pressed colours installed below.\n this.clearPressedBackgroundImage();\n this.clearHoverBackgroundImage();\n this.clearHoverShadow();\n\n // Install the flat hover frame + sunken pressed frame from the tokens.\n this.setHoverBackgroundColor(\"var(--ts-ui-button-flat-hover-bg, rgba(0, 0, 0, 0.06))\");\n this.setHoverBorder(\"var(--ts-ui-button-flat-hover-border, 1px solid rgb(200, 200, 200))\");\n this.setPressedBackgroundColor(\"var(--ts-ui-button-flat-pressed-bg, rgba(0, 0, 0, 0.10))\");\n this.setPressedShadow(\"var(--ts-ui-button-flat-pressed-shadow, inset 1px 1px 3px rgba(0, 0, 0, 0.25))\");\n this.setPressedBorder(\"var(--ts-ui-button-flat-pressed-border, 1px solid rgb(180, 180, 180))\");\n\n // Re-resolve the inset perimeter for the new flat state. Runs through\n // the shared resolver so the construction-time flat path — where\n // `_glyph` / `_text` don't yet exist during the super cascade — can\n // re-apply it from the constructor body once the content row is built.\n this._resolveInsets();\n }\n\n /**\n * Resolves and applies the button's inset perimeter from the three live\n * inputs — `_compact`, `_flat`, and whether the button is glyph-only (a\n * glyph with an empty label). Compact wins over the flat square so a compact\n * flat icon tightens to `(2,2,2,2)` rather than the non-compact flat\n * `(4,4,4,4)`. The resolver computes an absolute target each call (never a\n * delta), so the construction-time double-invocation (the cascade's\n * `_applyFlatChrome` before the content row exists, then the constructor's\n * re-evaluation) and later `setCompact` / `setFlat` flips all converge on\n * the same answer without drift.\n *\n * No-ops until the content row is built — `_text` is assigned in the\n * constructor body, after the super cascade that first fires the flat /\n * compact setters; the constructor re-invokes this once the row exists.\n */\n private _resolveInsets(): void {\n if (this._text === undefined) {\n return;\n }\n\n const glyphOnly = this._glyph !== null && this._text.getText().valueOf() === \"\";\n\n let insets: Insets;\n\n if (this._compact && glyphOnly) {\n insets = BUTTON_COMPACT_INSETS_GLYPH;\n } else if (this._compact) {\n insets = BUTTON_COMPACT_INSETS_TEXT;\n } else if (this._flat && glyphOnly) {\n insets = BUTTON_FLAT_GLYPH_INSETS;\n } else {\n insets = BUTTON_DEFAULT_INSETS;\n }\n\n this.setInsets(insets);\n }\n\n /**\n * Updates the button's insets. Overrides Component's setter so the\n * auto-sized preferred size re-syncs to the new inset perimeter\n * without explicit consumer prodding (subclasses like `MenuBarButton`\n * change insets in their constructor body).\n *\n * @param insets - The new perimeter insets.\n * @returns This component, for method chaining.\n */\n setInsets(insets: Insets): this {\n super.setInsets(insets);\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Records a consumer-supplied preferred size. Flips\n * `_consumerSetPreferredSize` so future auto-fires from\n * `recomputePreferredSize` no-op — the consumer's explicit intent\n * wins permanently for the lifetime of this instance.\n *\n * @param size - The preferred size in pixels.\n * @returns This component, for method chaining.\n */\n setPreferredSize(size: Size): this {\n this._consumerSetPreferredSize = true;\n super.setPreferredSize(size);\n\n return this;\n }\n\n /**\n * Returns the preferred size, derived live from the content row +\n * perimeter while the consumer hasn't pinned one. Deriving live (rather\n * than reading a cached value) means\n * the button always tracks its current label / glyph: a {@link setText}\n * that grows or shrinks the label is reflected the next time the parent\n * layout queries this button, with no manual recompute call needed at the\n * mutation site.\n *\n * When the consumer has supplied an explicit `preferredSize`, that pinned\n * value (recorded by {@link setPreferredSize}) wins via `super`.\n *\n * @returns The preferred `{width, height}`.\n */\n getPreferredSize(): Size | null {\n // A consumer-pinned size or a class-level `preferredSize` default both\n // pin the button rather than deriving from content. The default now\n // lives in `_defaultOptions` (it is no longer dispatched through\n // `setPreferredSize`, so it never flips `_consumerSetPreferredSize`);\n // honour it here so subclasses like TabCloseButton keep their fixed size.\n if (this._consumerSetPreferredSize || this._defaultOptions.preferredSize) {\n return super.getPreferredSize();\n }\n\n return this.computePreferredSize();\n }\n\n /**\n * Notifies the parent layout that this button's content-derived size may\n * have changed, so it re-queries {@link getPreferredSize} (which derives\n * live). Pushes the freshly computed size through Component's setter both\n * to fire the parent-relayout notification and to dedupe (the setter\n * no-ops when the size is unchanged). Auto-fires from the end of the\n * constructor, `setGlyph`, `clearGlyph`, `setInsets`, and the registered\n * `ThemeManager.onThemeChange` handler — the content mutations that do\n * not bubble a preferred-size change on their own (a label `setText`\n * already bubbles via its own measurement).\n *\n * No-ops when the consumer has supplied an explicit `preferredSize`\n * (Button's `setPreferredSize` override records that intent).\n *\n * Subclasses customise the size by overriding {@link computePreferredSize}\n * rather than touching this method — the consumer-flag and auto-fire\n * wiring stays here.\n */\n protected recomputePreferredSize(): void {\n // Size the glyph to the title line box first, above the consumer-pinned\n // early-return, so the glyph still line-height-matches even when the\n // button's own size is frozen. Subclasses that pin their glyph size opt\n // out via _syncGlyphSize's per-glyph guard, not this early-return.\n this._syncGlyphSize();\n\n if (this._consumerSetPreferredSize || this._defaultOptions.preferredSize) {\n return;\n }\n\n const size = this.computePreferredSize();\n\n // Bypass our own override of setPreferredSize so the consumer flag\n // doesn't flip on an auto-fire. `super` is `Component`. The written\n // value isn't read back for sizing (getPreferredSize derives live);\n // the call stands in for the parent-relayout notification + dedupe.\n super.setPreferredSize({ width: size.width, height: size.height });\n }\n\n /**\n * Computes the auto-sized preferred size from the content row's\n * preferred size plus this button's perimeter (insets + border).\n * Mirrors `Fit.getPreferredSize`'s use of `getPerimeterSize` so the\n * border width isn't truncated off the text. Subclasses override to\n * alter — the typical case is replacing the derived height with a\n * fixed token (see `MenuBarButton`).\n *\n * @returns The `{ width, height }` Button reports as its preferred size\n * while the consumer hasn't pinned one.\n */\n protected computePreferredSize(): { width: number; height: number } {\n const content = this._content?.getPreferredSize() ?? { width: 0, height: 0 };\n const perim = this.getPerimeterSize();\n\n return {\n width: content.width + perim.left + perim.right,\n height: content.height + perim.top + perim.bottom,\n };\n }\n\n /**\n * Returns the background color applied when the button is in the :active state.\n *\n * @returns The CSS color string, or null if not set.\n */\n getPressedBackgroundColor(): string | null {\n return this._options.pressedBackgroundColor ?? this._defaultOptions.pressedBackgroundColor ?? null;\n }\n\n /**\n * Sets the background color for the :active CSS rule.\n *\n * @param backgroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedBackgroundColor(backgroundColor: string): this {\n this._options.pressedBackgroundColor = backgroundColor;\n this.pressedStyleRule.set(\"backgroundColor\", backgroundColor);\n\n return this;\n }\n\n /**\n * Removes the background-color from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBackgroundColor(): this {\n this._options.pressedBackgroundColor = undefined;\n this.pressedStyleRule.set(\"backgroundColor\", null);\n\n return this;\n }\n\n /**\n * Returns the background image applied when the button is in the :active state.\n *\n * @returns The CSS background-image string, or null if not set.\n */\n getPressedBackgroundImage(): string | null {\n return this._options.pressedBackgroundImage ?? this._defaultOptions.pressedBackgroundImage ?? null;\n }\n\n /**\n * Sets the background image for the :active CSS rule.\n *\n * @param backgroundImage - Optional. A CSS background-image string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedBackgroundImage(backgroundImage: string): this {\n this._options.pressedBackgroundImage = backgroundImage;\n this.pressedStyleRule.set(\"backgroundImage\", backgroundImage);\n\n return this;\n }\n\n /**\n * Removes the background-image from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBackgroundImage(): this {\n this._options.pressedBackgroundImage = undefined;\n this.pressedStyleRule.set(\"backgroundImage\", null);\n\n return this;\n }\n\n /**\n * Returns the text color applied when the button is in the :active state.\n *\n * @returns The CSS color string, or null if not set.\n */\n getPressedForegroundColor(): string | null {\n return this._options.pressedForegroundColor ?? this._defaultOptions.pressedForegroundColor ?? null;\n }\n\n /**\n * Sets the text color for the :active CSS rule.\n *\n * @param foregroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedForegroundColor(foregroundColor: string): this {\n this._options.pressedForegroundColor = foregroundColor;\n this.pressedStyleRule.set(\"color\", foregroundColor);\n\n return this;\n }\n\n /**\n * Removes the color (foreground) from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedForegroundColor(): this {\n this._options.pressedForegroundColor = undefined;\n this.pressedStyleRule.set(\"color\", null);\n\n return this;\n }\n\n /**\n * Returns the border applied when the button is in the :active state.\n *\n * @returns The {@link BorderOptions} for the :active state, or null if not set.\n */\n getPressedBorder(): BorderOptions | null {\n return this._pressedBorder;\n }\n\n /**\n * Sets the border for the :active CSS rule. Accepts either a {@link BorderOptions}\n * bag or a CSS `border` shorthand string (sugar for `{ border: <string> }`).\n *\n * @param options - Border configuration, a CSS `border` shorthand string, or omitted for a `none` border.\n *\n * @returns This component, for method chaining.\n */\n setPressedBorder(options?: BorderOptions | string): this {\n this._pressedBorder = typeof options === \"string\" ? { border: options } : (options ?? {});\n this.pressedStyleRule.setMany(borderToStyle(this._pressedBorder));\n\n return this;\n }\n\n /**\n * Removes the border from the :active CSS rule, reverting the pressed\n * state to no explicit border. Lets the un-flatten / un-chromeless round\n * trip strip a flat pressed border that has no raised default to restore.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBorder(): this {\n this._pressedBorder = null;\n this.pressedStyleRule.setMany({\n borderTop: null,\n borderRight: null,\n borderBottom: null,\n borderLeft: null,\n });\n\n return this;\n }\n\n /**\n * Returns the border radius applied when the button is in the :active state.\n *\n * @returns The CSS border-radius string, or null if not set.\n */\n getPressedBorderRadius(): string | null {\n return this._options.pressedBorderRadius ?? null;\n }\n\n /**\n * Sets the border radius for the :active CSS rule.\n *\n * @param borderRadius - Optional. A CSS border-radius string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedBorderRadius(borderRadius: string): this {\n this._options.pressedBorderRadius = borderRadius;\n this.pressedStyleRule.set(\"borderRadius\", borderRadius);\n\n return this;\n }\n\n /**\n * Removes the border-radius from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBorderRadius(): this {\n this._options.pressedBorderRadius = undefined;\n this.pressedStyleRule.set(\"borderRadius\", null);\n\n return this;\n }\n\n /**\n * Returns the box shadow applied when the button is in the :active state.\n *\n * @returns The CSS box-shadow string, or null if not set.\n */\n getPressedShadow(): string | null {\n return this._options.pressedShadow ?? this._defaultOptions.pressedShadow ?? null;\n }\n\n /**\n * Sets the box shadow for the :active CSS rule.\n *\n * @param shadow - A CSS box-shadow string, or null to set the shadow to \"none\".\n *\n * @returns This component, for method chaining.\n */\n setPressedShadow(shadow: string): this {\n this._options.pressedShadow = shadow;\n this.pressedStyleRule.set(\"boxShadow\", shadow);\n\n return this;\n }\n\n /**\n * Removes the box-shadow from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedShadow(): this {\n this._options.pressedShadow = undefined;\n this.pressedStyleRule.set(\"boxShadow\", null);\n\n return this;\n }\n\n /**\n * Returns the background color applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS color string, or null if not set.\n */\n getHoverBackgroundColor(): string | null {\n return this._options.hoverBackgroundColor ?? this._defaultOptions.hoverBackgroundColor ?? null;\n }\n\n /**\n * Sets the background color for the `:hover:not(:active)` CSS rule.\n *\n * @param backgroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverBackgroundColor(backgroundColor: string): this {\n this._options.hoverBackgroundColor = backgroundColor;\n this.hoverStyleRule.set(\"backgroundColor\", backgroundColor);\n\n return this;\n }\n\n /**\n * Removes the background-color from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBackgroundColor(): this {\n this._options.hoverBackgroundColor = undefined;\n this.hoverStyleRule.set(\"backgroundColor\", null);\n\n return this;\n }\n\n /**\n * Returns the background image applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS background-image string, or null if not set.\n */\n getHoverBackgroundImage(): string | null {\n return this._options.hoverBackgroundImage ?? this._defaultOptions.hoverBackgroundImage ?? null;\n }\n\n /**\n * Sets the background image for the `:hover:not(:active)` CSS rule.\n *\n * @param backgroundImage - A CSS background-image string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverBackgroundImage(backgroundImage: string): this {\n this._options.hoverBackgroundImage = backgroundImage;\n this.hoverStyleRule.set(\"backgroundImage\", backgroundImage);\n\n return this;\n }\n\n /**\n * Removes the background-image from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBackgroundImage(): this {\n this._options.hoverBackgroundImage = undefined;\n this.hoverStyleRule.set(\"backgroundImage\", null);\n\n return this;\n }\n\n /**\n * Returns the text color applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS color string, or null if not set.\n */\n getHoverForegroundColor(): string | null {\n return this._options.hoverForegroundColor ?? null;\n }\n\n /**\n * Sets the text color for the `:hover:not(:active)` CSS rule.\n *\n * @param foregroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverForegroundColor(foregroundColor: string): this {\n this._options.hoverForegroundColor = foregroundColor;\n this.hoverStyleRule.set(\"color\", foregroundColor);\n\n return this;\n }\n\n /**\n * Removes the color (foreground) from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverForegroundColor(): this {\n this._options.hoverForegroundColor = undefined;\n this.hoverStyleRule.set(\"color\", null);\n\n return this;\n }\n\n /**\n * Returns the border applied when the pointer is over the button (but not pressed).\n *\n * @returns The {@link BorderOptions} for the hover state, or null if not set.\n */\n getHoverBorder(): BorderOptions | null {\n return this._hoverBorder;\n }\n\n /**\n * Sets the border for the `:hover:not(:active)` CSS rule. Accepts either a\n * {@link BorderOptions} bag or a CSS `border` shorthand string (sugar for\n * `{ border: <string> }`, e.g. `\"1px solid rgb(...)\"` or `\"none\"`). The four\n * CSS longhands are written so a per-side hover border survives.\n *\n * @param options - Border configuration, a CSS `border` shorthand string, or omitted for a `none` border.\n *\n * @returns This component, for method chaining.\n */\n setHoverBorder(options?: BorderOptions | string): this {\n this._hoverBorder = typeof options === \"string\" ? { border: options } : (options ?? {});\n this.hoverStyleRule.setMany(borderToStyle(this._hoverBorder));\n\n return this;\n }\n\n /**\n * Removes the border from the :hover CSS rule, reverting the hover state\n * to no explicit border. Lets the un-flatten / un-chromeless round trip\n * strip a flat hover border that has no raised default to restore.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBorder(): this {\n this._hoverBorder = null;\n this.hoverStyleRule.setMany({\n borderTop: null,\n borderRight: null,\n borderBottom: null,\n borderLeft: null,\n });\n\n return this;\n }\n\n /**\n * Returns the border radius applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS border-radius string, or null if not set.\n */\n getHoverBorderRadius(): string | null {\n return this._options.hoverBorderRadius ?? null;\n }\n\n /**\n * Sets the border radius for the `:hover:not(:active)` CSS rule.\n *\n * @param borderRadius - A CSS border-radius string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverBorderRadius(borderRadius: string): this {\n this._options.hoverBorderRadius = borderRadius;\n this.hoverStyleRule.set(\"borderRadius\", borderRadius);\n\n return this;\n }\n\n /**\n * Removes the border-radius from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBorderRadius(): this {\n this._options.hoverBorderRadius = undefined;\n this.hoverStyleRule.set(\"borderRadius\", null);\n\n return this;\n }\n\n /**\n * Returns the box shadow applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS box-shadow string, or null if not set.\n */\n getHoverShadow(): string | null {\n return this._options.hoverShadow ?? this._defaultOptions.hoverShadow ?? null;\n }\n\n /**\n * Sets the box shadow for the `:hover:not(:active)` CSS rule.\n *\n * @param shadow - A CSS box-shadow string, or null to set the shadow to \"none\".\n *\n * @returns This component, for method chaining.\n */\n setHoverShadow(shadow: string): this {\n this._options.hoverShadow = shadow;\n this.hoverStyleRule.set(\"boxShadow\", shadow);\n\n return this;\n }\n\n /**\n * Removes the box-shadow from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverShadow(): this {\n this._options.hoverShadow = undefined;\n this.hoverStyleRule.set(\"boxShadow\", null);\n\n return this;\n }\n\n /**\n * Enables or disables the button.\n *\n * @param enabled - True to enable, false to disable.\n *\n * @remarks\n * When disabled, sets the native `disabled` attribute on the underlying\n * `<button>` element (which suppresses pointer events and `:active`),\n * dims the button to 0.5 opacity, and switches the cursor to `not-allowed`.\n * Re-enabling restores the previous cursor and clears the opacity override.\n */\n setEnabled(enabled: boolean): this {\n if ((this._options.enabled ?? true) === enabled) {\n return this;\n }\n\n this._options.enabled = enabled;\n\n if (enabled) {\n this.setDisabledAttribute(false);\n this.clearOpacity();\n this.setCursor(this._enabledCursor);\n } else {\n this._enabledCursor = this.getCursor() ?? \"pointer\";\n this.setDisabledAttribute(true);\n this.setOpacity(0.5);\n this.setCursor(\"not-allowed\");\n }\n\n return this;\n }\n\n /**\n * Returns whether the button is currently enabled.\n *\n * @returns True if the button accepts user interaction.\n */\n isEnabled(): boolean {\n return this._options.enabled ?? true;\n }\n\n /**\n * Sets whether a description aligns full-width below the glyph (`true`,\n * default) or indented under the title text beside the glyph (`false`).\n * Only has a visible effect when the button has both a leading glyph and a\n * visible description; otherwise both modes resolve to the same minimal\n * row. Restructures the content row live.\n *\n * @param value - `true` for under-glyph (full width), `false` for under-title.\n *\n * @returns This component, for method chaining.\n */\n setDescriptionUnderGlyph(value: boolean): this {\n this._options.descriptionUnderGlyph = value;\n\n this._rebuildContentRow();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns whether the description aligns full-width under the glyph.\n *\n * @returns `true` for under-glyph (the default), `false` for under-title.\n */\n isDescriptionUnderGlyph(): boolean {\n return this._isDescriptionUnderGlyph();\n }\n\n /**\n * Sets whether the description is rendered on the button face. When\n * `false`, the button shows only its glyph and title, but the description\n * still appears in the hover tooltip — the text is stored either way. Does\n * not touch the tooltip (which is unaffected by this flag).\n *\n * @param value - `true` (default) to show the description on the button, `false` to hide it.\n *\n * @returns This component, for method chaining.\n */\n setShowDescription(value: boolean): this {\n this._options.showDescription = value;\n\n this._rebuildContentRow();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns whether the description is rendered on the button face.\n *\n * @returns `true` when the description shows on the button (the default), `false` when it is tooltip-only.\n */\n isShowDescription(): boolean {\n return this._isShowDescription();\n }\n\n /**\n * Sets whether the title is rendered on the button face. When `false`, the\n * button shows only its glyph, but the title still drives the hover tooltip\n * and is reflected into `aria-label` as the accessible name — the title is\n * stored either way. Toggling back to `true` restores the on-face label and\n * clears the reflected `aria-label`.\n *\n * @param value - `true` (default) to show the title on the button, `false` to hide it.\n *\n * @returns This component, for method chaining.\n */\n setShowText(value: boolean): this {\n this._options.showText = value;\n\n // Re-render `_text` to the title or blank, then rebuild the row and\n // re-resolve insets — hiding the title flips the button to glyph-only,\n // which changes both the inset perimeter and the glyph-only spacing.\n this._text.setText(value ? (this._options.text ?? \"\") : \"\");\n this._rebuildContentRow();\n this._resolveInsets();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns whether the title is rendered on the button face.\n *\n * @returns `true` when the title shows on the button (the default), `false` when it is tooltip-only.\n */\n isShowText(): boolean {\n return this._isShowText();\n }\n}\n\nconst ButtonCallable = callable(Button);\ntype ButtonCallable<TOptions extends ButtonOptions = ButtonOptions> = Button<TOptions>;\nexport {\n Button as _Button,\n ButtonCallable as Button\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Button-bjpWruNc.js","names":[],"sources":["../../src/typescript/lib/component/button/Button.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 { Fit } from \"~/layout/Fit.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { FillType } from \"~/layout/FillType.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { BorderOptions, borderToStyle } from \"~/primitive/Border.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Size } from \"~/primitive/Size.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Util } from \"~/core/Util.js\";\n\n/**\n * String-literal union of the events emitted by {@link Button}. A typed\n * shorthand over [`Event.addListener`](/api/core/classes/Component) /\n * [`Event.removeListener`](/api/core/classes/Component) — Button does not\n * own a [`ListenerBag`](/api/core/classes/ListenerBag); the DOM `\"click\"`\n * event is dispatched through the framework's window-level capture handler.\n *\n * @category Components\n */\nexport type ButtonEvent = \"action\";\n\n/**\n * The handler shape consumers register for the `\"click\"` button event.\n *\n * @category Components\n */\nexport type ClickListener = (event: MouseEvent) => Event.ListenerResult;\n\n/**\n * Construction-time options for {@link Button}.\n *\n * @category Components\n */\nexport interface ButtonOptions extends ComponentOptions {\n text?: string;\n\n /**\n * Optional subtitle rendered on a second line *below* the title, in a\n * smaller, dimmer style. Dispatched late (mirroring `text`) so it lands\n * once the content row's children exist. Drives the hover tooltip\n * alongside `text` — see [`setDescription`](/api/component/button/classes/Button#setdescription).\n */\n description?: string;\n\n /**\n * When a leading glyph *and* a description are both present, controls\n * where the description aligns. `true` (default) spans the description\n * full-width *below* the glyph+title row, its left edge under the glyph.\n * `false` indents it under the title text, beside the glyph. No visible\n * effect without both a glyph and a description, or when `showDescription`\n * is `false`. Runtime counterpart: `setDescriptionUnderGlyph`.\n */\n descriptionUnderGlyph?: boolean;\n\n /**\n * When `false`, the description is *not* rendered on the button face — the\n * button shows only its glyph and title — but it still appears in the\n * hover tooltip (`{title}\\n\\n{description}`). Default `true`. The text is\n * always stored; only its on-button render is suppressed. No effect\n * without a description. Runtime counterpart: `setShowDescription`.\n */\n showDescription?: boolean;\n\n /**\n * When `false`, the title is *not* rendered on the button face — the\n * button shows only its glyph — but the title still drives the hover\n * tooltip (`{title}\\n\\n{description}`) and is reflected into `aria-label`\n * as the accessible name. Default `true`. The title is always stored; only\n * its on-button render is suppressed. Pairs with `showDescription` to give\n * a glyph-only button a tooltip + accessible name without an on-face label.\n * Runtime counterpart: `setShowText`.\n */\n showText?: boolean;\n glyph?: string;\n\n /**\n * Tints the leading glyph independently of the button's `foregroundColor`,\n * which colours the whole face (title *and* glyph). Sets the glyph\n * element's own `color`, picked up by its `currentColor` SVG fill — so a\n * button can render, say, a green glyph beside default-coloured text.\n * Survives a later `setGlyph`. Runtime counterpart: `setGlyphColor`.\n */\n glyphColor?: string;\n\n /**\n * Tints the description subtitle independently of the button's\n * `foregroundColor`, overriding the default dim description colour.\n * Runtime counterpart: `setDescriptionColor`.\n */\n descriptionColor?: string;\n\n enabled?: boolean;\n pressedBackgroundColor?: string;\n pressedBackgroundImage?: string;\n pressedForegroundColor?: string;\n pressedBorder?: BorderOptions | string;\n pressedBorderRadius?: string;\n pressedShadow?: string;\n hoverBackgroundColor?: string;\n hoverBackgroundImage?: string;\n hoverForegroundColor?: string;\n hoverBorder?: BorderOptions | string;\n hoverBorderRadius?: string;\n hoverShadow?: string;\n\n /**\n * Suppresses the framework's visual-chrome defaults — `border`,\n * `borderRadius`, `shadow`, `backgroundImage`, the twelve `pressedX` /\n * `hoverX` fields, and the UA `<button>` background. Use for buttons\n * that want only the cursor, color, and inset behaviour of `Button`\n * without the ridge border, drop shadow, and gradient background.\n * Runtime-toggle counterpart is `setChromeless`; read with\n * `isChromeless`. Used by [`PickerButton`](/api/component/input/classes/PickerButton)\n * and [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton).\n *\n * `applyOptions({ chromeless: true })` on a previously-chromeful button\n * writes the flag pure into `_options` and gates future chrome\n * dispatches, but does not clear the chrome already on the element —\n * callers wanting a runtime flip should call `setChromeless(true)`\n * directly.\n */\n chromeless?: boolean;\n\n /**\n * Classical \"flat\" appearance: no resting border/shadow/gradient; a light\n * frame + fill on `:hover:not(:active)` and a sunken inset frame on\n * `:active`. Mutually exclusive with `chromeless` (chromeless wins).\n * Runtime counterpart `setFlat`; read with `isFlat`.\n */\n flat?: boolean;\n\n /**\n * Compact rendering: tighter symmetric insets so the button reads denser\n * (e.g. packed into a [`ToolBar`](/api/component/menubar/classes/ToolBar)).\n * Text buttons go `(5,10,5,10)` →\n * `(2,6,2,6)`; glyph-only buttons collapse to a `(2,2,2,2)` square.\n * Defaults to `false`. Runtime counterpart `setCompact`; read with\n * `isCompact`.\n */\n compact?: boolean;\n\n /**\n * Anchor for the inner content row (glyph + label) within Button's outer\n * `Fit` layout. Defaults to {@link AnchorType.CENTER}. Pass\n * {@link AnchorType.WEST} for left-anchored menubar-style buttons.\n */\n anchor?: AnchorType;\n\n /**\n * Fill mode for the inner content row within Button's outer `Fit`\n * layout. Defaults to {@link FillType.NONE} (content sits at preferred\n * size, anchor decides displacement). `BOTH` stretches it to fill.\n */\n fill?: FillType;\n\n /**\n * Construction-time listener bag — the declarative form of `on()`. The\n * `action` entry is wired as if `on(\"action\", fn)` had been called (it\n * fires on the underlying DOM `click`).\n */\n listeners?: { action?: ClickListener };\n}\n\n/**\n * Default button inset perimeter — generous `5/10` (vertical/horizontal) so a\n * raised button reads as a comfortable click target. The asymmetric horizontal\n * padding gives the label breathing room either side of its centred glyph.\n */\nconst BUTTON_DEFAULT_INSETS: Insets = new Insets(5, 10, 5, 10);\n\n/**\n * Compact inset perimeter for a text-bearing button — `2/6`. Tightens the\n * generous default so the button reads denser (e.g. packed into a `ToolBar`)\n * while keeping enough horizontal padding that the label doesn't touch the\n * frame. Driven by {@link Button.setCompact}.\n */\nconst BUTTON_COMPACT_INSETS_TEXT: Insets = new Insets(2, 6, 2, 6);\n\n/**\n * Compact inset perimeter for a glyph-only button — a tight `2/2` square so a\n * toolbar icon collapses to its glyph with minimal margin. Driven by\n * {@link Button.setCompact}; supersedes the non-compact flat square below.\n */\nconst BUTTON_COMPACT_INSETS_GLYPH: Insets = new Insets(2, 2, 2, 2);\n\n/**\n * Flat glyph-only square inset — `4/4`. A non-compact flat glyph button (a\n * toolbar icon on a non-compact bar) collapses its asymmetric `5/10` default\n * to this square so it reads as a tight icon without the full compact tightening.\n */\nconst BUTTON_FLAT_GLYPH_INSETS: Insets = new Insets(4, 4, 4, 4);\n\n/**\n * Resting background the chromeful path installs as a colour (mirroring the\n * `--ts-ui-button-bg` image `super.applyChromeOptions` paints). Shared between\n * `applyChromeOptions` and `_applyFlatChrome` so the flat path can recognise\n * this framework default in `_options.backgroundColor` and tell it apart from a\n * colour the consumer set — the flat appearance is transparent at rest, so it\n * must clear the former but preserve the latter.\n */\nconst BUTTON_RESTING_BACKGROUND: string = \"var(--ts-ui-button-bg, transparent)\";\n\n/**\n * User-overridable visual defaults forwarded to `super` via the options bag.\n * The cascade in `Component`'s constructor dispatches each setter once with\n * the final value, so any field the caller supplied wins. Includes the\n * `pressedX` and `hoverX` defaults because both `pressedStyleRule` and\n * `hoverStyleRule` are lazy getters — those setters are safe to fire during\n * the super cascade and queue their writes until the rule materialises.\n */\nconst _defaultButtonOptions: Partial<ButtonOptions> = {\n tag: \"button\",\n cursor: \"pointer\",\n foregroundColor: \"var(--ts-ui-text-color, black)\",\n border: \"2px ridge var(--ts-ui-button-border, rgb(200, 200, 200))\",\n borderRadius: \"var(--ts-ui-border-radius, 4px)\",\n shadow: \"var(--ts-ui-button-shadow, 1px 2px 5px 0 rgba(0, 0, 0, 0.2))\",\n backgroundImage: \"var(--ts-ui-button-bg, linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200)))\",\n insets: BUTTON_DEFAULT_INSETS,\n anchor: AnchorType.CENTER,\n fill: FillType.NONE,\n pressedForegroundColor: \"var(--ts-ui-button-pressed-fg, rgb(150, 150, 150))\",\n pressedBackgroundColor: \"var(--ts-ui-button-pressed-bg, rgb(200, 200, 200))\",\n pressedBackgroundImage: \"var(--ts-ui-button-pressed-bg, none)\",\n pressedShadow: \"var(--ts-ui-button-pressed-shadow, 1px 2px 5px 0 rgba(0, 0, 0, 0.2) inset)\",\n hoverBackgroundColor: \"var(--ts-ui-button-hover-bg, rgb(252, 252, 252))\",\n hoverBackgroundImage: \"var(--ts-ui-button-hover-bg, none)\",\n hoverShadow: \"var(--ts-ui-button-hover-shadow, 1px 3px 6px 0 rgba(0, 0, 0, 0.25))\",\n};\n\n/**\n * A push button component with a text label and configurable pressed-state appearance.\n *\n * Maintains separate CSS rules for the normal and `:active` states, allowing\n * independent control of border, shadow, background, and foreground color when pressed.\n *\n * @example\n * ```typescript\n * import { Event } from '@jimka/typescript-ui/core';\n * import { Button } from '@jimka/typescript-ui/component/button';\n *\n * const button = new Button('Save');\n * Event.addListener(button, 'click', () => save());\n * panel.addComponent(button);\n * ```\n *\n * @category Components\n */\nclass Button<TOptions extends ButtonOptions = ButtonOptions> extends Component<TOptions> {\n\n private _text!: Text;\n /**\n * The button's content-row container. Holds the optional leading\n * [`Glyph`](/api/component/display/classes/Glyph) plus the `_text` label,\n * laid out by an `HBox`. Exposed as `protected` so subclasses can\n * re-anchor (`removeComponent` + `addComponent`) or rebuild the row\n * without having to opt into `customLayout: true`. Treat as part of the\n * subclass contract — future restructuring of Button's content row\n * needs to keep this field's identity and shape stable.\n */\n protected _content!: Component;\n\n /**\n * The vertical title/subtitle stack nested inside `_content`'s HBox. Holds\n * `[_text, _description?]` laid out by a `VBox`, so the optional\n * description renders on a line below the title while the leading glyph\n * stays beside the whole stack. Private — unlike `_content` this is an\n * internal detail of how the label row stacks and is not part of the\n * subclass re-anchor contract.\n */\n private _titleColumn!: Component;\n\n /**\n * The subtitle label, created lazily on the first {@link setDescription}\n * call (mirroring `_glyph`'s lazy creation) so a button with no\n * description never reserves a second line. `null` until then. Stays\n * alive (detached, not nulled) when `showDescription` is `false` so the\n * tooltip can still read its text.\n */\n private _description: Text | null = null;\n\n /**\n * When `true`, {@link _rebuildTooltip} detaches the hover tooltip instead of\n * attaching it, regardless of the title/description text. Lets a subclass\n * silence the tooltip while the button is showing its own popup — e.g. a\n * [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton) whose\n * dropdown is open, where the tooltip would otherwise float over the menu.\n * Toggled via {@link setTooltipSuppressed}.\n */\n private _tooltipSuppressed: boolean = false;\n\n /**\n * Lazy containers used only by the \"under glyph\" (full-width) description\n * topology: `_outerColumn` (VBox) holds `[_innerRow, _description]` and\n * `_innerRow` (HBox) holds `[glyph?, _text]`. Created on first entry into\n * that mode so a button with no description (or in under-title mode) never\n * allocates them — keeping the minimal tree the subclasses depend on.\n * Private; not part of the subclass re-anchor contract.\n */\n private _outerColumn: Component | null = null;\n private _innerRow: Component | null = null;\n\n private _glyph: Glyph | null = null;\n\n /**\n * The square pixel size {@link _syncGlyphSize} last wrote to `_glyph`'s\n * preferred size. Acts as the per-glyph opt-out guard: on a re-sync (e.g.\n * theme toggle) the glyph is only re-matched to the title line height when\n * its current preferred size still equals this value — so a subclass /\n * consumer that pinned its own glyph size (SpinButton, Tab close-glyph)\n * is left untouched. `null` until the framework first sizes the glyph.\n *\n * This size-difference guard is unreliable on its own: a consumer that\n * pins the glyph *before* the first successful sync (while this is still\n * `null` — e.g. before the button is attached and the line height\n * resolves) is not protected, so the first post-attach re-sync clobbers\n * the pin. {@link _glyphSizePinned} is the authoritative opt-out.\n */\n private _glyphSyncedSize: number | null = null;\n\n /**\n * Set by {@link pinGlyphSize} when a consumer fixes the leading glyph to a\n * specific size. While `true`, {@link _syncGlyphSize} skips the glyph\n * entirely, so a theme change never re-tracks it to the title line height.\n * This is the reliable opt-out (the {@link _glyphSyncedSize} size-diff guard\n * has a null-window hole — see there).\n */\n private _glyphSizePinned: boolean = false;\n\n /**\n * Flipped to `true` the first time a consumer calls `setPreferredSize`\n * (directly or via the options bag). When set, the native auto-sizing\n * pipeline (see `recomputePreferredSize`) no-ops so the consumer's\n * explicit intent wins permanently. There's no public surface to\n * re-enable auto-sizing — a future plan can add a `clearPreferredSize`\n * method that resets this flag and re-fires the recompute.\n *\n * `declare` rather than `= false` so the class-field super-cascade trap\n * doesn't clobber the value when `Component.applyOptions` dispatches\n * `setPreferredSize` during the super-time cascade (an `= false`\n * initializer runs *after* super returns and would silently revert the\n * setter's `_consumerSetPreferredSize = true` write — letting the\n * end-of-constructor `recomputePreferredSize` overwrite the consumer's\n * preferred size with the content-derived value). The early-return\n * check at the top of `recomputePreferredSize` treats `undefined` as\n * falsy, so the no-cascade-write case still auto-sizes correctly.\n */\n private declare _consumerSetPreferredSize?: boolean;\n\n /**\n * Bound theme-change handler. The auto-sizing pipeline reads font-size\n * and glyph metrics that can shift with the active theme, so the recompute\n * re-fires whenever the theme cascade flips. `_rebuildContentRow` runs\n * first so the single-line optical inset re-reads the freshly-invalidated\n * `Util.opticalCenterOffset()` — `recomputePreferredSize` alone does not\n * touch the content row. Held on the instance so a future `dispose` path\n * can unregister it.\n */\n private readonly _onThemeChange: () => void = () => {\n this._rebuildContentRow();\n this.recomputePreferredSize();\n };\n\n // Lazy `:active` rule. The slot is just a fast-path cache — the\n // `createStyleRule` builder on Component dedupes by selector suffix, so\n // even if the slot is reset between calls (e.g. by TypeScript class-field\n // init after super returns), the next access still returns the same\n // wrapper that the super-cascade allocated. `declare` keeps the slot off\n // the runtime class so the fast-path doesn't pay an unnecessary Map\n // lookup after construction.\n private declare _pressedStyleRule?: StyleRule;\n private get pressedStyleRule(): StyleRule {\n return this._pressedStyleRule ??= this.createStyleRule(\":active\");\n }\n private _pressedBorder: BorderOptions | null = null;\n\n // Lazy `:hover:not(:active)` rule. The `:not(:active)` guard makes the\n // cascade unambiguous regardless of source order — the moment the\n // pointer goes down, `:active` matches and `:hover:not(:active)` stops\n // matching, so the pressed treatment always wins.\n private declare _hoverStyleRule?: StyleRule;\n private get hoverStyleRule(): StyleRule {\n return this._hoverStyleRule ??= this.createStyleRule(\":hover:not(:active)\");\n }\n private _hoverBorder: BorderOptions | null = null;\n\n /**\n * Cached `flat` appearance flag. `declare` rather than initialized because\n * `setFlat` can fire during the super-time cascade via `applyChromeOptions`\n * (the flat branch installs hover/pressed treatments through the lazy\n * style-rule getters), and an `= false` initializer would run after super\n * returns and silently clobber the cascaded value.\n */\n private declare _flat?: boolean;\n\n /**\n * Cached `compact` flag. `declare` rather than initialized because\n * `setCompact` can fire during the super-time cascade via `applyOptions`,\n * and an `= false` initializer would run after super returns and silently\n * clobber the cascaded value (same trap as `_flat` above). The inset\n * resolution it drives is deferred to `_resolveInsets`, which guards on the\n * content row existing.\n */\n private declare _compact?: boolean;\n\n private _enabledCursor: string = \"pointer\";\n\n /**\n * Constructs a Button. `text` (positional or via options) and `glyph` are\n * both optional — an empty Button renders as a chrome-shaped placeholder\n * whose label / glyph can be filled in later via `setText` / `setGlyph`.\n *\n * @example\n * ```typescript\n * new Button('Save');\n * new Button({ glyph: 'times' });\n * new Button('Save', { glyph: 'check-circle' });\n * ```\n */\n constructor(text?: string, options?: ButtonOptions, subclassDefaults?: Partial<ButtonOptions>);\n constructor(options: ButtonOptions);\n constructor(\n textOrOptions?: string | ButtonOptions,\n options?: ButtonOptions,\n subclassDefaults?: Partial<ButtonOptions>,\n ) {\n // Normalise the overload: a non-string first argument is the options bag.\n let text: string | undefined;\n if (typeof textOrOptions === \"string\") {\n text = textOrOptions;\n } else if (textOrOptions !== undefined) {\n options = textOrOptions;\n }\n\n // Hand defaults to Component via the second super arg so they land in\n // `_defaultOptions` and survive subsequent `applyOptions` re-merges.\n // Subclass defaults (forwarded by callers via the third constructor\n // arg) layer on top so the deepest class's overrides win.\n super(\n options as TOptions,\n { ..._defaultButtonOptions, ...(subclassDefaults ?? {}) } as Partial<TOptions>,\n );\n\n // Structural state — can't go through the bag because consumers must\n // not be able to override it.\n this.setLayoutManager(new Fit());\n\n // Build the text/glyph content row. The title (and optional subtitle)\n // live in a vertical `_titleColumn` so the description stacks below the\n // title; the glyph sits beside the whole column in the outer HBox.\n this._text = new Text();\n this._titleColumn = new Component();\n this._titleColumn.setLayoutManager(new VBox({ spacing: 0 }));\n this._titleColumn.setInsets(new Insets(0, 0, 0, 0));\n this._titleColumn.setPointerEvents(\"none\");\n this._titleColumn.addComponent(this._text);\n\n this._content = new Component();\n this._content.setLayoutManager(new HBox({ spacing: 2 }));\n this._content.setInsets(new Insets(0, 0, 0, 0));\n this._content.setPointerEvents(\"none\");\n this._content.addComponent(this._titleColumn);\n\n this._text.setPointerEvents(\"none\");\n this._text.setTextAlign(\"center\");\n this._text.setFontWeight(\"bold\");\n this._text.setFontSize(\"--ts-ui-button-font-size\");\n\n this.addComponent(this._content, {\n fill: this.getFill(),\n anchor: this.getAnchor(),\n });\n\n // Late-built state: applyOptions wrote any caller `text`/`glyph`/\n // `description` into `_options` pure (no setter dispatch) because\n // `this.text`/`_content` didn't exist yet. Dispatch the effective value\n // now that children are wired up, folding a class default (e.g.\n // TabCloseButton's `glyph: \"xmark\"`) that raw dispatch left in\n // `_defaultOptions` rather than `_options`.\n const effectiveText = this._options.text ?? this._defaultOptions.text ?? text;\n if (effectiveText !== undefined) {\n this.setText(effectiveText);\n }\n const effectiveGlyph = this._options.glyph ?? this._defaultOptions.glyph;\n if (effectiveGlyph !== undefined) {\n this.setGlyph(effectiveGlyph);\n }\n const effectiveDescription = this._options.description ?? this._defaultOptions.description;\n if (effectiveDescription !== undefined) {\n this.setDescription(effectiveDescription);\n }\n\n // Construction-time flat / compact: the flat and compact setters ran\n // during the super cascade before `_glyph` / `_text` existed, so\n // `_resolveInsets` no-op'd. Re-evaluate now the content row is built so\n // the icon-square / compact insets land.\n if (this._flat || this._compact) {\n this._resolveInsets();\n }\n\n // Initial auto-sized preferred-size pass. No-ops when the consumer\n // already supplied `preferredSize` (the override of `setPreferredSize`\n // below flips `_consumerSetPreferredSize`).\n this.recomputePreferredSize();\n\n // Re-fire the auto-sized recompute on theme changes so any\n // font-size / glyph-metric shifts cascade into the button's preferred\n // size without explicit consumer prodding.\n this.subscribeTheme(this._onThemeChange);\n\n // Wire the listener bag — but only when this IS a plain Button.\n // Subclasses wire their own bag from their constructor body after their\n // `super()` returns, because a subclass event (e.g. SpinButton's\n // `tick`) may live in a `ListenerBag` that does not exist yet during\n // this base constructor. The check is instance-identity, not\n // `new.target`: construction routes through the `callable()` Proxy,\n // which has no `construct` trap, so `new.target` is the proxy rather\n // than this class — but the instance's prototype still resolves to the\n // raw class prototype, so a plain Button matches and any subclass does not.\n if (Object.getPrototypeOf(this) === Button.prototype) {\n this.applyListeners(options?.listeners);\n }\n }\n\n /**\n * Returns the content anchor used inside the button's `Fit` layout — the\n * caller value, else the class default ({@link AnchorType.CENTER}).\n *\n * @returns The resolved anchor.\n */\n getAnchor(): AnchorType {\n return (this._options.anchor ?? this._defaultOptions.anchor) as AnchorType;\n }\n\n /**\n * Returns the content fill used inside the button's `Fit` layout — the\n * caller value, else the class default ({@link FillType.NONE}).\n *\n * @returns The resolved fill.\n */\n getFill(): FillType {\n return (this._options.fill ?? this._defaultOptions.fill) as FillType;\n }\n\n /**\n * Applies a {@link ButtonOptions} bag. Inherited Component fields cascade\n * through `super.applyOptions`; pressed-state, hover-state, and `enabled`\n * fields cascade through their own setters (the lazy `pressedStyleRule`\n * and `hoverStyleRule` getters make them safe to fire during the\n * super-time cascade). `text`, `description`, and `glyph` are written pure\n * into `_options` during the super-time cascade (before the content row\n * exists) and dispatched from the constructor body once children exist —\n * but a *post-construction* call (a subclass's tail `applyOptions`, or a\n * runtime re-apply) dispatches the setters directly, so an option supplied\n * after the constructor's late dispatch still renders. The\n * `descriptionUnderGlyph` / `showDescription` flags are always pure-written\n * and consumed live by the content-row rebuild those dispatches trigger.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: TOptions): this {\n // Button structurally owns a per-instance Fit layout, installed\n // imperatively in the constructor because a fresh manager can't live in\n // the shared `_defaultOptions` bag. `super.applyOptions` re-dispatches\n // `_defaultOptions.layoutManager` (always an `Absolute`) on every call,\n // so a subclass that hands its consumer options to a tail `applyOptions`\n // (ToggleButton / TabButton: `if (options) this.applyOptions(options)`)\n // would clobber that Fit — and an Absolute layout never positions the\n // content row, collapsing the label into a corner. Capture the manager\n // and restore it when the cascade re-defaulted it away and the consumer\n // did not explicitly request a different one (ButtonOptions omits\n // `layoutManager`, so that override path is intentionally unreachable).\n const structuralLayout = this.getLayoutManager();\n\n super.applyOptions(options);\n\n if (options.layoutManager === undefined && this.getLayoutManager() !== structuralLayout) {\n this.setLayoutManager(structuralLayout);\n }\n\n // Content-row flags first (pure writes): a dispatched\n // setText/setGlyph/setDescription below reads them (via `_isShowText()`\n // and the content-row rebuild). They ride the options bag, not a class\n // field, so the super-cascade class-field trap does not apply.\n if (options.descriptionUnderGlyph !== undefined) this._options.descriptionUnderGlyph = options.descriptionUnderGlyph;\n if (options.showDescription !== undefined) this._options.showDescription = options.showDescription;\n if (options.showText !== undefined) this._options.showText = options.showText;\n\n // text / glyph / description. Before the content row exists — the\n // super-time cascade, where `_text` is unassigned — write pure and let\n // the constructor body dispatch the setters once children are wired.\n // After construction the row is built, so dispatch the setters to\n // rebuild the content: a subclass that forwards only `text` to super and\n // hands its bag to a tail `applyOptions` (ToggleButton / TabButton) has\n // already missed the constructor dispatch, so a pure write would record\n // the glyph/description but never render it. Instance-identity guard\n // (`_text`), so raw dispatch loses no `_defaultOptions` default (folded\n // by the constructor's own late dispatch for the cascade path).\n const contentBuilt = this._text !== undefined;\n\n // Glyph / description tints are stored before the glyph / description\n // dispatch below, so a `setGlyph` / `setDescription` fired here (or by the\n // constructor's late dispatch) re-reads and re-applies them to the fresh\n // element. A post-construction call with only the colour (the element\n // already built) tints it directly via the setter.\n if (options.glyphColor !== undefined) {\n if (contentBuilt) this.setGlyphColor(options.glyphColor); else this._options.glyphColor = options.glyphColor;\n }\n\n if (options.descriptionColor !== undefined) {\n if (contentBuilt) this.setDescriptionColor(options.descriptionColor); else this._options.descriptionColor = options.descriptionColor;\n }\n\n if (options.text !== undefined) {\n if (contentBuilt) this.setText(options.text); else this._options.text = options.text;\n }\n\n if (options.description !== undefined) {\n if (contentBuilt) this.setDescription(options.description); else this._options.description = options.description;\n }\n\n if (options.glyph !== undefined) {\n if (contentBuilt) this.setGlyph(options.glyph); else this._options.glyph = options.glyph;\n }\n\n if (options.enabled !== undefined) this.setEnabled(options.enabled);\n\n // chromeless / anchor / fill are pure writes — no setter dispatch.\n // Runtime flag flips for chromeless go through setChromeless(), which\n // also reconciles the DOM via clearChrome / restoreChrome. anchor and\n // fill are consumed by the constructor body when adding `_content` to\n // the outer Fit layout; later applyOptions calls don't reanchor.\n if (options.chromeless !== undefined) this._options.chromeless = options.chromeless;\n if (options.flat !== undefined) this._options.flat = options.flat;\n // Dispatched (not pure-written like `flat`) because it must resolve the\n // inset perimeter; `_resolveInsets` guards on the content row so the\n // cascade-time call before `_text` exists is a safe no-op.\n if (options.compact !== undefined) this.setCompact(options.compact);\n if (options.anchor !== undefined) this._options.anchor = options.anchor;\n if (options.fill !== undefined) this._options.fill = options.fill;\n\n return this;\n }\n\n /**\n * Gates Component's chrome dispatch on the `chromeless` flag and, when\n * the flag is off, extends it with Button's twelve `pressedX` / `hoverX`\n * chrome fields. Reads the flag from the runtime cache first so a flag\n * previously written (by an earlier `applyOptions` or `setChromeless`)\n * keeps gating future re-applies that omit `chromeless`.\n *\n * @param options - The raw caller options bag passed by {@link applyOptions}.\n */\n protected override applyChromeOptions(options: TOptions): void {\n const chromeless = (options.chromeless ?? this.isChromeless()) === true;\n if (chromeless) {\n // The chrome group's class defaults are dispatched only by\n // `super.applyChromeOptions` (skipped here for chromeless), so a\n // fresh chromeless button never receives them. The explicit\n // `undefined` writes still matter on a re-apply (e.g. `setChromeless`\n // after a chromeful pass): they clear a previously-written value so\n // the chrome getters report `null` and `applyStyle` skips the\n // property.\n //\n // Border goes through the private `_border` field, so `clearBorder`\n // clears it to a `none` border that overrides the UA `<button>` ridge.\n //\n // Finally, the UA `<button>` element has a non-transparent\n // background-color; set transparent unless the caller specified\n // their own backgroundColor.\n this.clearBorder();\n this._options.borderRadius = undefined;\n this._options.shadow = undefined;\n this._options.backgroundImage = undefined;\n if (this.getBackgroundColor() === null) {\n this._options.backgroundColor = \"transparent\";\n }\n return;\n }\n\n super.applyChromeOptions(options);\n\n // Chromeful resting background: `super` applied the `--ts-ui-button-bg`\n // token as a background *image* (for gradient tokens); also apply it as\n // a background *colour* so a flat-colour token (e.g. the Modern theme)\n // renders too — whichever channel is valid paints, the other is dropped.\n // Without the colour channel a flat-colour token was invalid as an image\n // and the button fell back to the UA `<button>` background. Skipped when\n // the caller set their own backgroundColor, and when flat (flat is\n // transparent at rest — see `_applyFlatChrome`).\n const isFlat = (this._options.flat ?? options.flat) === true;\n\n if (!isFlat && options.backgroundColor === undefined) {\n this.setBackgroundColor(BUTTON_RESTING_BACKGROUND);\n }\n\n // The pressed/hover colour + shadow + image fields carry class defaults,\n // so dispatch the caller value or the class default. The pressed/hover\n // border fields are not defaulted, so they stay caller-gated.\n this.setPressedForegroundColor(options.pressedForegroundColor ?? this.getPressedForegroundColor()!);\n this.setPressedBackgroundColor(options.pressedBackgroundColor ?? this.getPressedBackgroundColor()!);\n this.setPressedBackgroundImage(options.pressedBackgroundImage ?? this.getPressedBackgroundImage()!);\n this.setPressedShadow (options.pressedShadow ?? this.getPressedShadow()!);\n if (options.pressedBorder !== undefined) this.setPressedBorder (options.pressedBorder);\n if (options.pressedBorderRadius !== undefined) this.setPressedBorderRadius(options.pressedBorderRadius);\n\n if (options.hoverForegroundColor !== undefined) this.setHoverForegroundColor(options.hoverForegroundColor);\n this.setHoverBackgroundColor(options.hoverBackgroundColor ?? this.getHoverBackgroundColor()!);\n this.setHoverBackgroundImage(options.hoverBackgroundImage ?? this.getHoverBackgroundImage()!);\n this.setHoverShadow (options.hoverShadow ?? this.getHoverShadow()!);\n if (options.hoverBorder !== undefined) this.setHoverBorder (options.hoverBorder);\n if (options.hoverBorderRadius !== undefined) this.setHoverBorderRadius(options.hoverBorderRadius);\n\n // Flat is the third appearance mode: it runs in the chromeful `else`\n // path (chromeless already returned above) and re-points the resting\n // chrome + the raised `pressedX`/`hoverX` treatments just dispatched to\n // the flat tokens. Reading the runtime cache first keeps a previously\n // written flag gating future re-applies that omit `flat`.\n if ((this._options.flat ?? options.flat) === true) {\n this._flat = true;\n this._applyFlatChrome();\n }\n }\n\n /**\n * Sets the button's title text. The single entry point for mutating the\n * title — there is no public accessor for the inner label, so every title\n * change routes through here, keeping the auto-sized preferred size and\n * the hover tooltip (composed from title + description) in sync.\n *\n * @param text - The new title string.\n *\n * @returns This component, for method chaining.\n */\n setText(text: string): this {\n this._options.text = text;\n\n // `_text` is the on-face renderer. The title string lives in\n // `_options.text`; `_text` shows it only when `showText` is true, and\n // renders blank when hidden so the button stays metrically a glyph-only\n // button while the tooltip and accessible name still carry the title.\n this._text.setText(this._isShowText() ? text : \"\");\n this.recomputePreferredSize();\n this._rebuildTooltip();\n this._reflectAccessibleName();\n\n return this;\n }\n\n /**\n * Returns the button's current title text, or `\"\"` when no label is set.\n * A read-only companion to {@link setText} — the inner label has no other\n * public accessor; the [`ToolBar`](/api/component/menubar/classes/ToolBar)\n * overflow menu reads it to label each dropdown row.\n *\n * @returns The current title string, or `\"\"` if none is set.\n */\n getText(): string {\n return this._options.text ?? \"\";\n }\n\n /**\n * Applies a writing mode to the button and propagates it to the inner label\n * (and description) so their measured preferred size reflects the rotated\n * text run — a label-only `getWritingMode` would otherwise stay horizontal\n * even while inheriting the vertical mode through CSS, and report the wrong\n * extents. Re-syncs the auto-sized preferred size after the labels swap.\n *\n * @param value - A CSS `writing-mode` value (e.g. `vertical-rl`).\n *\n * @returns This component, for method chaining.\n */\n setWritingMode(value: string): this {\n const previousOrientation = this._contentOrientation();\n\n super.setWritingMode(value);\n\n // `writingMode` is a Component option, so this can fire from\n // `applyOptions` during `super()` — before the constructor assigns\n // `_text`. Forward (and resize) only once the labels exist; the Tab sets\n // the mode post-construction, so its labels are always present.\n if (this._text) {\n this._text.setWritingMode(value);\n this._description?.setWritingMode(value);\n\n // Re-lay the content row when the reading direction flips, but only\n // when a glyph or description is present — a lone label is a single\n // child whose container axis is irrelevant, so plain-text tabs keep\n // their exact prior layout.\n if (this._contentOrientation() !== previousOrientation && (this._glyph || this._description)) {\n this._rebuildContentRow();\n }\n\n this.recomputePreferredSize();\n }\n\n return this;\n }\n\n /**\n * Justifies the button's title (and leading glyph) within a button wider\n * than its content. Forwards the value to the inner\n * [`Text`](/api/component/input/classes/Text)'s CSS `text-align` and\n * re-anchors the whole content row in the Fit layout — `\"left\"` / `\"right\"`\n * track the start / end of the label's reading direction, so they map to the\n * vertical edges on a rotated (west/east) label. `\"center\"` is the default.\n *\n * @param align - A CSS `text-align` value (`\"left\"`, `\"center\"`, `\"right\"`).\n *\n * @returns This component, for method chaining.\n */\n setTextAlign(align: string): this {\n // `setTextAlign` can be driven post-construction (the Tab strip sets it\n // each layout pass), but a future option-bag dispatch could fire from\n // `applyOptions` during `super()` — before the constructor assigns\n // `_text`. Guard like `setWritingMode` does.\n if (this._text) {\n this._text.setTextAlign(align);\n\n // CSS text-align only nudges glyphs inside the label's own\n // content-hugging box; to justify the whole glyph+label block\n // within a button wider than its content, re-anchor the content row\n // in the Fit layout along the (possibly rotated) reading axis.\n const constraints = this.getLayoutConstraints(this._content);\n\n if (constraints) {\n constraints.anchor = this._anchorForTextAlign(align);\n this.scheduleLayout();\n }\n }\n\n return this;\n }\n\n /**\n * Maps a `text-align` value to the {@link AnchorType} that positions the\n * content row at the matching edge of a button wider than its content.\n * Accepts both physical (`\"left\"` / `\"right\"`) and flow-relative (`\"start\"`\n * / `\"end\"`) values; either way they track the start / end of the label's\n * reading direction, so on a rotated label they map to the vertical edges\n * (`NORTH` / `SOUTH`) — reversed between clockwise and counter-clockwise.\n *\n * @param align - A `text-align` value (`\"left\"`/`\"start\"`, `\"center\"`, `\"right\"`/`\"end\"`).\n *\n * @returns The anchor that justifies the content row accordingly.\n */\n private _anchorForTextAlign(align: string): AnchorType {\n const leading = align === \"left\" || align === \"start\";\n const trailing = align === \"right\" || align === \"end\";\n const orient = this._contentOrientation();\n\n if (orient === \"cw\") {\n // Reads top→bottom: leading is the top edge, trailing the bottom.\n return leading ? AnchorType.NORTH : trailing ? AnchorType.SOUTH : AnchorType.CENTER;\n }\n\n if (orient === \"ccw\") {\n // Reads bottom→top: leading is the bottom edge, trailing the top.\n return leading ? AnchorType.SOUTH : trailing ? AnchorType.NORTH : AnchorType.CENTER;\n }\n\n return leading ? AnchorType.WEST : trailing ? AnchorType.EAST : AnchorType.CENTER;\n }\n\n /**\n * Clears the writing mode from the button and its inner label (and\n * description), restoring horizontal text measurement.\n *\n * @returns This component, for method chaining.\n */\n clearWritingMode(): this {\n const previousOrientation = this._contentOrientation();\n\n super.clearWritingMode();\n\n if (this._text) {\n this._text.clearWritingMode();\n this._description?.clearWritingMode();\n\n // Restoring horizontal flow flips the reading direction back; re-lay\n // the content row for glyph/description buttons (see setWritingMode).\n if (this._contentOrientation() !== previousOrientation && (this._glyph || this._description)) {\n this._rebuildContentRow();\n }\n\n this.recomputePreferredSize();\n }\n\n return this;\n }\n\n /**\n * Sets the button's subtitle, shown on a line below the title in a\n * smaller, dimmer style. The subtitle label is created lazily on the\n * first call (mirroring {@link setGlyph}) so a button with no description\n * never reserves a second line. Re-syncs the auto-sized preferred size\n * and the hover tooltip.\n *\n * @param text - The subtitle string.\n *\n * @returns This component, for method chaining.\n */\n setDescription(text: string): this {\n if (!this._description) {\n this._description = new Text();\n this._description.setPointerEvents(\"none\");\n this._description.setTextAlign(\"center\");\n this._description.setFontSize(\"--ts-ui-button-description-font-size\");\n this._description.setFontWeight(\"var(--ts-ui-button-description-weight, normal)\");\n this._description.setForegroundColor(\n this._options.descriptionColor ?? \"var(--ts-ui-button-description-fg, rgb(110, 110, 110))\"\n );\n }\n\n // The instance is created above unconditionally so `_rebuildTooltip`\n // can always read it; `_rebuildContentRow` decides whether it is\n // actually parented onto the button face (gated by `showDescription`)\n // and in which topology (gated by `descriptionUnderGlyph`).\n this._description.setText(text);\n this._rebuildContentRow();\n this._rebuildTooltip();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns the subtitle Text child, or `null` when no description has been\n * set (mirroring {@link getGlyph}). Read-only access — the only way to\n * change the subtitle string is {@link setDescription}, so the tooltip\n * stays authoritative.\n *\n * @returns The description [`Text`](/api/component/input/classes/Text) instance, or null.\n */\n getDescription(): Text | null {\n return this._description;\n }\n\n /**\n * Removes the subtitle line, if one is present, and re-syncs the\n * auto-sized preferred size and the hover tooltip.\n *\n * @returns This component, for method chaining.\n */\n clearDescription(): this {\n this._description = null;\n\n this._rebuildContentRow();\n this._rebuildTooltip();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Recomposes the hover tooltip from the current title and description and\n * (re)attaches it. The tooltip body is `{title}\\n\\n{description}` when a\n * description exists, the title alone when it doesn't, and the description\n * alone when there is no title. Detaches entirely when both are empty.\n * Routed through [`Tooltip`](/api/overlay/classes/Tooltip), which renders the\n * `\\n` breaks across multiple lines.\n */\n private _rebuildTooltip(): void {\n // A suppressed tooltip stays detached whatever the text — the button is\n // showing its own popup and a hover hint would occlude it.\n if (this._tooltipSuppressed) {\n Tooltip.detach(this);\n\n return;\n }\n\n const title = this._options.text ?? \"\";\n const desc = this._description?.getText().valueOf() ?? \"\";\n\n let str: string;\n if (title && desc) {\n str = `${title}\\n\\n${desc}`;\n } else {\n str = title || desc;\n }\n\n if (str) {\n Tooltip.attach(this, str);\n } else {\n Tooltip.detach(this);\n }\n }\n\n /**\n * Suppresses or restores this button's hover tooltip. While suppressed the\n * tooltip is detached (and any visible/pending one dismissed) and stays off\n * across `setText`/`setDescription` rebuilds; restoring re-composes it from\n * the current title and description. Intended for a subclass that opens its\n * own popup — see {@link _tooltipSuppressed}.\n *\n * @param value - `true` to detach the tooltip, `false` to restore it.\n *\n * @returns This button, for method chaining.\n */\n protected setTooltipSuppressed(value: boolean): this {\n if (value === this._tooltipSuppressed) {\n return this;\n }\n\n this._tooltipSuppressed = value;\n\n this._rebuildTooltip();\n\n return this;\n }\n\n /**\n * Reflects the title into the accessible name exactly when it is hidden from\n * the button face (`showText:false`). A glyph-only face renders the title\n * blank, so without this the button would have no accessible name; the\n * stored title (`_options.text`) is mirrored into `aria-label`. When the\n * title is visible the rendered text already supplies the accessible name,\n * so any previously reflected label is cleared (e.g. after `setShowText(true)`).\n */\n private _reflectAccessibleName(): void {\n const title = this._options.text ?? \"\";\n\n if (!this._isShowText() && title !== \"\") {\n this.getAria().setLabel(title);\n } else {\n this.getAria().clearLabel();\n }\n }\n\n /**\n * Resolves the `descriptionUnderGlyph` flag with its `true` default — the\n * single place the default lives, read by {@link _rebuildContentRow}.\n *\n * @returns Whether the description aligns full-width under the glyph.\n */\n private _isDescriptionUnderGlyph(): boolean {\n return this._options.descriptionUnderGlyph ?? true;\n }\n\n /**\n * Resolves the `showDescription` flag with its `true` default.\n *\n * @returns Whether the description is rendered on the button face.\n */\n private _isShowDescription(): boolean {\n return this._options.showDescription ?? true;\n }\n\n /**\n * Resolves the `showText` flag with its `true` default.\n *\n * @returns Whether the title is rendered on the button face.\n */\n private _isShowText(): boolean {\n return this._options.showText ?? true;\n }\n\n /**\n * Classifies the active writing mode into the orientation the content row\n * must follow. The Tab strip rotates west/east labels with `sideways-rl`\n * (clockwise, reads top→bottom) and `sideways-lr` (counter-clockwise, reads\n * bottom→top); every other value — including none — lays out horizontally.\n *\n * @returns `\"cw\"`, `\"ccw\"`, or `\"horizontal\"`.\n */\n private _contentOrientation(): \"horizontal\" | \"cw\" | \"ccw\" {\n const wm = this.getWritingMode();\n\n if (wm === \"sideways-rl\") {\n return \"cw\";\n }\n\n if (wm === \"sideways-lr\") {\n return \"ccw\";\n }\n\n return \"horizontal\";\n }\n\n /**\n * Swaps `container`'s box layout to the given axis and spacing so a content\n * row follows the label's reading direction (`\"h\"` → `HBox`, `\"v\"` → `VBox`).\n *\n * @param container - The content container to re-lay out.\n * @param axis - `\"h\"` for a horizontal row, `\"v\"` for a vertical stack.\n * @param spacing - Inter-child spacing in px.\n */\n private _orientBox(container: Component, axis: \"h\" | \"v\", spacing: number): void {\n container.setLayoutManager(axis === \"v\" ? new VBox({ spacing }) : new HBox({ spacing }));\n }\n\n /**\n * Adds `children` to `container` in reading order, reversing when the axis\n * runs against the document order (a counter-clockwise inline run or a\n * clockwise block run), so the leading glyph / title stays first in the\n * rotated flow.\n *\n * @param container - The container to populate.\n * @param children - The children in their logical (unrotated) order.\n * @param reversed - Whether to add them in reverse.\n */\n private _addContentChildren(container: Component, children: Component[], reversed: boolean): void {\n const ordered = reversed ? [...children].reverse() : children;\n\n for (const child of ordered) {\n container.addComponent(child);\n }\n }\n\n /**\n * Rebuilds `_content`'s child tree from the current `_glyph` / `_text` /\n * `_description` instances and the `descriptionUnderGlyph` /\n * `showDescription` flags. The single point that (re)parents those shared\n * children, so the topology stays consistent across every glyph /\n * description / flag mutation. Never recreates `_content` itself, so its\n * identity and HBox layout — the documented subclass re-anchor seam — are\n * preserved.\n *\n * Idempotent: it empties every content container up front\n * ({@link Component.removeAllComponents} fully detaches each child), then\n * re-adds from the current field state, so it is safe to call any number\n * of times and after a field has been nulled.\n *\n * Two topologies:\n * - **under glyph** (`descriptionUnderGlyph` true, glyph + visible\n * description): `_content[ _outerColumn( _innerRow[glyph, _text], _description ) ]`\n * — the description spans full width below the glyph+title row.\n * - **under title** (the default fallback, also used whenever there is no\n * glyph, no description, or `showDescription` is false):\n * `_content[ glyph?, _titleColumn[ _text, _description? ] ]`.\n *\n * When `showDescription` is false the description is treated as absent for\n * layout — the minimal `[glyph?, _titleColumn[_text]]` tree is built and\n * `_description` is left detached but alive (so {@link _rebuildTooltip}\n * can still read it).\n */\n private _rebuildContentRow(): void {\n // Disassemble: empty every container so all shared children are\n // parentless and re-addable (add/insertComponent throw on a child that\n // still has a parent). Emptying wholesale also drops any outgoing glyph\n // / description whose field was reassigned or nulled by the caller.\n this._content.removeAllComponents();\n this._titleColumn.removeAllComponents();\n this._innerRow?.removeAllComponents();\n this._outerColumn?.removeAllComponents();\n\n // A hidden description is treated as absent for rendering; the instance\n // stays alive for the tooltip.\n const renderDesc = this._description !== null && this._isShowDescription();\n\n // A hidden title (`showText:false`) is rendered blank rather than\n // unparented: `_text` keeps its place in the no-description topology so\n // its line box still drives the button height (exactly like a genuinely\n // empty label), and the title string lives in `_options.text` to feed\n // the tooltip and the reflected `aria-label`. In the description\n // topologies the description drives the height, so a hidden title is\n // dropped outright to avoid reserving a blank title line.\n const renderText = this._isShowText();\n\n // The label's writing mode rotates both the inline (glyph→text) and the\n // block (title→description) reading directions; the content boxes have\n // to follow it, or a leading glyph lands beside vertical text instead of\n // before it. The inline run becomes a VBox and the block stack an HBox.\n // `sideways-rl` (cw) reads top→bottom with blocks running right→left;\n // `sideways-lr` (ccw) reads bottom→top with blocks running left→right —\n // hence the opposite reversals on the two axes.\n const orient = this._contentOrientation();\n const vertical = orient !== \"horizontal\";\n\n const inlineAxis: \"h\" | \"v\" = vertical ? \"v\" : \"h\";\n const blockAxis: \"h\" | \"v\" = vertical ? \"h\" : \"v\";\n const inlineReversed = orient === \"ccw\";\n const blockReversed = orient === \"cw\";\n\n if (renderDesc && this._glyph && this._isDescriptionUnderGlyph()) {\n if (!this._innerRow) {\n this._innerRow = new Component();\n this._innerRow.setInsets(new Insets(0, 0, 0, 0));\n this._innerRow.setPointerEvents(\"none\");\n }\n if (!this._outerColumn) {\n this._outerColumn = new Component();\n this._outerColumn.setInsets(new Insets(0, 0, 0, 0));\n this._outerColumn.setPointerEvents(\"none\");\n }\n\n this._orientBox(this._content, inlineAxis, 2);\n this._orientBox(this._innerRow, inlineAxis, 2);\n this._orientBox(this._outerColumn, blockAxis, 0);\n\n this._addContentChildren(this._innerRow, renderText ? [this._glyph, this._text] : [this._glyph], inlineReversed);\n this._addContentChildren(this._outerColumn, [this._innerRow, this._description!], blockReversed);\n this._content.addComponent(this._outerColumn);\n } else if (renderDesc) {\n // Title + description stack in the column so the description trails\n // the title; the glyph (if any) leads the whole stack on the inline\n // axis.\n this._orientBox(this._content, inlineAxis, 2);\n this._orientBox(this._titleColumn, blockAxis, 0);\n\n this._addContentChildren(this._titleColumn, renderText ? [this._text, this._description!] : [this._description!], blockReversed);\n\n const inlineChildren = this._glyph ? [this._glyph, this._titleColumn] : [this._titleColumn];\n this._addContentChildren(this._content, inlineChildren, inlineReversed);\n } else {\n // No description: place the title directly beside (or before) the\n // glyph rather than wrapping it in `_titleColumn`. The column reports\n // a null baseline (a plain VBox-backed Component), which would hide\n // the title's baseline from `_content` and leave the glyph centred;\n // added directly, the title's baseline is visible so the glyph drops\n // onto the text baseline — matching the description topology.\n // A glyph-only button (empty label) drops the inline spacing to 0 so\n // the glyph sits flush at the box centre: the default spacing between\n // the glyph and a zero-width label pads one side and pushes the glyph\n // off centre (the visible \"hugs the left edge\" on icon buttons). The\n // empty `_text` stays in the row so its line box still drives the\n // button height — dropping it shrinks the button vertically. A hidden\n // title renders blank (`_text.getText() === \"\"`), so it takes this\n // same glyph-only path: still parented to drive the height, spacing\n // collapsed, no title shown.\n const hasText = this._text.getText().valueOf() !== \"\";\n this._orientBox(this._content, inlineAxis, this._glyph && !hasText ? 0 : 2);\n\n const inlineChildren = this._glyph ? [this._glyph, this._text] : [this._text];\n this._addContentChildren(this._content, inlineChildren, inlineReversed);\n }\n\n // Optically centre a single line of label text. The Fit/anchor centring\n // centres the line box geometrically, but a label's ink occupies only\n // cap-top→baseline, so the empty descender band makes the text read as\n // too high. A top inset of `Util.opticalCenterOffset()` nudges it down\n // onto its optical centre. Applies only to a non-empty single-line\n // horizontal label: a two-line title+description block centres as a\n // block (offset 0), a glyph-only button (empty `_text`) is already\n // box-centred, and a rotated label has no horizontal descender band.\n const opticalOffset = (!vertical && !renderDesc && this._text.getText().valueOf() !== \"\") ? Util.opticalCenterOffset() : 0;\n\n this._content.setInsets(new Insets(opticalOffset, 0, 0, 0));\n\n this._reflectAccessibleName();\n\n this._afterRebuildContentRow();\n }\n\n /**\n * Hook invoked at the end of {@link _rebuildContentRow}, after the glyph /\n * title / description children have been re-parented onto the content row.\n * The base implementation does nothing; subclasses that attach their own\n * persistent child to `_content` (e.g. [`SplitButton`](/api/component/button/classes/SplitButton)'s\n * trailing chevron) override it to re-append that child, since the rebuild\n * empties the row wholesale via `removeAllComponents` and would otherwise\n * orphan it on a `setGlyph` / `setDescription` / writing-mode change.\n */\n protected _afterRebuildContentRow(): void {\n }\n\n /**\n * Sizes the leading glyph's box so its height matches the title's rendered\n * line-box height (the line of text it sits beside), instead of the\n * Glyph's static 16×16 default. The lever is `setPreferredSize`, not\n * `setFontSize` — a font-size token does not resize an SVG glyph's box.\n *\n * Theme-reactive: it reads `_text.getLineHeight()` (the button title's\n * additive line box, button font-size + `--ts-ui-line-padding`) and runs\n * from {@link recomputePreferredSize},\n * which already re-fires on every theme change — so the glyph re-tracks the\n * title line height without an extra listener. Bails when the line height\n * isn't resolved yet (pre-measure) so it never writes a 0/NaN size.\n *\n * Opt-out guard: it only overwrites a glyph size it itself last wrote\n * (tracked in `_glyphSyncedSize`). A subclass / consumer that pinned its\n * own glyph size (SpinButton's chevron, Tab's close ✕) leaves the current\n * size different from `_glyphSyncedSize`, so this method skips it and the\n * explicit size survives later re-syncs.\n */\n private _syncGlyphSize(): void {\n const glyph = this._glyph;\n if (!glyph) {\n return;\n }\n\n // A consumer-pinned glyph keeps its fixed size across theme changes.\n if (this._glyphSizePinned) {\n return;\n }\n\n const lineHeight = this._text.getLineHeight();\n if (lineHeight === null) {\n return;\n }\n\n const px = Math.round(lineHeight);\n\n const current = glyph.getPreferredSize();\n const ours = this._glyphSyncedSize;\n if (current && ours !== null && (current.width !== ours || current.height !== ours)) {\n return;\n }\n\n glyph.setPreferredSize({ width: px, height: px });\n this._glyphSyncedSize = px;\n }\n\n /**\n * Sets or clears an optional leading [`Glyph`](/api/component/display/classes/Glyph) shown alongside the button's text.\n *\n * @param name - Registry glyph name to display, or `null` to clear an existing glyph.\n *\n * @returns This component, for method chaining.\n *\n * @remarks\n * The glyph leads the content row. Its box is auto-sized to match the\n * title's line height; a consumer can override by sizing the returned glyph\n * explicitly via `getGlyph().setPreferredSize(...)`. Empty text combined\n * with `setGlyph(name)` therefore renders as a glyph-only button with no\n * visual artifacts at the default 0px spacing.\n */\n setGlyph(name: string): this {\n const glyph = new Glyph(name);\n glyph.setPointerEvents(\"none\");\n\n // Reassign before the rebuild; the rebuild empties the content\n // containers, detaching any previous glyph so it is dropped cleanly.\n this._glyph = glyph;\n // A fresh glyph hasn't been line-height-synced yet — clear the guard\n // so `_syncGlyphSize` (via recomputePreferredSize) sizes it.\n this._glyphSyncedSize = null;\n\n // Re-apply an instance glyph tint so it survives a glyph swap.\n if (this._options.glyphColor !== undefined) {\n glyph.setForegroundColor(this._options.glyphColor);\n }\n\n this._rebuildContentRow();\n\n // The content row's preferred size shifted — re-sync the button's\n // auto-derived preferred size (also sizes the glyph) unless the\n // consumer has pinned it.\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Removes the leading glyph from the button, if one is present.\n *\n * @returns This component, for method chaining.\n */\n clearGlyph(): this {\n this._glyph = null;\n this._glyphSyncedSize = null;\n\n this._rebuildContentRow();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns the current leading glyph component, or null if none is set.\n *\n * @returns The [`Glyph`](/api/component/display/classes/Glyph) instance, or null.\n */\n getGlyph(): Glyph | null {\n return this._glyph;\n }\n\n /**\n * Tints the leading glyph independently of the button's `foregroundColor`.\n * `foregroundColor` sets `color` on the whole button, which both the title\n * and the glyph (via its `currentColor` SVG fill) inherit; this sets `color`\n * on the glyph element alone, so the glyph can be tinted while the title\n * keeps the button's own colour. Stored so a later {@link setGlyph} re-applies\n * it. A no-op on the element until a glyph exists; the colour still takes\n * effect once one is set.\n *\n * @param color - A CSS color string.\n *\n * @returns This component, for method chaining.\n */\n setGlyphColor(color: string): this {\n this._options.glyphColor = color;\n this._glyph?.setForegroundColor(color);\n\n return this;\n }\n\n /**\n * Tints the description subtitle independently of the button's\n * `foregroundColor`, overriding the default dim description colour. Stored so\n * a lazily-created description ({@link setDescription}) picks it up. A no-op on\n * the element until a description exists.\n *\n * @param color - A CSS color string.\n *\n * @returns This component, for method chaining.\n */\n setDescriptionColor(color: string): this {\n this._options.descriptionColor = color;\n this._description?.setForegroundColor(color);\n\n return this;\n }\n\n /**\n * Pins the leading glyph to a fixed square size and opts it out of the\n * theme-reactive line-height sync. Use for compact icon buttons whose glyph\n * must stay a constant size regardless of the active theme's font metrics —\n * a tab's close ✕, a spinner chevron. A plain\n * `getGlyph().setPreferredSize(...)` is unreliable here: if it runs before\n * the button is attached (line height unresolved), the first theme change\n * re-tracks the glyph to the title line height and grows it. This sets the\n * authoritative `_glyphSizePinned` opt-out so the sync skips the glyph.\n *\n * @param px - The square glyph size in pixels.\n *\n * @returns This button, for chaining.\n */\n pinGlyphSize(px: number): this {\n this._glyphSizePinned = true;\n\n this._glyph?.setPreferredSize({ width: px, height: px });\n\n return this;\n }\n\n /**\n * Returns the offset from the top of the button to the label's text baseline.\n *\n * @returns The baseline offset in pixels, or `null` when the label has no baseline.\n */\n getBaseline(): number | null {\n return this.wrapInnerBaseline(this._text.getBaseline());\n }\n\n /**\n * Registers a listener for this button's `\"action\"` event — fired on\n * click. A typed semantic shorthand over {@link Event.addListener}\n * (the underlying DOM event is `\"click\"`); `\"action\"` is currently the\n * only allowed event name.\n *\n * @param event - The event name. Only `\"action\"` is accepted.\n * @param listener - The callback to invoke when the button is actioned.\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, \"click\", listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"action\"` listener. A typed shorthand\n * over {@link Event.removeListener}; the exact callback reference must\n * 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, \"click\", listener);\n\n return this;\n }\n\n /**\n * Programmatically actions the button, as if the user had clicked it:\n * fires this button's own `\"click\"` event so every registered\n * {@link on | `\"action\"`} handler runs. Provided so other components can\n * drive a button through its named surface rather than synthesising its\n * DOM event from the outside — e.g. [`ToolBar`](/api/component/menubar/classes/ToolBar)\n * replays an overflowed button's action from its dropdown. A\n * [`ToggleButton`](/api/component/button/classes/ToggleButton)\n * inherits this and toggles, because its internal toggle is wired to the\n * same `\"click\"`.\n *\n * @returns This button, for method chaining.\n */\n click(): this {\n Event.fireEvent(this, \"click\");\n\n return this;\n }\n\n /**\n * Registers a `pointerdown` event listener on this button. The named\n * surface lets external consumers (e.g.\n * [`AbstractPickerField`](/api/component/input/classes/AbstractPickerField))\n * route through the component rather than reaching for\n * `Event.addListener(button, \"pointerdown\", ...)` directly, preserving\n * the framework's named-listener contract.\n *\n * @param listener - Called with the originating PointerEvent.\n *\n * @returns This component, for method chaining.\n */\n addPointerDownListener(listener: Event.Listener): this {\n Event.addListener(this, \"pointerdown\", listener);\n\n return this;\n }\n\n /**\n * Returns whether this button is currently in `chromeless` mode (no\n * border / shadow / gradient / pressed-hover treatments).\n *\n * @returns True when chrome dispatches are gated off.\n */\n isChromeless(): boolean {\n return this._options.chromeless ?? this._defaultOptions.chromeless ?? false;\n }\n\n /**\n * Toggles the `chromeless` flag and reconciles the DOM. When flipping\n * to `true`, clears every chrome property currently on the element\n * before recording the flag. When flipping to `false`, restores the\n * chromeful defaults from `_defaultOptions` (which retains both\n * Button's base defaults and any subclass chrome layered in at\n * construction, so the round-trip is loss-free for both sources).\n *\n * @param value - The new chromeless state.\n *\n * @returns This component, for method chaining.\n *\n * @remarks Consumer-supplied chrome that came in via the caller's\n * `options` bag (rather than the subclass's `_defaultOptions`) is not\n * recovered by `setChromeless(false)` — only the defaults round-trip.\n */\n setChromeless(value: boolean): this {\n if (this.isChromeless() === value) {\n return this;\n }\n\n if (value) {\n // Clear the DOM before flipping the flag. The clear* setters\n // are not gated today, but the ordering keeps the intent\n // self-evident if a future change does gate them.\n this._clearChrome();\n this._options.chromeless = true;\n\n // Chromeless wins over flat: `_clearChrome` already stripped the\n // flat hover/pressed treatments, so drop the flat flag too to keep\n // `isFlat()` honest.\n this._flat = false;\n this._options.flat = false;\n } else {\n // Flip the flag first so the clear/set side-effects on the\n // restore path are not intercepted by anything that might\n // later gate them.\n this._options.chromeless = false;\n this._restoreChrome();\n }\n\n return this;\n }\n\n /**\n * Clears every chrome property the framework defaults touch. Pressed\n * and hover rules are only touched when their lazy backing slots have\n * already been allocated — calling the `clearX` setters when nothing\n * was ever installed would touch the lazy getters and acquire empty\n * orphan rules.\n */\n private _clearChrome(): void {\n this.clearBorder();\n this.clearBorderRadius();\n this.clearShadow();\n this.clearBackgroundImage();\n\n if (this._pressedStyleRule !== undefined) {\n this.clearPressedBackgroundColor();\n this.clearPressedBackgroundImage();\n this.clearPressedForegroundColor();\n this.clearPressedShadow();\n this.clearPressedBorderRadius();\n this.clearPressedBorder();\n }\n\n if (this._hoverStyleRule !== undefined) {\n this.clearHoverBackgroundColor();\n this.clearHoverBackgroundImage();\n this.clearHoverForegroundColor();\n this.clearHoverShadow();\n this.clearHoverBorderRadius();\n this.clearHoverBorder();\n }\n }\n\n /**\n * Re-applies chrome from `_defaultOptions`, which carries both Button's\n * base defaults and any subclass chrome layered in at construction via\n * the third constructor arg. Consumer-supplied chrome (from the\n * caller's options bag, not `_defaultOptions`) is not recovered here.\n */\n private _restoreChrome(): void {\n const d = this._defaultOptions as ButtonOptions;\n\n // Re-apply the chromeful resting background `_applyFlatChrome` cleared.\n // Flat set the sentinel \"transparent\" only when neither the consumer nor\n // a subclass default pinned a colour (otherwise that colour was\n // preserved through flat and needs no restore); swap that sentinel back\n // for the framework token so the un-flattened button regains its fill.\n if (d.backgroundColor === undefined && this._options.backgroundColor === \"transparent\") {\n this.setBackgroundColor(BUTTON_RESTING_BACKGROUND);\n }\n\n if (d.border !== undefined) this.setBorder(d.border);\n if (d.borderRadius !== undefined) this.setBorderRadius(d.borderRadius);\n if (d.shadow !== undefined) this.setShadow(d.shadow);\n if (d.backgroundImage !== undefined) this.setBackgroundImage(d.backgroundImage);\n\n if (d.pressedForegroundColor !== undefined) this.setPressedForegroundColor(d.pressedForegroundColor);\n if (d.pressedBackgroundColor !== undefined) this.setPressedBackgroundColor(d.pressedBackgroundColor);\n if (d.pressedBackgroundImage !== undefined) this.setPressedBackgroundImage(d.pressedBackgroundImage);\n if (d.pressedShadow !== undefined) this.setPressedShadow (d.pressedShadow);\n if (d.pressedBorder !== undefined) this.setPressedBorder (d.pressedBorder);\n else this.clearPressedBorder ();\n if (d.pressedBorderRadius !== undefined) this.setPressedBorderRadius (d.pressedBorderRadius);\n\n if (d.hoverForegroundColor !== undefined) this.setHoverForegroundColor (d.hoverForegroundColor);\n if (d.hoverBackgroundColor !== undefined) this.setHoverBackgroundColor (d.hoverBackgroundColor);\n if (d.hoverBackgroundImage !== undefined) this.setHoverBackgroundImage (d.hoverBackgroundImage);\n if (d.hoverShadow !== undefined) this.setHoverShadow (d.hoverShadow);\n if (d.hoverBorder !== undefined) this.setHoverBorder (d.hoverBorder);\n else this.clearHoverBorder ();\n if (d.hoverBorderRadius !== undefined) this.setHoverBorderRadius (d.hoverBorderRadius);\n }\n\n /**\n * Returns whether this button is currently in `flat` mode (no resting\n * border / shadow / gradient; a light frame on hover and a sunken inset\n * frame on press).\n *\n * @returns True when the flat appearance is applied.\n */\n isFlat(): boolean {\n return this._flat ?? false;\n }\n\n /**\n * Toggles the classical `flat` appearance and reconciles the DOM. When\n * flipping to `true`, suppresses the resting frame and installs the flat\n * hover / pressed treatments from the `--ts-ui-button-flat-*` tokens; when\n * flipping to `false`, restores the raised chrome from `_defaultOptions`\n * via the same round-trip `setChromeless(false)` uses. Mutually exclusive\n * with `chromeless` (chromeless wins): `setFlat(true)` no-ops with a\n * dev-time warning when the button is chromeless.\n *\n * @param value - The new flat state.\n *\n * @returns This component, for method chaining.\n *\n * @remarks Consumer-supplied chrome that came in via the caller's\n * `options` bag (rather than the subclass's `_defaultOptions`) is not\n * recovered by `setFlat(false)` — only the defaults round-trip, matching\n * the documented `setChromeless(false)` tradeoff.\n */\n setFlat(value: boolean): this {\n if (value && this.isChromeless()) {\n console.warn(\"Button #\" + this.getId() + \": setFlat(true) ignored — flat and chromeless are mutually exclusive, chromeless wins.\");\n\n return this;\n }\n\n if ((this._flat ?? false) === value) {\n return this;\n }\n\n if (value) {\n this._flat = true;\n this._options.flat = true;\n this._applyFlatChrome();\n } else {\n this._flat = false;\n this._options.flat = false;\n this._restoreChrome();\n // `_restoreChrome` recovers borders/shadows but not insets, so a\n // flat glyph-only button un-flattened here would keep its tight\n // square. Re-resolve so it falls back to the default (or compact)\n // perimeter for its new state.\n this._resolveInsets();\n }\n\n return this;\n }\n\n /**\n * Returns whether this button is currently in `compact` mode (tighter\n * symmetric insets).\n *\n * @returns True when compact rendering is applied.\n */\n isCompact(): boolean {\n return this._compact ?? false;\n }\n\n /**\n * Toggles compact rendering. When `true`, the button's inset perimeter\n * tightens — text buttons to `(2,6,2,6)`, glyph-only buttons to a\n * `(2,2,2,2)` square — so the button reads denser (e.g. packed into a\n * [`ToolBar`](/api/component/menubar/classes/ToolBar)); `false` restores the\n * default `(5,10,5,10)` (or the flat glyph square, if flat). Orthogonal to\n * `flat`: a button can be compact, flat, both, or neither, and compact\n * insets win over the flat square when both apply.\n *\n * @param value - The new compact state.\n *\n * @returns This component, for method chaining.\n */\n setCompact(value: boolean): this {\n if ((this._compact ?? false) === value) {\n return this;\n }\n\n this._compact = value;\n this._options.compact = value;\n this._resolveInsets();\n\n return this;\n }\n\n /**\n * Suppresses the resting frame (border / radius / shadow / gradient) the\n * same way the chromeless branch does, masks the inherited raised\n * `pressedX` / `hoverX` defaults so a re-apply can't leak them, then\n * installs the flat hover and sunken-pressed treatments sourced from the\n * `--ts-ui-button-flat-*` tokens through the existing lazy style-rule\n * setters. Glyph-only buttons (a glyph with an empty label) tighten to a\n * compact square inset so they read as toolbar icon buttons.\n */\n private _applyFlatChrome(): void {\n // Reserve the 1px frame at rest with a transparent border (rather than\n // clearing it like the chromeless branch). Under `box-sizing: border-box`\n // a border consumes content-box space, so a border that only appears on\n // `:hover` / `:active` would nudge the centred label by 1px. Reserving it\n // transparent keeps the geometry identical across states — the hover /\n // pressed rules below only swap the border *colour*. Matches the 1px\n // width of the `--ts-ui-button-flat-{hover,pressed}-border` tokens;\n // widen this in lockstep if those ever exceed 1px.\n this.setBorder(\"1px solid transparent\");\n\n // Clear (not just mask) the resting radius, shadow, and background image:\n // the chromeful `super.applyChromeOptions` already wrote these defaults\n // (`--ts-ui-border-radius`, `--ts-ui-button-shadow`, `--ts-ui-button-bg`)\n // into the rules, so nulling only the options would leave them painting\n // behind a \"flat\" button — a drop shadow ringing the button and rounded\n // corners.\n this.clearBorderRadius();\n this.clearShadow();\n this.clearBackgroundImage();\n\n // Drop the resting background *colour* too — flat is transparent at\n // rest. The chromeful path wrote `BUTTON_RESTING_BACKGROUND` into the\n // rule via `setBackgroundColor` (see `applyChromeOptions`), which also\n // stored it in `_options.backgroundColor`. A runtime `setFlat(true)` on\n // an already-rendered chromeful button therefore sees that token here,\n // not `undefined`; recognising it lets us override the rule to\n // transparent while still preserving a genuine consumer-set colour (the\n // old field-only write left the token painting). `_restoreChrome`\n // re-applies the token on `setFlat(false)`.\n const restingBackground = this.getBackgroundColor();\n\n if (restingBackground === undefined || restingBackground === BUTTON_RESTING_BACKGROUND) {\n this.setBackgroundColor(\"transparent\");\n }\n\n // Clear the inherited raised pressed/hover background images and the\n // raised hover shadow (written by the chromeful path above) so they\n // don't paint under the flat hover / pressed colours installed below.\n this.clearPressedBackgroundImage();\n this.clearHoverBackgroundImage();\n this.clearHoverShadow();\n\n // Install the flat hover frame + sunken pressed frame from the tokens.\n this.setHoverBackgroundColor(\"var(--ts-ui-button-flat-hover-bg, rgba(0, 0, 0, 0.06))\");\n this.setHoverBorder(\"var(--ts-ui-button-flat-hover-border, 1px solid rgb(200, 200, 200))\");\n this.setPressedBackgroundColor(\"var(--ts-ui-button-flat-pressed-bg, rgba(0, 0, 0, 0.10))\");\n this.setPressedShadow(\"var(--ts-ui-button-flat-pressed-shadow, inset 1px 1px 3px rgba(0, 0, 0, 0.25))\");\n this.setPressedBorder(\"var(--ts-ui-button-flat-pressed-border, 1px solid rgb(180, 180, 180))\");\n\n // Re-resolve the inset perimeter for the new flat state. Runs through\n // the shared resolver so the construction-time flat path — where\n // `_glyph` / `_text` don't yet exist during the super cascade — can\n // re-apply it from the constructor body once the content row is built.\n this._resolveInsets();\n }\n\n /**\n * Resolves and applies the button's inset perimeter from the three live\n * inputs — `_compact`, `_flat`, and whether the button is glyph-only (a\n * glyph with an empty label). Compact wins over the flat square so a compact\n * flat icon tightens to `(2,2,2,2)` rather than the non-compact flat\n * `(4,4,4,4)`. The resolver computes an absolute target each call (never a\n * delta), so the construction-time double-invocation (the cascade's\n * `_applyFlatChrome` before the content row exists, then the constructor's\n * re-evaluation) and later `setCompact` / `setFlat` flips all converge on\n * the same answer without drift.\n *\n * No-ops until the content row is built — `_text` is assigned in the\n * constructor body, after the super cascade that first fires the flat /\n * compact setters; the constructor re-invokes this once the row exists.\n */\n private _resolveInsets(): void {\n if (this._text === undefined) {\n return;\n }\n\n const glyphOnly = this._glyph !== null && this._text.getText().valueOf() === \"\";\n\n let insets: Insets;\n\n if (this._compact && glyphOnly) {\n insets = BUTTON_COMPACT_INSETS_GLYPH;\n } else if (this._compact) {\n insets = BUTTON_COMPACT_INSETS_TEXT;\n } else if (this._flat && glyphOnly) {\n insets = BUTTON_FLAT_GLYPH_INSETS;\n } else {\n insets = BUTTON_DEFAULT_INSETS;\n }\n\n this.setInsets(insets);\n }\n\n /**\n * Updates the button's insets. Overrides Component's setter so the\n * auto-sized preferred size re-syncs to the new inset perimeter\n * without explicit consumer prodding (subclasses like `MenuBarButton`\n * change insets in their constructor body).\n *\n * @param insets - The new perimeter insets.\n * @returns This component, for method chaining.\n */\n setInsets(insets: Insets): this {\n super.setInsets(insets);\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Records a consumer-supplied preferred size. Flips\n * `_consumerSetPreferredSize` so future auto-fires from\n * `recomputePreferredSize` no-op — the consumer's explicit intent\n * wins permanently for the lifetime of this instance.\n *\n * @param size - The preferred size in pixels.\n * @returns This component, for method chaining.\n */\n setPreferredSize(size: Size): this {\n this._consumerSetPreferredSize = true;\n super.setPreferredSize(size);\n\n return this;\n }\n\n /**\n * Returns the preferred size, derived live from the content row +\n * perimeter while the consumer hasn't pinned one. Deriving live (rather\n * than reading a cached value) means\n * the button always tracks its current label / glyph: a {@link setText}\n * that grows or shrinks the label is reflected the next time the parent\n * layout queries this button, with no manual recompute call needed at the\n * mutation site.\n *\n * When the consumer has supplied an explicit `preferredSize`, that pinned\n * value (recorded by {@link setPreferredSize}) wins via `super`.\n *\n * @returns The preferred `{width, height}`.\n */\n getPreferredSize(): Size | null {\n // A consumer-pinned size or a class-level `preferredSize` default both\n // pin the button rather than deriving from content. The default now\n // lives in `_defaultOptions` (it is no longer dispatched through\n // `setPreferredSize`, so it never flips `_consumerSetPreferredSize`);\n // honour it here so subclasses like TabCloseButton keep their fixed size.\n if (this._consumerSetPreferredSize || this._defaultOptions.preferredSize) {\n return super.getPreferredSize();\n }\n\n return this.computePreferredSize();\n }\n\n /**\n * Notifies the parent layout that this button's content-derived size may\n * have changed, so it re-queries {@link getPreferredSize} (which derives\n * live). Pushes the freshly computed size through Component's setter both\n * to fire the parent-relayout notification and to dedupe (the setter\n * no-ops when the size is unchanged). Auto-fires from the end of the\n * constructor, `setGlyph`, `clearGlyph`, `setInsets`, and the registered\n * `ThemeManager.onThemeChange` handler — the content mutations that do\n * not bubble a preferred-size change on their own (a label `setText`\n * already bubbles via its own measurement).\n *\n * No-ops when the consumer has supplied an explicit `preferredSize`\n * (Button's `setPreferredSize` override records that intent).\n *\n * Subclasses customise the size by overriding {@link computePreferredSize}\n * rather than touching this method — the consumer-flag and auto-fire\n * wiring stays here.\n */\n protected recomputePreferredSize(): void {\n // Size the glyph to the title line box first, above the consumer-pinned\n // early-return, so the glyph still line-height-matches even when the\n // button's own size is frozen. Subclasses that pin their glyph size opt\n // out via _syncGlyphSize's per-glyph guard, not this early-return.\n this._syncGlyphSize();\n\n if (this._consumerSetPreferredSize || this._defaultOptions.preferredSize) {\n return;\n }\n\n const size = this.computePreferredSize();\n\n // Bypass our own override of setPreferredSize so the consumer flag\n // doesn't flip on an auto-fire. `super` is `Component`. The written\n // value isn't read back for sizing (getPreferredSize derives live);\n // the call stands in for the parent-relayout notification + dedupe.\n super.setPreferredSize({ width: size.width, height: size.height });\n }\n\n /**\n * Computes the auto-sized preferred size from the content row's\n * preferred size plus this button's perimeter (insets + border).\n * Mirrors `Fit.getPreferredSize`'s use of `getPerimeterSize` so the\n * border width isn't truncated off the text. Subclasses override to\n * alter — the typical case is replacing the derived height with a\n * fixed token (see `MenuBarButton`).\n *\n * @returns The `{ width, height }` Button reports as its preferred size\n * while the consumer hasn't pinned one.\n */\n protected computePreferredSize(): { width: number; height: number } {\n const content = this._content?.getPreferredSize() ?? { width: 0, height: 0 };\n const perim = this.getPerimeterSize();\n\n return {\n width: content.width + perim.left + perim.right,\n height: content.height + perim.top + perim.bottom,\n };\n }\n\n /**\n * Returns the background color applied when the button is in the :active state.\n *\n * @returns The CSS color string, or null if not set.\n */\n getPressedBackgroundColor(): string | null {\n return this._options.pressedBackgroundColor ?? this._defaultOptions.pressedBackgroundColor ?? null;\n }\n\n /**\n * Sets the background color for the :active CSS rule.\n *\n * @param backgroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedBackgroundColor(backgroundColor: string): this {\n this._options.pressedBackgroundColor = backgroundColor;\n this.pressedStyleRule.set(\"backgroundColor\", backgroundColor);\n\n return this;\n }\n\n /**\n * Removes the background-color from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBackgroundColor(): this {\n this._options.pressedBackgroundColor = undefined;\n this.pressedStyleRule.set(\"backgroundColor\", null);\n\n return this;\n }\n\n /**\n * Returns the background image applied when the button is in the :active state.\n *\n * @returns The CSS background-image string, or null if not set.\n */\n getPressedBackgroundImage(): string | null {\n return this._options.pressedBackgroundImage ?? this._defaultOptions.pressedBackgroundImage ?? null;\n }\n\n /**\n * Sets the background image for the :active CSS rule.\n *\n * @param backgroundImage - Optional. A CSS background-image string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedBackgroundImage(backgroundImage: string): this {\n this._options.pressedBackgroundImage = backgroundImage;\n this.pressedStyleRule.set(\"backgroundImage\", backgroundImage);\n\n return this;\n }\n\n /**\n * Removes the background-image from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBackgroundImage(): this {\n this._options.pressedBackgroundImage = undefined;\n this.pressedStyleRule.set(\"backgroundImage\", null);\n\n return this;\n }\n\n /**\n * Returns the text color applied when the button is in the :active state.\n *\n * @returns The CSS color string, or null if not set.\n */\n getPressedForegroundColor(): string | null {\n return this._options.pressedForegroundColor ?? this._defaultOptions.pressedForegroundColor ?? null;\n }\n\n /**\n * Sets the text color for the :active CSS rule.\n *\n * @param foregroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedForegroundColor(foregroundColor: string): this {\n this._options.pressedForegroundColor = foregroundColor;\n this.pressedStyleRule.set(\"color\", foregroundColor);\n\n return this;\n }\n\n /**\n * Removes the color (foreground) from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedForegroundColor(): this {\n this._options.pressedForegroundColor = undefined;\n this.pressedStyleRule.set(\"color\", null);\n\n return this;\n }\n\n /**\n * Returns the border applied when the button is in the :active state.\n *\n * @returns The {@link BorderOptions} for the :active state, or null if not set.\n */\n getPressedBorder(): BorderOptions | null {\n return this._pressedBorder;\n }\n\n /**\n * Sets the border for the :active CSS rule. Accepts either a {@link BorderOptions}\n * bag or a CSS `border` shorthand string (sugar for `{ border: <string> }`).\n *\n * @param options - Border configuration, a CSS `border` shorthand string, or omitted for a `none` border.\n *\n * @returns This component, for method chaining.\n */\n setPressedBorder(options?: BorderOptions | string): this {\n this._pressedBorder = typeof options === \"string\" ? { border: options } : (options ?? {});\n this.pressedStyleRule.setMany(borderToStyle(this._pressedBorder));\n\n return this;\n }\n\n /**\n * Removes the border from the :active CSS rule, reverting the pressed\n * state to no explicit border. Lets the un-flatten / un-chromeless round\n * trip strip a flat pressed border that has no raised default to restore.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBorder(): this {\n this._pressedBorder = null;\n this.pressedStyleRule.setMany({\n borderTop: null,\n borderRight: null,\n borderBottom: null,\n borderLeft: null,\n });\n\n return this;\n }\n\n /**\n * Returns the border radius applied when the button is in the :active state.\n *\n * @returns The CSS border-radius string, or null if not set.\n */\n getPressedBorderRadius(): string | null {\n return this._options.pressedBorderRadius ?? null;\n }\n\n /**\n * Sets the border radius for the :active CSS rule.\n *\n * @param borderRadius - Optional. A CSS border-radius string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setPressedBorderRadius(borderRadius: string): this {\n this._options.pressedBorderRadius = borderRadius;\n this.pressedStyleRule.set(\"borderRadius\", borderRadius);\n\n return this;\n }\n\n /**\n * Removes the border-radius from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedBorderRadius(): this {\n this._options.pressedBorderRadius = undefined;\n this.pressedStyleRule.set(\"borderRadius\", null);\n\n return this;\n }\n\n /**\n * Returns the box shadow applied when the button is in the :active state.\n *\n * @returns The CSS box-shadow string, or null if not set.\n */\n getPressedShadow(): string | null {\n return this._options.pressedShadow ?? this._defaultOptions.pressedShadow ?? null;\n }\n\n /**\n * Sets the box shadow for the :active CSS rule.\n *\n * @param shadow - A CSS box-shadow string, or null to set the shadow to \"none\".\n *\n * @returns This component, for method chaining.\n */\n setPressedShadow(shadow: string): this {\n this._options.pressedShadow = shadow;\n this.pressedStyleRule.set(\"boxShadow\", shadow);\n\n return this;\n }\n\n /**\n * Removes the box-shadow from the :active CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearPressedShadow(): this {\n this._options.pressedShadow = undefined;\n this.pressedStyleRule.set(\"boxShadow\", null);\n\n return this;\n }\n\n /**\n * Returns the background color applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS color string, or null if not set.\n */\n getHoverBackgroundColor(): string | null {\n return this._options.hoverBackgroundColor ?? this._defaultOptions.hoverBackgroundColor ?? null;\n }\n\n /**\n * Sets the background color for the `:hover:not(:active)` CSS rule.\n *\n * @param backgroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverBackgroundColor(backgroundColor: string): this {\n this._options.hoverBackgroundColor = backgroundColor;\n this.hoverStyleRule.set(\"backgroundColor\", backgroundColor);\n\n return this;\n }\n\n /**\n * Removes the background-color from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBackgroundColor(): this {\n this._options.hoverBackgroundColor = undefined;\n this.hoverStyleRule.set(\"backgroundColor\", null);\n\n return this;\n }\n\n /**\n * Returns the background image applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS background-image string, or null if not set.\n */\n getHoverBackgroundImage(): string | null {\n return this._options.hoverBackgroundImage ?? this._defaultOptions.hoverBackgroundImage ?? null;\n }\n\n /**\n * Sets the background image for the `:hover:not(:active)` CSS rule.\n *\n * @param backgroundImage - A CSS background-image string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverBackgroundImage(backgroundImage: string): this {\n this._options.hoverBackgroundImage = backgroundImage;\n this.hoverStyleRule.set(\"backgroundImage\", backgroundImage);\n\n return this;\n }\n\n /**\n * Removes the background-image from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBackgroundImage(): this {\n this._options.hoverBackgroundImage = undefined;\n this.hoverStyleRule.set(\"backgroundImage\", null);\n\n return this;\n }\n\n /**\n * Returns the text color applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS color string, or null if not set.\n */\n getHoverForegroundColor(): string | null {\n return this._options.hoverForegroundColor ?? null;\n }\n\n /**\n * Sets the text color for the `:hover:not(:active)` CSS rule.\n *\n * @param foregroundColor - A CSS color string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverForegroundColor(foregroundColor: string): this {\n this._options.hoverForegroundColor = foregroundColor;\n this.hoverStyleRule.set(\"color\", foregroundColor);\n\n return this;\n }\n\n /**\n * Removes the color (foreground) from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverForegroundColor(): this {\n this._options.hoverForegroundColor = undefined;\n this.hoverStyleRule.set(\"color\", null);\n\n return this;\n }\n\n /**\n * Returns the border applied when the pointer is over the button (but not pressed).\n *\n * @returns The {@link BorderOptions} for the hover state, or null if not set.\n */\n getHoverBorder(): BorderOptions | null {\n return this._hoverBorder;\n }\n\n /**\n * Sets the border for the `:hover:not(:active)` CSS rule. Accepts either a\n * {@link BorderOptions} bag or a CSS `border` shorthand string (sugar for\n * `{ border: <string> }`, e.g. `\"1px solid rgb(...)\"` or `\"none\"`). The four\n * CSS longhands are written so a per-side hover border survives.\n *\n * @param options - Border configuration, a CSS `border` shorthand string, or omitted for a `none` border.\n *\n * @returns This component, for method chaining.\n */\n setHoverBorder(options?: BorderOptions | string): this {\n this._hoverBorder = typeof options === \"string\" ? { border: options } : (options ?? {});\n this.hoverStyleRule.setMany(borderToStyle(this._hoverBorder));\n\n return this;\n }\n\n /**\n * Removes the border from the :hover CSS rule, reverting the hover state\n * to no explicit border. Lets the un-flatten / un-chromeless round trip\n * strip a flat hover border that has no raised default to restore.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBorder(): this {\n this._hoverBorder = null;\n this.hoverStyleRule.setMany({\n borderTop: null,\n borderRight: null,\n borderBottom: null,\n borderLeft: null,\n });\n\n return this;\n }\n\n /**\n * Returns the border radius applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS border-radius string, or null if not set.\n */\n getHoverBorderRadius(): string | null {\n return this._options.hoverBorderRadius ?? null;\n }\n\n /**\n * Sets the border radius for the `:hover:not(:active)` CSS rule.\n *\n * @param borderRadius - A CSS border-radius string, or null to clear the property.\n *\n * @returns This component, for method chaining.\n */\n setHoverBorderRadius(borderRadius: string): this {\n this._options.hoverBorderRadius = borderRadius;\n this.hoverStyleRule.set(\"borderRadius\", borderRadius);\n\n return this;\n }\n\n /**\n * Removes the border-radius from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverBorderRadius(): this {\n this._options.hoverBorderRadius = undefined;\n this.hoverStyleRule.set(\"borderRadius\", null);\n\n return this;\n }\n\n /**\n * Returns the box shadow applied when the pointer is over the button (but not pressed).\n *\n * @returns The CSS box-shadow string, or null if not set.\n */\n getHoverShadow(): string | null {\n return this._options.hoverShadow ?? this._defaultOptions.hoverShadow ?? null;\n }\n\n /**\n * Sets the box shadow for the `:hover:not(:active)` CSS rule.\n *\n * @param shadow - A CSS box-shadow string, or null to set the shadow to \"none\".\n *\n * @returns This component, for method chaining.\n */\n setHoverShadow(shadow: string): this {\n this._options.hoverShadow = shadow;\n this.hoverStyleRule.set(\"boxShadow\", shadow);\n\n return this;\n }\n\n /**\n * Removes the box-shadow from the `:hover:not(:active)` CSS rule.\n *\n * @returns This component, for method chaining.\n */\n clearHoverShadow(): this {\n this._options.hoverShadow = undefined;\n this.hoverStyleRule.set(\"boxShadow\", null);\n\n return this;\n }\n\n /**\n * Enables or disables the button.\n *\n * @param enabled - True to enable, false to disable.\n *\n * @remarks\n * When disabled, sets the native `disabled` attribute on the underlying\n * `<button>` element (which suppresses pointer events and `:active`),\n * dims the button to 0.5 opacity, and switches the cursor to `not-allowed`.\n * Re-enabling restores the previous cursor and clears the opacity override.\n */\n setEnabled(enabled: boolean): this {\n if ((this._options.enabled ?? true) === enabled) {\n return this;\n }\n\n this._options.enabled = enabled;\n\n if (enabled) {\n this.setDisabledAttribute(false);\n this.clearOpacity();\n this.setCursor(this._enabledCursor);\n } else {\n this._enabledCursor = this.getCursor() ?? \"pointer\";\n this.setDisabledAttribute(true);\n this.setOpacity(0.5);\n this.setCursor(\"not-allowed\");\n }\n\n return this;\n }\n\n /**\n * Returns whether the button is currently enabled.\n *\n * @returns True if the button accepts user interaction.\n */\n isEnabled(): boolean {\n return this._options.enabled ?? true;\n }\n\n /**\n * Sets whether a description aligns full-width below the glyph (`true`,\n * default) or indented under the title text beside the glyph (`false`).\n * Only has a visible effect when the button has both a leading glyph and a\n * visible description; otherwise both modes resolve to the same minimal\n * row. Restructures the content row live.\n *\n * @param value - `true` for under-glyph (full width), `false` for under-title.\n *\n * @returns This component, for method chaining.\n */\n setDescriptionUnderGlyph(value: boolean): this {\n this._options.descriptionUnderGlyph = value;\n\n this._rebuildContentRow();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns whether the description aligns full-width under the glyph.\n *\n * @returns `true` for under-glyph (the default), `false` for under-title.\n */\n isDescriptionUnderGlyph(): boolean {\n return this._isDescriptionUnderGlyph();\n }\n\n /**\n * Sets whether the description is rendered on the button face. When\n * `false`, the button shows only its glyph and title, but the description\n * still appears in the hover tooltip — the text is stored either way. Does\n * not touch the tooltip (which is unaffected by this flag).\n *\n * @param value - `true` (default) to show the description on the button, `false` to hide it.\n *\n * @returns This component, for method chaining.\n */\n setShowDescription(value: boolean): this {\n this._options.showDescription = value;\n\n this._rebuildContentRow();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns whether the description is rendered on the button face.\n *\n * @returns `true` when the description shows on the button (the default), `false` when it is tooltip-only.\n */\n isShowDescription(): boolean {\n return this._isShowDescription();\n }\n\n /**\n * Sets whether the title is rendered on the button face. When `false`, the\n * button shows only its glyph, but the title still drives the hover tooltip\n * and is reflected into `aria-label` as the accessible name — the title is\n * stored either way. Toggling back to `true` restores the on-face label and\n * clears the reflected `aria-label`.\n *\n * @param value - `true` (default) to show the title on the button, `false` to hide it.\n *\n * @returns This component, for method chaining.\n */\n setShowText(value: boolean): this {\n this._options.showText = value;\n\n // Re-render `_text` to the title or blank, then rebuild the row and\n // re-resolve insets — hiding the title flips the button to glyph-only,\n // which changes both the inset perimeter and the glyph-only spacing.\n this._text.setText(value ? (this._options.text ?? \"\") : \"\");\n this._rebuildContentRow();\n this._resolveInsets();\n this.recomputePreferredSize();\n\n return this;\n }\n\n /**\n * Returns whether the title is rendered on the button face.\n *\n * @returns `true` when the title shows on the button (the default), `false` when it is tooltip-only.\n */\n isShowText(): boolean {\n return this._isShowText();\n }\n}\n\nconst ButtonCallable = callable(Button);\ntype ButtonCallable<TOptions extends ButtonOptions = ButtonOptions> = Button<TOptions>;\nexport {\n Button as _Button,\n ButtonCallable as Button\n};\n"],"mappings":"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"}