@jimka/typescript-ui 0.1.0 → 0.1.1
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/README.md +5 -18
- package/dist/lib/AbstractBooleanInput-C6CidMMX.js +2 -0
- package/dist/lib/AbstractBooleanInput-C6CidMMX.js.map +1 -0
- package/dist/lib/AbstractInput-CfjW_HM2.js +2 -0
- package/dist/lib/AbstractInput-CfjW_HM2.js.map +1 -0
- package/dist/lib/AnimatedDropdown-BFz-DO7R.js +2 -0
- package/dist/lib/AnimatedDropdown-BFz-DO7R.js.map +1 -0
- package/dist/lib/Border-5Ki2DCWz.js +2 -0
- package/dist/lib/Border-5Ki2DCWz.js.map +1 -0
- package/dist/lib/BoxLayout-8ryMD4j5.js +2 -0
- package/dist/lib/BoxLayout-8ryMD4j5.js.map +1 -0
- package/dist/lib/Button-Cdq2ySPL.js +2 -0
- package/dist/lib/Button-Cdq2ySPL.js.map +1 -0
- package/dist/lib/Card-AKIUAWYS.js +2 -0
- package/dist/lib/Card-AKIUAWYS.js.map +1 -0
- package/dist/lib/ComboBox-e_YVsWBr.js +2 -0
- package/dist/lib/ComboBox-e_YVsWBr.js.map +1 -0
- package/dist/lib/Component-Bs6TvaGZ.js +2 -0
- package/dist/lib/Component-Bs6TvaGZ.js.map +1 -0
- package/dist/lib/Container-CzyZeMyZ.js +2 -0
- package/dist/lib/Container-CzyZeMyZ.js.map +1 -0
- package/dist/lib/DOM-DeF_dWe2.js +2 -0
- package/dist/lib/DOM-DeF_dWe2.js.map +1 -0
- package/dist/lib/DragManager-c6M_mE3e.js +2 -0
- package/dist/lib/DragManager-c6M_mE3e.js.map +1 -0
- package/dist/lib/FieldDecorator-Bl0h9UEF.js +2 -0
- package/dist/lib/FieldDecorator-Bl0h9UEF.js.map +1 -0
- package/dist/lib/Fit-CUS1JXy3.js +2 -0
- package/dist/lib/Fit-CUS1JXy3.js.map +1 -0
- package/dist/lib/Glyph-6Ki3A1_a.js +2 -0
- package/dist/lib/Glyph-6Ki3A1_a.js.map +1 -0
- package/dist/lib/Grid-D8luwRPI.js +2 -0
- package/dist/lib/Grid-D8luwRPI.js.map +1 -0
- package/dist/lib/GridConstraints-CzC0w4i-.js +2 -0
- package/dist/lib/GridConstraints-CzC0w4i-.js.map +1 -0
- package/dist/lib/HBox-BWMf9h6r.js +2 -0
- package/dist/lib/HBox-BWMf9h6r.js.map +1 -0
- package/dist/lib/Header-CQjomjo0.js +2 -0
- package/dist/lib/Header-CQjomjo0.js.map +1 -0
- package/dist/lib/IconText-BXJK0MOq.js +2 -0
- package/dist/lib/IconText-BXJK0MOq.js.map +1 -0
- package/dist/lib/LayoutSerialization-C_zcwaN3.js +2 -0
- package/dist/lib/LayoutSerialization-C_zcwaN3.js.map +1 -0
- package/dist/lib/LayoutSizes-XdhpAOmc.js +2 -0
- package/dist/lib/LayoutSizes-XdhpAOmc.js.map +1 -0
- package/dist/lib/List-BgxnLWnl.js +2 -0
- package/dist/lib/List-BgxnLWnl.js.map +1 -0
- package/dist/lib/Menu-BdnwRY0M.js +2 -0
- package/dist/lib/Menu-BdnwRY0M.js.map +1 -0
- package/dist/lib/MenuButton-D15cSbMf.js +2 -0
- package/dist/lib/MenuButton-D15cSbMf.js.map +1 -0
- package/dist/lib/Panel-DApa7vA6.js +2 -0
- package/dist/lib/Panel-DApa7vA6.js.map +1 -0
- package/dist/lib/Position-DJKB0N3l.js +2 -0
- package/dist/lib/Position-DJKB0N3l.js.map +1 -0
- package/dist/lib/ProgressSpinner-CNYcsBhw.js +2 -0
- package/dist/lib/ProgressSpinner-CNYcsBhw.js.map +1 -0
- package/dist/lib/RadioButton-CepkneSA.js +2 -0
- package/dist/lib/RadioButton-CepkneSA.js.map +1 -0
- package/dist/lib/RovingTabIndex-D-zOuptw.js +2 -0
- package/dist/lib/RovingTabIndex-D-zOuptw.js.map +1 -0
- package/dist/lib/Scrollbar-DQdCdj94.js +2 -0
- package/dist/lib/Scrollbar-DQdCdj94.js.map +1 -0
- package/dist/lib/Slider-DvDdVvRm.js +2 -0
- package/dist/lib/Slider-DvDdVvRm.js.map +1 -0
- package/dist/lib/Spacer-BqiPiCqz.js +2 -0
- package/dist/lib/Spacer-BqiPiCqz.js.map +1 -0
- package/dist/lib/TabButton-DqYDfN7P.js +2 -0
- package/dist/lib/TabButton-DqYDfN7P.js.map +1 -0
- package/dist/lib/Text-DJoUHukb.js +2 -0
- package/dist/lib/Text-DJoUHukb.js.map +1 -0
- package/dist/lib/Tooltip-5BR7yYEX.js +3 -0
- package/dist/lib/Tooltip-5BR7yYEX.js.map +1 -0
- package/dist/lib/VBox-CdV9VGxR.js +2 -0
- package/dist/lib/VBox-CdV9VGxR.js.map +1 -0
- package/dist/lib/VirtualRowView-C18j90n5.js +2 -0
- package/dist/lib/VirtualRowView-C18j90n5.js.map +1 -0
- package/dist/lib/VirtualScroller-BKMvTisA.js +2 -0
- package/dist/lib/VirtualScroller-BKMvTisA.js.map +1 -0
- package/dist/lib/component/button.es.js +1 -1
- package/dist/lib/component/button.es.js.map +1 -1
- package/dist/lib/component/chart.es.js +1 -1
- package/dist/lib/component/chart.es.js.map +1 -1
- package/dist/lib/component/container.es.js +1 -1
- package/dist/lib/component/container.es.js.map +1 -1
- package/dist/lib/component/diagram.es.js +1 -1
- package/dist/lib/component/diagram.es.js.map +1 -1
- package/dist/lib/component/display.es.js +1 -1
- package/dist/lib/component/display.es.js.map +1 -1
- package/dist/lib/component/editor.es.js +3 -1
- package/dist/lib/component/editor.es.js.map +1 -1
- package/dist/lib/component/input.es.js +1 -1
- package/dist/lib/component/input.es.js.map +1 -1
- package/dist/lib/component/list.es.js +1 -1
- package/dist/lib/component/list.es.js.map +1 -1
- package/dist/lib/component/menubar.es.js +1 -1
- package/dist/lib/component/menubar.es.js.map +1 -1
- package/dist/lib/component/table.es.js +2 -2
- package/dist/lib/component/table.es.js.map +1 -1
- package/dist/lib/component/tree.es.js +1 -1
- package/dist/lib/component/tree.es.js.map +1 -1
- package/dist/lib/core.es.js +1 -1
- package/dist/lib/core.es.js.map +1 -1
- package/dist/lib/layout.es.js +1 -1
- package/dist/lib/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 +2 -0
- package/dist/lib/router.es.js.map +1 -0
- package/dist/lib/types/component/container/SplitGutter.d.ts +1 -1
- package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
- package/dist/lib/types/component/display/Markdown.d.ts +10 -0
- package/dist/lib/types/component/display/index.d.ts +1 -1
- package/dist/lib/types/component/editor/MarkdownEditor.d.ts +6 -0
- package/dist/lib/types/component/editor/markdownTableTransformer.d.ts +2 -0
- package/dist/lib/types/core/DOM.d.ts +6 -0
- package/dist/lib/types/overlay/AbstractWindow.d.ts +1 -1
- package/dist/lib/types/router/RoutePattern.d.ts +18 -0
- package/dist/lib/types/router/Router.d.ts +36 -0
- package/dist/lib/types/router/index.d.ts +2 -0
- package/dist/lib/validation.es.js +1 -1
- package/llms.txt +8 -4
- package/package.json +6 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{t as e}from"../DOM-DeF_dWe2.js";import{n as t}from"../Position-DJKB0N3l.js";import{f as n,t as r}from"../Component-Bs6TvaGZ.js";import{t as i}from"../Glyph-6Ki3A1_a.js";import{t as a}from"../Text-DJoUHukb.js";import{i as o,n as s,r as c,t as l}from"../List-BgxnLWnl.js";import{t as u}from"../reduceModifierSelection-DTo8HyFl.js";var MultiSelectList=class extends s{constructor(e){super(e),this.getAria().setMultiselectable(!0),this._options.selectedIndices!==void 0&&this.applyInitialSelection(this._options.selectedIndices)}applyOptions(e){return super.applyOptions(e),e.selectedIndices!==void 0&&(this._options.selectedIndices=e.selectedIndices),this}setValues(e){let t=new Set(e);this._selectedSet.clear();for(let e=0;e<this._items.length;e++)t.has(this._items[e].key)&&this._selectedSet.add(e);return this._selectedSet.size>0?this._anchorIndex=Math.max(...this._selectedSet):this._anchorIndex=null,this.refreshRowVisualState(),this.updateActiveDescendant(),this}setValue(e){return this.setValues(e)}getValue(){return[...this._selectedSet].sort((e,t)=>e-t).map(e=>this._items[e].key)}getSelectedRecords(){let e=this.getStore();if(!e)return[];let t=e.getRecords(),n=[...this._selectedSet].sort((e,t)=>e-t),r=[];for(let e of n)e<t.length&&r.push(t[e]);return r}setSelectedRecords(e){let t=this.getStore();if(!t)return this;let n=new Set(e),r=t.getRecords(),i=[];for(let e=0;e<this._items.length&&e<r.length;e++)n.has(r[e])&&i.push(this._items[e].key);return this.setValues(i)}reduceSelection(e,t){this._anchorIndex=u(this._selectedSet,this._anchorIndex,e,e=>e,e=>e,t),this._focusedIndex=e}notifyUserChange(){this.fireChange()}handleKeyDown(e){if(!(!this.isEnabled()||this.isReadOnly())){if((e.ctrlKey||e.metaKey)&&(e.key===`a`||e.key===`A`)){e.preventDefault(),this.selectAll();return}super.handleKeyDown(e)}}selectAll(){if(this._items.length!==0){this._selectedSet.clear();for(let e=0;e<this._items.length;e++)this._selectedSet.add(e);this._anchorIndex=0,this._focusedIndex=this._items.length-1,this.refreshRowVisualState(),this.updateActiveDescendant(),this.notifyUserChange()}}applyInitialSelection(e){this._selectedSet.clear();for(let t of e)t>=0&&t<this._items.length&&this._selectedSet.add(t);this._selectedSet.size>0&&(this._anchorIndex=Math.max(...this._selectedSet)),this.refreshRowVisualState(),this.updateActiveDescendant()}},d=n(MultiSelectList),AbstractMarkerList=class extends r{constructor(e,n,r,i){super(r,{tag:e,preferredSize:{width:200,height:200},padding:new t(0,0,0,25),itemStyle:n,...i??{}})}applyOptions(e){return super.applyOptions(e),this.setStyle(e.itemStyle??this.getStyle()),this}getStyle(){return this._style??this._defaultOptions.itemStyle}setStyle(e){return this._style=e,this.setElementCSSRule(`list-style-type`,e),this}addComponent(e,t){return super.addComponent(e,t),this}removeComponent(e){return super.removeComponent(e)}},f=function(e){return e.NONE=`none`,e.DISC=`disc`,e.CIRCLE=`circle`,e.SQUARE=`square`,e}({}),BulletedList=class extends AbstractMarkerList{constructor(e){super(`ul`,f.DISC,e)}},p=n(BulletedList),m=function(e){return e.NONE=`none`,e.DECIMAL=`decimal`,e.DECIMAL_LEADING_ZERO=`decimal-leading-zero`,e.LOWER_ALPHA=`lower-alpha`,e.LOWER_GREEK=`lower-greek`,e.LOWER_LATIN=`lower-latin`,e.LOWER_ROMAN=`lower-roman`,e.UPPER_ALPHA=`upper-alpha`,e.UPPER_GREEK=`upper-greek`,e.UPPER_LATIN=`upper-latin`,e.UPPER_ROMAN=`upper-roman`,e}({}),NumberedList=class extends AbstractMarkerList{constructor(e){super(`ol`,m.DECIMAL,e)}},h=n(NumberedList),g={tag:`li`},ListItem=class extends r{_key;constructor(e,t,n){super(n,g),this._key=e,this._value=n?.text??t}applyOptions(t){if(super.applyOptions(t),t.text!==void 0){this._value=t.text;let n=this.getElement();n&&e.sink.apply(n,{text:t.text})}return this}applyStyle(){return this}getKey(){return this._key}render(){let t=super.render();return e.sink.apply(t,{dataset:{key:this._key},text:this._value}),t}},_=n(ListItem),v=20,y=16,GlyphListItemRenderer=class extends o{_icon=null;_label;_currentGlyph=null;_measured=!1;constructor(){super(),this.clearInsets(),this._label=new a,this._label.clearInsets(),this._label.setAutoMeasure(!1)}update(t){let n=t.item.glyph?t.item.glyph:null;if(n!==this._currentGlyph){let t=this.getElement();if(t&&this._icon){let n=this._icon.getElement();n&&e.source.getParentNode(n)===t&&e.sink.removeChild(t,n)}this._icon=null,this._currentGlyph=n,n!==null&&(this._icon=new i(n),this._icon.clearInsets(),this._icon.setPointerEvents(`none`),t&&e.sink.insertBefore(t,this._icon.getElement(!0),this._label.getElement()??null))}this._label.setText(t.item.label),this._measured=!1}getContentWidth(){this._measured||=(this._label.measure(),!0);let e=this._label.getPreferredSize()?.width??0;return(this._icon?v:0)+e}layoutChildren(e,t){this._icon&&(this._icon.setAutoCommitStyle(!1),this._icon.setX(0),this._icon.setY(Math.max(0,(t-y)/2)),this._icon.setWidth(y),this._icon.setHeight(y),this._icon.setAutoCommitStyle(!0));let n=this._icon?v:0,r=Math.max(0,e-n);this._label.setAutoCommitStyle(!1),this._label.setX(n),this._label.setY(0),this._label.setWidth(r),this._label.setHeight(t),this._label.setLineHeight(t),this._label.setAutoCommitStyle(!0)}init(t){super.init(t);let n=t||this.getElement();return n?(this._icon&&e.sink.appendChild(n,this._icon.getElement(!0)),e.sink.appendChild(n,this._label.getElement(!0)),this):this}},b=n(GlyphListItemRenderer);export{AbstractMarkerList,p as BulletedList,f as BulletedListItemStyle,b as GlyphListItemRenderer,c as LabelListItemRenderer,l as List,_ as ListItem,o as ListItemRenderer,d as MultiSelectList,h as NumberedList,m as NumberedListItemStyle};
|
|
2
2
|
//# sourceMappingURL=list.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.es.js","names":[],"sources":["../../../src/typescript/lib/component/list/MultiSelectList.ts","../../../src/typescript/lib/component/list/AbstractMarkerList.ts","../../../src/typescript/lib/component/list/BulletedListItemStyle.ts","../../../src/typescript/lib/component/list/BulletedList.ts","../../../src/typescript/lib/component/list/NumberedListItemStyle.ts","../../../src/typescript/lib/component/list/NumberedList.ts","../../../src/typescript/lib/component/list/ListItem.ts","../../../src/typescript/lib/component/list/renderer/Glyph.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { AbstractSelectableList, AbstractSelectableListOptions } from \"~/component/list/AbstractSelectableList.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { reduceModifierSelection } from \"~/component/shared/reduceModifierSelection.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MultiSelectList}.\n *\n * @category Components\n */\nexport interface MultiSelectListOptions extends AbstractSelectableListOptions {\n selectedIndices?: number[];\n}\n\n/**\n * A scrollable multi-selection list box.\n *\n * Rendered as a `<div role=\"listbox\" aria-multiselectable=\"true\">`\n * populated with `<div role=\"option\">` rows. Implements\n * [`Bindable<string[]>`](/api/core/interfaces/Bindable) so it can be\n * plugged into a [`Binding`](/api/core/classes/Binding) directly — earlier\n * versions required explicit `BindingAccessors` because the single-value\n * `Bindable<string>` contract didn't fit. Selection follows the\n * standard modifier-key model:\n *\n * - Plain click / Enter / Space replaces the selection with the\n * clicked row.\n * - Ctrl-click (Cmd on macOS) / Ctrl+Space toggles the clicked row's\n * selection.\n * - Shift-click / Shift+Arrow extends the selection from the anchor\n * row to the targeted row.\n *\n * @category Components\n */\nclass MultiSelectList extends AbstractSelectableList<string[], MultiSelectListOptions> {\n\n /**\n * @param options - Optional. Construction-time options applied to\n * the list.\n */\n constructor(options?: MultiSelectListOptions) {\n super(options);\n\n this.getAria().setMultiselectable(true);\n\n // Late-built state: `selectedIndices` was written pure to\n // `_options` by the super-time cascade. Dispatch it now that the\n // row pool exists (`super()` already populated `_items` from\n // `items` / `store`, and applied `enabled` / `readOnly`).\n if (this._options.selectedIndices !== undefined) {\n this.applyInitialSelection(this._options.selectedIndices);\n }\n }\n\n /**\n * Applies a {@link MultiSelectListOptions} bag. Multi-select state\n * fields are written pure into `_options` here and dispatched from the\n * constructor body after the row pool is built.\n *\n * @param options - The options bag carrying the values to apply.\n *\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: MultiSelectListOptions): this {\n super.applyOptions(options);\n\n if (options.selectedIndices !== undefined) this._options.selectedIndices = options.selectedIndices;\n\n return this;\n }\n\n /**\n * Replaces the selection with the rows whose keys appear in\n * `values`. Programmatic — does not fire the `change` event; user-\n * driven gestures route through the click / keyboard reducer\n * instead.\n *\n * @param values - The row keys to select.\n *\n * @returns This component, for method chaining.\n */\n setValues(values: string[]): this {\n const valueSet = new Set(values);\n\n this._selectedSet.clear();\n\n for (let i = 0; i < this._items.length; i++) {\n if (valueSet.has(this._items[i].key)) {\n this._selectedSet.add(i);\n }\n }\n\n // Park the anchor at the last selected index when there is one,\n // so a follow-up Shift-arrow extends from a sensible position.\n if (this._selectedSet.size > 0) {\n this._anchorIndex = Math.max(...this._selectedSet);\n } else {\n this._anchorIndex = null;\n }\n\n this.refreshRowVisualState();\n this.updateActiveDescendant();\n\n return this;\n }\n\n /**\n * `Bindable<string[]>` write-side — alias of {@link setValues}.\n *\n * @param value - The row keys to select.\n *\n * @returns This component, for method chaining.\n */\n setValue(value: string[]): this {\n return this.setValues(value);\n }\n\n /**\n * Returns the keys of every currently selected row, sorted by row\n * order. Satisfies the `Bindable<string[]>` read-side.\n *\n * @returns The selected row keys.\n */\n getValue(): string[] {\n const indices = [...this._selectedSet].sort((a, b) => a - b);\n\n return indices.map(i => this._items[i].key);\n }\n\n /**\n * Returns the store records that correspond to the currently\n * selected rows.\n *\n * @returns The selected [`ModelRecord`](/api/data/classes/ModelRecord)\n * instances in row order, or an empty array when no store is bound.\n */\n getSelectedRecords(): ModelRecord[] {\n const store = this.getStore();\n\n if (!store) {\n return [];\n }\n\n const records = store.getRecords();\n const indices = [...this._selectedSet].sort((a, b) => a - b);\n const result: ModelRecord[] = [];\n\n for (const i of indices) {\n if (i < records.length) {\n result.push(records[i]);\n }\n }\n\n return result;\n }\n\n /**\n * Replaces the selection with the rows whose backing records appear\n * in `records`. Relies on the parallel ordering of `_items` and\n * `store.getRecords()` that the store-bound refresh guarantees — see\n * [`setStore`](/api/component/list/classes/MultiSelectList#setstore).\n *\n * @param records - The store records to select.\n *\n * @returns This component, for method chaining.\n */\n setSelectedRecords(records: ModelRecord[]): this {\n const store = this.getStore();\n\n if (!store) {\n return this;\n }\n\n const recordSet = new Set(records);\n const storeRecords = store.getRecords();\n const values: string[] = [];\n\n for (let i = 0; i < this._items.length && i < storeRecords.length; i++) {\n if (recordSet.has(storeRecords[i])) {\n values.push(this._items[i].key);\n }\n }\n\n return this.setValues(values);\n }\n\n /**\n * Reduces a click / keyboard gesture into the multi-select shape.\n * Ported verbatim from [`Body.onRowClick`](/api/component/table/classes/Body) — the same modifier\n * rules apply: plain replaces, `ctrl` toggles individual rows,\n * `shift` extends the selection from the anchor row to `idx`.\n *\n * @param idx - The row index the gesture targeted.\n * @param ev - The normalised modifier flags.\n */\n protected reduceSelection(idx: number, ev: { ctrl: boolean, shift: boolean }): void {\n this._anchorIndex = reduceModifierSelection(\n this._selectedSet,\n this._anchorIndex,\n idx,\n i => i,\n i => i,\n ev,\n );\n\n this._focusedIndex = idx;\n }\n\n /**\n * User-driven selection commit. Fires the `change` event the same\n * way the prior native `<select multiple>` did — only user-driven\n * gestures (click / keyboard reducer) route through it.\n */\n protected notifyUserChange(): void {\n this.fireChange();\n }\n\n /**\n * Extends the base keyboard map with `Ctrl+A` (select-all) — every\n * other key delegates to {@link AbstractSelectableList.handleKeyDown}.\n *\n * @param e - The keyboard event.\n */\n protected handleKeyDown(e: KeyboardEvent): void {\n if (!this.isEnabled() || this.isReadOnly()) {\n return;\n }\n\n const ctrl = e.ctrlKey || e.metaKey;\n\n if (ctrl && (e.key === \"a\" || e.key === \"A\")) {\n e.preventDefault();\n this.selectAll();\n\n return;\n }\n\n super.handleKeyDown(e);\n }\n\n /**\n * Selects every row in the list. Used by the Ctrl+A keyboard shortcut.\n */\n protected selectAll(): void {\n if (this._items.length === 0) {\n return;\n }\n\n this._selectedSet.clear();\n\n for (let i = 0; i < this._items.length; i++) {\n this._selectedSet.add(i);\n }\n\n this._anchorIndex = 0;\n this._focusedIndex = this._items.length - 1;\n\n this.refreshRowVisualState();\n this.updateActiveDescendant();\n this.notifyUserChange();\n }\n\n /**\n * Construction-tail dispatch of `selectedIndices`. Writes the\n * indices directly into the selection set without firing `change`,\n * matching the construct-time semantics of `setValues` (programmatic\n * writes are silent; user gestures emit `change`).\n *\n * @param indices - Initial selection indices from the options bag.\n */\n private applyInitialSelection(indices: number[]): void {\n this._selectedSet.clear();\n\n for (const i of indices) {\n if (i >= 0 && i < this._items.length) {\n this._selectedSet.add(i);\n }\n }\n\n if (this._selectedSet.size > 0) {\n this._anchorIndex = Math.max(...this._selectedSet);\n }\n\n this.refreshRowVisualState();\n this.updateActiveDescendant();\n }\n}\n\nconst MultiSelectListCallable = callable(MultiSelectList);\ntype MultiSelectListCallable = MultiSelectList;\nexport {\n MultiSelectList as _MultiSelectList,\n MultiSelectListCallable as MultiSelectList,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { BulletedListItemStyle } from \"~/component/list/BulletedListItemStyle.js\";\nimport { NumberedListItemStyle } from \"~/component/list/NumberedListItemStyle.js\";\nimport { ListItem } from \"~/component/list/ListItem.js\";\n\n/**\n * Construction-time options for {@link AbstractMarkerList}.\n *\n * @category Components\n */\nexport interface AbstractMarkerListOptions<U extends BulletedListItemStyle | NumberedListItemStyle> extends ComponentOptions {\n itemStyle?: U;\n}\n\n/**\n * Abstract base for bulleted and numbered list components.\n *\n * Manages the CSS list-style-type, selection state, and restricts child components to\n * ListItem instances. Concrete subclasses supply the HTML tag and default style.\n */\nexport abstract class AbstractMarkerList<U extends BulletedListItemStyle | NumberedListItemStyle> extends Component<AbstractMarkerListOptions<U>> {\n\n // `declare` rather than initializer to dodge the class-field\n // super-cascade trap: the cascade-time setStyle write would otherwise\n // be clobbered by a `= undefined` initializer running after super().\n // The cascade always dispatches setStyle because the constructor seeds\n // `itemStyle: style` into the defaults bag below.\n declare private _style: U | undefined;\n\n constructor(\n tag: string,\n style: U,\n options?: AbstractMarkerListOptions<U>,\n subclassDefaults?: Partial<AbstractMarkerListOptions<U>>,\n ) {\n super(options, {\n tag,\n preferredSize: { width: 200, height: 200 },\n padding: new Insets(0, 0, 0, 25),\n itemStyle: style,\n ...(subclassDefaults ?? {}),\n } as Partial<AbstractMarkerListOptions<U>>);\n }\n\n /**\n * Applies an {@link AbstractMarkerListOptions} bag, dispatching the list bullet\n * style and initial selection after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: AbstractMarkerListOptions<U>): this {\n super.applyOptions(options);\n\n // `itemStyle` is always defaulted (the constructor seeds the required\n // `style` param into `_defaultOptions`), so always dispatch the caller\n // value or the class default — this seeds the `declare`d `_style` field\n // and queues the `list-style-type` rule without touching `_options`.\n this.setStyle(options.itemStyle ?? this.getStyle()!);\n\n return this;\n }\n\n /**\n * Returns the current list-style-type value.\n *\n * @returns The current style enum value, or undefined if not yet set.\n */\n getStyle() {\n return this._style ?? this._defaultOptions.itemStyle;\n }\n\n /**\n * Sets the list-style-type CSS property.\n *\n * @param style - The list item style enum value to apply.\n */\n setStyle(style: U) : this {\n this._style = style;\n this.setElementCSSRule(\"list-style-type\", style);\n\n return this;\n }\n\n /**\n * Adds a ListItem child; restricts the type accepted by this container to ListItem.\n *\n * @param component - The ListItem to add.\n * @param constraints - Optional. Layout constraints for the item.\n */\n addComponent(component: ListItem, constraints?: LayoutConstraints): this {\n super.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Removes a ListItem child.\n *\n * @param component - The ListItem instance to remove.\n *\n * @returns The layout constraints that were registered for the removed item, or undefined.\n */\n removeComponent(component: ListItem) {\n return super.removeComponent(component);\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * CSS list-style-type values for bulleted (unordered) lists.\n *\n * @category Components\n */\nexport enum BulletedListItemStyle {\n NONE = \"none\",\n DISC = \"disc\",\n CIRCLE = \"circle\",\n SQUARE = \"square\"\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { AbstractMarkerList, AbstractMarkerListOptions } from \"~/component/list/AbstractMarkerList.js\";\nimport { BulletedListItemStyle } from \"~/component/list/BulletedListItemStyle.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link BulletedList}.\n *\n * @category Components\n */\nexport interface BulletedListOptions extends AbstractMarkerListOptions<BulletedListItemStyle> {\n}\n\n/**\n * An unordered (bulleted) list component.\n *\n * Renders a `<ul>` element and defaults to the DISC bullet style.\n *\n * @category Components\n */\nclass BulletedList extends AbstractMarkerList<BulletedListItemStyle> {\n\n constructor(options?: BulletedListOptions) {\n super(\"ul\", BulletedListItemStyle.DISC, options);\n }\n}\n\nconst BulletedListCallable = callable(BulletedList);\ntype BulletedListCallable = BulletedList;\nexport {\n BulletedList as _BulletedList,\n BulletedListCallable as BulletedList\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * CSS list-style-type values for numbered (ordered) lists.\n *\n * @category Components\n */\nexport enum NumberedListItemStyle {\n NONE = \"none\",\n DECIMAL = \"decimal\",\n DECIMAL_LEADING_ZERO = \"decimal-leading-zero\",\n LOWER_ALPHA = \"lower-alpha\",\n LOWER_GREEK = \"lower-greek\",\n LOWER_LATIN = \"lower-latin\",\n LOWER_ROMAN = \"lower-roman\",\n UPPER_ALPHA = \"upper-alpha\",\n UPPER_GREEK = \"upper-greek\",\n UPPER_LATIN = \"upper-latin\",\n UPPER_ROMAN = \"upper-roman\"\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { AbstractMarkerList, AbstractMarkerListOptions } from \"~/component/list/AbstractMarkerList.js\";\nimport { NumberedListItemStyle } from \"~/component/list/NumberedListItemStyle.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link NumberedList}.\n *\n * @category Components\n */\nexport interface NumberedListOptions extends AbstractMarkerListOptions<NumberedListItemStyle> {\n}\n\n/**\n * An ordered (numbered) list component.\n *\n * Renders an `<ol>` element and defaults to the DECIMAL numbering style.\n *\n * @category Components\n */\nclass NumberedList extends AbstractMarkerList<NumberedListItemStyle> {\n\n constructor(options?: NumberedListOptions) {\n super(\"ol\", NumberedListItemStyle.DECIMAL, options);\n }\n}\n\nconst NumberedListCallable = callable(NumberedList);\ntype NumberedListCallable = NumberedList;\nexport {\n NumberedList as _NumberedList,\n NumberedListCallable as NumberedList\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\n\n/**\n * Construction-time options for {@link ListItem}.\n *\n * @category Components\n */\nexport interface ListItemOptions extends ComponentOptions {\n text?: string;\n}\n\n/**\n * Class-level defaults. Only `tag` is a genuine class default; the\n * positional `value` is a per-instance value resolved in the constructor\n * body, not smuggled through this bag.\n */\nconst _defaultListItemOptions: Partial<ListItemOptions> = {\n tag: \"li\",\n};\n\n/**\n * A single list item component backed by a `<li>` element.\n *\n * Stores a key/value pair and suppresses framework positioning styles so\n * the browser can render the item natively inside a list.\n *\n * @category Components\n */\nclass ListItem extends Component<ListItemOptions> {\n\n private _key: string;\n // Resolved in the constructor body from the positional `value` (or a\n // caller-supplied `text` option). `declare` rather than an initializer so\n // a `text` option written by the super-time `applyOptions` cascade is not\n // clobbered by a field initializer running after super().\n declare private _value: string;\n\n constructor(key: string, value: string, options?: ListItemOptions) {\n super(options, _defaultListItemOptions);\n\n this._key = key;\n // `value` is positional (per-instance), so it is not a class default —\n // resolve it here; an explicit `text` option still wins.\n this._value = options?.text ?? value;\n }\n\n /**\n * Applies a {@link ListItemOptions} bag, dispatching the item's display\n * text after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: ListItemOptions): this {\n super.applyOptions(options);\n\n if (options.text !== undefined) {\n this._value = options.text;\n const element = this.getElement();\n if (element) {\n DOM.sink.apply(element, { text: options.text });\n }\n }\n\n return this;\n }\n\n /**\n * Overrides applyStyle as a no-op; framework positioning styles break native list rendering.\n *\n * @returns This component, for method chaining.\n */\n applyStyle(): this {\n // Framework styles (absolute positioning etc.) break native list rendering.\n return this;\n }\n\n /**\n * Returns the data-key identifier for this list item.\n *\n * @returns The key string.\n */\n getKey() {\n return this._key;\n }\n\n /**\n * Renders the li element, sets its data-key attribute, and populates its text content.\n *\n * @returns The created HTMLElement (`<li>`) with data-key and textContent set.\n */\n render() {\n let element = super.render();\n\n DOM.sink.apply(element, { dataset: { key: this._key }, text: this._value });\n\n return element;\n }\n}\n\nconst ListItemCallable = callable(ListItem);\ntype ListItemCallable = ListItem;\nexport {\n ListItem as _ListItem,\n ListItemCallable as ListItem\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { ListItemRenderer } from \"~/component/list/ListItemRenderer.js\";\nimport { ListItemRenderContext } from \"~/component/list/ListItemRenderContext.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Pixel width reserved for the icon column (icon plus a small gap before the\n * label). Mirrors the tree icon-label renderer so a glyph list row and a glyph\n * tree row share the same icon gutter.\n */\nconst ICON_WIDTH = 20;\n\n/** Square edge length used for the icon glyph, matching the tree renderer. */\nconst ICON_SIZE = 16;\n\n/**\n * A [`ListItemRenderer`](/api/component/list/classes/ListItemRenderer) that\n * paints an icon before the item label, sourcing the icon from each item's\n * `glyph` field.\n *\n * @remarks\n * The glyph name comes from the bound item (`SelectableListItem.glyph`), populated\n * either directly on an array-supplied item or from a store record via the\n * list's `glyphField`. An item with no glyph renders label-only, with the label\n * filling the full row — matching the render-blank-on-empty contract of the\n * table glyph cell renderer. Because [`Glyph`](/api/component/display/classes/Glyph)\n * names are immutable, a fresh `Glyph` is constructed whenever the bound name\n * changes, matching the pattern `TreeRow` uses for its toggle.\n *\n * Register the glyphs the items reference before use, exactly as the table\n * glyph cell renderer requires:\n *\n * @example\n * ```typescript\n * import { star } from \"~/glyphs/solid/star.js\";\n *\n * Glyph.register(star);\n * list.setRendererFactory(() => new GlyphListItemRenderer());\n * list.setItems([{ key: \"a\", label: \"Alpha\", glyph: \"star\" }]);\n * ```\n *\n * @category Components\n */\nclass GlyphListItemRenderer extends ListItemRenderer {\n\n private _icon: Glyph | null = null;\n private _label: Text;\n private _currentGlyph: string | null = null;\n /**\n * Whether `_label`'s cached natural width matches the bound text. The label\n * runs with `autoMeasure(false)`, so the measure is driven from\n * {@link getContentWidth} rather than {@link update} — a list with\n * horizontal scrolling off never asks, and so never pays for it.\n */\n private _measured: boolean = false;\n\n /**\n * Constructs a glyph+label renderer with an empty label and no icon. Both\n * are populated on the first {@link update} call.\n */\n constructor() {\n super();\n this.clearInsets();\n\n this._label = new Text();\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Rebinds the icon (constructing a new Glyph when the item's glyph name\n * changed, removing it when the item has none) and the label text.\n *\n * @param context - The bound-item state for this render pass.\n */\n update(context: ListItemRenderContext): void {\n const next = context.item.glyph ? context.item.glyph : null;\n\n if (next !== this._currentGlyph) {\n const el = this.getElement();\n\n if (el && this._icon) {\n const oldEl = this._icon.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n }\n\n this._icon = null;\n this._currentGlyph = next;\n\n if (next !== null) {\n this._icon = new Glyph(next);\n this._icon.clearInsets();\n this._icon.setPointerEvents(\"none\");\n\n if (el) {\n DOM.sink.insertBefore(el, this._icon.getElement(true)!, this._label.getElement() ?? null);\n }\n }\n }\n\n this._label.setText(context.item.label);\n this._measured = false;\n }\n\n /**\n * Returns the natural width of the bound content — the icon gutter, when an\n * icon is bound, plus the label's natural width (measured on first ask\n * after each {@link update}). Mirrors {@link layoutChildren}'s geometry, so\n * a row sized to this width fits the icon and the whole label.\n *\n * @returns The bound content's natural width in pixels.\n */\n getContentWidth(): number {\n if (!this._measured) {\n this._label.measure();\n this._measured = true;\n }\n\n const labelWidth = this._label.getPreferredSize()?.width ?? 0;\n\n return (this._icon ? ICON_WIDTH : 0) + labelWidth;\n }\n\n /**\n * Positions the icon (vertically centred in the row) and the label. When no\n * icon is bound the label fills the full width from the left edge.\n *\n * @param width - The horizontal extent of the row in pixels.\n * @param height - The vertical extent of the row in pixels.\n */\n layoutChildren(width: number, height: number): void {\n if (this._icon) {\n this._icon.setAutoCommitStyle(false);\n this._icon.setX(0);\n this._icon.setY(Math.max(0, (height - ICON_SIZE) / 2));\n this._icon.setWidth(ICON_SIZE);\n this._icon.setHeight(ICON_SIZE);\n this._icon.setAutoCommitStyle(true);\n }\n\n const labelX = this._icon ? ICON_WIDTH : 0;\n const labelWidth = Math.max(0, width - labelX);\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(labelX);\n this._label.setY(0);\n this._label.setWidth(labelWidth);\n this._label.setHeight(height);\n this._label.setLineHeight(height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element (and the icon, when one is\n * already bound) to the renderer's DOM element. Later icon changes are\n * inserted before the label by {@link update}.\n *\n * @param element - Optional element passed by the rendering pipeline; falls\n * back to getElement().\n *\n * @returns This renderer, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n if (this._icon) {\n DOM.sink.appendChild(el, this._icon.getElement(true)!);\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n\nconst GlyphListItemRendererCallable = callable(GlyphListItemRenderer);\ntype GlyphListItemRendererCallable = GlyphListItemRenderer;\nexport {\n GlyphListItemRenderer as _GlyphListItemRenderer,\n GlyphListItemRendererCallable as GlyphListItemRenderer,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"list.es.js","names":[],"sources":["../../../src/typescript/lib/component/list/MultiSelectList.ts","../../../src/typescript/lib/component/list/AbstractMarkerList.ts","../../../src/typescript/lib/component/list/BulletedListItemStyle.ts","../../../src/typescript/lib/component/list/BulletedList.ts","../../../src/typescript/lib/component/list/NumberedListItemStyle.ts","../../../src/typescript/lib/component/list/NumberedList.ts","../../../src/typescript/lib/component/list/ListItem.ts","../../../src/typescript/lib/component/list/renderer/Glyph.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { AbstractSelectableList, AbstractSelectableListOptions } from \"~/component/list/AbstractSelectableList.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { reduceModifierSelection } from \"~/component/shared/reduceModifierSelection.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MultiSelectList}.\n *\n * @category Components\n */\nexport interface MultiSelectListOptions extends AbstractSelectableListOptions {\n selectedIndices?: number[];\n}\n\n/**\n * A scrollable multi-selection list box.\n *\n * Rendered as a `<div role=\"listbox\" aria-multiselectable=\"true\">`\n * populated with `<div role=\"option\">` rows. Implements\n * [`Bindable<string[]>`](/api/core/interfaces/Bindable) so it can be\n * plugged into a [`Binding`](/api/core/classes/Binding) directly — earlier\n * versions required explicit `BindingAccessors` because the single-value\n * `Bindable<string>` contract didn't fit. Selection follows the\n * standard modifier-key model:\n *\n * - Plain click / Enter / Space replaces the selection with the\n * clicked row.\n * - Ctrl-click (Cmd on macOS) / Ctrl+Space toggles the clicked row's\n * selection.\n * - Shift-click / Shift+Arrow extends the selection from the anchor\n * row to the targeted row.\n *\n * @category Components\n */\nclass MultiSelectList extends AbstractSelectableList<string[], MultiSelectListOptions> {\n\n /**\n * @param options - Optional. Construction-time options applied to\n * the list.\n */\n constructor(options?: MultiSelectListOptions) {\n super(options);\n\n this.getAria().setMultiselectable(true);\n\n // Late-built state: `selectedIndices` was written pure to\n // `_options` by the super-time cascade. Dispatch it now that the\n // row pool exists (`super()` already populated `_items` from\n // `items` / `store`, and applied `enabled` / `readOnly`).\n if (this._options.selectedIndices !== undefined) {\n this.applyInitialSelection(this._options.selectedIndices);\n }\n }\n\n /**\n * Applies a {@link MultiSelectListOptions} bag. Multi-select state\n * fields are written pure into `_options` here and dispatched from the\n * constructor body after the row pool is built.\n *\n * @param options - The options bag carrying the values to apply.\n *\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: MultiSelectListOptions): this {\n super.applyOptions(options);\n\n if (options.selectedIndices !== undefined) this._options.selectedIndices = options.selectedIndices;\n\n return this;\n }\n\n /**\n * Replaces the selection with the rows whose keys appear in\n * `values`. Programmatic — does not fire the `change` event; user-\n * driven gestures route through the click / keyboard reducer\n * instead.\n *\n * @param values - The row keys to select.\n *\n * @returns This component, for method chaining.\n */\n setValues(values: string[]): this {\n const valueSet = new Set(values);\n\n this._selectedSet.clear();\n\n for (let i = 0; i < this._items.length; i++) {\n if (valueSet.has(this._items[i].key)) {\n this._selectedSet.add(i);\n }\n }\n\n // Park the anchor at the last selected index when there is one,\n // so a follow-up Shift-arrow extends from a sensible position.\n if (this._selectedSet.size > 0) {\n this._anchorIndex = Math.max(...this._selectedSet);\n } else {\n this._anchorIndex = null;\n }\n\n this.refreshRowVisualState();\n this.updateActiveDescendant();\n\n return this;\n }\n\n /**\n * `Bindable<string[]>` write-side — alias of {@link setValues}.\n *\n * @param value - The row keys to select.\n *\n * @returns This component, for method chaining.\n */\n setValue(value: string[]): this {\n return this.setValues(value);\n }\n\n /**\n * Returns the keys of every currently selected row, sorted by row\n * order. Satisfies the `Bindable<string[]>` read-side.\n *\n * @returns The selected row keys.\n */\n getValue(): string[] {\n const indices = [...this._selectedSet].sort((a, b) => a - b);\n\n return indices.map(i => this._items[i].key);\n }\n\n /**\n * Returns the store records that correspond to the currently\n * selected rows.\n *\n * @returns The selected [`ModelRecord`](/api/data/classes/ModelRecord)\n * instances in row order, or an empty array when no store is bound.\n */\n getSelectedRecords(): ModelRecord[] {\n const store = this.getStore();\n\n if (!store) {\n return [];\n }\n\n const records = store.getRecords();\n const indices = [...this._selectedSet].sort((a, b) => a - b);\n const result: ModelRecord[] = [];\n\n for (const i of indices) {\n if (i < records.length) {\n result.push(records[i]);\n }\n }\n\n return result;\n }\n\n /**\n * Replaces the selection with the rows whose backing records appear\n * in `records`. Relies on the parallel ordering of `_items` and\n * `store.getRecords()` that the store-bound refresh guarantees — see\n * [`setStore`](/api/component/list/classes/MultiSelectList#setstore).\n *\n * @param records - The store records to select.\n *\n * @returns This component, for method chaining.\n */\n setSelectedRecords(records: ModelRecord[]): this {\n const store = this.getStore();\n\n if (!store) {\n return this;\n }\n\n const recordSet = new Set(records);\n const storeRecords = store.getRecords();\n const values: string[] = [];\n\n for (let i = 0; i < this._items.length && i < storeRecords.length; i++) {\n if (recordSet.has(storeRecords[i])) {\n values.push(this._items[i].key);\n }\n }\n\n return this.setValues(values);\n }\n\n /**\n * Reduces a click / keyboard gesture into the multi-select shape.\n * Ported verbatim from [`Body.onRowClick`](/api/component/table/classes/Body) — the same modifier\n * rules apply: plain replaces, `ctrl` toggles individual rows,\n * `shift` extends the selection from the anchor row to `idx`.\n *\n * @param idx - The row index the gesture targeted.\n * @param ev - The normalised modifier flags.\n */\n protected reduceSelection(idx: number, ev: { ctrl: boolean, shift: boolean }): void {\n this._anchorIndex = reduceModifierSelection(\n this._selectedSet,\n this._anchorIndex,\n idx,\n i => i,\n i => i,\n ev,\n );\n\n this._focusedIndex = idx;\n }\n\n /**\n * User-driven selection commit. Fires the `change` event the same\n * way the prior native `<select multiple>` did — only user-driven\n * gestures (click / keyboard reducer) route through it.\n */\n protected notifyUserChange(): void {\n this.fireChange();\n }\n\n /**\n * Extends the base keyboard map with `Ctrl+A` (select-all) — every\n * other key delegates to {@link AbstractSelectableList.handleKeyDown}.\n *\n * @param e - The keyboard event.\n */\n protected handleKeyDown(e: KeyboardEvent): void {\n if (!this.isEnabled() || this.isReadOnly()) {\n return;\n }\n\n const ctrl = e.ctrlKey || e.metaKey;\n\n if (ctrl && (e.key === \"a\" || e.key === \"A\")) {\n e.preventDefault();\n this.selectAll();\n\n return;\n }\n\n super.handleKeyDown(e);\n }\n\n /**\n * Selects every row in the list. Used by the Ctrl+A keyboard shortcut.\n */\n protected selectAll(): void {\n if (this._items.length === 0) {\n return;\n }\n\n this._selectedSet.clear();\n\n for (let i = 0; i < this._items.length; i++) {\n this._selectedSet.add(i);\n }\n\n this._anchorIndex = 0;\n this._focusedIndex = this._items.length - 1;\n\n this.refreshRowVisualState();\n this.updateActiveDescendant();\n this.notifyUserChange();\n }\n\n /**\n * Construction-tail dispatch of `selectedIndices`. Writes the\n * indices directly into the selection set without firing `change`,\n * matching the construct-time semantics of `setValues` (programmatic\n * writes are silent; user gestures emit `change`).\n *\n * @param indices - Initial selection indices from the options bag.\n */\n private applyInitialSelection(indices: number[]): void {\n this._selectedSet.clear();\n\n for (const i of indices) {\n if (i >= 0 && i < this._items.length) {\n this._selectedSet.add(i);\n }\n }\n\n if (this._selectedSet.size > 0) {\n this._anchorIndex = Math.max(...this._selectedSet);\n }\n\n this.refreshRowVisualState();\n this.updateActiveDescendant();\n }\n}\n\nconst MultiSelectListCallable = callable(MultiSelectList);\ntype MultiSelectListCallable = MultiSelectList;\nexport {\n MultiSelectList as _MultiSelectList,\n MultiSelectListCallable as MultiSelectList,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { BulletedListItemStyle } from \"~/component/list/BulletedListItemStyle.js\";\nimport { NumberedListItemStyle } from \"~/component/list/NumberedListItemStyle.js\";\nimport { ListItem } from \"~/component/list/ListItem.js\";\n\n/**\n * Construction-time options for {@link AbstractMarkerList}.\n *\n * @category Components\n */\nexport interface AbstractMarkerListOptions<U extends BulletedListItemStyle | NumberedListItemStyle> extends ComponentOptions {\n itemStyle?: U;\n}\n\n/**\n * Abstract base for bulleted and numbered list components.\n *\n * Manages the CSS list-style-type, selection state, and restricts child components to\n * ListItem instances. Concrete subclasses supply the HTML tag and default style.\n */\nexport abstract class AbstractMarkerList<U extends BulletedListItemStyle | NumberedListItemStyle> extends Component<AbstractMarkerListOptions<U>> {\n\n // `declare` rather than initializer to dodge the class-field\n // super-cascade trap: the cascade-time setStyle write would otherwise\n // be clobbered by a `= undefined` initializer running after super().\n // The cascade always dispatches setStyle because the constructor seeds\n // `itemStyle: style` into the defaults bag below.\n declare private _style: U | undefined;\n\n constructor(\n tag: string,\n style: U,\n options?: AbstractMarkerListOptions<U>,\n subclassDefaults?: Partial<AbstractMarkerListOptions<U>>,\n ) {\n super(options, {\n tag,\n preferredSize: { width: 200, height: 200 },\n padding: new Insets(0, 0, 0, 25),\n itemStyle: style,\n ...(subclassDefaults ?? {}),\n } as Partial<AbstractMarkerListOptions<U>>);\n }\n\n /**\n * Applies an {@link AbstractMarkerListOptions} bag, dispatching the list bullet\n * style and initial selection after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: AbstractMarkerListOptions<U>): this {\n super.applyOptions(options);\n\n // `itemStyle` is always defaulted (the constructor seeds the required\n // `style` param into `_defaultOptions`), so always dispatch the caller\n // value or the class default — this seeds the `declare`d `_style` field\n // and queues the `list-style-type` rule without touching `_options`.\n this.setStyle(options.itemStyle ?? this.getStyle()!);\n\n return this;\n }\n\n /**\n * Returns the current list-style-type value.\n *\n * @returns The current style enum value, or undefined if not yet set.\n */\n getStyle() {\n return this._style ?? this._defaultOptions.itemStyle;\n }\n\n /**\n * Sets the list-style-type CSS property.\n *\n * @param style - The list item style enum value to apply.\n */\n setStyle(style: U) : this {\n this._style = style;\n this.setElementCSSRule(\"list-style-type\", style);\n\n return this;\n }\n\n /**\n * Adds a ListItem child; restricts the type accepted by this container to ListItem.\n *\n * @param component - The ListItem to add.\n * @param constraints - Optional. Layout constraints for the item.\n */\n addComponent(component: ListItem, constraints?: LayoutConstraints): this {\n super.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Removes a ListItem child.\n *\n * @param component - The ListItem instance to remove.\n *\n * @returns The layout constraints that were registered for the removed item, or undefined.\n */\n removeComponent(component: ListItem) {\n return super.removeComponent(component);\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * CSS list-style-type values for bulleted (unordered) lists.\n *\n * @category Components\n */\nexport enum BulletedListItemStyle {\n NONE = \"none\",\n DISC = \"disc\",\n CIRCLE = \"circle\",\n SQUARE = \"square\"\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { AbstractMarkerList, AbstractMarkerListOptions } from \"~/component/list/AbstractMarkerList.js\";\nimport { BulletedListItemStyle } from \"~/component/list/BulletedListItemStyle.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link BulletedList}.\n *\n * @category Components\n */\nexport interface BulletedListOptions extends AbstractMarkerListOptions<BulletedListItemStyle> {\n}\n\n/**\n * An unordered (bulleted) list component.\n *\n * Renders a `<ul>` element and defaults to the DISC bullet style.\n *\n * @category Components\n */\nclass BulletedList extends AbstractMarkerList<BulletedListItemStyle> {\n\n constructor(options?: BulletedListOptions) {\n super(\"ul\", BulletedListItemStyle.DISC, options);\n }\n}\n\nconst BulletedListCallable = callable(BulletedList);\ntype BulletedListCallable = BulletedList;\nexport {\n BulletedList as _BulletedList,\n BulletedListCallable as BulletedList\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n/**\n * CSS list-style-type values for numbered (ordered) lists.\n *\n * @category Components\n */\nexport enum NumberedListItemStyle {\n NONE = \"none\",\n DECIMAL = \"decimal\",\n DECIMAL_LEADING_ZERO = \"decimal-leading-zero\",\n LOWER_ALPHA = \"lower-alpha\",\n LOWER_GREEK = \"lower-greek\",\n LOWER_LATIN = \"lower-latin\",\n LOWER_ROMAN = \"lower-roman\",\n UPPER_ALPHA = \"upper-alpha\",\n UPPER_GREEK = \"upper-greek\",\n UPPER_LATIN = \"upper-latin\",\n UPPER_ROMAN = \"upper-roman\"\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { AbstractMarkerList, AbstractMarkerListOptions } from \"~/component/list/AbstractMarkerList.js\";\nimport { NumberedListItemStyle } from \"~/component/list/NumberedListItemStyle.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link NumberedList}.\n *\n * @category Components\n */\nexport interface NumberedListOptions extends AbstractMarkerListOptions<NumberedListItemStyle> {\n}\n\n/**\n * An ordered (numbered) list component.\n *\n * Renders an `<ol>` element and defaults to the DECIMAL numbering style.\n *\n * @category Components\n */\nclass NumberedList extends AbstractMarkerList<NumberedListItemStyle> {\n\n constructor(options?: NumberedListOptions) {\n super(\"ol\", NumberedListItemStyle.DECIMAL, options);\n }\n}\n\nconst NumberedListCallable = callable(NumberedList);\ntype NumberedListCallable = NumberedList;\nexport {\n NumberedList as _NumberedList,\n NumberedListCallable as NumberedList\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\n\n/**\n * Construction-time options for {@link ListItem}.\n *\n * @category Components\n */\nexport interface ListItemOptions extends ComponentOptions {\n text?: string;\n}\n\n/**\n * Class-level defaults. Only `tag` is a genuine class default; the\n * positional `value` is a per-instance value resolved in the constructor\n * body, not smuggled through this bag.\n */\nconst _defaultListItemOptions: Partial<ListItemOptions> = {\n tag: \"li\",\n};\n\n/**\n * A single list item component backed by a `<li>` element.\n *\n * Stores a key/value pair and suppresses framework positioning styles so\n * the browser can render the item natively inside a list.\n *\n * @category Components\n */\nclass ListItem extends Component<ListItemOptions> {\n\n private _key: string;\n // Resolved in the constructor body from the positional `value` (or a\n // caller-supplied `text` option). `declare` rather than an initializer so\n // a `text` option written by the super-time `applyOptions` cascade is not\n // clobbered by a field initializer running after super().\n declare private _value: string;\n\n constructor(key: string, value: string, options?: ListItemOptions) {\n super(options, _defaultListItemOptions);\n\n this._key = key;\n // `value` is positional (per-instance), so it is not a class default —\n // resolve it here; an explicit `text` option still wins.\n this._value = options?.text ?? value;\n }\n\n /**\n * Applies a {@link ListItemOptions} bag, dispatching the item's display\n * text after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: ListItemOptions): this {\n super.applyOptions(options);\n\n if (options.text !== undefined) {\n this._value = options.text;\n const element = this.getElement();\n if (element) {\n DOM.sink.apply(element, { text: options.text });\n }\n }\n\n return this;\n }\n\n /**\n * Overrides applyStyle as a no-op; framework positioning styles break native list rendering.\n *\n * @returns This component, for method chaining.\n */\n applyStyle(): this {\n // Framework styles (absolute positioning etc.) break native list rendering.\n return this;\n }\n\n /**\n * Returns the data-key identifier for this list item.\n *\n * @returns The key string.\n */\n getKey() {\n return this._key;\n }\n\n /**\n * Renders the li element, sets its data-key attribute, and populates its text content.\n *\n * @returns The created HTMLElement (`<li>`) with data-key and textContent set.\n */\n render() {\n let element = super.render();\n\n DOM.sink.apply(element, { dataset: { key: this._key }, text: this._value });\n\n return element;\n }\n}\n\nconst ListItemCallable = callable(ListItem);\ntype ListItemCallable = ListItem;\nexport {\n ListItem as _ListItem,\n ListItemCallable as ListItem\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { ListItemRenderer } from \"~/component/list/ListItemRenderer.js\";\nimport { ListItemRenderContext } from \"~/component/list/ListItemRenderContext.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Pixel width reserved for the icon column (icon plus a small gap before the\n * label). Mirrors the tree icon-label renderer so a glyph list row and a glyph\n * tree row share the same icon gutter.\n */\nconst ICON_WIDTH = 20;\n\n/** Square edge length used for the icon glyph, matching the tree renderer. */\nconst ICON_SIZE = 16;\n\n/**\n * A [`ListItemRenderer`](/api/component/list/classes/ListItemRenderer) that\n * paints an icon before the item label, sourcing the icon from each item's\n * `glyph` field.\n *\n * @remarks\n * The glyph name comes from the bound item (`SelectableListItem.glyph`), populated\n * either directly on an array-supplied item or from a store record via the\n * list's `glyphField`. An item with no glyph renders label-only, with the label\n * filling the full row — matching the render-blank-on-empty contract of the\n * table glyph cell renderer. Because [`Glyph`](/api/component/display/classes/Glyph)\n * names are immutable, a fresh `Glyph` is constructed whenever the bound name\n * changes, matching the pattern `TreeRow` uses for its toggle.\n *\n * Register the glyphs the items reference before use, exactly as the table\n * glyph cell renderer requires:\n *\n * @example\n * ```typescript\n * import { star } from \"~/glyphs/solid/star.js\";\n *\n * Glyph.register(star);\n * list.setRendererFactory(() => new GlyphListItemRenderer());\n * list.setItems([{ key: \"a\", label: \"Alpha\", glyph: \"star\" }]);\n * ```\n *\n * @category Components\n */\nclass GlyphListItemRenderer extends ListItemRenderer {\n\n private _icon: Glyph | null = null;\n private _label: Text;\n private _currentGlyph: string | null = null;\n /**\n * Whether `_label`'s cached natural width matches the bound text. The label\n * runs with `autoMeasure(false)`, so the measure is driven from\n * {@link getContentWidth} rather than {@link update} — a list with\n * horizontal scrolling off never asks, and so never pays for it.\n */\n private _measured: boolean = false;\n\n /**\n * Constructs a glyph+label renderer with an empty label and no icon. Both\n * are populated on the first {@link update} call.\n */\n constructor() {\n super();\n this.clearInsets();\n\n this._label = new Text();\n this._label.clearInsets();\n this._label.setAutoMeasure(false);\n }\n\n /**\n * Rebinds the icon (constructing a new Glyph when the item's glyph name\n * changed, removing it when the item has none) and the label text.\n *\n * @param context - The bound-item state for this render pass.\n */\n update(context: ListItemRenderContext): void {\n const next = context.item.glyph ? context.item.glyph : null;\n\n if (next !== this._currentGlyph) {\n const el = this.getElement();\n\n if (el && this._icon) {\n const oldEl = this._icon.getElement();\n if (oldEl && DOM.source.getParentNode(oldEl) === el) {\n DOM.sink.removeChild(el, oldEl);\n }\n }\n\n this._icon = null;\n this._currentGlyph = next;\n\n if (next !== null) {\n this._icon = new Glyph(next);\n this._icon.clearInsets();\n this._icon.setPointerEvents(\"none\");\n\n if (el) {\n DOM.sink.insertBefore(el, this._icon.getElement(true)!, this._label.getElement() ?? null);\n }\n }\n }\n\n this._label.setText(context.item.label);\n this._measured = false;\n }\n\n /**\n * Returns the natural width of the bound content — the icon gutter, when an\n * icon is bound, plus the label's natural width (measured on first ask\n * after each {@link update}). Mirrors {@link layoutChildren}'s geometry, so\n * a row sized to this width fits the icon and the whole label.\n *\n * @returns The bound content's natural width in pixels.\n */\n getContentWidth(): number {\n if (!this._measured) {\n this._label.measure();\n this._measured = true;\n }\n\n const labelWidth = this._label.getPreferredSize()?.width ?? 0;\n\n return (this._icon ? ICON_WIDTH : 0) + labelWidth;\n }\n\n /**\n * Positions the icon (vertically centred in the row) and the label. When no\n * icon is bound the label fills the full width from the left edge.\n *\n * @param width - The horizontal extent of the row in pixels.\n * @param height - The vertical extent of the row in pixels.\n */\n layoutChildren(width: number, height: number): void {\n if (this._icon) {\n this._icon.setAutoCommitStyle(false);\n this._icon.setX(0);\n this._icon.setY(Math.max(0, (height - ICON_SIZE) / 2));\n this._icon.setWidth(ICON_SIZE);\n this._icon.setHeight(ICON_SIZE);\n this._icon.setAutoCommitStyle(true);\n }\n\n const labelX = this._icon ? ICON_WIDTH : 0;\n const labelWidth = Math.max(0, width - labelX);\n\n this._label.setAutoCommitStyle(false);\n this._label.setX(labelX);\n this._label.setY(0);\n this._label.setWidth(labelWidth);\n this._label.setHeight(height);\n this._label.setLineHeight(height);\n this._label.setAutoCommitStyle(true);\n }\n\n /**\n * Appends the label sub-component element (and the icon, when one is\n * already bound) to the renderer's DOM element. Later icon changes are\n * inserted before the label by {@link update}.\n *\n * @param element - Optional element passed by the rendering pipeline; falls\n * back to getElement().\n *\n * @returns This renderer, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n if (this._icon) {\n DOM.sink.appendChild(el, this._icon.getElement(true)!);\n }\n\n DOM.sink.appendChild(el, this._label.getElement(true)!);\n\n return this;\n }\n}\n\nconst GlyphListItemRendererCallable = callable(GlyphListItemRenderer);\ntype GlyphListItemRendererCallable = GlyphListItemRenderer;\nexport {\n GlyphListItemRenderer as _GlyphListItemRenderer,\n GlyphListItemRendererCallable as GlyphListItemRenderer,\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{t as e}from"../DOM-DeF_dWe2.js";import{n as t}from"../Position-DJKB0N3l.js";import{f as n,t as r,y as i}from"../Component-Bs6TvaGZ.js";import{t as a}from"../Container-CzyZeMyZ.js";import{t as o}from"../Glyph-6Ki3A1_a.js";import{t as s}from"../RovingTabIndex-D-zOuptw.js";import{t as c}from"../HBox-BWMf9h6r.js";import{n as l}from"../VBox-CdV9VGxR.js";import{t as u}from"../Button-Cdq2ySPL.js";import{t as d}from"../Menu-BdnwRY0M.js";import{t as f}from"../Spacer-BqiPiCqz.js";import{ellipsis_v as p}from"../glyphs/solid/ellipsis_v.es.js";var m={backgroundColor:`var(--ts-ui-menu-bar-btn-bg, transparent)`,foregroundColor:`var(--ts-ui-menu-bar-btn-fg, inherit)`,cursor:`pointer`},h=4,g=10,MenuBarButton=class extends u{_onClickHandler;_onMouseOverHandler;constructor(e,n,r,a){super(e,{...a,chromeless:!0,styleRules:[...a?.styleRules??[],{suffix:`:hover`,styles:{backgroundColor:`var(--ts-ui-menu-bar-btn-hover-bg, rgba(30, 100, 200, 0.10))`}}]},m),this._content.getLayoutManager().setComponentSpacing(h),this.setInsets(new t(0,g,0,g)),this.getAria().setRole(`menuitem`),this.getAria().setHasPopup(`menu`),this.getAria().setExpanded(!1),this._onClickHandler=()=>{n()},this._onMouseOverHandler=()=>{r()},i.addListener(this,`click`,this._onClickHandler),i.addListener(this,`mouseover`,this._onMouseOverHandler)}computePreferredSize(){let{width:e}=super.computePreferredSize();return{width:e,height:28}}setActive(e){return this.setBackgroundColor(e?`var(--ts-ui-menu-bar-btn-hover-bg, rgba(30, 100, 200, 0.10))`:`var(--ts-ui-menu-bar-btn-bg, transparent)`),this.getAria().setExpanded(e),this.setTooltipSuppressed(e),this}dispose(){i.removeListener(this,`click`,this._onClickHandler),i.removeListener(this,`mouseover`,this._onMouseOverHandler)}},_=n(MenuBarButton),MenuBar=class extends r{_buttons=[];_panels=[];_openIndex=-1;_quickSwitchActive=!1;_keydownListening=!1;_onKeyDown;constructor(e){super(e);let t=new c;t.setComponentSpacing(0),t.setStretching(!0),this.setLayoutManager(t),this.setBackgroundColor(`var(--ts-ui-menu-bar-bg, rgb(245, 245, 245))`),this.setElementCSSRule(`borderBottom`,`1px solid var(--ts-ui-menu-bar-border, rgb(220, 220, 220))`),this.setMinSize(0,28),this.getAria().setRole(`menubar`),this.getAria().setLabel(`Main menu`),this.getAria().setTabIndex(0),this._onKeyDown=e=>{if(this._openIndex<0)return;let t=this._panels[this._openIndex];switch(e.key){case`Escape`:e.preventDefault(),e.stopPropagation(),this.closeMenu();break;case`ArrowLeft`:e.preventDefault(),e.stopPropagation(),this.openMenu((this._openIndex-1+this._panels.length)%this._panels.length);break;case`ArrowRight`:e.preventDefault(),e.stopPropagation(),this.openMenu((this._openIndex+1)%this._panels.length);break;case`ArrowDown`:e.preventDefault(),e.stopPropagation(),t.getFocusedIndex()<0?t.focusItem(0):t.focusNext();break;case`ArrowUp`:e.preventDefault(),e.stopPropagation(),t.focusPrev();break;case`Enter`:e.preventDefault(),e.stopPropagation(),t.activateFocused();break}},e?.menus&&this.setMenus(e.menus)}setMenus(e){this._openIndex>=0&&this.closeMenu();for(let e of this._panels)e.dispose();for(let e of this._buttons)e.dispose();this._buttons.length=0,this._panels.length=0,this.removeAllComponents();for(let t=0;t<e.length;t++){let n=e[t],r=t,i=new _(n.label,()=>{this._openIndex===r?this.closeMenu():this.openMenu(r)},()=>{this._quickSwitchActive&&this._openIndex!==r&&this.openMenu(r)},n.glyph===void 0?void 0:{glyph:n.glyph}),a=new d(n.items,()=>{this.closeMenu()});this._buttons.push(i),this._panels.push(a),this.addComponent(i)}return this}openMenu(e){return e<0||e>=this._panels.length||e===this._openIndex?this:(this._openIndex>=0&&(this._panels[this._openIndex].close(),this._buttons[this._openIndex].setActive(!1)),this._openIndex=e,this._quickSwitchActive=!0,this._buttons[e].setActive(!0),this._panels[e].setExcludedElement(this._buttons[e].getElement(!0)),this._panels[e].open(this._buttons[e].getElement(!0)),this._keydownListening||(this._keydownListening=!0,i.addViewportListener(this,`keydown`,this._onKeyDown)),this)}closeMenu(){return this._openIndex<0?this:(this._panels[this._openIndex].close(),this._buttons[this._openIndex].setActive(!1),this._openIndex=-1,this._quickSwitchActive=!1,this._keydownListening&&(this._keydownListening=!1,i.removeViewportListener(this,`keydown`,this._onKeyDown)),this)}getOpenIndex(){return this._openIndex}},v=n(MenuBar);o.register(p);var y=4,b=`ellipsis-v`,x={orientation:`horizontal`,compact:!0,overflow:`clip`,overflowSide:`end`,flat:!0,backgroundColor:`var(--ts-ui-toolbar-bg, rgb(245, 245, 245))`},ToolBar=class extends a{constructor(e){super(e,x),this.getAria().setRole(`toolbar`),this.getAria().setTabIndex(0),this._onKeyDown=e=>{let t=this._orientation===`horizontal`,n=t?`ArrowRight`:`ArrowDown`,r=t?`ArrowLeft`:`ArrowUp`;e.key===n?(e.preventDefault(),this._rovingTabIndex.moveNext()):e.key===r&&(e.preventDefault(),this._rovingTabIndex.movePrev())},i.addSubtreeListener(this,`keydown`,this._onKeyDown)}applyOptions(e){return super.applyOptions(e),this.setOrientation(e.orientation??this.getOrientation()),this.setCompact(e.compact??this.isCompact()),this.setOverflowSide(e.overflowSide??this.getOverflowSide()),this.setOverflow(e.overflow??this.getOverflow()),this.setFlat(e.flat??this.isFlat()),this}setOrientation(e){if(e===this._orientation)return this;let t=this.getLayoutManager(),n=t instanceof c||t instanceof l?t.getComponentSpacing():0,r=e===`horizontal`?new c:new l;r.setComponentSpacing(n),r.setStretching(!0),this.setLayoutManager(r),this._orientation=e,this.getAria().setOrientation(e);let i=`var(--ts-ui-toolbar-border, rgb(220, 220, 220))`;return e===`horizontal`?this.setBorder({borderBottom:`1px solid ${i}`}):this.setBorder({borderRight:`1px solid ${i}`}),this}getOrientation(){return this._orientation??this._defaultOptions.orientation}setCompact(e){if(e===this._compact)return this;this._compact=e;let n=e?0:4;this.setInsets(new t(n,n,n,n));for(let t of this.getComponents())t instanceof u&&t.setCompact(e);return this.doLayout(),this}isCompact(){return this._compact??this._defaultOptions.compact}setOverflow(e){return this._overflowMode=e,e===`menu`&&this._overflowButton===void 0&&this._createOverflowAffordance(),this.doLayout(),this}_createOverflowAffordance(){let e=new u({flat:!0,glyph:b});e.setDisplayed(!1),e.getAria().setLabel(`More`),e.getAria().setHasPopup(`menu`),e.on(`action`,()=>{this._toggleOverflowMenu()}),this._overflowButton=e,this._overflowMenu=new d,this._overflowSpacer=f.flex(),super.addComponent(e),this._positionOverflowTrigger()}_positionOverflowTrigger(){let e=this._overflowButton,t=this._overflowSpacer;e==null||t==null||(this._overflowSide===`end`?(t.getParentComponent()!==this&&super.addComponent(t),this.moveComponent(e,this.getComponents().length-1),this.moveComponent(t,this.getComponents().length-2)):(t.getParentComponent()===this&&this.removeComponent(t),this.moveComponent(e,0)))}getOverflow(){return this._overflowMode??this._defaultOptions.overflow}setOverflowSide(e){return e===this._overflowSide?this:(this._overflowSide=e,this._positionOverflowTrigger(),this.doLayout(),this)}getOverflowSide(){return this._overflowSide??this._defaultOptions.overflowSide}setFlat(e){if(e===this._flat)return this;this._flat=e;for(let t of this.getComponents())t instanceof u&&t.setFlat(e);return this}isFlat(){return this._flat??this._defaultOptions.flat}addComponent(e,t){return super.addComponent(e,t),this._rovingTabIndex===void 0&&(this._rovingTabIndex=new s),e.getAria().getTabIndex()!==-1&&this._rovingTabIndex.add(e),this._flat&&e instanceof u&&e.setFlat(!0),this._compact&&e instanceof u&&e.setCompact(!0),this._positionOverflowTrigger(),this}doLayout(){return super.doLayout(),this._overflowMode!==`menu`||this._orientation!==`horizontal`||this._overflowButton===void 0||this._overflowButton===null||this._reflowOverflow(this._overflowButton),this}_reflowOverflow(e){let t=this.getInsets(),n=this.getWidth()-t.getLeft()-t.getRight(),r=this.getLayoutManager(),i=r instanceof c?r.getComponentSpacing():y,a=this.getComponents().filter(t=>t!==e&&t!==this._overflowSpacer),o=this._computeOverflowed(a,e,n,i),s=!1;for(let e of a){let t=o.includes(e);e.isDisplayed()===t&&(e.setDisplayed(!t),s=!0)}let l=o.length>0;e.isDisplayed()!==l&&(e.setDisplayed(l),s=!0),s&&super.doLayout()}_computeOverflowed(e,t,n,r){let widthOf=e=>e.getPreferredSize()?.width??0,i=0;for(let t=0;t<e.length;t++)i+=widthOf(e[t]),t>0&&(i+=r);if(i<=n)return[];let a=n-(widthOf(t)+r),o=[],s=0,c=!1;for(let t=0;t<e.length;t++){let n=e[t],i=s+widthOf(n)+(t>0?r:0);i>a&&(c=!0),c&&n instanceof u?o.push(n):s=i}return o}_toggleOverflowMenu(){let t=this._overflowButton,n=this._overflowMenu;if(t==null||n==null)return;let r=[];for(let e of this.getComponents()){if(e===t||e.isDisplayed()||!(e instanceof u))continue;let n=e.getGlyph()?.getGlyphName();r.push({text:e.getText(),glyph:n,action:()=>{e.click()}})}let i=t.getElement();if(!i)return;let a=e.source.getViewportRect(t);n.toggleFor(i,a,r)}},S=n(ToolBar),C={},w=n(class ToolBarSeparator extends r{static THICKNESS=1;_orientation;constructor(e){super(e,C),this._orientation=e?.orientation??`vertical`,this._orientation===`vertical`?(this.setPreferredSize(ToolBarSeparator.THICKNESS,0),this.setMaxSize(ToolBarSeparator.THICKNESS,Number.MAX_VALUE)):(this.setPreferredSize(0,ToolBarSeparator.THICKNESS),this.setMaxSize(Number.MAX_VALUE,ToolBarSeparator.THICKNESS)),this.setBackgroundColor(`var(--ts-ui-toolbar-separator-color, rgb(220, 220, 220))`),this.getAria().setRole(`separator`),this.getAria().setOrientation(this._orientation),this.getAria().setTabIndex(-1)}getOrientation(){return this._orientation}});export{v as MenuBar,_ as MenuBarButton,S as ToolBar,w as ToolBarSeparator};
|
|
2
2
|
//# sourceMappingURL=menubar.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menubar.es.js","names":[],"sources":["../../../src/typescript/lib/component/menubar/MenuBarButton.ts","../../../src/typescript/lib/component/menubar/MenuBar.ts","../../../src/typescript/lib/component/menubar/ToolBar.ts","../../../src/typescript/lib/component/menubar/ToolBarSeparator.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button, ButtonOptions } from \"~/component/button/Button.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuBarButton}.\n *\n * @category Components\n */\nexport interface MenuBarButtonOptions extends ButtonOptions {\n}\n\n/**\n * User-overridable visual defaults forwarded to `super` via the third\n * constructor arg. The cascade in `Component`'s constructor merges these\n * over Button's own defaults and dispatches each setter once with the\n * final value, so any field the caller supplied wins.\n */\nconst _defaultMenuBarButtonOptions: Partial<MenuBarButtonOptions> = {\n backgroundColor: \"var(--ts-ui-menu-bar-btn-bg, transparent)\",\n foregroundColor: \"var(--ts-ui-menu-bar-btn-fg, inherit)\",\n cursor: \"pointer\",\n};\n\n/** Pixel gap between the leading glyph and the label in the content row. */\nconst GLYPH_TEXT_GAP = 4;\n\n/** Horizontal padding inside the button, applied via `setInsets`. */\nconst HORIZONTAL_PAD = 10;\n\n/**\n * Fixed row height shared by every `MenuBarButton` and by the parent\n * [`MenuBar`](/api/component/menubar/classes/MenuBar) container's `setMinSize`. Exported so the two stay in\n * lockstep — changing one without the other would let the bar grow taller\n * than its buttons.\n */\nexport const MENU_BAR_BUTTON_HEIGHT: number = 28;\n\n/**\n * A single top-level button in a [`MenuBar`](/api/component/menubar/classes/MenuBar) (e.g. \"File\", \"Edit\").\n *\n * Extends [`Button`](/api/component/button/classes/Button) with `chromeless: true`\n * so the menubar's flat label-style appearance dodges Button's ridge border,\n * drop shadow, and gradient defaults. Reuses Button's content-row machinery\n * for the optional leading glyph + label pair, re-anchored to the west edge.\n * Adds menubar-specific ARIA (`role=\"menuitem\"`, `aria-haspopup=\"menu\"`,\n * `aria-expanded`) and the sticky `setActive` highlight for the\n * dropdown-open state. The `:hover` rule rides Button's `styleRules` bag.\n *\n * Communicates open/close intent back to [`MenuBar`](/api/component/menubar/classes/MenuBar) via callbacks passed at\n * construction time.\n *\n * @category Components\n */\nclass MenuBarButton extends Button<MenuBarButtonOptions> {\n\n private readonly _onClickHandler: () => void;\n private readonly _onMouseOverHandler: () => void;\n\n /**\n * Constructs a `MenuBarButton`.\n *\n * @param text - The label shown in the bar (e.g. `\"File\"`).\n * @param onClick - Called when the user clicks this button.\n * @param onHover - Called when the mouse enters this button while a menu is open (quick-switch).\n * @param options - Optional configuration bag. `glyph` is inherited from\n * [`ButtonOptions`](/api/component/button/interfaces/ButtonOptions).\n */\n constructor(\n text: string,\n onClick: () => void,\n onHover: () => void,\n options?: MenuBarButtonOptions,\n ) {\n super(\n text,\n {\n ...options,\n // chromeless suppresses Button's ridge border, drop shadow,\n // gradient background, and the pressed/hover treatments —\n // the menubar wants a flat label-shaped surface.\n chromeless: true,\n // Menubar-specific :hover highlight rides Button's styleRules\n // bag so the cascade routes through createStyleRule's\n // dedupe-and-defer machinery. Merge with any caller-supplied\n // styleRules entries via the canonical array-spread idiom.\n styleRules: [\n ...(options?.styleRules ?? []),\n {\n suffix: \":hover\",\n styles: {\n backgroundColor: \"var(--ts-ui-menu-bar-btn-hover-bg, rgba(30, 100, 200, 0.10))\",\n },\n },\n ],\n },\n _defaultMenuBarButtonOptions,\n );\n\n // Bump the HBox content-row spacing from Button's default (2) to the\n // menubar's GLYPH_TEXT_GAP (4).\n (this._content.getLayoutManager() as HBox).setComponentSpacing(GLYPH_TEXT_GAP);\n\n // Horizontal padding inside the button — replaces Button's 4-px\n // insets default. `setInsets` (overridden in Button) auto-fires\n // `recomputePreferredSize`, so the menubar's fixed-height\n // `computePreferredSize` override lands the right dimensions\n // without explicit prodding here.\n this.setInsets(new Insets(0, HORIZONTAL_PAD, 0, HORIZONTAL_PAD));\n\n this.getAria().setRole(\"menuitem\");\n this.getAria().setHasPopup(\"menu\");\n this.getAria().setExpanded(false);\n\n this._onClickHandler = (): void => { onClick(); };\n this._onMouseOverHandler = (): void => { onHover(); };\n\n // Internal `Event.addListener(this, …)` calls — the class registers\n // listeners on itself, which is the allowed shape. The named-method\n // bypass rule only forbids *external* consumers from doing the same.\n Event.addListener(this, \"click\", this._onClickHandler);\n Event.addListener(this, \"mouseover\", this._onMouseOverHandler);\n }\n\n /**\n * Pins the button's height to {@link MENU_BAR_BUTTON_HEIGHT} while\n * inheriting Button's content-derived width.\n */\n protected override computePreferredSize(): { width: number; height: number } {\n const { width } = super.computePreferredSize();\n\n return { width, height: MENU_BAR_BUTTON_HEIGHT };\n }\n\n /**\n * Sets the active (dropdown-open) visual state.\n *\n * Active buttons show a persistent highlight background and report `aria-expanded=\"true\"`.\n *\n * @param active - `true` to show the open state, `false` to revert to the default.\n */\n setActive(active: boolean): this {\n this.setBackgroundColor(\n active\n ? \"var(--ts-ui-menu-bar-btn-hover-bg, rgba(30, 100, 200, 0.10))\"\n : \"var(--ts-ui-menu-bar-btn-bg, transparent)\"\n );\n this.getAria().setExpanded(active);\n\n // While this button's dropdown is open, silence its hover tooltip — the\n // title tooltip would otherwise pop over the open menu (and re-arm on a\n // quick-switch re-hover). Restored when the menu closes.\n this.setTooltipSuppressed(active);\n\n return this;\n }\n\n /**\n * Removes Event listeners registered by this button.\n */\n dispose(): void {\n Event.removeListener(this, \"click\", this._onClickHandler);\n Event.removeListener(this, \"mouseover\", this._onMouseOverHandler);\n }\n}\n\nconst MenuBarButtonCallable = callable(MenuBarButton);\ntype MenuBarButtonCallable = MenuBarButton;\nexport {\n MenuBarButton as _MenuBarButton,\n MenuBarButtonCallable as MenuBarButton\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { MenuBarButton, MENU_BAR_BUTTON_HEIGHT } from \"~/component/menubar/MenuBarButton.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuConfig } from \"~/component/container/MenuItem.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuBar}.\n *\n * @category Components\n */\nexport interface MenuBarOptions extends ComponentOptions {\n /** Top-level menus to populate the bar with, equivalent to a tail `setMenus()`. */\n menus?: MenuConfig[];\n}\n\n/**\n * A persistent horizontal menu bar that hosts top-level dropdown menus.\n *\n * `setMenus()` populates the bar with [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton) children. Clicking a button\n * opens the corresponding [`Menu`](/api/overlay/classes/Menu) dropdown. While any dropdown is open,\n * hovering another button switches menus immediately (quick-switch mode), and\n * keyboard navigation (Arrow keys, Enter, Escape) is handled via a viewport-level\n * keydown listener.\n *\n * @example\n * ```typescript\n * const bar = new MenuBar();\n * bar.setMenus([\n * { label: 'File', items: [\n * { text: 'New', shortcut: 'Ctrl+N', action: () => newDoc() },\n * { separator: true },\n * { text: 'Quit', enabled: false },\n * ]},\n * { label: 'Edit', items: [\n * { text: 'Undo', shortcut: 'Ctrl+Z', action: () => undo() },\n * ]},\n * ]);\n * container.addComponent(bar);\n * ```\n *\n * @category Components\n */\nclass MenuBar extends Component {\n\n private readonly _buttons: MenuBarButton[] = [];\n private readonly _panels: Menu[] = [];\n private _openIndex: number = -1;\n private _quickSwitchActive: boolean = false;\n private _keydownListening: boolean = false;\n\n private readonly _onKeyDown: (e: KeyboardEvent) => void;\n\n /**\n * Constructs a `MenuBar`, optionally populated from `options.menus` (the\n * options-bag equivalent of a tail `setMenus()` call).\n *\n * @param options - Optional construction-time options; `menus` populates the bar.\n */\n constructor(options?: MenuBarOptions) {\n super(options);\n\n const hbox = new HBox();\n hbox.setComponentSpacing(0);\n hbox.setStretching(true);\n this.setLayoutManager(hbox);\n\n // Default to the tool bar's background so menu bars and tool bars read as\n // one surface; the shipped themes set --ts-ui-menu-bar-bg to their\n // toolBar.background, and this untokened fallback matches ToolBar's own.\n this.setBackgroundColor(\"var(--ts-ui-menu-bar-bg, rgb(245, 245, 245))\");\n this.setElementCSSRule(\n \"borderBottom\",\n \"1px solid var(--ts-ui-menu-bar-border, rgb(220, 220, 220))\"\n );\n this.setMinSize(0, MENU_BAR_BUTTON_HEIGHT);\n\n this.getAria().setRole(\"menubar\");\n this.getAria().setLabel(\"Main menu\");\n this.getAria().setTabIndex(0);\n\n this._onKeyDown = (e: KeyboardEvent) => {\n if (this._openIndex < 0) {\n return;\n }\n\n const panel = this._panels[this._openIndex];\n\n switch (e.key) {\n case \"Escape\":\n e.preventDefault();\n this.closeMenu();\n break;\n\n case \"ArrowLeft\":\n e.preventDefault();\n this.openMenu((this._openIndex - 1 + this._panels.length) % this._panels.length);\n break;\n\n case \"ArrowRight\":\n e.preventDefault();\n this.openMenu((this._openIndex + 1) % this._panels.length);\n break;\n\n case \"ArrowDown\":\n e.preventDefault();\n\n if (panel.getFocusedIndex() < 0) {\n panel.focusItem(0);\n } else {\n panel.focusNext();\n }\n\n break;\n\n case \"ArrowUp\":\n e.preventDefault();\n panel.focusPrev();\n break;\n\n case \"Enter\":\n e.preventDefault();\n panel.activateFocused();\n break;\n }\n };\n\n if (options?.menus) {\n this.setMenus(options.menus);\n }\n }\n\n /**\n * Replaces the current set of top-level menus.\n *\n * Disposes all existing [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton) and [`Menu`](/api/overlay/classes/Menu) instances, then rebuilds\n * them from the given descriptors.\n *\n * @param menus - Ordered list of top-level menu descriptors.\n */\n setMenus(menus: MenuConfig[]): this {\n if (this._openIndex >= 0) {\n this.closeMenu();\n }\n\n for (const panel of this._panels) {\n panel.dispose();\n }\n\n for (const button of this._buttons) {\n button.dispose();\n }\n\n this._buttons.length = 0;\n this._panels.length = 0;\n\n this.removeAllComponents();\n\n for (let i = 0; i < menus.length; i++) {\n const menu = menus[i];\n const index = i;\n\n const button = new MenuBarButton(\n menu.label,\n () => {\n if (this._openIndex === index) {\n this.closeMenu();\n } else {\n this.openMenu(index);\n }\n },\n () => {\n if (this._quickSwitchActive && this._openIndex !== index) {\n this.openMenu(index);\n }\n },\n menu.glyph !== undefined ? { glyph: menu.glyph } : undefined\n );\n\n const panel = new Menu(menu.items, () => { this.closeMenu(); });\n\n this._buttons.push(button);\n this._panels.push(panel);\n this.addComponent(button);\n }\n\n return this;\n }\n\n /**\n * Programmatically opens the menu at the given index, closing any currently open menu first.\n *\n * @param index - Zero-based index into the menus array.\n */\n openMenu(index: number): this {\n if (index < 0 || index >= this._panels.length) {\n return this;\n }\n\n if (index === this._openIndex) {\n return this;\n }\n\n if (this._openIndex >= 0) {\n this._panels[this._openIndex].close();\n this._buttons[this._openIndex].setActive(false);\n }\n\n this._openIndex = index;\n this._quickSwitchActive = true;\n\n this._buttons[index].setActive(true);\n // Exclude only the opener button from the panel's outside-click dismissal\n // (so its own mousedown does not self-close before the click toggles it),\n // not the whole bar — otherwise a click on empty bar space would be\n // treated as inside and never close the menu.\n this._panels[index].setExcludedElement(this._buttons[index].getElement(true)!);\n this._panels[index].open(this._buttons[index].getElement(true)!);\n\n if (!this._keydownListening) {\n this._keydownListening = true;\n Event.addViewportListener(this, \"keydown\", this._onKeyDown);\n }\n\n return this;\n }\n\n /**\n * Closes the currently open menu, if any.\n */\n closeMenu(): this {\n if (this._openIndex < 0) {\n return this;\n }\n\n this._panels[this._openIndex].close();\n this._buttons[this._openIndex].setActive(false);\n\n this._openIndex = -1;\n this._quickSwitchActive = false;\n\n if (this._keydownListening) {\n this._keydownListening = false;\n Event.removeViewportListener(this, \"keydown\", this._onKeyDown);\n }\n\n return this;\n }\n\n /**\n * Returns the index of the currently open menu, or `-1` if none is open.\n *\n * @returns The open menu index, or -1.\n */\n getOpenIndex(): number {\n return this._openIndex;\n }\n}\n\nconst MenuBarCallable = callable(MenuBar);\ntype MenuBarCallable = MenuBar;\nexport {\n MenuBar as _MenuBar,\n MenuBarCallable as MenuBar\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button } from \"~/component/button/Button.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { RovingTabIndex } from \"~/core/RovingTabIndex.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { Spacer } from \"~/component/container/Spacer.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { ellipsis_v } from \"~/glyphs/solid/ellipsis_v.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisOrientation, AxisEnd } from \"~/primitive/Axis.js\";\n\n// Register the overflow trigger's chevron eagerly at module load — same pattern\n// as SplitButton registering its caret_down — so the lazily-created \"more\"\n// affordance always resolves its glyph without the consumer pre-registering it.\nGlyph.register(ellipsis_v);\n\n/**\n * Overflow behaviour for a {@link ToolBar} whose children exceed its measured\n * extent. `\"clip\"` (the default) lets the children spill into the parent's\n * clipping region. `\"menu\"` hides the `Button` / `ToggleButton` children that\n * don't fit and surfaces them in a dropdown opened by a trailing chevron\n * affordance. Menu overflow is horizontal-only; vertical bars always clip.\n *\n * @category Components\n */\nexport type ToolBarOverflow = \"clip\" | \"menu\";\n\n/**\n * Construction-time options for {@link ToolBar}.\n *\n * @category Components\n */\nexport interface ToolBarOptions extends ContainerOptions {\n orientation?: AxisOrientation;\n compact?: boolean;\n overflow?: ToolBarOverflow;\n /**\n * Edge the `\"menu\"` overflow trigger sits on — `\"end\"` (default) or\n * `\"start\"`. No visible effect unless `overflow` is `\"menu\"`. Runtime\n * counterpart `setOverflowSide`.\n */\n overflowSide?: AxisEnd;\n /**\n * When `true` (the default), `Button` / `ToggleButton` children added to the\n * bar are switched to flat appearance for the classical toolbar look — no\n * resting frame, a light frame on hover, and a sunken inset frame on press.\n * Set `false` to keep raised buttons. Runtime counterpart `setFlat`.\n */\n flat?: boolean;\n}\n\n/**\n * Fallback inter-child spacing in pixels for the overflow reserve math only.\n * `_computeOverflowed` reserves a gap between children when measuring the\n * fit-set; it reads the live `getComponentSpacing()` and falls back to this\n * constant only when the layout manager isn't an `HBox`. The bar no longer\n * drives any gap itself (spacing defaults to 0), so this is purely the typed\n * fallback that keeps the overflow arithmetic well-defined.\n */\nconst TOOLBAR_GAP_DEFAULT: number = 4;\n\n/**\n * Registry glyph rendered on the overflow (\"more\") trigger button. Verified\n * present in the solid glyph set (`glyphs/solid/ellipsis_v.ts`); the vertical\n * ellipsis is the conventional overflow affordance for a horizontal bar.\n */\nconst OVERFLOW_TRIGGER_GLYPH: string = \"ellipsis-v\";\n\n/**\n * User-overridable defaults forwarded to `super` via the options bag.\n */\nconst _defaultToolBarOptions: Partial<ToolBarOptions> = {\n orientation: \"horizontal\",\n compact: true,\n overflow: \"clip\",\n overflowSide: \"end\",\n flat: true,\n backgroundColor: \"var(--ts-ui-toolbar-bg, rgb(245, 245, 245))\",\n};\n\n/**\n * A horizontal (or vertical) strip of related controls — e.g. Bold / Italic\n * / Underline in a text editor, or Cut / Copy / Paste in a file manager.\n *\n * `ToolBar` extends [`Container`](/api/core/classes/Container) and defaults to\n * compact mode (`compact: true`), so at construction it drops its own panel\n * insets to zero and renders its `Button` / `ToggleButton` children compact;\n * `setCompact(false)` restores the 4-pixel panel insets and the children's\n * default rendering. Layout defaults to a horizontal\n * [`HBox`](/api/layout/classes/HBox); pass `orientation: \"vertical\"` (or call\n * `setOrientation(\"vertical\")`) to swap to a [`VBox`](/api/layout/classes/VBox).\n *\n * Children can be any [`Component`](/api/core/classes/Component) — typically\n * [`Button`](/api/component/button/classes/Button),\n * [`ToggleButton`](/api/component/button/classes/ToggleButton),\n * [`ButtonGroup`](/api/overlay/classes/ButtonGroup) members,\n * [`ComboBox`](/api/component/input/classes/ComboBox), or {@link ToolBarSeparator}.\n * Focusable children (`tabindex >= 0`) are auto-registered with an internal\n * [`RovingTabIndex`](/api/core/classes/RovingTabIndex) so Arrow keys cycle\n * focus between them, matching the\n * [`ButtonGroup`](/api/overlay/classes/ButtonGroup) keyboard-nav pattern.\n *\n * @example\n * ```typescript\n * import { ToolBar, ToolBarSeparator } from '@jimka/typescript-ui/component/menubar';\n * import { Button } from '@jimka/typescript-ui/component/button';\n *\n * const bar = new ToolBar();\n * bar.addComponent(new Button('Cut'));\n * bar.addComponent(new Button('Copy'));\n * bar.addComponent(new Button('Paste'));\n * bar.addComponent(new ToolBarSeparator());\n * bar.addComponent(new Button('Find'));\n * ```\n *\n * @category Components\n */\nclass ToolBar<TOptions extends ToolBarOptions = ToolBarOptions> extends Container<TOptions> {\n\n declare private _orientation: AxisOrientation;\n declare private _compact: boolean;\n declare private _overflowMode: ToolBarOverflow;\n declare private _overflowSide: AxisEnd;\n declare private _flat: boolean;\n declare private _rovingTabIndex: RovingTabIndex;\n declare private _onKeyDown: (e: KeyboardEvent) => void;\n declare private _overflowButton: Button | null;\n declare private _overflowMenu: Menu | null;\n declare private _overflowSpacer: Spacer | null;\n\n /**\n * Constructs a `ToolBar`.\n *\n * @param options - Optional construction-time options.\n *\n * @remarks The parameter is typed as the concrete {@link ToolBarOptions}\n * rather than the class's `TOptions` parameter so that passing an options\n * literal (e.g. `new ToolBar({ compact: true })`) cannot narrow `TOptions`\n * to that literal — which would type the instance as `ToolBar<{ compact:\n * true }>` and fail the weak-type assignability check when it is later used\n * as a base `Component` (e.g. `container.addComponent(toolbar)`). `TOptions`\n * stays at its `ToolBarOptions` default.\n */\n constructor(options?: ToolBarOptions) {\n super(options as TOptions, _defaultToolBarOptions as Partial<TOptions>);\n\n this.getAria().setRole(\"toolbar\");\n this.getAria().setTabIndex(0);\n\n this._onKeyDown = (e: KeyboardEvent) => {\n const isHoriz = this._orientation === \"horizontal\";\n const fwd = isHoriz ? \"ArrowRight\" : \"ArrowDown\";\n const back = isHoriz ? \"ArrowLeft\" : \"ArrowUp\";\n\n if (e.key === fwd) {\n e.preventDefault();\n this._rovingTabIndex.moveNext();\n } else if (e.key === back) {\n e.preventDefault();\n this._rovingTabIndex.movePrev();\n }\n };\n\n Event.addSubtreeListener(this, \"keydown\", this._onKeyDown);\n }\n\n /**\n * Applies a {@link ToolBarOptions} bag. Inherited `Container` fields cascade\n * through `super.applyOptions`; the `ToolBar`-specific fields\n * (orientation, compact, overflow) are dispatched here when set.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: TOptions): this {\n super.applyOptions(options);\n\n // These fields all carry a class default and seed construction-time\n // backing state, so dispatch the caller value or the class default —\n // never leave the setter unfired (mirrors Panel.setAutoScroll).\n this.setOrientation(options.orientation ?? this.getOrientation());\n this.setCompact(options.compact ?? this.isCompact());\n // Dispatched before `overflow` so the trigger, created on entry to\n // `\"menu\"` mode, is positioned on the configured side from the start.\n this.setOverflowSide(options.overflowSide ?? this.getOverflowSide());\n this.setOverflow(options.overflow ?? this.getOverflow());\n this.setFlat(options.flat ?? this.isFlat());\n\n return this;\n }\n\n /**\n * Sets the layout direction. Horizontal toolbars pack children\n * left-to-right via [`HBox`](/api/layout/classes/HBox); vertical toolbars\n * pack them top-to-bottom via [`VBox`](/api/layout/classes/VBox). Child\n * spacing is preserved across the swap; the trailing-edge border flips\n * from bottom to right (or vice versa) to match the new direction.\n *\n * Existing {@link ToolBarSeparator} children are **not** auto-flipped —\n * see the architecture note in the plan.\n *\n * @param value - The new orientation.\n *\n * @returns This component, for method chaining.\n */\n setOrientation(value: AxisOrientation): this {\n if (value === this._orientation) {\n return this;\n }\n\n const oldLM = this.getLayoutManager();\n // Default to flush (0) — the bar no longer drives a gap; child density\n // comes from the buttons' own compact insets. A consumer-set spacing on\n // an existing HBox/VBox is preserved across the orientation swap.\n const gap = (oldLM instanceof HBox || oldLM instanceof VBox)\n ? oldLM.getComponentSpacing()\n : 0;\n\n const newLM: HBox | VBox = value === \"horizontal\" ? new HBox() : new VBox();\n newLM.setComponentSpacing(gap);\n newLM.setStretching(true);\n\n this.setLayoutManager(newLM);\n this._orientation = value;\n\n this.getAria().setOrientation(value);\n\n const ruleColor = \"var(--ts-ui-toolbar-border, rgb(220, 220, 220))\";\n\n if (value === \"horizontal\") {\n this.setBorder({ borderBottom: `1px solid ${ruleColor}` });\n } else {\n this.setBorder({ borderRight: `1px solid ${ruleColor}` });\n }\n\n return this;\n }\n\n /**\n * Returns the current orientation.\n *\n * @returns `\"horizontal\"` or `\"vertical\"`.\n */\n getOrientation(): AxisOrientation {\n return this._orientation ?? this._defaultOptions.orientation!;\n }\n\n /**\n * Toggles compact mode. In compact mode the bar's own panel insets shrink\n * from `(4, 4, 4, 4)` to `(0, 0, 0, 0)` and every `Button` / `ToggleButton`\n * child is switched to compact rendering (tighter button insets), the same\n * way {@link setFlat} drives the flat appearance onto its button children.\n * Child spacing is left untouched — the bar packs its children flush and the\n * density comes from the buttons themselves.\n *\n * @param value - `true` to enable compact mode, `false` to restore defaults.\n *\n * @returns This component, for method chaining.\n */\n setCompact(value: boolean): this {\n if (value === this._compact) {\n return this;\n }\n\n this._compact = value;\n\n // Compact packs flush to its edges (zero inset) so a toolbar sits tight\n // against the surrounding chrome; the density between children still\n // comes from the buttons' own compact insets. Non-compact restores the\n // roomy 4px panel insets.\n const inset = value ? 0 : 4;\n\n this.setInsets(new Insets(inset, inset, inset, inset));\n\n for (const child of this.getComponents()) {\n if (child instanceof Button) {\n child.setCompact(value);\n }\n }\n\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns whether compact mode is currently active.\n *\n * @returns `true` if compact mode is enabled.\n */\n isCompact(): boolean {\n return this._compact ?? this._defaultOptions.compact!;\n }\n\n /**\n * Sets the overflow strategy. `\"clip\"` lets children spill into the parent's\n * clipping region. `\"menu\"` hides the `Button` / `ToggleButton` children\n * that don't fit and surfaces them in a dropdown opened by a trailing\n * chevron affordance; the trigger and its rebuild-mode\n * [`Menu`](/api/overlay/classes/Menu) are created lazily on first entry to\n * `\"menu\"` mode. Menu overflow applies to horizontal bars only — a vertical\n * bar always clips.\n *\n * @param value - `\"clip\"` or `\"menu\"`.\n *\n * @returns This component, for method chaining.\n */\n setOverflow(value: ToolBarOverflow): this {\n this._overflowMode = value;\n\n if (value === \"menu\" && this._overflowButton === undefined) {\n this._createOverflowAffordance();\n }\n\n this.doLayout();\n\n return this;\n }\n\n /**\n * Lazily builds the overflow trigger — a flat, glyph-only `Button` carrying\n * the chevron glyph — and the rebuild-mode `Menu` it opens. The trigger is\n * appended through `super.addComponent` so it bypasses the flatten-children\n * pass in {@link addComponent} and is excluded from the overflow set by\n * identity. It starts hidden; {@link doLayout} shows it only once at least\n * one button has overflowed.\n */\n private _createOverflowAffordance(): void {\n const trigger = new Button({ flat: true, glyph: OVERFLOW_TRIGGER_GLYPH });\n\n trigger.setDisplayed(false);\n trigger.getAria().setLabel(\"More\");\n trigger.getAria().setHasPopup(\"menu\");\n\n trigger.on(\"action\", () => { this._toggleOverflowMenu(); });\n\n this._overflowButton = trigger;\n this._overflowMenu = new Menu();\n // Flex spacer that pushes a right-side trigger to the bar's far edge; it\n // is only parented while the side is `\"right\"` (see _positionOverflowTrigger).\n this._overflowSpacer = Spacer.flex();\n\n super.addComponent(trigger);\n\n this._positionOverflowTrigger();\n }\n\n /**\n * Pins the overflow trigger to the configured edge. For `\"end\"` a flex\n * {@link Spacer} is parented just before the trigger so the chevron is\n * driven to the bar's trailing edge (rather than sitting flush against the\n * last visible button); the final child order is `[content…, spacer, trigger]`.\n * For `\"start\"` the spacer is detached and the trigger leads as the first\n * child. A no-op when no trigger exists yet. Re-run whenever content is added\n * (the trigger must stay at the edge) or the side changes; the trailing run\n * of buttons still overflows regardless of the trigger's side.\n */\n private _positionOverflowTrigger(): void {\n const trigger = this._overflowButton;\n const spacer = this._overflowSpacer;\n if (trigger === undefined || trigger === null || spacer === undefined || spacer === null) {\n return;\n }\n\n if (this._overflowSide === \"end\") {\n if (spacer.getParentComponent() !== this) {\n super.addComponent(spacer);\n }\n\n // Order the tail as [spacer, trigger]: move the trigger last, then\n // slot the spacer immediately before it. `moveComponent` recomputes\n // the index after splicing the moved child out, so the pre-move\n // length-relative targets land both at the end.\n this.moveComponent(trigger, this.getComponents().length - 1);\n this.moveComponent(spacer, this.getComponents().length - 2);\n } else {\n if (spacer.getParentComponent() === this) {\n this.removeComponent(spacer);\n }\n\n this.moveComponent(trigger, 0);\n }\n }\n\n /**\n * Returns the current overflow strategy.\n *\n * @returns `\"clip\"` or `\"menu\"`.\n */\n getOverflow(): ToolBarOverflow {\n return this._overflowMode ?? this._defaultOptions.overflow!;\n }\n\n /**\n * Sets which edge the `\"menu\"` overflow trigger sits on. `\"end\"` (the\n * default) trails the visible buttons; `\"start\"` leads them. Only the\n * trigger's position changes — the overflowing buttons are the trailing run\n * either way. No visible effect until `overflow` is `\"menu\"`.\n *\n * @param value - `\"start\"` or `\"end\"`.\n *\n * @returns This component, for method chaining.\n */\n setOverflowSide(value: AxisEnd): this {\n if (value === this._overflowSide) {\n return this;\n }\n\n this._overflowSide = value;\n\n this._positionOverflowTrigger();\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns the edge the overflow trigger sits on.\n *\n * @returns `\"start\"` or `\"end\"`.\n */\n getOverflowSide(): AxisEnd {\n return this._overflowSide ?? this._defaultOptions.overflowSide!;\n }\n\n /**\n * Toggles the classical flat appearance for the bar's `Button` /\n * `ToggleButton` children. When `true` (the default), each such child is\n * switched to flat mode — no resting frame, a light frame on hover, a sunken\n * inset frame on press, and a depressed look for a toggled-on\n * `ToggleButton`. Glyph-only flat buttons also tighten to compact squares.\n * Setting `false` reverts existing button children to raised chrome.\n *\n * Non-`Button` children (separators, combo boxes, spacers) are left\n * untouched. The flag also governs children added later through\n * {@link addComponent}.\n *\n * @param value - `true` to flatten button children, `false` to restore them.\n *\n * @returns This component, for method chaining.\n */\n setFlat(value: boolean): this {\n if (value === this._flat) {\n return this;\n }\n\n this._flat = value;\n\n for (const child of this.getComponents()) {\n if (child instanceof Button) {\n child.setFlat(value);\n }\n }\n\n return this;\n }\n\n /**\n * Returns whether the bar flattens its `Button` children.\n *\n * @returns `true` if flat mode is enabled.\n */\n isFlat(): boolean {\n return this._flat ?? this._defaultOptions.flat!;\n }\n\n /**\n * Appends a child component and, when its tab-index marks it focusable,\n * registers it with the internal roving-tabindex group so Arrow keys\n * cycle focus through it.\n *\n * @param component - The child component to add.\n *\n * @returns This component, for method chaining.\n */\n override addComponent(component: Component, constraints?: LayoutConstraints): this {\n super.addComponent(component, constraints);\n\n if (this._rovingTabIndex === undefined) {\n this._rovingTabIndex = new RovingTabIndex();\n }\n\n if (component.getAria().getTabIndex() !== -1) {\n this._rovingTabIndex.add(component);\n }\n\n if (this._flat && component instanceof Button) {\n component.setFlat(true);\n }\n\n if (this._compact && component instanceof Button) {\n component.setCompact(true);\n }\n\n // Keep the overflow trigger pinned to its edge: a content child appended\n // after the trigger (the trigger is created before the demo's buttons\n // for a construction-time `overflow: \"menu\"`) would otherwise leave the\n // chevron stranded mid-row. No-op when no trigger exists.\n this._positionOverflowTrigger();\n\n return this;\n }\n\n /**\n * Lays the bar out and, in `\"menu\"` overflow mode on a horizontal bar,\n * reflows the `Button` / `ToggleButton` children that no longer fit into the\n * trailing chevron dropdown. The base pass runs first so children report\n * their resolved preferred widths; the reflow then re-derives the fit-set\n * from scratch each pass, so widening the bar restores previously-hidden\n * buttons. Vertical bars and `\"clip\"` mode skip the reflow entirely.\n *\n * @returns This component, for method chaining.\n */\n override doLayout(): this {\n super.doLayout();\n\n if (this._overflowMode !== \"menu\" || this._orientation !== \"horizontal\") {\n return this;\n }\n\n if (this._overflowButton === undefined || this._overflowButton === null) {\n return this;\n }\n\n this._reflowOverflow(this._overflowButton);\n\n return this;\n }\n\n /**\n * Re-derives which `Button` / `ToggleButton` children overflow the bar's\n * inner width and toggles their `display` accordingly, reserving room for\n * the trigger whenever at least one button is hidden. Only mutates child\n * visibility when the fit-set actually changed, so the\n * `setDisplayed`-triggered re-layout converges instead of thrashing. The\n * trigger itself is shown only while ≥1 button is overflowed and is then\n * positioned for the dropdown anchor.\n *\n * @param trigger - The lazily-created overflow trigger button.\n */\n private _reflowOverflow(trigger: Button): void {\n const insets = this.getInsets();\n const inner = this.getWidth() - insets.getLeft() - insets.getRight();\n\n const lm = this.getLayoutManager();\n const gap = (lm instanceof HBox) ? lm.getComponentSpacing() : TOOLBAR_GAP_DEFAULT;\n\n const children = this.getComponents().filter(child => child !== trigger && child !== this._overflowSpacer);\n const overflowed = this._computeOverflowed(children, trigger, inner, gap);\n\n let changed = false;\n\n for (const child of children) {\n const shouldHide = overflowed.includes(child);\n\n if (child.isDisplayed() === shouldHide) {\n child.setDisplayed(!shouldHide);\n changed = true;\n }\n }\n\n const wantTrigger = overflowed.length > 0;\n\n if (trigger.isDisplayed() !== wantTrigger) {\n trigger.setDisplayed(wantTrigger);\n changed = true;\n }\n\n if (changed) {\n super.doLayout();\n }\n }\n\n /**\n * Walks the (non-trigger) children left-to-right, accumulating preferred\n * widths plus inter-child gaps, and returns the `Button` / `ToggleButton`\n * children that cross the inner width. When nothing overflows the result is\n * empty; otherwise the trigger's own width is reserved so the chevron has\n * room. Non-`Button` children that don't fit are left in place (clipped) —\n * only buttons have a well-defined menu-row representation.\n *\n * @param children - The bar's children, excluding the trigger.\n * @param trigger - The overflow trigger, measured for its reserved width.\n * @param inner - The bar's inner content width in pixels.\n * @param gap - The inter-child spacing in pixels.\n *\n * @returns The overflowed button children, in document order.\n */\n private _computeOverflowed(children: Component[], trigger: Button, inner: number, gap: number): Component[] {\n const widthOf = (child: Component): number => child.getPreferredSize()?.width ?? 0;\n\n let total = 0;\n\n for (let i = 0; i < children.length; i++) {\n total += widthOf(children[i]);\n\n if (i > 0) {\n total += gap;\n }\n }\n\n if (total <= inner) {\n return [];\n }\n\n const reserve = widthOf(trigger) + gap;\n const available = inner - reserve;\n\n const overflowed: Component[] = [];\n\n let used = 0;\n let crossed = false;\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n const next = used + widthOf(child) + (i > 0 ? gap : 0);\n\n if (next > available) {\n crossed = true;\n }\n\n // Once the width crosses the available extent, every following\n // Button overflows so the hidden set stays a contiguous trailing\n // run; non-Button children past the crossover stay put (clipped).\n if (crossed && child instanceof Button) {\n overflowed.push(child);\n } else {\n used = next;\n }\n }\n\n return overflowed;\n }\n\n /**\n * Builds one {@link MenuItemConfig} per currently-overflowed button and\n * toggles the rebuild-mode dropdown anchored under the trigger — opening it,\n * or closing it when the trigger is pressed again. Each row's `action`\n * re-fires the source button's `\"click\"` (the DOM event behind its\n * `\"action\"`) so the dropdown drives the original handler.\n */\n private _toggleOverflowMenu(): void {\n const trigger = this._overflowButton;\n const menu = this._overflowMenu;\n\n if (trigger === null || trigger === undefined || menu === null || menu === undefined) {\n return;\n }\n\n const configs: MenuItemConfig[] = [];\n\n for (const child of this.getComponents()) {\n if (child === trigger || child.isDisplayed() || !(child instanceof Button)) {\n continue;\n }\n\n const glyph = child.getGlyph()?.getGlyphName();\n\n configs.push({\n text: child.getText(),\n glyph: glyph,\n action: () => { child.click(); },\n });\n }\n\n const triggerEl = trigger.getElement();\n\n if (!triggerEl) {\n return;\n }\n\n const rect = DOM.source.getViewportRect(trigger);\n\n // toggleFor excludes the trigger from the menu's outside-click dismissal\n // and remembers it, so re-pressing the overflow button closes the menu\n // instead of the close-then-reopen flash a bare show() would produce.\n menu.toggleFor(triggerEl, rect, configs);\n }\n}\n\nconst ToolBarCallable = callable(ToolBar);\ntype ToolBarCallable<TOptions extends ToolBarOptions = ToolBarOptions> = ToolBar<TOptions>;\nexport {\n ToolBar as _ToolBar,\n ToolBarCallable as ToolBar\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisOrientation } from \"~/primitive/Axis.js\";\n\n/**\n * Construction-time options for {@link ToolBarSeparator}.\n *\n * @category Components\n */\nexport interface ToolBarSeparatorOptions extends ComponentOptions {\n orientation?: AxisOrientation;\n}\n\n/**\n * Empty subclass-default const so the super call follows the framework's\n * `(options, defaults)` shape uniformly.\n */\nconst _defaultToolBarSeparatorOptions: Partial<ToolBarSeparatorOptions> = {};\n\n/**\n * A thin divider rule used inside a [`ToolBar`](/api/component/menubar/classes/ToolBar) to visually group\n * related controls. Defaults to a vertical rule (for horizontal toolbars);\n * pass `orientation: \"horizontal\"` for a vertical toolbar.\n *\n * Renders as a single bordered DOM element. The rule colour is theme-driven\n * via `--ts-ui-toolbar-separator-color`. Separators report `role=\"separator\"`\n * with a matching `aria-orientation`, and stay out of the keyboard tab order.\n *\n * @example\n * ```typescript\n * import { ToolBar, ToolBarSeparator } from '@jimka/typescript-ui/component/menubar';\n * const bar = new ToolBar();\n * bar.addComponent(new ToolBarSeparator());\n * ```\n *\n * @category Components\n */\nclass ToolBarSeparator extends Component<ToolBarSeparatorOptions> {\n\n /**\n * Pixel thickness of the rendered rule — a 1-pixel hairline. The breathing\n * room on either side of the rule comes from the parent toolbar's HBox /\n * VBox `componentSpacing` (theme token `--ts-ui-toolbar-gap`, default 4 px),\n * so the separator itself only carries the line. A wider THICKNESS would\n * stack on top of the parent's spacing and produce an asymmetric gap.\n */\n static readonly THICKNESS: number = 1;\n\n private readonly _orientation: AxisOrientation;\n\n /**\n * Constructs a `ToolBarSeparator`.\n *\n * @param options - Optional construction-time options. `options.orientation`\n * selects the rule direction; defaults to `\"vertical\"`.\n */\n constructor(options?: ToolBarSeparatorOptions) {\n super(options, _defaultToolBarSeparatorOptions);\n\n this._orientation = options?.orientation ?? \"vertical\";\n\n if (this._orientation === \"vertical\") {\n // preferredSize.height = 0 keeps the separator out of the parent\n // HBox's row-height computation. The rendered height comes from\n // the parent's stretching=true branch, clamped to maxSize.height\n // (MAX_VALUE → containerSize.height). preferredSize.width = THICKNESS\n // makes the toolbar's own preferred width count the separator.\n this.setPreferredSize(ToolBarSeparator.THICKNESS, 0);\n this.setMaxSize(ToolBarSeparator.THICKNESS, Number.MAX_VALUE);\n } else {\n this.setPreferredSize(0, ToolBarSeparator.THICKNESS);\n this.setMaxSize(Number.MAX_VALUE, ToolBarSeparator.THICKNESS);\n }\n\n // The element IS the rule — a 1 px line filled with the theme colour.\n this.setBackgroundColor(\"var(--ts-ui-toolbar-separator-color, rgb(220, 220, 220))\");\n\n this.getAria().setRole(\"separator\");\n this.getAria().setOrientation(this._orientation);\n this.getAria().setTabIndex(-1);\n }\n\n /**\n * Returns the orientation passed at construction time.\n *\n * @returns The `ToolBarSeparator` orientation — `\"vertical\"` or `\"horizontal\"`.\n */\n getOrientation(): AxisOrientation {\n return this._orientation;\n }\n}\n\nconst ToolBarSeparatorCallable = callable(ToolBarSeparator);\ntype ToolBarSeparatorCallable = ToolBarSeparator;\nexport {\n ToolBarSeparator as _ToolBarSeparator,\n ToolBarSeparatorCallable as ToolBarSeparator\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"menubar.es.js","names":[],"sources":["../../../src/typescript/lib/component/menubar/MenuBarButton.ts","../../../src/typescript/lib/component/menubar/MenuBar.ts","../../../src/typescript/lib/component/menubar/ToolBar.ts","../../../src/typescript/lib/component/menubar/ToolBarSeparator.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button, ButtonOptions } from \"~/component/button/Button.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuBarButton}.\n *\n * @category Components\n */\nexport interface MenuBarButtonOptions extends ButtonOptions {\n}\n\n/**\n * User-overridable visual defaults forwarded to `super` via the third\n * constructor arg. The cascade in `Component`'s constructor merges these\n * over Button's own defaults and dispatches each setter once with the\n * final value, so any field the caller supplied wins.\n */\nconst _defaultMenuBarButtonOptions: Partial<MenuBarButtonOptions> = {\n backgroundColor: \"var(--ts-ui-menu-bar-btn-bg, transparent)\",\n foregroundColor: \"var(--ts-ui-menu-bar-btn-fg, inherit)\",\n cursor: \"pointer\",\n};\n\n/** Pixel gap between the leading glyph and the label in the content row. */\nconst GLYPH_TEXT_GAP = 4;\n\n/** Horizontal padding inside the button, applied via `setInsets`. */\nconst HORIZONTAL_PAD = 10;\n\n/**\n * Fixed row height shared by every `MenuBarButton` and by the parent\n * [`MenuBar`](/api/component/menubar/classes/MenuBar) container's `setMinSize`. Exported so the two stay in\n * lockstep — changing one without the other would let the bar grow taller\n * than its buttons.\n */\nexport const MENU_BAR_BUTTON_HEIGHT: number = 28;\n\n/**\n * A single top-level button in a [`MenuBar`](/api/component/menubar/classes/MenuBar) (e.g. \"File\", \"Edit\").\n *\n * Extends [`Button`](/api/component/button/classes/Button) with `chromeless: true`\n * so the menubar's flat label-style appearance dodges Button's ridge border,\n * drop shadow, and gradient defaults. Reuses Button's content-row machinery\n * for the optional leading glyph + label pair, re-anchored to the west edge.\n * Adds menubar-specific ARIA (`role=\"menuitem\"`, `aria-haspopup=\"menu\"`,\n * `aria-expanded`) and the sticky `setActive` highlight for the\n * dropdown-open state. The `:hover` rule rides Button's `styleRules` bag.\n *\n * Communicates open/close intent back to [`MenuBar`](/api/component/menubar/classes/MenuBar) via callbacks passed at\n * construction time.\n *\n * @category Components\n */\nclass MenuBarButton extends Button<MenuBarButtonOptions> {\n\n private readonly _onClickHandler: () => void;\n private readonly _onMouseOverHandler: () => void;\n\n /**\n * Constructs a `MenuBarButton`.\n *\n * @param text - The label shown in the bar (e.g. `\"File\"`).\n * @param onClick - Called when the user clicks this button.\n * @param onHover - Called when the mouse enters this button while a menu is open (quick-switch).\n * @param options - Optional configuration bag. `glyph` is inherited from\n * [`ButtonOptions`](/api/component/button/interfaces/ButtonOptions).\n */\n constructor(\n text: string,\n onClick: () => void,\n onHover: () => void,\n options?: MenuBarButtonOptions,\n ) {\n super(\n text,\n {\n ...options,\n // chromeless suppresses Button's ridge border, drop shadow,\n // gradient background, and the pressed/hover treatments —\n // the menubar wants a flat label-shaped surface.\n chromeless: true,\n // Menubar-specific :hover highlight rides Button's styleRules\n // bag so the cascade routes through createStyleRule's\n // dedupe-and-defer machinery. Merge with any caller-supplied\n // styleRules entries via the canonical array-spread idiom.\n styleRules: [\n ...(options?.styleRules ?? []),\n {\n suffix: \":hover\",\n styles: {\n backgroundColor: \"var(--ts-ui-menu-bar-btn-hover-bg, rgba(30, 100, 200, 0.10))\",\n },\n },\n ],\n },\n _defaultMenuBarButtonOptions,\n );\n\n // Bump the HBox content-row spacing from Button's default (2) to the\n // menubar's GLYPH_TEXT_GAP (4).\n (this._content.getLayoutManager() as HBox).setComponentSpacing(GLYPH_TEXT_GAP);\n\n // Horizontal padding inside the button — replaces Button's 4-px\n // insets default. `setInsets` (overridden in Button) auto-fires\n // `recomputePreferredSize`, so the menubar's fixed-height\n // `computePreferredSize` override lands the right dimensions\n // without explicit prodding here.\n this.setInsets(new Insets(0, HORIZONTAL_PAD, 0, HORIZONTAL_PAD));\n\n this.getAria().setRole(\"menuitem\");\n this.getAria().setHasPopup(\"menu\");\n this.getAria().setExpanded(false);\n\n this._onClickHandler = (): void => { onClick(); };\n this._onMouseOverHandler = (): void => { onHover(); };\n\n // Internal `Event.addListener(this, …)` calls — the class registers\n // listeners on itself, which is the allowed shape. The named-method\n // bypass rule only forbids *external* consumers from doing the same.\n Event.addListener(this, \"click\", this._onClickHandler);\n Event.addListener(this, \"mouseover\", this._onMouseOverHandler);\n }\n\n /**\n * Pins the button's height to {@link MENU_BAR_BUTTON_HEIGHT} while\n * inheriting Button's content-derived width.\n */\n protected override computePreferredSize(): { width: number; height: number } {\n const { width } = super.computePreferredSize();\n\n return { width, height: MENU_BAR_BUTTON_HEIGHT };\n }\n\n /**\n * Sets the active (dropdown-open) visual state.\n *\n * Active buttons show a persistent highlight background and report `aria-expanded=\"true\"`.\n *\n * @param active - `true` to show the open state, `false` to revert to the default.\n */\n setActive(active: boolean): this {\n this.setBackgroundColor(\n active\n ? \"var(--ts-ui-menu-bar-btn-hover-bg, rgba(30, 100, 200, 0.10))\"\n : \"var(--ts-ui-menu-bar-btn-bg, transparent)\"\n );\n this.getAria().setExpanded(active);\n\n // While this button's dropdown is open, silence its hover tooltip — the\n // title tooltip would otherwise pop over the open menu (and re-arm on a\n // quick-switch re-hover). Restored when the menu closes.\n this.setTooltipSuppressed(active);\n\n return this;\n }\n\n /**\n * Removes Event listeners registered by this button.\n */\n dispose(): void {\n Event.removeListener(this, \"click\", this._onClickHandler);\n Event.removeListener(this, \"mouseover\", this._onMouseOverHandler);\n }\n}\n\nconst MenuBarButtonCallable = callable(MenuBarButton);\ntype MenuBarButtonCallable = MenuBarButton;\nexport {\n MenuBarButton as _MenuBarButton,\n MenuBarButtonCallable as MenuBarButton\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { MenuBarButton, MENU_BAR_BUTTON_HEIGHT } from \"~/component/menubar/MenuBarButton.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuConfig } from \"~/component/container/MenuItem.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link MenuBar}.\n *\n * @category Components\n */\nexport interface MenuBarOptions extends ComponentOptions {\n /** Top-level menus to populate the bar with, equivalent to a tail `setMenus()`. */\n menus?: MenuConfig[];\n}\n\n/**\n * A persistent horizontal menu bar that hosts top-level dropdown menus.\n *\n * `setMenus()` populates the bar with [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton) children. Clicking a button\n * opens the corresponding [`Menu`](/api/overlay/classes/Menu) dropdown. While any dropdown is open,\n * hovering another button switches menus immediately (quick-switch mode), and\n * keyboard navigation (Arrow keys, Enter, Escape) is handled via a viewport-level\n * keydown listener.\n *\n * @example\n * ```typescript\n * const bar = new MenuBar();\n * bar.setMenus([\n * { label: 'File', items: [\n * { text: 'New', shortcut: 'Ctrl+N', action: () => newDoc() },\n * { separator: true },\n * { text: 'Quit', enabled: false },\n * ]},\n * { label: 'Edit', items: [\n * { text: 'Undo', shortcut: 'Ctrl+Z', action: () => undo() },\n * ]},\n * ]);\n * container.addComponent(bar);\n * ```\n *\n * @category Components\n */\nclass MenuBar extends Component {\n\n private readonly _buttons: MenuBarButton[] = [];\n private readonly _panels: Menu[] = [];\n private _openIndex: number = -1;\n private _quickSwitchActive: boolean = false;\n private _keydownListening: boolean = false;\n\n private readonly _onKeyDown: (e: KeyboardEvent) => void;\n\n /**\n * Constructs a `MenuBar`, optionally populated from `options.menus` (the\n * options-bag equivalent of a tail `setMenus()` call).\n *\n * @param options - Optional construction-time options; `menus` populates the bar.\n */\n constructor(options?: MenuBarOptions) {\n super(options);\n\n const hbox = new HBox();\n hbox.setComponentSpacing(0);\n hbox.setStretching(true);\n this.setLayoutManager(hbox);\n\n // Default to the tool bar's background so menu bars and tool bars read as\n // one surface; the shipped themes set --ts-ui-menu-bar-bg to their\n // toolBar.background, and this untokened fallback matches ToolBar's own.\n this.setBackgroundColor(\"var(--ts-ui-menu-bar-bg, rgb(245, 245, 245))\");\n this.setElementCSSRule(\n \"borderBottom\",\n \"1px solid var(--ts-ui-menu-bar-border, rgb(220, 220, 220))\"\n );\n this.setMinSize(0, MENU_BAR_BUTTON_HEIGHT);\n\n this.getAria().setRole(\"menubar\");\n this.getAria().setLabel(\"Main menu\");\n this.getAria().setTabIndex(0);\n\n this._onKeyDown = (e: KeyboardEvent) => {\n if (this._openIndex < 0) {\n return;\n }\n\n const panel = this._panels[this._openIndex];\n\n switch (e.key) {\n case \"Escape\":\n e.preventDefault();\n e.stopPropagation();\n this.closeMenu();\n break;\n\n case \"ArrowLeft\":\n e.preventDefault();\n e.stopPropagation();\n this.openMenu((this._openIndex - 1 + this._panels.length) % this._panels.length);\n break;\n\n case \"ArrowRight\":\n e.preventDefault();\n e.stopPropagation();\n this.openMenu((this._openIndex + 1) % this._panels.length);\n break;\n\n case \"ArrowDown\":\n e.preventDefault();\n e.stopPropagation();\n\n if (panel.getFocusedIndex() < 0) {\n panel.focusItem(0);\n } else {\n panel.focusNext();\n }\n\n break;\n\n case \"ArrowUp\":\n e.preventDefault();\n e.stopPropagation();\n panel.focusPrev();\n break;\n\n case \"Enter\":\n e.preventDefault();\n e.stopPropagation();\n panel.activateFocused();\n break;\n }\n };\n\n if (options?.menus) {\n this.setMenus(options.menus);\n }\n }\n\n /**\n * Replaces the current set of top-level menus.\n *\n * Disposes all existing [`MenuBarButton`](/api/component/menubar/classes/MenuBarButton) and [`Menu`](/api/overlay/classes/Menu) instances, then rebuilds\n * them from the given descriptors.\n *\n * @param menus - Ordered list of top-level menu descriptors.\n */\n setMenus(menus: MenuConfig[]): this {\n if (this._openIndex >= 0) {\n this.closeMenu();\n }\n\n for (const panel of this._panels) {\n panel.dispose();\n }\n\n for (const button of this._buttons) {\n button.dispose();\n }\n\n this._buttons.length = 0;\n this._panels.length = 0;\n\n this.removeAllComponents();\n\n for (let i = 0; i < menus.length; i++) {\n const menu = menus[i];\n const index = i;\n\n const button = new MenuBarButton(\n menu.label,\n () => {\n if (this._openIndex === index) {\n this.closeMenu();\n } else {\n this.openMenu(index);\n }\n },\n () => {\n if (this._quickSwitchActive && this._openIndex !== index) {\n this.openMenu(index);\n }\n },\n menu.glyph !== undefined ? { glyph: menu.glyph } : undefined\n );\n\n const panel = new Menu(menu.items, () => { this.closeMenu(); });\n\n this._buttons.push(button);\n this._panels.push(panel);\n this.addComponent(button);\n }\n\n return this;\n }\n\n /**\n * Programmatically opens the menu at the given index, closing any currently open menu first.\n *\n * @param index - Zero-based index into the menus array.\n */\n openMenu(index: number): this {\n if (index < 0 || index >= this._panels.length) {\n return this;\n }\n\n if (index === this._openIndex) {\n return this;\n }\n\n if (this._openIndex >= 0) {\n this._panels[this._openIndex].close();\n this._buttons[this._openIndex].setActive(false);\n }\n\n this._openIndex = index;\n this._quickSwitchActive = true;\n\n this._buttons[index].setActive(true);\n // Exclude only the opener button from the panel's outside-click dismissal\n // (so its own mousedown does not self-close before the click toggles it),\n // not the whole bar — otherwise a click on empty bar space would be\n // treated as inside and never close the menu.\n this._panels[index].setExcludedElement(this._buttons[index].getElement(true)!);\n this._panels[index].open(this._buttons[index].getElement(true)!);\n\n if (!this._keydownListening) {\n this._keydownListening = true;\n Event.addViewportListener(this, \"keydown\", this._onKeyDown);\n }\n\n return this;\n }\n\n /**\n * Closes the currently open menu, if any.\n */\n closeMenu(): this {\n if (this._openIndex < 0) {\n return this;\n }\n\n this._panels[this._openIndex].close();\n this._buttons[this._openIndex].setActive(false);\n\n this._openIndex = -1;\n this._quickSwitchActive = false;\n\n if (this._keydownListening) {\n this._keydownListening = false;\n Event.removeViewportListener(this, \"keydown\", this._onKeyDown);\n }\n\n return this;\n }\n\n /**\n * Returns the index of the currently open menu, or `-1` if none is open.\n *\n * @returns The open menu index, or -1.\n */\n getOpenIndex(): number {\n return this._openIndex;\n }\n}\n\nconst MenuBarCallable = callable(MenuBar);\ntype MenuBarCallable = MenuBar;\nexport {\n MenuBar as _MenuBar,\n MenuBarCallable as MenuBar\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Button } from \"~/component/button/Button.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { VBox } from \"~/layout/VBox.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { RovingTabIndex } from \"~/core/RovingTabIndex.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { Spacer } from \"~/component/container/Spacer.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { ellipsis_v } from \"~/glyphs/solid/ellipsis_v.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisOrientation, AxisEnd } from \"~/primitive/Axis.js\";\n\n// Register the overflow trigger's chevron eagerly at module load — same pattern\n// as SplitButton registering its caret_down — so the lazily-created \"more\"\n// affordance always resolves its glyph without the consumer pre-registering it.\nGlyph.register(ellipsis_v);\n\n/**\n * Overflow behaviour for a {@link ToolBar} whose children exceed its measured\n * extent. `\"clip\"` (the default) lets the children spill into the parent's\n * clipping region. `\"menu\"` hides the `Button` / `ToggleButton` children that\n * don't fit and surfaces them in a dropdown opened by a trailing chevron\n * affordance. Menu overflow is horizontal-only; vertical bars always clip.\n *\n * @category Components\n */\nexport type ToolBarOverflow = \"clip\" | \"menu\";\n\n/**\n * Construction-time options for {@link ToolBar}.\n *\n * @category Components\n */\nexport interface ToolBarOptions extends ContainerOptions {\n orientation?: AxisOrientation;\n compact?: boolean;\n overflow?: ToolBarOverflow;\n /**\n * Edge the `\"menu\"` overflow trigger sits on — `\"end\"` (default) or\n * `\"start\"`. No visible effect unless `overflow` is `\"menu\"`. Runtime\n * counterpart `setOverflowSide`.\n */\n overflowSide?: AxisEnd;\n /**\n * When `true` (the default), `Button` / `ToggleButton` children added to the\n * bar are switched to flat appearance for the classical toolbar look — no\n * resting frame, a light frame on hover, and a sunken inset frame on press.\n * Set `false` to keep raised buttons. Runtime counterpart `setFlat`.\n */\n flat?: boolean;\n}\n\n/**\n * Fallback inter-child spacing in pixels for the overflow reserve math only.\n * `_computeOverflowed` reserves a gap between children when measuring the\n * fit-set; it reads the live `getComponentSpacing()` and falls back to this\n * constant only when the layout manager isn't an `HBox`. The bar no longer\n * drives any gap itself (spacing defaults to 0), so this is purely the typed\n * fallback that keeps the overflow arithmetic well-defined.\n */\nconst TOOLBAR_GAP_DEFAULT: number = 4;\n\n/**\n * Registry glyph rendered on the overflow (\"more\") trigger button. Verified\n * present in the solid glyph set (`glyphs/solid/ellipsis_v.ts`); the vertical\n * ellipsis is the conventional overflow affordance for a horizontal bar.\n */\nconst OVERFLOW_TRIGGER_GLYPH: string = \"ellipsis-v\";\n\n/**\n * User-overridable defaults forwarded to `super` via the options bag.\n */\nconst _defaultToolBarOptions: Partial<ToolBarOptions> = {\n orientation: \"horizontal\",\n compact: true,\n overflow: \"clip\",\n overflowSide: \"end\",\n flat: true,\n backgroundColor: \"var(--ts-ui-toolbar-bg, rgb(245, 245, 245))\",\n};\n\n/**\n * A horizontal (or vertical) strip of related controls — e.g. Bold / Italic\n * / Underline in a text editor, or Cut / Copy / Paste in a file manager.\n *\n * `ToolBar` extends [`Container`](/api/core/classes/Container) and defaults to\n * compact mode (`compact: true`), so at construction it drops its own panel\n * insets to zero and renders its `Button` / `ToggleButton` children compact;\n * `setCompact(false)` restores the 4-pixel panel insets and the children's\n * default rendering. Layout defaults to a horizontal\n * [`HBox`](/api/layout/classes/HBox); pass `orientation: \"vertical\"` (or call\n * `setOrientation(\"vertical\")`) to swap to a [`VBox`](/api/layout/classes/VBox).\n *\n * Children can be any [`Component`](/api/core/classes/Component) — typically\n * [`Button`](/api/component/button/classes/Button),\n * [`ToggleButton`](/api/component/button/classes/ToggleButton),\n * [`ButtonGroup`](/api/overlay/classes/ButtonGroup) members,\n * [`ComboBox`](/api/component/input/classes/ComboBox), or {@link ToolBarSeparator}.\n * Focusable children (`tabindex >= 0`) are auto-registered with an internal\n * [`RovingTabIndex`](/api/core/classes/RovingTabIndex) so Arrow keys cycle\n * focus between them, matching the\n * [`ButtonGroup`](/api/overlay/classes/ButtonGroup) keyboard-nav pattern.\n *\n * @example\n * ```typescript\n * import { ToolBar, ToolBarSeparator } from '@jimka/typescript-ui/component/menubar';\n * import { Button } from '@jimka/typescript-ui/component/button';\n *\n * const bar = new ToolBar();\n * bar.addComponent(new Button('Cut'));\n * bar.addComponent(new Button('Copy'));\n * bar.addComponent(new Button('Paste'));\n * bar.addComponent(new ToolBarSeparator());\n * bar.addComponent(new Button('Find'));\n * ```\n *\n * @category Components\n */\nclass ToolBar<TOptions extends ToolBarOptions = ToolBarOptions> extends Container<TOptions> {\n\n declare private _orientation: AxisOrientation;\n declare private _compact: boolean;\n declare private _overflowMode: ToolBarOverflow;\n declare private _overflowSide: AxisEnd;\n declare private _flat: boolean;\n declare private _rovingTabIndex: RovingTabIndex;\n declare private _onKeyDown: (e: KeyboardEvent) => void;\n declare private _overflowButton: Button | null;\n declare private _overflowMenu: Menu | null;\n declare private _overflowSpacer: Spacer | null;\n\n /**\n * Constructs a `ToolBar`.\n *\n * @param options - Optional construction-time options.\n *\n * @remarks The parameter is typed as the concrete {@link ToolBarOptions}\n * rather than the class's `TOptions` parameter so that passing an options\n * literal (e.g. `new ToolBar({ compact: true })`) cannot narrow `TOptions`\n * to that literal — which would type the instance as `ToolBar<{ compact:\n * true }>` and fail the weak-type assignability check when it is later used\n * as a base `Component` (e.g. `container.addComponent(toolbar)`). `TOptions`\n * stays at its `ToolBarOptions` default.\n */\n constructor(options?: ToolBarOptions) {\n super(options as TOptions, _defaultToolBarOptions as Partial<TOptions>);\n\n this.getAria().setRole(\"toolbar\");\n this.getAria().setTabIndex(0);\n\n this._onKeyDown = (e: KeyboardEvent) => {\n const isHoriz = this._orientation === \"horizontal\";\n const fwd = isHoriz ? \"ArrowRight\" : \"ArrowDown\";\n const back = isHoriz ? \"ArrowLeft\" : \"ArrowUp\";\n\n if (e.key === fwd) {\n e.preventDefault();\n this._rovingTabIndex.moveNext();\n } else if (e.key === back) {\n e.preventDefault();\n this._rovingTabIndex.movePrev();\n }\n };\n\n Event.addSubtreeListener(this, \"keydown\", this._onKeyDown);\n }\n\n /**\n * Applies a {@link ToolBarOptions} bag. Inherited `Container` fields cascade\n * through `super.applyOptions`; the `ToolBar`-specific fields\n * (orientation, compact, overflow) are dispatched here when set.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: TOptions): this {\n super.applyOptions(options);\n\n // These fields all carry a class default and seed construction-time\n // backing state, so dispatch the caller value or the class default —\n // never leave the setter unfired (mirrors Panel.setAutoScroll).\n this.setOrientation(options.orientation ?? this.getOrientation());\n this.setCompact(options.compact ?? this.isCompact());\n // Dispatched before `overflow` so the trigger, created on entry to\n // `\"menu\"` mode, is positioned on the configured side from the start.\n this.setOverflowSide(options.overflowSide ?? this.getOverflowSide());\n this.setOverflow(options.overflow ?? this.getOverflow());\n this.setFlat(options.flat ?? this.isFlat());\n\n return this;\n }\n\n /**\n * Sets the layout direction. Horizontal toolbars pack children\n * left-to-right via [`HBox`](/api/layout/classes/HBox); vertical toolbars\n * pack them top-to-bottom via [`VBox`](/api/layout/classes/VBox). Child\n * spacing is preserved across the swap; the trailing-edge border flips\n * from bottom to right (or vice versa) to match the new direction.\n *\n * Existing {@link ToolBarSeparator} children are **not** auto-flipped —\n * see the architecture note in the plan.\n *\n * @param value - The new orientation.\n *\n * @returns This component, for method chaining.\n */\n setOrientation(value: AxisOrientation): this {\n if (value === this._orientation) {\n return this;\n }\n\n const oldLM = this.getLayoutManager();\n // Default to flush (0) — the bar no longer drives a gap; child density\n // comes from the buttons' own compact insets. A consumer-set spacing on\n // an existing HBox/VBox is preserved across the orientation swap.\n const gap = (oldLM instanceof HBox || oldLM instanceof VBox)\n ? oldLM.getComponentSpacing()\n : 0;\n\n const newLM: HBox | VBox = value === \"horizontal\" ? new HBox() : new VBox();\n newLM.setComponentSpacing(gap);\n newLM.setStretching(true);\n\n this.setLayoutManager(newLM);\n this._orientation = value;\n\n this.getAria().setOrientation(value);\n\n const ruleColor = \"var(--ts-ui-toolbar-border, rgb(220, 220, 220))\";\n\n if (value === \"horizontal\") {\n this.setBorder({ borderBottom: `1px solid ${ruleColor}` });\n } else {\n this.setBorder({ borderRight: `1px solid ${ruleColor}` });\n }\n\n return this;\n }\n\n /**\n * Returns the current orientation.\n *\n * @returns `\"horizontal\"` or `\"vertical\"`.\n */\n getOrientation(): AxisOrientation {\n return this._orientation ?? this._defaultOptions.orientation!;\n }\n\n /**\n * Toggles compact mode. In compact mode the bar's own panel insets shrink\n * from `(4, 4, 4, 4)` to `(0, 0, 0, 0)` and every `Button` / `ToggleButton`\n * child is switched to compact rendering (tighter button insets), the same\n * way {@link setFlat} drives the flat appearance onto its button children.\n * Child spacing is left untouched — the bar packs its children flush and the\n * density comes from the buttons themselves.\n *\n * @param value - `true` to enable compact mode, `false` to restore defaults.\n *\n * @returns This component, for method chaining.\n */\n setCompact(value: boolean): this {\n if (value === this._compact) {\n return this;\n }\n\n this._compact = value;\n\n // Compact packs flush to its edges (zero inset) so a toolbar sits tight\n // against the surrounding chrome; the density between children still\n // comes from the buttons' own compact insets. Non-compact restores the\n // roomy 4px panel insets.\n const inset = value ? 0 : 4;\n\n this.setInsets(new Insets(inset, inset, inset, inset));\n\n for (const child of this.getComponents()) {\n if (child instanceof Button) {\n child.setCompact(value);\n }\n }\n\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns whether compact mode is currently active.\n *\n * @returns `true` if compact mode is enabled.\n */\n isCompact(): boolean {\n return this._compact ?? this._defaultOptions.compact!;\n }\n\n /**\n * Sets the overflow strategy. `\"clip\"` lets children spill into the parent's\n * clipping region. `\"menu\"` hides the `Button` / `ToggleButton` children\n * that don't fit and surfaces them in a dropdown opened by a trailing\n * chevron affordance; the trigger and its rebuild-mode\n * [`Menu`](/api/overlay/classes/Menu) are created lazily on first entry to\n * `\"menu\"` mode. Menu overflow applies to horizontal bars only — a vertical\n * bar always clips.\n *\n * @param value - `\"clip\"` or `\"menu\"`.\n *\n * @returns This component, for method chaining.\n */\n setOverflow(value: ToolBarOverflow): this {\n this._overflowMode = value;\n\n if (value === \"menu\" && this._overflowButton === undefined) {\n this._createOverflowAffordance();\n }\n\n this.doLayout();\n\n return this;\n }\n\n /**\n * Lazily builds the overflow trigger — a flat, glyph-only `Button` carrying\n * the chevron glyph — and the rebuild-mode `Menu` it opens. The trigger is\n * appended through `super.addComponent` so it bypasses the flatten-children\n * pass in {@link addComponent} and is excluded from the overflow set by\n * identity. It starts hidden; {@link doLayout} shows it only once at least\n * one button has overflowed.\n */\n private _createOverflowAffordance(): void {\n const trigger = new Button({ flat: true, glyph: OVERFLOW_TRIGGER_GLYPH });\n\n trigger.setDisplayed(false);\n trigger.getAria().setLabel(\"More\");\n trigger.getAria().setHasPopup(\"menu\");\n\n trigger.on(\"action\", () => { this._toggleOverflowMenu(); });\n\n this._overflowButton = trigger;\n this._overflowMenu = new Menu();\n // Flex spacer that pushes a right-side trigger to the bar's far edge; it\n // is only parented while the side is `\"right\"` (see _positionOverflowTrigger).\n this._overflowSpacer = Spacer.flex();\n\n super.addComponent(trigger);\n\n this._positionOverflowTrigger();\n }\n\n /**\n * Pins the overflow trigger to the configured edge. For `\"end\"` a flex\n * {@link Spacer} is parented just before the trigger so the chevron is\n * driven to the bar's trailing edge (rather than sitting flush against the\n * last visible button); the final child order is `[content…, spacer, trigger]`.\n * For `\"start\"` the spacer is detached and the trigger leads as the first\n * child. A no-op when no trigger exists yet. Re-run whenever content is added\n * (the trigger must stay at the edge) or the side changes; the trailing run\n * of buttons still overflows regardless of the trigger's side.\n */\n private _positionOverflowTrigger(): void {\n const trigger = this._overflowButton;\n const spacer = this._overflowSpacer;\n if (trigger === undefined || trigger === null || spacer === undefined || spacer === null) {\n return;\n }\n\n if (this._overflowSide === \"end\") {\n if (spacer.getParentComponent() !== this) {\n super.addComponent(spacer);\n }\n\n // Order the tail as [spacer, trigger]: move the trigger last, then\n // slot the spacer immediately before it. `moveComponent` recomputes\n // the index after splicing the moved child out, so the pre-move\n // length-relative targets land both at the end.\n this.moveComponent(trigger, this.getComponents().length - 1);\n this.moveComponent(spacer, this.getComponents().length - 2);\n } else {\n if (spacer.getParentComponent() === this) {\n this.removeComponent(spacer);\n }\n\n this.moveComponent(trigger, 0);\n }\n }\n\n /**\n * Returns the current overflow strategy.\n *\n * @returns `\"clip\"` or `\"menu\"`.\n */\n getOverflow(): ToolBarOverflow {\n return this._overflowMode ?? this._defaultOptions.overflow!;\n }\n\n /**\n * Sets which edge the `\"menu\"` overflow trigger sits on. `\"end\"` (the\n * default) trails the visible buttons; `\"start\"` leads them. Only the\n * trigger's position changes — the overflowing buttons are the trailing run\n * either way. No visible effect until `overflow` is `\"menu\"`.\n *\n * @param value - `\"start\"` or `\"end\"`.\n *\n * @returns This component, for method chaining.\n */\n setOverflowSide(value: AxisEnd): this {\n if (value === this._overflowSide) {\n return this;\n }\n\n this._overflowSide = value;\n\n this._positionOverflowTrigger();\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns the edge the overflow trigger sits on.\n *\n * @returns `\"start\"` or `\"end\"`.\n */\n getOverflowSide(): AxisEnd {\n return this._overflowSide ?? this._defaultOptions.overflowSide!;\n }\n\n /**\n * Toggles the classical flat appearance for the bar's `Button` /\n * `ToggleButton` children. When `true` (the default), each such child is\n * switched to flat mode — no resting frame, a light frame on hover, a sunken\n * inset frame on press, and a depressed look for a toggled-on\n * `ToggleButton`. Glyph-only flat buttons also tighten to compact squares.\n * Setting `false` reverts existing button children to raised chrome.\n *\n * Non-`Button` children (separators, combo boxes, spacers) are left\n * untouched. The flag also governs children added later through\n * {@link addComponent}.\n *\n * @param value - `true` to flatten button children, `false` to restore them.\n *\n * @returns This component, for method chaining.\n */\n setFlat(value: boolean): this {\n if (value === this._flat) {\n return this;\n }\n\n this._flat = value;\n\n for (const child of this.getComponents()) {\n if (child instanceof Button) {\n child.setFlat(value);\n }\n }\n\n return this;\n }\n\n /**\n * Returns whether the bar flattens its `Button` children.\n *\n * @returns `true` if flat mode is enabled.\n */\n isFlat(): boolean {\n return this._flat ?? this._defaultOptions.flat!;\n }\n\n /**\n * Appends a child component and, when its tab-index marks it focusable,\n * registers it with the internal roving-tabindex group so Arrow keys\n * cycle focus through it.\n *\n * @param component - The child component to add.\n *\n * @returns This component, for method chaining.\n */\n override addComponent(component: Component, constraints?: LayoutConstraints): this {\n super.addComponent(component, constraints);\n\n if (this._rovingTabIndex === undefined) {\n this._rovingTabIndex = new RovingTabIndex();\n }\n\n if (component.getAria().getTabIndex() !== -1) {\n this._rovingTabIndex.add(component);\n }\n\n if (this._flat && component instanceof Button) {\n component.setFlat(true);\n }\n\n if (this._compact && component instanceof Button) {\n component.setCompact(true);\n }\n\n // Keep the overflow trigger pinned to its edge: a content child appended\n // after the trigger (the trigger is created before the demo's buttons\n // for a construction-time `overflow: \"menu\"`) would otherwise leave the\n // chevron stranded mid-row. No-op when no trigger exists.\n this._positionOverflowTrigger();\n\n return this;\n }\n\n /**\n * Lays the bar out and, in `\"menu\"` overflow mode on a horizontal bar,\n * reflows the `Button` / `ToggleButton` children that no longer fit into the\n * trailing chevron dropdown. The base pass runs first so children report\n * their resolved preferred widths; the reflow then re-derives the fit-set\n * from scratch each pass, so widening the bar restores previously-hidden\n * buttons. Vertical bars and `\"clip\"` mode skip the reflow entirely.\n *\n * @returns This component, for method chaining.\n */\n override doLayout(): this {\n super.doLayout();\n\n if (this._overflowMode !== \"menu\" || this._orientation !== \"horizontal\") {\n return this;\n }\n\n if (this._overflowButton === undefined || this._overflowButton === null) {\n return this;\n }\n\n this._reflowOverflow(this._overflowButton);\n\n return this;\n }\n\n /**\n * Re-derives which `Button` / `ToggleButton` children overflow the bar's\n * inner width and toggles their `display` accordingly, reserving room for\n * the trigger whenever at least one button is hidden. Only mutates child\n * visibility when the fit-set actually changed, so the\n * `setDisplayed`-triggered re-layout converges instead of thrashing. The\n * trigger itself is shown only while ≥1 button is overflowed and is then\n * positioned for the dropdown anchor.\n *\n * @param trigger - The lazily-created overflow trigger button.\n */\n private _reflowOverflow(trigger: Button): void {\n const insets = this.getInsets();\n const inner = this.getWidth() - insets.getLeft() - insets.getRight();\n\n const lm = this.getLayoutManager();\n const gap = (lm instanceof HBox) ? lm.getComponentSpacing() : TOOLBAR_GAP_DEFAULT;\n\n const children = this.getComponents().filter(child => child !== trigger && child !== this._overflowSpacer);\n const overflowed = this._computeOverflowed(children, trigger, inner, gap);\n\n let changed = false;\n\n for (const child of children) {\n const shouldHide = overflowed.includes(child);\n\n if (child.isDisplayed() === shouldHide) {\n child.setDisplayed(!shouldHide);\n changed = true;\n }\n }\n\n const wantTrigger = overflowed.length > 0;\n\n if (trigger.isDisplayed() !== wantTrigger) {\n trigger.setDisplayed(wantTrigger);\n changed = true;\n }\n\n if (changed) {\n super.doLayout();\n }\n }\n\n /**\n * Walks the (non-trigger) children left-to-right, accumulating preferred\n * widths plus inter-child gaps, and returns the `Button` / `ToggleButton`\n * children that cross the inner width. When nothing overflows the result is\n * empty; otherwise the trigger's own width is reserved so the chevron has\n * room. Non-`Button` children that don't fit are left in place (clipped) —\n * only buttons have a well-defined menu-row representation.\n *\n * @param children - The bar's children, excluding the trigger.\n * @param trigger - The overflow trigger, measured for its reserved width.\n * @param inner - The bar's inner content width in pixels.\n * @param gap - The inter-child spacing in pixels.\n *\n * @returns The overflowed button children, in document order.\n */\n private _computeOverflowed(children: Component[], trigger: Button, inner: number, gap: number): Component[] {\n const widthOf = (child: Component): number => child.getPreferredSize()?.width ?? 0;\n\n let total = 0;\n\n for (let i = 0; i < children.length; i++) {\n total += widthOf(children[i]);\n\n if (i > 0) {\n total += gap;\n }\n }\n\n if (total <= inner) {\n return [];\n }\n\n const reserve = widthOf(trigger) + gap;\n const available = inner - reserve;\n\n const overflowed: Component[] = [];\n\n let used = 0;\n let crossed = false;\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n const next = used + widthOf(child) + (i > 0 ? gap : 0);\n\n if (next > available) {\n crossed = true;\n }\n\n // Once the width crosses the available extent, every following\n // Button overflows so the hidden set stays a contiguous trailing\n // run; non-Button children past the crossover stay put (clipped).\n if (crossed && child instanceof Button) {\n overflowed.push(child);\n } else {\n used = next;\n }\n }\n\n return overflowed;\n }\n\n /**\n * Builds one {@link MenuItemConfig} per currently-overflowed button and\n * toggles the rebuild-mode dropdown anchored under the trigger — opening it,\n * or closing it when the trigger is pressed again. Each row's `action`\n * re-fires the source button's `\"click\"` (the DOM event behind its\n * `\"action\"`) so the dropdown drives the original handler.\n */\n private _toggleOverflowMenu(): void {\n const trigger = this._overflowButton;\n const menu = this._overflowMenu;\n\n if (trigger === null || trigger === undefined || menu === null || menu === undefined) {\n return;\n }\n\n const configs: MenuItemConfig[] = [];\n\n for (const child of this.getComponents()) {\n if (child === trigger || child.isDisplayed() || !(child instanceof Button)) {\n continue;\n }\n\n const glyph = child.getGlyph()?.getGlyphName();\n\n configs.push({\n text: child.getText(),\n glyph: glyph,\n action: () => { child.click(); },\n });\n }\n\n const triggerEl = trigger.getElement();\n\n if (!triggerEl) {\n return;\n }\n\n const rect = DOM.source.getViewportRect(trigger);\n\n // toggleFor excludes the trigger from the menu's outside-click dismissal\n // and remembers it, so re-pressing the overflow button closes the menu\n // instead of the close-then-reopen flash a bare show() would produce.\n menu.toggleFor(triggerEl, rect, configs);\n }\n}\n\nconst ToolBarCallable = callable(ToolBar);\ntype ToolBarCallable<TOptions extends ToolBarOptions = ToolBarOptions> = ToolBar<TOptions>;\nexport {\n ToolBar as _ToolBar,\n ToolBarCallable as ToolBar\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { AxisOrientation } from \"~/primitive/Axis.js\";\n\n/**\n * Construction-time options for {@link ToolBarSeparator}.\n *\n * @category Components\n */\nexport interface ToolBarSeparatorOptions extends ComponentOptions {\n orientation?: AxisOrientation;\n}\n\n/**\n * Empty subclass-default const so the super call follows the framework's\n * `(options, defaults)` shape uniformly.\n */\nconst _defaultToolBarSeparatorOptions: Partial<ToolBarSeparatorOptions> = {};\n\n/**\n * A thin divider rule used inside a [`ToolBar`](/api/component/menubar/classes/ToolBar) to visually group\n * related controls. Defaults to a vertical rule (for horizontal toolbars);\n * pass `orientation: \"horizontal\"` for a vertical toolbar.\n *\n * Renders as a single bordered DOM element. The rule colour is theme-driven\n * via `--ts-ui-toolbar-separator-color`. Separators report `role=\"separator\"`\n * with a matching `aria-orientation`, and stay out of the keyboard tab order.\n *\n * @example\n * ```typescript\n * import { ToolBar, ToolBarSeparator } from '@jimka/typescript-ui/component/menubar';\n * const bar = new ToolBar();\n * bar.addComponent(new ToolBarSeparator());\n * ```\n *\n * @category Components\n */\nclass ToolBarSeparator extends Component<ToolBarSeparatorOptions> {\n\n /**\n * Pixel thickness of the rendered rule — a 1-pixel hairline. The breathing\n * room on either side of the rule comes from the parent toolbar's HBox /\n * VBox `componentSpacing` (theme token `--ts-ui-toolbar-gap`, default 4 px),\n * so the separator itself only carries the line. A wider THICKNESS would\n * stack on top of the parent's spacing and produce an asymmetric gap.\n */\n static readonly THICKNESS: number = 1;\n\n private readonly _orientation: AxisOrientation;\n\n /**\n * Constructs a `ToolBarSeparator`.\n *\n * @param options - Optional construction-time options. `options.orientation`\n * selects the rule direction; defaults to `\"vertical\"`.\n */\n constructor(options?: ToolBarSeparatorOptions) {\n super(options, _defaultToolBarSeparatorOptions);\n\n this._orientation = options?.orientation ?? \"vertical\";\n\n if (this._orientation === \"vertical\") {\n // preferredSize.height = 0 keeps the separator out of the parent\n // HBox's row-height computation. The rendered height comes from\n // the parent's stretching=true branch, clamped to maxSize.height\n // (MAX_VALUE → containerSize.height). preferredSize.width = THICKNESS\n // makes the toolbar's own preferred width count the separator.\n this.setPreferredSize(ToolBarSeparator.THICKNESS, 0);\n this.setMaxSize(ToolBarSeparator.THICKNESS, Number.MAX_VALUE);\n } else {\n this.setPreferredSize(0, ToolBarSeparator.THICKNESS);\n this.setMaxSize(Number.MAX_VALUE, ToolBarSeparator.THICKNESS);\n }\n\n // The element IS the rule — a 1 px line filled with the theme colour.\n this.setBackgroundColor(\"var(--ts-ui-toolbar-separator-color, rgb(220, 220, 220))\");\n\n this.getAria().setRole(\"separator\");\n this.getAria().setOrientation(this._orientation);\n this.getAria().setTabIndex(-1);\n }\n\n /**\n * Returns the orientation passed at construction time.\n *\n * @returns The `ToolBarSeparator` orientation — `\"vertical\"` or `\"horizontal\"`.\n */\n getOrientation(): AxisOrientation {\n return this._orientation;\n }\n}\n\nconst ToolBarSeparatorCallable = callable(ToolBarSeparator);\ntype ToolBarSeparatorCallable = ToolBarSeparator;\nexport {\n ToolBarSeparator as _ToolBarSeparator,\n ToolBarSeparatorCallable as ToolBarSeparator\n};\n"],"mappings":"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"}
|