UNPKG

@eclipse-scout/core

Version:
365 lines (319 loc) 10.9 kB
/* * 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 {aria, Event, EventHandler, GlassPaneContribution, InitModelOf, ObjectIdProvider, PropertyChangeEvent, SimpleTabEventMap, SimpleTabModel, Status, strings, tooltips, Widget} from '../index'; export type DisplayViewId = 'N' | 'NE' | 'E' | 'SE' | 'S' | 'SW' | 'W' | 'NW' | 'C' | 'OUTLINE' | 'OUTLINE_SELECTOR' | 'PAGE_DETAIL' | 'PAGE_SEARCH' | 'PAGE_TABLE'; export class SimpleTab<TView extends SimpleTabView = SimpleTabView> extends Widget implements SimpleTabModel<TView> { declare model: SimpleTabModel<TView>; declare eventMap: SimpleTabEventMap<TView>; declare self: SimpleTab<any>; view: TView; title: string; subTitle: string; iconId: string; closable: boolean; saveNeeded: boolean; saveNeededVisible: boolean; status: Status; selected: boolean; $title: JQuery; $subTitle: JQuery; $iconContainer: JQuery; $statusContainer: JQuery; $close: JQuery; $titleLine: JQuery; $saveNeeded: JQuery; $statusIcons: JQuery[]; protected _statusContainerUsageCounter: number; protected _viewPropertyChangeListener: EventHandler<PropertyChangeEvent>; protected _viewRemoveListener: EventHandler<Event<TView>>; protected _glassPaneContribution: GlassPaneContribution; constructor() { super(); this.view = null; this.title = null; this.subTitle = null; this.iconId = null; this.cssClass = null; this.closable = false; this.saveNeeded = false; this.saveNeededVisible = false; this.status = null; this.selected = false; this.$title = null; this.$subTitle = null; this.$iconContainer = null; this.$statusContainer = null; this.$statusIcons = []; this._statusContainerUsageCounter = 0; this._viewPropertyChangeListener = this._onViewPropertyChange.bind(this); this._viewRemoveListener = this._onViewRemove.bind(this); this._glassPaneContribution = element => { if (!this.$close) { return null; } // glass pane will be added as direct child which does not prevent clicks and hover effects -> glasspane-parent marker needed this.$close.addClass('glasspane-parent'); return this.$close; }; } protected override _init(model: InitModelOf<this>) { super._init(model); this.view = model.view; this.title = (this.view ? this.view.title : model.title); this.subTitle = (this.view ? this.view.subTitle : model.subTitle); this.iconId = (this.view ? this.view.iconId : model.iconId); this.cssClass = (this.view ? this.view.cssClass : model.cssClass); this.closable = (this.view ? this.view.closable : model.closable); this.saveNeeded = (this.view ? this.view.saveNeeded : model.saveNeeded); this.saveNeededVisible = (this.view ? this.view.saveNeededVisible : model.saveNeededVisible); this.status = (this.view ? this.view.status : model.status); ObjectIdProvider.get().setDependentUuid('tab', this.view, this); if (this.view) { this._installViewListeners(); this.view.addGlassPaneContribution(this._glassPaneContribution); } } renderAfter($parent: JQuery, sibling?: Widget) { this.render($parent); if (sibling) { this.$container.insertAfter(sibling.$container); } } protected override _render() { this.$container = this.$parent.prependDiv('simple-tab'); this.$container.on('mousedown', this._onMouseDown.bind(this)); this.$titleLine = this.$container.appendDiv('title-line'); this.$iconContainer = this.$titleLine.appendDiv('icon-container'); this.$title = this.$titleLine.appendDiv('title'); tooltips.installForEllipsis(this.$title, { parent: this }); this.$statusContainer = this.$titleLine.appendDiv('status-container'); this.$subTitle = this.$container.appendDiv('sub-title'); tooltips.installForEllipsis(this.$subTitle, { parent: this }); } protected override _renderProperties() { super._renderProperties(); this._renderTitle(); this._renderSubTitle(); this._renderIconId(); this._renderCssClass(); this._renderClosable(); this._renderSaveNeeded(); this._renderStatus(); this._renderSelected(); } protected override _remove() { this.$close = null; super._remove(); } setTitle(title: string) { this.setProperty('title', title); } protected _renderTitle() { this.$title.textOrNbsp(this.title); } setSubTitle(subTitle: string) { this.setProperty('subTitle', subTitle); } protected _renderSubTitle() { this.$subTitle.text(this.subTitle); } setIconId(iconId: string) { this.setProperty('iconId', iconId); } protected _renderIconId() { this.$iconContainer.icon(this.iconId); } setClosable(closable: boolean) { this.setProperty('closable', closable); } protected _renderClosable() { if (this.closable) { if (this.$close) { return; } this.$container.addClass('closable'); this.$close = this.$container.appendDiv('closer') .on('click', this._onClose.bind(this)); aria.role(this.$close, 'button'); aria.label(this.$close, this.session.text('ui.Close')); } else { if (!this.$close) { return; } this.$container.removeClass('closable'); this.$close.remove(); this.$close = null; } } setSaveNeededVisible(saveNeededVisible: boolean) { if (this.saveNeededVisible === saveNeededVisible) { return; } this._setProperty('saveNeededVisible', saveNeededVisible); if (this.rendered) { this._renderSaveNeeded(); } } setSaveNeeded(saveNeeded: boolean) { if (this.saveNeeded === saveNeeded) { return; } this._setProperty('saveNeeded', saveNeeded); if (this.rendered) { this._renderSaveNeeded(); } } protected _renderSaveNeeded() { if (this.saveNeeded && this.saveNeededVisible) { this.$container.addClass('save-needed'); if (this.$saveNeeded) { return; } this.$saveNeeded = this.$statusContainer.prependDiv('status save-needer'); this._statusContainerUsageCounter++; } else { if (!this.$saveNeeded) { return; } this.$container.removeClass('save-needed'); this.$saveNeeded.remove(); this.$saveNeeded = null; this._statusContainerUsageCounter--; } } setStatus(status: Status) { this.setProperty('status', status); } protected _renderStatus() { this._statusContainerUsageCounter -= (this.$statusIcons.length === 0 ? 0 : 1); this.$statusIcons.forEach($statusIcon => $statusIcon.remove()); this.$statusIcons = []; if (this.status) { this.status.asFlatList().forEach(status => { if (!status || (!status.iconId && !status.message)) { return; } if (status.iconId) { let $statusIcon = this.$statusContainer.appendIcon(status.iconId, 'status'); if (status.cssClass()) { $statusIcon.addClass(status.cssClass()); } this.$statusIcons.push($statusIcon); } if (status.message) { let $statusMessage = this.$statusContainer.appendSpan('status message'); if (status.cssClass()) { $statusMessage.addClass(status.cssClass()); } let $text = $statusMessage.appendSpan('text', status.message); tooltips.installForEllipsis($text, { parent: this }); this.$statusIcons.push($statusMessage); } }); } this._statusContainerUsageCounter += (this.$statusIcons.length === 0 ? 0 : 1); this.$container.toggleClass('has-status', this._statusContainerUsageCounter > 0); } select() { this.setSelected(true); } deselect() { this.setSelected(false); } setSelected(selected: boolean) { this.setProperty('selected', selected); } protected _renderSelected() { this.$container.toggleClass('selected', this.selected); aria.role(this.$titleLine, this.selected ? 'heading' : null); aria.level(this.$titleLine, this.selected ? 1 : null); } protected _onMouseDown(event: JQuery.MouseDownEvent) { if (this.$close && this.$close.isOrHas(event.target)) { return; } this.trigger('click'); // When the tab is clicked the user wants to execute the action and not see the tooltip if (this.$title) { tooltips.cancel(this.$title); tooltips.close(this.$title); } if (this.$subTitle) { tooltips.cancel(this.$subTitle); tooltips.close(this.$subTitle); } event.preventDefault(); } protected _onClose(event: JQuery.ClickEvent) { if (this.view) { this.view.abort(); } } getMenuText(): string { return strings.join(' \u2013 ', this.title, this.subTitle); } protected _installViewListeners() { this.view.on('propertyChange', this._viewPropertyChangeListener); this.view.on('remove', this._viewRemoveListener); } protected _uninstallViewListeners() { this.view.off('propertyChange', this._viewPropertyChangeListener); this.view.off('remove', this._viewRemoveListener); } protected _onViewPropertyChange(event: PropertyChangeEvent) { if (event.propertyName === 'title') { this.setTitle(this.view.title); } else if (event.propertyName === 'subTitle') { this.setSubTitle(this.view.subTitle); } else if (event.propertyName === 'iconId') { this.setIconId(this.view.iconId); } else if (event.propertyName === 'cssClass') { this.setCssClass(event.newValue); } else if (event.propertyName === 'saveNeeded') { this.setSaveNeeded(event.newValue); } else if (event.propertyName === 'saveNeededVisible') { this.setSaveNeededVisible(event.newValue); } else if (event.propertyName === 'closable') { this.setClosable(event.newValue); } else if (event.propertyName === 'status') { this.setStatus(event.newValue); } } /** * We cannot not bind the 'remove' event of the view to the remove function * of this tab, because in bench-mode the tab is never rendered * and thus the _remove function is never called. */ protected _onViewRemove(event: Event<TView>) { this._uninstallViewListeners(); if (this.rendered) { this.remove(); } else { this.trigger('remove'); } } } export interface SimpleTabView extends Widget { title?: string; subTitle?: string; iconId?: string; closable?: boolean; saveNeeded?: boolean; saveNeededVisible?: boolean; status?: Status; displayViewId?: DisplayViewId; abort?: () => void; }