devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
502 lines (499 loc) • 19.8 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/tab_panel/m_tab_panel.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.TABPANEL_CLASS = void 0;
var _component_registrator = _interopRequireDefault(require("../../../core/component_registrator"));
var _devices = _interopRequireDefault(require("../../../core/devices"));
var _dom_adapter = _interopRequireDefault(require("../../../core/dom_adapter"));
var _element = require("../../../core/element");
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _bindable_template = require("../../../core/templates/bindable_template");
var _icon = require("../../../core/utils/icon");
var _type = require("../../../core/utils/type");
var _themes = require("../../../ui/themes");
var _m_support = _interopRequireDefault(require("../../core/utils/m_support"));
var _m_multi_view = _interopRequireDefault(require("../../ui/m_multi_view"));
var _m_tabs = _interopRequireDefault(require("../../ui/tabs/m_tabs"));
var _m_item = _interopRequireDefault(require("./m_item"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function(n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) {
({}).hasOwnProperty.call(t, r) && (n[r] = t[r])
}
}
return n
}, _extends.apply(null, arguments)
}
const TABPANEL_CLASS = exports.TABPANEL_CLASS = "dx-tabpanel";
const TABPANEL_TABS_CLASS = "dx-tabpanel-tabs";
const TABPANEL_TABS_ITEM_CLASS = "dx-tabpanel-tab";
const TABPANEL_CONTAINER_CLASS = "dx-tabpanel-container";
const TABS_ITEM_TEXT_CLASS = "dx-tab-text";
const DISABLED_FOCUSED_TAB_CLASS = "dx-disabled-focused-tab";
const TABS_ITEM_TEXT_SPAN_CLASS = "dx-tab-text-span";
const TABS_ITEM_TEXT_SPAN_PSEUDO_CLASS = "dx-tab-text-span-pseudo";
const TABPANEL_TABS_POSITION_CLASS = {
top: "dx-tabpanel-tabs-position-top",
right: "dx-tabpanel-tabs-position-right",
bottom: "dx-tabpanel-tabs-position-bottom",
left: "dx-tabpanel-tabs-position-left"
};
const TABS_POSITION = {
top: "top",
right: "right",
bottom: "bottom",
left: "left"
};
const TABS_INDICATOR_POSITION_BY_TABS_POSITION = {
top: "bottom",
right: "left",
bottom: "top",
left: "right"
};
const TABS_ORIENTATION = {
horizontal: "horizontal",
vertical: "vertical"
};
const ICON_POSITION = {
top: "top",
end: "end",
bottom: "bottom",
start: "start"
};
const STYLING_MODE = {
primary: "primary",
secondary: "secondary"
};
class TabPanel extends _m_multi_view.default {
_getDefaultOptions() {
return _extends({}, super._getDefaultOptions(), {
itemTitleTemplate: "title",
hoverStateEnabled: true,
selectOnFocus: false,
showNavButtons: false,
scrollByContent: true,
scrollingEnabled: true,
tabsPosition: TABS_POSITION.top,
iconPosition: ICON_POSITION.start,
stylingMode: STYLING_MODE.primary,
onTitleClick: null,
onTitleHold: null,
onTitleRendered: null,
badgeExpr: data => data ? data.badge : void 0,
_tabsIndicatorPosition: null
})
}
_defaultOptionsRules() {
const themeName = (0, _themes.current)();
return super._defaultOptionsRules().concat([{
device: () => "desktop" === _devices.default.real().deviceType && !_devices.default.isSimulator(),
options: {
focusStateEnabled: true
}
}, {
device: () => !_m_support.default.touch,
options: {
swipeEnabled: false
}
}, {
device: {
platform: "generic"
},
options: {
animationEnabled: false
}
}, {
device: () => (0, _themes.isFluent)(themeName),
options: {
stylingMode: STYLING_MODE.secondary
}
}, {
device: () => (0, _themes.isMaterialBased)(themeName),
options: {
iconPosition: ICON_POSITION.top
}
}])
}
_init() {
super._init();
this.$element().addClass(TABPANEL_CLASS);
this._toggleTabPanelTabsPositionClass()
}
_getElementAria() {
return {
role: "tabpanel"
}
}
_getItemAria() {
return {
role: "tabpanel"
}
}
_initMarkup() {
super._initMarkup();
this._createTitleActions();
this._renderLayout()
}
_prepareTabsItemTemplate(data, $container) {
const $iconElement = (0, _icon.getImageContainer)(null === data || void 0 === data ? void 0 : data.icon);
if ($iconElement) {
$container.append($iconElement)
}
const title = (0, _type.isPlainObject)(data) ? null === data || void 0 === data ? void 0 : data.title : data;
if ((0, _type.isDefined)(title) && !(0, _type.isPlainObject)(title)) {
const $tabTextSpan = (0, _renderer.default)("<span>").addClass("dx-tab-text-span");
$tabTextSpan.append(_dom_adapter.default.createTextNode(title));
const $tabTextSpanPseudo = (0, _renderer.default)("<span>").addClass("dx-tab-text-span-pseudo");
$tabTextSpanPseudo.append(_dom_adapter.default.createTextNode(title));
$tabTextSpanPseudo.appendTo($tabTextSpan);
$tabTextSpan.appendTo($container)
}
}
_initTemplates() {
super._initTemplates();
this._templateManager.addDefaultTemplates({
title: new _bindable_template.BindableTemplate((($container, data) => {
this._prepareTabsItemTemplate(data, $container);
const $tabItem = (0, _renderer.default)("<div>").addClass("dx-tab-text");
$container.wrapInner($tabItem)
}), ["title", "icon"], this.option("integrationOptions.watchMethod"))
})
}
_createTitleActions() {
this._createTitleClickAction();
this._createTitleHoldAction();
this._createTitleRenderedAction()
}
_createTitleClickAction() {
this._titleClickAction = this._createActionByOption("onTitleClick")
}
_createTitleHoldAction() {
this._titleHoldAction = this._createActionByOption("onTitleHold")
}
_createTitleRenderedAction() {
this._titleRenderedAction = this._createActionByOption("onTitleRendered")
}
_renderLayout() {
if (this._tabs) {
return
}
const $element = this.$element();
this._$tabContainer = (0, _renderer.default)("<div>").addClass("dx-tabpanel-tabs").appendTo($element);
const $tabs = (0, _renderer.default)("<div>").appendTo(this._$tabContainer);
this._tabs = this._createComponent($tabs, _m_tabs.default, this._tabConfig());
this._$container = (0, _renderer.default)("<div>").addClass("dx-tabpanel-container").appendTo($element);
this._$container.append(this._$wrapper)
}
_refreshActiveDescendant() {
if (!this._tabs) {
return
}
const tabs = this._tabs;
const tabItems = tabs.itemElements();
const $activeTab = (0, _renderer.default)(tabItems[tabs.option("selectedIndex")]);
const id = this.getFocusedItemId();
this.setAria("controls", void 0, (0, _renderer.default)(tabItems));
this.setAria("controls", id, $activeTab)
}
_getTabsIndicatorPosition() {
const {
_tabsIndicatorPosition: _tabsIndicatorPosition,
tabsPosition: tabsPosition
} = this.option();
return _tabsIndicatorPosition ?? TABS_INDICATOR_POSITION_BY_TABS_POSITION[tabsPosition]
}
_tabConfig() {
const tabsIndicatorPosition = this._getTabsIndicatorPosition();
const {
focusStateEnabled: focusStateEnabled,
hoverStateEnabled: hoverStateEnabled,
repaintChangesOnly: repaintChangesOnly,
tabIndex: tabIndex,
selectedIndex: selectedIndex,
badgeExpr: badgeExpr,
itemHoldTimeout: itemHoldTimeout,
items: items,
scrollingEnabled: scrollingEnabled,
scrollByContent: scrollByContent,
showNavButtons: showNavButtons,
loop: loop,
iconPosition: iconPosition,
stylingMode: stylingMode
} = this.option();
return {
selectOnFocus: true,
focusStateEnabled: focusStateEnabled,
hoverStateEnabled: hoverStateEnabled,
repaintChangesOnly: repaintChangesOnly,
tabIndex: tabIndex,
selectedIndex: selectedIndex,
badgeExpr: badgeExpr,
onItemClick: this._titleClickAction.bind(this),
onItemHold: this._titleHoldAction.bind(this),
itemHoldTimeout: itemHoldTimeout,
onSelectionChanging: e => {
const newTabsSelectedItemData = e.addedItems[0];
const newTabsSelectedIndex = this._getIndexByItemData(newTabsSelectedItemData);
const selectingResult = this.selectItem(newTabsSelectedIndex);
const promiseState = selectingResult.state();
if ("pending" !== promiseState) {
e.cancel = "rejected" === promiseState;
return
}
e.cancel = new Promise((resolve => {
selectingResult.done((() => {
resolve(false)
})).fail((() => {
resolve(true)
}))
}))
},
onSelectionChanged: () => {
this._refreshActiveDescendant()
},
onItemRendered: this._titleRenderedAction.bind(this),
itemTemplate: this._getTemplateByOption("itemTitleTemplate"),
items: items,
noDataText: null,
scrollingEnabled: scrollingEnabled,
scrollByContent: scrollByContent,
showNavButtons: showNavButtons,
itemTemplateProperty: "tabTemplate",
loopItemFocus: loop,
selectionRequired: true,
onOptionChanged: function(args) {
if ("focusedElement" === args.name) {
if (args.value) {
const $value = (0, _renderer.default)(args.value);
const $newItem = this._itemElements().eq($value.index());
this.option("focusedElement", (0, _element.getPublicElement)($newItem))
} else {
this.option("focusedElement", args.value)
}
}
}.bind(this),
onFocusIn: function(args) {
this._focusInHandler(args.event)
}.bind(this),
onFocusOut: function(args) {
if (!this._isFocusOutHandlerExecuting) {
this._focusOutHandler(args.event)
}
}.bind(this),
orientation: this._getTabsOrientation(),
iconPosition: iconPosition,
stylingMode: stylingMode,
_itemAttributes: {
class: "dx-tabpanel-tab"
},
_indicatorPosition: tabsIndicatorPosition
}
}
_renderFocusTarget() {
this._focusTarget().attr("tabIndex", -1)
}
_getTabsOrientation() {
const {
tabsPosition: tabsPosition
} = this.option();
if ([TABS_POSITION.right, TABS_POSITION.left].includes(tabsPosition)) {
return TABS_ORIENTATION.vertical
}
return TABS_ORIENTATION.horizontal
}
_getTabPanelTabsPositionClass() {
const position = this.option("tabsPosition");
switch (position) {
case TABS_POSITION.right:
return TABPANEL_TABS_POSITION_CLASS.right;
case TABS_POSITION.bottom:
return TABPANEL_TABS_POSITION_CLASS.bottom;
case TABS_POSITION.left:
return TABPANEL_TABS_POSITION_CLASS.left;
case TABS_POSITION.top:
default:
return TABPANEL_TABS_POSITION_CLASS.top
}
}
_toggleTabPanelTabsPositionClass() {
for (const key in TABPANEL_TABS_POSITION_CLASS) {
this.$element().removeClass(TABPANEL_TABS_POSITION_CLASS[key])
}
const newClass = this._getTabPanelTabsPositionClass();
this.$element().addClass(newClass)
}
_updateTabsOrientation() {
const orientation = this._getTabsOrientation();
this._setTabsOption("orientation", orientation)
}
_toggleWrapperFocusedClass(isFocused) {
this._toggleFocusClass(isFocused, this._$wrapper)
}
_toggleDisabledFocusedClass(isFocused) {
this._focusTarget().toggleClass("dx-disabled-focused-tab", isFocused)
}
_updateFocusState(e, isFocused) {
super._updateFocusState(e, isFocused);
const isTabsTarget = e.target === this._tabs._focusTarget().get(0);
const isMultiViewTarget = e.target === this._focusTarget().get(0);
if (isTabsTarget) {
this._toggleFocusClass(isFocused, this._focusTarget())
}
if (isTabsTarget || isMultiViewTarget) {
const isDisabled = this._isDisabled(this.option("focusedElement"));
this._toggleWrapperFocusedClass(isFocused && !isDisabled);
this._toggleDisabledFocusedClass(isFocused && isDisabled)
}
if (isMultiViewTarget) {
this._toggleFocusClass(isFocused, this._tabs.$element());
this._toggleFocusClass(isFocused, this._tabs.option("focusedElement"))
}
}
_focusOutHandler(e) {
this._isFocusOutHandlerExecuting = true;
super._focusOutHandler.apply(this, arguments);
this._tabs._focusOutHandler(e);
this._isFocusOutHandlerExecuting = false
}
_setTabsOption(name, value) {
if (this._tabs) {
this._tabs.option(name, value)
}
}
_postprocessSwipe(_ref) {
let {
swipedTabsIndex: swipedTabsIndex
} = _ref;
this._setTabsOption("selectedIndex", swipedTabsIndex)
}
_visibilityChanged(visible) {
if (visible) {
this._tabs._dimensionChanged()
}
}
registerKeyHandler(key, handler) {
super.registerKeyHandler(key, handler);
if (this._tabs) {
this._tabs.registerKeyHandler(key, handler)
}
}
repaint() {
super.repaint();
this._tabs.repaint()
}
_updateTabsIndicatorPosition() {
const value = this._getTabsIndicatorPosition();
this._setTabsOption("_indicatorPosition", value)
}
_optionChanged(args) {
const {
name: name,
value: value,
fullName: fullName
} = args;
switch (name) {
case "dataSource":
default:
super._optionChanged(args);
break;
case "items":
this._setTabsOption(name, this.option(name));
if (!this.option("repaintChangesOnly")) {
this._tabs.repaint()
}
super._optionChanged(args);
break;
case "width":
super._optionChanged(args);
this._tabs.repaint();
break;
case "selectedIndex":
case "selectedItem":
this._setTabsOption(fullName, value);
super._optionChanged(args);
if (true === this.option("focusStateEnabled")) {
const selectedIndex = this.option("selectedIndex");
const selectedTabContent = this._itemElements().eq(selectedIndex);
this.option("focusedElement", (0, _element.getPublicElement)(selectedTabContent))
}
break;
case "itemHoldTimeout":
case "focusStateEnabled":
case "hoverStateEnabled":
this._setTabsOption(fullName, value);
super._optionChanged(args);
break;
case "scrollingEnabled":
case "scrollByContent":
case "showNavButtons":
this._setTabsOption(fullName, value);
break;
case "focusedElement": {
const id = value ? (0, _renderer.default)(value).index() : value;
const newItem = value && this._tabs ? this._tabs._itemElements().eq(id) : value;
this._setTabsOption("focusedElement", (0, _element.getPublicElement)(newItem));
if (value) {
const isDisabled = this._isDisabled(value);
this._toggleWrapperFocusedClass(!isDisabled);
this._toggleDisabledFocusedClass(isDisabled)
}
super._optionChanged(args);
break
}
case "itemTitleTemplate":
this._setTabsOption("itemTemplate", this._getTemplateByOption("itemTitleTemplate"));
break;
case "onTitleClick":
this._createTitleClickAction();
this._setTabsOption("onItemClick", this._titleClickAction.bind(this));
break;
case "onTitleHold":
this._createTitleHoldAction();
this._setTabsOption("onItemHold", this._titleHoldAction.bind(this));
break;
case "onTitleRendered":
this._createTitleRenderedAction();
this._setTabsOption("onItemRendered", this._titleRenderedAction.bind(this));
break;
case "loop":
this._setTabsOption("loopItemFocus", value);
super._optionChanged(args);
break;
case "badgeExpr":
this._invalidate();
break;
case "tabsPosition":
this._toggleTabPanelTabsPositionClass();
this._updateTabsIndicatorPosition();
this._updateTabsOrientation();
break;
case "iconPosition":
this._setTabsOption("iconPosition", value);
break;
case "stylingMode":
this._setTabsOption("stylingMode", value);
break;
case "_tabsIndicatorPosition":
this._setTabsOption("_indicatorPosition", value)
}
}
}
TabPanel.ItemClass = _m_item.default;
(0, _component_registrator.default)("dxTabPanel", TabPanel);
var _default = exports.default = TabPanel;