@jimka/typescript-ui 0.2.0 → 0.3.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 (167) hide show
  1. package/README.md +4 -0
  2. package/THIRD-PARTY-NOTICES.md +7 -3
  3. package/dist/lib/{AbstractBooleanInput-BGesvIZy.js → AbstractBooleanInput-C3O4kv7_.js} +2 -2
  4. package/dist/lib/{AbstractBooleanInput-BGesvIZy.js.map → AbstractBooleanInput-C3O4kv7_.js.map} +1 -1
  5. package/dist/lib/{AbstractInput-BxOtFrER.js → AbstractInput-DFWniHqq.js} +2 -2
  6. package/dist/lib/{AbstractInput-BxOtFrER.js.map → AbstractInput-DFWniHqq.js.map} +1 -1
  7. package/dist/lib/AnchorConstraints-CSUcj8Wx.js +2 -0
  8. package/dist/lib/AnchorConstraints-CSUcj8Wx.js.map +1 -0
  9. package/dist/lib/{AnimatedDropdown-CZQEqQi-.js → AnimatedDropdown-e9urCXK1.js} +2 -2
  10. package/dist/lib/{AnimatedDropdown-CZQEqQi-.js.map → AnimatedDropdown-e9urCXK1.js.map} +1 -1
  11. package/dist/lib/Border-Dq6dXkmS.js +2 -0
  12. package/dist/lib/{Border-BgaZkc1R.js.map → Border-Dq6dXkmS.js.map} +1 -1
  13. package/dist/lib/{Button-U1rsP4v8.js → Button-B36rBMKC.js} +2 -2
  14. package/dist/lib/{Button-U1rsP4v8.js.map → Button-B36rBMKC.js.map} +1 -1
  15. package/dist/lib/{Card-BVwOS8AG.js → Card-P8iUH2-C.js} +2 -2
  16. package/dist/lib/{Card-BVwOS8AG.js.map → Card-P8iUH2-C.js.map} +1 -1
  17. package/dist/lib/{ComboBox-B9EDPu6T.js → ComboBox-BjAhXXkY.js} +2 -2
  18. package/dist/lib/{ComboBox-B9EDPu6T.js.map → ComboBox-BjAhXXkY.js.map} +1 -1
  19. package/dist/lib/Component-CHTzJxEf.js +2 -0
  20. package/dist/lib/Component-CHTzJxEf.js.map +1 -0
  21. package/dist/lib/Container-xUbHk6l_.js +2 -0
  22. package/dist/lib/{Container-D5H8VxJK.js.map → Container-xUbHk6l_.js.map} +1 -1
  23. package/dist/lib/DOM-BeR95tE8.js +2 -0
  24. package/dist/lib/DOM-BeR95tE8.js.map +1 -0
  25. package/dist/lib/{DragManager-CI4mZQFy.js → DragManager-T5Oljd0F.js} +2 -2
  26. package/dist/lib/{DragManager-CI4mZQFy.js.map → DragManager-T5Oljd0F.js.map} +1 -1
  27. package/dist/lib/{FieldDecorator-CJs9gwqH.js → FieldDecorator-BaTGfKiW.js} +2 -2
  28. package/dist/lib/{FieldDecorator-CJs9gwqH.js.map → FieldDecorator-BaTGfKiW.js.map} +1 -1
  29. package/dist/lib/{Fit-DjRsiwlg.js → Fit-E-EIZv1k.js} +2 -2
  30. package/dist/lib/{Fit-DjRsiwlg.js.map → Fit-E-EIZv1k.js.map} +1 -1
  31. package/dist/lib/{Glyph-KEf4nIv2.js → Glyph-CnNslL95.js} +2 -2
  32. package/dist/lib/{Glyph-KEf4nIv2.js.map → Glyph-CnNslL95.js.map} +1 -1
  33. package/dist/lib/{Grid-DdL9TVeJ.js → Grid-BXS2T8MT.js} +2 -2
  34. package/dist/lib/{Grid-DdL9TVeJ.js.map → Grid-BXS2T8MT.js.map} +1 -1
  35. package/dist/lib/{GridConstraints-DkLueyrf.js → GridConstraints-D_kbd-h2.js} +2 -2
  36. package/dist/lib/{GridConstraints-DkLueyrf.js.map → GridConstraints-D_kbd-h2.js.map} +1 -1
  37. package/dist/lib/HBox-Dm3xgsDE.js +2 -0
  38. package/dist/lib/{HBox-Ce0GTCJP.js.map → HBox-Dm3xgsDE.js.map} +1 -1
  39. package/dist/lib/{Header-BQIVpBte.js → Header-CqFc2OUx.js} +2 -2
  40. package/dist/lib/{Header-BQIVpBte.js.map → Header-CqFc2OUx.js.map} +1 -1
  41. package/dist/lib/{IconText-Bo4Qcnot.js → IconText-CTM2r0Tv.js} +2 -2
  42. package/dist/lib/{IconText-Bo4Qcnot.js.map → IconText-CTM2r0Tv.js.map} +1 -1
  43. package/dist/lib/{LayoutSerialization-BT66TY83.js → LayoutSerialization-D1OHmxSB.js} +2 -2
  44. package/dist/lib/{LayoutSerialization-BT66TY83.js.map → LayoutSerialization-D1OHmxSB.js.map} +1 -1
  45. package/dist/lib/LayoutSizes-CvMmMgHJ.js +2 -0
  46. package/dist/lib/LayoutSizes-CvMmMgHJ.js.map +1 -0
  47. package/dist/lib/List-DHeOPAK2.js +2 -0
  48. package/dist/lib/List-DHeOPAK2.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-Dz4L7EKJ.js → Menu-B6ippw-p.js} +2 -2
  52. package/dist/lib/{Menu-Dz4L7EKJ.js.map → Menu-B6ippw-p.js.map} +1 -1
  53. package/dist/lib/{MenuButton-VJ5uC3c1.js → MenuButton-Bjm1OyB-.js} +2 -2
  54. package/dist/lib/{MenuButton-VJ5uC3c1.js.map → MenuButton-Bjm1OyB-.js.map} +1 -1
  55. package/dist/lib/{Panel-DzWdYwq1.js → Panel-D9lTLGqP.js} +2 -2
  56. package/dist/lib/{Panel-DzWdYwq1.js.map → Panel-D9lTLGqP.js.map} +1 -1
  57. package/dist/lib/Position-DRSlWn4Z.js +2 -0
  58. package/dist/lib/Position-DRSlWn4Z.js.map +1 -0
  59. package/dist/lib/{ProgressSpinner-zJKtGyNJ.js → ProgressSpinner-glv-29eK.js} +2 -2
  60. package/dist/lib/{ProgressSpinner-zJKtGyNJ.js.map → ProgressSpinner-glv-29eK.js.map} +1 -1
  61. package/dist/lib/{RadioButton-CYSSv4nV.js → RadioButton-R4S3c9WD.js} +2 -2
  62. package/dist/lib/{RadioButton-CYSSv4nV.js.map → RadioButton-R4S3c9WD.js.map} +1 -1
  63. package/dist/lib/{RovingTabIndex-BSZ31Vp5.js → RovingTabIndex-Dgt01LOE.js} +2 -2
  64. package/dist/lib/{RovingTabIndex-BSZ31Vp5.js.map → RovingTabIndex-Dgt01LOE.js.map} +1 -1
  65. package/dist/lib/{Scrollbar-BhtOjqkG.js → Scrollbar-CiVV3axj.js} +2 -2
  66. package/dist/lib/{Scrollbar-BhtOjqkG.js.map → Scrollbar-CiVV3axj.js.map} +1 -1
  67. package/dist/lib/{Slider-CdXm7E64.js → Slider-_JVvuQ8M.js} +2 -2
  68. package/dist/lib/{Slider-CdXm7E64.js.map → Slider-_JVvuQ8M.js.map} +1 -1
  69. package/dist/lib/{Spacer-D9NGW4QG.js → Spacer-B57i2-_G.js} +2 -2
  70. package/dist/lib/{Spacer-D9NGW4QG.js.map → Spacer-B57i2-_G.js.map} +1 -1
  71. package/dist/lib/{TabButton-CThevdY_.js → TabButton-DEypdlZf.js} +2 -2
  72. package/dist/lib/{TabButton-CThevdY_.js.map → TabButton-DEypdlZf.js.map} +1 -1
  73. package/dist/lib/Text-Bni4icFY.js +2 -0
  74. package/dist/lib/Text-Bni4icFY.js.map +1 -0
  75. package/dist/lib/{Tooltip-tkXDk5Bo.js → Tooltip-BUy6uFKN.js} +2 -2
  76. package/dist/lib/{Tooltip-tkXDk5Bo.js.map → Tooltip-BUy6uFKN.js.map} +1 -1
  77. package/dist/lib/VBox-Dr7Ge9M6.js +2 -0
  78. package/dist/lib/VBox-Dr7Ge9M6.js.map +1 -0
  79. package/dist/lib/{VirtualScroller-DwEEJE1_.js → VirtualScroller-BPy6KmSX.js} +2 -2
  80. package/dist/lib/{VirtualScroller-DwEEJE1_.js.map → VirtualScroller-BPy6KmSX.js.map} +1 -1
  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/diagram.es.js +1 -1
  86. package/dist/lib/component/diagram.es.js.map +1 -1
  87. package/dist/lib/component/display.es.js +1 -1
  88. package/dist/lib/component/editor.es.js +3 -3
  89. package/dist/lib/component/editor.es.js.map +1 -1
  90. package/dist/lib/component/input.es.js +1 -1
  91. package/dist/lib/component/input.es.js.map +1 -1
  92. package/dist/lib/component/list.es.js +1 -1
  93. package/dist/lib/component/menubar.es.js +1 -1
  94. package/dist/lib/component/table.es.js +2 -2
  95. package/dist/lib/component/table.es.js.map +1 -1
  96. package/dist/lib/component/tree.es.js +1 -1
  97. package/dist/lib/component/tree.es.js.map +1 -1
  98. package/dist/lib/core.es.js +1 -1
  99. package/dist/lib/core.es.js.map +1 -1
  100. package/dist/lib/data.es.js +1 -1
  101. package/dist/lib/data.es.js.map +1 -1
  102. package/dist/lib/glyphs/solid/index.es.js +1 -1
  103. package/dist/lib/layout.es.js +1 -1
  104. package/dist/lib/layout.es.js.map +1 -1
  105. package/dist/lib/overlay.es.js +1 -1
  106. package/dist/lib/overlay.es.js.map +1 -1
  107. package/dist/lib/primitive.es.js +1 -1
  108. package/dist/lib/router.es.js +1 -1
  109. package/dist/lib/router.es.js.map +1 -1
  110. package/dist/lib/{VirtualRowView-BxpHQ_u8.js → selectionsEqual-2PJrcimg.js} +2 -2
  111. package/dist/lib/{VirtualRowView-BxpHQ_u8.js.map → selectionsEqual-2PJrcimg.js.map} +1 -1
  112. package/dist/lib/types/component/diagram/DiagramEdgeLayer.d.ts +14 -1
  113. package/dist/lib/types/component/diagram/DiagramModel.d.ts +1 -0
  114. package/dist/lib/types/component/diagram/DiagramNode.d.ts +4 -0
  115. package/dist/lib/types/component/diagram/DiagramView.d.ts +76 -5
  116. package/dist/lib/types/component/diagram/ElkLayoutEngine.d.ts +14 -1
  117. package/dist/lib/types/component/diagram/index.d.ts +2 -2
  118. package/dist/lib/types/component/editor/CodeEditor.d.ts +3 -2
  119. package/dist/lib/types/component/input/Text.d.ts +3 -1
  120. package/dist/lib/types/component/shared/selectionsEqual.d.ts +1 -0
  121. package/dist/lib/types/component/table/Column.d.ts +2 -0
  122. package/dist/lib/types/component/table/ColumnConfig.d.ts +1 -0
  123. package/dist/lib/types/component/table/Header.d.ts +1 -0
  124. package/dist/lib/types/component/table/Table.d.ts +20 -0
  125. package/dist/lib/types/component/table/cell/Header.d.ts +1 -0
  126. package/dist/lib/types/component/table/index.d.ts +1 -1
  127. package/dist/lib/types/component/tree/Tree.d.ts +1 -0
  128. package/dist/lib/types/core/Aria.d.ts +0 -2
  129. package/dist/lib/types/core/Body.d.ts +1 -0
  130. package/dist/lib/types/core/ClassStyleRules.d.ts +17 -0
  131. package/dist/lib/types/core/Component.d.ts +10 -2
  132. package/dist/lib/types/core/ComponentDefaults.d.ts +1 -0
  133. package/dist/lib/types/core/DOM.d.ts +11 -2
  134. package/dist/lib/types/core/ElementAttributes.d.ts +15 -0
  135. package/dist/lib/types/core/PendingTransitions.d.ts +4 -0
  136. package/dist/lib/types/core/StyleTarget.d.ts +1 -0
  137. package/dist/lib/types/core/Util.d.ts +2 -0
  138. package/dist/lib/types/core/index.d.ts +1 -0
  139. package/dist/lib/types/layout/Border.d.ts +1 -0
  140. package/dist/lib/types/layout/Tab.d.ts +4 -1
  141. package/dist/lib/types/layout/Table.d.ts +1 -0
  142. package/dist/lib/types/overlay/AbstractWindow.d.ts +1 -0
  143. package/dist/lib/types/router/RoutePattern.d.ts +7 -0
  144. package/dist/lib/types/router/Router.d.ts +13 -2
  145. package/dist/lib/types/router/index.d.ts +1 -1
  146. package/dist/lib/validation.es.js +1 -1
  147. package/llms.txt +1 -1
  148. package/package.json +4 -8
  149. package/dist/lib/Border-BgaZkc1R.js +0 -2
  150. package/dist/lib/BoxLayout-DrKGfsup.js +0 -2
  151. package/dist/lib/BoxLayout-DrKGfsup.js.map +0 -1
  152. package/dist/lib/Component-DXf5a3g0.js +0 -2
  153. package/dist/lib/Component-DXf5a3g0.js.map +0 -1
  154. package/dist/lib/Container-D5H8VxJK.js +0 -2
  155. package/dist/lib/DOM-CGn7xyTw.js +0 -2
  156. package/dist/lib/DOM-CGn7xyTw.js.map +0 -1
  157. package/dist/lib/HBox-Ce0GTCJP.js +0 -2
  158. package/dist/lib/LayoutSizes-DU3qSq0w.js +0 -2
  159. package/dist/lib/LayoutSizes-DU3qSq0w.js.map +0 -1
  160. package/dist/lib/List-DtTVTyWW.js +0 -2
  161. package/dist/lib/List-DtTVTyWW.js.map +0 -1
  162. package/dist/lib/Position-A39T7-_1.js +0 -2
  163. package/dist/lib/Position-A39T7-_1.js.map +0 -1
  164. package/dist/lib/Text-gz6qjFfP.js +0 -2
  165. package/dist/lib/Text-gz6qjFfP.js.map +0 -1
  166. package/dist/lib/VBox-EIqw85Rh.js +0 -2
  167. package/dist/lib/VBox-EIqw85Rh.js.map +0 -1
