import API from '../api/ecosystem-base.js';
import {Menu_A} from './as-menu.js';
import {Component_A} from './basic/as-component.js';
import {FP_Tabcontainer_A} from './fp-ext/fp-tabcontainer-ext.js';
import {ErrorCode} from '../exception/exceptionDesc.js';
/**
* @ignore
*/
const logModule = 'as-tab-a';
/**
* @description Properties accepted by the Tab component, defining its appearance, behavior, and lifecycle hooks.
* This class focuses on the specific properties of the Tab component.
* Since it inherits from Accessor_A, all basic properties (e.g., height, width) are available but documented in the Accessor_A part.
* @typedef {object} TComponents.TabProps
* @prop {object} [options] Additional options for the tab component.
* Items in this object include:
* - **responsive** (boolean, default: false): Whether the tab container should be responsive.
* @prop {string} [tips] Tooltip text for the component.
* @prop {Function|string} [onCreated] Lifecycle hook invoked after component instantiation.
* @prop {Function|string} [onMounted] Lifecycle hook invoked after component is attached to the DOM.
* @prop {string} [position] CSS positioning of the element.
* @prop {number} [width] Component width.
* @prop {number} [height] Component height.
* @prop {number} [top] Top offset in pixels.
* @prop {number} [left] Left offset in pixels.
* @prop {number} [rotation] Rotation angle in degrees for visual transform.
* @prop {number} [borderRadius] Corner radius in pixels.
* @prop {number} [zIndex] z-index stacking order.
* @prop {string} [color] Primary color for text and icons.
* @prop {string} [backgroundColor] Background color for the content area.
* @prop {object} [font] Font configuration object containing size, family and style settings.
* This object controls text appearance:
* - **fontSize** (number, default: 12): Font size in pixels used for labels and text.
* - **fontFamily** (string, default: 'Segoe UI'): Font family name for text rendering.
* - **style** (object): Font style object, containing `fontStyle`, `fontWeight`, `textDecoration`.
* @prop {string} [size] Preset size key for the component.
* @prop {string} [tabPosition] Position of the tabs relative to content ('top' | 'bottom' | 'left' | 'right').
* @prop {string} [styleTemplate] Key of a style template to apply.
* @prop {number} [activeViewIndex] Zero-based index of the currently active tab.
* @prop {Function|string} [onChange] Callback when the active tab changes.
* @prop {boolean} [useViewIcon] Whether to show each tab’s icon.
* @prop {TComponents.ViewProps[]} [views] Array of view items to render as tabs.
* @prop {string} [defaultState] Default state of the component.
* @prop {boolean} [expandHeightToParentBottom] Whether to expand the height to the parent's bottom.
* @memberof TComponents
*/
/**
* @description Represents a tab component with multiple customizable properties and events.
* @class TComponents.Tab
* @memberof TComponents
* @extends TComponents.Menu_A
* @param {HTMLElement} parent - HTML element that is going to be the parent of the component.
* @param {TComponents.TabProps} [props] - The properties object.
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*/
export class Tab extends Menu_A {
constructor(parent, props) {
super(parent, props);
this.viewId = new Map();
/**
* @instance
* @private
* @type {TComponents.TabProps}
*/
this._props;
/**
* Use this variable to determine the source of the onchange event.
* @instance
* @private
* @type {string|null}
*/
this._changeSource = null; // 'user' | 'api'
}
/**
* @description An array of Tab's views, each containing properties
* like `id`,`name`, `content`, `active`, and `child`.
* @member {any[]} TComponents.Tab#views
* @instance
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* if(tab.views.length >= 1){
* console.log(tab.views[0].name); // Outputs: 'Tab 1'
* };
*/
get views() {
return this._views;
}
/**
* @deprecated
*/
get activeTab() {
const index = this._props.activeViewIndex;
if (index >= 0) {
const view = this.views[index];
return view.name;
} else {
return '';
}
}
/**
* @deprecated Use `activeViewName` instead.
* @description Sets the active tab by its display name.
* @member {string} TComponents.Tab#activeTab
* @instance
* @param {string} name - The target tab name (display text).
* If multiple tabs render the same text, the first match is used.
* @throws {Error} If the tab container is not initialized.
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* tab.activeTab = 'Tab 2'; // Sets the active tab to 'Tab 2'
*/
set activeTab(name) {
if (!this.tabContainer) throw new Error('TabContainer is not initialized. Please render the component before use.');
const index = this.views.findIndex((item) => Component_A.tParse(item.name) === name);
this.activeViewIndex = index;
}
/**
* @returns {string}
*/
get activeViewName() {
const index = this._props.activeViewIndex;
if (index >= 0) {
const view = this.views[index];
return view.name;
} else {
return '';
}
}
/**
* @description Sets the active tab by its display name.
* @member {string} TComponents.Tab#activeViewName
* @instance
* @param {string} name - The target tab name (display text).
* If multiple tabs render the same text, the first match is used.
* @throws {Error} If the tab container is not initialized.
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* tab.activeViewName = 'Tab 2'; // Sets the active tab to 'Tab 2'
*/
set activeViewName(name) {
if (!this.tabContainer) throw new Error('TabContainer is not initialized. Please render the component before use.');
const index = this.views.findIndex((item) => Component_A.tParse(item.name) === name);
this.activeViewIndex = index;
}
/**
* @returns {number}
*/
get activeViewIndex() {
return this._props.activeViewIndex;
}
/**
* @description Sets the active tab view index and updates the active tab in the tab container.
* @member {number} TComponents.Tab#activeViewIndex
* @instance
* @param {number} t - The new active view index.
* @throws {Error} If the new index is invalid or if the active view content is invalid.
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* tab.activeViewIndex = 1; // Sets the active tab to the second tab
*/
set activeViewIndex(t) {
const index = this.checkActiveViewIndex(t);
if (index === null || index === this._props.activeViewIndex) return;
// To ensure compatibility, this interface will trigger onchange previously.
if (!this._changeSource) {
this._changeSource = 'user';
}
this.setProps({activeViewIndex: index});
}
/**
* @returns {number}
*/
get activeViewIndexX() {
return this._props.activeViewIndex;
}
/**
* @description Sets the active tab view index and updates the active tab in the tab container.
* This will update the tab container but will NOT trigger the `onchange` event.
* Recommended for internal or programmatic updates, where change notification is not desired.
* @member {number} TComponents.Tab#activeViewIndexX
* @instance
* @param {number} t - The new active view index.
* @throws {Error} If the new index is invalid or if the active view content is invalid.
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* tab.activeViewIndexX = 1;
*/
set activeViewIndexX(t) {
const index = this.checkActiveViewIndex(t);
if (index === null || index === this._props.activeViewIndex) return;
// If it is not triggered by a user, it is considered an API.
if (!this._changeSource) {
this._changeSource = 'api';
}
this.setProps({activeViewIndex: index});
}
/**
* @returns {string}
*/
get tabPosition() {
return this._props.tabPosition;
}
/**
* @description Sets the tab bar position.
* The tab position can be `top`, `left`, or `right`.
* @member {string} TComponents.Tab#tabPosition
* @instance
* @param {string} t - The new tab position (e.g., "top" | "left" | "right").
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* tab.tabPosition = 'left'; // Sets the tab position to the left
*/
set tabPosition(t) {
const positionList = ['top', 'left', 'right'];
if (!positionList.includes(t)) return;
this.setProps({tabPosition: t});
}
/**
* @description Returns the default properties of the tab component.
* @member TComponents.Tab#defaultProps
* @method
* @protected
* @returns {TComponents.TabProps} Default properties.
*/
defaultProps() {
return {
options: {
responsive: false,
},
tips: '',
onCreated: '',
onMounted: '',
onDispose: '',
position: 'static',
width: 200,
height: 200,
top: 0,
left: 0,
rotation: 0,
borderRadius: 4,
zIndex: 0,
color: 'rgba(0,0,0,1)',
backgroundColor: 'rgba(245,245,245,1)',
font: {
fontSize: 12,
fontFamily: 'Segoe UI',
style: {
fontStyle: 'normal',
fontWeight: 'normal',
textDecoration: 'none',
},
},
size: '',
tabPosition: 'top',
styleTemplate: '',
activeViewIndex: 0,
onChange: '',
useViewIcon: true,
views: [
{
name: Component_A.tParse('Tab 0'),
content: `View_${API.generateUUID()}`,
icon: 'abb-icon abb-icon-abb_robot-tool_32',
children: [],
},
],
defaultState: 'show_enable',
expandHeightToParentBottom: false,
};
}
/**
* @description Initializes the tab component, setting up event handlers and tab container.
* @member TComponents.Tab#onInit
* @method
* @throws {Error} If the component fails to initialize.
* @returns {void}
*/
onInit() {
try {
// Process the views prior to instantiating the new hamburger object.
this._initViews();
// Initialize the tab container component.
this.tabContainer = new FP_Tabcontainer_A();
this.tabContainer.onTabClick = this._handleTabClick.bind(this);
// Initialize views of the tab container.
this.viewId.clear();
this.views.forEach((view) => {
const dom = this._getDom(view.content, view.id, view.name);
view.id = this.tabContainer.addTab(Component_A.tParse(view.name), dom, view.icon);
this.viewId.set(view.id, view.name);
});
} catch (e) {
Logger.e(logModule, ErrorCode.FailedToInitComponent, `Failed to initialize Tab component`, e);
throw ErrorCode.FailedToInitComponent;
}
}
/**
* @description Returns the HTML markup for the tab component.
* @member TComponents.Tab#markup
* @method
* @returns {string} The HTML markup
*/
markup() {
return /*html*/ `<div class="tc-tab"></div>`;
}
/**
* @description Maps components to their identifiers.
* @member TComponents.Tab#mapComponents
* @method
* @returns {object}
*/
mapComponents() {
if (!Array.isArray(this._children)) return {};
for (let i = 0; i < this._children.length; i++) {
if (i === this._props.activeViewIndex) {
const instance = this._children[i];
return {children: instance};
}
}
return {};
}
/**
* @description Renders the tab component, applying styles and attaching event listeners.
* @member TComponents.Tab#onRender
* @method
* @throws {Error} If the component fails to render.
* @returns {void}
*/
onRender() {
try {
this.removeAllEventListeners();
this.tabContainer.attachToElement(this.find('.tc-tab'));
this.tabContainer.position = this._props.tabPosition;
this.tabContainer.setTabTitle = this._props.title;
this.tabContainer.borderRadius = this._props.borderRadius;
const tabbar = this.find('.fp-components-tabcontainer-tabbar');
const tabbarIcon = this.find('.fp-components-tabcontainer-tabbar-icon');
if (tabbar) {
tabbar.style.cssText = `
font-family: ${this._props.font.fontFamily};
font-size: ${this._props.font.fontSize}px;
font-style: ${this._props.font.style.fontStyle};
font-weight: ${this._props.font.style.fontWeight};
text-decoration: ${this._props.font.style.textDecoration};
color: ${this._props.color};`;
}
if (tabbarIcon) {
tabbarIcon.style.cssText = `
color:${this._props.color};
font-size: ${this._props.font.fontSize}px;
font-weight:${this._props.font.style.fontWeight};
`;
}
this._setTabContainerBorder();
this.container.classList.add('is-container');
// Set up the tab container active view.
this._setActiveView();
// After setting the `activeView` in the component, assign the `onchange` handler.
// Prevent it from being executed during initialization.
this.tabContainer.onchange = this._cbOnChange.bind(this);
this._addTips();
} catch (e) {
// Runtime errors: write specific content to the log, throw error code
Logger.e(
logModule,
ErrorCode.FailedToRunOnRender,
`Error happens on onRender of tab component ${this.compId}.`,
e,
);
throw new Error(ErrorCode.FailedToRunOnRender, {cause: e});
}
}
/**
* @deprecated No longer supported interfaces.
* @description Gets the properties of the tab component.
* @member TComponents.Tab#getProps
* @method
* @returns {object} The properties object
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* const props = tab.getProps();
* console.log(props.views); // Outputs the views array with content
*/
getProps() {
const tempView = this._props.views;
const ret = super.getProps();
ret.views = ret.views.map((view, index) => {
view.content = tempView[index].content;
return view;
});
return ret;
}
/**
* @todo This is an experimental interface and may change at any time; it is not recommended to use it in the current version.
* @description Adds a new tab to the component. Experimental interfaces are not recommended for use.
* @member TComponents.Tab#addTab
* @method
* @param {object} tab - The tab object
* @param {string} tab.name - The name of the tab
* @param {string} tab.content - The content of the tab
* @param {object} [tab.extra] - Additional optional properties (e.g. icon, children)
* @returns {void}
* @example
* const tab = new TComponents.Tab(document.body, {
* position: 'absolute',
* width: 200,
* height: 200,
* zIndex: 1000,
* views: [
* {
* name: 'Tab 1',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* {
* name: 'Tab 2',
* icon: 'abb-icon abb-icon-home-house_32',
* content: `view_${API.generateUUID()}`,
* children: [],
* },
* ],
* activeViewIndex: 0,
* });
*
* // Render the component.
* await tab.render();
*
* // Add a new tab.
* const v1 = {name: 'Tab 3', content: 'test-1-001'};
* tab.addView(v1);
*
* @example
* const div1 = document.createElement('div');
* div1.textContent = 'Hello world';
* div1.style.fontSize = '96px';
* div1.style.fontWeight = 'bold';
* div1.style.textAlign = 'center';
*
* const v2 = {name: 'Tab 4', content: div1};
* tab.addView(v2);
*/
addTab(itemOptions) {
this.addView(itemOptions);
const lastTab = this._views[this._views.length - 1];
const dom = this._getDom(lastTab.content, lastTab.id, lastTab.name);
lastTab.id = this.tabContainer.addTab(Component_A.tParse(lastTab.name), dom, lastTab.icon);
this.viewId.set(lastTab.id, lastTab.name);
}
/**
* @description Removes a tab by id, content, or name.
* Lookup priority:
* 1. `id`
* 2. `content`
* 3. `name`
* @member TComponents.Tab#removeTab
* @method
* @param {object} options
* @param {string} [options.id] - The tab id.
* @param {string|HTMLElement} [options.content] - The tab content id or content object.
* @param {string} [options.name] - The tab display name.
* @returns {void}
* @example
* // remove the tab 2.
* tab.removeTab({name: 'Tab 2'});
*/
removeTab({id = null, content, name} = {}) {
const tab = this.findView({id, content, name});
if (!tab) return;
this._removeTab(tab);
}
/**
* @description Removes a tab by its index in the views array.
* @member TComponents.Tab#removeTabByIndex
* @method
* @param {number} index - Zero-based tab index.
* @returns {void}
* @example
* tab.removeTabByIndex(1);
*/
removeTabByIndex(index) {
if (!this.isValidIndex(index)) return;
const tab = this._views[index];
this._removeTab(tab);
}
/**
* @description Shows a tab by matching its name or content.
* @member TComponents.Tab#showTab
* @method
* @param {object} options
* @param {string} [options.id] - The tab id.
* @param {string|HTMLElement} [options.content] - The tab content id or content object.
* @returns {void}
* @example
* // Hide the tab 1.
* tab.hideTabByIndex(0);
*
* // Show the tab 1;
* tab.showTab({name: 'Tab 1'});
*/
showTab({id = null, name, content}) {
const tab = this.findView({id, name, content});
this._showTab(tab);
}
/**
* @description Shows a tab by its index.
* @member TComponents.Tab#showTabByIndex
* @method
* @param {number} index - Zero-based tab index.
* @returns {void}
* @example
* // Hide the tab 1.
* tab.hideTabByIndex(0);
*
* // Show the tab 1;
* tab.showTabByIndex(0);
*/
showTabByIndex(index) {
if (!this.isValidIndex(index)) return;
const tab = this._views[index];
this._showTab(tab);
}
/**
* @description Hides a tab by matching its name or content.
* @member TComponents.Tab#hideTab
* @method
* @param {object} options
* @param {string} [options.id] - The tab id.
* @param {string|HTMLElement} [options.content] - The tab content id or content object.
* @returns {void}
* @example
* // Hide the tab 2.
* tab.hideTab({name: 'Tab 2'});
*/
hideTab({id = null, name, content}) {
const tab = this.findView({id, name, content});
this._hideTab(tab);
}
/**
* @description Hides a tab by its index.
* @member TComponents.Tab#hideTabByIndex
* @method
* @param {number} index - Zero-based tab index.
* @returns {void}
* @example
* // Hide the tab 1.
* tab.hideTabByIndex(0);
*/
hideTabByIndex(index) {
if (!this.isValidIndex(index)) return;
const tab = this._views[index];
this._hideTab(tab);
}
/**
* @description Internal method to hide a tab and its associated view.
* @member TComponents.Tab#_hideTab
* @method
* @private
* @param {object} tab - The tab view object to hide.
* @returns {void}
*/
_hideTab(tab) {
if (!tab) return;
const index = this._views.findIndex((v) => v.id === tab.id);
if (!this.isValidIndex(index)) return;
const node = this.getMenuBarItem('.fp-components-tabcontainer-tabbar', index);
if (node) {
node.style.display = 'none';
}
this.hideView(tab);
}
/**
* @description Internal method to show a tab and its associated view.
* @member TComponents.Tab#_showTab
* @method
* @private
* @param {object} tab - The tab view object to show.
* @returns {void}
*/
_showTab(tab) {
if (!tab) return;
const index = this._views.findIndex((v) => v.id === tab.id);
if (!this.isValidIndex(index)) return;
const node = this.getMenuBarItem('.fp-components-tabcontainer-tabbar', index);
if (node) {
node.style.display = '';
}
this.showView(tab);
}
/**
* @description Internal method to remove a tab and its associated view.
* @member TComponents.Tab#_removeTab
* @method
* @private
* @param {object} tab - The tab view object to remove.
* @returns {void}
*/
_removeTab(tab) {
this.tabContainer.removeTab(tab.id);
this.removeView(tab);
}
/**
* @description Applies the background color and rounded-corner border to the tab container
* based on the configured tab position.
* @member TComponents.Tab#setTabContainerBorder
* @method
* @protected
* @returns {void}
*/
setTabContainerBorder() {
this._setTabContainerBorder();
}
/**
* @member TComponents.Tab~_setTabContainerBorder
* @method
* @private
* @returns {void}
*/
_setTabContainerBorder() {
const tabContainer = this.find('.fp-components-tabcontainer-content');
if (Component_A._isHTMLElement(tabContainer)) {
tabContainer.style.backgroundColor = this._props.backgroundColor;
switch (this._props.tabPosition) {
case 'top':
tabContainer.style.borderRadius = `0px 0px ${this._props.borderRadius}px ${this._props.borderRadius}px`;
break;
case 'left':
tabContainer.style.borderRadius = `0px ${this._props.borderRadius}px ${this._props.borderRadius}px 0px`;
break;
case 'right':
tabContainer.style.borderRadius = `${this._props.borderRadius}px 0px 0px ${this._props.borderRadius}px`;
break;
default:
break;
}
}
}
/**
* @description Activates the view at the specified index.
* @member TComponents.Tab#setActiveView
* @method
* @protected
* @throws {Error} If the tab container is not initialized or the target view cannot be found.
* @return {void}
*/
setActiveView() {
this._setActiveView();
}
/**
* @member TComponents.Tab~_setActiveView
* @method
* @private
* @throws {Error}
* @return {void}
*/
_setActiveView() {
if (!this.tabContainer) throw new Error('Tab container not initialized. Please render the component first.');
var idx = this._props.activeViewIndex || 0;
const view = this.views[idx];
if (view.id === this.tabContainer.activeTab) return;
this.tabContainer.activeTab = view.id;
}
/**
* @description Handles the tab click event.
* @member TComponents.Tab#handleTabClick
* @method
* @protected
* @param {Event} e - The event object.
* @returns {void}
*/
handleTabClick() {
this._handleTabClick();
}
/**
* @member TComponents.Tab~_handleTabClick
* @method
* @private
* @param {Event}
* @returns {void}
*/
_handleTabClick(e) {
try {
// Check if the clicked element is a tab && Remove the event and use a callback instead.
const strId = e.target && e.target.dataset && e.target.dataset.viewId;
if (strId) {
const index = this.views.findIndex((item) => item.content.id === strId);
if (this._props.activeViewIndex === index) return;
// Define the origin of the onchange event.
this._changeSource = 'user';
// Set up the tab container active view.
this.activeViewIndex = index;
}
} catch (e) {
Component_A.popupEventError(e, 'tabClick', logModule);
}
}
/**
* @description Callback for when the view changes.
* @member TComponents.Tab~_cbOnChange
* @method
* @private
* @param {string} oldView - The old view identifier.
* @param {string} newView - The new view identifier.
* @returns {void}
*/
async _cbOnChange(oldView, newView) {
// If oldView is invalid, it means the tab container has just been initialized.
if (!oldView) return;
// Only trigger the change event on user interaction.
const source = this._changeSource;
this._changeSource = null;
if (source !== 'user') return;
try {
var fn = Component_A.genFuncTemplate(this._props.onChange, this);
fn && (await fn(oldView, newView));
return;
} catch (e) {
Component_A.popupEventError(e, 'onChange', logModule);
}
}
}
/**
* @description Add css properties to the component
* @alias loadCssClassFromString
* @member TComponents.Tab.loadCssClassFromString
* @method
* @static
* @param {string} css - The css string to be loaded into style tag
* @returns {void}
* @example
* TComponents.Tab.loadCssClassFromString(`
* .tc-tab {
* background-color: #f0f0f0;
* border: 1px solid #ccc;
* border-radius: 4px;
* }
* }`);
*/
Tab.loadCssClassFromString(`
.tc-tab {
position: absolute;
height: 100%;
width: 100%;
min-width: 0px;
min-height: 0px;
padding: 0px;
margin: 0px;
}
.tc-tab .tabcontainer-top,
.tc-tab .tabcontainer-left,
.tc-tab .tabcontainer-right {
display: flex;
overflow: hidden;
}
.tc-tab .tabcontainer-top {
flex-direction: column;
}
.tc-tab .tabcontainer-left {
flex-direction: row;
}
.tc-tab .tabbar-left > .fp-components-tabcontainer-activetab-noclose {
border-right: 2px solid blue;
margin-right:0;
}
.tc-tab .tabbar-right > .fp-components-tabcontainer-activetab-noclose {
border-left: 2px solid blue;
}
.tc-tab .tabbar-top > .fp-components-tabcontainer-activetab-noclose {
border-bottom: 2px solid blue;
}
.tc-tab .tabcontainer-right {
flex-direction: row-reverse;
}
.tc-tab .fp-components-tabcontainer {
position: absolute;
height: 100%;
width: 100%;
min-width: 0px;
min-height: 0px;
padding: 0px;
margin: 0px;
}
.tc-tab .fp-components-tabcontainer .fp-components-tabcontainer-tabbar > * {
background-color: transparent;
min-width: 0px;
min-height: 0px;
max-width: unset;
max-height: unset;
}
.tc-tab .fp-components-tabcontainer .fp-components-tabcontainer-tabbar {
position: relative;
background-color: transparent;
padding: 0px;
gap: 8px;
font-size: inherit;
-ms-overflow-style: auto !important;
scrollbar-width: thin !important;
}
.tc-tab .fp-components-tabcontainer .tabbar-top {
height: 55px;
width: 100%;
display: flex;
flex-direction: row;
min-height: 0px;
}
.tc-tab .fp-components-tabcontainer .tabbar-left,
.tc-tab .fp-components-tabcontainer .tabbar-right {
height: 100%;
width: 60px;
display: flex;
flex-direction: column;
}
.tc-tab .fp-components-tabcontainer-activetab {
font-weight: inherit;
width: auto;
height: auto;
min-height: 0px;
max-height: 0px;
display: flex;
flex-direction: initial;
flex: none;
transition: none;
}
.tc-tab .fp-components-tabcontainer-tabbar::-webkit-scrollbar {
display: block;
width: 8px;
}
.tc-tab .fp-components-tabcontainer .fp-components-tabcontainer-tabbar-content {
background-color: transparent;
}
.tc-tab .fp-components-tabcontainer .tabbar-content-top {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
height: 45px;
width: 140px;
min-width: 0px;
max-width: 140px;
}
.tc-tab .fp-components-tabcontainer .tabbar-content-left,
.tc-tab .fp-components-tabcontainer .tabbar-content-right {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 80px;
width: 60px;
}
.tc-tab .fp-components-tabcontainer .fp-components-tabcontainer-content {
position: relative;
}
.tc-tab .fp-components-tabcontainer .fp-components-tabcontainer-tabbar-icon {
height: 20px;
width: 20px;
margin: 6px;
pointer-events: none;
}
.tc-tab .fp-components-tabcontainer .fp-components-tabcontainer-tabbar-text {
display: block;
pointer-events: none;
}
.tc-tab .tabbar-text-top {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 80px;
}
.tc-tab .tabbar-text-left,
.tc-tab .tabbar-text-right {
height: 35px;
width: auto;
max-width: 50px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
`);
/**
* Add disabled css properties to tab-type components.
*/
Tab.loadCssClassFromString(`
.tabcontainer-disabled {
opacity:0.7;
cursor: not-allowed !important;
}
.tabcontainer-disabled .fp-components-tabcontainer-tabbar,
.tabcontainer-disabled .fp-components-tabcontainer-content {
pointer-events: none;
}
`);