@eclipse-scout/core
Version:
Eclipse Scout runtime
228 lines (197 loc) • 6.88 kB
text/typescript
/*
* Copyright (c) 2010, 2025 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, aria, EnumObject, Event, EventHandler, HtmlComponent, InitModelOf, scout, SimpleTab, SimpleTabAreaEventMap, SimpleTabAreaLayout, SimpleTabAreaModel, SimpleTabOverflowMenu, SimpleTabView, TabbableCoordinator, TabbableItem,
Widget, widgets
} from '../index';
export type SimpleTabAreaPosition = EnumObject<typeof SimpleTabArea.Position>;
export type SimpleTabAreaDisplayStyle = EnumObject<typeof SimpleTabArea.DisplayStyle>;
export class SimpleTabArea<TView extends SimpleTabView = SimpleTabView> extends Widget implements SimpleTabAreaModel<TView> {
declare model: SimpleTabAreaModel<TView>;
declare eventMap: SimpleTabAreaEventMap<TView>;
declare self: SimpleTabArea<any>;
static Position = {
TOP: 'top',
BOTTOM: 'bottom',
RIGHT: 'right',
LEFT: 'left'
} as const;
static DisplayStyle = {
DEFAULT: 'default',
SPREAD_EVEN: 'spreadEven'
} as const;
position: SimpleTabAreaPosition;
displayStyle: SimpleTabAreaDisplayStyle;
tabs: SimpleTab<TView>[];
tabbableCoordinator: TabbableCoordinator;
selectOnFocus = true;
protected _selectedViewTab: SimpleTab<TView>;
protected _tabClickHandler: EventHandler<Event<SimpleTab<TView>>>;
constructor() {
super();
this.position = SimpleTabArea.Position.TOP;
this.displayStyle = SimpleTabArea.DisplayStyle.DEFAULT;
this.tabs = [];
this._selectedViewTab = null;
this._tabClickHandler = this._onTabClick.bind(this);
this._addWidgetProperties(['tabs']);
}
protected override _init(model: InitModelOf<this>) {
super._init(model);
this.tabbableCoordinator = scout.create(TabbableCoordinator, {parent: this});
if (this.selectOnFocus) {
this.tabbableCoordinator.on('propertyChange:currentItem', event => {
if (event.newValue instanceof SimpleTab) {
this.selectTab(event.newValue);
}
});
}
this._setPosition(this.position);
}
protected override _render() {
this.$container = this.$parent.appendDiv('simple-tab-area');
aria.role(this.$container, 'tablist');
this.htmlComp = HtmlComponent.install(this.$container, this.session);
this.htmlComp.setLayout(this._createLayout());
}
protected _createLayout(): AbstractLayout {
return new SimpleTabAreaLayout(this);
}
protected override _renderProperties() {
super._renderProperties();
this._renderPosition();
this._renderDisplayStyle();
this._renderTabs();
}
setPosition(position: SimpleTabAreaPosition) {
this.setProperty('position', position);
}
protected _setPosition(position: SimpleTabAreaPosition) {
this._setProperty('position', position);
this.tabbableCoordinator.setOrientation(scout.isOneOf(this.position, SimpleTabArea.Position.TOP, SimpleTabArea.Position.BOTTOM) ? 'horizontal' : 'vertical');
}
protected _renderPosition() {
const positions: SimpleTabAreaPosition[] = [SimpleTabArea.Position.TOP, SimpleTabArea.Position.BOTTOM, SimpleTabArea.Position.LEFT, SimpleTabArea.Position.RIGHT];
positions.forEach(position => this.$container.toggleClass(this._cssClassForPosition(position), this.position === position));
this.invalidateLayoutTree();
}
protected _cssClassForPosition(position: SimpleTabAreaPosition) {
return 'position-' + position;
}
setDisplayStyle(displayStyle: SimpleTabAreaDisplayStyle) {
this.setProperty('displayStyle', displayStyle);
}
protected _renderDisplayStyle() {
this.$container.toggleClass('spread-even', this.displayStyle === SimpleTabArea.DisplayStyle.SPREAD_EVEN);
this.invalidateLayoutTree();
}
protected _renderTabs() {
// reverse since tab.renderAfter() called without sibling=true argument (see _renderTab)
// will _prepend_ themselves into the container.
this.tabs.slice().reverse().forEach(tab => this._renderTab(tab));
widgets.updateFirstLastMarker(this.tabs);
}
protected _renderTab(tab: SimpleTab<TView>) {
tab.renderAfter(this.$container);
}
protected override _renderVisible() {
this.$container.setVisible(this.visible && this.tabs.length > 0);
this.invalidateLayoutTree();
}
protected _onTabClick(event: Event<SimpleTab<TView>>) {
this.selectTab(event.source);
}
getTabs(): SimpleTab<TView>[] {
return this.tabs;
}
/**
* @param true to also return the visible tabs that are currently overflown. Default is false.
*/
getVisibleTabs(includeOverflown = false): SimpleTab<TView>[] {
return this.tabs.filter(tab => {
let visible = tab.visible;
if (!includeOverflown) {
visible &&= !tab.overflown;
}
return visible;
});
}
selectTab(viewTab: SimpleTab<TView>) {
if (this._selectedViewTab === viewTab) {
return;
}
this.deselectTab(this._selectedViewTab);
this._selectedViewTab = viewTab;
if (viewTab) {
if (this.selectOnFocus) {
this.tabbableCoordinator.setCurrentItem(viewTab);
}
// Select the new view tab.
viewTab.select();
}
this.trigger('tabSelect', {viewTab});
if (viewTab?.overflown) {
this.invalidateLayoutTree();
}
}
deselectTab(viewTab: SimpleTab<TView>) {
if (!viewTab) {
return;
}
if (this._selectedViewTab !== viewTab) {
return;
}
this._selectedViewTab.deselect();
}
getSelectedTab(): SimpleTab<TView> {
return this._selectedViewTab;
}
addTab(tab: SimpleTab<TView>, sibling?: SimpleTab<TView>) {
let insertPosition = -1;
if (sibling) {
insertPosition = this.tabs.indexOf(sibling);
}
this.tabs.splice(insertPosition + 1, 0, tab);
tab.on('click', this._tabClickHandler);
this._updateTabbableItems();
if (this.rendered) {
this._renderVisible();
tab.renderAfter(this.$container, sibling);
widgets.updateFirstLastMarker(this.getTabs());
this.invalidateLayoutTree();
}
}
destroyTab(tab: SimpleTab<TView>) {
let index = this.tabs.indexOf(tab);
if (index < 0) {
return;
}
this.tabs.splice(index, 1);
tab.destroy();
tab.off('click', this._tabClickHandler);
this._updateTabbableItems();
if (this.rendered) {
this._renderVisible();
widgets.updateFirstLastMarker(this.getTabs());
this.invalidateLayoutTree();
}
}
/**
* @internal
*/
_updateTabbableItems() {
let items: TabbableItem[] = [...this.tabs];
let overflowTab = this.findChild(SimpleTabOverflowMenu);
if (overflowTab) {
items.push(overflowTab);
}
this.tabbableCoordinator.setItems(items);
}
}