diff --git a/packages/openscd/src/addons/menu-tabs/menu-tabs.ts b/packages/openscd/src/addons/menu-tabs/menu-tabs.ts index f06e3e1dbd..7f6520726d 100644 --- a/packages/openscd/src/addons/menu-tabs/menu-tabs.ts +++ b/packages/openscd/src/addons/menu-tabs/menu-tabs.ts @@ -5,78 +5,185 @@ import { property, query, state, - TemplateResult, - css + css, } from 'lit-element'; import '@material/mwc-list'; -import '@material/mwc-tab'; -import '@material/mwc-tab-bar'; +import '@material/mwc-menu'; +import type { Menu } from '@material/mwc-menu'; import '@material/mwc-button'; +import '@material/mwc-icon-button'; +import '@material/mwc-icon'; -import { - Plugin, -} from "../../plugin.js" - +import { Plugin } from '../../plugin.js'; @customElement('oscd-menu-tabs') export class OscdMenuTabs extends LitElement { - @property({ type: Array }) editors: Plugin[] = []; _activeEditor: Plugin | undefined; - @property({ type: Object }) get activeEditor() { return this._activeEditor; } + @property({ type: Object }) get activeEditor() { + return this._activeEditor; + } set activeEditor(editor: Plugin | undefined) { this._activeEditor = editor; - const editorIndex = this.editors.findIndex(e => e.name === editor?.name && e.src === editor?.src); + const editorIndex = this.editors.findIndex( + e => e.name === editor?.name && e.src === editor?.src + ); this.activeTabIndex = editorIndex > -1 ? editorIndex : 0; this.requestUpdate(); - }; + } @state() private activeTabIndex = 0; + @state() private visibleTabs: Plugin[] = []; + @state() private hiddenTabs: Plugin[] = []; + @query('.app-bar-container') private appBarContainer!: HTMLElement; + @query('mwc-menu') private overflowMenu!: Menu; + + firstUpdated() { + this.#calculateVisibleTabs(); + window.addEventListener('resize', this.#calculateVisibleTabs); + } + + disconnectedCallback() { + window.removeEventListener('resize', this.#calculateVisibleTabs); + super.disconnectedCallback(); + } + + #calculateVisibleTabs = async () => { + await this.updateComplete; + + const visibleTabs: Plugin[] = []; + const hiddenTabs: Plugin[] = []; + let totalWidth = 0; + + const measurer = document.createElement('div'); + Object.assign(measurer.style, { + position: 'absolute', + visibility: 'hidden', + whiteSpace: 'nowrap', + fontSize: '14px', + padding: '0 20 px', + }); + document.body.appendChild(measurer); + + try { + for (let i = 0; i < this.editors.length; i++) { + measurer.textContent = this.editors[i].name; + const approximateWidthOtherThanText = 112; + const buttonWidth = + measurer.offsetWidth + approximateWidthOtherThanText; + + var availableWidth = this.appBarContainer.offsetWidth; + const isMenuButtonVisible = this.hiddenTabs.length > 0; + if (isMenuButtonVisible) { + availableWidth -= 48; + } + if (totalWidth + buttonWidth <= availableWidth) { + totalWidth += buttonWidth; + visibleTabs.push(this.editors[i]); + } else { + hiddenTabs.push(this.editors[i]); + } + } - render(){ - if(this.editors.length === 0){ return html``; } + this.visibleTabs = visibleTabs; + this.hiddenTabs = hiddenTabs; + } finally { + document.body.removeChild(measurer); + } + }; + + render() { + if (this.activeEditor === undefined && this.editors.length > 0) { + this.#activateTab(0); + } return html` - - ${ this.editors.map( EditorTab ) } - - ` +
+ ${this.visibleTabs.map( + (editor, index) => html` + this.#activateTab(index)} + > + ` + )} + ${this.hiddenTabs.length > 0 + ? html` + this.overflowMenu.show()} + > + + + ${this.hiddenTabs.map( + (editor, index) => html` + + this.#activateTab(this.visibleTabs.length + index)} + > + ${editor.name} + ${editor.icon} + + ` + )} + + ` + : ''} +
+ `; + } + + #activateTab(index: number) { + this.activeTabIndex = index; + this._activeEditor = this.editors[index]; + this.dispatchEvent( + new CustomEvent(TabActivatedEventKey, { + detail: { editor: this.editors[index] }, + composed: true, + bubbles: true, + }) + ); } static styles = css` - mwc-tab { - background-color: var(--primary); + .app-bar-container { + display: flex; + align-items: center; + justify-content: space-between; + height: 36px; + background-color: var(--mdc-theme-primary, #6200ee); + position: relative; + } + + mwc-button { + --mdc-theme-on-primary: #174b46; --mdc-theme-primary: var(--mdc-theme-on-primary); + --mdc-shape-small: 0px; + --mdc-button-horizontal-padding: 24px; + --mdc-typography-button-font-size: 0.9rem; + white-space: nowrap; } - ` - private handleActivatedEditorTab(e: CustomEvent): void { - const tabIndex = e.detail.index; - const editor = this.editors[tabIndex]; - this.activeTabIndex = tabIndex; - this.dispatchActivateEditor(editor); - } + mwc-button[active] { + --mdc-theme-on-primary: #d9e0ce; + } - private dispatchActivateEditor( editor: Plugin ){ - const newEvent = new CustomEvent(TabActivatedEventKey, { - detail: { editor }, - composed: true, - bubbles: true - }) - this.dispatchEvent(newEvent) - } -} + mwc-icon-button { + color: #174b46; + } -function EditorTab({ name, icon }: Plugin): TemplateResult { - return html` - + mwc-menu { + position: absolute; + left: 0; + top: 100%; + } `; } -export const TabActivatedEventKey = 'oscd-editor-tab-activated' +export const TabActivatedEventKey = 'oscd-editor-tab-activated'; export type TabActivatedEvent = CustomEvent; -export type TabActivatedEventDetail = { editor: Plugin } +export type TabActivatedEventDetail = { editor: Plugin };