@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
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{t as e}from"./DOM-DDhDN8lQ.js";import{l as t,n}from"./Position-DWR1nIpA.js";import{T as r,g as i,i as a,t as o}from"./Component-YrLXKJZS.js";import{t as s}from"./Glyph-Cof7BxfJ.js";import{a as c,i as l,r as u}from"./Tooltip-DgRHQZhL.js";import{n as d,t as f}from"./OverlayFade-C1yQKffL.js";import{t as p}from"./Text-CGFXPhHD.js";import{n as m}from"./VBox-hFIZAEpT.js";var h=class MenuRow extends o{static HEIGHT=24;_cssVarPrefix=`menu-bar`;_closeMenu=null;constructor(e,t){super(e,t),this.setPreferredSize({width:0,height:MenuRow.HEIGHT})}isSeparator(){return!1}isEnabled(){return!0}isNavigable(){return!1}setFocused(e){return this.setBackgroundColor(e?`var(--ts-ui-${this._cssVarPrefix}-item-hover-bg, rgba(30, 100, 200, 0.12))`:`transparent`),this}activate(){}hasCheck(){return!1}hasIcon(){return!1}hasSubmenu(){return!1}titleTextWidth(){return 0}shortcutTextWidth(){return 0}getContentWidth(){return 0}setColumns(e,t,n){}setCssVarPrefix(e){return this._cssVarPrefix=e,this}setMenuCloseHandler(e){this._closeMenu=e}getCssVarPrefix(){return this._cssVarPrefix}closeMenu(){this._closeMenu?.()}};function menuIconPx(){return a.getResolvedScale().glyphLg}var g=class MenuItem extends h{static HEIGHT=h.HEIGHT;static SEPARATOR_HEIGHT=9;static TEXT_INSET=8;static ICON_ZONE=24;static CHECK_ZONE=16;static CHEVRON_ZONE=16;static RIGHT_PAD=8;static TEXT_GAP=10;_config;_onActivate;_onOpenSubmenu;_checked=!1;_checkText=null;_iconText=null;_iconGlyph=null;_titleText=null;_shortcutText=null;_chevronText=null;_checkZone=null;_iconStart=null;_titleColumn=null;_submenuTimer=null;_onMouseOver;_onMouseOut;_onClick;constructor(e,t,n,i=`menu-bar`,a){if(super(),this._config=e,this._onActivate=t,this._onOpenSubmenu=n,this.setCssVarPrefix(i),e.separator){this.setHeight(MenuItem.SEPARATOR_HEIGHT),this.setPreferredSize({width:0,height:MenuItem.SEPARATOR_HEIGHT}),this.setBackgroundColor(`transparent`),this.setElementCSSRule(`borderTop`,`1px solid var(--ts-ui-${i}-separator-color, rgb(220, 220, 220))`),this.setElementCSSRule(`margin`,`4px 0`),this.getAria().setRole(`separator`),this._onMouseOver=()=>{},this._onMouseOut=()=>{},this._onClick=()=>{},a&&this.applyOptions(a);return}let o=e.enabled!==!1;if(this.setHeight(MenuItem.HEIGHT),this.setPreferredSize({width:0,height:MenuItem.HEIGHT}),this.setBackgroundColor(`transparent`),this.setElementCSSRule(`fontSize`,`var(--ts-ui-button-font-size, var(--ts-ui-font-size))`),this.setCursor(o?`pointer`:`default`),this.getAria().setRole(`menuitem`),o||(this.setForegroundColor(`var(--ts-ui-${i}-item-disabled-color, rgb(170, 170, 170))`),this.getAria().setDisabled(!0)),e.checked!==void 0&&(this._checked=e.checked,this._checkText=new p(e.checked?`✓`:``),this._checkText.setPointerEvents(`none`),this._checkText.setTextAlign(`center`),this.addComponent(this._checkText)),e.glyph){this._iconGlyph=new s(e.glyph),this._iconGlyph.setPointerEvents(`none`);let t=menuIconPx();this._iconGlyph.setPreferredSize({width:t,height:t}),e.glyphColor&&this._iconGlyph.setForegroundColor(e.glyphColor),this.addComponent(this._iconGlyph)}else this._iconText=new p(e.icon??``),this._iconText.setPointerEvents(`none`),this._iconText.setVisible(!!e.icon),this.addComponent(this._iconText);this._titleText=new p(e.text??``),this._titleText.setPointerEvents(`none`),this._titleText.setWhiteSpace(`nowrap`),this._titleText.setOverflow(`hidden`),this._titleText.setTextOverflow(`ellipsis`),this.addComponent(this._titleText),e.shortcut&&(this._shortcutText=new p(e.shortcut),this._shortcutText.setPointerEvents(`none`),this._shortcutText.setTextAlign(`left`),this._shortcutText.setForegroundColor(`var(--ts-ui-${i}-item-shortcut-color, rgb(140, 140, 140))`),this.addComponent(this._shortcutText)),this.hasSubmenu()&&(this._chevronText=new p(`▶`),this._chevronText.setPointerEvents(`none`),this._chevronText.setTextAlign(`center`),this._chevronText.setForegroundColor(`var(--ts-ui-${i}-item-shortcut-color, rgb(140, 140, 140))`),this.addComponent(this._chevronText),this.getAria().setHasPopup(`menu`),this.getAria().setExpanded(!1)),this._onMouseOver=()=>{this.setFocused(!0),this.isEnabled()&&this.hasSubmenu()?this._submenuTimer=setTimeout(()=>{this._submenuTimer=null,this._onOpenSubmenu(this)},150):this._onOpenSubmenu(this)},this._onMouseOut=()=>{this.setFocused(!1),this._submenuTimer!==null&&(clearTimeout(this._submenuTimer),this._submenuTimer=null)},this._onClick=()=>{this.isEnabled()&&!this.hasSubmenu()&&this.activateLeaf()},r.addListener(this,`mouseover`,this._onMouseOver),r.addListener(this,`mouseout`,this._onMouseOut),r.addListener(this,`click`,this._onClick),a&&this.applyOptions(a),this.updateLabelHeights()}applyOptions(e){return super.applyOptions(e),e.text!==void 0&&this._titleText&&this._titleText.setText(e.text),e.enabled!==void 0&&(this.getAria().setDisabled(!e.enabled),this.setCursor(e.enabled?`pointer`:`default`)),e.focused!==void 0&&this.setFocused(e.focused),this}hasSubmenu(){return!!this._config.submenu}hasIcon(){return!!this._config.icon||!!this._config.glyph}hasCheck(){return this._config.checked!==void 0}isChecked(){return this._checked}titleTextWidth(){return Math.ceil(this._titleText?.getPreferredSize()?.width??0)}shortcutTextWidth(){return!this._config.shortcut||this._shortcutText===null?0:Math.ceil(this._shortcutText.getPreferredSize()?.width??0)}setColumns(e,t,n){this._checkZone=e,this._iconStart=t,this._titleColumn=n,this.scheduleLayout()}getSubmenuConfig(){return this._config.submenu??null}isSeparator(){return!!this._config.separator}isNavigable(){return!this.isSeparator()}isEnabled(){return this._config.enabled!==!1}setEnabled(e){return this._config.separator?this:(this._config.enabled=e,this.setCursor(e?`pointer`:`default`),this.getAria().setDisabled(!e),e?this.clearForegroundColor():this.setForegroundColor(`var(--ts-ui-${this.getCssVarPrefix()}-item-disabled-color, rgb(170, 170, 170))`),this)}activateLeaf(){this.hasCheck()&&this._config.closeOnActivate===!1&&(this._checked=!this._checked,this._checkText?.setText(this._checked?`✓`:``)),this._onActivate()}activate(){this.isSeparator()||!this.isEnabled()||(this.hasSubmenu()?this._onOpenSubmenu(this):this.activateLeaf())}destructor(){this._submenuTimer!==null&&(clearTimeout(this._submenuTimer),this._submenuTimer=null),super.destructor()}getBaseline(){return this._titleText===null?null:this.wrapInnerBaseline(this._titleText.getBaseline())}updateLabelHeights(){let e=this.getPerimeterSize(),t=Math.max(1,MenuItem.HEIGHT-e.top-e.bottom);this._checkText?.centerInHeight(t),this._iconText?.centerInHeight(t),this._titleText?.centerInHeight(t),this._shortcutText?.centerInHeight(t),this._chevronText?.centerInHeight(t)}setBorder(e){return super.setBorder(e),this.updateLabelHeights(),this}clearBorder(){return super.clearBorder(),this.updateLabelHeights(),this}doLayout(){if(super.doLayout(),this._config.separator)return this;let e=this.getContentBounds();if(!e)return this;let t=e.height,n=e.width,r=!!this._config.shortcut&&this._shortcutText!==null,i=this.hasSubmenu(),a=this._checkZone??(this.hasCheck()?MenuItem.CHECK_ZONE:0),o=this._iconStart??a+(this.hasIcon()?MenuItem.ICON_ZONE:MenuItem.TEXT_INSET),s=this._titleColumn??this.titleTextWidth();if(this._checkText&&(this._checkText.setX(e.x+4),this._checkText.setY(e.y),this._checkText.setWidth(Math.max(0,a-4)),this._checkText.setHeight(t)),this._iconGlyph){let n=menuIconPx(),r=this._iconGlyph.getPreferredSize()??{width:n,height:n},i=Math.max(0,Math.floor((t-r.height)/2));this._iconGlyph.setX(e.x+a+4),this._iconGlyph.setY(e.y+i),this._iconGlyph.setWidth(r.width),this._iconGlyph.setHeight(r.height)}else this._iconText&&(this._iconText.setX(e.x+a+4),this._iconText.setY(e.y),this._iconText.setWidth(20),this._iconText.setHeight(t));return this._titleText&&(this._titleText.setX(e.x+o),this._titleText.setY(e.y),this._titleText.setWidth(s),this._titleText.setHeight(t)),i&&this._chevronText&&(this._chevronText.setX(e.x+n-MenuItem.RIGHT_PAD-MenuItem.CHEVRON_ZONE),this._chevronText.setY(e.y),this._chevronText.setWidth(MenuItem.CHEVRON_ZONE),this._chevronText.setHeight(t)),r&&this._shortcutText&&(this._shortcutText.setX(e.x+o+s+MenuItem.TEXT_GAP),this._shortcutText.setY(e.y),this._shortcutText.setWidth(this.shortcutTextWidth()),this._shortcutText.setHeight(t)),this}},_=i(g),v={backgroundColor:`transparent`},y=i(class MenuSeparator extends h{static HEIGHT=9;constructor(e=`menu-bar`,t,n){super(t,{...v,...n??{}}),this.setHeight(MenuSeparator.HEIGHT),this.setPreferredSize({width:0,height:MenuSeparator.HEIGHT}),this.setElementCSSRule(`borderTop`,`1px solid var(--ts-ui-${e}-separator-color, rgb(220, 220, 220))`),this.setElementCSSRule(`margin`,`4px 0`),this.getAria().setRole(`separator`)}isSeparator(){return!0}}),b=120,x=360,S=120,C=4;function pointRect(e,t){return{x:e,y:t,left:e,top:t,right:e,bottom:t,width:0,height:0}}var w=i(class Menu extends o{_persistent;_itemsProvider;_onClose;_menuItems=[];_fadeShowAnimation=null;_fadeHideAnimation=null;_focusedIndex=-1;_openSubmenuPanel=null;_openSubmenuItem=null;_excludedEl=null;_menuWidth=null;_rebuildOnClose=null;_currentOpener=null;_scrollToBottomOnShow=!1;_disposeOnClose=!1;constructor(e,t){super(),this._persistent=e!==void 0,this._itemsProvider=typeof e==`function`?e:null,this._onClose=t??null;let n=new m;n.setComponentSpacing(0),n.setStretching(!0),this.setLayoutManager(n),this._persistent?(this.applyPersistentChrome(),this._itemsProvider===null&&this.buildPersistentItems(e)):this.applyRebuildChrome()}layOutColumns(){let e=this._menuItems.filter(e=>!e.isSeparator()),t=e.some(e=>e.hasCheck())?_.CHECK_ZONE:0,n=t+(e.some(e=>e.hasIcon())?_.ICON_ZONE:_.TEXT_INSET),r=e.reduce((e,t)=>Math.max(e,t.titleTextWidth()),0),i=e.reduce((e,t)=>Math.max(e,t.shortcutTextWidth()),0),a=e.some(e=>e.hasSubmenu()),o=Math.max(i,a?_.CHEVRON_ZONE:0),s=o>0?_.TEXT_GAP+o:0,c=n+e.reduce((e,t)=>Math.max(e,t.getContentWidth()),0),l=Math.max(n+r+s+_.RIGHT_PAD,c),u=Math.min(x,Math.max(b,l)),d=Math.min(r,u-n-s-_.RIGHT_PAD);for(let r of e)r.setColumns(t,n,d);let f=this.getBorderSize();return u+f.left+f.right}show(n,r,i,a,o){this.assertRebuildMode(`show`);let s=e.source.getViewportSize();return this.showAnchored(pointRect(t.clamp(n,0,s.width),t.clamp(r,0,s.height)),i,a??null,o??null)}showAnchored(t,r,i,a){this._rebuildOnClose=i,this._excludedEl=a,this._menuItems=[],this.disposeAllComponents(),this.pauseLayout();for(let e of r){let t;e.separator===!0?t=new y(`context-menu`):e.row?(t=e.row(),t.setCssVarPrefix(`context-menu`),t.setMenuCloseHandler(()=>{this.dismissAll()})):t=new _(e,()=>{e.action?.(),e.closeOnActivate===!1?this.closeOpenSubmenu():this.hide()},e=>{this.handleItemOpenSubmenu(e)},`context-menu`),this.addComponent(t),this._menuItems.push(t)}this.resumeLayout();let o=this.layOutColumns(),s=this._menuWidth??o;this.setWidth(s),this.setMaxSize({width:Number.MAX_VALUE,height:Number.MAX_VALUE});let l=this.getPreferredSize()?.height??0,d=this.getElement(!0),f=e.source.getViewportSize(),p=l>u(t,{width:s,height:l},f,C).available?e.source.getScrollBarWidth():0;this.setInsets(new n(4,p,4,0)),this.setWidth(s+p);let m=u(t,{width:this.getWidth(),height:l},f,C);return this.setX(m.x),this.setY(m.y),this.applyViewportHeightClamp(m.available,l),this.scheduleLayout(),c.mount(d),this.setVisible(!0),this.fadeIn(),c.register(this),this.setZIndex(c.getZIndex(this)),this._scrollToBottomOnShow&&(this.flushLayout(),this.setScrollTop(this.getMaxScrollTop())),this}toggleFor(e,t,n,r){return this.assertRebuildMode(`toggleFor`),this._currentOpener===e?(this.hide(),this):n.length===0?(r?.(),this):(this.showAnchored(t,n,r??null,e),this._currentOpener=e,this)}hide(){this.assertRebuildMode(`hide`),this.closeOpenSubmenu(),c.unregister(this),this.fadeOutAndDetach(),this._currentOpener=null;let e=this._rebuildOnClose;return this._rebuildOnClose=null,e?.(),this}getMenuWidth(){return this.getWidth()}setMenuWidth(e){return this.assertRebuildMode(`setMenuWidth`),this._menuWidth=e,this}setItemEnabled(e,t){this.assertRebuildMode(`setItemEnabled`);let n=this._menuItems[e];return n instanceof _&&n.setEnabled(t),this}setScrollToBottomOnShow(e){return this.assertRebuildMode(`setScrollToBottomOnShow`),this._scrollToBottomOnShow=e,this}open(t,n){this.assertPersistentMode(`open`),this._itemsProvider&&this.rebuildPersistentItems(this._itemsProvider()),this.setMaxSize({width:Number.MAX_VALUE,height:Number.MAX_VALUE});let r=this.getPreferredSize()?.height??this._menuItems.length*_.HEIGHT+8,i=this.getWidth(),a=this.getElement(!0);c.mount(a);let o=e.source.getViewportSize();if(n){let a=n.getElement(),s=a?e.source.getElementRect(a):{left:0,right:0,top:0,bottom:0},c=e.source.getElementRect(t),l=s.right;l+i>o.width&&(l=s.left-i);let u=this.placeVertically(c.top,c.top,r,o.height);this.setAutoCommitStyle(!1),this.setX(Math.max(0,l)),this.setY(Math.max(0,u)),this.setAutoCommitStyle(!0)}else{let n=e.source.getElementRect(t),a=n.left;a+i>o.width&&(a=o.width-i);let s=this.placeVertically(n.bottom,n.top,r,o.height);this.setAutoCommitStyle(!1),this.setX(Math.max(0,a)),this.setY(Math.max(0,s)),this.setAutoCommitStyle(!0)}let s=e.source.getId(t);return s&&this.getAria().setLabelledBy(s),this.setVisible(!0),this.doLayout(),this.fadeIn(),c.register(this),this.setZIndex(c.getZIndex(this)),this}close(){return this.assertPersistentMode(`close`),this.closeOpenSubmenu(),this.setFocusedIndex(-1),this.clearItemHighlights(),c.unregister(this),this.fadeOutAndDetach(this._disposeOnClose?()=>this.dispose():void 0),this}clearItemHighlights(){for(let e of this._menuItems)e.isNavigable()&&e.setFocused(!1)}fadeIn(){this._fadeShowAnimation?.cancel(),this._fadeShowAnimation=d(this,{durationMs:S})}fadeOutAndDetach(e){this._fadeHideAnimation?.cancel(),this._fadeHideAnimation=f(this,{durationMs:S,onComplete:e})}destructor(){this._fadeShowAnimation?.cancel(),this._fadeShowAnimation=null,this._fadeHideAnimation?.cancel(),this._fadeHideAnimation=null,this._openSubmenuPanel?.dispose(),this._openSubmenuPanel=null,c.unregister(this),super.destructor()}focusItem(e){return this.assertPersistentMode(`focusItem`),this.setFocusedIndex(e),this}focusNext(){this.assertPersistentMode(`focusNext`);let e=this._focusedIndex+1;for(;e<this._menuItems.length&&this.isItemSkipped(e);)e++;if(e>=this._menuItems.length)for(e=0;e<this._menuItems.length&&this.isItemSkipped(e);)e++;return this.setFocusedIndex(e),this}focusPrev(){this.assertPersistentMode(`focusPrev`);let e=this._focusedIndex-1;for(;e>=0&&this.isItemSkipped(e);)e--;if(e<0)for(e=this._menuItems.length-1;e>=0&&this.isItemSkipped(e);)e--;return this.setFocusedIndex(e),this}activateFocused(){if(this.assertPersistentMode(`activateFocused`),this._focusedIndex<0||this._focusedIndex>=this._menuItems.length)return;let e=this._menuItems[this._focusedIndex];e.isNavigable()&&e.isEnabled()&&e.activate()}getFocusedIndex(){return this.assertPersistentMode(`getFocusedIndex`),this._focusedIndex}setExcludedElement(e){return this.assertPersistentMode(`setExcludedElement`),this._excludedEl=e,this}getExcludedElement(){return this._excludedEl}applyPersistentChrome(){this.setBackgroundColor(`var(--ts-ui-menu-bar-panel-bg, rgb(255, 255, 255))`),this.setInsets(new n(4,0,4,0)),this.setBorder({border:`1px solid var(--ts-ui-menu-bar-panel-border, rgb(200, 200, 200))`}),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),this.setShadow(`var(--ts-ui-menu-bar-panel-shadow, 2px 4px 8px rgba(0, 0, 0, 0.15))`),this.getAria().setRole(`menu`),this.setContain(`layout`),this.enableVerticalScroll()}applyRebuildChrome(){this.setVisible(!1),this.setBackgroundColor(`var(--ts-ui-context-menu-bg, rgb(255, 255, 255))`),this.setInsets(new n(4,0,4,0)),this.setBorder({border:`1px solid var(--ts-ui-context-menu-border, rgb(200, 200, 200))`}),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),this.setShadow(`var(--ts-ui-context-menu-shadow, 2px 4px 8px rgba(0, 0, 0, 0.15))`),this.setContain(`layout`),this.enableVerticalScroll()}enableVerticalScroll(){this.setOverflowX(`hidden`),this.setOverflowY(`auto`),this.getLayoutManager().setOverflowing(!1,!0)}applyViewportHeightClamp(e,t){this.setMaxSize({width:Number.MAX_VALUE,height:Math.max(0,e)}),this.setHeight(t)}placeVertically(e,t,n,r){let{start:i,available:a}=l(t,e,n,r,C);return this.applyViewportHeightClamp(a,n),i}rebuildPersistentItems(e){this.closeOpenSubmenu(),this._menuItems=[],this._focusedIndex=-1,this.disposeAllComponents(),this.buildPersistentItems(e)}buildPersistentItems(e){this.pauseLayout();for(let t of e){let e;t.row&&t.separator!==!0?(e=t.row(),e.setCssVarPrefix(`menu-bar`),e.setMenuCloseHandler(()=>{this.dismissAll()})):e=new _(t,()=>{t.action?.(),t.closeOnActivate===!1?this.closeOpenSubmenu():this._onClose()},e=>{this.handleItemOpenSubmenu(e)}),this.addComponent(e),this._menuItems.push(e)}this.resumeLayout(),this.setWidth(this.layOutColumns())}setFocusedIndex(e){if(this._focusedIndex>=0&&this._focusedIndex<this._menuItems.length){let e=this._menuItems[this._focusedIndex];e.isNavigable()&&e.setFocused(!1)}if(this._focusedIndex=e,e>=0&&e<this._menuItems.length){let t=this._menuItems[e];t.isNavigable()&&t.setFocused(!0)}}isItemSkipped(e){return!this._menuItems[e].isNavigable()}closeOpenSubmenu(){this._openSubmenuPanel&&(this._openSubmenuPanel.close(),this._openSubmenuItem?.getAria().setExpanded(!1),this._openSubmenuPanel=null,this._openSubmenuItem=null)}dismissAll(){this._persistent?this._onClose():this.hide()}getLayerElement(){return this.getElement()??null}getDismissMode(){return`click-outside`}requestClose(){this.dismissAll()}getAnchorElement(){return this._excludedEl}getBand(){return c.Band.Dropdown}onZIndexChanged(e){this.setZIndex(e)}handleItemOpenSubmenu(e){if(!e.hasSubmenu()||!e.isEnabled()){this.closeOpenSubmenu();return}if(this._openSubmenuItem===e)return;this.closeOpenSubmenu();let t=new Menu(e.getSubmenuConfig().items,()=>{this.dismissAll()});t._disposeOnClose=!0,this._openSubmenuPanel=t,this._openSubmenuItem=e,e.getAria().setExpanded(!0),t.setExcludedElement(this.getElement(!0)),t.open(e.getElement(!0),this)}assertRebuildMode(e){if(this._persistent)throw Error(`Menu.${e}() is only valid in rebuild mode (constructed without arguments).`)}assertPersistentMode(e){if(!this._persistent)throw Error(`Menu.${e}() is only valid in persistent mode (constructed with items and onClose).`)}});export{h as i,y as n,_ as r,w as t};
|
|
2
|
+
//# sourceMappingURL=Menu-BCQISKiQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu-BCQISKiQ.js","names":[],"sources":["../../src/typescript/lib/component/container/MenuRow.ts","../../src/typescript/lib/component/container/MenuItem.ts","../../src/typescript/lib/component/container/MenuSeparator.ts","../../src/typescript/lib/overlay/Menu.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport type { MenuItemCSSVarPrefix } from \"~/component/container/MenuItem.js\";\n\n/**\n * Abstract base class for a row inside a [`Menu`](/api/overlay/classes/Menu) panel.\n *\n * @remarks\n * `Menu` drives every row through this fixed set of methods rather than\n * assuming a [`MenuItem`](/api/component/container/classes/MenuItem). `MenuItem`\n * and [`MenuSeparator`](/api/component/container/classes/MenuSeparator) are its\n * two built-in subclasses; a consumer wanting a menu row that hosts an\n * arbitrary component (a real checkbox, a text field, …) supplies a\n * `row: () => MenuRow` factory on a `MenuItemConfig` and extends this class.\n * Every method has a working default, so a subclass overrides only what it\n * needs — mirroring\n * [`ListItemRenderer`](/api/component/list/classes/ListItemRenderer)'s\n * \"defaults in the base\" shape for the same kind of pluggable-content problem.\n *\n * Not wrapped with `callable()` — abstract classes are never instantiated.\n *\n * @category Components\n */\nexport abstract class MenuRow<TOptions extends ComponentOptions = ComponentOptions>\n extends Component<TOptions>\n{\n\n /** Standard row height in pixels, shared by every built-in row type. */\n static readonly HEIGHT: number = 24;\n\n // Which CSS-variable family this row's highlight reads. Framework-managed\n // (the owning Menu picks it from its mode), so it is deliberately NOT an\n // options-bag field — ARCHITECTURE.md reserves the bag for consumer\n // configuration. No cascade-dispatched setter writes it, so a plain\n // initializer is correct here; `declare` is not needed.\n private _cssVarPrefix: MenuItemCSSVarPrefix = \"menu-bar\";\n\n // Injected by the owning Menu so a row can dismiss a panel it has no\n // reference to. Null for a row constructed outside a Menu.\n private _closeMenu: (() => void) | null = null;\n\n /**\n * Constructs a MenuRow, seeding the standard row height as the initial\n * preferred size. A subclass whose content needs a different size\n * overrides it after calling `super()`.\n *\n * @param options - Optional construction options, forwarded to `Component`.\n * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(options?: TOptions, subclassDefaults?: Partial<TOptions>) {\n super(options, subclassDefaults);\n\n this.setPreferredSize({ width: 0, height: MenuRow.HEIGHT });\n }\n\n /**\n * Whether this row is a non-interactive separator. `Menu` never\n * navigates to, activates, or measures shared columns for a separator row.\n *\n * @returns `false` by default.\n */\n isSeparator(): boolean {\n return false;\n }\n\n /**\n * Whether this row is interactive. `Menu.activateFocused()` refuses to\n * activate a disabled row even when it is otherwise navigable.\n *\n * @returns `true` by default.\n */\n isEnabled(): boolean {\n return true;\n }\n\n /**\n * Whether `Menu`'s roving arrow-key highlight may land on this row.\n * `false` by default, so a row hosting a real focusable control — which\n * owns its own DOM focus and keys — is skipped by `focusNext` /\n * `focusPrev` exactly like a separator. Override to `true` for a row\n * that behaves like a menu item: it then receives the highlight, and\n * Enter activates it via {@link activate}.\n *\n * @returns `false` by default.\n */\n isNavigable(): boolean {\n return false;\n }\n\n /**\n * Applies or removes the keyboard/hover focus highlight background,\n * reading the CSS-variable family selected by {@link setCssVarPrefix}.\n *\n * @param focused - `true` to highlight, `false` to clear.\n * @returns This row, for method chaining.\n */\n setFocused(focused: boolean): this {\n this.setBackgroundColor(\n focused\n ? `var(--ts-ui-${this._cssVarPrefix}-item-hover-bg, rgba(30, 100, 200, 0.12))`\n : \"transparent\"\n );\n\n return this;\n }\n\n /**\n * Activates this row as if the user clicked or pressed Enter on it.\n * Called by `Menu.activateFocused()` for a navigable, enabled row. A\n * no-op by default; a subclass with an activation behaviour overrides it.\n */\n activate(): void {\n // No-op by default.\n }\n\n /**\n * Whether this row reserves the menu's shared leading checkmark column.\n *\n * @returns `false` by default.\n */\n hasCheck(): boolean {\n return false;\n }\n\n /**\n * Whether this row reserves the menu's shared leading icon column.\n *\n * @returns `false` by default.\n */\n hasIcon(): boolean {\n return false;\n }\n\n /**\n * Whether this row opens a child submenu. Only `MenuItem` ever reports\n * `true` — a custom row has no hook to open a submenu panel.\n *\n * @returns `false` by default.\n */\n hasSubmenu(): boolean {\n return false;\n }\n\n /**\n * This row's measured title width, feeding the menu's shared title column.\n *\n * @returns `0` by default — a row contributing no title-column metrics.\n */\n titleTextWidth(): number {\n return 0;\n }\n\n /**\n * This row's measured shortcut width, feeding the menu's shared\n * shortcut / chevron zone.\n *\n * @returns `0` by default.\n */\n shortcutTextWidth(): number {\n return 0;\n }\n\n /**\n * This row's own content width, measured to the right of wherever\n * `Menu` positions the row's content — exclude any left-inset\n * assumption of your own; `Menu` adds the shared `iconStart` uniformly\n * across every row when reserving panel width, since a row cannot know\n * here whether a sibling row widens it via `hasCheck()` / `hasIcon()`.\n * Read by `Menu.layOutColumns` **before** {@link setColumns} is called,\n * so an override must not depend on the injected `iconStart` value\n * itself. Floors the panel's natural width, so a row contributing no\n * title/shortcut metrics still gets enough room to render.\n *\n * @returns `0` by default.\n */\n getContentWidth(): number {\n return 0;\n }\n\n /**\n * Applies the menu-computed shared column geometry. A no-op by\n * default — a custom row opts out of the shared grid and renders across\n * the row's full width. A row that wants to line up with the\n * `MenuItem`s around it overrides this and stores the `iconStart` it is\n * handed.\n *\n * @param _checkZone - Width reserved for the leading checkmark column, in\n * pixels; `0` when no item in the menu declares `checked`.\n * @param _iconStart - The title's left offset in pixels; already includes `checkZone`.\n * @param _titleColumn - The shared title-column width across the menu's items.\n */\n setColumns(_checkZone: number, _iconStart: number, _titleColumn: number): void {\n // No-op by default.\n }\n\n /**\n * Selects which CSS-variable family {@link setFocused} reads its hover\n * highlight colours from. Called by the owning `Menu` immediately after\n * building this row from a `row` factory — not for consumer use.\n *\n * @param prefix - `\"menu-bar\"` or `\"context-menu\"`, matching the owning menu's mode.\n * @returns This row, for method chaining.\n */\n setCssVarPrefix(prefix: MenuItemCSSVarPrefix): this {\n this._cssVarPrefix = prefix;\n\n return this;\n }\n\n /**\n * Injects the callback that dismisses the owning menu. Called by the\n * owning `Menu` immediately after building this row from a `row`\n * factory — not for consumer use. A factory row has no reference to its\n * menu, so this is the only way a subclass can close the panel it is\n * hosted in, via its inherited protected close helper.\n *\n * @param close - Callback that dismisses the owning menu.\n */\n setMenuCloseHandler(close: () => void): void {\n this._closeMenu = close;\n }\n\n /**\n * Returns the CSS-variable family currently selected via\n * {@link setCssVarPrefix}.\n *\n * @returns The active CSS-variable prefix.\n */\n protected getCssVarPrefix(): MenuItemCSSVarPrefix {\n return this._cssVarPrefix;\n }\n\n /**\n * Dismisses the owning menu, if one was injected via\n * {@link setMenuCloseHandler}. A no-op for a row constructed outside a\n * `Menu` (e.g. in isolation in a test).\n */\n protected closeMenu(): void {\n this._closeMenu?.();\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { ComponentOptions } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { BorderOptions } from \"~/primitive/Border.js\";\nimport { MenuRow } from \"~/component/container/MenuRow.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\n\n/**\n * Square edge length used for a menu item's leading icon glyph — the theme's\n * `glyphLg` default icon step (16px at the shipped base). Read per call, not\n * frozen in a module constant, so a theme that raises `scale.base` moves the\n * icon with it.\n */\nfunction menuIconPx(): number {\n return ThemeManager.getResolvedScale().glyphLg;\n}\n\n/**\n * Construction-time component-level options for {@link MenuItem}. Use this\n * for cosmetic/layout overrides on the item row; the menu data (text, action,\n * shortcut, submenu) still comes through {@link MenuItemConfig}.\n *\n * @category Components\n */\nexport interface MenuItemOptions extends ComponentOptions {\n text?: string;\n enabled?: boolean;\n focused?: boolean;\n}\n\n/**\n * Selects which CSS-variable family a `MenuItem` reads its colours from.\n *\n * `'menu-bar'` resolves to `--ts-ui-menu-bar-item-*` tokens; `'context-menu'`\n * resolves to `--ts-ui-context-menu-item-*` tokens. The [`Menu`](/api/overlay/classes/Menu) class chooses\n * the prefix based on its mode.\n */\nexport type MenuItemCSSVarPrefix = \"menu-bar\" | \"context-menu\";\n\n/**\n * Describes a leaf action item, submenu trigger, or separator row inside a [`Menu`](/api/overlay/classes/Menu).\n *\n * @remarks When `separator` is true all other fields are ignored; the item renders as\n * a thin horizontal rule. When `submenu` is set the item opens a child [`Menu`](/api/overlay/classes/Menu)\n * instead of calling `action`.\n *\n * @category Components\n */\nexport interface MenuItemConfig {\n /** Display label shown in the menu row. */\n text?: string;\n /** Called when the item is activated (click or Enter). Ignored when `submenu` is set. */\n action?: () => void;\n /**\n * When `false`, activating this item runs `action` but leaves the menu\n * open — the menu still closes on an outside click, Escape, or window\n * blur. Defaults to `true` (close on activation, today's behaviour).\n * Ignored for a submenu-opening item (see `submenu`), which never calls\n * `action`.\n *\n * Pair with {@link MenuItemConfig.checked} for a multi-select menu: each\n * activation flips that item's own checkmark without dismissing the\n * panel, so the user can pick several items in one open.\n */\n closeOnActivate?: boolean;\n /** Defaults to `true`. Disabled items are dimmed and non-interactive. */\n enabled?: boolean;\n /** Keyboard shortcut hint displayed on the right (e.g. `\"Ctrl+S\"`). */\n shortcut?: string;\n /**\n * Marks this item as part of a checkable set (a toggle, or one option in\n * a mutually-exclusive group) and whether it is currently checked. A\n * checkmark renders in a dedicated leading zone, to the left of any\n * `icon`/`glyph`, so a checked and an unchecked row's icon and label\n * still start at the same x position — unlike hand-prefixing the\n * checkmark onto `text`, whose leading whitespace collapses inconsistently\n * under this component's `white-space: nowrap`.\n *\n * When at least one item in a menu sets `checked` (`true` or `false`),\n * every item in that menu reserves the check column, so the whole menu's\n * icon/title columns stay aligned even for items that omit `checked`.\n *\n * @remarks Omit entirely for a plain action item — this is opt-in per\n * menu, not a default a plain item pays for. Paired with\n * `closeOnActivate: false`, the checkmark also flips automatically on\n * each activation — see `closeOnActivate`.\n */\n checked?: boolean;\n /**\n * Icon displayed on the left (e.g. a Unicode character).\n *\n * @remarks Prefer `glyph` for crisp SVG output from the framework's glyph\n * registry; `icon` remains supported for callers that want to embed a raw\n * character.\n */\n icon?: string;\n /**\n * Registry glyph name displayed on the left of the item row. Takes\n * precedence over `icon` when both are provided.\n */\n glyph?: string;\n /**\n * Optional colour for the leading `glyph`, overriding the item's default\n * foreground (e.g. a severity tint). Any CSS colour string. Ignored when the\n * row has no `glyph`.\n */\n glyphColor?: string;\n /** When present this item opens a submenu rather than calling `action`. */\n submenu?: MenuConfig;\n /** When `true` the item renders as a separator; all other fields are ignored. */\n separator?: true;\n /**\n * A zero-argument factory returning a [`MenuRow`](/api/component/container/classes/MenuRow)\n * to render in place of this config's own `MenuItem`. When set, `separator`\n * still wins if also set; otherwise every other field on this config is\n * ignored. The factory is called once per menu build, so it must never\n * return a shared instance — `Menu` disposes its whole item list on every\n * rebuild, which would leave a reused instance dead after the first show.\n */\n row?: () => MenuRow;\n}\n\n/**\n * Describes one top-level entry in a [`MenuBar`](/api/component/menubar/classes/MenuBar).\n *\n * @category Components\n */\nexport interface MenuConfig {\n /** Label shown in the bar button (e.g. `\"File\"`). */\n label: string;\n /**\n * The items in the dropdown panel: either a fixed array, or a provider called\n * to produce them when the panel is built. A submenu panel is (re)built each\n * time it opens, so a provider on a submenu's `items` is re-invoked on every\n * open — letting the submenu reflect current state (e.g. an export chooser\n * whose labels track the active tab) without the parent menu rebuilding.\n */\n items: MenuItemConfig[] | (() => MenuItemConfig[]);\n /**\n * Optional registry glyph name displayed to the left of the bar button's\n * label (e.g. `\"file\"`, `\"eye\"`, `\"info-circle\"`). Omit for a text-only\n * menu button.\n */\n glyph?: string;\n}\n\n/**\n * A single row inside a [`Menu`](/api/overlay/classes/Menu) panel.\n *\n * Renders a five-zone layout: check | icon | text | shortcut | chevron. The\n * check zone is reserved only when the menu has at least one checkable item\n * (see {@link MenuItemConfig.checked}). When `config.separator` is true the\n * item renders instead as a thin horizontal rule and ignores all other\n * config fields.\n *\n * Mouse hover triggers the submenu open callback after a 150 ms delay.\n * Keyboard focus is applied programmatically via `setFocused`.\n *\n * @category Components\n */\nclass MenuItem extends MenuRow {\n\n /** Fixed pixel height for every non-separator menu item. */\n static readonly HEIGHT: number = MenuRow.HEIGHT;\n\n private static readonly SEPARATOR_HEIGHT: number = 9;\n /** Title left inset when the menu reserves no icon column. */\n static readonly TEXT_INSET: number = 8;\n /** Title left offset when the menu reserves an icon column. */\n static readonly ICON_ZONE: number = 24;\n /** Width reserved for the leading checkmark column, when the menu has one. */\n static readonly CHECK_ZONE: number = 16;\n /** Width reserved for a submenu chevron in the right zone. */\n static readonly CHEVRON_ZONE: number = 16;\n /** Padding after the right zone, at the panel's inner edge. */\n static readonly RIGHT_PAD: number = 8;\n /**\n * Gap between the title column and the shortcut/chevron right zone, so a\n * content-sized menu never butts the label against them.\n */\n static readonly TEXT_GAP: number = 10;\n\n private readonly _config: MenuItemConfig;\n private readonly _onActivate: () => void;\n private readonly _onOpenSubmenu: (item: MenuItem) => void;\n\n // The item's own live checkmark state, decoupled from `_config.checked`\n // (the caller-owned initial value) so `activateLeaf` can flip it without\n // mutating a config object the caller still holds a reference to.\n private _checked: boolean = false;\n\n private _checkText: Text | null = null;\n private _iconText: Text | null = null;\n private _iconGlyph: Glyph | null = null;\n private _titleText: Text | null = null;\n private _shortcutText: Text | null = null;\n private _chevronText: Text | null = null;\n\n // Column geometry applied by the parent Menu so every item lines up: the\n // check zone's width, the title's left offset (which already includes the\n // check zone, when reserved), and the shared title-column width the right\n // zone begins after. Null until the menu sets them (a standalone item\n // falls back to its own natural metrics).\n private _checkZone: number | null = null;\n private _iconStart: number | null = null;\n private _titleColumn: number | null = null;\n\n private _submenuTimer: ReturnType<typeof setTimeout> | null = null;\n\n private readonly _onMouseOver: () => void;\n private readonly _onMouseOut: () => void;\n private readonly _onClick: () => void;\n\n /**\n * Constructs a MenuItem from a config descriptor.\n *\n * @param config - The item descriptor including text, action, shortcut, icon, and submenu.\n * @param onActivate - Called when this item is activated (click or Enter on a leaf item).\n * @param onOpenSubmenu - Called when this item's submenu should open (or close others when no submenu).\n * @param cssVarPrefix - Selects which CSS-variable family supplies disabled, hover, and shortcut colours. Defaults to `'menu-bar'`.\n */\n constructor(\n config: MenuItemConfig,\n onActivate: () => void,\n onOpenSubmenu: (item: MenuItem) => void,\n cssVarPrefix: MenuItemCSSVarPrefix = \"menu-bar\",\n options?: MenuItemOptions\n ) {\n // Child components are built first; options are applied via applyOptions at the constructor tail.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n this._config = config;\n this._onActivate = onActivate;\n this._onOpenSubmenu = onOpenSubmenu;\n this.setCssVarPrefix(cssVarPrefix);\n\n if (config.separator) {\n this.setHeight(MenuItem.SEPARATOR_HEIGHT);\n this.setPreferredSize({ width: 0, height: MenuItem.SEPARATOR_HEIGHT });\n this.setBackgroundColor(\"transparent\");\n this.setElementCSSRule(\n \"borderTop\",\n `1px solid var(--ts-ui-${cssVarPrefix}-separator-color, rgb(220, 220, 220))`\n );\n this.setElementCSSRule(\"margin\", \"4px 0\");\n this.getAria().setRole(\"separator\");\n\n this._onMouseOver = () => {};\n this._onMouseOut = () => {};\n this._onClick = () => {};\n\n if (options) {\n this.applyOptions(options);\n }\n\n return;\n }\n\n const enabled = config.enabled !== false;\n\n this.setHeight(MenuItem.HEIGHT);\n this.setPreferredSize({ width: 0, height: MenuItem.HEIGHT });\n this.setBackgroundColor(\"transparent\");\n this.setElementCSSRule(\"fontSize\", \"var(--ts-ui-button-font-size, var(--ts-ui-font-size))\");\n this.setCursor(enabled ? \"pointer\" : \"default\");\n this.getAria().setRole(\"menuitem\");\n\n if (!enabled) {\n this.setForegroundColor(\n `var(--ts-ui-${cssVarPrefix}-item-disabled-color, rgb(170, 170, 170))`\n );\n this.getAria().setDisabled(true);\n }\n\n if (config.checked !== undefined) {\n this._checked = config.checked;\n this._checkText = new Text(config.checked ? \"✓\" : \"\");\n this._checkText.setPointerEvents(\"none\");\n this._checkText.setTextAlign(\"center\");\n this.addComponent(this._checkText);\n }\n\n if (config.glyph) {\n this._iconGlyph = new Glyph(config.glyph);\n this._iconGlyph.setPointerEvents(\"none\");\n const iconPx = menuIconPx();\n this._iconGlyph.setPreferredSize({ width: iconPx, height: iconPx });\n\n if (config.glyphColor) {\n this._iconGlyph.setForegroundColor(config.glyphColor);\n }\n\n this.addComponent(this._iconGlyph);\n } else {\n this._iconText = new Text(config.icon ?? \"\");\n this._iconText.setPointerEvents(\"none\");\n this._iconText.setVisible(!!config.icon);\n this.addComponent(this._iconText);\n }\n\n this._titleText = new Text(config.text ?? \"\");\n this._titleText.setPointerEvents(\"none\");\n this._titleText.setWhiteSpace(\"nowrap\");\n this._titleText.setOverflow(\"hidden\");\n this._titleText.setTextOverflow(\"ellipsis\");\n this.addComponent(this._titleText);\n\n if (config.shortcut) {\n this._shortcutText = new Text(config.shortcut);\n this._shortcutText.setPointerEvents(\"none\");\n this._shortcutText.setTextAlign(\"left\");\n this._shortcutText.setForegroundColor(\n `var(--ts-ui-${cssVarPrefix}-item-shortcut-color, rgb(140, 140, 140))`\n );\n this.addComponent(this._shortcutText);\n }\n\n if (this.hasSubmenu()) {\n this._chevronText = new Text(\"▶\");\n this._chevronText.setPointerEvents(\"none\");\n this._chevronText.setTextAlign(\"center\");\n this._chevronText.setForegroundColor(\n `var(--ts-ui-${cssVarPrefix}-item-shortcut-color, rgb(140, 140, 140))`\n );\n this.addComponent(this._chevronText);\n this.getAria().setHasPopup(\"menu\");\n this.getAria().setExpanded(false);\n }\n\n this._onMouseOver = () => {\n this.setFocused(true);\n\n // Reads the live `isEnabled()` (not the `enabled` captured above) so\n // a later `setEnabled()` call takes effect on the next hover — see\n // `setEnabled`'s own doc. A disabled item never opens its own\n // submenu; it still signals the parent (the else branch) so\n // hovering it closes any sibling submenu.\n if (this.isEnabled() && this.hasSubmenu()) {\n this._submenuTimer = setTimeout(() => {\n this._submenuTimer = null;\n this._onOpenSubmenu(this);\n }, 150);\n } else {\n this._onOpenSubmenu(this);\n }\n };\n\n this._onMouseOut = () => {\n this.setFocused(false);\n\n if (this._submenuTimer !== null) {\n clearTimeout(this._submenuTimer);\n this._submenuTimer = null;\n }\n };\n\n this._onClick = () => {\n // Reads the live `isEnabled()`, not the `enabled` captured above —\n // see `setEnabled`'s own doc.\n if (this.isEnabled() && !this.hasSubmenu()) {\n this.activateLeaf();\n }\n };\n\n Event.addListener(this, \"mouseover\", this._onMouseOver);\n Event.addListener(this, \"mouseout\", this._onMouseOut);\n Event.addListener(this, \"click\", this._onClick);\n\n if (options) {\n this.applyOptions(options);\n }\n\n this.updateLabelHeights();\n }\n\n /**\n * Applies a {@link MenuItemOptions} bag, dispatching menu item label,\n * enabled state, and focus highlight after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: MenuItemOptions): this {\n super.applyOptions(options);\n\n if (options.text !== undefined && this._titleText) {\n this._titleText.setText(options.text);\n }\n\n if (options.enabled !== undefined) {\n this.getAria().setDisabled(!options.enabled);\n this.setCursor(options.enabled ? \"pointer\" : \"default\");\n }\n\n if (options.focused !== undefined) {\n this.setFocused(options.focused);\n }\n\n return this;\n }\n\n /**\n * Returns `true` if this item has a child submenu.\n *\n * @returns Whether a `submenu` config was provided.\n */\n hasSubmenu(): boolean {\n return !!this._config.submenu;\n }\n\n /** True when the item reserves an icon column (has a glyph or icon slot). */\n hasIcon(): boolean {\n return !!this._config.icon || !!this._config.glyph;\n }\n\n /** True when the item declares `checked` (participates in a checkable set). */\n hasCheck(): boolean {\n return this._config.checked !== undefined;\n }\n\n /**\n * Returns the item's current checkmark state. Meaningless when\n * `hasCheck()` is `false` — there is no checkmark to report.\n *\n * @returns Whether the checkmark is currently shown.\n */\n isChecked(): boolean {\n return this._checked;\n }\n\n /** The item's measured title width, feeding the menu's shared title column. */\n titleTextWidth(): number {\n return Math.ceil(this._titleText?.getPreferredSize()?.width ?? 0);\n }\n\n /** The item's measured shortcut width, or 0 when it has no shortcut. */\n shortcutTextWidth(): number {\n if (!this._config.shortcut || this._shortcutText === null) {\n return 0;\n }\n\n return Math.ceil(this._shortcutText.getPreferredSize()?.width ?? 0);\n }\n\n /**\n * Applies the menu-computed column geometry so every item lines up: the\n * check column's width, the title's left offset (icon column plus the\n * check column, or the bare inset), and the shared title-column width the\n * shortcut/chevron right zone begins after.\n *\n * @param checkZone - Width reserved for the leading checkmark column, in\n * pixels; `0` when no item in the menu declares `checked`.\n * @param iconStart - The title's left offset in pixels; already includes `checkZone`.\n * @param titleColumn - The shared title-column width across the menu's items.\n */\n setColumns(checkZone: number, iconStart: number, titleColumn: number): void {\n this._checkZone = checkZone;\n this._iconStart = iconStart;\n this._titleColumn = titleColumn;\n\n this.scheduleLayout();\n }\n\n /**\n * Returns the submenu config, or `null` if this item has no submenu.\n *\n * @returns The [`MenuConfig`](/api/component/container/interfaces/MenuConfig) for the child panel, or null.\n */\n getSubmenuConfig(): MenuConfig | null {\n return this._config.submenu ?? null;\n }\n\n /**\n * Returns `true` when this item was constructed with `separator: true`.\n *\n * @returns Whether this item is a non-interactive separator row.\n */\n isSeparator(): boolean {\n return !!this._config.separator;\n }\n\n /**\n * True for any item the menu's arrow-key highlight may land on — every\n * item except a separator. A disabled item is still navigable; `activate`\n * is what refuses to run for it.\n *\n * @returns Whether the roving highlight may land on this item.\n */\n isNavigable(): boolean {\n return !this.isSeparator();\n }\n\n /**\n * Returns `true` when the item is interactive (not disabled).\n *\n * @returns Whether `enabled` was not explicitly set to `false`.\n */\n isEnabled(): boolean {\n return this._config.enabled !== false;\n }\n\n /**\n * Updates the item's enabled state after construction — dims (or\n * restores) the title colour, sets the aria-disabled flag and pointer\n * cursor, and updates the flag {@link isEnabled} reads, which the\n * `_onMouseOver` / `_onClick` handlers installed at construction consult\n * live rather than a frozen construction-time value. Lets `Menu.setItemEnabled`\n * push a live availability change into an already-open panel — e.g. a\n * sibling row's own toggle that should immediately grey out this item —\n * without closing or rebuilding the panel. No-op on a separator, which\n * has no enabled state.\n *\n * @param value - True to enable the item.\n *\n * @returns This item, for method chaining.\n */\n setEnabled(value: boolean): this {\n if (this._config.separator) {\n return this;\n }\n\n this._config.enabled = value;\n\n this.setCursor(value ? \"pointer\" : \"default\");\n this.getAria().setDisabled(!value);\n\n if (value) {\n this.clearForegroundColor();\n } else {\n this.setForegroundColor(\n `var(--ts-ui-${this.getCssVarPrefix()}-item-disabled-color, rgb(170, 170, 170))`\n );\n }\n\n return this;\n }\n\n /**\n * Shared leaf-activation path for a pointer click and {@link activate}.\n * A checkable item that keeps the menu open (`closeOnActivate: false`)\n * flips its own checkmark first; a checkable item that closes the menu\n * (the default) is left alone here, since the menu tears the item down\n * immediately after and there is nothing left to keep in sync.\n */\n private activateLeaf(): void {\n if (this.hasCheck() && this._config.closeOnActivate === false) {\n this._checked = !this._checked;\n this._checkText?.setText(this._checked ? \"✓\" : \"\");\n }\n\n this._onActivate();\n }\n\n /**\n * Activates this item as if the user clicked or pressed Enter.\n *\n * For submenu items, calls `onOpenSubmenu` immediately (skipping the hover delay).\n * For leaf items, calls `onActivate`. Does nothing for disabled or separator items.\n */\n activate(): void {\n if (this.isSeparator() || !this.isEnabled()) {\n return;\n }\n\n if (this.hasSubmenu()) {\n this._onOpenSubmenu(this);\n } else {\n this.activateLeaf();\n }\n }\n\n /**\n * Cancels any pending submenu timer, then defers to the base class for\n * the rest of teardown.\n */\n protected destructor(): void {\n if (this._submenuTimer !== null) {\n clearTimeout(this._submenuTimer);\n this._submenuTimer = null;\n }\n\n super.destructor();\n }\n\n /**\n * Returns the offset from the top of the menu item to the title text's baseline.\n *\n * @returns The baseline offset in pixels, or `null` when the title has no baseline\n * (e.g. for a separator row).\n */\n getBaseline(): number | null {\n if (this._titleText === null) {\n return null;\n }\n\n return this.wrapInnerBaseline(this._titleText.getBaseline());\n }\n\n /**\n * Re-pins the five labels' line boxes to the item's CONTENT height —\n * `MenuItem.HEIGHT` less this item's own vertical chrome. `centerInHeight`\n * pins a label's minimum height as well as its line box, so a label pinned to\n * the outer height cannot shrink into a bordered item's content box and is\n * clipped. Called from the constructor tail and whenever the border changes.\n */\n private updateLabelHeights(): void {\n const perimeter = this.getPerimeterSize();\n // Floor of 1: chrome at or past the row height leaves nothing to centre\n // in, and a zero or negative line-height is not a value CSS should see.\n // The label clips in that case either way — the floor only keeps the\n // written value sane.\n const height = Math.max(1, MenuItem.HEIGHT - perimeter.top - perimeter.bottom);\n\n // Optional chaining, not null checks: a separator has none of these, and a\n // border arriving through the options bag could in principle reach here\n // before the field initializers have run.\n this._checkText?.centerInHeight(height);\n this._iconText?.centerInHeight(height);\n this._titleText?.centerInHeight(height);\n this._shortcutText?.centerInHeight(height);\n this._chevronText?.centerInHeight(height);\n }\n\n /**\n * Applies a border, then re-pins the four labels' line boxes to the item's\n * new content height so a bordered item's labels shrink to fit instead of\n * overrunning the smaller content box.\n *\n * @param options - Border configuration, or a CSS `border` shorthand string.\n * @returns This component, for method chaining.\n */\n setBorder(options: BorderOptions | string): this {\n super.setBorder(options);\n this.updateLabelHeights();\n\n return this;\n }\n\n /**\n * Clears the border, then re-pins the four labels' line boxes back to the\n * item's now-larger content height.\n *\n * @returns This component, for method chaining.\n */\n clearBorder(): this {\n super.clearBorder();\n this.updateLabelHeights();\n\n return this;\n }\n\n /**\n * Positions the five label zones within the item's content box.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n if (this._config.separator) {\n return this;\n }\n\n const box = this.getContentBounds();\n\n if (!box) {\n return this;\n }\n\n // Heights come from the content box, not MenuItem.HEIGHT: the constant\n // is the item's outer height. With no border the two are the same\n // number. The texts' line boxes are pinned to match by\n // updateLabelHeights, so they fit this box rather than overrunning it.\n const H = box.height;\n const totalWidth = box.width;\n const hasShortcut = !!this._config.shortcut && this._shortcutText !== null;\n const hasSub = this.hasSubmenu();\n\n // The menu aligns every item into shared columns; a standalone item falls\n // back to its own check/icon offsets and natural title width.\n const checkZone = this._checkZone ?? (this.hasCheck() ? MenuItem.CHECK_ZONE : 0);\n const iconStart = this._iconStart ?? (checkZone + (this.hasIcon() ? MenuItem.ICON_ZONE : MenuItem.TEXT_INSET));\n const titleColumn = this._titleColumn ?? this.titleTextWidth();\n\n if (this._checkText) {\n this._checkText.setX(box.x + 4);\n this._checkText.setY(box.y);\n this._checkText.setWidth(Math.max(0, checkZone - 4));\n this._checkText.setHeight(H);\n }\n\n if (this._iconGlyph) {\n const fallbackPx = menuIconPx();\n const size = this._iconGlyph.getPreferredSize() ?? { width: fallbackPx, height: fallbackPx };\n const iconY = Math.max(0, Math.floor((H - size.height) / 2));\n\n this._iconGlyph.setX(box.x + checkZone + 4);\n this._iconGlyph.setY(box.y + iconY);\n this._iconGlyph.setWidth(size.width);\n this._iconGlyph.setHeight(size.height);\n } else if (this._iconText) {\n this._iconText.setX(box.x + checkZone + 4);\n this._iconText.setY(box.y);\n this._iconText.setWidth(20);\n this._iconText.setHeight(H);\n }\n\n // Title fills the shared title column, left-aligned from the icon offset.\n if (this._titleText) {\n this._titleText.setX(box.x + iconStart);\n this._titleText.setY(box.y);\n this._titleText.setWidth(titleColumn);\n this._titleText.setHeight(H);\n }\n\n // Chevron: right-justified at the panel's inner edge.\n if (hasSub && this._chevronText) {\n this._chevronText.setX(box.x + totalWidth - MenuItem.RIGHT_PAD - MenuItem.CHEVRON_ZONE);\n this._chevronText.setY(box.y);\n this._chevronText.setWidth(MenuItem.CHEVRON_ZONE);\n this._chevronText.setHeight(H);\n }\n\n // Shortcut: left-justified in the right zone, one gap past the title column.\n if (hasShortcut && this._shortcutText) {\n this._shortcutText.setX(box.x + iconStart + titleColumn + MenuItem.TEXT_GAP);\n this._shortcutText.setY(box.y);\n this._shortcutText.setWidth(this.shortcutTextWidth());\n this._shortcutText.setHeight(H);\n }\n\n return this;\n }\n}\n\nconst MenuItemCallable = callable(MenuItem);\ntype MenuItemCallable = MenuItem;\nexport {\n MenuItem as _MenuItem,\n MenuItemCallable as MenuItem\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { ComponentOptions } from \"~/core/Component.js\";\nimport type { MenuItemCSSVarPrefix } from \"~/component/container/MenuItem.js\";\nimport { MenuRow } from \"~/component/container/MenuRow.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuSeparator}.\n *\n * @category Components\n */\nexport interface MenuSeparatorOptions extends ComponentOptions {\n}\n\n/**\n * User-overridable default fill; a caller-supplied `backgroundColor` wins.\n */\nconst _defaultMenuSeparatorOptions: Partial<MenuSeparatorOptions> = {\n backgroundColor: \"transparent\",\n};\n\n/**\n * A horizontal separator rule used inside a [`Menu`](/api/overlay/classes/Menu) panel to visually group menu items.\n *\n * Renders as a thin border line. Width is set externally by `Menu.doLayout()`.\n *\n * @category Components\n */\nclass MenuSeparator extends MenuRow<MenuSeparatorOptions> {\n\n /** Fixed pixel height of every separator. */\n static readonly HEIGHT: number = 9;\n\n /**\n * Constructs a MenuSeparator with a thin horizontal rule appearance.\n *\n * @param cssVarPrefix - Selects which CSS-variable family supplies the border colour. Defaults to `'menu-bar'`.\n * @param options - Optional construction options.\n * @param subclassDefaults - Per-subclass default bag layered over this\n * class's defaults; subclasses forward their `_defaultXxxOptions`\n * constant here.\n */\n constructor(\n cssVarPrefix: MenuItemCSSVarPrefix = \"menu-bar\",\n options?: MenuSeparatorOptions,\n subclassDefaults?: Partial<MenuSeparatorOptions>,\n ) {\n super(options, { ..._defaultMenuSeparatorOptions, ...(subclassDefaults ?? {}) });\n\n this.setHeight(MenuSeparator.HEIGHT);\n this.setPreferredSize({ width: 0, height: MenuSeparator.HEIGHT });\n this.setElementCSSRule(\n \"borderTop\",\n `1px solid var(--ts-ui-${cssVarPrefix}-separator-color, rgb(220, 220, 220))`\n );\n this.setElementCSSRule(\"margin\", \"4px 0\");\n this.getAria().setRole(\"separator\");\n }\n\n /**\n * Returns `true` — a `MenuSeparator` is always a separator row.\n *\n * @returns `true`.\n */\n isSeparator(): boolean {\n return true;\n }\n}\n\nconst MenuSeparatorCallable = callable(MenuSeparator);\ntype MenuSeparatorCallable = MenuSeparator;\nexport {\n MenuSeparator as _MenuSeparator,\n MenuSeparatorCallable as MenuSeparator\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle, Rect } from \"~/core/DOM.js\";\nimport { LayerManager, DismissableLayer, LayerDismissMode } from \"~/core/LayerManager.js\";\nimport { positionAnchoredFlexible, positionFlexibleAnchored } from \"~/core/OverlayPosition.js\";\nimport { fadeShow, fadeHideAndDetach } from \"~/core/OverlayFade.js\";\nimport type { Animation } from \"~/core/Animation.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { MenuItem, MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { MenuSeparator } from \"~/component/container/MenuSeparator.js\";\nimport { MenuRow } from \"~/component/container/MenuRow.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Util } from \"~/core/Util.js\";\n\n/**\n * Pixel bounds a content-sized menu panel clamps to. Menus size to their widest\n * item (via the layout manager's preferred width); the floor keeps a short menu\n * from looking cramped, and the ceiling stops a very long label running off-screen\n * (the item's title ellipsizes past it).\n */\nconst MIN_MENU_WIDTH = 120;\nconst MAX_MENU_WIDTH = 360;\n\n/** Duration (ms) of the fade-in / fade-out transitions on every menu panel. */\nconst MENU_ANIM_DURATION_MS = 120;\n\n/** Pixels kept between a clamped menu and the viewport edge so the panel\n * border and shadow are never flush against the screen. Mirrors the small\n * inset used by other floating panels; purely cosmetic breathing room. */\nconst VIEWPORT_MARGIN = 4;\n\n/**\n * A zero-size rect at a cursor point. A cursor is a degenerate anchor: with\n * `left === right` and `top === bottom`, the adjacency and alignment flips\n * collapse to the same operation — grow down-right from the point, or end at it.\n * That is native context-menu behaviour, so a pointer needs no separate path.\n */\nfunction pointRect(x: number, y: number): Rect {\n return { x, y, left: x, top: y, right: x, bottom: y, width: 0, height: 0 };\n}\n\n/**\n * A floating menu panel that operates in one of two modes:\n *\n * - **Rebuild mode** (`new Menu()`): a right-click context menu. Items are passed per\n * `show(x, y, items)` call and disposed on the next show or hide.\n * - **Persistent mode** (`new Menu(items, onClose)`): a [`MenuBar`](/api/component/menubar/classes/MenuBar) dropdown. Items are\n * built once in the constructor and reused across `open()` / `close()` cycles.\n *\n * The two API surfaces are disjoint by mode:\n * `show()` / `hide()` / `setMenuWidth()` are valid only in rebuild mode;\n * `open()` / `close()` / focus and submenu methods are valid only in persistent mode.\n *\n * @example\n * ```typescript\n * // Rebuild mode — right-click context menu\n * const menu = new Menu();\n * Event.addListener(myComponent, 'contextmenu', (e: MouseEvent): Event.ListenerResult => {\n * menu.show(e.clientX, e.clientY, [\n * { text: 'Cut', action: () => cut() },\n * { separator: true },\n * { text: 'Paste', action: () => paste() },\n * ]);\n *\n * return { prevent: true };\n * });\n *\n * // Persistent mode — MenuBar dropdown\n * const panel = new Menu(\n * [{ text: 'Save', shortcut: 'Ctrl+S', action: () => save() }],\n * () => bar.closeMenu()\n * );\n * panel.open(buttonElement);\n * ```\n *\n * @category Components\n */\nclass Menu extends Component implements DismissableLayer {\n\n private readonly _persistent: boolean;\n // A persistent menu's items provider, kept so each open() re-resolves it —\n // null when the items were passed as a fixed array (built once, never rebuilt).\n private readonly _itemsProvider: (() => MenuItemConfig[]) | null;\n private readonly _onClose: (() => void) | null;\n // In both modes every entry is registered via `addComponent` (see\n // `showAnchored` / `buildPersistentItems`), so the `destructor()` override\n // below has no item disposal to do — the base class's recursive teardown\n // already disposes each item; the override exists only to cancel the\n // in-flight fades. (A prior override manually\n // re-disposed them here, guarded to persistent mode only via\n // `assertPersistentMode`. That guard was never reachable through ancestor\n // teardown — no `Menu` anywhere in the library is itself registered via\n // `addComponent` (`Table._columnContextMenu` / `TabBar._contextMenu` are\n // plain fields, never added as children), so an ancestor's `destructor()`\n // recursion can never reach a `Menu` at all. The loop was removed simply\n // because it was redundant, not because the guard made it unsafe.)\n private _menuItems: MenuRow[] = [];\n\n // In-flight fades, cancelled on teardown so their fallback timers cannot\n // fire against this menu's released element handle.\n private _fadeShowAnimation: Animation.CancelHandle | null = null;\n private _fadeHideAnimation: Animation.CancelHandle | null = null;\n private _focusedIndex: number = -1;\n private _openSubmenuPanel: Menu | null = null;\n private _openSubmenuItem: MenuItem | null = null;\n private _excludedEl: Handle | null = null;\n // An explicit rebuild-mode width set via setMenuWidth; null means size to\n // content (the widest item, clamped to [MIN_MENU_WIDTH, MAX_MENU_WIDTH]).\n private _menuWidth: number | null = null;\n private _rebuildOnClose: (() => void) | null = null;\n private _currentOpener: Handle | null = null;\n // Rebuild-mode: when true, each show() scrolls the menu to the bottom after\n // layout so the latest (bottom-most) items are visible on open.\n private _scrollToBottomOnShow: boolean = false;\n // True for a submenu panel (set by handleItemOpenSubmenu right after\n // construction): a submenu is single-use, a fresh instance built on every\n // open, so once closed it is never reused — unlike a persistent top-level\n // dropdown, its close() should dispose it once its fade completes rather\n // than just fading-and-detaching it for reuse.\n private _disposeOnClose: boolean = false;\n\n /**\n * Constructs a Menu. With no arguments, a rebuild-mode (right-click context)\n * menu whose items are supplied per `show()` call. With `items` (and `onClose`),\n * a persistent-mode ([`MenuBar`](/api/component/menubar/classes/MenuBar) dropdown)\n * menu whose items are built immediately and reused across `open()` / `close()`\n * cycles.\n *\n * A single optional-parameter signature, deliberately not an overload pair: the\n * `callable()` export's construct/call type is derived via TS `ConstructorParameters`,\n * which captures only the *last* overload — an overload pair would drop the\n * no-arg form and make `Menu()` a type error for consumers.\n *\n * @param items - The menu item configurations, or a provider called once now\n * to produce them. A submenu is rebuilt on each open, so a provider passed\n * for a submenu's items is re-invoked every time that submenu opens. Omit for\n * a rebuild-mode context menu.\n * @param onClose - Callback invoked when the panel should close (item activated or outside click).\n */\n constructor(items?: MenuItemConfig[] | (() => MenuItemConfig[]), onClose?: () => void) {\n super();\n\n this._persistent = items !== undefined;\n this._itemsProvider = typeof items === \"function\" ? items : null;\n this._onClose = onClose ?? null;\n\n const vbox = new VBox();\n\n vbox.setComponentSpacing(0);\n vbox.setStretching(true);\n\n this.setLayoutManager(vbox);\n\n if (this._persistent) {\n this.applyPersistentChrome();\n // Fixed array: build once now. A provider defers to open(), which\n // re-resolves it on every open (so the sole resolution is per-open).\n if (this._itemsProvider === null) {\n this.buildPersistentItems(items as MenuItemConfig[]);\n }\n } else {\n this.applyRebuildChrome();\n }\n }\n\n /**\n * Lays the menu's items into aligned columns and returns the panel width.\n * A leading check column is reserved when any item declares `checked`, so\n * a checked and an unchecked row's icon and title still start at the same\n * x position. Every title shares one column sized to the widest title;\n * the shortcut/chevron right zone is the widest of the shortcut column\n * and a submenu chevron, so shortcuts left-justify in a column and\n * chevrons right-justify at the edge. The width is clamped to\n * `[MIN_MENU_WIDTH, MAX_MENU_WIDTH]`; when the ceiling bites, the title\n * column shrinks and its titles ellipsize. Measured from the items\n * directly so a menu reused across shows re-measures its new content. A\n * custom row (built from a `MenuItemConfig.row` factory) contributes no\n * title/shortcut metrics by default, so the panel's natural width is\n * floored with the widest row's own `getContentWidth()` report.\n *\n * The clamp and every row's column geometry are computed against the\n * panel's CONTENT box (matching `RIGHT_PAD`'s own \"at the panel's inner\n * edge\" contract); the panel's own left/right border is added only to the\n * returned value, so a caller applying it via `setWidth` (a border-box\n * write) hands rows exactly the content width this method measured them\n * against — short-changing it here previously starved every row's\n * content box by the panel's border thickness, invisible on a\n * `MenuItem`'s ellipsized title but a visible hard clip on an unellipsized\n * custom row's content (e.g. `CheckboxMenuRow`'s label).\n *\n * @returns The clamped panel width in pixels, border included.\n */\n private layOutColumns(): number {\n const rows = this._menuItems.filter(row => !row.isSeparator());\n\n const checkZone = rows.some(r => r.hasCheck()) ? MenuItem.CHECK_ZONE : 0;\n const iconStart = checkZone + (rows.some(r => r.hasIcon()) ? MenuItem.ICON_ZONE : MenuItem.TEXT_INSET);\n const maxTitle = rows.reduce((m, r) => Math.max(m, r.titleTextWidth()), 0);\n const maxShortcut = rows.reduce((m, r) => Math.max(m, r.shortcutTextWidth()), 0);\n const hasChevron = rows.some(r => r.hasSubmenu());\n const rightZone = Math.max(maxShortcut, hasChevron ? MenuItem.CHEVRON_ZONE : 0);\n const rightReserve = rightZone > 0 ? MenuItem.TEXT_GAP + rightZone : 0;\n // A custom row contributes no title/shortcut metrics, so the panel\n // would be too narrow for it; floor the natural width with the widest\n // row's own report instead. `getContentWidth()` excludes any left\n // inset of its own — a row cannot know at this point whether a\n // sibling row widens the shared `iconStart` via `hasCheck()` /\n // `hasIcon()` — so `iconStart` (just computed above) is added here,\n // uniformly, on Menu's side.\n const maxContent = iconStart + rows.reduce((m, r) => Math.max(m, r.getContentWidth()), 0);\n\n const natural = Math.max(iconStart + maxTitle + rightReserve + MenuItem.RIGHT_PAD, maxContent);\n const width = Math.min(MAX_MENU_WIDTH, Math.max(MIN_MENU_WIDTH, natural));\n\n // When the ceiling bites, the title column absorbs the shortfall (titles\n // ellipsize); otherwise it is the full widest-title width.\n const titleColumn = Math.min(maxTitle, width - iconStart - rightReserve - MenuItem.RIGHT_PAD);\n\n for (const row of rows) {\n row.setColumns(checkZone, iconStart, titleColumn);\n }\n\n const border = this.getBorderSize();\n\n return width + border.left + border.right;\n }\n\n /**\n * Shows the menu at the given viewport coordinates, replacing any previously\n * displayed items with the new list. **Rebuild-mode only.**\n *\n * The menu grows down-right from the cursor; when there is no room it flips\n * so its bottom / right edge ends at the cursor, never covering it. A menu\n * taller than the room on the side it lands on is capped there and scrolls.\n *\n * @param x - Horizontal viewport coordinate (e.g. `MouseEvent.clientX`).\n * @param y - Vertical viewport coordinate (e.g. `MouseEvent.clientY`).\n * @param configs - Ordered list of item descriptors to render.\n * @param onClose - Optional callback invoked once when the menu next closes\n * (item activated or dismissed by an outside click), letting the opener\n * revert an open-state affordance such as a rotated dropdown chevron.\n * @param excludeEl - Optional element whose subtree is exempt from the\n * outside-click-to-close check. Pass the trigger that opened the menu (e.g.\n * a [`SplitButton`](/api/component/button/classes/SplitButton) chevron) so a\n * pointerdown on it does not self-close the menu before the trigger's own\n * click can toggle it shut — mirroring [`MenuBar`](/api/component/menubar/classes/MenuBar)'s\n * dropdown-button exclusion.\n */\n show(x: number, y: number, configs: MenuItemConfig[], onClose?: () => void, excludeEl?: Handle | null): this {\n this.assertRebuildMode(\"show\");\n\n // The placement primitives only guarantee an on-screen result for an anchor\n // inside the viewport. A cursor from a real event always is; show() is public,\n // so pin it here rather than in showAnchored, which also takes real trigger\n // rects that may legitimately extend past a viewport edge.\n const vp = DOM.source.getViewportSize();\n\n return this.showAnchored(\n pointRect(Util.clamp(x, 0, vp.width), Util.clamp(y, 0, vp.height)),\n configs, onClose ?? null, excludeEl ?? null,\n );\n }\n\n /**\n * Rebuild-mode geometry-and-content core shared by {@link show} (pointer-anchored)\n * and {@link toggleFor} (rect-anchored). Both grow down-right from `anchorRect`\n * and flip per axis when the room runs short. Tears down and rebuilds the item\n * list, then resolves `anchorRect` to a placement and applies the resulting\n * position and height clamp.\n *\n * @param anchorRect - The cursor point (as a zero-size rect) or trigger rect to\n * place the panel against.\n * @param configs - Ordered list of item descriptors to render.\n * @param onClose - Callback invoked once when the menu next closes, or `null`.\n * @param excludeEl - Element whose subtree is exempt from the outside-click\n * check, or `null`.\n */\n private showAnchored(anchorRect: Rect, configs: MenuItemConfig[], onClose: (() => void) | null, excludeEl: Handle | null): this {\n this._rebuildOnClose = onClose;\n this._excludedEl = excludeEl;\n\n this._menuItems = [];\n this.disposeAllComponents();\n\n this.pauseLayout();\n\n for (const config of configs) {\n let row: MenuRow;\n\n if (config.separator === true) {\n row = new MenuSeparator(\"context-menu\");\n } else if (config.row) {\n row = config.row();\n row.setCssVarPrefix(\"context-menu\");\n row.setMenuCloseHandler(() => { this.dismissAll(); });\n } else {\n row = new MenuItem(\n config,\n () => {\n config.action?.();\n\n if (config.closeOnActivate === false) {\n this.closeOpenSubmenu();\n } else {\n this.hide();\n }\n },\n (hoveredItem) => { this.handleItemOpenSubmenu(hoveredItem); },\n \"context-menu\"\n );\n }\n\n this.addComponent(row);\n this._menuItems.push(row);\n }\n\n this.resumeLayout();\n\n const contentWidth = this.layOutColumns();\n const naturalWidth = this._menuWidth ?? contentWidth;\n\n this.setWidth(naturalWidth);\n\n // A reused menu still carries the previous show's max-height — set by\n // `applyViewportHeightClamp` to that show's available room. Because\n // `getPreferredSize` clamps to the component's own max, leaving it in\n // place would cap the new content at the old height, so a menu that\n // grew past its previous size would neither expand nor reserve a\n // scrollbar gutter on reopen. Clear it before measuring.\n this.setMaxSize({ width: Number.MAX_VALUE, height: Number.MAX_VALUE });\n\n const totalHeight = this.getPreferredSize()?.height ?? 0;\n\n const el = this.getElement(true)!;\n\n const vp = DOM.source.getViewportSize();\n\n // First pass at the natural width resolves the vertical room. Width does not\n // affect the vertical placement, so `available` stays correct after the\n // scrollbar-gutter widening below; only `x` needs the second pass.\n const available = positionAnchoredFlexible(anchorRect, { width: naturalWidth, height: totalHeight }, vp, VIEWPORT_MARGIN).available;\n\n // When the content is taller than the room on the side the menu lands on,\n // `applyViewportHeightClamp` caps the height and the `overflow-y: auto`\n // scrollbar engages. Reserve its width as a right inset — and widen the\n // content-sized panel to match — so items lay out beside the native\n // scrollbar instead of beneath it. `Component.getInnerSize` subtracts the\n // inset, so the VBox stretches items to `naturalWidth`, unchanged from the\n // no-scroll case.\n const gutter = totalHeight > available ? DOM.source.getScrollBarWidth() : 0;\n\n this.setInsets(new Insets(4, gutter, 4, 0));\n this.setWidth(naturalWidth + gutter);\n\n const placement = positionAnchoredFlexible(anchorRect, { width: this.getWidth(), height: totalHeight }, vp, VIEWPORT_MARGIN);\n\n this.setX(placement.x);\n this.setY(placement.y);\n this.applyViewportHeightClamp(placement.available, totalHeight);\n\n this.scheduleLayout();\n\n LayerManager.mount(el);\n\n this.setVisible(true);\n this.fadeIn();\n\n // Join the central layer tree: the manager owns the outside-pointerdown /\n // window-blur / Escape dismissal and stamps the z-index. Registering while\n // whatever opened the menu is topmost links it under that layer, so a\n // context menu raised over a modal dialog inherits the dialog band and\n // paints in front of it.\n LayerManager.register(this);\n this.setZIndex(LayerManager.getZIndex(this));\n\n // Optionally reveal the bottom of the list on open (e.g. a history menu\n // whose latest entries sit at the bottom). Flush the scheduled layout\n // first so the item heights are committed and `getMaxScrollTop` is\n // accurate; when nothing overflows the offset is 0 and this is a no-op.\n if (this._scrollToBottomOnShow) {\n this.flushLayout();\n this.setScrollTop(this.getMaxScrollTop());\n }\n\n return this;\n }\n\n /**\n * Opens or closes the menu for a given opener element. **Rebuild-mode only.**\n *\n * This is the toggle form of {@link show} for a left-click dropdown trigger\n * (e.g. a [`SplitButton`](/api/component/button/classes/SplitButton) chevron\n * or a [`ToolBar`](/api/component/menubar/classes/ToolBar) overflow button):\n * the opener is excluded from the outside-click dismissal (so its own\n * pointerdown does not self-close the menu) and remembered, so a second press\n * of the *same* opener closes the menu instead of reopening it. Pressing a\n * *different* opener while the menu is open switches it to that opener. The\n * menu opens below `anchorRect` and flips above it when the room below is\n * short, right-aligning to it when the left alignment overflows. Plain\n * {@link show} stays the right call for right-click context menus, which\n * should reposition — not close — on a repeat trigger; the remaining\n * distinction from `toggleFor` is the toggle identity, the opener exclusion,\n * and the empty-list suppression below.\n *\n * An empty `configs` opens nothing: the menu is not shown, `onClose` fires once\n * so the opener can revert an open-state affordance, and no opener state is\n * recorded. A repeat press of the *same* opener still closes an open menu — the\n * toggle-shut branch runs before the empty check.\n *\n * @param openerEl - The element that triggers the menu; excluded from the\n * outside-click check and used as the toggle identity.\n * @param anchorRect - The trigger's viewport rect (e.g.\n * `DOM.source.getViewportRect(button)`); the menu opens below it and flips\n * above it when the room below is short.\n * @param configs - Ordered list of item descriptors to render. An empty list\n * opens nothing (see above).\n * @param onClose - Optional callback invoked once when the menu next closes,\n * or when an empty `configs` suppresses the open.\n *\n * @returns This menu, for method chaining.\n */\n toggleFor(openerEl: Handle, anchorRect: Rect, configs: MenuItemConfig[], onClose?: () => void): this {\n this.assertRebuildMode(\"toggleFor\");\n\n // Same opener fired again while its menu is open: close it. Its pointerdown\n // was excluded from the dismissal above, so this click is the toggle-shut.\n // MUST stay ahead of the empty check below — a provider that has since gone\n // empty must still be able to close the panel it opened.\n if (this._currentOpener === openerEl) {\n this.hide();\n\n return this;\n }\n\n // Nothing to show: an empty panel is never useful, so open nothing. `onClose`\n // still fires so the opener reverts an optimistic open-state affordance (e.g.\n // SplitButton's spun-up chevron), and `_currentOpener` stays untouched, so the\n // next press of this opener is a fresh open rather than a stale toggle-shut.\n if (configs.length === 0) {\n onClose?.();\n\n return this;\n }\n\n // Closed, or open for a different opener: (re)show anchored for this one.\n this.showAnchored(anchorRect, configs, onClose ?? null, openerEl);\n this._currentOpener = openerEl;\n\n return this;\n }\n\n /**\n * Hides the menu and detaches it from the DOM. **Rebuild-mode only.**\n *\n * The instance remains alive and can be shown again by calling `show()`.\n */\n hide(): this {\n this.assertRebuildMode(\"hide\");\n\n this.closeOpenSubmenu();\n\n LayerManager.unregister(this);\n\n this.fadeOutAndDetach();\n\n // Forget the toggle opener so the next `toggleFor` for it opens rather\n // than seeing a stale match and closing an already-closed menu.\n this._currentOpener = null;\n\n // Fire the per-show close callback exactly once, clearing it first so a\n // later bare `hide()` (or re-show) can't re-invoke a stale opener's hook.\n const onClose = this._rebuildOnClose;\n this._rebuildOnClose = null;\n onClose?.();\n\n return this;\n }\n\n /**\n * Returns the current pixel width of the rebuild-mode menu panel (the\n * content-fit width, or an explicit override set via {@link setMenuWidth}).\n *\n * @returns The current panel width in pixels.\n */\n getMenuWidth(): number {\n return this.getWidth();\n }\n\n /**\n * Pins the rebuild-mode menu panel to an explicit width, overriding the\n * default content-fit sizing. **Rebuild-mode only.**\n *\n * @param width - Width in pixels.\n */\n setMenuWidth(width: number): this {\n this.assertRebuildMode(\"setMenuWidth\");\n\n this._menuWidth = width;\n\n return this;\n }\n\n /**\n * Updates the `enabled` state of the row at `index`, in place — without\n * closing, rebuilding, or re-animating the panel. **Rebuild-mode only.**\n *\n * Lets a caller that deliberately keeps the panel open after an action\n * (e.g. a `CheckboxMenuRow`'s own toggle, which never closes the menu —\n * see `MenuItemConfig.closeOnActivate`) push a live availability change\n * into a *different*, sibling row, rather than leaving it stale until the\n * panel is next closed and reopened. No-op when `index` is out of range,\n * or names a separator or a custom `row()` factory row — a `MenuRow`\n * built from a factory owns its own enabled state (see\n * `MenuRow.isEnabled`) and has no shared update surface to push into.\n *\n * @param index - Zero-based index into the `configs` array passed to the\n * `show()` / `toggleFor()` call that built the currently-displayed rows.\n * @param enabled - The row's new enabled state.\n *\n * @returns This menu, for method chaining.\n */\n setItemEnabled(index: number, enabled: boolean): this {\n this.assertRebuildMode(\"setItemEnabled\");\n\n const row = this._menuItems[index];\n\n if (row instanceof MenuItem) {\n row.setEnabled(enabled);\n }\n\n return this;\n }\n\n /**\n * Controls whether the menu scrolls to the bottom of its item list each time\n * it is shown. **Rebuild-mode only.**\n *\n * Use this for a menu whose latest entries sit at the bottom (e.g. a\n * chronological notification history), so the most recent items are visible\n * on open rather than the user having to scroll down. A no-op when the list\n * fits without scrolling.\n *\n * @param value - `true` to scroll to the bottom on every show.\n *\n * @returns This menu, for method chaining.\n */\n setScrollToBottomOnShow(value: boolean): this {\n this.assertRebuildMode(\"setScrollToBottomOnShow\");\n\n this._scrollToBottomOnShow = value;\n\n return this;\n }\n\n /**\n * Opens this panel positioned below the anchor element (top-level) or to the\n * right of the parent panel (submenu). Appends to `document.documentElement`.\n * **Persistent-mode only.**\n *\n * @param anchorEl - The `HTMLElement` of the triggering button or menu item.\n * @param parentPanel - When set, positions the panel as a submenu of this parent.\n */\n open(anchorEl: Handle, parentPanel?: Menu): this {\n this.assertPersistentMode(\"open\");\n\n // A provider-sourced menu re-resolves its items on every open, so labels\n // and enabled state reflect current state each time it is shown (a submenu\n // is a fresh panel per open, but a reused top-level dropdown is not).\n if (this._itemsProvider) {\n this.rebuildPersistentItems(this._itemsProvider());\n }\n\n // Clear the previous open's max-height (set by `applyViewportHeightClamp`)\n // before measuring, so a provider-sourced dropdown that grew reflects its\n // new content height rather than being capped at the old available room.\n this.setMaxSize({ width: Number.MAX_VALUE, height: Number.MAX_VALUE });\n\n const totalHeight = this.getPreferredSize()?.height ?? (this._menuItems.length * MenuItem.HEIGHT + 8);\n const width = this.getWidth();\n\n const el = this.getElement(true)!;\n LayerManager.mount(el);\n\n const vp = DOM.source.getViewportSize();\n\n if (parentPanel) {\n const parentEl = parentPanel.getElement();\n const parentRect = parentEl\n ? DOM.source.getElementRect(parentEl)\n : { left: 0, right: 0, top: 0, bottom: 0 };\n const anchorRect = DOM.source.getElementRect(anchorEl);\n\n let x = parentRect.right;\n\n if (x + width > vp.width) {\n x = parentRect.left - width;\n }\n\n // A submenu grows down from the anchor item's top, and flips up\n // against that same top edge when there is more room above.\n const y = this.placeVertically(anchorRect.top, anchorRect.top, totalHeight, vp.height);\n\n this.setAutoCommitStyle(false);\n this.setX(Math.max(0, x));\n this.setY(Math.max(0, y));\n this.setAutoCommitStyle(true);\n } else {\n const anchorRect = DOM.source.getElementRect(anchorEl);\n\n let x = anchorRect.left;\n\n if (x + width > vp.width) {\n x = vp.width - width;\n }\n\n // A top-level dropdown grows down from the anchor's bottom, and\n // flips up against the anchor's top when there is more room above.\n const y = this.placeVertically(anchorRect.bottom, anchorRect.top, totalHeight, vp.height);\n\n this.setAutoCommitStyle(false);\n this.setX(Math.max(0, x));\n this.setY(Math.max(0, y));\n this.setAutoCommitStyle(true);\n }\n\n const anchorId = DOM.source.getId(anchorEl);\n\n if (anchorId) {\n this.getAria().setLabelledBy(anchorId);\n }\n\n this.setVisible(true);\n this.doLayout();\n this.fadeIn();\n\n // Join the central layer tree so the manager owns dismissal and the\n // z-stamp. A submenu opened via `open(item, this)` registers while its\n // parent menu is topmost, so it links under the parent — a pointerdown\n // inside the submenu counts as inside the parent (cross-portal\n // containment) and keeps the whole chain open.\n LayerManager.register(this);\n this.setZIndex(LayerManager.getZIndex(this));\n\n return this;\n }\n\n /**\n * Closes this panel and any open child submenus. Detaches from the DOM.\n * **Persistent-mode only.**\n */\n close(): this {\n this.assertPersistentMode(\"close\");\n\n this.closeOpenSubmenu();\n\n this.setFocusedIndex(-1);\n this.clearItemHighlights();\n\n LayerManager.unregister(this);\n\n this.fadeOutAndDetach(this._disposeOnClose ? () => this.dispose() : undefined);\n\n return this;\n }\n\n /**\n * Clears the hover highlight from every item. A click that activates an item\n * dismisses the menu, detaching the panel from the DOM under the pointer; the\n * browser fires no `mouseout` for an element removed under a stationary\n * pointer, so the hovered item's `setFocused(true)` is never undone by\n * `_onMouseOut`. `setFocusedIndex(-1)` only resets the keyboard-tracked item,\n * not one highlighted purely by hover. Persistent-mode menus reuse these item\n * elements across open/close, so without this sweep the stale highlight\n * reappears the next time the menu opens.\n */\n private clearItemHighlights(): void {\n for (const row of this._menuItems) {\n if (row.isNavigable()) {\n row.setFocused(false);\n }\n }\n }\n\n /**\n * Plays the standard menu-panel entrance fade. Cancels any in-flight\n * fade-out so the deferred detach skips removing the still-visible panel.\n */\n private fadeIn(): void {\n this._fadeShowAnimation?.cancel();\n this._fadeShowAnimation = fadeShow(this, { durationMs: MENU_ANIM_DURATION_MS });\n }\n\n /**\n * Plays the standard menu-panel exit fade, then hides and detaches the\n * panel from the DOM when the transition completes. A fresh `show()` /\n * `open()` during the fade cancels the deferred detach.\n *\n * @param onComplete - Optional callback run once the fade-and-detach\n * finishes (see {@link _disposeOnClose}), after the panel is hidden and\n * detached.\n */\n private fadeOutAndDetach(onComplete?: () => void): void {\n this._fadeHideAnimation?.cancel();\n this._fadeHideAnimation = fadeHideAndDetach(this, { durationMs: MENU_ANIM_DURATION_MS, onComplete });\n }\n\n /**\n * Cancels any in-flight fade, then defers to the base class. Cancelling\n * first keeps a fade's fallback timer from firing after `super.destructor()`\n * has released this menu's element handle.\n */\n protected destructor(): void {\n this._fadeShowAnimation?.cancel();\n this._fadeShowAnimation = null;\n this._fadeHideAnimation?.cancel();\n this._fadeHideAnimation = null;\n\n // A still-open submenu is a raw field, not a registered child (see the\n // class comment above _menuItems: no Menu anywhere in the library is\n // registered via addComponent), so the child recursion in\n // super.destructor() below can never reach it.\n this._openSubmenuPanel?.dispose();\n this._openSubmenuPanel = null;\n\n // Cancelling the fades above suppresses whatever fade-completion callback\n // would otherwise have unregistered this menu from the layer tree\n // (mirrors AnimatedDropdown.destructor()). Idempotent, so an\n // already-closed menu costs nothing.\n LayerManager.unregister(this);\n\n super.destructor();\n }\n\n /**\n * Moves keyboard focus to the item at the given index. Pass `-1` to clear focus.\n * **Persistent-mode only.**\n *\n * @param index - Zero-based item index, or `-1` to clear.\n */\n focusItem(index: number): this {\n this.assertPersistentMode(\"focusItem\");\n\n this.setFocusedIndex(index);\n\n return this;\n }\n\n /**\n * Moves focus to the next focusable item, wrapping around and skipping separators.\n * **Persistent-mode only.**\n */\n focusNext(): this {\n this.assertPersistentMode(\"focusNext\");\n\n let next = this._focusedIndex + 1;\n\n while (next < this._menuItems.length && this.isItemSkipped(next)) {\n next++;\n }\n\n if (next >= this._menuItems.length) {\n next = 0;\n\n while (next < this._menuItems.length && this.isItemSkipped(next)) {\n next++;\n }\n }\n\n this.setFocusedIndex(next);\n\n return this;\n }\n\n /**\n * Moves focus to the previous focusable item, wrapping around and skipping separators.\n * **Persistent-mode only.**\n */\n focusPrev(): this {\n this.assertPersistentMode(\"focusPrev\");\n\n let prev = this._focusedIndex - 1;\n\n while (prev >= 0 && this.isItemSkipped(prev)) {\n prev--;\n }\n\n if (prev < 0) {\n prev = this._menuItems.length - 1;\n\n while (prev >= 0 && this.isItemSkipped(prev)) {\n prev--;\n }\n }\n\n this.setFocusedIndex(prev);\n\n return this;\n }\n\n /**\n * Activates the currently focused item. No-op when no item is focused or the item\n * is disabled or a separator. **Persistent-mode only.**\n */\n activateFocused(): void {\n this.assertPersistentMode(\"activateFocused\");\n\n if (this._focusedIndex < 0 || this._focusedIndex >= this._menuItems.length) {\n return;\n }\n\n const row = this._menuItems[this._focusedIndex];\n\n if (row.isNavigable() && row.isEnabled()) {\n row.activate();\n }\n }\n\n /**\n * Returns the index of the currently focused item, or `-1` if no item is focused.\n * **Persistent-mode only.**\n *\n * @returns The focused item index, or -1.\n */\n getFocusedIndex(): number {\n this.assertPersistentMode(\"getFocusedIndex\");\n\n return this._focusedIndex;\n }\n\n /**\n * Sets an element whose subtree is excluded from the outside-click-to-close check.\n * **Persistent-mode only.**\n *\n * Used by [`MenuBar`](/api/component/menubar/classes/MenuBar) to prevent a pointerdown on its own buttons from closing the panel\n * before the button's click handler has a chance to toggle the menu.\n *\n * @param el - The element to exclude, or `null` to clear.\n */\n setExcludedElement(el: Handle | null): this {\n this.assertPersistentMode(\"setExcludedElement\");\n\n this._excludedEl = el;\n\n return this;\n }\n\n /**\n * Returns the element whose subtree is currently excluded from the\n * outside-click-to-close check, or `null` if none is set.\n *\n * @returns The excluded element, or null.\n */\n getExcludedElement(): Handle | null {\n return this._excludedEl;\n }\n\n /**\n * Applies the persistent-mode chrome (MenuBar dropdown CSS variables, aria role).\n */\n private applyPersistentChrome(): void {\n this.setBackgroundColor(\"var(--ts-ui-menu-bar-panel-bg, rgb(255, 255, 255))\");\n this.setInsets(new Insets(4, 0, 4, 0));\n this.setBorder({ border: \"1px solid var(--ts-ui-menu-bar-panel-border, rgb(200, 200, 200))\" });\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setShadow(\"var(--ts-ui-menu-bar-panel-shadow, 2px 4px 8px rgba(0, 0, 0, 0.15))\");\n this.getAria().setRole(\"menu\");\n this.setContain(\"layout\");\n\n this.enableVerticalScroll();\n }\n\n /**\n * Applies the rebuild-mode chrome (right-click context-menu CSS variables).\n */\n private applyRebuildChrome(): void {\n this.setVisible(false);\n this.setBackgroundColor(\"var(--ts-ui-context-menu-bg, rgb(255, 255, 255))\");\n this.setInsets(new Insets(4, 0, 4, 0));\n this.setBorder({ border: \"1px solid var(--ts-ui-context-menu-border, rgb(200, 200, 200))\" });\n this.setBorderRadius(\"var(--ts-ui-border-radius, 4px)\");\n this.setShadow(\"var(--ts-ui-context-menu-shadow, 2px 4px 8px rgba(0, 0, 0, 0.15))\");\n this.setContain(\"layout\");\n\n this.enableVerticalScroll();\n }\n\n /**\n * Wires the native vertical-scroll primitives so an over-tall menu scrolls\n * its items instead of overflowing the clamped panel height. This is the\n * `\"y\"` case of [`Panel.setAutoScroll`](/api/core/classes/Panel#setautoscroll)\n * replicated directly: `overflow-x: hidden` (so a `visible` x-axis does not\n * compute to `auto` and sprout a spurious horizontal scrollbar), `overflow-y:\n * auto`, and the layout manager's vertical overflow flag so the `VBox` lays\n * items out past the clamped inner height rather than compressing them.\n */\n private enableVerticalScroll(): void {\n this.setOverflowX(\"hidden\");\n this.setOverflowY(\"auto\");\n\n (this.getLayoutManager() as VBox).setOverflowing(false, true);\n }\n\n /**\n * Caps the menu's height to the vertical room available at its anchored\n * position, then applies the content height. Because `Menu` is a\n * `clampsToContentSize()` component, `setHeight` runs `contentHeight` through\n * `clampHeight`, which caps it at `availableHeight`; when the content fits no\n * clamp fires, and when it overflows the height is capped and the\n * `overflow-y: auto` scrollbar engages. Width is left unconstrained via the\n * `Number.MAX_VALUE` \"no constraint\" sentinel — horizontal scroll is a\n * non-goal.\n *\n * @param availableHeight - Vertical room (px) the menu may occupy at its anchor.\n * @param contentHeight - The unclamped preferred height (px) of the menu's items.\n */\n private applyViewportHeightClamp(availableHeight: number, contentHeight: number): void {\n this.setMaxSize({ width: Number.MAX_VALUE, height: Math.max(0, availableHeight) });\n this.setHeight(contentHeight);\n }\n\n /**\n * Resolves the persistent-mode panel's top coordinate and applies the\n * matching height clamp. The menu grows downward from `growTop` unless the\n * content overflows the room below *and* the room above the anchor is\n * larger, in which case it flips to grow upward from `anchorTop`. Delegates\n * the room/flip arithmetic to {@link positionFlexibleAnchored}, which measures\n * the room against the side the menu actually grows toward — never re-derived\n * from a clamped top, which would let a flipped, viewport-pinned menu grow\n * back down across its anchor.\n *\n * @param growTop - The viewport edge the menu grows downward from.\n * @param anchorTop - The anchor edge the menu flips up against.\n * @param totalHeight - The unclamped preferred height (px) of the menu's items.\n * @param viewportHeight - The current viewport height (px).\n * @returns The resolved top coordinate (px) for the panel.\n */\n private placeVertically(growTop: number, anchorTop: number, totalHeight: number, viewportHeight: number): number {\n const { start, available } = positionFlexibleAnchored(anchorTop, growTop, totalHeight, viewportHeight, VIEWPORT_MARGIN);\n\n this.applyViewportHeightClamp(available, totalHeight);\n\n return start;\n }\n\n /**\n * Tear down the current persistent items and rebuild them from `configs`.\n * Used by `open()` to re-resolve a provider-sourced dropdown each time it\n * shows, so its labels / enabled state track current state. Mirrors the\n * rebuild-mode `show()` teardown, then defers to {@link buildPersistentItems}.\n *\n * @param configs - The freshly-resolved item configurations.\n */\n private rebuildPersistentItems(configs: MenuItemConfig[]): void {\n this.closeOpenSubmenu();\n\n this._menuItems = [];\n this._focusedIndex = -1;\n this.disposeAllComponents();\n\n this.buildPersistentItems(configs);\n }\n\n /**\n * Builds the item list for persistent mode and sets the panel width.\n *\n * @param items - The item configs from the constructor.\n */\n private buildPersistentItems(items: MenuItemConfig[]): void {\n this.pauseLayout();\n\n for (const config of items) {\n let row: MenuRow;\n\n // `separator` wins over `row`, mirroring rebuild mode's\n // precedence (see `showAnchored`'s loop and the config-entry\n // table in the plan's Architecture Decisions): MenuItem's own\n // constructor already renders a separator config as a rule\n // regardless of `row`, via the `else` branch below.\n if (config.row && config.separator !== true) {\n row = config.row();\n // The default, made explicit so both build loops read alike.\n row.setCssVarPrefix(\"menu-bar\");\n row.setMenuCloseHandler(() => { this.dismissAll(); });\n } else {\n row = new MenuItem(\n config,\n () => {\n config.action?.();\n\n if (config.closeOnActivate === false) {\n this.closeOpenSubmenu();\n } else {\n this._onClose!();\n }\n },\n (hoveredItem) => { this.handleItemOpenSubmenu(hoveredItem); }\n );\n }\n\n this.addComponent(row);\n this._menuItems.push(row);\n }\n\n this.resumeLayout();\n\n this.setWidth(this.layOutColumns());\n }\n\n /**\n * Updates the focused-item highlight, clearing the previous item and setting the new one.\n *\n * @param index - The new focused index, or -1 to clear.\n */\n private setFocusedIndex(index: number): void {\n if (this._focusedIndex >= 0 && this._focusedIndex < this._menuItems.length) {\n const prev = this._menuItems[this._focusedIndex];\n\n if (prev.isNavigable()) {\n prev.setFocused(false);\n }\n }\n\n this._focusedIndex = index;\n\n if (index >= 0 && index < this._menuItems.length) {\n const next = this._menuItems[index];\n\n if (next.isNavigable()) {\n next.setFocused(true);\n }\n }\n }\n\n /**\n * Returns `true` when the row at the given index must be skipped by the\n * arrow-key highlight — a separator, or a custom row that owns its own\n * focus (see [`MenuRow`](/api/component/container/classes/MenuRow)'s\n * navigable flag).\n *\n * @param index - Zero-based row index.\n * @returns Whether focus traversal skips this row.\n */\n private isItemSkipped(index: number): boolean {\n return !this._menuItems[index].isNavigable();\n }\n\n /**\n * Closes and forgets this menu's open child submenu, if any. Shared by the\n * teardown paths (`hide` / `close`) and by the submenu switch in\n * `handleItemOpenSubmenu` when a different item takes the child slot.\n */\n private closeOpenSubmenu(): void {\n if (this._openSubmenuPanel) {\n this._openSubmenuPanel.close();\n this._openSubmenuItem?.getAria().setExpanded(false);\n this._openSubmenuPanel = null;\n this._openSubmenuItem = null;\n }\n }\n\n /**\n * Closes the whole menu chain through the current mode's close path: the\n * persistent-mode `onClose` callback (a menubar dropdown), or `hide()` for a\n * rebuild-mode context menu — which also tears down any open child submenu.\n * Used by an activated submenu leaf and the outside-click / window-blur\n * dismissals, so a submenu selection closes the parent context menu too.\n */\n private dismissAll(): void {\n if (this._persistent) {\n this._onClose!();\n } else {\n this.hide();\n }\n }\n\n // ----- DismissableLayer -----\n\n /**\n * Returns the menu panel's root element for the central layer tree.\n *\n * @returns The menu's element, or null when not yet rendered.\n */\n getLayerElement(): Handle | null {\n return this.getElement() ?? null;\n }\n\n /**\n * Returns the dismiss mode the document-level handlers consult. A menu is\n * `\"click-outside\"`: dismissed by a `pointerdown` outside its layer subtree\n * (and its excluded trigger) and by a window blur, but never by a focus\n * move — exactly the pointerdown + window-blur behaviour the menu owned\n * before it joined the layer tree.\n *\n * @returns The layer dismiss mode.\n */\n getDismissMode(): LayerDismissMode {\n return \"click-outside\";\n }\n\n /**\n * Advisory close request from the manager (an outside `pointerdown`, a\n * window blur, or Escape). Routes through the mode-aware close chain: a\n * persistent (MenuBar) menu fires its `onClose`, a rebuild (context) menu\n * calls `hide()`; either way any open submenu is torn down.\n */\n requestClose(): void {\n this.dismissAll();\n }\n\n /**\n * Returns the trigger element excluded from the manager's outside-pointerdown\n * test — the context-menu opener passed to {@link show} / {@link toggleFor},\n * or the [`MenuBar`](/api/component/menubar/classes/MenuBar) button set via\n * {@link setExcludedElement} — so the gesture that opened the menu does not\n * immediately re-close it.\n *\n * @returns The excluded element, or null.\n */\n getAnchorElement(): Handle | null {\n return this._excludedEl;\n }\n\n /**\n * Returns the dropdown z-index band so an unrelated top-level menu stacks in\n * the dropdown family. A submenu — or a menu opened while another layer is\n * topmost — inherits its opener's band instead, so it rises above the\n * surface it was opened from.\n *\n * @returns The dropdown band base.\n */\n getBand(): number {\n return LayerManager.Band.Dropdown;\n }\n\n /**\n * Mirrors a manager-allocated z-index onto the element when the layer this\n * menu was opened inside is raised via the manager's bring-to-front path,\n * so an open menu never falls behind the surface it belongs to.\n *\n * @param zIndex - The fresh z-index assigned by the manager.\n */\n onZIndexChanged(zIndex: number): void {\n this.setZIndex(zIndex);\n }\n\n /**\n * Handles the open-submenu signal from a hovered or activated [`MenuItem`](/api/component/container/classes/MenuItem).\n *\n * Closes any existing child submenu when the item has no submenu (or is\n * disabled); opens or switches to the child panel when it does.\n *\n * @param item - The [`MenuItem`](/api/component/container/classes/MenuItem) that triggered the signal.\n */\n private handleItemOpenSubmenu(item: MenuItem): void {\n // A disabled item opens no submenu (even though it has one); like a plain\n // item, hovering it just closes any sibling submenu that is open.\n if (!item.hasSubmenu() || !item.isEnabled()) {\n this.closeOpenSubmenu();\n\n return;\n }\n\n if (this._openSubmenuItem === item) {\n return;\n }\n\n this.closeOpenSubmenu();\n\n const submenuPanel = new Menu(\n item.getSubmenuConfig()!.items,\n () => { this.dismissAll(); }\n );\n\n // A submenu is single-use — see _disposeOnClose — so its own close()\n // (via closeOpenSubmenu, below) disposes it once its fade completes\n // instead of fading-and-detaching it for reuse like a persistent\n // top-level dropdown.\n submenuPanel._disposeOnClose = true;\n\n this._openSubmenuPanel = submenuPanel;\n this._openSubmenuItem = item;\n item.getAria().setExpanded(true);\n\n submenuPanel.setExcludedElement(this.getElement(true)!);\n submenuPanel.open(item.getElement(true)!, this);\n }\n\n /**\n * Throws if the method called is not valid in the current mode (rebuild-only check).\n *\n * @param method - Name of the method that was invoked, used in the error message.\n */\n private assertRebuildMode(method: string): void {\n if (this._persistent) {\n throw new Error(`Menu.${method}() is only valid in rebuild mode (constructed without arguments).`);\n }\n }\n\n /**\n * Throws if the method called is not valid in the current mode (persistent-only check).\n *\n * @param method - Name of the method that was invoked, used in the error message.\n */\n private assertPersistentMode(method: string): void {\n if (!this._persistent) {\n throw new Error(`Menu.${method}() is only valid in persistent mode (constructed with items and onClose).`);\n }\n }\n}\n\nconst MenuCallable = callable(Menu);\ntype MenuCallable = Menu;\nexport {\n Menu as _Menu,\n MenuCallable as Menu\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./DOM-
|
|
2
|
-
//# sourceMappingURL=MenuButton-
|
|
1
|
+
import{t as e}from"./DOM-DDhDN8lQ.js";import{g as t}from"./Component-YrLXKJZS.js";import{t as n}from"./Menu-BCQISKiQ.js";import{t as r}from"./Button-CKzjQyqA.js";var i=t(class MenuButton extends r{_menu=null;_boundToggleMenu=()=>{this.toggleMenu()};constructor(e,t,n){let r;typeof e==`string`?r=e:e!==void 0&&(t=e),super(r,t,n),this.on(`action`,this._boundToggleMenu),Object.getPrototypeOf(this)===MenuButton.prototype&&this.applyListeners(t?.listeners)}destructor(){this._menu?.dispose(),super.destructor()}setMenuItems(e){return this._options.menuItems=e,this}getMenuItems(){return this._options.menuItems??this._defaultOptions.menuItems??null}isScrollToBottomOnShow(){return this._options.scrollToBottomOnShow??this._defaultOptions.scrollToBottomOnShow??!1}applyOptions(e){return super.applyOptions(e),e.menuItems!==void 0&&this.setMenuItems(e.menuItems),e.scrollToBottomOnShow!==void 0&&(this._options.scrollToBottomOnShow=e.scrollToBottomOnShow),this}shouldToggleMenu(){return!0}toggleMenu(){let t=this.getElement();!t||!this.shouldToggleMenu()||(this._menu??=new n().setScrollToBottomOnShow(this.isScrollToBottomOnShow()),this._menu.toggleFor(t,e.source.getViewportRect(this),this.resolveMenuItems()))}resolveMenuItems(){let e=this.getMenuItems();return typeof e==`function`?e():e??[]}});export{i as t};
|
|
2
|
+
//# sourceMappingURL=MenuButton-BO4y3jf2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MenuButton-BqCT-1yv.js","names":[],"sources":["../../src/typescript/lib/component/button/MenuButton.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button, ButtonOptions } from \"~/component/button/Button.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuButton}.\n *\n * @category Components\n */\nexport interface MenuButtonOptions extends ButtonOptions {\n /** Items for the dropdown, or a provider re-invoked on every open. */\n menuItems?: MenuItemConfig[] | (() => MenuItemConfig[]);\n /** Open the menu scrolled to the bottom of its item list. Default `false`. */\n scrollToBottomOnShow?: boolean;\n}\n\n/**\n * A push button whose click toggles a rebuild-mode dropdown [`Menu`](/api/overlay/classes/Menu)\n * anchored under its bottom-left corner. The menu opens below the button, and\n * flips above it when the room below is short — see\n * [`Menu.toggleFor`](/api/overlay/classes/Menu#togglefor). A provider resolving\n * to an empty item list opens nothing (the click is a no-op beyond the\n * `\"action\"` event still firing).\n *\n * The dropdown panel is created lazily on first open and reused across opens;\n * `menuItems` accepts either a fixed array or a provider function, and the\n * provider is re-invoked on every open so its output (e.g. relative\n * timestamps, current record counts) is always current.\n *\n * @example\n * ```typescript\n * import { MenuButton } from '@jimka/typescript-ui/component/button';\n *\n * const exportBtn = MenuButton({\n * glyph: 'file-export',\n * menuItems: [\n * { text: 'Export as CSV', action: () => exportCsv() },\n * { text: 'Export as JSON', action: () => exportJson() },\n * ],\n * });\n * toolbar.addComponent(exportBtn);\n * ```\n *\n * @category Components\n */\nclass MenuButton<TOptions extends MenuButtonOptions = MenuButtonOptions> extends Button<TOptions> {\n\n // Lazily created on first open and reused across opens; a rebuild-mode Menu\n // rebuilds its items on every toggle, so a provider's output is always current.\n private _menu: Menu | null = null;\n\n private readonly _boundToggleMenu: () => void = () => { this.toggleMenu(); };\n\n /**\n * Constructs a MenuButton with an optional title and options bag (both\n * optional — an empty MenuButton renders as a chrome-shaped placeholder).\n *\n * @example\n * ```typescript\n * new MenuButton('Export', { menuItems: [ … ] });\n * MenuButton({ glyph: 'file-export', menuItems: [ … ] });\n * ```\n */\n constructor(text?: string, options?: TOptions, subclassDefaults?: Partial<TOptions>);\n constructor(options: TOptions);\n constructor(\n textOrOptions?: string | TOptions,\n options?: TOptions,\n subclassDefaults?: Partial<TOptions>,\n ) {\n // Normalise the overload: a non-string first argument is the options bag.\n // Copied from Button.ts:437-443 in shape. Legal before super() because it\n // touches no `this` (TS >= 4.6); the field initializers still run after\n // super() returns.\n let text: string | undefined;\n\n if (typeof textOrOptions === \"string\") {\n text = textOrOptions;\n } else if (textOrOptions !== undefined) {\n options = textOrOptions;\n }\n\n super(text, options, subclassDefaults);\n\n this.on(\"action\", this._boundToggleMenu);\n\n // Button wires the listener bag only when it is the directly-constructed\n // class; mirror its instance-identity guard so a MenuButton subclass wires\n // its own bag once, from its own constructor.\n if (Object.getPrototypeOf(this) === MenuButton.prototype) {\n this.applyListeners(options?.listeners);\n }\n }\n\n /**\n * Disposes the lazily-created dropdown, then runs the inherited teardown.\n * `_menu` is a LayerManager-mounted panel, never a registered child (see\n * Menu.ts's class comment), so `super.destructor()`'s child recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._menu?.dispose();\n\n super.destructor();\n }\n\n /**\n * Replaces the configured dropdown items — a fixed array, or a provider\n * re-invoked on every open.\n *\n * @param items - The new item configurations or provider.\n *\n * @returns This button, for method chaining.\n */\n setMenuItems(items: MenuItemConfig[] | (() => MenuItemConfig[])): this {\n this._options.menuItems = items;\n\n return this;\n }\n\n /**\n * Returns the configured dropdown items or provider — the caller value,\n * else the class default, else `null` when neither is set.\n *\n * @returns The configured items/provider, or `null`.\n */\n getMenuItems(): MenuItemConfig[] | (() => MenuItemConfig[]) | null {\n return this._options.menuItems ?? this._defaultOptions.menuItems ?? null;\n }\n\n /**\n * Returns whether the menu opens scrolled to the bottom of its item list —\n * the caller value, else the class default, else `false`.\n *\n * @returns Whether the menu opens scrolled to the bottom.\n */\n isScrollToBottomOnShow(): boolean {\n return this._options.scrollToBottomOnShow ?? this._defaultOptions.scrollToBottomOnShow ?? false;\n }\n\n /**\n * Applies a {@link MenuButtonOptions} bag, dispatching `menuItems` after\n * the inherited Button fields cascade.\n *\n * @param options - The options bag carrying the values to apply.\n *\n * @returns This button, for method chaining.\n */\n protected applyOptions(options: TOptions): this {\n super.applyOptions(options);\n\n if (options.menuItems !== undefined) {\n this.setMenuItems(options.menuItems);\n }\n\n // No setter: a construction-time-only option read once when the lazy\n // Menu is created, so a pure bag write is all that is needed.\n if (options.scrollToBottomOnShow !== undefined) {\n this._options.scrollToBottomOnShow = options.scrollToBottomOnShow;\n }\n\n return this;\n }\n\n /**\n * Hook consulted at the start of every {@link toggleMenu} call, before the\n * dropdown is created or toggled. Returns `true` by default. Override in a\n * subclass to veto the default open/close for some clicks — e.g. a button\n * that substitutes a different overlay once some external condition holds\n * — while leaving every other click's default dropdown behaviour\n * untouched. The `\"action\"` event still fires for a vetoed click; only\n * the internal menu toggle is skipped, so a subclass (or an external\n * listener registered via {@link Button.on}) is free to run its own\n * behaviour for that same click.\n *\n * @returns `true` to proceed with the normal toggle; `false` to leave the\n * dropdown untouched for this click.\n */\n protected shouldToggleMenu(): boolean {\n return true;\n }\n\n /**\n * Toggles the menu anchored under the button's bottom-left corner, flipping\n * above the button when the room below is short. No-op when the button is not\n * yet attached (no anchor rect to read), when {@link shouldToggleMenu} vetoes\n * the toggle, or when the items resolve to an empty list — `Menu.toggleFor`\n * owns that last suppression, so no check is needed here.\n */\n private toggleMenu(): void {\n const el = this.getElement();\n\n if (!el || !this.shouldToggleMenu()) {\n return;\n }\n\n this._menu ??= new Menu().setScrollToBottomOnShow(this.isScrollToBottomOnShow());\n\n // Items are resolved eagerly as an argument, so a provider re-runs on\n // every toggle — including a toggle that only closes the menu, whose\n // result Menu.toggleFor discards. Keeping toggleFor the single source of\n // truth for open-vs-close is worth that spare provider call; the\n // alternative (asking the menu whether it is open before resolving)\n // reintroduces the open-state desync toggleFor exists to avoid.\n this._menu.toggleFor(el, DOM.source.getViewportRect(this), this.resolveMenuItems());\n }\n\n /** Resolves the configured items — invoking the provider form on every open. */\n private resolveMenuItems(): MenuItemConfig[] {\n const items = this.getMenuItems();\n\n if (typeof items === \"function\") {\n return items();\n }\n\n return items ?? [];\n }\n}\n\nconst MenuButtonCallable = callable(MenuButton);\ntype MenuButtonCallable<TOptions extends MenuButtonOptions = MenuButtonOptions> = MenuButton<TOptions>;\nexport {\n MenuButton as _MenuButton,\n MenuButtonCallable as MenuButton,\n};\n"],"mappings":"kKA+NA,IAAM,EAAqB,EAAS,MA9K9B,mBAA2E,CAAiB,CAI9F,MAA6B,KAE7B,qBAAsD,CAAE,KAAK,WAAW,CAAG,EAc3E,YACI,EACA,EACA,EACF,CAKE,IAAI,EAEA,OAAO,GAAkB,SACzB,EAAO,EACA,IAAkB,IAAA,KACzB,EAAU,GAGd,MAAM,EAAM,EAAS,CAAgB,EAErC,KAAK,GAAG,SAAU,KAAK,gBAAgB,EAKnC,OAAO,eAAe,IAAI,IAAM,WAAW,WAC3C,KAAK,eAAe,GAAS,SAAS,CAE9C,CAQA,YAA6B,CACzB,KAAK,OAAO,QAAQ,EAEpB,MAAM,WAAW,CACrB,CAUA,aAAa,EAA0D,CAGnE,MAFA,MAAK,SAAS,UAAY,EAEnB,IACX,CAQA,cAAmE,CAC/D,OAAO,KAAK,SAAS,WAAa,KAAK,gBAAgB,WAAa,IACxE,CAQA,wBAAkC,CAC9B,OAAO,KAAK,SAAS,sBAAwB,KAAK,gBAAgB,sBAAwB,EAC9F,CAUA,aAAuB,EAAyB,CAa5C,OAZA,MAAM,aAAa,CAAO,EAEtB,EAAQ,YAAc,IAAA,IACtB,KAAK,aAAa,EAAQ,SAAS,EAKnC,EAAQ,uBAAyB,IAAA,KACjC,KAAK,SAAS,qBAAuB,EAAQ,sBAG1C,IACX,CAgBA,kBAAsC,CAClC,MAAO,EACX,CASA,YAA2B,CACvB,IAAM,EAAK,KAAK,WAAW,EAEvB,CAAC,GAAM,CAAC,KAAK,iBAAiB,IAIlC,KAAK,QAAU,IAAI,EAAK,CAAC,CAAC,wBAAwB,KAAK,uBAAuB,CAAC,EAQ/E,KAAK,MAAM,UAAU,EAAI,EAAI,OAAO,gBAAgB,IAAI,EAAG,KAAK,iBAAiB,CAAC,EACtF,CAGA,kBAA6C,CACzC,IAAM,EAAQ,KAAK,aAAa,EAMhC,OAJI,OAAO,GAAU,WACV,EAAM,EAGV,GAAS,CAAC,CACrB,CACJ,CAE8C"}
|
|
1
|
+
{"version":3,"file":"MenuButton-BO4y3jf2.js","names":[],"sources":["../../src/typescript/lib/component/button/MenuButton.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button, ButtonOptions } from \"~/component/button/Button.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuButton}.\n *\n * @category Components\n */\nexport interface MenuButtonOptions extends ButtonOptions {\n /** Items for the dropdown, or a provider re-invoked on every open. */\n menuItems?: MenuItemConfig[] | (() => MenuItemConfig[]);\n /** Open the menu scrolled to the bottom of its item list. Default `false`. */\n scrollToBottomOnShow?: boolean;\n}\n\n/**\n * A push button whose click toggles a rebuild-mode dropdown [`Menu`](/api/overlay/classes/Menu)\n * anchored under its bottom-left corner. The menu opens below the button, and\n * flips above it when the room below is short — see\n * [`Menu.toggleFor`](/api/overlay/classes/Menu#togglefor). A provider resolving\n * to an empty item list opens nothing (the click is a no-op beyond the\n * `\"action\"` event still firing).\n *\n * The dropdown panel is created lazily on first open and reused across opens;\n * `menuItems` accepts either a fixed array or a provider function, and the\n * provider is re-invoked on every open so its output (e.g. relative\n * timestamps, current record counts) is always current.\n *\n * @example\n * ```typescript\n * import { MenuButton } from '@jimka/typescript-ui/component/button';\n *\n * const exportBtn = MenuButton({\n * glyph: 'file-export',\n * menuItems: [\n * { text: 'Export as CSV', action: () => exportCsv() },\n * { text: 'Export as JSON', action: () => exportJson() },\n * ],\n * });\n * toolbar.addComponent(exportBtn);\n * ```\n *\n * @category Components\n */\nclass MenuButton<TOptions extends MenuButtonOptions = MenuButtonOptions> extends Button<TOptions> {\n\n // Lazily created on first open and reused across opens; a rebuild-mode Menu\n // rebuilds its items on every toggle, so a provider's output is always current.\n private _menu: Menu | null = null;\n\n private readonly _boundToggleMenu: () => void = () => { this.toggleMenu(); };\n\n /**\n * Constructs a MenuButton with an optional title and options bag (both\n * optional — an empty MenuButton renders as a chrome-shaped placeholder).\n *\n * @example\n * ```typescript\n * new MenuButton('Export', { menuItems: [ … ] });\n * MenuButton({ glyph: 'file-export', menuItems: [ … ] });\n * ```\n */\n constructor(text?: string, options?: TOptions, subclassDefaults?: Partial<TOptions>);\n constructor(options: TOptions);\n constructor(\n textOrOptions?: string | TOptions,\n options?: TOptions,\n subclassDefaults?: Partial<TOptions>,\n ) {\n // Normalise the overload: a non-string first argument is the options bag.\n // Copied from Button.ts:437-443 in shape. Legal before super() because it\n // touches no `this` (TS >= 4.6); the field initializers still run after\n // super() returns.\n let text: string | undefined;\n\n if (typeof textOrOptions === \"string\") {\n text = textOrOptions;\n } else if (textOrOptions !== undefined) {\n options = textOrOptions;\n }\n\n super(text, options, subclassDefaults);\n\n this.on(\"action\", this._boundToggleMenu);\n\n // Button wires the listener bag only when it is the directly-constructed\n // class; mirror its instance-identity guard so a MenuButton subclass wires\n // its own bag once, from its own constructor.\n if (Object.getPrototypeOf(this) === MenuButton.prototype) {\n this.applyListeners(options?.listeners);\n }\n }\n\n /**\n * Disposes the lazily-created dropdown, then runs the inherited teardown.\n * `_menu` is a LayerManager-mounted panel, never a registered child (see\n * Menu.ts's class comment), so `super.destructor()`'s child recursion\n * cannot reach it.\n */\n protected destructor(): void {\n this._menu?.dispose();\n\n super.destructor();\n }\n\n /**\n * Replaces the configured dropdown items — a fixed array, or a provider\n * re-invoked on every open.\n *\n * @param items - The new item configurations or provider.\n *\n * @returns This button, for method chaining.\n */\n setMenuItems(items: MenuItemConfig[] | (() => MenuItemConfig[])): this {\n this._options.menuItems = items;\n\n return this;\n }\n\n /**\n * Returns the configured dropdown items or provider — the caller value,\n * else the class default, else `null` when neither is set.\n *\n * @returns The configured items/provider, or `null`.\n */\n getMenuItems(): MenuItemConfig[] | (() => MenuItemConfig[]) | null {\n return this._options.menuItems ?? this._defaultOptions.menuItems ?? null;\n }\n\n /**\n * Returns whether the menu opens scrolled to the bottom of its item list —\n * the caller value, else the class default, else `false`.\n *\n * @returns Whether the menu opens scrolled to the bottom.\n */\n isScrollToBottomOnShow(): boolean {\n return this._options.scrollToBottomOnShow ?? this._defaultOptions.scrollToBottomOnShow ?? false;\n }\n\n /**\n * Applies a {@link MenuButtonOptions} bag, dispatching `menuItems` after\n * the inherited Button fields cascade.\n *\n * @param options - The options bag carrying the values to apply.\n *\n * @returns This button, for method chaining.\n */\n protected applyOptions(options: TOptions): this {\n super.applyOptions(options);\n\n if (options.menuItems !== undefined) {\n this.setMenuItems(options.menuItems);\n }\n\n // No setter: a construction-time-only option read once when the lazy\n // Menu is created, so a pure bag write is all that is needed.\n if (options.scrollToBottomOnShow !== undefined) {\n this._options.scrollToBottomOnShow = options.scrollToBottomOnShow;\n }\n\n return this;\n }\n\n /**\n * Hook consulted at the start of every {@link toggleMenu} call, before the\n * dropdown is created or toggled. Returns `true` by default. Override in a\n * subclass to veto the default open/close for some clicks — e.g. a button\n * that substitutes a different overlay once some external condition holds\n * — while leaving every other click's default dropdown behaviour\n * untouched. The `\"action\"` event still fires for a vetoed click; only\n * the internal menu toggle is skipped, so a subclass (or an external\n * listener registered via {@link Button.on}) is free to run its own\n * behaviour for that same click.\n *\n * @returns `true` to proceed with the normal toggle; `false` to leave the\n * dropdown untouched for this click.\n */\n protected shouldToggleMenu(): boolean {\n return true;\n }\n\n /**\n * Toggles the menu anchored under the button's bottom-left corner, flipping\n * above the button when the room below is short. No-op when the button is not\n * yet attached (no anchor rect to read), when {@link shouldToggleMenu} vetoes\n * the toggle, or when the items resolve to an empty list — `Menu.toggleFor`\n * owns that last suppression, so no check is needed here.\n */\n private toggleMenu(): void {\n const el = this.getElement();\n\n if (!el || !this.shouldToggleMenu()) {\n return;\n }\n\n this._menu ??= new Menu().setScrollToBottomOnShow(this.isScrollToBottomOnShow());\n\n // Items are resolved eagerly as an argument, so a provider re-runs on\n // every toggle — including a toggle that only closes the menu, whose\n // result Menu.toggleFor discards. Keeping toggleFor the single source of\n // truth for open-vs-close is worth that spare provider call; the\n // alternative (asking the menu whether it is open before resolving)\n // reintroduces the open-state desync toggleFor exists to avoid.\n this._menu.toggleFor(el, DOM.source.getViewportRect(this), this.resolveMenuItems());\n }\n\n /** Resolves the configured items — invoking the provider form on every open. */\n private resolveMenuItems(): MenuItemConfig[] {\n const items = this.getMenuItems();\n\n if (typeof items === \"function\") {\n return items();\n }\n\n return items ?? [];\n }\n}\n\nconst MenuButtonCallable = callable(MenuButton);\ntype MenuButtonCallable<TOptions extends MenuButtonOptions = MenuButtonOptions> = MenuButton<TOptions>;\nexport {\n MenuButton as _MenuButton,\n MenuButtonCallable as MenuButton,\n};\n"],"mappings":"kKA+NA,IAAM,EAAqB,EAAS,MA9K9B,mBAA2E,CAAiB,CAI9F,MAA6B,KAE7B,qBAAsD,CAAE,KAAK,WAAW,CAAG,EAc3E,YACI,EACA,EACA,EACF,CAKE,IAAI,EAEA,OAAO,GAAkB,SACzB,EAAO,EACA,IAAkB,IAAA,KACzB,EAAU,GAGd,MAAM,EAAM,EAAS,CAAgB,EAErC,KAAK,GAAG,SAAU,KAAK,gBAAgB,EAKnC,OAAO,eAAe,IAAI,IAAM,WAAW,WAC3C,KAAK,eAAe,GAAS,SAAS,CAE9C,CAQA,YAA6B,CACzB,KAAK,OAAO,QAAQ,EAEpB,MAAM,WAAW,CACrB,CAUA,aAAa,EAA0D,CAGnE,MAFA,MAAK,SAAS,UAAY,EAEnB,IACX,CAQA,cAAmE,CAC/D,OAAO,KAAK,SAAS,WAAa,KAAK,gBAAgB,WAAa,IACxE,CAQA,wBAAkC,CAC9B,OAAO,KAAK,SAAS,sBAAwB,KAAK,gBAAgB,sBAAwB,EAC9F,CAUA,aAAuB,EAAyB,CAa5C,OAZA,MAAM,aAAa,CAAO,EAEtB,EAAQ,YAAc,IAAA,IACtB,KAAK,aAAa,EAAQ,SAAS,EAKnC,EAAQ,uBAAyB,IAAA,KACjC,KAAK,SAAS,qBAAuB,EAAQ,sBAG1C,IACX,CAgBA,kBAAsC,CAClC,MAAO,EACX,CASA,YAA2B,CACvB,IAAM,EAAK,KAAK,WAAW,EAEvB,CAAC,GAAM,CAAC,KAAK,iBAAiB,IAIlC,KAAK,QAAU,IAAI,EAAK,CAAC,CAAC,wBAAwB,KAAK,uBAAuB,CAAC,EAQ/E,KAAK,MAAM,UAAU,EAAI,EAAI,OAAO,gBAAgB,IAAI,EAAG,KAAK,iBAAiB,CAAC,EACtF,CAGA,kBAA6C,CACzC,IAAM,EAAQ,KAAK,aAAa,EAMhC,OAJI,OAAO,GAAU,WACV,EAAM,EAGV,GAAS,CAAC,CACrB,CACJ,CAE8C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=OverlayFade-
|
|
1
|
+
import{y as e}from"./Component-YrLXKJZS.js";var t=new WeakMap,n={cancel:()=>{}};function fadeShow(r,i){let a=i?.durationMs??120,o=i?.translatePx??4,s=i?.animated??!0;t.set(r,!1);let c=r.getElement();return!c||!s?(i?.onComplete?.(),n):(r.setWillChange(`opacity, transform`),e.play(c,{from:{opacity:`0`,transform:`translateY(-${o}px)`},to:{opacity:`1`,transform:`translateY(0)`},durationMs:a,properties:[`opacity`,`transform`],onComplete:()=>{r.setWillChange(null),i?.onComplete?.()}}))}function fadeHideAndDetach(r,i){let a=i?.durationMs??120,o=i?.translatePx??4,s=i?.animated??!0,c=r.getElement(),finalize=()=>{r.setVisible(!1),r.removeElement(),i?.onComplete?.()};return!c||!s?(finalize(),n):(t.set(r,!0),r.setWillChange(`opacity, transform`),e.play(c,{to:{opacity:`0`,transform:`translateY(-${o}px)`},durationMs:a,properties:[`opacity`,`transform`],onComplete:()=>{if(!t.get(r)){r.setWillChange(null);return}t.set(r,!1),r.setWillChange(null),finalize()}}))}export{fadeShow as n,fadeHideAndDetach as t};
|
|
2
|
+
//# sourceMappingURL=OverlayFade-C1yQKffL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverlayFade-
|
|
1
|
+
{"version":3,"file":"OverlayFade-C1yQKffL.js","names":[],"sources":["../../src/typescript/lib/core/OverlayFade.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Animation } from \"~/core/Animation.js\";\n\n/** Default fade duration in milliseconds. Matches `MENU_ANIM_DURATION_MS` from `Menu`. */\nexport const DEFAULT_DURATION_MS: number = 120;\n\n/** Default entrance vertical-offset distance in pixels. */\nexport const DEFAULT_TRANSLATE_PX: number = 4;\n\n/**\n * Per-component `_dismissing` flag used by the free-function form\n * (`fadeShow` / `fadeHideAndDetach`). `WeakMap`-keyed so a `Component` that\n * never opts into the helper carries no extra state.\n */\nconst _dismissingByComponent: WeakMap<Component, boolean> = new WeakMap();\n\n/** Handle for the fade paths that finish before there is anything to cancel. */\nconst NOOP_HANDLE: Animation.CancelHandle = { cancel: (): void => {} };\n\n/**\n * Options forwarded to the free-function forms `fadeShow` / `fadeHideAndDetach`.\n *\n * @category Core\n */\nexport interface FadeOptions {\n /** Fade duration in milliseconds. Default: 120. */\n durationMs?: number;\n /** Vertical translation distance in pixels for the entrance. Default: 4. */\n translatePx?: number;\n /** When false, the helper bypasses the transition and applies the end state synchronously. Default: true. */\n animated?: boolean;\n /** Called once the fade completes (or immediately when animation is disabled). */\n onComplete?: () => void;\n}\n\n/**\n * Plays the standard dropdown-style entrance fade on the given component's\n * element. Shared by any overlay surface that fades in on show — `Menu` and\n * `Popover` use it directly; {@link AnimatedDropdown} (core/AnimatedDropdown.ts)\n * wraps the same transition as its `showAnimated` method for subclasses that\n * extend it rather than composing this free-function form.\n *\n * Cancels any in-flight fade-out queued by {@link fadeHideAndDetach} so a\n * fresh show mid-dismiss keeps the panel on screen.\n *\n * @param component - The component to fade in.\n * @param options - Optional duration / translate / animated overrides.\n * @returns A handle the caller stores and cancels on its own teardown.\n */\nexport function fadeShow(component: Component, options?: FadeOptions): Animation.CancelHandle {\n const durationMs = options?.durationMs ?? DEFAULT_DURATION_MS;\n const translatePx = options?.translatePx ?? DEFAULT_TRANSLATE_PX;\n const animated = options?.animated ?? true;\n\n _dismissingByComponent.set(component, false);\n\n const el = component.getElement();\n\n if (!el) {\n options?.onComplete?.();\n return NOOP_HANDLE;\n }\n\n if (!animated) {\n options?.onComplete?.();\n return NOOP_HANDLE;\n }\n\n component.setWillChange(\"opacity, transform\");\n\n return Animation.play(el, {\n from: { opacity: \"0\", transform: `translateY(-${translatePx}px)` },\n to: { opacity: \"1\", transform: \"translateY(0)\" },\n durationMs: durationMs,\n properties: [\"opacity\", \"transform\"],\n onComplete: () => {\n component.setWillChange(null);\n options?.onComplete?.();\n },\n });\n}\n\n/**\n * Plays the standard dropdown-style exit fade on the given component's\n * element, then hides and detaches it from the DOM when the transition\n * completes.\n *\n * A fresh {@link fadeShow} during the fade cancels the deferred detach so the\n * panel stays mounted.\n *\n * @param component - The component to fade out and detach.\n * @param options - Optional duration / translate / animated overrides; `onComplete` fires after detach.\n * @returns A handle the caller stores and cancels on its own teardown.\n */\nexport function fadeHideAndDetach(component: Component, options?: FadeOptions): Animation.CancelHandle {\n const durationMs = options?.durationMs ?? DEFAULT_DURATION_MS;\n const translatePx = options?.translatePx ?? DEFAULT_TRANSLATE_PX;\n const animated = options?.animated ?? true;\n\n const el = component.getElement();\n const finalize = (): void => {\n component.setVisible(false);\n component.removeElement();\n options?.onComplete?.();\n };\n\n if (!el || !animated) {\n finalize();\n return NOOP_HANDLE;\n }\n\n _dismissingByComponent.set(component, true);\n component.setWillChange(\"opacity, transform\");\n\n return Animation.play(el, {\n to: { opacity: \"0\", transform: `translateY(-${translatePx}px)` },\n durationMs: durationMs,\n properties: [\"opacity\", \"transform\"],\n onComplete: () => {\n if (!_dismissingByComponent.get(component)) {\n component.setWillChange(null);\n return;\n }\n _dismissingByComponent.set(component, false);\n component.setWillChange(null);\n finalize();\n },\n });\n}\n"],"mappings":"4CAgBA,IAAM,EAAsD,IAAI,QAG1D,EAAsC,CAAE,WAAoB,CAAC,CAAE,EAgCrE,SAAgB,SAAS,EAAsB,EAA+C,CAC1F,IAAM,EAAc,GAAS,YAAA,IACvB,EAAc,GAAS,aAAA,EACvB,EAAc,GAAS,UAAe,GAE5C,EAAuB,IAAI,EAAW,EAAK,EAE3C,IAAM,EAAK,EAAU,WAAW,EAchC,MAZI,CAAC,GAKD,CAAC,GACD,GAAS,aAAa,EACf,IAGX,EAAU,cAAc,oBAAoB,EAErC,EAAU,KAAK,EAAI,CACtB,KAAY,CAAE,QAAS,IAAK,UAAW,eAAe,EAAY,IAAK,EACvE,GAAY,CAAE,QAAS,IAAK,UAAW,eAAgB,EAC3C,aACZ,WAAY,CAAC,UAAW,WAAW,EACnC,eAAkB,CACd,EAAU,cAAc,IAAI,EAC5B,GAAS,aAAa,CAC1B,CACJ,CAAC,EACL,CAcA,SAAgB,kBAAkB,EAAsB,EAA+C,CACnG,IAAM,EAAc,GAAS,YAAA,IACvB,EAAc,GAAS,aAAA,EACvB,EAAc,GAAS,UAAe,GAEtC,EAAK,EAAU,WAAW,EAC1B,aAAuB,CACzB,EAAU,WAAW,EAAK,EAC1B,EAAU,cAAc,EACxB,GAAS,aAAa,CAC1B,EAUA,MARI,CAAC,GAAM,CAAC,GACR,SAAS,EACF,IAGX,EAAuB,IAAI,EAAW,EAAI,EAC1C,EAAU,cAAc,oBAAoB,EAErC,EAAU,KAAK,EAAI,CACtB,GAAY,CAAE,QAAS,IAAK,UAAW,eAAe,EAAY,IAAK,EAC3D,aACZ,WAAY,CAAC,UAAW,WAAW,EACnC,eAAkB,CACd,GAAI,CAAC,EAAuB,IAAI,CAAS,EAAG,CACxC,EAAU,cAAc,IAAI,EAC5B,MACJ,CACA,EAAuB,IAAI,EAAW,EAAK,EAC3C,EAAU,cAAc,IAAI,EAC5B,SAAS,CACb,CACJ,CAAC,EACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./DOM-Cjh7_7TF.js";import{n as t}from"./Position-BIEU1Eze.js";import{S as n,b as r,h as i,y as a}from"./Component-D99-15Zt.js";import{a as o,o as s,s as c,t as l}from"./Scrollbar-DcnueNJd.js";import{t as u}from"./Container-FWRjgF6T.js";var d={tag:`div`,insets:new t(4,4,4,4),autoScroll:`none`,scrollShadows:!0,scrollbarStyle:`overlay`},f=`PanelOverlayScroller`,p=null;function ensureOverlayScrollerClassRule(){p||=[new r({scope:`class`,name:f,styles:{scrollbarWidth:`none`}}),new r({scope:`selector`,name:`.${f}::-webkit-scrollbar`,styles:{display:`none`}})]}var Panel=class extends u{_shadowOverlayStyle=new a;_lastChildCount=0;_overlayScrollStyle=new a;_onOverlayScrollV=e=>{this.setScrollTop(e)};_onOverlayScrollH=e=>{this.setScrollLeft(e)};constructor(e,n){let r=e?.flush?{insets:new t(0,0,0,0)}:{};super(e,{...d,...n??{},...r})}applyOptions(e){return super.applyOptions(e),this.setScrollbarGutter(0,0),this._shadowEdges={top:0,bottom:0,left:0,right:0},this._lastContentExtent={width:0,height:0},this.setAutoScroll(e.autoScroll??this.getAutoScroll()),this._shadowOverlay=null,this._shadowScrollHandler=null,this.setScrollShadows(e.scrollShadows??this.getScrollShadows()),this._overlayScrollElement=null,this._scrollbarV=null,this._scrollbarH=null,this._overlayScrollHandler=null,this.setScrollbarStyle(e.scrollbarStyle??this.getScrollbarStyle()),this}setAutoScroll(e){switch(this._autoScroll=e,e){case`none`:this.setOverflowX(`hidden`).setOverflowY(`hidden`);break;case`auto`:this.setOverflowX(`auto`).setOverflowY(`auto`);break;case`x`:this.setOverflowX(`auto`).setOverflowY(`hidden`);break;case`y`:this.setOverflowX(`hidden`).setOverflowY(`auto`);break;case`both`:this.setOverflowX(`scroll`).setOverflowY(`scroll`)}e===`none`&&(this._scrollbarGutter.right!==0||this._scrollbarGutter.bottom!==0)&&this.setScrollbarGutter(0,0);let t=this.scrollableAxes();return this.getLayoutManager()?.setOverflowing(t.x,t.y),this.refreshOverlayScrollbars(),this.refreshScrollShadows(),this}setLayoutManager(e){return super.setLayoutManager(e),this.setAutoScroll(this._autoScroll),this}getAutoScroll(){return this._autoScroll??this._defaultOptions.autoScroll}clearAutoScroll(){return this.setAutoScroll(`none`)}setScrollShadows(e){return this._scrollShadows=e,this.refreshScrollShadows(),this}getScrollShadows(){return this._scrollShadows??this._defaultOptions.scrollShadows}setScrollbarStyle(e){return this._scrollbarStyle=e,this.refreshOverlayScrollbars(),this.refreshScrollShadows(),this}getScrollbarStyle(){return this._scrollbarStyle??this._defaultOptions.scrollbarStyle}getScrollElement(){return this._overlayScrollElement??this.getElement()}getInnerSize(){let e=super.getInnerSize();return e?{width:e.width-this._scrollbarGutter.right,height:e.height-this._scrollbarGutter.bottom}:null}doLayout(){return super.doLayout(),this.commitElementStyle(),this.resizeScrollShadowOverlay(),this.measureScrollbarGutter(),this.updateScrollShadows(),this.scheduleGutterSettleOnShrink(),this}scheduleGutterSettleOnShrink(){if(this._autoScroll===`none`)return;let e=this.getComponents().length,t=e<this._lastChildCount;this._lastChildCount=e;let n=!1;if(this.showsScrollAffordance()){let e=this.getPreferredSize(),t=e?e.width:0,r=e?e.height:0;n=t<this._lastContentExtent.width||r<this._lastContentExtent.height,this._lastContentExtent={width:t,height:r}}(t||n)&&this.scheduleLayout()}showsScrollAffordance(){return this._scrollbarGutter.right>0||this._scrollbarGutter.bottom>0||this._shadowEdges.top>0||this._shadowEdges.bottom>0||this._shadowEdges.left>0||this._shadowEdges.right>0}init(e){super.init(e);let t=e??this.getElement();return t&&this._scrollbarStyle===`overlay`&&this._autoScroll!==`none`&&(this.installOverlayScrollbars(t),this.layoutOverlayScrollbars(t)),t&&this._scrollShadows&&this._autoScroll!==`none`&&(this.installScrollShadows(t),this.updateScrollShadows(t)),this}destructor(){this.removeScrollShadows(),this.removeOverlayScrollbars(),super.destructor()}scrollableAxes(){let e=this._autoScroll;return{x:e===`x`||e===`auto`||e===`both`,y:e===`y`||e===`auto`||e===`both`}}setScrollbarGutter(e,t){this._scrollbarGutter={right:e,bottom:t}}measureScrollbarGutter(){if(this._autoScroll===`none`)return;if(this._scrollbarStyle===`overlay`){this.layoutOverlayScrollbars();return}let t=this.getElement();if(!t)return;let n=e.source.getScrollBarWidth();if(n===0)return;let r,i;if(this._autoScroll===`both`)r=!0,i=!0;else{let n=this.scrollableAxes(),a=e.source.getScrollMetrics(t);r=n.y&&a.scrollHeight>a.clientHeight,i=n.x&&a.scrollWidth>a.clientWidth}let a=r?n:0,o=i?n:0;(a!==this._scrollbarGutter.right||o!==this._scrollbarGutter.bottom)&&(this.setScrollbarGutter(a,o),this.scheduleLayout())}refreshScrollShadows(){if(!this._scrollShadows||this._autoScroll===`none`){this.removeScrollShadows();return}let e=this.getElement();e&&(this.installScrollShadows(e),this.updateScrollShadows(e))}installScrollShadows(e){if(this._shadowOverlay||this.createScrollShadowOverlay(e),!this._shadowScrollHandler){let handler=()=>{this.updateScrollShadows()};this._shadowScrollHandler=handler,n.addSubtreeListener(this,`scroll`,handler)}}createScrollShadowOverlay(t){let n=e.sink.createElement(`div`);this._shadowOverlayStyle.attach(n),this._shadowOverlayStyle.setMany({position:`sticky`,left:`0px`,top:`0px`,pointerEvents:`none`,zIndex:`1`,boxShadow:o()}),e.sink.appendChild(t,n),this.trackHandle(n),this._shadowOverlay=n}removeScrollShadows(){this._shadowScrollHandler&&=(n.removeSubtreeListener(this,`scroll`,this._shadowScrollHandler),null),this._shadowOverlay&&(e.sink.removeElement(this._shadowOverlay),this.untrackHandle(this._shadowOverlay),e.sink.release(this._shadowOverlay),this._shadowOverlay=null,this._shadowOverlayStyle=new a),this._shadowEdges={top:0,bottom:0,left:0,right:0}}resizeScrollShadowOverlay(t){let n=t??this.getElement();if(!n||!this._shadowOverlay)return;let{clientWidth:r,clientHeight:i}=e.source.getScrollMetrics(n),a=this._scrollbarStyle===`overlay`?this._scrollbarGutter.right:0,o=this._scrollbarStyle===`overlay`?this._scrollbarGutter.bottom:0;this._shadowOverlayStyle.setMany({width:r-a+`px`,height:i-o+`px`})}updateScrollShadows(t){let n=t??this.getElement();if(!n||!this._shadowOverlay)return;let{scrollTop:r,scrollLeft:i,scrollWidth:a,scrollHeight:o,clientWidth:s,clientHeight:l}=e.source.getScrollMetrics(this.getScrollElement()??n);this.resizeScrollShadowOverlay(n);let u=o-l,d=a-s,f=c,p=this.scrollableAxes();this.setShadowEdge(`top`,`--ts-ss-top`,p.y?f(r):0),this.setShadowEdge(`bottom`,`--ts-ss-bottom`,p.y?f(u-r):0),this.setShadowEdge(`left`,`--ts-ss-left`,p.x?f(i):0),this.setShadowEdge(`right`,`--ts-ss-right`,p.x?f(d-i):0)}setShadowEdge(e,t,n){let r=Math.round(n*100);this._shadowEdges[e]!==r&&(this._shadowEdges[e]=r,this._shadowOverlayStyle.set(t,s(r)))}refreshOverlayScrollbars(){if(this._scrollbarStyle!==`overlay`||this._autoScroll===`none`){this.removeOverlayScrollbars();return}let e=this.getElement();e&&(this.installOverlayScrollbars(e),this.layoutOverlayScrollbars(e))}installOverlayScrollbars(t){if(!this._overlayScrollElement){ensureOverlayScrollerClassRule();let n=e.sink.createElement(`div`);this._overlayScrollStyle.attach(n),this._overlayScrollStyle.setMany({position:`absolute`,left:`0px`,top:`0px`,width:`100%`,height:`100%`}),e.sink.apply(n,{addClass:[f]}),e.sink.appendChild(t,n),this.reparentContent(t,n),this.trackHandle(n),this._overlayScrollElement=n}let r=this.scrollableAxes();if(this._overlayScrollStyle.setMany({overflowX:r.x?`auto`:`hidden`,overflowY:r.y?`auto`:`hidden`}),this._scrollbarV||(this._scrollbarV=new l(`vertical`),this._scrollbarV.setZIndex(2),e.sink.appendChild(t,this._scrollbarV.getElement(!0)),this._scrollbarV.on(`scroll`,this._onOverlayScrollV)),this._scrollbarH||(this._scrollbarH=new l(`horizontal`),this._scrollbarH.setZIndex(2),e.sink.appendChild(t,this._scrollbarH.getElement(!0)),this._scrollbarH.on(`scroll`,this._onOverlayScrollH)),!this._overlayScrollHandler){let handler=()=>{this.syncOverlayScrollbars()};this._overlayScrollHandler=handler,n.addSubtreeListener(this,`scroll`,handler)}this.setNativeScrollbarHidden(!0)}removeOverlayScrollbars(){if(this._overlayScrollHandler&&=(n.removeSubtreeListener(this,`scroll`,this._overlayScrollHandler),null),this._scrollbarV&&=(this._scrollbarV.off(`scroll`,this._onOverlayScrollV),this._scrollbarV.dispose(),null),this._scrollbarH&&=(this._scrollbarH.off(`scroll`,this._onOverlayScrollH),this._scrollbarH.dispose(),null),this._overlayScrollElement){let t=this.getElement();t&&this.reparentContent(this._overlayScrollElement,t),e.sink.removeElement(this._overlayScrollElement),this.untrackHandle(this._overlayScrollElement),e.sink.release(this._overlayScrollElement),this._overlayScrollElement=null,this._overlayScrollStyle=new a}this.setNativeScrollbarHidden(!1),this.setScrollbarGutter(0,0)}setNativeScrollbarHidden(e){this.setElementCSSRule(`scrollbarWidth`,e?`none`:null),this.createStyleRule(`::-webkit-scrollbar`).set(`display`,e?`none`:null)}layoutOverlayScrollbars(t){let n=t??this.getElement(),r=this._overlayScrollElement;if(!n||!r||!this._scrollbarV||!this._scrollbarH)return;let i=this._scrollbarV.getTrackWidth(),a=e.source.getScrollMetrics(n),o=a.clientWidth,s=a.clientHeight,c=this._scrollbarGutter.right,l=this._scrollbarGutter.bottom;this._overlayScrollStyle.setMany({width:o-c+`px`,height:s-l+`px`});let u=e.source.getScrollMetrics(r),d=this.scrollableAxes(),f=d.y&&u.scrollHeight>u.clientHeight,p=d.x&&u.scrollWidth>u.clientWidth,m=o-(f?i:0),h=s-(p?i:0);(m!==o-c||h!==s-l)&&this._overlayScrollStyle.setMany({width:m+`px`,height:h+`px`}),this._scrollbarV.setX(m),this._scrollbarV.setY(0),this._scrollbarV.setHeight(h),this._scrollbarV.setMetrics(h,u.scrollHeight,u.scrollTop),this._scrollbarH.setX(0),this._scrollbarH.setY(h),this._scrollbarH.setWidth(m),this._scrollbarH.setMetrics(m,u.scrollWidth,u.scrollLeft);let g=f?i:0,_=p?i:0;(g!==c||_!==l)&&(this.setScrollbarGutter(g,_),this.scheduleLayout())}syncOverlayScrollbars(){let t=this._overlayScrollElement;if(!t||!this._scrollbarV||!this._scrollbarH)return;let n=e.source.getScrollMetrics(t);this._scrollbarV.setMetrics(n.clientHeight,n.scrollHeight,n.scrollTop),this._scrollbarH.setMetrics(n.clientWidth,n.scrollWidth,n.scrollLeft)}},m=i(Panel);export{m as t};
|
|
2
|
-
//# sourceMappingURL=Panel-
|
|
1
|
+
import{t as e}from"./DOM-DDhDN8lQ.js";import{n as t}from"./Position-DWR1nIpA.js";import{T as n,b as r,g as i,x as a}from"./Component-YrLXKJZS.js";import{a as o,o as s,s as c,t as l}from"./Scrollbar-orAWHWap.js";import{t as u}from"./Container-C5cxnioS.js";var d={tag:`div`,insets:new t(4,4,4,4),autoScroll:`none`,scrollShadows:!0,scrollbarStyle:`overlay`},f=`PanelOverlayScroller`,p=null;function ensureOverlayScrollerClassRule(){p||=[new a({scope:`class`,name:f,styles:{scrollbarWidth:`none`}}),new a({scope:`selector`,name:`.${f}::-webkit-scrollbar`,styles:{display:`none`}})]}var Panel=class extends u{_shadowOverlayStyle=new r;_lastChildCount=0;_overlayScrollStyle=new r;_onOverlayScrollV=e=>{this.setScrollTop(e)};_onOverlayScrollH=e=>{this.setScrollLeft(e)};constructor(e,n){let r=e?.flush?{insets:new t(0,0,0,0)}:{};super(e,{...d,...n??{},...r})}applyOptions(e){return super.applyOptions(e),this.setScrollbarGutter(0,0),this._shadowEdges={top:0,bottom:0,left:0,right:0},this._lastContentExtent={width:0,height:0},this.setAutoScroll(e.autoScroll??this.getAutoScroll()),this._shadowOverlay=null,this._shadowScrollHandler=null,this.setScrollShadows(e.scrollShadows??this.getScrollShadows()),this._overlayScrollElement=null,this._scrollbarV=null,this._scrollbarH=null,this._overlayScrollHandler=null,this.setScrollbarStyle(e.scrollbarStyle??this.getScrollbarStyle()),this}setAutoScroll(e){switch(this._autoScroll=e,e){case`none`:this.setOverflowX(`hidden`).setOverflowY(`hidden`);break;case`auto`:this.setOverflowX(`auto`).setOverflowY(`auto`);break;case`x`:this.setOverflowX(`auto`).setOverflowY(`hidden`);break;case`y`:this.setOverflowX(`hidden`).setOverflowY(`auto`);break;case`both`:this.setOverflowX(`scroll`).setOverflowY(`scroll`)}e===`none`&&(this._scrollbarGutter.right!==0||this._scrollbarGutter.bottom!==0)&&this.setScrollbarGutter(0,0);let t=this.scrollableAxes();return this.getLayoutManager()?.setOverflowing(t.x,t.y),this.refreshOverlayScrollbars(),this.refreshScrollShadows(),this}setLayoutManager(e){return super.setLayoutManager(e),this.setAutoScroll(this._autoScroll),this}getAutoScroll(){return this._autoScroll??this._defaultOptions.autoScroll}clearAutoScroll(){return this.setAutoScroll(`none`)}setScrollShadows(e){return this._scrollShadows=e,this.refreshScrollShadows(),this}getScrollShadows(){return this._scrollShadows??this._defaultOptions.scrollShadows}setScrollbarStyle(e){return this._scrollbarStyle=e,this.refreshOverlayScrollbars(),this.refreshScrollShadows(),this}getScrollbarStyle(){return this._scrollbarStyle??this._defaultOptions.scrollbarStyle}getScrollElement(){return this._overlayScrollElement??this.getElement()}getInnerSize(){let e=super.getInnerSize();return e?{width:e.width-this._scrollbarGutter.right,height:e.height-this._scrollbarGutter.bottom}:null}doLayout(){return super.doLayout(),this.commitElementStyle(),this.resizeScrollShadowOverlay(),this.measureScrollbarGutter(),this.updateScrollShadows(),this.scheduleGutterSettleOnShrink(),this}scheduleGutterSettleOnShrink(){if(this._autoScroll===`none`)return;let e=this.getComponents().length,t=e<this._lastChildCount;this._lastChildCount=e;let n=!1;if(this.showsScrollAffordance()){let e=this.getPreferredSize(),t=e?e.width:0,r=e?e.height:0;n=t<this._lastContentExtent.width||r<this._lastContentExtent.height,this._lastContentExtent={width:t,height:r}}(t||n)&&this.scheduleLayout()}showsScrollAffordance(){return this._scrollbarGutter.right>0||this._scrollbarGutter.bottom>0||this._shadowEdges.top>0||this._shadowEdges.bottom>0||this._shadowEdges.left>0||this._shadowEdges.right>0}init(e){super.init(e);let t=e??this.getElement();return t&&this._scrollbarStyle===`overlay`&&this._autoScroll!==`none`&&(this.installOverlayScrollbars(t),this.layoutOverlayScrollbars(t)),t&&this._scrollShadows&&this._autoScroll!==`none`&&(this.installScrollShadows(t),this.updateScrollShadows(t)),this}destructor(){this.removeScrollShadows(),this.removeOverlayScrollbars(),super.destructor()}scrollableAxes(){let e=this._autoScroll;return{x:e===`x`||e===`auto`||e===`both`,y:e===`y`||e===`auto`||e===`both`}}setScrollbarGutter(e,t){this._scrollbarGutter={right:e,bottom:t}}measureScrollbarGutter(){if(this._autoScroll===`none`)return;if(this._scrollbarStyle===`overlay`){this.layoutOverlayScrollbars();return}let t=this.getElement();if(!t)return;let n=e.source.getScrollBarWidth();if(n===0)return;let r,i;if(this._autoScroll===`both`)r=!0,i=!0;else{let n=this.scrollableAxes(),a=e.source.getScrollMetrics(t);r=n.y&&a.scrollHeight>a.clientHeight,i=n.x&&a.scrollWidth>a.clientWidth}let a=r?n:0,o=i?n:0;(a!==this._scrollbarGutter.right||o!==this._scrollbarGutter.bottom)&&(this.setScrollbarGutter(a,o),this.scheduleLayout())}refreshScrollShadows(){if(!this._scrollShadows||this._autoScroll===`none`){this.removeScrollShadows();return}let e=this.getElement();e&&(this.installScrollShadows(e),this.updateScrollShadows(e))}installScrollShadows(e){if(this._shadowOverlay||this.createScrollShadowOverlay(e),!this._shadowScrollHandler){let handler=()=>{this.updateScrollShadows()};this._shadowScrollHandler=handler,n.addSubtreeListener(this,`scroll`,handler)}}createScrollShadowOverlay(t){let n=e.sink.createElement(`div`);this._shadowOverlayStyle.attach(n),this._shadowOverlayStyle.setMany({position:`sticky`,left:`0px`,top:`0px`,pointerEvents:`none`,zIndex:`1`,boxShadow:o()}),e.sink.appendChild(t,n),this.trackHandle(n),this._shadowOverlay=n}removeScrollShadows(){this._shadowScrollHandler&&=(n.removeSubtreeListener(this,`scroll`,this._shadowScrollHandler),null),this._shadowOverlay&&(e.sink.removeElement(this._shadowOverlay),this.untrackHandle(this._shadowOverlay),e.sink.release(this._shadowOverlay),this._shadowOverlay=null,this._shadowOverlayStyle=new r),this._shadowEdges={top:0,bottom:0,left:0,right:0}}resizeScrollShadowOverlay(t){let n=t??this.getElement();if(!n||!this._shadowOverlay)return;let{clientWidth:r,clientHeight:i}=e.source.getScrollMetrics(n),a=this._scrollbarStyle===`overlay`?this._scrollbarGutter.right:0,o=this._scrollbarStyle===`overlay`?this._scrollbarGutter.bottom:0;this._shadowOverlayStyle.setMany({width:r-a+`px`,height:i-o+`px`})}updateScrollShadows(t){let n=t??this.getElement();if(!n||!this._shadowOverlay)return;let{scrollTop:r,scrollLeft:i,scrollWidth:a,scrollHeight:o,clientWidth:s,clientHeight:l}=e.source.getScrollMetrics(this.getScrollElement()??n);this.resizeScrollShadowOverlay(n);let u=o-l,d=a-s,f=c,p=this.scrollableAxes();this.setShadowEdge(`top`,`--ts-ss-top`,p.y?f(r):0),this.setShadowEdge(`bottom`,`--ts-ss-bottom`,p.y?f(u-r):0),this.setShadowEdge(`left`,`--ts-ss-left`,p.x?f(i):0),this.setShadowEdge(`right`,`--ts-ss-right`,p.x?f(d-i):0)}setShadowEdge(e,t,n){let r=Math.round(n*100);this._shadowEdges[e]!==r&&(this._shadowEdges[e]=r,this._shadowOverlayStyle.set(t,s(r)))}refreshOverlayScrollbars(){if(this._scrollbarStyle!==`overlay`||this._autoScroll===`none`){this.removeOverlayScrollbars();return}let e=this.getElement();e&&(this.installOverlayScrollbars(e),this.layoutOverlayScrollbars(e))}installOverlayScrollbars(t){if(!this._overlayScrollElement){ensureOverlayScrollerClassRule();let n=e.sink.createElement(`div`);this._overlayScrollStyle.attach(n),this._overlayScrollStyle.setMany({position:`absolute`,left:`0px`,top:`0px`,width:`100%`,height:`100%`}),e.sink.apply(n,{addClass:[f]}),e.sink.appendChild(t,n),this.reparentContent(t,n),this.trackHandle(n),this._overlayScrollElement=n}let r=this.scrollableAxes();if(this._overlayScrollStyle.setMany({overflowX:r.x?`auto`:`hidden`,overflowY:r.y?`auto`:`hidden`}),this._scrollbarV||(this._scrollbarV=new l(`vertical`),this._scrollbarV.setZIndex(2),e.sink.appendChild(t,this._scrollbarV.getElement(!0)),this._scrollbarV.on(`scroll`,this._onOverlayScrollV)),this._scrollbarH||(this._scrollbarH=new l(`horizontal`),this._scrollbarH.setZIndex(2),e.sink.appendChild(t,this._scrollbarH.getElement(!0)),this._scrollbarH.on(`scroll`,this._onOverlayScrollH)),!this._overlayScrollHandler){let handler=()=>{this.syncOverlayScrollbars()};this._overlayScrollHandler=handler,n.addSubtreeListener(this,`scroll`,handler)}this.setNativeScrollbarHidden(!0)}removeOverlayScrollbars(){if(this._overlayScrollHandler&&=(n.removeSubtreeListener(this,`scroll`,this._overlayScrollHandler),null),this._scrollbarV&&=(this._scrollbarV.off(`scroll`,this._onOverlayScrollV),this._scrollbarV.dispose(),null),this._scrollbarH&&=(this._scrollbarH.off(`scroll`,this._onOverlayScrollH),this._scrollbarH.dispose(),null),this._overlayScrollElement){let t=this.getElement();t&&this.reparentContent(this._overlayScrollElement,t),e.sink.removeElement(this._overlayScrollElement),this.untrackHandle(this._overlayScrollElement),e.sink.release(this._overlayScrollElement),this._overlayScrollElement=null,this._overlayScrollStyle=new r}this.setNativeScrollbarHidden(!1),this.setScrollbarGutter(0,0)}setNativeScrollbarHidden(e){this.setElementCSSRule(`scrollbarWidth`,e?`none`:null),this.createStyleRule(`::-webkit-scrollbar`).set(`display`,e?`none`:null)}layoutOverlayScrollbars(t){let n=t??this.getElement(),r=this._overlayScrollElement;if(!n||!r||!this._scrollbarV||!this._scrollbarH)return;let i=this._scrollbarV.getTrackWidth(),a=e.source.getScrollMetrics(n),o=a.clientWidth,s=a.clientHeight,c=this._scrollbarGutter.right,l=this._scrollbarGutter.bottom;this._overlayScrollStyle.setMany({width:o-c+`px`,height:s-l+`px`});let u=e.source.getScrollMetrics(r),d=this.scrollableAxes(),f=d.y&&u.scrollHeight>u.clientHeight,p=d.x&&u.scrollWidth>u.clientWidth,m=o-(f?i:0),h=s-(p?i:0);(m!==o-c||h!==s-l)&&this._overlayScrollStyle.setMany({width:m+`px`,height:h+`px`}),this._scrollbarV.setX(m),this._scrollbarV.setY(0),this._scrollbarV.setHeight(h),this._scrollbarV.setMetrics(h,u.scrollHeight,u.scrollTop),this._scrollbarH.setX(0),this._scrollbarH.setY(h),this._scrollbarH.setWidth(m),this._scrollbarH.setMetrics(m,u.scrollWidth,u.scrollLeft);let g=f?i:0,_=p?i:0;(g!==c||_!==l)&&(this.setScrollbarGutter(g,_),this.scheduleLayout())}syncOverlayScrollbars(){let t=this._overlayScrollElement;if(!t||!this._scrollbarV||!this._scrollbarH)return;let n=e.source.getScrollMetrics(t);this._scrollbarV.setMetrics(n.clientHeight,n.scrollHeight,n.scrollTop),this._scrollbarH.setMetrics(n.clientWidth,n.scrollWidth,n.scrollLeft)}},m=i(Panel);export{m as t};
|
|
2
|
+
//# sourceMappingURL=Panel-DlsVVuvn.js.map
|