@@ -1,3 +1,3 @@
1
- import{t as e}from"./DOM-CGn7xyTw.js";import{l as t}from"./Position-A39T7-_1.js";import{h as n,t as r,y as i}from"./Component-DXf5a3g0.js";import{t as a}from"./Text-gz6qjFfP.js";var o=9e3,s=9800,c=1e4,l=11e3,u=12e3,d=new r,f;(function(t){let n=[],r=new WeakMap,a=0,f=null,p=!1;t.Band={Window:o,Popover:s,Dropdown:c,Dialog:l,Tooltip:u};function bandFor(e,t){return e?e.band:t}function register(e){if(r.has(e))return;let t=n.length>0?n[n.length-1]:null,i=e.isLayerRoot?.()?null:t,o=bandFor(i,e.getBand?.()??c),s={layer:e,parent:i,children:[],band:o,zIndex:o+ ++a};r.set(e,s),n.push(s),i&&i.children.push(s),p||installListeners()}t.register=register;function mount(t){e.source.contains(e.source.getDocumentElement(),t)||e.sink.appendChild(e.source.getDocumentElement(),t)}t.mount=mount;function unregister(e){let t=r.get(e);if(!t)return;r.delete(e);let i=n.indexOf(t);if(i>=0&&n.splice(i,1),t.parent){let e=t.parent.children.indexOf(t);e>=0&&t.parent.children.splice(e,1)}f===e&&(f=null),n.length===0&&p&&uninstallListeners()}t.unregister=unregister;function containsAcrossLayers(t,n){if(!n)return!1;let i=r.get(t);if(!i){let r=t.getLayerElement();return r?e.source.contains(r,n):!1}return nodeContains(i,n)}t.containsAcrossLayers=containsAcrossLayers;function getTopLayer(){return n.length>0?n[n.length-1].layer:null}t.getTopLayer=getTopLayer;function bringToFront(e){let t=r.get(e);t&&(restampSubtree(t),markActive(e))}t.bringToFront=bringToFront;function getZIndex(e){let t=r.get(e);return t?t.zIndex:c}t.getZIndex=getZIndex;function restampSubtree(e){let walk=e=>{e.zIndex=e.band+ ++a,e.layer.onZIndexChanged?.(e.zIndex);for(let t of e.children)walk(t)};walk(e)}function nodeContains(t,n){let r=t.layer.getLayerElement();if(r&&e.source.contains(r,n))return!0;for(let e of t.children)if(nodeContains(e,n))return!0;return!1}function markActive(e){f!==e&&(deactivateActive(),f=e,e.onActivate&&e.onActivate(!0))}function deactivateActive(){f&&f.onActivate&&f.onActivate(!1),f=null}function activatableAncestor(e){let t=e;for(;t;){if(t.layer.onActivate)return t.layer;t=t.parent}return null}function handleOutside(t,r){let i=n.slice().reverse(),a=!1,o=!1;for(let n of i){let i=n.layer.getDismissMode();if(containsAcrossLayers(n.layer,t)){markActive(activatableAncestor(n)??n.layer),a=!0,o=!0;break}let s=n.layer.getAnchorElement?.();if(s&&t&&e.source.contains(s,t)){o=!0;break}if(i===`manual`||i===`modal`||!(r?i===`blur`:i===`click-outside`||i===`blur`)){if(i===`modal`){o=!0;break}continue}n.layer.requestClose()}!a&&!o&&!r&&deactivateActive()}function onPointerDown(t){handleOutside(t.target===null?null:e.source.intern(t.target),!1)}function onWindowBlur(t){e.source.isWindow(t.target===null?null:e.source.intern(t.target))&&handleOutside(null,!1)}function onFocusIn(t){handleOutside(t.target===null?null:e.source.intern(t.target),!0)}function onKeyDown(e){if(e.key===`Escape`){for(let e=n.length-1;e>=0;e--)if(n[e].layer.getDismissMode()!==`manual`)return n[e].layer.requestClose(),!0}}function installListeners(){i.addViewportListener(d,`pointerdown`,onPointerDown),i.addViewportListener(d,`focusin`,onFocusIn),i.addViewportListener(d,`keydown`,onKeyDown),i.addViewportListener(d,`blur`,onWindowBlur),p=!0}function uninstallListeners(){i.removeViewportListener(d,`pointerdown`,onPointerDown),i.removeViewportListener(d,`focusin`,onFocusIn),i.removeViewportListener(d,`keydown`,onKeyDown),i.removeViewportListener(d,`blur`,onWindowBlur),p=!1}})(f||={});function positionAdjacent(e,t,n,r,i){let a=t+i,o=r-a,s=e-i;return n<=o?a:n<=s?e-n-i:o>=s?Math.max(0,r-n):0}function positionAligned(e,t,n,r,i){if(e>=i&&e+n<=r-i)return e;let a=t-n;return a>=i&&a+n<=r-i?a:clampAxis(e,n,r,i)}function clampAxis(e,n,r,i){return t.clamp(e,i,Math.max(i,r-n-i))}function positionFlexibleAnchored(e,t,n,r,i){let a=r-t-i,o=e-i;return n<=a||a>=o?{start:t,available:a}:{start:e-Math.min(n,o),available:o}}function positionAnchored(e,t,n,r){let i=r.gap??0,a=r.margin??0;if(r.axis===`vertical`){let r=positionAdjacent(e.top,e.bottom,t.height,n.height,i);return{x:positionAligned(e.left,e.right,t.width,n.width,a),y:r}}return{x:positionAdjacent(e.left,e.right,t.width,n.width,i),y:positionAligned(e.top,e.bottom,t.height,n.height,a)}}var p=100,m=class Tooltip extends r{_showAnimation=null;_hideAnimation=null;static instance=null;static showTimer=null;static attachments=new Map;static elementAttachments=new Map;static activeElement=null;static watching=!1;static _onAnchorWatch=()=>{Tooltip.activeElement!==null&&!e.source.isConnected(Tooltip.activeElement)&&Tooltip.hide()};static dismissing=!1;static H_PADDING=16;static V_PADDING=8;static MAX_WIDTH=300;static ITEM_HEIGHT=20;static CURSOR_LEFT=4;static CURSOR_RIGHT=12;static CURSOR_UP=0;static CURSOR_DOWN=12;static CURSOR_GAP=2;_text;_lineCount=1;_perLine=Tooltip.ITEM_HEIGHT;constructor(){super(),this.setVisible(!1),this.setZIndex(f.Band.Tooltip),this.setBackgroundColor(`var(--ts-ui-tooltip-bg, rgb(255, 255, 240))`),this.setForegroundColor(`var(--ts-ui-tooltip-color, rgb(0, 0, 0))`),this.setBorder({border:`1px solid var(--ts-ui-tooltip-border, rgb(180, 180, 100))`}),this.setShadow(`var(--ts-ui-tooltip-shadow, 1px 2px 4px rgba(0, 0, 0, 0.2))`),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),this.setPointerEvents(`none`),this.setContain(`layout paint`),this._text=new a,this._text.setPointerEvents(`none`),this._text.setWhiteSpace(`pre-wrap`),this.addComponent(this._text)}static getInstance(){return Tooltip.instance||=new Tooltip,Tooltip.instance}static show(r,a,o){Tooltip.showTimer!==null&&(clearTimeout(Tooltip.showTimer),Tooltip.showTimer=null);let s=Tooltip.getInstance(),c=s.isVisible()&&!Tooltip.dismissing;s._text.setText(r);let l=r.split(`
1
+ import{t as e}from"./DOM-BeR95tE8.js";import{l as t}from"./Position-DRSlWn4Z.js";import{_ as n,t as r,x as i}from"./Component-CHTzJxEf.js";import{t as a}from"./Text-Bni4icFY.js";var o=9e3,s=9800,c=1e4,l=11e3,u=12e3,d=new r,f;(function(t){let n=[],r=new WeakMap,a=0,f=null,p=!1;t.Band={Window:o,Popover:s,Dropdown:c,Dialog:l,Tooltip:u};function bandFor(e,t){return e?e.band:t}function register(e){if(r.has(e))return;let t=n.length>0?n[n.length-1]:null,i=e.isLayerRoot?.()?null:t,o=bandFor(i,e.getBand?.()??c),s={layer:e,parent:i,children:[],band:o,zIndex:o+ ++a};r.set(e,s),n.push(s),i&&i.children.push(s),p||installListeners()}t.register=register;function mount(t){e.source.contains(e.source.getDocumentElement(),t)||e.sink.appendChild(e.source.getDocumentElement(),t)}t.mount=mount;function unregister(e){let t=r.get(e);if(!t)return;r.delete(e);let i=n.indexOf(t);if(i>=0&&n.splice(i,1),t.parent){let e=t.parent.children.indexOf(t);e>=0&&t.parent.children.splice(e,1)}f===e&&(f=null),n.length===0&&p&&uninstallListeners()}t.unregister=unregister;function containsAcrossLayers(t,n){if(!n)return!1;let i=r.get(t);if(!i){let r=t.getLayerElement();return r?e.source.contains(r,n):!1}return nodeContains(i,n)}t.containsAcrossLayers=containsAcrossLayers;function getTopLayer(){return n.length>0?n[n.length-1].layer:null}t.getTopLayer=getTopLayer;function bringToFront(e){let t=r.get(e);t&&(restampSubtree(t),markActive(e))}t.bringToFront=bringToFront;function getZIndex(e){let t=r.get(e);return t?t.zIndex:c}t.getZIndex=getZIndex;function restampSubtree(e){let walk=e=>{e.zIndex=e.band+ ++a,e.layer.onZIndexChanged?.(e.zIndex);for(let t of e.children)walk(t)};walk(e)}function nodeContains(t,n){let r=t.layer.getLayerElement();if(r&&e.source.contains(r,n))return!0;for(let e of t.children)if(nodeContains(e,n))return!0;return!1}function markActive(e){f!==e&&(deactivateActive(),f=e,e.onActivate&&e.onActivate(!0))}function deactivateActive(){f&&f.onActivate&&f.onActivate(!1),f=null}function activatableAncestor(e){let t=e;for(;t;){if(t.layer.onActivate)return t.layer;t=t.parent}return null}function handleOutside(t,r){let i=n.slice().reverse(),a=!1,o=!1;for(let n of i){let i=n.layer.getDismissMode();if(containsAcrossLayers(n.layer,t)){markActive(activatableAncestor(n)??n.layer),a=!0,o=!0;break}let s=n.layer.getAnchorElement?.();if(s&&t&&e.source.contains(s,t)){o=!0;break}if(i===`manual`||i===`modal`||!(r?i===`blur`:i===`click-outside`||i===`blur`)){if(i===`modal`){o=!0;break}continue}n.layer.requestClose()}!a&&!o&&!r&&deactivateActive()}function onPointerDown(t){handleOutside(t.target===null?null:e.source.intern(t.target),!1)}function onWindowBlur(t){e.source.isWindow(t.target===null?null:e.source.intern(t.target))&&handleOutside(null,!1)}function onFocusIn(t){handleOutside(t.target===null?null:e.source.intern(t.target),!0)}function onKeyDown(e){if(e.key===`Escape`){for(let e=n.length-1;e>=0;e--)if(n[e].layer.getDismissMode()!==`manual`)return n[e].layer.requestClose(),!0}}function installListeners(){i.addViewportListener(d,`pointerdown`,onPointerDown),i.addViewportListener(d,`focusin`,onFocusIn),i.addViewportListener(d,`keydown`,onKeyDown),i.addViewportListener(d,`blur`,onWindowBlur),p=!0}function uninstallListeners(){i.removeViewportListener(d,`pointerdown`,onPointerDown),i.removeViewportListener(d,`focusin`,onFocusIn),i.removeViewportListener(d,`keydown`,onKeyDown),i.removeViewportListener(d,`blur`,onWindowBlur),p=!1}})(f||={});function positionAdjacent(e,t,n,r,i){let a=t+i,o=r-a,s=e-i;return n<=o?a:n<=s?e-n-i:o>=s?Math.max(0,r-n):0}function positionAligned(e,t,n,r,i){if(e>=i&&e+n<=r-i)return e;let a=t-n;return a>=i&&a+n<=r-i?a:clampAxis(e,n,r,i)}function clampAxis(e,n,r,i){return t.clamp(e,i,Math.max(i,r-n-i))}function positionFlexibleAnchored(e,t,n,r,i){let a=r-t-i,o=e-i;return n<=a||a>=o?{start:t,available:a}:{start:e-Math.min(n,o),available:o}}function positionAnchored(e,t,n,r){let i=r.gap??0,a=r.margin??0;if(r.axis===`vertical`){let r=positionAdjacent(e.top,e.bottom,t.height,n.height,i);return{x:positionAligned(e.left,e.right,t.width,n.width,a),y:r}}return{x:positionAdjacent(e.left,e.right,t.width,n.width,i),y:positionAligned(e.top,e.bottom,t.height,n.height,a)}}var p=100,m=class Tooltip extends r{_showAnimation=null;_hideAnimation=null;static instance=null;static showTimer=null;static attachments=new Map;static elementAttachments=new Map;static activeElement=null;static watching=!1;static _onAnchorWatch=()=>{Tooltip.activeElement!==null&&!e.source.isConnected(Tooltip.activeElement)&&Tooltip.hide()};static dismissing=!1;static H_PADDING=16;static V_PADDING=8;static MAX_WIDTH=300;static ITEM_HEIGHT=20;static CURSOR_LEFT=4;static CURSOR_RIGHT=12;static CURSOR_UP=0;static CURSOR_DOWN=12;static CURSOR_GAP=2;_text;_lineCount=1;_perLine=Tooltip.ITEM_HEIGHT;constructor(){super(),this.setVisible(!1),this.setZIndex(f.Band.Tooltip),this.setBackgroundColor(`var(--ts-ui-tooltip-bg, rgb(255, 255, 240))`),this.setForegroundColor(`var(--ts-ui-tooltip-color, rgb(0, 0, 0))`),this.setBorder({border:`1px solid var(--ts-ui-tooltip-border, rgb(180, 180, 100))`}),this.setShadow(`var(--ts-ui-tooltip-shadow, 1px 2px 4px rgba(0, 0, 0, 0.2))`),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),this.setPointerEvents(`none`),this.setContain(`layout paint`),this._text=new a,this._text.setPointerEvents(`none`),this._text.setWhiteSpace(`pre-wrap`),this.addComponent(this._text)}static getInstance(){return Tooltip.instance||=new Tooltip,Tooltip.instance}static show(r,a,o){Tooltip.showTimer!==null&&(clearTimeout(Tooltip.showTimer),Tooltip.showTimer=null);let s=Tooltip.getInstance(),c=s.isVisible()&&!Tooltip.dismissing;s._text.setText(r);let l=r.split(`
2
2
  `),u=l.reduce((e,n)=>Math.max(e,t.measureTextWidth(n)),0);s._perLine=s._perLineHeight();let d=Math.min(Tooltip.MAX_WIDTH,u+Tooltip.H_PADDING);if(u+Tooltip.H_PADDING>Tooltip.MAX_WIDTH){let t=d-Tooltip.H_PADDING,n=e.source.measureText(r,{maxWidth:t}).height;s._lineCount=Math.max(l.length,Math.round(n/s._perLine))}else s._lineCount=l.length;let m=s._lineCount*s._perLine+Tooltip.V_PADDING;s._lineCount===1&&(m=Math.max(m,Tooltip.ITEM_HEIGHT+Tooltip.V_PADDING)),s.setWidth(d),s.setHeight(m);let h=e.source.getViewportSize(),g=t.clamp(a,0,h.width),_=t.clamp(o,0,h.height);s.setX(positionAdjacent(g-Tooltip.CURSOR_LEFT,g+Tooltip.CURSOR_RIGHT,d,h.width,Tooltip.CURSOR_GAP)),s.setY(positionAdjacent(_-Tooltip.CURSOR_UP,_+Tooltip.CURSOR_DOWN,m,h.height,Tooltip.CURSOR_GAP));let v=s.getElement(!0);if(s.scheduleLayout(),f.mount(v),s.setVisible(!0),Tooltip.watching||=(i.addViewportListener(s,`mousemove`,Tooltip._onAnchorWatch),!0),Tooltip.dismissing=!1,c){e.sink.apply(v,{style:{opacity:`1`}});return}s._showAnimation?.cancel(),s._showAnimation=n.play(v,{from:{opacity:`0`},to:{opacity:`1`},durationMs:p,properties:[`opacity`]})}static hide(){Tooltip.showTimer!==null&&(clearTimeout(Tooltip.showTimer),Tooltip.showTimer=null);let e=Tooltip.getInstance(),t=e.getElement();Tooltip.watching&&=(i.removeViewportListener(e,`mousemove`,Tooltip._onAnchorWatch),!1),Tooltip.activeElement=null,t&&(Tooltip.dismissing=!0,e._hideAnimation?.cancel(),e._hideAnimation=n.play(t,{to:{opacity:`0`},durationMs:p,properties:[`opacity`],onComplete:()=>{Tooltip.dismissing&&(Tooltip.dismissing=!1,e.setVisible(!1),e.removeElement())}}))}static attach(e,t,n){Tooltip.detach(e);let r=0,a=0,mouseoverFn=i=>{Tooltip.showTimer===null&&(r=i.clientX,a=i.clientY,Tooltip.showTimer=setTimeout(()=>{Tooltip._applyColors(n),Tooltip.activeElement=e.getElement()??null,Tooltip.show(t,r,a),Tooltip.showTimer=null},500))},mousemoveFn=e=>{r=e.clientX,a=e.clientY},mouseoutFn=()=>{Tooltip.hide()},mousedownFn=()=>{Tooltip.hide()};i.addListener(e,`mouseover`,mouseoverFn),i.addListener(e,`mousemove`,mousemoveFn),i.addListener(e,`mouseout`,mouseoutFn),i.addListener(e,`mousedown`,mousedownFn),Tooltip.attachments.set(e.getId(),{text:t,colors:n,mouseoverFn,mousemoveFn,mouseoutFn,mousedownFn})}static detach(e){let t=e.getId(),n=Tooltip.attachments.get(t);n&&(i.removeListener(e,`mouseover`,n.mouseoverFn),i.removeListener(e,`mousemove`,n.mousemoveFn),i.removeListener(e,`mouseout`,n.mouseoutFn),i.removeListener(e,`mousedown`,n.mousedownFn),Tooltip.attachments.delete(t),Tooltip.hide())}static _applyColors(e){let t=Tooltip.getInstance();t.setBackgroundColor(e?.background??`var(--ts-ui-tooltip-bg, rgb(255, 255, 240))`),t.setForegroundColor(e?.color??`var(--ts-ui-tooltip-color, rgb(0, 0, 0))`),t.setBorder({border:`1px solid ${e?.border??`var(--ts-ui-tooltip-border, rgb(180, 180, 100))`}`})}static attachToElement(t,n){let r=Tooltip.elementAttachments.get(t),i=Tooltip.activeElement===t,a=r?r.lastX:0,o=r?r.lastY:0;Tooltip.detachElement(t);let s={text:n,showTimer:null,lastX:a,lastY:o,mouseoverFn:function onTooltipMouseOver(e){s.showTimer===null&&(s.lastX=e.clientX,s.lastY=e.clientY,s.showTimer=setTimeout(function onTooltipShowTimer(){Tooltip.activeElement=t,Tooltip.show(s.text,s.lastX,s.lastY),s.showTimer=null},500))},mousemoveFn:function onTooltipMouseMove(e){s.lastX=e.clientX,s.lastY=e.clientY},mouseoutFn:function onTooltipMouseOut(){s.showTimer!==null&&(clearTimeout(s.showTimer),s.showTimer=null),Tooltip.activeElement===t&&(Tooltip.activeElement=null),Tooltip.hide()},mousedownFn:function onTooltipMouseDown(){s.showTimer!==null&&(clearTimeout(s.showTimer),s.showTimer=null),Tooltip.hide()}};e.sink.addListener(t,`mouseover`,s.mouseoverFn),e.sink.addListener(t,`mousemove`,s.mousemoveFn),e.sink.addListener(t,`mouseout`,s.mouseoutFn),e.sink.addListener(t,`mousedown`,s.mousedownFn),Tooltip.elementAttachments.set(t,s),i&&(Tooltip.activeElement=t,Tooltip.show(n,s.lastX,s.lastY))}static detachElement(t){let n=Tooltip.elementAttachments.get(t);n&&(e.sink.removeListener(t,`mouseover`,n.mouseoverFn),e.sink.removeListener(t,`mousemove`,n.mousemoveFn),e.sink.removeListener(t,`mouseout`,n.mouseoutFn),e.sink.removeListener(t,`mousedown`,n.mousedownFn),n.showTimer!==null&&(clearTimeout(n.showTimer),n.showTimer=null),Tooltip.elementAttachments.delete(t),Tooltip.activeElement===t&&(Tooltip.activeElement=null))}_perLineHeight(){let{height:t}=e.source.measureText(`X`);return Math.ceil(t)}doLayout(){return super.doLayout(),this._text.setX(Tooltip.H_PADDING/2),this._text.setY(Tooltip.V_PADDING/2),this._text.setWidth(Math.max(0,this.getWidth()-Tooltip.H_PADDING)),this._text.setHeight(this._lineCount*this._perLine),this}destructor(){this._showAnimation?.cancel(),this._showAnimation=null,this._hideAnimation?.cancel(),this._hideAnimation=null,super.destructor()}};export{f as a,positionFlexibleAnchored as i,positionAligned as n,positionAnchored as r,m as t};
3
- //# sourceMappingURL=Tooltip-tkXDk5Bo.js.map
3
+ //# sourceMappingURL=Tooltip-BUy6uFKN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip-tkXDk5Bo.js","names":[],"sources":["../../src/typescript/lib/core/LayerManager.ts","../../src/typescript/lib/core/OverlayPosition.ts","../../src/typescript/lib/overlay/Tooltip.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * How a layer responds to an outside interaction.\n *\n * - `\"click-outside\"`: dismissed when a `pointerdown` lands outside the\n * layer's subtree (and its anchor); ignores focus moves.\n * - `\"blur\"`: dismissed when either a `pointerdown` or a focus move lands\n * outside the layer's subtree (and its anchor).\n * - `\"manual\"`: never dismissed by the document-level handlers; the host\n * drives its own teardown.\n * - `\"modal\"`: never dismissed by an outside `pointerdown` or focus move\n * (the layer captures the interaction); only Escape, routed through the\n * keydown handler, asks it to close.\n *\n * @category Core\n */\nexport type LayerDismissMode = \"click-outside\" | \"blur\" | \"manual\" | \"modal\";\n\n/**\n * A portaled overlay surface that participates in the runtime layer tree.\n * Implemented by [`AnimatedDropdown`](/api/core/classes/AnimatedDropdown),\n * [`Popover`](/api/overlay/classes/Popover),\n * [`Dialog`](/api/overlay/classes/Dialog), and\n * [`Window`](/api/overlay/classes/Window).\n *\n * A surface registers itself with {@link LayerManager.register} from its\n * show path and unregisters with {@link LayerManager.unregister} from its\n * hide path. The manager then owns the z-index stamp, the activation\n * marking, and the dismissal decision — calling {@link DismissableLayer.requestClose}\n * (advisory) when an outside interaction meets the layer's\n * {@link LayerDismissMode}.\n *\n * @category Core\n */\nexport interface DismissableLayer {\n /** The layer's root element (already mounted on `documentElement`). */\n getLayerElement(): Handle | null;\n\n /** Dismiss policy consulted by the document-level interaction handlers. */\n getDismissMode(): LayerDismissMode;\n\n /** Advisory request to close; the surface runs its own teardown + unregister. */\n requestClose(): void;\n\n /**\n * Optional anchor element excluded from \"outside\" tests so the trigger\n * click that opened the layer does not immediately re-close it.\n */\n getAnchorElement?(): Handle | null;\n\n /**\n * Optional activation hook. The manager calls it with `true` when this\n * layer becomes the active (topmost containing) layer for a pointer /\n * focus interaction and `false` when it is superseded. Surfaces that\n * carry an active-state affordance (e.g. a window title bar) map it to\n * their own toggle; surfaces without one omit the method.\n */\n onActivate?(active: boolean): void;\n\n /**\n * Optional re-stamp hook. The manager calls it with a fresh z-index when\n * {@link LayerManager.bringToFront} re-allocates the layer (or one of its\n * ancestors) while it is already shown, so the surface can mirror the new\n * value onto its element through its own typed `setZIndex`. Surfaces that\n * only read z once in their show path (and never get raised afterwards)\n * may omit it.\n */\n onZIndexChanged?(zIndex: number): void;\n\n /**\n * Optional z-index band hint. Returns one of {@link LayerManager.Band}'s\n * values so an unrelated peer layer stacks in its surface family's band\n * (Window < Popover < dropdown < Dialog). Omitted surfaces default to the\n * dropdown band. A nested layer ignores its own band and inherits its\n * opener's, so this only matters for top-level (unparented) registrations.\n */\n getBand?(): number;\n\n /**\n * Optional top-level hint. Returns `true` for a surface that is an\n * independent peer rather than a layer opened from another — a window is\n * not a child of whatever happened to be topmost when it appeared. Such a\n * layer registers as a tree root, so raising it does not drag an unrelated\n * peer up with it (see {@link LayerManager.bringToFront}). Omitted surfaces\n * register under the current topmost layer (the opened-from edge).\n */\n isLayerRoot?(): boolean;\n}\n\n/**\n * Node in the runtime layer tree. Generalizes the per-host `LayerEntry`\n * that `AnimatedDropdown` previously kept module-private: `children` are the\n * layers opened while this one was topmost, `band` is the z-index band base\n * the layer was assigned (inherited from its opener), and `zIndex` is the\n * band + counter stamp the manager assigned at register time.\n */\ninterface LayerNode {\n layer: DismissableLayer;\n parent: LayerNode | null;\n children: LayerNode[];\n band: number;\n zIndex: number;\n}\n\n// Z-index bands. Plain module constants because z-index is not themed\n// anywhere today (Theme.ts carries no z-index tokens; the values were inline\n// per class). The bands reconcile the four historical bases into one\n// ascending allocator and preserve relative order between unrelated peers:\n// Window 9000 < Popover 9800 < dropdowns 10000 < Dialog 11000.\n// A nested child inherits its opener's band but always lands above it because\n// it registers later and so draws a higher counter. The 200-1000 gap between\n// bands leaves headroom for the monotonic `_zCounter`; it is not reset, on the\n// assumption a single session opens far fewer than 200 unrelated layers in the\n// same band before a reload — the historical inline values made the same bet.\nconst Z_BAND_WINDOW: number = 9000;\nconst Z_BAND_POPOVER: number = 9800;\nconst Z_BAND_DROPDOWN: number = 10000;\nconst Z_BAND_DIALOG: number = 11000;\n// Above every managed layer: a tooltip is a transient, non-interactive\n// affordance that must float over even a modal Dialog and its backdrop. Not a\n// registered layer band — exposed so the Tooltip singleton stamps itself here\n// rather than carrying a magic number that could fall below the Dialog band.\nconst Z_BAND_TOOLTIP: number = 12000;\n\n/**\n * Sentinel `Component` used as the registration key for the manager's three\n * document-level listeners. `Event.addViewportListener` binds a listener to a\n * `Component`, but the manager's handlers are module-global (one per type, not\n * per layer), so a single stable sentinel owns them — mirroring `Window`'s\n * `_viewportListenerOwner`. The install/teardown lifecycle keys off the tree\n * being non-empty rather than any individual layer.\n */\nconst _listenerOwner: Component = new Component();\n\n/**\n * Central registry and document-level interaction broker for portaled overlay\n * surfaces. Owns one runtime layer tree (push-on-register /\n * link-under-current-topmost / pop-on-unregister), one z-index allocator with\n * reserved bands, and a single set of document-level `pointerdown` / `focusin`\n * / `keydown` listeners installed on the first register and removed on the\n * last unregister.\n *\n * The relationship the tree captures — the runtime \"opened-from\" edge (which\n * layer was topmost when this one opened) — is distinct from the static\n * component hierarchy and changes per activation, so it lives here rather than\n * in the component tree. Containment queries hop across portaled descendant\n * layers via {@link LayerManager.containsAcrossLayers}, which is the single\n * place cross-portal containment is reasoned about.\n *\n * @category Core\n */\nexport namespace LayerManager {\n\n // LIFO stack of open layers in register order; the last entry is topmost.\n const _stack: LayerNode[] = [];\n\n // Membership + node lookup keyed by the layer instance, generalizing the\n // per-host `_entryByLayer` WeakMap.\n const _nodeByLayer: WeakMap<DismissableLayer, LayerNode> = new WeakMap();\n\n // Ascending per-register counter; combined with a band base to stamp z.\n let _zCounter: number = 0;\n\n // The layer currently marked active (received the last `onActivate(true)`).\n let _activeLayer: DismissableLayer | null = null;\n\n let _listenersInstalled: boolean = false;\n\n /**\n * The four z-index bands a surface returns from\n * {@link DismissableLayer.getBand}. Exposed so each surface can tag itself\n * without re-declaring the constants. Reconciles the historical inline\n * bases (Window 9000, Popover 9998, dropdowns 10050, Dialog 10101) into\n * one ascending allocator.\n */\n export const Band = {\n Window: Z_BAND_WINDOW,\n Popover: Z_BAND_POPOVER,\n Dropdown: Z_BAND_DROPDOWN,\n Dialog: Z_BAND_DIALOG,\n Tooltip: Z_BAND_TOOLTIP,\n } as const;\n\n /**\n * Picks the z-index band for a layer. A nested layer inherits its\n * opener's band so it stays in the same stacking neighbourhood (and rises\n * above the opener because it registers later); an unrelated peer uses its\n * own surface-type band from {@link DismissableLayer.getBand}.\n */\n function bandFor(parent: LayerNode | null, ownBand: number): number {\n return parent ? parent.band : ownBand;\n }\n\n /**\n * Pushes `layer` as a child of the current topmost layer — unless it\n * declares itself a top-level peer via\n * {@link DismissableLayer.isLayerRoot}, in which case it registers as a\n * tree root — assigns its band-based z-index, and installs the\n * document-level listeners on the first call. A duplicate register (e.g. a\n * `showAnimated` that cancels an in-flight fade-out) is a no-op so the tree\n * never double-pushes.\n *\n * @param layer - The surface entering the layer tree.\n */\n export function register(layer: DismissableLayer): void {\n if (_nodeByLayer.has(layer)) {\n return;\n }\n\n const topmost = _stack.length > 0 ? _stack[_stack.length - 1] : null;\n const parent = layer.isLayerRoot?.() ? null : topmost;\n const band = bandFor(parent, layer.getBand?.() ?? Z_BAND_DROPDOWN);\n const zIndex = band + (++_zCounter);\n\n const node: LayerNode = { layer, parent, children: [], band, zIndex };\n\n _nodeByLayer.set(layer, node);\n _stack.push(node);\n\n if (parent) {\n parent.children.push(node);\n }\n\n if (!_listenersInstalled) {\n installListeners();\n }\n }\n\n /**\n * Appends a portaled overlay element to `document.documentElement` if it is\n * not already contained there. The single home for the \"portal mount\"\n * idiom every floating surface used to inline. Idempotent — a surface that\n * re-shows after detach re-appends, and one already mounted is left alone.\n * Does **not** register the layer; surfaces still call {@link register}\n * explicitly.\n *\n * @param el - The overlay element to portal onto the document root.\n */\n export function mount(el: Handle): void {\n if (!DOM.source.contains(DOM.source.getDocumentElement(), el)) {\n DOM.sink.appendChild(DOM.source.getDocumentElement(), el);\n }\n }\n\n /**\n * Pops `layer` from the tree (and unlinks it from its parent's child\n * list), clears it as active if it was, and removes the document-level\n * listeners on the last call.\n *\n * @param layer - The surface leaving the layer tree.\n */\n export function unregister(layer: DismissableLayer): void {\n const node = _nodeByLayer.get(layer);\n\n if (!node) {\n return;\n }\n\n _nodeByLayer.delete(layer);\n\n const idx = _stack.indexOf(node);\n\n if (idx >= 0) {\n _stack.splice(idx, 1);\n }\n\n if (node.parent) {\n const ci = node.parent.children.indexOf(node);\n\n if (ci >= 0) {\n node.parent.children.splice(ci, 1);\n }\n }\n\n if (_activeLayer === layer) {\n _activeLayer = null;\n }\n\n if (_stack.length === 0 && _listenersInstalled) {\n uninstallListeners();\n }\n }\n\n /**\n * Returns true when `node` is inside `layer`'s own element OR inside any\n * descendant layer's element. This is the only cross-portal containment\n * hop in the framework: a `pointerdown` or focus move that lands in a\n * child layer (e.g. a dropdown opened from inside a popover) counts as\n * inside the ancestor so the ancestor stays open.\n *\n * @param layer - The layer whose subtree is tested.\n * @param node - The DOM node receiving the interaction.\n * @returns True when `node` is inside `layer` or any descendant layer.\n */\n export function containsAcrossLayers(layer: DismissableLayer, node: Handle | null): boolean {\n if (!node) {\n return false;\n }\n\n const layerNode = _nodeByLayer.get(layer);\n\n if (!layerNode) {\n const el = layer.getLayerElement();\n\n return el ? DOM.source.contains(el, node) : false;\n }\n\n return nodeContains(layerNode, node);\n }\n\n /**\n * Returns the topmost currently-registered layer, or null when none is\n * open.\n *\n * @returns The topmost open layer, or null.\n */\n export function getTopLayer(): DismissableLayer | null {\n return _stack.length > 0 ? _stack[_stack.length - 1].layer : null;\n }\n\n /**\n * Re-stamps `layer` (and its descendant layers) with fresh top-of-band\n * z-indices and marks it active. Used by surfaces that raise on click\n * (e.g. a window brought to front), so the raised layer — and anything it\n * opened — jumps above its band peers without disturbing other bands.\n *\n * @param layer - The layer to raise and activate.\n */\n export function bringToFront(layer: DismissableLayer): void {\n const node = _nodeByLayer.get(layer);\n\n if (!node) {\n return;\n }\n\n restampSubtree(node);\n markActive(layer);\n }\n\n /**\n * Returns the z-index currently assigned to `layer`, for surfaces that\n * mirror it elsewhere. Falls back to the dropdown band when the layer is\n * not registered.\n *\n * @param layer - The layer whose stamp to read.\n * @returns The assigned z-index.\n */\n export function getZIndex(layer: DismissableLayer): number {\n const node = _nodeByLayer.get(layer);\n\n return node ? node.zIndex : Z_BAND_DROPDOWN;\n }\n\n /**\n * Re-stamps `node`'s subtree from a fresh top-of-band counter run so the\n * raised layer and its descendants ascend together, preserving their\n * relative order. Each re-stamped layer is notified via\n * {@link DismissableLayer.onZIndexChanged} so it can mirror the new value\n * through its own typed `setZIndex` (the manager never writes the DOM).\n */\n function restampSubtree(node: LayerNode): void {\n const walk = (n: LayerNode): void => {\n n.zIndex = n.band + (++_zCounter);\n n.layer.onZIndexChanged?.(n.zIndex);\n\n for (const child of n.children) {\n walk(child);\n }\n };\n\n walk(node);\n }\n\n /** True when `node` (or a descendant layer) contains `target`. */\n function nodeContains(node: LayerNode, target: Handle): boolean {\n const el = node.layer.getLayerElement();\n\n if (el && DOM.source.contains(el, target)) {\n return true;\n }\n\n for (const child of node.children) {\n if (nodeContains(child, target)) {\n return true;\n }\n }\n\n return false;\n }\n\n /** Marks `layer` active and deactivates the previously-active layer. */\n function markActive(layer: DismissableLayer): void {\n if (_activeLayer === layer) {\n return;\n }\n\n deactivateActive();\n\n _activeLayer = layer;\n\n if (layer.onActivate) {\n layer.onActivate(true);\n }\n }\n\n /** Deactivates the currently-active layer, if any. */\n function deactivateActive(): void {\n if (_activeLayer && _activeLayer.onActivate) {\n _activeLayer.onActivate(false);\n }\n\n _activeLayer = null;\n }\n\n /**\n * Walks up the \"opened-from\" chain from `node` and returns the nearest\n * ancestor-or-self layer that carries an activation affordance\n * ({@link DismissableLayer.onActivate}), or `null` if none does. A layer\n * opened inside a window resolves to that window, so the window stays\n * active while its descendant holds focus — interacting with a child layer\n * must not darken the title bar of the window that owns it.\n */\n function activatableAncestor(node: LayerNode): DismissableLayer | null {\n let cur: LayerNode | null = node;\n\n while (cur) {\n if (cur.layer.onActivate) {\n return cur.layer;\n }\n\n cur = cur.parent;\n }\n\n return null;\n }\n\n /**\n * Walks the stack top-down for a pointer / focus interaction at `target`,\n * dismissing each topmost layer whose dismiss mode and containment test\n * say the interaction landed outside it, and marking the topmost\n * containing layer active. `focusOnly` is true for the `focusin` pass, so\n * only `\"blur\"` layers act on it (a `\"click-outside\"` layer ignores focus\n * moves).\n */\n function handleOutside(target: Handle | null, focusOnly: boolean): void {\n // Snapshot top-down so requestClose-driven unregisters don't disturb\n // the walk; re-read containment against the live tree each step.\n const snapshot = _stack.slice().reverse();\n let activated = false;\n let landedInside = false;\n\n for (const node of snapshot) {\n const mode = node.layer.getDismissMode();\n\n if (containsAcrossLayers(node.layer, target)) {\n // Activation belongs to the nearest activatable ancestor (the\n // window a child layer was opened from), not the innermost\n // containing layer; fall back to the layer itself when nothing\n // up the chain carries an activation affordance, preserving the\n // prior behaviour for standalone layers.\n markActive(activatableAncestor(node) ?? node.layer);\n activated = true;\n landedInside = true;\n\n break;\n }\n\n const anchor = node.layer.getAnchorElement?.();\n\n if (anchor && target && DOM.source.contains(anchor, target)) {\n landedInside = true;\n\n break;\n }\n\n const dismissable = mode === \"click-outside\" || mode === \"blur\";\n const acts = focusOnly ? mode === \"blur\" : dismissable;\n\n if (mode === \"manual\" || mode === \"modal\" || !acts) {\n // Modal captures the interaction: an outside pointer/focus on a\n // modal must not fall through to dismiss layers beneath it.\n if (mode === \"modal\") {\n landedInside = true;\n\n break;\n }\n\n continue;\n }\n\n node.layer.requestClose();\n }\n\n // An interaction that landed outside every layer's subtree (and any\n // anchor / modal capture) deactivates the active layer — e.g. an\n // empty-viewport click that should drop a window's active title-bar\n // highlight. A focus move to a non-layer element (focusOnly) does not\n // deactivate, so tabbing through page chrome doesn't flicker the\n // active window.\n if (!activated && !landedInside && !focusOnly) {\n deactivateActive();\n }\n }\n\n /** Document `pointerdown` handler — see {@link handleOutside}. */\n function onPointerDown(e: PointerEvent): void {\n handleOutside(e.target === null ? null : DOM.source.intern(e.target), false);\n }\n\n /**\n * Window `blur` handler. When the whole browser window loses focus (the user\n * clicks another application or alt-tabs away) no in-page `pointerdown` or\n * `focusin` fires, so an open layer would otherwise stay up. Treat it as an\n * interaction landing outside every layer, dismissing each `\"click-outside\"`\n * / `\"blur\"` layer while a `\"modal\"` shields those beneath it and `\"manual\"`\n * layers (windows) are left open.\n *\n * @remarks Viewport listeners are capture-phase, so element blurs from within\n * the page surface here too; only a genuine window blur (`target` is the\n * window itself) should dismiss layers.\n */\n function onWindowBlur(e: FocusEvent): void {\n if (!DOM.source.isWindow(e.target === null ? null : DOM.source.intern(e.target))) {\n return;\n }\n\n handleOutside(null, false);\n }\n\n /** Document `focusin` handler — only `\"blur\"` layers act on it. */\n function onFocusIn(e: FocusEvent): void {\n handleOutside(e.target === null ? null : DOM.source.intern(e.target), true);\n }\n\n /**\n * Document `keydown` handler — Escape asks the topmost non-manual layer to close.\n *\n * @param e - The keydown event.\n * @returns `true` when Escape actually closed a layer; nothing otherwise, so an\n * Escape with no dismissible layer open keeps propagating.\n */\n function onKeyDown(e: KeyboardEvent): Event.ListenerResult {\n if (e.key !== \"Escape\") {\n return;\n }\n\n for (let i = _stack.length - 1; i >= 0; i--) {\n const mode = _stack[i].layer.getDismissMode();\n\n if (mode === \"manual\") {\n continue;\n }\n\n _stack[i].layer.requestClose();\n\n return true;\n }\n\n return;\n }\n\n /** Installs the three document-level listeners against the sentinel owner. */\n function installListeners(): void {\n Event.addViewportListener(_listenerOwner, \"pointerdown\", onPointerDown);\n Event.addViewportListener(_listenerOwner, \"focusin\", onFocusIn);\n Event.addViewportListener(_listenerOwner, \"keydown\", onKeyDown);\n Event.addViewportListener(_listenerOwner, \"blur\", onWindowBlur);\n\n _listenersInstalled = true;\n }\n\n /** Removes the three document-level listeners. */\n function uninstallListeners(): void {\n Event.removeViewportListener(_listenerOwner, \"pointerdown\", onPointerDown);\n Event.removeViewportListener(_listenerOwner, \"focusin\", onFocusIn);\n Event.removeViewportListener(_listenerOwner, \"keydown\", onKeyDown);\n Event.removeViewportListener(_listenerOwner, \"blur\", onWindowBlur);\n\n _listenersInstalled = false;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Util } from \"~/core/Util.js\";\nimport type { Rect } from \"~/core/DOM.js\";\nimport type { Size } from \"~/primitive/Size.js\";\n\n/**\n * The primary growth axis for an anchored overlay.\n *\n * @category Core\n */\nexport type AnchorAxis = \"vertical\" | \"horizontal\";\n\n/**\n * Options controlling how {@link positionAnchored} places an element against an\n * anchor rect.\n *\n * @category Core\n */\nexport interface AnchorOptions {\n /** Primary growth axis: `\"vertical\"` grows below/above, `\"horizontal\"` right/left. */\n axis: AnchorAxis;\n /** Gap in px between the anchor edge and the element on the primary axis. Default 0. */\n gap?: number;\n /** Viewport-edge margin in px kept on the cross axis. Default 0. */\n margin?: number;\n}\n\n/**\n * Chooses a top-left coordinate on the primary axis. Prefers growing past the\n * anchor's far edge (`farEdge + gap`); flips to the near edge\n * (`nearEdge - extent - gap`) only when the far side lacks room AND the near\n * side has it. When neither side fits, saturates on-screen toward the side with\n * more room — pinned to the viewport's far end when the far side is roomier,\n * otherwise to `0` — so the element never overflows off-screen.\n *\n * @param nearEdge - The anchor's near edge (top / left) on this axis.\n * @param farEdge - The anchor's far edge (bottom / right) on this axis.\n * @param extent - The element's size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param gap - Gap in px kept between the anchor edge and the element.\n * @returns The chosen top-left coordinate on this axis.\n *\n * @category Core\n */\nexport function positionAdjacent(nearEdge: number, farEdge: number, extent: number, viewportExtent: number, gap: number): number {\n const farStart = farEdge + gap;\n const spaceFar = viewportExtent - farStart;\n const spaceNear = nearEdge - gap;\n\n if (extent <= spaceFar) {\n return farStart;\n }\n\n if (extent <= spaceNear) {\n return nearEdge - extent - gap;\n }\n\n // Neither side fits: pin to whichever side has more room, saturated so the\n // element stays on-screen (mirrors placeAnchored's spaceBelow/spaceAbove\n // fallback, axis-agnostic).\n if (spaceFar >= spaceNear) {\n return Math.max(0, viewportExtent - extent);\n }\n\n return 0;\n}\n\n/**\n * Chooses a top-left coordinate on the cross axis for a **fixed-size** element\n * that ALIGNS WITH (and overlaps) its anchor rather than sitting beside it.\n * Prefers the element's near edge flush with the anchor's near edge\n * (`nearEdge`); flips to the element's FAR edge flush with the anchor's far\n * edge (`farEdge - extent`) only when the near alignment overflows the\n * viewport. This is alignment, not adjacency — the element overlaps the\n * anchor either way; only which pair of edges is made flush changes. It is\n * fixed-size and returns no `available` (contrast {@link\n * positionFlexibleAnchored}); `margin` binds on **this** axis. When neither\n * alignment fits on-screen (an anchor whose own far edge is off-viewport, or\n * an element wider than the viewport), falls back to {@link clampAxis} — the\n * right answer when no placement fits, not a flip substitute.\n *\n * @param nearEdge - The anchor's near edge (left / top) on this axis.\n * @param farEdge - The anchor's far edge (right / bottom) on this axis.\n * @param extent - The element's fixed size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param margin - Viewport-edge margin in px kept on this axis.\n * @returns The chosen top-left coordinate on this axis.\n *\n * @category Core\n */\nexport function positionAligned(\n nearEdge: number, farEdge: number, extent: number, viewportExtent: number, margin: number,\n): number {\n // Preferred: the element's near edge aligns with the anchor's near edge.\n if (nearEdge >= margin && nearEdge + extent <= viewportExtent - margin) {\n return nearEdge;\n }\n\n // Flipped: the element's FAR edge aligns with the anchor's far edge. Note this\n // is alignment, not adjacency — the element overlaps the anchor either way; only\n // which pair of edges is made flush changes.\n const farStart = farEdge - extent;\n\n if (farStart >= margin && farStart + extent <= viewportExtent - margin) {\n return farStart;\n }\n\n // Neither alignment fits on-screen (an anchor whose own far edge is off-viewport,\n // or an element wider than the viewport): fall back to the clamp. Clamping is the\n // right answer when NO placement fits — it is only wrong as a substitute for a flip.\n return clampAxis(nearEdge, extent, viewportExtent, margin);\n}\n\n/**\n * Clamps a top-left coordinate into `[margin, extent - size - margin]`, pinning\n * to `margin` (top-aligned) when the element is larger than the available span\n * so the coordinate never goes below `margin` and off-screen. The upper bound is\n * floored to `margin` before clamping, because `Util.clamp` resolves a `min >\n * max` range to the max — the opposite of the `Math.max(margin, Math.min(...))`\n * this replaces; flooring keeps `min <= max`, so an over-large element pins to\n * `margin` and the caller's height-cap / scroll carries the overflow.\n *\n * @param value - The proposed coordinate.\n * @param size - The element's extent on this axis.\n * @param extent - The viewport's extent on this axis.\n * @param margin - Viewport-edge margin kept on this axis.\n * @returns The clamped coordinate.\n */\nfunction clampAxis(value: number, size: number, extent: number, margin: number): number {\n return Util.clamp(value, margin, Math.max(margin, extent - size - margin));\n}\n\n/**\n * The chosen coordinate for a size-flexible anchored element, plus the room\n * available on the side it landed on.\n *\n * @category Core\n */\nexport interface FlexiblePlacement {\n /** Top-left coordinate on the primary axis. */\n start: number;\n /** Room (px) on the side actually chosen — the caller's height/width cap. */\n available: number;\n}\n\n/**\n * Chooses a top-left coordinate on the primary axis for a **size-flexible**\n * element — one whose extent may be capped and the overflow scrolled, rather\n * than being placed at a fixed size. Grows from `farEdge`; flips to end at\n * `nearEdge` only when the content overflows the far room **and** the near\n * side is roomier. Unlike {@link positionAdjacent}, the far-fits check is against the\n * *room*, not the raw `extent` — so a flip that still doesn't fully fit on\n * the near side still flips (and clamps) rather than falling back to filling\n * the viewport over the anchor. The returned `available` is the room on the\n * chosen side, so a caller that caps the extent measures the correct side —\n * re-deriving it from `start` would measure the wrong side for a flipped\n * element. `viewportMargin` binds on **this** (primary) axis, unlike\n * {@link positionAnchored}'s cross-axis-only `margin`. Always flush against\n * the anchor edge — no `gap` parameter, because every current caller sits\n * flush.\n *\n * @param nearEdge - The anchor's near edge (top / left) on this axis — the\n * edge a flipped element's far edge meets.\n * @param farEdge - The anchor's far edge (bottom / right) on this axis — the\n * edge an unflipped element grows from.\n * @param extent - The element's unclamped preferred size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param viewportMargin - Viewport-edge margin in px kept on this axis.\n * @returns The chosen coordinate and the room available at it.\n *\n * @category Core\n */\nexport function positionFlexibleAnchored(\n nearEdge: number,\n farEdge: number,\n extent: number,\n viewportExtent: number,\n viewportMargin: number,\n): FlexiblePlacement {\n const roomFar = viewportExtent - farEdge - viewportMargin;\n const roomNear = nearEdge - viewportMargin;\n\n if (extent <= roomFar || roomFar >= roomNear) {\n return { start: farEdge, available: roomFar };\n }\n\n return { start: nearEdge - Math.min(extent, roomNear), available: roomNear };\n}\n\n/**\n * Places an element of `size` against `anchorRect` inside `viewport`. On the\n * primary axis it grows past the anchor's far edge (below / right), flipping to\n * the near edge (above / left) only when the far side lacks room AND the near\n * side has more; on the cross axis it aligns to the anchor's near edge,\n * flipping to align with the anchor's far edge when the near alignment\n * overflows, and clamping only when neither alignment fits. Pure — all\n * viewport reads are supplied by the caller, so it is directly unit-testable\n * with no DOM.\n *\n * @param anchorRect - The anchor element's bounding rect.\n * @param size - The element's width/height to place.\n * @param viewport - The viewport size to clamp/flip within.\n * @param opts - Axis, gap, and cross-axis margin.\n * @returns The resolved top-left `{ x, y }` for the element.\n *\n * @category Core\n */\nexport function positionAnchored(anchorRect: Rect, size: Size, viewport: Size, opts: AnchorOptions): { x: number; y: number } {\n const gap = opts.gap ?? 0;\n const margin = opts.margin ?? 0;\n\n if (opts.axis === \"vertical\") {\n const y = positionAdjacent(anchorRect.top, anchorRect.bottom, size.height, viewport.height, gap);\n const x = positionAligned(anchorRect.left, anchorRect.right, size.width, viewport.width, margin);\n\n return { x, y };\n }\n\n const x = positionAdjacent(anchorRect.left, anchorRect.right, size.width, viewport.width, gap);\n const y = positionAligned(anchorRect.top, anchorRect.bottom, size.height, viewport.height, margin);\n\n return { x, y };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM, type Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { LayerManager } from \"~/core/LayerManager.js\";\nimport { positionAdjacent } from \"~/core/OverlayPosition.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { Animation } from \"~/core/Animation.js\";\nimport { Text } from \"~/component/input/Text.js\";\n\nconst TOOLTIP_ANIM_DURATION_MS: number = 100;\n\n/**\n * Optional color overrides for a tooltip attachment.\n *\n * When provided to {@link Tooltip.attach}, these replace the default theme variables\n * for the duration that the tooltip is shown for that component.\n *\n * @category Core\n */\nexport interface TooltipColors {\n /** CSS color for the tooltip background. */\n background?: string;\n /** CSS color for the tooltip text. */\n color?: string;\n /** CSS color for the tooltip border. */\n border?: string;\n}\n\n/** Internal record of a component's tooltip attachment. */\ninterface TooltipAttachment {\n text : string;\n colors : TooltipColors | undefined;\n mouseoverFn : (e: MouseEvent) => void;\n mousemoveFn : (e: MouseEvent) => void;\n mouseoutFn : () => void;\n mousedownFn : () => void;\n}\n\n/** Internal record of a raw-element tooltip attachment. */\ninterface ElementTooltipAttachment {\n text : string;\n mouseoverFn : (e: MouseEvent) => void;\n mousemoveFn : (e: MouseEvent) => void;\n mouseoutFn : () => void;\n mousedownFn : () => void;\n showTimer : ReturnType<typeof setTimeout> | null;\n lastX : number;\n lastY : number;\n}\n\n/**\n * A singleton floating tooltip that appears near the cursor after a short delay.\n *\n * Use `Tooltip.attach(component, text)` to wire hover listeners onto any component.\n * For manual control use `Tooltip.show(text, x, y)` and `Tooltip.hide()` directly.\n *\n * @example\n * ```typescript\n * Tooltip.attach(myButton, 'Save the document');\n * ```\n *\n * @category Core\n */\n// Intentional exception to the `callable()`-wrap rule: `Tooltip` has a private\n// constructor and is used only through its static methods (`show` / `attach` /\n// …), so there is no public `new Tooltip()` / `Tooltip(...)` surface to wrap.\n// The singleton is exported directly (see `overlay/index.ts`).\nexport class Tooltip extends Component {\n\n // In-flight fades, cancelled on teardown so their fallback timers cannot\n // fire against this tooltip's released element handle. Instance fields\n // written through the singleton `inst` in the static show / hide below.\n private _showAnimation: Animation.CancelHandle | null = null;\n private _hideAnimation: Animation.CancelHandle | null = null;\n\n private static instance: Tooltip | null = null;\n private static showTimer: ReturnType<typeof setTimeout> | null = null;\n private static attachments: Map<string, TooltipAttachment> = new Map();\n private static elementAttachments: Map<Handle, ElementTooltipAttachment> = new Map();\n private static activeElement: Handle | null = null;\n\n // True while the anchor-watch viewport listener is installed (only while a\n // tooltip is shown). See _onAnchorWatch.\n private static watching: boolean = false;\n\n /**\n * Hides the tooltip when its anchor element has left the DOM. The browser\n * fires no `mouseout` when an element is removed under a stationary pointer,\n * so a tooltip whose anchor vanishes would otherwise linger; this runs on\n * pointer movement while a tooltip is shown and dismisses it once the anchor\n * is no longer connected.\n */\n private static readonly _onAnchorWatch = (): void => {\n if (Tooltip.activeElement !== null && !DOM.source.isConnected(Tooltip.activeElement)) {\n Tooltip.hide();\n }\n };\n\n // Set true while a fade-out is in flight; reset to false when the fade-out\n // completes (so the deferred removeElement fires) or when a fresh `show()`\n // re-displays the tooltip mid-fade (the deferred removeElement is then\n // skipped because the tooltip is back on screen).\n private static dismissing: boolean = false;\n\n private static readonly H_PADDING: number = 16;\n private static readonly V_PADDING: number = 8;\n private static readonly MAX_WIDTH: number = 300;\n private static readonly ITEM_HEIGHT: number = 20;\n\n // Clearance kept free around the pointer *hotspot* (the arrow's tip, the\n // hand's fingertip) — the coordinate `show` is handed. The cursor glyph\n // hangs down and to the right of its hotspot, so this box is deliberately\n // NOT centred on it: nothing sits above the tip, and only a few px reach\n // left of it. A box symmetric about the hotspot would push a flipped\n // tooltip visibly further from the cursor than an unflipped one, since the\n // same gap spans glyph on one side and empty space on the other.\n //\n // These are clearance, not glyph metrics: DOWN is shorter than the ~24px\n // glyph on purpose, so an unflipped tooltip keeps sitting alongside the\n // pointer's lower half (where it tapers and hides nothing) rather than\n // being pushed clear of it. No API reports the live cursor's size or\n // hotspot, so the numbers approximate the platform defaults.\n private static readonly CURSOR_LEFT: number = 4;\n private static readonly CURSOR_RIGHT: number = 12;\n private static readonly CURSOR_UP: number = 0;\n private static readonly CURSOR_DOWN: number = 12;\n private static readonly CURSOR_GAP: number = 2;\n\n private _text: Text;\n\n // Number of `\\n`-separated lines in the text currently shown. Cached by\n // `show()` (which knows the raw string) and read by `doLayout()` (which\n // only sees the committed size) so the label's height tracks the line\n // count. 1 for a single-line tooltip — the unchanged legacy case.\n private _lineCount: number = 1;\n\n // Resolved per-line pixel height of the tooltip text, set by `show()` from\n // the live additive line box and read by `doLayout()`. Replaces the\n // fixed `ITEM_HEIGHT` multiplier, which over-allocated ~3px/line and left\n // a trailing empty line on multi-line tooltips.\n private _perLine: number = Tooltip.ITEM_HEIGHT;\n\n /** Private — use the static methods; only one instance is ever created. */\n private constructor() {\n super();\n\n this.setVisible(false);\n // Above every managed layer (incl. a modal Dialog and its backdrop) so a\n // hover tooltip is never occluded.\n this.setZIndex(LayerManager.Band.Tooltip);\n this.setBackgroundColor(\"var(--ts-ui-tooltip-bg, rgb(255, 255, 240))\");\n this.setForegroundColor(\"var(--ts-ui-tooltip-color, rgb(0, 0, 0))\");\n this.setBorder({ border: \"1px solid var(--ts-ui-tooltip-border, rgb(180, 180, 100))\" });\n this.setShadow(\"var(--ts-ui-tooltip-shadow, 1px 2px 4px rgba(0, 0, 0, 0.2))\");\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setPointerEvents(\"none\");\n // Top-level overlay, dynamic size — layout+paint containment scopes reflow without\n // committing to a fixed size.\n this.setContain(\"layout paint\");\n\n this._text = new Text();\n this._text.setPointerEvents(\"none\");\n // `pre-wrap` preserves explicit `\\n` breaks as real lines *and* wraps\n // a line that overflows the fixed width — `nowrap` would collapse the\n // breaks onto one line. A single-line tooltip narrower than MAX_WIDTH\n // renders identically to the old `nowrap` behaviour.\n this._text.setWhiteSpace(\"pre-wrap\");\n this.addComponent(this._text);\n }\n\n /**\n * Returns the single shared `Tooltip` instance, creating it on first call.\n *\n * @returns The singleton `Tooltip` component.\n */\n private static getInstance(): Tooltip {\n if (!Tooltip.instance) {\n Tooltip.instance = new Tooltip();\n }\n\n return Tooltip.instance;\n }\n\n /**\n * Immediately shows the tooltip with the given text near the specified coordinates.\n *\n * The tooltip sits just past the cursor on each axis — down and to the right —\n * flipping to sit just before it when there is no room past, so it never covers\n * the cursor it's hinting at. The clearance is measured from a small box around\n * the pointer hotspot rather than from the hotspot itself, so a flipped tooltip\n * hugs the cursor as closely as an unflipped one. A tooltip taller than roughly\n * half the viewport (an unusually long multi-line label) can still saturate\n * on-screen rather than flipping cleanly, since its height is uncapped and never\n * scrolls; this is a known limitation, not a bug to fix here.\n *\n * @param text - The string to display inside the tooltip.\n * @param x - Horizontal viewport coordinate for the tooltip origin.\n * @param y - Vertical viewport coordinate for the tooltip origin.\n */\n static show(text: string, x: number, y: number): void {\n if (Tooltip.showTimer !== null) {\n clearTimeout(Tooltip.showTimer);\n Tooltip.showTimer = null;\n }\n\n const inst = Tooltip.getInstance();\n\n // Whether the tooltip is already on screen (and not mid-dismiss). A\n // caller that shows on every mousemove — a chart hover — reaches `show`\n // dozens of times a second; replaying the entrance fade each time would\n // reset opacity to 0 before the 100 ms fade completes, flickering the\n // tooltip. When already shown we skip the fade and just reposition/retext.\n const alreadyShown = inst.isVisible() && !Tooltip.dismissing;\n\n inst._text.setText(text);\n\n // Size width to the widest line and height to the line count so a\n // `\\n`-containing string renders as a multi-line block. A string with\n // no `\\n` yields a single line and the original single-line sizing.\n const lines = text.split(\"\\n\");\n const widestLine = lines.reduce((max, line) => Math.max(max, Util.measureTextWidth(line)), 0);\n\n inst._perLine = inst._perLineHeight();\n\n // No minimum width — the tooltip hugs its content (widest line plus\n // horizontal padding), capped at MAX_WIDTH. A min-width floor would\n // pad short labels out to a fixed box wider than their text.\n const tooltipWidth = Math.min(Tooltip.MAX_WIDTH, widestLine + Tooltip.H_PADDING);\n\n // When the widest line is wider than the cap, the label soft-wraps at the\n // available text width, so the visual line count exceeds the `\\n`-split\n // count. Measure the wrapped height at that width and derive the real line\n // count; an uncapped tooltip never wraps, so the split count stands.\n if (widestLine + Tooltip.H_PADDING > Tooltip.MAX_WIDTH) {\n const availTextWidth = tooltipWidth - Tooltip.H_PADDING;\n const wrappedHeight = DOM.source.measureText(text, { maxWidth: availTextWidth }).height;\n inst._lineCount = Math.max(lines.length, Math.round(wrappedHeight / inst._perLine));\n } else {\n inst._lineCount = lines.length;\n }\n\n // Height hugs the text: one resolved line height per line plus padding.\n // Floor the single-line case to the legacy ITEM_HEIGHT box so plain\n // one-line tooltips stay pixel-stable; only multi-line tooltips switch\n // to the tighter per-line height that removes the trailing gap.\n let tooltipHeight = inst._lineCount * inst._perLine + Tooltip.V_PADDING;\n if (inst._lineCount === 1) {\n tooltipHeight = Math.max(tooltipHeight, Tooltip.ITEM_HEIGHT + Tooltip.V_PADDING);\n }\n\n inst.setWidth(tooltipWidth);\n inst.setHeight(tooltipHeight);\n\n const vp = DOM.source.getViewportSize();\n\n // Clamp the cursor into the viewport first: the flip primitive only guarantees\n // an on-screen result for an in-viewport anchor, and show() is public.\n const cx = Util.clamp(x, 0, vp.width);\n const cy = Util.clamp(y, 0, vp.height);\n\n // Sit past the cursor's clearance box, flipping to sit *before* it when there\n // is no room past — so the tooltip can never land under the cursor. The box\n // (not the bare hotspot) is the anchor rect, which is what lets the two sides\n // differ: the primitive applies one `gap` to whichever edge it picks, and the\n // asymmetry the cursor actually has lives in the edges. No viewport margin,\n // since the tooltip may sit flush against an edge.\n inst.setX(positionAdjacent(\n cx - Tooltip.CURSOR_LEFT, cx + Tooltip.CURSOR_RIGHT, tooltipWidth, vp.width, Tooltip.CURSOR_GAP,\n ));\n inst.setY(positionAdjacent(\n cy - Tooltip.CURSOR_UP, cy + Tooltip.CURSOR_DOWN, tooltipHeight, vp.height, Tooltip.CURSOR_GAP,\n ));\n\n const el = inst.getElement(true)!;\n\n inst.scheduleLayout();\n\n LayerManager.mount(el);\n\n inst.setVisible(true);\n\n // Watch for the anchor leaving the DOM while the tooltip is up (a removed\n // element fires no mouseout), so an orphaned tooltip dismisses on the next\n // pointer move instead of lingering.\n if (!Tooltip.watching) {\n Event.addViewportListener(inst, \"mousemove\", Tooltip._onAnchorWatch);\n Tooltip.watching = true;\n }\n\n // Cancel a pending fade-out's removeElement so a fresh show during the\n // outgoing transition keeps the element in the DOM.\n Tooltip.dismissing = false;\n\n if (alreadyShown) {\n // Already visible — hold at full opacity; the position/size/text\n // updates above are all this repeat show needs. No entrance fade.\n DOM.sink.apply(el, { style: { opacity: \"1\" } });\n\n return;\n }\n\n inst._showAnimation?.cancel();\n inst._showAnimation = Animation.play(el, {\n from: { opacity: \"0\" },\n to: { opacity: \"1\" },\n durationMs: TOOLTIP_ANIM_DURATION_MS,\n properties: [\"opacity\"],\n });\n }\n\n /**\n * Hides and detaches the tooltip.\n *\n * Any pending show timer is also cancelled.\n */\n static hide(): void {\n if (Tooltip.showTimer !== null) {\n clearTimeout(Tooltip.showTimer);\n Tooltip.showTimer = null;\n }\n\n const inst = Tooltip.getInstance();\n const el = inst.getElement();\n\n // The tooltip is going away; stop watching the (now-irrelevant) anchor\n // and forget it.\n if (Tooltip.watching) {\n Event.removeViewportListener(inst, \"mousemove\", Tooltip._onAnchorWatch);\n Tooltip.watching = false;\n }\n Tooltip.activeElement = null;\n\n if (!el) {\n return;\n }\n\n Tooltip.dismissing = true;\n\n inst._hideAnimation?.cancel();\n inst._hideAnimation = Animation.play(el, {\n to: { opacity: \"0\" },\n durationMs: TOOLTIP_ANIM_DURATION_MS,\n properties: [\"opacity\"],\n onComplete: () => {\n if (!Tooltip.dismissing) {\n return;\n }\n Tooltip.dismissing = false;\n inst.setVisible(false);\n inst.removeElement();\n },\n });\n }\n\n /**\n * Wires `mouseover` and `mouseout` listeners onto `component` so the tooltip\n * shows automatically after a 500 ms hover delay.\n *\n * @remarks Uses `mouseover`/`mouseout` rather than `mouseenter`/`mouseleave`\n * because the Event system routes through a window-level capture handler, and\n * the non-bubbling enter/leave events are not reliably seen there in Chrome.\n *\n * If `colors` is provided the tooltip uses those colors instead of the default\n * theme variables while it is showing for this component.\n *\n * Calling `attach` on a component that already has an attachment replaces it.\n *\n * @param component - The component to attach hover behaviour to.\n * @param text - The tooltip text to display.\n * @param colors - Optional color overrides applied while this tooltip is visible.\n */\n static attach(component: Component, text: string, colors?: TooltipColors): void {\n Tooltip.detach(component);\n\n let cursorX = 0;\n let cursorY = 0;\n\n const mouseoverFn = (e: MouseEvent) => {\n if (Tooltip.showTimer !== null) {\n return;\n }\n\n cursorX = e.clientX;\n cursorY = e.clientY;\n\n Tooltip.showTimer = setTimeout(() => {\n Tooltip._applyColors(colors);\n // Record the anchor so the anchor-watch dismisses the tooltip if\n // this component is later removed from the DOM.\n Tooltip.activeElement = component.getElement() ?? null;\n Tooltip.show(text, cursorX, cursorY);\n Tooltip.showTimer = null;\n }, 500);\n };\n\n const mousemoveFn = (e: MouseEvent) => {\n cursorX = e.clientX;\n cursorY = e.clientY;\n };\n\n const mouseoutFn = () => {\n Tooltip.hide();\n };\n\n // Acting on the component (a click, or the start of a drag) dismisses\n // its tooltip: once the pointer is pressed the hover hint has served its\n // purpose and would only obscure the result of the action. `hide` also\n // cancels a still-pending show timer, so a press during the hover delay\n // suppresses the tooltip entirely. It stays hidden until the pointer\n // leaves and re-enters, since no fresh `mouseover` fires under a\n // stationary cursor.\n const mousedownFn = () => {\n Tooltip.hide();\n };\n\n Event.addListener(component, \"mouseover\", mouseoverFn);\n Event.addListener(component, \"mousemove\", mousemoveFn);\n Event.addListener(component, \"mouseout\", mouseoutFn);\n Event.addListener(component, \"mousedown\", mousedownFn);\n\n Tooltip.attachments.set(component.getId(), {\n text, colors, mouseoverFn, mousemoveFn, mouseoutFn, mousedownFn,\n });\n }\n\n /**\n * Removes the tooltip attachment from a component, cancelling any pending show\n * and hiding the tooltip if it is currently visible for this component.\n *\n * @param component - The component whose attachment should be removed.\n */\n static detach(component: Component): void {\n const id = component.getId();\n const att = Tooltip.attachments.get(id);\n\n if (!att) {\n return;\n }\n\n Event.removeListener(component, \"mouseover\", att.mouseoverFn);\n Event.removeListener(component, \"mousemove\", att.mousemoveFn);\n Event.removeListener(component, \"mouseout\", att.mouseoutFn);\n Event.removeListener(component, \"mousedown\", att.mousedownFn);\n\n Tooltip.attachments.delete(id);\n Tooltip.hide();\n }\n\n /**\n * Applies optional color overrides to the singleton tooltip instance before it is shown.\n * Resets to the default theme variables when no overrides are provided.\n *\n * @param colors - The color overrides to apply, or `undefined` to use defaults.\n */\n private static _applyColors(colors?: TooltipColors): void {\n const inst = Tooltip.getInstance();\n\n inst.setBackgroundColor(\n colors?.background ?? 'var(--ts-ui-tooltip-bg, rgb(255, 255, 240))'\n );\n inst.setForegroundColor(\n colors?.color ?? 'var(--ts-ui-tooltip-color, rgb(0, 0, 0))'\n );\n inst.setBorder({ border: `1px solid ${colors?.border ?? 'var(--ts-ui-tooltip-border, rgb(180, 180, 100))'}` });\n }\n\n /**\n * Wires native `mouseover`, `mousemove`, and `mouseout` listeners directly onto\n * a raw DOM element so the tooltip shows after a 500 ms hover delay.\n *\n * @remarks Use this instead of {@link attach} when the element contains child nodes\n * that would receive the event as `evnt.target` — the Event system's component\n * listener only matches the exact target id, so `attach` would miss those cases.\n *\n * Calling `attachToElement` on an element that already has an attachment\n * replaces it — the previous listeners are removed and a fresh set is\n * installed against the new text. If the tooltip is currently visible for\n * this element when the call happens, it re-renders immediately at the\n * last known cursor position so the visible text updates without a\n * re-hover.\n *\n * @param element - The element handle to attach hover behaviour to.\n * @param text - The tooltip text to display.\n */\n static attachToElement(element: Handle, text: string): void {\n const previous = Tooltip.elementAttachments.get(element);\n const wasActive = Tooltip.activeElement === element;\n\n // Carry the previous binding's last cursor coords so the mid-hover\n // repaint below has real values instead of (0, 0) — detachElement\n // deletes the WeakMap entry, so the read has to happen first.\n const carryX = previous ? previous.lastX : 0;\n const carryY = previous ? previous.lastY : 0;\n\n Tooltip.detachElement(element);\n\n const att: ElementTooltipAttachment = {\n text,\n showTimer : null,\n lastX : carryX,\n lastY : carryY,\n mouseoverFn: function onTooltipMouseOver(e: MouseEvent): void {\n if (att.showTimer !== null) {\n return;\n }\n\n att.lastX = e.clientX;\n att.lastY = e.clientY;\n\n att.showTimer = setTimeout(function onTooltipShowTimer(): void {\n Tooltip.activeElement = element;\n Tooltip.show(att.text, att.lastX, att.lastY);\n att.showTimer = null;\n }, 500);\n },\n mousemoveFn: function onTooltipMouseMove(e: MouseEvent): void {\n att.lastX = e.clientX;\n att.lastY = e.clientY;\n },\n mouseoutFn: function onTooltipMouseOut(): void {\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n if (Tooltip.activeElement === element) {\n Tooltip.activeElement = null;\n }\n\n Tooltip.hide();\n },\n // Pressing the element (click or drag start) dismisses its tooltip\n // and cancels any pending show — see the `attach` counterpart.\n mousedownFn: function onTooltipMouseDown(): void {\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n Tooltip.hide();\n },\n };\n\n DOM.sink.addListener(element, \"mouseover\", att.mouseoverFn);\n DOM.sink.addListener(element, \"mousemove\", att.mousemoveFn);\n DOM.sink.addListener(element, \"mouseout\", att.mouseoutFn);\n DOM.sink.addListener(element, \"mousedown\", att.mousedownFn);\n\n Tooltip.elementAttachments.set(element, att);\n\n // Mid-hover update: if the previous attachment was the active tooltip\n // target, repaint the visible tooltip with the new text at the last\n // known cursor coords so the swap is seen without a re-hover.\n if (wasActive) {\n Tooltip.activeElement = element;\n Tooltip.show(text, att.lastX, att.lastY);\n }\n }\n\n /**\n * Removes an `attachToElement` binding installed on an element handle.\n *\n * Detaches the three hover listeners, cancels any pending show timer, and\n * clears the active-element tracker if it pointed at this element.\n * Idempotent — calling on an unattached element is a no-op.\n *\n * @param element - The element handle whose attachment should be removed.\n */\n static detachElement(element: Handle): void {\n const att = Tooltip.elementAttachments.get(element);\n\n if (!att) {\n return;\n }\n\n DOM.sink.removeListener(element, \"mouseover\", att.mouseoverFn);\n DOM.sink.removeListener(element, \"mousemove\", att.mousemoveFn);\n DOM.sink.removeListener(element, \"mouseout\", att.mouseoutFn);\n DOM.sink.removeListener(element, \"mousedown\", att.mousedownFn);\n\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n Tooltip.elementAttachments.delete(element);\n\n if (Tooltip.activeElement === element) {\n Tooltip.activeElement = null;\n }\n }\n\n /**\n * Resolves the tooltip text's rendered per-line height in pixels from the\n * live additive line box, measured at hover time. Matches the `_text` label\n * (a default `Text`: 14px font, theme leading) so the box hugs the text\n * vertically with no trailing gap, and tracks theme/font changes on the next\n * hover.\n *\n * @returns The per-line height in pixels, ceiled to a whole pixel.\n *\n * @remarks `measureTextMetrics` already defaults `lineHeight` to\n * `calc(1em + var(--ts-ui-line-padding, 2px))`, so the line box is left to\n * that default rather than passed explicitly.\n */\n private _perLineHeight(): number {\n const { height } = DOM.source.measureText(\"X\");\n\n return Math.ceil(height);\n }\n\n /**\n * Positions the label to fill the tooltip body with uniform padding.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n this._text.setX(Tooltip.H_PADDING / 2);\n this._text.setY(Tooltip.V_PADDING / 2);\n this._text.setWidth(Math.max(0, this.getWidth() - Tooltip.H_PADDING));\n this._text.setHeight(this._lineCount * this._perLine);\n\n return this;\n }\n\n /**\n * Cancels any in-flight fade, then defers to the base class. Cancelling\n * first keeps their fallback timers from firing after `super.destructor()`\n * has released this tooltip's element handle.\n */\n protected destructor(): void {\n this._showAnimation?.cancel();\n this._showAnimation = null;\n this._hideAnimation?.cancel();\n this._hideAnimation = null;\n\n super.destructor();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tooltip-BUy6uFKN.js","names":[],"sources":["../../src/typescript/lib/core/LayerManager.ts","../../src/typescript/lib/core/OverlayPosition.ts","../../src/typescript/lib/overlay/Tooltip.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * How a layer responds to an outside interaction.\n *\n * - `\"click-outside\"`: dismissed when a `pointerdown` lands outside the\n * layer's subtree (and its anchor); ignores focus moves.\n * - `\"blur\"`: dismissed when either a `pointerdown` or a focus move lands\n * outside the layer's subtree (and its anchor).\n * - `\"manual\"`: never dismissed by the document-level handlers; the host\n * drives its own teardown.\n * - `\"modal\"`: never dismissed by an outside `pointerdown` or focus move\n * (the layer captures the interaction); only Escape, routed through the\n * keydown handler, asks it to close.\n *\n * @category Core\n */\nexport type LayerDismissMode = \"click-outside\" | \"blur\" | \"manual\" | \"modal\";\n\n/**\n * A portaled overlay surface that participates in the runtime layer tree.\n * Implemented by [`AnimatedDropdown`](/api/core/classes/AnimatedDropdown),\n * [`Popover`](/api/overlay/classes/Popover),\n * [`Dialog`](/api/overlay/classes/Dialog), and\n * [`Window`](/api/overlay/classes/Window).\n *\n * A surface registers itself with {@link LayerManager.register} from its\n * show path and unregisters with {@link LayerManager.unregister} from its\n * hide path. The manager then owns the z-index stamp, the activation\n * marking, and the dismissal decision — calling {@link DismissableLayer.requestClose}\n * (advisory) when an outside interaction meets the layer's\n * {@link LayerDismissMode}.\n *\n * @category Core\n */\nexport interface DismissableLayer {\n /** The layer's root element (already mounted on `documentElement`). */\n getLayerElement(): Handle | null;\n\n /** Dismiss policy consulted by the document-level interaction handlers. */\n getDismissMode(): LayerDismissMode;\n\n /** Advisory request to close; the surface runs its own teardown + unregister. */\n requestClose(): void;\n\n /**\n * Optional anchor element excluded from \"outside\" tests so the trigger\n * click that opened the layer does not immediately re-close it.\n */\n getAnchorElement?(): Handle | null;\n\n /**\n * Optional activation hook. The manager calls it with `true` when this\n * layer becomes the active (topmost containing) layer for a pointer /\n * focus interaction and `false` when it is superseded. Surfaces that\n * carry an active-state affordance (e.g. a window title bar) map it to\n * their own toggle; surfaces without one omit the method.\n */\n onActivate?(active: boolean): void;\n\n /**\n * Optional re-stamp hook. The manager calls it with a fresh z-index when\n * {@link LayerManager.bringToFront} re-allocates the layer (or one of its\n * ancestors) while it is already shown, so the surface can mirror the new\n * value onto its element through its own typed `setZIndex`. Surfaces that\n * only read z once in their show path (and never get raised afterwards)\n * may omit it.\n */\n onZIndexChanged?(zIndex: number): void;\n\n /**\n * Optional z-index band hint. Returns one of {@link LayerManager.Band}'s\n * values so an unrelated peer layer stacks in its surface family's band\n * (Window < Popover < dropdown < Dialog). Omitted surfaces default to the\n * dropdown band. A nested layer ignores its own band and inherits its\n * opener's, so this only matters for top-level (unparented) registrations.\n */\n getBand?(): number;\n\n /**\n * Optional top-level hint. Returns `true` for a surface that is an\n * independent peer rather than a layer opened from another — a window is\n * not a child of whatever happened to be topmost when it appeared. Such a\n * layer registers as a tree root, so raising it does not drag an unrelated\n * peer up with it (see {@link LayerManager.bringToFront}). Omitted surfaces\n * register under the current topmost layer (the opened-from edge).\n */\n isLayerRoot?(): boolean;\n}\n\n/**\n * Node in the runtime layer tree. Generalizes the per-host `LayerEntry`\n * that `AnimatedDropdown` previously kept module-private: `children` are the\n * layers opened while this one was topmost, `band` is the z-index band base\n * the layer was assigned (inherited from its opener), and `zIndex` is the\n * band + counter stamp the manager assigned at register time.\n */\ninterface LayerNode {\n layer: DismissableLayer;\n parent: LayerNode | null;\n children: LayerNode[];\n band: number;\n zIndex: number;\n}\n\n// Z-index bands. Plain module constants because z-index is not themed\n// anywhere today (Theme.ts carries no z-index tokens; the values were inline\n// per class). The bands reconcile the four historical bases into one\n// ascending allocator and preserve relative order between unrelated peers:\n// Window 9000 < Popover 9800 < dropdowns 10000 < Dialog 11000.\n// A nested child inherits its opener's band but always lands above it because\n// it registers later and so draws a higher counter. The 200-1000 gap between\n// bands leaves headroom for the monotonic `_zCounter`; it is not reset, on the\n// assumption a single session opens far fewer than 200 unrelated layers in the\n// same band before a reload — the historical inline values made the same bet.\nconst Z_BAND_WINDOW: number = 9000;\nconst Z_BAND_POPOVER: number = 9800;\nconst Z_BAND_DROPDOWN: number = 10000;\nconst Z_BAND_DIALOG: number = 11000;\n// Above every managed layer: a tooltip is a transient, non-interactive\n// affordance that must float over even a modal Dialog and its backdrop. Not a\n// registered layer band — exposed so the Tooltip singleton stamps itself here\n// rather than carrying a magic number that could fall below the Dialog band.\nconst Z_BAND_TOOLTIP: number = 12000;\n\n/**\n * Sentinel `Component` used as the registration key for the manager's three\n * document-level listeners. `Event.addViewportListener` binds a listener to a\n * `Component`, but the manager's handlers are module-global (one per type, not\n * per layer), so a single stable sentinel owns them — mirroring `Window`'s\n * `_viewportListenerOwner`. The install/teardown lifecycle keys off the tree\n * being non-empty rather than any individual layer.\n */\nconst _listenerOwner: Component = new Component();\n\n/**\n * Central registry and document-level interaction broker for portaled overlay\n * surfaces. Owns one runtime layer tree (push-on-register /\n * link-under-current-topmost / pop-on-unregister), one z-index allocator with\n * reserved bands, and a single set of document-level `pointerdown` / `focusin`\n * / `keydown` listeners installed on the first register and removed on the\n * last unregister.\n *\n * The relationship the tree captures — the runtime \"opened-from\" edge (which\n * layer was topmost when this one opened) — is distinct from the static\n * component hierarchy and changes per activation, so it lives here rather than\n * in the component tree. Containment queries hop across portaled descendant\n * layers via {@link LayerManager.containsAcrossLayers}, which is the single\n * place cross-portal containment is reasoned about.\n *\n * @category Core\n */\nexport namespace LayerManager {\n\n // LIFO stack of open layers in register order; the last entry is topmost.\n const _stack: LayerNode[] = [];\n\n // Membership + node lookup keyed by the layer instance, generalizing the\n // per-host `_entryByLayer` WeakMap.\n const _nodeByLayer: WeakMap<DismissableLayer, LayerNode> = new WeakMap();\n\n // Ascending per-register counter; combined with a band base to stamp z.\n let _zCounter: number = 0;\n\n // The layer currently marked active (received the last `onActivate(true)`).\n let _activeLayer: DismissableLayer | null = null;\n\n let _listenersInstalled: boolean = false;\n\n /**\n * The four z-index bands a surface returns from\n * {@link DismissableLayer.getBand}. Exposed so each surface can tag itself\n * without re-declaring the constants. Reconciles the historical inline\n * bases (Window 9000, Popover 9998, dropdowns 10050, Dialog 10101) into\n * one ascending allocator.\n */\n export const Band = {\n Window: Z_BAND_WINDOW,\n Popover: Z_BAND_POPOVER,\n Dropdown: Z_BAND_DROPDOWN,\n Dialog: Z_BAND_DIALOG,\n Tooltip: Z_BAND_TOOLTIP,\n } as const;\n\n /**\n * Picks the z-index band for a layer. A nested layer inherits its\n * opener's band so it stays in the same stacking neighbourhood (and rises\n * above the opener because it registers later); an unrelated peer uses its\n * own surface-type band from {@link DismissableLayer.getBand}.\n */\n function bandFor(parent: LayerNode | null, ownBand: number): number {\n return parent ? parent.band : ownBand;\n }\n\n /**\n * Pushes `layer` as a child of the current topmost layer — unless it\n * declares itself a top-level peer via\n * {@link DismissableLayer.isLayerRoot}, in which case it registers as a\n * tree root — assigns its band-based z-index, and installs the\n * document-level listeners on the first call. A duplicate register (e.g. a\n * `showAnimated` that cancels an in-flight fade-out) is a no-op so the tree\n * never double-pushes.\n *\n * @param layer - The surface entering the layer tree.\n */\n export function register(layer: DismissableLayer): void {\n if (_nodeByLayer.has(layer)) {\n return;\n }\n\n const topmost = _stack.length > 0 ? _stack[_stack.length - 1] : null;\n const parent = layer.isLayerRoot?.() ? null : topmost;\n const band = bandFor(parent, layer.getBand?.() ?? Z_BAND_DROPDOWN);\n const zIndex = band + (++_zCounter);\n\n const node: LayerNode = { layer, parent, children: [], band, zIndex };\n\n _nodeByLayer.set(layer, node);\n _stack.push(node);\n\n if (parent) {\n parent.children.push(node);\n }\n\n if (!_listenersInstalled) {\n installListeners();\n }\n }\n\n /**\n * Appends a portaled overlay element to `document.documentElement` if it is\n * not already contained there. The single home for the \"portal mount\"\n * idiom every floating surface used to inline. Idempotent — a surface that\n * re-shows after detach re-appends, and one already mounted is left alone.\n * Does **not** register the layer; surfaces still call {@link register}\n * explicitly.\n *\n * @param el - The overlay element to portal onto the document root.\n */\n export function mount(el: Handle): void {\n if (!DOM.source.contains(DOM.source.getDocumentElement(), el)) {\n DOM.sink.appendChild(DOM.source.getDocumentElement(), el);\n }\n }\n\n /**\n * Pops `layer` from the tree (and unlinks it from its parent's child\n * list), clears it as active if it was, and removes the document-level\n * listeners on the last call.\n *\n * @param layer - The surface leaving the layer tree.\n */\n export function unregister(layer: DismissableLayer): void {\n const node = _nodeByLayer.get(layer);\n\n if (!node) {\n return;\n }\n\n _nodeByLayer.delete(layer);\n\n const idx = _stack.indexOf(node);\n\n if (idx >= 0) {\n _stack.splice(idx, 1);\n }\n\n if (node.parent) {\n const ci = node.parent.children.indexOf(node);\n\n if (ci >= 0) {\n node.parent.children.splice(ci, 1);\n }\n }\n\n if (_activeLayer === layer) {\n _activeLayer = null;\n }\n\n if (_stack.length === 0 && _listenersInstalled) {\n uninstallListeners();\n }\n }\n\n /**\n * Returns true when `node` is inside `layer`'s own element OR inside any\n * descendant layer's element. This is the only cross-portal containment\n * hop in the framework: a `pointerdown` or focus move that lands in a\n * child layer (e.g. a dropdown opened from inside a popover) counts as\n * inside the ancestor so the ancestor stays open.\n *\n * @param layer - The layer whose subtree is tested.\n * @param node - The DOM node receiving the interaction.\n * @returns True when `node` is inside `layer` or any descendant layer.\n */\n export function containsAcrossLayers(layer: DismissableLayer, node: Handle | null): boolean {\n if (!node) {\n return false;\n }\n\n const layerNode = _nodeByLayer.get(layer);\n\n if (!layerNode) {\n const el = layer.getLayerElement();\n\n return el ? DOM.source.contains(el, node) : false;\n }\n\n return nodeContains(layerNode, node);\n }\n\n /**\n * Returns the topmost currently-registered layer, or null when none is\n * open.\n *\n * @returns The topmost open layer, or null.\n */\n export function getTopLayer(): DismissableLayer | null {\n return _stack.length > 0 ? _stack[_stack.length - 1].layer : null;\n }\n\n /**\n * Re-stamps `layer` (and its descendant layers) with fresh top-of-band\n * z-indices and marks it active. Used by surfaces that raise on click\n * (e.g. a window brought to front), so the raised layer — and anything it\n * opened — jumps above its band peers without disturbing other bands.\n *\n * @param layer - The layer to raise and activate.\n */\n export function bringToFront(layer: DismissableLayer): void {\n const node = _nodeByLayer.get(layer);\n\n if (!node) {\n return;\n }\n\n restampSubtree(node);\n markActive(layer);\n }\n\n /**\n * Returns the z-index currently assigned to `layer`, for surfaces that\n * mirror it elsewhere. Falls back to the dropdown band when the layer is\n * not registered.\n *\n * @param layer - The layer whose stamp to read.\n * @returns The assigned z-index.\n */\n export function getZIndex(layer: DismissableLayer): number {\n const node = _nodeByLayer.get(layer);\n\n return node ? node.zIndex : Z_BAND_DROPDOWN;\n }\n\n /**\n * Re-stamps `node`'s subtree from a fresh top-of-band counter run so the\n * raised layer and its descendants ascend together, preserving their\n * relative order. Each re-stamped layer is notified via\n * {@link DismissableLayer.onZIndexChanged} so it can mirror the new value\n * through its own typed `setZIndex` (the manager never writes the DOM).\n */\n function restampSubtree(node: LayerNode): void {\n const walk = (n: LayerNode): void => {\n n.zIndex = n.band + (++_zCounter);\n n.layer.onZIndexChanged?.(n.zIndex);\n\n for (const child of n.children) {\n walk(child);\n }\n };\n\n walk(node);\n }\n\n /** True when `node` (or a descendant layer) contains `target`. */\n function nodeContains(node: LayerNode, target: Handle): boolean {\n const el = node.layer.getLayerElement();\n\n if (el && DOM.source.contains(el, target)) {\n return true;\n }\n\n for (const child of node.children) {\n if (nodeContains(child, target)) {\n return true;\n }\n }\n\n return false;\n }\n\n /** Marks `layer` active and deactivates the previously-active layer. */\n function markActive(layer: DismissableLayer): void {\n if (_activeLayer === layer) {\n return;\n }\n\n deactivateActive();\n\n _activeLayer = layer;\n\n if (layer.onActivate) {\n layer.onActivate(true);\n }\n }\n\n /** Deactivates the currently-active layer, if any. */\n function deactivateActive(): void {\n if (_activeLayer && _activeLayer.onActivate) {\n _activeLayer.onActivate(false);\n }\n\n _activeLayer = null;\n }\n\n /**\n * Walks up the \"opened-from\" chain from `node` and returns the nearest\n * ancestor-or-self layer that carries an activation affordance\n * ({@link DismissableLayer.onActivate}), or `null` if none does. A layer\n * opened inside a window resolves to that window, so the window stays\n * active while its descendant holds focus — interacting with a child layer\n * must not darken the title bar of the window that owns it.\n */\n function activatableAncestor(node: LayerNode): DismissableLayer | null {\n let cur: LayerNode | null = node;\n\n while (cur) {\n if (cur.layer.onActivate) {\n return cur.layer;\n }\n\n cur = cur.parent;\n }\n\n return null;\n }\n\n /**\n * Walks the stack top-down for a pointer / focus interaction at `target`,\n * dismissing each topmost layer whose dismiss mode and containment test\n * say the interaction landed outside it, and marking the topmost\n * containing layer active. `focusOnly` is true for the `focusin` pass, so\n * only `\"blur\"` layers act on it (a `\"click-outside\"` layer ignores focus\n * moves).\n */\n function handleOutside(target: Handle | null, focusOnly: boolean): void {\n // Snapshot top-down so requestClose-driven unregisters don't disturb\n // the walk; re-read containment against the live tree each step.\n const snapshot = _stack.slice().reverse();\n let activated = false;\n let landedInside = false;\n\n for (const node of snapshot) {\n const mode = node.layer.getDismissMode();\n\n if (containsAcrossLayers(node.layer, target)) {\n // Activation belongs to the nearest activatable ancestor (the\n // window a child layer was opened from), not the innermost\n // containing layer; fall back to the layer itself when nothing\n // up the chain carries an activation affordance, preserving the\n // prior behaviour for standalone layers.\n markActive(activatableAncestor(node) ?? node.layer);\n activated = true;\n landedInside = true;\n\n break;\n }\n\n const anchor = node.layer.getAnchorElement?.();\n\n if (anchor && target && DOM.source.contains(anchor, target)) {\n landedInside = true;\n\n break;\n }\n\n const dismissable = mode === \"click-outside\" || mode === \"blur\";\n const acts = focusOnly ? mode === \"blur\" : dismissable;\n\n if (mode === \"manual\" || mode === \"modal\" || !acts) {\n // Modal captures the interaction: an outside pointer/focus on a\n // modal must not fall through to dismiss layers beneath it.\n if (mode === \"modal\") {\n landedInside = true;\n\n break;\n }\n\n continue;\n }\n\n node.layer.requestClose();\n }\n\n // An interaction that landed outside every layer's subtree (and any\n // anchor / modal capture) deactivates the active layer — e.g. an\n // empty-viewport click that should drop a window's active title-bar\n // highlight. A focus move to a non-layer element (focusOnly) does not\n // deactivate, so tabbing through page chrome doesn't flicker the\n // active window.\n if (!activated && !landedInside && !focusOnly) {\n deactivateActive();\n }\n }\n\n /** Document `pointerdown` handler — see {@link handleOutside}. */\n function onPointerDown(e: PointerEvent): void {\n handleOutside(e.target === null ? null : DOM.source.intern(e.target), false);\n }\n\n /**\n * Window `blur` handler. When the whole browser window loses focus (the user\n * clicks another application or alt-tabs away) no in-page `pointerdown` or\n * `focusin` fires, so an open layer would otherwise stay up. Treat it as an\n * interaction landing outside every layer, dismissing each `\"click-outside\"`\n * / `\"blur\"` layer while a `\"modal\"` shields those beneath it and `\"manual\"`\n * layers (windows) are left open.\n *\n * @remarks Viewport listeners are capture-phase, so element blurs from within\n * the page surface here too; only a genuine window blur (`target` is the\n * window itself) should dismiss layers.\n */\n function onWindowBlur(e: FocusEvent): void {\n if (!DOM.source.isWindow(e.target === null ? null : DOM.source.intern(e.target))) {\n return;\n }\n\n handleOutside(null, false);\n }\n\n /** Document `focusin` handler — only `\"blur\"` layers act on it. */\n function onFocusIn(e: FocusEvent): void {\n handleOutside(e.target === null ? null : DOM.source.intern(e.target), true);\n }\n\n /**\n * Document `keydown` handler — Escape asks the topmost non-manual layer to close.\n *\n * @param e - The keydown event.\n * @returns `true` when Escape actually closed a layer; nothing otherwise, so an\n * Escape with no dismissible layer open keeps propagating.\n */\n function onKeyDown(e: KeyboardEvent): Event.ListenerResult {\n if (e.key !== \"Escape\") {\n return;\n }\n\n for (let i = _stack.length - 1; i >= 0; i--) {\n const mode = _stack[i].layer.getDismissMode();\n\n if (mode === \"manual\") {\n continue;\n }\n\n _stack[i].layer.requestClose();\n\n return true;\n }\n\n return;\n }\n\n /** Installs the three document-level listeners against the sentinel owner. */\n function installListeners(): void {\n Event.addViewportListener(_listenerOwner, \"pointerdown\", onPointerDown);\n Event.addViewportListener(_listenerOwner, \"focusin\", onFocusIn);\n Event.addViewportListener(_listenerOwner, \"keydown\", onKeyDown);\n Event.addViewportListener(_listenerOwner, \"blur\", onWindowBlur);\n\n _listenersInstalled = true;\n }\n\n /** Removes the three document-level listeners. */\n function uninstallListeners(): void {\n Event.removeViewportListener(_listenerOwner, \"pointerdown\", onPointerDown);\n Event.removeViewportListener(_listenerOwner, \"focusin\", onFocusIn);\n Event.removeViewportListener(_listenerOwner, \"keydown\", onKeyDown);\n Event.removeViewportListener(_listenerOwner, \"blur\", onWindowBlur);\n\n _listenersInstalled = false;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Util } from \"~/core/Util.js\";\nimport type { Rect } from \"~/core/DOM.js\";\nimport type { Size } from \"~/primitive/Size.js\";\n\n/**\n * The primary growth axis for an anchored overlay.\n *\n * @category Core\n */\nexport type AnchorAxis = \"vertical\" | \"horizontal\";\n\n/**\n * Options controlling how {@link positionAnchored} places an element against an\n * anchor rect.\n *\n * @category Core\n */\nexport interface AnchorOptions {\n /** Primary growth axis: `\"vertical\"` grows below/above, `\"horizontal\"` right/left. */\n axis: AnchorAxis;\n /** Gap in px between the anchor edge and the element on the primary axis. Default 0. */\n gap?: number;\n /** Viewport-edge margin in px kept on the cross axis. Default 0. */\n margin?: number;\n}\n\n/**\n * Chooses a top-left coordinate on the primary axis. Prefers growing past the\n * anchor's far edge (`farEdge + gap`); flips to the near edge\n * (`nearEdge - extent - gap`) only when the far side lacks room AND the near\n * side has it. When neither side fits, saturates on-screen toward the side with\n * more room — pinned to the viewport's far end when the far side is roomier,\n * otherwise to `0` — so the element never overflows off-screen.\n *\n * @param nearEdge - The anchor's near edge (top / left) on this axis.\n * @param farEdge - The anchor's far edge (bottom / right) on this axis.\n * @param extent - The element's size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param gap - Gap in px kept between the anchor edge and the element.\n * @returns The chosen top-left coordinate on this axis.\n *\n * @category Core\n */\nexport function positionAdjacent(nearEdge: number, farEdge: number, extent: number, viewportExtent: number, gap: number): number {\n const farStart = farEdge + gap;\n const spaceFar = viewportExtent - farStart;\n const spaceNear = nearEdge - gap;\n\n if (extent <= spaceFar) {\n return farStart;\n }\n\n if (extent <= spaceNear) {\n return nearEdge - extent - gap;\n }\n\n // Neither side fits: pin to whichever side has more room, saturated so the\n // element stays on-screen (mirrors placeAnchored's spaceBelow/spaceAbove\n // fallback, axis-agnostic).\n if (spaceFar >= spaceNear) {\n return Math.max(0, viewportExtent - extent);\n }\n\n return 0;\n}\n\n/**\n * Chooses a top-left coordinate on the cross axis for a **fixed-size** element\n * that ALIGNS WITH (and overlaps) its anchor rather than sitting beside it.\n * Prefers the element's near edge flush with the anchor's near edge\n * (`nearEdge`); flips to the element's FAR edge flush with the anchor's far\n * edge (`farEdge - extent`) only when the near alignment overflows the\n * viewport. This is alignment, not adjacency — the element overlaps the\n * anchor either way; only which pair of edges is made flush changes. It is\n * fixed-size and returns no `available` (contrast {@link\n * positionFlexibleAnchored}); `margin` binds on **this** axis. When neither\n * alignment fits on-screen (an anchor whose own far edge is off-viewport, or\n * an element wider than the viewport), falls back to {@link clampAxis} — the\n * right answer when no placement fits, not a flip substitute.\n *\n * @param nearEdge - The anchor's near edge (left / top) on this axis.\n * @param farEdge - The anchor's far edge (right / bottom) on this axis.\n * @param extent - The element's fixed size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param margin - Viewport-edge margin in px kept on this axis.\n * @returns The chosen top-left coordinate on this axis.\n *\n * @category Core\n */\nexport function positionAligned(\n nearEdge: number, farEdge: number, extent: number, viewportExtent: number, margin: number,\n): number {\n // Preferred: the element's near edge aligns with the anchor's near edge.\n if (nearEdge >= margin && nearEdge + extent <= viewportExtent - margin) {\n return nearEdge;\n }\n\n // Flipped: the element's FAR edge aligns with the anchor's far edge. Note this\n // is alignment, not adjacency — the element overlaps the anchor either way; only\n // which pair of edges is made flush changes.\n const farStart = farEdge - extent;\n\n if (farStart >= margin && farStart + extent <= viewportExtent - margin) {\n return farStart;\n }\n\n // Neither alignment fits on-screen (an anchor whose own far edge is off-viewport,\n // or an element wider than the viewport): fall back to the clamp. Clamping is the\n // right answer when NO placement fits — it is only wrong as a substitute for a flip.\n return clampAxis(nearEdge, extent, viewportExtent, margin);\n}\n\n/**\n * Clamps a top-left coordinate into `[margin, extent - size - margin]`, pinning\n * to `margin` (top-aligned) when the element is larger than the available span\n * so the coordinate never goes below `margin` and off-screen. The upper bound is\n * floored to `margin` before clamping, because `Util.clamp` resolves a `min >\n * max` range to the max — the opposite of the `Math.max(margin, Math.min(...))`\n * this replaces; flooring keeps `min <= max`, so an over-large element pins to\n * `margin` and the caller's height-cap / scroll carries the overflow.\n *\n * @param value - The proposed coordinate.\n * @param size - The element's extent on this axis.\n * @param extent - The viewport's extent on this axis.\n * @param margin - Viewport-edge margin kept on this axis.\n * @returns The clamped coordinate.\n */\nfunction clampAxis(value: number, size: number, extent: number, margin: number): number {\n return Util.clamp(value, margin, Math.max(margin, extent - size - margin));\n}\n\n/**\n * The chosen coordinate for a size-flexible anchored element, plus the room\n * available on the side it landed on.\n *\n * @category Core\n */\nexport interface FlexiblePlacement {\n /** Top-left coordinate on the primary axis. */\n start: number;\n /** Room (px) on the side actually chosen — the caller's height/width cap. */\n available: number;\n}\n\n/**\n * Chooses a top-left coordinate on the primary axis for a **size-flexible**\n * element — one whose extent may be capped and the overflow scrolled, rather\n * than being placed at a fixed size. Grows from `farEdge`; flips to end at\n * `nearEdge` only when the content overflows the far room **and** the near\n * side is roomier. Unlike {@link positionAdjacent}, the far-fits check is against the\n * *room*, not the raw `extent` — so a flip that still doesn't fully fit on\n * the near side still flips (and clamps) rather than falling back to filling\n * the viewport over the anchor. The returned `available` is the room on the\n * chosen side, so a caller that caps the extent measures the correct side —\n * re-deriving it from `start` would measure the wrong side for a flipped\n * element. `viewportMargin` binds on **this** (primary) axis, unlike\n * {@link positionAnchored}'s cross-axis-only `margin`. Always flush against\n * the anchor edge — no `gap` parameter, because every current caller sits\n * flush.\n *\n * @param nearEdge - The anchor's near edge (top / left) on this axis — the\n * edge a flipped element's far edge meets.\n * @param farEdge - The anchor's far edge (bottom / right) on this axis — the\n * edge an unflipped element grows from.\n * @param extent - The element's unclamped preferred size on this axis.\n * @param viewportExtent - The viewport's size on this axis.\n * @param viewportMargin - Viewport-edge margin in px kept on this axis.\n * @returns The chosen coordinate and the room available at it.\n *\n * @category Core\n */\nexport function positionFlexibleAnchored(\n nearEdge: number,\n farEdge: number,\n extent: number,\n viewportExtent: number,\n viewportMargin: number,\n): FlexiblePlacement {\n const roomFar = viewportExtent - farEdge - viewportMargin;\n const roomNear = nearEdge - viewportMargin;\n\n if (extent <= roomFar || roomFar >= roomNear) {\n return { start: farEdge, available: roomFar };\n }\n\n return { start: nearEdge - Math.min(extent, roomNear), available: roomNear };\n}\n\n/**\n * Places an element of `size` against `anchorRect` inside `viewport`. On the\n * primary axis it grows past the anchor's far edge (below / right), flipping to\n * the near edge (above / left) only when the far side lacks room AND the near\n * side has more; on the cross axis it aligns to the anchor's near edge,\n * flipping to align with the anchor's far edge when the near alignment\n * overflows, and clamping only when neither alignment fits. Pure — all\n * viewport reads are supplied by the caller, so it is directly unit-testable\n * with no DOM.\n *\n * @param anchorRect - The anchor element's bounding rect.\n * @param size - The element's width/height to place.\n * @param viewport - The viewport size to clamp/flip within.\n * @param opts - Axis, gap, and cross-axis margin.\n * @returns The resolved top-left `{ x, y }` for the element.\n *\n * @category Core\n */\nexport function positionAnchored(anchorRect: Rect, size: Size, viewport: Size, opts: AnchorOptions): { x: number; y: number } {\n const gap = opts.gap ?? 0;\n const margin = opts.margin ?? 0;\n\n if (opts.axis === \"vertical\") {\n const y = positionAdjacent(anchorRect.top, anchorRect.bottom, size.height, viewport.height, gap);\n const x = positionAligned(anchorRect.left, anchorRect.right, size.width, viewport.width, margin);\n\n return { x, y };\n }\n\n const x = positionAdjacent(anchorRect.left, anchorRect.right, size.width, viewport.width, gap);\n const y = positionAligned(anchorRect.top, anchorRect.bottom, size.height, viewport.height, margin);\n\n return { x, y };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM, type Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { LayerManager } from \"~/core/LayerManager.js\";\nimport { positionAdjacent } from \"~/core/OverlayPosition.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { Animation } from \"~/core/Animation.js\";\nimport { Text } from \"~/component/input/Text.js\";\n\nconst TOOLTIP_ANIM_DURATION_MS: number = 100;\n\n/**\n * Optional color overrides for a tooltip attachment.\n *\n * When provided to {@link Tooltip.attach}, these replace the default theme variables\n * for the duration that the tooltip is shown for that component.\n *\n * @category Core\n */\nexport interface TooltipColors {\n /** CSS color for the tooltip background. */\n background?: string;\n /** CSS color for the tooltip text. */\n color?: string;\n /** CSS color for the tooltip border. */\n border?: string;\n}\n\n/** Internal record of a component's tooltip attachment. */\ninterface TooltipAttachment {\n text : string;\n colors : TooltipColors | undefined;\n mouseoverFn : (e: MouseEvent) => void;\n mousemoveFn : (e: MouseEvent) => void;\n mouseoutFn : () => void;\n mousedownFn : () => void;\n}\n\n/** Internal record of a raw-element tooltip attachment. */\ninterface ElementTooltipAttachment {\n text : string;\n mouseoverFn : (e: MouseEvent) => void;\n mousemoveFn : (e: MouseEvent) => void;\n mouseoutFn : () => void;\n mousedownFn : () => void;\n showTimer : ReturnType<typeof setTimeout> | null;\n lastX : number;\n lastY : number;\n}\n\n/**\n * A singleton floating tooltip that appears near the cursor after a short delay.\n *\n * Use `Tooltip.attach(component, text)` to wire hover listeners onto any component.\n * For manual control use `Tooltip.show(text, x, y)` and `Tooltip.hide()` directly.\n *\n * @example\n * ```typescript\n * Tooltip.attach(myButton, 'Save the document');\n * ```\n *\n * @category Core\n */\n// Intentional exception to the `callable()`-wrap rule: `Tooltip` has a private\n// constructor and is used only through its static methods (`show` / `attach` /\n// …), so there is no public `new Tooltip()` / `Tooltip(...)` surface to wrap.\n// The singleton is exported directly (see `overlay/index.ts`).\nexport class Tooltip extends Component {\n\n // In-flight fades, cancelled on teardown so their fallback timers cannot\n // fire against this tooltip's released element handle. Instance fields\n // written through the singleton `inst` in the static show / hide below.\n private _showAnimation: Animation.CancelHandle | null = null;\n private _hideAnimation: Animation.CancelHandle | null = null;\n\n private static instance: Tooltip | null = null;\n private static showTimer: ReturnType<typeof setTimeout> | null = null;\n private static attachments: Map<string, TooltipAttachment> = new Map();\n private static elementAttachments: Map<Handle, ElementTooltipAttachment> = new Map();\n private static activeElement: Handle | null = null;\n\n // True while the anchor-watch viewport listener is installed (only while a\n // tooltip is shown). See _onAnchorWatch.\n private static watching: boolean = false;\n\n /**\n * Hides the tooltip when its anchor element has left the DOM. The browser\n * fires no `mouseout` when an element is removed under a stationary pointer,\n * so a tooltip whose anchor vanishes would otherwise linger; this runs on\n * pointer movement while a tooltip is shown and dismisses it once the anchor\n * is no longer connected.\n */\n private static readonly _onAnchorWatch = (): void => {\n if (Tooltip.activeElement !== null && !DOM.source.isConnected(Tooltip.activeElement)) {\n Tooltip.hide();\n }\n };\n\n // Set true while a fade-out is in flight; reset to false when the fade-out\n // completes (so the deferred removeElement fires) or when a fresh `show()`\n // re-displays the tooltip mid-fade (the deferred removeElement is then\n // skipped because the tooltip is back on screen).\n private static dismissing: boolean = false;\n\n private static readonly H_PADDING: number = 16;\n private static readonly V_PADDING: number = 8;\n private static readonly MAX_WIDTH: number = 300;\n private static readonly ITEM_HEIGHT: number = 20;\n\n // Clearance kept free around the pointer *hotspot* (the arrow's tip, the\n // hand's fingertip) — the coordinate `show` is handed. The cursor glyph\n // hangs down and to the right of its hotspot, so this box is deliberately\n // NOT centred on it: nothing sits above the tip, and only a few px reach\n // left of it. A box symmetric about the hotspot would push a flipped\n // tooltip visibly further from the cursor than an unflipped one, since the\n // same gap spans glyph on one side and empty space on the other.\n //\n // These are clearance, not glyph metrics: DOWN is shorter than the ~24px\n // glyph on purpose, so an unflipped tooltip keeps sitting alongside the\n // pointer's lower half (where it tapers and hides nothing) rather than\n // being pushed clear of it. No API reports the live cursor's size or\n // hotspot, so the numbers approximate the platform defaults.\n private static readonly CURSOR_LEFT: number = 4;\n private static readonly CURSOR_RIGHT: number = 12;\n private static readonly CURSOR_UP: number = 0;\n private static readonly CURSOR_DOWN: number = 12;\n private static readonly CURSOR_GAP: number = 2;\n\n private _text: Text;\n\n // Number of `\\n`-separated lines in the text currently shown. Cached by\n // `show()` (which knows the raw string) and read by `doLayout()` (which\n // only sees the committed size) so the label's height tracks the line\n // count. 1 for a single-line tooltip — the unchanged legacy case.\n private _lineCount: number = 1;\n\n // Resolved per-line pixel height of the tooltip text, set by `show()` from\n // the live additive line box and read by `doLayout()`. Replaces the\n // fixed `ITEM_HEIGHT` multiplier, which over-allocated ~3px/line and left\n // a trailing empty line on multi-line tooltips.\n private _perLine: number = Tooltip.ITEM_HEIGHT;\n\n /** Private — use the static methods; only one instance is ever created. */\n private constructor() {\n super();\n\n this.setVisible(false);\n // Above every managed layer (incl. a modal Dialog and its backdrop) so a\n // hover tooltip is never occluded.\n this.setZIndex(LayerManager.Band.Tooltip);\n this.setBackgroundColor(\"var(--ts-ui-tooltip-bg, rgb(255, 255, 240))\");\n this.setForegroundColor(\"var(--ts-ui-tooltip-color, rgb(0, 0, 0))\");\n this.setBorder({ border: \"1px solid var(--ts-ui-tooltip-border, rgb(180, 180, 100))\" });\n this.setShadow(\"var(--ts-ui-tooltip-shadow, 1px 2px 4px rgba(0, 0, 0, 0.2))\");\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setPointerEvents(\"none\");\n // Top-level overlay, dynamic size — layout+paint containment scopes reflow without\n // committing to a fixed size.\n this.setContain(\"layout paint\");\n\n this._text = new Text();\n this._text.setPointerEvents(\"none\");\n // `pre-wrap` preserves explicit `\\n` breaks as real lines *and* wraps\n // a line that overflows the fixed width — `nowrap` would collapse the\n // breaks onto one line. A single-line tooltip narrower than MAX_WIDTH\n // renders identically to the old `nowrap` behaviour.\n this._text.setWhiteSpace(\"pre-wrap\");\n this.addComponent(this._text);\n }\n\n /**\n * Returns the single shared `Tooltip` instance, creating it on first call.\n *\n * @returns The singleton `Tooltip` component.\n */\n private static getInstance(): Tooltip {\n if (!Tooltip.instance) {\n Tooltip.instance = new Tooltip();\n }\n\n return Tooltip.instance;\n }\n\n /**\n * Immediately shows the tooltip with the given text near the specified coordinates.\n *\n * The tooltip sits just past the cursor on each axis — down and to the right —\n * flipping to sit just before it when there is no room past, so it never covers\n * the cursor it's hinting at. The clearance is measured from a small box around\n * the pointer hotspot rather than from the hotspot itself, so a flipped tooltip\n * hugs the cursor as closely as an unflipped one. A tooltip taller than roughly\n * half the viewport (an unusually long multi-line label) can still saturate\n * on-screen rather than flipping cleanly, since its height is uncapped and never\n * scrolls; this is a known limitation, not a bug to fix here.\n *\n * @param text - The string to display inside the tooltip.\n * @param x - Horizontal viewport coordinate for the tooltip origin.\n * @param y - Vertical viewport coordinate for the tooltip origin.\n */\n static show(text: string, x: number, y: number): void {\n if (Tooltip.showTimer !== null) {\n clearTimeout(Tooltip.showTimer);\n Tooltip.showTimer = null;\n }\n\n const inst = Tooltip.getInstance();\n\n // Whether the tooltip is already on screen (and not mid-dismiss). A\n // caller that shows on every mousemove — a chart hover — reaches `show`\n // dozens of times a second; replaying the entrance fade each time would\n // reset opacity to 0 before the 100 ms fade completes, flickering the\n // tooltip. When already shown we skip the fade and just reposition/retext.\n const alreadyShown = inst.isVisible() && !Tooltip.dismissing;\n\n inst._text.setText(text);\n\n // Size width to the widest line and height to the line count so a\n // `\\n`-containing string renders as a multi-line block. A string with\n // no `\\n` yields a single line and the original single-line sizing.\n const lines = text.split(\"\\n\");\n const widestLine = lines.reduce((max, line) => Math.max(max, Util.measureTextWidth(line)), 0);\n\n inst._perLine = inst._perLineHeight();\n\n // No minimum width — the tooltip hugs its content (widest line plus\n // horizontal padding), capped at MAX_WIDTH. A min-width floor would\n // pad short labels out to a fixed box wider than their text.\n const tooltipWidth = Math.min(Tooltip.MAX_WIDTH, widestLine + Tooltip.H_PADDING);\n\n // When the widest line is wider than the cap, the label soft-wraps at the\n // available text width, so the visual line count exceeds the `\\n`-split\n // count. Measure the wrapped height at that width and derive the real line\n // count; an uncapped tooltip never wraps, so the split count stands.\n if (widestLine + Tooltip.H_PADDING > Tooltip.MAX_WIDTH) {\n const availTextWidth = tooltipWidth - Tooltip.H_PADDING;\n const wrappedHeight = DOM.source.measureText(text, { maxWidth: availTextWidth }).height;\n inst._lineCount = Math.max(lines.length, Math.round(wrappedHeight / inst._perLine));\n } else {\n inst._lineCount = lines.length;\n }\n\n // Height hugs the text: one resolved line height per line plus padding.\n // Floor the single-line case to the legacy ITEM_HEIGHT box so plain\n // one-line tooltips stay pixel-stable; only multi-line tooltips switch\n // to the tighter per-line height that removes the trailing gap.\n let tooltipHeight = inst._lineCount * inst._perLine + Tooltip.V_PADDING;\n if (inst._lineCount === 1) {\n tooltipHeight = Math.max(tooltipHeight, Tooltip.ITEM_HEIGHT + Tooltip.V_PADDING);\n }\n\n inst.setWidth(tooltipWidth);\n inst.setHeight(tooltipHeight);\n\n const vp = DOM.source.getViewportSize();\n\n // Clamp the cursor into the viewport first: the flip primitive only guarantees\n // an on-screen result for an in-viewport anchor, and show() is public.\n const cx = Util.clamp(x, 0, vp.width);\n const cy = Util.clamp(y, 0, vp.height);\n\n // Sit past the cursor's clearance box, flipping to sit *before* it when there\n // is no room past — so the tooltip can never land under the cursor. The box\n // (not the bare hotspot) is the anchor rect, which is what lets the two sides\n // differ: the primitive applies one `gap` to whichever edge it picks, and the\n // asymmetry the cursor actually has lives in the edges. No viewport margin,\n // since the tooltip may sit flush against an edge.\n inst.setX(positionAdjacent(\n cx - Tooltip.CURSOR_LEFT, cx + Tooltip.CURSOR_RIGHT, tooltipWidth, vp.width, Tooltip.CURSOR_GAP,\n ));\n inst.setY(positionAdjacent(\n cy - Tooltip.CURSOR_UP, cy + Tooltip.CURSOR_DOWN, tooltipHeight, vp.height, Tooltip.CURSOR_GAP,\n ));\n\n const el = inst.getElement(true)!;\n\n inst.scheduleLayout();\n\n LayerManager.mount(el);\n\n inst.setVisible(true);\n\n // Watch for the anchor leaving the DOM while the tooltip is up (a removed\n // element fires no mouseout), so an orphaned tooltip dismisses on the next\n // pointer move instead of lingering.\n if (!Tooltip.watching) {\n Event.addViewportListener(inst, \"mousemove\", Tooltip._onAnchorWatch);\n Tooltip.watching = true;\n }\n\n // Cancel a pending fade-out's removeElement so a fresh show during the\n // outgoing transition keeps the element in the DOM.\n Tooltip.dismissing = false;\n\n if (alreadyShown) {\n // Already visible — hold at full opacity; the position/size/text\n // updates above are all this repeat show needs. No entrance fade.\n DOM.sink.apply(el, { style: { opacity: \"1\" } });\n\n return;\n }\n\n inst._showAnimation?.cancel();\n inst._showAnimation = Animation.play(el, {\n from: { opacity: \"0\" },\n to: { opacity: \"1\" },\n durationMs: TOOLTIP_ANIM_DURATION_MS,\n properties: [\"opacity\"],\n });\n }\n\n /**\n * Hides and detaches the tooltip.\n *\n * Any pending show timer is also cancelled.\n */\n static hide(): void {\n if (Tooltip.showTimer !== null) {\n clearTimeout(Tooltip.showTimer);\n Tooltip.showTimer = null;\n }\n\n const inst = Tooltip.getInstance();\n const el = inst.getElement();\n\n // The tooltip is going away; stop watching the (now-irrelevant) anchor\n // and forget it.\n if (Tooltip.watching) {\n Event.removeViewportListener(inst, \"mousemove\", Tooltip._onAnchorWatch);\n Tooltip.watching = false;\n }\n Tooltip.activeElement = null;\n\n if (!el) {\n return;\n }\n\n Tooltip.dismissing = true;\n\n inst._hideAnimation?.cancel();\n inst._hideAnimation = Animation.play(el, {\n to: { opacity: \"0\" },\n durationMs: TOOLTIP_ANIM_DURATION_MS,\n properties: [\"opacity\"],\n onComplete: () => {\n if (!Tooltip.dismissing) {\n return;\n }\n Tooltip.dismissing = false;\n inst.setVisible(false);\n inst.removeElement();\n },\n });\n }\n\n /**\n * Wires `mouseover` and `mouseout` listeners onto `component` so the tooltip\n * shows automatically after a 500 ms hover delay.\n *\n * @remarks Uses `mouseover`/`mouseout` rather than `mouseenter`/`mouseleave`\n * because the Event system routes through a window-level capture handler, and\n * the non-bubbling enter/leave events are not reliably seen there in Chrome.\n *\n * If `colors` is provided the tooltip uses those colors instead of the default\n * theme variables while it is showing for this component.\n *\n * Calling `attach` on a component that already has an attachment replaces it.\n *\n * @param component - The component to attach hover behaviour to.\n * @param text - The tooltip text to display.\n * @param colors - Optional color overrides applied while this tooltip is visible.\n */\n static attach(component: Component, text: string, colors?: TooltipColors): void {\n Tooltip.detach(component);\n\n let cursorX = 0;\n let cursorY = 0;\n\n const mouseoverFn = (e: MouseEvent) => {\n if (Tooltip.showTimer !== null) {\n return;\n }\n\n cursorX = e.clientX;\n cursorY = e.clientY;\n\n Tooltip.showTimer = setTimeout(() => {\n Tooltip._applyColors(colors);\n // Record the anchor so the anchor-watch dismisses the tooltip if\n // this component is later removed from the DOM.\n Tooltip.activeElement = component.getElement() ?? null;\n Tooltip.show(text, cursorX, cursorY);\n Tooltip.showTimer = null;\n }, 500);\n };\n\n const mousemoveFn = (e: MouseEvent) => {\n cursorX = e.clientX;\n cursorY = e.clientY;\n };\n\n const mouseoutFn = () => {\n Tooltip.hide();\n };\n\n // Acting on the component (a click, or the start of a drag) dismisses\n // its tooltip: once the pointer is pressed the hover hint has served its\n // purpose and would only obscure the result of the action. `hide` also\n // cancels a still-pending show timer, so a press during the hover delay\n // suppresses the tooltip entirely. It stays hidden until the pointer\n // leaves and re-enters, since no fresh `mouseover` fires under a\n // stationary cursor.\n const mousedownFn = () => {\n Tooltip.hide();\n };\n\n Event.addListener(component, \"mouseover\", mouseoverFn);\n Event.addListener(component, \"mousemove\", mousemoveFn);\n Event.addListener(component, \"mouseout\", mouseoutFn);\n Event.addListener(component, \"mousedown\", mousedownFn);\n\n Tooltip.attachments.set(component.getId(), {\n text, colors, mouseoverFn, mousemoveFn, mouseoutFn, mousedownFn,\n });\n }\n\n /**\n * Removes the tooltip attachment from a component, cancelling any pending show\n * and hiding the tooltip if it is currently visible for this component.\n *\n * @param component - The component whose attachment should be removed.\n */\n static detach(component: Component): void {\n const id = component.getId();\n const att = Tooltip.attachments.get(id);\n\n if (!att) {\n return;\n }\n\n Event.removeListener(component, \"mouseover\", att.mouseoverFn);\n Event.removeListener(component, \"mousemove\", att.mousemoveFn);\n Event.removeListener(component, \"mouseout\", att.mouseoutFn);\n Event.removeListener(component, \"mousedown\", att.mousedownFn);\n\n Tooltip.attachments.delete(id);\n Tooltip.hide();\n }\n\n /**\n * Applies optional color overrides to the singleton tooltip instance before it is shown.\n * Resets to the default theme variables when no overrides are provided.\n *\n * @param colors - The color overrides to apply, or `undefined` to use defaults.\n */\n private static _applyColors(colors?: TooltipColors): void {\n const inst = Tooltip.getInstance();\n\n inst.setBackgroundColor(\n colors?.background ?? 'var(--ts-ui-tooltip-bg, rgb(255, 255, 240))'\n );\n inst.setForegroundColor(\n colors?.color ?? 'var(--ts-ui-tooltip-color, rgb(0, 0, 0))'\n );\n inst.setBorder({ border: `1px solid ${colors?.border ?? 'var(--ts-ui-tooltip-border, rgb(180, 180, 100))'}` });\n }\n\n /**\n * Wires native `mouseover`, `mousemove`, and `mouseout` listeners directly onto\n * a raw DOM element so the tooltip shows after a 500 ms hover delay.\n *\n * @remarks Use this instead of {@link attach} when the element contains child nodes\n * that would receive the event as `evnt.target` — the Event system's component\n * listener only matches the exact target id, so `attach` would miss those cases.\n *\n * Calling `attachToElement` on an element that already has an attachment\n * replaces it — the previous listeners are removed and a fresh set is\n * installed against the new text. If the tooltip is currently visible for\n * this element when the call happens, it re-renders immediately at the\n * last known cursor position so the visible text updates without a\n * re-hover.\n *\n * @param element - The element handle to attach hover behaviour to.\n * @param text - The tooltip text to display.\n */\n static attachToElement(element: Handle, text: string): void {\n const previous = Tooltip.elementAttachments.get(element);\n const wasActive = Tooltip.activeElement === element;\n\n // Carry the previous binding's last cursor coords so the mid-hover\n // repaint below has real values instead of (0, 0) — detachElement\n // deletes the WeakMap entry, so the read has to happen first.\n const carryX = previous ? previous.lastX : 0;\n const carryY = previous ? previous.lastY : 0;\n\n Tooltip.detachElement(element);\n\n const att: ElementTooltipAttachment = {\n text,\n showTimer : null,\n lastX : carryX,\n lastY : carryY,\n mouseoverFn: function onTooltipMouseOver(e: MouseEvent): void {\n if (att.showTimer !== null) {\n return;\n }\n\n att.lastX = e.clientX;\n att.lastY = e.clientY;\n\n att.showTimer = setTimeout(function onTooltipShowTimer(): void {\n Tooltip.activeElement = element;\n Tooltip.show(att.text, att.lastX, att.lastY);\n att.showTimer = null;\n }, 500);\n },\n mousemoveFn: function onTooltipMouseMove(e: MouseEvent): void {\n att.lastX = e.clientX;\n att.lastY = e.clientY;\n },\n mouseoutFn: function onTooltipMouseOut(): void {\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n if (Tooltip.activeElement === element) {\n Tooltip.activeElement = null;\n }\n\n Tooltip.hide();\n },\n // Pressing the element (click or drag start) dismisses its tooltip\n // and cancels any pending show — see the `attach` counterpart.\n mousedownFn: function onTooltipMouseDown(): void {\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n Tooltip.hide();\n },\n };\n\n DOM.sink.addListener(element, \"mouseover\", att.mouseoverFn);\n DOM.sink.addListener(element, \"mousemove\", att.mousemoveFn);\n DOM.sink.addListener(element, \"mouseout\", att.mouseoutFn);\n DOM.sink.addListener(element, \"mousedown\", att.mousedownFn);\n\n Tooltip.elementAttachments.set(element, att);\n\n // Mid-hover update: if the previous attachment was the active tooltip\n // target, repaint the visible tooltip with the new text at the last\n // known cursor coords so the swap is seen without a re-hover.\n if (wasActive) {\n Tooltip.activeElement = element;\n Tooltip.show(text, att.lastX, att.lastY);\n }\n }\n\n /**\n * Removes an `attachToElement` binding installed on an element handle.\n *\n * Detaches the three hover listeners, cancels any pending show timer, and\n * clears the active-element tracker if it pointed at this element.\n * Idempotent — calling on an unattached element is a no-op.\n *\n * @param element - The element handle whose attachment should be removed.\n */\n static detachElement(element: Handle): void {\n const att = Tooltip.elementAttachments.get(element);\n\n if (!att) {\n return;\n }\n\n DOM.sink.removeListener(element, \"mouseover\", att.mouseoverFn);\n DOM.sink.removeListener(element, \"mousemove\", att.mousemoveFn);\n DOM.sink.removeListener(element, \"mouseout\", att.mouseoutFn);\n DOM.sink.removeListener(element, \"mousedown\", att.mousedownFn);\n\n if (att.showTimer !== null) {\n clearTimeout(att.showTimer);\n att.showTimer = null;\n }\n\n Tooltip.elementAttachments.delete(element);\n\n if (Tooltip.activeElement === element) {\n Tooltip.activeElement = null;\n }\n }\n\n /**\n * Resolves the tooltip text's rendered per-line height in pixels from the\n * live additive line box, measured at hover time. Matches the `_text` label\n * (a default `Text`: 14px font, theme leading) so the box hugs the text\n * vertically with no trailing gap, and tracks theme/font changes on the next\n * hover.\n *\n * @returns The per-line height in pixels, ceiled to a whole pixel.\n *\n * @remarks `measureTextMetrics` already defaults `lineHeight` to\n * `calc(1em + var(--ts-ui-line-padding, 2px))`, so the line box is left to\n * that default rather than passed explicitly.\n */\n private _perLineHeight(): number {\n const { height } = DOM.source.measureText(\"X\");\n\n return Math.ceil(height);\n }\n\n /**\n * Positions the label to fill the tooltip body with uniform padding.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n this._text.setX(Tooltip.H_PADDING / 2);\n this._text.setY(Tooltip.V_PADDING / 2);\n this._text.setWidth(Math.max(0, this.getWidth() - Tooltip.H_PADDING));\n this._text.setHeight(this._lineCount * this._perLine);\n\n return this;\n }\n\n /**\n * Cancels any in-flight fade, then defers to the base class. Cancelling\n * first keeps their fallback timers from firing after `super.destructor()`\n * has released this tooltip's element handle.\n */\n protected destructor(): void {\n this._showAnimation?.cancel();\n this._showAnimation = null;\n this._hideAnimation?.cancel();\n this._hideAnimation = null;\n\n super.destructor();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{a as e,o as t}from"./Position-DRSlWn4Z.js";import{d as n,f as r,m as i,p as a}from"./Component-CHTzJxEf.js";var BoxLayout=class extends n{_spacing=5;_stretching=!1;_mode=`preferred`;_overflowSizing=`preferred`;_justify=`start`;constructor(e){super(),e&&this.applyOptions(e)}applyOptions(e){super.applyOptions(e),e.mode!==void 0&&this.setMode(e.mode),e.spacing!==void 0&&this.setComponentSpacing(e.spacing),e.stretching!==void 0&&this.setStretching(e.stretching),e.overflowSizing!==void 0&&this.setOverflowSizing(e.overflowSizing),e.justify!==void 0&&this.setJustify(e.justify)}getComponentSpacing(){return this._spacing||0}setComponentSpacing(e){return this._spacing=e||0,this}isStretching(){return this._stretching||!1}setStretching(e){return this._stretching=!!e,this}getMode(){return this._mode}setMode(e){return this._mode=e,this}getOverflowSizing(){return this._overflowSizing}setOverflowSizing(e){return this._overflowSizing=e,this}getJustify(){return this._justify}setJustify(e){return this._justify=e,this}aggregateMaxSize(n){let r=this.getContainer();if(!r)return null;let i=r.getPerimeterSize(),a=r.getLaidOutComponents(),o=n?i.left+i.right:i.top+i.bottom,s=n?i.top+i.bottom:i.left+i.right,c=o,l=0,u=!1,d=!1,f=0;for(let e of a){let r=e.getMaxSize();if(!r){u=!0,d=!0;continue}let i=n?r.width:r.height,a=n?r.height:r.width;t(i)?u=!0:this._mode===`equal`?f=Math.max(f,i):c+=i,t(a)?d=!0:l=Math.max(l,a)}this._mode===`equal`?c+=a.length*f+this._spacing*Math.max(0,a.length-1):c+=this._spacing*Math.max(0,a.length-1),l+=s;let p=u?e:c,m=d?e:l;return{width:n?p:m,height:n?m:p}}computeShrink(e,t,n,r){if(e<=n||r)return{remaining:Math.max(0,n-e),shrinkRatio:0};let i=e-n,a=e-t;return{remaining:0,shrinkRatio:a>0?Math.min(1,i/a):1}}justifyOffsets(e,t,n){let r=Math.max(0,t-e);return r===0||this._justify===`start`?{lead:0,gap:0}:this._justify===`center`?{lead:r/2,gap:0}:this._justify===`end`?{lead:r,gap:0}:this._justify===`between`?{lead:0,gap:n>1?r/(n-1):0}:{lead:n>0?r/(2*n):0,gap:n>0?r/n:0}}crossPlacement(e,t,n,r,i){let o=this.getLayoutConstraints(e),s=o?.fill??null,c=o?.anchor??null;if(i?s===a.VERTICAL||s===a.BOTH:s===a.HORIZONTAL||s===a.BOTH)return{offset:t,extent:n};let l=this.crossAnchorEdge(c,i);if(l===null)return null;let u=Math.min(r,n);return{offset:l===`lead`?t:t+(n-u),extent:u}}crossAnchorEdge(e,t){return e===null?null:t?e===r.NORTH||e===r.NORTHWEST||e===r.NORTHEAST?`lead`:e===r.SOUTH||e===r.SOUTHWEST||e===r.SOUTHEAST?`trail`:null:e===r.WEST||e===r.NORTHWEST||e===r.SOUTHWEST?`lead`:e===r.EAST||e===r.NORTHEAST||e===r.SOUTHEAST?`trail`:null}},VBox=class extends BoxLayout{_defaultComponentHeight=100;getContentBaseline(){let e=this.getContainer();if(!e)return null;let t=e.getLaidOutComponents();return t.length===0?null:t[0].getBaseline()}getPreferredSize(){let n=this.getContainer();if(!n)return null;let r=n.getPerimeterSize(),i=n.getLaidOutComponents();if(this._mode===`equal`){let e=0,t=0;for(let n in i){let r=i[n].getPreferredSize();r&&(e=Math.max(e,r.width),t=Math.max(t,r.height))}return{width:e+r.left+r.right,height:i.length*(t+this._spacing)-this._spacing+r.top+r.bottom}}let a=e,o=r.top+r.bottom;for(let e in i){let n=i[e].getPreferredSize();n&&(a=t(a)?Math.min(a,n.width):Math.max(a,n.width),o+=n.height)}return a+=r.left+r.right,o+=this._spacing*(i.length-1),{width:a,height:o}}getMinSize(){let e=this.getContainer();if(!e)return null;let t=e.getPerimeterSize(),n=e.getLaidOutComponents();if(this._mode===`equal`){let e=0,r=0;for(let t in n){let i=n[t].getMinSize();i&&(e=Math.max(e,i.width),r=Math.max(r,i.height))}return{width:e+t.left+t.right,height:n.length*(r+this._spacing)-this._spacing+t.top+t.bottom}}let r=0,i=t.top+t.bottom;for(let e in n){let t=n[e].getMinSize();t&&(r=Math.max(r,t.width),i+=t.height)}return r+=t.left+t.right,i+=this._spacing*(n.length-1),{width:r,height:i}}getMaxSize(){return this.aggregateMaxSize(!1)}computeTotalMinSize(){let e=this.getContainer();if(!e)return{width:0,height:0};let t=e.getLaidOutComponents();if(t.length===0)return{width:0,height:0};let n=0;if(this._mode===`equal`){let e=0;for(let r of t){let t=r.getMinSize();t&&(n=Math.max(n,t.width),e=Math.max(e,t.height))}return{width:n,height:t.length*(e+this._spacing)-this._spacing}}let r=this._spacing*(t.length-1);for(let e of t){let t=e.getMinSize();t&&(n=Math.max(n,t.width),r+=t.height)}return{width:n,height:r}}doLayout(){let e=this.getContainer();if(!e)return;let t=e.getInnerSize();if(!t)return;let n=e.getLaidOutComponents(),r=e.getContentInsets(),i=this.getComponentSpacing(),a=this.inflateForOverflow(t);this._mode===`equal`?this.layoutEqualMode(n,t,a,r,i):this.layoutPreferredMode(n,a,r,i),this.reserveContentFrame()}layoutEqualMode(e,t,n,r,i){let o=this.computeEqualCellHeight(e,t.height,i),s=r.getLeft(),c=r.getTop();if(this.isStretching()){let t=n.width;for(let n of e)this.placeComponent(n,s,c,t,o,a.BOTH),c+=o+i;return}let l=r.getLeft(),u=n.width;for(let t of e){let e=t.getPreferredSize(),n=e?e.width:0,r=this.crossPlacement(t,l,u,n,!1);r?this.placeComponent(t,r.offset,c,r.extent,o,a.BOTH):this.placeComponent(t,s,c,n,o,a.BOTH),c+=o+i}}computeEqualCellHeight(e,t,n){let r=0,i=0;for(let t of e){let e=t.getMinSize();e&&(r=Math.max(r,e.height));let n=t.getPreferredSize();n&&(i=Math.max(i,n.height))}let a=(t-n*(e.length-1))/e.length;return a>=r?a:this.isOverflowingY()&&this._overflowSizing===`preferred`?Math.max(r,i):r}layoutPreferredMode(e,t,n,r){let{totalWeight:i,fixedPreferred:o,fixedMin:s}=this.measureFixedHeights(e,r),{remaining:c,shrinkRatio:l}=this.computeShrink(o,s,t.height,this.isOverflowingY()),u=[];for(let t of e){let e=this.getLayoutConstraints(t)?.weight??0,n=t.getPreferredSize(),r=t.getMinSize(),a=t.getMaxSize();u.push(this.resolveChildHeight(n,r,a,e,i,c,l))}let d=0,f=0;if(i===0){let i=r*(e.length-1);for(let e of u)i+=e;let a=t.height-(n.getTop()+n.getBottom());({lead:d,gap:f}=this.justifyOffsets(i,a,e.length))}let p=n.getLeft(),m=t.width-(n.getLeft()+n.getRight()),h=n.getLeft(),g=n.getTop()+d;for(let n=0;n<e.length;n+=1){let i=e[n],o=i.getPreferredSize(),s=i.getMaxSize(),c;c=!o||this.isStretching()?t.width:Math.min(o.width,t.width),s&&(c=Math.min(c,s.width));let l=o?Math.min(o.width,m):m;s&&(l=Math.min(l,s.width));let d=this.crossPlacement(i,p,m,l,!1);d?this.placeComponent(i,d.offset,g,d.extent,u[n],a.BOTH):this.placeComponent(i,h,g,c,u[n],a.BOTH),g+=u[n],g+=r+f}}measureFixedHeights(e,t){let n=0,r=t*(e.length-1),i=t*(e.length-1);for(let t of e){let e=this.getLayoutConstraints(t)?.weight??0;if(e>0)n+=e;else{let e=t.getPreferredSize(),n=t.getMinSize();r+=this.preferredChildHeight(e,n),i+=n?n.height:0}}return{totalWeight:n,fixedPreferred:r,fixedMin:i}}preferredChildHeight(e,t){let n=(e?e.height:void 0)??(t&&t.height>0?t.height:void 0)??this._defaultComponentHeight;return t?Math.max(n,t.height):n}resolveChildHeight(e,t,n,r,i,a,o){let s;if(r>0&&i>0)s=r/i*a;else{let n=this.preferredChildHeight(e,t);s=n-o*(n-(t?t.height:0))}return t&&(s=Math.max(s,t.height)),n&&(s=Math.min(s,n.height)),s}},o=i(VBox);export{o as n,BoxLayout as r,VBox as t};
2
+ //# sourceMappingURL=VBox-Dr7Ge9M6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VBox-Dr7Ge9M6.js","names":[],"sources":["../../src/typescript/lib/layout/BoxLayout.ts","../../src/typescript/lib/layout/VBox.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutManager, LayoutManagerOptions } from \"~/layout/LayoutManager.js\";\nimport { FillType } from \"~/layout/FillType.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport type { AxisPosition, AxisSpread } from \"~/primitive/Axis.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Size, UNBOUNDED, isUnbounded } from \"~/primitive/Size.js\";\n\n/**\n * Sizing strategy along an {@link HBox} or {@link VBox}'s main axis.\n *\n * - `\"preferred\"` — each child gets its preferred width (height for VBox);\n * `weight`-constrained children split the remaining space; an overflow\n * shrinks non-weighted children proportionally toward their min sizes.\n * - `\"equal\"` — children split the container's inner extent equally; the\n * per-cell floor is `max(child.minSize.width)` (height for VBox);\n * `weight` constraints are ignored.\n *\n * @category Layouts\n */\nexport type BoxMode = \"preferred\" | \"equal\";\n\n/**\n * How an `\"equal\"`-mode {@link HBox}/{@link VBox} sizes its cells when the\n * row/column overflows the host's inner extent and the host has opted into\n * scrolling (`Panel.setAutoScroll`).\n *\n * - `\"preferred\"` (the default) — every cell takes the widest/tallest child's\n * preferred extent, so cells keep their preferred size and the host scrolls.\n * - `\"min\"` — every cell stays at the min floor (`max(child.minSize)`), so the\n * row/column scrolls at the minimum cell size instead of growing.\n *\n * Has no effect outside `\"equal\"` mode, nor when the cells fit (the equal share\n * clears the min floor), nor when the host does not scroll (cells always clamp\n * to the min floor and the host's `overflow: hidden` clips the surplus).\n *\n * @category Layouts\n */\nexport type BoxOverflowSizing = \"preferred\" | \"min\";\n\n/**\n * How a `\"preferred\"`-mode {@link HBox}/{@link VBox} distributes the leftover\n * main-axis space when its children are shorter than the inner main extent and\n * no `weight` cells consume the slack. Mirrors CSS `justify-content`.\n *\n * A single line makes exactly one main-axis slack decision, so the two shared\n * axis concepts fuse into this one field: the {@link AxisPosition} half\n * (`\"start\"`/`\"center\"`/`\"end\"`) positions the whole child block as a unit,\n * while the {@link AxisSpread} half (`\"between\"`/`\"around\"`) spreads the slack\n * into the inter-child gaps. `justifyOffsets` reflects the split — position\n * values yield a leading offset (`lead`, `gap: 0`) and spread values yield an\n * inter-child `gap`. `\"start\"` is the shared identity of both halves, so the\n * union collapses to these five distinct values.\n *\n * - `\"start\"` (the default) — children pack at the leading edge; the slack sits\n * at the trailing edge.\n * - `\"center\"` — the child block is centred; equal slack on both ends.\n * - `\"end\"` — children pack at the trailing edge; the slack sits at the leading\n * edge.\n * - `\"between\"` — the first child sits at the leading edge, the last at the\n * trailing edge, and the slack is split evenly into the gaps between them.\n * - `\"around\"` — every child gets equal space around it: a half-unit before the\n * first and after the last, a full unit between neighbours.\n *\n * Has no effect outside `\"preferred\"` mode, nor when `weight` cells absorb the\n * slack, nor when the content fills or overflows the inner extent (the row\n * clamps to `\"start\"` so the leading child is never pushed out of view).\n *\n * @category Layouts\n */\nexport type BoxJustify = AxisPosition | AxisSpread;\n\n/**\n * Construction-time options shared by {@link HBox} and {@link VBox}.\n *\n * @remarks `mode` selects the sizing strategy along the box's main axis.\n * `\"preferred\"` (the default) honours each child's preferred main-axis extent\n * and supports `weight` cells. `\"equal\"` divides the container's main axis\n * equally and ignores `weight`. `mode` is independent of `stretching`, which\n * governs the cross axis: `stretching` defaults to `false` in both modes, so\n * `\"equal\"` divides the main axis but leaves children at their preferred\n * cross-axis extent unless `stretching: true` is passed.\n *\n * @category Layouts\n */\nexport interface BoxLayoutOptions extends LayoutManagerOptions {\n spacing?: number;\n stretching?: boolean;\n mode?: BoxMode;\n overflowSizing?: BoxOverflowSizing;\n justify?: BoxJustify;\n}\n\n/**\n * Abstract base for the single-axis box layouts {@link HBox} and {@link VBox}.\n * Holds the axis-agnostic configuration plumbing — spacing, stretching, sizing\n * mode, and overflow sizing — and dispatches a {@link BoxLayoutOptions} bag.\n * The geometric algorithms (`getPreferredSize`, `getMinSize`, `getMaxSize`,\n * `computeTotalMinSize`, `doLayout`) are mirror-image per axis and stay\n * concrete on each subclass.\n *\n * @category Layouts\n */\nexport abstract class BoxLayout extends LayoutManager {\n\n // `protected`, not `private`: the subclasses' geometric methods\n // (getPreferredSize/getMinSize/getMaxSize/computeTotalMinSize/doLayout)\n // read these fields directly, so they must be visible to HBox/VBox.\n protected _spacing: number = 5;\n protected _stretching: boolean = false;\n protected _mode: BoxMode = \"preferred\";\n protected _overflowSizing: BoxOverflowSizing = \"preferred\";\n protected _justify: BoxJustify = \"start\";\n\n constructor(options?: BoxLayoutOptions) {\n // LayoutManager's constructor takes no options; applied via applyOptions below.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n if (options) {\n this.applyOptions(options);\n }\n }\n\n /**\n * Applies a {@link BoxLayoutOptions} bag, dispatching mode, spacing, and\n * stretching after the inherited LayoutManager defaults.\n *\n * @param options - The options bag carrying the values to apply.\n *\n * @remarks `mode` and `stretching` are independent axes: `mode` governs the\n * main-axis sizing strategy, `stretching` governs whether children fill the\n * cross axis. Neither defaults from the other — `stretching` is `false`\n * unless the options bag passes it explicitly, in both `\"preferred\"` and\n * `\"equal\"` mode.\n */\n protected applyOptions(options: BoxLayoutOptions): void {\n super.applyOptions(options);\n\n if (options.mode !== undefined) {\n this.setMode(options.mode);\n }\n\n if (options.spacing !== undefined) {\n this.setComponentSpacing(options.spacing);\n }\n\n if (options.stretching !== undefined) {\n this.setStretching(options.stretching);\n }\n\n if (options.overflowSizing !== undefined) {\n this.setOverflowSizing(options.overflowSizing);\n }\n\n if (options.justify !== undefined) {\n this.setJustify(options.justify);\n }\n }\n\n /**\n * Returns the pixel spacing between child components.\n *\n * @returns The current spacing in pixels.\n */\n getComponentSpacing(): number {\n return this._spacing || 0;\n }\n\n /**\n * Sets the pixel spacing between child components.\n *\n * @param spacing - Spacing in pixels.\n *\n * @returns This layout manager, for method chaining.\n */\n setComponentSpacing(spacing: number): this {\n this._spacing = spacing || 0;\n\n return this;\n }\n\n /**\n * Returns whether children stretch to fill the container's cross axis.\n *\n * @returns `true` if stretching is enabled.\n */\n isStretching(): boolean {\n return this._stretching || false;\n }\n\n /**\n * Sets whether children stretch to fill the container's cross axis.\n *\n * @param stretching - Pass `true` to enable cross-axis stretching.\n *\n * @returns This layout manager, for method chaining.\n */\n setStretching(stretching: boolean): this {\n this._stretching = !!stretching;\n\n return this;\n }\n\n /**\n * Returns the current sizing mode along the main axis.\n *\n * @returns Either `\"preferred\"` or `\"equal\"`.\n */\n getMode(): BoxMode {\n return this._mode;\n }\n\n /**\n * Sets the sizing mode along the main axis.\n *\n * @param mode - `\"preferred\"` honours each child's preferred main-axis\n * extent; `\"equal\"` divides the container's main axis equally among\n * children.\n *\n * @returns This layout manager, for method chaining.\n */\n setMode(mode: BoxMode): this {\n this._mode = mode;\n\n return this;\n }\n\n /**\n * Returns the cell-sizing strategy used when an `\"equal\"`-mode row/column\n * overflows a scrolling host.\n *\n * @returns Either `\"preferred\"` or `\"min\"`.\n */\n getOverflowSizing(): BoxOverflowSizing {\n return this._overflowSizing;\n }\n\n /**\n * Sets the cell-sizing strategy used when an `\"equal\"`-mode row/column\n * overflows a scrolling host.\n *\n * @param overflowSizing - `\"preferred\"` grows every cell to the largest\n * child's preferred extent and scrolls; `\"min\"` keeps cells at the min\n * floor and scrolls at the minimum cell size. See {@link BoxOverflowSizing}.\n *\n * @returns This layout manager, for method chaining.\n */\n setOverflowSizing(overflowSizing: BoxOverflowSizing): this {\n this._overflowSizing = overflowSizing;\n\n return this;\n }\n\n /**\n * Returns how leftover main-axis space is distributed in `\"preferred\"` mode.\n *\n * @returns The current {@link BoxJustify} mode.\n */\n getJustify(): BoxJustify {\n return this._justify;\n }\n\n /**\n * Sets how leftover main-axis space is distributed in `\"preferred\"` mode.\n *\n * @param justify - `\"start\"` packs children at the leading edge (the\n * default), `\"center\"`/`\"end\"` shift the block, and `\"between\"`/`\"around\"`\n * spread the slack into the inter-child gaps. See {@link BoxJustify}.\n *\n * @returns This layout manager, for method chaining.\n */\n setJustify(justify: BoxJustify): this {\n this._justify = justify;\n\n return this;\n }\n\n /**\n * Aggregates the children's maximum sizes per the box contract: main axis =\n * sum of child maxima (+ spacing; in `\"equal\"` mode count * widest-child-max),\n * cross axis = max of child maxima, a null or unbounded child max making that\n * axis unbounded. Saturated to {@link UNBOUNDED}. Includes the container\n * perimeter.\n *\n * @param horizontal - `true` for {@link HBox} (main = width), `false` for\n * {@link VBox} (main = height).\n * @returns The aggregated maximum `{width, height}`, or `null` if no\n * container is attached.\n */\n protected aggregateMaxSize(horizontal: boolean): Size | null {\n const container = this.getContainer();\n if (!container) {\n return null;\n }\n\n const perimeterSize = container.getPerimeterSize();\n const components = container.getLaidOutComponents();\n\n const mainStart = horizontal ? perimeterSize.left + perimeterSize.right : perimeterSize.top + perimeterSize.bottom;\n const crossExtra = horizontal ? perimeterSize.top + perimeterSize.bottom : perimeterSize.left + perimeterSize.right;\n\n let main = mainStart;\n let cross = 0;\n let mainUnbounded = false;\n let crossUnbounded = false;\n let maxChildMain = 0;\n\n for (const component of components) {\n const size = component.getMaxSize();\n\n if (!size) {\n mainUnbounded = true;\n crossUnbounded = true;\n continue;\n }\n\n const mainExtent = horizontal ? size.width : size.height;\n const crossExtent = horizontal ? size.height : size.width;\n\n if (isUnbounded(mainExtent)) {\n mainUnbounded = true;\n } else if (this._mode === \"equal\") {\n maxChildMain = Math.max(maxChildMain, mainExtent);\n } else {\n main += mainExtent;\n }\n\n if (isUnbounded(crossExtent)) {\n crossUnbounded = true;\n } else {\n cross = Math.max(cross, crossExtent);\n }\n }\n\n if (this._mode === \"equal\") {\n main += components.length * maxChildMain + this._spacing * Math.max(0, components.length - 1);\n } else {\n main += this._spacing * Math.max(0, components.length - 1);\n }\n\n cross += crossExtra;\n\n const mainValue = mainUnbounded ? UNBOUNDED : main;\n const crossValue = crossUnbounded ? UNBOUNDED : cross;\n\n return {\n width: horizontal ? mainValue : crossValue,\n height: horizontal ? crossValue : mainValue\n };\n }\n\n /**\n * Resolves how much main-axis space the weight cells share and how hard the\n * non-weighted children must shrink. When the non-weighted children's\n * preferred extents fit (or the host scrolls on this axis), nothing shrinks\n * and the leftover is the weight remainder. Otherwise the children shrink\n * proportionally toward their min extents so the last child's far edge lands\n * inside the container.\n *\n * @param fixedPreferred - Summed preferred main-axis extent of the\n * non-weighted children, including inter-child spacing.\n * @param fixedMin - Summed minimum main-axis extent of the same children,\n * including spacing.\n * @param available - The working container's inner extent on the main axis.\n * @param overflowing - Whether the host has opted into scrolling on this\n * axis; when `true` the shrink is skipped so the host's CSS overflow\n * engages instead.\n * @returns `remaining` — the main-axis space left for weight cells — and\n * `shrinkRatio` — `0` (no shrink) through `1` (shrink fully to min).\n */\n protected computeShrink(fixedPreferred: number, fixedMin: number, available: number, overflowing: boolean): { remaining: number; shrinkRatio: number } {\n if (fixedPreferred <= available || overflowing) {\n return { remaining: Math.max(0, available - fixedPreferred), shrinkRatio: 0 };\n }\n\n const excess = fixedPreferred - available;\n const shrinkable = fixedPreferred - fixedMin;\n\n return { remaining: 0, shrinkRatio: shrinkable > 0 ? Math.min(1, excess / shrinkable) : 1 };\n }\n\n /**\n * Distributes the trailing main-axis slack per {@link BoxJustify}, returning\n * a leading offset and an extra inter-child gap. The subclass adds `lead` to\n * the placement cursor's start and `gap` to the inter-child spacing. A row\n * that fills or overflows the inner extent (`residual === 0`) and the\n * `\"start\"` fast path both return `{ lead: 0, gap: 0 }`, leaving placement\n * byte-identical to the un-justified loop. Generalises\n * {@link FlowLayout.alignLead}, which only produces a leading offset, by also\n * spreading slack into the gaps for `\"between\"`/`\"around\"`.\n *\n * @param contentMain - Summed placed child main-extents plus inter-child\n * spacing — the block's total main extent.\n * @param innerMain - The container's inner extent along the main axis.\n * @param count - The number of children sharing the axis.\n * @returns `lead` — pixels added before the first child — and `gap` — pixels\n * added to the spacing after each child. Both are non-negative.\n */\n protected justifyOffsets(contentMain: number, innerMain: number, count: number): { lead: number; gap: number } {\n const residual = Math.max(0, innerMain - contentMain);\n\n if (residual === 0 || this._justify === \"start\") {\n return { lead: 0, gap: 0 };\n }\n\n if (this._justify === \"center\") {\n return { lead: residual / 2, gap: 0 };\n }\n\n if (this._justify === \"end\") {\n return { lead: residual, gap: 0 };\n }\n\n if (this._justify === \"between\") {\n return { lead: 0, gap: count > 1 ? residual / (count - 1) : 0 };\n }\n\n // \"around\"\n return { lead: count > 0 ? residual / (2 * count) : 0, gap: count > 0 ? residual / count : 0 };\n }\n\n /**\n * Resolves a child's cross-axis offset and extent within the full cross\n * band, honouring an explicit per-child cross `fill`/`anchor` (align-self).\n * A cross-axis `fill` (`VERTICAL`/`BOTH` in an {@link HBox}, `HORIZONTAL`/\n * `BOTH` in a {@link VBox}) stretches the child to the whole band; a\n * cross-axis `anchor` edge (NORTH/SOUTH for HBox, WEST/EAST for VBox, plus\n * the corners carrying that component) pins the child's natural cross extent\n * to the leading or trailing edge. The main-axis `anchor` component and\n * `CENTER` are deliberately inert — the box owns main-axis sequencing and\n * each box's default cross placement (HBox baseline, VBox WEST origin) is\n * more specific than geometric centring.\n *\n * @param component - The child whose constraints supply the cross intent.\n * @param crossLead - The band's leading edge (`insets.top` for HBox,\n * `insets.left` for VBox).\n * @param crossExtent - The full cross band extent.\n * @param naturalCross - The child's preferred cross extent, already capped to\n * the band and the child's maximum; the anchored child sits at this extent.\n * @param horizontal - `true` for {@link HBox} (cross axis is vertical),\n * `false` for {@link VBox} (cross axis is horizontal).\n * @returns `{ offset, extent }` when the child sets an explicit cross intent,\n * or `null` to signal the caller to keep its default cross placement.\n */\n protected crossPlacement(component: Component, crossLead: number, crossExtent: number, naturalCross: number, horizontal: boolean): { offset: number; extent: number } | null {\n const constraints = this.getLayoutConstraints(component);\n const fill = constraints?.fill ?? null;\n const anchor = constraints?.anchor ?? null;\n\n const crossFill = horizontal\n ? (fill === FillType.VERTICAL || fill === FillType.BOTH)\n : (fill === FillType.HORIZONTAL || fill === FillType.BOTH);\n\n if (crossFill) {\n return { offset: crossLead, extent: crossExtent };\n }\n\n const edge = this.crossAnchorEdge(anchor, horizontal);\n\n if (edge === null) {\n return null;\n }\n\n const extent = Math.min(naturalCross, crossExtent);\n const offset = edge === \"lead\"\n ? crossLead\n : crossLead + (crossExtent - extent);\n\n return { offset, extent };\n }\n\n /**\n * Projects a child's `anchor` onto the cross axis as a leading/trailing edge.\n * CENTER and pure main-axis anchors carry no cross component and return\n * `null`, leaving the caller on its default cross placement.\n *\n * @param anchor - The child's anchor constraint, or `null`.\n * @param horizontal - `true` for {@link HBox} (cross = vertical: NORTH=lead,\n * SOUTH=trail), `false` for {@link VBox} (cross = horizontal: WEST=lead,\n * EAST=trail).\n * @returns `\"lead\"`/`\"trail\"` for an anchor with a cross component, else `null`.\n */\n private crossAnchorEdge(anchor: AnchorType | null, horizontal: boolean): \"lead\" | \"trail\" | null {\n if (anchor === null) {\n return null;\n }\n\n if (horizontal) {\n if (anchor === AnchorType.NORTH || anchor === AnchorType.NORTHWEST || anchor === AnchorType.NORTHEAST) {\n return \"lead\";\n }\n\n if (anchor === AnchorType.SOUTH || anchor === AnchorType.SOUTHWEST || anchor === AnchorType.SOUTHEAST) {\n return \"trail\";\n }\n\n return null;\n }\n\n if (anchor === AnchorType.WEST || anchor === AnchorType.NORTHWEST || anchor === AnchorType.SOUTHWEST) {\n return \"lead\";\n }\n\n if (anchor === AnchorType.EAST || anchor === AnchorType.NORTHEAST || anchor === AnchorType.SOUTHEAST) {\n return \"trail\";\n }\n\n return null;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { FillType } from \"~/layout/FillType.js\";\nimport { Size, UNBOUNDED, isUnbounded } from \"~/primitive/Size.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { BoxLayout, BoxLayoutOptions } from \"~/layout/BoxLayout.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link VBox}.\n *\n * @remarks `mode` selects the sizing strategy along the vertical axis.\n * `\"preferred\"` (the default) honours each child's preferred height and\n * supports `weight` cells. `\"equal\"` divides the container height equally\n * and ignores `weight`. `mode` is independent of `stretching` (the cross-axis\n * fill): `stretching` defaults to `false` in both modes, so `\"equal\"` divides\n * the height but leaves children at their preferred width unless\n * `stretching: true` is passed.\n *\n * @category Layouts\n */\nexport interface VBoxOptions extends BoxLayoutOptions {}\n\n/**\n * A layout manager that places children in a single vertical column. The\n * `mode` option selects between preferred-height sequencing (with weight-cell\n * support) and equal-height division of the container.\n *\n * @category Layouts\n */\nclass VBox extends BoxLayout {\n\n private _defaultComponentHeight: number = 100;\n\n /**\n * Returns the column's baseline — the first laid-out child's baseline,\n * measured from the container's content-top — so a baseline-aware parent\n * aligns this VBox container by its first row's text baseline rather than\n * auto-centring the whole container.\n *\n * @returns The first child's baseline offset in pixels, or `null` when there\n * is no container, no laid-out children, or the first child reports no\n * baseline of its own.\n *\n * @remarks Forwards the **first** child's baseline verbatim — unlike\n * {@link HBox}, which takes the maximum baseline across the row's children.\n * A column has a single well-defined first row, so forwarding it (including\n * a `null` when that first row is graphical) is the most predictable\n * contract. The first child stacks at the container's content-top in every\n * mode, so its own baseline is already content-relative — `Component`'s\n * `getBaseline` then adds the container's chrome. Unlike `HBox`, this is not\n * disabled while stretching: VBox `stretching` fills the cross (width) axis\n * and leaves each child's height and intrinsic baseline untouched.\n */\n getContentBaseline(): number | null {\n const container = this.getContainer();\n if (!container) {\n return null;\n }\n\n const components = container.getLaidOutComponents();\n if (components.length === 0) {\n return null;\n }\n\n return components[0].getBaseline();\n }\n\n /**\n * Returns the preferred size. In `\"preferred\"` mode this is the widest\n * child width and the sum of child heights plus spacing. In `\"equal\"`\n * mode height is `count * (maxChildHeight + spacing) - spacing`.\n *\n * @returns The preferred `{width, height}`, or `null` if no container is attached.\n */\n getPreferredSize(): Size | null {\n let container = this.getContainer();\n if (!container) {\n return null;\n }\n\n let perimeterSize = container.getPerimeterSize();\n let components = container.getLaidOutComponents();\n\n if (this._mode === \"equal\") {\n let innerWidth = 0;\n let innerHeight = 0;\n\n for (let idx in components) {\n let component = components[idx];\n let size = component.getPreferredSize();\n\n if (size) {\n innerWidth = Math.max(innerWidth, size.width);\n innerHeight = Math.max(innerHeight, size.height);\n }\n }\n\n const width = innerWidth + perimeterSize.left + perimeterSize.right;\n const height = components.length * (innerHeight + this._spacing) - this._spacing\n + perimeterSize.top + perimeterSize.bottom;\n\n return { width, height };\n }\n\n let width = UNBOUNDED;\n let height = perimeterSize.top + perimeterSize.bottom;\n\n for (let idx in components) {\n let component = components[idx];\n let size = component.getPreferredSize();\n\n if (size) {\n width = isUnbounded(width) ? Math.min(width, size.width) : Math.max(width, size.width);\n height += size.height;\n }\n }\n\n width += perimeterSize.left + perimeterSize.right;\n height += this._spacing * (components.length - 1);\n\n return {\n width: width,\n height: height\n };\n }\n\n /**\n * Returns the minimum size. In `\"preferred\"` mode this is the widest\n * child minimum width and the sum of child minimum heights plus\n * spacing. In `\"equal\"` mode height is\n * `count * (maxChildMinHeight + spacing) - spacing`.\n *\n * @returns The minimum `{width, height}`, or `null` if no container is attached.\n */\n getMinSize(): Size | null {\n let container = this.getContainer();\n if (!container) {\n return null;\n }\n\n let perimeterSize = container.getPerimeterSize();\n let components = container.getLaidOutComponents();\n\n if (this._mode === \"equal\") {\n let innerWidth = 0;\n let innerHeight = 0;\n\n for (let idx in components) {\n let component = components[idx];\n let size = component.getMinSize();\n\n if (size) {\n innerWidth = Math.max(innerWidth, size.width);\n innerHeight = Math.max(innerHeight, size.height);\n }\n }\n\n const width = innerWidth + perimeterSize.left + perimeterSize.right;\n const height = components.length * (innerHeight + this._spacing) - this._spacing\n + perimeterSize.top + perimeterSize.bottom;\n\n return { width, height };\n }\n\n let width = 0;\n let height = perimeterSize.top + perimeterSize.bottom;\n\n for (let idx in components) {\n let component = components[idx];\n let size = component.getMinSize();\n\n if (size) {\n width = Math.max(width, size.width);\n height += size.height;\n }\n }\n\n width += perimeterSize.left + perimeterSize.right;\n height += this._spacing * (components.length - 1);\n\n return {\n width: width,\n height: height\n };\n }\n\n /**\n * Returns the maximum size the column can usefully occupy. Along the main\n * axis (height) it sums the children's *maximum* heights plus spacing; in\n * `\"equal\"` mode it is `count * maxChildMaxHeight` plus spacing, since every\n * cell shares the tallest child's allowance. The cross axis (width) takes\n * the *largest* child maximum — the widest a child permits the column to\n * grow to. A child whose maximum is `null` or at the unbounded sentinel\n * makes that axis unbounded.\n *\n * @returns The maximum `{width, height}`, or `null` if no container is attached.\n */\n getMaxSize(): Size | null {\n return this.aggregateMaxSize(false);\n }\n\n /**\n * Computes the children's combined minSize along this manager's geometry.\n * In `\"preferred\"` mode height is the sum of per-child `minSize.height`\n * plus spacing. In `\"equal\"` mode height is\n * `count * maxChildMinHeight + spacing*(n-1)` (VBox distributes height\n * equally so the per-cell floor is the max of every child's min height).\n * Width in both modes is the max per-child `minSize.width`. Used by\n * `doLayout` to inflate the working size when the host has opted into\n * `setOverflowing` on the corresponding axis.\n *\n * @returns The total min-size; `{ width: 0, height: 0 }` when the\n * container is absent or has no children.\n */\n protected computeTotalMinSize(): Size {\n const container = this.getContainer();\n if (!container) {\n return { width: 0, height: 0 };\n }\n\n const components = container.getLaidOutComponents();\n if (components.length === 0) {\n return { width: 0, height: 0 };\n }\n\n let width = 0;\n\n if (this._mode === \"equal\") {\n let maxHeight = 0;\n\n for (const component of components) {\n const min = component.getMinSize();\n if (min) {\n width = Math.max(width, min.width);\n maxHeight = Math.max(maxHeight, min.height);\n }\n }\n\n return {\n width,\n height: components.length * (maxHeight + this._spacing) - this._spacing,\n };\n }\n\n let height = this._spacing * (components.length - 1);\n\n for (const component of components) {\n const min = component.getMinSize();\n if (min) {\n width = Math.max(width, min.width);\n height += min.height;\n }\n }\n\n return { width, height };\n }\n\n /**\n * Lays out the children top-to-bottom, dispatching to `layoutEqualMode` or\n * `layoutPreferredMode` per the sizing mode after inflating the working\n * size for any scroll-enabled axis.\n */\n doLayout(): void {\n const container = this.getContainer();\n if (!container) {\n return;\n }\n\n const innerSize = container.getInnerSize();\n if (!innerSize) {\n return;\n }\n\n const components = container.getLaidOutComponents();\n const containerInsets = container.getContentInsets();\n const spacing = this.getComponentSpacing();\n\n // Universal scroll: when the host has opted into per-axis overflow, lay\n // out against the children's combined minSize so trailing children land\n // past the viewport and the host's CSS `overflow: auto` scrolls.\n // `innerSize` stays the real viewport for equal mode's overflow test.\n const containerSize = this.inflateForOverflow(innerSize);\n\n if (this._mode === \"equal\") {\n this.layoutEqualMode(components, innerSize, containerSize, containerInsets, spacing);\n } else {\n this.layoutPreferredMode(components, containerSize, containerInsets, spacing);\n }\n\n this.reserveContentFrame();\n }\n\n /**\n * Places children in equal-height cells stacked top-to-bottom. Every cell\n * takes the same height (see {@link VBox.computeEqualCellHeight}); when\n * stretching, cells fill the container width, otherwise children keep their\n * preferred width and are left-aligned within the column.\n *\n * @param components - The children to place, in order.\n * @param innerSize - The host's real inner size (pre-inflation), used to\n * measure the equal share against the true viewport.\n * @param containerSize - The working size, possibly inflated for overflow.\n * @param insets - The container's content insets.\n * @param spacing - Inter-child spacing in pixels.\n */\n private layoutEqualMode(components: Component[], innerSize: Size, containerSize: Size, insets: Insets, spacing: number): void {\n const cellHeight = this.computeEqualCellHeight(components, innerSize.height, spacing);\n\n const x = insets.getLeft();\n let y = insets.getTop();\n\n if (this.isStretching()) {\n const cellWidth = containerSize.width;\n\n for (const component of components) {\n this.placeComponent(component, x, y, cellWidth, cellHeight, FillType.BOTH);\n\n y += cellHeight + spacing;\n }\n\n return;\n }\n\n // Match the equal-stretch band exactly: the stretch branch fills\n // containerSize.width from insets.left with no right-inset subtraction,\n // so an EAST/fill align-self child reaches the same trailing edge.\n const crossLead = insets.getLeft();\n const crossExtent = containerSize.width;\n\n for (const component of components) {\n const size = component.getPreferredSize();\n const width = size ? size.width : 0;\n\n const cross = this.crossPlacement(component, crossLead, crossExtent, width, false);\n\n if (cross) {\n this.placeComponent(component, cross.offset, y, cross.extent, cellHeight, FillType.BOTH);\n } else {\n this.placeComponent(component, x, y, width, cellHeight, FillType.BOTH);\n }\n\n y += cellHeight + spacing;\n }\n }\n\n /**\n * Computes the shared cell height for equal mode: the equal share of the\n * inner height, floored at the tallest child's min height.\n *\n * @param components - The children sharing the column.\n * @param innerHeight - The host's real inner height (pre-inflation).\n * @param spacing - Inter-child spacing in pixels.\n * @returns The height every equal-mode cell receives.\n *\n * @remarks Three cases drive the floor: (1) the share clears the tallest\n * child's min height → the column fits, divide equally; (2) the share is\n * below the min floor, the host scrolls on this axis, and `overflowSizing`\n * is `\"preferred\"` → every cell takes the tallest child's preferred height\n * so cells regain preferred size instead of sticking at min while\n * scrolling; (3) otherwise → clamp to the min floor (a non-scrolling host's\n * `overflow: hidden` clips the surplus).\n */\n private computeEqualCellHeight(components: Component[], innerHeight: number, spacing: number): number {\n let maxChildMinHeight = 0;\n let maxChildPreferredHeight = 0;\n\n for (const component of components) {\n const min = component.getMinSize();\n if (min) {\n maxChildMinHeight = Math.max(maxChildMinHeight, min.height);\n }\n\n const pref = component.getPreferredSize();\n if (pref) {\n maxChildPreferredHeight = Math.max(maxChildPreferredHeight, pref.height);\n }\n }\n\n const equalShare = (innerHeight - spacing * (components.length - 1)) / components.length;\n\n if (equalShare >= maxChildMinHeight) {\n return equalShare;\n }\n\n if (this.isOverflowingY() && this._overflowSizing === \"preferred\") {\n return Math.max(maxChildMinHeight, maxChildPreferredHeight);\n }\n\n return maxChildMinHeight;\n }\n\n /**\n * Places children at their preferred heights stacked top-to-bottom. Weight\n * cells split the space left after the non-weighted children; when the\n * column overflows, the non-weighted children shrink proportionally toward\n * their min heights. Children keep their preferred width, or fill the\n * container width when stretching.\n *\n * @param components - The children to place, in order.\n * @param containerSize - The working size, possibly inflated for overflow.\n * @param insets - The container's content insets.\n * @param spacing - Inter-child spacing in pixels.\n */\n private layoutPreferredMode(components: Component[], containerSize: Size, insets: Insets, spacing: number): void {\n const { totalWeight, fixedPreferred, fixedMin } = this.measureFixedHeights(components, spacing);\n\n const { remaining: remainingHeight, shrinkRatio } = this.computeShrink(\n fixedPreferred,\n fixedMin,\n containerSize.height,\n this.isOverflowingY()\n );\n\n // Pre-pass: resolve each child's height into an array so the trailing\n // slack can be measured before placement (mirrors HBox's widths[]).\n const heights: number[] = [];\n\n for (const component of components) {\n const weight = this.getLayoutConstraints(component)?.weight ?? 0;\n const size = component.getPreferredSize();\n const minSize = component.getMinSize();\n const maxSize = component.getMaxSize();\n\n heights.push(this.resolveChildHeight(size, minSize, maxSize, weight, totalWeight, remainingHeight, shrinkRatio));\n }\n\n // Sum the placed main extents to find the trailing slack, then ask the\n // shared helper how to distribute it. Weight cells already consume all\n // slack, so justify is a no-op when any are present.\n let lead = 0;\n let gap = 0;\n\n if (totalWeight === 0) {\n let contentHeight = spacing * (components.length - 1);\n\n for (const h of heights) {\n contentHeight += h;\n }\n\n const innerHeight = containerSize.height - (insets.getTop() + insets.getBottom());\n\n ({ lead, gap } = this.justifyOffsets(contentHeight, innerHeight, components.length));\n }\n\n // Cross band for per-child align-self: the horizontal band trimmed by\n // both insets. The default path keeps reading containerSize.width\n // (un-trimmed) to stay byte-identical with today's body.\n const crossLead = insets.getLeft();\n const crossExtent = containerSize.width - (insets.getLeft() + insets.getRight());\n\n const x = insets.getLeft();\n let y = insets.getTop() + lead;\n\n for (let idx = 0; idx < components.length; idx += 1) {\n const component = components[idx];\n const size = component.getPreferredSize();\n const maxSize = component.getMaxSize();\n\n // Cross-axis (width): give the child the column's available width —\n // the full inner width when stretching or sizeless, otherwise its\n // preferred width capped to the column. Cap to the child's maximum,\n // but do NOT floor to its minimum: enforcing the minimum here would\n // dogmatically inflate a child back up to its content size even when\n // the column is narrower, defeating a scrolling/clipping child. The\n // child's own setWidth → clampWidth applies its minimum (its content\n // minimum for a general component, or nothing for a Panel, which fits\n // its allocation and scrolls). When the host opts into horizontal\n // scroll, `containerSize` is already inflated to the content extent\n // upstream, so `min(pref, containerSize)` still yields the full width\n // without a floor here.\n let defaultWidth: number;\n\n if (!size || this.isStretching()) {\n defaultWidth = containerSize.width;\n } else {\n defaultWidth = Math.min(size.width, containerSize.width);\n }\n\n if (maxSize) {\n defaultWidth = Math.min(defaultWidth, maxSize.width);\n }\n\n // Align-self sizes from the child's *preferred* width (capped to the\n // band + max), independent of global stretching, so an anchored child\n // shrinks-and-anchors even when the column is stretching.\n let naturalWidth = size ? Math.min(size.width, crossExtent) : crossExtent;\n\n if (maxSize) {\n naturalWidth = Math.min(naturalWidth, maxSize.width);\n }\n\n const cross = this.crossPlacement(component, crossLead, crossExtent, naturalWidth, false);\n\n if (cross) {\n this.placeComponent(component, cross.offset, y, cross.extent, heights[idx], FillType.BOTH);\n } else {\n this.placeComponent(component, x, y, defaultWidth, heights[idx], FillType.BOTH);\n }\n\n // Advance by the resolved height, not getHeight(): the gap is\n // measured against the same extent contentHeight summed, so a\n // placeComponent clamp must not skew the trailing edge.\n y += heights[idx];\n y += spacing + gap;\n }\n }\n\n /**\n * Sums the non-weighted children's preferred and minimum heights (each\n * including inter-child spacing) and the total weight of the weight cells.\n *\n * @param components - The children sharing the column.\n * @param spacing - Inter-child spacing in pixels.\n * @returns `totalWeight` of the weight cells, plus the `fixedPreferred` and\n * `fixedMin` height totals of the non-weighted children.\n */\n private measureFixedHeights(components: Component[], spacing: number): { totalWeight: number; fixedPreferred: number; fixedMin: number } {\n let totalWeight = 0;\n let fixedPreferred = spacing * (components.length - 1);\n let fixedMin = spacing * (components.length - 1);\n\n for (const component of components) {\n const weight = this.getLayoutConstraints(component)?.weight ?? 0;\n\n if (weight > 0) {\n totalWeight += weight;\n } else {\n const size = component.getPreferredSize();\n const minSize = component.getMinSize();\n\n fixedPreferred += this.preferredChildHeight(size, minSize);\n fixedMin += minSize ? minSize.height : 0;\n }\n }\n\n return { totalWeight, fixedPreferred, fixedMin };\n }\n\n /**\n * Resolves a non-weighted child's preferred height, falling back through min\n * height to `_defaultComponentHeight`.\n *\n * @param size - The child's preferred size, or `null`.\n * @param minSize - The child's minimum size, or `null`.\n * @returns The height the child contributes before shrinking.\n *\n * @remarks Nullish-coalesce, not `||`: a component with an explicit\n * preferred height of 0 must contribute 0, not fall through to\n * `_defaultComponentHeight`. The `minSize.height > 0` guard prevents\n * `LayoutManager._defaultMinSize = {0,0}` from short-circuiting the chain\n * into a 0 height (which would land a layout-managed Table on a 0 height\n * even though no preferred size was set).\n *\n * The result is floored at the child's own min height: a child placed at\n * `max(preferred, min)` (see {@link VBox.resolveChildHeight}) must reserve\n * that same height in the fixed total, or a child reporting `preferred < min`\n * (a `min ≤ preferred` invariant violation) would be under-reserved and push\n * the weighted cells out, overflowing the column.\n */\n private preferredChildHeight(size: Size | null, minSize: Size | null): number {\n const preferred = (size ? size.height : undefined)\n ?? (minSize && minSize.height > 0 ? minSize.height : undefined)\n ?? this._defaultComponentHeight;\n\n return minSize ? Math.max(preferred, minSize.height) : preferred;\n }\n\n /**\n * Resolves a child's final height within the column, clamped to its\n * min/max. Weight cells take a share of `remainingHeight`; non-weighted\n * children take their preferred height reduced by the shrink ratio toward\n * their min height.\n *\n * @param size - The child's preferred size, or `null`.\n * @param minSize - The child's minimum size, or `null`.\n * @param maxSize - The child's maximum size, or `null`.\n * @param weight - The child's weight constraint (0 when non-weighted).\n * @param totalWeight - The summed weight of all weight cells.\n * @param remainingHeight - The space available to weight cells.\n * @param shrinkRatio - How far (0–1) non-weighted children shrink to min.\n * @returns The child's height.\n */\n private resolveChildHeight(size: Size | null, minSize: Size | null, maxSize: Size | null, weight: number, totalWeight: number, remainingHeight: number, shrinkRatio: number): number {\n let height: number;\n\n if (weight > 0 && totalWeight > 0) {\n height = (weight / totalWeight) * remainingHeight;\n } else {\n const pref = this.preferredChildHeight(size, minSize);\n const min = minSize ? minSize.height : 0;\n\n height = pref - shrinkRatio * (pref - min);\n }\n\n if (minSize) {\n height = Math.max(height, minSize.height);\n }\n\n if (maxSize) {\n height = Math.min(height, maxSize.height);\n }\n\n return height;\n }\n}\n\nconst VBoxCallable = callable(VBox);\ntype VBoxCallable = VBox;\nexport {\n VBox as _VBox,\n VBoxCallable as VBox\n};\n"],"mappings":"mHAwGA,IAAsB,UAAtB,cAAwC,CAAc,CAKlD,SAA6B,EAC7B,YAAiC,GACjC,MAA2B,YAC3B,gBAA+C,YAC/C,SAAiC,QAEjC,YAAY,EAA4B,CAGpC,MAAM,EAEF,GACA,KAAK,aAAa,CAAO,CAEjC,CAcA,aAAuB,EAAiC,CACpD,MAAM,aAAa,CAAO,EAEtB,EAAQ,OAAS,IAAA,IACjB,KAAK,QAAQ,EAAQ,IAAI,EAGzB,EAAQ,UAAY,IAAA,IACpB,KAAK,oBAAoB,EAAQ,OAAO,EAGxC,EAAQ,aAAe,IAAA,IACvB,KAAK,cAAc,EAAQ,UAAU,EAGrC,EAAQ,iBAAmB,IAAA,IAC3B,KAAK,kBAAkB,EAAQ,cAAc,EAG7C,EAAQ,UAAY,IAAA,IACpB,KAAK,WAAW,EAAQ,OAAO,CAEvC,CAOA,qBAA8B,CAC1B,OAAO,KAAK,UAAY,CAC5B,CASA,oBAAoB,EAAuB,CAGvC,MAFA,MAAK,SAAW,GAAW,EAEpB,IACX,CAOA,cAAwB,CACpB,OAAO,KAAK,aAAe,EAC/B,CASA,cAAc,EAA2B,CAGrC,MAFA,MAAK,YAAc,CAAC,CAAC,EAEd,IACX,CAOA,SAAmB,CACf,OAAO,KAAK,KAChB,CAWA,QAAQ,EAAqB,CAGzB,MAFA,MAAK,MAAQ,EAEN,IACX,CAQA,mBAAuC,CACnC,OAAO,KAAK,eAChB,CAYA,kBAAkB,EAAyC,CAGvD,MAFA,MAAK,gBAAkB,EAEhB,IACX,CAOA,YAAyB,CACrB,OAAO,KAAK,QAChB,CAWA,WAAW,EAA2B,CAGlC,MAFA,MAAK,SAAW,EAET,IACX,CAcA,iBAA2B,EAAkC,CACzD,IAAM,EAAY,KAAK,aAAa,EACpC,GAAI,CAAC,EACD,OAAO,KAGX,IAAM,EAAgB,EAAU,iBAAiB,EAC3C,EAAa,EAAU,qBAAqB,EAE5C,EAAa,EAAa,EAAc,KAAO,EAAc,MAAQ,EAAc,IAAM,EAAc,OACvG,EAAa,EAAa,EAAc,IAAM,EAAc,OAAS,EAAc,KAAO,EAAc,MAE1G,EAAO,EACP,EAAQ,EACR,EAAgB,GAChB,EAAiB,GACjB,EAAe,EAEnB,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAO,EAAU,WAAW,EAElC,GAAI,CAAC,EAAM,CACP,EAAgB,GAChB,EAAiB,GACjB,QACJ,CAEA,IAAM,EAAc,EAAa,EAAK,MAAS,EAAK,OAC9C,EAAc,EAAa,EAAK,OAAS,EAAK,MAEhD,EAAY,CAAU,EACtB,EAAgB,GACT,KAAK,QAAU,QACtB,EAAe,KAAK,IAAI,EAAc,CAAU,EAEhD,GAAQ,EAGR,EAAY,CAAW,EACvB,EAAiB,GAEjB,EAAQ,KAAK,IAAI,EAAO,CAAW,CAE3C,CAEI,KAAK,QAAU,QACf,GAAQ,EAAW,OAAS,EAAe,KAAK,SAAW,KAAK,IAAI,EAAG,EAAW,OAAS,CAAC,EAE5F,GAAQ,KAAK,SAAW,KAAK,IAAI,EAAG,EAAW,OAAS,CAAC,EAG7D,GAAS,EAET,IAAM,EAAa,EAAiB,EAAY,EAC1C,EAAa,EAAiB,EAAY,EAEhD,MAAO,CACH,MAAQ,EAAa,EAAa,EAClC,OAAQ,EAAa,EAAa,CACtC,CACJ,CAqBA,cAAwB,EAAwB,EAAkB,EAAmB,EAAkE,CACnJ,GAAI,GAAkB,GAAa,EAC/B,MAAO,CAAE,UAAW,KAAK,IAAI,EAAG,EAAY,CAAc,EAAG,YAAa,CAAE,EAGhF,IAAM,EAAa,EAAiB,EAC9B,EAAa,EAAiB,EAEpC,MAAO,CAAE,UAAW,EAAG,YAAa,EAAa,EAAI,KAAK,IAAI,EAAG,EAAS,CAAU,EAAI,CAAE,CAC9F,CAmBA,eAAyB,EAAqB,EAAmB,EAA8C,CAC3G,IAAM,EAAW,KAAK,IAAI,EAAG,EAAY,CAAW,EAmBpD,OAjBI,IAAa,GAAK,KAAK,WAAa,QAC7B,CAAE,KAAM,EAAG,IAAK,CAAE,EAGzB,KAAK,WAAa,SACX,CAAE,KAAM,EAAW,EAAG,IAAK,CAAE,EAGpC,KAAK,WAAa,MACX,CAAE,KAAM,EAAU,IAAK,CAAE,EAGhC,KAAK,WAAa,UACX,CAAE,KAAM,EAAG,IAAK,EAAQ,EAAI,GAAY,EAAQ,GAAK,CAAE,EAI3D,CAAE,KAAM,EAAQ,EAAI,GAAY,EAAI,GAAS,EAAG,IAAK,EAAQ,EAAI,EAAW,EAAQ,CAAE,CACjG,CAyBA,eAAyB,EAAsB,EAAmB,EAAqB,EAAsB,EAAgE,CACzK,IAAM,EAAc,KAAK,qBAAqB,CAAS,EACjD,EAAS,GAAa,MAAU,KAChC,EAAS,GAAa,QAAU,KAMtC,GAJkB,EACX,IAAS,EAAS,UAAY,IAAS,EAAS,KAChD,IAAS,EAAS,YAAc,IAAS,EAAS,KAGrD,MAAO,CAAE,OAAQ,EAAW,OAAQ,CAAY,EAGpD,IAAM,EAAO,KAAK,gBAAgB,EAAQ,CAAU,EAEpD,GAAI,IAAS,KACT,OAAO,KAGX,IAAM,EAAS,KAAK,IAAI,EAAc,CAAW,EAKjD,MAAO,CAAE,OAJM,IAAS,OAClB,EACA,GAAa,EAAc,GAEhB,QAAO,CAC5B,CAaA,gBAAwB,EAA2B,EAA8C,CAyB7F,OAxBI,IAAW,KACJ,KAGP,EACI,IAAW,EAAW,OAAS,IAAW,EAAW,WAAa,IAAW,EAAW,UACjF,OAGP,IAAW,EAAW,OAAS,IAAW,EAAW,WAAa,IAAW,EAAW,UACjF,QAGJ,KAGP,IAAW,EAAW,MAAQ,IAAW,EAAW,WAAa,IAAW,EAAW,UAChF,OAGP,IAAW,EAAW,MAAQ,IAAW,EAAW,WAAa,IAAW,EAAW,UAChF,QAGJ,IACX,CACJ,ECheM,KAAN,cAAmB,SAAU,CAEzB,wBAA0C,IAsB1C,oBAAoC,CAChC,IAAM,EAAY,KAAK,aAAa,EACpC,GAAI,CAAC,EACD,OAAO,KAGX,IAAM,EAAa,EAAU,qBAAqB,EAKlD,OAJI,EAAW,SAAW,EACf,KAGJ,EAAW,EAAE,CAAC,YAAY,CACrC,CASA,kBAAgC,CAC5B,IAAI,EAAY,KAAK,aAAa,EAClC,GAAI,CAAC,EACD,OAAO,KAGX,IAAI,EAAgB,EAAU,iBAAiB,EAC3C,EAAa,EAAU,qBAAqB,EAEhD,GAAI,KAAK,QAAU,QAAS,CACxB,IAAI,EAAa,EACb,EAAc,EAElB,IAAK,IAAI,KAAO,EAAY,CAExB,IAAI,EADY,EAAW,EAChB,CAAU,iBAAiB,EAElC,IACA,EAAc,KAAK,IAAI,EAAa,EAAK,KAAK,EAC9C,EAAc,KAAK,IAAI,EAAa,EAAK,MAAM,EAEvD,CAMA,MAAO,CAAE,MAJM,EAAa,EAAc,KAAO,EAAc,MAI/C,OAHD,EAAW,QAAU,EAAc,KAAK,UAAY,KAAK,SACzD,EAAc,IAAM,EAAc,MAE1B,CAC3B,CAEA,IAAI,EAAQ,EACR,EAAS,EAAc,IAAM,EAAc,OAE/C,IAAK,IAAI,KAAO,EAAY,CAExB,IAAI,EADY,EAAW,EAChB,CAAU,iBAAiB,EAElC,IACA,EAAQ,EAAY,CAAK,EAAI,KAAK,IAAI,EAAO,EAAK,KAAK,EAAI,KAAK,IAAI,EAAO,EAAK,KAAK,EACrF,GAAU,EAAK,OAEvB,CAKA,MAHA,IAAS,EAAc,KAAO,EAAc,MAC5C,GAAU,KAAK,UAAY,EAAW,OAAS,GAExC,CACI,QACC,QACZ,CACJ,CAUA,YAA0B,CACtB,IAAI,EAAY,KAAK,aAAa,EAClC,GAAI,CAAC,EACD,OAAO,KAGX,IAAI,EAAgB,EAAU,iBAAiB,EAC3C,EAAa,EAAU,qBAAqB,EAEhD,GAAI,KAAK,QAAU,QAAS,CACxB,IAAI,EAAa,EACb,EAAc,EAElB,IAAK,IAAI,KAAO,EAAY,CAExB,IAAI,EADY,EAAW,EAChB,CAAU,WAAW,EAE5B,IACA,EAAc,KAAK,IAAI,EAAa,EAAK,KAAK,EAC9C,EAAc,KAAK,IAAI,EAAa,EAAK,MAAM,EAEvD,CAMA,MAAO,CAAE,MAJM,EAAa,EAAc,KAAO,EAAc,MAI/C,OAHD,EAAW,QAAU,EAAc,KAAK,UAAY,KAAK,SACzD,EAAc,IAAM,EAAc,MAE1B,CAC3B,CAEA,IAAI,EAAQ,EACR,EAAS,EAAc,IAAM,EAAc,OAE/C,IAAK,IAAI,KAAO,EAAY,CAExB,IAAI,EADY,EAAW,EAChB,CAAU,WAAW,EAE5B,IACA,EAAQ,KAAK,IAAI,EAAO,EAAK,KAAK,EAClC,GAAU,EAAK,OAEvB,CAKA,MAHA,IAAS,EAAc,KAAO,EAAc,MAC5C,GAAU,KAAK,UAAY,EAAW,OAAS,GAExC,CACI,QACC,QACZ,CACJ,CAaA,YAA0B,CACtB,OAAO,KAAK,iBAAiB,EAAK,CACtC,CAeA,qBAAsC,CAClC,IAAM,EAAY,KAAK,aAAa,EACpC,GAAI,CAAC,EACD,MAAO,CAAE,MAAO,EAAG,OAAQ,CAAE,EAGjC,IAAM,EAAa,EAAU,qBAAqB,EAClD,GAAI,EAAW,SAAW,EACtB,MAAO,CAAE,MAAO,EAAG,OAAQ,CAAE,EAGjC,IAAI,EAAQ,EAEZ,GAAI,KAAK,QAAU,QAAS,CACxB,IAAI,EAAY,EAEhB,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAM,EAAU,WAAW,EAC7B,IACA,EAAY,KAAK,IAAI,EAAW,EAAI,KAAK,EACzC,EAAY,KAAK,IAAI,EAAW,EAAI,MAAM,EAElD,CAEA,MAAO,CACH,QACA,OAAQ,EAAW,QAAU,EAAY,KAAK,UAAY,KAAK,QACnE,CACJ,CAEA,IAAI,EAAS,KAAK,UAAY,EAAW,OAAS,GAElD,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAM,EAAU,WAAW,EAC7B,IACA,EAAU,KAAK,IAAI,EAAO,EAAI,KAAK,EACnC,GAAU,EAAI,OAEtB,CAEA,MAAO,CAAE,QAAO,QAAO,CAC3B,CAOA,UAAiB,CACb,IAAM,EAAY,KAAK,aAAa,EACpC,GAAI,CAAC,EACD,OAGJ,IAAM,EAAY,EAAU,aAAa,EACzC,GAAI,CAAC,EACD,OAGJ,IAAM,EAAkB,EAAU,qBAAqB,EACjD,EAAkB,EAAU,iBAAiB,EAC7C,EAAkB,KAAK,oBAAoB,EAM3C,EAAgB,KAAK,mBAAmB,CAAS,EAEnD,KAAK,QAAU,QACf,KAAK,gBAAgB,EAAY,EAAW,EAAe,EAAiB,CAAO,EAEnF,KAAK,oBAAoB,EAAY,EAAe,EAAiB,CAAO,EAGhF,KAAK,oBAAoB,CAC7B,CAeA,gBAAwB,EAAyB,EAAiB,EAAqB,EAAgB,EAAuB,CAC1H,IAAM,EAAa,KAAK,uBAAuB,EAAY,EAAU,OAAQ,CAAO,EAE9E,EAAI,EAAO,QAAQ,EACrB,EAAI,EAAO,OAAO,EAEtB,GAAI,KAAK,aAAa,EAAG,CACrB,IAAM,EAAY,EAAc,MAEhC,IAAK,IAAM,KAAa,EACpB,KAAK,eAAe,EAAW,EAAG,EAAG,EAAW,EAAY,EAAS,IAAI,EAEzE,GAAK,EAAa,EAGtB,MACJ,CAKA,IAAM,EAAc,EAAO,QAAQ,EAC7B,EAAc,EAAc,MAElC,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAQ,EAAU,iBAAiB,EACnC,EAAQ,EAAO,EAAK,MAAQ,EAE5B,EAAQ,KAAK,eAAe,EAAW,EAAW,EAAa,EAAO,EAAK,EAE7E,EACA,KAAK,eAAe,EAAW,EAAM,OAAQ,EAAG,EAAM,OAAQ,EAAY,EAAS,IAAI,EAEvF,KAAK,eAAe,EAAW,EAAG,EAAG,EAAO,EAAY,EAAS,IAAI,EAGzE,GAAK,EAAa,CACtB,CACJ,CAmBA,uBAA+B,EAAyB,EAAqB,EAAyB,CAClG,IAAI,EAAoB,EACpB,EAA0B,EAE9B,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAM,EAAU,WAAW,EAC7B,IACA,EAAoB,KAAK,IAAI,EAAmB,EAAI,MAAM,GAG9D,IAAM,EAAO,EAAU,iBAAiB,EACpC,IACA,EAA0B,KAAK,IAAI,EAAyB,EAAK,MAAM,EAE/E,CAEA,IAAM,GAAc,EAAc,GAAW,EAAW,OAAS,IAAM,EAAW,OAUlF,OARI,GAAc,EACP,EAGP,KAAK,eAAe,GAAK,KAAK,kBAAoB,YAC3C,KAAK,IAAI,EAAmB,CAAuB,EAGvD,CACX,CAcA,oBAA4B,EAAyB,EAAqB,EAAgB,EAAuB,CAC7G,GAAM,CAAE,cAAa,iBAAgB,YAAa,KAAK,oBAAoB,EAAY,CAAO,EAExF,CAAE,UAAW,EAAiB,eAAgB,KAAK,cACrD,EACA,EACA,EAAc,OACd,KAAK,eAAe,CACxB,EAIM,EAAoB,CAAC,EAE3B,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAU,KAAK,qBAAqB,CAAS,CAAC,EAAE,QAAU,EAC1D,EAAU,EAAU,iBAAiB,EACrC,EAAU,EAAU,WAAW,EAC/B,EAAU,EAAU,WAAW,EAErC,EAAQ,KAAK,KAAK,mBAAmB,EAAM,EAAS,EAAS,EAAQ,EAAa,EAAiB,CAAW,CAAC,CACnH,CAKA,IAAI,EAAO,EACP,EAAO,EAEX,GAAI,IAAgB,EAAG,CACnB,IAAI,EAAgB,GAAW,EAAW,OAAS,GAEnD,IAAK,IAAM,KAAK,EACZ,GAAiB,EAGrB,IAAM,EAAc,EAAc,QAAU,EAAO,OAAO,EAAI,EAAO,UAAU,IAE9E,eAAgB,KAAK,eAAe,EAAe,EAAa,EAAW,MAAM,EACtF,CAKA,IAAM,EAAc,EAAO,QAAQ,EAC7B,EAAc,EAAc,OAAS,EAAO,QAAQ,EAAI,EAAO,SAAS,GAExE,EAAI,EAAO,QAAQ,EACrB,EAAI,EAAO,OAAO,EAAI,EAE1B,IAAK,IAAI,EAAM,EAAG,EAAM,EAAW,OAAQ,GAAO,EAAG,CACjD,IAAM,EAAY,EAAW,GACvB,EAAU,EAAU,iBAAiB,EACrC,EAAU,EAAU,WAAW,EAcjC,EAEJ,AAGI,EAHA,CAAC,GAAQ,KAAK,aAAa,EACZ,EAAc,MAEd,KAAK,IAAI,EAAK,MAAO,EAAc,KAAK,EAGvD,IACA,EAAe,KAAK,IAAI,EAAc,EAAQ,KAAK,GAMvD,IAAI,EAAe,EAAO,KAAK,IAAI,EAAK,MAAO,CAAW,EAAI,EAE1D,IACA,EAAe,KAAK,IAAI,EAAc,EAAQ,KAAK,GAGvD,IAAM,EAAQ,KAAK,eAAe,EAAW,EAAW,EAAa,EAAc,EAAK,EAEpF,EACA,KAAK,eAAe,EAAW,EAAM,OAAQ,EAAG,EAAM,OAAQ,EAAQ,GAAM,EAAS,IAAI,EAEzF,KAAK,eAAe,EAAW,EAAG,EAAG,EAAc,EAAQ,GAAM,EAAS,IAAI,EAMlF,GAAK,EAAQ,GACb,GAAK,EAAU,CACnB,CACJ,CAWA,oBAA4B,EAAyB,EAAoF,CACrI,IAAI,EAAc,EACd,EAAiB,GAAW,EAAW,OAAS,GAChD,EAAiB,GAAW,EAAW,OAAS,GAEpD,IAAK,IAAM,KAAa,EAAY,CAChC,IAAM,EAAS,KAAK,qBAAqB,CAAS,CAAC,EAAE,QAAU,EAE/D,GAAI,EAAS,EACT,GAAe,MACZ,CACH,IAAM,EAAU,EAAU,iBAAiB,EACrC,EAAU,EAAU,WAAW,EAErC,GAAkB,KAAK,qBAAqB,EAAM,CAAO,EACzD,GAAkB,EAAU,EAAQ,OAAS,CACjD,CACJ,CAEA,MAAO,CAAE,cAAa,iBAAgB,UAAS,CACnD,CAuBA,qBAA6B,EAAmB,EAA8B,CAC1E,IAAM,GAAa,EAAO,EAAK,OAAS,IAAA,MAChC,GAAW,EAAQ,OAAS,EAAI,EAAQ,OAAS,IAAA,KAClD,KAAK,wBAEZ,OAAO,EAAU,KAAK,IAAI,EAAW,EAAQ,MAAM,EAAI,CAC3D,CAiBA,mBAA2B,EAAmB,EAAsB,EAAsB,EAAgB,EAAqB,EAAyB,EAA6B,CACjL,IAAI,EAEJ,GAAI,EAAS,GAAK,EAAc,EAC5B,EAAU,EAAS,EAAe,MAC/B,CACH,IAAM,EAAO,KAAK,qBAAqB,EAAM,CAAO,EAGpD,EAAS,EAAO,GAAe,GAFlB,EAAU,EAAQ,OAAS,GAG5C,CAUA,OARI,IACA,EAAS,KAAK,IAAI,EAAQ,EAAQ,MAAM,GAGxC,IACA,EAAS,KAAK,IAAI,EAAQ,EAAQ,MAAM,GAGrC,CACX,CACJ,EAEM,EAAe,EAAS,IAAI"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"./DOM-CGn7xyTw.js";import{l as t}from"./Position-A39T7-_1.js";import{m as n,p as r,y as i}from"./Component-DXf5a3g0.js";import{i as a,n as o,r as s,t as c}from"./Scrollbar-BhtOjqkG.js";var VirtualScroller=class{_owner;_onScroll;_clipBox;_rowsContainer;_scrollbarV;_scrollbarH;_scrollX=0;_scrollY=0;_contentWidth=0;_contentHeight=0;_smooth;_shadowOverlay;_shadowEdges={top:0,bottom:0,left:0,right:0};constructor(t,n,a){this._owner=t,this._onScroll=a;let s=e.sink.createElement(`div`);e.sink.apply(s,{style:{position:`absolute`,top:`0`,left:`0`,width:`100%`,height:`100%`,overflow:`hidden`}}),e.sink.appendChild(n,s),this._clipBox=s;let l=e.sink.createElement(`div`);e.sink.apply(l,{style:{position:`absolute`,top:`0`,left:`0`,width:`100%`,transform:`translate3d(0, 0, 0)`,willChange:`transform`}}),e.sink.appendChild(s,l),this._rowsContainer=l;let u=e.sink.createElement(`div`);e.sink.apply(u,{style:{position:`absolute`,top:`0`,left:`0`,width:`100%`,height:`100%`,pointerEvents:`none`,boxShadow:o()}}),e.sink.appendChild(s,u),this._shadowOverlay=u,this._smooth=new r({read:e=>e===`x`?this._scrollX:this._scrollY,write:(e,t)=>e===`x`?this.setScrollX(t):this.setScrollY(t),clamp:(e,t)=>this.clampAxis(e,t)}),this._scrollbarV=new c(`vertical`),e.sink.appendChild(n,this._scrollbarV.getElement(!0)),this._scrollbarV.on(`scroll`,e=>{this._smooth.reset(),this.setScrollY(e)}),this._scrollbarH=new c(`horizontal`),e.sink.appendChild(n,this._scrollbarH.getElement(!0)),this._scrollbarH.on(`scroll`,e=>{this._smooth.reset(),this.setScrollX(e)}),this._owner.setTouchAction(`none`),i.addSubtreeListener(this._owner,`wheel`,e=>this.onWheel(e),{passive:!1}),this.attachTouchHandlers()}ownedHandles(){return[this._clipBox,this._rowsContainer,this._shadowOverlay]}getRowsContainer(){return this._rowsContainer}getScrollY(){return this._scrollY}getScrollX(){return this._scrollX}setScrollY(e){let n=Math.max(0,this._contentHeight-this.effectiveViewportH()),r=t.clamp(e,0,n);return r===this._scrollY?this:(this._scrollY=r,this.updateTransform(),this._onScroll(),this)}setScrollX(e){let n=Math.max(0,this._contentWidth-this.effectiveViewportW()),r=t.clamp(e,0,n);return r===this._scrollX?this:(this._scrollX=r,this.updateTransform(),this._onScroll(),this)}clampAxis(e,n){let r=e===`x`?Math.max(0,this._contentWidth-this.effectiveViewportW()):Math.max(0,this._contentHeight-this.effectiveViewportH());return t.clamp(n,0,r)}resetWheelEase(){this._smooth.reset()}computeScrollbarVisibility(e,t){let n=this._owner.getHeight()||0,r=this._owner.getWidth()||0,i=this._scrollbarV.getTrackWidth(),a=!1,o=!1;for(let s=0;s<2;s++){let s=n-(o?i:0),c=r-(a?i:0);a=t>s,o=e>c}let s=n-(o?i:0),c=r-(a?i:0);return{vVisible:a,hVisible:o,effW:c,effH:s}}effectiveViewportH(){return this.computeScrollbarVisibility(this._contentWidth,this._contentHeight).effH}effectiveViewportW(){return this.computeScrollbarVisibility(this._contentWidth,this._contentHeight).effW}getViewportWidth(){return this.effectiveViewportW()}clampToContent(e,t){this._contentWidth=e,this._contentHeight=t;let n=Math.max(0,t-this.effectiveViewportH()),r=Math.max(0,e-this.effectiveViewportW()),i=!1;this._scrollY>n&&(this._scrollY=n,i=!0),this._scrollX>r&&(this._scrollX=r,i=!0),i&&this.updateTransform()}layoutScrollbars(t,n){this._contentWidth=t,this._contentHeight=n;let r=this._scrollbarV.getTrackWidth(),i=this._owner.getWidth(),a=this._owner.getHeight(),{effW:o,effH:s}=this.computeScrollbarVisibility(t,n),c=Math.max(0,n-s),l=Math.max(0,t-o);(this._scrollY>c||this._scrollX>l)&&(this._scrollY>c&&(this._scrollY=c),this._scrollX>l&&(this._scrollX=l),this.updateTransform()),this._scrollbarV.setX(Math.max(0,i-r)),this._scrollbarV.setHeight(s),this._scrollbarV.setMetrics(s,n,this._scrollY),this._scrollbarH.setY(Math.max(0,a-r)),this._scrollbarH.setWidth(o),this._scrollbarH.setMetrics(o,t,this._scrollX),e.sink.apply(this._clipBox,{style:{width:o+`px`,height:s+`px`}}),this.updateShadows()}updateTransform(){e.sink.apply(this._rowsContainer,{style:{transform:`translate3d(${-this._scrollX}px, ${-this._scrollY}px, 0)`}}),this.updateShadows()}updateShadows(){let{effW:e,effH:t}=this.computeScrollbarVisibility(this._contentWidth,this._contentHeight),n=Math.max(0,this._contentWidth-e),r=Math.max(0,this._contentHeight-t);this.setShadowEdge(`top`,`--ts-ss-top`,a(this._scrollY)),this.setShadowEdge(`bottom`,`--ts-ss-bottom`,a(r-this._scrollY)),this.setShadowEdge(`left`,`--ts-ss-left`,a(this._scrollX)),this.setShadowEdge(`right`,`--ts-ss-right`,a(n-this._scrollX))}setShadowEdge(t,n,r){let i=Math.round(r*100);this._shadowEdges[t]!==i&&(this._shadowEdges[t]=i,e.sink.apply(this._shadowOverlay,{style:{[n]:s(i)}}))}onWheel(e){if(n(e)){if(e.preventDefault(),e.shiftKey&&e.deltaY!==0&&e.deltaX===0){this._smooth.scrollBy(e.deltaY,0);return}this._smooth.scrollBy(e.deltaX,e.deltaY)}}attachTouchHandlers(){let t=!1,n=0,r=0,a=0,o=0,s=[],c=null,cancelMomentum=()=>{c!==null&&(e.sink.cancelAnimationFrame(c),c=null)};i.addSubtreeListener(this._owner,`touchstart`,e=>{e.touches.length===1&&(cancelMomentum(),this._smooth.reset(),t=!0,n=e.touches[0].clientY,r=e.touches[0].clientX,a=this._scrollY,o=this._scrollX,s=[{time:performance.now(),x:r,y:n}])}),i.addSubtreeListener(this._owner,`touchmove`,e=>{if(!t||e.touches.length!==1)return;let i=e.touches[0].clientY,c=e.touches[0].clientX,l=performance.now();for(s.push({time:l,x:c,y:i});s.length>1&&s[0].time<l-150;)s.shift();this.setScrollY(a+(n-i)),this.setScrollX(o+(r-c))});let startMomentum=()=>{if(s.length<2)return;let t=s[s.length-1];if(performance.now()-t.time>50)return;let n=t.time-80,r=s.length-1;for(;r>0&&s[r-1].time>=n;)r--;let i=s[r],a=t.time-i.time;if(a<=0)return;let o=(i.y-t.y)/a,l=(i.x-t.x)/a;if(Math.sqrt(l*l+o*o)<.1)return;let u=performance.now(),step=()=>{let t=performance.now(),n=t-u;u=t;let r=.95**(n/16.667);if(o*=r,l*=r,Math.sqrt(l*l+o*o)<.02){c=null;return}let i=this._scrollY,a=this._scrollX;if(this.setScrollY(this._scrollY+o*n),this.setScrollX(this._scrollX+l*n),this._scrollY===i&&(o=0),this._scrollX===a&&(l=0),o===0&&l===0){c=null;return}c=e.sink.requestAnimationFrame(step)};c=e.sink.requestAnimationFrame(step)};i.addSubtreeListener(this._owner,`touchend`,()=>{t&&(t=!1,startMomentum())}),i.addSubtreeListener(this._owner,`touchcancel`,()=>{t=!1,cancelMomentum()})}};export{VirtualScroller as t};
2
- //# sourceMappingURL=VirtualScroller-DwEEJE1_.js.map
1
+ import{t as e}from"./DOM-BeR95tE8.js";import{l as t}from"./Position-DRSlWn4Z.js";import{g as n,h as r,x as i}from"./Component-CHTzJxEf.js";import{i as a,n as o,r as s,t as c}from"./Scrollbar-CiVV3axj.js";var VirtualScroller=class{_owner;_onScroll;_clipBox;_rowsContainer;_scrollbarV;_scrollbarH;_scrollX=0;_scrollY=0;_contentWidth=0;_contentHeight=0;_smooth;_shadowOverlay;_shadowEdges={top:0,bottom:0,left:0,right:0};constructor(t,n,a){this._owner=t,this._onScroll=a;let s=e.sink.createElement(`div`);e.sink.apply(s,{style:{position:`absolute`,top:`0`,left:`0`,width:`100%`,height:`100%`,overflow:`hidden`}}),e.sink.appendChild(n,s),this._clipBox=s;let l=e.sink.createElement(`div`);e.sink.apply(l,{style:{position:`absolute`,top:`0`,left:`0`,width:`100%`,transform:`translate3d(0, 0, 0)`,willChange:`transform`}}),e.sink.appendChild(s,l),this._rowsContainer=l;let u=e.sink.createElement(`div`);e.sink.apply(u,{style:{position:`absolute`,top:`0`,left:`0`,width:`100%`,height:`100%`,pointerEvents:`none`,boxShadow:o()}}),e.sink.appendChild(s,u),this._shadowOverlay=u,this._smooth=new r({read:e=>e===`x`?this._scrollX:this._scrollY,write:(e,t)=>e===`x`?this.setScrollX(t):this.setScrollY(t),clamp:(e,t)=>this.clampAxis(e,t)}),this._scrollbarV=new c(`vertical`),e.sink.appendChild(n,this._scrollbarV.getElement(!0)),this._scrollbarV.on(`scroll`,e=>{this._smooth.reset(),this.setScrollY(e)}),this._scrollbarH=new c(`horizontal`),e.sink.appendChild(n,this._scrollbarH.getElement(!0)),this._scrollbarH.on(`scroll`,e=>{this._smooth.reset(),this.setScrollX(e)}),this._owner.setTouchAction(`none`),i.addSubtreeListener(this._owner,`wheel`,e=>this.onWheel(e),{passive:!1}),this.attachTouchHandlers()}ownedHandles(){return[this._clipBox,this._rowsContainer,this._shadowOverlay]}getRowsContainer(){return this._rowsContainer}getScrollY(){return this._scrollY}getScrollX(){return this._scrollX}setScrollY(e){let n=Math.max(0,this._contentHeight-this.effectiveViewportH()),r=t.clamp(e,0,n);return r===this._scrollY?this:(this._scrollY=r,this.updateTransform(),this._onScroll(),this)}setScrollX(e){let n=Math.max(0,this._contentWidth-this.effectiveViewportW()),r=t.clamp(e,0,n);return r===this._scrollX?this:(this._scrollX=r,this.updateTransform(),this._onScroll(),this)}clampAxis(e,n){let r=e===`x`?Math.max(0,this._contentWidth-this.effectiveViewportW()):Math.max(0,this._contentHeight-this.effectiveViewportH());return t.clamp(n,0,r)}resetWheelEase(){this._smooth.reset()}computeScrollbarVisibility(e,t){let n=this._owner.getHeight()||0,r=this._owner.getWidth()||0,i=this._scrollbarV.getTrackWidth(),a=!1,o=!1;for(let s=0;s<2;s++){let s=n-(o?i:0),c=r-(a?i:0);a=t>s,o=e>c}let s=n-(o?i:0),c=r-(a?i:0);return{vVisible:a,hVisible:o,effW:c,effH:s}}effectiveViewportH(){return this.computeScrollbarVisibility(this._contentWidth,this._contentHeight).effH}effectiveViewportW(){return this.computeScrollbarVisibility(this._contentWidth,this._contentHeight).effW}getViewportWidth(){return this.effectiveViewportW()}clampToContent(e,t){this._contentWidth=e,this._contentHeight=t;let n=Math.max(0,t-this.effectiveViewportH()),r=Math.max(0,e-this.effectiveViewportW()),i=!1;this._scrollY>n&&(this._scrollY=n,i=!0),this._scrollX>r&&(this._scrollX=r,i=!0),i&&this.updateTransform()}layoutScrollbars(t,n){this._contentWidth=t,this._contentHeight=n;let r=this._scrollbarV.getTrackWidth(),i=this._owner.getWidth(),a=this._owner.getHeight(),{effW:o,effH:s}=this.computeScrollbarVisibility(t,n),c=Math.max(0,n-s),l=Math.max(0,t-o);(this._scrollY>c||this._scrollX>l)&&(this._scrollY>c&&(this._scrollY=c),this._scrollX>l&&(this._scrollX=l),this.updateTransform()),this._scrollbarV.setX(Math.max(0,i-r)),this._scrollbarV.setHeight(s),this._scrollbarV.setMetrics(s,n,this._scrollY),this._scrollbarH.setY(Math.max(0,a-r)),this._scrollbarH.setWidth(o),this._scrollbarH.setMetrics(o,t,this._scrollX),e.sink.apply(this._clipBox,{style:{width:o+`px`,height:s+`px`}}),this.updateShadows()}updateTransform(){e.sink.apply(this._rowsContainer,{style:{transform:`translate3d(${-this._scrollX}px, ${-this._scrollY}px, 0)`}}),this.updateShadows()}updateShadows(){let{effW:e,effH:t}=this.computeScrollbarVisibility(this._contentWidth,this._contentHeight),n=Math.max(0,this._contentWidth-e),r=Math.max(0,this._contentHeight-t);this.setShadowEdge(`top`,`--ts-ss-top`,a(this._scrollY)),this.setShadowEdge(`bottom`,`--ts-ss-bottom`,a(r-this._scrollY)),this.setShadowEdge(`left`,`--ts-ss-left`,a(this._scrollX)),this.setShadowEdge(`right`,`--ts-ss-right`,a(n-this._scrollX))}setShadowEdge(t,n,r){let i=Math.round(r*100);this._shadowEdges[t]!==i&&(this._shadowEdges[t]=i,e.sink.apply(this._shadowOverlay,{style:{[n]:s(i)}}))}onWheel(e){if(n(e)){if(e.preventDefault(),e.shiftKey&&e.deltaY!==0&&e.deltaX===0){this._smooth.scrollBy(e.deltaY,0);return}this._smooth.scrollBy(e.deltaX,e.deltaY)}}attachTouchHandlers(){let t=!1,n=0,r=0,a=0,o=0,s=[],c=null,cancelMomentum=()=>{c!==null&&(e.sink.cancelAnimationFrame(c),c=null)};i.addSubtreeListener(this._owner,`touchstart`,e=>{e.touches.length===1&&(cancelMomentum(),this._smooth.reset(),t=!0,n=e.touches[0].clientY,r=e.touches[0].clientX,a=this._scrollY,o=this._scrollX,s=[{time:performance.now(),x:r,y:n}])}),i.addSubtreeListener(this._owner,`touchmove`,e=>{if(!t||e.touches.length!==1)return;let i=e.touches[0].clientY,c=e.touches[0].clientX,l=performance.now();for(s.push({time:l,x:c,y:i});s.length>1&&s[0].time<l-150;)s.shift();this.setScrollY(a+(n-i)),this.setScrollX(o+(r-c))});let startMomentum=()=>{if(s.length<2)return;let t=s[s.length-1];if(performance.now()-t.time>50)return;let n=t.time-80,r=s.length-1;for(;r>0&&s[r-1].time>=n;)r--;let i=s[r],a=t.time-i.time;if(a<=0)return;let o=(i.y-t.y)/a,l=(i.x-t.x)/a;if(Math.sqrt(l*l+o*o)<.1)return;let u=performance.now(),step=()=>{let t=performance.now(),n=t-u;u=t;let r=.95**(n/16.667);if(o*=r,l*=r,Math.sqrt(l*l+o*o)<.02){c=null;return}let i=this._scrollY,a=this._scrollX;if(this.setScrollY(this._scrollY+o*n),this.setScrollX(this._scrollX+l*n),this._scrollY===i&&(o=0),this._scrollX===a&&(l=0),o===0&&l===0){c=null;return}c=e.sink.requestAnimationFrame(step)};c=e.sink.requestAnimationFrame(step)};i.addSubtreeListener(this._owner,`touchend`,()=>{t&&(t=!1,startMomentum())}),i.addSubtreeListener(this._owner,`touchcancel`,()=>{t=!1,cancelMomentum()})}};export{VirtualScroller as t};
2
+ //# sourceMappingURL=VirtualScroller-BPy6KmSX.js.map