@jimka/typescript-ui 0.7.0 → 0.8.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/AbstractInput-Dt9ooHjo.js +2 -0
- package/dist/lib/AbstractInput-Dt9ooHjo.js.map +1 -0
- package/dist/lib/AccordionConstraints-DahZEfEp.js +2 -0
- package/dist/lib/AccordionConstraints-DahZEfEp.js.map +1 -0
- package/dist/lib/{Anchor-iZhsvMlT.js → Anchor-DdYgyn2V.js} +2 -2
- package/dist/lib/{Anchor-iZhsvMlT.js.map → Anchor-DdYgyn2V.js.map} +1 -1
- package/dist/lib/{AnimatedDropdown-D0RhVgv3.js → AnimatedDropdown-_KhHzX5D.js} +2 -2
- package/dist/lib/AnimatedDropdown-_KhHzX5D.js.map +1 -0
- package/dist/lib/Border-BELbqMDJ.js +2 -0
- package/dist/lib/Border-BELbqMDJ.js.map +1 -0
- package/dist/lib/Button-CKzjQyqA.js +2 -0
- package/dist/lib/Button-CKzjQyqA.js.map +1 -0
- package/dist/lib/{Card-C20k2YHU.js → Card-CAp7LcB5.js} +2 -2
- package/dist/lib/{Card-C20k2YHU.js.map → Card-CAp7LcB5.js.map} +1 -1
- package/dist/lib/Checkbox-CvAZJ4_T.js +2 -0
- package/dist/lib/Checkbox-CvAZJ4_T.js.map +1 -0
- package/dist/lib/CodeEditor-DoGGdL1Y.js +2 -0
- package/dist/lib/CodeEditor-DoGGdL1Y.js.map +1 -0
- package/dist/lib/ComboBox-DgjMnpdh.js +2 -0
- package/dist/lib/ComboBox-DgjMnpdh.js.map +1 -0
- package/dist/lib/Component-YrLXKJZS.js +2 -0
- package/dist/lib/Component-YrLXKJZS.js.map +1 -0
- package/dist/lib/Container-C5cxnioS.js +2 -0
- package/dist/lib/{Container-FWRjgF6T.js.map → Container-C5cxnioS.js.map} +1 -1
- package/dist/lib/DOM-DDhDN8lQ.js +2 -0
- package/dist/lib/DOM-DDhDN8lQ.js.map +1 -0
- package/dist/lib/Diagnostics-MI6Yw-3E.js +2 -0
- package/dist/lib/Diagnostics-MI6Yw-3E.js.map +1 -0
- package/dist/lib/Dialog-CXQXRQQK.js +2 -0
- package/dist/lib/Dialog-CXQXRQQK.js.map +1 -0
- package/dist/lib/DragManager-D5pBaPPg.js +2 -0
- package/dist/lib/DragManager-D5pBaPPg.js.map +1 -0
- package/dist/lib/{FieldDecorator-DyPX8CFh.js → FieldDecorator-37IgKHHm.js} +2 -2
- package/dist/lib/{FieldDecorator-DyPX8CFh.js.map → FieldDecorator-37IgKHHm.js.map} +1 -1
- package/dist/lib/{Fit-CaPMj7Up.js → Fit-amTWlJM2.js} +2 -2
- package/dist/lib/{Fit-CaPMj7Up.js.map → Fit-amTWlJM2.js.map} +1 -1
- package/dist/lib/{FloatingPanel-Dsy_vJxi.js → FloatingPanel-CwsFPYbc.js} +2 -2
- package/dist/lib/{FloatingPanel-Dsy_vJxi.js.map → FloatingPanel-CwsFPYbc.js.map} +1 -1
- package/dist/lib/Glyph-Cof7BxfJ.js +2 -0
- package/dist/lib/Glyph-Cof7BxfJ.js.map +1 -0
- package/dist/lib/{Grid-ft_UUpj8.js → Grid-BQZ4UIUH.js} +2 -2
- package/dist/lib/{Grid-ft_UUpj8.js.map → Grid-BQZ4UIUH.js.map} +1 -1
- package/dist/lib/GridConstraints-BkXa62z0.js +2 -0
- package/dist/lib/GridConstraints-BkXa62z0.js.map +1 -0
- package/dist/lib/{Header-Osm3IjDw.js → Header-Cj33ABP1.js} +2 -2
- package/dist/lib/{Header-Osm3IjDw.js.map → Header-Cj33ABP1.js.map} +1 -1
- package/dist/lib/{IconText-BBaYJ2Fx.js → IconText-Dp64iq_q.js} +2 -2
- package/dist/lib/{IconText-BBaYJ2Fx.js.map → IconText-Dp64iq_q.js.map} +1 -1
- package/dist/lib/LabeledGrid-Dpelrc1k.js +2 -0
- package/dist/lib/LabeledGrid-Dpelrc1k.js.map +1 -0
- package/dist/lib/LayoutSerialization-DgLJSuew.js +2 -0
- package/dist/lib/LayoutSerialization-DgLJSuew.js.map +1 -0
- package/dist/lib/Link-DE8tE3u3.js +2 -0
- package/dist/lib/Link-DE8tE3u3.js.map +1 -0
- package/dist/lib/List-TfgHScl3.js +2 -0
- package/dist/lib/List-TfgHScl3.js.map +1 -0
- package/dist/lib/ListenerBag-DYRl4TSb.js +2 -0
- package/dist/lib/ListenerBag-DYRl4TSb.js.map +1 -0
- package/dist/lib/{MemoryStore-DhPoU-7X.js → MemoryStore-ByGDEKYI.js} +2 -2
- package/dist/lib/{MemoryStore-DhPoU-7X.js.map → MemoryStore-ByGDEKYI.js.map} +1 -1
- package/dist/lib/Menu-BCQISKiQ.js +2 -0
- package/dist/lib/Menu-BCQISKiQ.js.map +1 -0
- package/dist/lib/{MenuButton-BqCT-1yv.js → MenuButton-BO4y3jf2.js} +2 -2
- package/dist/lib/{MenuButton-BqCT-1yv.js.map → MenuButton-BO4y3jf2.js.map} +1 -1
- package/dist/lib/{OverlayFade-CuEXgimf.js → OverlayFade-C1yQKffL.js} +2 -2
- package/dist/lib/{OverlayFade-CuEXgimf.js.map → OverlayFade-C1yQKffL.js.map} +1 -1
- package/dist/lib/{Panel-CtViePqY.js → Panel-DlsVVuvn.js} +2 -2
- package/dist/lib/{Panel-CtViePqY.js.map → Panel-DlsVVuvn.js.map} +1 -1
- package/dist/lib/{Position-BIEU1Eze.js → Position-DWR1nIpA.js} +2 -2
- package/dist/lib/{Position-BIEU1Eze.js.map → Position-DWR1nIpA.js.map} +1 -1
- package/dist/lib/ProgressSpinner-BI9tFIVm.js +2 -0
- package/dist/lib/ProgressSpinner-BI9tFIVm.js.map +1 -0
- package/dist/lib/RadioButton-BxcuFChp.js +2 -0
- package/dist/lib/RadioButton-BxcuFChp.js.map +1 -0
- package/dist/lib/RadioMenuRow-B2bthoxj.js +2 -0
- package/dist/lib/RadioMenuRow-B2bthoxj.js.map +1 -0
- package/dist/lib/{RovingTabIndex--w28AnXc.js → RovingTabIndex-BttlsARv.js} +2 -2
- package/dist/lib/{RovingTabIndex--w28AnXc.js.map → RovingTabIndex-BttlsARv.js.map} +1 -1
- package/dist/lib/Scrollbar-orAWHWap.js +2 -0
- package/dist/lib/Scrollbar-orAWHWap.js.map +1 -0
- package/dist/lib/SelectableText-BbS_lRzY.js +2 -0
- package/dist/lib/SelectableText-BbS_lRzY.js.map +1 -0
- package/dist/lib/Slider-BvXc-cx5.js +2 -0
- package/dist/lib/Slider-BvXc-cx5.js.map +1 -0
- package/dist/lib/Spacer-C6faKe1V.js +2 -0
- package/dist/lib/Spacer-C6faKe1V.js.map +1 -0
- package/dist/lib/StyleTraits-CAtzeyRR.js +2 -0
- package/dist/lib/StyleTraits-CAtzeyRR.js.map +1 -0
- package/dist/lib/TabButton-BQDaBszs.js +2 -0
- package/dist/lib/TabButton-BQDaBszs.js.map +1 -0
- package/dist/lib/TabCloseButton-B-iLVPGZ.js +2 -0
- package/dist/lib/TabCloseButton-B-iLVPGZ.js.map +1 -0
- package/dist/lib/Table-3McEw5gr.js +7 -0
- package/dist/lib/Table-3McEw5gr.js.map +1 -0
- package/dist/lib/Text-CGFXPhHD.js +2 -0
- package/dist/lib/Text-CGFXPhHD.js.map +1 -0
- package/dist/lib/Tooltip-DgRHQZhL.js +3 -0
- package/dist/lib/Tooltip-DgRHQZhL.js.map +1 -0
- package/dist/lib/Tree-Bq2pjjAz.js +2 -0
- package/dist/lib/Tree-Bq2pjjAz.js.map +1 -0
- package/dist/lib/{VBox-DVe4UXwX.js → VBox-hFIZAEpT.js} +2 -2
- package/dist/lib/{VBox-DVe4UXwX.js.map → VBox-hFIZAEpT.js.map} +1 -1
- package/dist/lib/{VirtualScroller-ggPTCQb0.js → VirtualScroller-Csa3GBSM.js} +2 -2
- package/dist/lib/{VirtualScroller-ggPTCQb0.js.map → VirtualScroller-Csa3GBSM.js.map} +1 -1
- package/dist/lib/Window-CQ14_65z.js +2 -0
- package/dist/lib/Window-CQ14_65z.js.map +1 -0
- package/dist/lib/component/button.es.js +1 -1
- package/dist/lib/component/button.es.js.map +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 +1 -6
- 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-DpzpKLWE.js +2 -0
- package/dist/lib/core-DpzpKLWE.js.map +1 -0
- package/dist/lib/core.es.js +1 -2
- package/dist/lib/data.es.js +1 -1
- package/dist/lib/diagnostics.es.js +24 -0
- package/dist/lib/diagnostics.es.js.map +1 -0
- package/dist/lib/glyphs/solid/index.es.js +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-8YvORgGS.js +2 -0
- package/dist/lib/primitive-8YvORgGS.js.map +1 -0
- package/dist/lib/primitive.es.js +1 -2
- package/dist/lib/router.es.js +1 -1
- package/dist/lib/selectionsEqual-DWdW5pxP.js +2 -0
- package/dist/lib/{selectionsEqual-C7MrKMQr.js.map → selectionsEqual-DWdW5pxP.js.map} +1 -1
- package/dist/lib/types/component/button/Button.d.ts +6 -14
- package/dist/lib/types/component/button/TabButton.d.ts +3 -5
- package/dist/lib/types/component/button/TabCloseButton.d.ts +3 -0
- package/dist/lib/types/component/button/ToggleButton.d.ts +2 -5
- package/dist/lib/types/component/chart/AbstractChart.d.ts +2 -2
- package/dist/lib/types/component/container/AccordionHeader.d.ts +6 -1
- package/dist/lib/types/component/container/AccordionIndicator.d.ts +2 -0
- package/dist/lib/types/component/container/FieldSet.d.ts +2 -0
- package/dist/lib/types/component/container/LabeledFieldSet.d.ts +1 -1
- package/dist/lib/types/component/container/LabeledGrid.d.ts +4 -1
- package/dist/lib/types/component/container/Legend.d.ts +2 -2
- package/dist/lib/types/component/container/MenuItem.d.ts +1 -0
- package/dist/lib/types/component/container/WindowBorder.d.ts +2 -2
- package/dist/lib/types/component/container/WindowHeader.d.ts +6 -0
- package/dist/lib/types/component/diagram/DiagramEdgeLayer.d.ts +10 -0
- package/dist/lib/types/component/diagram/DiagramGroupNode.d.ts +2 -0
- package/dist/lib/types/component/diagram/DiagramNode.d.ts +6 -2
- package/dist/lib/types/component/diagram/DiagramNodeLayer.d.ts +25 -0
- package/dist/lib/types/component/diagram/DiagramResidency.d.ts +11 -0
- package/dist/lib/types/component/diagram/DiagramView.d.ts +22 -0
- package/dist/lib/types/component/input/AbstractCalendarDropdown.d.ts +4 -4
- package/dist/lib/types/component/input/AbstractInput.d.ts +1 -0
- package/dist/lib/types/component/input/AbstractPickerField.d.ts +4 -2
- package/dist/lib/types/component/input/AutoCompleteDropdown.d.ts +2 -2
- package/dist/lib/types/component/input/AutoCompleteField.d.ts +1 -0
- package/dist/lib/types/component/input/ComboBox.d.ts +5 -2
- package/dist/lib/types/component/input/Link.d.ts +2 -2
- package/dist/lib/types/component/input/PasswordField.d.ts +2 -2
- package/dist/lib/types/component/input/PickerButton.d.ts +5 -2
- package/dist/lib/types/component/input/PickerInput.d.ts +2 -2
- package/dist/lib/types/component/input/SelectableText.d.ts +2 -2
- package/dist/lib/types/component/input/SpinButton.d.ts +3 -2
- package/dist/lib/types/component/input/Text.d.ts +3 -7
- package/dist/lib/types/component/input/TextArea.d.ts +2 -2
- package/dist/lib/types/component/input/TextField.d.ts +2 -2
- package/dist/lib/types/component/input/TextInput.d.ts +4 -2
- package/dist/lib/types/component/input/TimePickerDropdown.d.ts +2 -2
- package/dist/lib/types/component/input/UsernameField.d.ts +2 -2
- package/dist/lib/types/component/list/AbstractMarkerList.d.ts +2 -0
- package/dist/lib/types/component/list/AbstractSelectableList.d.ts +4 -2
- package/dist/lib/types/component/list/ListItem.d.ts +2 -1
- package/dist/lib/types/component/menubar/MenuBarButton.d.ts +3 -0
- package/dist/lib/types/component/menubar/ToolBar.d.ts +2 -0
- package/dist/lib/types/component/table/Body.d.ts +14 -6
- package/dist/lib/types/component/table/Column.d.ts +2 -0
- package/dist/lib/types/component/table/ColumnConfig.d.ts +1 -0
- package/dist/lib/types/component/table/Footer.d.ts +2 -0
- package/dist/lib/types/component/table/Header.d.ts +7 -0
- package/dist/lib/types/component/table/Row.d.ts +2 -0
- package/dist/lib/types/component/table/Table.d.ts +4 -1
- package/dist/lib/types/component/table/TablePanel.d.ts +5 -0
- package/dist/lib/types/component/table/TreeTablePanel.d.ts +5 -0
- package/dist/lib/types/component/table/cell/Cell.d.ts +3 -2
- package/dist/lib/types/component/table/cell/Header.d.ts +4 -3
- package/dist/lib/types/component/table/cell/ParentHeader.d.ts +4 -0
- package/dist/lib/types/component/tree/TreeRow.d.ts +2 -0
- package/dist/lib/types/core/AnimatedDropdown.d.ts +2 -2
- package/dist/lib/types/core/Binding.d.ts +1 -0
- package/dist/lib/types/core/ClassStyleRules.d.ts +44 -22
- package/dist/lib/types/core/Component.d.ts +55 -28
- package/dist/lib/types/core/DOM.d.ts +6 -0
- package/dist/lib/types/core/Diagnostics.d.ts +22 -0
- package/dist/lib/types/core/Event.d.ts +7 -0
- package/dist/lib/types/core/LayerManager.d.ts +2 -0
- package/dist/lib/types/core/ListenerBag.d.ts +1 -0
- package/dist/lib/types/core/StyleTarget.d.ts +12 -0
- package/dist/lib/types/core/StyleTraits.d.ts +4 -0
- package/dist/lib/types/core/Theme.d.ts +5 -2
- package/dist/lib/types/core/index.d.ts +5 -2
- package/dist/lib/types/diagnostics/DiagnosticsOverlay.d.ts +29 -0
- package/dist/lib/types/diagnostics/DiagnosticsSampler.d.ts +61 -0
- package/dist/lib/types/diagnostics/StyleAudit.d.ts +19 -0
- package/dist/lib/types/diagnostics/StyleAuditOverlay.d.ts +12 -0
- package/dist/lib/types/diagnostics/StyleAuditView.d.ts +13 -0
- package/dist/lib/types/diagnostics/index.d.ts +7 -0
- package/dist/lib/types/layout/LayoutManager.d.ts +3 -0
- package/dist/lib/types/layout/Table.d.ts +1 -0
- package/dist/lib/types/overlay/AbstractWindow.d.ts +35 -2
- package/dist/lib/types/overlay/ButtonGroup.d.ts +1 -0
- package/dist/lib/types/overlay/Dialog.d.ts +1 -0
- package/dist/lib/types/overlay/Menu.d.ts +2 -0
- package/dist/lib/types/overlay/Notification.d.ts +0 -1
- package/dist/lib/types/overlay/PopupPanel.d.ts +2 -2
- package/dist/lib/types/overlay/RailHandle.d.ts +4 -5
- package/dist/lib/types/overlay/TabWindow.d.ts +3 -0
- package/dist/lib/types/overlay/Tooltip.d.ts +1 -0
- package/dist/lib/types/overlay/Window.d.ts +4 -1
- package/dist/lib/validation.es.js +1 -1
- package/llms.txt +2 -0
- package/package.json +5 -1
- package/dist/lib/AbstractInput-W2PLJV5_.js +0 -2
- package/dist/lib/AbstractInput-W2PLJV5_.js.map +0 -1
- package/dist/lib/AnimatedDropdown-D0RhVgv3.js.map +0 -1
- package/dist/lib/Border-6GsTNV-2.js +0 -2
- package/dist/lib/Border-6GsTNV-2.js.map +0 -1
- package/dist/lib/Button-CnFAiQuV.js +0 -2
- package/dist/lib/Button-CnFAiQuV.js.map +0 -1
- package/dist/lib/Checkbox-CM1z8keM.js +0 -2
- package/dist/lib/Checkbox-CM1z8keM.js.map +0 -1
- package/dist/lib/CheckboxMenuRow-C1h4LPHR.js +0 -2
- package/dist/lib/CheckboxMenuRow-C1h4LPHR.js.map +0 -1
- package/dist/lib/CodeEditor-CTL2WjF8.js +0 -2
- package/dist/lib/CodeEditor-CTL2WjF8.js.map +0 -1
- package/dist/lib/ComboBox-PZ5YPL0X.js +0 -2
- package/dist/lib/ComboBox-PZ5YPL0X.js.map +0 -1
- package/dist/lib/Component-D99-15Zt.js +0 -2
- package/dist/lib/Component-D99-15Zt.js.map +0 -1
- package/dist/lib/Container-FWRjgF6T.js +0 -2
- package/dist/lib/DOM-Cjh7_7TF.js +0 -2
- package/dist/lib/DOM-Cjh7_7TF.js.map +0 -1
- package/dist/lib/Dialog-DN78RaVH.js +0 -2
- package/dist/lib/Dialog-DN78RaVH.js.map +0 -1
- package/dist/lib/Glyph-DiEpD3gy.js +0 -2
- package/dist/lib/Glyph-DiEpD3gy.js.map +0 -1
- package/dist/lib/GridConstraints-CvZ0Nzaw.js +0 -2
- package/dist/lib/GridConstraints-CvZ0Nzaw.js.map +0 -1
- package/dist/lib/LayoutSerialization-BC64__O7.js +0 -2
- package/dist/lib/LayoutSerialization-BC64__O7.js.map +0 -1
- package/dist/lib/List-CQbZK_pT.js +0 -2
- package/dist/lib/List-CQbZK_pT.js.map +0 -1
- package/dist/lib/ListenerBag-WPdKuuds.js +0 -2
- package/dist/lib/ListenerBag-WPdKuuds.js.map +0 -1
- package/dist/lib/Menu-CwHluLGT.js +0 -2
- package/dist/lib/Menu-CwHluLGT.js.map +0 -1
- package/dist/lib/ProgressSpinner-BuBvC1FY.js +0 -2
- package/dist/lib/ProgressSpinner-BuBvC1FY.js.map +0 -1
- package/dist/lib/RadioButton-Df5J-H6m.js +0 -2
- package/dist/lib/RadioButton-Df5J-H6m.js.map +0 -1
- package/dist/lib/RadioMenuRow-Cfv0x6Qj.js +0 -2
- package/dist/lib/RadioMenuRow-Cfv0x6Qj.js.map +0 -1
- package/dist/lib/Scrollbar-DcnueNJd.js +0 -2
- package/dist/lib/Scrollbar-DcnueNJd.js.map +0 -1
- package/dist/lib/SelectableText-DZNxjKXR.js +0 -2
- package/dist/lib/SelectableText-DZNxjKXR.js.map +0 -1
- package/dist/lib/Slider-DNfd8Lxz.js +0 -2
- package/dist/lib/Slider-DNfd8Lxz.js.map +0 -1
- package/dist/lib/Spacer-BznfGbwG.js +0 -2
- package/dist/lib/Spacer-BznfGbwG.js.map +0 -1
- package/dist/lib/TabButton-CAACsMov.js +0 -2
- package/dist/lib/TabButton-CAACsMov.js.map +0 -1
- package/dist/lib/TabCloseButton-CCiqsX3y.js +0 -2
- package/dist/lib/TabCloseButton-CCiqsX3y.js.map +0 -1
- package/dist/lib/Text-O-FWa5Cy.js +0 -2
- package/dist/lib/Text-O-FWa5Cy.js.map +0 -1
- package/dist/lib/Tooltip-CyICVu15.js +0 -3
- package/dist/lib/Tooltip-CyICVu15.js.map +0 -1
- package/dist/lib/Tree-CSEguhlk.js +0 -2
- package/dist/lib/Tree-CSEguhlk.js.map +0 -1
- package/dist/lib/core.es.js.map +0 -1
- package/dist/lib/primitive.es.js.map +0 -1
- package/dist/lib/selectionsEqual-C7MrKMQr.js +0 -2
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tree-CSEguhlk.js","names":[],"sources":["../../src/typescript/lib/component/tree/TreeNodeRenderer.ts","../../src/typescript/lib/component/tree/renderer/Label.ts","../../src/typescript/lib/component/tree/TreeRow.ts","../../src/typescript/lib/component/tree/Tree.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * Abstract base class for tree node renderers.\n *\n * @remarks\n * A `TreeNodeRenderer` controls the content area of a single\n * [`TreeRow`](/api/component/tree/classes/TreeRow) — that is, everything to\n * the right of the expand/collapse toggle. The owning [`Tree`](/api/component/tree/classes/Tree)\n * holds a renderer factory; each pool slot calls that factory once at\n * construction and rebinds via {@link update} when the slot is remapped to a\n * different node or its selection/expansion state changes.\n *\n * The toggle remains under `TreeRow`'s control because click detection routes\n * through it; renderers never see toggle clicks.\n *\n * Subclasses must implement {@link update}, {@link getContentWidth}, and\n * {@link layoutChildren}. See [`LabelTreeNodeRenderer`](/api/component/tree/classes/LabelTreeNodeRenderer)\n * for the canonical default.\n *\n * @category Components\n */\nexport abstract class TreeNodeRenderer extends Component {\n\n constructor() {\n super();\n }\n\n /**\n * Rebinds this renderer to new node data.\n *\n * @param context - The bound-node state for this render pass.\n *\n * @remarks\n * Called every time the owning pool slot is mapped to a different node, or\n * when an expansion-state change forces a full re-bind. Plain selection\n * style changes (background highlight, focus ring) are handled by the\n * `Tree` directly on the row element and do not flow through `update()`.\n */\n abstract update(context: TreeNodeRenderContext): void;\n\n /**\n * Returns the natural pixel width this renderer would render at given the\n * current bound node.\n *\n * @returns Content width in pixels.\n *\n * @remarks\n * Used by [`Tree`](/api/component/tree/classes/Tree) to compute the\n * horizontal scroll extent. Renderers that auto-measure their children\n * should return the sum of those measured widths.\n */\n abstract getContentWidth(): number;\n\n /**\n * Positions this renderer's internal children inside its content box.\n *\n * @param width - The horizontal extent of the renderer in pixels.\n * @param height - The vertical extent of the renderer in pixels.\n *\n * @remarks\n * Called by `TreeRow.layoutChildren` after the renderer's own size has\n * been set via `setX`/`setY`/`setWidth`/`setHeight`.\n *\n * The `width` and `height` arguments are this renderer's **outer** box, so\n * an implementation must not place children against them directly: take the\n * rectangle from [`getContentBounds()`](/api/core/classes/Component), which\n * subtracts any border and padding the renderer carries, and fall back to\n * the arguments only when it returns `null` (no element yet). A child's\n * containing block is already this renderer's padding box, so one placed\n * at `(0, 0)` sized to `width` / `height` starts inside the border and\n * overruns the opposite edge by its width, where the `overflow: hidden`\n * every component carries clips it.\n */\n abstract layoutChildren(width: number, height: number): void;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { TreeNodeRenderContext } from \"~/component/tree/TreeNodeRenderContext.js\";\n\n/**\n * The default [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) —\n * a single `<span>` carrying the node's label.\n *\n * @remarks\n * Used as the implicit factory by [`Tree`](/api/component/tree/classes/Tree)\n * when no custom renderer is set. The internal `Text` runs with\n * `autoMeasure(false)`; an explicit `measure()` call after each `setText`\n * caches the natural width for `getContentWidth()` to read on the next layout\n * pass.\n *\n * @example\n * ```typescript\n * tree.setRendererFactory(() => new LabelTreeNodeRenderer());\n * ```\n *\n * @category Components\n */\nexport class LabelTreeNodeRenderer extends TreeNodeRenderer {\n\n private _label: Text;\n\n /**\n * Constructs a label renderer with an empty text node. The label is\n * populated on the first {@link update} call.\n */\n constructor() {\n super();\n this.clearInsets();\n\n this._label = new Text(undefined, { truncate: true });\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Returns the underlying label component so consumers can tweak font /\n * colour properties at construction time.\n *\n * @returns The internal [`Text`](/api/component/input/classes/Text) instance.\n */\n getLabel(): Text {\n return this._label;\n }\n\n /**\n * Updates the rendered label text and re-measures.\n *\n * @param context - The bound-node state for this render pass.\n */\n update(context: TreeNodeRenderContext): void {\n this._label.setText(context.node.label);\n this._label.measure();\n }\n\n /**\n * Returns the cached natural width of the rendered label.\n *\n * @returns Label width in pixels.\n */\n getContentWidth(): number {\n return this._label.getPreferredSize()?.width ?? 0;\n }\n\n /**\n * Sizes the label to fill the renderer's allocated box and centres its\n * line-box vertically by matching `line-height` to the row height.\n *\n * The label goes inside this renderer's content box, so a border or padding\n * on the renderer shrinks it rather than being painted over.\n *\n * @param width - The row's available width in pixels. The label clamps to\n * this when it is narrower than the label's own natural width — its box\n * is otherwise always exactly as wide as its content (`getContentWidth`),\n * so `text-overflow: ellipsis` would never actually have room to trigger.\n * In `Tree`'s default `rowOverflow: \"scroll\"` mode this is a no-op: rows\n * already grow to fit the widest label (`Tree`'s `_maxContentWidth`), so\n * `width` is never narrower than `getContentWidth()` there. It only\n * clamps when `rowOverflow: \"clip\"` caps the row at the viewport width.\n * @param height - The vertical extent of the row in pixels. Used only while\n * the renderer has no element yet and the content box is unavailable.\n */\n layoutChildren(width: number, height: number): void {\n const box = this.getContentBounds() ?? { x: 0, y: 0, height };\n const labelWidth = Math.min(this.getContentWidth(), width);\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(box.x);\n this._label.setY(box.y);\n this._label.setWidth(labelWidth);\n this._label.setHeight(box.height);\n this._label.setLineHeight(box.height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element to the renderer's DOM element.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { caret_down } from \"~/glyphs/solid/caret_down.js\";\nimport { caret_right } from \"~/glyphs/solid/caret_right.js\";\n\nGlyph.register(caret_down, caret_right);\n\n/** Width in pixels reserved for the expand/collapse toggle icon. */\nexport const TOGGLE_WIDTH = 20;\n\n/**\n * A single visible row in the {@link Tree} virtual-scroll pool.\n *\n * Each pool slot holds one {@link TreeNode} at a time. The {@link Tree} calls\n * {@link setRowData} to rebind a slot to a different node and\n * {@link layoutChildren} to reposition the toggle and renderer after the row's\n * own dimensions are updated.\n *\n * @remarks\n * The toggle ([`Glyph`](/api/component/display/classes/Glyph)) and content\n * renderer are appended directly to the row's DOM element in `init()` rather\n * than via `addComponent`, so their preferred-size change notifications do not\n * propagate up to the Tree and trigger unnecessary layout passes. Leaf rows\n * have no toggle; non-leaf rows swap in a fresh `caret-down` / `caret-right`\n * glyph on each state change rather than mutating a single character. The row\n * content area (everything to the right of the toggle) is owned by a\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer) supplied\n * via the constructor factory.\n */\nclass TreeRow extends Component {\n\n private _toggle: Glyph | null = null;\n private _spinner: ProgressSpinner | null = null;\n private _renderer: TreeNodeRenderer;\n private _node: TreeNode | null = null;\n private _depth: number = 0;\n\n constructor(rendererFactory: () => TreeNodeRenderer = () => new LabelTreeNodeRenderer()) {\n super();\n\n this.getAria().setRole(\"treeitem\");\n\n this._renderer = rendererFactory();\n }\n\n /**\n * Returns the toggle icon glyph, or null when the bound node is a leaf.\n *\n * @returns The toggle [`Glyph`](/api/component/display/classes/Glyph) instance, or null.\n */\n getToggle(): Glyph | null {\n return this._toggle;\n }\n\n /**\n * Returns the renderer currently bound to this pool row.\n *\n * @returns The active [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n */\n getRenderer(): TreeNodeRenderer {\n return this._renderer;\n }\n\n /**\n * Replaces this row's content renderer, swapping the underlying DOM\n * element in place.\n *\n * @param renderer - The new renderer instance to install.\n * @returns This row, for method chaining.\n *\n * @remarks\n * The new renderer's element is appended to the row immediately. The\n * caller is expected to follow up with `setRowData` (typically via the\n * Tree's next render pass) so the renderer receives an `update()` before\n * being laid out.\n */\n setRenderer(renderer: TreeNodeRenderer): this {\n const el = this.getElement();\n\n if (el) {\n const oldEl = this._renderer.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n\n DOM.sink.appendChild(el, renderer.getElement(true)!);\n }\n\n this._renderer = renderer;\n\n return this;\n }\n\n /**\n * Returns the tree node currently bound to this pool row.\n *\n * @returns The bound {@link TreeNode}, or null if the slot is unbound.\n */\n getNode(): TreeNode | null {\n return this._node;\n }\n\n /**\n * Returns the depth of the currently bound node.\n *\n * @returns The zero-based depth level.\n */\n getDepth(): number {\n return this._depth;\n }\n\n /**\n * Binds this pool slot to a new node, updating the toggle icon, content\n * renderer, and ARIA positional attributes.\n *\n * @param node - The tree node to display.\n * @param depth - The zero-based nesting depth (controls indentation).\n * @param hasChildren - Whether the node has child nodes (determines toggle visibility).\n * @param expanded - Whether the node is currently expanded.\n * @param siblingCount - Total number of siblings at this level under the same parent.\n * @param posInSet - 1-based position of this node among its siblings.\n * @param selected - Whether the node is currently selected.\n * @param loading - Whether this node's lazy children are currently loading;\n * when true a spinner replaces the toggle caret.\n */\n setRowData(node: TreeNode, depth: number, hasChildren: boolean, expanded: boolean, siblingCount: number, posInSet: number, selected: boolean, loading: boolean): this {\n this._node = node;\n this._depth = depth;\n\n if (this._toggle) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._toggle.getElement(true)!);\n }\n this._toggle = null;\n }\n\n if (this._spinner) {\n const el = this.getElement();\n if (el) {\n DOM.sink.removeChild(el, this._spinner.getElement(true)!);\n }\n this._spinner = null;\n }\n\n if (loading) {\n // No explicit size: the spinner tracks the theme font-size so it\n // reads as the same visual weight as the caret glyph it replaces,\n // and `layoutChildren` fits it into the TOGGLE_WIDTH box.\n const spinner = new ProgressSpinner();\n this._spinner = spinner;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, spinner.getElement(true)!);\n }\n } else if (hasChildren) {\n const toggle = new Glyph(expanded ? \"caret-down\" : \"caret-right\");\n toggle.setCursor(\"pointer\");\n toggle.clearInsets();\n toggle.getAria().setHidden(true);\n this._toggle = toggle;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.appendChild(el, toggle.getElement(true)!);\n }\n }\n\n this._renderer.update({ node, depth, expanded, selected, hasChildren });\n\n this.getAria().setLevel(depth + 1);\n this.getAria().setExpanded(hasChildren ? expanded : null);\n this.getAria().setSetSize(siblingCount);\n this.getAria().setPosInSet(posInSet);\n\n return this;\n }\n\n /**\n * Positions the toggle and renderer sub-components inside the row's content\n * box — the rectangle {@link Component.getContentBounds} returns, which a\n * border or padding on the row shrinks. Placing them against the row's outer\n * box instead makes the renderer overflow on both axes, and the row's\n * `overflow: hidden` clips it.\n *\n * @param rowHeight - The current height of this row in pixels. Used only\n * while the row has no element yet and the content box is unavailable;\n * otherwise the children's height comes from the content box, which is\n * this same height less the row's own border and padding.\n * @param indentPx - Pixels of indentation per depth level.\n */\n layoutChildren(rowHeight: number, indentPx: number): void {\n const box = this.getContentBounds()\n ?? { x: 0, y: 0, width: this.getWidth() || 0, height: rowHeight };\n\n const indent = box.x + this._depth * indentPx;\n\n if (this._toggle) {\n // The caret is a rigid SVG Glyph (min/max pinned to its 16×16\n // preferredSize), so it cannot fill the taller row — centre it\n // geometrically instead of relying on line-height (a no-op for SVG).\n const glyphHeight = this._toggle.getPreferredSize()?.height ?? box.height;\n this._toggle.setAutoCommitStyle(false);\n this._toggle.setX(indent);\n this._toggle.setY(box.y + Math.max(0, (box.height - glyphHeight) / 2));\n this._toggle.setWidth(TOGGLE_WIDTH);\n this._toggle.setHeight(glyphHeight);\n this._toggle.setAutoCommitStyle(true);\n }\n\n if (this._spinner) {\n this._spinner.setAutoCommitStyle(false);\n this._spinner.setX(indent);\n this._spinner.setY(box.y);\n this._spinner.setWidth(TOGGLE_WIDTH);\n this._spinner.setHeight(box.height);\n this._spinner.setAutoCommitStyle(true);\n\n // The spinner owns its inner arc, which only positions itself when\n // its own doLayout runs; the row appends it directly rather than via\n // addComponent, so drive that layout here once the box is sized.\n this._spinner.doLayout();\n }\n\n const labelX = indent + TOGGLE_WIDTH;\n const labelBoxW = Math.max(0, box.x + box.width - labelX);\n\n this._renderer.setAutoCommitStyle(false);\n this._renderer.setX(labelX);\n this._renderer.setY(box.y);\n this._renderer.setWidth(labelBoxW);\n this._renderer.setHeight(box.height);\n this._renderer.setAutoCommitStyle(true);\n\n this._renderer.layoutChildren(labelBoxW, box.height);\n }\n\n /**\n * Returns the natural pixel width needed to display this row's full content\n * (indent + toggle + renderer content) without horizontal clipping.\n *\n * @param indentPx - Pixels of indentation per depth level.\n */\n getContentWidth(indentPx: number): number {\n const indent = this._depth * indentPx;\n const contentW = this._renderer.getContentWidth();\n\n return indent + TOGGLE_WIDTH + contentW;\n }\n\n /**\n * Appends the renderer sub-component element to the row's DOM element.\n * The toggle glyph (if any) is appended on demand by `setRowData`.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n DOM.sink.appendChild(el, this._renderer.getElement(true)!);\n\n return this;\n }\n}\n\nconst TreeRowCallable = callable(TreeRow);\ntype TreeRowCallable = TreeRow;\nexport {\n TreeRow as _TreeRow,\n TreeRowCallable as TreeRow\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport type { Size } from \"~/primitive/Size.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { VirtualRowView } from \"~/component/shared/VirtualRowView.js\";\nimport { selectionsEqual } from \"~/component/shared/selectionsEqual.js\";\nimport { TreeNode } from \"~/component/tree/TreeNode.js\";\nimport { TreeRow } from \"~/component/tree/TreeRow.js\";\nimport { TreeNodeRenderer } from \"~/component/tree/TreeNodeRenderer.js\";\nimport { LabelTreeNodeRenderer } from \"~/component/tree/renderer/Label.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link Tree}.\n *\n * @category Components\n */\nexport type TreeEvent = \"selection\" | \"loaderror\" | \"contextmenu\" | \"dblclick\" | \"expand\" | \"collapse\";\n\n/**\n * How a row wider than the viewport is handled.\n *\n * - `\"scroll\"` (the default) — every row grows to the widest label seen so\n * far and the horizontal scrollbar reveals the rest. Appropriate when a\n * label's full text matters (e.g. a file path).\n * - `\"clip\"` — every row caps at the viewport width and a row-node renderer\n * that honours it (e.g. {@link LabelTreeNodeRenderer}) truncates with an\n * ellipsis instead. Appropriate for a TOC-style outline, where reading a\n * label matters more than its exact text and scrolling sideways to read\n * one would be worse than truncating it.\n *\n * @category Components\n */\nexport type TreeRowOverflow = \"scroll\" | \"clip\";\n\n/** Which click gesture on a row's body expands or collapses it. Default `\"dblclick\"`. */\nexport type TreeExpandTrigger = \"dblclick\" | \"click\";\n\n/** Pixels of indentation added per depth level. */\nconst INDENT_PX = 16;\n\n/** Fixed height in pixels for every visible row. */\nconst ROW_HEIGHT = 24;\n\n/** Default preferred width when the caller sets no explicit `preferredSize`. */\nconst DEFAULT_PREFERRED_WIDTH = 200;\n\n/** CSS background applied to the selected row. */\nconst SELECTED_BG = \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\";\n\n/**\n * A pre-flattened entry in the visible subtree used by the virtual scroll.\n */\ninterface FlatRow {\n node: TreeNode;\n depth: number;\n siblingCount: number;\n posInSet: number;\n}\n\n/**\n * Construction-time options for {@link Tree}. Root nodes are set via\n * `setNodes()` and the renderer factory via `setRendererFactory()`; this\n * interface only carries the event-listener bag and the inherited\n * {@link ComponentOptions}.\n *\n * @category Components\n */\nexport interface TreeOptions extends ComponentOptions {\n /** How a row wider than the viewport is handled. Default `\"scroll\"`. See {@link TreeRowOverflow}. */\n rowOverflow?: TreeRowOverflow;\n\n /** Which click gesture on a row's body expands/collapses it. Default `\"dblclick\"`. See {@link TreeExpandTrigger}. */\n expandTrigger?: TreeExpandTrigger;\n\n /**\n * Multi-event listener bag dispatched to {@link Tree.on} at\n * construction time.\n */\n listeners?: {\n selection?: (nodes: TreeNode[]) => void;\n loaderror?: (node: TreeNode, error: unknown) => void;\n contextmenu?: (node: TreeNode, event: MouseEvent) => void;\n dblclick?: (node: TreeNode) => void;\n expand?: (node: TreeNode) => void;\n collapse?: (node: TreeNode) => void;\n };\n}\n\n/** User-overridable default fill; a caller-supplied `backgroundColor` wins. */\nconst _defaultTreeOptions: Partial<TreeOptions> = {\n backgroundColor: \"var(--ts-ui-input-bg, rgb(255, 255, 255))\",\n overflow: \"hidden\",\n maxSize: { width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER },\n rowOverflow: \"scroll\",\n expandTrigger: \"dblclick\",\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 _pendingExpansions : Map<TreeNode, Promise<boolean>> = new Map();\n private _anchorNode : TreeNode | null = null;\n private _focusNode : TreeNode | null = null;\n private _listeners : ListenerBag<TreeEvent> = new ListenerBag<TreeEvent>();\n private _rendererFactory : () => TreeNodeRenderer = () => new LabelTreeNodeRenderer();\n\n constructor(options?: TreeOptions, subclassDefaults?: Partial<TreeOptions>) {\n super(options, { ..._defaultTreeOptions, ...(subclassDefaults ?? {}) });\n\n this.getAria().setRole(\"tree\");\n this.getAria().setTabIndex(0);\n this.getAria().setMultiselectable(true);\n\n this.subscribeTheme(() => this.onThemeReflow());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Dispatches {@link TreeOptions.rowOverflow} and\n * {@link TreeOptions.expandTrigger}; every other option is inherited from\n * {@link Component}.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This tree, for method chaining.\n */\n protected applyOptions(options: TreeOptions): this {\n super.applyOptions(options);\n\n if (options.rowOverflow !== undefined) {\n this.setRowOverflow(options.rowOverflow);\n }\n\n if (options.expandTrigger !== undefined) {\n this.setExpandTrigger(options.expandTrigger);\n }\n\n return this;\n }\n\n /**\n * How a row wider than the viewport is handled.\n *\n * @returns The cached {@link TreeOptions.rowOverflow}, or the class default when never set.\n */\n getRowOverflow(): TreeRowOverflow {\n return this._options.rowOverflow ?? this._defaultOptions.rowOverflow ?? \"scroll\";\n }\n\n /**\n * Sets how a row wider than the viewport is handled.\n *\n * @param rowOverflow - `\"scroll\"` grows rows to fit the widest label and\n * scrolls; `\"clip\"` caps rows at the viewport width and truncates. See\n * {@link TreeRowOverflow}.\n * @returns This tree, for method chaining.\n */\n setRowOverflow(rowOverflow: TreeRowOverflow): this {\n this._options.rowOverflow = rowOverflow;\n\n return this;\n }\n\n /**\n * Which click gesture on a row's body expands or collapses it.\n *\n * @returns The cached {@link TreeOptions.expandTrigger}, or the class default when never set.\n */\n getExpandTrigger(): TreeExpandTrigger {\n return this._options.expandTrigger ?? this._defaultOptions.expandTrigger ?? \"dblclick\";\n }\n\n /**\n * Sets which click gesture on a row's body expands or collapses it.\n *\n * @param expandTrigger - `\"dblclick\"` requires a double-click on the row\n * body to toggle it, the file-explorer convention; `\"click\"` toggles it\n * on a plain single click, the IDE-sidebar convention. The caret always\n * toggles on a single click regardless of this setting. See\n * {@link TreeExpandTrigger}.\n * @returns This tree, for method chaining.\n */\n setExpandTrigger(expandTrigger: TreeExpandTrigger): this {\n this._options.expandTrigger = expandTrigger;\n\n return this;\n }\n\n /**\n * Reports a height derived from the current flattened row count as the\n * preferred height when the caller has set no explicit `preferredSize`,\n * mirroring `Markdown.getPreferredSize`'s own measured-content override —\n * so a `Tree` sized by its preferred size (e.g. a corner-pinned `Anchor`\n * child, not stretched) grows and shrinks with its actual content instead\n * of reporting a fixed guess. Width keeps the class default; an explicit\n * `preferredSize` constraint wins outright.\n *\n * @returns The preferred `{width, height}`.\n */\n getPreferredSize(): Size | null {\n if (this.getPreferredSizeConstraint() !== null) {\n return super.getPreferredSize();\n }\n\n return { width: DEFAULT_PREFERRED_WIDTH, height: this._flatRows.length * this.getRowHeight() };\n }\n\n /**\n * Replaces the root nodes, collapses all nodes, clears selection, and re-renders.\n *\n * @param nodes - The new array of root {@link TreeNode} objects.\n */\n setNodes(nodes: TreeNode[]): this {\n this._nodes = nodes;\n this._expandedNodes.clear();\n this._selectedNodes.clear();\n this._loadingNodes.clear();\n this._loadedNodes.clear();\n this._pendingExpansions.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 * Returns every currently expanded node, in no guaranteed order.\n *\n * @returns A snapshot array of expanded {@link TreeNode} instances.\n *\n * @remarks\n * `expandAll` and `revealByPredicate` fill the expanded set without\n * emitting `\"expand\"`, so this getter is how a caller reads the state\n * after either.\n */\n getExpandedNodes(): TreeNode[] {\n return Array.from(this._expandedNodes);\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. It likewise expands ancestors without\n * emitting `\"expand\"` — read {@link getExpandedNodes} afterwards.\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\n /**\n * Registers a listener for a node's expansion committing.\n *\n * @param event - `\"expand\"` fires after the expansion has committed and\n * the rows have been rebuilt — for an unloaded lazy node, after its\n * `loadChildren` resolved and the children were attached, not when the\n * toggle was first requested. It never fires from `setNodes`,\n * `expandAll`, or `revealByPredicate`; a rejected lazy load fires only\n * `\"loaderror\"` instead.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"expand\", listener: (node: TreeNode) => void): this;\n\n /**\n * Registers a listener for a node leaving the expanded set.\n *\n * @param event - `\"collapse\"` fires after the node has left the expanded\n * set and the rows have been rebuilt. It never fires from `setNodes`,\n * `expandAll`, or `revealByPredicate`.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This tree, for method chaining.\n */\n on(event: \"collapse\", 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: \"expand\", node: TreeNode): void;\n protected emit(event: \"collapse\", node: TreeNode): void;\n protected emit(event: TreeEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the renderer factory used to create one\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer)\n * instance per pool slot.\n *\n * @param factory - Zero-argument function returning a new\n * [`TreeNodeRenderer`](/api/component/tree/classes/TreeNodeRenderer).\n * Defaults to `() => new LabelTreeNodeRenderer()`.\n * @returns This tree, for method chaining.\n *\n * @remarks\n * If pool rows already exist, each one's renderer is replaced in place via\n * `TreeRow.setRenderer(factory())`. The DOM pool itself is preserved.\n * Bindings are cleared so the next render pass rebinds and updates the\n * new renderers.\n */\n setRendererFactory(factory: () => TreeNodeRenderer): this {\n this._rendererFactory = factory;\n\n for (const row of this._rowPool) {\n row.setRenderer(factory());\n }\n\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n\n if (this.getElement()) {\n this.renderWindow();\n }\n\n return this;\n }\n\n /**\n * Returns the renderer factory currently in use.\n *\n * @returns The zero-argument factory function.\n */\n getRendererFactory(): () => TreeNodeRenderer {\n return this._rendererFactory;\n }\n\n /**\n * Reports whether a node renders an expand/collapse caret.\n *\n * @param node - The node to test.\n * @returns True when the node already carries children, or is a lazy node\n * declared with `hasChildren: true`.\n *\n * @remarks\n * Centralising the predicate keeps the caret, ARIA, and flatten decisions\n * from drifting apart. An eager node with an empty `children` array is not\n * expandable; a lazy node is expandable before its children exist.\n */\n private _isExpandable(node: TreeNode): boolean {\n return !!(node.children && node.children.length > 0) || node.hasChildren === true;\n }\n\n /**\n * Rebuilds the flat visible-row list from the current root nodes and expanded set,\n * computing `siblingCount` and `posInSet` (1-based) for each entry.\n */\n private _flatten(): void {\n this._flatRows = [];\n\n // The visible set is changing, so the widest-row running maximum no\n // longer describes it — reset it and let the next render pass re-derive\n // the content width from the new rows (a collapsed wide branch or a\n // narrower dataset must be able to shrink the horizontal extent).\n this._maxContentWidth = 0;\n\n const recurse = (nodes: TreeNode[], depth: number): void => {\n const siblingCount = nodes.length;\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n this._flatRows.push({ node, depth, siblingCount, posInSet: i + 1 });\n\n if (this._isExpandable(node) && this._expandedNodes.has(node) && node.children) {\n recurse(node.children, depth + 1);\n }\n }\n };\n\n recurse(this._nodes, 0);\n }\n\n /**\n * Re-flattens the visible subtree and forces a full rebind of the pool so\n * every row reflects the current expanded/loading state.\n *\n * @remarks\n * Called from the collapse path, the synchronous expand path, and the async\n * load path — anywhere the flattened-row set or a node's loading affordance\n * changes.\n */\n private _reflattenAndRender(): void {\n this._flatten();\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n }\n\n /**\n * Expands `node` if it is not already expanded — the same commit path as\n * clicking its collapsed caret (loading its children first when it is an\n * unloaded lazy node), without requiring a prior click. No-ops if `node`\n * is already expanded. Useful for defaulting specific sections open after\n * {@link setNodes}, which always starts every node collapsed.\n *\n * @param node - The node to expand.\n *\n * @returns This tree, for method chaining.\n *\n * @see Use {@link expandNodeAsync} instead when the caller needs to know\n * when a lazy expansion finished.\n */\n expandNode(node: TreeNode): this {\n if (!this._expandedNodes.has(node)) {\n void this._expand(node);\n }\n\n return this;\n }\n\n /**\n * Expands `node` and resolves once the expansion has committed.\n *\n * @param node - The node to expand.\n *\n * @returns A promise resolving to `true` when the node ends up expanded —\n * including when it already was — or `false` when a lazy load rejected\n * and the node stayed collapsed.\n *\n * @remarks\n * A second call for a node whose lazy load is already in flight joins\n * that load instead of starting another, and both callers resolve with\n * its outcome. Emits `\"expand\"` on a real transition, the same as\n * {@link expandNode}.\n */\n async expandNodeAsync(node: TreeNode): Promise<boolean> {\n if (this._expandedNodes.has(node)) {\n return true;\n }\n\n return this._expand(node);\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 * Delegates to {@link _collapse} for an expanded node and {@link _expand}\n * for a collapsed one — the same commit path (and lazy-load join\n * behaviour, via {@link _expandLazy}) used by every other caller.\n */\n private _onToggle(node: TreeNode): void {\n if (this._expandedNodes.has(node)) {\n this._collapse(node);\n\n return;\n }\n\n void this._expand(node);\n }\n\n /**\n * Commits `node`'s expansion, loading a lazy node's children first.\n *\n * @param node - The node to expand.\n *\n * @returns A promise resolving to `true` once the expansion has committed,\n * or `false` when a lazy load failed or was orphaned.\n *\n * @remarks\n * An already-loaded node commits synchronously — the returned promise is\n * already resolved by the time this returns, so a caller that ignores it\n * still sees the expansion in the same tick.\n */\n private _expand(node: TreeNode): Promise<boolean> {\n const needsLoad = node.loadChildren !== undefined\n && !this._loadedNodes.has(node)\n && !(node.children && node.children.length);\n\n if (needsLoad) {\n return this._expandLazy(node);\n }\n\n this._expandedNodes.add(node);\n this._reflattenAndRender();\n this.emit(\"expand\", node);\n\n return Promise.resolve(true);\n }\n\n /**\n * Removes `node` from the expanded set, re-renders, then reports the change.\n *\n * @param node - The node to collapse.\n */\n private _collapse(node: TreeNode): void {\n this._expandedNodes.delete(node);\n this._reflattenAndRender();\n this.emit(\"collapse\", node);\n }\n\n /**\n * Starts a lazy node's load-and-expand, or joins the one already running for\n * that node so a second caller never triggers a second `loadChildren`.\n *\n * @param node - The lazy node to expand.\n *\n * @returns The (possibly shared) promise resolving once the load settles.\n */\n private _expandLazy(node: TreeNode): Promise<boolean> {\n const inFlight = this._pendingExpansions.get(node);\n\n if (inFlight !== undefined) {\n return inFlight;\n }\n\n const pending = this._loadAndExpand(node);\n\n this._pendingExpansions.set(node, pending);\n\n // Identity-checked so a `setNodes` that cleared the map mid-flight, followed\n // by a fresh load for the same node object, is not un-registered by the\n // orphaned load's own cleanup.\n void pending.then(() => {\n if (this._pendingExpansions.get(node) === pending) {\n this._pendingExpansions.delete(node);\n }\n });\n\n return pending;\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 * The `\"expand\"` emission is the last thing this method does, after the\n * re-render, so a listener sees the loaded children already flattened.\n *\n * @returns `true` when the node's expansion committed, `false` when the\n * load failed or was orphaned by a `setNodes` swap.\n */\n private async _loadAndExpand(node: TreeNode): Promise<boolean> {\n this._loadingNodes.add(node);\n this._reflattenAndRender();\n\n let expanded = false;\n\n try {\n const children = await node.loadChildren!();\n\n if (!this._loadingNodes.has(node)) {\n return false;\n }\n\n node.children = children;\n this._loadedNodes.add(node);\n this._expandedNodes.add(node);\n expanded = true;\n } catch (error) {\n if (!this._loadingNodes.has(node)) {\n return false;\n }\n\n this.emit(\"loaderror\", node, error);\n } finally {\n this._loadingNodes.delete(node);\n this._reflattenAndRender();\n }\n\n if (expanded) {\n this.emit(\"expand\", node);\n }\n\n return expanded;\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): Event.ListenerResult {\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 // 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\n return { prevent: true };\n }\n\n const { node } = flatRows[focusIdx];\n const hasChildren = this._isExpandable(node);\n\n if (!hasChildren) {\n return { prevent: true };\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 { prevent: true };\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 return { prevent: true };\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 * When {@link getExpandTrigger} is `\"click\"`, a plain click (no\n * modifiers) on an expandable row's body also toggles its expansion\n * after selection is applied; Ctrl/Cmd-click and an anchored Shift-click\n * never toggle; the caret always toggles regardless of this setting.\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 if (this.getExpandTrigger() === \"click\" && this._isExpandable(node)) {\n this._onToggle(node);\n }\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): Event.ListenerResult {\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 this.emit(\"contextmenu\", node, e);\n\n return { prevent: true };\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. When {@link getExpandTrigger} is `\"click\"`, the row-body toggle\n * below is skipped too, for the same reason the caret's is — each click of\n * the pair already toggled it via {@link _handleClick}.\n *\n * @param e - The dblclick event whose target is inside the tree's subtree.\n */\n private _handleDblClick(e: MouseEvent): Event.ListenerResult {\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 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. The row body is skipped the same way\n // when `expandTrigger` is `\"click\"` — there too, each of the two\n // clicks already toggled it via `_handleClick`. Leaf nodes have\n // nothing to expand, so their `\"dblclick\"` stays a pure activation\n // 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) && this.getExpandTrigger() === \"dblclick\") {\n this._onToggle(node);\n }\n\n return { prevent: true };\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 this.alignPoolWindow(win.firstRow);\n\n const { reboundFlags, maxContentWidth } =\n this._bindAndMeasure(win.firstRow, win.windowSize);\n\n // Only the visible window was measured, so fold the widest row seen this\n // frame into the running maximum for the current flattened set (reset in\n // `_flatten`). The content width then only grows as wider rows are\n // discovered and never shrinks when they scroll away, so the horizontal\n // scrollbar stays stable instead of jittering with the visible rows.\n if (maxContentWidth > this._maxContentWidth) {\n this._maxContentWidth = maxContentWidth;\n }\n\n // Fill to the effective viewport width (owner width minus the vertical\n // scrollbar's reservation when that bar is visible), not the raw owner\n // width. Sizing rows to the full width would push their trailing edge\n // under the vertical bar, and handing that full width to\n // `layoutScrollbars` as the content width forces a spurious horizontal\n // bar for the reserved band. `clampToContent` above has already refreshed\n // the scroller's content metrics this pass.\n //\n // `rowOverflow: \"clip\"` never grows past the viewport at all — every\n // row (and so every renderer) is capped there instead, trading the\n // horizontal scrollbar for truncation (see `LabelTreeNodeRenderer`).\n const rowWidth = this.getRowOverflow() === \"clip\"\n ? scroller.getViewportWidth()\n : Math.max(scroller.getViewportWidth(), this._maxContentWidth);\n if (rowWidth !== this._lastRowWidth) {\n this._lastRowWidth = rowWidth;\n this.invalidateGeom();\n }\n\n this._positionRows(win.firstRow, win.windowSize, rowWidth, reboundFlags);\n this.hideExcessPoolRows(win.windowSize);\n\n this._updateSelectionStyle();\n\n scroller.layoutScrollbars(rowWidth, totalHeight);\n\n // A pass the startup font gate deferred skipped whatever its caller did\n // after asking for it — including the active-descendant refresh, which\n // could not name a row that did not exist yet. Redo it now the rows do.\n if (this.finishResumedRender()) {\n this._updateActiveDescendant();\n }\n }\n\n /**\n * First pass: binds visible pool slots to their flattened rows (when\n * rebound) and computes the maximum content width across visible rows so\n * the second pass knows how wide each row should be.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @returns Per-slot rebind flags (parallel to the window) and the widest\n * content width seen, both consumed by {@link _positionRows}.\n */\n private _bindAndMeasure(firstRow: number, windowSize: number): { reboundFlags: boolean[], maxContentWidth: number } {\n const reboundFlags: boolean[] = new Array(windowSize);\n let maxContentWidth = 0;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const flatRow = this._flatRows[dataIndex];\n const hasChildren = this._isExpandable(flatRow.node);\n const expanded = this._expandedNodes.has(flatRow.node);\n const loading = this._loadingNodes.has(flatRow.node);\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound) {\n const selected = this._selectedNodes.has(flatRow.node);\n row.setRowData(flatRow.node, flatRow.depth, hasChildren, expanded, flatRow.siblingCount, flatRow.posInSet, selected, loading);\n this._boundIndices[i] = dataIndex;\n }\n\n reboundFlags[i] = wasRebound;\n\n const cw = row.getContentWidth(INDENT_PX);\n if (cw > maxContentWidth) {\n maxContentWidth = cw;\n }\n }\n\n return { reboundFlags, maxContentWidth };\n }\n\n /**\n * Second pass: positions visible rows at `dataIndex * ROW_HEIGHT`, sizes\n * them to `rowWidth`, marks them displayed, and re-lays out their children\n * only when the row was rebound or its geometry changed.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @param rowWidth - The horizontal extent of each row in pixels.\n * @param reboundFlags - The per-slot rebind flags produced by {@link _bindAndMeasure}.\n */\n private _positionRows(firstRow: number, windowSize: number, rowWidth: number, reboundFlags: boolean[]): void {\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const wasRebound = reboundFlags[i];\n\n const geomChanged = this.positionRow(i, dataIndex * ROW_HEIGHT, rowWidth);\n\n if (wasRebound || geomChanged) {\n row.layoutChildren(ROW_HEIGHT, INDENT_PX);\n }\n }\n }\n\n /**\n * Constructs the `VirtualScroller` and wires click and keyboard listeners.\n *\n * @param element - Optional element passed by the rendering pipeline; falls back to getElement().\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n this.initScroller(el);\n\n Event.addSubtreeListener(this, \"click\", this._handleClick);\n Event.addSubtreeListener(this, \"contextmenu\", this._handleContextMenu);\n Event.addSubtreeListener(this, \"dblclick\", this._handleDblClick);\n Event.addListener(this, \"keydown\", this._onKeyDown);\n\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Calls the layout manager and then refreshes the virtual-scroll viewport.\n *\n * @returns This component, for method chaining.\n *\n * @remarks\n * Overrides [`Component.doLayout`](/api/core/classes/Component#doLayout) so that layout-manager-driven size changes\n * (e.g. from a parent Tab or Split) automatically update the rendered row window.\n */\n doLayout(): this {\n if (this.isLayoutPaused()) {\n return this;\n }\n\n super.doLayout();\n\n // A pass the startup font gate deferred already renders the window, so\n // running the unconditional one as well would render twice on the frame\n // the gate opens.\n if (!this.renderWindowIfDeferred()) {\n this.renderWindow();\n }\n\n return this;\n }\n}\n\nconst TreeCallable = callable(Tree);\ntype TreeCallable = Tree;\nexport {\n Tree as _Tree,\n TreeCallable as Tree\n};\n"],"mappings":"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"}
|
package/dist/lib/core.es.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"core.es.js","names":[],"sources":["../../src/typescript/lib/core/themes/ClassicTheme.ts","../../src/typescript/lib/core/themes/DarkTheme.ts","../../src/typescript/lib/core/Form.ts","../../src/typescript/lib/core/Favicon.ts","../../src/typescript/lib/core/Body.ts","../../src/typescript/lib/core/FocusHistory.ts","../../src/typescript/lib/core/Binding.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BaseTheme } from '~/core/themes/BaseTheme.js';\nimport { defineTheme } from '~/core/Theme.js';\nimport type { Theme } from '~/core/Theme.js';\n\n/**\n * Classic light theme using white backgrounds, black text, and gradient\n * buttons. The original default look, retained as an opt-in alternative now\n * that {@link ModernTheme} is the preselected theme.\n *\n * Authored as `defineTheme(BaseTheme, …)`: it declares only its palette (and\n * `colorScheme`); every structural token is inherited from {@link BaseTheme}.\n * Classic carries no structural override — its `tab.underBorderFullWidth`\n * already matches the base value.\n *\n * @category Theme\n */\nexport const ClassicTheme: Theme = defineTheme(BaseTheme, {\n colorScheme: 'light',\n text : { color: 'rgb(0, 0, 0)' },\n body : { background: 'rgb(255, 255, 255)' },\n border : { color: 'black' },\n button : {\n background: 'linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200))',\n border : 'rgb(200, 200, 200)',\n shadow : '1px 2px 5px 0 rgba(0, 0, 0, 0.2)',\n description: {\n foreground: 'rgb(110, 110, 110)',\n },\n pressed : {\n foreground: 'rgb(150, 150, 150)',\n background: 'linear-gradient(rgb(200, 200, 200), rgb(214, 214, 214))',\n shadow : 'inset 0 1px 4px rgba(0, 0, 0, 0.30)',\n },\n hover : {\n foreground: 'inherit',\n background: 'linear-gradient(rgb(252, 252, 252), rgb(220, 220, 220))',\n shadow : '1px 3px 6px 0 rgba(0, 0, 0, 0.25)',\n },\n flat : {\n hover : {\n background: 'rgba(0, 0, 0, 0.06)',\n border : '1px solid rgb(200, 200, 200)',\n },\n pressed: {\n background: 'rgba(0, 0, 0, 0.10)',\n shadow : 'inset 1px 1px 3px rgba(0, 0, 0, 0.25)',\n border : '1px solid rgb(180, 180, 180)',\n },\n },\n },\n toggle : {\n selected: {\n background: 'rgb(200, 200, 200)',\n shadow : '2px 2px 1px inset grey',\n },\n },\n input : {\n background : 'rgb(255, 255, 255)',\n border : '1px solid rgb(160, 160, 160)',\n borderHover: '1px solid rgb(120, 120, 120)',\n },\n form : {\n background : 'rgb(255, 255, 255)',\n border : 'rgb(160, 160, 160)',\n color : 'rgb(40, 40, 40)',\n disabledBackground: 'rgb(240, 240, 240)',\n disabledColor : 'rgb(170, 170, 170)',\n focusRing : 'rgb(30, 100, 200)',\n toggle : {\n trackOffBackground: 'rgb(200, 200, 200)',\n trackOnBackground : 'rgb(30, 100, 200)',\n thumbBackground : 'rgb(255, 255, 255)',\n },\n slider : {\n trackBackground : 'rgb(220, 220, 220)',\n trackActiveBackground: 'rgb(30, 100, 200)',\n thumbBackground : 'rgb(255, 255, 255)',\n },\n checkbox: {\n background : 'rgb(255, 255, 255)',\n selectedBackground : 'rgb(30, 100, 200)',\n indeterminateBackground: 'rgb(160, 160, 160)',\n checkColor : 'rgb(255, 255, 255)',\n },\n radio : {\n background : 'rgb(255, 255, 255)',\n selectedBackground: 'rgb(30, 100, 200)',\n dotColor : 'rgb(255, 255, 255)',\n },\n },\n gutter: { background: '#AAAAAA' },\n collapse: {\n strip: { background: '#AAAAAA', size: '18px' },\n button: { color: 'rgb(100,100,100)' },\n },\n accordion: {\n border : '1px solid rgb(190,190,190)',\n header : {\n background: 'linear-gradient(rgb(230,230,230),rgb(210,210,210))',\n border : '1px solid rgb(190,190,190)',\n color : 'inherit',\n },\n panel : { border: 'rgb(210,210,210)' },\n indicator: { color: 'rgb(100,100,100)' },\n },\n tab : {\n toolbar: { background: '#eee', border: '#e1e1e8' },\n button : {\n background: 'linear-gradient(rgb(208, 208, 216), rgb(185, 185, 196))',\n border : 'none',\n hover : { background: 'linear-gradient(rgb(220, 220, 227), rgb(198, 198, 208))', border: 'none' },\n selected : { background: 'linear-gradient(rgb(255, 255, 255), rgb(235, 235, 240))', border: 'none' },\n },\n indicator: { color: '#1a73e8' },\n },\n window: {\n shadow : '3px 3px 2px rgba(0, 0, 0, 0.4)',\n snapGlow : '0 0 0 2px rgba(30, 100, 200, 0.7)',\n control : {\n background : 'linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200))',\n border : '1px solid rgb(200, 200, 200)',\n shadow : '1px 2px 5px 0 rgba(0, 0, 0, 0.2)',\n hoverBackground : 'linear-gradient(rgb(252, 252, 252), rgb(220, 220, 220))',\n activeBackground: 'linear-gradient(rgb(200, 200, 200), rgb(214, 214, 214))',\n },\n header : { background: '#eee' },\n },\n table : {\n header: {\n background: 'linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200))',\n border: 'black',\n glyph : { color: 'currentColor' },\n },\n row : {\n selected : 'rgba(30, 100, 200, 0.15)',\n selectedBorder: 'inset 0 0 0 1px rgba(30, 100, 200, 0.6)',\n new : 'rgba(70, 200, 70, 0.15)',\n dirty : 'rgba(255, 165, 0, 0.15)',\n stripe : 'rgba(0, 0, 0, 0.035)',\n },\n cell : {\n background : 'transparent',\n readonlyBackground : 'rgba(0, 0, 0, 0.04)',\n rangeSelectedBackground : 'rgba(30, 100, 200, 0.15)',\n requiredEmptyOutlineColor : 'rgba(220, 60, 60, 0.6)',\n color : 'inherit',\n border : 'none',\n editorBorderColor : 'rgba(30, 100, 200, 0.6)',\n },\n filterRow: {\n background: 'rgba(255, 200, 0, 0.2)',\n },\n resizeHandle: {\n color : 'rgba(0, 0, 0, 0.2)',\n },\n sortBadge: {\n background: 'rgba(0, 0, 0, 0.15)',\n color : 'inherit',\n },\n // Solid (not translucent) and independent of `color: inherit` — this\n // badge sits on the operator button's own glyph (`fill: currentColor`),\n // so inheriting `color` risks the digit rendering the same shade as\n // the glyph beneath/around it. The theme's own blue accent (matching\n // the focus ring — see the chart palette comment below) on white\n // gives a guaranteed ~5.7:1 contrast ratio, independent of whatever\n // sits behind it.\n filterClauseBadge: {\n background: 'rgb(30, 100, 200)',\n color : 'rgb(255, 255, 255)',\n },\n },\n contextMenu: {\n background : 'rgb(255, 255, 255)',\n border : 'rgb(200, 200, 200)',\n shadow : '2px 4px 8px rgba(0, 0, 0, 0.15)',\n item : {\n hoverBackground: 'rgba(30, 100, 200, 0.12)',\n disabledColor : 'rgb(170, 170, 170)',\n },\n separatorColor: 'rgb(220, 220, 220)',\n },\n menuBar: {\n // Menu bars share the tool bar's background by default (see toolBar below).\n background : 'rgb(245, 245, 245)',\n border : 'rgb(220, 220, 220)',\n button : {\n background : 'transparent',\n hoverBackground: 'rgba(30, 100, 200, 0.10)',\n foreground : 'inherit',\n },\n panel : {\n background: 'rgb(255, 255, 255)',\n border : 'rgb(200, 200, 200)',\n shadow : '2px 4px 8px rgba(0, 0, 0, 0.15)',\n },\n item : {\n hoverBackground: 'rgba(30, 100, 200, 0.12)',\n disabledColor : 'rgb(170, 170, 170)',\n shortcutColor : 'rgb(140, 140, 140)',\n },\n separatorColor: 'rgb(220, 220, 220)',\n },\n statusBar: {\n background: 'rgb(245, 245, 245)',\n color : 'rgb(60, 60, 60)',\n border : 'rgb(220, 220, 220)',\n },\n toolBar: {\n background : 'rgb(245, 245, 245)',\n border : 'rgb(220, 220, 220)',\n separatorColor: 'rgb(220, 220, 220)',\n },\n tooltip: {\n background: 'rgb(255, 255, 240)',\n color : 'rgb(0, 0, 0)',\n border : 'rgb(180, 180, 100)',\n shadow : '1px 2px 4px rgba(0, 0, 0, 0.2)',\n },\n popover: {\n background: 'rgb(255, 255, 255)',\n color : 'rgb(0, 0, 0)',\n border : 'rgb(200, 200, 200)',\n shadow : '2px 4px 12px rgba(0, 0, 0, 0.18)',\n },\n notification: {\n shadow : '2px 4px 8px rgba(0, 0, 0, 0.15)',\n info : { background: 'rgba(210, 224, 244, 0.75)', border: 'rgb(30, 100, 200)' },\n success: { background: 'rgba(210, 240, 220, 0.75)', border: 'rgb(30, 180, 80)' },\n warning: { background: 'rgba(248, 232, 204, 0.75)', border: 'rgb(220, 140, 0)' },\n error : { background: 'rgba(244, 214, 214, 0.75)', border: 'rgb(200, 50, 50)' },\n },\n validation: {\n error: {\n border : 'rgb(200, 50, 50)',\n tooltip: {\n background: 'rgb(180, 30, 30)',\n color : 'rgb(255, 255, 255)',\n border : 'rgb(140, 20, 20)',\n },\n },\n },\n autoComplete: {\n background: 'rgb(255, 255, 255)',\n border : 'rgb(200, 200, 200)',\n shadow : '2px 4px 8px rgba(0,0,0,0.15)',\n item: {\n hoverBackground : 'rgba(30, 100, 200, 0.08)',\n highlightBackground: 'rgba(30, 100, 200, 0.18)',\n highlightColor : 'inherit',\n disabledColor : 'rgb(170, 170, 170)',\n },\n },\n // Defaults mirror `autoComplete` numerically because both surfaces present\n // selectable rows; a theme that customises one gets the other matching\n // automatically. `row.separator` defaults to `transparent` so the visual\n // matches the prior native `<select>` (no row hairlines); themes can opt\n // in to a denser look by overriding to a `1px solid rgba(...)` colour.\n list: {\n background: 'rgb(255, 255, 255)',\n border : 'rgb(200, 200, 200)',\n row: {\n hoverBackground : 'rgba(30, 100, 200, 0.08)',\n selectedBackground: 'rgba(30, 100, 200, 0.18)',\n selectedColor : 'inherit',\n focusRing : 'rgb(30, 100, 200)',\n disabledColor : 'rgb(170, 170, 170)',\n separator : 'transparent',\n },\n },\n indicator: {\n focus : 'rgb(30, 100, 200)',\n selection: '1px dashed rgb(120, 170, 240)',\n },\n // Picker tokens stay numerically in step with `autoComplete.item.*` — they\n // share the {@link PickerCell} class — so a theme that customises one\n // gets the other matching automatically.\n picker: {\n navForeground: 'var(--ts-ui-text-color)',\n navHoverBackground: 'rgba(30, 100, 200, 0.08)',\n cellDisabledBackground: 'transparent',\n },\n dialog: {\n backdrop: { background: 'rgba(0, 0, 0, 0.45)' },\n border : 'rgb(220, 220, 220)',\n shadow : '4px 8px 24px rgba(0, 0, 0, 0.35)',\n confirm : 'rgb(30, 180, 80)',\n cancel : 'rgb(200, 50, 50)',\n info : { background: 'rgba(30, 100, 200, 0.15)', foreground: 'rgb(30, 100, 200)' },\n affirm : { background: 'rgba(30, 180, 80, 0.15)', foreground: 'rgb(30, 180, 80)' },\n },\n drawer: {\n shadow : '4px 0 24px rgba(0, 0, 0, 0.25)',\n border : 'rgb(220, 220, 220)',\n },\n rail: {\n border : 'rgb(200, 200, 200)',\n shadow : '2px 0 12px rgba(0, 0, 0, 0.18)',\n handle: {\n hoverBackground : 'rgba(30, 100, 200, 0.08)',\n selectedBackground: 'rgba(30, 100, 200, 0.16)',\n },\n },\n spinner: {\n dividerColor: 'rgb(180, 180, 180)',\n },\n progressBar: {\n track : { background: 'rgb(220, 220, 220)' },\n fill : { background: 'rgb(30, 100, 200)' },\n indeterminate: { background: 'rgb(30, 100, 200)' },\n },\n progressSpinner: {\n color : 'rgb(30, 100, 200)',\n backdrop: 'rgba(255, 255, 255, 0.6)',\n },\n drag: {\n ghost: {\n background: 'rgba(200, 200, 200, 0.9)',\n border : 'rgb(150, 150, 150)',\n shadow : '2px 4px 12px rgba(0, 0, 0, 0.25)',\n },\n feedback: {\n valid : { background: 'rgba(30, 180, 80, 0.12)', border: 'rgb(30, 180, 80)' },\n invalid: { background: 'rgba(200, 50, 50, 0.10)', border: 'rgb(200, 50, 50)' },\n },\n reorderIndicator: {\n color: 'rgb(30, 100, 200)',\n },\n dropzone: {\n background : 'rgba(80, 140, 240, 0.10)',\n border : 'rgba(80, 140, 240, 0.40)',\n activeBackground : 'rgba(80, 140, 240, 0.28)',\n invalidBackground: 'rgba(200, 50, 50, 0.28)',\n },\n },\n fileDropZone: {\n background : 'rgba(80, 140, 240, 0.06)',\n border : '2px dashed rgba(80, 140, 240, 0.40)',\n activeBackground: 'rgba(80, 140, 240, 0.18)',\n activeBorder : '2px dashed rgba(80, 140, 240, 0.80)',\n },\n scroll: {\n shadowColor: 'rgba(0, 0, 0, 1)',\n },\n // Okabe–Ito colour-blind-safe categorical palette, tuned for a light\n // background (Classic is a light scheme). Neutral grey axis/grid/label; the\n // selection ring reuses the theme's blue accent.\n chart: {\n series: [\n '#0072B2', // blue\n '#E69F00', // orange\n '#009E73', // green\n '#D55E00', // vermilion\n '#CC79A7', // purple\n '#56B4E9', // sky\n '#8C6D1F', // gold\n '#555555', // grey\n ],\n axis : 'rgb(120, 120, 120)',\n grid : 'rgb(220, 220, 220)',\n label : 'rgb(80, 80, 80)',\n selection: '#1a73e8',\n },\n});\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BaseTheme } from '~/core/themes/BaseTheme.js';\nimport { defineTheme } from '~/core/Theme.js';\nimport type { Theme } from '~/core/Theme.js';\n\n/**\n * Dark-mode theme using dark backgrounds and light text.\n *\n * Authored as `defineTheme(BaseTheme, …)`: beyond its palette and\n * `colorScheme: 'dark'`, its only structural divergence is\n * `tab.underBorderFullWidth: false` (the base value is `true`). Every other\n * structural token is inherited from {@link BaseTheme}.\n *\n * @category Theme\n */\nexport const DarkTheme: Theme = defineTheme(BaseTheme, {\n colorScheme: 'dark',\n text : { color: 'rgb(220, 220, 220)' },\n body : { background: 'rgb(30, 30, 30)' },\n border : { color: 'rgb(90, 90, 90)' },\n button : {\n background: 'linear-gradient(rgb(70, 70, 70), rgb(50, 50, 50))',\n border : 'rgb(80, 80, 80)',\n shadow : '1px 2px 5px 0 rgba(0, 0, 0, 0.5)',\n description: {\n foreground: 'rgb(160, 160, 160)',\n },\n pressed : {\n foreground: 'rgb(180, 180, 180)',\n background: 'linear-gradient(rgb(35, 35, 35), rgb(48, 48, 48))',\n shadow : 'inset 0 1px 4px rgba(0, 0, 0, 0.6)',\n },\n hover : {\n foreground: 'inherit',\n background: 'linear-gradient(rgb(90, 90, 90), rgb(65, 65, 65))',\n shadow : '1px 3px 6px 0 rgba(0, 0, 0, 0.55)',\n },\n flat : {\n hover : {\n background: 'rgba(255, 255, 255, 0.08)',\n border : '1px solid rgb(90, 90, 90)',\n },\n pressed: {\n background: 'rgba(255, 255, 255, 0.12)',\n shadow : 'inset 1px 1px 3px rgba(0, 0, 0, 0.55)',\n border : '1px solid rgb(70, 70, 70)',\n },\n },\n },\n toggle : {\n selected: {\n background: 'rgb(35, 35, 35)',\n shadow : '2px 2px 1px inset #333',\n },\n },\n input : {\n background : 'rgb(40, 40, 40)',\n border : '1px solid rgb(110, 110, 110)',\n borderHover: '1px solid rgb(150, 150, 150)',\n },\n form : {\n background : 'rgb(40, 40, 40)',\n border : 'rgb(110, 110, 110)',\n color : 'rgb(230, 230, 230)',\n disabledBackground: 'rgb(60, 60, 60)',\n disabledColor : 'rgb(120, 120, 120)',\n focusRing : 'rgb(120, 170, 240)',\n toggle : {\n trackOffBackground: 'rgb(70, 70, 70)',\n trackOnBackground : 'rgb(120, 170, 240)',\n thumbBackground : 'rgb(230, 230, 230)',\n },\n slider : {\n trackBackground : 'rgb(70, 70, 70)',\n trackActiveBackground: 'rgb(120, 170, 240)',\n thumbBackground : 'rgb(230, 230, 230)',\n },\n checkbox: {\n background : 'rgb(40, 40, 40)',\n selectedBackground : 'rgb(120, 170, 240)',\n indeterminateBackground: 'rgb(100, 100, 100)',\n checkColor : 'rgb(20, 20, 20)',\n },\n radio : {\n background : 'rgb(40, 40, 40)',\n selectedBackground: 'rgb(120, 170, 240)',\n dotColor : 'rgb(20, 20, 20)',\n },\n },\n gutter: { background: '#555' },\n collapse: {\n strip: { background: '#555', size: '18px' },\n button: { color: 'rgb(160,160,160)' },\n },\n accordion: {\n border : '1px solid rgb(80,80,80)',\n header : {\n background: 'linear-gradient(rgb(60,60,60),rgb(45,45,45))',\n border : '1px solid rgb(80,80,80)',\n color : 'inherit',\n },\n panel : { border: 'rgb(70,70,70)' },\n indicator: { color: 'rgb(160,160,160)' },\n },\n tab : {\n underBorderFullWidth: false,\n toolbar: { background: '#2a2a2a', border: '#444' },\n button : {\n background: '#3a3a3a',\n border : 'none',\n hover : { background: '#454545', border: 'none' },\n selected : { background: 'rgb(30, 30, 30)', border: 'none' },\n },\n indicator: { color: '#4a9eff' },\n },\n window : {\n shadow : '3px 3px 2px rgba(0, 0, 0, 0.6)',\n snapGlow : '0 0 0 2px rgba(80, 150, 240, 0.8)',\n control : {\n background : 'rgb(30, 30, 30)',\n border : '1px solid transparent',\n shadow : 'none',\n hoverBackground : 'rgb(55, 55, 55)',\n activeBackground: 'rgb(45, 45, 45)',\n },\n header : { background: '#2a2a2a' },\n },\n table : {\n header: {\n background: 'linear-gradient(rgb(70, 70, 70), rgb(50, 50, 50))',\n border: '#555',\n glyph : { color: 'currentColor' },\n },\n row : {\n selected : 'rgba(30, 100, 200, 0.25)',\n selectedBorder: 'inset 0 0 0 1px rgba(30, 100, 200, 0.8)',\n new : 'rgba(70, 200, 70, 0.2)',\n dirty : 'rgba(255, 165, 0, 0.2)',\n stripe : 'rgba(255, 255, 255, 0.045)',\n },\n cell : {\n background : 'transparent',\n readonlyBackground : 'rgba(255, 255, 255, 0.04)',\n rangeSelectedBackground : 'rgba(30, 100, 200, 0.25)',\n requiredEmptyOutlineColor : 'rgba(255, 90, 90, 0.8)',\n color : 'inherit',\n border : 'none',\n editorBorderColor : 'rgba(30, 100, 200, 0.8)',\n },\n filterRow: {\n background: 'rgba(255, 200, 0, 0.28)',\n },\n resizeHandle: {\n color : 'rgba(255, 255, 255, 0.25)',\n },\n sortBadge: {\n background: 'rgba(255, 255, 255, 0.2)',\n color : 'inherit',\n },\n // Solid (not translucent) and independent of `color: inherit` — this\n // badge sits on the operator button's own glyph (`fill: currentColor`),\n // so inheriting `color` risks the digit rendering the same shade as\n // the glyph beneath/around it. The theme's own (brightened) blue\n // accent (matching the focus ring — see the chart palette comment\n // below) needs a dark, not white, digit for real contrast: white on\n // this lighter blue is only ~2.4:1, while the body's own dark\n // background tone on it is ~7:1.\n filterClauseBadge: {\n background: 'rgb(120, 170, 240)',\n color : 'rgb(30, 30, 30)',\n },\n },\n contextMenu: {\n background : 'rgb(45, 45, 45)',\n border : 'rgb(80, 80, 80)',\n shadow : '2px 4px 8px rgba(0, 0, 0, 0.5)',\n item : {\n hoverBackground: 'rgba(100, 140, 220, 0.2)',\n disabledColor : 'rgb(100, 100, 100)',\n },\n separatorColor: 'rgb(70, 70, 70)',\n },\n menuBar: {\n // Menu bars share the tool bar's background by default (see toolBar below).\n background : 'rgb(45, 45, 45)',\n border : 'rgb(70, 70, 70)',\n button : {\n background : 'transparent',\n hoverBackground: 'rgba(100, 140, 220, 0.15)',\n foreground : 'inherit',\n },\n panel : {\n background: 'rgb(45, 45, 45)',\n border : 'rgb(80, 80, 80)',\n shadow : '2px 4px 8px rgba(0, 0, 0, 0.5)',\n },\n item : {\n hoverBackground: 'rgba(100, 140, 220, 0.2)',\n disabledColor : 'rgb(100, 100, 100)',\n shortcutColor : 'rgb(140, 140, 140)',\n },\n separatorColor: 'rgb(70, 70, 70)',\n },\n statusBar: {\n background: 'rgb(45, 45, 45)',\n color : 'rgb(200, 200, 200)',\n border : 'rgb(70, 70, 70)',\n },\n toolBar: {\n background : 'rgb(45, 45, 45)',\n border : 'rgb(70, 70, 70)',\n separatorColor: 'rgb(70, 70, 70)',\n },\n tooltip: {\n background: 'rgb(60, 60, 45)',\n color : 'rgb(220, 220, 180)',\n border : 'rgb(120, 110, 70)',\n shadow : '1px 2px 4px rgba(0, 0, 0, 0.5)',\n },\n popover: {\n background: 'rgb(50, 50, 55)',\n color : 'rgb(230, 230, 235)',\n border : 'rgb(90, 90, 100)',\n shadow : '2px 4px 12px rgba(0, 0, 0, 0.55)',\n },\n notification: {\n shadow : '2px 4px 8px rgba(0, 0, 0, 0.4)',\n info : { background: 'rgba(30, 100, 200, 0.4)', border: 'rgb(30, 100, 200)' },\n success: { background: 'rgba(30, 180, 80, 0.4)', border: 'rgb(30, 180, 80)' },\n warning: { background: 'rgba(220, 140, 0, 0.4)', border: 'rgb(220, 140, 0)' },\n error : { background: 'rgba(200, 50, 50, 0.4)', border: 'rgb(200, 50, 50)' },\n },\n validation: {\n error: {\n border : 'rgb(220, 80, 80)',\n tooltip: {\n background: 'rgb(160, 30, 30)',\n color : 'rgb(255, 220, 220)',\n border : 'rgb(120, 20, 20)',\n },\n },\n },\n autoComplete: {\n background: 'rgb(45, 45, 45)',\n border : 'rgb(80, 80, 80)',\n shadow : '2px 4px 8px rgba(0,0,0,0.5)',\n item: {\n hoverBackground : 'rgba(100, 140, 220, 0.12)',\n highlightBackground: 'rgba(100, 140, 220, 0.28)',\n highlightColor : 'rgb(220, 220, 255)',\n disabledColor : 'rgb(100, 100, 100)',\n },\n },\n // Dark defaults track `autoComplete` for the same hover/selected hues so\n // the row chrome stays consistent across the two popover surfaces.\n list: {\n background: 'rgb(40, 40, 40)',\n border : 'rgb(80, 80, 80)',\n row: {\n hoverBackground : 'rgba(100, 140, 220, 0.12)',\n selectedBackground: 'rgba(100, 140, 220, 0.28)',\n selectedColor : 'rgb(220, 220, 255)',\n focusRing : 'rgb(120, 170, 240)',\n disabledColor : 'rgb(100, 100, 100)',\n separator : 'transparent',\n },\n },\n indicator: {\n focus : 'rgb(120, 170, 240)',\n selection: '1px dashed rgb(120, 170, 240)',\n },\n // Dark picker tokens follow the dark `autoComplete.item.*` hue family for\n // chevron-hover consistency with cell hover.\n picker: {\n navForeground: 'var(--ts-ui-text-color)',\n navHoverBackground: 'rgba(120, 170, 255, 0.12)',\n cellDisabledBackground: 'transparent',\n },\n dialog: {\n backdrop: { background: 'rgba(0, 0, 0, 0.65)' },\n border : 'rgb(70, 70, 70)',\n shadow : '4px 8px 24px rgba(0, 0, 0, 0.6)',\n confirm : 'rgb(80, 200, 110)',\n cancel : 'rgb(220, 90, 90)',\n info : { background: 'rgba(30, 100, 200, 0.25)', foreground: 'rgb(60, 130, 220)' },\n affirm : { background: 'rgba(80, 200, 110, 0.25)', foreground: 'rgb(80, 200, 110)' },\n },\n drawer: {\n shadow : '4px 0 24px rgba(0, 0, 0, 0.55)',\n border : 'rgb(70, 70, 70)',\n },\n rail: {\n border : 'rgb(70, 70, 70)',\n shadow : '2px 0 12px rgba(0, 0, 0, 0.45)',\n handle: {\n hoverBackground : 'rgba(120, 170, 240, 0.12)',\n selectedBackground: 'rgba(120, 170, 240, 0.22)',\n },\n },\n spinner: {\n dividerColor: 'rgb(80, 80, 80)',\n },\n progressBar: {\n track : { background: 'rgb(55, 55, 55)' },\n fill : { background: 'rgb(60, 130, 220)' },\n indeterminate: { background: 'rgb(60, 130, 220)' },\n },\n progressSpinner: {\n color : 'rgb(60, 130, 220)',\n backdrop: 'rgba(20, 20, 20, 0.6)',\n },\n drag: {\n ghost: {\n background: 'rgba(60, 60, 60, 0.9)',\n border : 'rgb(100, 100, 100)',\n shadow : '2px 4px 12px rgba(0, 0, 0, 0.6)',\n },\n feedback: {\n valid : { background: 'rgba(30, 180, 80, 0.2)', border: 'rgb(30, 180, 80)' },\n invalid: { background: 'rgba(200, 50, 50, 0.18)', border: 'rgb(200, 50, 50)' },\n },\n reorderIndicator: {\n color: 'rgb(80, 140, 240)',\n },\n dropzone: {\n background : 'rgba(80, 140, 240, 0.12)',\n border : 'rgba(80, 140, 240, 0.45)',\n activeBackground : 'rgba(80, 140, 240, 0.32)',\n invalidBackground: 'rgba(200, 50, 50, 0.32)',\n },\n },\n fileDropZone: {\n background : 'rgba(80, 140, 240, 0.10)',\n border : '2px dashed rgba(80, 140, 240, 0.45)',\n activeBackground: 'rgba(80, 140, 240, 0.24)',\n activeBorder : '2px dashed rgba(80, 140, 240, 0.85)',\n },\n scroll: {\n shadowColor: 'rgba(0, 0, 0, 1)',\n },\n // Okabe–Ito-derived categorical palette, brightened for adequate contrast on\n // a dark background. Light-grey axis/label with a dim grid; the selection\n // ring reuses the theme's blue accent (matching the focus indicator).\n chart: {\n series: [\n '#56B4E9', // sky\n '#E69F00', // orange\n '#00C08B', // green\n '#FF7F4D', // vermilion\n '#E48FC1', // purple\n '#0091D5', // blue\n '#F0E442', // yellow\n '#AAAAAA', // grey\n ],\n axis : 'rgb(176, 176, 176)',\n grid : 'rgb(58, 58, 58)',\n label : 'rgb(192, 192, 192)',\n selection: '#4a9eff',\n },\n});\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Panel, PanelOptions } from \"~/core/Panel.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link Form}.\n *\n * @category Core\n */\nexport interface FormOptions extends PanelOptions {\n /**\n * Called after a native form submission is requested — via the submit\n * button, Enter in a field, or `requestSubmit()`. The framework has already\n * called `preventDefault()` on the submit event, so the page will not\n * navigate; the handler owns what submission means.\n */\n onSubmit?: (form: Form) => void;\n}\n\n/**\n * A {@link Panel} that bakes the semantic `<form>` tag and wires the native\n * `submit` event to a single `onSubmit` callback.\n *\n * Use `Form` as the content surface for a group of fields that should submit\n * together — it inherits `Panel`'s 4-pixel insets and `autoScroll` stack, so a\n * tall form in a short area scrolls rather than inflating to content size.\n * Trigger a submission from an external control (e.g. a footer button outside\n * the form) via {@link Form.requestSubmit}, which reaches the browser's native\n * `HTMLFormElement.requestSubmit()` — firing the cancelable `submit` event and\n * running constraint validation — through the `DOM.sink` seam, mirroring how\n * {@link Component.focus} reaches the native `focus()`.\n *\n * @example\n * ```typescript\n * const form = new Form({\n * onSubmit: (f) => console.log('submitted'),\n * });\n * // ...\n * submitButton.on(\"action\", () => form.requestSubmit());\n * ```\n *\n * @category Core\n */\nclass Form<TOptions extends FormOptions = FormOptions> extends Panel<TOptions> {\n\n private _onSubmit: ((form: Form) => void) | null = null;\n\n /**\n * @param options - Optional. Construction-time options applied to the form.\n */\n constructor(options?: TOptions) {\n super(options, { tag: \"form\" } as Partial<TOptions>);\n\n // Wire after super() returns: Event.addListener needs the fully\n // constructed component, and this is the deferred-dispatch site the\n // super-cascade trap requires (never applyOptions).\n this._onSubmit = options?.onSubmit ?? null;\n Event.addListener(this, \"submit\", this.handleSubmit);\n }\n\n /**\n * Handles the native `submit` event: prevents the browser's default\n * navigation and forwards to the `onSubmit` callback, if any.\n *\n * @param e - The native submit event.\n */\n private handleSubmit(e: SubmitEvent): void {\n e.preventDefault();\n this._onSubmit?.(this);\n }\n\n /**\n * Requests a native form submission, firing the cancelable `submit` event\n * and running the browser's constraint validation (unlike `.submit()`,\n * which skips both). No-op if the form is not yet rendered.\n */\n requestSubmit(): void {\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n DOM.sink.requestSubmit(element);\n }\n}\n\nconst FormCallable = callable(Form);\ntype FormCallable<TOptions extends FormOptions = FormOptions> = Form<TOptions>;\nexport {\n Form as _Form,\n FormCallable as Form,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM, type Handle } from \"~/core/DOM.js\";\n\n/**\n * The library's built-in mark: the framework's `Border` layout drawn literally\n * as a north bar, a west sidebar and a centre pane on a rounded plate.\n *\n * Geometry, on a 32-unit viewBox so every dimension is a whole number and each\n * unit is half a pixel in a 16px browser tab: the plate is the full 32×32 with\n * a 6-unit corner radius, and clips the three regions so they inherit its\n * rounded corners. The north bar is `0,0 32×10` (full width, as `Border` docks\n * north); the west sidebar `0,12 10×20`; the centre pane `12,12 20×20`. The\n * 2-unit gaps between them are the field showing through — the regions carry\n * no stroke, so nothing in the mark is thinner than a solid 10-unit block.\n *\n * Colours are fixed rather than read from the active theme, and the SVG carries\n * its own `prefers-color-scheme` rule so the mark suits light *and* dark\n * browser chrome without any framework wiring. Light mode is plain black on\n * white — the highest-contrast pairing, which is what holds the 1px gaps\n * together once a browser downsamples the mark to tab size. The dark palette is\n * lifted from `DarkTheme`: `#505050` is its `button.border` and `#78AAF0` its\n * `input.focusRing`, the framework's accent hue.\n *\n * Declared with single quotes because the string is full of double quotes.\n *\n * @internal Exported for the encoding round-trip test only; not part of the\n * public API (the `core` barrel does not re-export it).\n */\nexport const MARK_SVG = '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><style>.pane{fill:#FFFFFF}.region{fill:#000000}@media(prefers-color-scheme:dark){.pane{fill:#505050}.region{fill:#78AAF0}}</style><clipPath id=\"plate\"><rect width=\"32\" height=\"32\" rx=\"6\"/></clipPath><g clip-path=\"url(#plate)\"><rect class=\"pane\" width=\"32\" height=\"32\"/><rect class=\"region\" width=\"32\" height=\"10\"/><rect class=\"region\" y=\"12\" width=\"10\" height=\"20\"/><rect class=\"region\" x=\"12\" y=\"12\" width=\"20\" height=\"20\"/></g></svg>';\n\n/**\n * The library's built-in mark as a ready-to-use `data:` URI — what\n * {@link Favicon.install} writes when no other icon is supplied.\n *\n * Encoded with `encodeURIComponent` rather than a hand-written escape list.\n * Getting that list wrong fails silently: an unescaped `#` would truncate the\n * URI at `url(#plate)`, dropping the clip path and rendering a blank tab icon\n * with no error anywhere.\n *\n * @category Core\n */\nexport const DEFAULT_FAVICON = `data:image/svg+xml,${encodeURIComponent(MARK_SVG)}`;\n\n/**\n * Matches `rel=\"icon\"` and `rel=\"shortcut icon\"` — `~=` is the\n * whitespace-separated word match, so a `rel` of `\"shortcut icon\"` contains the\n * word `icon` and matches. `apple-touch-icon` and `mask-icon` are single words\n * and deliberately do not: neither sets the browser-tab icon, so a page\n * carrying only one of them still has no tab icon worth preserving.\n */\nconst ICON_LINK_SELECTOR = 'link[rel~=\"icon\"]';\n\n/**\n * Installs the browser-tab icon by appending a `<link rel=\"icon\">` to `<head>`.\n *\n * Apps do not normally call this: `Body.init` installs {@link DEFAULT_FAVICON}\n * on mount, and the `favicon` option on that call is the ordinary way to supply\n * a different one or to suppress injection. Call it directly only when mounting\n * without `Body.init`.\n *\n * An icon the document already declares always wins — see {@link Favicon.install}.\n *\n * @category Core\n */\nexport class Favicon {\n\n /**\n * The link this class injected, or `null` when it has not injected one.\n * Held so a later install swaps the `href` instead of stacking a second\n * `<link>` — the browser would honour the last one, but the page would\n * accumulate a link per call.\n */\n private static _link: Handle | null = null;\n\n /**\n * Appends a `<link rel=\"icon\">` to `<head>`, unless the document already\n * declares an icon link of its own — an app's `<link rel=\"icon\">` written\n * in its `index.html` is never replaced. Module scripts run after the\n * document is parsed, so such a link is always visible by the time this\n * runs.\n *\n * Calling it again replaces the `href` on the link it previously injected\n * rather than appending a second one.\n *\n * @param href - The icon URL or `data:` URI. Defaults to the library's\n * built-in mark.\n *\n * @returns `true` when the icon was installed, `false` when the document's\n * own icon link was left in place.\n */\n static install(href: string = DEFAULT_FAVICON): boolean {\n if (Favicon._link !== null) {\n DOM.sink.apply(Favicon._link, { setAttr: { href } });\n\n return true;\n }\n\n const head = DOM.source.getHead();\n\n if (DOM.source.querySelector(head, ICON_LINK_SELECTOR) !== null) {\n return false;\n }\n\n const link = DOM.sink.createElement(\"link\");\n\n DOM.sink.apply(link, { setAttr: { rel: \"icon\", href } });\n DOM.sink.appendChild(head, link);\n\n Favicon._link = link;\n\n return true;\n }\n\n /**\n * Forgets the injected link, so the next {@link Favicon.install} appends a\n * fresh one instead of writing through the handle it is holding.\n *\n * @internal Test-only. A handle minted before `DOM.reset()` does not\n * resolve against the registry that replaces it, so a suite that resets the\n * DOM between cases must reset this too.\n */\n static _reset(): void {\n Favicon._link = null;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { Favicon, DEFAULT_FAVICON } from \"~/core/Favicon.js\";\nimport { ThemeManager, ModernTheme } from \"~/core/Theme.js\";\n\n/**\n * Options for the singleton {@link Body}.\n *\n * @category Core\n */\nexport interface BodyOptions extends ComponentOptions {\n /**\n * Browser-tab icon. A URL or `data:` URI installs that icon; `false`\n * suppresses injection entirely. Omitted, the library's built-in mark is\n * used. In every case a `<link rel=\"icon\">` already present in the page's\n * HTML wins and nothing is injected.\n */\n favicon?: string | false;\n\n /**\n * Whether the browser's own right-click menu is allowed. Defaults to\n * `false` — `Body.init` suppresses the native menu page-wide, so only\n * menus the library or the app opens on `contextmenu` ever appear.\n * `true` restores the browser's menu everywhere.\n */\n nativeContextMenu?: boolean;\n}\n\n/**\n * A {@link Component} that wraps the page's `<body>` element.\n *\n * Mount a top-level layout in one call:\n * ```\n * Body.init({ layoutManager: Fit(), components: [shell] });\n * ```\n *\n * Once mounted, reach the singleton again with `Body.getInstance()` — to add a\n * further child, read the layout manager, or attach a listener.\n *\n * @category Core\n */\nexport class Body extends Component<BodyOptions> {\n\n private static readonly INSTANCE: Body = new Body();\n\n /**\n * Returns the singleton Body instance — the accessor for reaching the body\n * *after* it is mounted: adding a further child, reading its layout\n * manager, or attaching a listener. To mount a top-level layout in one\n * call, use `Body.init` instead.\n *\n * @returns The single shared Body component for this page.\n */\n static getInstance() {\n return this.INSTANCE;\n }\n\n /**\n * Applies an options bag to the singleton Body and returns it — the\n * ergonomic entry point for mounting a top-level layout in one call:\n * `Body.init({ layoutManager: Fit(), components: [shell] })`. Only the\n * supplied fields are dispatched (it delegates to\n * `Component.applyOptions`), so the body's viewport-size tracking and\n * theme set up at construction are preserved.\n *\n * Also installs the browser-tab icon, unless the page already declares a\n * `<link rel=\"icon\">` of its own or `options.favicon` is `false`, and\n * suppresses the browser's native right-click menu page-wide, unless\n * `options.nativeContextMenu` is `true`.\n *\n * @param options - Component options to apply (layout manager, children, …).\n *\n * @returns The singleton Body instance, for chaining.\n *\n * @remarks Re-binds the style and attribute buffers to the current body\n * element before dispatching `options` — see\n * `Component.reattachElementBuffers`. The singleton is constructed once\n * per page load, so this only matters when the underlying DOM has been\n * swapped since construction (a test harness); it is a no-op rebind\n * otherwise.\n */\n static init(options: BodyOptions = {}): Body {\n this.INSTANCE.reattachElementBuffers();\n this.INSTANCE.applyOptions(options);\n\n // The built-in default is dispatched here rather than from\n // applyOptions, which also runs during the singleton's construction at\n // module import — too early for a caller to have opted out, and before\n // a test harness has swapped the DOM seams.\n if (options.favicon === undefined) {\n this.INSTANCE.setFavicon(DEFAULT_FAVICON);\n }\n\n // Same reasoning as the favicon default above: dispatched here rather\n // than from applyOptions, which also runs during the singleton's\n // construction at module import — too early for a caller to have\n // opted out, and before a test harness has swapped the DOM seams.\n if (options.nativeContextMenu === undefined) {\n this.INSTANCE.setNativeContextMenu(false);\n }\n\n return this.INSTANCE;\n }\n\n /** @inheritDoc */\n protected applyOptions(options: BodyOptions): this {\n super.applyOptions(options);\n\n if (options.favicon !== undefined) this.setFavicon(options.favicon);\n if (options.nativeContextMenu !== undefined) this.setNativeContextMenu(options.nativeContextMenu);\n\n return this;\n }\n\n /**\n * Installs the browser-tab icon, or suppresses it.\n *\n * A `<link rel=\"icon\">` the page's own HTML declares always wins, so this\n * writes nothing when one is present. `false` suppresses injection; it does\n * not remove an icon already installed.\n *\n * @param favicon - The icon URL or `data:` URI, or `false` for none.\n *\n * @returns This component, for method chaining.\n */\n setFavicon(favicon: string | false): this {\n this._options.favicon = favicon;\n\n if (favicon !== false) {\n Favicon.install(favicon);\n }\n\n return this;\n }\n\n /**\n * Returns the browser-tab icon this body was configured with.\n *\n * @returns The configured URL or `data:` URI, `false` when injection is\n * suppressed, or the library's built-in mark when nothing was configured.\n */\n getFavicon(): string | false {\n return this._options.favicon ?? DEFAULT_FAVICON;\n }\n\n /**\n * Allows or suppresses the browser's native right-click menu page-wide.\n *\n * @param allowed - `true` lets the browser's own menu open on\n * `contextmenu`; `false` suppresses it everywhere.\n *\n * @returns This component, for method chaining.\n */\n setNativeContextMenu(allowed: boolean): this {\n // The listener is registered exactly when the option reads `false`;\n // `undefined` means \"not configured yet\", so nothing is registered.\n const listening = this._options.nativeContextMenu === false;\n\n this._options.nativeContextMenu = allowed;\n\n if (!allowed && !listening) {\n Event.addViewportListener(this, \"contextmenu\", this.onContextMenu);\n } else if (allowed && listening) {\n Event.removeViewportListener(this, \"contextmenu\", this.onContextMenu);\n }\n\n return this;\n }\n\n /**\n * Returns whether the browser's native right-click menu is allowed.\n *\n * @returns `true` when the native menu is allowed, `false` when it is\n * suppressed (the default).\n */\n getNativeContextMenu(): boolean {\n return this._options.nativeContextMenu ?? false;\n }\n\n private constructor() {\n super({ tag: \"body\" });\n\n this.init();\n\n this.setBackgroundColor(\"var(--ts-ui-body-bg, rgb(241, 241, 241))\");\n\n ThemeManager.setTheme(ModernTheme);\n }\n\n /**\n * Returns the document body element.\n *\n * @returns The `<body>` HTMLElement.\n */\n getElement() {\n return DOM.source.getBody();\n }\n\n /**\n * Initializes the body size from the viewport and registers a resize listener to keep it in sync.\n *\n * @returns This component, for method chaining.\n */\n protected init(): this {\n super.init();\n\n this.setSize(DOM.source.getViewportSize());\n this.clearInsets();\n\n Event.addViewportListener(this, \"resize\", this._onViewportResize);\n this.subscribeTheme(this._onThemeReflow);\n\n return this;\n }\n\n /**\n * Bound viewport-resize handler. Reads the current viewport extent on\n * each fire (DOM `resize` events carry no payload) and writes it\n * through the typed setter.\n */\n private _onViewportResize = (): void => {\n this.setSize(DOM.source.getViewportSize());\n };\n\n /**\n * Viewport `contextmenu` handler registered by {@link setNativeContextMenu}\n * while the native menu is suppressed. Returns a disposition rather than\n * calling `preventDefault()` itself, and deliberately never returns a stop\n * disposition: a `contextmenu` event is also routed to any component's\n * own handler (e.g. `Tree`, `DiagramView`) through `Event`'s other\n * window-capture listener, and this handler must not cut that walk short.\n * A plain prototype method, not an arrow field, so it exists before the\n * constructor runs and has a stable identity `removeViewportListener` can\n * find (see `Markdown.handleViewportChange` for the same reasoning).\n *\n * @returns `{ prevent: true }`, suppressing the browser's own menu.\n */\n private onContextMenu(): Event.ListenerResult {\n return { prevent: true };\n }\n\n /**\n * Bound theme-change handler. Schedules a layout pass so every `Text` in\n * the page re-measures lazily against the new theme's metrics — replaces\n * the per-`Text` theme subscription this component's children used to\n * hold individually.\n */\n private _onThemeReflow = (): void => {\n this.scheduleLayout();\n };\n\n /**\n * Overrides {@link Component.clampsToContentSize} to `false`: the body *is*\n * the viewport, so its size must track {@link Util.getViewportSize} exactly\n * and let oversized content overflow (the page scrolls / a child `Panel`\n * scrolls internally) rather than inflating the body up to its\n * content-derived minimum. With the `Component` default (`true`),\n * {@link Component.setSize} ran the viewport extent through\n * {@link Component.clampHeight}, which floored it to `getMinSize()` — the\n * tall content's minimum — so once that minimum was measured the body could\n * never shrink back below its content when the viewport shrank.\n *\n * @returns `false`, so size clamping uses the body's own explicit\n * constraints only, not its content-derived ones.\n */\n protected clampsToContentSize(): boolean {\n return false;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { LayerManager } from \"~/core/LayerManager.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\n\n/**\n * A physical-key chord. `code` is a `KeyboardEvent.code` value (layout-independent).\n *\n * @category Core\n */\nexport interface FocusHistoryKeyCombo {\n code: string;\n ctrl?: boolean;\n alt?: boolean;\n shift?: boolean;\n meta?: boolean;\n}\n\n/**\n * Options for {@link FocusHistory.enable} / {@link FocusHistory.configure}.\n *\n * @category Core\n */\nexport interface FocusHistoryOptions {\n /** Max entries retained; oldest dropped past this. Default 50. */\n maxSize?: number;\n /** Back accelerator. Default `Alt+[` (`{ code: \"BracketLeft\", alt: true }`). */\n back?: FocusHistoryKeyCombo;\n /** Forward accelerator. Default `Alt+]` (`{ code: \"BracketRight\", alt: true }`). */\n forward?: FocusHistoryKeyCombo;\n}\n\n/**\n * The sole custom event name fired by {@link FocusHistory}.\n *\n * @category Core\n */\nexport type FocusHistoryEvent = \"change\";\n\n/**\n * Payload of the `\"change\"` event: the navigability of the trail after the change.\n *\n * @category Core\n */\nexport interface FocusHistoryChange {\n canGoBack: boolean;\n canGoForward: boolean;\n}\n\n// 50: a working session's focus trail rarely exceeds a few dozen stops; caps\n// memory at 50 handle numbers while leaving generous back-depth.\nconst DEFAULT_MAX_ENTRIES: number = 50;\n// Alt+[ / Alt+]: editor-style \"navigate back/forward\"; no default browser\n// action (unlike Alt+Left/Right = browser history); layout-independent via code.\nconst DEFAULT_BACK_COMBO: FocusHistoryKeyCombo = { code: \"BracketLeft\", alt: true };\nconst DEFAULT_FORWARD_COMBO: FocusHistoryKeyCombo = { code: \"BracketRight\", alt: true };\n\n// Sentinel used to register the service's viewport listeners — see\n// LayerManager's identical `_listenerOwner` pattern. `FocusHistory` has no DOM\n// element of its own, so a stable, otherwise-unused `Component` owns them.\nconst _owner: Component = new Component();\nconst _listeners = new ListenerBag<FocusHistoryEvent>();\n\n// Chronological trail, oldest first. Entries are bare `Handle`s (not\n// `Component`s) because focus frequently lands on an element with no 1:1\n// component (an input inside a Text, a raw button).\nlet _entries: Handle[] = [];\n// Current position in `_entries`; -1 when the trail is empty.\nlet _index: number = -1;\nlet _enabled: boolean = false;\n// Re-entrancy guard: true for the duration of a service-driven `DOM.sink.focus`\n// call, so the synchronous `focusin` it triggers is not recorded as a new move.\nlet _navigating: boolean = false;\nlet _maxSize: number = DEFAULT_MAX_ENTRIES;\nlet _back: FocusHistoryKeyCombo = DEFAULT_BACK_COMBO;\nlet _forward: FocusHistoryKeyCombo = DEFAULT_FORWARD_COMBO;\n\n/**\n * Whether `handle` still resolves to a connected element. A GC-collected weak\n * handle throws on resolve inside the DOM seam; that is treated as stale too.\n */\nfunction isLive(handle: Handle): boolean {\n try {\n return DOM.source.isConnected(handle);\n } catch {\n return false;\n }\n}\n\n/**\n * Drops every stale (no-longer-connected) entry from the trail, keeping\n * `_index` pointed at the nearest surviving entry at or before its old\n * position (or -1 if nothing survived there). Run before every navigation\n * and every `canGo*` query so those never act on a dead entry.\n */\nfunction pruneStale(): void {\n const kept: Handle[] = [];\n let newIndex = -1;\n\n for (let i = 0; i < _entries.length; i++) {\n const handle = _entries[i];\n\n if (isLive(handle)) {\n kept.push(handle);\n\n if (i <= _index) {\n newIndex = kept.length - 1;\n }\n }\n }\n\n _entries = kept;\n _index = newIndex;\n}\n\n/** Computes the current navigability and notifies `\"change\"` listeners. */\nfunction fireChange(): void {\n const change: FocusHistoryChange = {\n canGoBack: _index > 0,\n canGoForward: _index < _entries.length - 1,\n };\n\n _listeners.fire(\"change\", change);\n}\n\n/**\n * Appends `handle` to the trail (deduping a repeat of the current entry and\n * truncating any forward branch), advances `_index`, and fires `\"change\"`.\n */\nfunction record(handle: Handle): void {\n if (_index >= 0 && _entries[_index] === handle) {\n return;\n }\n\n _entries.length = _index + 1;\n _entries.push(handle);\n\n while (_entries.length > _maxSize) {\n _entries.shift();\n }\n\n _index = _entries.length - 1;\n fireChange();\n}\n\n/** Re-focuses `handle` under the re-entrancy guard, then fires `\"change\"`. */\nfunction focusEntry(handle: Handle): void {\n _navigating = true;\n DOM.sink.focus(handle);\n _navigating = false;\n fireChange();\n}\n\n/**\n * Prunes stale entries, then moves `_index` by `direction` and re-focuses the\n * entry there, if one exists.\n *\n * @returns True if focus moved.\n */\nfunction navigate(direction: -1 | 1): boolean {\n pruneStale();\n\n const target = _index + direction;\n\n if (target < 0 || target >= _entries.length) {\n return false;\n }\n\n _index = target;\n focusEntry(_entries[_index]);\n\n return true;\n}\n\n/** Whether a `KeyboardEvent` matches a configured combo. */\nfunction matchesCombo(e: KeyboardEvent, combo: FocusHistoryKeyCombo): boolean {\n return e.code === combo.code &&\n e.altKey === !!combo.alt && e.ctrlKey === !!combo.ctrl &&\n e.shiftKey === !!combo.shift && e.metaKey === !!combo.meta;\n}\n\n/** Document `focusin` handler: records the newly-focused element. */\nfunction onFocusIn(e: FocusEvent): void {\n if (!_enabled || _navigating) {\n return;\n }\n\n if (!DOM.source.isElement(e.target)) {\n return;\n }\n\n record(DOM.source.intern(e.target));\n}\n\n/**\n * Document `keydown` handler: drives `back()`/`forward()` on the configured\n * combos, suppressed while a modal layer is on top so the accelerator does\n * not fight the modal's own focus trap.\n *\n * @returns `{ stop: true, prevent: true }` when the key matches a navigation combo\n * and no modal layer is suppressing it; nothing otherwise, so every other key\n * keeps propagating.\n */\nfunction onKeyDown(e: KeyboardEvent): Event.ListenerResult {\n if (!_enabled) {\n return;\n }\n\n const direction: -1 | 1 | 0 = matchesCombo(e, _back) ? -1 : matchesCombo(e, _forward) ? 1 : 0;\n\n if (direction === 0) {\n return;\n }\n\n const top = LayerManager.getTopLayer();\n\n if (top && top.getDismissMode() === \"modal\") {\n return;\n }\n\n navigate(direction);\n\n return { stop: true, prevent: true };\n}\n\n/**\n * Global focus-trail navigation service: records the chronological order in\n * which elements receive keyboard focus across the document and drives an\n * accelerator that walks the trail backward and forward, like browser\n * back/forward but for keyboard focus. Opt-in — call {@link enable} to start\n * recording; nothing auto-starts it.\n *\n * @category Core\n */\nexport namespace FocusHistory {\n /**\n * Installs the `focusin` + `keydown` listeners, seeds the trail with the\n * current active element (if any), and begins recording. Idempotent —\n * calling it again while already enabled only applies `options`.\n *\n * @param options - Optional `maxSize` / combo overrides, applied via\n * {@link configure} before enabling.\n */\n export function enable(options?: FocusHistoryOptions): void {\n if (options) {\n configure(options);\n }\n\n if (_enabled) {\n return;\n }\n\n _enabled = true;\n\n Event.addViewportListener(_owner, \"focusin\", onFocusIn);\n Event.addViewportListener(_owner, \"keydown\", onKeyDown);\n\n const active = DOM.source.getActiveElement();\n\n if (active !== null) {\n record(active);\n }\n }\n\n /**\n * Removes the listeners and stops recording. Preserves the trail —\n * re-enabling resumes from where it left off. Idempotent.\n */\n export function disable(): void {\n if (!_enabled) {\n return;\n }\n\n _enabled = false;\n _navigating = false;\n\n Event.removeViewportListener(_owner, \"focusin\", onFocusIn);\n Event.removeViewportListener(_owner, \"keydown\", onKeyDown);\n }\n\n /** Whether the service is currently observing. */\n export function isEnabled(): boolean {\n return _enabled;\n }\n\n /**\n * Updates `maxSize` / combos without toggling enablement. Only the\n * supplied fields change.\n *\n * @param options - The fields to update.\n */\n export function configure(options: FocusHistoryOptions): void {\n if (options.maxSize !== undefined) {\n _maxSize = options.maxSize;\n }\n\n if (options.back !== undefined) {\n _back = options.back;\n }\n\n if (options.forward !== undefined) {\n _forward = options.forward;\n }\n }\n\n /**\n * Re-focuses the previous live entry, skipping and dropping any stale\n * ones encountered along the way.\n *\n * @returns True if focus moved.\n */\n export function back(): boolean {\n return navigate(-1);\n }\n\n /**\n * Re-focuses the next live entry, skipping and dropping any stale ones\n * encountered along the way.\n *\n * @returns True if focus moved.\n */\n export function forward(): boolean {\n return navigate(1);\n }\n\n /**\n * Whether a live entry exists before the current position. Prunes stale\n * entries as a side effect, so a stale-only direction never reports true.\n */\n export function canGoBack(): boolean {\n pruneStale();\n\n return _index > 0;\n }\n\n /**\n * Whether a live entry exists after the current position. Prunes stale\n * entries as a side effect, so a stale-only direction never reports true.\n */\n export function canGoForward(): boolean {\n pruneStale();\n\n return _index < _entries.length - 1;\n }\n\n /** Empties the trail and fires `\"change\"` with both flags false. */\n export function clear(): void {\n _entries = [];\n _index = -1;\n fireChange();\n }\n\n /**\n * Subscribes to trail-navigability changes, fired after every recorded\n * focus move and after every `back()` / `forward()` navigation.\n *\n * @param event - Always `\"change\"`.\n * @param listener - Invoked with the trail's navigability after the change.\n */\n export function on(event: \"change\", listener: (change: FocusHistoryChange) => void): void {\n _listeners.add(event, listener);\n }\n\n /**\n * Unsubscribes a `\"change\"` listener.\n *\n * @param event - Always `\"change\"`.\n * @param listener - The exact callback reference passed to {@link on}.\n */\n export function off(event: \"change\", listener: (change: FocusHistoryChange) => void): void {\n _listeners.remove(event, listener);\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BaseObject } from '~/core/BaseObject.js';\nimport { ModelRecord } from '~/data/ModelRecord.js';\nimport { Bindable, BindingAccessors } from '~/core/Bindable.js';\nimport { Component } from '~/core/Component.js';\nimport { ListenerBag } from '~/core/ListenerBag.js';\nimport { ValidationRule, FieldValidationConfig } from '~/validation/ValidationRule.js';\nimport { FieldDecorator } from '~/validation/FieldDecorator.js';\nimport { applyRule } from '~/validation/Validator.js';\n\n/**\n * String-literal union of events emitted by {@link Binding}.\n *\n * @category Data\n */\nexport type BindingEvent = \"change\" | \"commit\" | \"reject\" | \"beforerecord\";\n\ninterface BoundEntry {\n accessors: BindingAccessors<any>;\n active: boolean;\n}\n\n/**\n * Listener consulted before a record change takes effect.\n *\n * Receives the *next* record (or `null`) and returns `false` to cancel the\n * change. Any other return value (`true`, `undefined`, …) allows it.\n *\n * @category Data\n */\nexport type BeforeRecordListener = (next: ModelRecord | null) => boolean;\n\n/**\n * Synchronises a {@link ModelRecord} with a set of UI components.\n *\n * Components are registered with {@link bind}, either as {@link Bindable} implementors\n * (short form) or via explicit accessor callbacks (long form). Once a record is loaded\n * via {@link setRecord}, every bound component is populated from the record's fields.\n * Conversely, whenever a component's value changes, the record is updated automatically.\n *\n * @example Short form — component implements Bindable\n * ```typescript\n * const binding = new Binding()\n * .bind('name', nameField)\n * .bind('active', activeCheckbox)\n * .bind('role', roleCombo);\n *\n * binding.setRecord(record);\n * ```\n *\n * @example Long form — explicit accessors for any component\n * ```typescript\n * const binding = new Binding()\n * .bind('name', myWidget, {\n * get: () => myWidget.getValue(),\n * set: (v) => myWidget.setValue(v),\n * listen: (fn) => myWidget.on(\"change\", fn),\n * });\n * ```\n *\n * @category Data\n */\nexport class Binding extends BaseObject {\n\n private _record: ModelRecord | null = null;\n private _entries: Map<string, BoundEntry> = new Map();\n private _listeners: ListenerBag<BindingEvent> = new ListenerBag<BindingEvent>();\n private _validationConfigs: Map<string, FieldValidationConfig> = new Map();\n private _globalValidateOnChange: boolean = false;\n private _loading: boolean = false;\n\n // ── Registration ────────────────────────────────────────────────────────\n\n /**\n * Registers a field binding using a component that implements {@link Bindable}.\n *\n * @param fieldName - The record field to bind to.\n * @param component - A component implementing the {@link Bindable} interface.\n */\n bind<T>(fieldName: string, component: Bindable<T>): this;\n\n /**\n * Registers a field binding using explicit accessor callbacks.\n * Use this overload for components that do not implement {@link Bindable}.\n *\n * @param fieldName - The record field to bind to.\n * @param component - Any object (used only as a placeholder for the overload).\n * @param accessors - Getter, setter, and change-listener callbacks.\n */\n bind<T>(fieldName: string, component: object, accessors: BindingAccessors<T>): this;\n\n bind<T>(fieldName: string, component: Bindable<T> | object, accessors?: BindingAccessors<T>): this {\n this.unbind(fieldName);\n\n const acc: BindingAccessors<any> = accessors ?? {\n get: () => (component as Bindable<T>).getValue(),\n set: (v: T) => (component as Bindable<T>).setValue(v),\n listen: (fn) => (component as Bindable<T>).on(\"binding\", fn),\n };\n\n const entry: BoundEntry = { accessors: acc, active: true };\n this._entries.set(fieldName, entry);\n\n acc.listen(() => {\n if (this._loading) {\n return;\n }\n\n if (!entry.active || !this._record) {\n return;\n }\n\n const value = acc.get();\n this._record.set(fieldName, value);\n\n this.emit(\"change\", fieldName, value);\n\n this._validateFieldIfLive(fieldName);\n });\n\n if (this._record) {\n acc.set(this._record.get(fieldName));\n }\n\n return this;\n }\n\n /**\n * Removes the binding for the given field.\n * The change listener previously attached to the component becomes a no-op.\n *\n * @param fieldName - The field whose binding should be removed.\n */\n unbind(fieldName: string): this {\n const entry = this._entries.get(fieldName);\n if (entry) {\n entry.active = false;\n\n this._entries.delete(fieldName);\n }\n\n return this;\n }\n\n // ── Record management ────────────────────────────────────────────────────\n\n /**\n * Loads a record into the binding. All registered components are immediately\n * populated with the corresponding field values. Pass `null` to clear the binding.\n *\n * Before any state mutation, every `on(\"beforerecord\", fn)` listener is\n * consulted; if any returns `false` the\n * call is a complete no-op (no validation reset, no field population) and\n * `this` is returned unchanged.\n *\n * @param record - The record to bind, or `null` to detach.\n */\n setRecord(record: ModelRecord | null): this {\n for (const fn of this._listeners.get(\"beforerecord\") as BeforeRecordListener[]) {\n if (fn(record) === false) {\n return this;\n }\n }\n\n this._record = record;\n\n this.clearValidation();\n\n if (!record) {\n return this;\n }\n\n this._loading = true;\n\n try {\n for (const [fieldName, entry] of this._entries) {\n entry.accessors.set(record.get(fieldName));\n }\n } finally {\n this._loading = false;\n }\n\n for (const [fieldName] of this._validationConfigs) {\n this._validateFieldIfLive(fieldName);\n }\n\n return this;\n }\n\n /**\n * Returns the currently bound record, or `null` if none is loaded.\n */\n getRecord(): ModelRecord | null {\n return this._record;\n }\n\n // ── Commit / reject ──────────────────────────────────────────────────────\n\n /**\n * Commits the current record, clearing its dirty and new flags.\n * Fires all registered commit listeners.\n */\n commit(): this {\n this._record?.commit();\n\n this.emit(\"commit\");\n\n return this;\n }\n\n /**\n * Rejects all changes on the current record and re-syncs every component\n * from the reverted field values. Fires all registered reject listeners.\n * Also clears any active validation error decorations.\n */\n reject(): void {\n this._record?.reject();\n\n if (this._record) {\n for (const [fieldName, entry] of this._entries) {\n entry.accessors.set(this._record.get(fieldName));\n }\n }\n\n this.clearValidation();\n\n this.emit(\"reject\");\n }\n\n // ── Listeners ────────────────────────────────────────────────────────────\n\n /**\n * Registers a listener for one of this binding's events.\n *\n * @param event - `\"change\"` fires whenever any bound component changes a\n * field value, receiving `(fieldName, value)`. `\"commit\"` fires after\n * {@link commit}. `\"reject\"` fires after {@link reject}.\n * `\"beforerecord\"` is consulted before {@link setRecord} mutates state;\n * returning `false` vetoes the call as a complete no-op.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This binding, for method chaining.\n *\n * @remarks `\"beforerecord\"` veto semantics: iteration stops on the first\n * `false`; if any listener vetoes, {@link setRecord} returns without\n * modifying any state. Returning `true` — or anything other than\n * `false`, including `undefined` — allows the change. Async\n * confirmation must be handled at the call site — {@link setRecord}\n * stays synchronous. A listener that itself calls {@link setRecord}\n * re-enters the same veto loop, which is supported but discouraged.\n * `null` is a valid `next` value (it represents a clear); a listener\n * that only wants to guard non-clear switches must short-circuit\n * `next === null` itself.\n */\n on(event: \"change\", listener: (fieldName: string, value: unknown) => void): this;\n on(event: \"commit\", listener: () => void): this;\n on(event: \"reject\", listener: () => void): this;\n on(event: \"beforerecord\", listener: BeforeRecordListener): this;\n on(event: BindingEvent, 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 binding, for method chaining.\n */\n off(event: BindingEvent, 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. Internal use only — external callers route\n * dispatch through {@link commit} / {@link reject} / {@link setRecord}.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"change\", fieldName: string, value: unknown): void;\n protected emit(event: \"commit\"): void;\n protected emit(event: \"reject\"): void;\n protected emit(event: BindingEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n // ── Validation ───────────────────────────────────────────────────────────\n\n /**\n * Attaches one or more validation rules to a bound field.\n *\n * @param fieldName - The field name registered with {@link bind}.\n * @param component - The Component instance to wrap with a {@link FieldDecorator} on error.\n * @param rules - One or more {@link ValidationRule} objects to apply to this field.\n *\n * @returns this, for chaining.\n */\n addValidation(fieldName: string, component: Component, rules: ValidationRule | ValidationRule[]): this {\n const ruleArray = Array.isArray(rules) ? rules : [rules];\n\n this._validationConfigs.set(fieldName, {\n rules : ruleArray,\n component,\n validateOnChange: false,\n decorator : null,\n });\n\n return this;\n }\n\n /**\n * Removes all validation rules for a field and clears any existing error decoration.\n *\n * @param fieldName - The field whose validation should be removed.\n *\n * @returns this, for chaining.\n */\n removeValidation(fieldName: string): this {\n const config = this._validationConfigs.get(fieldName);\n\n if (config?.decorator) {\n config.decorator.clearError();\n }\n\n this._validationConfigs.delete(fieldName);\n\n return this;\n }\n\n /**\n * Runs all registered validation rules against the current field values.\n * Applies or clears {@link FieldDecorator} error state for each validated field.\n *\n * @returns true if all fields pass; false if any field fails.\n */\n validate(): boolean {\n let allValid = true;\n\n for (const [fieldName] of this._validationConfigs) {\n const valid = this._validateField(fieldName);\n\n if (!valid) {\n allValid = false;\n }\n }\n\n return allValid;\n }\n\n /**\n * Enables or disables live validation (validation on every field change).\n *\n * @param enabled - true to validate on every change event; false for explicit-only.\n */\n setValidateOnChange(enabled: boolean): this {\n this._globalValidateOnChange = enabled;\n\n return this;\n }\n\n /**\n * Returns whether live validation is globally enabled.\n *\n * @returns true if live validation is active.\n */\n getValidateOnChange(): boolean {\n return this._globalValidateOnChange;\n }\n\n /**\n * Clears all error decorations without re-running rules.\n * Called automatically by {@link reject}.\n */\n clearValidation(): this {\n for (const [, config] of this._validationConfigs) {\n if (config.decorator) {\n config.decorator.clearError();\n }\n }\n\n return this;\n }\n\n /**\n * Validates a single field and updates its decorator.\n *\n * @param fieldName - The field to validate.\n *\n * @returns true if the field passes all rules.\n */\n private _validateField(fieldName: string): boolean {\n const config = this._validationConfigs.get(fieldName);\n\n if (!config) {\n return true;\n }\n\n const entry = this._entries.get(fieldName);\n const value = entry ? entry.accessors.get() : undefined;\n\n for (const rule of config.rules) {\n const result = applyRule(rule, value);\n\n if (result.valid) {\n continue;\n }\n\n if (!config.decorator) {\n const parent = config.component.getParentComponent();\n\n if (parent) {\n config.decorator = new FieldDecorator(config.component, parent);\n }\n }\n\n config.decorator?.showError(result.message);\n\n return false;\n }\n\n config.decorator?.clearError();\n\n return true;\n }\n\n /**\n * Calls {@link _validateField} only if live validation is active for this field.\n *\n * @param fieldName - The field to conditionally validate.\n */\n private _validateFieldIfLive(fieldName: string): void {\n const config = this._validationConfigs.get(fieldName);\n\n if (!config) {\n return;\n }\n\n const live = config.validateOnChange || this._globalValidateOnChange;\n\n if (live) {\n this._validateField(fieldName);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"primitive.es.js","names":[],"sources":["../../src/typescript/lib/primitive/Point.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BaseObject } from \"~/core/BaseObject.js\";\n\n/**\n * Represents a two-dimensional point with x and y coordinates.\n *\n * @category Util\n */\nexport class Point extends BaseObject {\n private _x: number;\n private _y: number;\n\n /**\n * @param x - The x coordinate.\n * @param y - The y coordinate.\n */\n constructor(x: number, y: number) {\n super();\n\n this._x = x ?? 0;\n this._y = y ?? 0;\n\n }\n\n /**\n * Returns the x coordinate.\n *\n * @returns The x coordinate value, defaulting to `0` if unset.\n */\n getX() {\n return this._x ?? 0;\n }\n\n /**\n * Returns the y coordinate.\n *\n * @returns The y coordinate value, defaulting to `0` if unset.\n */\n getY() {\n return this._y ?? 0;\n }\n\n /**\n * Returns the point as a space-separated `\"x y\"` string.\n *\n * @returns A string of the form `\"<x> <y>\"`.\n */\n render() {\n return this.getX() + \" \" + this.getY();\n }\n}\n"],"mappings":"wIASA,IAAa,MAAb,cAA2B,CAAW,CAClC,GACA,GAMA,YAAY,EAAW,EAAW,CAC9B,MAAM,EAEN,KAAK,GAAK,GAAK,EACf,KAAK,GAAK,GAAK,CAEnB,CAOA,MAAO,CACH,OAAO,KAAK,IAAM,CACtB,CAOA,MAAO,CACH,OAAO,KAAK,IAAM,CACtB,CAOA,QAAS,CACL,OAAO,KAAK,KAAK,EAAI,IAAM,KAAK,KAAK,CACzC,CACJ"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./DOM-Cjh7_7TF.js";import{t,u as n}from"./Component-D99-15Zt.js";import{t as r}from"./VirtualScroller-ggPTCQb0.js";var i=2;function rotateLeft(e,t){e.push(...e.splice(0,t))}var VirtualRowView=class extends t{_rowPool=[];_boundIndices=[];_rowGeom=[];_rowDisplayed=[];_scroller=null;_lastWindowStart=null;_renderDeferred=!1;_renderResumed=!1;_pendingScrollX=null;_pendingScrollY=null;_pendingScrollRow=null;initScroller(e){this._scroller=new r(this,e,()=>this.onScrollerTick());for(let e of this._scroller.ownedHandles())this.trackHandle(e)}onScrollerTick(){this.renderWindow()}destructor(){for(let e of this._rowPool)e.dispose();this._scroller?.dispose(),super.destructor()}setScrollX(e){return this._scroller?.resetWheelEase(),this.holdScrollWhileFirstLayoutHeld(`x`,e)||this._scroller?.setScrollX(e),this}setScrollY(e){return this._scroller?.resetWheelEase(),this.holdScrollWhileFirstLayoutHeld(`y`,e)||this._scroller?.setScrollY(e),this}holdScrollWhileFirstLayoutHeld(e,t){return n()?(e===`x`?this._pendingScrollX=t:(this._pendingScrollY=t,this._pendingScrollRow=null),!0):(e===`x`?this._pendingScrollX=null:(this._pendingScrollY=null,this._pendingScrollRow=null),!1)}applyPendingScroll(){let e=this._pendingScrollX,t=this._pendingScrollY,n=this._pendingScrollRow;this._pendingScrollX=null,this._pendingScrollY=null,this._pendingScrollRow=null,e!==null&&this.setScrollX(e),t!==null&&this.setScrollY(t),n!==null&&this.scrollRowIntoView(n)}computeVisibleWindow(e,t,n){let r=this.getRowHeight(),a=Math.max(0,Math.floor(e/r)-i),o=Math.min(n-1,Math.ceil((e+t)/r)+i);return{firstRow:a,lastRow:o,windowSize:o-a+1>0?o-a+1:0}}computePoolTarget(e,t,n){return Math.min(n,Math.max(e,Math.ceil(t/this.getRowHeight())+4+2))}computePageSize(){let e=this.getRowHeight();return Math.max(1,Math.floor((this.getHeight()||e)/e))}growRowPool(t){if(!this._scroller||this._rowPool.length>=t)return;let n=this._scroller.getRowsContainer(),r=e.sink.createDocumentFragment();for(;this._rowPool.length<t;){let t=this.createPoolRow(),n=t.getElement(!0);e.sink.appendChild(r,n),t.setY(0),t.setWillChange(`transform`),this._rowPool.push(t),this._boundIndices.push(-1),this._rowGeom.push(null),this._rowDisplayed.push(!1)}e.sink.appendChild(n,r),e.sink.release(r)}alignPoolWindow(e){let t=this._lastWindowStart===null?0:e-this._lastWindowStart;this._lastWindowStart=e;let n=this._rowPool.length;if(t===0||n===0)return;let r=(t%n+n)%n;rotateLeft(this._rowPool,r),rotateLeft(this._boundIndices,r),rotateLeft(this._rowGeom,r),rotateLeft(this._rowDisplayed,r)}positionRow(e,t,n){let r=this._rowPool[e],i=this.getRowHeight(),a=this._rowGeom[e],o=!a||a.ty!==t||a.w!==n||a.h!==i;return o&&(r.setAutoCommitStyle(!1),r.setX(0),r.setTranslate(0,t),r.setWidth(n),r.setHeight(i),r.setAutoCommitStyle(!0),this._rowGeom[e]={ty:t,w:n,h:i}),this._rowDisplayed[e]||(r.setDisplayed(!0),this._rowDisplayed[e]=!0),o}hideExcessPoolRows(e){for(let t=e;t<this._rowPool.length;t++)this._rowDisplayed[t]&&(this._rowPool[t].setDisplayed(!1),this._rowDisplayed[t]=!1),this._boundIndices[t]=-1,this._rowGeom[t]=null}invalidateGeom(){for(let e=0;e<this._rowGeom.length;e++)this._rowGeom[e]=null}deferRenderWhileFirstLayoutHeld(){return n()?(this._renderDeferred=!0,this.scheduleLayout(),!0):(this._renderResumed=this._renderDeferred,this._renderDeferred=!1,!1)}wasRenderDeferred(){return this._renderDeferred}finishResumedRender(){let e=this._renderResumed;return this._renderResumed=!1,this.applyPendingScroll(),e}renderWindowIfDeferred(){return!this._renderDeferred||this.isLayoutPaused()?!1:(this.renderWindow(),!0)}onThemeReflow(){this._boundIndices.fill(-1),this.invalidateGeom(),this.renderWindow()}scrollRowIntoView(e){if(e<0||!this._scroller)return;if(n()){this._pendingScrollRow=e;return}let t=this.getRowHeight(),r=e*t,i=r+t,a=this._scroller.getScrollY(),o=this.getHeight(),s=a+o,c=a;r<a?c=r:i>s&&(c=i-o),c!==a&&this.setScrollY(c)}};function selectionsEqual(e,t){if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0}export{VirtualRowView as n,selectionsEqual as t};
|
|
2
|
-
//# sourceMappingURL=selectionsEqual-C7MrKMQr.js.map
|