tabs.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /**
  2. * DevExtreme (ui/tabs.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var _renderer = require("../core/renderer");
  11. var _renderer2 = _interopRequireDefault(_renderer);
  12. var _events_engine = require("../events/core/events_engine");
  13. var _events_engine2 = _interopRequireDefault(_events_engine);
  14. var _devices = require("../core/devices");
  15. var _devices2 = _interopRequireDefault(_devices);
  16. var _component_registrator = require("../core/component_registrator");
  17. var _component_registrator2 = _interopRequireDefault(_component_registrator);
  18. var _button = require("./button");
  19. var _button2 = _interopRequireDefault(_button);
  20. var _utils = require("./widget/utils.ink_ripple");
  21. var _utils2 = _interopRequireDefault(_utils);
  22. var _utils3 = require("../events/utils");
  23. var _extend = require("../core/utils/extend");
  24. var _type = require("../core/utils/type");
  25. var _pointer = require("../events/pointer");
  26. var _pointer2 = _interopRequireDefault(_pointer);
  27. var _iterator = require("../core/utils/iterator");
  28. var _item = require("./tabs/item");
  29. var _item2 = _interopRequireDefault(_item);
  30. var _themes = require("./themes");
  31. var _themes2 = _interopRequireDefault(_themes);
  32. var _hold = require("../events/hold");
  33. var _hold2 = _interopRequireDefault(_hold);
  34. var _ui = require("./scroll_view/ui.scrollable");
  35. var _ui2 = _interopRequireDefault(_ui);
  36. var _uiCollection_widget = require("./collection/ui.collection_widget.live_update");
  37. var _uiCollection_widget2 = _interopRequireDefault(_uiCollection_widget);
  38. var _icon = require("../core/utils/icon");
  39. var _bindable_template = require("./widget/bindable_template");
  40. var _bindable_template2 = _interopRequireDefault(_bindable_template);
  41. function _interopRequireDefault(obj) {
  42. return obj && obj.__esModule ? obj : {
  43. "default": obj
  44. }
  45. }
  46. var TABS_CLASS = "dx-tabs";
  47. var TABS_WRAPPER_CLASS = "dx-tabs-wrapper";
  48. var TABS_EXPANDED_CLASS = "dx-tabs-expanded";
  49. var TABS_STRETCHED_CLASS = "dx-tabs-stretched";
  50. var TABS_SCROLLABLE_CLASS = "dx-tabs-scrollable";
  51. var TABS_NAV_BUTTONS_CLASS = "dx-tabs-nav-buttons";
  52. var OVERFLOW_HIDDEN_CLASS = "dx-overflow-hidden";
  53. var TABS_ITEM_CLASS = "dx-tab";
  54. var TABS_ITEM_SELECTED_CLASS = "dx-tab-selected";
  55. var TABS_NAV_BUTTON_CLASS = "dx-tabs-nav-button";
  56. var TABS_LEFT_NAV_BUTTON_CLASS = "dx-tabs-nav-button-left";
  57. var TABS_RIGHT_NAV_BUTTON_CLASS = "dx-tabs-nav-button-right";
  58. var TABS_ITEM_TEXT_CLASS = "dx-tab-text";
  59. var TABS_ITEM_DATA_KEY = "dxTabData";
  60. var BUTTON_NEXT_ICON = "chevronnext";
  61. var BUTTON_PREV_ICON = "chevronprev";
  62. var FEEDBACK_HIDE_TIMEOUT = 100;
  63. var FEEDBACK_DURATION_INTERVAL = 5;
  64. var FEEDBACK_SCROLL_TIMEOUT = 300;
  65. var TAB_OFFSET = 30;
  66. var Tabs = _uiCollection_widget2.default.inherit({
  67. _activeStateUnit: "." + TABS_ITEM_CLASS,
  68. _getDefaultOptions: function() {
  69. return (0, _extend.extend)(this.callBase(), {
  70. hoverStateEnabled: true,
  71. showNavButtons: true,
  72. scrollByContent: true,
  73. scrollingEnabled: true,
  74. selectionMode: "single",
  75. activeStateEnabled: true,
  76. selectionRequired: false,
  77. selectOnFocus: true,
  78. loopItemFocus: false,
  79. useInkRipple: false,
  80. badgeExpr: function(data) {
  81. return data ? data.badge : void 0
  82. }
  83. })
  84. },
  85. _defaultOptionsRules: function() {
  86. var themeName = _themes2.default.current();
  87. return this.callBase().concat([{
  88. device: function() {
  89. return "generic" !== _devices2.default.real().platform
  90. },
  91. options: {
  92. showNavButtons: false
  93. }
  94. }, {
  95. device: {
  96. platform: "generic"
  97. },
  98. options: {
  99. scrollByContent: false
  100. }
  101. }, {
  102. device: function() {
  103. return "desktop" === _devices2.default.real().deviceType && !_devices2.default.isSimulator()
  104. },
  105. options: {
  106. focusStateEnabled: true
  107. }
  108. }, {
  109. device: function() {
  110. return _themes2.default.isMaterial(themeName)
  111. },
  112. options: {
  113. useInkRipple: true,
  114. selectOnFocus: false
  115. }
  116. }])
  117. },
  118. _init: function() {
  119. this.callBase();
  120. this.setAria("role", "tablist");
  121. this.$element().addClass(TABS_CLASS);
  122. this._renderWrapper();
  123. this._renderMultiple();
  124. this._feedbackHideTimeout = FEEDBACK_HIDE_TIMEOUT
  125. },
  126. _initTemplates: function() {
  127. this.callBase();
  128. this._defaultTemplates.item = new _bindable_template2.default(function($container, data) {
  129. if ((0, _type.isPlainObject)(data)) {
  130. this._prepareDefaultItemTemplate(data, $container)
  131. } else {
  132. $container.text(String(data))
  133. }
  134. var $iconElement = (0, _icon.getImageContainer)(data.icon);
  135. $container.wrapInner((0, _renderer2.default)("<span>").addClass(TABS_ITEM_TEXT_CLASS));
  136. $iconElement && $iconElement.prependTo($container)
  137. }.bind(this), ["text", "html", "icon"], this.option("integrationOptions.watchMethod"))
  138. },
  139. _itemClass: function() {
  140. return TABS_ITEM_CLASS
  141. },
  142. _selectedItemClass: function() {
  143. return TABS_ITEM_SELECTED_CLASS
  144. },
  145. _itemDataKey: function() {
  146. return TABS_ITEM_DATA_KEY
  147. },
  148. _initMarkup: function() {
  149. this.callBase();
  150. this.setAria("role", "tab", this.itemElements());
  151. this.option("useInkRipple") && this._renderInkRipple();
  152. this.$element().addClass(OVERFLOW_HIDDEN_CLASS)
  153. },
  154. _render: function() {
  155. this.callBase();
  156. this._renderScrolling()
  157. },
  158. _renderScrolling: function() {
  159. var removeClasses = [TABS_STRETCHED_CLASS, TABS_EXPANDED_CLASS, OVERFLOW_HIDDEN_CLASS];
  160. this.$element().removeClass(removeClasses.join(" "));
  161. if (this.option("scrollingEnabled") && this._isItemsWidthExceeded()) {
  162. if (!this._scrollable) {
  163. this._renderScrollable();
  164. this._renderNavButtons()
  165. }
  166. this._scrollable.update();
  167. this._updateNavButtonsVisibility();
  168. if (this.option("rtlEnabled")) {
  169. this._scrollable.scrollTo({
  170. left: this._scrollable.scrollWidth() - this._scrollable.clientWidth()
  171. })
  172. }
  173. this._scrollToItem(this.option("selectedItem"))
  174. }
  175. if (!(this.option("scrollingEnabled") && this._isItemsWidthExceeded())) {
  176. this._cleanScrolling();
  177. if (this._needStretchItems() && !this._isItemsWidthExceeded()) {
  178. this.$element().addClass(TABS_STRETCHED_CLASS)
  179. }
  180. this.$element().removeClass(TABS_NAV_BUTTONS_CLASS).addClass(TABS_EXPANDED_CLASS)
  181. }
  182. },
  183. _isItemsWidthExceeded: function() {
  184. var tabItemsWidth = this._getSummaryItemsWidth(this._getVisibleItems(), true);
  185. return tabItemsWidth - 1 > this.$element().width()
  186. },
  187. _needStretchItems: function() {
  188. var $visibleItems = this._getVisibleItems();
  189. var elementWidth = this.$element().width();
  190. var itemsWidth = [];
  191. (0, _iterator.each)($visibleItems, function(_, item) {
  192. itemsWidth.push((0, _renderer2.default)(item).outerWidth(true))
  193. });
  194. var maxTabWidth = Math.max.apply(null, itemsWidth);
  195. return maxTabWidth > elementWidth / $visibleItems.length
  196. },
  197. _cleanNavButtons: function() {
  198. if (!this._leftButton || !this._rightButton) {
  199. return
  200. }
  201. this._leftButton.$element().remove();
  202. this._rightButton.$element().remove();
  203. this._leftButton = null;
  204. this._rightButton = null
  205. },
  206. _cleanScrolling: function() {
  207. if (!this._scrollable) {
  208. return
  209. }
  210. this._$wrapper.appendTo(this.$element());
  211. this._scrollable.$element().remove();
  212. this._scrollable = null;
  213. this._cleanNavButtons()
  214. },
  215. _renderInkRipple: function() {
  216. this._inkRipple = _utils2.default.render()
  217. },
  218. _toggleActiveState: function($element, value, e) {
  219. this.callBase.apply(this, arguments);
  220. if (!this._inkRipple) {
  221. return
  222. }
  223. var config = {
  224. element: $element,
  225. event: e
  226. };
  227. if (value) {
  228. this._inkRipple.showWave(config)
  229. } else {
  230. this._inkRipple.hideWave(config)
  231. }
  232. },
  233. _renderMultiple: function() {
  234. if ("multiple" === this.option("selectionMode")) {
  235. this.option("selectOnFocus", false)
  236. }
  237. },
  238. _renderWrapper: function() {
  239. this._$wrapper = (0, _renderer2.default)("<div>").addClass(TABS_WRAPPER_CLASS);
  240. this.$element().append(this._$wrapper)
  241. },
  242. _itemContainer: function() {
  243. return this._$wrapper
  244. },
  245. _renderScrollable: function() {
  246. var $itemContainer = this.$element().wrapInner((0, _renderer2.default)("<div>").addClass(TABS_SCROLLABLE_CLASS)).children();
  247. this._scrollable = this._createComponent($itemContainer, _ui2.default, {
  248. direction: "horizontal",
  249. showScrollbar: false,
  250. useKeyboard: false,
  251. useNative: false,
  252. scrollByContent: this.option("scrollByContent"),
  253. onScroll: this._updateNavButtonsVisibility.bind(this)
  254. });
  255. this.$element().append(this._scrollable.$element())
  256. },
  257. _scrollToItem: function(itemData) {
  258. if (!this._scrollable) {
  259. return
  260. }
  261. var $item = this._editStrategy.getItemElement(itemData);
  262. this._scrollable.scrollToElement($item)
  263. },
  264. _renderNavButtons: function() {
  265. this.$element().toggleClass(TABS_NAV_BUTTONS_CLASS, this.option("showNavButtons"));
  266. if (!this.option("showNavButtons")) {
  267. return
  268. }
  269. var rtlEnabled = this.option("rtlEnabled");
  270. this._leftButton = this._createNavButton(-TAB_OFFSET, rtlEnabled ? BUTTON_NEXT_ICON : BUTTON_PREV_ICON);
  271. var $leftButton = this._leftButton.$element();
  272. $leftButton.addClass(TABS_LEFT_NAV_BUTTON_CLASS);
  273. this.$element().prepend($leftButton);
  274. this._rightButton = this._createNavButton(TAB_OFFSET, rtlEnabled ? BUTTON_PREV_ICON : BUTTON_NEXT_ICON);
  275. var $rightButton = this._rightButton.$element();
  276. $rightButton.addClass(TABS_RIGHT_NAV_BUTTON_CLASS);
  277. this.$element().append($rightButton)
  278. },
  279. _updateNavButtonsVisibility: function() {
  280. this._leftButton && this._leftButton.option("disabled", this._scrollable.scrollLeft() <= 0);
  281. this._rightButton && this._rightButton.option("disabled", this._scrollable.scrollLeft() >= Math.round(this._scrollable.scrollWidth() - this._scrollable.clientWidth()))
  282. },
  283. _updateScrollPosition: function(offset, duration) {
  284. this._scrollable.update();
  285. this._scrollable.scrollBy(offset / duration)
  286. },
  287. _createNavButton: function(offset, icon) {
  288. var that = this;
  289. var holdAction = that._createAction(function() {
  290. that._holdInterval = setInterval(function() {
  291. that._updateScrollPosition(offset, FEEDBACK_DURATION_INTERVAL)
  292. }, FEEDBACK_DURATION_INTERVAL)
  293. });
  294. var holdEventName = (0, _utils3.addNamespace)(_hold2.default.name, "dxNavButton");
  295. var pointerUpEventName = (0, _utils3.addNamespace)(_pointer2.default.up, "dxNavButton");
  296. var pointerOutEventName = (0, _utils3.addNamespace)(_pointer2.default.out, "dxNavButton");
  297. var navButton = this._createComponent((0, _renderer2.default)("<div>").addClass(TABS_NAV_BUTTON_CLASS), _button2.default, {
  298. focusStateEnabled: false,
  299. icon: icon,
  300. onClick: function() {
  301. that._updateScrollPosition(offset, 1)
  302. },
  303. integrationOptions: {}
  304. });
  305. var $navButton = navButton.$element();
  306. _events_engine2.default.on($navButton, holdEventName, {
  307. timeout: FEEDBACK_SCROLL_TIMEOUT
  308. }, function(e) {
  309. holdAction({
  310. event: e
  311. })
  312. }.bind(this));
  313. _events_engine2.default.on($navButton, pointerUpEventName, function() {
  314. that._clearInterval()
  315. });
  316. _events_engine2.default.on($navButton, pointerOutEventName, function() {
  317. that._clearInterval()
  318. });
  319. return navButton
  320. },
  321. _clearInterval: function() {
  322. if (this._holdInterval) {
  323. clearInterval(this._holdInterval)
  324. }
  325. },
  326. _updateSelection: function(addedSelection) {
  327. this._scrollable && this._scrollable.scrollToElement(this.itemElements().eq(addedSelection[0]), {
  328. left: 1,
  329. right: 1
  330. })
  331. },
  332. _visibilityChanged: function(visible) {
  333. if (visible) {
  334. this._dimensionChanged()
  335. }
  336. },
  337. _dimensionChanged: function() {
  338. this._renderScrolling()
  339. },
  340. _itemSelectHandler: function(e) {
  341. if ("single" === this.option("selectionMode") && this.isItemSelected(e.currentTarget)) {
  342. return
  343. }
  344. this.callBase(e)
  345. },
  346. _clean: function() {
  347. this._cleanScrolling();
  348. this.callBase()
  349. },
  350. _optionChanged: function(args) {
  351. switch (args.name) {
  352. case "useInkRipple":
  353. case "scrollingEnabled":
  354. case "showNavButtons":
  355. this._invalidate();
  356. break;
  357. case "scrollByContent":
  358. this._scrollable && this._scrollable.option(args.name, args.value);
  359. break;
  360. case "width":
  361. this.callBase(args);
  362. this._dimensionChanged();
  363. break;
  364. case "selectionMode":
  365. this._renderMultiple();
  366. this.callBase(args);
  367. break;
  368. case "badgeExpr":
  369. this._invalidate();
  370. break;
  371. default:
  372. this.callBase(args)
  373. }
  374. },
  375. _afterItemElementInserted: function() {
  376. this.callBase();
  377. this._renderScrolling()
  378. },
  379. _afterItemElementDeleted: function($item, deletedActionArgs) {
  380. this.callBase($item, deletedActionArgs);
  381. this._renderScrolling()
  382. }
  383. });
  384. Tabs.ItemClass = _item2.default;
  385. (0, _component_registrator2.default)("dxTabs", Tabs);
  386. module.exports = Tabs;
  387. module.exports.getTabsExpandedClass = TABS_EXPANDED_CLASS;
  388. module.exports.default = module.exports;