@jimka/typescript-ui 0.5.0 → 0.6.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-B10DfLOf.js → AbstractInput-sRNPzWel.js} +2 -2
- package/dist/lib/{AbstractInput-B10DfLOf.js.map → AbstractInput-sRNPzWel.js.map} +1 -1
- package/dist/lib/Anchor-CwRhdP2J.js +2 -0
- package/dist/lib/Anchor-CwRhdP2J.js.map +1 -0
- package/dist/lib/AnimatedDropdown-H3Rh-YMZ.js +2 -0
- package/dist/lib/AnimatedDropdown-H3Rh-YMZ.js.map +1 -0
- package/dist/lib/Border-0DJFDiij.js +2 -0
- package/dist/lib/Border-0DJFDiij.js.map +1 -0
- package/dist/lib/Button-MpMIYBYJ.js +2 -0
- package/dist/lib/Button-MpMIYBYJ.js.map +1 -0
- package/dist/lib/{Card-CD3jxshU.js → Card-QYo27r5b.js} +2 -2
- package/dist/lib/{Card-CD3jxshU.js.map → Card-QYo27r5b.js.map} +1 -1
- package/dist/lib/Checkbox-D0JYK-wc.js +2 -0
- package/dist/lib/Checkbox-D0JYK-wc.js.map +1 -0
- package/dist/lib/CheckboxMenuRow-CesLhSnp.js +2 -0
- package/dist/lib/CheckboxMenuRow-CesLhSnp.js.map +1 -0
- package/dist/lib/{CodeEditor-BGlB232h.js → CodeEditor-N1FQodcF.js} +2 -2
- package/dist/lib/CodeEditor-N1FQodcF.js.map +1 -0
- package/dist/lib/ComboBox-Dx8Tk9yg.js +2 -0
- package/dist/lib/ComboBox-Dx8Tk9yg.js.map +1 -0
- package/dist/lib/Component-B0ShfPVi.js +2 -0
- package/dist/lib/Component-B0ShfPVi.js.map +1 -0
- package/dist/lib/Container-abY5Ww3W.js +2 -0
- package/dist/lib/{Container-C7RPLQEc.js.map → Container-abY5Ww3W.js.map} +1 -1
- package/dist/lib/{DOM-Ba8YRVel.js → DOM-C-Edb3tA.js} +2 -2
- package/dist/lib/DOM-C-Edb3tA.js.map +1 -0
- package/dist/lib/Dialog-cbA9V17e.js +2 -0
- package/dist/lib/Dialog-cbA9V17e.js.map +1 -0
- package/dist/lib/{FieldDecorator-HG65hEtk.js → FieldDecorator-BlYMfZ9L.js} +2 -2
- package/dist/lib/{FieldDecorator-HG65hEtk.js.map → FieldDecorator-BlYMfZ9L.js.map} +1 -1
- package/dist/lib/{Fit-Cg3dPyZg.js → Fit-BhlB-HAo.js} +2 -2
- package/dist/lib/{Fit-Cg3dPyZg.js.map → Fit-BhlB-HAo.js.map} +1 -1
- package/dist/lib/{FloatingPanel-D5buuVrQ.js → FloatingPanel-DkJXsZ2D.js} +2 -2
- package/dist/lib/{FloatingPanel-D5buuVrQ.js.map → FloatingPanel-DkJXsZ2D.js.map} +1 -1
- package/dist/lib/{Glyph-DeJF_zsR.js → Glyph-_VeBacS2.js} +2 -2
- package/dist/lib/{Glyph-DeJF_zsR.js.map → Glyph-_VeBacS2.js.map} +1 -1
- package/dist/lib/{Grid-BYBohJIA.js → Grid-Daiz7F5d.js} +2 -2
- package/dist/lib/Grid-Daiz7F5d.js.map +1 -0
- package/dist/lib/GridConstraints-C58LuTqM.js +2 -0
- package/dist/lib/{GridConstraints-Bts284FI.js.map → GridConstraints-C58LuTqM.js.map} +1 -1
- package/dist/lib/{Header-BIFJK9nR.js → Header-Dql3Zxc6.js} +2 -2
- package/dist/lib/{Header-BIFJK9nR.js.map → Header-Dql3Zxc6.js.map} +1 -1
- package/dist/lib/{IconText-Dsb1xS-E.js → IconText-PUjhTLlp.js} +2 -2
- package/dist/lib/{IconText-Dsb1xS-E.js.map → IconText-PUjhTLlp.js.map} +1 -1
- package/dist/lib/LayoutSerialization-CMxunLfV.js +2 -0
- package/dist/lib/LayoutSerialization-CMxunLfV.js.map +1 -0
- package/dist/lib/List-nx8220Fi.js +2 -0
- package/dist/lib/{List-BPd-IXZR.js.map → List-nx8220Fi.js.map} +1 -1
- package/dist/lib/MemoryStore-DhPoU-7X.js +2 -0
- package/dist/lib/MemoryStore-DhPoU-7X.js.map +1 -0
- package/dist/lib/Menu-Q0bpXXze.js +2 -0
- package/dist/lib/Menu-Q0bpXXze.js.map +1 -0
- package/dist/lib/{MenuButton-BR4KOFld.js → MenuButton-DCXi0tsP.js} +2 -2
- package/dist/lib/{MenuButton-BR4KOFld.js.map → MenuButton-DCXi0tsP.js.map} +1 -1
- package/dist/lib/OverlayFade-3pof2AJH.js +2 -0
- package/dist/lib/OverlayFade-3pof2AJH.js.map +1 -0
- package/dist/lib/{Panel-CpNQTLNF.js → Panel-Cy1pdlWO.js} +2 -2
- package/dist/lib/{Panel-CpNQTLNF.js.map → Panel-Cy1pdlWO.js.map} +1 -1
- package/dist/lib/{Position-PBVBW1JS.js → Position-BwtxouxK.js} +2 -2
- package/dist/lib/{Position-PBVBW1JS.js.map → Position-BwtxouxK.js.map} +1 -1
- package/dist/lib/{ProgressSpinner-lAsK26C-.js → ProgressSpinner-DK8xEQ9y.js} +2 -2
- package/dist/lib/{ProgressSpinner-lAsK26C-.js.map → ProgressSpinner-DK8xEQ9y.js.map} +1 -1
- package/dist/lib/RadioButton-BEwpqg1k.js +2 -0
- package/dist/lib/{RadioButton-CCcIaL54.js.map → RadioButton-BEwpqg1k.js.map} +1 -1
- package/dist/lib/RadioMenuRow-DP5qPyFR.js +2 -0
- package/dist/lib/RadioMenuRow-DP5qPyFR.js.map +1 -0
- package/dist/lib/{RovingTabIndex-C2Vzq0IX.js → RovingTabIndex-pHEv5oLD.js} +2 -2
- package/dist/lib/{RovingTabIndex-C2Vzq0IX.js.map → RovingTabIndex-pHEv5oLD.js.map} +1 -1
- package/dist/lib/Scrollbar-t8fFrUVQ.js +2 -0
- package/dist/lib/Scrollbar-t8fFrUVQ.js.map +1 -0
- package/dist/lib/{Slider-DZ9drUxn.js → Slider-CTbwR6HR.js} +2 -2
- package/dist/lib/{Slider-DZ9drUxn.js.map → Slider-CTbwR6HR.js.map} +1 -1
- package/dist/lib/{Spacer-SDhOMp2g.js → Spacer-srdN2d2b.js} +2 -2
- package/dist/lib/{Spacer-SDhOMp2g.js.map → Spacer-srdN2d2b.js.map} +1 -1
- package/dist/lib/TabButton-DLoMQzky.js +2 -0
- package/dist/lib/TabButton-DLoMQzky.js.map +1 -0
- package/dist/lib/TabCloseButton-CY7BGitY.js +2 -0
- package/dist/lib/TabCloseButton-CY7BGitY.js.map +1 -0
- package/dist/lib/{Text-CdHf3B-1.js → Text-CMKnK9uB.js} +2 -2
- package/dist/lib/{Text-CdHf3B-1.js.map → Text-CMKnK9uB.js.map} +1 -1
- package/dist/lib/{Tooltip-B_UF5bXD.js → Tooltip-CUOJOqQi.js} +3 -3
- package/dist/lib/Tooltip-CUOJOqQi.js.map +1 -0
- package/dist/lib/Tree-C64JNPWJ.js +2 -0
- package/dist/lib/Tree-C64JNPWJ.js.map +1 -0
- package/dist/lib/VBox-DfJPWXqc.js +2 -0
- package/dist/lib/VBox-DfJPWXqc.js.map +1 -0
- package/dist/lib/VirtualScroller-DTIn19o1.js +2 -0
- package/dist/lib/VirtualScroller-DTIn19o1.js.map +1 -0
- package/dist/lib/assets/StoreWorker-B5NwRM2I.js +2 -0
- package/dist/lib/assets/StoreWorker-B5NwRM2I.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/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 +6 -3
- package/dist/lib/component/table.es.js.map +1 -1
- package/dist/lib/component/tree.es.js +1 -1
- package/dist/lib/core.es.js +1 -1
- package/dist/lib/core.es.js.map +1 -1
- package/dist/lib/data.es.js +1 -1
- 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.es.js +1 -1
- package/dist/lib/router.es.js +1 -1
- package/dist/lib/router.es.js.map +1 -1
- package/dist/lib/selectionsEqual-gMG7gCVI.js +2 -0
- package/dist/lib/selectionsEqual-gMG7gCVI.js.map +1 -0
- package/dist/lib/types/component/button/Button.d.ts +13 -0
- package/dist/lib/types/component/button/MenuButton.d.ts +1 -0
- package/dist/lib/types/component/button/PopupButton.d.ts +22 -0
- package/dist/lib/types/component/button/TabButton.d.ts +4 -0
- package/dist/lib/types/component/button/index.d.ts +2 -0
- package/dist/lib/types/component/container/CheckboxMenuRow.d.ts +33 -0
- package/dist/lib/types/component/container/CollapseButton.d.ts +11 -3
- package/dist/lib/types/component/container/MenuItem.d.ts +15 -5
- package/dist/lib/types/component/container/MenuRow.d.ts +24 -0
- package/dist/lib/types/component/container/MenuSeparator.d.ts +4 -2
- package/dist/lib/types/component/container/RadioMenuRow.d.ts +33 -0
- package/dist/lib/types/component/container/Scrollbar.d.ts +5 -0
- package/dist/lib/types/component/container/SplitGutter.d.ts +7 -2
- package/dist/lib/types/component/container/TabBar.d.ts +2 -0
- package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
- package/dist/lib/types/component/container/index.d.ts +6 -1
- package/dist/lib/types/component/display/WebGLCanvas.d.ts +1 -0
- package/dist/lib/types/component/input/AbstractCalendarDropdown.d.ts +2 -0
- package/dist/lib/types/component/input/AbstractPickerField.d.ts +3 -2
- package/dist/lib/types/component/list/AbstractSelectableList.d.ts +1 -1
- package/dist/lib/types/component/list/MultiSelectList.d.ts +2 -1
- package/dist/lib/types/component/shared/VirtualRowView.d.ts +2 -0
- package/dist/lib/types/component/table/Body.d.ts +22 -1
- package/dist/lib/types/component/table/Column.d.ts +3 -1
- package/dist/lib/types/component/table/ColumnConfig.d.ts +2 -0
- package/dist/lib/types/component/table/ColumnFilter.d.ts +27 -0
- package/dist/lib/types/component/table/Header.d.ts +30 -3
- package/dist/lib/types/component/table/Row.d.ts +4 -0
- package/dist/lib/types/component/table/Table.d.ts +29 -0
- package/dist/lib/types/component/table/TableExporter.d.ts +4 -3
- package/dist/lib/types/component/table/TreeBody.d.ts +2 -1
- package/dist/lib/types/component/table/cell/CellText.d.ts +9 -0
- package/dist/lib/types/component/table/cell/Filter.d.ts +54 -0
- package/dist/lib/types/component/table/cell/FilterClauseBadge.d.ts +21 -0
- package/dist/lib/types/component/table/cell/GroupSeparator.d.ts +9 -0
- package/dist/lib/types/component/table/cell/Header.d.ts +1 -0
- package/dist/lib/types/component/table/cell/editor/CellEditor.d.ts +1 -0
- package/dist/lib/types/component/table/cell/renderer/CellRenderer.d.ts +1 -0
- package/dist/lib/types/component/table/cell/renderer/Combo.d.ts +2 -0
- package/dist/lib/types/component/table/cell/renderer/Date.d.ts +2 -0
- package/dist/lib/types/component/table/cell/renderer/DateTime.d.ts +2 -0
- package/dist/lib/types/component/table/cell/renderer/Filter.d.ts +16 -0
- package/dist/lib/types/component/table/cell/renderer/Glyph.d.ts +1 -0
- package/dist/lib/types/component/table/cell/renderer/Link.d.ts +2 -0
- package/dist/lib/types/component/table/cell/renderer/Number.d.ts +3 -1
- package/dist/lib/types/component/table/cell/renderer/String.d.ts +2 -0
- package/dist/lib/types/component/table/cell/renderer/Time.d.ts +2 -0
- package/dist/lib/types/component/table/cell/renderer/TreeCell.d.ts +1 -0
- package/dist/lib/types/component/table/index.d.ts +6 -0
- package/dist/lib/types/component/tree/Tree.d.ts +17 -1
- package/dist/lib/types/component/tree/index.d.ts +1 -1
- package/dist/lib/types/core/AnimatedDropdown.d.ts +0 -9
- package/dist/lib/types/core/Body.d.ts +4 -0
- package/dist/lib/types/core/ClassStyleRules.d.ts +1 -0
- package/dist/lib/types/core/DOM.d.ts +10 -0
- package/dist/lib/types/core/Event.d.ts +11 -2
- package/dist/lib/types/core/OverlayFade.d.ts +12 -0
- package/dist/lib/types/core/OverlayPosition.d.ts +6 -0
- package/dist/lib/types/core/Theme.d.ts +8 -0
- package/dist/lib/types/core/index.d.ts +5 -3
- package/dist/lib/types/data/AbstractStore.d.ts +3 -0
- package/dist/lib/types/data/FilterDescriptor.d.ts +13 -0
- package/dist/lib/types/data/index.d.ts +2 -1
- package/dist/lib/types/data/temporalText.d.ts +2 -0
- package/dist/lib/types/layout/BoxLayout.d.ts +6 -1
- package/dist/lib/types/layout/Grid.d.ts +1 -0
- package/dist/lib/types/layout/HFlow.d.ts +1 -1
- package/dist/lib/types/layout/Split.d.ts +10 -1
- package/dist/lib/types/layout/Tab.d.ts +7 -1
- package/dist/lib/types/layout/VFlow.d.ts +1 -1
- package/dist/lib/types/layout/index.d.ts +1 -1
- package/dist/lib/types/overlay/Dock.d.ts +1 -0
- package/dist/lib/types/overlay/Menu.d.ts +1 -1
- package/dist/lib/types/overlay/Popover.d.ts +3 -0
- package/dist/lib/types/overlay/PopupPanel.d.ts +17 -0
- package/dist/lib/types/overlay/Window.d.ts +2 -0
- package/dist/lib/types/overlay/index.d.ts +2 -0
- package/dist/lib/types/router/RoutePattern.d.ts +7 -0
- package/dist/lib/types/router/Router.d.ts +6 -2
- package/dist/lib/types/router/index.d.ts +1 -1
- package/dist/lib/validation.es.js +1 -1
- package/llms.txt +4 -0
- package/package.json +1 -1
- package/dist/lib/AbstractBooleanInput-CbW7ZrDT.js +0 -2
- package/dist/lib/AbstractBooleanInput-CbW7ZrDT.js.map +0 -1
- package/dist/lib/Anchor-BsblS5it.js +0 -2
- package/dist/lib/Anchor-BsblS5it.js.map +0 -1
- package/dist/lib/AnimatedDropdown-kL47HPGP.js +0 -2
- package/dist/lib/AnimatedDropdown-kL47HPGP.js.map +0 -1
- package/dist/lib/Border-CNS6aEXB.js +0 -2
- package/dist/lib/Border-CNS6aEXB.js.map +0 -1
- package/dist/lib/Button-T16zSZip.js +0 -2
- package/dist/lib/Button-T16zSZip.js.map +0 -1
- package/dist/lib/CodeEditor-BGlB232h.js.map +0 -1
- package/dist/lib/ComboBox-C9FYMuLv.js +0 -2
- package/dist/lib/ComboBox-C9FYMuLv.js.map +0 -1
- package/dist/lib/Component-w65rPK75.js +0 -2
- package/dist/lib/Component-w65rPK75.js.map +0 -1
- package/dist/lib/Container-C7RPLQEc.js +0 -2
- package/dist/lib/DOM-Ba8YRVel.js.map +0 -1
- package/dist/lib/DragManager-UvPHSoRm.js +0 -2
- package/dist/lib/DragManager-UvPHSoRm.js.map +0 -1
- package/dist/lib/Grid-BYBohJIA.js.map +0 -1
- package/dist/lib/GridConstraints-Bts284FI.js +0 -2
- package/dist/lib/LayoutSerialization-ySzY1h2l.js +0 -2
- package/dist/lib/LayoutSerialization-ySzY1h2l.js.map +0 -1
- package/dist/lib/LayoutSizes-DKDK1Cle.js +0 -2
- package/dist/lib/LayoutSizes-DKDK1Cle.js.map +0 -1
- package/dist/lib/List-BPd-IXZR.js +0 -2
- package/dist/lib/MemoryStore-BgP7-Uzn.js +0 -2
- package/dist/lib/MemoryStore-BgP7-Uzn.js.map +0 -1
- package/dist/lib/Menu-C0roOCY7.js +0 -2
- package/dist/lib/Menu-C0roOCY7.js.map +0 -1
- package/dist/lib/RadioButton-CCcIaL54.js +0 -2
- package/dist/lib/Scrollbar-C3ZZyZ1e.js +0 -2
- package/dist/lib/Scrollbar-C3ZZyZ1e.js.map +0 -1
- package/dist/lib/TabButton-DNo4ZgV8.js +0 -2
- package/dist/lib/TabButton-DNo4ZgV8.js.map +0 -1
- package/dist/lib/Tooltip-B_UF5bXD.js.map +0 -1
- package/dist/lib/Tree-C7qLbIYH.js +0 -2
- package/dist/lib/Tree-C7qLbIYH.js.map +0 -1
- package/dist/lib/VBox-32fxpkDl.js +0 -2
- package/dist/lib/VBox-32fxpkDl.js.map +0 -1
- package/dist/lib/VirtualScroller-cvFNMS7U.js +0 -2
- package/dist/lib/VirtualScroller-cvFNMS7U.js.map +0 -1
- package/dist/lib/assets/StoreWorker-CgOBYoai.js +0 -2
- package/dist/lib/assets/StoreWorker-CgOBYoai.js.map +0 -1
- package/dist/lib/selectionsEqual-BrTxPMmK.js +0 -2
- package/dist/lib/selectionsEqual-BrTxPMmK.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GridConstraints-Bts284FI.js","names":[],"sources":["../../src/typescript/lib/component/container/AccordionIndicator.ts","../../src/typescript/lib/component/container/AccordionHeader.ts","../../src/typescript/lib/layout/Accordion.ts","../../src/typescript/lib/layout/AccordionConstraints.ts","../../src/typescript/lib/layout/GridConstraints.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link AccordionIndicator}.\n *\n * @category Components\n */\nexport interface AccordionIndicatorOptions extends ComponentOptions {\n expanded?: boolean;\n /** The character drawn as the chevron. Defaults to `\"▶\"` (a right-pointing triangle). */\n character?: string;\n}\n\n/**\n * Width of the chevron's HBox cell, in pixels. Wide enough to seat the chevron\n * glyph plus a little breathing room; the height is governed by the header's\n * stretching HBox, so only the width is fixed here.\n */\nconst CHEVRON_CELL_WIDTH: number = 14;\n\n/**\n * Default chevron character — a right-pointing triangle that the `.expanded`\n * rule rotates 90° to point down. A single rotating character covers both\n * states, which is why the chevron is a plain character rather than a registry\n * glyph.\n */\nconst DEFAULT_CHEVRON: string = \"▶\";\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.AccordionIndicator` class rule once on first use. The\n * rule holds the chevron's typography (font-size), horizontal centring,\n * colour, and the default `transform` transition. Vertical centring is done by\n * tracking `line-height` to the stretched cell height (see {@link setHeight}),\n * and position is owned by the framework's absolute layout (the indicator is an\n * ordinary in-flow cell, no longer an overlay), so neither lives here.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureAccordionIndicatorClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"AccordionIndicator\",\n styles: {\n textAlign: \"center\",\n pointerEvents: \"none\",\n fontSize: \"10px\",\n color: \"var(--ts-ui-accordion-indicator-color, rgb(100,100,100))\",\n transition: \"transform 200ms ease\",\n },\n });\n}\n\n/**\n * The expand/collapse chevron used by {@link AccordionHeader}.\n *\n * An ordinary in-flow cell in the header's HBox row (positioned by the\n * framework's absolute layout like any other Component). When expanded, a\n * per-instance state rule rotates the chevron 90° via a CSS class toggle on\n * `.expanded`.\n *\n * The static typography + centring lives in a shared `.AccordionIndicator`\n * class rule registered on first use. The per-instance `.expanded` rotation\n * rule is allocated through `Component.createStyleRule` so the framework\n * materialises it at render time.\n *\n * @category Components\n */\nclass AccordionIndicator extends Component<AccordionIndicatorOptions> {\n\n declare private _expanded: boolean;\n declare private _character: string;\n private _lineHeight: number | null = null;\n\n /**\n * Constructs an accordion indicator. The chevron starts collapsed unless\n * `options.expanded` is `true`.\n *\n * @param options - Optional configuration bag (initial expanded state plus\n * common Component fields).\n */\n constructor(options?: AccordionIndicatorOptions) {\n ensureAccordionIndicatorClassRule();\n\n super({ tag: \"span\", preferredSize: { width: CHEVRON_CELL_WIDTH, height: CHEVRON_CELL_WIDTH }, ...(options ?? {}) });\n\n this._expanded ??= false;\n this._character ??= DEFAULT_CHEVRON;\n\n const expandedRule = this.createStyleRule(\".expanded\");\n expandedRule.set(\"transform\", \"rotate(90deg)\");\n }\n\n /**\n * Sets the chevron's `line-height`. Vertical centring relies on a single\n * text line whose `line-height` equals the element's height, so\n * {@link setHeight} keeps this in lockstep with the stretched cell height —\n * the same idiom row renderers use to centre a glyph in a sized row.\n *\n * @param px - Line height in pixels.\n *\n * @returns This indicator, for method chaining.\n */\n private setLineHeight(px: number): this {\n if (this._lineHeight === px) {\n return this;\n }\n\n this._lineHeight = px;\n this.setElementCSSRule(\"lineHeight\", px + \"px\");\n\n return this;\n }\n\n /**\n * Sets the chevron's height and re-syncs `line-height` to it so the single\n * chevron glyph stays vertically centred at whatever height the header's\n * stretching HBox assigns.\n *\n * @param height - Height in pixels.\n *\n * @returns This indicator, for method chaining.\n */\n setHeight(height: number): this {\n super.setHeight(height);\n this.setLineHeight(height);\n\n return this;\n }\n\n /**\n * Applies an {@link AccordionIndicatorOptions} bag. The expanded field is\n * dispatched through {@link setExpanded} so the cached state and class\n * toggle stay in sync; inherited Component fields cascade through\n * `super.applyOptions`.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: AccordionIndicatorOptions): this {\n super.applyOptions(options);\n\n if (options.expanded !== undefined) {\n this.setExpanded(options.expanded);\n }\n\n if (options.character !== undefined) {\n this.setCharacter(options.character);\n }\n\n return this;\n }\n\n /**\n * Returns the chevron character.\n *\n * @returns The current chevron character.\n */\n getCharacter(): string {\n return this._character ?? DEFAULT_CHEVRON;\n }\n\n /**\n * Sets the chevron character, updating the live text when rendered.\n *\n * @param character - The character to draw as the chevron.\n *\n * @returns This indicator, for method chaining.\n */\n setCharacter(character: string): this {\n this._character = character;\n\n const element = this.getElement();\n\n if (element) {\n DOM.sink.apply(element, { text: character });\n }\n\n return this;\n }\n\n /**\n * Renders the root span, writes the chevron glyph as its text content, and\n * applies the cached `.expanded` class when the indicator is in the\n * expanded state at first paint.\n *\n * @returns The rendered root element.\n */\n protected render(): Handle {\n const element = super.render();\n\n DOM.sink.apply(element, { text: this._character ?? DEFAULT_CHEVRON });\n\n if (this._expanded) {\n DOM.sink.apply(element, { addClass: [\"expanded\"] });\n }\n\n return element;\n }\n\n /**\n * Returns the cached expanded state.\n *\n * @returns True when the indicator is in the expanded (rotated) state.\n */\n getExpanded(): boolean {\n return this._expanded;\n }\n\n /**\n * Sets the expanded state of the indicator. Toggles the live `.expanded`\n * class when the element is rendered; otherwise the cached state is\n * applied on first paint by `render`.\n *\n * @param value - True to rotate the chevron to the expanded position.\n * @returns This component, for method chaining.\n */\n setExpanded(value: boolean): this {\n this._expanded = value;\n\n const element = this.getElement();\n\n if (element) {\n DOM.sink.apply(element, { toggleClass: { expanded: value } });\n }\n\n return this;\n }\n\n /**\n * Clears the expanded state (equivalent to `setExpanded(false)`).\n *\n * @returns This component, for method chaining.\n */\n clearExpanded(): this {\n return this.setExpanded(false);\n }\n\n /**\n * Overrides the chevron's `transform` transition timing so it matches the\n * duration and easing of the owning Accordion's panel-height transition.\n * Routes through [`Component.setTransition`](/api/core/classes/Component#settransition), which writes the\n * shorthand to the indicator's own inline style — each instance keeps its\n * own timing without affecting siblings.\n *\n * @param durationMs - Transition duration in milliseconds.\n * @param easing - CSS easing function string.\n * @returns This component, for method chaining.\n */\n setAnimationTiming(durationMs: number, easing: string): this {\n this.setTransition(`transform ${durationMs}ms ${easing}`);\n\n return this;\n }\n}\n\nconst AccordionIndicatorCallable = callable(AccordionIndicator);\ntype AccordionIndicatorCallable = AccordionIndicator;\nexport {\n AccordionIndicator as _AccordionIndicator,\n AccordionIndicatorCallable as AccordionIndicator\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { AccordionIndicator } from \"~/component/container/AccordionIndicator.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisEnd } from \"~/primitive/Axis.js\";\n\n/**\n * Left padding of the header row, in pixels. Reproduces the 8px gap the former\n * Button-based header added to its label's left inset, so the title text keeps\n * the same start offset under the new HBox structure.\n */\nconst HEADER_PADDING_LEFT: number = 8;\n\n/**\n * Right padding of the header row, in pixels. Mirrors the former `right: 10px`\n * offset the chevron overlay used, so the trailing chevron keeps the same gap\n * from the header's right edge now that it is an in-flow cell.\n */\nconst HEADER_PADDING_RIGHT: number = 10;\n\n/**\n * Horizontal gap between the header's HBox cells (title, tool group, chevron),\n * in pixels. Keeps the chevron and tools from butting against the label or each\n * other; small because the title's flex weight already absorbs the slack.\n */\nconst HEADER_CELL_SPACING: number = 4;\n\n/**\n * Compact-mode counterparts of the header's horizontal padding and cell gap,\n * in pixels. Tighter than the defaults so a compact accordion reads denser\n * without changing the text or glyph sizes.\n */\nconst COMPACT_PADDING_LEFT: number = 6;\nconst COMPACT_PADDING_RIGHT: number = 6;\nconst COMPACT_CELL_SPACING: number = 2;\n\n/**\n * Construction-time options for {@link AccordionHeader}.\n *\n * @category Components\n */\nexport interface AccordionHeaderOptions extends ComponentOptions {\n /** Initial expanded state of the chevron indicator. */\n expanded?: boolean;\n /** Which end of the header the chevron sits at. Defaults to `\"end\"`. */\n chevronSide?: AxisEnd;\n /** Optional registry glyph name shown leading the title label. */\n glyph?: string;\n /** Whether the header uses compact (tighter) padding. Defaults to `false`. */\n compact?: boolean;\n /** Character drawn as the expand/collapse chevron. Defaults to `\"▶\"`. */\n chevronGlyph?: string;\n}\n\n/**\n * A section header used by [`Accordion`](/api/layout/classes/Accordion) (from `@jimka/typescript-ui/layout`)\n * to represent one collapsible section.\n *\n * The header is a plain styled {@link Component} that lays its children out with\n * an {@link HBox} row, left-to-right:\n *\n * 1. the {@link AccordionIndicator} chevron — **only** when `chevronSide` is `\"start\"`;\n * 2. a `chromeless` title {@link Button} (the section label, the clickable toggle\n * target and the focusable element), given a flex `weight` so it spans the\n * whole clickable region while its label stays left-anchored;\n * 3. the tool group container (an `HBox` row of per-section / re-parented tools);\n * 4. the chevron — **only** when `chevronSide` is `\"end\"` (the default), so the\n * chevron sits outermost and tools sit inboard of it.\n *\n * Because the tools are *siblings* of the title button rather than descendants,\n * a click on a tool is simply not a click on the title button — there is nothing\n * to stop from propagating and no marker to maintain.\n *\n * @category Components\n */\nclass AccordionHeader extends Component<AccordionHeaderOptions> {\n\n declare private _indicator: AccordionIndicator;\n declare private _title: Button;\n declare private _toolGroup: Component;\n private _tools: Component[] = [];\n private _chevronSide: AxisEnd = \"end\";\n\n /**\n * @param label - Text displayed in the header's title button.\n * @param options - Optional construction-time options.\n */\n constructor(label: string, options?: AccordionHeaderOptions) {\n super({ tag: \"div\", ...options });\n\n // The chevron, title and tool group are independent child Components in\n // an HBox row — one DOM element per class, no side-loaded overlay.\n this._indicator = new AccordionIndicator({ character: options?.chevronGlyph });\n this._title = new Button(label, { chromeless: true, anchor: AnchorType.WEST, glyph: options?.glyph });\n this._toolGroup = new Component();\n\n this._toolGroup.setLayoutManager(new HBox({ spacing: HEADER_CELL_SPACING, stretching: true }));\n this._toolGroup.setBackgroundColor(\"transparent\");\n\n // Stretching so each cell fills the header height and centres its own\n // content (the title button and tool buttons centre vertically; the\n // chevron centres via its own rule). Padding + cell gap are applied by\n // `setCompact` at the end of construction.\n this.setLayoutManager(new HBox({ spacing: HEADER_CELL_SPACING, stretching: true }));\n\n // Flex weight on the title makes the whole left region clickable while\n // the label stays left (anchor WEST); tools/chevron keep their preferred\n // width at the trailing edge.\n const titleConstraints = new LayoutConstraints();\n\n titleConstraints.weight = 1;\n\n this.addComponent(this._title, titleConstraints);\n this.addComponent(this._toolGroup);\n\n this._chevronSide = options?.chevronSide ?? \"end\";\n this.placeIndicator();\n\n this.setExpanded(options?.expanded ?? false);\n this.setCompact(options?.compact ?? false);\n }\n\n /**\n * Toggles compact padding: tighter horizontal insets and a smaller inter-cell\n * gap so the header reads denser. The header *height* is driven by the\n * owning [`Accordion`](/api/layout/classes/Accordion); this method only\n * affects the row's own padding.\n *\n * @param value - True for compact padding, false for the defaults.\n *\n * @returns This header, for method chaining.\n */\n setCompact(value: boolean): this {\n const left = value ? COMPACT_PADDING_LEFT : HEADER_PADDING_LEFT;\n const right = value ? COMPACT_PADDING_RIGHT : HEADER_PADDING_RIGHT;\n\n this.setInsets(new Insets(0, right, 0, left));\n (this.getLayoutManager() as HBox).setComponentSpacing(value ? COMPACT_CELL_SPACING : HEADER_CELL_SPACING);\n\n // Compact the title button too: its tighter insets lower the header's\n // content-min height so the compact header height can actually take\n // effect (otherwise the title's min floors the row taller).\n this._title?.setCompact(value);\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Inserts the chevron cell at the head (when `chevronSide` is `\"start\"`) or\n * the tail (when `\"end\"`) of the HBox row, removing it from its current\n * slot first so repeated calls re-position rather than duplicate.\n */\n private placeIndicator(): void {\n if (this.getComponents().includes(this._indicator)) {\n this.removeComponent(this._indicator);\n }\n\n if (this._chevronSide === \"start\") {\n this.insertComponent(this._indicator, 0);\n } else {\n this.addComponent(this._indicator);\n }\n }\n\n /**\n * Returns the title button — the clickable toggle target and the focusable\n * element. The owning [`Accordion`](/api/layout/classes/Accordion) wires its\n * `action` / `keydown` listeners and calls `focus()` on it.\n *\n * @returns The title button.\n */\n getTitleButton(): Button {\n return this._title;\n }\n\n /**\n * Toggles the expanded visual state: rotates the chevron and updates the\n * title button's `aria-expanded`.\n *\n * @param expanded - True to show the section as expanded.\n *\n * @returns This header, for method chaining.\n */\n setExpanded(expanded: boolean): this {\n this._indicator?.setExpanded(expanded);\n this._title?.getAria().setExpanded(expanded);\n\n return this;\n }\n\n /**\n * Returns whether the chevron is in the expanded state.\n *\n * @returns True if expanded.\n */\n isExpanded(): boolean {\n return this._indicator?.getExpanded() ?? false;\n }\n\n /**\n * Moves the chevron to the given end of the header and re-runs layout.\n *\n * @param side - `\"start\"` or `\"end\"`.\n *\n * @returns This header, for method chaining.\n */\n setChevronSide(side: AxisEnd): this {\n if (this._chevronSide === side) {\n return this;\n }\n\n this._chevronSide = side;\n this.placeIndicator();\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns which end the chevron currently sits at.\n *\n * @returns `\"start\"` or `\"end\"`.\n */\n getChevronSide(): AxisEnd {\n return this._chevronSide;\n }\n\n /**\n * Sets the character drawn as the chevron.\n *\n * @param glyph - The chevron character.\n *\n * @returns This header, for method chaining.\n */\n setChevronGlyph(glyph: string): this {\n this._indicator?.setCharacter(glyph);\n\n return this;\n }\n\n /**\n * Adds a tool to this header's tool group (the cell between the title and the\n * trailing chevron). Used both for per-section tools and for the manager's\n * re-parented global tool. A no-op if the tool is already present.\n *\n * A {@link Button} tool is forced into `flat` appearance so header tools read\n * as flat icons regardless of how the caller configured them. Re-applied on\n * every add because `setFlat` is idempotent and a re-parented global tool\n * flows back through here on each header it lands on.\n *\n * @param tool - The tool component to add.\n *\n * @returns This header, for method chaining.\n */\n addTool(tool: Component): this {\n if (tool instanceof Button) {\n tool.setFlat(true);\n }\n\n if (this._tools.includes(tool)) {\n return this;\n }\n\n this._tools.push(tool);\n this._toolGroup.addComponent(tool);\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Removes a previously-added tool from this header's tool group. A no-op if\n * the tool is not present.\n *\n * @param tool - The tool component to remove.\n *\n * @returns This header, for method chaining.\n */\n removeTool(tool: Component): this {\n const index = this._tools.indexOf(tool);\n\n if (index === -1) {\n return this;\n }\n\n this._tools.splice(index, 1);\n this._toolGroup.removeComponent(tool);\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Shows or hides the tool group as a whole. The manager calls this to\n * implement hover-reveal: hidden at rest, shown while the header is hovered.\n * Uses `display` (not `visibility`) so a hidden group reserves no width and\n * the flex-weighted title fills the row.\n *\n * @param revealed - True to show the tool group, false to hide it.\n *\n * @returns This header, for method chaining.\n */\n setToolsRevealed(revealed: boolean): this {\n this._toolGroup.setDisplayed(revealed);\n\n return this;\n }\n\n /**\n * Overrides the chevron's `transform` transition timing so it matches the\n * duration and easing of the owning Accordion's panel-height transition.\n * Called by [`Accordion`](/api/layout/classes/Accordion) from\n * `createSection`; not exposed via {@link AccordionHeaderOptions} since the\n * timing is a wiring detail, not a configuration knob.\n *\n * @param durationMs - Transition duration in milliseconds.\n * @param easing - CSS easing function string.\n *\n * @returns This header, for method chaining.\n */\n setAnimationTiming(durationMs: number, easing: string): this {\n this._indicator?.setAnimationTiming(durationMs, easing);\n\n return this;\n }\n}\n\nconst AccordionHeaderCallable = callable(AccordionHeader);\ntype AccordionHeaderCallable = AccordionHeader;\nexport {\n AccordionHeader as _AccordionHeader,\n AccordionHeaderCallable as AccordionHeader\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutManager, LayoutManagerOptions } from \"~/layout/LayoutManager.js\";\nimport { AccordionConstraints } from \"~/layout/AccordionConstraints.js\";\nimport { AccordionHeader } from \"~/component/container/AccordionHeader.js\";\nimport { SplitGutter } from \"~/component/container/SplitGutter.js\";\nimport { Animation } from \"~/core/Animation.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { LayoutSize, LayoutSizeUnit, toLayoutSizes, fromLayoutSizes, isRestorableSizes } from \"~/layout/LayoutSizes.js\";\nimport { Size, UNBOUNDED } from \"~/primitive/Size.js\";\nimport type { AxisEnd } from \"~/primitive/Axis.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { chainRoom, distributeDragChain } from \"~/core/DragChain.js\";\n\n/**\n * String-literal union of the events emitted by {@link Accordion}.\n *\n * @category Layouts\n */\nexport type AccordionEvent = \"sectiontoggle\" | \"sectionresize\";\n\n/**\n * Symmetric easing curve, shared between the panel wrapper height transition,\n * the headers'/wrappers' `top` transitions, and the indicator transform\n * transition so the open and close animations are exact time-reverses of each\n * other. Encoded as a module-private constant — motion personality belongs to\n * the layout, not the theme.\n *\n * The Material \"standard\" curve `cubic-bezier(0.4, 0, 0.2, 1)` was rejected\n * because it's asymmetric — `easing(0.5) ≈ 0.77`, so a close shrinks ~77% of\n * the way in the first half of the duration and crawls through the final 23%\n * for the second half. The visual weight lands at large sizes first, which\n * reads as \"content vanished, then nothing happened.\" A symmetric curve has\n * `easing(t) + easing(1 - t) = 1`, so the close mirrors the open frame-for-frame.\n *\n * `scaleY` was rejected as the animated property: the wrapper participates\n * in document flow and siblings need to reflow as it grows. `height` with\n * `contain: layout paint` scopes the reflow cost and produces the correct\n * layout-tracking motion.\n */\nconst ACCORDION_EASING: string = \"cubic-bezier(0.4, 0, 0.6, 1)\";\n\n/**\n * Header height in pixels used by {@link Accordion} compact mode. Tighter than\n * the default 28px so a stack of sections reads denser; applied only when the\n * consumer has not pinned an explicit header height via `setHeaderHeight`.\n */\nconst COMPACT_HEADER_HEIGHT: number = 22;\n\n/**\n * Thickness (px) of a resizable-mode drag gutter. It overlays the bottom edge\n * of the upper open section's content and reserves NO layout budget, so the\n * open-content sizing math (`computeShrinkRatio`/`computeFill`/\n * `computeResizableHeights`) is unaffected by whether gutters are shown.\n */\nconst RESIZE_GUTTER_SIZE: number = 6;\n\n/**\n * Themed-mode CSS values for the accordion theme tokens, with fallbacks\n * mirroring the default light theme. Applied to each header only when `themed`\n * is on, so an un-themed accordion stays chromeless. The header border is a\n * single bottom divider (not a four-side box): stacked headers then read as a\n * flat list whose dividers never double, so no separate `flat`/collapse option\n * is needed — the look is driven entirely by the `accordion.header.border` token.\n */\nconst THEMED_HEADER_BG: string = \"var(--ts-ui-accordion-header-bg, rgb(243,244,246))\";\nconst THEMED_HEADER_BORDER: string = \"var(--ts-ui-accordion-header-border, 1px solid rgb(214,217,222))\";\nconst THEMED_HEADER_COLOR: string = \"var(--ts-ui-accordion-header-color, inherit)\";\n\n/**\n * All-around border drawn on the accordion's own container when `themed`, from\n * the `accordion.border` token — boxes the whole stack while the header borders\n * remain single bottom dividers between sections. The token carries the full CSS\n * border shorthand (width, style, colour), so a theme controls all three; the\n * fallback applies only when the variable is undefined.\n */\nconst THEMED_BORDER: string = \"var(--ts-ui-accordion-border, 1px solid rgb(214,217,222))\";\n\n/**\n * Callback invoked when a section is opened or closed.\n *\n * @param index - Zero-based index of the toggled section.\n * @param open - True if the section is now open.\n *\n * @category Layouts\n */\nexport type SectionToggleCallback = (index: number, open: boolean) => void;\n\n/**\n * Callback invoked once a completed resizable-gutter drag settles a\n * section's sizes.\n *\n * @param sizes - The open sections' sizes after the drag, in child order —\n * the same array {@link Accordion.getSectionSizes} would return.\n *\n * @category Layouts\n */\nexport type SectionResizeCallback = (sizes: LayoutSize[]) => void;\n\n/**\n * Construction-time options for {@link Accordion}.\n *\n * @category Layouts\n */\nexport interface AccordionOptions extends LayoutManagerOptions {\n singleOpen?: boolean;\n headerHeight?: number;\n animationDuration?: number;\n chevronSide?: AxisEnd;\n toolsVisibility?: \"always\" | \"hover\";\n compact?: boolean;\n chevronGlyph?: string;\n spacing?: number;\n themed?: boolean;\n fillHeight?: boolean;\n /**\n * Opts into draggable gutters between adjacent open sections, letting the\n * user trade height between them. See {@link Accordion.setResizable}.\n */\n resizable?: boolean;\n /**\n * Section sizes to restore on the first resizable layout; discarded whole\n * when stale. Only meaningful with `resizable`.\n */\n sectionSizes?: LayoutSize[];\n /**\n * Multi-event listener bag dispatched to {@link Accordion.on} at\n * construction time.\n */\n listeners?: {\n sectiontoggle?: SectionToggleCallback;\n sectionresize?: SectionResizeCallback;\n };\n}\n\n/**\n * A layout manager that stacks vertically collapsible sections, each with a\n * clickable header and an animated content panel.\n *\n * Sections are defined by child components added to the container via\n * `container.addComponent(content, new AccordionConstraints(...))`. The layout\n * manager owns internally-created {@link AccordionHeader} components and panel\n * wrapper elements; these are not visible through `container.getComponents()`.\n *\n * Animation uses a CSS `height` transition with `overflow: hidden` on the panel\n * wrapper so closing/opening animates smoothly without affecting the layout model.\n *\n * @example\n * ```typescript\n * const accordion = new Accordion();\n * accordion.setSingleOpen(true);\n *\n * const panel = new Component();\n * panel.setLayoutManager(new Accordion());\n *\n * const content1 = new Component();\n * panel.addComponent(content1, new AccordionConstraints('Section 1', true));\n *\n * const content2 = new Component();\n * panel.addComponent(content2, new AccordionConstraints('Section 2'));\n *\n * panel.setLayoutManager(accordion);\n * ```\n *\n * @category Layouts\n */\nclass Accordion extends LayoutManager {\n\n private _headers: AccordionHeader[] = [];\n private _panelWrappers: Component[] = [];\n private _openState: boolean[] = [];\n private _singleOpen: boolean = false;\n private _headerHeight: number = 28;\n private _headerHeightExplicit: boolean = false;\n private _compact: boolean = false;\n private _animationDuration: number = 200;\n private _chevronSide: AxisEnd = \"end\";\n private _chevronGlyph: string | null = null;\n private _spacing: number = 0;\n private _themed: boolean = true;\n private _fillHeight: boolean = false;\n private _tools: Component[] = [];\n private _toolsVisibility: \"always\" | \"hover\" = \"hover\";\n private _hoveredHeader: number = -1;\n private _listeners: ListenerBag<AccordionEvent> = new ListenerBag<AccordionEvent>();\n private _resizable: boolean = false;\n // User-dragged (or fill-seeded) content heights per open section, absolute\n // px summing to the open budget when written. Keyed by Component\n // (reorder-safe, like Split._sizes); pruned each resizable layout for\n // removed components. A closed section keeps its entry frozen for reopen.\n private _resizeSizes: Map<Component, number> = new Map<Component, number>();\n // Sizes to restore on the first resizable layout that can resolve an open\n // budget, taken from the `sectionSizes` option (or a direct\n // `applySectionSizes` call before one is resolvable). Drained once,\n // mirroring `Split._pendingSizes`.\n private _pendingSectionSizes: LayoutSize[] | null = null;\n // The `openBudget / storedTotal` scale factor computeResizableHeights last\n // applied — i.e. `rendered height == _resizeSizes value * _resizeFactor`.\n // Only 1 when the open set's stored sizes already sum to the budget (the\n // common weight/fillHeight-seeded case); otherwise onGutterDrag needs\n // it to convert its rendered-pixel drag math back to _resizeSizes' stored\n // scale — writing rendered values directly would silently rescale the\n // whole open set (including untouched sections) on the next layout.\n // Applies to the weighted sections only; a resize-pinned section renders\n // at scale 1 (see `_resizePinned`).\n private _resizeFactor: number = 1;\n // The open sections `distributeWithinConstraints` last held at their\n // stored px (scale 1) instead of scaling by `_resizeFactor`. Read by\n // `onGutterDrag`, which must divide each dragged section's rendered\n // height by its own stored scale — `_resizeFactor` is no longer one\n // global scalar once a pin is held.\n private _resizePinned: Set<Component> = new Set<Component>();\n // Gutter pool, reused across layouts; one shown per adjacent open-section pair.\n private _resizeGutters: SplitGutter[] = [];\n // Rebuilt each layout: for gutter i, the two content components it resizes.\n private _gutterPairs: Array<{ upper: Component; lower: Component }> = [];\n // Drag state captured on gutter dragstart. The dragged pair (for the\n // reentrancy guard); the pointer coordinate at the previous move (the drag\n // is applied incrementally, frame by frame, so a reversed drag responds\n // closest-section-first); and — so the drag can chain across sections at\n // their min/max — every open section's index plus the dragged gutter's\n // position in that open list.\n private _dragUpper: Component | null = null;\n private _dragLower: Component | null = null;\n private _dragLastPointer: number = 0;\n private _dragOpenIndices: number[] = [];\n private _dragGutterUpperPos: number = 0;\n // Open/close toggle animations currently in flight. Transitions are off by\n // default (so resize and drag relayouts snap); a toggle enables them and the\n // global disable waits until this returns to zero — single-open mode primes\n // several sections at once, and the first to finish must not snap the rest.\n private _toggleAnimations: number = 0;\n\n // In-flight height transitions, keyed by section index and cancelled on\n // detach so their fallback timers cannot fire against released wrapper\n // element handles. Two channels, because a section can be mid-shrink from a\n // reflow while a toggle animation is separately in flight on the same index.\n private _shrinkAnimations: Map<number, Animation.CancelHandle> = new Map();\n private _wrapperAnimations: Map<number, Animation.CancelHandle> = new Map();\n // Stable bound reference so add/removeViewportListener target the same\n // callback; Accordion is a LayoutManager, not a Component, so it cannot\n // key the registration on `this` the way every Component call site does —\n // see the Potential Challenges drift note in the resizable-sections plan.\n // A concise-arrow body returns onGutterDragEnd's disposition, so the\n // wrapper forwards the consume without needing to carry the event.\n private _boundOnGutterDragEnd: () => Event.ListenerResult = () => this.onGutterDragEnd();\n\n constructor(options?: AccordionOptions) {\n // LayoutManager's constructor takes no options; applied via applyOptions below.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n if (options) {\n this.applyOptions(options);\n }\n }\n\n /**\n * Applies an {@link AccordionOptions} bag, dispatching single-open mode,\n * header height, animation duration, and the toggle callback after the\n * inherited LayoutManager defaults.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: AccordionOptions): void {\n super.applyOptions(options);\n\n if (options.headerHeight !== undefined) {\n this.setHeaderHeight(options.headerHeight);\n }\n\n if (options.animationDuration !== undefined) {\n this.setAnimationDuration(options.animationDuration);\n }\n\n if (options.chevronSide !== undefined) {\n this.setChevronSide(options.chevronSide);\n }\n\n if (options.toolsVisibility !== undefined) {\n this.setToolsVisibility(options.toolsVisibility);\n }\n\n if (options.compact !== undefined) {\n this.setCompact(options.compact);\n }\n\n if (options.chevronGlyph !== undefined) {\n this.setChevronGlyph(options.chevronGlyph);\n }\n\n if (options.spacing !== undefined) {\n this.setSpacing(options.spacing);\n }\n\n if (options.themed !== undefined) {\n this.setThemed(options.themed);\n }\n\n if (options.fillHeight !== undefined) {\n this.setFillHeight(options.fillHeight);\n }\n\n if (options.resizable !== undefined) {\n this.setResizable(options.resizable);\n }\n\n if (options.sectionSizes !== undefined) {\n this._pendingSectionSizes = options.sectionSizes.map(size => ({ ...size }));\n }\n\n if (options.listeners !== undefined) {\n const listeners = options.listeners;\n\n for (const event of Object.keys(listeners) as Array<keyof typeof listeners>) {\n const listener = listeners[event];\n\n // A union of two events' callback types no longer narrows to a\n // single `on` overload; the cast mirrors `Component.applyListeners`'\n // own `(this as any).on` — sound for the same reason: `event` and\n // `listener` are still a matched pair from the same options key.\n if (listener !== undefined) {\n (this as any).on(event, listener);\n }\n }\n }\n\n if (options.singleOpen !== undefined) {\n this.setSingleOpen(options.singleOpen);\n }\n }\n\n /**\n * Returns whether at most one section can be open at a time.\n *\n * @returns True if single-open mode is active.\n */\n isSingleOpen(): boolean {\n return this._singleOpen;\n }\n\n /**\n * Sets whether at most one section can be open at a time.\n * When switching to `true`, immediately closes all but the first open section.\n *\n * @param value - True to enforce single-open mode.\n */\n setSingleOpen(value: boolean): this {\n this._singleOpen = value;\n\n if (!value) {\n return this;\n }\n\n let foundOpen = false;\n\n for (let i = 0; i < this._openState.length; i++) {\n if (!this._openState[i]) {\n continue;\n }\n\n if (foundOpen) {\n this.primeWrapper(i);\n this._openState[i] = false;\n this._headers[i].setExpanded(false);\n this.emit(\"sectiontoggle\", i, false);\n } else {\n foundOpen = true;\n }\n }\n\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Returns the height in pixels of each section header.\n *\n * @returns The header height.\n */\n getHeaderHeight(): number {\n return this._headerHeight;\n }\n\n /**\n * Sets the height of each section header in pixels. Marks the height as\n * explicitly set, so {@link setCompact} no longer overrides it with the\n * compact default.\n *\n * @param height - Height in pixels.\n */\n setHeaderHeight(height: number): this {\n this._headerHeight = height;\n this._headerHeightExplicit = true;\n\n return this;\n }\n\n /**\n * Returns the header height actually used for layout: the compact default\n * when {@link isCompact} is on and no explicit height was set, otherwise the\n * configured {@link getHeaderHeight}.\n *\n * @returns The effective header height in pixels.\n */\n private effectiveHeaderHeight(): number {\n return this._compact && !this._headerHeightExplicit\n ? COMPACT_HEADER_HEIGHT\n : this._headerHeight;\n }\n\n /**\n * Returns whether compact (denser) mode is active.\n *\n * @returns True if compact mode is on.\n */\n isCompact(): boolean {\n return this._compact;\n }\n\n /**\n * Sets compact (denser) mode: a smaller default header height (unless an\n * explicit height was set) plus tighter header padding and a smaller\n * chevron. Applies to existing headers immediately.\n *\n * @param value - True to enable compact mode.\n *\n * @returns This layout manager, for chaining.\n */\n setCompact(value: boolean): this {\n this._compact = value;\n\n for (const header of this._headers) {\n header.setCompact(value);\n }\n\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Returns the CSS transition duration for open/close animation in milliseconds.\n *\n * @returns Duration in milliseconds.\n */\n getAnimationDuration(): number {\n return this._animationDuration;\n }\n\n /**\n * Sets the CSS transition duration for open/close animation.\n *\n * @param ms - Duration in milliseconds.\n */\n setAnimationDuration(ms: number): this {\n this._animationDuration = ms;\n\n return this;\n }\n\n /**\n * Returns which end of each header the chevron sits at.\n *\n * @returns `\"start\"` or `\"end\"`.\n */\n getChevronSide(): AxisEnd {\n return this._chevronSide;\n }\n\n /**\n * Sets which end of each header the chevron sits at. The section label\n * always stays left-aligned; only the chevron moves. Applies to existing\n * headers immediately and to headers created afterwards.\n *\n * @param side - `\"start\"` or `\"end\"`.\n *\n * @returns This layout manager, for chaining.\n */\n setChevronSide(side: AxisEnd): this {\n this._chevronSide = side;\n\n for (const header of this._headers) {\n header.setChevronSide(side);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns the chevron character, or `null` when the default is used.\n *\n * @returns The configured chevron character, or `null`.\n */\n getChevronGlyph(): string | null {\n return this._chevronGlyph;\n }\n\n /**\n * Sets the character drawn as each header's expand/collapse chevron, in\n * place of the default `\"▶\"`. The character rotates 90° when a section\n * expands, so a single rotatable glyph covers both states. Applies to\n * existing headers immediately.\n *\n * @param glyph - The chevron character.\n *\n * @returns This layout manager, for chaining.\n */\n setChevronGlyph(glyph: string): this {\n this._chevronGlyph = glyph;\n\n for (const header of this._headers) {\n header.setChevronGlyph(glyph);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns the vertical gap inserted between sections.\n *\n * @returns The inter-section spacing in pixels.\n */\n getSpacing(): number {\n return this._spacing;\n }\n\n /**\n * Sets the vertical gap inserted between sections (never before the first or\n * after the last). Default `0` keeps the sections contiguous.\n *\n * @param spacing - Inter-section gap in pixels.\n *\n * @returns This layout manager, for chaining.\n */\n setSpacing(spacing: number): this {\n this._spacing = spacing;\n\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Returns whether fill mode is on.\n *\n * @returns True if open sections grow to absorb the container's leftover height.\n */\n isFillHeight(): boolean {\n return this._fillHeight;\n }\n\n /**\n * Sets fill mode. When on, every open section grows to absorb the\n * container's leftover height, sharing it in proportion to their\n * `weight` (unweighted sections count equally, so equal weights get\n * equal slices) and each capped at its own max — instead of every open\n * section sitting at its preferred height. Only meaningful when the host\n * stretches the accordion beyond its preferred height. No effect when the\n * content already overflows.\n *\n * @param value - True to enable fill.\n *\n * @returns This layout manager, for chaining.\n */\n setFillHeight(value: boolean): this {\n this._fillHeight = value;\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns whether resizable mode is on.\n *\n * @returns True if draggable gutters appear between adjacent open sections.\n */\n isResizable(): boolean {\n return this._resizable;\n }\n\n /**\n * Sets resizable mode. When on, a draggable gutter appears between every\n * adjacent pair of open sections, letting the user trade height between\n * them; the split starts from the section's usual `weight`/`fillHeight`\n * distribution and is overridden by the drag from then on. No gutter\n * appears with fewer than two open sections, or under {@link setSingleOpen}\n * (which never has more than one section open).\n *\n * @param value - True to enable resizable mode.\n *\n * @returns This layout manager, for chaining.\n */\n setResizable(value: boolean): this {\n this._resizable = value;\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns whether themed mode is on.\n *\n * @returns True if headers/panels paint the accordion theme tokens.\n */\n isThemed(): boolean {\n return this._themed;\n }\n\n /**\n * Sets themed mode. When on, each header paints the accordion theme tokens\n * (background, bottom-divider border, text colour) and the container draws\n * an all-around border; when off, headers stay chromeless and the container\n * border is cleared. Applies to existing sections immediately.\n *\n * @param value - True to paint the accordion theme tokens.\n *\n * @returns This layout manager, for chaining.\n */\n setThemed(value: boolean): this {\n this._themed = value;\n\n this.applyContainerTheming();\n\n for (let i = 0; i < this._headers.length; i++) {\n this.applySectionTheming(i);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Applies (or clears) the all-around border on the accordion's container per\n * the themed state. Idempotent — `setBorder`/`clearBorder` cache, so the\n * repeated call from `doLayout` is cheap.\n */\n private applyContainerTheming(): void {\n const container = this.getContainer();\n\n if (!container) {\n return;\n }\n\n if (this._themed) {\n container.setBorder({ border: THEMED_BORDER });\n } else {\n container.clearBorder();\n }\n }\n\n /**\n * Applies (or clears) the themed background/border/text styling for the\n * header at `index`. The themed border is a single bottom divider, so\n * stacked headers read as a flat list whose dividers never double — there is\n * no separate flat/collapse mode. Idempotent — the underlying setters cache,\n * so re-applying the same state is cheap.\n *\n * @param index - Zero-based section index.\n */\n private applySectionTheming(index: number): void {\n const header = this._headers[index];\n\n if (!header) {\n return;\n }\n\n if (!this._themed) {\n header.clearBackground();\n header.clearForegroundColor();\n header.clearBorder();\n\n return;\n }\n\n // `background` (not `background-color`) so a gradient token value paints;\n // the token may also be a flat colour, which the shorthand handles too.\n header.setBackground(THEMED_HEADER_BG);\n header.setForegroundColor(THEMED_HEADER_COLOR);\n // Bottom-only divider — collapses naturally with no per-index logic.\n header.setBorder({ border: \"none\", borderBottom: THEMED_HEADER_BORDER });\n }\n\n /**\n * Returns when per-section tools are shown.\n *\n * @returns `\"always\"` or `\"hover\"`.\n */\n getToolsVisibility(): \"always\" | \"hover\" {\n return this._toolsVisibility;\n }\n\n /**\n * Sets when per-section tools are shown. `\"hover\"` (the default) reveals a\n * header's tools only while it is hovered; `\"always\"` keeps them visible.\n * Global tools (see {@link addTool}) always follow hover regardless of this\n * setting, since a single global instance can only sit in the hovered header.\n *\n * @param mode - `\"always\"` or `\"hover\"`.\n *\n * @returns This layout manager, for chaining.\n */\n setToolsVisibility(mode: \"always\" | \"hover\"): this {\n this._toolsVisibility = mode;\n\n // Re-apply the resting state to every header; a subsequent hover\n // corrects the currently-hovered one.\n for (const header of this._headers) {\n header.setToolsRevealed(mode === \"always\");\n }\n\n return this;\n }\n\n /**\n * Registers a global tool shown on whichever header is currently hovered.\n * A single `Component` is one DOM node, so a global tool cannot appear on\n * every header at once — it is re-parented into the hovered header and\n * therefore follows hover/active visibility. Mirrors `Tab.addTool`.\n *\n * @param button - The tool component to register globally.\n *\n * @returns This layout manager, for chaining.\n */\n addTool(button: Component): this {\n if (this._tools.includes(button)) {\n return this;\n }\n\n this._tools.push(button);\n\n // If a header is hovered right now, show the new tool there immediately.\n if (this._hoveredHeader !== -1) {\n this._headers[this._hoveredHeader]?.addTool(button);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Removes a previously-registered global tool.\n *\n * @param button - The tool component to remove.\n *\n * @returns This layout manager, for chaining.\n */\n removeTool(button: Component): this {\n const index = this._tools.indexOf(button);\n\n if (index === -1) {\n return this;\n }\n\n this._tools.splice(index, 1);\n\n if (this._hoveredHeader !== -1) {\n this._headers[this._hoveredHeader]?.removeTool(button);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Adds the global tools to the header at `index` and, in hover mode, reveals\n * its tool group. Called when the pointer enters a header.\n *\n * @param index - The header being entered.\n */\n private revealHeaderTools(index: number): void {\n const header = this._headers[index];\n\n if (!header) {\n return;\n }\n\n for (const tool of this._tools) {\n header.addTool(tool);\n }\n\n if (this._toolsVisibility === \"hover\") {\n header.setToolsRevealed(true);\n }\n }\n\n /**\n * Removes the global tools from the header at `index` and, in hover mode,\n * hides its tool group again. Called when the pointer leaves a header.\n *\n * @param index - The header being left.\n */\n private hideHeaderTools(index: number): void {\n const header = this._headers[index];\n\n if (!header) {\n return;\n }\n\n for (const tool of this._tools) {\n header.removeTool(tool);\n }\n\n if (this._toolsVisibility === \"hover\") {\n header.setToolsRevealed(false);\n }\n }\n\n /**\n * Handles the pointer entering a header (filtered to genuine enters, not\n * moves between the header's own descendants): relocates the global tools\n * onto it and reveals its tools, moving them off the previously hovered\n * header first.\n *\n * @param index - The header receiving the pointer.\n * @param e - The originating `mouseover` event.\n */\n private onHeaderHoverEnter(index: number, e: MouseEvent): void {\n const element = this._headers[index]?.getElement();\n\n if (element && DOM.source.isNode(e.relatedTarget) && DOM.source.contains(element, DOM.source.intern(e.relatedTarget))) {\n return;\n }\n\n if (this._hoveredHeader === index) {\n return;\n }\n\n if (this._hoveredHeader !== -1) {\n this.hideHeaderTools(this._hoveredHeader);\n }\n\n this._hoveredHeader = index;\n this.revealHeaderTools(index);\n }\n\n /**\n * Handles the pointer leaving a header (filtered to genuine exits): removes\n * the global tools and hides the tools again.\n *\n * @param index - The header losing the pointer.\n * @param e - The originating `mouseout` event.\n */\n private onHeaderHoverLeave(index: number, e: MouseEvent): void {\n const element = this._headers[index]?.getElement();\n\n if (element && DOM.source.isNode(e.relatedTarget) && DOM.source.contains(element, DOM.source.intern(e.relatedTarget))) {\n return;\n }\n\n if (this._hoveredHeader !== index) {\n return;\n }\n\n this.hideHeaderTools(index);\n this._hoveredHeader = -1;\n }\n\n /**\n * Re-lays-out the host after a change to the accordion's own intrinsic\n * height — an open/close, or a header-metric change (compact, spacing,\n * single-open). `scheduleLayout` alone only re-fits the sections inside the\n * accordion's unchanged bounds; `notifyIntrinsicSizeChanged` additionally\n * tells the ancestors — notably a scrolling host — that the accordion's\n * preferred/min height moved, so they re-lay-out and refresh their\n * scrollbars instead of going stale until the next viewport resize.\n */\n private relayoutHost(): void {\n const container = this.getContainer();\n\n container?.scheduleLayout();\n container?.notifyIntrinsicSizeChanged();\n }\n\n /**\n * Opens the section at the given index.\n *\n * @param index - Zero-based section index.\n */\n openSection(index: number): this {\n if (index < 0 || index >= this._openState.length) {\n return this;\n }\n\n if (this._singleOpen) {\n for (let i = 0; i < this._openState.length; i++) {\n if (i !== index && this._openState[i]) {\n this.primeWrapper(i);\n this._openState[i] = false;\n this._headers[i].setExpanded(false);\n this.emit(\"sectiontoggle\", i, false);\n }\n }\n }\n\n this.primeWrapper(index);\n this._openState[index] = true;\n this._headers[index].setExpanded(true);\n this.emit(\"sectiontoggle\", index, true);\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Closes the section at the given index.\n *\n * @param index - Zero-based section index.\n */\n closeSection(index: number): this {\n if (index < 0 || index >= this._openState.length) {\n return this;\n }\n\n this.primeWrapper(index);\n this._openState[index] = false;\n this._headers[index].setExpanded(false);\n this.emit(\"sectiontoggle\", index, false);\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Opens every section. In single-open mode only one section may be open at\n * a time, so this opens the first section (a deterministic choice — the\n * topmost becomes the visible one) rather than leaving whichever section\n * `openSection` happened to settle on last.\n *\n * @returns This layout manager, for chaining.\n */\n expandAll(): this {\n if (this._singleOpen) {\n this.openSection(0);\n\n return this;\n }\n\n for (let i = 0; i < this._openState.length; i++) {\n this.openSection(i);\n }\n\n return this;\n }\n\n /**\n * Closes every section.\n *\n * @returns This layout manager, for chaining.\n */\n collapseAll(): this {\n for (let i = 0; i < this._openState.length; i++) {\n this.closeSection(i);\n }\n\n return this;\n }\n\n /**\n * Returns whether the section at the given index is currently open.\n *\n * @param index - Zero-based section index.\n * @returns True if the section is open.\n */\n isSectionOpen(index: number): boolean {\n return this._openState[index] ?? false;\n }\n\n /**\n * Resolves each section's persisted unit: `\"px\"` for a resize-pinned\n * section (effective weight `0`, per {@link effectiveWeight}), `\"ratio\"`\n * otherwise. `effectiveWeight` is `accordion-resize-weight`'s definition\n * of a resize-pinned section — `weight` unset or `0`, with `fillHeight`\n * off — so this is verbatim the predicate the layout itself pins by.\n *\n * @param components - The container's current content components, in\n * child order.\n * @returns One unit per section, in the same order.\n */\n private sectionSizeUnits(components: Component[]): LayoutSizeUnit[] {\n return components.map(component => (this.effectiveWeight(component) === 0 ? \"px\" : \"ratio\"));\n }\n\n /**\n * Returns the sections' content sizes in child order, one entry per\n * section (open or closed), for cross-session persistence: a\n * resize-pinned section (effective weight `0`) reports `px`, every other\n * section reports its `ratio` of the space the px sections leave.\n *\n * @remarks Reads `_resizeSizes` raw — never `getHeight()`, never\n * `× _resizeFactor`. A pinned section's stored value is already its px\n * (the pin block holds it at scale 1); `_resizeFactor` is one scalar\n * across the *free* set, so it cancels within that subset and a whole-set\n * read would need it, not a raw one; and a rendered height can be a\n * transient min/max clamp `distributeWithinConstraints` applied for this\n * pass only, which the stored value deliberately never carries forward.\n *\n * @returns One {@link LayoutSize} per section in child order; the\n * pending `sectionSizes` when one is still undrained; `[]` when\n * detached or the container has no sections.\n */\n getSectionSizes(): LayoutSize[] {\n const container = this.getContainer();\n\n if (!container) {\n return [];\n }\n\n const components = container.getComponents();\n\n if (components.length === 0) {\n return [];\n }\n\n const units = this.sectionSizeUnits(components);\n\n if (this._pendingSectionSizes !== null && isRestorableSizes(this._pendingSectionSizes, units)) {\n return this._pendingSectionSizes.map(size => ({ ...size }));\n }\n\n return toLayoutSizes(units, components.map(component => this._resizeSizes.get(component) ?? 0));\n }\n\n /**\n * Restores sizes captured by {@link getSectionSizes}, applied on the next\n * resizable layout that can resolve the open budget (the correct base\n * for a ratio entry). Discarded whole unless every entry's unit matches\n * the live section's weight (see the discard rule on {@link LayoutSize}).\n *\n * @param sizes - The persisted array to restore.\n * @returns This layout manager, for method chaining.\n */\n applySectionSizes(sizes: LayoutSize[]): this {\n // Deferred, not immediate: the correct base is `openBudget`, which only\n // `computeResizableHeights` can resolve. The option and this setter share\n // one drain, one base, and one discard rule.\n this._pendingSectionSizes = sizes.map(size => ({ ...size }));\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Registers a listener for one of this accordion's events.\n *\n * @param event - `\"sectiontoggle\"` fires whenever a section is opened or\n * closed, receiving the zero-based section index and whether it is\n * now open; `\"sectionresize\"` fires once a completed resizable-gutter\n * drag settles a section's sizes, receiving the open sections' sizes\n * in child order.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This accordion, for method chaining.\n */\n on(event: \"sectiontoggle\", listener: SectionToggleCallback): this;\n on(event: \"sectionresize\", listener: SectionResizeCallback): this;\n on(event: AccordionEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This accordion, for method chaining.\n */\n off(event: AccordionEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"sectiontoggle\", index: number, open: boolean): void;\n protected emit(event: \"sectionresize\", sizes: LayoutSize[]): void;\n protected emit(event: AccordionEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Attaches to a container. Section headers and panel wrappers are created\n * lazily in {@link doLayout} on first use.\n *\n * @param container - The container component to attach to.\n */\n attach(container: Component): this {\n super.attach(container);\n\n return this;\n }\n\n /**\n * Detaches from the container, moving each section's content element back\n * to the container and disposing every header and panel wrapper — they\n * are raw-appended to the container's element rather than registered as\n * children, so nothing else reaches their teardown.\n */\n detach(): this {\n const container = this.getContainer();\n\n for (const animation of this._shrinkAnimations.values()) {\n animation.cancel();\n }\n this._shrinkAnimations.clear();\n\n for (const animation of this._wrapperAnimations.values()) {\n animation.cancel();\n }\n this._wrapperAnimations.clear();\n\n // Those cancelled animations owned the toggle-cleanup branch, so run its\n // work here: without this a detach mid-toggle leaves the counter above\n // zero and the animated transitions installed, and a re-attached manager\n // would never reach the cleanup branch again.\n if (this._toggleAnimations > 0) {\n this._toggleAnimations = 0;\n this.setSectionTransitions(false);\n container?.setTransition(null);\n }\n\n // A detach mid-drag would otherwise leak the viewport listeners\n // registered in onGutterDragStart and strand the drag pair. `_dragUpper`\n // is non-null only while a drag is live (set alongside those listeners),\n // so end the drag first — and only then, so we never touch the viewport\n // map when no accordion drag is active.\n if (this._dragUpper) {\n this.onGutterDragEnd();\n }\n\n for (let i = 0; i < this._headers.length; i++) {\n const components = container ? container.getComponents() : [];\n const component = components[i];\n\n if (component && container) {\n DOM.sink.appendChild(container.getElement()!, component.getElement()!);\n }\n\n // Tools belong to the caller, not to this manager, and reach a\n // header from two places: `addTool` registers a global tool on\n // every header it is revealed on, and `createSection` registers\n // each `AccordionConstraints.tools` entry on its own header. Both\n // become children of the header's tool group, so both must be\n // released before the header is disposed — otherwise its dispose\n // recursion destroys a component the caller still holds and may\n // re-add elsewhere. This is the same protection the content\n // children get by being reparented above.\n const sectionTools = component\n ? ((this.getLayoutConstraints(component) as AccordionConstraints | undefined)?.tools ?? [])\n : [];\n\n for (const tool of [...this._tools, ...sectionTools]) {\n this._headers[i].removeTool(tool);\n }\n\n this._headers[i].dispose();\n this._panelWrappers[i].dispose();\n }\n\n this._headers = [];\n this._panelWrappers = [];\n this._openState = [];\n\n for (const gutter of this._resizeGutters) {\n DOM.sink.removeElement(gutter.getElement()!);\n gutter.dispose();\n }\n\n this._resizeGutters = [];\n this._resizeSizes.clear();\n this._gutterPairs = [];\n\n super.detach();\n\n return this;\n }\n\n /**\n * Returns the preferred size: sum of all header heights plus the preferred\n * heights of all open sections.\n *\n * @returns The preferred size, or null if not attached.\n */\n getPreferredSize(): Size | null {\n const container = this.getContainer();\n\n if (!container) {\n return null;\n }\n\n const perimeterSize = container.getPerimeterSize();\n const components = container.getComponents();\n let totalHeight = perimeterSize.top + perimeterSize.bottom;\n let maxWidth = 0;\n let displayedSoFar = 0;\n\n for (let i = 0; i < components.length; i++) {\n // A non-displayed section contributes neither its header nor its\n // (possibly open) content height to the stack.\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n // Inter-section gap between displayed sections (see doLayout).\n if (displayedSoFar > 0) {\n totalHeight += this._spacing;\n }\n\n displayedSoFar += 1;\n totalHeight += this.effectiveHeaderHeight();\n\n // openState is populated lazily in doLayout; fall back to the\n // constraint's initiallyOpen flag so getPreferredSize() is correct\n // even before the first doLayout pass.\n const isOpen = i < this._openState.length\n ? this._openState[i]\n : ((this.getLayoutConstraints(components[i]) as AccordionConstraints | undefined)?.initiallyOpen ?? false);\n\n if (isOpen) {\n const preferred = components[i].getPreferredSize();\n\n if (preferred) {\n totalHeight += preferred.height;\n\n if (preferred.width > maxWidth) {\n maxWidth = preferred.width;\n }\n }\n }\n }\n\n return {\n width : maxWidth + perimeterSize.left + perimeterSize.right,\n height: totalHeight,\n };\n }\n\n /**\n * Returns the minimum size: every header height (headers are always\n * visible) plus the minimum heights of all open sections. Mirrors\n * {@link getPreferredSize} but reads each open section's `getMinSize`\n * rather than its preferred size, so a host can tell how far the accordion\n * is genuinely allowed to shrink — the headers plus the open content's own\n * floor — instead of assuming the open content can collapse to nothing.\n *\n * @returns The minimum size, or null if not attached.\n */\n getMinSize(): Size | null {\n const container = this.getContainer();\n\n if (!container) {\n return null;\n }\n\n const perimeterSize = container.getPerimeterSize();\n const components = container.getComponents();\n let totalHeight = perimeterSize.top + perimeterSize.bottom;\n let maxWidth = 0;\n let displayedSoFar = 0;\n\n for (let i = 0; i < components.length; i++) {\n // A non-displayed section contributes neither its header nor its\n // (possibly open) content height to the stack.\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n // Inter-section gap between displayed sections (see doLayout).\n if (displayedSoFar > 0) {\n totalHeight += this._spacing;\n }\n\n displayedSoFar += 1;\n totalHeight += this.effectiveHeaderHeight();\n\n // openState is populated lazily in doLayout; fall back to the\n // constraint's initiallyOpen flag so getMinSize() is correct even\n // before the first doLayout pass (matches getPreferredSize).\n const isOpen = i < this._openState.length\n ? this._openState[i]\n : ((this.getLayoutConstraints(components[i]) as AccordionConstraints | undefined)?.initiallyOpen ?? false);\n\n if (isOpen) {\n const min = components[i].getMinSize();\n\n if (min) {\n totalHeight += min.height;\n\n if (min.width > maxWidth) {\n maxWidth = min.width;\n }\n }\n }\n }\n\n return {\n width : maxWidth + perimeterSize.left + perimeterSize.right,\n height: totalHeight,\n };\n }\n\n /**\n * The accordion's maximum size: deliberately unbounded on both axes. A\n * height-animated vertical stack whose open/closed section state and\n * in-flight animation make any static height ceiling meaningless — there is\n * no stable maximum to report. Returning unbounded directly is the honest\n * answer, and keeps the `min ≤ preferred ≤ max` invariant trivially\n * satisfied against the finite min/preferred reports above.\n *\n * @returns `{ width: UNBOUNDED, height: UNBOUNDED }`.\n */\n getMaxSize(): Size | null {\n return { width: UNBOUNDED, height: UNBOUNDED };\n }\n\n /**\n * Creates header and panel wrapper elements for a newly discovered content component,\n * appends them to the container's DOM, and reparents the content element into the wrapper.\n *\n * @param component - The content component for this section.\n * @param index - Zero-based section index.\n */\n private createSection(component: Component, index: number): void {\n const container = this.getContainer()!;\n const constraints = this.getLayoutConstraints(component) as AccordionConstraints | undefined;\n const label = constraints?.label ?? component.getId();\n const initiallyOpen = constraints?.initiallyOpen ?? false;\n\n const header = new AccordionHeader(label, { chevronSide: this._chevronSide, glyph: constraints?.glyph ?? undefined, compact: this._compact, chevronGlyph: this._chevronGlyph ?? undefined });\n\n header.setAnimationTiming(this._animationDuration, ACCORDION_EASING);\n\n // Transitions are off by default so resize and drag relayouts snap;\n // `primeWrapper` turns the header's `top` transition on only for the\n // duration of an open/close toggle (headers below a toggled section\n // slide with the panel height instead of snapping to their final spot).\n header.setTransition(\"none\");\n\n const title = header.getTitleButton();\n\n // The title button is the toggle + focus target; its `action` covers the\n // label/glyph area. A click listener on the header element covers the\n // chevron (pointer-events:none, so it falls through to the header) and\n // the padding gaps. Tools carry their own ids, so neither exact-target\n // listener fires for a tool click — toggling stays structural.\n title.on(\"action\", () => this.onHeaderClicked(index));\n Event.addListener(header, 'click', () => this.onHeaderClicked(index));\n Event.addListener(title, 'keydown', (e: KeyboardEvent) => this.onHeaderKeyDown(e, index));\n\n // Hover drives tool visibility: per-section tools reveal on hover (in\n // hover mode) and the single global tool set re-parents onto the hovered\n // header. Subtree listeners catch hover anywhere in the header; the\n // handlers filter intra-header moves via relatedTarget.\n Event.addSubtreeListener(header, 'mouseover', (e: MouseEvent) => this.onHeaderHoverEnter(index, e));\n Event.addSubtreeListener(header, 'mouseout', (e: MouseEvent) => this.onHeaderHoverLeave(index, e));\n\n const wrapper = new Component();\n\n wrapper.setOverflow('hidden');\n // Animation wrapper clips content via overflow:hidden — layout+paint containment scopes\n // reflow during the height transition without affecting the rest of the document.\n wrapper.setContain(\"layout paint\");\n\n // Off by default (relayouts snap); `primeWrapper` enables it for a\n // toggle. `height` animates this wrapper's own grow/shrink; `top`\n // animates wrappers below a toggled section so they slide with the\n // headers instead of jumping.\n wrapper.setTransition(\"none\");\n\n DOM.sink.appendChild(container.getElement()!, header.getElement(true)!);\n DOM.sink.appendChild(container.getElement()!, wrapper.getElement(true)!);\n\n // Reparent content element into wrapper so overflow:hidden clips it during animation.\n DOM.sink.appendChild(wrapper.getElement()!, component.getElement()!);\n\n // Off by default (relayouts snap); `primeWrapper` enables it for a\n // toggle so the content animates its own height in lockstep with the\n // wrapper. Without it, an open section that must *shrink* during a toggle\n // — e.g. a fill-mode sibling giving back its leftover height when a lower\n // section opens — would snap to its final height while only the wrapper's\n // `overflow: hidden` clip animates, reading as the panel above jumping.\n component.setTransition(\"none\");\n\n this._openState.push(initiallyOpen);\n this._headers.push(header);\n this._panelWrappers.push(wrapper);\n\n header.setExpanded(initiallyOpen);\n title.getAria().setControls(wrapper.getId());\n\n wrapper.getAria().setRole('region');\n wrapper.getAria().setLabelledBy(title.getId());\n\n for (const tool of constraints?.tools ?? []) {\n header.addTool(tool);\n }\n\n // In hover mode a header's tools stay hidden until the pointer enters.\n if (this._toolsVisibility === \"hover\") {\n header.setToolsRevealed(false);\n }\n\n this.applySectionTheming(index);\n }\n\n /**\n * Computes the children's combined minSize along this manager's geometry:\n * width is `max(children.minWidth)` (sections stack vertically so width\n * is shared); height is N/A because Accordion intentionally ignores the\n * Y-axis overflow flag — the height animation conflicts with letting\n * sections overflow vertically. Used by `doLayout` to inflate the\n * working width when the host has opted into X-axis `setOverflowing`.\n *\n * @returns The total min-size; the height field is always `0` (unused).\n */\n protected computeTotalMinSize(): Size {\n const container = this.getContainer();\n if (!container) {\n return { width: 0, height: 0 };\n }\n\n let maxWidth = 0;\n\n for (const component of container.getComponents()) {\n if (!component.isDisplayed()) {\n continue;\n }\n\n const min = component.getMinSize();\n if (min) {\n maxWidth = Math.max(maxWidth, min.width);\n }\n }\n\n return { width: maxWidth, height: 0 };\n }\n\n /**\n * Writes one section's header, wrapper, and content geometry and returns the\n * vertical cursor after it (the wrapper's bottom edge). Shared by\n * {@link doLayout}'s main loop and the lightweight drag path so their\n * geometry can never drift. Does not reflow the content — the caller decides\n * between an immediate and a shrink-deferred `component.doLayout()`.\n *\n * @param index - Section index into `_headers` / `_panelWrappers`.\n * @param component - The section's content component.\n * @param top - The header's top edge.\n * @param panelHeight - The wrapper height (0 for a closed section).\n * @param contentHeight - The content height (preferred height for a closed section).\n * @param width - The header/wrapper/content width.\n * @param left - The header/wrapper left edge.\n * @returns The vertical cursor after this section (its wrapper's bottom edge).\n */\n private placeSection(index: number, component: Component, top: number, panelHeight: number, contentHeight: number, width: number, left: number): number {\n const header = this._headers[index];\n const wrapper = this._panelWrappers[index];\n const headerHeight = this.effectiveHeaderHeight();\n\n header.setX(left);\n header.setY(top);\n header.setWidth(width);\n header.setHeight(headerHeight);\n header.doLayout();\n\n const wrapperTop = top + headerHeight;\n\n wrapper.setX(left);\n wrapper.setY(wrapperTop);\n wrapper.setWidth(width);\n wrapper.setHeight(panelHeight);\n\n component.setX(0);\n component.setY(0);\n component.setWidth(width);\n component.setHeight(contentHeight);\n\n return wrapperTop + panelHeight;\n }\n\n /**\n * Creates sections for any new components, then positions all headers, panel\n * wrappers, and content components top-to-bottom within the container.\n */\n doLayout(): void {\n const container = this.getContainer();\n\n if (!container) {\n return;\n }\n\n // Nothing to build until the container has an element: `createSection`\n // below appends each header and wrapper straight onto it, so a premature\n // pass would dereference a null element. Such a pass is normal — see\n // `HBox.doLayout` — so return and let the next pass build the sections.\n if (!container.getElement()) {\n return;\n }\n\n const components = container.getComponents();\n\n for (let i = this._headers.length; i < components.length; i++) {\n this.createSection(components[i], i);\n }\n\n // Apply the themed container border (idempotent) so it takes effect even\n // when `themed` defaults on and `setThemed` was never called explicitly.\n this.applyContainerTheming();\n\n const containerSize = container.getInnerSize();\n const insets = container.getContentInsets();\n let containerWidth = containerSize ? containerSize.width : 0;\n\n // Universal scroll, X-axis only: Accordion stacks sections vertically\n // and animates each section's height, so honouring vertical overflow\n // would conflict with the height animation (see plan's Architecture\n // Decisions). Only inflate the working width when the host has marked\n // X as overflowing.\n if (this.isOverflowingX()) {\n const totalMin = this.computeTotalMinSize();\n containerWidth = Math.max(containerWidth, totalMin.width);\n }\n\n // Shrink-to-fit along the vertical axis: when the open sections'\n // preferred heights overflow the container, shrink each toward its min\n // so the accordion fits — see computeShrinkRatio for the full policy.\n const shrinkRatio = this.computeShrinkRatio(components, containerSize);\n\n // Fill: open sections grow to absorb the container's leftover height\n // (underflow) — split across the sections by weight, with\n // setFillHeight opting every open section in at an equal default weight.\n // The counterpart to shrink: when the content\n // overflows, shrinkRatio > 0 and the leftover is <= 0, so the fill map is\n // empty and the two policies never both apply.\n const fills = this.computeFill(components, containerSize, shrinkRatio);\n\n // Resizable mode: open sections' content heights come from the\n // drag-backed distribution instead of `openContentHeight + fill`.\n // `null` when inactive (off, no size yet, or nothing open), in which\n // case the loop below falls back to the legacy formula unchanged.\n const resizeHeights = this.computeResizableHeights(components, containerSize, shrinkRatio, fills);\n\n this.layoutSections(\n components,\n containerWidth,\n insets.getLeft(),\n insets.getTop(),\n resizeHeights !== null,\n (i, isOpen): number => {\n // Open sections take the drag-backed resizable height, or fall\n // back to the preferred height shrunk toward min plus this\n // section's fill share. Closed sections keep their content at\n // preferred height so the wrapper's `overflow: hidden` clips it\n // during the close animation rather than collapsing instantly.\n if (isOpen) {\n return resizeHeights?.get(i) ?? (this.openContentHeight(components[i], shrinkRatio) + (fills.get(i) ?? 0));\n }\n\n const preferred = components[i].getPreferredSize();\n\n // 100px fallback when a closed section reports no preferred size.\n return preferred ? preferred.height : 100;\n },\n true,\n true,\n );\n }\n\n /**\n * Places every displayed section's header, wrapper, and content top to\n * bottom, plus a resizable gutter between each adjacent open pair. Shared by\n * {@link doLayout} (the full pass) and {@link onGutterDrag} (the chained\n * live resize) so both writers produce identical geometry.\n *\n * @param components - The container's content components, section-ordered.\n * @param containerWidth - The width given to every header and wrapper.\n * @param left - The header/wrapper left edge (container inset).\n * @param top - The first displayed section's top edge (container inset).\n * @param resizable - Whether to place drag gutters between open pairs.\n * @param contentHeightFor - The content height for section `i`; `isOpen`\n * selects an open section's live height versus a closed one's preferred\n * (clipped) height.\n * @param animateShrink - Whether a section that shrinks defers its content\n * reflow to the height transition (a toggle) instead of reflowing at once\n * (a drag, where transitions are off).\n * @param reflowAll - Whether to reflow every section's content, or only the\n * sections whose height actually changed (the drag's cheap path).\n */\n private layoutSections(components: Component[], containerWidth: number, left: number, top: number, resizable: boolean, contentHeightFor: (index: number, isOpen: boolean) => number, animateShrink: boolean, reflowAll: boolean): void {\n let y = top;\n let displayedSoFar = 0;\n\n // A gutter is placed between each consecutive pair of open sections,\n // overlaying the upper section's content bottom. `previousOpen*`\n // persists across closed sections, so a closed section between two open\n // ones does not get its own gutter — the handle stays at the upper open\n // section's content bottom.\n let previousOpenComponent: Component | null = null;\n let previousOpenBottom = 0;\n let placedGutterCount = 0;\n\n for (let i = 0; i < components.length; i++) {\n const component = components[i];\n const header = this._headers[i];\n const wrapper = this._panelWrappers[i];\n const isOpen = this._openState[i];\n\n // A non-displayed section drops out of the stack entirely: hide its\n // header and wrapper and don't advance the cursor, so the sections\n // below slide up to reclaim the space.\n if (!component.isDisplayed()) {\n header.setDisplayed(false);\n wrapper.setDisplayed(false);\n\n continue;\n }\n\n header.setDisplayed(true);\n wrapper.setDisplayed(true);\n\n // Inter-section gap: before every displayed section except the\n // first, so the gap never leads or trails the stack.\n if (displayedSoFar > 0) {\n y += this._spacing;\n }\n\n displayedSoFar += 1;\n\n const contentHeight = contentHeightFor(i, isOpen);\n const panelHeight = isOpen ? contentHeight : 0;\n\n if (isOpen && resizable && previousOpenComponent !== null) {\n this.placeGutter(placedGutterCount, previousOpenBottom, containerWidth, left);\n this._gutterPairs[placedGutterCount] = { upper: previousOpenComponent, lower: component };\n placedGutterCount += 1;\n }\n\n // A shrinking open section keeps its interior laid out at the current\n // (larger) height for the duration of the shrink — the wrapper's\n // overflow:hidden clip covers it — and reflows to the smaller height\n // only once the height transition ends, so a height-driven interior\n // (a Tree/Table scroll viewport) doesn't snap mid-animation. Growing,\n // closing, and reduced motion take the immediate path. Read the old\n // height before placeSection overwrites it.\n const oldHeight = component.getHeight();\n const shrinking = isOpen && animateShrink && !Animation.isReducedMotion() && contentHeight < oldHeight;\n\n const cursor = this.placeSection(i, component, y, panelHeight, contentHeight, containerWidth, left);\n\n if (reflowAll || contentHeight !== oldHeight) {\n if (shrinking) {\n this._shrinkAnimations.get(i)?.cancel();\n this._shrinkAnimations.set(i, Animation.afterTransition({\n component: wrapper,\n property: \"height\",\n durationMs: this._animationDuration,\n fallbackBufferMs: 40,\n onComplete: () => {\n this._shrinkAnimations.delete(i);\n component.doLayout();\n },\n }));\n } else {\n component.doLayout();\n }\n }\n\n if (isOpen && resizable) {\n previousOpenComponent = component;\n previousOpenBottom = cursor;\n }\n\n y = cursor;\n }\n\n // Gutters not placed this pass (resizable off, or a pair whose upper\n // section closed/dropped out) sit hidden in the pool for reuse.\n this._gutterPairs.length = placedGutterCount;\n\n for (let i = placedGutterCount; i < this._resizeGutters.length; i++) {\n this._resizeGutters[i].setVisible(false);\n }\n }\n\n /**\n * Returns the pooled resizable-mode gutter at `index`, creating and\n * appending it to the container's DOM on first use. Mirrors the lazy\n * gutter creation in `Split.doLayout`.\n *\n * The gutter's `top` transition is off by default so drags and resizes\n * snap; `primeWrapper` turns it on only for the duration of an open/close\n * toggle, so the boundary handle slides with the sections it sits between\n * during the animation but tracks the cursor instantly during a drag.\n *\n * @param index - The gutter's position in the pool (and in `_gutterPairs`).\n * @returns The gutter for this index.\n */\n private getOrCreateResizeGutter(index: number): SplitGutter {\n const existing = this._resizeGutters[index];\n\n if (existing) {\n return existing;\n }\n\n const container = this.getContainer()!;\n const gutter = new SplitGutter(\"vertical\", { collapsible: false, expandedBackground: \"transparent\" });\n\n gutter.setTransition(\"none\");\n gutter.on(\"dragstart\", (position: number) => this.onGutterDragStart(index, position));\n gutter.on(\"drag\", (position: number) => this.onGutterDrag(index, position));\n\n DOM.sink.appendChild(container.getElement()!, gutter.getElement(true)!);\n\n this._resizeGutters.push(gutter);\n\n return gutter;\n }\n\n /**\n * Positions and shows the pooled gutter at `index`, overlaying the upper\n * section's content bottom edge. Shared by {@link doLayout} and the\n * lightweight drag path; `_gutterPairs` bookkeeping stays with the caller.\n *\n * @param index - The gutter's pool index.\n * @param upperBottom - The upper section's content bottom edge.\n * @param width - The gutter width.\n * @param left - The gutter left edge.\n */\n private placeGutter(index: number, upperBottom: number, width: number, left: number): void {\n const gutter = this.getOrCreateResizeGutter(index);\n\n gutter.setX(left);\n gutter.setY(upperBottom - RESIZE_GUTTER_SIZE);\n gutter.setWidth(width);\n gutter.setHeight(RESIZE_GUTTER_SIZE);\n gutter.setVisible(true);\n }\n\n /**\n * Captures the drag origin when a resizable gutter's drag begins: the\n * absolute pointer coordinate and the current heights of the two adjacent\n * open sections. Mirrors {@link Split.onDragStart}. Transitions are already\n * off outside a toggle, so each {@link onGutterDrag} write lands instantly\n * without any per-drag suppression.\n *\n * @param gutterIndex - The dragged gutter's position in `_gutterPairs`.\n * @param position - The absolute pointer coordinate (`clientY`) at drag start.\n */\n private onGutterDragStart(gutterIndex: number, position: number): void {\n const pair = this._gutterPairs[gutterIndex];\n\n if (!pair) {\n return;\n }\n\n const components = this.getContainer()?.getComponents() ?? [];\n\n // Snapshot the open sections' indices so the drag can chain growth\n // outward from the gutter across sections already at their max.\n this._dragOpenIndices = [];\n\n for (let i = 0; i < components.length; i++) {\n if (components[i].isDisplayed() && this._openState[i]) {\n this._dragOpenIndices.push(i);\n }\n }\n\n this._dragGutterUpperPos = this._dragOpenIndices.indexOf(components.indexOf(pair.upper));\n\n if (this._dragGutterUpperPos === -1) {\n return;\n }\n\n this._dragUpper = pair.upper;\n this._dragLower = pair.lower;\n this._dragLastPointer = position;\n\n // Accordion is a LayoutManager, not a Component, so it cannot key this\n // registration on `this` the way every other `Event.addViewportListener`\n // call site does (they call it from inside a Component, on themselves).\n // The container Component it is attached to is the stable key instead —\n // the same \"arbitrary Component key\" shape `DragManager` uses from\n // module-level code. `_boundOnGutterDragEnd` is a field so add/remove\n // share one reference regardless of which component's `apply` invokes it.\n const container = this.getContainer();\n\n if (container) {\n Event.addViewportListener(container, \"mouseup\", this._boundOnGutterDragEnd);\n Event.addViewportListener(container, \"touchend\", this._boundOnGutterDragEnd);\n Event.addViewportListener(container, \"touchcancel\", this._boundOnGutterDragEnd);\n }\n }\n\n /**\n * Resizes the open sections when a resizable gutter is dragged. The gutter\n * splits the open set into an upper group (the upper section and everything\n * above it) and a lower group (the lower section and below); dragging down\n * grows the upper group and shrinks the lower one, dragging up does the\n * reverse. **Both sides chain outward** from the gutter: the nearest section\n * absorbs the travel first, spilling to the next once it reaches its max\n * (on the growing side) or its min (on the shrinking side). So dragging\n * toward — or away from — a panel that sits against a maxed or minned\n * neighbour keeps resizing it, growing/shrinking the first free panel beyond\n * the pinned one (which merely slides) rather than stalling at the boundary.\n *\n * The open set's combined height is conserved, so this re-places every\n * displayed section through the shared {@link layoutSections} (no\n * `getPreferredSize`, reflowing only the sections whose height changed) so\n * the handle tracks the cursor. Each open section's drag-backed size is\n * written to `_resizeSizes` (in pre-factor stored units, see `_resizeFactor`)\n * so the next full layout reproduces the distribution.\n *\n * @param gutterIndex - The dragged gutter's position in `_gutterPairs`.\n * @param position - The absolute pointer coordinate (`clientY`) for this move.\n */\n private onGutterDrag(gutterIndex: number, position: number): void {\n const pair = this._gutterPairs[gutterIndex];\n const container = this.getContainer();\n\n if (!pair || pair.upper !== this._dragUpper || pair.lower !== this._dragLower || !container) {\n return;\n }\n\n const components = container.getComponents();\n const openIndices = this._dragOpenIndices;\n const upperPos = this._dragGutterUpperPos;\n\n // Applied incrementally: this frame's pointer travel is distributed on\n // top of the live heights, so a reversed drag responds nearest-first.\n // `_dragLastPointer` is advanced below by only the travel actually applied,\n // not the raw pointer position — see the note next to `delta`.\n const frameDelta = position - this._dragLastPointer;\n\n // Snapshot each open section's live height and bounds once — `getMinSize`\n // / `getMaxSize` recurse through the content's own layout, so reading them\n // per pointer move (not per lookup) keeps the drag cheap.\n const current = openIndices.map((ci): number => components[ci].getHeight());\n const mins = openIndices.map((ci): number => {\n const min = components[ci].getMinSize();\n\n return min ? min.height : 0;\n });\n const maxs = openIndices.map((ci): number => {\n const max = components[ci].getMaxSize();\n\n return max ? max.height : Number.POSITIVE_INFINITY;\n });\n\n // The two chains fanning out from the gutter, each ordered nearest-first.\n const upperGroup: number[] = [];\n const lowerGroup: number[] = [];\n\n for (let pos = upperPos; pos >= 0; pos--) {\n upperGroup.push(pos);\n }\n\n for (let pos = upperPos + 1; pos < openIndices.length; pos++) {\n lowerGroup.push(pos);\n }\n\n // Dragging down grows the upper group and shrinks the lower one; up reverses it.\n const growGroup = frameDelta >= 0 ? upperGroup : lowerGroup;\n const shrinkGroup = frameDelta >= 0 ? lowerGroup : upperGroup;\n\n const growRoom = chainRoom(growGroup, current, 1, mins, maxs);\n const shrinkRoom = chainRoom(shrinkGroup, current, -1, mins, maxs);\n\n // This frame's boundary travel, capped by how much the growth chain can\n // still absorb and the shrink chain can still give up.\n const delta = Math.max(0, Math.min(Math.abs(frameDelta), growRoom, shrinkRoom));\n\n // Advance the tracked pointer only by the travel actually applied. When the\n // chain is fully maxed/minned, `delta` is 0 and the pointer stays put, so\n // dragging further past the limit accrues a dead zone the pointer must\n // retrace before the gutter moves again — keeping the cursor glued to the\n // handle on reversal instead of the handle jumping to a far-off cursor.\n // (Split/Border get this for free from their absolute origin+offset model.)\n this._dragLastPointer += Math.sign(frameDelta) * delta;\n\n const newHeights = current.slice();\n\n distributeDragChain(growGroup, current, delta, +1, mins, maxs, newHeights);\n distributeDragChain(shrinkGroup, current, delta, -1, mins, maxs, newHeights);\n\n const openHeightByIndex = new Map<number, number>();\n\n for (let pos = 0; pos < openIndices.length; pos++) {\n const component = components[openIndices[pos]];\n // A pinned section renders at its stored px (scale 1); every other open\n // section renders at stored × _resizeFactor. Dividing by the wrong one\n // silently rescales the whole open set on the next layout.\n const scale = this._resizePinned.has(component) ? 1 : this._resizeFactor;\n\n openHeightByIndex.set(openIndices[pos], newHeights[pos]);\n this._resizeSizes.set(component, newHeights[pos] / scale);\n }\n\n const insets = container.getContentInsets();\n const width = this._panelWrappers[openIndices[0]].getWidth();\n\n this.layoutSections(\n components,\n width,\n insets.getLeft(),\n insets.getTop(),\n true,\n (i, isOpen): number => (isOpen ? (openHeightByIndex.get(i) ?? components[i].getHeight()) : components[i].getHeight()),\n false,\n false,\n );\n }\n\n /**\n * Ends a resizable-gutter drag: removes the viewport listeners and clears\n * the captured drag pair. Transitions stay off (their default outside a\n * toggle), so there is nothing to restore. Fires `sectionresize` with the\n * post-drag sizes when a drag was actually live — including on the\n * `detach()` mid-drag path, which calls this before `_resizeSizes` is\n * cleared, so the emitted sizes still reflect the drag. Also callable\n * directly (with no argument) so `detach()` and tests can simulate a\n * drag end.\n *\n * @returns `true`, consuming the release that ends the gutter drag.\n */\n private onGutterDragEnd(): Event.ListenerResult {\n const wasDragging = this._dragUpper !== null;\n const container = this.getContainer();\n\n if (container) {\n Event.removeViewportListener(container, \"mouseup\", this._boundOnGutterDragEnd);\n Event.removeViewportListener(container, \"touchend\", this._boundOnGutterDragEnd);\n Event.removeViewportListener(container, \"touchcancel\", this._boundOnGutterDragEnd);\n }\n\n this._dragUpper = null;\n this._dragLower = null;\n\n if (wasDragging) {\n this.emit(\"sectionresize\", this.getSectionSizes());\n }\n\n return true;\n }\n\n /**\n * Computes the vertical shrink ratio applied to every open section's\n * content so the accordion fits its container, mirroring VBox's\n * preferred-mode shrink. Headers are fixed and never shrink; only open\n * sections contribute shrinkable content.\n *\n * Three cases:\n * 1. The open sections already fit (`preferred <= budget`) → ratio `0`,\n * so every section renders at its preferred height.\n * 2. They overflow but fit at their combined minimum\n * (`min <= budget < preferred`) → ratio\n * `(preferred - budget) / (preferred - min)`, clamped to `[0, 1]`, so\n * each section shrinks proportionally from preferred toward its min.\n * 3. They overflow even at the combined minimum (`budget < min`) →\n * ratio `0`, falling back to preferred and letting the host clip. A\n * layout crammed below every section's min reads worse than a clean\n * overflow, so the manager declines to shrink past min.\n *\n * @param components - The container's content components, section-ordered.\n * @param containerSize - The container's inner size; `null` short-circuits\n * to `0` (no shrink) since the budget is unknown.\n * @returns The shrink ratio in `[0, 1]`.\n */\n private computeShrinkRatio(components: Component[], containerSize: Size | null): number {\n if (!containerSize) {\n return 0;\n }\n\n let headerTotal = 0;\n let openPreferred = 0;\n let openMin = 0;\n let displayedSoFar = 0;\n\n for (let i = 0; i < components.length; i++) {\n // A non-displayed section shows neither its header nor its content,\n // so it contributes nothing to the height budget.\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n // The inter-section gap is fixed (non-shrinkable) budget, like the\n // headers — count it between displayed sections (see doLayout).\n if (displayedSoFar > 0) {\n headerTotal += this._spacing;\n }\n\n displayedSoFar += 1;\n headerTotal += this.effectiveHeaderHeight();\n\n if (!this._openState[i]) {\n continue;\n }\n\n const pref = components[i].getPreferredSize();\n const min = components[i].getMinSize();\n\n openPreferred += pref ? pref.height : 100;\n openMin += min ? min.height : 0;\n }\n\n const totalPreferred = headerTotal + openPreferred;\n const totalMin = headerTotal + openMin;\n const budget = containerSize.height;\n\n if (totalPreferred <= budget || totalMin > budget) {\n return 0;\n }\n\n const shrinkable = totalPreferred - totalMin;\n\n return shrinkable > 0 ? (totalPreferred - budget) / shrinkable : 0;\n }\n\n /**\n * The height an open section's content renders at: its preferred height\n * shrunk toward its minimum by `shrinkRatio`, then clamped to its merged\n * `[min, max]`. Falls back to 100px when the section reports no preferred\n * height.\n *\n * The clamp matters because `getPreferredSize` clamps only to a component's\n * *own* min/max constraints, not the merged {@link Component.getMinSize} /\n * {@link Component.getMaxSize} that fold in child-derived limits (e.g. a\n * Panel wrapping a fixed-row List, or a form whose fields floor its height).\n * Without it, a section whose preferred sits outside its real `[min, max]`\n * would render its wrapper past that bound while the content self-clamped\n * inside — and the drag-backed resizable path (which does respect the merged\n * bounds) then disagreed, making a resizable toggle resize the section.\n *\n * @param component - The section content component.\n * @param shrinkRatio - The container-driven shrink ratio in `[0, 1]`.\n * @returns The content height in pixels.\n */\n private openContentHeight(component: Component, shrinkRatio: number): number {\n const preferred = component.getPreferredSize();\n const contentPref = preferred ? preferred.height : 100;\n const min = component.getMinSize();\n const contentMin = min ? min.height : 0;\n\n const shrunk = contentPref - shrinkRatio * (contentPref - contentMin);\n const max = component.getMaxSize();\n const capped = max ? Math.min(shrunk, max.height) : shrunk;\n\n return Math.max(capped, contentMin);\n }\n\n /**\n * Computes how much extra height each open section absorbs from the\n * container's leftover space, keyed by section index. Active only when the\n * open sections underflow the container (`leftover > 0`); on overflow the\n * leftover is `<= 0` so fill yields nothing and shrink handles the fit.\n *\n * Each open section's effective fill weight is its explicit `weight`\n * constraint, or — when {@link setFillHeight} is on — a default of `1` so\n * every unweighted open section shares the slack equally. The leftover is\n * then split across all sections with a positive effective weight in\n * proportion to those weights, each capped at its own max (a capped\n * section's surplus is re-shared among the rest; see\n * {@link distributeFillWithinMax}). So `setFillHeight` spreads the slack\n * across every open section (equal weights → equal slices) rather than\n * padding a single one, and per-section `weight` still targets or biases\n * specific sections. With neither, nothing fills and the slack stays as\n * trailing space.\n *\n * @param components - The container's child components.\n * @param containerSize - The container's inner size, or null.\n * @param shrinkRatio - The shrink ratio applied to open content.\n * @returns A map from section index to the extra height it absorbs; empty\n * when nothing fills.\n */\n private computeFill(components: Component[], containerSize: Size | null, shrinkRatio: number): Map<number, number> {\n const fills = new Map<number, number>();\n\n if (!containerSize) {\n return fills;\n }\n\n let used = 0;\n let displayed = 0;\n const recipients: Array<{ index: number; weight: number; headroom: number }> = [];\n let weightTotal = 0;\n\n for (let i = 0; i < components.length; i++) {\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n if (displayed > 0) {\n used += this._spacing;\n }\n\n displayed += 1;\n used += this.effectiveHeaderHeight();\n\n if (this._openState[i]) {\n const contentHeight = this.openContentHeight(components[i], shrinkRatio);\n used += contentHeight;\n\n const weight = this.effectiveWeight(components[i]);\n\n if (weight > 0) {\n recipients.push({ index: i, weight, headroom: this.fillHeadroom(components[i], contentHeight) });\n weightTotal += weight;\n }\n }\n }\n\n const leftover = containerSize.height - used;\n\n if (leftover <= 0 || weightTotal <= 0) {\n return fills;\n }\n\n // Split the slack across the recipients by weight, each capped at its own\n // max so an open section is never padded past its maximum height; a\n // capped section's surplus is re-shared among the rest.\n return this.distributeFillWithinMax(recipients, leftover);\n }\n\n /**\n * Resolves an open section's effective weight — its explicit `weight`\n * constraint, or a default of `1` when {@link setFillHeight} is on so every\n * unweighted open section counts equally. `0` means unweighted: the section\n * takes no share of the container's leftover height, and in resizable mode it\n * holds its px across a container resize instead of rescaling with the rest.\n *\n * Reads the constraint as `?? 0`, matching the box managers — unlike `Split`,\n * where an unset weight falls through to a proportional fallback.\n *\n * @param component - The open section's content component.\n * @returns The effective weight; `0` when the section is unweighted.\n */\n private effectiveWeight(component: Component): number {\n const explicit = this.getLayoutConstraints(component)?.weight ?? 0;\n\n return explicit > 0 ? explicit : (this._fillHeight ? 1 : 0);\n }\n\n /**\n * The open sections that hold their stored px across a container resize —\n * those with an effective weight of `0`, so only the weighted sections absorb\n * the change. Returns empty (so the whole open set rescales proportionally, as\n * it always has) in the two cases where pinning cannot apply: no open section\n * is weighted, so there is nothing to absorb the change; or the pins alone\n * overrun the budget, where a pin must yield because geometry has to fill the\n * container — mirroring `Split.setPaneResizeWeight`'s contract that a pin holds\n * only while the container is large enough.\n *\n * @param components - The container's content components, section-ordered.\n * @param openIndices - Indices of the open sections.\n * @param openBudget - The height available to the open sections' content.\n * @returns The pinned sections' indices, or an empty array.\n */\n private resizePinnedSections(components: Component[], openIndices: number[], openBudget: number): number[] {\n const pinned: number[] = [];\n let flexible = 0;\n let pinnedTotal = 0;\n\n for (const i of openIndices) {\n if (this.effectiveWeight(components[i]) > 0) {\n flexible += 1;\n } else {\n pinned.push(i);\n pinnedTotal += this.clampSectionHeight(components[i], this._resizeSizes.get(components[i]) ?? 0);\n }\n }\n\n if (flexible === 0 || pinnedTotal > openBudget) {\n return [];\n }\n\n return pinned;\n }\n\n /**\n * Clamps a candidate content height to a section's `[min, max]`, so a pin is\n * held within the same bounds the proportional pass enforces.\n *\n * @param component - The open section's content component.\n * @param value - The candidate content height in px.\n * @returns The clamped content height.\n */\n private clampSectionHeight(component: Component, value: number): number {\n const min = component.getMinSize();\n const max = component.getMaxSize();\n\n return Util.clamp(value, min ? min.height : 0, max ? max.height : Number.POSITIVE_INFINITY);\n }\n\n /**\n * How much fill a section can still absorb before reaching its maximum\n * height — the gap between its max and the content height it already takes\n * without any fill. Unbounded when the section declares no max.\n *\n * @param component - The open section's content component.\n * @param contentHeight - The section's fill-free content height.\n * @returns The absorbable headroom in pixels, or `Infinity` when unbounded.\n */\n private fillHeadroom(component: Component, contentHeight: number): number {\n const max = component.getMaxSize();\n\n return max ? Math.max(0, max.height - contentHeight) : Number.POSITIVE_INFINITY;\n }\n\n /**\n * Splits `leftover` fill across the weighted recipients in proportion to\n * their weights, capping each at its remaining headroom (`max − content`)\n * and re-sharing a capped recipient's surplus among the rest. Mirrors\n * {@link distributeWithinConstraints} for the fill path so a weighted\n * section is never padded past its max; any surplus the remaining\n * recipients cannot absorb stays as slack.\n *\n * @param recipients - The weighted open sections: index, weight, headroom.\n * @param leftover - The container's leftover height to distribute.\n * @returns A map from section index to the extra height it absorbs.\n */\n private distributeFillWithinMax(recipients: Array<{ index: number; weight: number; headroom: number }>, leftover: number): Map<number, number> {\n const fills = new Map<number, number>();\n const free = new Set(recipients);\n let remaining = leftover;\n\n for (;;) {\n let freeWeight = 0;\n\n for (const r of free) {\n freeWeight += r.weight;\n }\n\n if (freeWeight <= 0) {\n break;\n }\n\n const perWeight = remaining / freeWeight;\n let capped = false;\n\n for (const r of free) {\n if (r.weight * perWeight > r.headroom) {\n fills.set(r.index, r.headroom);\n remaining -= r.headroom;\n free.delete(r);\n capped = true;\n\n break;\n }\n }\n\n if (!capped) {\n for (const r of free) {\n fills.set(r.index, r.weight * perWeight);\n }\n\n break;\n }\n\n if (free.size === 0) {\n break;\n }\n }\n\n return fills;\n }\n\n /**\n * Computes each open section's content height in resizable mode, keyed by\n * container index. Supersedes the `openContentHeight + fill` path when\n * active: a section with no stored size yet is seeded from what that\n * legacy path would have given it (so turning resizable on is visually\n * seamless), then every open section's stored size is rescaled by\n * `openBudget / storedTotal` so the set always fills the container without\n * rewriting the stored ratio — a drag ({@link onGutterDrag}) is the only\n * thing that changes the ratio itself. Also prunes `_resizeSizes` entries\n * for components no longer in `components`.\n *\n * @param components - The container's content components, section-ordered.\n * @param containerSize - The container's inner size; `null` short-circuits\n * to `null` (caller falls back to the legacy path).\n * @param shrinkRatio - The container-driven shrink ratio, used only to seed\n * a not-yet-stored section from the legacy formula.\n * @param fills - The legacy fill map, used only to seed a not-yet-stored\n * section from the legacy formula.\n * @returns A map from container index to content height for every open\n * section, or `null` when resizable mode is inactive, there is no\n * container size, or no section is open (the caller keeps the legacy\n * `openContentHeight + fill` path in all three cases).\n */\n private computeResizableHeights(components: Component[], containerSize: Size | null, shrinkRatio: number, fills: Map<number, number>): Map<number, number> | null {\n if (!this._resizable || !containerSize) {\n return null;\n }\n\n for (const stored of [...this._resizeSizes.keys()]) {\n if (!components.includes(stored)) {\n this._resizeSizes.delete(stored);\n }\n }\n\n let headerTotal = 0;\n let displayedSoFar = 0;\n const openIndices: number[] = [];\n\n for (let i = 0; i < components.length; i++) {\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n if (displayedSoFar > 0) {\n headerTotal += this._spacing;\n }\n\n displayedSoFar += 1;\n headerTotal += this.effectiveHeaderHeight();\n\n if (this._openState[i]) {\n openIndices.push(i);\n }\n }\n\n if (openIndices.length === 0) {\n return null;\n }\n\n const openBudget = containerSize.height - headerTotal;\n\n this.applyPendingSectionSizes(components, openBudget);\n\n for (const i of openIndices) {\n const component = components[i];\n\n if (!this._resizeSizes.has(component)) {\n this._resizeSizes.set(component, this.openContentHeight(component, shrinkRatio) + (fills.get(i) ?? 0));\n }\n }\n\n return this.distributeWithinConstraints(components, openIndices, openBudget);\n }\n\n /**\n * Drains the `sectionSizes` option (or a pre-layout\n * {@link applySectionSizes} call) into `_resizeSizes` on the first\n * resizable layout that can resolve an open budget. Runs once: cleared\n * unconditionally so a stale array is not retried on every later layout.\n *\n * @param components - The container's content components, section-ordered.\n * @param openBudget - The height available to the open sections' content,\n * the base {@link fromLayoutSizes} seeds the ratio entries against.\n */\n private applyPendingSectionSizes(components: Component[], openBudget: number): void {\n const pending = this._pendingSectionSizes;\n\n if (pending === null) {\n return;\n }\n\n this._pendingSectionSizes = null;\n\n const units = this.sectionSizeUnits(components);\n\n if (!isRestorableSizes(pending, units)) {\n return;\n }\n\n const stored = fromLayoutSizes(pending, openBudget);\n\n for (let idx = 0; idx < components.length; idx += 1) {\n // A zero-size section is *removed* rather than stored as 0, so it falls\n // back to the legacy `openContentHeight + fill` seed below instead of\n // taking a zero share of the budget.\n if (stored[idx] > 0) {\n this._resizeSizes.set(components[idx], stored[idx]);\n } else {\n this._resizeSizes.delete(components[idx]);\n }\n }\n }\n\n /**\n * Splits `openBudget` across the open sections in proportion to their\n * stored sizes, clamped to each section's `[min, max]` height. An unweighted\n * open section (effective weight `0`) is first held at its stored px and\n * removed from the budget, so only the weighted sections absorb a container\n * resize; the split falls back to rescaling the whole set when no section is\n * weighted or the pins overrun the budget. A section\n * whose proportional share would fall outside its bounds is pinned at the\n * violated bound and removed from the budget; the remaining sections then\n * re-share what is left, iterating until every free section fits. This\n * keeps the open heights summing to `openBudget` — so the stack neither\n * overflows the container (a min floor pushing content past the box) nor\n * leaves it under-filled — while never stretching a section past its max\n * or compressing it below its min. It generalises {@link onGutterDrag}'s\n * per-pair `[min, max]` clamp to the whole open set, so a full layout and a\n * drag agree on the constraints (a section rendered past its max otherwise\n * snapped down the instant its gutter was grabbed).\n *\n * When the constraints make an exact fill impossible — the mins already\n * exceed the budget, or the maxes cannot reach it — the pinned sizes stand:\n * the stack over- or under-fills, which the host clips/scrolls or leaves as\n * slack, matching the `getMinSize` contract.\n *\n * Also caches {@link _resizeFactor} — the stored→rendered scale of the\n * *unpinned* sections, which is the mapping a drag moves within.\n *\n * @param components - The container's content components, section-ordered.\n * @param openIndices - Indices of the open sections to distribute across.\n * @param openBudget - The height available to the open sections' content.\n * @returns A map from section index to its clamped content height.\n */\n private distributeWithinConstraints(components: Component[], openIndices: number[], openBudget: number): Map<number, number> {\n const heights = new Map<number, number>();\n const free = new Set<number>(openIndices);\n let remaining = openBudget;\n let freeFactor = 1;\n\n // Resize-pinned sections hold their stored px and leave the budget before the\n // proportional pass, so only the weighted sections absorb a container resize.\n // Empty unless the open set is mixed (some weighted, some not) — with no\n // weighted section the whole set rescales proportionally, exactly as before.\n this._resizePinned.clear();\n\n for (const i of this.resizePinnedSections(components, openIndices, openBudget)) {\n const height = this.clampSectionHeight(components[i], this._resizeSizes.get(components[i]) ?? 0);\n\n heights.set(i, height);\n remaining -= height;\n free.delete(i);\n this._resizePinned.add(components[i]);\n }\n\n // At most one section is pinned per pass, so this settles in at most\n // `openIndices.length` passes.\n for (;;) {\n let freeStored = 0;\n\n for (const i of free) {\n freeStored += this._resizeSizes.get(components[i]) ?? 0;\n }\n\n freeFactor = freeStored > 0 ? remaining / freeStored : 0;\n let pinned = false;\n\n for (const i of free) {\n const component = components[i];\n const share = (this._resizeSizes.get(component) ?? 0) * freeFactor;\n const min = component.getMinSize();\n const max = component.getMaxSize();\n const lo = min ? min.height : 0;\n const hi = max ? max.height : Number.POSITIVE_INFINITY;\n\n if (share < lo) {\n heights.set(i, lo);\n remaining -= lo;\n free.delete(i);\n pinned = true;\n\n break;\n }\n\n if (share > hi) {\n heights.set(i, hi);\n remaining -= hi;\n free.delete(i);\n pinned = true;\n\n break;\n }\n }\n\n if (!pinned) {\n for (const i of free) {\n heights.set(i, (this._resizeSizes.get(components[i]) ?? 0) * freeFactor);\n }\n\n break;\n }\n\n if (free.size === 0) {\n break;\n }\n }\n\n // Cached for onGutterDrag, which converts its rendered-pixel drag math\n // back to the stored scale before writing into `_resizeSizes`.\n this._resizeFactor = freeFactor > 0 ? freeFactor : 1;\n\n return heights;\n }\n\n /**\n * Handles ArrowDown, ArrowUp, Home, and End to move keyboard focus between headers.\n *\n * @param e - The keyboard event fired on a header.\n * @param index - Zero-based index of the header that received the event.\n */\n private onHeaderKeyDown(e: KeyboardEvent, index: number): void {\n const count = this._headers.length;\n\n if (count === 0) {\n return;\n }\n\n let target: number;\n\n switch (e.key) {\n case 'ArrowDown': target = (index + 1) % count; break;\n case 'ArrowUp': target = (index - 1 + count) % count; break;\n case 'Home': target = 0; break;\n case 'End': target = count - 1; break;\n default: return;\n }\n\n e.preventDefault();\n this._headers[target].getTitleButton().focus();\n }\n\n /**\n * Handles a header click: toggles the open state, enforces single-open mode,\n * updates the indicator and ARIA, fires the callback, and re-runs layout.\n *\n * @param index - Zero-based index of the clicked header.\n */\n private onHeaderClicked(index: number): void {\n const wasOpen = this._openState[index];\n\n if (this._singleOpen && !wasOpen) {\n for (let i = 0; i < this._openState.length; i++) {\n if (i !== index && this._openState[i]) {\n this.primeWrapper(i);\n this._openState[i] = false;\n this._headers[i].setExpanded(false);\n this.emit(\"sectiontoggle\", i, false);\n }\n }\n }\n\n const nowOpen = !wasOpen;\n\n this.primeWrapper(index);\n this._openState[index] = nowOpen;\n this._headers[index].setExpanded(nowOpen);\n this.emit(\"sectiontoggle\", index, nowOpen);\n this.relayoutHost();\n }\n\n /**\n * Pre-promotes the panel wrapper to its own compositor layer for the\n * duration of the active toggle so the first transition frame doesn't\n * pay a layer-creation cost, then schedules the layer's release on\n * `transitionend` (filtered to `height`) with a `setTimeout` fallback\n * for the cases where `transitionend` never fires — toggling a section\n * whose height didn't change, tab-switch mid-transition, …\n *\n * Transitions are off by default (so resize and drag relayouts snap), so a\n * toggle must turn them on before its `doLayout` writes land and off again\n * when the animation completes. Enabling covers every header, panel wrapper,\n * content component, and gutter (a toggle moves the wrapper's height, the\n * `top` of every header + wrapper below it, and each open section's content\n * height). The global disable is gated on the `_toggleAnimations` counter:\n * single-open mode primes several sections in one gesture, so the first\n * animation to finish must not snap the others still in flight — only the\n * last (`_toggleAnimations` back to zero) turns transitions off.\n *\n * Under `prefers-reduced-motion: reduce` transitions stay off (their\n * default) and nothing is enabled, so the toggle's writes land instantly.\n *\n * Mirrors the `transitionend`-with-fallback pattern in\n * [`Animation.play`](/api/core/namespaces/Animation/functions/play) so\n * the bookkeeping stays in one shape across the framework.\n *\n * @param index - Zero-based index of the section whose wrapper to prime.\n */\n private primeWrapper(index: number): void {\n // Reduced motion leaves transitions off (their default), so the toggle's\n // doLayout writes land instantly — there is nothing to prime.\n if (Animation.isReducedMotion()) {\n return;\n }\n\n const wrapper = this._panelWrappers[index];\n const container = this.getContainer();\n\n this.setSectionTransitions(true);\n\n // Give the container's own height the same transition so the outer\n // layout's resize (triggered when the parent re-queries our\n // `getPreferredSize` after the open state flips) animates in lockstep\n // with the wrappers and headers inside. Without this, the container\n // (which defaults to `overflow: hidden`) snaps to the closed size and\n // clips the still-animating sections — they vanish for ~75% of the\n // duration and pop back in near the end. Cleared on cleanup so\n // unrelated height changes (window resize, etc.) stay instant.\n container?.setTransition(`height ${this._animationDuration}ms ${ACCORDION_EASING}`);\n\n wrapper.setWillChange(\"height\");\n\n // Commit the just-enabled transitions as the animation's \"before\" frame\n // *before* the toggle's geometry change lands. That change can run\n // synchronously in this same task (relayoutHost -> notifyIntrinsicSize-\n // Changed re-lays out the accordion), and a CSS transition enabled and\n // triggered in one task never animates — the browser only ever sees the\n // after-state. A single forced layout read flushes the transition style\n // as the pre-toggle snapshot so the upcoming height change animates from\n // it. (Transitions are off outside a toggle, so nothing else pays this.)\n const element = container?.getElement();\n\n if (element) {\n DOM.source.getElementRect(element);\n }\n\n // A re-toggle inside the animation duration replaces the in-flight\n // animation rather than racing it. Its onComplete will never run, so\n // the replacement inherits its slot in `_toggleAnimations` instead of\n // adding a second one — otherwise the counter never returns to zero and\n // the cleanup branch below is unreachable for the rest of the manager's\n // life. Completed animations delete themselves from the map, so a\n // non-null `get` here always means a genuinely in-flight animation.\n const inFlight = this._wrapperAnimations.get(index);\n\n if (inFlight) {\n inFlight.cancel();\n this._wrapperAnimations.delete(index);\n } else {\n this._toggleAnimations += 1;\n }\n\n this._wrapperAnimations.set(index, Animation.afterTransition({\n component: wrapper,\n property: \"height\",\n durationMs: this._animationDuration,\n fallbackBufferMs: 40,\n onComplete: () => {\n this._wrapperAnimations.delete(index);\n\n wrapper.setWillChange(null);\n this._toggleAnimations -= 1;\n\n // Only the last toggle to complete turns transitions back off,\n // so an earlier finisher can't snap sections still animating.\n if (this._toggleAnimations <= 0) {\n this._toggleAnimations = 0;\n this.setSectionTransitions(false);\n container?.setTransition(null);\n\n // Re-lay-out the host now that the geometry has settled. The\n // toggle's own relayoutHost ran while this animation was\n // still in flight, so every DOM measurement it took saw the\n // pre-toggle extent: a scrolling host measured its scrollbar\n // gutter against content that was mid-transition and still\n // overflowing, and so kept reserving the gutter even though\n // the bar disappears once the animation lands. Nothing else\n // re-measures afterwards, so the stale reserve would linger\n // until some unrelated layout — in practice, the next toggle\n // — making the reclaim run one gesture behind.\n this.relayoutHost();\n }\n },\n }));\n }\n\n /**\n * Installs or removes the open/close animation transitions on every header,\n * panel wrapper, content component, and resize gutter. Enabled by\n * `primeWrapper` for the duration of a toggle; off otherwise so resize and\n * drag relayouts snap to their final geometry.\n *\n * @param enabled - True to install the animated transitions, false to clear them.\n */\n private setSectionTransitions(enabled: boolean): void {\n const components = this.getContainer()?.getComponents() ?? [];\n\n for (let i = 0; i < this._headers.length; i++) {\n this._headers[i].setTransition(enabled ? this.buildHeaderTransition() : \"none\");\n this._panelWrappers[i].setTransition(enabled ? this.buildWrapperTransition() : \"none\");\n components[i]?.setTransition(enabled ? this.buildContentTransition() : \"none\");\n }\n\n for (const gutter of this._resizeGutters) {\n gutter.setTransition(enabled ? this.buildHeaderTransition() : \"none\");\n }\n }\n\n /**\n * Builds the `top`-only transition shorthand applied to every header.\n * Centralised so `createSection` and `primeWrapper`'s reduced-motion\n * restore path stay in sync.\n */\n private buildHeaderTransition(): string {\n return `top ${this._animationDuration}ms ${ACCORDION_EASING}`;\n }\n\n /**\n * Builds the multi-property transition shorthand applied to every panel\n * wrapper — `height` animates the wrapper's own grow/shrink, `top`\n * animates the wrappers below a toggled section so they slide with the\n * headers instead of jumping.\n */\n private buildWrapperTransition(): string {\n return `height ${this._animationDuration}ms ${ACCORDION_EASING}, top ${this._animationDuration}ms ${ACCORDION_EASING}`;\n }\n\n /**\n * Builds the `height`-only transition applied to every section's content\n * component so it grows and shrinks in lockstep with its wrapper. The content\n * sits at `top: 0` inside the wrapper, so only its height ever animates.\n * Centralised so `createSection` and `primeWrapper`'s reduced-motion restore\n * path stay in sync.\n */\n private buildContentTransition(): string {\n return `height ${this._animationDuration}ms ${ACCORDION_EASING}`;\n }\n}\n\nconst AccordionCallable = callable(Accordion);\ntype AccordionCallable = Accordion;\nexport {\n Accordion as _Accordion,\n AccordionCallable as Accordion\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Component } from \"~/core/Component.js\";\n\n/**\n * Layout constraints for child components added to an {@link Accordion} container.\n * Provides the header label and initial open state for each collapsible section.\n *\n * @category Layouts\n */\nexport class AccordionConstraints extends LayoutConstraints {\n\n /** Text displayed in the section's header button. */\n label: string;\n\n /** Whether the section starts expanded. Defaults to false if omitted. */\n initiallyOpen?: boolean;\n\n /** Per-section tool components shown in this section's header tool group. */\n tools?: Component[];\n\n /**\n * @param label - Text displayed in the section header.\n * @param initiallyOpen - Whether the section starts expanded. Defaults to false.\n * @param glyph - Optional registry glyph name shown leading the header\n * label. Stored on the inherited `glyph` constraint field.\n * @param tools - Optional per-section tool components for this header.\n */\n constructor(label: string, initiallyOpen?: boolean, glyph?: string, tools?: Component[]) {\n super();\n\n this.label = label;\n this.initiallyOpen = initiallyOpen;\n this.glyph = glyph ?? null;\n this.tools = tools;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\n\n/**\n * Per-child layout constraints for components added to a {@link Grid} container.\n * Carries optional cell spanning ({@link GridConstraints.rowSpan} /\n * {@link GridConstraints.colSpan}) and explicit cell placement\n * ({@link GridConstraints.col} / {@link GridConstraints.row}).\n *\n * @remarks A child counts as explicitly placed if **either** `col` or `row` is\n * provided; the missing axis defaults to `0`. Explicitly placed children are\n * reserved before any auto-flow, so un-positioned children flow around them.\n * `col` / `row` are 0-based cell indices clamped to the grid's column/row count\n * at read time. `rowSpan` / `colSpan` default to `1` and clamp to the cells\n * remaining from the child's origin.\n *\n * The inherited `fill` ({@link FillType}) and `anchor` ({@link AnchorType})\n * control how the child sits inside its cell and take precedence over the\n * grid's `defaultFill` / `defaultAnchor`: a child with `fill = FillType.NONE`\n * shrinks to its preferred size and parks at its `anchor` even when the grid\n * default is `FillType.BOTH`. Leave them unset to inherit the grid defaults.\n *\n * @category Layouts\n */\nexport class GridConstraints extends LayoutConstraints {\n\n /** 0-based explicit column index; clamped to grid bounds at read time. */\n col?: number;\n\n /** 0-based explicit row index; clamped to grid bounds at read time. */\n row?: number;\n\n /** Number of rows the child spans. Defaults to `1`. */\n rowSpan?: number;\n\n /** Number of columns the child spans. Defaults to `1`. */\n colSpan?: number;\n\n constructor() {\n super();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GridConstraints-C58LuTqM.js","names":[],"sources":["../../src/typescript/lib/component/container/AccordionIndicator.ts","../../src/typescript/lib/component/container/AccordionHeader.ts","../../src/typescript/lib/layout/Accordion.ts","../../src/typescript/lib/layout/AccordionConstraints.ts","../../src/typescript/lib/layout/GridConstraints.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link AccordionIndicator}.\n *\n * @category Components\n */\nexport interface AccordionIndicatorOptions extends ComponentOptions {\n expanded?: boolean;\n /** The character drawn as the chevron. Defaults to `\"▶\"` (a right-pointing triangle). */\n character?: string;\n}\n\n/**\n * Width of the chevron's HBox cell, in pixels. Wide enough to seat the chevron\n * glyph plus a little breathing room; the height is governed by the header's\n * stretching HBox, so only the width is fixed here.\n */\nconst CHEVRON_CELL_WIDTH: number = 14;\n\n/**\n * Default chevron character — a right-pointing triangle that the `.expanded`\n * rule rotates 90° to point down. A single rotating character covers both\n * states, which is why the chevron is a plain character rather than a registry\n * glyph.\n */\nconst DEFAULT_CHEVRON: string = \"▶\";\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.AccordionIndicator` class rule once on first use. The\n * rule holds the chevron's typography (font-size), horizontal centring,\n * colour, and the default `transform` transition. Vertical centring is done by\n * tracking `line-height` to the stretched cell height (see {@link setHeight}),\n * and position is owned by the framework's absolute layout (the indicator is an\n * ordinary in-flow cell, no longer an overlay), so neither lives here.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureAccordionIndicatorClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"AccordionIndicator\",\n styles: {\n textAlign: \"center\",\n pointerEvents: \"none\",\n fontSize: \"10px\",\n color: \"var(--ts-ui-accordion-indicator-color, rgb(100,100,100))\",\n transition: \"transform 200ms ease\",\n },\n });\n}\n\n/**\n * The expand/collapse chevron used by {@link AccordionHeader}.\n *\n * An ordinary in-flow cell in the header's HBox row (positioned by the\n * framework's absolute layout like any other Component). When expanded, a\n * per-instance state rule rotates the chevron 90° via a CSS class toggle on\n * `.expanded`.\n *\n * The static typography + centring lives in a shared `.AccordionIndicator`\n * class rule registered on first use. The per-instance `.expanded` rotation\n * rule is allocated through `Component.createStyleRule` so the framework\n * materialises it at render time.\n *\n * @category Components\n */\nclass AccordionIndicator extends Component<AccordionIndicatorOptions> {\n\n declare private _expanded: boolean;\n declare private _character: string;\n private _lineHeight: number | null = null;\n\n /**\n * Constructs an accordion indicator. The chevron starts collapsed unless\n * `options.expanded` is `true`.\n *\n * @param options - Optional configuration bag (initial expanded state plus\n * common Component fields).\n */\n constructor(options?: AccordionIndicatorOptions) {\n ensureAccordionIndicatorClassRule();\n\n super({ tag: \"span\", preferredSize: { width: CHEVRON_CELL_WIDTH, height: CHEVRON_CELL_WIDTH }, ...(options ?? {}) });\n\n this._expanded ??= false;\n this._character ??= DEFAULT_CHEVRON;\n\n const expandedRule = this.createStyleRule(\".expanded\");\n expandedRule.set(\"transform\", \"rotate(90deg)\");\n }\n\n /**\n * Sets the chevron's `line-height`. Vertical centring relies on a single\n * text line whose `line-height` equals the element's height, so\n * {@link setHeight} keeps this in lockstep with the stretched cell height —\n * the same idiom row renderers use to centre a glyph in a sized row.\n *\n * @param px - Line height in pixels.\n *\n * @returns This indicator, for method chaining.\n */\n private setLineHeight(px: number): this {\n if (this._lineHeight === px) {\n return this;\n }\n\n this._lineHeight = px;\n this.setElementCSSRule(\"lineHeight\", px + \"px\");\n\n return this;\n }\n\n /**\n * Sets the chevron's height and re-syncs `line-height` to it so the single\n * chevron glyph stays vertically centred at whatever height the header's\n * stretching HBox assigns.\n *\n * @param height - Height in pixels.\n *\n * @returns This indicator, for method chaining.\n */\n setHeight(height: number): this {\n super.setHeight(height);\n this.setLineHeight(height);\n\n return this;\n }\n\n /**\n * Applies an {@link AccordionIndicatorOptions} bag. The expanded field is\n * dispatched through {@link setExpanded} so the cached state and class\n * toggle stay in sync; inherited Component fields cascade through\n * `super.applyOptions`.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: AccordionIndicatorOptions): this {\n super.applyOptions(options);\n\n if (options.expanded !== undefined) {\n this.setExpanded(options.expanded);\n }\n\n if (options.character !== undefined) {\n this.setCharacter(options.character);\n }\n\n return this;\n }\n\n /**\n * Returns the chevron character.\n *\n * @returns The current chevron character.\n */\n getCharacter(): string {\n return this._character ?? DEFAULT_CHEVRON;\n }\n\n /**\n * Sets the chevron character, updating the live text when rendered.\n *\n * @param character - The character to draw as the chevron.\n *\n * @returns This indicator, for method chaining.\n */\n setCharacter(character: string): this {\n this._character = character;\n\n const element = this.getElement();\n\n if (element) {\n DOM.sink.apply(element, { text: character });\n }\n\n return this;\n }\n\n /**\n * Renders the root span, writes the chevron glyph as its text content, and\n * applies the cached `.expanded` class when the indicator is in the\n * expanded state at first paint.\n *\n * @returns The rendered root element.\n */\n protected render(): Handle {\n const element = super.render();\n\n DOM.sink.apply(element, { text: this._character ?? DEFAULT_CHEVRON });\n\n if (this._expanded) {\n DOM.sink.apply(element, { addClass: [\"expanded\"] });\n }\n\n return element;\n }\n\n /**\n * Returns the cached expanded state.\n *\n * @returns True when the indicator is in the expanded (rotated) state.\n */\n getExpanded(): boolean {\n return this._expanded;\n }\n\n /**\n * Sets the expanded state of the indicator. Toggles the live `.expanded`\n * class when the element is rendered; otherwise the cached state is\n * applied on first paint by `render`.\n *\n * @param value - True to rotate the chevron to the expanded position.\n * @returns This component, for method chaining.\n */\n setExpanded(value: boolean): this {\n this._expanded = value;\n\n const element = this.getElement();\n\n if (element) {\n DOM.sink.apply(element, { toggleClass: { expanded: value } });\n }\n\n return this;\n }\n\n /**\n * Clears the expanded state (equivalent to `setExpanded(false)`).\n *\n * @returns This component, for method chaining.\n */\n clearExpanded(): this {\n return this.setExpanded(false);\n }\n\n /**\n * Overrides the chevron's `transform` transition timing so it matches the\n * duration and easing of the owning Accordion's panel-height transition.\n * Routes through [`Component.setTransition`](/api/core/classes/Component#settransition), which writes the\n * shorthand to the indicator's own inline style — each instance keeps its\n * own timing without affecting siblings.\n *\n * @param durationMs - Transition duration in milliseconds.\n * @param easing - CSS easing function string.\n * @returns This component, for method chaining.\n */\n setAnimationTiming(durationMs: number, easing: string): this {\n this.setTransition(`transform ${durationMs}ms ${easing}`);\n\n return this;\n }\n}\n\nconst AccordionIndicatorCallable = callable(AccordionIndicator);\ntype AccordionIndicatorCallable = AccordionIndicator;\nexport {\n AccordionIndicator as _AccordionIndicator,\n AccordionIndicatorCallable as AccordionIndicator\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { AccordionIndicator } from \"~/component/container/AccordionIndicator.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisEnd } from \"~/primitive/Axis.js\";\n\n/**\n * Left padding of the header row, in pixels. Reproduces the 8px gap the former\n * Button-based header added to its label's left inset, so the title text keeps\n * the same start offset under the new HBox structure.\n */\nconst HEADER_PADDING_LEFT: number = 8;\n\n/**\n * Right padding of the header row, in pixels. Mirrors the former `right: 10px`\n * offset the chevron overlay used, so the trailing chevron keeps the same gap\n * from the header's right edge now that it is an in-flow cell.\n */\nconst HEADER_PADDING_RIGHT: number = 10;\n\n/**\n * Horizontal gap between the header's HBox cells (title, tool group, chevron),\n * in pixels. Keeps the chevron and tools from butting against the label or each\n * other; small because the title's flex weight already absorbs the slack.\n */\nconst HEADER_CELL_SPACING: number = 4;\n\n/**\n * Compact-mode counterparts of the header's horizontal padding and cell gap,\n * in pixels. Tighter than the defaults so a compact accordion reads denser\n * without changing the text or glyph sizes.\n */\nconst COMPACT_PADDING_LEFT: number = 6;\nconst COMPACT_PADDING_RIGHT: number = 6;\nconst COMPACT_CELL_SPACING: number = 2;\n\n/**\n * Construction-time options for {@link AccordionHeader}.\n *\n * @category Components\n */\nexport interface AccordionHeaderOptions extends ComponentOptions {\n /** Initial expanded state of the chevron indicator. */\n expanded?: boolean;\n /** Which end of the header the chevron sits at. Defaults to `\"end\"`. */\n chevronSide?: AxisEnd;\n /** Optional registry glyph name shown leading the title label. */\n glyph?: string;\n /** Whether the header uses compact (tighter) padding. Defaults to `false`. */\n compact?: boolean;\n /** Character drawn as the expand/collapse chevron. Defaults to `\"▶\"`. */\n chevronGlyph?: string;\n}\n\n/**\n * A section header used by [`Accordion`](/api/layout/classes/Accordion) (from `@jimka/typescript-ui/layout`)\n * to represent one collapsible section.\n *\n * The header is a plain styled {@link Component} that lays its children out with\n * an {@link HBox} row, left-to-right:\n *\n * 1. the {@link AccordionIndicator} chevron — **only** when `chevronSide` is `\"start\"`;\n * 2. a `chromeless` title {@link Button} (the section label, the clickable toggle\n * target and the focusable element), given a flex `weight` so it spans the\n * whole clickable region while its label stays left-anchored;\n * 3. the tool group container (an `HBox` row of per-section / re-parented tools);\n * 4. the chevron — **only** when `chevronSide` is `\"end\"` (the default), so the\n * chevron sits outermost and tools sit inboard of it.\n *\n * Because the tools are *siblings* of the title button rather than descendants,\n * a click on a tool is simply not a click on the title button — there is nothing\n * to stop from propagating and no marker to maintain.\n *\n * @category Components\n */\nclass AccordionHeader extends Component<AccordionHeaderOptions> {\n\n declare private _indicator: AccordionIndicator;\n declare private _title: Button;\n declare private _toolGroup: Component;\n private _tools: Component[] = [];\n private _chevronSide: AxisEnd = \"end\";\n\n /**\n * @param label - Text displayed in the header's title button.\n * @param options - Optional construction-time options.\n */\n constructor(label: string, options?: AccordionHeaderOptions) {\n super({ tag: \"div\", ...options });\n\n // The chevron, title and tool group are independent child Components in\n // an HBox row — one DOM element per class, no side-loaded overlay.\n this._indicator = new AccordionIndicator({ character: options?.chevronGlyph });\n this._title = new Button(label, { chromeless: true, anchor: AnchorType.WEST, glyph: options?.glyph });\n this._toolGroup = new Component();\n\n this._toolGroup.setLayoutManager(new HBox({ spacing: HEADER_CELL_SPACING, stretching: true }));\n this._toolGroup.setBackgroundColor(\"transparent\");\n\n // Stretching so each cell fills the header height and centres its own\n // content (the title button and tool buttons centre vertically; the\n // chevron centres via its own rule). Padding + cell gap are applied by\n // `setCompact` at the end of construction.\n this.setLayoutManager(new HBox({ spacing: HEADER_CELL_SPACING, stretching: true }));\n\n // Flex weight on the title makes the whole left region clickable while\n // the label stays left (anchor WEST); tools/chevron keep their preferred\n // width at the trailing edge.\n const titleConstraints = new LayoutConstraints();\n\n titleConstraints.weight = 1;\n\n this.addComponent(this._title, titleConstraints);\n this.addComponent(this._toolGroup);\n\n this._chevronSide = options?.chevronSide ?? \"end\";\n this.placeIndicator();\n\n this.setExpanded(options?.expanded ?? false);\n this.setCompact(options?.compact ?? false);\n }\n\n /**\n * Toggles compact padding: tighter horizontal insets and a smaller inter-cell\n * gap so the header reads denser. The header *height* is driven by the\n * owning [`Accordion`](/api/layout/classes/Accordion); this method only\n * affects the row's own padding.\n *\n * @param value - True for compact padding, false for the defaults.\n *\n * @returns This header, for method chaining.\n */\n setCompact(value: boolean): this {\n const left = value ? COMPACT_PADDING_LEFT : HEADER_PADDING_LEFT;\n const right = value ? COMPACT_PADDING_RIGHT : HEADER_PADDING_RIGHT;\n\n this.setInsets(new Insets(0, right, 0, left));\n (this.getLayoutManager() as HBox).setComponentSpacing(value ? COMPACT_CELL_SPACING : HEADER_CELL_SPACING);\n\n // Compact the title button too: its tighter insets lower the header's\n // content-min height so the compact header height can actually take\n // effect (otherwise the title's min floors the row taller).\n this._title?.setCompact(value);\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Inserts the chevron cell at the head (when `chevronSide` is `\"start\"`) or\n * the tail (when `\"end\"`) of the HBox row, removing it from its current\n * slot first so repeated calls re-position rather than duplicate.\n */\n private placeIndicator(): void {\n if (this.getComponents().includes(this._indicator)) {\n this.removeComponent(this._indicator);\n }\n\n if (this._chevronSide === \"start\") {\n this.insertComponent(this._indicator, 0);\n } else {\n this.addComponent(this._indicator);\n }\n }\n\n /**\n * Returns the title button — the clickable toggle target and the focusable\n * element. The owning [`Accordion`](/api/layout/classes/Accordion) wires its\n * `action` / `keydown` listeners and calls `focus()` on it.\n *\n * @returns The title button.\n */\n getTitleButton(): Button {\n return this._title;\n }\n\n /**\n * Toggles the expanded visual state: rotates the chevron and updates the\n * title button's `aria-expanded`.\n *\n * @param expanded - True to show the section as expanded.\n *\n * @returns This header, for method chaining.\n */\n setExpanded(expanded: boolean): this {\n this._indicator?.setExpanded(expanded);\n this._title?.getAria().setExpanded(expanded);\n\n return this;\n }\n\n /**\n * Returns whether the chevron is in the expanded state.\n *\n * @returns True if expanded.\n */\n isExpanded(): boolean {\n return this._indicator?.getExpanded() ?? false;\n }\n\n /**\n * Moves the chevron to the given end of the header and re-runs layout.\n *\n * @param side - `\"start\"` or `\"end\"`.\n *\n * @returns This header, for method chaining.\n */\n setChevronSide(side: AxisEnd): this {\n if (this._chevronSide === side) {\n return this;\n }\n\n this._chevronSide = side;\n this.placeIndicator();\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns which end the chevron currently sits at.\n *\n * @returns `\"start\"` or `\"end\"`.\n */\n getChevronSide(): AxisEnd {\n return this._chevronSide;\n }\n\n /**\n * Sets the character drawn as the chevron.\n *\n * @param glyph - The chevron character.\n *\n * @returns This header, for method chaining.\n */\n setChevronGlyph(glyph: string): this {\n this._indicator?.setCharacter(glyph);\n\n return this;\n }\n\n /**\n * Adds a tool to this header's tool group (the cell between the title and the\n * trailing chevron). Used both for per-section tools and for the manager's\n * re-parented global tool. A no-op if the tool is already present.\n *\n * A {@link Button} tool is forced into `flat` appearance so header tools read\n * as flat icons regardless of how the caller configured them. Re-applied on\n * every add because `setFlat` is idempotent and a re-parented global tool\n * flows back through here on each header it lands on.\n *\n * @param tool - The tool component to add.\n *\n * @returns This header, for method chaining.\n */\n addTool(tool: Component): this {\n if (tool instanceof Button) {\n tool.setFlat(true);\n }\n\n if (this._tools.includes(tool)) {\n return this;\n }\n\n this._tools.push(tool);\n this._toolGroup.addComponent(tool);\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Removes a previously-added tool from this header's tool group. A no-op if\n * the tool is not present.\n *\n * @param tool - The tool component to remove.\n *\n * @returns This header, for method chaining.\n */\n removeTool(tool: Component): this {\n const index = this._tools.indexOf(tool);\n\n if (index === -1) {\n return this;\n }\n\n this._tools.splice(index, 1);\n this._toolGroup.removeComponent(tool);\n this.scheduleLayout();\n\n return this;\n }\n\n /**\n * Shows or hides the tool group as a whole. The manager calls this to\n * implement hover-reveal: hidden at rest, shown while the header is hovered.\n * Uses `display` (not `visibility`) so a hidden group reserves no width and\n * the flex-weighted title fills the row.\n *\n * @param revealed - True to show the tool group, false to hide it.\n *\n * @returns This header, for method chaining.\n */\n setToolsRevealed(revealed: boolean): this {\n this._toolGroup.setDisplayed(revealed);\n\n return this;\n }\n\n /**\n * Overrides the chevron's `transform` transition timing so it matches the\n * duration and easing of the owning Accordion's panel-height transition.\n * Called by [`Accordion`](/api/layout/classes/Accordion) from\n * `createSection`; not exposed via {@link AccordionHeaderOptions} since the\n * timing is a wiring detail, not a configuration knob.\n *\n * @param durationMs - Transition duration in milliseconds.\n * @param easing - CSS easing function string.\n *\n * @returns This header, for method chaining.\n */\n setAnimationTiming(durationMs: number, easing: string): this {\n this._indicator?.setAnimationTiming(durationMs, easing);\n\n return this;\n }\n}\n\nconst AccordionHeaderCallable = callable(AccordionHeader);\ntype AccordionHeaderCallable = AccordionHeader;\nexport {\n AccordionHeader as _AccordionHeader,\n AccordionHeaderCallable as AccordionHeader\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutManager, LayoutManagerOptions } from \"~/layout/LayoutManager.js\";\nimport { AccordionConstraints } from \"~/layout/AccordionConstraints.js\";\nimport { AccordionHeader } from \"~/component/container/AccordionHeader.js\";\nimport { SplitGutter } from \"~/component/container/SplitGutter.js\";\nimport { Animation } from \"~/core/Animation.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { LayoutSize, LayoutSizeUnit, toLayoutSizes, fromLayoutSizes, isRestorableSizes } from \"~/layout/LayoutSizes.js\";\nimport { Size, UNBOUNDED } from \"~/primitive/Size.js\";\nimport type { AxisEnd } from \"~/primitive/Axis.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { chainRoom, distributeDragChain } from \"~/core/DragChain.js\";\n\n/**\n * String-literal union of the events emitted by {@link Accordion}.\n *\n * @category Layouts\n */\nexport type AccordionEvent = \"sectiontoggle\" | \"sectionresize\";\n\n/**\n * Symmetric easing curve, shared between the panel wrapper height transition,\n * the headers'/wrappers' `top` transitions, and the indicator transform\n * transition so the open and close animations are exact time-reverses of each\n * other. Encoded as a module-private constant — motion personality belongs to\n * the layout, not the theme.\n *\n * The Material \"standard\" curve `cubic-bezier(0.4, 0, 0.2, 1)` was rejected\n * because it's asymmetric — `easing(0.5) ≈ 0.77`, so a close shrinks ~77% of\n * the way in the first half of the duration and crawls through the final 23%\n * for the second half. The visual weight lands at large sizes first, which\n * reads as \"content vanished, then nothing happened.\" A symmetric curve has\n * `easing(t) + easing(1 - t) = 1`, so the close mirrors the open frame-for-frame.\n *\n * `scaleY` was rejected as the animated property: the wrapper participates\n * in document flow and siblings need to reflow as it grows. `height` with\n * `contain: layout paint` scopes the reflow cost and produces the correct\n * layout-tracking motion.\n */\nconst ACCORDION_EASING: string = \"cubic-bezier(0.4, 0, 0.6, 1)\";\n\n/**\n * Header height in pixels used by {@link Accordion} compact mode. Tighter than\n * the default 28px so a stack of sections reads denser; applied only when the\n * consumer has not pinned an explicit header height via `setHeaderHeight`.\n */\nconst COMPACT_HEADER_HEIGHT: number = 22;\n\n/**\n * Thickness (px) of a resizable-mode drag gutter. It overlays the bottom edge\n * of the upper open section's content and reserves NO layout budget, so the\n * open-content sizing math (`computeShrinkRatio`/`computeFill`/\n * `computeResizableHeights`) is unaffected by whether gutters are shown.\n */\nconst RESIZE_GUTTER_SIZE: number = 6;\n\n/**\n * Themed-mode CSS values for the accordion theme tokens, with fallbacks\n * mirroring the default light theme. Applied to each header only when `themed`\n * is on, so an un-themed accordion stays chromeless. The header border is a\n * single bottom divider (not a four-side box): stacked headers then read as a\n * flat list whose dividers never double, so no separate `flat`/collapse option\n * is needed — the look is driven entirely by the `accordion.header.border` token.\n */\nconst THEMED_HEADER_BG: string = \"var(--ts-ui-accordion-header-bg, rgb(243,244,246))\";\nconst THEMED_HEADER_BORDER: string = \"var(--ts-ui-accordion-header-border, 1px solid rgb(214,217,222))\";\nconst THEMED_HEADER_COLOR: string = \"var(--ts-ui-accordion-header-color, inherit)\";\n\n/**\n * All-around border drawn on the accordion's own container when `themed`, from\n * the `accordion.border` token — boxes the whole stack while the header borders\n * remain single bottom dividers between sections. The token carries the full CSS\n * border shorthand (width, style, colour), so a theme controls all three; the\n * fallback applies only when the variable is undefined.\n */\nconst THEMED_BORDER: string = \"var(--ts-ui-accordion-border, 1px solid rgb(214,217,222))\";\n\n/**\n * Callback invoked when a section is opened or closed.\n *\n * @param index - Zero-based index of the toggled section.\n * @param open - True if the section is now open.\n *\n * @category Layouts\n */\nexport type SectionToggleCallback = (index: number, open: boolean) => void;\n\n/**\n * Callback invoked once a completed resizable-gutter drag settles a\n * section's sizes.\n *\n * @param sizes - The open sections' sizes after the drag, in child order —\n * the same array {@link Accordion.getSectionSizes} would return.\n *\n * @category Layouts\n */\nexport type SectionResizeCallback = (sizes: LayoutSize[]) => void;\n\n/**\n * Construction-time options for {@link Accordion}.\n *\n * @category Layouts\n */\nexport interface AccordionOptions extends LayoutManagerOptions {\n singleOpen?: boolean;\n headerHeight?: number;\n animationDuration?: number;\n chevronSide?: AxisEnd;\n toolsVisibility?: \"always\" | \"hover\";\n compact?: boolean;\n chevronGlyph?: string;\n spacing?: number;\n themed?: boolean;\n fillHeight?: boolean;\n /**\n * Opts into draggable gutters between adjacent open sections, letting the\n * user trade height between them. See {@link Accordion.setResizable}.\n */\n resizable?: boolean;\n /**\n * Section sizes to restore on the first resizable layout; discarded whole\n * when stale. Only meaningful with `resizable`.\n */\n sectionSizes?: LayoutSize[];\n /**\n * Multi-event listener bag dispatched to {@link Accordion.on} at\n * construction time.\n */\n listeners?: {\n sectiontoggle?: SectionToggleCallback;\n sectionresize?: SectionResizeCallback;\n };\n}\n\n/**\n * A layout manager that stacks vertically collapsible sections, each with a\n * clickable header and an animated content panel.\n *\n * Sections are defined by child components added to the container via\n * `container.addComponent(content, new AccordionConstraints(...))`. The layout\n * manager owns internally-created {@link AccordionHeader} components and panel\n * wrapper elements; these are not visible through `container.getComponents()`.\n *\n * Animation uses a CSS `height` transition with `overflow: hidden` on the panel\n * wrapper so closing/opening animates smoothly without affecting the layout model.\n *\n * @example\n * ```typescript\n * const accordion = new Accordion();\n * accordion.setSingleOpen(true);\n *\n * const panel = new Component();\n * panel.setLayoutManager(new Accordion());\n *\n * const content1 = new Component();\n * panel.addComponent(content1, new AccordionConstraints('Section 1', true));\n *\n * const content2 = new Component();\n * panel.addComponent(content2, new AccordionConstraints('Section 2'));\n *\n * panel.setLayoutManager(accordion);\n * ```\n *\n * @category Layouts\n */\nclass Accordion extends LayoutManager {\n\n private _headers: AccordionHeader[] = [];\n private _panelWrappers: Component[] = [];\n private _openState: boolean[] = [];\n private _singleOpen: boolean = false;\n private _headerHeight: number = 28;\n private _headerHeightExplicit: boolean = false;\n private _compact: boolean = false;\n private _animationDuration: number = 200;\n private _chevronSide: AxisEnd = \"end\";\n private _chevronGlyph: string | null = null;\n private _spacing: number = 0;\n private _themed: boolean = true;\n private _fillHeight: boolean = false;\n private _tools: Component[] = [];\n private _toolsVisibility: \"always\" | \"hover\" = \"hover\";\n private _hoveredHeader: number = -1;\n private _listeners: ListenerBag<AccordionEvent> = new ListenerBag<AccordionEvent>();\n private _resizable: boolean = false;\n // User-dragged (or fill-seeded) content heights per open section, absolute\n // px summing to the open budget when written. Keyed by Component\n // (reorder-safe, like Split._sizes); pruned each resizable layout for\n // removed components. A closed section keeps its entry frozen for reopen.\n private _resizeSizes: Map<Component, number> = new Map<Component, number>();\n // Sizes to restore on the first resizable layout that can resolve an open\n // budget, taken from the `sectionSizes` option (or a direct\n // `applySectionSizes` call before one is resolvable). Drained once,\n // mirroring `Split._pendingSizes`.\n private _pendingSectionSizes: LayoutSize[] | null = null;\n // The `openBudget / storedTotal` scale factor computeResizableHeights last\n // applied — i.e. `rendered height == _resizeSizes value * _resizeFactor`.\n // Only 1 when the open set's stored sizes already sum to the budget (the\n // common weight/fillHeight-seeded case); otherwise onGutterDrag needs\n // it to convert its rendered-pixel drag math back to _resizeSizes' stored\n // scale — writing rendered values directly would silently rescale the\n // whole open set (including untouched sections) on the next layout.\n // Applies to the weighted sections only; a resize-pinned section renders\n // at scale 1 (see `_resizePinned`).\n private _resizeFactor: number = 1;\n // The open sections `distributeWithinConstraints` last held at their\n // stored px (scale 1) instead of scaling by `_resizeFactor`. Read by\n // `onGutterDrag`, which must divide each dragged section's rendered\n // height by its own stored scale — `_resizeFactor` is no longer one\n // global scalar once a pin is held.\n private _resizePinned: Set<Component> = new Set<Component>();\n // Gutter pool, reused across layouts; one shown per adjacent open-section pair.\n private _resizeGutters: SplitGutter[] = [];\n // Rebuilt each layout: for gutter i, the two content components it resizes.\n private _gutterPairs: Array<{ upper: Component; lower: Component }> = [];\n // Drag state captured on gutter dragstart. The dragged pair (for the\n // reentrancy guard); the pointer coordinate at the previous move (the drag\n // is applied incrementally, frame by frame, so a reversed drag responds\n // closest-section-first); and — so the drag can chain across sections at\n // their min/max — every open section's index plus the dragged gutter's\n // position in that open list.\n private _dragUpper: Component | null = null;\n private _dragLower: Component | null = null;\n private _dragLastPointer: number = 0;\n private _dragOpenIndices: number[] = [];\n private _dragGutterUpperPos: number = 0;\n // Open/close toggle animations currently in flight. Transitions are off by\n // default (so resize and drag relayouts snap); a toggle enables them and the\n // global disable waits until this returns to zero — single-open mode primes\n // several sections at once, and the first to finish must not snap the rest.\n private _toggleAnimations: number = 0;\n\n // In-flight height transitions, keyed by section index and cancelled on\n // detach so their fallback timers cannot fire against released wrapper\n // element handles. Two channels, because a section can be mid-shrink from a\n // reflow while a toggle animation is separately in flight on the same index.\n private _shrinkAnimations: Map<number, Animation.CancelHandle> = new Map();\n private _wrapperAnimations: Map<number, Animation.CancelHandle> = new Map();\n // Stable bound reference so add/removeViewportListener target the same\n // callback; Accordion is a LayoutManager, not a Component, so it cannot\n // key the registration on `this` the way every Component call site does —\n // see the Potential Challenges drift note in the resizable-sections plan.\n // A concise-arrow body returns onGutterDragEnd's disposition, so the\n // wrapper forwards the consume without needing to carry the event.\n private _boundOnGutterDragEnd: () => Event.ListenerResult = () => this.onGutterDragEnd();\n\n constructor(options?: AccordionOptions) {\n // LayoutManager's constructor takes no options; applied via applyOptions below.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n if (options) {\n this.applyOptions(options);\n }\n }\n\n /**\n * Applies an {@link AccordionOptions} bag, dispatching single-open mode,\n * header height, animation duration, and the toggle callback after the\n * inherited LayoutManager defaults.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: AccordionOptions): void {\n super.applyOptions(options);\n\n if (options.headerHeight !== undefined) {\n this.setHeaderHeight(options.headerHeight);\n }\n\n if (options.animationDuration !== undefined) {\n this.setAnimationDuration(options.animationDuration);\n }\n\n if (options.chevronSide !== undefined) {\n this.setChevronSide(options.chevronSide);\n }\n\n if (options.toolsVisibility !== undefined) {\n this.setToolsVisibility(options.toolsVisibility);\n }\n\n if (options.compact !== undefined) {\n this.setCompact(options.compact);\n }\n\n if (options.chevronGlyph !== undefined) {\n this.setChevronGlyph(options.chevronGlyph);\n }\n\n if (options.spacing !== undefined) {\n this.setSpacing(options.spacing);\n }\n\n if (options.themed !== undefined) {\n this.setThemed(options.themed);\n }\n\n if (options.fillHeight !== undefined) {\n this.setFillHeight(options.fillHeight);\n }\n\n if (options.resizable !== undefined) {\n this.setResizable(options.resizable);\n }\n\n if (options.sectionSizes !== undefined) {\n this._pendingSectionSizes = options.sectionSizes.map(size => ({ ...size }));\n }\n\n if (options.listeners !== undefined) {\n const listeners = options.listeners;\n\n for (const event of Object.keys(listeners) as Array<keyof typeof listeners>) {\n const listener = listeners[event];\n\n // A union of two events' callback types no longer narrows to a\n // single `on` overload; the cast mirrors `Component.applyListeners`'\n // own `(this as any).on` — sound for the same reason: `event` and\n // `listener` are still a matched pair from the same options key.\n if (listener !== undefined) {\n (this as any).on(event, listener);\n }\n }\n }\n\n if (options.singleOpen !== undefined) {\n this.setSingleOpen(options.singleOpen);\n }\n }\n\n /**\n * Returns whether at most one section can be open at a time.\n *\n * @returns True if single-open mode is active.\n */\n isSingleOpen(): boolean {\n return this._singleOpen;\n }\n\n /**\n * Sets whether at most one section can be open at a time.\n * When switching to `true`, immediately closes all but the first open section.\n *\n * @param value - True to enforce single-open mode.\n */\n setSingleOpen(value: boolean): this {\n this._singleOpen = value;\n\n if (!value) {\n return this;\n }\n\n let foundOpen = false;\n\n for (let i = 0; i < this._openState.length; i++) {\n if (!this._openState[i]) {\n continue;\n }\n\n if (foundOpen) {\n this.primeWrapper(i);\n this._openState[i] = false;\n this._headers[i].setExpanded(false);\n this.emit(\"sectiontoggle\", i, false);\n } else {\n foundOpen = true;\n }\n }\n\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Returns the height in pixels of each section header.\n *\n * @returns The header height.\n */\n getHeaderHeight(): number {\n return this._headerHeight;\n }\n\n /**\n * Sets the height of each section header in pixels. Marks the height as\n * explicitly set, so {@link setCompact} no longer overrides it with the\n * compact default.\n *\n * @param height - Height in pixels.\n */\n setHeaderHeight(height: number): this {\n this._headerHeight = height;\n this._headerHeightExplicit = true;\n\n return this;\n }\n\n /**\n * Returns the header height actually used for layout: the compact default\n * when {@link isCompact} is on and no explicit height was set, otherwise the\n * configured {@link getHeaderHeight}.\n *\n * @returns The effective header height in pixels.\n */\n private effectiveHeaderHeight(): number {\n return this._compact && !this._headerHeightExplicit\n ? COMPACT_HEADER_HEIGHT\n : this._headerHeight;\n }\n\n /**\n * Returns whether compact (denser) mode is active.\n *\n * @returns True if compact mode is on.\n */\n isCompact(): boolean {\n return this._compact;\n }\n\n /**\n * Sets compact (denser) mode: a smaller default header height (unless an\n * explicit height was set) plus tighter header padding and a smaller\n * chevron. Applies to existing headers immediately.\n *\n * @param value - True to enable compact mode.\n *\n * @returns This layout manager, for chaining.\n */\n setCompact(value: boolean): this {\n this._compact = value;\n\n for (const header of this._headers) {\n header.setCompact(value);\n }\n\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Returns the CSS transition duration for open/close animation in milliseconds.\n *\n * @returns Duration in milliseconds.\n */\n getAnimationDuration(): number {\n return this._animationDuration;\n }\n\n /**\n * Sets the CSS transition duration for open/close animation.\n *\n * @param ms - Duration in milliseconds.\n */\n setAnimationDuration(ms: number): this {\n this._animationDuration = ms;\n\n return this;\n }\n\n /**\n * Returns which end of each header the chevron sits at.\n *\n * @returns `\"start\"` or `\"end\"`.\n */\n getChevronSide(): AxisEnd {\n return this._chevronSide;\n }\n\n /**\n * Sets which end of each header the chevron sits at. The section label\n * always stays left-aligned; only the chevron moves. Applies to existing\n * headers immediately and to headers created afterwards.\n *\n * @param side - `\"start\"` or `\"end\"`.\n *\n * @returns This layout manager, for chaining.\n */\n setChevronSide(side: AxisEnd): this {\n this._chevronSide = side;\n\n for (const header of this._headers) {\n header.setChevronSide(side);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns the chevron character, or `null` when the default is used.\n *\n * @returns The configured chevron character, or `null`.\n */\n getChevronGlyph(): string | null {\n return this._chevronGlyph;\n }\n\n /**\n * Sets the character drawn as each header's expand/collapse chevron, in\n * place of the default `\"▶\"`. The character rotates 90° when a section\n * expands, so a single rotatable glyph covers both states. Applies to\n * existing headers immediately.\n *\n * @param glyph - The chevron character.\n *\n * @returns This layout manager, for chaining.\n */\n setChevronGlyph(glyph: string): this {\n this._chevronGlyph = glyph;\n\n for (const header of this._headers) {\n header.setChevronGlyph(glyph);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns the vertical gap inserted between sections.\n *\n * @returns The inter-section spacing in pixels.\n */\n getSpacing(): number {\n return this._spacing;\n }\n\n /**\n * Sets the vertical gap inserted between sections (never before the first or\n * after the last). Default `0` keeps the sections contiguous.\n *\n * @param spacing - Inter-section gap in pixels.\n *\n * @returns This layout manager, for chaining.\n */\n setSpacing(spacing: number): this {\n this._spacing = spacing;\n\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Returns whether fill mode is on.\n *\n * @returns True if open sections grow to absorb the container's leftover height.\n */\n isFillHeight(): boolean {\n return this._fillHeight;\n }\n\n /**\n * Sets fill mode. When on, every open section grows to absorb the\n * container's leftover height, sharing it in proportion to their\n * `weight` (unweighted sections count equally, so equal weights get\n * equal slices) and each capped at its own max — instead of every open\n * section sitting at its preferred height. Only meaningful when the host\n * stretches the accordion beyond its preferred height. No effect when the\n * content already overflows.\n *\n * @param value - True to enable fill.\n *\n * @returns This layout manager, for chaining.\n */\n setFillHeight(value: boolean): this {\n this._fillHeight = value;\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns whether resizable mode is on.\n *\n * @returns True if draggable gutters appear between adjacent open sections.\n */\n isResizable(): boolean {\n return this._resizable;\n }\n\n /**\n * Sets resizable mode. When on, a draggable gutter appears between every\n * adjacent pair of open sections, letting the user trade height between\n * them; the split starts from the section's usual `weight`/`fillHeight`\n * distribution and is overridden by the drag from then on. No gutter\n * appears with fewer than two open sections, or under {@link setSingleOpen}\n * (which never has more than one section open).\n *\n * @param value - True to enable resizable mode.\n *\n * @returns This layout manager, for chaining.\n */\n setResizable(value: boolean): this {\n this._resizable = value;\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Returns whether themed mode is on.\n *\n * @returns True if headers/panels paint the accordion theme tokens.\n */\n isThemed(): boolean {\n return this._themed;\n }\n\n /**\n * Sets themed mode. When on, each header paints the accordion theme tokens\n * (background, bottom-divider border, text colour) and the container draws\n * an all-around border; when off, headers stay chromeless and the container\n * border is cleared. Applies to existing sections immediately.\n *\n * @param value - True to paint the accordion theme tokens.\n *\n * @returns This layout manager, for chaining.\n */\n setThemed(value: boolean): this {\n this._themed = value;\n\n this.applyContainerTheming();\n\n for (let i = 0; i < this._headers.length; i++) {\n this.applySectionTheming(i);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Applies (or clears) the all-around border on the accordion's container per\n * the themed state. Idempotent — `setBorder`/`clearBorder` cache, so the\n * repeated call from `doLayout` is cheap.\n */\n private applyContainerTheming(): void {\n const container = this.getContainer();\n\n if (!container) {\n return;\n }\n\n if (this._themed) {\n container.setBorder({ border: THEMED_BORDER });\n } else {\n container.clearBorder();\n }\n }\n\n /**\n * Applies (or clears) the themed background/border/text styling for the\n * header at `index`. The themed border is a single bottom divider, so\n * stacked headers read as a flat list whose dividers never double — there is\n * no separate flat/collapse mode. Idempotent — the underlying setters cache,\n * so re-applying the same state is cheap.\n *\n * @param index - Zero-based section index.\n */\n private applySectionTheming(index: number): void {\n const header = this._headers[index];\n\n if (!header) {\n return;\n }\n\n if (!this._themed) {\n header.clearBackground();\n header.clearForegroundColor();\n header.clearBorder();\n\n return;\n }\n\n // `background` (not `background-color`) so a gradient token value paints;\n // the token may also be a flat colour, which the shorthand handles too.\n header.setBackground(THEMED_HEADER_BG);\n header.setForegroundColor(THEMED_HEADER_COLOR);\n // Bottom-only divider — collapses naturally with no per-index logic.\n header.setBorder({ border: \"none\", borderBottom: THEMED_HEADER_BORDER });\n }\n\n /**\n * Returns when per-section tools are shown.\n *\n * @returns `\"always\"` or `\"hover\"`.\n */\n getToolsVisibility(): \"always\" | \"hover\" {\n return this._toolsVisibility;\n }\n\n /**\n * Sets when per-section tools are shown. `\"hover\"` (the default) reveals a\n * header's tools only while it is hovered; `\"always\"` keeps them visible.\n * Global tools (see {@link addTool}) always follow hover regardless of this\n * setting, since a single global instance can only sit in the hovered header.\n *\n * @param mode - `\"always\"` or `\"hover\"`.\n *\n * @returns This layout manager, for chaining.\n */\n setToolsVisibility(mode: \"always\" | \"hover\"): this {\n this._toolsVisibility = mode;\n\n // Re-apply the resting state to every header; a subsequent hover\n // corrects the currently-hovered one.\n for (const header of this._headers) {\n header.setToolsRevealed(mode === \"always\");\n }\n\n return this;\n }\n\n /**\n * Registers a global tool shown on whichever header is currently hovered.\n * A single `Component` is one DOM node, so a global tool cannot appear on\n * every header at once — it is re-parented into the hovered header and\n * therefore follows hover/active visibility. Mirrors `Tab.addTool`.\n *\n * @param button - The tool component to register globally.\n *\n * @returns This layout manager, for chaining.\n */\n addTool(button: Component): this {\n if (this._tools.includes(button)) {\n return this;\n }\n\n this._tools.push(button);\n\n // If a header is hovered right now, show the new tool there immediately.\n if (this._hoveredHeader !== -1) {\n this._headers[this._hoveredHeader]?.addTool(button);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Removes a previously-registered global tool.\n *\n * @param button - The tool component to remove.\n *\n * @returns This layout manager, for chaining.\n */\n removeTool(button: Component): this {\n const index = this._tools.indexOf(button);\n\n if (index === -1) {\n return this;\n }\n\n this._tools.splice(index, 1);\n\n if (this._hoveredHeader !== -1) {\n this._headers[this._hoveredHeader]?.removeTool(button);\n }\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Adds the global tools to the header at `index` and, in hover mode, reveals\n * its tool group. Called when the pointer enters a header.\n *\n * @param index - The header being entered.\n */\n private revealHeaderTools(index: number): void {\n const header = this._headers[index];\n\n if (!header) {\n return;\n }\n\n for (const tool of this._tools) {\n header.addTool(tool);\n }\n\n if (this._toolsVisibility === \"hover\") {\n header.setToolsRevealed(true);\n }\n }\n\n /**\n * Removes the global tools from the header at `index` and, in hover mode,\n * hides its tool group again. Called when the pointer leaves a header.\n *\n * @param index - The header being left.\n */\n private hideHeaderTools(index: number): void {\n const header = this._headers[index];\n\n if (!header) {\n return;\n }\n\n for (const tool of this._tools) {\n header.removeTool(tool);\n }\n\n if (this._toolsVisibility === \"hover\") {\n header.setToolsRevealed(false);\n }\n }\n\n /**\n * Handles the pointer entering a header (filtered to genuine enters, not\n * moves between the header's own descendants): relocates the global tools\n * onto it and reveals its tools, moving them off the previously hovered\n * header first.\n *\n * @param index - The header receiving the pointer.\n * @param e - The originating `mouseover` event.\n */\n private onHeaderHoverEnter(index: number, e: MouseEvent): void {\n const element = this._headers[index]?.getElement();\n\n if (element && DOM.source.isNode(e.relatedTarget) && DOM.source.contains(element, DOM.source.intern(e.relatedTarget))) {\n return;\n }\n\n if (this._hoveredHeader === index) {\n return;\n }\n\n if (this._hoveredHeader !== -1) {\n this.hideHeaderTools(this._hoveredHeader);\n }\n\n this._hoveredHeader = index;\n this.revealHeaderTools(index);\n }\n\n /**\n * Handles the pointer leaving a header (filtered to genuine exits): removes\n * the global tools and hides the tools again.\n *\n * @param index - The header losing the pointer.\n * @param e - The originating `mouseout` event.\n */\n private onHeaderHoverLeave(index: number, e: MouseEvent): void {\n const element = this._headers[index]?.getElement();\n\n if (element && DOM.source.isNode(e.relatedTarget) && DOM.source.contains(element, DOM.source.intern(e.relatedTarget))) {\n return;\n }\n\n if (this._hoveredHeader !== index) {\n return;\n }\n\n this.hideHeaderTools(index);\n this._hoveredHeader = -1;\n }\n\n /**\n * Re-lays-out the host after a change to the accordion's own intrinsic\n * height — an open/close, or a header-metric change (compact, spacing,\n * single-open). `scheduleLayout` alone only re-fits the sections inside the\n * accordion's unchanged bounds; `notifyIntrinsicSizeChanged` additionally\n * tells the ancestors — notably a scrolling host — that the accordion's\n * preferred/min height moved, so they re-lay-out and refresh their\n * scrollbars instead of going stale until the next viewport resize.\n */\n private relayoutHost(): void {\n const container = this.getContainer();\n\n container?.scheduleLayout();\n container?.notifyIntrinsicSizeChanged();\n }\n\n /**\n * Opens the section at the given index.\n *\n * @param index - Zero-based section index.\n */\n openSection(index: number): this {\n if (index < 0 || index >= this._openState.length) {\n return this;\n }\n\n if (this._singleOpen) {\n for (let i = 0; i < this._openState.length; i++) {\n if (i !== index && this._openState[i]) {\n this.primeWrapper(i);\n this._openState[i] = false;\n this._headers[i].setExpanded(false);\n this.emit(\"sectiontoggle\", i, false);\n }\n }\n }\n\n this.primeWrapper(index);\n this._openState[index] = true;\n this._headers[index].setExpanded(true);\n this.emit(\"sectiontoggle\", index, true);\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Closes the section at the given index.\n *\n * @param index - Zero-based section index.\n */\n closeSection(index: number): this {\n if (index < 0 || index >= this._openState.length) {\n return this;\n }\n\n this.primeWrapper(index);\n this._openState[index] = false;\n this._headers[index].setExpanded(false);\n this.emit(\"sectiontoggle\", index, false);\n this.relayoutHost();\n\n return this;\n }\n\n /**\n * Opens every section. In single-open mode only one section may be open at\n * a time, so this opens the first section (a deterministic choice — the\n * topmost becomes the visible one) rather than leaving whichever section\n * `openSection` happened to settle on last.\n *\n * @returns This layout manager, for chaining.\n */\n expandAll(): this {\n if (this._singleOpen) {\n this.openSection(0);\n\n return this;\n }\n\n for (let i = 0; i < this._openState.length; i++) {\n this.openSection(i);\n }\n\n return this;\n }\n\n /**\n * Closes every section.\n *\n * @returns This layout manager, for chaining.\n */\n collapseAll(): this {\n for (let i = 0; i < this._openState.length; i++) {\n this.closeSection(i);\n }\n\n return this;\n }\n\n /**\n * Returns whether the section at the given index is currently open.\n *\n * @param index - Zero-based section index.\n * @returns True if the section is open.\n */\n isSectionOpen(index: number): boolean {\n return this._openState[index] ?? false;\n }\n\n /**\n * Resolves each section's persisted unit: `\"px\"` for a resize-pinned\n * section (effective weight `0`, per {@link effectiveWeight}), `\"ratio\"`\n * otherwise. `effectiveWeight` is `accordion-resize-weight`'s definition\n * of a resize-pinned section — `weight` unset or `0`, with `fillHeight`\n * off — so this is verbatim the predicate the layout itself pins by.\n *\n * @param components - The container's current content components, in\n * child order.\n * @returns One unit per section, in the same order.\n */\n private sectionSizeUnits(components: Component[]): LayoutSizeUnit[] {\n return components.map(component => (this.effectiveWeight(component) === 0 ? \"px\" : \"ratio\"));\n }\n\n /**\n * Returns the sections' content sizes in child order, one entry per\n * section (open or closed), for cross-session persistence: a\n * resize-pinned section (effective weight `0`) reports `px`, every other\n * section reports its `ratio` of the space the px sections leave.\n *\n * @remarks Reads `_resizeSizes` raw — never `getHeight()`, never\n * `× _resizeFactor`. A pinned section's stored value is already its px\n * (the pin block holds it at scale 1); `_resizeFactor` is one scalar\n * across the *free* set, so it cancels within that subset and a whole-set\n * read would need it, not a raw one; and a rendered height can be a\n * transient min/max clamp `distributeWithinConstraints` applied for this\n * pass only, which the stored value deliberately never carries forward.\n *\n * @returns One {@link LayoutSize} per section in child order; the\n * pending `sectionSizes` when one is still undrained; `[]` when\n * detached or the container has no sections.\n */\n getSectionSizes(): LayoutSize[] {\n const container = this.getContainer();\n\n if (!container) {\n return [];\n }\n\n const components = container.getComponents();\n\n if (components.length === 0) {\n return [];\n }\n\n const units = this.sectionSizeUnits(components);\n\n if (this._pendingSectionSizes !== null && isRestorableSizes(this._pendingSectionSizes, units)) {\n return this._pendingSectionSizes.map(size => ({ ...size }));\n }\n\n return toLayoutSizes(units, components.map(component => this._resizeSizes.get(component) ?? 0));\n }\n\n /**\n * Restores sizes captured by {@link getSectionSizes}, applied on the next\n * resizable layout that can resolve the open budget (the correct base\n * for a ratio entry). Discarded whole unless every entry's unit matches\n * the live section's weight (see the discard rule on {@link LayoutSize}).\n *\n * @param sizes - The persisted array to restore.\n * @returns This layout manager, for method chaining.\n */\n applySectionSizes(sizes: LayoutSize[]): this {\n // Deferred, not immediate: the correct base is `openBudget`, which only\n // `computeResizableHeights` can resolve. The option and this setter share\n // one drain, one base, and one discard rule.\n this._pendingSectionSizes = sizes.map(size => ({ ...size }));\n\n this.getContainer()?.scheduleLayout();\n\n return this;\n }\n\n /**\n * Registers a listener for one of this accordion's events.\n *\n * @param event - `\"sectiontoggle\"` fires whenever a section is opened or\n * closed, receiving the zero-based section index and whether it is\n * now open; `\"sectionresize\"` fires once a completed resizable-gutter\n * drag settles a section's sizes, receiving the open sections' sizes\n * in child order.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This accordion, for method chaining.\n */\n on(event: \"sectiontoggle\", listener: SectionToggleCallback): this;\n on(event: \"sectionresize\", listener: SectionResizeCallback): this;\n on(event: AccordionEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This accordion, for method chaining.\n */\n off(event: AccordionEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"sectiontoggle\", index: number, open: boolean): void;\n protected emit(event: \"sectionresize\", sizes: LayoutSize[]): void;\n protected emit(event: AccordionEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Attaches to a container. Section headers and panel wrappers are created\n * lazily in {@link doLayout} on first use.\n *\n * @param container - The container component to attach to.\n */\n attach(container: Component): this {\n super.attach(container);\n\n return this;\n }\n\n /**\n * Detaches from the container, moving each section's content element back\n * to the container and disposing every header and panel wrapper — they\n * are raw-appended to the container's element rather than registered as\n * children, so nothing else reaches their teardown.\n */\n detach(): this {\n const container = this.getContainer();\n\n for (const animation of this._shrinkAnimations.values()) {\n animation.cancel();\n }\n this._shrinkAnimations.clear();\n\n for (const animation of this._wrapperAnimations.values()) {\n animation.cancel();\n }\n this._wrapperAnimations.clear();\n\n // Those cancelled animations owned the toggle-cleanup branch, so run its\n // work here: without this a detach mid-toggle leaves the counter above\n // zero and the animated transitions installed, and a re-attached manager\n // would never reach the cleanup branch again.\n if (this._toggleAnimations > 0) {\n this._toggleAnimations = 0;\n this.setSectionTransitions(false);\n container?.setTransition(null);\n }\n\n // A detach mid-drag would otherwise leak the viewport listeners\n // registered in onGutterDragStart and strand the drag pair. `_dragUpper`\n // is non-null only while a drag is live (set alongside those listeners),\n // so end the drag first — and only then, so we never touch the viewport\n // map when no accordion drag is active.\n if (this._dragUpper) {\n this.onGutterDragEnd();\n }\n\n for (let i = 0; i < this._headers.length; i++) {\n const components = container ? container.getComponents() : [];\n const component = components[i];\n\n if (component && container) {\n DOM.sink.appendChild(container.getElement()!, component.getElement()!);\n }\n\n // Tools belong to the caller, not to this manager, and reach a\n // header from two places: `addTool` registers a global tool on\n // every header it is revealed on, and `createSection` registers\n // each `AccordionConstraints.tools` entry on its own header. Both\n // become children of the header's tool group, so both must be\n // released before the header is disposed — otherwise its dispose\n // recursion destroys a component the caller still holds and may\n // re-add elsewhere. This is the same protection the content\n // children get by being reparented above.\n const sectionTools = component\n ? ((this.getLayoutConstraints(component) as AccordionConstraints | undefined)?.tools ?? [])\n : [];\n\n for (const tool of [...this._tools, ...sectionTools]) {\n this._headers[i].removeTool(tool);\n }\n\n this._headers[i].dispose();\n this._panelWrappers[i].dispose();\n }\n\n this._headers = [];\n this._panelWrappers = [];\n this._openState = [];\n\n for (const gutter of this._resizeGutters) {\n DOM.sink.removeElement(gutter.getElement()!);\n gutter.dispose();\n }\n\n this._resizeGutters = [];\n this._resizeSizes.clear();\n this._gutterPairs = [];\n\n super.detach();\n\n return this;\n }\n\n /**\n * Returns the preferred size: sum of all header heights plus the preferred\n * heights of all open sections.\n *\n * @returns The preferred size, or null if not attached.\n */\n getPreferredSize(): Size | null {\n const container = this.getContainer();\n\n if (!container) {\n return null;\n }\n\n const perimeterSize = container.getPerimeterSize();\n const components = container.getComponents();\n let totalHeight = perimeterSize.top + perimeterSize.bottom;\n let maxWidth = 0;\n let displayedSoFar = 0;\n\n for (let i = 0; i < components.length; i++) {\n // A non-displayed section contributes neither its header nor its\n // (possibly open) content height to the stack.\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n // Inter-section gap between displayed sections (see doLayout).\n if (displayedSoFar > 0) {\n totalHeight += this._spacing;\n }\n\n displayedSoFar += 1;\n totalHeight += this.effectiveHeaderHeight();\n\n // openState is populated lazily in doLayout; fall back to the\n // constraint's initiallyOpen flag so getPreferredSize() is correct\n // even before the first doLayout pass.\n const isOpen = i < this._openState.length\n ? this._openState[i]\n : ((this.getLayoutConstraints(components[i]) as AccordionConstraints | undefined)?.initiallyOpen ?? false);\n\n if (isOpen) {\n const preferred = components[i].getPreferredSize();\n\n if (preferred) {\n totalHeight += preferred.height;\n\n if (preferred.width > maxWidth) {\n maxWidth = preferred.width;\n }\n }\n }\n }\n\n return {\n width : maxWidth + perimeterSize.left + perimeterSize.right,\n height: totalHeight,\n };\n }\n\n /**\n * Returns the minimum size: every header height (headers are always\n * visible) plus the minimum heights of all open sections. Mirrors\n * {@link getPreferredSize} but reads each open section's `getMinSize`\n * rather than its preferred size, so a host can tell how far the accordion\n * is genuinely allowed to shrink — the headers plus the open content's own\n * floor — instead of assuming the open content can collapse to nothing.\n *\n * @returns The minimum size, or null if not attached.\n */\n getMinSize(): Size | null {\n const container = this.getContainer();\n\n if (!container) {\n return null;\n }\n\n const perimeterSize = container.getPerimeterSize();\n const components = container.getComponents();\n let totalHeight = perimeterSize.top + perimeterSize.bottom;\n let maxWidth = 0;\n let displayedSoFar = 0;\n\n for (let i = 0; i < components.length; i++) {\n // A non-displayed section contributes neither its header nor its\n // (possibly open) content height to the stack.\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n // Inter-section gap between displayed sections (see doLayout).\n if (displayedSoFar > 0) {\n totalHeight += this._spacing;\n }\n\n displayedSoFar += 1;\n totalHeight += this.effectiveHeaderHeight();\n\n // openState is populated lazily in doLayout; fall back to the\n // constraint's initiallyOpen flag so getMinSize() is correct even\n // before the first doLayout pass (matches getPreferredSize).\n const isOpen = i < this._openState.length\n ? this._openState[i]\n : ((this.getLayoutConstraints(components[i]) as AccordionConstraints | undefined)?.initiallyOpen ?? false);\n\n if (isOpen) {\n const min = components[i].getMinSize();\n\n if (min) {\n totalHeight += min.height;\n\n if (min.width > maxWidth) {\n maxWidth = min.width;\n }\n }\n }\n }\n\n return {\n width : maxWidth + perimeterSize.left + perimeterSize.right,\n height: totalHeight,\n };\n }\n\n /**\n * The accordion's maximum size: deliberately unbounded on both axes. A\n * height-animated vertical stack whose open/closed section state and\n * in-flight animation make any static height ceiling meaningless — there is\n * no stable maximum to report. Returning unbounded directly is the honest\n * answer, and keeps the `min ≤ preferred ≤ max` invariant trivially\n * satisfied against the finite min/preferred reports above.\n *\n * @returns `{ width: UNBOUNDED, height: UNBOUNDED }`.\n */\n getMaxSize(): Size | null {\n return { width: UNBOUNDED, height: UNBOUNDED };\n }\n\n /**\n * Creates header and panel wrapper elements for a newly discovered content component,\n * appends them to the container's DOM, and reparents the content element into the wrapper.\n *\n * @param component - The content component for this section.\n * @param index - Zero-based section index.\n */\n private createSection(component: Component, index: number): void {\n const container = this.getContainer()!;\n const constraints = this.getLayoutConstraints(component) as AccordionConstraints | undefined;\n const label = constraints?.label ?? component.getId();\n const initiallyOpen = constraints?.initiallyOpen ?? false;\n\n const header = new AccordionHeader(label, { chevronSide: this._chevronSide, glyph: constraints?.glyph ?? undefined, compact: this._compact, chevronGlyph: this._chevronGlyph ?? undefined });\n\n header.setAnimationTiming(this._animationDuration, ACCORDION_EASING);\n\n // Transitions are off by default so resize and drag relayouts snap;\n // `primeWrapper` turns the header's `top` transition on only for the\n // duration of an open/close toggle (headers below a toggled section\n // slide with the panel height instead of snapping to their final spot).\n header.setTransition(\"none\");\n\n const title = header.getTitleButton();\n\n // The title button is the toggle + focus target; its `action` covers the\n // label/glyph area. A click listener on the header element covers the\n // chevron (pointer-events:none, so it falls through to the header) and\n // the padding gaps. Tools carry their own ids, so neither exact-target\n // listener fires for a tool click — toggling stays structural.\n title.on(\"action\", () => this.onHeaderClicked(index));\n Event.addListener(header, 'click', () => this.onHeaderClicked(index));\n Event.addListener(title, 'keydown', (e: KeyboardEvent) => this.onHeaderKeyDown(e, index));\n\n // Hover drives tool visibility: per-section tools reveal on hover (in\n // hover mode) and the single global tool set re-parents onto the hovered\n // header. Subtree listeners catch hover anywhere in the header; the\n // handlers filter intra-header moves via relatedTarget.\n Event.addSubtreeListener(header, 'mouseover', (e: MouseEvent) => this.onHeaderHoverEnter(index, e));\n Event.addSubtreeListener(header, 'mouseout', (e: MouseEvent) => this.onHeaderHoverLeave(index, e));\n\n const wrapper = new Component();\n\n wrapper.setOverflow('hidden');\n // Animation wrapper clips content via overflow:hidden — layout+paint containment scopes\n // reflow during the height transition without affecting the rest of the document.\n wrapper.setContain(\"layout paint\");\n\n // Off by default (relayouts snap); `primeWrapper` enables it for a\n // toggle. `height` animates this wrapper's own grow/shrink; `top`\n // animates wrappers below a toggled section so they slide with the\n // headers instead of jumping.\n wrapper.setTransition(\"none\");\n\n DOM.sink.appendChild(container.getElement()!, header.getElement(true)!);\n DOM.sink.appendChild(container.getElement()!, wrapper.getElement(true)!);\n\n // Reparent content element into wrapper so overflow:hidden clips it during animation.\n DOM.sink.appendChild(wrapper.getElement()!, component.getElement()!);\n\n // Off by default (relayouts snap); `primeWrapper` enables it for a\n // toggle so the content animates its own height in lockstep with the\n // wrapper. Without it, an open section that must *shrink* during a toggle\n // — e.g. a fill-mode sibling giving back its leftover height when a lower\n // section opens — would snap to its final height while only the wrapper's\n // `overflow: hidden` clip animates, reading as the panel above jumping.\n component.setTransition(\"none\");\n\n this._openState.push(initiallyOpen);\n this._headers.push(header);\n this._panelWrappers.push(wrapper);\n\n header.setExpanded(initiallyOpen);\n title.getAria().setControls(wrapper.getId());\n\n wrapper.getAria().setRole('region');\n wrapper.getAria().setLabelledBy(title.getId());\n\n for (const tool of constraints?.tools ?? []) {\n header.addTool(tool);\n }\n\n // In hover mode a header's tools stay hidden until the pointer enters.\n if (this._toolsVisibility === \"hover\") {\n header.setToolsRevealed(false);\n }\n\n this.applySectionTheming(index);\n }\n\n /**\n * Computes the children's combined minSize along this manager's geometry:\n * width is `max(children.minWidth)` (sections stack vertically so width\n * is shared); height is N/A because Accordion intentionally ignores the\n * Y-axis overflow flag — the height animation conflicts with letting\n * sections overflow vertically. Used by `doLayout` to inflate the\n * working width when the host has opted into X-axis `setOverflowing`.\n *\n * @returns The total min-size; the height field is always `0` (unused).\n */\n protected computeTotalMinSize(): Size {\n const container = this.getContainer();\n if (!container) {\n return { width: 0, height: 0 };\n }\n\n let maxWidth = 0;\n\n for (const component of container.getComponents()) {\n if (!component.isDisplayed()) {\n continue;\n }\n\n const min = component.getMinSize();\n if (min) {\n maxWidth = Math.max(maxWidth, min.width);\n }\n }\n\n return { width: maxWidth, height: 0 };\n }\n\n /**\n * Writes one section's header, wrapper, and content geometry and returns the\n * vertical cursor after it (the wrapper's bottom edge). Shared by\n * {@link doLayout}'s main loop and the lightweight drag path so their\n * geometry can never drift. Does not reflow the content — the caller decides\n * between an immediate and a shrink-deferred `component.doLayout()`.\n *\n * @param index - Section index into `_headers` / `_panelWrappers`.\n * @param component - The section's content component.\n * @param top - The header's top edge.\n * @param panelHeight - The wrapper height (0 for a closed section).\n * @param contentHeight - The content height (preferred height for a closed section).\n * @param width - The header/wrapper/content width.\n * @param left - The header/wrapper left edge.\n * @returns The vertical cursor after this section (its wrapper's bottom edge).\n */\n private placeSection(index: number, component: Component, top: number, panelHeight: number, contentHeight: number, width: number, left: number): number {\n const header = this._headers[index];\n const wrapper = this._panelWrappers[index];\n const headerHeight = this.effectiveHeaderHeight();\n\n header.setX(left);\n header.setY(top);\n header.setWidth(width);\n header.setHeight(headerHeight);\n header.doLayout();\n\n const wrapperTop = top + headerHeight;\n\n wrapper.setX(left);\n wrapper.setY(wrapperTop);\n wrapper.setWidth(width);\n wrapper.setHeight(panelHeight);\n\n component.setX(0);\n component.setY(0);\n component.setWidth(width);\n component.setHeight(contentHeight);\n\n return wrapperTop + panelHeight;\n }\n\n /**\n * Creates sections for any new components, then positions all headers, panel\n * wrappers, and content components top-to-bottom within the container.\n */\n doLayout(): void {\n const container = this.getContainer();\n\n if (!container) {\n return;\n }\n\n // Nothing to build until the container has an element: `createSection`\n // below appends each header and wrapper straight onto it, so a premature\n // pass would dereference a null element. Such a pass is normal — see\n // `HBox.doLayout` — so return and let the next pass build the sections.\n if (!container.getElement()) {\n return;\n }\n\n const components = container.getComponents();\n\n for (let i = this._headers.length; i < components.length; i++) {\n this.createSection(components[i], i);\n }\n\n // Apply the themed container border (idempotent) so it takes effect even\n // when `themed` defaults on and `setThemed` was never called explicitly.\n this.applyContainerTheming();\n\n const containerSize = container.getInnerSize();\n const insets = container.getContentInsets();\n let containerWidth = containerSize ? containerSize.width : 0;\n\n // Universal scroll, X-axis only: Accordion stacks sections vertically\n // and animates each section's height, so honouring vertical overflow\n // would conflict with the height animation (see plan's Architecture\n // Decisions). Only inflate the working width when the host has marked\n // X as overflowing.\n if (this.isOverflowingX()) {\n const totalMin = this.computeTotalMinSize();\n containerWidth = Math.max(containerWidth, totalMin.width);\n }\n\n // Shrink-to-fit along the vertical axis: when the open sections'\n // preferred heights overflow the container, shrink each toward its min\n // so the accordion fits — see computeShrinkRatio for the full policy.\n const shrinkRatio = this.computeShrinkRatio(components, containerSize);\n\n // Fill: open sections grow to absorb the container's leftover height\n // (underflow) — split across the sections by weight, with\n // setFillHeight opting every open section in at an equal default weight.\n // The counterpart to shrink: when the content\n // overflows, shrinkRatio > 0 and the leftover is <= 0, so the fill map is\n // empty and the two policies never both apply.\n const fills = this.computeFill(components, containerSize, shrinkRatio);\n\n // Resizable mode: open sections' content heights come from the\n // drag-backed distribution instead of `openContentHeight + fill`.\n // `null` when inactive (off, no size yet, or nothing open), in which\n // case the loop below falls back to the legacy formula unchanged.\n const resizeHeights = this.computeResizableHeights(components, containerSize, shrinkRatio, fills);\n\n this.layoutSections(\n components,\n containerWidth,\n insets.getLeft(),\n insets.getTop(),\n resizeHeights !== null,\n (i, isOpen): number => {\n // Open sections take the drag-backed resizable height, or fall\n // back to the preferred height shrunk toward min plus this\n // section's fill share. Closed sections keep their content at\n // preferred height so the wrapper's `overflow: hidden` clips it\n // during the close animation rather than collapsing instantly.\n if (isOpen) {\n return resizeHeights?.get(i) ?? (this.openContentHeight(components[i], shrinkRatio) + (fills.get(i) ?? 0));\n }\n\n const preferred = components[i].getPreferredSize();\n\n // 100px fallback when a closed section reports no preferred size.\n return preferred ? preferred.height : 100;\n },\n true,\n true,\n );\n }\n\n /**\n * Places every displayed section's header, wrapper, and content top to\n * bottom, plus a resizable gutter between each adjacent open pair. Shared by\n * {@link doLayout} (the full pass) and {@link onGutterDrag} (the chained\n * live resize) so both writers produce identical geometry.\n *\n * @param components - The container's content components, section-ordered.\n * @param containerWidth - The width given to every header and wrapper.\n * @param left - The header/wrapper left edge (container inset).\n * @param top - The first displayed section's top edge (container inset).\n * @param resizable - Whether to place drag gutters between open pairs.\n * @param contentHeightFor - The content height for section `i`; `isOpen`\n * selects an open section's live height versus a closed one's preferred\n * (clipped) height.\n * @param animateShrink - Whether a section that shrinks defers its content\n * reflow to the height transition (a toggle) instead of reflowing at once\n * (a drag, where transitions are off).\n * @param reflowAll - Whether to reflow every section's content, or only the\n * sections whose height actually changed (the drag's cheap path).\n */\n private layoutSections(components: Component[], containerWidth: number, left: number, top: number, resizable: boolean, contentHeightFor: (index: number, isOpen: boolean) => number, animateShrink: boolean, reflowAll: boolean): void {\n let y = top;\n let displayedSoFar = 0;\n\n // A gutter is placed between each consecutive pair of open sections,\n // overlaying the upper section's content bottom. `previousOpen*`\n // persists across closed sections, so a closed section between two open\n // ones does not get its own gutter — the handle stays at the upper open\n // section's content bottom.\n let previousOpenComponent: Component | null = null;\n let previousOpenBottom = 0;\n let placedGutterCount = 0;\n\n for (let i = 0; i < components.length; i++) {\n const component = components[i];\n const header = this._headers[i];\n const wrapper = this._panelWrappers[i];\n const isOpen = this._openState[i];\n\n // A non-displayed section drops out of the stack entirely: hide its\n // header and wrapper and don't advance the cursor, so the sections\n // below slide up to reclaim the space.\n if (!component.isDisplayed()) {\n header.setDisplayed(false);\n wrapper.setDisplayed(false);\n\n continue;\n }\n\n header.setDisplayed(true);\n wrapper.setDisplayed(true);\n\n // Inter-section gap: before every displayed section except the\n // first, so the gap never leads or trails the stack.\n if (displayedSoFar > 0) {\n y += this._spacing;\n }\n\n displayedSoFar += 1;\n\n const contentHeight = contentHeightFor(i, isOpen);\n const panelHeight = isOpen ? contentHeight : 0;\n\n if (isOpen && resizable && previousOpenComponent !== null) {\n this.placeGutter(placedGutterCount, previousOpenBottom, containerWidth, left);\n this._gutterPairs[placedGutterCount] = { upper: previousOpenComponent, lower: component };\n placedGutterCount += 1;\n }\n\n // A shrinking open section keeps its interior laid out at the current\n // (larger) height for the duration of the shrink — the wrapper's\n // overflow:hidden clip covers it — and reflows to the smaller height\n // only once the height transition ends, so a height-driven interior\n // (a Tree/Table scroll viewport) doesn't snap mid-animation. Growing,\n // closing, and reduced motion take the immediate path. Read the old\n // height before placeSection overwrites it.\n const oldHeight = component.getHeight();\n const shrinking = isOpen && animateShrink && !Animation.isReducedMotion() && contentHeight < oldHeight;\n\n const cursor = this.placeSection(i, component, y, panelHeight, contentHeight, containerWidth, left);\n\n if (reflowAll || contentHeight !== oldHeight) {\n if (shrinking) {\n this._shrinkAnimations.get(i)?.cancel();\n this._shrinkAnimations.set(i, Animation.afterTransition({\n component: wrapper,\n property: \"height\",\n durationMs: this._animationDuration,\n fallbackBufferMs: 40,\n onComplete: () => {\n this._shrinkAnimations.delete(i);\n component.doLayout();\n },\n }));\n } else {\n component.doLayout();\n }\n }\n\n if (isOpen && resizable) {\n previousOpenComponent = component;\n previousOpenBottom = cursor;\n }\n\n y = cursor;\n }\n\n // Gutters not placed this pass (resizable off, or a pair whose upper\n // section closed/dropped out) sit hidden in the pool for reuse.\n this._gutterPairs.length = placedGutterCount;\n\n for (let i = placedGutterCount; i < this._resizeGutters.length; i++) {\n this._resizeGutters[i].setVisible(false);\n }\n }\n\n /**\n * Returns the pooled resizable-mode gutter at `index`, creating and\n * appending it to the container's DOM on first use. Mirrors the lazy\n * gutter creation in `Split.doLayout`.\n *\n * The gutter's `top` transition is off by default so drags and resizes\n * snap; `primeWrapper` turns it on only for the duration of an open/close\n * toggle, so the boundary handle slides with the sections it sits between\n * during the animation but tracks the cursor instantly during a drag.\n *\n * @param index - The gutter's position in the pool (and in `_gutterPairs`).\n * @returns The gutter for this index.\n */\n private getOrCreateResizeGutter(index: number): SplitGutter {\n const existing = this._resizeGutters[index];\n\n if (existing) {\n return existing;\n }\n\n const container = this.getContainer()!;\n const gutter = new SplitGutter(\"vertical\", { collapsible: false, expandedBackground: \"transparent\" });\n\n gutter.setTransition(\"none\");\n gutter.on(\"dragstart\", (position: number) => this.onGutterDragStart(index, position));\n gutter.on(\"drag\", (position: number) => this.onGutterDrag(index, position));\n\n DOM.sink.appendChild(container.getElement()!, gutter.getElement(true)!);\n\n this._resizeGutters.push(gutter);\n\n return gutter;\n }\n\n /**\n * Positions and shows the pooled gutter at `index`, overlaying the upper\n * section's content bottom edge. Shared by {@link doLayout} and the\n * lightweight drag path; `_gutterPairs` bookkeeping stays with the caller.\n *\n * @param index - The gutter's pool index.\n * @param upperBottom - The upper section's content bottom edge.\n * @param width - The gutter width.\n * @param left - The gutter left edge.\n */\n private placeGutter(index: number, upperBottom: number, width: number, left: number): void {\n const gutter = this.getOrCreateResizeGutter(index);\n\n gutter.setX(left);\n gutter.setY(upperBottom - RESIZE_GUTTER_SIZE);\n gutter.setWidth(width);\n gutter.setHeight(RESIZE_GUTTER_SIZE);\n gutter.setVisible(true);\n }\n\n /**\n * Captures the drag origin when a resizable gutter's drag begins: the\n * absolute pointer coordinate and the current heights of the two adjacent\n * open sections. Mirrors {@link Split.onDragStart}. Transitions are already\n * off outside a toggle, so each {@link onGutterDrag} write lands instantly\n * without any per-drag suppression.\n *\n * @param gutterIndex - The dragged gutter's position in `_gutterPairs`.\n * @param position - The absolute pointer coordinate (`clientY`) at drag start.\n */\n private onGutterDragStart(gutterIndex: number, position: number): void {\n const pair = this._gutterPairs[gutterIndex];\n\n if (!pair) {\n return;\n }\n\n const components = this.getContainer()?.getComponents() ?? [];\n\n // Snapshot the open sections' indices so the drag can chain growth\n // outward from the gutter across sections already at their max.\n this._dragOpenIndices = [];\n\n for (let i = 0; i < components.length; i++) {\n if (components[i].isDisplayed() && this._openState[i]) {\n this._dragOpenIndices.push(i);\n }\n }\n\n this._dragGutterUpperPos = this._dragOpenIndices.indexOf(components.indexOf(pair.upper));\n\n if (this._dragGutterUpperPos === -1) {\n return;\n }\n\n this._dragUpper = pair.upper;\n this._dragLower = pair.lower;\n this._dragLastPointer = position;\n\n // Accordion is a LayoutManager, not a Component, so it cannot key this\n // registration on `this` the way every other `Event.addViewportListener`\n // call site does (they call it from inside a Component, on themselves).\n // The container Component it is attached to is the stable key instead —\n // the same \"arbitrary Component key\" shape `DragManager` uses from\n // module-level code. `_boundOnGutterDragEnd` is a field so add/remove\n // share one reference regardless of which component's `apply` invokes it.\n const container = this.getContainer();\n\n if (container) {\n Event.addViewportListener(container, \"mouseup\", this._boundOnGutterDragEnd);\n Event.addViewportListener(container, \"touchend\", this._boundOnGutterDragEnd);\n Event.addViewportListener(container, \"touchcancel\", this._boundOnGutterDragEnd);\n }\n }\n\n /**\n * Resizes the open sections when a resizable gutter is dragged. The gutter\n * splits the open set into an upper group (the upper section and everything\n * above it) and a lower group (the lower section and below); dragging down\n * grows the upper group and shrinks the lower one, dragging up does the\n * reverse. **Both sides chain outward** from the gutter: the nearest section\n * absorbs the travel first, spilling to the next once it reaches its max\n * (on the growing side) or its min (on the shrinking side). So dragging\n * toward — or away from — a panel that sits against a maxed or minned\n * neighbour keeps resizing it, growing/shrinking the first free panel beyond\n * the pinned one (which merely slides) rather than stalling at the boundary.\n *\n * The open set's combined height is conserved, so this re-places every\n * displayed section through the shared {@link layoutSections} (no\n * `getPreferredSize`, reflowing only the sections whose height changed) so\n * the handle tracks the cursor. Each open section's drag-backed size is\n * written to `_resizeSizes` (in pre-factor stored units, see `_resizeFactor`)\n * so the next full layout reproduces the distribution.\n *\n * @param gutterIndex - The dragged gutter's position in `_gutterPairs`.\n * @param position - The absolute pointer coordinate (`clientY`) for this move.\n */\n private onGutterDrag(gutterIndex: number, position: number): void {\n const pair = this._gutterPairs[gutterIndex];\n const container = this.getContainer();\n\n if (!pair || pair.upper !== this._dragUpper || pair.lower !== this._dragLower || !container) {\n return;\n }\n\n const components = container.getComponents();\n const openIndices = this._dragOpenIndices;\n const upperPos = this._dragGutterUpperPos;\n\n // Applied incrementally: this frame's pointer travel is distributed on\n // top of the live heights, so a reversed drag responds nearest-first.\n // `_dragLastPointer` is advanced below by only the travel actually applied,\n // not the raw pointer position — see the note next to `delta`.\n const frameDelta = position - this._dragLastPointer;\n\n // Snapshot each open section's live height and bounds once — `getMinSize`\n // / `getMaxSize` recurse through the content's own layout, so reading them\n // per pointer move (not per lookup) keeps the drag cheap.\n const current = openIndices.map((ci): number => components[ci].getHeight());\n const mins = openIndices.map((ci): number => {\n const min = components[ci].getMinSize();\n\n return min ? min.height : 0;\n });\n const maxs = openIndices.map((ci): number => {\n const max = components[ci].getMaxSize();\n\n return max ? max.height : Number.POSITIVE_INFINITY;\n });\n\n // The two chains fanning out from the gutter, each ordered nearest-first.\n const upperGroup: number[] = [];\n const lowerGroup: number[] = [];\n\n for (let pos = upperPos; pos >= 0; pos--) {\n upperGroup.push(pos);\n }\n\n for (let pos = upperPos + 1; pos < openIndices.length; pos++) {\n lowerGroup.push(pos);\n }\n\n // Dragging down grows the upper group and shrinks the lower one; up reverses it.\n const growGroup = frameDelta >= 0 ? upperGroup : lowerGroup;\n const shrinkGroup = frameDelta >= 0 ? lowerGroup : upperGroup;\n\n const growRoom = chainRoom(growGroup, current, 1, mins, maxs);\n const shrinkRoom = chainRoom(shrinkGroup, current, -1, mins, maxs);\n\n // This frame's boundary travel, capped by how much the growth chain can\n // still absorb and the shrink chain can still give up.\n const delta = Math.max(0, Math.min(Math.abs(frameDelta), growRoom, shrinkRoom));\n\n // Advance the tracked pointer only by the travel actually applied. When the\n // chain is fully maxed/minned, `delta` is 0 and the pointer stays put, so\n // dragging further past the limit accrues a dead zone the pointer must\n // retrace before the gutter moves again — keeping the cursor glued to the\n // handle on reversal instead of the handle jumping to a far-off cursor.\n // (Split/Border get this for free from their absolute origin+offset model.)\n this._dragLastPointer += Math.sign(frameDelta) * delta;\n\n const newHeights = current.slice();\n\n distributeDragChain(growGroup, current, delta, +1, mins, maxs, newHeights);\n distributeDragChain(shrinkGroup, current, delta, -1, mins, maxs, newHeights);\n\n const openHeightByIndex = new Map<number, number>();\n\n for (let pos = 0; pos < openIndices.length; pos++) {\n const component = components[openIndices[pos]];\n // A pinned section renders at its stored px (scale 1); every other open\n // section renders at stored × _resizeFactor. Dividing by the wrong one\n // silently rescales the whole open set on the next layout.\n const scale = this._resizePinned.has(component) ? 1 : this._resizeFactor;\n\n openHeightByIndex.set(openIndices[pos], newHeights[pos]);\n this._resizeSizes.set(component, newHeights[pos] / scale);\n }\n\n const insets = container.getContentInsets();\n const width = this._panelWrappers[openIndices[0]].getWidth();\n\n this.layoutSections(\n components,\n width,\n insets.getLeft(),\n insets.getTop(),\n true,\n (i, isOpen): number => (isOpen ? (openHeightByIndex.get(i) ?? components[i].getHeight()) : components[i].getHeight()),\n false,\n false,\n );\n }\n\n /**\n * Ends a resizable-gutter drag: removes the viewport listeners and clears\n * the captured drag pair. Transitions stay off (their default outside a\n * toggle), so there is nothing to restore. Fires `sectionresize` with the\n * post-drag sizes when a drag was actually live — including on the\n * `detach()` mid-drag path, which calls this before `_resizeSizes` is\n * cleared, so the emitted sizes still reflect the drag. Also callable\n * directly (with no argument) so `detach()` and tests can simulate a\n * drag end.\n *\n * @returns `true`, consuming the release that ends the gutter drag.\n */\n private onGutterDragEnd(): Event.ListenerResult {\n const wasDragging = this._dragUpper !== null;\n const container = this.getContainer();\n\n if (container) {\n Event.removeViewportListener(container, \"mouseup\", this._boundOnGutterDragEnd);\n Event.removeViewportListener(container, \"touchend\", this._boundOnGutterDragEnd);\n Event.removeViewportListener(container, \"touchcancel\", this._boundOnGutterDragEnd);\n }\n\n this._dragUpper = null;\n this._dragLower = null;\n\n if (wasDragging) {\n this.emit(\"sectionresize\", this.getSectionSizes());\n }\n\n return true;\n }\n\n /**\n * Computes the vertical shrink ratio applied to every open section's\n * content so the accordion fits its container, mirroring VBox's\n * preferred-mode shrink. Headers are fixed and never shrink; only open\n * sections contribute shrinkable content.\n *\n * Three cases:\n * 1. The open sections already fit (`preferred <= budget`) → ratio `0`,\n * so every section renders at its preferred height.\n * 2. They overflow but fit at their combined minimum\n * (`min <= budget < preferred`) → ratio\n * `(preferred - budget) / (preferred - min)`, clamped to `[0, 1]`, so\n * each section shrinks proportionally from preferred toward its min.\n * 3. They overflow even at the combined minimum (`budget < min`) →\n * ratio `0`, falling back to preferred and letting the host clip. A\n * layout crammed below every section's min reads worse than a clean\n * overflow, so the manager declines to shrink past min.\n *\n * @param components - The container's content components, section-ordered.\n * @param containerSize - The container's inner size; `null` short-circuits\n * to `0` (no shrink) since the budget is unknown.\n * @returns The shrink ratio in `[0, 1]`.\n */\n private computeShrinkRatio(components: Component[], containerSize: Size | null): number {\n if (!containerSize) {\n return 0;\n }\n\n let headerTotal = 0;\n let openPreferred = 0;\n let openMin = 0;\n let displayedSoFar = 0;\n\n for (let i = 0; i < components.length; i++) {\n // A non-displayed section shows neither its header nor its content,\n // so it contributes nothing to the height budget.\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n // The inter-section gap is fixed (non-shrinkable) budget, like the\n // headers — count it between displayed sections (see doLayout).\n if (displayedSoFar > 0) {\n headerTotal += this._spacing;\n }\n\n displayedSoFar += 1;\n headerTotal += this.effectiveHeaderHeight();\n\n if (!this._openState[i]) {\n continue;\n }\n\n const pref = components[i].getPreferredSize();\n const min = components[i].getMinSize();\n\n openPreferred += pref ? pref.height : 100;\n openMin += min ? min.height : 0;\n }\n\n const totalPreferred = headerTotal + openPreferred;\n const totalMin = headerTotal + openMin;\n const budget = containerSize.height;\n\n if (totalPreferred <= budget || totalMin > budget) {\n return 0;\n }\n\n const shrinkable = totalPreferred - totalMin;\n\n return shrinkable > 0 ? (totalPreferred - budget) / shrinkable : 0;\n }\n\n /**\n * The height an open section's content renders at: its preferred height\n * shrunk toward its minimum by `shrinkRatio`, then clamped to its merged\n * `[min, max]`. Falls back to 100px when the section reports no preferred\n * height.\n *\n * The clamp matters because `getPreferredSize` clamps only to a component's\n * *own* min/max constraints, not the merged {@link Component.getMinSize} /\n * {@link Component.getMaxSize} that fold in child-derived limits (e.g. a\n * Panel wrapping a fixed-row List, or a form whose fields floor its height).\n * Without it, a section whose preferred sits outside its real `[min, max]`\n * would render its wrapper past that bound while the content self-clamped\n * inside — and the drag-backed resizable path (which does respect the merged\n * bounds) then disagreed, making a resizable toggle resize the section.\n *\n * @param component - The section content component.\n * @param shrinkRatio - The container-driven shrink ratio in `[0, 1]`.\n * @returns The content height in pixels.\n */\n private openContentHeight(component: Component, shrinkRatio: number): number {\n const preferred = component.getPreferredSize();\n const contentPref = preferred ? preferred.height : 100;\n const min = component.getMinSize();\n const contentMin = min ? min.height : 0;\n\n const shrunk = contentPref - shrinkRatio * (contentPref - contentMin);\n const max = component.getMaxSize();\n const capped = max ? Math.min(shrunk, max.height) : shrunk;\n\n return Math.max(capped, contentMin);\n }\n\n /**\n * Computes how much extra height each open section absorbs from the\n * container's leftover space, keyed by section index. Active only when the\n * open sections underflow the container (`leftover > 0`); on overflow the\n * leftover is `<= 0` so fill yields nothing and shrink handles the fit.\n *\n * Each open section's effective fill weight is its explicit `weight`\n * constraint, or — when {@link setFillHeight} is on — a default of `1` so\n * every unweighted open section shares the slack equally. The leftover is\n * then split across all sections with a positive effective weight in\n * proportion to those weights, each capped at its own max (a capped\n * section's surplus is re-shared among the rest; see\n * {@link distributeFillWithinMax}). So `setFillHeight` spreads the slack\n * across every open section (equal weights → equal slices) rather than\n * padding a single one, and per-section `weight` still targets or biases\n * specific sections. With neither, nothing fills and the slack stays as\n * trailing space.\n *\n * @param components - The container's child components.\n * @param containerSize - The container's inner size, or null.\n * @param shrinkRatio - The shrink ratio applied to open content.\n * @returns A map from section index to the extra height it absorbs; empty\n * when nothing fills.\n */\n private computeFill(components: Component[], containerSize: Size | null, shrinkRatio: number): Map<number, number> {\n const fills = new Map<number, number>();\n\n if (!containerSize) {\n return fills;\n }\n\n let used = 0;\n let displayed = 0;\n const recipients: Array<{ index: number; weight: number; headroom: number }> = [];\n let weightTotal = 0;\n\n for (let i = 0; i < components.length; i++) {\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n if (displayed > 0) {\n used += this._spacing;\n }\n\n displayed += 1;\n used += this.effectiveHeaderHeight();\n\n if (this._openState[i]) {\n const contentHeight = this.openContentHeight(components[i], shrinkRatio);\n used += contentHeight;\n\n const weight = this.effectiveWeight(components[i]);\n\n if (weight > 0) {\n recipients.push({ index: i, weight, headroom: this.fillHeadroom(components[i], contentHeight) });\n weightTotal += weight;\n }\n }\n }\n\n const leftover = containerSize.height - used;\n\n if (leftover <= 0 || weightTotal <= 0) {\n return fills;\n }\n\n // Split the slack across the recipients by weight, each capped at its own\n // max so an open section is never padded past its maximum height; a\n // capped section's surplus is re-shared among the rest.\n return this.distributeFillWithinMax(recipients, leftover);\n }\n\n /**\n * Resolves an open section's effective weight — its explicit `weight`\n * constraint, or a default of `1` when {@link setFillHeight} is on so every\n * unweighted open section counts equally. `0` means unweighted: the section\n * takes no share of the container's leftover height, and in resizable mode it\n * holds its px across a container resize instead of rescaling with the rest.\n *\n * Reads the constraint as `?? 0`, matching the box managers — unlike `Split`,\n * where an unset weight falls through to a proportional fallback.\n *\n * @param component - The open section's content component.\n * @returns The effective weight; `0` when the section is unweighted.\n */\n private effectiveWeight(component: Component): number {\n const explicit = this.getLayoutConstraints(component)?.weight ?? 0;\n\n return explicit > 0 ? explicit : (this._fillHeight ? 1 : 0);\n }\n\n /**\n * The open sections that hold their stored px across a container resize —\n * those with an effective weight of `0`, so only the weighted sections absorb\n * the change. Returns empty (so the whole open set rescales proportionally, as\n * it always has) in the two cases where pinning cannot apply: no open section\n * is weighted, so there is nothing to absorb the change; or the pins alone\n * overrun the budget, where a pin must yield because geometry has to fill the\n * container — mirroring `Split.setPaneResizeWeight`'s contract that a pin holds\n * only while the container is large enough.\n *\n * @param components - The container's content components, section-ordered.\n * @param openIndices - Indices of the open sections.\n * @param openBudget - The height available to the open sections' content.\n * @returns The pinned sections' indices, or an empty array.\n */\n private resizePinnedSections(components: Component[], openIndices: number[], openBudget: number): number[] {\n const pinned: number[] = [];\n let flexible = 0;\n let pinnedTotal = 0;\n\n for (const i of openIndices) {\n if (this.effectiveWeight(components[i]) > 0) {\n flexible += 1;\n } else {\n pinned.push(i);\n pinnedTotal += this.clampSectionHeight(components[i], this._resizeSizes.get(components[i]) ?? 0);\n }\n }\n\n if (flexible === 0 || pinnedTotal > openBudget) {\n return [];\n }\n\n return pinned;\n }\n\n /**\n * Clamps a candidate content height to a section's `[min, max]`, so a pin is\n * held within the same bounds the proportional pass enforces.\n *\n * @param component - The open section's content component.\n * @param value - The candidate content height in px.\n * @returns The clamped content height.\n */\n private clampSectionHeight(component: Component, value: number): number {\n const min = component.getMinSize();\n const max = component.getMaxSize();\n\n return Util.clamp(value, min ? min.height : 0, max ? max.height : Number.POSITIVE_INFINITY);\n }\n\n /**\n * How much fill a section can still absorb before reaching its maximum\n * height — the gap between its max and the content height it already takes\n * without any fill. Unbounded when the section declares no max.\n *\n * @param component - The open section's content component.\n * @param contentHeight - The section's fill-free content height.\n * @returns The absorbable headroom in pixels, or `Infinity` when unbounded.\n */\n private fillHeadroom(component: Component, contentHeight: number): number {\n const max = component.getMaxSize();\n\n return max ? Math.max(0, max.height - contentHeight) : Number.POSITIVE_INFINITY;\n }\n\n /**\n * Splits `leftover` fill across the weighted recipients in proportion to\n * their weights, capping each at its remaining headroom (`max − content`)\n * and re-sharing a capped recipient's surplus among the rest. Mirrors\n * {@link distributeWithinConstraints} for the fill path so a weighted\n * section is never padded past its max; any surplus the remaining\n * recipients cannot absorb stays as slack.\n *\n * @param recipients - The weighted open sections: index, weight, headroom.\n * @param leftover - The container's leftover height to distribute.\n * @returns A map from section index to the extra height it absorbs.\n */\n private distributeFillWithinMax(recipients: Array<{ index: number; weight: number; headroom: number }>, leftover: number): Map<number, number> {\n const fills = new Map<number, number>();\n const free = new Set(recipients);\n let remaining = leftover;\n\n for (;;) {\n let freeWeight = 0;\n\n for (const r of free) {\n freeWeight += r.weight;\n }\n\n if (freeWeight <= 0) {\n break;\n }\n\n const perWeight = remaining / freeWeight;\n let capped = false;\n\n for (const r of free) {\n if (r.weight * perWeight > r.headroom) {\n fills.set(r.index, r.headroom);\n remaining -= r.headroom;\n free.delete(r);\n capped = true;\n\n break;\n }\n }\n\n if (!capped) {\n for (const r of free) {\n fills.set(r.index, r.weight * perWeight);\n }\n\n break;\n }\n\n if (free.size === 0) {\n break;\n }\n }\n\n return fills;\n }\n\n /**\n * Computes each open section's content height in resizable mode, keyed by\n * container index. Supersedes the `openContentHeight + fill` path when\n * active: a section with no stored size yet is seeded from what that\n * legacy path would have given it (so turning resizable on is visually\n * seamless), then every open section's stored size is rescaled by\n * `openBudget / storedTotal` so the set always fills the container without\n * rewriting the stored ratio — a drag ({@link onGutterDrag}) is the only\n * thing that changes the ratio itself. Also prunes `_resizeSizes` entries\n * for components no longer in `components`.\n *\n * @param components - The container's content components, section-ordered.\n * @param containerSize - The container's inner size; `null` short-circuits\n * to `null` (caller falls back to the legacy path).\n * @param shrinkRatio - The container-driven shrink ratio, used only to seed\n * a not-yet-stored section from the legacy formula.\n * @param fills - The legacy fill map, used only to seed a not-yet-stored\n * section from the legacy formula.\n * @returns A map from container index to content height for every open\n * section, or `null` when resizable mode is inactive, there is no\n * container size, or no section is open (the caller keeps the legacy\n * `openContentHeight + fill` path in all three cases).\n */\n private computeResizableHeights(components: Component[], containerSize: Size | null, shrinkRatio: number, fills: Map<number, number>): Map<number, number> | null {\n if (!this._resizable || !containerSize) {\n return null;\n }\n\n for (const stored of [...this._resizeSizes.keys()]) {\n if (!components.includes(stored)) {\n this._resizeSizes.delete(stored);\n }\n }\n\n let headerTotal = 0;\n let displayedSoFar = 0;\n const openIndices: number[] = [];\n\n for (let i = 0; i < components.length; i++) {\n if (!components[i].isDisplayed()) {\n continue;\n }\n\n if (displayedSoFar > 0) {\n headerTotal += this._spacing;\n }\n\n displayedSoFar += 1;\n headerTotal += this.effectiveHeaderHeight();\n\n if (this._openState[i]) {\n openIndices.push(i);\n }\n }\n\n if (openIndices.length === 0) {\n return null;\n }\n\n const openBudget = containerSize.height - headerTotal;\n\n this.applyPendingSectionSizes(components, openBudget);\n\n for (const i of openIndices) {\n const component = components[i];\n\n if (!this._resizeSizes.has(component)) {\n this._resizeSizes.set(component, this.openContentHeight(component, shrinkRatio) + (fills.get(i) ?? 0));\n }\n }\n\n return this.distributeWithinConstraints(components, openIndices, openBudget);\n }\n\n /**\n * Drains the `sectionSizes` option (or a pre-layout\n * {@link applySectionSizes} call) into `_resizeSizes` on the first\n * resizable layout that can resolve an open budget. Runs once: cleared\n * unconditionally so a stale array is not retried on every later layout.\n *\n * @param components - The container's content components, section-ordered.\n * @param openBudget - The height available to the open sections' content,\n * the base {@link fromLayoutSizes} seeds the ratio entries against.\n */\n private applyPendingSectionSizes(components: Component[], openBudget: number): void {\n const pending = this._pendingSectionSizes;\n\n if (pending === null) {\n return;\n }\n\n this._pendingSectionSizes = null;\n\n const units = this.sectionSizeUnits(components);\n\n if (!isRestorableSizes(pending, units)) {\n return;\n }\n\n const stored = fromLayoutSizes(pending, openBudget);\n\n for (let idx = 0; idx < components.length; idx += 1) {\n // A zero-size section is *removed* rather than stored as 0, so it falls\n // back to the legacy `openContentHeight + fill` seed below instead of\n // taking a zero share of the budget.\n if (stored[idx] > 0) {\n this._resizeSizes.set(components[idx], stored[idx]);\n } else {\n this._resizeSizes.delete(components[idx]);\n }\n }\n }\n\n /**\n * Splits `openBudget` across the open sections in proportion to their\n * stored sizes, clamped to each section's `[min, max]` height. An unweighted\n * open section (effective weight `0`) is first held at its stored px and\n * removed from the budget, so only the weighted sections absorb a container\n * resize; the split falls back to rescaling the whole set when no section is\n * weighted or the pins overrun the budget. A section\n * whose proportional share would fall outside its bounds is pinned at the\n * violated bound and removed from the budget; the remaining sections then\n * re-share what is left, iterating until every free section fits. This\n * keeps the open heights summing to `openBudget` — so the stack neither\n * overflows the container (a min floor pushing content past the box) nor\n * leaves it under-filled — while never stretching a section past its max\n * or compressing it below its min. It generalises {@link onGutterDrag}'s\n * per-pair `[min, max]` clamp to the whole open set, so a full layout and a\n * drag agree on the constraints (a section rendered past its max otherwise\n * snapped down the instant its gutter was grabbed).\n *\n * When the constraints make an exact fill impossible — the mins already\n * exceed the budget, or the maxes cannot reach it — the pinned sizes stand:\n * the stack over- or under-fills, which the host clips/scrolls or leaves as\n * slack, matching the `getMinSize` contract.\n *\n * Also caches {@link _resizeFactor} — the stored→rendered scale of the\n * *unpinned* sections, which is the mapping a drag moves within.\n *\n * @param components - The container's content components, section-ordered.\n * @param openIndices - Indices of the open sections to distribute across.\n * @param openBudget - The height available to the open sections' content.\n * @returns A map from section index to its clamped content height.\n */\n private distributeWithinConstraints(components: Component[], openIndices: number[], openBudget: number): Map<number, number> {\n const heights = new Map<number, number>();\n const free = new Set<number>(openIndices);\n let remaining = openBudget;\n let freeFactor = 1;\n\n // Resize-pinned sections hold their stored px and leave the budget before the\n // proportional pass, so only the weighted sections absorb a container resize.\n // Empty unless the open set is mixed (some weighted, some not) — with no\n // weighted section the whole set rescales proportionally, exactly as before.\n this._resizePinned.clear();\n\n for (const i of this.resizePinnedSections(components, openIndices, openBudget)) {\n const height = this.clampSectionHeight(components[i], this._resizeSizes.get(components[i]) ?? 0);\n\n heights.set(i, height);\n remaining -= height;\n free.delete(i);\n this._resizePinned.add(components[i]);\n }\n\n // At most one section is pinned per pass, so this settles in at most\n // `openIndices.length` passes.\n for (;;) {\n let freeStored = 0;\n\n for (const i of free) {\n freeStored += this._resizeSizes.get(components[i]) ?? 0;\n }\n\n freeFactor = freeStored > 0 ? remaining / freeStored : 0;\n let pinned = false;\n\n for (const i of free) {\n const component = components[i];\n const share = (this._resizeSizes.get(component) ?? 0) * freeFactor;\n const min = component.getMinSize();\n const max = component.getMaxSize();\n const lo = min ? min.height : 0;\n const hi = max ? max.height : Number.POSITIVE_INFINITY;\n\n if (share < lo) {\n heights.set(i, lo);\n remaining -= lo;\n free.delete(i);\n pinned = true;\n\n break;\n }\n\n if (share > hi) {\n heights.set(i, hi);\n remaining -= hi;\n free.delete(i);\n pinned = true;\n\n break;\n }\n }\n\n if (!pinned) {\n for (const i of free) {\n heights.set(i, (this._resizeSizes.get(components[i]) ?? 0) * freeFactor);\n }\n\n break;\n }\n\n if (free.size === 0) {\n break;\n }\n }\n\n // Cached for onGutterDrag, which converts its rendered-pixel drag math\n // back to the stored scale before writing into `_resizeSizes`.\n this._resizeFactor = freeFactor > 0 ? freeFactor : 1;\n\n return heights;\n }\n\n /**\n * Handles ArrowDown, ArrowUp, Home, and End to move keyboard focus between headers.\n *\n * @param e - The keyboard event fired on a header.\n * @param index - Zero-based index of the header that received the event.\n */\n private onHeaderKeyDown(e: KeyboardEvent, index: number): Event.ListenerResult {\n const count = this._headers.length;\n\n if (count === 0) {\n return;\n }\n\n let target: number;\n\n switch (e.key) {\n case 'ArrowDown': target = (index + 1) % count; break;\n case 'ArrowUp': target = (index - 1 + count) % count; break;\n case 'Home': target = 0; break;\n case 'End': target = count - 1; break;\n default: return;\n }\n\n this._headers[target].getTitleButton().focus();\n\n return { prevent: true };\n }\n\n /**\n * Handles a header click: toggles the open state, enforces single-open mode,\n * updates the indicator and ARIA, fires the callback, and re-runs layout.\n *\n * @param index - Zero-based index of the clicked header.\n */\n private onHeaderClicked(index: number): void {\n const wasOpen = this._openState[index];\n\n if (this._singleOpen && !wasOpen) {\n for (let i = 0; i < this._openState.length; i++) {\n if (i !== index && this._openState[i]) {\n this.primeWrapper(i);\n this._openState[i] = false;\n this._headers[i].setExpanded(false);\n this.emit(\"sectiontoggle\", i, false);\n }\n }\n }\n\n const nowOpen = !wasOpen;\n\n this.primeWrapper(index);\n this._openState[index] = nowOpen;\n this._headers[index].setExpanded(nowOpen);\n this.emit(\"sectiontoggle\", index, nowOpen);\n this.relayoutHost();\n }\n\n /**\n * Pre-promotes the panel wrapper to its own compositor layer for the\n * duration of the active toggle so the first transition frame doesn't\n * pay a layer-creation cost, then schedules the layer's release on\n * `transitionend` (filtered to `height`) with a `setTimeout` fallback\n * for the cases where `transitionend` never fires — toggling a section\n * whose height didn't change, tab-switch mid-transition, …\n *\n * Transitions are off by default (so resize and drag relayouts snap), so a\n * toggle must turn them on before its `doLayout` writes land and off again\n * when the animation completes. Enabling covers every header, panel wrapper,\n * content component, and gutter (a toggle moves the wrapper's height, the\n * `top` of every header + wrapper below it, and each open section's content\n * height). The global disable is gated on the `_toggleAnimations` counter:\n * single-open mode primes several sections in one gesture, so the first\n * animation to finish must not snap the others still in flight — only the\n * last (`_toggleAnimations` back to zero) turns transitions off.\n *\n * Under `prefers-reduced-motion: reduce` transitions stay off (their\n * default) and nothing is enabled, so the toggle's writes land instantly.\n *\n * Mirrors the `transitionend`-with-fallback pattern in\n * [`Animation.play`](/api/core/namespaces/Animation/functions/play) so\n * the bookkeeping stays in one shape across the framework.\n *\n * @param index - Zero-based index of the section whose wrapper to prime.\n */\n private primeWrapper(index: number): void {\n // Reduced motion leaves transitions off (their default), so the toggle's\n // doLayout writes land instantly — there is nothing to prime.\n if (Animation.isReducedMotion()) {\n return;\n }\n\n const wrapper = this._panelWrappers[index];\n const container = this.getContainer();\n\n this.setSectionTransitions(true);\n\n // Give the container's own height the same transition so the outer\n // layout's resize (triggered when the parent re-queries our\n // `getPreferredSize` after the open state flips) animates in lockstep\n // with the wrappers and headers inside. Without this, the container\n // (which defaults to `overflow: hidden`) snaps to the closed size and\n // clips the still-animating sections — they vanish for ~75% of the\n // duration and pop back in near the end. Cleared on cleanup so\n // unrelated height changes (window resize, etc.) stay instant.\n container?.setTransition(`height ${this._animationDuration}ms ${ACCORDION_EASING}`);\n\n wrapper.setWillChange(\"height\");\n\n // Commit the just-enabled transitions as the animation's \"before\" frame\n // *before* the toggle's geometry change lands. That change can run\n // synchronously in this same task (relayoutHost -> notifyIntrinsicSize-\n // Changed re-lays out the accordion), and a CSS transition enabled and\n // triggered in one task never animates — the browser only ever sees the\n // after-state. A single forced layout read flushes the transition style\n // as the pre-toggle snapshot so the upcoming height change animates from\n // it. (Transitions are off outside a toggle, so nothing else pays this.)\n const element = container?.getElement();\n\n if (element) {\n DOM.source.getElementRect(element);\n }\n\n // A re-toggle inside the animation duration replaces the in-flight\n // animation rather than racing it. Its onComplete will never run, so\n // the replacement inherits its slot in `_toggleAnimations` instead of\n // adding a second one — otherwise the counter never returns to zero and\n // the cleanup branch below is unreachable for the rest of the manager's\n // life. Completed animations delete themselves from the map, so a\n // non-null `get` here always means a genuinely in-flight animation.\n const inFlight = this._wrapperAnimations.get(index);\n\n if (inFlight) {\n inFlight.cancel();\n this._wrapperAnimations.delete(index);\n } else {\n this._toggleAnimations += 1;\n }\n\n this._wrapperAnimations.set(index, Animation.afterTransition({\n component: wrapper,\n property: \"height\",\n durationMs: this._animationDuration,\n fallbackBufferMs: 40,\n onComplete: () => {\n this._wrapperAnimations.delete(index);\n\n wrapper.setWillChange(null);\n this._toggleAnimations -= 1;\n\n // Only the last toggle to complete turns transitions back off,\n // so an earlier finisher can't snap sections still animating.\n if (this._toggleAnimations <= 0) {\n this._toggleAnimations = 0;\n this.setSectionTransitions(false);\n container?.setTransition(null);\n\n // Re-lay-out the host now that the geometry has settled. The\n // toggle's own relayoutHost ran while this animation was\n // still in flight, so every DOM measurement it took saw the\n // pre-toggle extent: a scrolling host measured its scrollbar\n // gutter against content that was mid-transition and still\n // overflowing, and so kept reserving the gutter even though\n // the bar disappears once the animation lands. Nothing else\n // re-measures afterwards, so the stale reserve would linger\n // until some unrelated layout — in practice, the next toggle\n // — making the reclaim run one gesture behind.\n this.relayoutHost();\n }\n },\n }));\n }\n\n /**\n * Installs or removes the open/close animation transitions on every header,\n * panel wrapper, content component, and resize gutter. Enabled by\n * `primeWrapper` for the duration of a toggle; off otherwise so resize and\n * drag relayouts snap to their final geometry.\n *\n * @param enabled - True to install the animated transitions, false to clear them.\n */\n private setSectionTransitions(enabled: boolean): void {\n const components = this.getContainer()?.getComponents() ?? [];\n\n for (let i = 0; i < this._headers.length; i++) {\n this._headers[i].setTransition(enabled ? this.buildHeaderTransition() : \"none\");\n this._panelWrappers[i].setTransition(enabled ? this.buildWrapperTransition() : \"none\");\n components[i]?.setTransition(enabled ? this.buildContentTransition() : \"none\");\n }\n\n for (const gutter of this._resizeGutters) {\n gutter.setTransition(enabled ? this.buildHeaderTransition() : \"none\");\n }\n }\n\n /**\n * Builds the `top`-only transition shorthand applied to every header.\n * Centralised so `createSection` and `primeWrapper`'s reduced-motion\n * restore path stay in sync.\n */\n private buildHeaderTransition(): string {\n return `top ${this._animationDuration}ms ${ACCORDION_EASING}`;\n }\n\n /**\n * Builds the multi-property transition shorthand applied to every panel\n * wrapper — `height` animates the wrapper's own grow/shrink, `top`\n * animates the wrappers below a toggled section so they slide with the\n * headers instead of jumping.\n */\n private buildWrapperTransition(): string {\n return `height ${this._animationDuration}ms ${ACCORDION_EASING}, top ${this._animationDuration}ms ${ACCORDION_EASING}`;\n }\n\n /**\n * Builds the `height`-only transition applied to every section's content\n * component so it grows and shrinks in lockstep with its wrapper. The content\n * sits at `top: 0` inside the wrapper, so only its height ever animates.\n * Centralised so `createSection` and `primeWrapper`'s reduced-motion restore\n * path stay in sync.\n */\n private buildContentTransition(): string {\n return `height ${this._animationDuration}ms ${ACCORDION_EASING}`;\n }\n}\n\nconst AccordionCallable = callable(Accordion);\ntype AccordionCallable = Accordion;\nexport {\n Accordion as _Accordion,\n AccordionCallable as Accordion\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Component } from \"~/core/Component.js\";\n\n/**\n * Layout constraints for child components added to an {@link Accordion} container.\n * Provides the header label and initial open state for each collapsible section.\n *\n * @category Layouts\n */\nexport class AccordionConstraints extends LayoutConstraints {\n\n /** Text displayed in the section's header button. */\n label: string;\n\n /** Whether the section starts expanded. Defaults to false if omitted. */\n initiallyOpen?: boolean;\n\n /** Per-section tool components shown in this section's header tool group. */\n tools?: Component[];\n\n /**\n * @param label - Text displayed in the section header.\n * @param initiallyOpen - Whether the section starts expanded. Defaults to false.\n * @param glyph - Optional registry glyph name shown leading the header\n * label. Stored on the inherited `glyph` constraint field.\n * @param tools - Optional per-section tool components for this header.\n */\n constructor(label: string, initiallyOpen?: boolean, glyph?: string, tools?: Component[]) {\n super();\n\n this.label = label;\n this.initiallyOpen = initiallyOpen;\n this.glyph = glyph ?? null;\n this.tools = tools;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\n\n/**\n * Per-child layout constraints for components added to a {@link Grid} container.\n * Carries optional cell spanning ({@link GridConstraints.rowSpan} /\n * {@link GridConstraints.colSpan}) and explicit cell placement\n * ({@link GridConstraints.col} / {@link GridConstraints.row}).\n *\n * @remarks A child counts as explicitly placed if **either** `col` or `row` is\n * provided; the missing axis defaults to `0`. Explicitly placed children are\n * reserved before any auto-flow, so un-positioned children flow around them.\n * `col` / `row` are 0-based cell indices clamped to the grid's column/row count\n * at read time. `rowSpan` / `colSpan` default to `1` and clamp to the cells\n * remaining from the child's origin.\n *\n * The inherited `fill` ({@link FillType}) and `anchor` ({@link AnchorType})\n * control how the child sits inside its cell and take precedence over the\n * grid's `defaultFill` / `defaultAnchor`: a child with `fill = FillType.NONE`\n * shrinks to its preferred size and parks at its `anchor` even when the grid\n * default is `FillType.BOTH`. Leave them unset to inherit the grid defaults.\n *\n * @category Layouts\n */\nexport class GridConstraints extends LayoutConstraints {\n\n /** 0-based explicit column index; clamped to grid bounds at read time. */\n col?: number;\n\n /** 0-based explicit row index; clamped to grid bounds at read time. */\n row?: number;\n\n /** Number of rows the child spans. Defaults to `1`. */\n rowSpan?: number;\n\n /** Number of columns the child spans. Defaults to `1`. */\n colSpan?: number;\n\n constructor() {\n super();\n }\n}\n"],"mappings":"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"}
|