@jimka/typescript-ui 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/{AbstractBooleanInput-C3O4kv7_.js → AbstractBooleanInput-DSwKBAic.js} +2 -2
- package/dist/lib/{AbstractBooleanInput-C3O4kv7_.js.map → AbstractBooleanInput-DSwKBAic.js.map} +1 -1
- package/dist/lib/{AbstractInput-DFWniHqq.js → AbstractInput-BmWGJTs1.js} +2 -2
- package/dist/lib/{AbstractInput-DFWniHqq.js.map → AbstractInput-BmWGJTs1.js.map} +1 -1
- package/dist/lib/{AnchorConstraints-CSUcj8Wx.js → AnchorConstraints-CVrMP9rz.js} +2 -2
- package/dist/lib/{AnchorConstraints-CSUcj8Wx.js.map → AnchorConstraints-CVrMP9rz.js.map} +1 -1
- package/dist/lib/{AnimatedDropdown-e9urCXK1.js → AnimatedDropdown-DKas40B-.js} +2 -2
- package/dist/lib/{AnimatedDropdown-e9urCXK1.js.map → AnimatedDropdown-DKas40B-.js.map} +1 -1
- package/dist/lib/Border-Bz29z-_7.js +2 -0
- package/dist/lib/Border-Bz29z-_7.js.map +1 -0
- package/dist/lib/Button-bjpWruNc.js +2 -0
- package/dist/lib/{Button-B36rBMKC.js.map → Button-bjpWruNc.js.map} +1 -1
- package/dist/lib/{Card-P8iUH2-C.js → Card-BdfbvOD_.js} +2 -2
- package/dist/lib/{Card-P8iUH2-C.js.map → Card-BdfbvOD_.js.map} +1 -1
- package/dist/lib/ComboBox-CBw2VLLN.js +2 -0
- package/dist/lib/ComboBox-CBw2VLLN.js.map +1 -0
- package/dist/lib/Component-DHqrPwAw.js +2 -0
- package/dist/lib/Component-DHqrPwAw.js.map +1 -0
- package/dist/lib/Container-CAQpj4yb.js +2 -0
- package/dist/lib/{Container-xUbHk6l_.js.map → Container-CAQpj4yb.js.map} +1 -1
- package/dist/lib/{DOM-BeR95tE8.js → DOM-67StIm5G.js} +2 -2
- package/dist/lib/DOM-67StIm5G.js.map +1 -0
- package/dist/lib/DragChain-CYGuT7OA.js +2 -0
- package/dist/lib/DragChain-CYGuT7OA.js.map +1 -0
- package/dist/lib/DragManager-CHWCe3-8.js +2 -0
- package/dist/lib/DragManager-CHWCe3-8.js.map +1 -0
- package/dist/lib/{FieldDecorator-BaTGfKiW.js → FieldDecorator-CgAGcqCk.js} +2 -2
- package/dist/lib/{FieldDecorator-BaTGfKiW.js.map → FieldDecorator-CgAGcqCk.js.map} +1 -1
- package/dist/lib/{Fit-E-EIZv1k.js → Fit-DIYt2aQB.js} +2 -2
- package/dist/lib/{Fit-E-EIZv1k.js.map → Fit-DIYt2aQB.js.map} +1 -1
- package/dist/lib/Glyph-CeAIWyPB.js +2 -0
- package/dist/lib/Glyph-CeAIWyPB.js.map +1 -0
- package/dist/lib/{Grid-BXS2T8MT.js → Grid-ViA0joow.js} +2 -2
- package/dist/lib/{Grid-BXS2T8MT.js.map → Grid-ViA0joow.js.map} +1 -1
- package/dist/lib/GridConstraints-B1rxCw9B.js +2 -0
- package/dist/lib/GridConstraints-B1rxCw9B.js.map +1 -0
- package/dist/lib/Header-mKaqgfat.js +2 -0
- package/dist/lib/Header-mKaqgfat.js.map +1 -0
- package/dist/lib/IconText-BqwXA82-.js +2 -0
- package/dist/lib/IconText-BqwXA82-.js.map +1 -0
- package/dist/lib/{LayoutSerialization-D1OHmxSB.js → LayoutSerialization-D82UtnDD.js} +2 -2
- package/dist/lib/{LayoutSerialization-D1OHmxSB.js.map → LayoutSerialization-D82UtnDD.js.map} +1 -1
- package/dist/lib/LayoutSizes-DG9aW9k6.js +2 -0
- package/dist/lib/LayoutSizes-DG9aW9k6.js.map +1 -0
- package/dist/lib/List-CUntS8ux.js +2 -0
- package/dist/lib/List-CUntS8ux.js.map +1 -0
- package/dist/lib/MemoryStore-DT6iWWco.js.map +1 -1
- package/dist/lib/Menu-B_-SQj72.js +2 -0
- package/dist/lib/Menu-B_-SQj72.js.map +1 -0
- package/dist/lib/{MenuButton-Bjm1OyB-.js → MenuButton-C79l8Ysf.js} +2 -2
- package/dist/lib/{MenuButton-Bjm1OyB-.js.map → MenuButton-C79l8Ysf.js.map} +1 -1
- package/dist/lib/{Panel-D9lTLGqP.js → Panel-D2X33qR6.js} +2 -2
- package/dist/lib/{Panel-D9lTLGqP.js.map → Panel-D2X33qR6.js.map} +1 -1
- package/dist/lib/Position-Ck_KD4cR.js +2 -0
- package/dist/lib/Position-Ck_KD4cR.js.map +1 -0
- package/dist/lib/{ProgressSpinner-glv-29eK.js → ProgressSpinner-DCyq02pd.js} +2 -2
- package/dist/lib/{ProgressSpinner-glv-29eK.js.map → ProgressSpinner-DCyq02pd.js.map} +1 -1
- package/dist/lib/{RadioButton-R4S3c9WD.js → RadioButton-BJIAQkII.js} +2 -2
- package/dist/lib/{RadioButton-R4S3c9WD.js.map → RadioButton-BJIAQkII.js.map} +1 -1
- package/dist/lib/{RovingTabIndex-Dgt01LOE.js → RovingTabIndex-DFJyoveq.js} +2 -2
- package/dist/lib/{RovingTabIndex-Dgt01LOE.js.map → RovingTabIndex-DFJyoveq.js.map} +1 -1
- package/dist/lib/Scrollbar-CSFK-Y57.js +2 -0
- package/dist/lib/Scrollbar-CSFK-Y57.js.map +1 -0
- package/dist/lib/Slider-C-kQQYCj.js +2 -0
- package/dist/lib/Slider-C-kQQYCj.js.map +1 -0
- package/dist/lib/{Spacer-B57i2-_G.js → Spacer-D159iJy9.js} +2 -2
- package/dist/lib/{Spacer-B57i2-_G.js.map → Spacer-D159iJy9.js.map} +1 -1
- package/dist/lib/TabButton-CHkXBgCj.js +2 -0
- package/dist/lib/TabButton-CHkXBgCj.js.map +1 -0
- package/dist/lib/{Text-Bni4icFY.js → Text-D5pg7taZ.js} +2 -2
- package/dist/lib/{Text-Bni4icFY.js.map → Text-D5pg7taZ.js.map} +1 -1
- package/dist/lib/Tooltip-DzK6LxUt.js +3 -0
- package/dist/lib/{Tooltip-BUy6uFKN.js.map → Tooltip-DzK6LxUt.js.map} +1 -1
- package/dist/lib/VBox-BNegDPs_.js +2 -0
- package/dist/lib/VBox-BNegDPs_.js.map +1 -0
- package/dist/lib/VirtualScroller-zQr-vKrO.js +2 -0
- package/dist/lib/VirtualScroller-zQr-vKrO.js.map +1 -0
- package/dist/lib/component/button.es.js +1 -1
- package/dist/lib/component/chart.es.js +1 -1
- package/dist/lib/component/chart.es.js.map +1 -1
- package/dist/lib/component/container.es.js +1 -1
- package/dist/lib/component/container.es.js.map +1 -1
- package/dist/lib/component/diagram.es.js +1 -1
- package/dist/lib/component/diagram.es.js.map +1 -1
- package/dist/lib/component/display.es.js +1 -1
- package/dist/lib/component/display.es.js.map +1 -1
- package/dist/lib/component/editor.es.js +2 -2
- package/dist/lib/component/editor.es.js.map +1 -1
- package/dist/lib/component/input.es.js +1 -1
- package/dist/lib/component/input.es.js.map +1 -1
- package/dist/lib/component/list.es.js +1 -1
- package/dist/lib/component/list.es.js.map +1 -1
- package/dist/lib/component/menubar.es.js +1 -1
- package/dist/lib/component/menubar.es.js.map +1 -1
- package/dist/lib/component/table.es.js +2 -2
- package/dist/lib/component/table.es.js.map +1 -1
- package/dist/lib/component/tree.es.js +1 -1
- package/dist/lib/component/tree.es.js.map +1 -1
- package/dist/lib/core.es.js +1 -1
- package/dist/lib/core.es.js.map +1 -1
- package/dist/lib/layout.es.js +1 -1
- package/dist/lib/layout.es.js.map +1 -1
- package/dist/lib/overlay.es.js +1 -1
- package/dist/lib/overlay.es.js.map +1 -1
- package/dist/lib/primitive.es.js +1 -1
- package/dist/lib/router.es.js +1 -1
- package/dist/lib/selectionsEqual-4r5R7pTK.js +2 -0
- package/dist/lib/selectionsEqual-4r5R7pTK.js.map +1 -0
- package/dist/lib/types/component/button/TabCloseButton.d.ts +1 -1
- package/dist/lib/types/component/container/CollapseButton.d.ts +1 -1
- package/dist/lib/types/component/container/DialogBackdrop.d.ts +1 -1
- package/dist/lib/types/component/container/MenuItem.d.ts +4 -0
- package/dist/lib/types/component/container/MenuSeparator.d.ts +1 -1
- package/dist/lib/types/component/container/Scrollbar.d.ts +1 -0
- package/dist/lib/types/component/container/SplitGutter.d.ts +2 -2
- package/dist/lib/types/component/container/StatusBar.d.ts +1 -1
- package/dist/lib/types/component/container/VirtualScroller.d.ts +1 -0
- package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
- package/dist/lib/types/component/diagram/DiagramNode.d.ts +1 -1
- package/dist/lib/types/component/display/Canvas.d.ts +8 -2
- package/dist/lib/types/component/display/Glyph.d.ts +2 -1
- package/dist/lib/types/component/display/Header.d.ts +1 -1
- package/dist/lib/types/component/display/IconLabel.d.ts +1 -1
- package/dist/lib/types/component/display/IconText.d.ts +1 -1
- package/dist/lib/types/component/display/Image.d.ts +1 -1
- package/dist/lib/types/component/display/Video.d.ts +1 -1
- package/dist/lib/types/component/display/WebGLCanvas.d.ts +8 -2
- package/dist/lib/types/component/editor/CodeEditor.d.ts +1 -1
- package/dist/lib/types/component/input/AbstractPickerField.d.ts +1 -1
- package/dist/lib/types/component/input/AutoCompleteDropdown.d.ts +1 -1
- package/dist/lib/types/component/input/Link.d.ts +1 -1
- package/dist/lib/types/component/input/NumberSpinner.d.ts +1 -1
- package/dist/lib/types/component/input/PasswordField.d.ts +1 -1
- package/dist/lib/types/component/input/SpinButton.d.ts +1 -1
- package/dist/lib/types/component/input/TextArea.d.ts +1 -1
- package/dist/lib/types/component/input/TextField.d.ts +3 -1
- package/dist/lib/types/component/input/UsernameField.d.ts +1 -1
- package/dist/lib/types/component/list/AbstractMarkerList.d.ts +9 -1
- package/dist/lib/types/component/list/BulletedList.d.ts +1 -0
- package/dist/lib/types/component/list/ListItem.d.ts +10 -4
- package/dist/lib/types/component/list/NumberedList.d.ts +1 -0
- package/dist/lib/types/component/menubar/MenuBarButton.d.ts +1 -1
- package/dist/lib/types/component/menubar/ToolBar.d.ts +1 -1
- package/dist/lib/types/component/menubar/ToolBarSeparator.d.ts +1 -1
- package/dist/lib/types/component/shared/VirtualRowView.d.ts +13 -1
- package/dist/lib/types/component/table/Body.d.ts +15 -2
- package/dist/lib/types/component/table/CellGeometry.d.ts +6 -0
- package/dist/lib/types/component/table/Column.d.ts +4 -0
- package/dist/lib/types/component/table/ColumnConfig.d.ts +3 -0
- package/dist/lib/types/component/table/Header.d.ts +24 -2
- package/dist/lib/types/component/table/Row.d.ts +13 -2
- package/dist/lib/types/component/table/Table.d.ts +25 -4
- package/dist/lib/types/component/table/TableExporter.d.ts +1 -1
- package/dist/lib/types/component/table/TablePanel.d.ts +2 -1
- package/dist/lib/types/component/table/cell/Cell.d.ts +1 -1
- package/dist/lib/types/component/table/cell/Header.d.ts +3 -0
- package/dist/lib/types/component/table/index.d.ts +1 -1
- package/dist/lib/types/component/tree/Tree.d.ts +1 -0
- package/dist/lib/types/core/Aria.d.ts +1 -1
- package/dist/lib/types/core/Body.d.ts +8 -2
- package/dist/lib/types/core/Component.d.ts +6 -0
- package/dist/lib/types/core/DOM.d.ts +4 -0
- package/dist/lib/types/core/DragChain.d.ts +3 -0
- package/dist/lib/types/core/Favicon.d.ts +7 -0
- package/dist/lib/types/core/FirstLayoutGate.d.ts +5 -0
- package/dist/lib/types/core/Theme.d.ts +1 -0
- package/dist/lib/types/core/Util.d.ts +1 -0
- package/dist/lib/types/core/index.d.ts +2 -0
- package/dist/lib/types/layout/Accordion.d.ts +0 -1
- package/dist/lib/types/layout/FlowLayout.d.ts +4 -0
- package/dist/lib/types/layout/HFlow.d.ts +3 -0
- package/dist/lib/types/layout/VFlow.d.ts +1 -0
- package/dist/lib/types/overlay/AbstractWindow.d.ts +1 -1
- package/dist/lib/types/overlay/Dock.d.ts +1 -0
- package/dist/lib/types/overlay/DropZoneOverlay.d.ts +1 -0
- package/dist/lib/types/overlay/Popover.d.ts +1 -1
- package/dist/lib/validation.es.js +1 -1
- package/llms.txt +1 -0
- package/package.json +2 -2
- package/dist/lib/Border-Dq6dXkmS.js +0 -2
- package/dist/lib/Border-Dq6dXkmS.js.map +0 -1
- package/dist/lib/Button-B36rBMKC.js +0 -2
- package/dist/lib/ComboBox-BjAhXXkY.js +0 -2
- package/dist/lib/ComboBox-BjAhXXkY.js.map +0 -1
- package/dist/lib/Component-CHTzJxEf.js +0 -2
- package/dist/lib/Component-CHTzJxEf.js.map +0 -1
- package/dist/lib/Container-xUbHk6l_.js +0 -2
- package/dist/lib/DOM-BeR95tE8.js.map +0 -1
- package/dist/lib/DragManager-T5Oljd0F.js +0 -2
- package/dist/lib/DragManager-T5Oljd0F.js.map +0 -1
- package/dist/lib/Glyph-CnNslL95.js +0 -2
- package/dist/lib/Glyph-CnNslL95.js.map +0 -1
- package/dist/lib/GridConstraints-D_kbd-h2.js +0 -2
- package/dist/lib/GridConstraints-D_kbd-h2.js.map +0 -1
- package/dist/lib/HBox-Dm3xgsDE.js +0 -2
- package/dist/lib/HBox-Dm3xgsDE.js.map +0 -1
- package/dist/lib/Header-CqFc2OUx.js +0 -2
- package/dist/lib/Header-CqFc2OUx.js.map +0 -1
- package/dist/lib/IconText-CTM2r0Tv.js +0 -2
- package/dist/lib/IconText-CTM2r0Tv.js.map +0 -1
- package/dist/lib/LayoutSizes-CvMmMgHJ.js +0 -2
- package/dist/lib/LayoutSizes-CvMmMgHJ.js.map +0 -1
- package/dist/lib/List-DHeOPAK2.js +0 -2
- package/dist/lib/List-DHeOPAK2.js.map +0 -1
- package/dist/lib/Menu-B6ippw-p.js +0 -2
- package/dist/lib/Menu-B6ippw-p.js.map +0 -1
- package/dist/lib/Position-DRSlWn4Z.js +0 -2
- package/dist/lib/Position-DRSlWn4Z.js.map +0 -1
- package/dist/lib/Scrollbar-CiVV3axj.js +0 -2
- package/dist/lib/Scrollbar-CiVV3axj.js.map +0 -1
- package/dist/lib/Slider-_JVvuQ8M.js +0 -2
- package/dist/lib/Slider-_JVvuQ8M.js.map +0 -1
- package/dist/lib/TabButton-DEypdlZf.js +0 -2
- package/dist/lib/TabButton-DEypdlZf.js.map +0 -1
- package/dist/lib/Tooltip-BUy6uFKN.js +0 -3
- package/dist/lib/VBox-Dr7Ge9M6.js +0 -2
- package/dist/lib/VBox-Dr7Ge9M6.js.map +0 -1
- package/dist/lib/VirtualScroller-BPy6KmSX.js +0 -2
- package/dist/lib/VirtualScroller-BPy6KmSX.js.map +0 -1
- package/dist/lib/selectionsEqual-2PJrcimg.js +0 -2
- package/dist/lib/selectionsEqual-2PJrcimg.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"../DOM-BeR95tE8.js";import{m as t,t as n,x as r}from"../Component-CHTzJxEf.js";import{t as i}from"../ListenerBag-WPdKuuds.js";import{t as a}from"../Glyph-CnNslL95.js";import{t as o}from"../Text-Bni4icFY.js";import{t as s}from"../ProgressSpinner-glv-29eK.js";import{caret_down as c}from"../glyphs/solid/caret_down.es.js";import{caret_right as l}from"../glyphs/solid/caret_right.es.js";import{n as u,t as d}from"../selectionsEqual-2PJrcimg.js";var TreeNodeRenderer=class extends n{constructor(){super()}},LabelTreeNodeRenderer=class extends TreeNodeRenderer{_label;constructor(){super(),this.clearInsets(),this._label=new o,this._label.clearInsets(),this._label.setAutoMeasure(!1)}getLabel(){return this._label}update(e){this._label.setText(e.node.label),this._label.measure()}getContentWidth(){return this._label.getPreferredSize()?.width??0}layoutChildren(e,t){let n=this.getContentWidth();this._label.setAutoCommitStyle(!1),this._label.setX(0),this._label.setY(0),this._label.setWidth(n),this._label.setHeight(t),this._label.setLineHeight(t),this._label.setAutoCommitStyle(!0)}init(t){super.init(t);let n=t||this.getElement();return n&&e.sink.appendChild(n,this._label.getElement(!0)),this}};a.register(c,l);var TreeRow=class extends n{_toggle=null;_spinner=null;_renderer;_node=null;_depth=0;constructor(e=()=>new LabelTreeNodeRenderer){super(),this.getAria().setRole(`treeitem`),this._renderer=e()}getToggle(){return this._toggle}getRenderer(){return this._renderer}setRenderer(t){let n=this.getElement();if(n){let r=this._renderer.getElement();r&&e.source.getParentNode(r)===n&&e.sink.removeChild(n,r),e.sink.appendChild(n,t.getElement(!0))}return this._renderer=t,this}getNode(){return this._node}getDepth(){return this._depth}setRowData(t,n,r,i,o,c,l,u){if(this._node=t,this._depth=n,this._toggle){let t=this.getElement();t&&e.sink.removeChild(t,this._toggle.getElement(!0)),this._toggle=null}if(this._spinner){let t=this.getElement();t&&e.sink.removeChild(t,this._spinner.getElement(!0)),this._spinner=null}if(u){let t=new s;this._spinner=t;let n=this.getElement();n&&e.sink.appendChild(n,t.getElement(!0))}else if(r){let t=new a(i?`caret-down`:`caret-right`);t.setCursor(`pointer`),t.clearInsets(),t.getAria().setHidden(!0),this._toggle=t;let n=this.getElement();n&&e.sink.appendChild(n,t.getElement(!0))}return this._renderer.update({node:t,depth:n,expanded:i,selected:l,hasChildren:r}),this.getAria().setLevel(n+1),this.getAria().setExpanded(r?i:null),this.getAria().setSetSize(o),this.getAria().setPosInSet(c),this}layoutChildren(e,t){let n=this._depth*t;if(this._toggle){let t=this._toggle.getPreferredSize()?.height??e;this._toggle.setAutoCommitStyle(!1),this._toggle.setX(n),this._toggle.setY(Math.max(0,(e-t)/2)),this._toggle.setWidth(20),this._toggle.setHeight(t),this._toggle.setAutoCommitStyle(!0)}this._spinner&&(this._spinner.setAutoCommitStyle(!1),this._spinner.setX(n),this._spinner.setY(0),this._spinner.setWidth(20),this._spinner.setHeight(e),this._spinner.setAutoCommitStyle(!0),this._spinner.doLayout());let r=n+20,i=this.getWidth()||0,a=Math.max(0,i-r);this._renderer.setAutoCommitStyle(!1),this._renderer.setX(r),this._renderer.setY(0),this._renderer.setWidth(a),this._renderer.setHeight(e),this._renderer.setAutoCommitStyle(!0),this._renderer.layoutChildren(a,e)}getContentWidth(e){let t=this._depth*e,n=this._renderer.getContentWidth();return t+20+n}init(t){super.init(t);let n=t||this.getElement();return n&&e.sink.appendChild(n,this._renderer.getElement(!0)),this}},f=t(TreeRow),p=16,m=24,h=`var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))`,Tree=class extends u{_nodes=[];_expandedNodes=new Set;_flatRows=[];_lastRowWidth=0;_maxContentWidth=0;_selectedNodes=new Set;_loadingNodes=new Set;_loadedNodes=new Set;_anchorNode=null;_focusNode=null;_listeners=new i;_rendererFactory=()=>new LabelTreeNodeRenderer;constructor(e){super(e),this.setOverflow(`hidden`),this.setBackgroundColor(`var(--ts-ui-input-bg, rgb(255, 255, 255))`),this.setPreferredSize({width:200,height:300}),this.setMaxSize({width:2**53-1,height:2**53-1}),this.getAria().setRole(`tree`),this.getAria().setTabIndex(0),this.getAria().setMultiselectable(!0),this.applyListeners(e?.listeners)}setNodes(e){return this._nodes=e,this._expandedNodes.clear(),this._selectedNodes.clear(),this._loadingNodes.clear(),this._loadedNodes.clear(),this._anchorNode=null,this._focusNode=null,this._flatten(),this.getElement()&&(this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()),this}expandAll(){let addExpandable=e=>{for(let t of e)t.children&&t.children.length>0&&(this._expandedNodes.add(t),addExpandable(t.children))};return addExpandable(this._nodes),this._reflattenAndRender(),this}getRowHeight(){return m}createPoolRow(){return new f(this._rendererFactory)}getNodes(){return this._nodes}getSelectedNode(){return this._anchorNode}getSelectedNodes(){return Array.from(this._selectedNodes)}selectNode(e){let t=this._flatRows.findIndex(t=>t.node===e);return t<0?this:(this._selectedNodes.clear(),this._selectedNodes.add(e),this._anchorNode=e,this._focusNode=e,this._updateSelectionStyle(),this._scrollIntoView(t),this.renderWindow(),this._updateActiveDescendant(),this)}async revealByPredicate(e){let t=await this._findPath(this._nodes,e,[]);if(t===null)return null;for(let e=0;e<t.length-1;e++)this._expandedNodes.add(t[e]);this._reflattenAndRender();let n=t[t.length-1],r=this._flatRows.findIndex(e=>e.node===n);return r>=0&&this._scrollIntoView(r),n}async _findPath(e,t,n){for(let r of e){let e=[...n,r];if(t(r.data,r))return e;let i=await this._ensureChildrenLoaded(r);if(i.length>0){let n=await this._findPath(i,t,e);if(n!==null)return n}}return null}async _ensureChildrenLoaded(e){if(e.children&&e.children.length>0)return e.children;if(e.loadChildren!==void 0&&!this._loadedNodes.has(e))try{let t=await e.loadChildren();return e.children=t,this._loadedNodes.add(e),t}catch{return[]}return e.children??[]}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}setRendererFactory(e){this._rendererFactory=e;for(let t of this._rowPool)t.setRenderer(e());return this._boundIndices.fill(-1),this.invalidateGeom(),this.getElement()&&this.renderWindow(),this}getRendererFactory(){return this._rendererFactory}_isExpandable(e){return!!(e.children&&e.children.length>0)||e.hasChildren===!0}_flatten(){this._flatRows=[],this._maxContentWidth=0;let recurse=(e,t)=>{let n=e.length;for(let r=0;r<e.length;r++){let i=e[r];this._flatRows.push({node:i,depth:t,siblingCount:n,posInSet:r+1}),this._isExpandable(i)&&this._expandedNodes.has(i)&&i.children&&recurse(i.children,t+1)}};recurse(this._nodes,0)}_reflattenAndRender(){this._flatten(),this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()}_onToggle(e){if(this._expandedNodes.has(e)){this._expandedNodes.delete(e),this._reflattenAndRender();return}if(!(e.loadChildren!==void 0&&!this._loadedNodes.has(e)&&!(e.children&&e.children.length))){this._expandedNodes.add(e),this._reflattenAndRender();return}this._loadingNodes.has(e)||this._loadAndExpand(e)}async _loadAndExpand(e){this._loadingNodes.add(e),this._reflattenAndRender();try{let t=await e.loadChildren();if(!this._loadingNodes.has(e))return;e.children=t,this._loadedNodes.add(e),this._expandedNodes.add(e)}catch(t){if(!this._loadingNodes.has(e))return;this.emit(`loaderror`,e,t)}finally{this._loadingNodes.delete(e),this._reflattenAndRender()}}_notifySelectionChange(e){d(e,this._selectedNodes)||this.emit(`selection`,this.getSelectedNodes())}_rangeSelect(e,t){let n=Math.min(e,t),r=Math.max(e,t);this._selectedNodes.clear();for(let e=n;e<=r;e++)this._selectedNodes.add(this._flatRows[e].node)}_selectAtIndex(e){if(e<0||e>=this._flatRows.length)return;let t=this._flatRows[e].node,n=new Set(this._selectedNodes);this._selectedNodes.clear(),this._selectedNodes.add(t),this._anchorNode=t,this._focusNode=t,this._updateSelectionStyle(),this._scrollIntoView(e),this.renderWindow(),this._updateActiveDescendant(),this._notifySelectionChange(n)}_extendSelectionTo(e){if(e<0||e>=this._flatRows.length)return;let t=new Set(this._selectedNodes),n=this._anchorNode?this._flatRows.findIndex(e=>e.node===this._anchorNode):e;this._rangeSelect(n<0?e:n,e),this._focusNode=this._flatRows[e].node,this._updateSelectionStyle(),this._scrollIntoView(e),this.renderWindow(),this._updateActiveDescendant(),this._notifySelectionChange(t)}_scrollIntoView(e){this.scrollRowIntoView(e)}_onKeyDown(e){if(!new Set([`ArrowDown`,`ArrowUp`,`ArrowLeft`,`ArrowRight`,`Home`,`End`]).has(e.key))return;let t=this._flatRows;if(t.length===0)return;e.preventDefault();let n=this._focusNode?t.findIndex(e=>e.node===this._focusNode):-1;if(e.key===`ArrowDown`){let r=n<0?0:Math.min(n+1,t.length-1);e.shiftKey?this._extendSelectionTo(r):this._selectAtIndex(r)}else if(e.key===`ArrowUp`){let t=n<0?0:Math.max(n-1,0);e.shiftKey?this._extendSelectionTo(t):this._selectAtIndex(t)}else if(e.key===`Home`)e.shiftKey?this._extendSelectionTo(0):this._selectAtIndex(0);else if(e.key===`End`){let n=t.length-1;e.shiftKey?this._extendSelectionTo(n):this._selectAtIndex(n)}else if(e.key===`ArrowRight`){if(n<0){this._selectAtIndex(0);return}let{node:e}=t[n];if(!this._isExpandable(e))return;this._expandedNodes.has(e)?this._selectAtIndex(n+1):this._onToggle(e)}else if(e.key===`ArrowLeft`){if(n<0)return;let{node:e,depth:r}=t[n];if(this._isExpandable(e)&&this._expandedNodes.has(e)){this._onToggle(e);let t=this._flatRows.findIndex(t=>t.node===e);t>=0&&this._scrollIntoView(t)}else if(r>0){for(let e=n-1;e>=0;e--)if(t[e].depth===r-1){this._selectAtIndex(e);break}}}}_handleClick(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getToggle();if(a){let t=a.getElement();if(t&&(n===t||e.source.contains(t,n))){this._onToggle(i);return}}let o=r.getElement();if(!o||!e.source.contains(o,n)&&n!==o)continue;let s=this._flatRows.findIndex(e=>e.node===i);if(t.shiftKey&&this._anchorNode)this._extendSelectionTo(s);else if(t.ctrlKey||t.metaKey){let e=new Set(this._selectedNodes);this._selectedNodes.has(i)?this._selectedNodes.delete(i):this._selectedNodes.add(i),this._anchorNode=i,this._focusNode=i,this._updateSelectionStyle(),this._notifySelectionChange(e)}else this._selectAtIndex(s);return}}_handleContextMenu(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getElement();if(!(!a||!e.source.contains(a,n)&&n!==a)){t.preventDefault(),this.emit(`contextmenu`,i,t);return}}}_handleDblClick(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getElement();if(!a||!e.source.contains(a,n)&&n!==a)continue;t.preventDefault(),this.emit(`dblclick`,i);let o=r.getToggle(),s=o?o.getElement():void 0;!(s&&(n===s||e.source.contains(s,n)))&&this._isExpandable(i)&&this._onToggle(i);return}}_updateSelectionStyle(){for(let t of this._rowPool){let n=t.getElement();if(!n)continue;let r=t.getNode(),i=r!==null&&this._selectedNodes.has(r),a=r!==null&&r===this._focusNode;i?e.sink.apply(n,{style:{"background-color":h}}):e.sink.apply(n,{style:{"background-color":null}}),a?e.sink.apply(n,{style:{outline:`2px solid var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))`,"outline-offset":`-2px`}}):e.sink.apply(n,{style:{outline:null,"outline-offset":null}}),t.getAria().setSelected(i)}}_updateActiveDescendant(){if(!this._focusNode){this.getAria().setActiveDescendant(``);return}for(let e of this._rowPool)if(e.getNode()===this._focusNode){this.getAria().setActiveDescendant(e.getId());return}this.getAria().setActiveDescendant(``)}renderWindow(){if(!this.getElement()||!this._scroller)return;let e=this._scroller,t=this._flatRows.length,n=t*m;e.clampToContent(this._lastRowWidth,n);let r=this.getHeight()||0,i=this.computeVisibleWindow(e.getScrollY(),r,t),a=this.computePoolTarget(i.windowSize,r,t);this.growRowPool(a);let{reboundFlags:o,maxContentWidth:s}=this._bindAndMeasure(i.firstRow,i.windowSize);s>this._maxContentWidth&&(this._maxContentWidth=s);let c=Math.max(e.getViewportWidth(),this._maxContentWidth);c!==this._lastRowWidth&&(this._lastRowWidth=c,this.invalidateGeom()),this._positionRows(i.firstRow,i.windowSize,c,o),this.hideExcessPoolRows(i.windowSize),this._updateSelectionStyle(),e.layoutScrollbars(c,n)}_bindAndMeasure(e,t){let n=Array(t),r=0;for(let i=0;i<t;i++){let t=this._rowPool[i],a=e+i,o=this._flatRows[a],s=this._isExpandable(o.node),c=this._expandedNodes.has(o.node),l=this._loadingNodes.has(o.node),u=this._boundIndices[i]!==a;if(u){let e=this._selectedNodes.has(o.node);t.setRowData(o.node,o.depth,s,c,o.siblingCount,o.posInSet,e,l),this._boundIndices[i]=a}n[i]=u;let d=t.getContentWidth(p);d>r&&(r=d)}return{reboundFlags:n,maxContentWidth:r}}_positionRows(e,t,n,r){for(let i=0;i<t;i++){let t=this._rowPool[i],a=e+i,o=r[i],s=this.positionRow(i,a*m,n);(o||s)&&t.layoutChildren(m,p)}}init(e){super.init(e);let t=e||this.getElement();return t?(this.initScroller(t),r.addSubtreeListener(this,`click`,this._handleClick),r.addSubtreeListener(this,`contextmenu`,this._handleContextMenu),r.addSubtreeListener(this,`dblclick`,this._handleDblClick),r.addListener(this,`keydown`,this._onKeyDown),this.renderWindow(),this):this}doLayout(){return this.isLayoutPaused()?this:(super.doLayout(),this.renderWindow(),this)}},g=t(Tree),_=20,v=16,IconLabelTreeNodeRenderer=class extends TreeNodeRenderer{_icon=null;_label;_glyphResolver;_currentGlyph=null;constructor(e=()=>`file`){super(),this.clearInsets(),this._glyphResolver=e,this._label=new o,this._label.clearInsets(),this._label.setAutoMeasure(!1)}update(t){let n=this._glyphResolver(t.node,t);if(n!==this._currentGlyph){let t=this.getElement();if(t&&this._icon){let n=this._icon.getElement();n&&e.source.getParentNode(n)===t&&e.sink.removeChild(t,n)}this._icon=new a(n),this._icon.clearInsets(),this._currentGlyph=n,t&&e.sink.insertBefore(t,this._icon.getElement(!0),this._label.getElement()??null)}this._label.setText(t.node.label),this._label.measure()}getContentWidth(){return(this._icon?_:0)+(this._label.getPreferredSize()?.width??0)}layoutChildren(e,t){this._icon&&(this._icon.setAutoCommitStyle(!1),this._icon.setX(0),this._icon.setY(Math.max(0,(t-v)/2)),this._icon.setWidth(v),this._icon.setHeight(v),this._icon.setAutoCommitStyle(!0));let n=this._icon?_:0,r=Math.max(0,e-n);this._label.setAutoCommitStyle(!1),this._label.setX(n),this._label.setY(0),this._label.setWidth(r),this._label.setHeight(t),this._label.setLineHeight(t),this._label.setAutoCommitStyle(!0)}init(t){super.init(t);let n=t||this.getElement();return n?(this._icon&&e.sink.appendChild(n,this._icon.getElement(!0)),e.sink.appendChild(n,this._label.getElement(!0)),this):this}};export{IconLabelTreeNodeRenderer,LabelTreeNodeRenderer,g as Tree,TreeNodeRenderer};
|
|
1
|
+
import{t as e}from"../DOM-67StIm5G.js";import{S as t,h as n,t as r}from"../Component-DHqrPwAw.js";import{t as i}from"../ListenerBag-WPdKuuds.js";import{t as a}from"../Glyph-CeAIWyPB.js";import{t as o}from"../Text-D5pg7taZ.js";import{t as s}from"../ProgressSpinner-DCyq02pd.js";import{caret_down as c}from"../glyphs/solid/caret_down.es.js";import{caret_right as l}from"../glyphs/solid/caret_right.es.js";import{n as u,t as d}from"../selectionsEqual-4r5R7pTK.js";var TreeNodeRenderer=class extends r{constructor(){super()}},LabelTreeNodeRenderer=class extends TreeNodeRenderer{_label;constructor(){super(),this.clearInsets(),this._label=new o,this._label.clearInsets(),this._label.setAutoMeasure(!1)}getLabel(){return this._label}update(e){this._label.setText(e.node.label),this._label.measure()}getContentWidth(){return this._label.getPreferredSize()?.width??0}layoutChildren(e,t){let n=this.getContentBounds()??{x:0,y:0,height:t},r=this.getContentWidth();this._label.setAutoCommitStyle(!1),this._label.setX(n.x),this._label.setY(n.y),this._label.setWidth(r),this._label.setHeight(n.height),this._label.setLineHeight(n.height),this._label.setAutoCommitStyle(!0)}init(t){super.init(t);let n=t||this.getElement();return n&&e.sink.appendChild(n,this._label.getElement(!0)),this}};a.register(c,l);var TreeRow=class extends r{_toggle=null;_spinner=null;_renderer;_node=null;_depth=0;constructor(e=()=>new LabelTreeNodeRenderer){super(),this.getAria().setRole(`treeitem`),this._renderer=e()}getToggle(){return this._toggle}getRenderer(){return this._renderer}setRenderer(t){let n=this.getElement();if(n){let r=this._renderer.getElement();r&&e.source.getParentNode(r)===n&&e.sink.removeChild(n,r),e.sink.appendChild(n,t.getElement(!0))}return this._renderer=t,this}getNode(){return this._node}getDepth(){return this._depth}setRowData(t,n,r,i,o,c,l,u){if(this._node=t,this._depth=n,this._toggle){let t=this.getElement();t&&e.sink.removeChild(t,this._toggle.getElement(!0)),this._toggle=null}if(this._spinner){let t=this.getElement();t&&e.sink.removeChild(t,this._spinner.getElement(!0)),this._spinner=null}if(u){let t=new s;this._spinner=t;let n=this.getElement();n&&e.sink.appendChild(n,t.getElement(!0))}else if(r){let t=new a(i?`caret-down`:`caret-right`);t.setCursor(`pointer`),t.clearInsets(),t.getAria().setHidden(!0),this._toggle=t;let n=this.getElement();n&&e.sink.appendChild(n,t.getElement(!0))}return this._renderer.update({node:t,depth:n,expanded:i,selected:l,hasChildren:r}),this.getAria().setLevel(n+1),this.getAria().setExpanded(r?i:null),this.getAria().setSetSize(o),this.getAria().setPosInSet(c),this}layoutChildren(e,t){let n=this.getContentBounds()??{x:0,y:0,width:this.getWidth()||0,height:e},r=n.x+this._depth*t;if(this._toggle){let e=this._toggle.getPreferredSize()?.height??n.height;this._toggle.setAutoCommitStyle(!1),this._toggle.setX(r),this._toggle.setY(n.y+Math.max(0,(n.height-e)/2)),this._toggle.setWidth(20),this._toggle.setHeight(e),this._toggle.setAutoCommitStyle(!0)}this._spinner&&(this._spinner.setAutoCommitStyle(!1),this._spinner.setX(r),this._spinner.setY(n.y),this._spinner.setWidth(20),this._spinner.setHeight(n.height),this._spinner.setAutoCommitStyle(!0),this._spinner.doLayout());let i=r+20,a=Math.max(0,n.x+n.width-i);this._renderer.setAutoCommitStyle(!1),this._renderer.setX(i),this._renderer.setY(n.y),this._renderer.setWidth(a),this._renderer.setHeight(n.height),this._renderer.setAutoCommitStyle(!0),this._renderer.layoutChildren(a,n.height)}getContentWidth(e){let t=this._depth*e,n=this._renderer.getContentWidth();return t+20+n}init(t){super.init(t);let n=t||this.getElement();return n&&e.sink.appendChild(n,this._renderer.getElement(!0)),this}},f=n(TreeRow),p=16,m=24,h=`var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))`,Tree=class extends u{_nodes=[];_expandedNodes=new Set;_flatRows=[];_lastRowWidth=0;_maxContentWidth=0;_selectedNodes=new Set;_loadingNodes=new Set;_loadedNodes=new Set;_anchorNode=null;_focusNode=null;_listeners=new i;_rendererFactory=()=>new LabelTreeNodeRenderer;constructor(e){super(e),this.setOverflow(`hidden`),this.setBackgroundColor(`var(--ts-ui-input-bg, rgb(255, 255, 255))`),this.setPreferredSize({width:200,height:300}),this.setMaxSize({width:2**53-1,height:2**53-1}),this.getAria().setRole(`tree`),this.getAria().setTabIndex(0),this.getAria().setMultiselectable(!0),this.subscribeTheme(()=>this.onThemeReflow()),this.applyListeners(e?.listeners)}setNodes(e){return this._nodes=e,this._expandedNodes.clear(),this._selectedNodes.clear(),this._loadingNodes.clear(),this._loadedNodes.clear(),this._anchorNode=null,this._focusNode=null,this._flatten(),this.getElement()&&(this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()),this}expandAll(){let addExpandable=e=>{for(let t of e)t.children&&t.children.length>0&&(this._expandedNodes.add(t),addExpandable(t.children))};return addExpandable(this._nodes),this._reflattenAndRender(),this}getRowHeight(){return m}createPoolRow(){return new f(this._rendererFactory)}getNodes(){return this._nodes}getSelectedNode(){return this._anchorNode}getSelectedNodes(){return Array.from(this._selectedNodes)}selectNode(e){let t=this._flatRows.findIndex(t=>t.node===e);return t<0?this:(this._selectedNodes.clear(),this._selectedNodes.add(e),this._anchorNode=e,this._focusNode=e,this._updateSelectionStyle(),this._scrollIntoView(t),this.renderWindow(),this._updateActiveDescendant(),this)}async revealByPredicate(e){let t=await this._findPath(this._nodes,e,[]);if(t===null)return null;for(let e=0;e<t.length-1;e++)this._expandedNodes.add(t[e]);this._reflattenAndRender();let n=t[t.length-1],r=this._flatRows.findIndex(e=>e.node===n);return r>=0&&this._scrollIntoView(r),n}async _findPath(e,t,n){for(let r of e){let e=[...n,r];if(t(r.data,r))return e;let i=await this._ensureChildrenLoaded(r);if(i.length>0){let n=await this._findPath(i,t,e);if(n!==null)return n}}return null}async _ensureChildrenLoaded(e){if(e.children&&e.children.length>0)return e.children;if(e.loadChildren!==void 0&&!this._loadedNodes.has(e))try{let t=await e.loadChildren();return e.children=t,this._loadedNodes.add(e),t}catch{return[]}return e.children??[]}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}setRendererFactory(e){this._rendererFactory=e;for(let t of this._rowPool)t.setRenderer(e());return this._boundIndices.fill(-1),this.invalidateGeom(),this.getElement()&&this.renderWindow(),this}getRendererFactory(){return this._rendererFactory}_isExpandable(e){return!!(e.children&&e.children.length>0)||e.hasChildren===!0}_flatten(){this._flatRows=[],this._maxContentWidth=0;let recurse=(e,t)=>{let n=e.length;for(let r=0;r<e.length;r++){let i=e[r];this._flatRows.push({node:i,depth:t,siblingCount:n,posInSet:r+1}),this._isExpandable(i)&&this._expandedNodes.has(i)&&i.children&&recurse(i.children,t+1)}};recurse(this._nodes,0)}_reflattenAndRender(){this._flatten(),this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()}_onToggle(e){if(this._expandedNodes.has(e)){this._expandedNodes.delete(e),this._reflattenAndRender();return}if(!(e.loadChildren!==void 0&&!this._loadedNodes.has(e)&&!(e.children&&e.children.length))){this._expandedNodes.add(e),this._reflattenAndRender();return}this._loadingNodes.has(e)||this._loadAndExpand(e)}async _loadAndExpand(e){this._loadingNodes.add(e),this._reflattenAndRender();try{let t=await e.loadChildren();if(!this._loadingNodes.has(e))return;e.children=t,this._loadedNodes.add(e),this._expandedNodes.add(e)}catch(t){if(!this._loadingNodes.has(e))return;this.emit(`loaderror`,e,t)}finally{this._loadingNodes.delete(e),this._reflattenAndRender()}}_notifySelectionChange(e){d(e,this._selectedNodes)||this.emit(`selection`,this.getSelectedNodes())}_rangeSelect(e,t){let n=Math.min(e,t),r=Math.max(e,t);this._selectedNodes.clear();for(let e=n;e<=r;e++)this._selectedNodes.add(this._flatRows[e].node)}_selectAtIndex(e){if(e<0||e>=this._flatRows.length)return;let t=this._flatRows[e].node,n=new Set(this._selectedNodes);this._selectedNodes.clear(),this._selectedNodes.add(t),this._anchorNode=t,this._focusNode=t,this._updateSelectionStyle(),this._scrollIntoView(e),this.renderWindow(),this._updateActiveDescendant(),this._notifySelectionChange(n)}_extendSelectionTo(e){if(e<0||e>=this._flatRows.length)return;let t=new Set(this._selectedNodes),n=this._anchorNode?this._flatRows.findIndex(e=>e.node===this._anchorNode):e;this._rangeSelect(n<0?e:n,e),this._focusNode=this._flatRows[e].node,this._updateSelectionStyle(),this._scrollIntoView(e),this.renderWindow(),this._updateActiveDescendant(),this._notifySelectionChange(t)}_scrollIntoView(e){this.scrollRowIntoView(e)}_onKeyDown(e){if(!new Set([`ArrowDown`,`ArrowUp`,`ArrowLeft`,`ArrowRight`,`Home`,`End`]).has(e.key))return;let t=this._flatRows;if(t.length===0)return;e.preventDefault();let n=this._focusNode?t.findIndex(e=>e.node===this._focusNode):-1;if(e.key===`ArrowDown`){let r=n<0?0:Math.min(n+1,t.length-1);e.shiftKey?this._extendSelectionTo(r):this._selectAtIndex(r)}else if(e.key===`ArrowUp`){let t=n<0?0:Math.max(n-1,0);e.shiftKey?this._extendSelectionTo(t):this._selectAtIndex(t)}else if(e.key===`Home`)e.shiftKey?this._extendSelectionTo(0):this._selectAtIndex(0);else if(e.key===`End`){let n=t.length-1;e.shiftKey?this._extendSelectionTo(n):this._selectAtIndex(n)}else if(e.key===`ArrowRight`){if(n<0){this._selectAtIndex(0);return}let{node:e}=t[n];if(!this._isExpandable(e))return;this._expandedNodes.has(e)?this._selectAtIndex(n+1):this._onToggle(e)}else if(e.key===`ArrowLeft`){if(n<0)return;let{node:e,depth:r}=t[n];if(this._isExpandable(e)&&this._expandedNodes.has(e)){this._onToggle(e);let t=this._flatRows.findIndex(t=>t.node===e);t>=0&&this._scrollIntoView(t)}else if(r>0){for(let e=n-1;e>=0;e--)if(t[e].depth===r-1){this._selectAtIndex(e);break}}}}_handleClick(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getToggle();if(a){let t=a.getElement();if(t&&(n===t||e.source.contains(t,n))){this._onToggle(i);return}}let o=r.getElement();if(!o||!e.source.contains(o,n)&&n!==o)continue;let s=this._flatRows.findIndex(e=>e.node===i);if(t.shiftKey&&this._anchorNode)this._extendSelectionTo(s);else if(t.ctrlKey||t.metaKey){let e=new Set(this._selectedNodes);this._selectedNodes.has(i)?this._selectedNodes.delete(i):this._selectedNodes.add(i),this._anchorNode=i,this._focusNode=i,this._updateSelectionStyle(),this._notifySelectionChange(e)}else this._selectAtIndex(s);return}}_handleContextMenu(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getElement();if(!(!a||!e.source.contains(a,n)&&n!==a)){t.preventDefault(),this.emit(`contextmenu`,i,t);return}}}_handleDblClick(t){let n=t.target===null?null:e.source.intern(t.target);for(let r of this._rowPool){let i=r.getNode();if(!i)continue;let a=r.getElement();if(!a||!e.source.contains(a,n)&&n!==a)continue;t.preventDefault(),this.emit(`dblclick`,i);let o=r.getToggle(),s=o?o.getElement():void 0;!(s&&(n===s||e.source.contains(s,n)))&&this._isExpandable(i)&&this._onToggle(i);return}}_updateSelectionStyle(){for(let t of this._rowPool){let n=t.getElement();if(!n)continue;let r=t.getNode(),i=r!==null&&this._selectedNodes.has(r),a=r!==null&&r===this._focusNode;i?e.sink.apply(n,{style:{"background-color":h}}):e.sink.apply(n,{style:{"background-color":null}}),a?e.sink.apply(n,{style:{outline:`2px solid var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))`,"outline-offset":`-2px`}}):e.sink.apply(n,{style:{outline:null,"outline-offset":null}}),t.getAria().setSelected(i)}}_updateActiveDescendant(){if(!this._focusNode){this.getAria().setActiveDescendant(``);return}for(let e of this._rowPool)if(e.getNode()===this._focusNode){this.getAria().setActiveDescendant(e.getId());return}this.getAria().setActiveDescendant(``)}onThemeReflow(){this._maxContentWidth=0,super.onThemeReflow()}renderWindow(){if(!this.getElement()||!this._scroller||this.deferRenderWhileFirstLayoutHeld())return;let e=this._scroller,t=this._flatRows.length,n=t*m;e.clampToContent(this._lastRowWidth,n);let r=this.getHeight()||0,i=this.computeVisibleWindow(e.getScrollY(),r,t),a=this.computePoolTarget(i.windowSize,r,t);this.growRowPool(a);let{reboundFlags:o,maxContentWidth:s}=this._bindAndMeasure(i.firstRow,i.windowSize);s>this._maxContentWidth&&(this._maxContentWidth=s);let c=Math.max(e.getViewportWidth(),this._maxContentWidth);c!==this._lastRowWidth&&(this._lastRowWidth=c,this.invalidateGeom()),this._positionRows(i.firstRow,i.windowSize,c,o),this.hideExcessPoolRows(i.windowSize),this._updateSelectionStyle(),e.layoutScrollbars(c,n),this.finishResumedRender()&&this._updateActiveDescendant()}_bindAndMeasure(e,t){let n=Array(t),r=0;for(let i=0;i<t;i++){let t=this._rowPool[i],a=e+i,o=this._flatRows[a],s=this._isExpandable(o.node),c=this._expandedNodes.has(o.node),l=this._loadingNodes.has(o.node),u=this._boundIndices[i]!==a;if(u){let e=this._selectedNodes.has(o.node);t.setRowData(o.node,o.depth,s,c,o.siblingCount,o.posInSet,e,l),this._boundIndices[i]=a}n[i]=u;let d=t.getContentWidth(p);d>r&&(r=d)}return{reboundFlags:n,maxContentWidth:r}}_positionRows(e,t,n,r){for(let i=0;i<t;i++){let t=this._rowPool[i],a=e+i,o=r[i],s=this.positionRow(i,a*m,n);(o||s)&&t.layoutChildren(m,p)}}init(e){super.init(e);let n=e||this.getElement();return n?(this.initScroller(n),t.addSubtreeListener(this,`click`,this._handleClick),t.addSubtreeListener(this,`contextmenu`,this._handleContextMenu),t.addSubtreeListener(this,`dblclick`,this._handleDblClick),t.addListener(this,`keydown`,this._onKeyDown),this.renderWindow(),this):this}doLayout(){return this.isLayoutPaused()?this:(super.doLayout(),this.renderWindowIfDeferred()||this.renderWindow(),this)}},g=n(Tree),_=20,v=16,IconLabelTreeNodeRenderer=class extends TreeNodeRenderer{_icon=null;_label;_glyphResolver;_currentGlyph=null;constructor(e=()=>`file`){super(),this.clearInsets(),this._glyphResolver=e,this._label=new o,this._label.clearInsets(),this._label.setAutoMeasure(!1)}update(t){let n=this._glyphResolver(t.node,t);if(n!==this._currentGlyph){let t=this.getElement();if(t&&this._icon){let n=this._icon.getElement();n&&e.source.getParentNode(n)===t&&e.sink.removeChild(t,n)}this._icon=new a(n),this._icon.clearInsets(),this._currentGlyph=n,t&&e.sink.insertBefore(t,this._icon.getElement(!0),this._label.getElement()??null)}this._label.setText(t.node.label),this._label.measure()}getContentWidth(){return(this._icon?_:0)+(this._label.getPreferredSize()?.width??0)}layoutChildren(e,t){let n=this.getContentBounds()??{x:0,y:0,width:e,height:t};this._icon&&(this._icon.setAutoCommitStyle(!1),this._icon.setX(n.x),this._icon.setY(n.y+Math.max(0,(n.height-v)/2)),this._icon.setWidth(v),this._icon.setHeight(v),this._icon.setAutoCommitStyle(!0));let r=n.x+(this._icon?_:0),i=Math.max(0,n.x+n.width-r);this._label.setAutoCommitStyle(!1),this._label.setX(r),this._label.setY(n.y),this._label.setWidth(i),this._label.setHeight(n.height),this._label.setLineHeight(n.height),this._label.setAutoCommitStyle(!0)}init(t){super.init(t);let n=t||this.getElement();return n?(this._icon&&e.sink.appendChild(n,this._icon.getElement(!0)),e.sink.appendChild(n,this._label.getElement(!0)),this):this}};export{IconLabelTreeNodeRenderer,LabelTreeNodeRenderer,g as Tree,TreeNodeRenderer};
|
|
2
2
|
//# sourceMappingURL=tree.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.es.js","names":[],"sources":["../../../src/typescript/lib/component/tree/TreeNodeRenderer.ts","../../../src/typescript/lib/component/tree/renderer/Label.ts","../../../src/typescript/lib/component/tree/TreeRow.ts","../../../src/typescript/lib/component/tree/Tree.ts","../../../src/typescript/lib/component/tree/renderer/IconLabel.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * Abstract base class for tree node renderers.\n *\n * @remarks\n * A `TreeNodeRenderer` controls the content area of a single\n * [`TreeRow`](/api/component/tree/classes/TreeRow) — that is, everything to\n * the right of the expand/collapse toggle. The owning [`Tree`](/api/component/tree/classes/Tree)\n * holds a renderer factory; each pool slot calls that factory once at\n * construction and rebinds via {@link update} when the slot is remapped to a\n * different node or its selection/expansion state changes.\n *\n * The toggle remains under `TreeRow`'s control because click detection routes\n * through it; renderers never see toggle clicks.\n *\n * Subclasses must implement {@link update}, {@link getContentWidth}, and\n * {@link layoutChildren}. See [`LabelTreeNodeRenderer`](/api/component/tree/classes/LabelTreeNodeRenderer)\n * for the canonical default.\n *\n * @category Components\n */\nexport abstract class TreeNodeRenderer extends Component {\n\n constructor() {\n super();\n }\n\n /**\n * Rebinds this renderer to new node data.\n *\n * @param context - The bound-node state for this render pass.\n *\n * @remarks\n * Called every time the owning pool slot is mapped to a different node, or\n * when an expansion-state change forces a full re-bind. Plain selection\n * style changes (background highlight, focus ring) are handled by the\n * `Tree` directly on the row element and do not flow through `update()`.\n */\n abstract update(context: TreeNodeRenderContext): void;\n\n /**\n * Returns the natural pixel width this renderer would render at given the\n * current bound node.\n *\n * @returns Content width in pixels.\n *\n * @remarks\n * Used by [`Tree`](/api/component/tree/classes/Tree) to compute the\n * horizontal scroll extent. Renderers that auto-measure their children\n * should return the sum of those measured widths.\n */\n abstract getContentWidth(): number;\n\n /**\n * Positions this renderer's internal children within the given box.\n *\n * @param width - The horizontal extent of the renderer in pixels.\n * @param height - The vertical extent of the renderer in pixels.\n *\n * @remarks\n * Called by `TreeRow.layoutChildren` after the renderer's own size has\n * been set via `setX`/`setY`/`setWidth`/`setHeight`.\n */\n abstract layoutChildren(width: number, height: number): void;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * The default [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) —\n * a single `<span>` carrying the node's label.\n *\n * @remarks\n * Used as the implicit factory by [`Tree`](/api/component/tree/classes/Tree)\n * when no custom renderer is set. The internal `Text` runs with\n * `autoMeasure(false)`; an explicit `measure()` call after each `setText`\n * caches the natural width for `getContentWidth()` to read on the next layout\n * pass.\n *\n * @example\n * ```typescript\n * tree.setRendererFactory(() => new LabelTreeNodeRenderer());\n * ```\n *\n * @category Components\n */\nexport class LabelTreeNodeRenderer extends TreeNodeRenderer {\n\n private _label: Text;\n\n /**\n * Constructs a label renderer with an empty text node. The label is\n * populated on the first {@link update} call.\n */\n constructor() {\n super();\n this.clearInsets();\n\n this._label = new Text();\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Returns the underlying label component so consumers can tweak font /\n * colour properties at construction time.\n *\n * @returns The internal [`Text`](/api/component/input/classes/Text) instance.\n */\n getLabel(): Text {\n return this._label;\n }\n\n /**\n * Updates the rendered label text and re-measures.\n *\n * @param context - The bound-node state for this render pass.\n */\n update(context: TreeNodeRenderContext): void {\n this._label.setText(context.node.label);\n this._label.measure();\n }\n\n /**\n * Returns the cached natural width of the rendered label.\n *\n * @returns Label width in pixels.\n */\n getContentWidth(): number {\n return this._label.getPreferredSize()?.width ?? 0;\n }\n\n /**\n * Sizes the label to fill the renderer's allocated box and centres its\n * line-box vertically by matching `line-height` to the row height.\n *\n * @param _width - Unused; the label sizes to its natural content width.\n * @param height - The vertical extent of the row in pixels.\n */\n layoutChildren(_width: number, height: number): void {\n const labelWidth = this.getContentWidth();\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(0);\n this._label.setY(0);\n this._label.setWidth(labelWidth);\n this._label.setHeight(height);\n this._label.setLineHeight(height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element to the renderer's DOM element.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { caret_down } from \"~/glyphs/solid/caret_down.js\";\nimport { caret_right } from \"~/glyphs/solid/caret_right.js\";\n\nGlyph.register(caret_down, caret_right);\n\n/** Width in pixels reserved for the expand/collapse toggle icon. */\nexport const TOGGLE_WIDTH = 20;\n\n/**\n * A single visible row in the {@link Tree} virtual-scroll pool.\n *\n * Each pool slot holds one {@link TreeNode} at a time. The {@link Tree} calls\n * {@link setRowData} to rebind a slot to a different node and\n * {@link layoutChildren} to reposition the toggle and renderer after the row's\n * own dimensions are updated.\n *\n * @remarks\n * The toggle ([`Glyph`](/api/component/display/classes/Glyph)) and content\n * renderer are appended directly to the row's DOM element in `init()` rather\n * than via `addComponent`, so their preferred-size change notifications do not\n * propagate up to the Tree and trigger unnecessary layout passes. Leaf rows\n * have no toggle; non-leaf rows swap in a fresh `caret-down` / `caret-right`\n * glyph on each state change rather than mutating a single character. The row\n * content area (everything to the right of the toggle) is owned by a\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) supplied\n * via the constructor factory.\n */\nclass TreeRow extends Component {\n\n private _toggle: Glyph | null = null;\n private _spinner: ProgressSpinner | null = null;\n private _renderer: TreeNodeRenderer;\n private _node: TreeNode | null = null;\n private _depth: number = 0;\n\n constructor(rendererFactory: () => TreeNodeRenderer = () => new LabelTreeNodeRenderer()) {\n super();\n\n this.getAria().setRole(\"treeitem\");\n\n this._renderer = rendererFactory();\n }\n\n /**\n * Returns the toggle icon glyph, or null when the bound node is a leaf.\n *\n * @returns The toggle [`Glyph`](/api/component/display/classes/Glyph) instance, or null.\n */\n getToggle(): Glyph | null {\n return this._toggle;\n }\n\n /**\n * Returns the renderer currently bound to this pool row.\n *\n * @returns The active [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n */\n getRenderer(): TreeNodeRenderer {\n return this._renderer;\n }\n\n /**\n * Replaces this row's content renderer, swapping the underlying DOM\n * element in place.\n *\n * @param renderer - The new renderer instance to install.\n * @returns This row, for method chaining.\n *\n * @remarks\n * The new renderer's element is appended to the row immediately. The\n * caller is expected to follow up with `setRowData` (typically via the\n * Tree's next render pass) so the renderer receives an `update()` before\n * being laid out.\n */\n setRenderer(renderer: TreeNodeRenderer): this {\n const el = this.getElement();\n\n if (el) {\n const oldEl = this._renderer.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n\n DOM.sink.appendChild(el, renderer.getElement(true)!);\n }\n\n this._renderer = renderer;\n\n return this;\n }\n\n /**\n * Returns the tree node currently bound to this pool row.\n *\n * @returns The bound {@link TreeNode}, or null if the slot is unbound.\n */\n getNode(): TreeNode | null {\n return this._node;\n }\n\n /**\n * Returns the depth of the currently bound node.\n *\n * @returns The zero-based depth level.\n */\n getDepth(): number {\n return this._depth;\n }\n\n /**\n * Binds this pool slot to a new node, updating the toggle icon, content\n * renderer, and ARIA positional attributes.\n *\n * @param node - The tree node to display.\n * @param depth - The zero-based nesting depth (controls indentation).\n * @param hasChildren - Whether the node has child nodes (determines toggle visibility).\n * @param expanded - Whether the node is currently expanded.\n * @param siblingCount - Total number of siblings at this level under the same parent.\n * @param posInSet - 1-based position of this node among its siblings.\n * @param selected - Whether the node is currently selected.\n * @param loading - Whether this node's lazy children are currently loading;\n * when true a spinner replaces the toggle caret.\n */\n setRowData(node: TreeNode, depth: number, hasChildren: boolean, expanded: boolean, siblingCount: number, posInSet: number, selected: boolean, loading: boolean): this {\n this._node = node;\n this._depth = depth;\n\n if (this._toggle) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._toggle.getElement(true)!);\n }\n this._toggle = null;\n }\n\n if (this._spinner) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._spinner.getElement(true)!);\n }\n this._spinner = null;\n }\n\n if (loading) {\n // No explicit size: the spinner tracks the theme font-size so it\n // reads as the same visual weight as the caret glyph it replaces,\n // and `layoutChildren` fits it into the TOGGLE_WIDTH box.\n const spinner = new ProgressSpinner();\n this._spinner = spinner;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, spinner.getElement(true)!);\n }\n } else if (hasChildren) {\n const toggle = new Glyph(expanded ? \"caret-down\" : \"caret-right\");\n toggle.setCursor(\"pointer\");\n toggle.clearInsets();\n toggle.getAria().setHidden(true);\n this._toggle = toggle;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, toggle.getElement(true)!);\n }\n }\n\n this._renderer.update({ node, depth, expanded, selected, hasChildren });\n\n this.getAria().setLevel(depth + 1);\n this.getAria().setExpanded(hasChildren ? expanded : null);\n this.getAria().setSetSize(siblingCount);\n this.getAria().setPosInSet(posInSet);\n\n return this;\n }\n\n /**\n * Positions the toggle and renderer sub-components within the row's bounds.\n *\n * @param rowHeight - The current height of this row in pixels.\n * @param indentPx - Pixels of indentation per depth level.\n */\n layoutChildren(rowHeight: number, indentPx: number): void {\n const indent = this._depth * indentPx;\n\n if (this._toggle) {\n // The caret is a rigid SVG Glyph (min/max pinned to its 16×16\n // preferredSize), so it cannot fill the taller row — centre it\n // geometrically instead of relying on line-height (a no-op for SVG).\n const glyphHeight = this._toggle.getPreferredSize()?.height ?? rowHeight;\n this._toggle.setAutoCommitStyle(false);\n this._toggle.setX(indent);\n this._toggle.setY(Math.max(0, (rowHeight - glyphHeight) / 2));\n this._toggle.setWidth(TOGGLE_WIDTH);\n this._toggle.setHeight(glyphHeight);\n this._toggle.setAutoCommitStyle(true);\n }\n\n if (this._spinner) {\n this._spinner.setAutoCommitStyle(false);\n this._spinner.setX(indent);\n this._spinner.setY(0);\n this._spinner.setWidth(TOGGLE_WIDTH);\n this._spinner.setHeight(rowHeight);\n this._spinner.setAutoCommitStyle(true);\n\n // The spinner owns its inner arc, which only positions itself when\n // its own doLayout runs; the row appends it directly rather than via\n // addComponent, so drive that layout here once the box is sized.\n this._spinner.doLayout();\n }\n\n const labelX = indent + TOGGLE_WIDTH;\n const rowWidth = this.getWidth() || 0;\n const labelBoxW = Math.max(0, rowWidth - labelX);\n\n this._renderer.setAutoCommitStyle(false);\n this._renderer.setX(labelX);\n this._renderer.setY(0);\n this._renderer.setWidth(labelBoxW);\n this._renderer.setHeight(rowHeight);\n this._renderer.setAutoCommitStyle(true);\n\n this._renderer.layoutChildren(labelBoxW, rowHeight);\n }\n\n /**\n * Returns the natural pixel width needed to display this row's full content\n * (indent + toggle + renderer content) without horizontal clipping.\n *\n * @param indentPx - Pixels of indentation per depth level.\n */\n getContentWidth(indentPx: number): number {\n const indent = this._depth * indentPx;\n const contentW = this._renderer.getContentWidth();\n\n return indent + TOGGLE_WIDTH + contentW;\n }\n\n /**\n * Appends the renderer sub-component element to the row's DOM element.\n * The toggle glyph (if any) is appended on demand by `setRowData`.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n DOM.sink.appendChild(el, this._renderer.getElement(true)!);\n\n return this;\n }\n}\n\nconst TreeRowCallable = callable(TreeRow);\ntype TreeRowCallable = TreeRow;\nexport {\n TreeRow as _TreeRow,\n TreeRowCallable as TreeRow\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { VirtualRowView } from \"~/component/shared/VirtualRowView.js\";\nimport { selectionsEqual } from \"~/component/shared/selectionsEqual.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeRow } from \"~/component/tree/TreeRow.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link Tree}.\n *\n * @category Components\n */\nexport type TreeEvent = \"selection\" | \"loaderror\" | \"contextmenu\" | \"dblclick\";\n\n/** Pixels of indentation added per depth level. */\nconst INDENT_PX = 16;\n\n/** Fixed height in pixels for every visible row. */\nconst ROW_HEIGHT = 24;\n\n/** CSS background applied to the selected row. */\nconst SELECTED_BG = \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\";\n\n/**\n * A pre-flattened entry in the visible subtree used by the virtual scroll.\n */\ninterface FlatRow {\n node: TreeNode;\n depth: number;\n siblingCount: number;\n posInSet: number;\n}\n\n/**\n * Construction-time options for {@link Tree}. Root nodes are set via\n * `setNodes()` and the renderer factory via `setRendererFactory()`; this\n * interface only carries the event-listener bag and the inherited\n * {@link ComponentOptions}.\n *\n * @category Components\n */\nexport interface TreeOptions extends ComponentOptions {\n /**\n * Multi-event listener bag dispatched to {@link Tree.on} at\n * construction time.\n */\n listeners?: {\n selection?: (nodes: TreeNode[]) => void;\n loaderror?: (node: TreeNode, error: unknown) => void;\n contextmenu?: (node: TreeNode, event: MouseEvent) => void;\n dblclick?: (node: TreeNode) => void;\n };\n}\n\n/**\n * A hierarchical data view with collapsible nodes and virtual scrolling.\n *\n * Pass root nodes via {@link Tree.setNodes}. The tree flattens the currently\n * visible subtree into a single scrollable list and recycles a fixed pool of\n * internal row components — rebinding rows only when their data index changes,\n * mirroring the approach used in `table/Body.ts`.\n *\n * Scrolling is delegated to a `VirtualScroller` that owns the\n * rows-container transform, two custom scrollbar overlays, and the wheel/touch\n * handlers with fling momentum.\n *\n * @example\n * ```typescript\n * const tree = new Tree();\n * tree.setNodes([\n * { label: \"Fruits\", children: [\n * { label: \"Apple\" },\n * { label: \"Banana\" },\n * ]},\n * { label: \"Vegetables\" },\n * ]);\n * container.addComponent(tree);\n * ```\n *\n * @category Components\n */\nclass Tree extends VirtualRowView<TreeRow, TreeOptions> {\n\n private _nodes : TreeNode[] = [];\n private _expandedNodes : Set<TreeNode> = new Set();\n private _flatRows : FlatRow[] = [];\n private _lastRowWidth : number = 0;\n // Widest row content measured so far across the current flattened set. Only\n // the *visible* window is measured each frame, so tracking a running maximum\n // keeps the horizontal content width — and thus the H scrollbar — stable as\n // different-width rows scroll through the window, instead of jittering with\n // whatever happens to be on screen. Reset in `_flatten` when the row set is\n // rebuilt, so a narrower dataset (or a collapsed wide branch) re-derives it.\n private _maxContentWidth : number = 0;\n private _selectedNodes : Set<TreeNode> = new Set();\n private _loadingNodes : Set<TreeNode> = new Set();\n private _loadedNodes : Set<TreeNode> = new Set();\n private _anchorNode : TreeNode | null = null;\n private _focusNode : TreeNode | null = null;\n private _listeners : ListenerBag<TreeEvent> = new ListenerBag<TreeEvent>();\n private _rendererFactory : () => TreeNodeRenderer = () => new LabelTreeNodeRenderer();\n\n constructor(options?: TreeOptions) {\n super(options);\n\n this.setOverflow(\"hidden\");\n this.setBackgroundColor(\"var(--ts-ui-input-bg, rgb(255, 255, 255))\");\n this.setPreferredSize({ width: 200, height: 300 });\n this.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n\n this.getAria().setRole(\"tree\");\n this.getAria().setTabIndex(0);\n this.getAria().setMultiselectable(true);\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Replaces the root nodes, collapses all nodes, clears selection, and re-renders.\n *\n * @param nodes - The new array of root {@link TreeNode} objects.\n */\n setNodes(nodes: TreeNode[]): this {\n this._nodes = nodes;\n this._expandedNodes.clear();\n this._selectedNodes.clear();\n this._loadingNodes.clear();\n this._loadedNodes.clear();\n this._anchorNode = null;\n this._focusNode = null;\n this._flatten();\n\n if (this.getElement()) {\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Expands every node that has (already-loaded) children, so the whole tree\n * is flattened and visible. Does not load lazy branches and does not change\n * the selection or emit any event.\n *\n * @returns This tree, for method chaining.\n */\n expandAll(): this {\n const addExpandable = (nodes: TreeNode[]): void => {\n for (const node of nodes) {\n if (node.children && node.children.length > 0) {\n this._expandedNodes.add(node);\n addExpandable(node.children);\n }\n }\n };\n\n addExpandable(this._nodes);\n this._reflattenAndRender();\n\n return this;\n }\n\n /** Returns the fixed row height every tree row is laid out at. */\n protected getRowHeight(): number {\n return ROW_HEIGHT;\n }\n\n /**\n * Constructs one pool row backed by the current renderer factory. The\n * base's `growRowPool` owns the append + parallel-array bookkeeping.\n *\n * @returns The new, un-appended pool row.\n */\n protected createPoolRow(): TreeRow {\n return new TreeRow(this._rendererFactory);\n }\n\n /**\n * Returns the root nodes currently displayed by the tree.\n *\n * @returns The array of root {@link TreeNode} objects.\n */\n getNodes(): TreeNode[] {\n return this._nodes;\n }\n\n /**\n * Returns the anchor node of the current selection (the last node selected by a plain click or\n * arrow key without Shift), or null if there is no selection.\n *\n * @returns The anchor {@link TreeNode}, or null.\n */\n getSelectedNode(): TreeNode | null {\n return this._anchorNode;\n }\n\n /**\n * Returns all currently selected nodes in no guaranteed order.\n *\n * @returns An array of selected {@link TreeNode} instances.\n */\n getSelectedNodes(): TreeNode[] {\n return Array.from(this._selectedNodes);\n }\n\n /**\n * Programmatically selects a single node, replacing any existing selection\n * and scrolling it into view.\n *\n * Unlike a user click this does **not** emit the `\"selection\"` event: it is a\n * state setter for syncing the tree to an external source of truth (e.g. the\n * active dock tab), so it must not re-trigger selection-driven side effects.\n * No-op when the node is not in the currently visible (flattened) set — e.g.\n * an ancestor is collapsed.\n *\n * @param node - A node currently held by this tree.\n *\n * @returns This tree, for method chaining.\n */\n selectNode(node: TreeNode): this {\n const index = this._flatRows.findIndex(r => r.node === node);\n if (index < 0) {\n return this;\n }\n\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this._anchorNode = node;\n this._focusNode = node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n\n return this;\n }\n\n /**\n * Finds the first node whose payload satisfies `predicate`, expanding every\n * ancestor on the path (loading lazy branches as needed) so the node becomes\n * visible, then scrolls it into view. Returns the node, or `null` when no\n * node matches.\n *\n * Unlike {@link selectNode} — which no-ops when the target sits under a\n * collapsed or not-yet-loaded ancestor — this walks the whole tree,\n * awaiting each lazy branch's `loadChildren` on the way down, so it can\n * reveal a node the user has never manually expanded to. Because it may\n * load every branch it visits, it is O(nodes) and issues one `loadChildren`\n * per unloaded branch it descends; call it for a deliberate \"jump to this\n * object\" action, not on a hot path.\n *\n * Revealing is not selecting: this method does not change the selection or\n * emit `\"selection\"`. Select the returned node with {@link selectNode} if the\n * reveal should also highlight it.\n *\n * The search is depth-first in root-then-child order and stops at the first\n * match. A lazy branch whose `loadChildren` rejects is skipped (its subtree\n * is treated as empty) and the walk continues; the failure is not surfaced\n * here (no `\"loaderror\"` is emitted for a reveal-driven load).\n *\n * @param predicate - Tested against each node's `data` payload (and the node\n * itself); return `true` for the node to reveal.\n *\n * @returns A promise resolving to the revealed {@link TreeNode}, or `null`.\n */\n async revealByPredicate(predicate: (data: unknown, node: TreeNode) => boolean): Promise<TreeNode | null> {\n const path = await this._findPath(this._nodes, predicate, []);\n if (path === null) {\n return null;\n }\n\n // Expand every ancestor (all but the target itself) so the target's row\n // enters the flattened set; the target's own children stay as they are.\n for (let i = 0; i < path.length - 1; i++) {\n this._expandedNodes.add(path[i]);\n }\n this._reflattenAndRender();\n\n const target = path[path.length - 1];\n const index = this._flatRows.findIndex(r => r.node === target);\n if (index >= 0) {\n this._scrollIntoView(index);\n }\n\n return target;\n }\n\n /**\n * Depth-first search for the first node satisfying `predicate`, returning\n * the full root-to-match path (inclusive) or `null`. Awaits and caches each\n * visited branch's lazy children so the walk can descend into unloaded\n * subtrees.\n *\n * @param nodes - The sibling nodes to search at this level.\n * @param predicate - The match test (see {@link revealByPredicate}).\n * @param prefix - The ancestor path leading to `nodes`.\n *\n * @returns The path from a root to the matching node, or `null`.\n */\n private async _findPath(\n nodes: TreeNode[],\n predicate: (data: unknown, node: TreeNode) => boolean,\n prefix: TreeNode[],\n ): Promise<TreeNode[] | null> {\n for (const node of nodes) {\n const here = [...prefix, node];\n\n if (predicate(node.data, node)) {\n return here;\n }\n\n const children = await this._ensureChildrenLoaded(node);\n if (children.length > 0) {\n const found = await this._findPath(children, predicate, here);\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n }\n\n /**\n * Returns a node's children, loading and caching them from `loadChildren`\n * first when the node is lazy and unloaded. Mirrors {@link _loadAndExpand}'s\n * cache writes (`children` + `_loadedNodes`) but does not expand the node —\n * a reveal only expands the ancestors on the path to its match. A rejected\n * load is swallowed and treated as an empty subtree.\n *\n * @param node - The node whose children are needed.\n *\n * @returns The node's children (possibly empty).\n */\n private async _ensureChildrenLoaded(node: TreeNode): Promise<TreeNode[]> {\n if (node.children && node.children.length > 0) {\n return node.children;\n }\n\n if (node.loadChildren !== undefined && !this._loadedNodes.has(node)) {\n try {\n const children = await node.loadChildren();\n\n node.children = children;\n this._loadedNodes.add(node);\n\n return children;\n } catch {\n return [];\n }\n }\n\n return node.children ?? [];\n }\n\n /**\n * Registers a listener for one of this tree's events.\n *\n * @param event - `\"selection\"` fires whenever the selection changes,\n * receiving the full array of selected {@link TreeNode} instances.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"selection\", listener: (nodes: TreeNode[]) => void): this;\n\n /**\n * Registers a listener for a lazy node's failed child load.\n *\n * @param event - `\"loaderror\"` fires when a node's `loadChildren` rejects;\n * the node stays collapsed and unloaded so toggling again retries.\n * @param listener - Receives the {@link TreeNode} whose load failed and the\n * rejection reason.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"loaderror\", listener: (node: TreeNode, error: unknown) => void): this;\n\n /**\n * Registers a listener for a right-click on a node.\n *\n * @param event - `\"contextmenu\"` fires when a node row is right-clicked,\n * receiving the {@link TreeNode} under the cursor and the originating\n * `MouseEvent` (its `clientX`/`clientY` position a context menu). The\n * default browser menu is suppressed and the tree's selection is left\n * unchanged, so a right-click never opens a node the way a left-click does.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"contextmenu\", listener: (node: TreeNode, event: MouseEvent) => void): this;\n\n /**\n * Registers a listener for a double-click (activation) on a node.\n *\n * @param event - `\"dblclick\"` fires when a node row is double-clicked,\n * receiving the {@link TreeNode} under the cursor. The first click of the\n * pair still emits `\"selection\"` as usual, so `\"dblclick\"` is an\n * activation signal layered on top of selection — consumers typically use\n * `\"selection\"` to preview a node and `\"dblclick\"` to open it.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"dblclick\", listener: (node: TreeNode) => void): this;\n on(event: TreeEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This tree, for method chaining.\n */\n off(event: TreeEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"selection\", nodes: TreeNode[]): void;\n protected emit(event: \"loaderror\", node: TreeNode, error: unknown): void;\n protected emit(event: \"contextmenu\", node: TreeNode, event_: MouseEvent): void;\n protected emit(event: \"dblclick\", node: TreeNode): void;\n protected emit(event: TreeEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the renderer factory used to create one\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer)\n * instance per pool slot.\n *\n * @param factory - Zero-argument function returning a new\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n * Defaults to `() => new LabelTreeNodeRenderer()`.\n * @returns This tree, for method chaining.\n *\n * @remarks\n * If pool rows already exist, each one's renderer is replaced in place via\n * `TreeRow.setRenderer(factory())`. The DOM pool itself is preserved.\n * Bindings are cleared so the next render pass rebinds and updates the\n * new renderers.\n */\n setRendererFactory(factory: () => TreeNodeRenderer): this {\n this._rendererFactory = factory;\n\n for (const row of this._rowPool) {\n row.setRenderer(factory());\n }\n\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n\n if (this.getElement()) {\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Returns the renderer factory currently in use.\n *\n * @returns The zero-argument factory function.\n */\n getRendererFactory(): () => TreeNodeRenderer {\n return this._rendererFactory;\n }\n\n /**\n * Reports whether a node renders an expand/collapse caret.\n *\n * @param node - The node to test.\n * @returns True when the node already carries children, or is a lazy node\n * declared with `hasChildren: true`.\n *\n * @remarks\n * Centralising the predicate keeps the caret, ARIA, and flatten decisions\n * from drifting apart. An eager node with an empty `children` array is not\n * expandable; a lazy node is expandable before its children exist.\n */\n private _isExpandable(node: TreeNode): boolean {\n return !!(node.children && node.children.length > 0) || node.hasChildren === true;\n }\n\n /**\n * Rebuilds the flat visible-row list from the current root nodes and expanded set,\n * computing `siblingCount` and `posInSet` (1-based) for each entry.\n */\n private _flatten(): void {\n this._flatRows = [];\n\n // The visible set is changing, so the widest-row running maximum no\n // longer describes it — reset it and let the next render pass re-derive\n // the content width from the new rows (a collapsed wide branch or a\n // narrower dataset must be able to shrink the horizontal extent).\n this._maxContentWidth = 0;\n\n const recurse = (nodes: TreeNode[], depth: number): void => {\n const siblingCount = nodes.length;\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n this._flatRows.push({ node, depth, siblingCount, posInSet: i + 1 });\n\n if (this._isExpandable(node) && this._expandedNodes.has(node) && node.children) {\n recurse(node.children, depth + 1);\n }\n }\n };\n\n recurse(this._nodes, 0);\n }\n\n /**\n * Re-flattens the visible subtree and forces a full rebind of the pool so\n * every row reflects the current expanded/loading state.\n *\n * @remarks\n * Called from the collapse path, the synchronous expand path, and the async\n * load path — anywhere the flattened-row set or a node's loading affordance\n * changes.\n */\n private _reflattenAndRender(): void {\n this._flatten();\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n /**\n * Toggles the expanded state of a node, re-flattens, and re-renders.\n *\n * @param node - The node whose expanded state should be toggled.\n *\n * @remarks\n * Collapse and already-resolved expansion commit synchronously. A lazy node\n * declared with `loadChildren` that has not loaded yet defers its expansion\n * to {@link _loadAndExpand}, which only commits once the loader resolves.\n */\n private _onToggle(node: TreeNode): void {\n if (this._expandedNodes.has(node)) {\n this._expandedNodes.delete(node);\n this._reflattenAndRender();\n\n return;\n }\n\n const needsLoad = node.loadChildren !== undefined\n && !this._loadedNodes.has(node)\n && !(node.children && node.children.length);\n\n if (!needsLoad) {\n this._expandedNodes.add(node);\n this._reflattenAndRender();\n\n return;\n }\n\n if (this._loadingNodes.has(node)) {\n return;\n }\n\n void this._loadAndExpand(node);\n }\n\n /**\n * Loads a lazy node's children, then commits its expansion.\n *\n * @param node - The lazy node to load and expand.\n *\n * @remarks\n * Marks the node loading (driving its spinner affordance), awaits\n * `loadChildren`, and on success writes `children`, records the node as\n * loaded and expanded. A rejection emits `\"loaderror\"` and leaves the node\n * collapsed and unloaded so toggling again retries. An empty resolved array\n * is treated as success: the node renders as an expanded, empty parent.\n *\n * If `setNodes` swaps the dataset while the loader is in flight, it clears\n * `_loadingNodes`, so a still-present membership check after the await tells\n * us the node is still live; an orphaned resolve commits nothing.\n */\n private async _loadAndExpand(node: TreeNode): Promise<void> {\n this._loadingNodes.add(node);\n this._reflattenAndRender();\n\n try {\n const children = await node.loadChildren!();\n\n if (!this._loadingNodes.has(node)) {\n return;\n }\n\n node.children = children;\n this._loadedNodes.add(node);\n this._expandedNodes.add(node);\n } catch (error) {\n if (!this._loadingNodes.has(node)) {\n return;\n }\n\n this.emit(\"loaderror\", node, error);\n } finally {\n this._loadingNodes.delete(node);\n this._reflattenAndRender();\n }\n }\n\n /**\n * Fires `\"selection\"` with the current selection, unless it has the same\n * membership as `before` — in which case the gesture that ran did not\n * actually change what was selected, and the event is skipped.\n *\n * @param before - The selection as it stood immediately before the\n * mutating gesture ran.\n */\n private _notifySelectionChange(before: ReadonlySet<TreeNode>): void {\n if (selectionsEqual(before, this._selectedNodes)) {\n return;\n }\n\n this.emit(\"selection\", this.getSelectedNodes());\n }\n\n /**\n * Fills `_selectedNodes` with every node in `_flatRows` between `anchorIdx` and `focusIdx` (inclusive).\n *\n * @param anchorIdx - One end of the range.\n * @param focusIdx - The other end of the range (may be less than `anchorIdx`).\n */\n private _rangeSelect(anchorIdx: number, focusIdx: number): void {\n const lo = Math.min(anchorIdx, focusIdx);\n const hi = Math.max(anchorIdx, focusIdx);\n\n this._selectedNodes.clear();\n\n for (let i = lo; i <= hi; i++) {\n this._selectedNodes.add(this._flatRows[i].node);\n }\n }\n\n /**\n * Replaces the selection with the single node at `index`, resets the anchor and focus, and scrolls it into view.\n *\n * @param index - Zero-based index into `_flatRows`.\n */\n private _selectAtIndex(index: number): void {\n if (index < 0 || index >= this._flatRows.length) {\n return;\n }\n\n const node = this._flatRows[index].node;\n const before = new Set(this._selectedNodes);\n\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this._anchorNode = node;\n this._focusNode = node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n this._notifySelectionChange(before);\n }\n\n /**\n * Extends the range selection from `_anchorNode` to `index`, moving only the focus end.\n *\n * @param index - Zero-based index into `_flatRows` for the new focus position.\n */\n private _extendSelectionTo(index: number): void {\n if (index < 0 || index >= this._flatRows.length) {\n return;\n }\n\n const before = new Set(this._selectedNodes);\n\n const anchorIdx = this._anchorNode\n ? this._flatRows.findIndex(r => r.node === this._anchorNode)\n : index;\n\n this._rangeSelect(anchorIdx < 0 ? index : anchorIdx, index);\n this._focusNode = this._flatRows[index].node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n this._notifySelectionChange(before);\n }\n\n /**\n * Scrolls the tree element so the row at `index` is fully visible, without\n * moving if it already is. Delegates to the shared base implementation.\n *\n * @param index - Zero-based index into `_flatRows`.\n */\n private _scrollIntoView(index: number): void {\n this.scrollRowIntoView(index);\n }\n\n /**\n * Handles keyboard navigation for the tree widget.\n *\n * @remarks\n * Implements the WAI-ARIA tree keyboard interaction pattern:\n * - `ArrowDown` / `ArrowUp` — move to next/previous visible row (plain) or extend range (Shift)\n * - `ArrowRight` — expand a collapsed node, or move into an already-expanded node's first child\n * - `ArrowLeft` — collapse an expanded node, or move to its parent\n * - `Home` / `End` — jump to first/last visible row (plain) or extend range to edge (Shift)\n *\n * @param e - The keyboard event fired on the tree element.\n */\n private _onKeyDown(e: KeyboardEvent): void {\n const navigable = new Set(['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']);\n if (!navigable.has(e.key)) {\n return;\n }\n\n const flatRows = this._flatRows;\n if (flatRows.length === 0) {\n return;\n }\n\n e.preventDefault();\n\n // For Shift+Arrow the moving end is the focus node, not the anchor\n const focusIdx = this._focusNode\n ? flatRows.findIndex(r => r.node === this._focusNode)\n : -1;\n\n if (e.key === 'ArrowDown') {\n const next = focusIdx < 0 ? 0 : Math.min(focusIdx + 1, flatRows.length - 1);\n e.shiftKey ? this._extendSelectionTo(next) : this._selectAtIndex(next);\n\n } else if (e.key === 'ArrowUp') {\n const prev = focusIdx < 0 ? 0 : Math.max(focusIdx - 1, 0);\n e.shiftKey ? this._extendSelectionTo(prev) : this._selectAtIndex(prev);\n\n } else if (e.key === 'Home') {\n e.shiftKey ? this._extendSelectionTo(0) : this._selectAtIndex(0);\n\n } else if (e.key === 'End') {\n const last = flatRows.length - 1;\n e.shiftKey ? this._extendSelectionTo(last) : this._selectAtIndex(last);\n\n } else if (e.key === 'ArrowRight') {\n if (focusIdx < 0) {\n this._selectAtIndex(0);\n return;\n }\n\n const { node } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (!hasChildren) {\n return;\n }\n\n if (this._expandedNodes.has(node)) {\n // Already expanded — move focus into the first child\n this._selectAtIndex(focusIdx + 1);\n } else {\n // Collapsed — expand without moving focus\n this._onToggle(node);\n }\n\n } else if (e.key === 'ArrowLeft') {\n if (focusIdx < 0) {\n return;\n }\n\n const { node, depth } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (hasChildren && this._expandedNodes.has(node)) {\n // Expanded — collapse and stay on the same node\n this._onToggle(node);\n const newIdx = this._flatRows.findIndex(r => r.node === node);\n if (newIdx >= 0) {\n this._scrollIntoView(newIdx);\n }\n } else if (depth > 0) {\n // Collapsed or leaf — move to parent (nearest ancestor with depth - 1)\n for (let i = focusIdx - 1; i >= 0; i--) {\n if (flatRows[i].depth === depth - 1) {\n this._selectAtIndex(i);\n break;\n }\n }\n }\n }\n }\n\n /**\n * Dispatches clicks to either toggle expand/collapse or select a row.\n *\n * @remarks\n * Supports three selection modes via modifier keys:\n * - Plain click: replace selection with the clicked node\n * - Ctrl/Cmd+click: toggle the clicked node without disturbing others\n * - Shift+click: range-select from the anchor to the clicked node\n *\n * @param e - The click event whose target is inside the tree's DOM subtree.\n */\n private _handleClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const toggle = row.getToggle();\n if (toggle) {\n const toggleEl = toggle.getElement();\n if (toggleEl && (target === toggleEl || DOM.source.contains(toggleEl, target))) {\n this._onToggle(node);\n return;\n }\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n const clickedIdx = this._flatRows.findIndex(r => r.node === node);\n\n if (e.shiftKey && this._anchorNode) {\n this._extendSelectionTo(clickedIdx);\n } else if (e.ctrlKey || e.metaKey) {\n const before = new Set(this._selectedNodes);\n\n if (this._selectedNodes.has(node)) {\n this._selectedNodes.delete(node);\n } else {\n this._selectedNodes.add(node);\n }\n\n this._anchorNode = node;\n this._focusNode = node;\n this._updateSelectionStyle();\n this._notifySelectionChange(before);\n } else {\n this._selectAtIndex(clickedIdx);\n }\n\n return;\n }\n }\n\n /**\n * Resolves the node under a right-click and emits the `\"contextmenu\"` event.\n *\n * @remarks\n * Mirrors {@link _handleClick}'s row-matching but deliberately does not change\n * the selection: a right-click positions a context menu over a node without\n * triggering the selection-driven side effects (e.g. opening the node) that a\n * left-click would. When a node row is hit the browser's native menu is\n * suppressed via `preventDefault` and `\"contextmenu\"` fires with the node and\n * the originating event; a right-click on empty space is left to the browser.\n *\n * @param e - The contextmenu event whose target is inside the tree's subtree.\n */\n private _handleContextMenu(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n e.preventDefault();\n this.emit(\"contextmenu\", node, e);\n\n return;\n }\n }\n\n /**\n * Resolves the node under a double-click and emits the `\"dblclick\"` event.\n *\n * @remarks\n * Mirrors {@link _handleContextMenu}'s row-matching. The first click of the\n * pair has already run through {@link _handleClick} and set the selection, so\n * this only layers an activation signal on top — it does not re-select.\n * `preventDefault` suppresses the browser's double-click text selection of the\n * row label.\n *\n * @param e - The dblclick event whose target is inside the tree's subtree.\n */\n private _handleDblClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n e.preventDefault();\n this.emit(\"dblclick\", node);\n\n // Double-clicking a parent row toggles its expansion, the\n // file-explorer convention. A double-click on the toggle caret is\n // skipped: `_handleClick` already fired `_onToggle` on each of the\n // two clicks, so its net state is unchanged and toggling again here\n // would flip it a third time. Leaf nodes have nothing to expand, so\n // their `\"dblclick\"` stays a pure activation signal.\n const toggle = row.getToggle();\n const toggleEl = toggle ? toggle.getElement() : undefined;\n const onToggle = !!toggleEl\n && (target === toggleEl || DOM.source.contains(toggleEl, target));\n\n if (!onToggle && this._isExpandable(node)) {\n this._onToggle(node);\n }\n\n return;\n }\n }\n\n /**\n * Applies or removes the selection highlight, focus ring, and aria-selected on all bound pool rows.\n */\n private _updateSelectionStyle(): void {\n for (const row of this._rowPool) {\n const rowEl = row.getElement();\n\n if (!rowEl) {\n continue;\n }\n\n const node = row.getNode();\n const isSelected = node !== null && this._selectedNodes.has(node!);\n const isFocused = node !== null && node === this._focusNode;\n\n // Per-node ephemeral selection/focus styling on a pooled row re-bound\n // to a different node on every render. Routing this through cached\n // Component setters would persist it into _options and replay it onto\n // the next node bound to this reused row, so write/remove the inline\n // styles directly instead.\n if (isSelected) {\n DOM.sink.apply(rowEl, { style: { \"background-color\": SELECTED_BG } });\n } else {\n DOM.sink.apply(rowEl, { style: { \"background-color\": null } });\n }\n\n if (isFocused) {\n DOM.sink.apply(rowEl, { style: { \"outline\": \"2px solid var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))\", \"outline-offset\": \"-2px\" } });\n } else {\n DOM.sink.apply(rowEl, { style: { \"outline\": null, \"outline-offset\": null } });\n }\n\n row.getAria().setSelected(isSelected);\n }\n }\n\n /**\n * Sets `aria-activedescendant` on the tree container to the pool row bound to `_focusNode`.\n *\n * @remarks Must be called after `renderWindow()` so the pool slot is guaranteed in the DOM.\n */\n private _updateActiveDescendant(): void {\n if (!this._focusNode) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n for (const row of this._rowPool) {\n if (row.getNode() === this._focusNode) {\n this.getAria().setActiveDescendant(row.getId());\n\n return;\n }\n }\n\n this.getAria().setActiveDescendant(\"\");\n }\n\n /**\n * Recomputes the visible row window, rebinds changed pool slots, and hides excess rows.\n */\n protected renderWindow(): void {\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n const scroller = this._scroller;\n\n const totalRows = this._flatRows.length;\n const totalHeight = totalRows * ROW_HEIGHT;\n\n // Loose clamp using the last-known content width (the actual contentW\n // for this frame is computed below from the first row-bind pass).\n scroller.clampToContent(this._lastRowWidth, totalHeight);\n\n const visibleHeight = this.getHeight() || 0;\n const win = this.computeVisibleWindow(scroller.getScrollY(), visibleHeight, totalRows);\n\n const poolTarget = this.computePoolTarget(win.windowSize, visibleHeight, totalRows);\n this.growRowPool(poolTarget);\n\n const { reboundFlags, maxContentWidth } =\n this._bindAndMeasure(win.firstRow, win.windowSize);\n\n // Only the visible window was measured, so fold the widest row seen this\n // frame into the running maximum for the current flattened set (reset in\n // `_flatten`). The content width then only grows as wider rows are\n // discovered and never shrinks when they scroll away, so the horizontal\n // scrollbar stays stable instead of jittering with the visible rows.\n if (maxContentWidth > this._maxContentWidth) {\n this._maxContentWidth = maxContentWidth;\n }\n\n // Fill to the effective viewport width (owner width minus the vertical\n // scrollbar's reservation when that bar is visible), not the raw owner\n // width. Sizing rows to the full width would push their trailing edge\n // under the vertical bar, and handing that full width to\n // `layoutScrollbars` as the content width forces a spurious horizontal\n // bar for the reserved band. `clampToContent` above has already refreshed\n // the scroller's content metrics this pass.\n const rowWidth = Math.max(scroller.getViewportWidth(), this._maxContentWidth);\n if (rowWidth !== this._lastRowWidth) {\n this._lastRowWidth = rowWidth;\n this.invalidateGeom();\n }\n\n this._positionRows(win.firstRow, win.windowSize, rowWidth, reboundFlags);\n this.hideExcessPoolRows(win.windowSize);\n\n this._updateSelectionStyle();\n\n scroller.layoutScrollbars(rowWidth, totalHeight);\n }\n\n /**\n * First pass: binds visible pool slots to their flattened rows (when\n * rebound) and computes the maximum content width across visible rows so\n * the second pass knows how wide each row should be.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @returns Per-slot rebind flags (parallel to the window) and the widest\n * content width seen, both consumed by {@link _positionRows}.\n */\n private _bindAndMeasure(firstRow: number, windowSize: number): { reboundFlags: boolean[], maxContentWidth: number } {\n const reboundFlags: boolean[] = new Array(windowSize);\n let maxContentWidth = 0;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const flatRow = this._flatRows[dataIndex];\n const hasChildren = this._isExpandable(flatRow.node);\n const expanded = this._expandedNodes.has(flatRow.node);\n const loading = this._loadingNodes.has(flatRow.node);\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound) {\n const selected = this._selectedNodes.has(flatRow.node);\n row.setRowData(flatRow.node, flatRow.depth, hasChildren, expanded, flatRow.siblingCount, flatRow.posInSet, selected, loading);\n this._boundIndices[i] = dataIndex;\n }\n\n reboundFlags[i] = wasRebound;\n\n const cw = row.getContentWidth(INDENT_PX);\n if (cw > maxContentWidth) {\n maxContentWidth = cw;\n }\n }\n\n return { reboundFlags, maxContentWidth };\n }\n\n /**\n * Second pass: positions visible rows at `dataIndex * ROW_HEIGHT`, sizes\n * them to `rowWidth`, marks them displayed, and re-lays out their children\n * only when the row was rebound or its geometry changed.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @param rowWidth - The horizontal extent of each row in pixels.\n * @param reboundFlags - The per-slot rebind flags produced by {@link _bindAndMeasure}.\n */\n private _positionRows(firstRow: number, windowSize: number, rowWidth: number, reboundFlags: boolean[]): void {\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const wasRebound = reboundFlags[i];\n\n const geomChanged = this.positionRow(i, dataIndex * ROW_HEIGHT, rowWidth);\n\n if (wasRebound || geomChanged) {\n row.layoutChildren(ROW_HEIGHT, INDENT_PX);\n }\n }\n }\n\n /**\n * Constructs the `VirtualScroller` and wires click and keyboard listeners.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n this.initScroller(el);\n\n Event.addSubtreeListener(this, \"click\", this._handleClick);\n Event.addSubtreeListener(this, \"contextmenu\", this._handleContextMenu);\n Event.addSubtreeListener(this, \"dblclick\", this._handleDblClick);\n Event.addListener(this, \"keydown\", this._onKeyDown);\n\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Calls the layout manager and then refreshes the virtual-scroll viewport.\n *\n * @returns This component, for method chaining.\n *\n * @remarks\n * Overrides [`Component.doLayout`](/api/core/classes/Component#doLayout) so that layout-manager-driven size changes\n * (e.g. from a parent Tab or Split) automatically update the rendered row window.\n */\n doLayout(): this {\n if (this.isLayoutPaused()) {\n return this;\n }\n\n super.doLayout();\n this.renderWindow();\n\n return this;\n }\n}\n\nconst TreeCallable = callable(Tree);\ntype TreeCallable = Tree;\nexport {\n Tree as _Tree,\n TreeCallable as Tree\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * Pixel width reserved for the icon column (icon + small gap before the label).\n */\nconst ICON_WIDTH = 20;\n\n/** Square edge length used for the icon glyph. */\nconst ICON_SIZE = 16;\n\n/**\n * Resolves a [`Glyph`](/api/component/display/classes/Glyph) registry name for\n * the row currently being rendered.\n *\n * @param node - The bound tree node.\n * @param context - The bound-node state for this render pass.\n * @returns The glyph registry name to display.\n *\n * @category Components\n */\nexport type IconLabelGlyphResolver = (node: TreeNode, context: TreeNodeRenderContext) => string;\n\n/**\n * A [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) that\n * renders an icon followed by a text label.\n *\n * @remarks\n * The icon class is resolved per-row by a caller-supplied\n * {@link IconLabelGlyphResolver}. Because [`Glyph`](/api/component/display/classes/Glyph)\n * names are immutable, this renderer constructs a fresh `Glyph` whenever the\n * resolver returns a different name — matching the pattern used by `TreeRow`\n * for its expand/collapse toggle.\n *\n * @example\n * ```typescript\n * tree.setRendererFactory(() => new IconLabelTreeNodeRenderer(\n * (node) => (node.children?.length ? \"chevron-down\" : \"file\")\n * ));\n * ```\n *\n * @category Components\n */\nexport class IconLabelTreeNodeRenderer extends TreeNodeRenderer {\n\n private _icon: Glyph | null = null;\n private _label: Text;\n private _glyphResolver: IconLabelGlyphResolver;\n private _currentGlyph: string | null = null;\n\n /**\n * Constructs an icon+label renderer.\n *\n * @param glyphResolver - Callback that resolves the glyph registry name for\n * a given node. Defaults to `() => \"file\"`.\n */\n constructor(glyphResolver: IconLabelGlyphResolver = () => \"file\") {\n super();\n this.clearInsets();\n\n // No icon is built here: the glyph is constructed lazily in the first\n // update() from the resolved name, so a caller supplying its own resolver\n // never forces the default \"file\" glyph to be registered.\n this._glyphResolver = glyphResolver;\n this._label = new Text();\n\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Updates the icon (constructing a new Glyph if the resolved name changed)\n * and the label text.\n *\n * @param context - The bound-node state for this render pass.\n */\n update(context: TreeNodeRenderContext): void {\n const next = this._glyphResolver(context.node, context);\n\n if (next !== this._currentGlyph) {\n const el = this.getElement();\n\n if (el && this._icon) {\n const oldEl = this._icon.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n }\n\n this._icon = new Glyph(next);\n this._icon.clearInsets();\n this._currentGlyph = next;\n\n if (el) {\n DOM.sink.insertBefore(el, this._icon.getElement(true)!, this._label.getElement() ?? null);\n }\n }\n\n this._label.setText(context.node.label);\n this._label.measure();\n }\n\n /**\n * Returns the natural pixel width of the icon column plus the measured\n * label width.\n */\n getContentWidth(): number {\n return (this._icon ? ICON_WIDTH : 0) + (this._label.getPreferredSize()?.width ?? 0);\n }\n\n /**\n * Positions the icon (vertically centred in the row) and the label.\n *\n * @param width - The horizontal extent of the renderer in pixels.\n * @param height - The vertical extent of the renderer in pixels.\n */\n layoutChildren(width: number, height: number): void {\n if (this._icon) {\n this._icon.setAutoCommitStyle(false);\n this._icon.setX(0);\n this._icon.setY(Math.max(0, (height - ICON_SIZE) / 2));\n this._icon.setWidth(ICON_SIZE);\n this._icon.setHeight(ICON_SIZE);\n this._icon.setAutoCommitStyle(true);\n }\n\n const labelX = this._icon ? ICON_WIDTH : 0;\n const labelWidth = Math.max(0, width - labelX);\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(labelX);\n this._label.setY(0);\n this._label.setWidth(labelWidth);\n this._label.setHeight(height);\n this._label.setLineHeight(height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element (and the icon, when one is already\n * bound) to the renderer's DOM element. A later icon change is inserted before\n * the label by {@link update}.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n if (this._icon) {\n DOM.sink.appendChild(el, this._icon.getElement(true)!);\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tree.es.js","names":[],"sources":["../../../src/typescript/lib/component/tree/TreeNodeRenderer.ts","../../../src/typescript/lib/component/tree/renderer/Label.ts","../../../src/typescript/lib/component/tree/TreeRow.ts","../../../src/typescript/lib/component/tree/Tree.ts","../../../src/typescript/lib/component/tree/renderer/IconLabel.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * Abstract base class for tree node renderers.\n *\n * @remarks\n * A `TreeNodeRenderer` controls the content area of a single\n * [`TreeRow`](/api/component/tree/classes/TreeRow) — that is, everything to\n * the right of the expand/collapse toggle. The owning [`Tree`](/api/component/tree/classes/Tree)\n * holds a renderer factory; each pool slot calls that factory once at\n * construction and rebinds via {@link update} when the slot is remapped to a\n * different node or its selection/expansion state changes.\n *\n * The toggle remains under `TreeRow`'s control because click detection routes\n * through it; renderers never see toggle clicks.\n *\n * Subclasses must implement {@link update}, {@link getContentWidth}, and\n * {@link layoutChildren}. See [`LabelTreeNodeRenderer`](/api/component/tree/classes/LabelTreeNodeRenderer)\n * for the canonical default.\n *\n * @category Components\n */\nexport abstract class TreeNodeRenderer extends Component {\n\n constructor() {\n super();\n }\n\n /**\n * Rebinds this renderer to new node data.\n *\n * @param context - The bound-node state for this render pass.\n *\n * @remarks\n * Called every time the owning pool slot is mapped to a different node, or\n * when an expansion-state change forces a full re-bind. Plain selection\n * style changes (background highlight, focus ring) are handled by the\n * `Tree` directly on the row element and do not flow through `update()`.\n */\n abstract update(context: TreeNodeRenderContext): void;\n\n /**\n * Returns the natural pixel width this renderer would render at given the\n * current bound node.\n *\n * @returns Content width in pixels.\n *\n * @remarks\n * Used by [`Tree`](/api/component/tree/classes/Tree) to compute the\n * horizontal scroll extent. Renderers that auto-measure their children\n * should return the sum of those measured widths.\n */\n abstract getContentWidth(): number;\n\n /**\n * Positions this renderer's internal children inside its content box.\n *\n * @param width - The horizontal extent of the renderer in pixels.\n * @param height - The vertical extent of the renderer in pixels.\n *\n * @remarks\n * Called by `TreeRow.layoutChildren` after the renderer's own size has\n * been set via `setX`/`setY`/`setWidth`/`setHeight`.\n *\n * The `width` and `height` arguments are this renderer's **outer** box, so\n * an implementation must not place children against them directly: take the\n * rectangle from [`getContentBounds()`](/api/core/classes/Component), which\n * subtracts any border and padding the renderer carries, and fall back to\n * the arguments only when it returns `null` (no element yet). A child's\n * containing block is already this renderer's padding box, so one placed\n * at `(0, 0)` sized to `width` / `height` starts inside the border and\n * overruns the opposite edge by its width, where the `overflow: hidden`\n * every component carries clips it.\n */\n abstract layoutChildren(width: number, height: number): void;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * The default [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) —\n * a single `<span>` carrying the node's label.\n *\n * @remarks\n * Used as the implicit factory by [`Tree`](/api/component/tree/classes/Tree)\n * when no custom renderer is set. The internal `Text` runs with\n * `autoMeasure(false)`; an explicit `measure()` call after each `setText`\n * caches the natural width for `getContentWidth()` to read on the next layout\n * pass.\n *\n * @example\n * ```typescript\n * tree.setRendererFactory(() => new LabelTreeNodeRenderer());\n * ```\n *\n * @category Components\n */\nexport class LabelTreeNodeRenderer extends TreeNodeRenderer {\n\n private _label: Text;\n\n /**\n * Constructs a label renderer with an empty text node. The label is\n * populated on the first {@link update} call.\n */\n constructor() {\n super();\n this.clearInsets();\n\n this._label = new Text();\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Returns the underlying label component so consumers can tweak font /\n * colour properties at construction time.\n *\n * @returns The internal [`Text`](/api/component/input/classes/Text) instance.\n */\n getLabel(): Text {\n return this._label;\n }\n\n /**\n * Updates the rendered label text and re-measures.\n *\n * @param context - The bound-node state for this render pass.\n */\n update(context: TreeNodeRenderContext): void {\n this._label.setText(context.node.label);\n this._label.measure();\n }\n\n /**\n * Returns the cached natural width of the rendered label.\n *\n * @returns Label width in pixels.\n */\n getContentWidth(): number {\n return this._label.getPreferredSize()?.width ?? 0;\n }\n\n /**\n * Sizes the label to fill the renderer's allocated box and centres its\n * line-box vertically by matching `line-height` to the row height.\n *\n * The label goes inside this renderer's content box, so a border or padding\n * on the renderer shrinks it rather than being painted over.\n *\n * @param _width - Unused; the label sizes to its natural content width.\n * @param height - The vertical extent of the row in pixels. Used only while\n * the renderer has no element yet and the content box is unavailable.\n */\n layoutChildren(_width: number, height: number): void {\n // No `width` in the fallback: this renderer never reads the box's\n // width, so the argument stays genuinely unused.\n const box = this.getContentBounds() ?? { x: 0, y: 0, height };\n const labelWidth = this.getContentWidth();\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(box.x);\n this._label.setY(box.y);\n this._label.setWidth(labelWidth);\n this._label.setHeight(box.height);\n this._label.setLineHeight(box.height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element to the renderer's DOM element.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { caret_down } from \"~/glyphs/solid/caret_down.js\";\nimport { caret_right } from \"~/glyphs/solid/caret_right.js\";\n\nGlyph.register(caret_down, caret_right);\n\n/** Width in pixels reserved for the expand/collapse toggle icon. */\nexport const TOGGLE_WIDTH = 20;\n\n/**\n * A single visible row in the {@link Tree} virtual-scroll pool.\n *\n * Each pool slot holds one {@link TreeNode} at a time. The {@link Tree} calls\n * {@link setRowData} to rebind a slot to a different node and\n * {@link layoutChildren} to reposition the toggle and renderer after the row's\n * own dimensions are updated.\n *\n * @remarks\n * The toggle ([`Glyph`](/api/component/display/classes/Glyph)) and content\n * renderer are appended directly to the row's DOM element in `init()` rather\n * than via `addComponent`, so their preferred-size change notifications do not\n * propagate up to the Tree and trigger unnecessary layout passes. Leaf rows\n * have no toggle; non-leaf rows swap in a fresh `caret-down` / `caret-right`\n * glyph on each state change rather than mutating a single character. The row\n * content area (everything to the right of the toggle) is owned by a\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) supplied\n * via the constructor factory.\n */\nclass TreeRow extends Component {\n\n private _toggle: Glyph | null = null;\n private _spinner: ProgressSpinner | null = null;\n private _renderer: TreeNodeRenderer;\n private _node: TreeNode | null = null;\n private _depth: number = 0;\n\n constructor(rendererFactory: () => TreeNodeRenderer = () => new LabelTreeNodeRenderer()) {\n super();\n\n this.getAria().setRole(\"treeitem\");\n\n this._renderer = rendererFactory();\n }\n\n /**\n * Returns the toggle icon glyph, or null when the bound node is a leaf.\n *\n * @returns The toggle [`Glyph`](/api/component/display/classes/Glyph) instance, or null.\n */\n getToggle(): Glyph | null {\n return this._toggle;\n }\n\n /**\n * Returns the renderer currently bound to this pool row.\n *\n * @returns The active [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n */\n getRenderer(): TreeNodeRenderer {\n return this._renderer;\n }\n\n /**\n * Replaces this row's content renderer, swapping the underlying DOM\n * element in place.\n *\n * @param renderer - The new renderer instance to install.\n * @returns This row, for method chaining.\n *\n * @remarks\n * The new renderer's element is appended to the row immediately. The\n * caller is expected to follow up with `setRowData` (typically via the\n * Tree's next render pass) so the renderer receives an `update()` before\n * being laid out.\n */\n setRenderer(renderer: TreeNodeRenderer): this {\n const el = this.getElement();\n\n if (el) {\n const oldEl = this._renderer.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n\n DOM.sink.appendChild(el, renderer.getElement(true)!);\n }\n\n this._renderer = renderer;\n\n return this;\n }\n\n /**\n * Returns the tree node currently bound to this pool row.\n *\n * @returns The bound {@link TreeNode}, or null if the slot is unbound.\n */\n getNode(): TreeNode | null {\n return this._node;\n }\n\n /**\n * Returns the depth of the currently bound node.\n *\n * @returns The zero-based depth level.\n */\n getDepth(): number {\n return this._depth;\n }\n\n /**\n * Binds this pool slot to a new node, updating the toggle icon, content\n * renderer, and ARIA positional attributes.\n *\n * @param node - The tree node to display.\n * @param depth - The zero-based nesting depth (controls indentation).\n * @param hasChildren - Whether the node has child nodes (determines toggle visibility).\n * @param expanded - Whether the node is currently expanded.\n * @param siblingCount - Total number of siblings at this level under the same parent.\n * @param posInSet - 1-based position of this node among its siblings.\n * @param selected - Whether the node is currently selected.\n * @param loading - Whether this node's lazy children are currently loading;\n * when true a spinner replaces the toggle caret.\n */\n setRowData(node: TreeNode, depth: number, hasChildren: boolean, expanded: boolean, siblingCount: number, posInSet: number, selected: boolean, loading: boolean): this {\n this._node = node;\n this._depth = depth;\n\n if (this._toggle) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._toggle.getElement(true)!);\n }\n this._toggle = null;\n }\n\n if (this._spinner) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._spinner.getElement(true)!);\n }\n this._spinner = null;\n }\n\n if (loading) {\n // No explicit size: the spinner tracks the theme font-size so it\n // reads as the same visual weight as the caret glyph it replaces,\n // and `layoutChildren` fits it into the TOGGLE_WIDTH box.\n const spinner = new ProgressSpinner();\n this._spinner = spinner;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, spinner.getElement(true)!);\n }\n } else if (hasChildren) {\n const toggle = new Glyph(expanded ? \"caret-down\" : \"caret-right\");\n toggle.setCursor(\"pointer\");\n toggle.clearInsets();\n toggle.getAria().setHidden(true);\n this._toggle = toggle;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, toggle.getElement(true)!);\n }\n }\n\n this._renderer.update({ node, depth, expanded, selected, hasChildren });\n\n this.getAria().setLevel(depth + 1);\n this.getAria().setExpanded(hasChildren ? expanded : null);\n this.getAria().setSetSize(siblingCount);\n this.getAria().setPosInSet(posInSet);\n\n return this;\n }\n\n /**\n * Positions the toggle and renderer sub-components inside the row's content\n * box — the rectangle {@link Component.getContentBounds} returns, which a\n * border or padding on the row shrinks. Placing them against the row's outer\n * box instead makes the renderer overflow on both axes, and the row's\n * `overflow: hidden` clips it.\n *\n * @param rowHeight - The current height of this row in pixels. Used only\n * while the row has no element yet and the content box is unavailable;\n * otherwise the children's height comes from the content box, which is\n * this same height less the row's own border and padding.\n * @param indentPx - Pixels of indentation per depth level.\n */\n layoutChildren(rowHeight: number, indentPx: number): void {\n const box = this.getContentBounds()\n ?? { x: 0, y: 0, width: this.getWidth() || 0, height: rowHeight };\n\n const indent = box.x + this._depth * indentPx;\n\n if (this._toggle) {\n // The caret is a rigid SVG Glyph (min/max pinned to its 16×16\n // preferredSize), so it cannot fill the taller row — centre it\n // geometrically instead of relying on line-height (a no-op for SVG).\n const glyphHeight = this._toggle.getPreferredSize()?.height ?? box.height;\n this._toggle.setAutoCommitStyle(false);\n this._toggle.setX(indent);\n this._toggle.setY(box.y + Math.max(0, (box.height - glyphHeight) / 2));\n this._toggle.setWidth(TOGGLE_WIDTH);\n this._toggle.setHeight(glyphHeight);\n this._toggle.setAutoCommitStyle(true);\n }\n\n if (this._spinner) {\n this._spinner.setAutoCommitStyle(false);\n this._spinner.setX(indent);\n this._spinner.setY(box.y);\n this._spinner.setWidth(TOGGLE_WIDTH);\n this._spinner.setHeight(box.height);\n this._spinner.setAutoCommitStyle(true);\n\n // The spinner owns its inner arc, which only positions itself when\n // its own doLayout runs; the row appends it directly rather than via\n // addComponent, so drive that layout here once the box is sized.\n this._spinner.doLayout();\n }\n\n const labelX = indent + TOGGLE_WIDTH;\n const labelBoxW = Math.max(0, box.x + box.width - labelX);\n\n this._renderer.setAutoCommitStyle(false);\n this._renderer.setX(labelX);\n this._renderer.setY(box.y);\n this._renderer.setWidth(labelBoxW);\n this._renderer.setHeight(box.height);\n this._renderer.setAutoCommitStyle(true);\n\n this._renderer.layoutChildren(labelBoxW, box.height);\n }\n\n /**\n * Returns the natural pixel width needed to display this row's full content\n * (indent + toggle + renderer content) without horizontal clipping.\n *\n * @param indentPx - Pixels of indentation per depth level.\n */\n getContentWidth(indentPx: number): number {\n const indent = this._depth * indentPx;\n const contentW = this._renderer.getContentWidth();\n\n return indent + TOGGLE_WIDTH + contentW;\n }\n\n /**\n * Appends the renderer sub-component element to the row's DOM element.\n * The toggle glyph (if any) is appended on demand by `setRowData`.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n DOM.sink.appendChild(el, this._renderer.getElement(true)!);\n\n return this;\n }\n}\n\nconst TreeRowCallable = callable(TreeRow);\ntype TreeRowCallable = TreeRow;\nexport {\n TreeRow as _TreeRow,\n TreeRowCallable as TreeRow\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { VirtualRowView } from \"~/component/shared/VirtualRowView.js\";\nimport { selectionsEqual } from \"~/component/shared/selectionsEqual.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeRow } from \"~/component/tree/TreeRow.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link Tree}.\n *\n * @category Components\n */\nexport type TreeEvent = \"selection\" | \"loaderror\" | \"contextmenu\" | \"dblclick\";\n\n/** Pixels of indentation added per depth level. */\nconst INDENT_PX = 16;\n\n/** Fixed height in pixels for every visible row. */\nconst ROW_HEIGHT = 24;\n\n/** CSS background applied to the selected row. */\nconst SELECTED_BG = \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\";\n\n/**\n * A pre-flattened entry in the visible subtree used by the virtual scroll.\n */\ninterface FlatRow {\n node: TreeNode;\n depth: number;\n siblingCount: number;\n posInSet: number;\n}\n\n/**\n * Construction-time options for {@link Tree}. Root nodes are set via\n * `setNodes()` and the renderer factory via `setRendererFactory()`; this\n * interface only carries the event-listener bag and the inherited\n * {@link ComponentOptions}.\n *\n * @category Components\n */\nexport interface TreeOptions extends ComponentOptions {\n /**\n * Multi-event listener bag dispatched to {@link Tree.on} at\n * construction time.\n */\n listeners?: {\n selection?: (nodes: TreeNode[]) => void;\n loaderror?: (node: TreeNode, error: unknown) => void;\n contextmenu?: (node: TreeNode, event: MouseEvent) => void;\n dblclick?: (node: TreeNode) => void;\n };\n}\n\n/**\n * A hierarchical data view with collapsible nodes and virtual scrolling.\n *\n * Pass root nodes via {@link Tree.setNodes}. The tree flattens the currently\n * visible subtree into a single scrollable list and recycles a fixed pool of\n * internal row components — rebinding rows only when their data index changes,\n * mirroring the approach used in `table/Body.ts`.\n *\n * Scrolling is delegated to a `VirtualScroller` that owns the\n * rows-container transform, two custom scrollbar overlays, and the wheel/touch\n * handlers with fling momentum.\n *\n * @example\n * ```typescript\n * const tree = new Tree();\n * tree.setNodes([\n * { label: \"Fruits\", children: [\n * { label: \"Apple\" },\n * { label: \"Banana\" },\n * ]},\n * { label: \"Vegetables\" },\n * ]);\n * container.addComponent(tree);\n * ```\n *\n * @category Components\n */\nclass Tree extends VirtualRowView<TreeRow, TreeOptions> {\n\n private _nodes : TreeNode[] = [];\n private _expandedNodes : Set<TreeNode> = new Set();\n private _flatRows : FlatRow[] = [];\n private _lastRowWidth : number = 0;\n // Widest row content measured so far across the current flattened set. Only\n // the *visible* window is measured each frame, so tracking a running maximum\n // keeps the horizontal content width — and thus the H scrollbar — stable as\n // different-width rows scroll through the window, instead of jittering with\n // whatever happens to be on screen. Reset in `_flatten` when the row set is\n // rebuilt, so a narrower dataset (or a collapsed wide branch) re-derives it.\n private _maxContentWidth : number = 0;\n private _selectedNodes : Set<TreeNode> = new Set();\n private _loadingNodes : Set<TreeNode> = new Set();\n private _loadedNodes : Set<TreeNode> = new Set();\n private _anchorNode : TreeNode | null = null;\n private _focusNode : TreeNode | null = null;\n private _listeners : ListenerBag<TreeEvent> = new ListenerBag<TreeEvent>();\n private _rendererFactory : () => TreeNodeRenderer = () => new LabelTreeNodeRenderer();\n\n constructor(options?: TreeOptions) {\n super(options);\n\n this.setOverflow(\"hidden\");\n this.setBackgroundColor(\"var(--ts-ui-input-bg, rgb(255, 255, 255))\");\n this.setPreferredSize({ width: 200, height: 300 });\n this.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n\n this.getAria().setRole(\"tree\");\n this.getAria().setTabIndex(0);\n this.getAria().setMultiselectable(true);\n\n this.subscribeTheme(() => this.onThemeReflow());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Replaces the root nodes, collapses all nodes, clears selection, and re-renders.\n *\n * @param nodes - The new array of root {@link TreeNode} objects.\n */\n setNodes(nodes: TreeNode[]): this {\n this._nodes = nodes;\n this._expandedNodes.clear();\n this._selectedNodes.clear();\n this._loadingNodes.clear();\n this._loadedNodes.clear();\n this._anchorNode = null;\n this._focusNode = null;\n this._flatten();\n\n if (this.getElement()) {\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Expands every node that has (already-loaded) children, so the whole tree\n * is flattened and visible. Does not load lazy branches and does not change\n * the selection or emit any event.\n *\n * @returns This tree, for method chaining.\n */\n expandAll(): this {\n const addExpandable = (nodes: TreeNode[]): void => {\n for (const node of nodes) {\n if (node.children && node.children.length > 0) {\n this._expandedNodes.add(node);\n addExpandable(node.children);\n }\n }\n };\n\n addExpandable(this._nodes);\n this._reflattenAndRender();\n\n return this;\n }\n\n /** Returns the fixed row height every tree row is laid out at. */\n protected getRowHeight(): number {\n return ROW_HEIGHT;\n }\n\n /**\n * Constructs one pool row backed by the current renderer factory. The\n * base's `growRowPool` owns the append + parallel-array bookkeeping.\n *\n * @returns The new, un-appended pool row.\n */\n protected createPoolRow(): TreeRow {\n return new TreeRow(this._rendererFactory);\n }\n\n /**\n * Returns the root nodes currently displayed by the tree.\n *\n * @returns The array of root {@link TreeNode} objects.\n */\n getNodes(): TreeNode[] {\n return this._nodes;\n }\n\n /**\n * Returns the anchor node of the current selection (the last node selected by a plain click or\n * arrow key without Shift), or null if there is no selection.\n *\n * @returns The anchor {@link TreeNode}, or null.\n */\n getSelectedNode(): TreeNode | null {\n return this._anchorNode;\n }\n\n /**\n * Returns all currently selected nodes in no guaranteed order.\n *\n * @returns An array of selected {@link TreeNode} instances.\n */\n getSelectedNodes(): TreeNode[] {\n return Array.from(this._selectedNodes);\n }\n\n /**\n * Programmatically selects a single node, replacing any existing selection\n * and scrolling it into view.\n *\n * Unlike a user click this does **not** emit the `\"selection\"` event: it is a\n * state setter for syncing the tree to an external source of truth (e.g. the\n * active dock tab), so it must not re-trigger selection-driven side effects.\n * No-op when the node is not in the currently visible (flattened) set — e.g.\n * an ancestor is collapsed.\n *\n * @param node - A node currently held by this tree.\n *\n * @returns This tree, for method chaining.\n */\n selectNode(node: TreeNode): this {\n const index = this._flatRows.findIndex(r => r.node === node);\n if (index < 0) {\n return this;\n }\n\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this._anchorNode = node;\n this._focusNode = node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n\n return this;\n }\n\n /**\n * Finds the first node whose payload satisfies `predicate`, expanding every\n * ancestor on the path (loading lazy branches as needed) so the node becomes\n * visible, then scrolls it into view. Returns the node, or `null` when no\n * node matches.\n *\n * Unlike {@link selectNode} — which no-ops when the target sits under a\n * collapsed or not-yet-loaded ancestor — this walks the whole tree,\n * awaiting each lazy branch's `loadChildren` on the way down, so it can\n * reveal a node the user has never manually expanded to. Because it may\n * load every branch it visits, it is O(nodes) and issues one `loadChildren`\n * per unloaded branch it descends; call it for a deliberate \"jump to this\n * object\" action, not on a hot path.\n *\n * Revealing is not selecting: this method does not change the selection or\n * emit `\"selection\"`. Select the returned node with {@link selectNode} if the\n * reveal should also highlight it.\n *\n * The search is depth-first in root-then-child order and stops at the first\n * match. A lazy branch whose `loadChildren` rejects is skipped (its subtree\n * is treated as empty) and the walk continues; the failure is not surfaced\n * here (no `\"loaderror\"` is emitted for a reveal-driven load).\n *\n * @param predicate - Tested against each node's `data` payload (and the node\n * itself); return `true` for the node to reveal.\n *\n * @returns A promise resolving to the revealed {@link TreeNode}, or `null`.\n */\n async revealByPredicate(predicate: (data: unknown, node: TreeNode) => boolean): Promise<TreeNode | null> {\n const path = await this._findPath(this._nodes, predicate, []);\n if (path === null) {\n return null;\n }\n\n // Expand every ancestor (all but the target itself) so the target's row\n // enters the flattened set; the target's own children stay as they are.\n for (let i = 0; i < path.length - 1; i++) {\n this._expandedNodes.add(path[i]);\n }\n this._reflattenAndRender();\n\n const target = path[path.length - 1];\n const index = this._flatRows.findIndex(r => r.node === target);\n if (index >= 0) {\n this._scrollIntoView(index);\n }\n\n return target;\n }\n\n /**\n * Depth-first search for the first node satisfying `predicate`, returning\n * the full root-to-match path (inclusive) or `null`. Awaits and caches each\n * visited branch's lazy children so the walk can descend into unloaded\n * subtrees.\n *\n * @param nodes - The sibling nodes to search at this level.\n * @param predicate - The match test (see {@link revealByPredicate}).\n * @param prefix - The ancestor path leading to `nodes`.\n *\n * @returns The path from a root to the matching node, or `null`.\n */\n private async _findPath(\n nodes: TreeNode[],\n predicate: (data: unknown, node: TreeNode) => boolean,\n prefix: TreeNode[],\n ): Promise<TreeNode[] | null> {\n for (const node of nodes) {\n const here = [...prefix, node];\n\n if (predicate(node.data, node)) {\n return here;\n }\n\n const children = await this._ensureChildrenLoaded(node);\n if (children.length > 0) {\n const found = await this._findPath(children, predicate, here);\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n }\n\n /**\n * Returns a node's children, loading and caching them from `loadChildren`\n * first when the node is lazy and unloaded. Mirrors {@link _loadAndExpand}'s\n * cache writes (`children` + `_loadedNodes`) but does not expand the node —\n * a reveal only expands the ancestors on the path to its match. A rejected\n * load is swallowed and treated as an empty subtree.\n *\n * @param node - The node whose children are needed.\n *\n * @returns The node's children (possibly empty).\n */\n private async _ensureChildrenLoaded(node: TreeNode): Promise<TreeNode[]> {\n if (node.children && node.children.length > 0) {\n return node.children;\n }\n\n if (node.loadChildren !== undefined && !this._loadedNodes.has(node)) {\n try {\n const children = await node.loadChildren();\n\n node.children = children;\n this._loadedNodes.add(node);\n\n return children;\n } catch {\n return [];\n }\n }\n\n return node.children ?? [];\n }\n\n /**\n * Registers a listener for one of this tree's events.\n *\n * @param event - `\"selection\"` fires whenever the selection changes,\n * receiving the full array of selected {@link TreeNode} instances.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"selection\", listener: (nodes: TreeNode[]) => void): this;\n\n /**\n * Registers a listener for a lazy node's failed child load.\n *\n * @param event - `\"loaderror\"` fires when a node's `loadChildren` rejects;\n * the node stays collapsed and unloaded so toggling again retries.\n * @param listener - Receives the {@link TreeNode} whose load failed and the\n * rejection reason.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"loaderror\", listener: (node: TreeNode, error: unknown) => void): this;\n\n /**\n * Registers a listener for a right-click on a node.\n *\n * @param event - `\"contextmenu\"` fires when a node row is right-clicked,\n * receiving the {@link TreeNode} under the cursor and the originating\n * `MouseEvent` (its `clientX`/`clientY` position a context menu). The\n * default browser menu is suppressed and the tree's selection is left\n * unchanged, so a right-click never opens a node the way a left-click does.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"contextmenu\", listener: (node: TreeNode, event: MouseEvent) => void): this;\n\n /**\n * Registers a listener for a double-click (activation) on a node.\n *\n * @param event - `\"dblclick\"` fires when a node row is double-clicked,\n * receiving the {@link TreeNode} under the cursor. The first click of the\n * pair still emits `\"selection\"` as usual, so `\"dblclick\"` is an\n * activation signal layered on top of selection — consumers typically use\n * `\"selection\"` to preview a node and `\"dblclick\"` to open it.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"dblclick\", listener: (node: TreeNode) => void): this;\n on(event: TreeEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This tree, for method chaining.\n */\n off(event: TreeEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"selection\", nodes: TreeNode[]): void;\n protected emit(event: \"loaderror\", node: TreeNode, error: unknown): void;\n protected emit(event: \"contextmenu\", node: TreeNode, event_: MouseEvent): void;\n protected emit(event: \"dblclick\", node: TreeNode): void;\n protected emit(event: TreeEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the renderer factory used to create one\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer)\n * instance per pool slot.\n *\n * @param factory - Zero-argument function returning a new\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n * Defaults to `() => new LabelTreeNodeRenderer()`.\n * @returns This tree, for method chaining.\n *\n * @remarks\n * If pool rows already exist, each one's renderer is replaced in place via\n * `TreeRow.setRenderer(factory())`. The DOM pool itself is preserved.\n * Bindings are cleared so the next render pass rebinds and updates the\n * new renderers.\n */\n setRendererFactory(factory: () => TreeNodeRenderer): this {\n this._rendererFactory = factory;\n\n for (const row of this._rowPool) {\n row.setRenderer(factory());\n }\n\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n\n if (this.getElement()) {\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Returns the renderer factory currently in use.\n *\n * @returns The zero-argument factory function.\n */\n getRendererFactory(): () => TreeNodeRenderer {\n return this._rendererFactory;\n }\n\n /**\n * Reports whether a node renders an expand/collapse caret.\n *\n * @param node - The node to test.\n * @returns True when the node already carries children, or is a lazy node\n * declared with `hasChildren: true`.\n *\n * @remarks\n * Centralising the predicate keeps the caret, ARIA, and flatten decisions\n * from drifting apart. An eager node with an empty `children` array is not\n * expandable; a lazy node is expandable before its children exist.\n */\n private _isExpandable(node: TreeNode): boolean {\n return !!(node.children && node.children.length > 0) || node.hasChildren === true;\n }\n\n /**\n * Rebuilds the flat visible-row list from the current root nodes and expanded set,\n * computing `siblingCount` and `posInSet` (1-based) for each entry.\n */\n private _flatten(): void {\n this._flatRows = [];\n\n // The visible set is changing, so the widest-row running maximum no\n // longer describes it — reset it and let the next render pass re-derive\n // the content width from the new rows (a collapsed wide branch or a\n // narrower dataset must be able to shrink the horizontal extent).\n this._maxContentWidth = 0;\n\n const recurse = (nodes: TreeNode[], depth: number): void => {\n const siblingCount = nodes.length;\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n this._flatRows.push({ node, depth, siblingCount, posInSet: i + 1 });\n\n if (this._isExpandable(node) && this._expandedNodes.has(node) && node.children) {\n recurse(node.children, depth + 1);\n }\n }\n };\n\n recurse(this._nodes, 0);\n }\n\n /**\n * Re-flattens the visible subtree and forces a full rebind of the pool so\n * every row reflects the current expanded/loading state.\n *\n * @remarks\n * Called from the collapse path, the synchronous expand path, and the async\n * load path — anywhere the flattened-row set or a node's loading affordance\n * changes.\n */\n private _reflattenAndRender(): void {\n this._flatten();\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n /**\n * Toggles the expanded state of a node, re-flattens, and re-renders.\n *\n * @param node - The node whose expanded state should be toggled.\n *\n * @remarks\n * Collapse and already-resolved expansion commit synchronously. A lazy node\n * declared with `loadChildren` that has not loaded yet defers its expansion\n * to {@link _loadAndExpand}, which only commits once the loader resolves.\n */\n private _onToggle(node: TreeNode): void {\n if (this._expandedNodes.has(node)) {\n this._expandedNodes.delete(node);\n this._reflattenAndRender();\n\n return;\n }\n\n const needsLoad = node.loadChildren !== undefined\n && !this._loadedNodes.has(node)\n && !(node.children && node.children.length);\n\n if (!needsLoad) {\n this._expandedNodes.add(node);\n this._reflattenAndRender();\n\n return;\n }\n\n if (this._loadingNodes.has(node)) {\n return;\n }\n\n void this._loadAndExpand(node);\n }\n\n /**\n * Loads a lazy node's children, then commits its expansion.\n *\n * @param node - The lazy node to load and expand.\n *\n * @remarks\n * Marks the node loading (driving its spinner affordance), awaits\n * `loadChildren`, and on success writes `children`, records the node as\n * loaded and expanded. A rejection emits `\"loaderror\"` and leaves the node\n * collapsed and unloaded so toggling again retries. An empty resolved array\n * is treated as success: the node renders as an expanded, empty parent.\n *\n * If `setNodes` swaps the dataset while the loader is in flight, it clears\n * `_loadingNodes`, so a still-present membership check after the await tells\n * us the node is still live; an orphaned resolve commits nothing.\n */\n private async _loadAndExpand(node: TreeNode): Promise<void> {\n this._loadingNodes.add(node);\n this._reflattenAndRender();\n\n try {\n const children = await node.loadChildren!();\n\n if (!this._loadingNodes.has(node)) {\n return;\n }\n\n node.children = children;\n this._loadedNodes.add(node);\n this._expandedNodes.add(node);\n } catch (error) {\n if (!this._loadingNodes.has(node)) {\n return;\n }\n\n this.emit(\"loaderror\", node, error);\n } finally {\n this._loadingNodes.delete(node);\n this._reflattenAndRender();\n }\n }\n\n /**\n * Fires `\"selection\"` with the current selection, unless it has the same\n * membership as `before` — in which case the gesture that ran did not\n * actually change what was selected, and the event is skipped.\n *\n * @param before - The selection as it stood immediately before the\n * mutating gesture ran.\n */\n private _notifySelectionChange(before: ReadonlySet<TreeNode>): void {\n if (selectionsEqual(before, this._selectedNodes)) {\n return;\n }\n\n this.emit(\"selection\", this.getSelectedNodes());\n }\n\n /**\n * Fills `_selectedNodes` with every node in `_flatRows` between `anchorIdx` and `focusIdx` (inclusive).\n *\n * @param anchorIdx - One end of the range.\n * @param focusIdx - The other end of the range (may be less than `anchorIdx`).\n */\n private _rangeSelect(anchorIdx: number, focusIdx: number): void {\n const lo = Math.min(anchorIdx, focusIdx);\n const hi = Math.max(anchorIdx, focusIdx);\n\n this._selectedNodes.clear();\n\n for (let i = lo; i <= hi; i++) {\n this._selectedNodes.add(this._flatRows[i].node);\n }\n }\n\n /**\n * Replaces the selection with the single node at `index`, resets the anchor and focus, and scrolls it into view.\n *\n * @param index - Zero-based index into `_flatRows`.\n */\n private _selectAtIndex(index: number): void {\n if (index < 0 || index >= this._flatRows.length) {\n return;\n }\n\n const node = this._flatRows[index].node;\n const before = new Set(this._selectedNodes);\n\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this._anchorNode = node;\n this._focusNode = node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n this._notifySelectionChange(before);\n }\n\n /**\n * Extends the range selection from `_anchorNode` to `index`, moving only the focus end.\n *\n * @param index - Zero-based index into `_flatRows` for the new focus position.\n */\n private _extendSelectionTo(index: number): void {\n if (index < 0 || index >= this._flatRows.length) {\n return;\n }\n\n const before = new Set(this._selectedNodes);\n\n const anchorIdx = this._anchorNode\n ? this._flatRows.findIndex(r => r.node === this._anchorNode)\n : index;\n\n this._rangeSelect(anchorIdx < 0 ? index : anchorIdx, index);\n this._focusNode = this._flatRows[index].node;\n\n this._updateSelectionStyle();\n this._scrollIntoView(index);\n this.renderWindow();\n this._updateActiveDescendant();\n this._notifySelectionChange(before);\n }\n\n /**\n * Scrolls the tree element so the row at `index` is fully visible, without\n * moving if it already is. Delegates to the shared base implementation.\n *\n * @param index - Zero-based index into `_flatRows`.\n */\n private _scrollIntoView(index: number): void {\n this.scrollRowIntoView(index);\n }\n\n /**\n * Handles keyboard navigation for the tree widget.\n *\n * @remarks\n * Implements the WAI-ARIA tree keyboard interaction pattern:\n * - `ArrowDown` / `ArrowUp` — move to next/previous visible row (plain) or extend range (Shift)\n * - `ArrowRight` — expand a collapsed node, or move into an already-expanded node's first child\n * - `ArrowLeft` — collapse an expanded node, or move to its parent\n * - `Home` / `End` — jump to first/last visible row (plain) or extend range to edge (Shift)\n *\n * @param e - The keyboard event fired on the tree element.\n */\n private _onKeyDown(e: KeyboardEvent): void {\n const navigable = new Set(['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']);\n if (!navigable.has(e.key)) {\n return;\n }\n\n const flatRows = this._flatRows;\n if (flatRows.length === 0) {\n return;\n }\n\n e.preventDefault();\n\n // For Shift+Arrow the moving end is the focus node, not the anchor\n const focusIdx = this._focusNode\n ? flatRows.findIndex(r => r.node === this._focusNode)\n : -1;\n\n if (e.key === 'ArrowDown') {\n const next = focusIdx < 0 ? 0 : Math.min(focusIdx + 1, flatRows.length - 1);\n e.shiftKey ? this._extendSelectionTo(next) : this._selectAtIndex(next);\n\n } else if (e.key === 'ArrowUp') {\n const prev = focusIdx < 0 ? 0 : Math.max(focusIdx - 1, 0);\n e.shiftKey ? this._extendSelectionTo(prev) : this._selectAtIndex(prev);\n\n } else if (e.key === 'Home') {\n e.shiftKey ? this._extendSelectionTo(0) : this._selectAtIndex(0);\n\n } else if (e.key === 'End') {\n const last = flatRows.length - 1;\n e.shiftKey ? this._extendSelectionTo(last) : this._selectAtIndex(last);\n\n } else if (e.key === 'ArrowRight') {\n if (focusIdx < 0) {\n this._selectAtIndex(0);\n return;\n }\n\n const { node } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (!hasChildren) {\n return;\n }\n\n if (this._expandedNodes.has(node)) {\n // Already expanded — move focus into the first child\n this._selectAtIndex(focusIdx + 1);\n } else {\n // Collapsed — expand without moving focus\n this._onToggle(node);\n }\n\n } else if (e.key === 'ArrowLeft') {\n if (focusIdx < 0) {\n return;\n }\n\n const { node, depth } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (hasChildren && this._expandedNodes.has(node)) {\n // Expanded — collapse and stay on the same node\n this._onToggle(node);\n const newIdx = this._flatRows.findIndex(r => r.node === node);\n if (newIdx >= 0) {\n this._scrollIntoView(newIdx);\n }\n } else if (depth > 0) {\n // Collapsed or leaf — move to parent (nearest ancestor with depth - 1)\n for (let i = focusIdx - 1; i >= 0; i--) {\n if (flatRows[i].depth === depth - 1) {\n this._selectAtIndex(i);\n break;\n }\n }\n }\n }\n }\n\n /**\n * Dispatches clicks to either toggle expand/collapse or select a row.\n *\n * @remarks\n * Supports three selection modes via modifier keys:\n * - Plain click: replace selection with the clicked node\n * - Ctrl/Cmd+click: toggle the clicked node without disturbing others\n * - Shift+click: range-select from the anchor to the clicked node\n *\n * @param e - The click event whose target is inside the tree's DOM subtree.\n */\n private _handleClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const toggle = row.getToggle();\n if (toggle) {\n const toggleEl = toggle.getElement();\n if (toggleEl && (target === toggleEl || DOM.source.contains(toggleEl, target))) {\n this._onToggle(node);\n return;\n }\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n const clickedIdx = this._flatRows.findIndex(r => r.node === node);\n\n if (e.shiftKey && this._anchorNode) {\n this._extendSelectionTo(clickedIdx);\n } else if (e.ctrlKey || e.metaKey) {\n const before = new Set(this._selectedNodes);\n\n if (this._selectedNodes.has(node)) {\n this._selectedNodes.delete(node);\n } else {\n this._selectedNodes.add(node);\n }\n\n this._anchorNode = node;\n this._focusNode = node;\n this._updateSelectionStyle();\n this._notifySelectionChange(before);\n } else {\n this._selectAtIndex(clickedIdx);\n }\n\n return;\n }\n }\n\n /**\n * Resolves the node under a right-click and emits the `\"contextmenu\"` event.\n *\n * @remarks\n * Mirrors {@link _handleClick}'s row-matching but deliberately does not change\n * the selection: a right-click positions a context menu over a node without\n * triggering the selection-driven side effects (e.g. opening the node) that a\n * left-click would. When a node row is hit the browser's native menu is\n * suppressed via `preventDefault` and `\"contextmenu\"` fires with the node and\n * the originating event; a right-click on empty space is left to the browser.\n *\n * @param e - The contextmenu event whose target is inside the tree's subtree.\n */\n private _handleContextMenu(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n e.preventDefault();\n this.emit(\"contextmenu\", node, e);\n\n return;\n }\n }\n\n /**\n * Resolves the node under a double-click and emits the `\"dblclick\"` event.\n *\n * @remarks\n * Mirrors {@link _handleContextMenu}'s row-matching. The first click of the\n * pair has already run through {@link _handleClick} and set the selection, so\n * this only layers an activation signal on top — it does not re-select.\n * `preventDefault` suppresses the browser's double-click text selection of the\n * row label.\n *\n * @param e - The dblclick event whose target is inside the tree's subtree.\n */\n private _handleDblClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this._rowPool) {\n const node = row.getNode();\n if (!node) {\n continue;\n }\n\n const rowEl = row.getElement();\n if (!rowEl || (!DOM.source.contains(rowEl, target) && target !== rowEl)) {\n continue;\n }\n\n e.preventDefault();\n this.emit(\"dblclick\", node);\n\n // Double-clicking a parent row toggles its expansion, the\n // file-explorer convention. A double-click on the toggle caret is\n // skipped: `_handleClick` already fired `_onToggle` on each of the\n // two clicks, so its net state is unchanged and toggling again here\n // would flip it a third time. Leaf nodes have nothing to expand, so\n // their `\"dblclick\"` stays a pure activation signal.\n const toggle = row.getToggle();\n const toggleEl = toggle ? toggle.getElement() : undefined;\n const onToggle = !!toggleEl\n && (target === toggleEl || DOM.source.contains(toggleEl, target));\n\n if (!onToggle && this._isExpandable(node)) {\n this._onToggle(node);\n }\n\n return;\n }\n }\n\n /**\n * Applies or removes the selection highlight, focus ring, and aria-selected on all bound pool rows.\n */\n private _updateSelectionStyle(): void {\n for (const row of this._rowPool) {\n const rowEl = row.getElement();\n\n if (!rowEl) {\n continue;\n }\n\n const node = row.getNode();\n const isSelected = node !== null && this._selectedNodes.has(node!);\n const isFocused = node !== null && node === this._focusNode;\n\n // Per-node ephemeral selection/focus styling on a pooled row re-bound\n // to a different node on every render. Routing this through cached\n // Component setters would persist it into _options and replay it onto\n // the next node bound to this reused row, so write/remove the inline\n // styles directly instead.\n if (isSelected) {\n DOM.sink.apply(rowEl, { style: { \"background-color\": SELECTED_BG } });\n } else {\n DOM.sink.apply(rowEl, { style: { \"background-color\": null } });\n }\n\n if (isFocused) {\n DOM.sink.apply(rowEl, { style: { \"outline\": \"2px solid var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))\", \"outline-offset\": \"-2px\" } });\n } else {\n DOM.sink.apply(rowEl, { style: { \"outline\": null, \"outline-offset\": null } });\n }\n\n row.getAria().setSelected(isSelected);\n }\n }\n\n /**\n * Sets `aria-activedescendant` on the tree container to the pool row bound to `_focusNode`.\n *\n * @remarks Must be called after `renderWindow()` so the pool slot is guaranteed in the DOM.\n */\n private _updateActiveDescendant(): void {\n if (!this._focusNode) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n for (const row of this._rowPool) {\n if (row.getNode() === this._focusNode) {\n this.getAria().setActiveDescendant(row.getId());\n\n return;\n }\n }\n\n this.getAria().setActiveDescendant(\"\");\n }\n\n /**\n * Drops the running content-width maximum before the shared re-bind pass.\n *\n * @remarks The maximum is deliberately monotonic within a flattened set so\n * the horizontal scrollbar does not jitter as rows scroll in and out (it is\n * otherwise reset only in `_flatten`). A metrics reflow invalidates the\n * measurements it was accumulated from, so keeping it would pin the content\n * width to the old font — leaving a phantom scroll range when the new face\n * is narrower, and never widening past the stale value.\n */\n protected onThemeReflow(): void {\n this._maxContentWidth = 0;\n\n super.onThemeReflow();\n }\n\n /**\n * Recomputes the visible row window, rebinds changed pool slots, and hides excess rows.\n */\n protected renderWindow(): void {\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n\n if (this.deferRenderWhileFirstLayoutHeld()) {\n return;\n }\n\n const scroller = this._scroller;\n\n const totalRows = this._flatRows.length;\n const totalHeight = totalRows * ROW_HEIGHT;\n\n // Loose clamp using the last-known content width (the actual contentW\n // for this frame is computed below from the first row-bind pass).\n scroller.clampToContent(this._lastRowWidth, totalHeight);\n\n const visibleHeight = this.getHeight() || 0;\n const win = this.computeVisibleWindow(scroller.getScrollY(), visibleHeight, totalRows);\n\n const poolTarget = this.computePoolTarget(win.windowSize, visibleHeight, totalRows);\n this.growRowPool(poolTarget);\n\n const { reboundFlags, maxContentWidth } =\n this._bindAndMeasure(win.firstRow, win.windowSize);\n\n // Only the visible window was measured, so fold the widest row seen this\n // frame into the running maximum for the current flattened set (reset in\n // `_flatten`). The content width then only grows as wider rows are\n // discovered and never shrinks when they scroll away, so the horizontal\n // scrollbar stays stable instead of jittering with the visible rows.\n if (maxContentWidth > this._maxContentWidth) {\n this._maxContentWidth = maxContentWidth;\n }\n\n // Fill to the effective viewport width (owner width minus the vertical\n // scrollbar's reservation when that bar is visible), not the raw owner\n // width. Sizing rows to the full width would push their trailing edge\n // under the vertical bar, and handing that full width to\n // `layoutScrollbars` as the content width forces a spurious horizontal\n // bar for the reserved band. `clampToContent` above has already refreshed\n // the scroller's content metrics this pass.\n const rowWidth = Math.max(scroller.getViewportWidth(), this._maxContentWidth);\n if (rowWidth !== this._lastRowWidth) {\n this._lastRowWidth = rowWidth;\n this.invalidateGeom();\n }\n\n this._positionRows(win.firstRow, win.windowSize, rowWidth, reboundFlags);\n this.hideExcessPoolRows(win.windowSize);\n\n this._updateSelectionStyle();\n\n scroller.layoutScrollbars(rowWidth, totalHeight);\n\n // A pass the startup font gate deferred skipped whatever its caller did\n // after asking for it — including the active-descendant refresh, which\n // could not name a row that did not exist yet. Redo it now the rows do.\n if (this.finishResumedRender()) {\n this._updateActiveDescendant();\n }\n }\n\n /**\n * First pass: binds visible pool slots to their flattened rows (when\n * rebound) and computes the maximum content width across visible rows so\n * the second pass knows how wide each row should be.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @returns Per-slot rebind flags (parallel to the window) and the widest\n * content width seen, both consumed by {@link _positionRows}.\n */\n private _bindAndMeasure(firstRow: number, windowSize: number): { reboundFlags: boolean[], maxContentWidth: number } {\n const reboundFlags: boolean[] = new Array(windowSize);\n let maxContentWidth = 0;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const flatRow = this._flatRows[dataIndex];\n const hasChildren = this._isExpandable(flatRow.node);\n const expanded = this._expandedNodes.has(flatRow.node);\n const loading = this._loadingNodes.has(flatRow.node);\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound) {\n const selected = this._selectedNodes.has(flatRow.node);\n row.setRowData(flatRow.node, flatRow.depth, hasChildren, expanded, flatRow.siblingCount, flatRow.posInSet, selected, loading);\n this._boundIndices[i] = dataIndex;\n }\n\n reboundFlags[i] = wasRebound;\n\n const cw = row.getContentWidth(INDENT_PX);\n if (cw > maxContentWidth) {\n maxContentWidth = cw;\n }\n }\n\n return { reboundFlags, maxContentWidth };\n }\n\n /**\n * Second pass: positions visible rows at `dataIndex * ROW_HEIGHT`, sizes\n * them to `rowWidth`, marks them displayed, and re-lays out their children\n * only when the row was rebound or its geometry changed.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @param rowWidth - The horizontal extent of each row in pixels.\n * @param reboundFlags - The per-slot rebind flags produced by {@link _bindAndMeasure}.\n */\n private _positionRows(firstRow: number, windowSize: number, rowWidth: number, reboundFlags: boolean[]): void {\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const wasRebound = reboundFlags[i];\n\n const geomChanged = this.positionRow(i, dataIndex * ROW_HEIGHT, rowWidth);\n\n if (wasRebound || geomChanged) {\n row.layoutChildren(ROW_HEIGHT, INDENT_PX);\n }\n }\n }\n\n /**\n * Constructs the `VirtualScroller` and wires click and keyboard listeners.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n this.initScroller(el);\n\n Event.addSubtreeListener(this, \"click\", this._handleClick);\n Event.addSubtreeListener(this, \"contextmenu\", this._handleContextMenu);\n Event.addSubtreeListener(this, \"dblclick\", this._handleDblClick);\n Event.addListener(this, \"keydown\", this._onKeyDown);\n\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Calls the layout manager and then refreshes the virtual-scroll viewport.\n *\n * @returns This component, for method chaining.\n *\n * @remarks\n * Overrides [`Component.doLayout`](/api/core/classes/Component#doLayout) so that layout-manager-driven size changes\n * (e.g. from a parent Tab or Split) automatically update the rendered row window.\n */\n doLayout(): this {\n if (this.isLayoutPaused()) {\n return this;\n }\n\n super.doLayout();\n\n // A pass the startup font gate deferred already renders the window, so\n // running the unconditional one as well would render twice on the frame\n // the gate opens.\n if (!this.renderWindowIfDeferred()) {\n this.renderWindow();\n }\n\n return this;\n }\n}\n\nconst TreeCallable = callable(Tree);\ntype TreeCallable = Tree;\nexport {\n Tree as _Tree,\n TreeCallable as Tree\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * Pixel width reserved for the icon column (icon + small gap before the label).\n */\nconst ICON_WIDTH = 20;\n\n/** Square edge length used for the icon glyph. */\nconst ICON_SIZE = 16;\n\n/**\n * Resolves a [`Glyph`](/api/component/display/classes/Glyph) registry name for\n * the row currently being rendered.\n *\n * @param node - The bound tree node.\n * @param context - The bound-node state for this render pass.\n * @returns The glyph registry name to display.\n *\n * @category Components\n */\nexport type IconLabelGlyphResolver = (node: TreeNode, context: TreeNodeRenderContext) => string;\n\n/**\n * A [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) that\n * renders an icon followed by a text label.\n *\n * @remarks\n * The icon class is resolved per-row by a caller-supplied\n * {@link IconLabelGlyphResolver}. Because [`Glyph`](/api/component/display/classes/Glyph)\n * names are immutable, this renderer constructs a fresh `Glyph` whenever the\n * resolver returns a different name — matching the pattern used by `TreeRow`\n * for its expand/collapse toggle.\n *\n * @example\n * ```typescript\n * tree.setRendererFactory(() => new IconLabelTreeNodeRenderer(\n * (node) => (node.children?.length ? \"chevron-down\" : \"file\")\n * ));\n * ```\n *\n * @category Components\n */\nexport class IconLabelTreeNodeRenderer extends TreeNodeRenderer {\n\n private _icon: Glyph | null = null;\n private _label: Text;\n private _glyphResolver: IconLabelGlyphResolver;\n private _currentGlyph: string | null = null;\n\n /**\n * Constructs an icon+label renderer.\n *\n * @param glyphResolver - Callback that resolves the glyph registry name for\n * a given node. Defaults to `() => \"file\"`.\n */\n constructor(glyphResolver: IconLabelGlyphResolver = () => \"file\") {\n super();\n this.clearInsets();\n\n // No icon is built here: the glyph is constructed lazily in the first\n // update() from the resolved name, so a caller supplying its own resolver\n // never forces the default \"file\" glyph to be registered.\n this._glyphResolver = glyphResolver;\n this._label = new Text();\n\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Updates the icon (constructing a new Glyph if the resolved name changed)\n * and the label text.\n *\n * @param context - The bound-node state for this render pass.\n */\n update(context: TreeNodeRenderContext): void {\n const next = this._glyphResolver(context.node, context);\n\n if (next !== this._currentGlyph) {\n const el = this.getElement();\n\n if (el && this._icon) {\n const oldEl = this._icon.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n }\n\n this._icon = new Glyph(next);\n this._icon.clearInsets();\n this._currentGlyph = next;\n\n if (el) {\n DOM.sink.insertBefore(el, this._icon.getElement(true)!, this._label.getElement() ?? null);\n }\n }\n\n this._label.setText(context.node.label);\n this._label.measure();\n }\n\n /**\n * Returns the natural pixel width of the icon column plus the measured\n * label width.\n */\n getContentWidth(): number {\n return (this._icon ? ICON_WIDTH : 0) + (this._label.getPreferredSize()?.width ?? 0);\n }\n\n /**\n * Positions the icon (vertically centred in the row) and the label.\n *\n * Both go inside this renderer's content box, so a border or padding on the\n * renderer shrinks them rather than being painted over.\n *\n * @param width - The horizontal extent of the renderer in pixels. Used only\n * while the renderer has no element yet and the content box is\n * unavailable.\n * @param height - The vertical extent of the renderer in pixels, used under\n * the same condition as `width`.\n */\n layoutChildren(width: number, height: number): void {\n const box = this.getContentBounds() ?? { x: 0, y: 0, width, height };\n\n if (this._icon) {\n this._icon.setAutoCommitStyle(false);\n this._icon.setX(box.x);\n this._icon.setY(box.y + Math.max(0, (box.height - ICON_SIZE) / 2));\n this._icon.setWidth(ICON_SIZE);\n this._icon.setHeight(ICON_SIZE);\n this._icon.setAutoCommitStyle(true);\n }\n\n const labelX = box.x + (this._icon ? ICON_WIDTH : 0);\n const labelWidth = Math.max(0, box.x + box.width - labelX);\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(labelX);\n this._label.setY(box.y);\n this._label.setWidth(labelWidth);\n this._label.setHeight(box.height);\n this._label.setLineHeight(box.height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element (and the icon, when one is already\n * bound) to the renderer's DOM element. A later icon change is inserted before\n * the label by {@link update}.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n if (this._icon) {\n DOM.sink.appendChild(el, this._icon.getElement(true)!);\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n"],"mappings":"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"}
|