@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":"Panel-CpNQTLNF.js","names":[],"sources":["../../src/typescript/lib/core/Panel.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Insets } from \"~/primitive/Insets\";\nimport { LayoutManager } from \"~/layout/LayoutManager.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { InlineStyle, StyleRule } from \"~/core/StyleTarget.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { scrollShadowBoxShadow, scrollShadowEdgeValue, scrollShadowRamp } from \"~/core/ScrollShadow.js\";\nimport { Scrollbar } from \"~/component/container/Scrollbar.js\";\n\n/**\n * Per-edge shadow strength for a panel's scroll shadows, cached as a whole\n * percentage (0–100). The per-scroll update only rewrites a custom property\n * when an edge's quantised strength actually changes, so a scroll that doesn't\n * move the visible strength costs nothing here.\n */\ntype ScrollShadowEdges = { top: number; bottom: number; left: number; right: number };\n\n/**\n * Selects the per-axis scroll behaviour for a {@link Panel}.\n *\n * - `\"none\"` — never scroll; oversized children clip (the default).\n * - `\"auto\"` — scrollbars appear on either axis only when content overflows.\n * - `\"x\"` — horizontal scrollbar on overflow; vertical overflow clips.\n * - `\"y\"` — vertical scrollbar on overflow; horizontal overflow clips.\n * - `\"both\"` — both scrollbars are always shown (`overflow: scroll`).\n *\n * @remarks For every value except `\"none\"` the panel measures the native\n * scrollbar gutter after each `doLayout` pass and subtracts it from\n * `getInnerSize` when a scrollbar is actually visible, so layout managers\n * naturally lay out children within the post-gutter content area instead of\n * letting them spill behind the scrollbar (which the browser would otherwise\n * resolve by adding the opposite-axis scrollbar — the classic V→H cascade).\n * A scrollbar transition triggers a one-frame re-layout via `scheduleLayout`.\n *\n * @category Core\n */\nexport type AutoScrollMode = \"none\" | \"auto\" | \"x\" | \"y\" | \"both\";\n\n/**\n * Selects how a scrolling {@link Panel} renders its scrollbar.\n *\n * - `\"overlay\"` — the default. Scrolling stays native (`overflow: auto`), the\n * native scrollbar is hidden visually, and two custom `Scrollbar` widgets\n * are overlaid at the trailing edges, synced to the element's native\n * `scrollTop` / `scrollLeft`. Every native scroll behaviour (keyboard, find,\n * focus-scroll, caret scroll, assistive tech) is preserved.\n * - `\"native\"` — the OS scrollbar renders as usual; the panel reserves the\n * measured native gutter width instead of the fixed overlay track width.\n *\n * Ignored while `autoScroll === \"none\"` (a non-scrolling panel shows neither).\n *\n * @category Core\n */\nexport type ScrollbarStyle = \"native\" | \"overlay\";\n\n/**\n * Construction-time options for {@link Panel}.\n *\n * @remarks `insets` is inherited from {@link ComponentOptions} but defaults to\n * `(4, 4, 4, 4)` for `Panel` (Component itself defaults to zero insets). Pass\n * an explicit `insets` to override.\n *\n * @category Core\n */\nexport interface PanelOptions extends ContainerOptions {\n tag?: string;\n\n /**\n * Construction-time shortcut for [`Panel.setAutoScroll`](/api/core/classes/Panel#setautoscroll). Defaults to\n * `\"none\"` (oversized children clip, matching the inherited `Component`\n * `overflow: hidden` behaviour).\n */\n autoScroll?: AutoScrollMode;\n\n /**\n * When `true` (the default), an `autoScroll` panel paints a fading edge\n * shadow on each side where hidden content can still be scrolled into\n * view — a cue that the content continues past the viewport border rather\n * than ending there. Set `false` to suppress the shadows. Ignored while\n * `autoScroll === \"none\"` (a non-scrolling panel never shows them).\n */\n scrollShadows?: boolean;\n\n /**\n * Selects the scrollbar rendering for an `autoScroll` panel. Defaults to\n * `\"overlay\"` — a scrolling panel hides its native bar and paints synced\n * overlay `Scrollbar` widgets instead. Pass `\"native\"` to opt out and keep\n * the OS scrollbar. Ignored while `autoScroll === \"none\"`.\n */\n scrollbarStyle?: ScrollbarStyle;\n\n /**\n * When `true`, the panel's default content insets are zero instead of the\n * usual `(4, 4, 4, 4)` — the rail-style default for a fixed-width strip\n * (activity rail, narrow Border/VBox region) that must sit flush against\n * its host and keep a constant width. Construction-time only; a\n * caller-supplied `insets` still wins. Defaults to `false`.\n */\n flush?: boolean;\n}\n\n/**\n * User-overridable defaults forwarded to `super` via the options bag. The\n * cascade in `Component`'s constructor dispatches `setInsets` once with the\n * final value, so a caller-supplied `insets` wins over the panel default.\n */\nconst _defaultPanelOptions: Partial<PanelOptions> = {\n tag: \"div\",\n insets: new Insets(4, 4, 4, 4),\n autoScroll: \"none\",\n scrollShadows: true,\n scrollbarStyle: \"overlay\",\n};\n\n/**\n * Class applied to the raw inner scroll element of an overlay-mode panel. The\n * inner element has no `#id` (it is not a `Component`), so its native bar is\n * hidden through a shared class rule rather than the per-`#id`\n * `setElementCSSRule` path the panel element uses — the framework's module-level\n * shared class-rule pattern (as the focus-ring and `Component` class rules use).\n */\nconst OVERLAY_SCROLLER_CLASS = \"PanelOverlayScroller\";\n\n// The shared inner-scroller class rules, retained once registered (mirrors the\n// CollapseButton / header-glyph module-singleton pattern). The array doubles as\n// the idempotency guard.\nlet _scrollerClassRules: StyleRule[] | null = null;\n\n/**\n * Registers, once, the shared class rules that hide the native scrollbar on an\n * overlay panel's raw inner scroll element: `scrollbar-width: none` (Firefox /\n * Chromium >= 121) plus a `::-webkit-scrollbar { display: none }` selector rule\n * (WebKit / older Blink) — the two-write pair the panel element's\n * `setNativeScrollbarHidden` uses, expressed as a shared class instead of a\n * per-`#id` rule.\n */\nfunction ensureOverlayScrollerClassRule(): void {\n if (_scrollerClassRules) {\n return;\n }\n\n _scrollerClassRules = [\n new StyleRule({\n scope: \"class\",\n name: OVERLAY_SCROLLER_CLASS,\n styles: { scrollbarWidth: \"none\" },\n }),\n new StyleRule({\n scope: \"selector\",\n name: `.${OVERLAY_SCROLLER_CLASS}::-webkit-scrollbar`,\n styles: { display: \"none\" },\n }),\n ];\n}\n\n/**\n * A [`Container`](/api/core/classes/Container) subclass that applies a default 4-pixel inset on all sides.\n *\n * Use `Panel` as the base class for grouped UI containers where children\n * should not sit flush against the outer edge. A plain [`Container`](/api/core/classes/Container) fits\n * its parent's allocation with zero insets to keep structural regions\n * pixel-predictable; `Panel` opts into the visual breathing room that grouped\n * content layouts typically want.\n *\n * `Panel` also exposes `setAutoScroll` to opt the container into native\n * browser scrolling when its children overflow the allocated rect.\n *\n * Pass `flush: true` to opt a panel into zero default insets instead of the\n * 4px default, for rail-style fixed-width strips that must sit flush against\n * their host.\n *\n * @category Core\n */\nclass Panel<TOptions extends PanelOptions = PanelOptions> extends Container<TOptions> {\n\n // `declare` rather than initialiser to dodge the class-field super-cascade\n // trap: a `= \"none\"` initialiser runs *after* super() returns, which\n // overwrites whatever `setAutoScroll(options.autoScroll)` had already\n // written during the super-time cascade. `applyOptions` below always\n // dispatches `setAutoScroll`, so the field gets seeded there.\n declare private _autoScroll: AutoScrollMode;\n declare private _scrollbarGutter: { right: number; bottom: number };\n\n // Scroll-shadow state. `_scrollShadows`, `_shadowOverlay` and `_shadowScrollHandler`\n // are written by `setScrollShadows` / `setAutoScroll` during the super-time\n // options cascade, so they are `declare`d (no initialiser) and seeded in\n // `applyOptions` to dodge the class-field super-cascade trap — an\n // initialiser would run after super() and clobber the seeded value.\n declare private _scrollShadows: boolean;\n declare private _shadowOverlay: Handle | null;\n declare private _shadowScrollHandler: (() => void) | null; // cached bound scroll handler — wired once\n\n // Runtime-only: never touched during the super cascade (the overlay only\n // exists post-render), so a plain initialiser is safe here.\n private _shadowOverlayStyle: InlineStyle = new InlineStyle();\n // Read during the super-time options cascade (setAutoScroll → doLayout →\n // scheduleGutterSettleOnShrink → showsScrollAffordance inspects the edges),\n // so `declare`d and seeded in `applyOptions` to dodge the class-field\n // super-cascade trap, exactly like `_scrollbarGutter`.\n declare private _shadowEdges: ScrollShadowEdges;\n // Child count observed on the previous layout pass, so a shrink (removed\n // children) can force one follow-up gutter/shadow re-measure. See\n // `scheduleGutterSettleOnShrink`.\n private _lastChildCount: number = 0;\n\n // Content preferred extent observed on the previous layout pass while a\n // scroll affordance was showing, so a shrink that happens inside a nested\n // descendant (whose removal leaves this panel's own child count unchanged)\n // still forces the follow-up re-measure. See `scheduleGutterSettleOnShrink`.\n // `declare`d + seeded in `applyOptions` for the same super-cascade reason.\n declare private _lastContentExtent: { width: number; height: number };\n\n // Overlay-scrollbar state. `_scrollbarStyle` is written by\n // `setScrollbarStyle` during the super-time options cascade, and\n // `_overlayScrollElement` / `_scrollbarV` / `_scrollbarH` / `_overlayScrollHandler`\n // are read (for the teardown guard) by the setter's install/refresh path\n // it triggers — so all five are `declare`d and seeded in `applyOptions`\n // for the same class-field super-cascade reason as the scroll-shadow\n // fields above.\n declare private _scrollbarStyle: ScrollbarStyle;\n declare private _overlayScrollElement: Handle | null; // raw inner scroll div (bars are its siblings)\n declare private _scrollbarV: Scrollbar | null;\n declare private _scrollbarH: Scrollbar | null;\n declare private _overlayScrollHandler: (() => void) | null; // native \"scroll\" -> sync\n\n // Runtime-only: never touched during the super cascade (the inner scroll\n // element only exists post-render), so a plain initialiser is safe here —\n // mirrors `_shadowOverlayStyle`.\n private _overlayScrollStyle: InlineStyle = new InlineStyle();\n\n // Bound scroll-forwarders wired to each overlay Scrollbar's \"scroll\"\n // event. Named class fields (per ARCHITECTURE.md *Listeners must\n // reference a named function*) so they are stable, removable references.\n private _onOverlayScrollV = (position: number): void => { this.setScrollTop(position); };\n private _onOverlayScrollH = (position: number): void => { this.setScrollLeft(position); };\n\n /**\n * Creates a panel with 4-pixel insets on all sides by default.\n *\n * @param options - Optional. Construction-time options applied to the panel.\n * `options.tag` overrides the default `\"div\"` tag for subclasses that need\n * a different element (e.g. `\"header\"`, `\"section\"`). `options.insets`\n * overrides the default `(4, 4, 4, 4)` perimeter. `options.flush` zeroes\n * that default instead (a caller-supplied `insets` still wins).\n */\n constructor(options?: TOptions, subclassDefaults?: Partial<TOptions>) {\n // `flush` seeds a zero-inset default; a caller-supplied `insets` still\n // wins because Component.applyOptions dispatches setInsets only when\n // options.insets is defined, overriding whatever default we pick here.\n const flushDefault: Partial<TOptions> =\n options?.flush ? ({ insets: new Insets(0, 0, 0, 0) } as Partial<TOptions>) : {};\n\n super(\n options,\n { ..._defaultPanelOptions, ...(subclassDefaults ?? {}), ...flushDefault } as Partial<TOptions>,\n );\n }\n\n /**\n * Dispatches `Panel`-specific options after delegating the inherited\n * {@link Component} options bag to `super`.\n *\n * @param options - The options bag whose fields populate this panel.\n *\n * @returns This panel, for method chaining.\n */\n protected applyOptions(options: TOptions): this {\n super.applyOptions(options);\n\n // Seed the scrollbar gutter cache before `setAutoScroll` — the latter\n // reads `_scrollbarGutter` to decide whether to clear it on a\n // `\"none\"` transition, and the `declare`d field would otherwise be\n // undefined at first dispatch.\n this.setScrollbarGutter(0, 0);\n\n // Seed the shadow-edge and content-extent caches for the same reason:\n // `setAutoScroll` below triggers a `doLayout` whose\n // `scheduleGutterSettleOnShrink` reads both, and their `declare`d fields\n // would otherwise be undefined during this super-time cascade.\n this._shadowEdges = { top: 0, bottom: 0, left: 0, right: 0 };\n this._lastContentExtent = { width: 0, height: 0 };\n\n // Always dispatch `setAutoScroll` — the fallback is the class\n // default from `_defaultPanelOptions`. Routing through the setter\n // (even for the default) keeps the `declare`d backing field\n // initialised and dodges the class-field super-cascade trap that\n // would bite a `= \"none\"` initialiser.\n this.setAutoScroll(options.autoScroll ?? this.getAutoScroll());\n\n // Seed the `declare`d overlay/handler fields before `setScrollShadows`\n // dispatches — the setter's teardown branch reads them, and the\n // `declare` leaves them `undefined` until first written.\n this._shadowOverlay = null;\n this._shadowScrollHandler = null;\n\n // Always dispatch so the backing field is seeded through the setter,\n // mirroring the `setAutoScroll` cascade above; the fallback is the\n // class default from `_defaultPanelOptions`.\n this.setScrollShadows(options.scrollShadows ?? this.getScrollShadows());\n\n // Seed the `declare`d overlay fields before `setScrollbarStyle`\n // dispatches — its refresh path (via `refreshOverlayScrollbars` ->\n // `removeOverlayScrollbars`) reads them, and the `declare` leaves\n // them `undefined` until first written.\n this._overlayScrollElement = null;\n this._scrollbarV = null;\n this._scrollbarH = null;\n this._overlayScrollHandler = null;\n\n // Always dispatch so the backing field is seeded through the setter,\n // mirroring the `setAutoScroll` / `setScrollShadows` cascades above;\n // the fallback is the class default from `_defaultPanelOptions`. Must\n // run after `setAutoScroll` — the install path this triggers reads\n // `_autoScroll`.\n this.setScrollbarStyle(options.scrollbarStyle ?? this.getScrollbarStyle());\n\n return this;\n }\n\n /**\n * Selects the panel's native scroll behaviour. Translates `mode` to\n * per-axis `overflow` writes via [`Component.setOverflowX`](/api/core/classes/Component#setoverflowx) /\n * [`Component.setOverflowY`](/api/core/classes/Component#setoverflowy).\n *\n * @param mode - The {@link AutoScrollMode} to apply.\n *\n * @returns This panel, for method chaining.\n *\n * @remarks Children render at their preferred size when `mode !== \"none\"`\n * — the panel no longer clips them to its allocated rect.\n *\n * Whenever a scrollbar becomes visible, `doLayout` measures the gutter\n * and shrinks the panel's reported inner size by that amount so the next\n * layout pass keeps children inside the visible content area (preventing\n * the classic V→H cascade where a right-anchored child gets exposed\n * behind a freshly-shown V scrollbar and triggers an H one).\n *\n * Do not combine with a [`Scrollbar`](/api/component/container/classes/Scrollbar) overlay or a\n * component (e.g. [`Table`](/api/component/table/classes/Table)) that already manages its own scroll\n * state — stacking native overflow on top of the custom scrollbar would\n * produce two scrollbars.\n */\n setAutoScroll(mode: AutoScrollMode): this {\n this._autoScroll = mode;\n\n switch (mode) {\n case \"none\":\n this.setOverflowX(\"hidden\").setOverflowY(\"hidden\");\n break;\n case \"auto\":\n this.setOverflowX(\"auto\").setOverflowY(\"auto\");\n break;\n case \"x\":\n this.setOverflowX(\"auto\").setOverflowY(\"hidden\");\n break;\n case \"y\":\n this.setOverflowX(\"hidden\").setOverflowY(\"auto\");\n break;\n case \"both\":\n this.setOverflowX(\"scroll\").setOverflowY(\"scroll\");\n break;\n }\n\n // Mode switched — drop any cached gutter from the previous mode so\n // the next `doLayout` re-measures against the new overflow setting.\n // (\"none\" never has a gutter; the other modes recompute below.)\n if (mode === \"none\" && (this._scrollbarGutter.right !== 0 || this._scrollbarGutter.bottom !== 0)) {\n this.setScrollbarGutter(0, 0);\n }\n\n // Forward the per-axis \"let children overflow the host\" decision to\n // the layout manager. Each manager honours these flags from its own\n // `doLayout` so trailing children land past `innerSize` when their\n // combined minSize exceeds the host's allocated rect, producing the\n // scrollbar the CSS `overflow: auto` above is waiting for.\n const axes = this.scrollableAxes();\n\n this.getLayoutManager()?.setOverflowing(axes.x, axes.y);\n\n // Re-evaluate the overlay scrollbar for the new mode FIRST: a transition\n // into `\"none\"` tears it (and the inner scroll element) down, a\n // transition into a scrolling mode installs it (when\n // `scrollbarStyle === \"overlay\"`). Must precede `refreshScrollShadows`\n // so `getScrollElement()` already resolves to the inner element when the\n // shadow refresh reads its scroll offsets. No-op before the element\n // exists (creation is deferred to `init`).\n this.refreshOverlayScrollbars();\n\n // Then re-evaluate the shadows for the new mode against the (now\n // correct) scroll element.\n this.refreshScrollShadows();\n\n return this;\n }\n\n /**\n * Re-applies the cached `autoScroll` mode to the new layout manager so\n * swapping managers preserves scroll behaviour. The base `setLayoutManager`\n * does the attach work; this override only forwards the overflow flags.\n *\n * @param layoutManager - The new LayoutManager to use for this panel.\n *\n * @returns This panel, for method chaining.\n */\n setLayoutManager(layoutManager: LayoutManager): this {\n super.setLayoutManager(layoutManager);\n this.setAutoScroll(this._autoScroll);\n\n return this;\n }\n\n /**\n * Returns the panel's current scroll mode.\n *\n * @returns The cached {@link AutoScrollMode}, or the class default when never set.\n */\n getAutoScroll(): AutoScrollMode {\n return this._autoScroll ?? this._defaultOptions.autoScroll!;\n }\n\n /**\n * Resets the panel's scroll mode to `\"none\"`, restoring the inherited\n * `overflow: hidden` clipping behaviour.\n *\n * @returns This panel, for method chaining.\n */\n clearAutoScroll(): this {\n return this.setAutoScroll(\"none\");\n }\n\n /**\n * Enables or disables the position-aware edge shadows on a scrolling\n * panel. When enabled (the default), each side that can still be scrolled\n * toward fades its content into the viewport border; the shadows are\n * suppressed entirely while `autoScroll === \"none\"` or when content does\n * not overflow.\n *\n * @param enabled - `true` to paint the edge shadows, `false` to suppress them.\n *\n * @returns This panel, for method chaining.\n */\n setScrollShadows(enabled: boolean): this {\n this._scrollShadows = enabled;\n\n this.refreshScrollShadows();\n\n return this;\n }\n\n /**\n * Returns whether the panel's scroll edge shadows are enabled.\n *\n * @returns The cached `scrollShadows` flag, or the class default when never set.\n */\n getScrollShadows(): boolean {\n return this._scrollShadows ?? this._defaultOptions.scrollShadows!;\n }\n\n /**\n * Selects the scrollbar rendering for this panel — the overlay default\n * (native scroll, hidden native bar, two synced `Scrollbar` widgets) or\n * `\"native\"` to keep the OS scrollbar. Installs or tears down the overlay\n * immediately when the element already exists; a no-op before render\n * beyond caching the value (the first install happens in `init`).\n *\n * @param style - The {@link ScrollbarStyle} to apply.\n *\n * @returns This panel, for method chaining.\n */\n setScrollbarStyle(style: ScrollbarStyle): this {\n this._scrollbarStyle = style;\n\n this.refreshOverlayScrollbars();\n\n // Re-home the shadow metric source: an overlay<->native toggle changes\n // which element `getScrollElement()` resolves to (inner element vs panel\n // element), so the shadows must re-read from the new scroller.\n this.refreshScrollShadows();\n\n return this;\n }\n\n /**\n * Returns the panel's current scrollbar style.\n *\n * @returns The cached {@link ScrollbarStyle}, or the class default when never set.\n */\n getScrollbarStyle(): ScrollbarStyle {\n return this._scrollbarStyle ?? this._defaultOptions.scrollbarStyle!;\n }\n\n /**\n * Routes every scroll read/write, the child host, and the content frame to\n * the inner scroll element while overlay mode is installed, and to the panel\n * element otherwise (native mode, `autoScroll: \"none\"`, pre-render). This is\n * the single seam that lets the overlay restructure move the actual scroller\n * inward without each scroll-plumbing call site knowing about it.\n *\n * @returns The inner scroll element in overlay mode, else the panel element.\n */\n protected getScrollElement(): Handle | undefined {\n return this._overlayScrollElement ?? this.getElement();\n }\n\n /**\n * Returns the panel's usable inner size with the currently-reserved\n * scrollbar gutter subtracted from each axis. Layout managers read this\n * to lay out children inside the post-gutter content area when a native\n * scrollbar is visible, instead of letting them fill the full rect and\n * spill behind (or be clipped by) the scrollbar.\n *\n * @returns The inner size minus the active scrollbar gutter, or null\n * when the element is not yet in the DOM (matches the base\n * `Component.getInnerSize` contract).\n */\n getInnerSize(): { width: number, height: number } | null {\n const size = super.getInnerSize();\n if (!size) {\n return null;\n }\n\n return {\n width: size.width - this._scrollbarGutter.right,\n height: size.height - this._scrollbarGutter.bottom,\n };\n }\n\n /**\n * Lays out children, then measures the post-layout scrollbar visibility\n * and, when it has changed since the last pass, caches the new gutter\n * and schedules a follow-up layout so children land inside the new\n * post-gutter content area. The follow-up is the \"one-frame reflow\"\n * documented on {@link AutoScrollMode}.\n *\n * @returns This panel, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n // Flush queued inline-style writes (own size in particular) before\n // reading scrollbar geometry: `LayoutManager.commitBounds` runs us\n // with `autoCommitStyle === false`, so the new width/height\n // `setSize` queued during the parent's layout pass haven't reached\n // the DOM yet — `scrollHeight` / `clientHeight` would otherwise\n // report the previous frame's dimensions and `measureScrollbarGutter`\n // wouldn't see the scrollbar transition.\n this.commitElementStyle();\n\n // Re-size the scroll-shadow overlay against the just-committed geometry\n // before measuring: it is the only in-flow child, so a stale height left\n // over from the previous pass floors `scrollHeight` and fakes an overflow\n // on every pass that shrinks the panel. See `resizeScrollShadowOverlay`.\n this.resizeScrollShadowOverlay();\n this.measureScrollbarGutter();\n\n // Re-pin the overlay and recompute edge state against the freshly\n // committed geometry (content-size or scrollbar-gutter changes can\n // flip which edges overflow). The preceding `commitElementStyle`\n // guarantees the reads see this frame's dimensions.\n this.updateScrollShadows();\n\n this.scheduleGutterSettleOnShrink();\n\n return this;\n }\n\n /**\n * Forces one follow-up layout pass after this panel's content shrinks, so a\n * shrink that brings overflowing content back within the viewport re-clears\n * the reserved scrollbar gutter and scroll shadow.\n *\n * `measureScrollbarGutter` only reschedules a pass when the gutter *value*\n * it reads changes. When content is removed, the overflow→fit transition\n * often has not settled on the pass that runs immediately after the\n * removal — the DOM `scrollHeight` still reads its old (overflowing) value,\n * so both the gutter and the shadow measure stale, see no change, and\n * schedule nothing; the stale gutter and shadow then linger until some later\n * unrelated layout (or a scroll event) re-measures. So this schedules one\n * more pass off a signal that *is* accurate at layout time — the content's\n * preferred extent, which drops synchronously when content is removed — and\n * the next frame re-measures against the settled content and clears anything\n * no longer needed.\n *\n * Two shrink signals are used. A direct-child-count drop is the cheap common\n * case. But content can also shrink inside a nested descendant (e.g. rows\n * removed from a grid several levels down), leaving this panel's own child\n * count unchanged; a drop in the panel's preferred extent catches that. The\n * preferred-extent read is gated behind an actually-showing scroll affordance\n * (a reserved gutter or a painted shadow edge) so it costs nothing on the\n * overwhelming majority of layouts, where there is nothing to settle.\n *\n * Bounded and non-looping: it fires only on the pass *after* a shrink (the\n * follow-up pass sees an unchanged count and extent), and never for a\n * `\"none\"` panel, which reserves no gutter and paints no shadow.\n */\n private scheduleGutterSettleOnShrink(): void {\n if (this._autoScroll === \"none\") {\n return;\n }\n\n const count = this.getComponents().length;\n const childShrank = count < this._lastChildCount;\n\n this._lastChildCount = count;\n\n // A shrink inside a nested descendant leaves `count` unchanged, so also\n // watch the preferred extent — but only while a scroll affordance is on\n // screen, since that is the only state a shrink could leave stale.\n let contentShrank = false;\n\n if (this.showsScrollAffordance()) {\n const preferred = this.getPreferredSize();\n const width = preferred ? preferred.width : 0;\n const height = preferred ? preferred.height : 0;\n\n contentShrank = width < this._lastContentExtent.width\n || height < this._lastContentExtent.height;\n\n this._lastContentExtent = { width, height };\n }\n\n if (childShrank || contentShrank) {\n this.scheduleLayout();\n }\n }\n\n /**\n * Whether this panel is currently painting a scroll affordance — a reserved\n * scrollbar gutter or any lit shadow edge. Used by\n * {@link Panel.scheduleGutterSettleOnShrink} to decide whether a shrink could\n * have left a stale gutter/shadow worth re-measuring.\n *\n * @returns `true` when a gutter is reserved or any shadow edge is lit.\n */\n private showsScrollAffordance(): boolean {\n return this._scrollbarGutter.right > 0\n || this._scrollbarGutter.bottom > 0\n || this._shadowEdges.top > 0\n || this._shadowEdges.bottom > 0\n || this._shadowEdges.left > 0\n || this._shadowEdges.right > 0;\n }\n\n /**\n * Initialises the panel element, then installs the scroll-shadow overlay\n * if the panel is a scroll-shadow candidate. Overlay creation is deferred\n * to here (rather than `applyOptions`) because the element only exists\n * once rendered.\n *\n * @param element - Optional. The element to initialise; falls back to the rendered element.\n *\n * @returns This panel, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n // `getElement()` is still undefined inside `init` (the base assigns\n // `_element` only after `render` returns), so hand the resolved\n // element straight to the installer instead of re-reading it.\n const resolved = element ?? this.getElement();\n\n // Install the overlay scrollbars (which create the inner scroll element)\n // BEFORE the scroll shadows, so `getScrollElement()` already resolves to\n // the inner element when `updateScrollShadows` first reads its offsets.\n if (resolved && this._scrollbarStyle === \"overlay\" && this._autoScroll !== \"none\") {\n this.installOverlayScrollbars(resolved);\n this.layoutOverlayScrollbars(resolved);\n }\n\n if (resolved && this._scrollShadows && this._autoScroll !== \"none\") {\n this.installScrollShadows(resolved);\n this.updateScrollShadows(resolved);\n }\n\n return this;\n }\n\n /**\n * Removes the cached scroll listener before the base destructor detaches\n * the element. The overlay is a child of that element, so it is removed\n * with it; only the window-level listener registration needs explicit\n * cleanup.\n */\n protected destructor(): void {\n this.removeScrollShadows();\n this.removeOverlayScrollbars();\n\n super.destructor();\n }\n\n /**\n * Which axes the current `autoScroll` mode lets the user scroll along. The\n * single source of truth behind the layout manager's overflow flags, the\n * gutter measurement, and the shadow edges — all three must agree on what\n * \"scrollable\" means or the panel paints an affordance for an axis that\n * cannot move.\n *\n * @returns A flag per axis; `true` when that axis scrolls under the current mode.\n */\n private scrollableAxes(): { x: boolean; y: boolean } {\n const mode = this._autoScroll;\n\n return {\n x: mode === \"x\" || mode === \"auto\" || mode === \"both\",\n y: mode === \"y\" || mode === \"auto\" || mode === \"both\",\n };\n }\n\n /**\n * Caches the new gutter for each axis. Internal — driven by\n * `measureScrollbarGutter` after a layout pass; consumers can't\n * configure this (it's derived from runtime DOM measurement, not a\n * declarative input), so it stays off the `PanelOptions` bag.\n *\n * @param right - Reserved gutter on the right edge in pixels.\n * @param bottom - Reserved gutter on the bottom edge in pixels.\n */\n private setScrollbarGutter(right: number, bottom: number): void {\n this._scrollbarGutter = { right, bottom };\n }\n\n /**\n * Reads the post-layout scrollbar visibility from the live DOM and\n * updates the cached gutter to match. When the gutter changed,\n * schedules a follow-up layout pass so children re-flow inside the new\n * inner area. No-op for `mode === \"none\"` and on browsers whose\n * scrollbars don't reserve space (e.g. macOS overlay scrollbars, where\n * the native width measures as 0 — the cascade can't happen there).\n */\n private measureScrollbarGutter(): void {\n if (this._autoScroll === \"none\") {\n return;\n }\n\n if (this._scrollbarStyle === \"overlay\") {\n this.layoutOverlayScrollbars();\n\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n const trackW = DOM.source.getScrollBarWidth();\n if (trackW === 0) {\n return;\n }\n\n // `\"both\"` forces both scrollbars on (`overflow: scroll` on both\n // axes), so the gutter is always reserved on both sides. The\n // single-axis modes only show their one bar, and `\"auto\"` shows\n // each independently; reading `scrollHeight > clientHeight` (and\n // its X-axis twin) detects whichever bars the browser has chosen\n // to render this frame, which matches the visible-only criterion.\n let vReserved: boolean;\n let hReserved: boolean;\n\n if (this._autoScroll === \"both\") {\n vReserved = true;\n hReserved = true;\n } else {\n const axes = this.scrollableAxes();\n const metrics = DOM.source.getScrollMetrics(el);\n\n vReserved = axes.y && metrics.scrollHeight > metrics.clientHeight;\n hReserved = axes.x && metrics.scrollWidth > metrics.clientWidth;\n }\n\n const newRight = vReserved ? trackW : 0;\n const newBottom = hReserved ? trackW : 0;\n\n if (newRight === this._scrollbarGutter.right && newBottom === this._scrollbarGutter.bottom) {\n return;\n }\n\n this.setScrollbarGutter(newRight, newBottom);\n this.scheduleLayout();\n }\n\n /**\n * Brings the scroll-shadow overlay into the state implied by the current\n * `scrollShadows` / `autoScroll` settings: torn down when disabled or\n * non-scrolling, otherwise installed and refreshed. No-op before the\n * element exists — `init` performs the first install once rendered.\n */\n private refreshScrollShadows(): void {\n if (!this._scrollShadows || this._autoScroll === \"none\") {\n this.removeScrollShadows();\n\n return;\n }\n\n const element = this.getElement();\n if (!element) {\n return;\n }\n\n this.installScrollShadows(element);\n this.updateScrollShadows(element);\n }\n\n /**\n * Creates the overlay and wires the scroll listener if they are not\n * already present. Idempotent: the `_shadowOverlay` / `_shadowScrollHandler` guards\n * keep it from stacking a duplicate overlay or listener across repeated\n * calls (the \"wire once\" rule).\n *\n * @param element - The rendered panel element to append the overlay to.\n */\n private installScrollShadows(element: Handle): void {\n if (!this._shadowOverlay) {\n this.createScrollShadowOverlay(element);\n }\n\n if (!this._shadowScrollHandler) {\n const handler = (): void => {\n this.updateScrollShadows();\n };\n\n this._shadowScrollHandler = handler;\n // Subtree, not exact-target: in overlay mode the scroll fires on the\n // id-less inner element, which only reaches the panel's id-keyed\n // listener bag by climbing the subtree. Native mode's scroll fires on\n // the panel element itself, which the subtree walk also matches.\n Event.addSubtreeListener(this, \"scroll\", handler);\n }\n }\n\n /**\n * Builds the non-interactive shadow overlay: an id-less, listener-free\n * presentational sheath (mirroring the clip/content frames) carrying four\n * blurred inset edge shadows, one per side. Each shadow's colour is a local\n * custom property defaulting to `transparent`, so the per-scroll path only\n * flips a property to light an edge rather than rebuilding the shadow.\n *\n * @param element - The panel element the overlay is appended to.\n */\n private createScrollShadowOverlay(element: Handle): void {\n const overlay = DOM.sink.createElement(\"div\");\n\n this._shadowOverlayStyle.attach(overlay);\n this._shadowOverlayStyle.setMany({\n // `sticky` pins the overlay to the scroll-port viewport on the\n // compositor: the browser keeps it at the `top: 0` / `left: 0`\n // edge as the content scrolls underneath, so it tracks the\n // viewport without any per-scroll JS write (no transform repin, no\n // main-thread flicker). It also does not extend the scrollable\n // region, since it stays inside the viewport box.\n position: \"sticky\",\n left: \"0px\",\n top: \"0px\",\n pointerEvents: \"none\",\n // Paint above the content frame: `setContentFrame` re-appends that\n // frame as the element's last child during layout, so DOM order\n // alone would let it cover an overlay appended here at `init`.\n zIndex: \"1\",\n // Four blurred inset shadows — one per edge — each gated by a local\n // custom property defaulting to `transparent` (flipped to the theme\n // colour by `setShadowEdge`). See `scrollShadowBoxShadow`.\n boxShadow: scrollShadowBoxShadow(),\n });\n\n DOM.sink.appendChild(element, overlay);\n // Track the panel-owned overlay so a discarded panel releases it on GC\n // even if removeScrollShadows never runs; untracked there on eager removal.\n this.trackHandle(overlay);\n this._shadowOverlay = overlay;\n }\n\n /**\n * Tears the overlay down and unwires the scroll listener, resetting the\n * cached edge state. Each step is guarded so this is safe to call before\n * the overlay was ever created (e.g. during the construction cascade).\n */\n private removeScrollShadows(): void {\n if (this._shadowScrollHandler) {\n Event.removeSubtreeListener(this, \"scroll\", this._shadowScrollHandler);\n this._shadowScrollHandler = null;\n }\n\n if (this._shadowOverlay) {\n DOM.sink.removeElement(this._shadowOverlay);\n this.untrackHandle(this._shadowOverlay);\n DOM.sink.release(this._shadowOverlay);\n this._shadowOverlay = null;\n\n // The buffer was bound to the now-removed overlay; a fresh one is\n // needed for any future re-install (mirrors `disposeFrame`).\n this._shadowOverlayStyle = new InlineStyle();\n }\n\n this._shadowEdges = { top: 0, bottom: 0, left: 0, right: 0 };\n }\n\n /**\n * Re-asserts the shadow overlay's size against the live viewport box (a\n * no-op write unless it changed).\n *\n * The overlay is the panel's only in-flow child — every child *component*\n * is absolutely positioned — so its height alone floors the element's\n * `scrollHeight`. That makes its size load-bearing for\n * {@link Panel.measureScrollbarGutter}, not merely cosmetic: while it\n * carries the previous pass's height, a panel that just shrank reads\n * `scrollHeight` (the stale, taller overlay) above `clientHeight` (the\n * freshly committed height) and reserves a scrollbar gutter for an overflow\n * that does not exist. Hence `doLayout` re-sizes the overlay *before* it\n * measures, which is what keeps the \"stays inside the viewport box, so it\n * never extends the scrollable region\" invariant true on the shrinking pass\n * as well as the settled one.\n *\n * @param element - Optional. The panel element; falls back to the rendered\n * element. Passed explicitly from `init`, where `getElement` is not yet\n * populated.\n */\n private resizeScrollShadowOverlay(element?: Handle): void {\n const el = element ?? this.getElement();\n if (!el || !this._shadowOverlay) {\n return;\n }\n\n const { clientWidth, clientHeight } = DOM.source.getScrollMetrics(el);\n\n // In overlay-scrollbar mode the native bar is hidden, so clientWidth /\n // clientHeight still span the strip the overlay Scrollbar paints on.\n // Inset the shadow overlay by the reserved overlay gutter so each edge\n // shadow lands just inside its bar — aligned with the content viewport\n // getInnerSize exposes — instead of bleeding under the translucent bar\n // track (which reads as the shadow painting on top of the scrollbar).\n // Native mode needs no inset: clientWidth / clientHeight already exclude\n // the OS scrollbar, so the gutter there is physical, not overlaid.\n const rightInset = this._scrollbarStyle === \"overlay\" ? this._scrollbarGutter.right : 0;\n const bottomInset = this._scrollbarStyle === \"overlay\" ? this._scrollbarGutter.bottom : 0;\n\n // Size the overlay to the viewport box; `position: sticky` keeps it\n // pinned there as the content scrolls, so no transform is needed.\n this._shadowOverlayStyle.setMany({\n width: (clientWidth - rightInset) + \"px\",\n height: (clientHeight - bottomInset) + \"px\",\n });\n }\n\n /**\n * Sizes the overlay to the live viewport and recomputes each edge's shadow\n * strength from its distance to that extreme. `sticky` handles the\n * positioning, so the per-scroll path only re-asserts the viewport size (a\n * no-op write unless it changed) and rescales the edges — no positioning\n * work runs here.\n *\n * @param element - Optional. The panel element; falls back to the rendered\n * element. Passed explicitly from `init`, where `getElement` is not yet\n * populated.\n */\n private updateScrollShadows(element?: Handle): void {\n const el = element ?? this.getElement();\n if (!el || !this._shadowOverlay) {\n return;\n }\n\n // Read the scroll offsets and extents from the element that actually\n // scrolls — the inner scroller in overlay mode (the panel element's own\n // offsets are always 0 there), the panel element otherwise. The overlay\n // is still sized against, and pinned to, the panel element (`el`).\n const { scrollTop, scrollLeft, scrollWidth, scrollHeight, clientWidth, clientHeight } =\n DOM.source.getScrollMetrics(this.getScrollElement() ?? el);\n\n this.resizeScrollShadowOverlay(el);\n\n const maxTop = scrollHeight - clientHeight;\n const maxLeft = scrollWidth - clientWidth;\n\n // Ramp an edge in by its distance past that extreme (see `scrollShadowRamp`).\n const ramp = scrollShadowRamp;\n\n // A shadow says \"there is more content this way, scroll to reach it\", so\n // only an axis the user can actually scroll may light its edges. A\n // clipped axis still reports overflow through `scrollWidth` /\n // `scrollHeight` — an `autoScroll: \"y\"` panel whose content is a few px\n // wider than its post-gutter width reads a non-zero `maxLeft` — and\n // ramping that would paint a right-edge fade promising content no\n // gesture can reveal.\n const axes = this.scrollableAxes();\n\n this.setShadowEdge(\"top\", \"--ts-ss-top\", axes.y ? ramp(scrollTop) : 0);\n this.setShadowEdge(\"bottom\", \"--ts-ss-bottom\", axes.y ? ramp(maxTop - scrollTop) : 0);\n this.setShadowEdge(\"left\", \"--ts-ss-left\", axes.x ? ramp(scrollLeft) : 0);\n this.setShadowEdge(\"right\", \"--ts-ss-right\", axes.x ? ramp(maxLeft - scrollLeft): 0);\n }\n\n /**\n * Sets a single edge's shadow strength by scaling the theme shadow colour\n * toward transparent. Strength is quantised to a whole percent so an\n * in-ramp scroll only repaints when the visible strength actually changes\n * (and never sub-pixel-thrashes); at zero the property is unset so the\n * `box-shadow` layer falls back to `transparent`.\n *\n * @param edge - The edge whose cached strength this updates.\n * @param property - The overlay custom property backing that edge's shadow.\n * @param strength - The target strength in the range 0–1.\n */\n private setShadowEdge(edge: keyof ScrollShadowEdges, property: string, strength: number): void {\n const percent = Math.round(strength * 100); // quantise: 0–1 → 0–100%\n\n if (this._shadowEdges[edge] === percent) {\n return;\n }\n\n this._shadowEdges[edge] = percent;\n this._shadowOverlayStyle.set(property, scrollShadowEdgeValue(percent));\n }\n\n /**\n * Brings the overlay scrollbar into the state implied by the current\n * `scrollbarStyle` / `autoScroll` settings: torn down when native or\n * non-scrolling, otherwise installed and laid out. No-op before the\n * element exists — `init` performs the first install once rendered.\n */\n private refreshOverlayScrollbars(): void {\n if (this._scrollbarStyle !== \"overlay\" || this._autoScroll === \"none\") {\n this.removeOverlayScrollbars();\n\n return;\n }\n\n const element = this.getElement();\n if (!element) {\n return;\n }\n\n this.installOverlayScrollbars(element);\n this.layoutOverlayScrollbars(element);\n }\n\n /**\n * Creates the inner scroll element (if absent), appends the two `Scrollbar`\n * widgets as its siblings on the panel element, and hides the native bar.\n * Idempotent: the element/bars/listener are each guarded by a `null` check,\n * so repeated calls neither stack duplicates nor re-hide an already-hidden\n * bar — but the inner element's per-axis overflow IS re-asserted on every\n * call so a runtime `setAutoScroll` mode-to-mode change (which keeps the\n * existing inner element) updates which axes scroll.\n *\n * @param element - The rendered panel element to append the inner scroller\n * and bars into.\n */\n private installOverlayScrollbars(element: Handle): void {\n if (!this._overlayScrollElement) {\n // The native scroll happens on this inner element, physically inset\n // by the reserved track (see `layoutOverlayScrollbars`) so content\n // clips at the inner viewport edge and can never scroll under a bar.\n // The bars are its SIBLINGS on the panel element (below), outside\n // this element's overflow clip — the two-element structure\n // `VirtualScroller` uses. The panel element keeps its own\n // `overflow: auto` (inert: the inner element is absolute / out of\n // flow and always fits, so the panel element never scrolls).\n ensureOverlayScrollerClassRule();\n\n const inner = DOM.sink.createElement(\"div\");\n\n // `width/height: 100%` fills the panel element's padding box (its\n // containing block, since every Component is positioned) so the\n // first overflow read sees the full viewport; `layoutOverlayScrollbars`\n // then overrides with the explicit post-gutter px size each pass.\n this._overlayScrollStyle.attach(inner);\n this._overlayScrollStyle.setMany({\n position: \"absolute\",\n left: \"0px\",\n top: \"0px\",\n width: \"100%\",\n height: \"100%\",\n });\n\n // Hide the inner element's own native bar via the shared class rule\n // (it has no `#id`, so the panel element's per-`#id` path can't reach it).\n DOM.sink.apply(inner, { addClass: [OVERLAY_SCROLLER_CLASS] });\n DOM.sink.appendChild(element, inner);\n\n // Shift the existing children (or the active content frame) onto the\n // inner scroller, preserving the scroll offset across the host swap.\n this.reparentContent(element, inner);\n\n this.trackHandle(inner);\n this._overlayScrollElement = inner;\n }\n\n // Re-assert the inner element's per-axis overflow every call: on a\n // runtime `setAutoScroll` mode-to-mode change the inner element already\n // exists (no teardown), so the guard above is skipped — but the newly\n // scrollable axis must flip from `hidden` to `auto` (and vice versa) or\n // native wheel/keyboard scroll and the matching bar would be inert.\n const axes = this.scrollableAxes();\n this._overlayScrollStyle.setMany({\n overflowX: axes.x ? \"auto\" : \"hidden\",\n overflowY: axes.y ? \"auto\" : \"hidden\",\n });\n\n if (!this._scrollbarV) {\n this._scrollbarV = new Scrollbar(\"vertical\");\n this._scrollbarV.setZIndex(2); // above the shadow overlay's z-index: 1\n DOM.sink.appendChild(element, this._scrollbarV.getElement(true)!);\n this._scrollbarV.on(\"scroll\", this._onOverlayScrollV);\n }\n\n if (!this._scrollbarH) {\n this._scrollbarH = new Scrollbar(\"horizontal\");\n this._scrollbarH.setZIndex(2);\n DOM.sink.appendChild(element, this._scrollbarH.getElement(true)!);\n this._scrollbarH.on(\"scroll\", this._onOverlayScrollH);\n }\n\n if (!this._overlayScrollHandler) {\n // Subtree, not exact-target: the inner scroll element is a raw,\n // id-less div, so its native \"scroll\" only reaches the panel's\n // id-keyed listener bag by climbing the subtree to the panel element\n // (the same mechanism the wheel listener uses). The handler reads\n // `getScrollElement()`, not the event target, so a nested\n // descendant's scroll only triggers a harmless re-read.\n const handler = (): void => {\n this.syncOverlayScrollbars();\n };\n\n this._overlayScrollHandler = handler;\n Event.addSubtreeListener(this, \"scroll\", handler);\n }\n\n this.setNativeScrollbarHidden(true);\n }\n\n /**\n * Tears the overlay scrollbar down: unwires the native scroll listener,\n * disposes both bars, removes the sticky host, un-hides the native bar,\n * and clears any reserved gutter. Each step is guarded so this is safe to\n * call before the overlay was ever created (e.g. during the construction\n * cascade). Disposing (rather than only detaching) is required because\n * each bar is appended straight onto the panel element with a raw\n * `DOM.sink.appendChild` and held only in `_scrollbarV` / `_scrollbarH` —\n * never registered via `addComponent` — so `Component.destructor()`'s\n * child recursion can never reach it to reclaim its per-instance\n * stylesheet rule.\n */\n private removeOverlayScrollbars(): void {\n if (this._overlayScrollHandler) {\n Event.removeSubtreeListener(this, \"scroll\", this._overlayScrollHandler);\n this._overlayScrollHandler = null;\n }\n\n if (this._scrollbarV) {\n this._scrollbarV.off(\"scroll\", this._onOverlayScrollV);\n this._scrollbarV.dispose();\n this._scrollbarV = null;\n }\n\n if (this._scrollbarH) {\n this._scrollbarH.off(\"scroll\", this._onOverlayScrollH);\n this._scrollbarH.dispose();\n this._scrollbarH = null;\n }\n\n if (this._overlayScrollElement) {\n // Re-parent the children (or content frame) back onto the panel\n // element — which resumes scrolling in native mode — before the\n // inner element is destroyed, preserving the scroll offset.\n const element = this.getElement();\n\n if (element) {\n this.reparentContent(this._overlayScrollElement, element);\n }\n\n DOM.sink.removeElement(this._overlayScrollElement);\n this.untrackHandle(this._overlayScrollElement);\n DOM.sink.release(this._overlayScrollElement);\n this._overlayScrollElement = null;\n\n // The buffer was bound to the now-removed inner element; a fresh one\n // is needed for any future re-install (mirrors `_shadowOverlayStyle`\n // in `removeScrollShadows`).\n this._overlayScrollStyle = new InlineStyle();\n }\n\n this.setNativeScrollbarHidden(false);\n\n // Unconditional (rather than gated on the previous value, as\n // `setAutoScroll`'s native-path gutter-clear is): `setLayoutManager`\n // can re-enter this teardown from inside `Component.applyOptions`'s\n // own `layoutManager` option handling, before Panel's `applyOptions`\n // body has seeded `_scrollbarGutter` at all. The assignment itself is\n // a cheap plain-object write, so skipping the read-before-write\n // avoids that ordering hazard for free.\n this.setScrollbarGutter(0, 0);\n }\n\n /**\n * Hides or restores the native scrollbar through the framework's deferred\n * style seams — a `scrollbar-width: none` write on the component's own\n * `#id` rule (Firefox / Chromium >= 121) plus a `#id::-webkit-scrollbar {\n * display: none }` state rule (WebKit / older Blink).\n *\n * @param hidden - `true` to hide the native bar, `false` to restore it.\n */\n private setNativeScrollbarHidden(hidden: boolean): void {\n this.setElementCSSRule(\"scrollbarWidth\", hidden ? \"none\" : null);\n this.createStyleRule(\"::-webkit-scrollbar\").set(\"display\", hidden ? \"none\" : null);\n }\n\n /**\n * Sizes the inner scroll element to the available viewport minus the track\n * on each axis whose perpendicular bar is visible, positions both bars in\n * the reserved band at the trailing edges, pushes their metrics, and\n * reserves the matching gutter — rescheduling a layout pass when it changed.\n * Called from `init` (first install) and from `measureScrollbarGutter`'s\n * overlay branch (every `doLayout` pass).\n *\n * The dual read is the crux: the **available viewport** comes from the panel\n * element (which never scrolls, so its client box is the full viewport),\n * while content extent, offsets, and the current inner client box come from\n * the **inner scroll element**. Physically insetting the inner element is\n * what makes overflowing content clip before the bar band instead of\n * scrolling under it.\n *\n * @param element - Optional. The panel element; falls back to the rendered\n * element. Passed explicitly from `init`, where `getElement` is not yet\n * populated.\n */\n private layoutOverlayScrollbars(element?: Handle): void {\n const panelEl = element ?? this.getElement();\n const innerEl = this._overlayScrollElement;\n if (!panelEl || !innerEl || !this._scrollbarV || !this._scrollbarH) {\n return;\n }\n\n const trackW = this._scrollbarV.getTrackWidth();\n\n // Available viewport: the panel element's client box. The parent layout\n // sizes the panel element (and `doLayout` flushes it via\n // `commitElementStyle`) before this runs, so `avail` is always current —\n // it never lags a resize.\n const avail = DOM.source.getScrollMetrics(panelEl);\n const availW = avail.clientWidth;\n const availH = avail.clientHeight;\n\n const curRight = this._scrollbarGutter.right;\n const curBottom = this._scrollbarGutter.bottom;\n\n // Size the inner scroller to the CURRENT viewport (minus the currently\n // reserved gutter) BEFORE reading its metrics, so its `clientWidth`/\n // `clientHeight` — and thus `scrollWidth`/`scrollHeight`, which the\n // browser floors at the client box — reflect THIS frame's viewport. The\n // inner element otherwise carries the previous pass's size, which lags a\n // resize in BOTH directions: on expand the stale-small client box kept a\n // bar the widened viewport no longer needs; on shrink the stale-large\n // client box floored `scrollWidth` so content that now fits still read as\n // overflowing — each flickered a transient bar that could stick. A gutter\n // change below re-sizes and reschedules, converging in one extra pass.\n this._overlayScrollStyle.setMany({\n width: (availW - curRight) + \"px\",\n height: (availH - curBottom) + \"px\",\n });\n\n // Content extent, offsets, and the now-current inner client box.\n const m = DOM.source.getScrollMetrics(innerEl);\n const axes = this.scrollableAxes();\n\n // A bar shows when content exceeds the inner scroller's (now current)\n // viewport. The V<->H dependency — reserving one bar shrinks the other's\n // viewport — settles across passes via the gutter-change reschedule\n // below, the same one-extra-pass convergence the gutter always used.\n const vVisible = axes.y && m.scrollHeight > m.clientHeight;\n const hVisible = axes.x && m.scrollWidth > m.clientWidth;\n\n const innerW = availW - (vVisible ? trackW : 0);\n const innerH = availH - (hVisible ? trackW : 0);\n\n // Re-inset the inner scroller when this pass's gutter differs from the\n // one the pre-read sizing used, so overflowing content clips at the inner\n // viewport edge and can never scroll under a bar.\n if (innerW !== availW - curRight || innerH !== availH - curBottom) {\n this._overlayScrollStyle.setMany({ width: innerW + \"px\", height: innerH + \"px\" });\n }\n\n // Bars occupy the reserved band at the trailing edges, sized to the\n // inner extent so each stops short of the shared corner. setMetrics\n // takes the same reduced viewport (`innerH`/`innerW`) the visibility\n // test used, so a bar's shown/hidden state and the reserved gutter\n // never disagree.\n this._scrollbarV.setX(innerW);\n this._scrollbarV.setY(0);\n this._scrollbarV.setHeight(innerH);\n this._scrollbarV.setMetrics(innerH, m.scrollHeight, m.scrollTop);\n\n this._scrollbarH.setX(0);\n this._scrollbarH.setY(innerH);\n this._scrollbarH.setWidth(innerW);\n this._scrollbarH.setMetrics(innerW, m.scrollWidth, m.scrollLeft);\n\n const newRight = vVisible ? trackW : 0;\n const newBottom = hVisible ? trackW : 0;\n\n if (newRight !== curRight || newBottom !== curBottom) {\n this.setScrollbarGutter(newRight, newBottom);\n this.scheduleLayout();\n }\n }\n\n /**\n * Re-pushes metrics (thumb size/position only) to both overlay bars against\n * the inner scroller's live scroll offset. Called from the native `\"scroll\"`\n * handler — geometry (bar position/size, reserved gutter, inner element\n * size) changes only on layout, so this never repositions or resizes\n * anything and never schedules a layout.\n */\n private syncOverlayScrollbars(): void {\n const innerEl = this._overlayScrollElement;\n if (!innerEl || !this._scrollbarV || !this._scrollbarH) {\n return;\n }\n\n const m = DOM.source.getScrollMetrics(innerEl);\n\n this._scrollbarV.setMetrics(m.clientHeight, m.scrollHeight, m.scrollTop);\n this._scrollbarH.setMetrics(m.clientWidth, m.scrollWidth, m.scrollLeft);\n }\n}\n\nconst PanelCallable = callable(Panel);\ntype PanelCallable<TOptions extends PanelOptions = PanelOptions> = Panel<TOptions>;\nexport {\n Panel as _Panel,\n PanelCallable as Panel\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Panel-Cy1pdlWO.js","names":[],"sources":["../../src/typescript/lib/core/Panel.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Insets } from \"~/primitive/Insets\";\nimport { LayoutManager } from \"~/layout/LayoutManager.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { InlineStyle, StyleRule } from \"~/core/StyleTarget.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { scrollShadowBoxShadow, scrollShadowEdgeValue, scrollShadowRamp } from \"~/core/ScrollShadow.js\";\nimport { Scrollbar } from \"~/component/container/Scrollbar.js\";\n\n/**\n * Per-edge shadow strength for a panel's scroll shadows, cached as a whole\n * percentage (0–100). The per-scroll update only rewrites a custom property\n * when an edge's quantised strength actually changes, so a scroll that doesn't\n * move the visible strength costs nothing here.\n */\ntype ScrollShadowEdges = { top: number; bottom: number; left: number; right: number };\n\n/**\n * Selects the per-axis scroll behaviour for a {@link Panel}.\n *\n * - `\"none\"` — never scroll; oversized children clip (the default).\n * - `\"auto\"` — scrollbars appear on either axis only when content overflows.\n * - `\"x\"` — horizontal scrollbar on overflow; vertical overflow clips.\n * - `\"y\"` — vertical scrollbar on overflow; horizontal overflow clips.\n * - `\"both\"` — both scrollbars are always shown (`overflow: scroll`).\n *\n * @remarks For every value except `\"none\"` the panel measures the native\n * scrollbar gutter after each `doLayout` pass and subtracts it from\n * `getInnerSize` when a scrollbar is actually visible, so layout managers\n * naturally lay out children within the post-gutter content area instead of\n * letting them spill behind the scrollbar (which the browser would otherwise\n * resolve by adding the opposite-axis scrollbar — the classic V→H cascade).\n * A scrollbar transition triggers a one-frame re-layout via `scheduleLayout`.\n *\n * @category Core\n */\nexport type AutoScrollMode = \"none\" | \"auto\" | \"x\" | \"y\" | \"both\";\n\n/**\n * Selects how a scrolling {@link Panel} renders its scrollbar.\n *\n * - `\"overlay\"` — the default. Scrolling stays native (`overflow: auto`), the\n * native scrollbar is hidden visually, and two custom `Scrollbar` widgets\n * are overlaid at the trailing edges, synced to the element's native\n * `scrollTop` / `scrollLeft`. Every native scroll behaviour (keyboard, find,\n * focus-scroll, caret scroll, assistive tech) is preserved.\n * - `\"native\"` — the OS scrollbar renders as usual; the panel reserves the\n * measured native gutter width instead of the fixed overlay track width.\n *\n * Ignored while `autoScroll === \"none\"` (a non-scrolling panel shows neither).\n *\n * @category Core\n */\nexport type ScrollbarStyle = \"native\" | \"overlay\";\n\n/**\n * Construction-time options for {@link Panel}.\n *\n * @remarks `insets` is inherited from {@link ComponentOptions} but defaults to\n * `(4, 4, 4, 4)` for `Panel` (Component itself defaults to zero insets). Pass\n * an explicit `insets` to override.\n *\n * @category Core\n */\nexport interface PanelOptions extends ContainerOptions {\n tag?: string;\n\n /**\n * Construction-time shortcut for [`Panel.setAutoScroll`](/api/core/classes/Panel#setautoscroll). Defaults to\n * `\"none\"` (oversized children clip, matching the inherited `Component`\n * `overflow: hidden` behaviour).\n */\n autoScroll?: AutoScrollMode;\n\n /**\n * When `true` (the default), an `autoScroll` panel paints a fading edge\n * shadow on each side where hidden content can still be scrolled into\n * view — a cue that the content continues past the viewport border rather\n * than ending there. Set `false` to suppress the shadows. Ignored while\n * `autoScroll === \"none\"` (a non-scrolling panel never shows them).\n */\n scrollShadows?: boolean;\n\n /**\n * Selects the scrollbar rendering for an `autoScroll` panel. Defaults to\n * `\"overlay\"` — a scrolling panel hides its native bar and paints synced\n * overlay `Scrollbar` widgets instead. Pass `\"native\"` to opt out and keep\n * the OS scrollbar. Ignored while `autoScroll === \"none\"`.\n */\n scrollbarStyle?: ScrollbarStyle;\n\n /**\n * When `true`, the panel's default content insets are zero instead of the\n * usual `(4, 4, 4, 4)` — the rail-style default for a fixed-width strip\n * (activity rail, narrow Border/VBox region) that must sit flush against\n * its host and keep a constant width. Construction-time only; a\n * caller-supplied `insets` still wins. Defaults to `false`.\n */\n flush?: boolean;\n}\n\n/**\n * User-overridable defaults forwarded to `super` via the options bag. The\n * cascade in `Component`'s constructor dispatches `setInsets` once with the\n * final value, so a caller-supplied `insets` wins over the panel default.\n */\nconst _defaultPanelOptions: Partial<PanelOptions> = {\n tag: \"div\",\n insets: new Insets(4, 4, 4, 4),\n autoScroll: \"none\",\n scrollShadows: true,\n scrollbarStyle: \"overlay\",\n};\n\n/**\n * Class applied to the raw inner scroll element of an overlay-mode panel. The\n * inner element has no `#id` (it is not a `Component`), so its native bar is\n * hidden through a shared class rule rather than the per-`#id`\n * `setElementCSSRule` path the panel element uses — the framework's module-level\n * shared class-rule pattern (as the focus-ring and `Component` class rules use).\n */\nconst OVERLAY_SCROLLER_CLASS = \"PanelOverlayScroller\";\n\n// The shared inner-scroller class rules, retained once registered (mirrors the\n// CollapseButton / header-glyph module-singleton pattern). The array doubles as\n// the idempotency guard.\nlet _scrollerClassRules: StyleRule[] | null = null;\n\n/**\n * Registers, once, the shared class rules that hide the native scrollbar on an\n * overlay panel's raw inner scroll element: `scrollbar-width: none` (Firefox /\n * Chromium >= 121) plus a `::-webkit-scrollbar { display: none }` selector rule\n * (WebKit / older Blink) — the two-write pair the panel element's\n * `setNativeScrollbarHidden` uses, expressed as a shared class instead of a\n * per-`#id` rule.\n */\nfunction ensureOverlayScrollerClassRule(): void {\n if (_scrollerClassRules) {\n return;\n }\n\n _scrollerClassRules = [\n new StyleRule({\n scope: \"class\",\n name: OVERLAY_SCROLLER_CLASS,\n styles: { scrollbarWidth: \"none\" },\n }),\n new StyleRule({\n scope: \"selector\",\n name: `.${OVERLAY_SCROLLER_CLASS}::-webkit-scrollbar`,\n styles: { display: \"none\" },\n }),\n ];\n}\n\n/**\n * A [`Container`](/api/core/classes/Container) subclass that applies a default 4-pixel inset on all sides.\n *\n * Use `Panel` as the base class for grouped UI containers where children\n * should not sit flush against the outer edge. A plain [`Container`](/api/core/classes/Container) fits\n * its parent's allocation with zero insets to keep structural regions\n * pixel-predictable; `Panel` opts into the visual breathing room that grouped\n * content layouts typically want.\n *\n * `Panel` also exposes `setAutoScroll` to opt the container into native\n * browser scrolling when its children overflow the allocated rect.\n *\n * Pass `flush: true` to opt a panel into zero default insets instead of the\n * 4px default, for rail-style fixed-width strips that must sit flush against\n * their host.\n *\n * @category Core\n */\nclass Panel<TOptions extends PanelOptions = PanelOptions> extends Container<TOptions> {\n\n // `declare` rather than initialiser to dodge the class-field super-cascade\n // trap: a `= \"none\"` initialiser runs *after* super() returns, which\n // overwrites whatever `setAutoScroll(options.autoScroll)` had already\n // written during the super-time cascade. `applyOptions` below always\n // dispatches `setAutoScroll`, so the field gets seeded there.\n declare private _autoScroll: AutoScrollMode;\n declare private _scrollbarGutter: { right: number; bottom: number };\n\n // Scroll-shadow state. `_scrollShadows`, `_shadowOverlay` and `_shadowScrollHandler`\n // are written by `setScrollShadows` / `setAutoScroll` during the super-time\n // options cascade, so they are `declare`d (no initialiser) and seeded in\n // `applyOptions` to dodge the class-field super-cascade trap — an\n // initialiser would run after super() and clobber the seeded value.\n declare private _scrollShadows: boolean;\n declare private _shadowOverlay: Handle | null;\n declare private _shadowScrollHandler: (() => void) | null; // cached bound scroll handler — wired once\n\n // Runtime-only: never touched during the super cascade (the overlay only\n // exists post-render), so a plain initialiser is safe here.\n private _shadowOverlayStyle: InlineStyle = new InlineStyle();\n // Read during the super-time options cascade (setAutoScroll → doLayout →\n // scheduleGutterSettleOnShrink → showsScrollAffordance inspects the edges),\n // so `declare`d and seeded in `applyOptions` to dodge the class-field\n // super-cascade trap, exactly like `_scrollbarGutter`.\n declare private _shadowEdges: ScrollShadowEdges;\n // Child count observed on the previous layout pass, so a shrink (removed\n // children) can force one follow-up gutter/shadow re-measure. See\n // `scheduleGutterSettleOnShrink`.\n private _lastChildCount: number = 0;\n\n // Content preferred extent observed on the previous layout pass while a\n // scroll affordance was showing, so a shrink that happens inside a nested\n // descendant (whose removal leaves this panel's own child count unchanged)\n // still forces the follow-up re-measure. See `scheduleGutterSettleOnShrink`.\n // `declare`d + seeded in `applyOptions` for the same super-cascade reason.\n declare private _lastContentExtent: { width: number; height: number };\n\n // Overlay-scrollbar state. `_scrollbarStyle` is written by\n // `setScrollbarStyle` during the super-time options cascade, and\n // `_overlayScrollElement` / `_scrollbarV` / `_scrollbarH` / `_overlayScrollHandler`\n // are read (for the teardown guard) by the setter's install/refresh path\n // it triggers — so all five are `declare`d and seeded in `applyOptions`\n // for the same class-field super-cascade reason as the scroll-shadow\n // fields above.\n declare private _scrollbarStyle: ScrollbarStyle;\n declare private _overlayScrollElement: Handle | null; // raw inner scroll div (bars are its siblings)\n declare private _scrollbarV: Scrollbar | null;\n declare private _scrollbarH: Scrollbar | null;\n declare private _overlayScrollHandler: (() => void) | null; // native \"scroll\" -> sync\n\n // Runtime-only: never touched during the super cascade (the inner scroll\n // element only exists post-render), so a plain initialiser is safe here —\n // mirrors `_shadowOverlayStyle`.\n private _overlayScrollStyle: InlineStyle = new InlineStyle();\n\n // Bound scroll-forwarders wired to each overlay Scrollbar's \"scroll\"\n // event. Named class fields (per ARCHITECTURE.md *Listeners must\n // reference a named function*) so they are stable, removable references.\n private _onOverlayScrollV = (position: number): void => { this.setScrollTop(position); };\n private _onOverlayScrollH = (position: number): void => { this.setScrollLeft(position); };\n\n /**\n * Creates a panel with 4-pixel insets on all sides by default.\n *\n * @param options - Optional. Construction-time options applied to the panel.\n * `options.tag` overrides the default `\"div\"` tag for subclasses that need\n * a different element (e.g. `\"header\"`, `\"section\"`). `options.insets`\n * overrides the default `(4, 4, 4, 4)` perimeter. `options.flush` zeroes\n * that default instead (a caller-supplied `insets` still wins).\n */\n constructor(options?: TOptions, subclassDefaults?: Partial<TOptions>) {\n // `flush` seeds a zero-inset default; a caller-supplied `insets` still\n // wins because Component.applyOptions dispatches setInsets only when\n // options.insets is defined, overriding whatever default we pick here.\n const flushDefault: Partial<TOptions> =\n options?.flush ? ({ insets: new Insets(0, 0, 0, 0) } as Partial<TOptions>) : {};\n\n super(\n options,\n { ..._defaultPanelOptions, ...(subclassDefaults ?? {}), ...flushDefault } as Partial<TOptions>,\n );\n }\n\n /**\n * Dispatches `Panel`-specific options after delegating the inherited\n * {@link Component} options bag to `super`.\n *\n * @param options - The options bag whose fields populate this panel.\n *\n * @returns This panel, for method chaining.\n */\n protected applyOptions(options: TOptions): this {\n super.applyOptions(options);\n\n // Seed the scrollbar gutter cache before `setAutoScroll` — the latter\n // reads `_scrollbarGutter` to decide whether to clear it on a\n // `\"none\"` transition, and the `declare`d field would otherwise be\n // undefined at first dispatch.\n this.setScrollbarGutter(0, 0);\n\n // Seed the shadow-edge and content-extent caches for the same reason:\n // `setAutoScroll` below triggers a `doLayout` whose\n // `scheduleGutterSettleOnShrink` reads both, and their `declare`d fields\n // would otherwise be undefined during this super-time cascade.\n this._shadowEdges = { top: 0, bottom: 0, left: 0, right: 0 };\n this._lastContentExtent = { width: 0, height: 0 };\n\n // Always dispatch `setAutoScroll` — the fallback is the class\n // default from `_defaultPanelOptions`. Routing through the setter\n // (even for the default) keeps the `declare`d backing field\n // initialised and dodges the class-field super-cascade trap that\n // would bite a `= \"none\"` initialiser.\n this.setAutoScroll(options.autoScroll ?? this.getAutoScroll());\n\n // Seed the `declare`d overlay/handler fields before `setScrollShadows`\n // dispatches — the setter's teardown branch reads them, and the\n // `declare` leaves them `undefined` until first written.\n this._shadowOverlay = null;\n this._shadowScrollHandler = null;\n\n // Always dispatch so the backing field is seeded through the setter,\n // mirroring the `setAutoScroll` cascade above; the fallback is the\n // class default from `_defaultPanelOptions`.\n this.setScrollShadows(options.scrollShadows ?? this.getScrollShadows());\n\n // Seed the `declare`d overlay fields before `setScrollbarStyle`\n // dispatches — its refresh path (via `refreshOverlayScrollbars` ->\n // `removeOverlayScrollbars`) reads them, and the `declare` leaves\n // them `undefined` until first written.\n this._overlayScrollElement = null;\n this._scrollbarV = null;\n this._scrollbarH = null;\n this._overlayScrollHandler = null;\n\n // Always dispatch so the backing field is seeded through the setter,\n // mirroring the `setAutoScroll` / `setScrollShadows` cascades above;\n // the fallback is the class default from `_defaultPanelOptions`. Must\n // run after `setAutoScroll` — the install path this triggers reads\n // `_autoScroll`.\n this.setScrollbarStyle(options.scrollbarStyle ?? this.getScrollbarStyle());\n\n return this;\n }\n\n /**\n * Selects the panel's native scroll behaviour. Translates `mode` to\n * per-axis `overflow` writes via [`Component.setOverflowX`](/api/core/classes/Component#setoverflowx) /\n * [`Component.setOverflowY`](/api/core/classes/Component#setoverflowy).\n *\n * @param mode - The {@link AutoScrollMode} to apply.\n *\n * @returns This panel, for method chaining.\n *\n * @remarks Children render at their preferred size when `mode !== \"none\"`\n * — the panel no longer clips them to its allocated rect.\n *\n * Whenever a scrollbar becomes visible, `doLayout` measures the gutter\n * and shrinks the panel's reported inner size by that amount so the next\n * layout pass keeps children inside the visible content area (preventing\n * the classic V→H cascade where a right-anchored child gets exposed\n * behind a freshly-shown V scrollbar and triggers an H one).\n *\n * Do not combine with a [`Scrollbar`](/api/component/container/classes/Scrollbar) overlay or a\n * component (e.g. [`Table`](/api/component/table/classes/Table)) that already manages its own scroll\n * state — stacking native overflow on top of the custom scrollbar would\n * produce two scrollbars.\n */\n setAutoScroll(mode: AutoScrollMode): this {\n this._autoScroll = mode;\n\n switch (mode) {\n case \"none\":\n this.setOverflowX(\"hidden\").setOverflowY(\"hidden\");\n break;\n case \"auto\":\n this.setOverflowX(\"auto\").setOverflowY(\"auto\");\n break;\n case \"x\":\n this.setOverflowX(\"auto\").setOverflowY(\"hidden\");\n break;\n case \"y\":\n this.setOverflowX(\"hidden\").setOverflowY(\"auto\");\n break;\n case \"both\":\n this.setOverflowX(\"scroll\").setOverflowY(\"scroll\");\n break;\n }\n\n // Mode switched — drop any cached gutter from the previous mode so\n // the next `doLayout` re-measures against the new overflow setting.\n // (\"none\" never has a gutter; the other modes recompute below.)\n if (mode === \"none\" && (this._scrollbarGutter.right !== 0 || this._scrollbarGutter.bottom !== 0)) {\n this.setScrollbarGutter(0, 0);\n }\n\n // Forward the per-axis \"let children overflow the host\" decision to\n // the layout manager. Each manager honours these flags from its own\n // `doLayout` so trailing children land past `innerSize` when their\n // combined minSize exceeds the host's allocated rect, producing the\n // scrollbar the CSS `overflow: auto` above is waiting for.\n const axes = this.scrollableAxes();\n\n this.getLayoutManager()?.setOverflowing(axes.x, axes.y);\n\n // Re-evaluate the overlay scrollbar for the new mode FIRST: a transition\n // into `\"none\"` tears it (and the inner scroll element) down, a\n // transition into a scrolling mode installs it (when\n // `scrollbarStyle === \"overlay\"`). Must precede `refreshScrollShadows`\n // so `getScrollElement()` already resolves to the inner element when the\n // shadow refresh reads its scroll offsets. No-op before the element\n // exists (creation is deferred to `init`).\n this.refreshOverlayScrollbars();\n\n // Then re-evaluate the shadows for the new mode against the (now\n // correct) scroll element.\n this.refreshScrollShadows();\n\n return this;\n }\n\n /**\n * Re-applies the cached `autoScroll` mode to the new layout manager so\n * swapping managers preserves scroll behaviour. The base `setLayoutManager`\n * does the attach work; this override only forwards the overflow flags.\n *\n * @param layoutManager - The new LayoutManager to use for this panel.\n *\n * @returns This panel, for method chaining.\n */\n setLayoutManager(layoutManager: LayoutManager): this {\n super.setLayoutManager(layoutManager);\n this.setAutoScroll(this._autoScroll);\n\n return this;\n }\n\n /**\n * Returns the panel's current scroll mode.\n *\n * @returns The cached {@link AutoScrollMode}, or the class default when never set.\n */\n getAutoScroll(): AutoScrollMode {\n return this._autoScroll ?? this._defaultOptions.autoScroll!;\n }\n\n /**\n * Resets the panel's scroll mode to `\"none\"`, restoring the inherited\n * `overflow: hidden` clipping behaviour.\n *\n * @returns This panel, for method chaining.\n */\n clearAutoScroll(): this {\n return this.setAutoScroll(\"none\");\n }\n\n /**\n * Enables or disables the position-aware edge shadows on a scrolling\n * panel. When enabled (the default), each side that can still be scrolled\n * toward fades its content into the viewport border; the shadows are\n * suppressed entirely while `autoScroll === \"none\"` or when content does\n * not overflow.\n *\n * @param enabled - `true` to paint the edge shadows, `false` to suppress them.\n *\n * @returns This panel, for method chaining.\n */\n setScrollShadows(enabled: boolean): this {\n this._scrollShadows = enabled;\n\n this.refreshScrollShadows();\n\n return this;\n }\n\n /**\n * Returns whether the panel's scroll edge shadows are enabled.\n *\n * @returns The cached `scrollShadows` flag, or the class default when never set.\n */\n getScrollShadows(): boolean {\n return this._scrollShadows ?? this._defaultOptions.scrollShadows!;\n }\n\n /**\n * Selects the scrollbar rendering for this panel — the overlay default\n * (native scroll, hidden native bar, two synced `Scrollbar` widgets) or\n * `\"native\"` to keep the OS scrollbar. Installs or tears down the overlay\n * immediately when the element already exists; a no-op before render\n * beyond caching the value (the first install happens in `init`).\n *\n * @param style - The {@link ScrollbarStyle} to apply.\n *\n * @returns This panel, for method chaining.\n */\n setScrollbarStyle(style: ScrollbarStyle): this {\n this._scrollbarStyle = style;\n\n this.refreshOverlayScrollbars();\n\n // Re-home the shadow metric source: an overlay<->native toggle changes\n // which element `getScrollElement()` resolves to (inner element vs panel\n // element), so the shadows must re-read from the new scroller.\n this.refreshScrollShadows();\n\n return this;\n }\n\n /**\n * Returns the panel's current scrollbar style.\n *\n * @returns The cached {@link ScrollbarStyle}, or the class default when never set.\n */\n getScrollbarStyle(): ScrollbarStyle {\n return this._scrollbarStyle ?? this._defaultOptions.scrollbarStyle!;\n }\n\n /**\n * Routes every scroll read/write, the child host, and the content frame to\n * the inner scroll element while overlay mode is installed, and to the panel\n * element otherwise (native mode, `autoScroll: \"none\"`, pre-render). This is\n * the single seam that lets the overlay restructure move the actual scroller\n * inward without each scroll-plumbing call site knowing about it.\n *\n * @returns The inner scroll element in overlay mode, else the panel element.\n */\n protected getScrollElement(): Handle | undefined {\n return this._overlayScrollElement ?? this.getElement();\n }\n\n /**\n * Returns the panel's usable inner size with the currently-reserved\n * scrollbar gutter subtracted from each axis. Layout managers read this\n * to lay out children inside the post-gutter content area when a native\n * scrollbar is visible, instead of letting them fill the full rect and\n * spill behind (or be clipped by) the scrollbar.\n *\n * @returns The inner size minus the active scrollbar gutter, or null\n * when the element is not yet in the DOM (matches the base\n * `Component.getInnerSize` contract).\n */\n getInnerSize(): { width: number, height: number } | null {\n const size = super.getInnerSize();\n if (!size) {\n return null;\n }\n\n return {\n width: size.width - this._scrollbarGutter.right,\n height: size.height - this._scrollbarGutter.bottom,\n };\n }\n\n /**\n * Lays out children, then measures the post-layout scrollbar visibility\n * and, when it has changed since the last pass, caches the new gutter\n * and schedules a follow-up layout so children land inside the new\n * post-gutter content area. The follow-up is the \"one-frame reflow\"\n * documented on {@link AutoScrollMode}.\n *\n * @returns This panel, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n // Flush queued inline-style writes (own size in particular) before\n // reading scrollbar geometry: `LayoutManager.commitBounds` runs us\n // with `autoCommitStyle === false`, so the new width/height\n // `setSize` queued during the parent's layout pass haven't reached\n // the DOM yet — `scrollHeight` / `clientHeight` would otherwise\n // report the previous frame's dimensions and `measureScrollbarGutter`\n // wouldn't see the scrollbar transition.\n this.commitElementStyle();\n\n // Re-size the scroll-shadow overlay against the just-committed geometry\n // before measuring: it is the only in-flow child, so a stale height left\n // over from the previous pass floors `scrollHeight` and fakes an overflow\n // on every pass that shrinks the panel. See `resizeScrollShadowOverlay`.\n this.resizeScrollShadowOverlay();\n this.measureScrollbarGutter();\n\n // Re-pin the overlay and recompute edge state against the freshly\n // committed geometry (content-size or scrollbar-gutter changes can\n // flip which edges overflow). The preceding `commitElementStyle`\n // guarantees the reads see this frame's dimensions.\n this.updateScrollShadows();\n\n this.scheduleGutterSettleOnShrink();\n\n return this;\n }\n\n /**\n * Forces one follow-up layout pass after this panel's content shrinks, so a\n * shrink that brings overflowing content back within the viewport re-clears\n * the reserved scrollbar gutter and scroll shadow.\n *\n * `measureScrollbarGutter` only reschedules a pass when the gutter *value*\n * it reads changes. When content is removed, the overflow→fit transition\n * often has not settled on the pass that runs immediately after the\n * removal — the DOM `scrollHeight` still reads its old (overflowing) value,\n * so both the gutter and the shadow measure stale, see no change, and\n * schedule nothing; the stale gutter and shadow then linger until some later\n * unrelated layout (or a scroll event) re-measures. So this schedules one\n * more pass off a signal that *is* accurate at layout time — the content's\n * preferred extent, which drops synchronously when content is removed — and\n * the next frame re-measures against the settled content and clears anything\n * no longer needed.\n *\n * Two shrink signals are used. A direct-child-count drop is the cheap common\n * case. But content can also shrink inside a nested descendant (e.g. rows\n * removed from a grid several levels down), leaving this panel's own child\n * count unchanged; a drop in the panel's preferred extent catches that. The\n * preferred-extent read is gated behind an actually-showing scroll affordance\n * (a reserved gutter or a painted shadow edge) so it costs nothing on the\n * overwhelming majority of layouts, where there is nothing to settle.\n *\n * Bounded and non-looping: it fires only on the pass *after* a shrink (the\n * follow-up pass sees an unchanged count and extent), and never for a\n * `\"none\"` panel, which reserves no gutter and paints no shadow.\n */\n private scheduleGutterSettleOnShrink(): void {\n if (this._autoScroll === \"none\") {\n return;\n }\n\n const count = this.getComponents().length;\n const childShrank = count < this._lastChildCount;\n\n this._lastChildCount = count;\n\n // A shrink inside a nested descendant leaves `count` unchanged, so also\n // watch the preferred extent — but only while a scroll affordance is on\n // screen, since that is the only state a shrink could leave stale.\n let contentShrank = false;\n\n if (this.showsScrollAffordance()) {\n const preferred = this.getPreferredSize();\n const width = preferred ? preferred.width : 0;\n const height = preferred ? preferred.height : 0;\n\n contentShrank = width < this._lastContentExtent.width\n || height < this._lastContentExtent.height;\n\n this._lastContentExtent = { width, height };\n }\n\n if (childShrank || contentShrank) {\n this.scheduleLayout();\n }\n }\n\n /**\n * Whether this panel is currently painting a scroll affordance — a reserved\n * scrollbar gutter or any lit shadow edge. Used by\n * {@link Panel.scheduleGutterSettleOnShrink} to decide whether a shrink could\n * have left a stale gutter/shadow worth re-measuring.\n *\n * @returns `true` when a gutter is reserved or any shadow edge is lit.\n */\n private showsScrollAffordance(): boolean {\n return this._scrollbarGutter.right > 0\n || this._scrollbarGutter.bottom > 0\n || this._shadowEdges.top > 0\n || this._shadowEdges.bottom > 0\n || this._shadowEdges.left > 0\n || this._shadowEdges.right > 0;\n }\n\n /**\n * Initialises the panel element, then installs the scroll-shadow overlay\n * if the panel is a scroll-shadow candidate. Overlay creation is deferred\n * to here (rather than `applyOptions`) because the element only exists\n * once rendered.\n *\n * @param element - Optional. The element to initialise; falls back to the rendered element.\n *\n * @returns This panel, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n // `getElement()` is still undefined inside `init` (the base assigns\n // `_element` only after `render` returns), so hand the resolved\n // element straight to the installer instead of re-reading it.\n const resolved = element ?? this.getElement();\n\n // Install the overlay scrollbars (which create the inner scroll element)\n // BEFORE the scroll shadows, so `getScrollElement()` already resolves to\n // the inner element when `updateScrollShadows` first reads its offsets.\n if (resolved && this._scrollbarStyle === \"overlay\" && this._autoScroll !== \"none\") {\n this.installOverlayScrollbars(resolved);\n this.layoutOverlayScrollbars(resolved);\n }\n\n if (resolved && this._scrollShadows && this._autoScroll !== \"none\") {\n this.installScrollShadows(resolved);\n this.updateScrollShadows(resolved);\n }\n\n return this;\n }\n\n /**\n * Removes the cached scroll listener before the base destructor detaches\n * the element. The overlay is a child of that element, so it is removed\n * with it; only the window-level listener registration needs explicit\n * cleanup.\n */\n protected destructor(): void {\n this.removeScrollShadows();\n this.removeOverlayScrollbars();\n\n super.destructor();\n }\n\n /**\n * Which axes the current `autoScroll` mode lets the user scroll along. The\n * single source of truth behind the layout manager's overflow flags, the\n * gutter measurement, and the shadow edges — all three must agree on what\n * \"scrollable\" means or the panel paints an affordance for an axis that\n * cannot move.\n *\n * @returns A flag per axis; `true` when that axis scrolls under the current mode.\n */\n private scrollableAxes(): { x: boolean; y: boolean } {\n const mode = this._autoScroll;\n\n return {\n x: mode === \"x\" || mode === \"auto\" || mode === \"both\",\n y: mode === \"y\" || mode === \"auto\" || mode === \"both\",\n };\n }\n\n /**\n * Caches the new gutter for each axis. Internal — driven by\n * `measureScrollbarGutter` after a layout pass; consumers can't\n * configure this (it's derived from runtime DOM measurement, not a\n * declarative input), so it stays off the `PanelOptions` bag.\n *\n * @param right - Reserved gutter on the right edge in pixels.\n * @param bottom - Reserved gutter on the bottom edge in pixels.\n */\n private setScrollbarGutter(right: number, bottom: number): void {\n this._scrollbarGutter = { right, bottom };\n }\n\n /**\n * Reads the post-layout scrollbar visibility from the live DOM and\n * updates the cached gutter to match. When the gutter changed,\n * schedules a follow-up layout pass so children re-flow inside the new\n * inner area. No-op for `mode === \"none\"` and on browsers whose\n * scrollbars don't reserve space (e.g. macOS overlay scrollbars, where\n * the native width measures as 0 — the cascade can't happen there).\n */\n private measureScrollbarGutter(): void {\n if (this._autoScroll === \"none\") {\n return;\n }\n\n if (this._scrollbarStyle === \"overlay\") {\n this.layoutOverlayScrollbars();\n\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n const trackW = DOM.source.getScrollBarWidth();\n if (trackW === 0) {\n return;\n }\n\n // `\"both\"` forces both scrollbars on (`overflow: scroll` on both\n // axes), so the gutter is always reserved on both sides. The\n // single-axis modes only show their one bar, and `\"auto\"` shows\n // each independently; reading `scrollHeight > clientHeight` (and\n // its X-axis twin) detects whichever bars the browser has chosen\n // to render this frame, which matches the visible-only criterion.\n let vReserved: boolean;\n let hReserved: boolean;\n\n if (this._autoScroll === \"both\") {\n vReserved = true;\n hReserved = true;\n } else {\n const axes = this.scrollableAxes();\n const metrics = DOM.source.getScrollMetrics(el);\n\n vReserved = axes.y && metrics.scrollHeight > metrics.clientHeight;\n hReserved = axes.x && metrics.scrollWidth > metrics.clientWidth;\n }\n\n const newRight = vReserved ? trackW : 0;\n const newBottom = hReserved ? trackW : 0;\n\n if (newRight === this._scrollbarGutter.right && newBottom === this._scrollbarGutter.bottom) {\n return;\n }\n\n this.setScrollbarGutter(newRight, newBottom);\n this.scheduleLayout();\n }\n\n /**\n * Brings the scroll-shadow overlay into the state implied by the current\n * `scrollShadows` / `autoScroll` settings: torn down when disabled or\n * non-scrolling, otherwise installed and refreshed. No-op before the\n * element exists — `init` performs the first install once rendered.\n */\n private refreshScrollShadows(): void {\n if (!this._scrollShadows || this._autoScroll === \"none\") {\n this.removeScrollShadows();\n\n return;\n }\n\n const element = this.getElement();\n if (!element) {\n return;\n }\n\n this.installScrollShadows(element);\n this.updateScrollShadows(element);\n }\n\n /**\n * Creates the overlay and wires the scroll listener if they are not\n * already present. Idempotent: the `_shadowOverlay` / `_shadowScrollHandler` guards\n * keep it from stacking a duplicate overlay or listener across repeated\n * calls (the \"wire once\" rule).\n *\n * @param element - The rendered panel element to append the overlay to.\n */\n private installScrollShadows(element: Handle): void {\n if (!this._shadowOverlay) {\n this.createScrollShadowOverlay(element);\n }\n\n if (!this._shadowScrollHandler) {\n const handler = (): void => {\n this.updateScrollShadows();\n };\n\n this._shadowScrollHandler = handler;\n // Subtree, not exact-target: in overlay mode the scroll fires on the\n // id-less inner element, which only reaches the panel's id-keyed\n // listener bag by climbing the subtree. Native mode's scroll fires on\n // the panel element itself, which the subtree walk also matches.\n Event.addSubtreeListener(this, \"scroll\", handler);\n }\n }\n\n /**\n * Builds the non-interactive shadow overlay: an id-less, listener-free\n * presentational sheath (mirroring the clip/content frames) carrying four\n * blurred inset edge shadows, one per side. Each shadow's colour is a local\n * custom property defaulting to `transparent`, so the per-scroll path only\n * flips a property to light an edge rather than rebuilding the shadow.\n *\n * @param element - The panel element the overlay is appended to.\n */\n private createScrollShadowOverlay(element: Handle): void {\n const overlay = DOM.sink.createElement(\"div\");\n\n this._shadowOverlayStyle.attach(overlay);\n this._shadowOverlayStyle.setMany({\n // `sticky` pins the overlay to the scroll-port viewport on the\n // compositor: the browser keeps it at the `top: 0` / `left: 0`\n // edge as the content scrolls underneath, so it tracks the\n // viewport without any per-scroll JS write (no transform repin, no\n // main-thread flicker). It also does not extend the scrollable\n // region, since it stays inside the viewport box.\n position: \"sticky\",\n left: \"0px\",\n top: \"0px\",\n pointerEvents: \"none\",\n // Paint above the content frame: `setContentFrame` re-appends that\n // frame as the element's last child during layout, so DOM order\n // alone would let it cover an overlay appended here at `init`.\n zIndex: \"1\",\n // Four blurred inset shadows — one per edge — each gated by a local\n // custom property defaulting to `transparent` (flipped to the theme\n // colour by `setShadowEdge`). See `scrollShadowBoxShadow`.\n boxShadow: scrollShadowBoxShadow(),\n });\n\n DOM.sink.appendChild(element, overlay);\n // Track the panel-owned overlay so a discarded panel releases it on GC\n // even if removeScrollShadows never runs; untracked there on eager removal.\n this.trackHandle(overlay);\n this._shadowOverlay = overlay;\n }\n\n /**\n * Tears the overlay down and unwires the scroll listener, resetting the\n * cached edge state. Each step is guarded so this is safe to call before\n * the overlay was ever created (e.g. during the construction cascade).\n */\n private removeScrollShadows(): void {\n if (this._shadowScrollHandler) {\n Event.removeSubtreeListener(this, \"scroll\", this._shadowScrollHandler);\n this._shadowScrollHandler = null;\n }\n\n if (this._shadowOverlay) {\n DOM.sink.removeElement(this._shadowOverlay);\n this.untrackHandle(this._shadowOverlay);\n DOM.sink.release(this._shadowOverlay);\n this._shadowOverlay = null;\n\n // The buffer was bound to the now-removed overlay; a fresh one is\n // needed for any future re-install (mirrors `disposeFrame`).\n this._shadowOverlayStyle = new InlineStyle();\n }\n\n this._shadowEdges = { top: 0, bottom: 0, left: 0, right: 0 };\n }\n\n /**\n * Re-asserts the shadow overlay's size against the live viewport box (a\n * no-op write unless it changed).\n *\n * The overlay is the panel's only in-flow child — every child *component*\n * is absolutely positioned — so its height alone floors the element's\n * `scrollHeight`. That makes its size load-bearing for\n * {@link Panel.measureScrollbarGutter}, not merely cosmetic: while it\n * carries the previous pass's height, a panel that just shrank reads\n * `scrollHeight` (the stale, taller overlay) above `clientHeight` (the\n * freshly committed height) and reserves a scrollbar gutter for an overflow\n * that does not exist. Hence `doLayout` re-sizes the overlay *before* it\n * measures, which is what keeps the \"stays inside the viewport box, so it\n * never extends the scrollable region\" invariant true on the shrinking pass\n * as well as the settled one.\n *\n * @param element - Optional. The panel element; falls back to the rendered\n * element. Passed explicitly from `init`, where `getElement` is not yet\n * populated.\n */\n private resizeScrollShadowOverlay(element?: Handle): void {\n const el = element ?? this.getElement();\n if (!el || !this._shadowOverlay) {\n return;\n }\n\n const { clientWidth, clientHeight } = DOM.source.getScrollMetrics(el);\n\n // In overlay-scrollbar mode the native bar is hidden, so clientWidth /\n // clientHeight still span the strip the overlay Scrollbar paints on.\n // Inset the shadow overlay by the reserved overlay gutter so each edge\n // shadow lands just inside its bar — aligned with the content viewport\n // getInnerSize exposes — instead of bleeding under the translucent bar\n // track (which reads as the shadow painting on top of the scrollbar).\n // Native mode needs no inset: clientWidth / clientHeight already exclude\n // the OS scrollbar, so the gutter there is physical, not overlaid.\n const rightInset = this._scrollbarStyle === \"overlay\" ? this._scrollbarGutter.right : 0;\n const bottomInset = this._scrollbarStyle === \"overlay\" ? this._scrollbarGutter.bottom : 0;\n\n // Size the overlay to the viewport box; `position: sticky` keeps it\n // pinned there as the content scrolls, so no transform is needed.\n this._shadowOverlayStyle.setMany({\n width: (clientWidth - rightInset) + \"px\",\n height: (clientHeight - bottomInset) + \"px\",\n });\n }\n\n /**\n * Sizes the overlay to the live viewport and recomputes each edge's shadow\n * strength from its distance to that extreme. `sticky` handles the\n * positioning, so the per-scroll path only re-asserts the viewport size (a\n * no-op write unless it changed) and rescales the edges — no positioning\n * work runs here.\n *\n * @param element - Optional. The panel element; falls back to the rendered\n * element. Passed explicitly from `init`, where `getElement` is not yet\n * populated.\n */\n private updateScrollShadows(element?: Handle): void {\n const el = element ?? this.getElement();\n if (!el || !this._shadowOverlay) {\n return;\n }\n\n // Read the scroll offsets and extents from the element that actually\n // scrolls — the inner scroller in overlay mode (the panel element's own\n // offsets are always 0 there), the panel element otherwise. The overlay\n // is still sized against, and pinned to, the panel element (`el`).\n const { scrollTop, scrollLeft, scrollWidth, scrollHeight, clientWidth, clientHeight } =\n DOM.source.getScrollMetrics(this.getScrollElement() ?? el);\n\n this.resizeScrollShadowOverlay(el);\n\n const maxTop = scrollHeight - clientHeight;\n const maxLeft = scrollWidth - clientWidth;\n\n // Ramp an edge in by its distance past that extreme (see `scrollShadowRamp`).\n const ramp = scrollShadowRamp;\n\n // A shadow says \"there is more content this way, scroll to reach it\", so\n // only an axis the user can actually scroll may light its edges. A\n // clipped axis still reports overflow through `scrollWidth` /\n // `scrollHeight` — an `autoScroll: \"y\"` panel whose content is a few px\n // wider than its post-gutter width reads a non-zero `maxLeft` — and\n // ramping that would paint a right-edge fade promising content no\n // gesture can reveal.\n const axes = this.scrollableAxes();\n\n this.setShadowEdge(\"top\", \"--ts-ss-top\", axes.y ? ramp(scrollTop) : 0);\n this.setShadowEdge(\"bottom\", \"--ts-ss-bottom\", axes.y ? ramp(maxTop - scrollTop) : 0);\n this.setShadowEdge(\"left\", \"--ts-ss-left\", axes.x ? ramp(scrollLeft) : 0);\n this.setShadowEdge(\"right\", \"--ts-ss-right\", axes.x ? ramp(maxLeft - scrollLeft): 0);\n }\n\n /**\n * Sets a single edge's shadow strength by scaling the theme shadow colour\n * toward transparent. Strength is quantised to a whole percent so an\n * in-ramp scroll only repaints when the visible strength actually changes\n * (and never sub-pixel-thrashes); at zero the property is unset so the\n * `box-shadow` layer falls back to `transparent`.\n *\n * @param edge - The edge whose cached strength this updates.\n * @param property - The overlay custom property backing that edge's shadow.\n * @param strength - The target strength in the range 0–1.\n */\n private setShadowEdge(edge: keyof ScrollShadowEdges, property: string, strength: number): void {\n const percent = Math.round(strength * 100); // quantise: 0–1 → 0–100%\n\n if (this._shadowEdges[edge] === percent) {\n return;\n }\n\n this._shadowEdges[edge] = percent;\n this._shadowOverlayStyle.set(property, scrollShadowEdgeValue(percent));\n }\n\n /**\n * Brings the overlay scrollbar into the state implied by the current\n * `scrollbarStyle` / `autoScroll` settings: torn down when native or\n * non-scrolling, otherwise installed and laid out. No-op before the\n * element exists — `init` performs the first install once rendered.\n */\n private refreshOverlayScrollbars(): void {\n if (this._scrollbarStyle !== \"overlay\" || this._autoScroll === \"none\") {\n this.removeOverlayScrollbars();\n\n return;\n }\n\n const element = this.getElement();\n if (!element) {\n return;\n }\n\n this.installOverlayScrollbars(element);\n this.layoutOverlayScrollbars(element);\n }\n\n /**\n * Creates the inner scroll element (if absent), appends the two `Scrollbar`\n * widgets as its siblings on the panel element, and hides the native bar.\n * Idempotent: the element/bars/listener are each guarded by a `null` check,\n * so repeated calls neither stack duplicates nor re-hide an already-hidden\n * bar — but the inner element's per-axis overflow IS re-asserted on every\n * call so a runtime `setAutoScroll` mode-to-mode change (which keeps the\n * existing inner element) updates which axes scroll.\n *\n * @param element - The rendered panel element to append the inner scroller\n * and bars into.\n */\n private installOverlayScrollbars(element: Handle): void {\n if (!this._overlayScrollElement) {\n // The native scroll happens on this inner element, physically inset\n // by the reserved track (see `layoutOverlayScrollbars`) so content\n // clips at the inner viewport edge and can never scroll under a bar.\n // The bars are its SIBLINGS on the panel element (below), outside\n // this element's overflow clip — the two-element structure\n // `VirtualScroller` uses. The panel element keeps its own\n // `overflow: auto` (inert: the inner element is absolute / out of\n // flow and always fits, so the panel element never scrolls).\n ensureOverlayScrollerClassRule();\n\n const inner = DOM.sink.createElement(\"div\");\n\n // `width/height: 100%` fills the panel element's padding box (its\n // containing block, since every Component is positioned) so the\n // first overflow read sees the full viewport; `layoutOverlayScrollbars`\n // then overrides with the explicit post-gutter px size each pass.\n this._overlayScrollStyle.attach(inner);\n this._overlayScrollStyle.setMany({\n position: \"absolute\",\n left: \"0px\",\n top: \"0px\",\n width: \"100%\",\n height: \"100%\",\n });\n\n // Hide the inner element's own native bar via the shared class rule\n // (it has no `#id`, so the panel element's per-`#id` path can't reach it).\n DOM.sink.apply(inner, { addClass: [OVERLAY_SCROLLER_CLASS] });\n DOM.sink.appendChild(element, inner);\n\n // Shift the existing children (or the active content frame) onto the\n // inner scroller, preserving the scroll offset across the host swap.\n this.reparentContent(element, inner);\n\n this.trackHandle(inner);\n this._overlayScrollElement = inner;\n }\n\n // Re-assert the inner element's per-axis overflow every call: on a\n // runtime `setAutoScroll` mode-to-mode change the inner element already\n // exists (no teardown), so the guard above is skipped — but the newly\n // scrollable axis must flip from `hidden` to `auto` (and vice versa) or\n // native wheel/keyboard scroll and the matching bar would be inert.\n const axes = this.scrollableAxes();\n this._overlayScrollStyle.setMany({\n overflowX: axes.x ? \"auto\" : \"hidden\",\n overflowY: axes.y ? \"auto\" : \"hidden\",\n });\n\n if (!this._scrollbarV) {\n this._scrollbarV = new Scrollbar(\"vertical\");\n this._scrollbarV.setZIndex(2); // above the shadow overlay's z-index: 1\n DOM.sink.appendChild(element, this._scrollbarV.getElement(true)!);\n this._scrollbarV.on(\"scroll\", this._onOverlayScrollV);\n }\n\n if (!this._scrollbarH) {\n this._scrollbarH = new Scrollbar(\"horizontal\");\n this._scrollbarH.setZIndex(2);\n DOM.sink.appendChild(element, this._scrollbarH.getElement(true)!);\n this._scrollbarH.on(\"scroll\", this._onOverlayScrollH);\n }\n\n if (!this._overlayScrollHandler) {\n // Subtree, not exact-target: the inner scroll element is a raw,\n // id-less div, so its native \"scroll\" only reaches the panel's\n // id-keyed listener bag by climbing the subtree to the panel element\n // (the same mechanism the wheel listener uses). The handler reads\n // `getScrollElement()`, not the event target, so a nested\n // descendant's scroll only triggers a harmless re-read.\n const handler = (): void => {\n this.syncOverlayScrollbars();\n };\n\n this._overlayScrollHandler = handler;\n Event.addSubtreeListener(this, \"scroll\", handler);\n }\n\n this.setNativeScrollbarHidden(true);\n }\n\n /**\n * Tears the overlay scrollbar down: unwires the native scroll listener,\n * disposes both bars, removes the sticky host, un-hides the native bar,\n * and clears any reserved gutter. Each step is guarded so this is safe to\n * call before the overlay was ever created (e.g. during the construction\n * cascade). Disposing (rather than only detaching) is required because\n * each bar is appended straight onto the panel element with a raw\n * `DOM.sink.appendChild` and held only in `_scrollbarV` / `_scrollbarH` —\n * never registered via `addComponent` — so `Component.destructor()`'s\n * child recursion can never reach it to reclaim its per-instance\n * stylesheet rule.\n */\n private removeOverlayScrollbars(): void {\n if (this._overlayScrollHandler) {\n Event.removeSubtreeListener(this, \"scroll\", this._overlayScrollHandler);\n this._overlayScrollHandler = null;\n }\n\n if (this._scrollbarV) {\n this._scrollbarV.off(\"scroll\", this._onOverlayScrollV);\n this._scrollbarV.dispose();\n this._scrollbarV = null;\n }\n\n if (this._scrollbarH) {\n this._scrollbarH.off(\"scroll\", this._onOverlayScrollH);\n this._scrollbarH.dispose();\n this._scrollbarH = null;\n }\n\n if (this._overlayScrollElement) {\n // Re-parent the children (or content frame) back onto the panel\n // element — which resumes scrolling in native mode — before the\n // inner element is destroyed, preserving the scroll offset.\n const element = this.getElement();\n\n if (element) {\n this.reparentContent(this._overlayScrollElement, element);\n }\n\n DOM.sink.removeElement(this._overlayScrollElement);\n this.untrackHandle(this._overlayScrollElement);\n DOM.sink.release(this._overlayScrollElement);\n this._overlayScrollElement = null;\n\n // The buffer was bound to the now-removed inner element; a fresh one\n // is needed for any future re-install (mirrors `_shadowOverlayStyle`\n // in `removeScrollShadows`).\n this._overlayScrollStyle = new InlineStyle();\n }\n\n this.setNativeScrollbarHidden(false);\n\n // Unconditional (rather than gated on the previous value, as\n // `setAutoScroll`'s native-path gutter-clear is): `setLayoutManager`\n // can re-enter this teardown from inside `Component.applyOptions`'s\n // own `layoutManager` option handling, before Panel's `applyOptions`\n // body has seeded `_scrollbarGutter` at all. The assignment itself is\n // a cheap plain-object write, so skipping the read-before-write\n // avoids that ordering hazard for free.\n this.setScrollbarGutter(0, 0);\n }\n\n /**\n * Hides or restores the native scrollbar through the framework's deferred\n * style seams — a `scrollbar-width: none` write on the component's own\n * `#id` rule (Firefox / Chromium >= 121) plus a `#id::-webkit-scrollbar {\n * display: none }` state rule (WebKit / older Blink).\n *\n * @param hidden - `true` to hide the native bar, `false` to restore it.\n */\n private setNativeScrollbarHidden(hidden: boolean): void {\n this.setElementCSSRule(\"scrollbarWidth\", hidden ? \"none\" : null);\n this.createStyleRule(\"::-webkit-scrollbar\").set(\"display\", hidden ? \"none\" : null);\n }\n\n /**\n * Sizes the inner scroll element to the available viewport minus the track\n * on each axis whose perpendicular bar is visible, positions both bars in\n * the reserved band at the trailing edges, pushes their metrics, and\n * reserves the matching gutter — rescheduling a layout pass when it changed.\n * Called from `init` (first install) and from `measureScrollbarGutter`'s\n * overlay branch (every `doLayout` pass).\n *\n * The dual read is the crux: the **available viewport** comes from the panel\n * element (which never scrolls, so its client box is the full viewport),\n * while content extent, offsets, and the current inner client box come from\n * the **inner scroll element**. Physically insetting the inner element is\n * what makes overflowing content clip before the bar band instead of\n * scrolling under it.\n *\n * @param element - Optional. The panel element; falls back to the rendered\n * element. Passed explicitly from `init`, where `getElement` is not yet\n * populated.\n */\n private layoutOverlayScrollbars(element?: Handle): void {\n const panelEl = element ?? this.getElement();\n const innerEl = this._overlayScrollElement;\n if (!panelEl || !innerEl || !this._scrollbarV || !this._scrollbarH) {\n return;\n }\n\n const trackW = this._scrollbarV.getTrackWidth();\n\n // Available viewport: the panel element's client box. The parent layout\n // sizes the panel element (and `doLayout` flushes it via\n // `commitElementStyle`) before this runs, so `avail` is always current —\n // it never lags a resize.\n const avail = DOM.source.getScrollMetrics(panelEl);\n const availW = avail.clientWidth;\n const availH = avail.clientHeight;\n\n const curRight = this._scrollbarGutter.right;\n const curBottom = this._scrollbarGutter.bottom;\n\n // Size the inner scroller to the CURRENT viewport (minus the currently\n // reserved gutter) BEFORE reading its metrics, so its `clientWidth`/\n // `clientHeight` — and thus `scrollWidth`/`scrollHeight`, which the\n // browser floors at the client box — reflect THIS frame's viewport. The\n // inner element otherwise carries the previous pass's size, which lags a\n // resize in BOTH directions: on expand the stale-small client box kept a\n // bar the widened viewport no longer needs; on shrink the stale-large\n // client box floored `scrollWidth` so content that now fits still read as\n // overflowing — each flickered a transient bar that could stick. A gutter\n // change below re-sizes and reschedules, converging in one extra pass.\n this._overlayScrollStyle.setMany({\n width: (availW - curRight) + \"px\",\n height: (availH - curBottom) + \"px\",\n });\n\n // Content extent, offsets, and the now-current inner client box.\n const m = DOM.source.getScrollMetrics(innerEl);\n const axes = this.scrollableAxes();\n\n // A bar shows when content exceeds the inner scroller's (now current)\n // viewport. The V<->H dependency — reserving one bar shrinks the other's\n // viewport — settles across passes via the gutter-change reschedule\n // below, the same one-extra-pass convergence the gutter always used.\n const vVisible = axes.y && m.scrollHeight > m.clientHeight;\n const hVisible = axes.x && m.scrollWidth > m.clientWidth;\n\n const innerW = availW - (vVisible ? trackW : 0);\n const innerH = availH - (hVisible ? trackW : 0);\n\n // Re-inset the inner scroller when this pass's gutter differs from the\n // one the pre-read sizing used, so overflowing content clips at the inner\n // viewport edge and can never scroll under a bar.\n if (innerW !== availW - curRight || innerH !== availH - curBottom) {\n this._overlayScrollStyle.setMany({ width: innerW + \"px\", height: innerH + \"px\" });\n }\n\n // Bars occupy the reserved band at the trailing edges, sized to the\n // inner extent so each stops short of the shared corner. setMetrics\n // takes the same reduced viewport (`innerH`/`innerW`) the visibility\n // test used, so a bar's shown/hidden state and the reserved gutter\n // never disagree.\n this._scrollbarV.setX(innerW);\n this._scrollbarV.setY(0);\n this._scrollbarV.setHeight(innerH);\n this._scrollbarV.setMetrics(innerH, m.scrollHeight, m.scrollTop);\n\n this._scrollbarH.setX(0);\n this._scrollbarH.setY(innerH);\n this._scrollbarH.setWidth(innerW);\n this._scrollbarH.setMetrics(innerW, m.scrollWidth, m.scrollLeft);\n\n const newRight = vVisible ? trackW : 0;\n const newBottom = hVisible ? trackW : 0;\n\n if (newRight !== curRight || newBottom !== curBottom) {\n this.setScrollbarGutter(newRight, newBottom);\n this.scheduleLayout();\n }\n }\n\n /**\n * Re-pushes metrics (thumb size/position only) to both overlay bars against\n * the inner scroller's live scroll offset. Called from the native `\"scroll\"`\n * handler — geometry (bar position/size, reserved gutter, inner element\n * size) changes only on layout, so this never repositions or resizes\n * anything and never schedules a layout.\n */\n private syncOverlayScrollbars(): void {\n const innerEl = this._overlayScrollElement;\n if (!innerEl || !this._scrollbarV || !this._scrollbarH) {\n return;\n }\n\n const m = DOM.source.getScrollMetrics(innerEl);\n\n this._scrollbarV.setMetrics(m.clientHeight, m.scrollHeight, m.scrollTop);\n this._scrollbarH.setMetrics(m.clientWidth, m.scrollWidth, m.scrollLeft);\n }\n}\n\nconst PanelCallable = callable(Panel);\ntype PanelCallable<TOptions extends PanelOptions = PanelOptions> = Panel<TOptions>;\nexport {\n Panel as _Panel,\n PanelCallable as Panel\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./DOM-
|
|
2
|
-
//# sourceMappingURL=Position-
|
|
1
|
+
import{t as e}from"./DOM-C-Edb3tA.js";var t;(function(n){let r=-1,i=-1,a=-1,o=-1,s=0,c=new Map;function measureTextSize(t,n={}){let r=e.source.measureText(t,n);return{width:r.width,height:r.height}}n.measureTextSize=measureTextSize;function measureTextWidth(e,t){return measureTextSize(e,t).width}n.measureTextWidth=measureTextWidth;function measureTextWidths(t,n){return e.source.measureTextWidths(t,n)}n.measureTextWidths=measureTextWidths;function linePaddingPx(){if(r>=0)return r;let t=e.source.getThemeVar(`--ts-ui-line-padding`),n=parseFloat(t);return r=isNaN(n)?2:n,r}function rootFontSizePx(){if(i>=0)return i;let t=e.source.getThemeVar(`--ts-ui-font-size`),n=parseFloat(t);return i=isNaN(n)?14:n,i}function lineHeightPx(e={}){let t=e.fontSizePx??rootFontSizePx(),n=e.linePadding??!0;return n===!0?t+=linePaddingPx():typeof n==`number`&&(t+=n),Math.round(t)}n.lineHeightPx=lineHeightPx;function measureTextBaseline(){if(a>=0)return a;let t=e.source.measureFontMetrics(),n=lineHeightPx()-(t.ascent+t.descent);return a=Math.round(n/2+t.ascent),a}n.measureTextBaseline=measureTextBaseline;function singleLineBoxHeight(e,t,n){let r=e.getTop()+e.getBottom()+(t?t.getTop()+t.getBottom():0)+n.top+n.bottom;return lineHeightPx()+r}n.singleLineBoxHeight=singleLineBoxHeight;function opticalCenterOffset(){if(o>=0)return o;let t=e.source.measureFontMetrics(),n=(t.ascent-t.descent)/2,r=t.capTop/2;return o=Math.max(0,Math.round(n-r)),o}n.opticalCenterOffset=opticalCenterOffset;function textMetricsGeneration(){return s}n.textMetricsGeneration=textMetricsGeneration;function boundFontSizePx(t,n){let r=t+`|`+(n??``),i=c.get(r);if(i!==void 0)return i;let a=parseFloat(e.source.getThemeVar(t)),o=isNaN(a)?e.source.resolveFontSizePx(n??`var(${t})`):a;return c.set(r,o),o}n.boundFontSizePx=boundFontSizePx;function invalidateTextMetricsCache(){r=-1,i=-1,a=-1,o=-1,c.clear(),s++}n.invalidateTextMetricsCache=invalidateTextMetricsCache;function generateUUID(){let e=`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,function(e){let t=Math.random()*16|0;return(e==`x`?t:t&3|8).toString(16)}),n=parseInt(e[0]);return t.isInteger(n)&&(e=`a`+e.substring(1,e.length)),e}n.generateUUID=generateUUID;function isInteger(e){return Number.isInteger(e)}n.isInteger=isInteger;function clamp(e,t,n){return Math.min(Math.max(e,t),n)}n.clamp=clamp;function kebabToCamel(e){return e.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}n.kebabToCamel=kebabToCamel})(t||={});var BaseObject=class{_id;constructor(){this._id=t.generateUUID()}getId(){return this._id}setId(e){return this._id=e,this}getClassName(){return this.constructor.name}},n=2**53-1;function isUnbounded(e){return e>=2**53-1}function saturate(e){return Math.min(e,n)}function borderToStyle(e){let t=e.border??`none`;return{borderTop:e.borderTop??t,borderRight:e.borderRight??t,borderBottom:e.borderBottom??t,borderLeft:e.borderLeft??t}}function borderSideWidth(e){if(!e)return 0;let t=e.trim().match(/^([\d.]+)px\b/i);return t?parseFloat(t[1]):0}var Insets=class extends BaseObject{_top;_right;_bottom;_left;constructor(e,t,n,r){super(),this._top=e||0,this._right=t||0,this._bottom=n||0,this._left=r||0}getTop(){return this._top||0}setTop(e){return this._top=e,this}getRight(){return this._right||0}setRight(e){return this._right=e,this}getBottom(){return this._bottom||0}setBottom(e){return this._bottom=e,this}getLeft(){return this._left||0}setLeft(e){return this._left=e,this}set(e,t,n,r){return this._top=e,this._right=t,this._bottom=n,this._left=r,this}render(){return this.getTop()+`px `+this.getRight()+`px `+this.getBottom()+`px `+this.getLeft()+`px`}},r=function(e){return e.STATIC=`static`,e.FIXED=`fixed`,e.ABSOLUTE=`absolute`,e}({});export{n as a,BaseObject as c,borderToStyle as i,t as l,Insets as n,isUnbounded as o,borderSideWidth as r,saturate as s,r as t};
|
|
2
|
+
//# sourceMappingURL=Position-BwtxouxK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Position-PBVBW1JS.js","names":[],"sources":["../../src/typescript/lib/core/Util.ts","../../src/typescript/lib/core/BaseObject.ts","../../src/typescript/lib/primitive/Size.ts","../../src/typescript/lib/primitive/Border.ts","../../src/typescript/lib/primitive/Insets.ts","../../src/typescript/lib/primitive/Position.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Size } from \"~/primitive/Size.js\";\nimport type { Insets } from \"~/primitive/Insets.js\";\nimport { DOM } from \"~/core/DOM.js\";\n\n/**\n * Font options for off-screen text measurement.\n * All properties default to the active theme variables when omitted.\n */\nexport interface TextMeasureOptions {\n fontFamily?: string;\n fontSize? : string;\n fontWeight?: string;\n fontStyle? : string;\n fontVariant?: string;\n fontStretch?: string;\n lineHeight?: string\n /**\n * When set, the probe wraps at this pixel width (using `pre-wrap`) instead of\n * measuring on a single `nowrap` line. The returned `height` then reflects the\n * wrapped, multi-line box. Omit to measure the natural single-line size.\n */\n maxWidth?: number;\n}\n\n/**\n * Result of an off-screen text measurement that also reports the typographic baseline.\n *\n * @remarks `baseline` is the offset from the top of the measured box to the font baseline,\n * in pixels — analogous to CSS `vertical-align: baseline` on an inline-block element.\n */\nexport interface TextMetrics {\n width: number;\n height: number;\n baseline: number;\n}\n\n/**\n * General-purpose utility functions for DOM interaction and browser environment queries.\n *\n * @category Util\n */\nexport namespace Util {\n\n // Cached text-metric results, invalidated together on theme change via\n // `invalidateTextMetricsCache`. `-1` is the \"not yet measured\" sentinel\n // (a real padding / font size / baseline / offset is always >= 0).\n let linePaddingCache: number = -1;\n let rootFontSizeCache: number = -1;\n let textBaselineCache: number = -1;\n let opticalOffsetCache: number = -1;\n\n // Generation counter bumped by `invalidateTextMetricsCache`, so a caller\n // (e.g. `Text.needsMeasure`) can tell whether its last measurement\n // predates the active theme without holding its own subscription.\n let metricsGeneration = 0;\n\n // Resolved bound-font-size results, keyed by `cssVar + \"|\" + cssRule`, so\n // every `Text` bound to the same CSS var/rule pair shares one resolution\n // per theme change instead of each instance re-probing the cascade.\n const boundFontSizeCache = new Map<string, number | null>();\n\n /**\n * Measures the rendered size of a text string using an off-screen probe `<span>`.\n *\n * @param text - The string to measure.\n * @param options - Font properties to apply. Defaults to the active theme variables.\n * @returns The measured `{width, height}` in pixels, ceiled to whole pixels.\n */\n export function measureTextSize(text: string, options: TextMeasureOptions = {}): Size {\n const metrics = DOM.source.measureText(text, options);\n\n return { width: metrics.width, height: metrics.height };\n }\n\n /**\n * Returns the rendered pixel width of a text string.\n *\n * @param text - The string to measure.\n * @param options - Font properties to apply. Defaults to the active theme variables.\n * @returns The measured width in pixels, ceiled to a whole pixel.\n */\n export function measureTextWidth(text: string, options?: TextMeasureOptions): number {\n return measureTextSize(text, options).width;\n }\n\n /**\n * Measures many strings under one font in a single document reflow,\n * instead of one reflow per string.\n *\n * @param texts - The strings to measure.\n * @param options - Font properties to apply. Defaults to the active theme variables.\n * @returns One width per input, in input order.\n */\n export function measureTextWidths(texts: string[], options?: TextMeasureOptions): number[] {\n return DOM.source.measureTextWidths(texts, options);\n }\n\n /**\n * Returns the active theme's leading (`--ts-ui-line-padding`) in pixels.\n *\n * @returns The integer-pixel value of `--ts-ui-line-padding`, or `4` as a\n * fallback when the variable is missing or unparseable.\n */\n function linePaddingPx(): number {\n if (linePaddingCache >= 0) {\n return linePaddingCache;\n }\n\n const raw = DOM.source.getThemeVar(\"--ts-ui-line-padding\");\n const parsed = parseFloat(raw);\n\n // 2 mirrors the `--ts-ui-line-padding` default shipped by every theme\n // (see ModernTheme/DarkTheme/ClassicTheme `font.linePadding: '2px'`);\n // it only applies when the var is absent (e.g. pre-theme-apply probe).\n linePaddingCache = isNaN(parsed) ? 2 : parsed;\n\n return linePaddingCache;\n }\n\n /**\n * Returns the document root font size (`--ts-ui-font-size`) in pixels, the\n * default font size for a control that doesn't override it.\n *\n * @returns The integer-pixel root font size, or `14` as a fallback.\n */\n function rootFontSizePx(): number {\n if (rootFontSizeCache >= 0) {\n return rootFontSizeCache;\n }\n\n const raw = DOM.source.getThemeVar(\"--ts-ui-font-size\");\n const parsed = parseFloat(raw);\n\n rootFontSizeCache = isNaN(parsed) ? 14 : parsed;\n\n return rootFontSizeCache;\n }\n\n /**\n * Returns a vertical text metric in integer pixels: a control's font size,\n * plus the theme leading (`--ts-ui-line-padding`) by default.\n *\n * @param options - Measurement options.\n * @param options.fontSizePx - The control's font size in pixels. Omit to use\n * the document root font size (`--ts-ui-font-size`), which is what the\n * native `<input>`-backed controls render at.\n * @param options.linePadding - Controls the leading added to the font size:\n * `true` (the default) adds the theme `--ts-ui-line-padding`, giving the\n * full rendered line box (matching the `calc(1em + …)` line-height controls\n * render at); `false` adds nothing, returning the bare font size to size a\n * box from its font without leading; a number adds that exact pixel padding.\n * @returns `round(fontSize + leading)`, where leading is the theme padding,\n * `0`, or the given number.\n *\n * @remarks With the default leading the line box scales with font size, so\n * 12px and 14px text get proportionate line boxes from the one token. Text\n * components, table rows, and the baseline computation use the default so\n * their measurement matches the rendered line box; the native input box\n * heights pass `false` so the box hugs the font size plus their own chrome.\n * The padding and root font size are cached; call\n * {@link invalidateTextMetricsCache} after a theme change to force a\n * re-read.\n */\n export function lineHeightPx(options: { fontSizePx?: number, linePadding?: boolean | number } = {}): number {\n let fs = options.fontSizePx ?? rootFontSizePx();\n\n const linePadding = options.linePadding ?? true;\n\n if (linePadding === true) {\n fs += linePaddingPx();\n } else if (typeof linePadding === \"number\") {\n fs += linePadding;\n }\n\n return Math.round(fs);\n }\n\n /**\n * Returns the content-relative text baseline for the unified line-height\n * model: the offset from the top of the `lineHeightPx()` line box to the\n * font baseline.\n *\n * @returns The baseline offset in pixels, rounded to the nearest integer.\n *\n * @remarks Computed from the canvas 2D `measureText` font metrics rather\n * than a DOM probe, so it is deterministic and UA-independent. A CSS line\n * box centres the font's ascent+descent within `line-height`; this\n * reproduces that centring with the known px line box —\n * `round(lineGap / 2 + ascent)` where `lineGap = lineHeightPx - (ascent +\n * descent)` — so the measured baseline matches where the browser paints the\n * glyph in both a native `<input>` and a `Text`/`Label`. `fontBoundingBox*`\n * (font-intrinsic, string-independent) is used in preference to\n * `actualBoundingBox*` (glyph-ink specific) so the baseline does not shift\n * per measured string; `\"X\"` is passed only to satisfy `measureText`. The\n * result is cached; call {@link invalidateTextMetricsCache} after a theme\n * change to force re-measurement.\n */\n export function measureTextBaseline(): number {\n if (textBaselineCache >= 0) {\n return textBaselineCache;\n }\n\n const m = DOM.source.measureFontMetrics();\n const gap = lineHeightPx() - (m.ascent + m.descent);\n\n textBaselineCache = Math.round(gap / 2 + m.ascent);\n\n return textBaselineCache;\n }\n\n /**\n * Returns the pixel height of a single-line input box: the theme line-box\n * height ({@link lineHeightPx}) plus the component's own vertical chrome\n * (insets + optional padding + border).\n *\n * @param insets - The component's layout insets (top/bottom read).\n * @param padding - The component's CSS padding, or `null` when it has none.\n * @param border - The component's border widths (top/bottom read).\n * @returns The single-line box height in pixels.\n *\n * @remarks Factors out the `chrome = insets + padding + border; h =\n * lineHeightPx() + chrome` idiom shared by every single-line native-input\n * box height (`TextField` / `PasswordField` / `ComboBox` / the picker fields\n * / `NumberSpinner`). Only the vertical (top/bottom) edges contribute; the\n * horizontal edges are the caller's width concern. `NumberSpinner` uniquely\n * passes its *inner input's* padding rather than its own.\n */\n export function singleLineBoxHeight(\n insets: Insets,\n padding: Insets | null,\n border: { top: number; bottom: number },\n ): number {\n const chrome = insets.getTop() + insets.getBottom()\n + (padding ? padding.getTop() + padding.getBottom() : 0)\n + border.top + border.bottom;\n\n return lineHeightPx() + chrome;\n }\n\n /**\n * Returns the downward pixel offset that moves a single line of text from\n * its line-box (geometric) centre to its optical (cap-height) centre.\n *\n * @returns The downward offset in pixels (`>= 0`), rounded to the nearest\n * integer.\n *\n * @remarks A label's visible glyphs occupy cap-top→baseline; the descender\n * band below the baseline is empty ink, so the ink's visual centre sits\n * above the font box's geometric centre and a geometrically-centred label\n * reads as too high. This returns roughly half the unused descender space —\n * `round(boxMid - inkMid)` where `boxMid = (ascent - descent) / 2` and\n * `inkMid = capTop / 2` with `capTop = actualBoundingBoxAscent` (the cap-top\n * ink of `\"X\"`) — so a consumer ([`Button`](/api/component/button/classes/Button))\n * can nudge single-line text down onto its true optical centre. Derived\n * from the same cached canvas\n * metrics as {@link measureTextBaseline}; cached and invalidated together\n * via {@link invalidateTextMetricsCache}.\n */\n export function opticalCenterOffset(): number {\n if (opticalOffsetCache >= 0) {\n return opticalOffsetCache;\n }\n\n const m = DOM.source.measureFontMetrics();\n const boxMid = (m.ascent - m.descent) / 2;\n const inkMid = m.capTop / 2;\n\n opticalOffsetCache = Math.max(0, Math.round(boxMid - inkMid));\n\n return opticalOffsetCache;\n }\n\n /**\n * Returns the generation counter for the cached text metrics, bumped once\n * every time {@link invalidateTextMetricsCache} runs.\n *\n * @returns The current generation number.\n *\n * @remarks Lets a caller that measured against a past generation (stashed\n * from a prior read of this function) tell, cheaply and without holding\n * its own theme subscription, whether a re-measure is due — the pattern\n * `Text` uses instead of subscribing to `ThemeManager` per instance.\n */\n export function textMetricsGeneration(): number {\n return metricsGeneration;\n }\n\n /**\n * Resolves a CSS custom property bound to a control's font size to a\n * pixel number, caching the result per `cssVar`/`cssRule` pair so every\n * control bound to the same token shares one resolution per theme change.\n *\n * @param cssVar - The CSS custom property name (e.g. `\"--ts-ui-font-size\"`).\n * @param cssRule - The CSS value the control's `font-size` rule is set to\n * (e.g. `\"var(--ts-ui-font-size, 14px)\"`), used as the fallback probe\n * when `cssVar`'s raw value isn't a bare, parseable pixel number (a\n * `calc(...)`-valued relative token). Pass `null` when the control has\n * no rule text of its own; the probe then falls back to `var(${cssVar})`.\n * @returns The resolved pixel size.\n *\n * @remarks Mirrors the theme-var-then-probe strategy every other cached\n * metric in this namespace uses: a simple var parses straight off its raw\n * string; a `calc(...)`-valued token falls back to\n * {@link DOMSource.resolveFontSizePx}, a cascade-evaluating probe. Cleared\n * together with the rest of the text metrics by\n * {@link invalidateTextMetricsCache}.\n */\n export function boundFontSizePx(cssVar: string, cssRule: string | null): number | null {\n const key = cssVar + \"|\" + (cssRule ?? \"\");\n const cached = boundFontSizeCache.get(key);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const raw = parseFloat(DOM.source.getThemeVar(cssVar));\n const resolved = isNaN(raw)\n ? DOM.source.resolveFontSizePx(cssRule ?? `var(${cssVar})`)\n : raw;\n\n boundFontSizeCache.set(key, resolved);\n\n return resolved;\n }\n\n /**\n * Discards every cached text metric (line box, baseline, optical offset,\n * bound font sizes) so the next read re-measures against the active theme\n * font.\n *\n * @remarks Call this whenever the active theme's font size, family, or\n * line-height changes, since the cached values reflect the font in use at\n * the time of the first measurement and would otherwise mis-align controls\n * against each other after a theme swap.\n */\n export function invalidateTextMetricsCache(): void {\n linePaddingCache = -1;\n rootFontSizeCache = -1;\n textBaselineCache = -1;\n opticalOffsetCache = -1;\n boundFontSizeCache.clear();\n metricsGeneration++;\n }\n\n /**\n * Generates a UUID string, ensuring the first character is never a digit.\n *\n * @returns A UUID v4 string with a guaranteed non-numeric first character.\n *\n * @remarks If the standard UUID generation produces a leading digit, it is replaced\n * with the letter \"a\" so the result is safe to use as a DOM id or CSS identifier.\n */\n export function generateUUID() {\n let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n let r = Math.random() * 16 | 0,\n v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n\n let first = parseInt(uuid[0]);\n if (Util.isInteger(first)) {\n uuid = \"a\" + uuid.substring(1, uuid.length);\n }\n\n return uuid;\n }\n\n /**\n * Returns true if value is an integer.\n *\n * @param value - The value to test.\n *\n * @returns `true` if `value` is an integer, `false` otherwise.\n */\n export function isInteger(value: Object) {\n return Number.isInteger(value);\n }\n\n /**\n * Clamps a number into an inclusive `[min, max]` range.\n *\n * @param value - The number to clamp.\n * @param min - The lower bound.\n * @param max - The upper bound.\n *\n * @returns `value` when it lies within the range, otherwise the nearer bound.\n *\n * @remarks Assumes `min <= max` (the framework's `min <= preferred <= max`\n * size invariant). When `min > max` the maximum wins (`clamp(v, 10, 0) === 0`)\n * — the low-first `Math.min(Math.max(...))` tie-break; callers must not rely\n * on it. `NaN` propagates (a `NaN` input returns `NaN`), matching the inlined\n * `Math.min`/`Math.max` expressions this replaces.\n */\n export function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n }\n\n /**\n * Converts a hyphen-separated identifier (e.g. `\"border-top-width\"`) to its\n * camelCase equivalent (`\"borderTopWidth\"`). Useful for translating\n * CSS-style kebab-case property names into the camelCase form expected by\n * `CSSStyleDeclaration` bracket-indexed assignment.\n *\n * @param value - The kebab-case input string.\n * @returns The camelCase equivalent.\n */\n export function kebabToCamel(value: string): string {\n return value.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n }\n\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Util } from \"~/core/Util.js\"\n\n/**\n * Base class for all library objects.\n * Automatically assigns a unique identifier on construction.\n *\n * @category Core\n */\nexport class BaseObject {\n\n private _id: string;\n\n constructor() {\n this._id = Util.generateUUID();\n }\n\n /**\n * Returns the unique identifier for this object.\n *\n * @returns The UUID string assigned at construction time.\n */\n getId() {\n return this._id;\n }\n\n /**\n * Sets the unique identifier for this object.\n *\n * @param id - The new identifier string to assign.\n */\n setId(id: string) : this {\n this._id = id;\n\n return this;\n }\n\n /**\n * Returns the runtime class name of this object.\n *\n * @returns The name of the constructor function as a string.\n */\n getClassName() {\n return this.constructor.name;\n }\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * Represents a two-dimensional size with width and height in pixels.\n *\n * @category Util\n */\nexport interface Size {\n width: number,\n height: number\n}\n\n/**\n * The sentinel extent meaning \"no upper bound\" on a size axis.\n *\n * @category Util\n */\nexport const UNBOUNDED: number = Number.MAX_SAFE_INTEGER;\n\n/**\n * True when an extent is at or above the unbounded sentinel (recognises the\n * legacy `Number.MAX_VALUE` too).\n *\n * @param n - The extent to test.\n * @returns `true` when `n` is at or above {@link UNBOUNDED}.\n *\n * @category Util\n */\nexport function isUnbounded(n: number): boolean {\n return n >= Number.MAX_SAFE_INTEGER;\n}\n\n/**\n * Caps an extent at the unbounded sentinel so an unbounded sum cannot overflow\n * it.\n *\n * @param n - The extent to cap.\n * @returns `n`, capped at {@link UNBOUNDED}.\n *\n * @category Util\n */\nexport function saturate(n: number): number {\n return Math.min(n, UNBOUNDED);\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * A border specification built from complete CSS border strings.\n * `border` is the all-sides fallback; each per-side field overrides it for\n * that side. An unspecified side falls back to `border`, then to `\"none\"`.\n *\n * @category Util\n */\nexport interface BorderOptions {\n /** CSS `border` shorthand applied to all four sides (e.g. `\"1px solid rgb(...)\"`, `\"none\"`, `\"var(--x)\"`). */\n border?: string;\n /** CSS `border-top` value; overrides `border` for the top side. */\n borderTop?: string;\n /** CSS `border-right` value; overrides `border` for the right side. */\n borderRight?: string;\n /** CSS `border-bottom` value; overrides `border` for the bottom side. */\n borderBottom?: string;\n /** CSS `border-left` value; overrides `border` for the left side. */\n borderLeft?: string;\n}\n\n/**\n * Expands a {@link BorderOptions} into the four camelCase longhand style keys\n * (`borderTop`/`borderRight`/`borderBottom`/`borderLeft`) that `StyleRule.setMany`\n * consumes. Each side resolves via `side ?? border ?? \"none\"`, so a pure-longhand\n * map replays deterministically regardless of what else touched the rule.\n *\n * @param border - The border specification to expand.\n *\n * @returns A map of the four longhand keys to their resolved CSS values.\n *\n * @category Util\n */\nexport function borderToStyle(border: BorderOptions): Record<string, string | null> {\n const all = border.border ?? \"none\";\n\n return {\n borderTop: border.borderTop ?? all,\n borderRight: border.borderRight ?? all,\n borderBottom: border.borderBottom ?? all,\n borderLeft: border.borderLeft ?? all,\n };\n}\n\n/**\n * Best-effort leading-`<n>px` width of one side's CSS value. Returns `0` for\n * `undefined`, `none`, `0`, `var(...)`, or any non-`px` leading token. Used both\n * to parse the always-`<n>px` values `getComputedStyle` returns (authoritative,\n * post-render) and to estimate a width from a spec string before an element exists.\n *\n * @param value - A single side's CSS border value, or `undefined`.\n *\n * @returns The leading pixel width, or `0` when none can be parsed.\n *\n * @category Util\n */\nexport function borderSideWidth(value: string | undefined): number {\n if (!value) {\n return 0;\n }\n\n const match = value.trim().match(/^([\\d.]+)px\\b/i);\n\n return match ? parseFloat(match[1]) : 0;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BaseObject } from \"~/core/BaseObject.js\";\n\n/**\n * Represents the four directional inset (padding/margin) values for a rectangular area.\n * All values are expressed in pixels.\n *\n * @example\n * ```typescript\n * import { Insets } from '@jimka/typescript-ui/primitive';\n*\n * component.setInsets(new Insets(8, 12, 8, 12)); // top, right, bottom, left\n * ```\n *\n * @category Util\n */\nexport class Insets extends BaseObject {\n\n private _top: number;\n private _right: number;\n private _bottom: number;\n private _left: number;\n\n /**\n * @param top - Top inset in pixels.\n * @param right - Right inset in pixels.\n * @param bottom - Bottom inset in pixels.\n * @param left - Left inset in pixels.\n */\n constructor(top: number, right: number, bottom: number, left: number) {\n super();\n\n this._top = top || 0;\n this._right = right || 0;\n this._bottom = bottom || 0;\n this._left = left || 0;\n }\n\n /**\n * Returns the top inset in pixels.\n *\n * @returns The top inset value, defaulting to `0` if unset.\n */\n getTop() {\n return this._top || 0;\n }\n\n /**\n * Sets the top inset in pixels.\n *\n * @param value - The new top inset value in pixels.\n */\n setTop(value: number) : this {\n this._top = value;\n\n return this;\n }\n\n /**\n * Returns the right inset in pixels.\n *\n * @returns The right inset value, defaulting to `0` if unset.\n */\n getRight() {\n return this._right || 0;\n }\n\n /**\n * Sets the right inset in pixels.\n *\n * @param value - The new right inset value in pixels.\n */\n setRight(value: number) : this {\n this._right = value;\n\n return this;\n }\n\n /**\n * Returns the bottom inset in pixels.\n *\n * @returns The bottom inset value, defaulting to `0` if unset.\n */\n getBottom() {\n return this._bottom || 0;\n }\n\n /**\n * Sets the bottom inset in pixels.\n *\n * @param value - The new bottom inset value in pixels.\n */\n setBottom(value: number) : this {\n this._bottom = value;\n\n return this;\n }\n\n /**\n * Returns the left inset in pixels.\n *\n * @returns The left inset value, defaulting to `0` if unset.\n */\n getLeft() {\n return this._left || 0;\n }\n\n /**\n * Sets the left inset in pixels.\n *\n * @param value - The new left inset value in pixels.\n */\n setLeft(value: number) : this {\n this._left = value;\n\n return this;\n }\n\n /**\n * Sets all four inset values at once.\n *\n * @param top - Top inset in pixels.\n * @param right - Right inset in pixels.\n * @param bottom - Bottom inset in pixels.\n * @param left - Left inset in pixels.\n */\n set(top: number, right: number, bottom: number, left: number) : this {\n this._top = top;\n this._right = right;\n this._bottom = bottom;\n this._left = left;\n\n return this;\n }\n\n /**\n * Returns the insets as a CSS shorthand string (e.g. `\"4px 4px 4px 4px\"`).\n *\n * @returns A CSS margin/padding shorthand string with all four sides in top-right-bottom-left order.\n */\n render() {\n return this.getTop() + \"px \" + this.getRight() + \"px \" + this.getBottom() + \"px \" + this.getLeft() + \"px\";\n }\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * CSS `position` values used by the framework.\n *\n * The framework positions every component absolutely (see\n * [ARCHITECTURE.md](/ARCHITECTURE.md) §Positioning). Only three values\n * are exposed:\n *\n * - `ABSOLUTE` — the universal default for every framework component.\n * - `FIXED` — the documented exception for floating overlays that anchor to\n * the viewport (`AnimatedDropdown`, `Popover`, `Notification`, `Dialog`,\n * `DialogBackdrop`). Set internally by these subclasses; never by callers.\n * - `STATIC` — the documented exception for an HTML element whose native\n * semantics require in-flow rendering (currently only `Legend`, which needs\n * the notch in its parent `<fieldset>`'s border).\n *\n * Other CSS values (`relative`, `sticky`, `initial`, `inherit`) are\n * intentionally **not** members. Application code does not call\n * `setPosition` — that setter is `protected` on `Component`.\n *\n * @category Util\n */\nexport enum Position {\n STATIC = \"static\",\n FIXED = \"fixed\",\n ABSOLUTE = \"absolute\",\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Position-BwtxouxK.js","names":[],"sources":["../../src/typescript/lib/core/Util.ts","../../src/typescript/lib/core/BaseObject.ts","../../src/typescript/lib/primitive/Size.ts","../../src/typescript/lib/primitive/Border.ts","../../src/typescript/lib/primitive/Insets.ts","../../src/typescript/lib/primitive/Position.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Size } from \"~/primitive/Size.js\";\nimport type { Insets } from \"~/primitive/Insets.js\";\nimport { DOM } from \"~/core/DOM.js\";\n\n/**\n * Font options for off-screen text measurement.\n * All properties default to the active theme variables when omitted.\n */\nexport interface TextMeasureOptions {\n fontFamily?: string;\n fontSize? : string;\n fontWeight?: string;\n fontStyle? : string;\n fontVariant?: string;\n fontStretch?: string;\n lineHeight?: string\n /**\n * When set, the probe wraps at this pixel width (using `pre-wrap`) instead of\n * measuring on a single `nowrap` line. The returned `height` then reflects the\n * wrapped, multi-line box. Omit to measure the natural single-line size.\n */\n maxWidth?: number;\n}\n\n/**\n * Result of an off-screen text measurement that also reports the typographic baseline.\n *\n * @remarks `baseline` is the offset from the top of the measured box to the font baseline,\n * in pixels — analogous to CSS `vertical-align: baseline` on an inline-block element.\n */\nexport interface TextMetrics {\n width: number;\n height: number;\n baseline: number;\n}\n\n/**\n * General-purpose utility functions for DOM interaction and browser environment queries.\n *\n * @category Util\n */\nexport namespace Util {\n\n // Cached text-metric results, invalidated together on theme change via\n // `invalidateTextMetricsCache`. `-1` is the \"not yet measured\" sentinel\n // (a real padding / font size / baseline / offset is always >= 0).\n let linePaddingCache: number = -1;\n let rootFontSizeCache: number = -1;\n let textBaselineCache: number = -1;\n let opticalOffsetCache: number = -1;\n\n // Generation counter bumped by `invalidateTextMetricsCache`, so a caller\n // (e.g. `Text.needsMeasure`) can tell whether its last measurement\n // predates the active theme without holding its own subscription.\n let metricsGeneration = 0;\n\n // Resolved bound-font-size results, keyed by `cssVar + \"|\" + cssRule`, so\n // every `Text` bound to the same CSS var/rule pair shares one resolution\n // per theme change instead of each instance re-probing the cascade.\n const boundFontSizeCache = new Map<string, number | null>();\n\n /**\n * Measures the rendered size of a text string using an off-screen probe `<span>`.\n *\n * @param text - The string to measure.\n * @param options - Font properties to apply. Defaults to the active theme variables.\n * @returns The measured `{width, height}` in pixels, ceiled to whole pixels.\n */\n export function measureTextSize(text: string, options: TextMeasureOptions = {}): Size {\n const metrics = DOM.source.measureText(text, options);\n\n return { width: metrics.width, height: metrics.height };\n }\n\n /**\n * Returns the rendered pixel width of a text string.\n *\n * @param text - The string to measure.\n * @param options - Font properties to apply. Defaults to the active theme variables.\n * @returns The measured width in pixels, ceiled to a whole pixel.\n */\n export function measureTextWidth(text: string, options?: TextMeasureOptions): number {\n return measureTextSize(text, options).width;\n }\n\n /**\n * Measures many strings under one font in a single document reflow,\n * instead of one reflow per string.\n *\n * @param texts - The strings to measure.\n * @param options - Font properties to apply. Defaults to the active theme variables.\n * @returns One width per input, in input order.\n */\n export function measureTextWidths(texts: string[], options?: TextMeasureOptions): number[] {\n return DOM.source.measureTextWidths(texts, options);\n }\n\n /**\n * Returns the active theme's leading (`--ts-ui-line-padding`) in pixels.\n *\n * @returns The integer-pixel value of `--ts-ui-line-padding`, or `4` as a\n * fallback when the variable is missing or unparseable.\n */\n function linePaddingPx(): number {\n if (linePaddingCache >= 0) {\n return linePaddingCache;\n }\n\n const raw = DOM.source.getThemeVar(\"--ts-ui-line-padding\");\n const parsed = parseFloat(raw);\n\n // 2 mirrors the `--ts-ui-line-padding` default shipped by every theme\n // (see ModernTheme/DarkTheme/ClassicTheme `font.linePadding: '2px'`);\n // it only applies when the var is absent (e.g. pre-theme-apply probe).\n linePaddingCache = isNaN(parsed) ? 2 : parsed;\n\n return linePaddingCache;\n }\n\n /**\n * Returns the document root font size (`--ts-ui-font-size`) in pixels, the\n * default font size for a control that doesn't override it.\n *\n * @returns The integer-pixel root font size, or `14` as a fallback.\n */\n function rootFontSizePx(): number {\n if (rootFontSizeCache >= 0) {\n return rootFontSizeCache;\n }\n\n const raw = DOM.source.getThemeVar(\"--ts-ui-font-size\");\n const parsed = parseFloat(raw);\n\n rootFontSizeCache = isNaN(parsed) ? 14 : parsed;\n\n return rootFontSizeCache;\n }\n\n /**\n * Returns a vertical text metric in integer pixels: a control's font size,\n * plus the theme leading (`--ts-ui-line-padding`) by default.\n *\n * @param options - Measurement options.\n * @param options.fontSizePx - The control's font size in pixels. Omit to use\n * the document root font size (`--ts-ui-font-size`), which is what the\n * native `<input>`-backed controls render at.\n * @param options.linePadding - Controls the leading added to the font size:\n * `true` (the default) adds the theme `--ts-ui-line-padding`, giving the\n * full rendered line box (matching the `calc(1em + …)` line-height controls\n * render at); `false` adds nothing, returning the bare font size to size a\n * box from its font without leading; a number adds that exact pixel padding.\n * @returns `round(fontSize + leading)`, where leading is the theme padding,\n * `0`, or the given number.\n *\n * @remarks With the default leading the line box scales with font size, so\n * 12px and 14px text get proportionate line boxes from the one token. Text\n * components, table rows, and the baseline computation use the default so\n * their measurement matches the rendered line box; the native input box\n * heights pass `false` so the box hugs the font size plus their own chrome.\n * The padding and root font size are cached; call\n * {@link invalidateTextMetricsCache} after a theme change to force a\n * re-read.\n */\n export function lineHeightPx(options: { fontSizePx?: number, linePadding?: boolean | number } = {}): number {\n let fs = options.fontSizePx ?? rootFontSizePx();\n\n const linePadding = options.linePadding ?? true;\n\n if (linePadding === true) {\n fs += linePaddingPx();\n } else if (typeof linePadding === \"number\") {\n fs += linePadding;\n }\n\n return Math.round(fs);\n }\n\n /**\n * Returns the content-relative text baseline for the unified line-height\n * model: the offset from the top of the `lineHeightPx()` line box to the\n * font baseline.\n *\n * @returns The baseline offset in pixels, rounded to the nearest integer.\n *\n * @remarks Computed from the canvas 2D `measureText` font metrics rather\n * than a DOM probe, so it is deterministic and UA-independent. A CSS line\n * box centres the font's ascent+descent within `line-height`; this\n * reproduces that centring with the known px line box —\n * `round(lineGap / 2 + ascent)` where `lineGap = lineHeightPx - (ascent +\n * descent)` — so the measured baseline matches where the browser paints the\n * glyph in both a native `<input>` and a `Text`/`Label`. `fontBoundingBox*`\n * (font-intrinsic, string-independent) is used in preference to\n * `actualBoundingBox*` (glyph-ink specific) so the baseline does not shift\n * per measured string; `\"X\"` is passed only to satisfy `measureText`. The\n * result is cached; call {@link invalidateTextMetricsCache} after a theme\n * change to force re-measurement.\n */\n export function measureTextBaseline(): number {\n if (textBaselineCache >= 0) {\n return textBaselineCache;\n }\n\n const m = DOM.source.measureFontMetrics();\n const gap = lineHeightPx() - (m.ascent + m.descent);\n\n textBaselineCache = Math.round(gap / 2 + m.ascent);\n\n return textBaselineCache;\n }\n\n /**\n * Returns the pixel height of a single-line input box: the theme line-box\n * height ({@link lineHeightPx}) plus the component's own vertical chrome\n * (insets + optional padding + border).\n *\n * @param insets - The component's layout insets (top/bottom read).\n * @param padding - The component's CSS padding, or `null` when it has none.\n * @param border - The component's border widths (top/bottom read).\n * @returns The single-line box height in pixels.\n *\n * @remarks Factors out the `chrome = insets + padding + border; h =\n * lineHeightPx() + chrome` idiom shared by every single-line native-input\n * box height (`TextField` / `PasswordField` / `ComboBox` / the picker fields\n * / `NumberSpinner`). Only the vertical (top/bottom) edges contribute; the\n * horizontal edges are the caller's width concern. `NumberSpinner` uniquely\n * passes its *inner input's* padding rather than its own.\n */\n export function singleLineBoxHeight(\n insets: Insets,\n padding: Insets | null,\n border: { top: number; bottom: number },\n ): number {\n const chrome = insets.getTop() + insets.getBottom()\n + (padding ? padding.getTop() + padding.getBottom() : 0)\n + border.top + border.bottom;\n\n return lineHeightPx() + chrome;\n }\n\n /**\n * Returns the downward pixel offset that moves a single line of text from\n * its line-box (geometric) centre to its optical (cap-height) centre.\n *\n * @returns The downward offset in pixels (`>= 0`), rounded to the nearest\n * integer.\n *\n * @remarks A label's visible glyphs occupy cap-top→baseline; the descender\n * band below the baseline is empty ink, so the ink's visual centre sits\n * above the font box's geometric centre and a geometrically-centred label\n * reads as too high. This returns roughly half the unused descender space —\n * `round(boxMid - inkMid)` where `boxMid = (ascent - descent) / 2` and\n * `inkMid = capTop / 2` with `capTop = actualBoundingBoxAscent` (the cap-top\n * ink of `\"X\"`) — so a consumer ([`Button`](/api/component/button/classes/Button))\n * can nudge single-line text down onto its true optical centre. Derived\n * from the same cached canvas\n * metrics as {@link measureTextBaseline}; cached and invalidated together\n * via {@link invalidateTextMetricsCache}.\n */\n export function opticalCenterOffset(): number {\n if (opticalOffsetCache >= 0) {\n return opticalOffsetCache;\n }\n\n const m = DOM.source.measureFontMetrics();\n const boxMid = (m.ascent - m.descent) / 2;\n const inkMid = m.capTop / 2;\n\n opticalOffsetCache = Math.max(0, Math.round(boxMid - inkMid));\n\n return opticalOffsetCache;\n }\n\n /**\n * Returns the generation counter for the cached text metrics, bumped once\n * every time {@link invalidateTextMetricsCache} runs.\n *\n * @returns The current generation number.\n *\n * @remarks Lets a caller that measured against a past generation (stashed\n * from a prior read of this function) tell, cheaply and without holding\n * its own theme subscription, whether a re-measure is due — the pattern\n * `Text` uses instead of subscribing to `ThemeManager` per instance.\n */\n export function textMetricsGeneration(): number {\n return metricsGeneration;\n }\n\n /**\n * Resolves a CSS custom property bound to a control's font size to a\n * pixel number, caching the result per `cssVar`/`cssRule` pair so every\n * control bound to the same token shares one resolution per theme change.\n *\n * @param cssVar - The CSS custom property name (e.g. `\"--ts-ui-font-size\"`).\n * @param cssRule - The CSS value the control's `font-size` rule is set to\n * (e.g. `\"var(--ts-ui-font-size, 14px)\"`), used as the fallback probe\n * when `cssVar`'s raw value isn't a bare, parseable pixel number (a\n * `calc(...)`-valued relative token). Pass `null` when the control has\n * no rule text of its own; the probe then falls back to `var(${cssVar})`.\n * @returns The resolved pixel size.\n *\n * @remarks Mirrors the theme-var-then-probe strategy every other cached\n * metric in this namespace uses: a simple var parses straight off its raw\n * string; a `calc(...)`-valued token falls back to\n * {@link DOMSource.resolveFontSizePx}, a cascade-evaluating probe. Cleared\n * together with the rest of the text metrics by\n * {@link invalidateTextMetricsCache}.\n */\n export function boundFontSizePx(cssVar: string, cssRule: string | null): number | null {\n const key = cssVar + \"|\" + (cssRule ?? \"\");\n const cached = boundFontSizeCache.get(key);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const raw = parseFloat(DOM.source.getThemeVar(cssVar));\n const resolved = isNaN(raw)\n ? DOM.source.resolveFontSizePx(cssRule ?? `var(${cssVar})`)\n : raw;\n\n boundFontSizeCache.set(key, resolved);\n\n return resolved;\n }\n\n /**\n * Discards every cached text metric (line box, baseline, optical offset,\n * bound font sizes) so the next read re-measures against the active theme\n * font.\n *\n * @remarks Call this whenever the active theme's font size, family, or\n * line-height changes, since the cached values reflect the font in use at\n * the time of the first measurement and would otherwise mis-align controls\n * against each other after a theme swap.\n */\n export function invalidateTextMetricsCache(): void {\n linePaddingCache = -1;\n rootFontSizeCache = -1;\n textBaselineCache = -1;\n opticalOffsetCache = -1;\n boundFontSizeCache.clear();\n metricsGeneration++;\n }\n\n /**\n * Generates a UUID string, ensuring the first character is never a digit.\n *\n * @returns A UUID v4 string with a guaranteed non-numeric first character.\n *\n * @remarks If the standard UUID generation produces a leading digit, it is replaced\n * with the letter \"a\" so the result is safe to use as a DOM id or CSS identifier.\n */\n export function generateUUID() {\n let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n let r = Math.random() * 16 | 0,\n v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n\n let first = parseInt(uuid[0]);\n if (Util.isInteger(first)) {\n uuid = \"a\" + uuid.substring(1, uuid.length);\n }\n\n return uuid;\n }\n\n /**\n * Returns true if value is an integer.\n *\n * @param value - The value to test.\n *\n * @returns `true` if `value` is an integer, `false` otherwise.\n */\n export function isInteger(value: Object) {\n return Number.isInteger(value);\n }\n\n /**\n * Clamps a number into an inclusive `[min, max]` range.\n *\n * @param value - The number to clamp.\n * @param min - The lower bound.\n * @param max - The upper bound.\n *\n * @returns `value` when it lies within the range, otherwise the nearer bound.\n *\n * @remarks Assumes `min <= max` (the framework's `min <= preferred <= max`\n * size invariant). When `min > max` the maximum wins (`clamp(v, 10, 0) === 0`)\n * — the low-first `Math.min(Math.max(...))` tie-break; callers must not rely\n * on it. `NaN` propagates (a `NaN` input returns `NaN`), matching the inlined\n * `Math.min`/`Math.max` expressions this replaces.\n */\n export function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n }\n\n /**\n * Converts a hyphen-separated identifier (e.g. `\"border-top-width\"`) to its\n * camelCase equivalent (`\"borderTopWidth\"`). Useful for translating\n * CSS-style kebab-case property names into the camelCase form expected by\n * `CSSStyleDeclaration` bracket-indexed assignment.\n *\n * @param value - The kebab-case input string.\n * @returns The camelCase equivalent.\n */\n export function kebabToCamel(value: string): string {\n return value.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n }\n\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Util } from \"~/core/Util.js\"\n\n/**\n * Base class for all library objects.\n * Automatically assigns a unique identifier on construction.\n *\n * @category Core\n */\nexport class BaseObject {\n\n private _id: string;\n\n constructor() {\n this._id = Util.generateUUID();\n }\n\n /**\n * Returns the unique identifier for this object.\n *\n * @returns The UUID string assigned at construction time.\n */\n getId() {\n return this._id;\n }\n\n /**\n * Sets the unique identifier for this object.\n *\n * @param id - The new identifier string to assign.\n */\n setId(id: string) : this {\n this._id = id;\n\n return this;\n }\n\n /**\n * Returns the runtime class name of this object.\n *\n * @returns The name of the constructor function as a string.\n */\n getClassName() {\n return this.constructor.name;\n }\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * Represents a two-dimensional size with width and height in pixels.\n *\n * @category Util\n */\nexport interface Size {\n width: number,\n height: number\n}\n\n/**\n * The sentinel extent meaning \"no upper bound\" on a size axis.\n *\n * @category Util\n */\nexport const UNBOUNDED: number = Number.MAX_SAFE_INTEGER;\n\n/**\n * True when an extent is at or above the unbounded sentinel (recognises the\n * legacy `Number.MAX_VALUE` too).\n *\n * @param n - The extent to test.\n * @returns `true` when `n` is at or above {@link UNBOUNDED}.\n *\n * @category Util\n */\nexport function isUnbounded(n: number): boolean {\n return n >= Number.MAX_SAFE_INTEGER;\n}\n\n/**\n * Caps an extent at the unbounded sentinel so an unbounded sum cannot overflow\n * it.\n *\n * @param n - The extent to cap.\n * @returns `n`, capped at {@link UNBOUNDED}.\n *\n * @category Util\n */\nexport function saturate(n: number): number {\n return Math.min(n, UNBOUNDED);\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * A border specification built from complete CSS border strings.\n * `border` is the all-sides fallback; each per-side field overrides it for\n * that side. An unspecified side falls back to `border`, then to `\"none\"`.\n *\n * @category Util\n */\nexport interface BorderOptions {\n /** CSS `border` shorthand applied to all four sides (e.g. `\"1px solid rgb(...)\"`, `\"none\"`, `\"var(--x)\"`). */\n border?: string;\n /** CSS `border-top` value; overrides `border` for the top side. */\n borderTop?: string;\n /** CSS `border-right` value; overrides `border` for the right side. */\n borderRight?: string;\n /** CSS `border-bottom` value; overrides `border` for the bottom side. */\n borderBottom?: string;\n /** CSS `border-left` value; overrides `border` for the left side. */\n borderLeft?: string;\n}\n\n/**\n * Expands a {@link BorderOptions} into the four camelCase longhand style keys\n * (`borderTop`/`borderRight`/`borderBottom`/`borderLeft`) that `StyleRule.setMany`\n * consumes. Each side resolves via `side ?? border ?? \"none\"`, so a pure-longhand\n * map replays deterministically regardless of what else touched the rule.\n *\n * @param border - The border specification to expand.\n *\n * @returns A map of the four longhand keys to their resolved CSS values.\n *\n * @category Util\n */\nexport function borderToStyle(border: BorderOptions): Record<string, string | null> {\n const all = border.border ?? \"none\";\n\n return {\n borderTop: border.borderTop ?? all,\n borderRight: border.borderRight ?? all,\n borderBottom: border.borderBottom ?? all,\n borderLeft: border.borderLeft ?? all,\n };\n}\n\n/**\n * Best-effort leading-`<n>px` width of one side's CSS value. Returns `0` for\n * `undefined`, `none`, `0`, `var(...)`, or any non-`px` leading token. Used both\n * to parse the always-`<n>px` values `getComputedStyle` returns (authoritative,\n * post-render) and to estimate a width from a spec string before an element exists.\n *\n * @param value - A single side's CSS border value, or `undefined`.\n *\n * @returns The leading pixel width, or `0` when none can be parsed.\n *\n * @category Util\n */\nexport function borderSideWidth(value: string | undefined): number {\n if (!value) {\n return 0;\n }\n\n const match = value.trim().match(/^([\\d.]+)px\\b/i);\n\n return match ? parseFloat(match[1]) : 0;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BaseObject } from \"~/core/BaseObject.js\";\n\n/**\n * Represents the four directional inset (padding/margin) values for a rectangular area.\n * All values are expressed in pixels.\n *\n * @example\n * ```typescript\n * import { Insets } from '@jimka/typescript-ui/primitive';\n*\n * component.setInsets(new Insets(8, 12, 8, 12)); // top, right, bottom, left\n * ```\n *\n * @category Util\n */\nexport class Insets extends BaseObject {\n\n private _top: number;\n private _right: number;\n private _bottom: number;\n private _left: number;\n\n /**\n * @param top - Top inset in pixels.\n * @param right - Right inset in pixels.\n * @param bottom - Bottom inset in pixels.\n * @param left - Left inset in pixels.\n */\n constructor(top: number, right: number, bottom: number, left: number) {\n super();\n\n this._top = top || 0;\n this._right = right || 0;\n this._bottom = bottom || 0;\n this._left = left || 0;\n }\n\n /**\n * Returns the top inset in pixels.\n *\n * @returns The top inset value, defaulting to `0` if unset.\n */\n getTop() {\n return this._top || 0;\n }\n\n /**\n * Sets the top inset in pixels.\n *\n * @param value - The new top inset value in pixels.\n */\n setTop(value: number) : this {\n this._top = value;\n\n return this;\n }\n\n /**\n * Returns the right inset in pixels.\n *\n * @returns The right inset value, defaulting to `0` if unset.\n */\n getRight() {\n return this._right || 0;\n }\n\n /**\n * Sets the right inset in pixels.\n *\n * @param value - The new right inset value in pixels.\n */\n setRight(value: number) : this {\n this._right = value;\n\n return this;\n }\n\n /**\n * Returns the bottom inset in pixels.\n *\n * @returns The bottom inset value, defaulting to `0` if unset.\n */\n getBottom() {\n return this._bottom || 0;\n }\n\n /**\n * Sets the bottom inset in pixels.\n *\n * @param value - The new bottom inset value in pixels.\n */\n setBottom(value: number) : this {\n this._bottom = value;\n\n return this;\n }\n\n /**\n * Returns the left inset in pixels.\n *\n * @returns The left inset value, defaulting to `0` if unset.\n */\n getLeft() {\n return this._left || 0;\n }\n\n /**\n * Sets the left inset in pixels.\n *\n * @param value - The new left inset value in pixels.\n */\n setLeft(value: number) : this {\n this._left = value;\n\n return this;\n }\n\n /**\n * Sets all four inset values at once.\n *\n * @param top - Top inset in pixels.\n * @param right - Right inset in pixels.\n * @param bottom - Bottom inset in pixels.\n * @param left - Left inset in pixels.\n */\n set(top: number, right: number, bottom: number, left: number) : this {\n this._top = top;\n this._right = right;\n this._bottom = bottom;\n this._left = left;\n\n return this;\n }\n\n /**\n * Returns the insets as a CSS shorthand string (e.g. `\"4px 4px 4px 4px\"`).\n *\n * @returns A CSS margin/padding shorthand string with all four sides in top-right-bottom-left order.\n */\n render() {\n return this.getTop() + \"px \" + this.getRight() + \"px \" + this.getBottom() + \"px \" + this.getLeft() + \"px\";\n }\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * CSS `position` values used by the framework.\n *\n * The framework positions every component absolutely (see\n * [ARCHITECTURE.md](/ARCHITECTURE.md) §Positioning). Only three values\n * are exposed:\n *\n * - `ABSOLUTE` — the universal default for every framework component.\n * - `FIXED` — the documented exception for floating overlays that anchor to\n * the viewport (`AnimatedDropdown`, `Popover`, `Notification`, `Dialog`,\n * `DialogBackdrop`). Set internally by these subclasses; never by callers.\n * - `STATIC` — the documented exception for an HTML element whose native\n * semantics require in-flow rendering (currently only `Legend`, which needs\n * the notch in its parent `<fieldset>`'s border).\n *\n * Other CSS values (`relative`, `sticky`, `initial`, `inherit`) are\n * intentionally **not** members. Application code does not call\n * `setPosition` — that setter is `protected` on `Component`.\n *\n * @category Util\n */\nexport enum Position {\n STATIC = \"static\",\n FIXED = \"fixed\",\n ABSOLUTE = \"absolute\",\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./DOM-
|
|
2
|
-
//# sourceMappingURL=ProgressSpinner-
|
|
1
|
+
import{t as e}from"./DOM-C-Edb3tA.js";import{b as t,h as n,t as r}from"./Component-B0ShfPVi.js";t.ensureKeyframes(`ts-ui-progress-spinner-rotate`,`from { transform: rotate(0deg); } to { transform: rotate(360deg); }`);var i=3;function readThemeFontSizePx(){let t=e.source.getThemeVar(`--ts-ui-font-size`),n=parseFloat(t);return isNaN(n)?14:n}var ProgressSpinner=class extends r{_arc;_size;_trackThemeFontSize;_themeFontSizeResolved=!1;_overlayTarget=null;constructor(e,t){super(),this._trackThemeFontSize=e===void 0,this._size=this._trackThemeFontSize?14:e,this.clearInsets(),this._arc=new r,this._arc.setBorderRadius(`50%`),this._arc.setBorder({border:`${i}px solid var(--ts-ui-progress-spinner-color, rgb(30, 100, 200))`,borderTop:`${i}px solid transparent`}),this._arc.setAnimation(`ts-ui-progress-spinner-rotate 0.8s linear infinite`),super.addComponent(this._arc),this.setPreferredSize({width:this._size,height:this._size}),this._trackThemeFontSize&&this.subscribeTheme(()=>{if(!this._trackThemeFontSize)return;let e=readThemeFontSizePx();e!==this._size&&(this._size=e,this.setPreferredSize({width:e,height:e}),this.scheduleLayout())}),this.getAria().setRole(`status`),this.getAria().setLabel(`Loading`),t&&this.applyOptions(t)}applyOptions(e){return super.applyOptions(e),e.spinnerSize!==void 0&&this.setSpinnerSize(e.spinnerSize),this}getSpinnerSize(){return this._size}getBaseline(){let e=this.getPreferredSize();return e?e.height-2:null}setSpinnerSize(e){return this._trackThemeFontSize=!1,this._size===e?this:(this._size=e,this.setPreferredSize({width:e,height:e}),this.scheduleLayout(),this)}showOverlay(t){if(this._overlayTarget)return;this._overlayTarget=t,this.setBackgroundColor(`var(--ts-ui-progress-spinner-backdrop, rgba(255, 255, 255, 0.6))`),this.setZIndex(9999);let n=t.getElement(!0),r=this.getElement(!0);e.sink.appendChild(n,r),this.setX(0),this.setY(0),this.setSize({width:t.getWidth(),height:t.getHeight()}),this.doLayout()}hideOverlay(){this._overlayTarget&&(this._overlayTarget=null,this.removeElement(),this.clearBackgroundColor(),this.setZIndex(0))}isOverlay(){return this._overlayTarget!==null}doLayout(){if(this._trackThemeFontSize&&!this._themeFontSizeResolved){this._themeFontSizeResolved=!0;let e=readThemeFontSizePx();e!==this._size&&(this._size=e,this.setPreferredSize({width:e,height:e}))}this._overlayTarget&&this.setSize({width:this._overlayTarget.getWidth(),height:this._overlayTarget.getHeight()});let e=this.getContentBounds();if(!e)return super.doLayout(),this;let t=Math.min(this._size,e.width,e.height),n=e.x+Math.round((e.width-t)/2),r=e.y+Math.round((e.height-t)/2);return this._arc.setX(n),this._arc.setY(r),this._arc.setSize({width:t,height:t}),super.doLayout(),this}},a=n(ProgressSpinner);export{a as t};
|
|
2
|
+
//# sourceMappingURL=ProgressSpinner-DK8xEQ9y.js.map
|