UNPKG

@eclipse-scout/core

Version:
186 lines (166 loc) 6.58 kB
/* * Copyright (c) 2010, 2023 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {AbstractLayout, ContextMenuPopup, Dimension, graphics, Menu, PrefSizeOptions, scout, SimpleTabArea, styles, widgets} from '../index'; import $ from 'jquery'; export class SimpleTabAreaLayout extends AbstractLayout { tabArea: SimpleTabArea; tabWidth: number; tabMinWidth: number; overflowTabItemWidth: number; protected _$overflowTab: JQuery; protected _overflowTabsIndizes: number[]; constructor(tabArea: SimpleTabArea) { super(); this.tabArea = tabArea; this._$overflowTab = null; this._overflowTabsIndizes = []; this.tabWidth = null; this.tabMinWidth = null; this.overflowTabItemWidth = null; } override layout($container: JQuery) { let htmlContainer = this.tabArea.htmlComp, containerSize = htmlContainer.size({ exact: true }), $tabs = htmlContainer.$comp.children('.simple-tab'), numTabs = this.tabArea.getTabs().length, smallPrefSize = this.smallPrefSize(); containerSize = containerSize.subtract(htmlContainer.insets()); this._initSizes(); // Reset tabs if (this._$overflowTab) { this._$overflowTab.remove(); } $tabs.setVisible(true); this._overflowTabsIndizes = []; widgets.updateFirstLastMarker(this.tabArea.getTabs()); // All tabs fit in container -> no overflow menu necessary if (smallPrefSize.width <= containerSize.width) { $container.removeClass('overflown'); return; } // Not all tabs fit in container -> put tabs into overflow menu $container.addClass('overflown'); containerSize.width -= this.overflowTabItemWidth; // check how many tabs fit into remaining containerSize.width let numVisibleTabs = Math.floor(containerSize.width / this.tabMinWidth); let numOverflowTabs = numTabs - numVisibleTabs; let selectedIndex = 0; $tabs.each((i, tab) => { if ($(tab).hasClass('selected')) { selectedIndex = i; } }); // determine visible range let rightEnd; let leftEnd = selectedIndex - Math.floor(numVisibleTabs / 2); if (leftEnd < 0) { leftEnd = 0; rightEnd = numVisibleTabs - 1; } else { rightEnd = leftEnd + numVisibleTabs - 1; if (rightEnd > numTabs - 1) { rightEnd = numTabs - 1; leftEnd = rightEnd - numVisibleTabs + 1; } } this._$overflowTab = htmlContainer.$comp .appendDiv('simple-overflow-tab-item') .on('mousedown', this._onOverflowTabItemMouseDown.bind(this)); this._$overflowTab.appendDiv('num-tabs').text(numOverflowTabs); $tabs.each((i, tab) => { if (i < leftEnd || i > rightEnd) { $(tab).setVisible(false); this._overflowTabsIndizes.push(i); } }); widgets.updateFirstLastMarker(this.tabArea.getVisibleTabs()); } smallPrefSize(options: PrefSizeOptions & { minTabWidth?: number } = {}): Dimension { this._initSizes(); options = $.extend({minTabWidth: this.tabMinWidth}, options); return this.preferredLayoutSize(this.tabArea.$container, options); } override preferredLayoutSize($container: JQuery, options?: PrefSizeOptions & { minTabWidth?: number }): Dimension { this._initSizes(); let minTabWidth = scout.nvl(options.minTabWidth, 0) || scout.nvl(this.tabWidth, 0); let numTabs = this.tabArea.getTabs().length; let minWidth = numTabs * minTabWidth; options = $.extend({useCssSize: true}, options); let prefSize = graphics.prefSize(this.tabArea.$container, options); if (options.widthHint && this.tabArea.displayStyle === SimpleTabArea.DisplayStyle.SPREAD_EVEN) { minWidth = Math.max(options.widthHint, minWidth); } return new Dimension(minWidth, prefSize.height); } /** * Reads the default sizes from CSS -> the tabs need to specify a width and a min-width. * The layout expects all tabs to have the same width. */ protected _initSizes() { if (this.tabWidth != null && this.tabMinWidth != null && this.overflowTabItemWidth != null) { return; } let $tab = this.tabArea.$container.children('.simple-tab').eq(0); if ($tab.length === 0) { return; } $tab = $tab.clone().addClass('selected'); // Non selected items have a margin, selected ones don't -> we need to get the width incl. margin let tabAreaClasses = this.tabArea.$container.attr('class'); let tabItemClasses = $tab.attr('class'); if (this.tabWidth === null) { this.tabWidth = styles.getSize([tabAreaClasses, tabItemClasses], 'width', 'width', 0); } if (this.tabMinWidth === null) { this.tabMinWidth = styles.getSize([tabAreaClasses, tabItemClasses], 'min-width', 'minWidth'); } if (this.overflowTabItemWidth === null) { this.overflowTabItemWidth = styles.getSize([tabAreaClasses, 'simple-overflow-tab-item'], 'min-width', 'minWidth'); this.overflowTabItemWidth += styles.getSize([tabAreaClasses, 'simple-overflow-tab-item'], 'margin-left', 'marginLeft'); this.overflowTabItemWidth += styles.getSize([tabAreaClasses, 'simple-overflow-tab-item'], 'margin-right', 'marginRight'); } } protected _onOverflowTabItemMouseDown(event: JQuery.MouseDownEvent) { let tabArea = this.tabArea; let overflowMenus = []; let $overflowTabItem = $(event.currentTarget); if ($overflowTabItem.data('popup')) { $overflowTabItem.data('popup').close(); return; } this._overflowTabsIndizes.forEach(i => { let tab = this.tabArea.getTabs()[i]; let menu = scout.create(Menu, { parent: this.tabArea, text: tab.getMenuText() }); menu.on('action', function() { $.log.isDebugEnabled() && $.log.debug('(SimpleTabAreaLayout#_onMouseDownOverflow) tab=' + this); tabArea.selectTab(this); }.bind(tab)); overflowMenus.push(menu); }); let popup = scout.create(ContextMenuPopup, { parent: this.tabArea, menuItems: overflowMenus, cloneMenuItems: false, $anchor: $overflowTabItem, closeOnAnchorMouseDown: false }); $overflowTabItem.addClass('selected'); $overflowTabItem.data('popup', popup); popup.one('remove', () => { $overflowTabItem.removeClass('selected'); $overflowTabItem.data('popup', null); }); popup.open(); } }