accordion.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /**
  2. * DevExtreme (ui/accordion.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 _fx = require("../animation/fx");
  15. var _fx2 = _interopRequireDefault(_fx);
  16. var _click = require("../events/click");
  17. var _click2 = _interopRequireDefault(_click);
  18. var _devices = require("../core/devices");
  19. var _devices2 = _interopRequireDefault(_devices);
  20. var _extend = require("../core/utils/extend");
  21. var _common = require("../core/utils/common");
  22. var _dom = require("../core/utils/dom");
  23. var _iterator = require("../core/utils/iterator");
  24. var _iterator2 = _interopRequireDefault(_iterator);
  25. var _type = require("../core/utils/type");
  26. var _component_registrator = require("../core/component_registrator");
  27. var _component_registrator2 = _interopRequireDefault(_component_registrator);
  28. var _utils = require("../events/utils");
  29. var _utils2 = _interopRequireDefault(_utils);
  30. var _uiCollection_widget = require("./collection/ui.collection_widget.live_update");
  31. var _uiCollection_widget2 = _interopRequireDefault(_uiCollection_widget);
  32. var _deferred = require("../core/utils/deferred");
  33. var _bindable_template = require("./widget/bindable_template");
  34. var _bindable_template2 = _interopRequireDefault(_bindable_template);
  35. var _icon = require("../core/utils/icon");
  36. var _themes = require("./themes");
  37. var _themes2 = _interopRequireDefault(_themes);
  38. function _interopRequireDefault(obj) {
  39. return obj && obj.__esModule ? obj : {
  40. "default": obj
  41. }
  42. }
  43. var ACCORDION_CLASS = "dx-accordion";
  44. var ACCORDION_WRAPPER_CLASS = "dx-accordion-wrapper";
  45. var ACCORDION_ITEM_CLASS = "dx-accordion-item";
  46. var ACCORDION_ITEM_OPENED_CLASS = "dx-accordion-item-opened";
  47. var ACCORDION_ITEM_CLOSED_CLASS = "dx-accordion-item-closed";
  48. var ACCORDION_ITEM_TITLE_CLASS = "dx-accordion-item-title";
  49. var ACCORDION_ITEM_BODY_CLASS = "dx-accordion-item-body";
  50. var ACCORDION_ITEM_TITLE_CAPTION_CLASS = "dx-accordion-item-title-caption";
  51. var ACCORDION_ITEM_DATA_KEY = "dxAccordionItemData";
  52. var Accordion = _uiCollection_widget2.default.inherit({
  53. _activeStateUnit: "." + ACCORDION_ITEM_CLASS,
  54. _getDefaultOptions: function() {
  55. return (0, _extend.extend)(this.callBase(), {
  56. hoverStateEnabled: true,
  57. height: void 0,
  58. itemTitleTemplate: "title",
  59. onItemTitleClick: null,
  60. selectedIndex: 0,
  61. collapsible: false,
  62. multiple: false,
  63. animationDuration: 300,
  64. deferRendering: true,
  65. selectionByClick: true,
  66. activeStateEnabled: true,
  67. _itemAttributes: {
  68. role: "tab"
  69. },
  70. _animationEasing: "ease"
  71. })
  72. },
  73. _defaultOptionsRules: function() {
  74. return this.callBase().concat([{
  75. device: function() {
  76. return "desktop" === _devices2.default.real().deviceType && !_devices2.default.isSimulator()
  77. },
  78. options: {
  79. focusStateEnabled: true
  80. }
  81. }, {
  82. device: function() {
  83. return _themes2.default.isMaterial()
  84. },
  85. options: {
  86. animationDuration: 200,
  87. _animationEasing: "cubic-bezier(0.4, 0, 0.2, 1)"
  88. }
  89. }])
  90. },
  91. _itemElements: function() {
  92. return this._itemContainer().children(this._itemSelector())
  93. },
  94. _init: function() {
  95. this.callBase();
  96. this.option("selectionRequired", !this.option("collapsible"));
  97. this.option("selectionMode", this.option("multiple") ? "multiple" : "single");
  98. var $element = this.$element();
  99. $element.addClass(ACCORDION_CLASS);
  100. this._$container = (0, _renderer2.default)("<div>").addClass(ACCORDION_WRAPPER_CLASS);
  101. $element.append(this._$container)
  102. },
  103. _initTemplates: function() {
  104. this.callBase();
  105. this._defaultTemplates.title = new _bindable_template2.default(function($container, data) {
  106. if ((0, _type.isPlainObject)(data)) {
  107. if ((0, _type.isDefined)(data.title) && !(0, _type.isPlainObject)(data.title)) {
  108. $container.text(data.title)
  109. }
  110. var $iconElement = (0, _icon.getImageContainer)(data.icon);
  111. $iconElement && $iconElement.appendTo($container)
  112. } else {
  113. if ((0, _type.isDefined)(data)) {
  114. $container.text(String(data))
  115. }
  116. }
  117. $container.wrapInner((0, _renderer2.default)("<div>").addClass(ACCORDION_ITEM_TITLE_CAPTION_CLASS))
  118. }, ["title", "icon"], this.option("integrationOptions.watchMethod"))
  119. },
  120. _initMarkup: function() {
  121. var _this = this;
  122. this._deferredItems = [];
  123. this.callBase();
  124. this.setAria({
  125. role: "tablist",
  126. multiselectable: this.option("multiple")
  127. });
  128. (0, _common.deferRender)(function() {
  129. var selectedItemIndices = _this._getSelectedItemIndices();
  130. _this._renderSelection(selectedItemIndices, [])
  131. })
  132. },
  133. _render: function() {
  134. this.callBase();
  135. this._updateItemHeightsWrapper(true)
  136. },
  137. _itemDataKey: function() {
  138. return ACCORDION_ITEM_DATA_KEY
  139. },
  140. _itemClass: function() {
  141. return ACCORDION_ITEM_CLASS
  142. },
  143. _itemContainer: function() {
  144. return this._$container
  145. },
  146. _itemTitles: function() {
  147. return this._itemElements().find("." + ACCORDION_ITEM_TITLE_CLASS)
  148. },
  149. _itemContents: function() {
  150. return this._itemElements().find("." + ACCORDION_ITEM_BODY_CLASS)
  151. },
  152. _getItemData: function(target) {
  153. return (0, _renderer2.default)(target).parent().data(this._itemDataKey()) || this.callBase.apply(this, arguments)
  154. },
  155. _executeItemRenderAction: function(itemData) {
  156. if (itemData.type) {
  157. return
  158. }
  159. this.callBase.apply(this, arguments)
  160. },
  161. _itemSelectHandler: function(e) {
  162. if ((0, _renderer2.default)(e.target).closest(this._itemContents()).length) {
  163. return
  164. }
  165. this.callBase.apply(this, arguments)
  166. },
  167. _afterItemElementDeleted: function($item, deletedActionArgs) {
  168. this._deferredItems.splice(deletedActionArgs.itemIndex, 1);
  169. this.callBase.apply(this, arguments)
  170. },
  171. _renderItemContent: function(args) {
  172. var itemTitle = this.callBase((0, _extend.extend)({}, args, {
  173. contentClass: ACCORDION_ITEM_TITLE_CLASS,
  174. templateProperty: "titleTemplate",
  175. defaultTemplateName: this.option("itemTitleTemplate")
  176. }));
  177. this._attachItemTitleClickAction(itemTitle);
  178. var deferred = new _deferred.Deferred;
  179. if ((0, _type.isDefined)(this._deferredItems[args.index])) {
  180. this._deferredItems[args.index] = deferred
  181. } else {
  182. this._deferredItems.push(deferred)
  183. }
  184. if (!this.option("deferRendering") || this._getSelectedItemIndices().indexOf(args.index) >= 0) {
  185. deferred.resolve()
  186. }
  187. deferred.done(this.callBase.bind(this, (0, _extend.extend)({}, args, {
  188. contentClass: ACCORDION_ITEM_BODY_CLASS,
  189. container: (0, _dom.getPublicElement)((0, _renderer2.default)("<div>").appendTo((0, _renderer2.default)(itemTitle).parent()))
  190. })))
  191. },
  192. _attachItemTitleClickAction: function(itemTitle) {
  193. var eventName = _utils2.default.addNamespace(_click2.default.name, this.NAME);
  194. _events_engine2.default.off(itemTitle, eventName);
  195. _events_engine2.default.on(itemTitle, eventName, this._itemTitleClickHandler.bind(this))
  196. },
  197. _itemTitleClickHandler: function(e) {
  198. this._itemDXEventHandler(e, "onItemTitleClick")
  199. },
  200. _renderSelection: function(addedSelection, removedSelection) {
  201. this._itemElements().addClass(ACCORDION_ITEM_CLOSED_CLASS);
  202. this.setAria("hidden", true, this._itemContents());
  203. this._updateItems(addedSelection, removedSelection)
  204. },
  205. _updateSelection: function(addedSelection, removedSelection) {
  206. this._updateItems(addedSelection, removedSelection);
  207. this._updateItemHeightsWrapper(false)
  208. },
  209. _updateItems: function(addedSelection, removedSelection) {
  210. var $items = this._itemElements();
  211. var that = this;
  212. _iterator2.default.each(addedSelection, function(_, index) {
  213. that._deferredItems[index].resolve();
  214. var $item = $items.eq(index).addClass(ACCORDION_ITEM_OPENED_CLASS).removeClass(ACCORDION_ITEM_CLOSED_CLASS);
  215. that.setAria("hidden", false, $item.find("." + ACCORDION_ITEM_BODY_CLASS))
  216. });
  217. _iterator2.default.each(removedSelection, function(_, index) {
  218. var $item = $items.eq(index).removeClass(ACCORDION_ITEM_OPENED_CLASS);
  219. that.setAria("hidden", true, $item.find("." + ACCORDION_ITEM_BODY_CLASS))
  220. })
  221. },
  222. _updateItemHeightsWrapper: function(skipAnimation) {
  223. if (this.option("templatesRenderAsynchronously")) {
  224. this._animationTimer = setTimeout(function() {
  225. this._updateItemHeights(skipAnimation)
  226. }.bind(this))
  227. } else {
  228. this._updateItemHeights(skipAnimation)
  229. }
  230. },
  231. _updateItemHeights: function(skipAnimation) {
  232. var that = this;
  233. var deferredAnimate = that._deferredAnimate;
  234. var itemHeight = this._splitFreeSpace(this._calculateFreeSpace());
  235. clearTimeout(this._animationTimer);
  236. return _deferred.when.apply(_renderer2.default, [].slice.call(this._itemElements()).map(function(item) {
  237. return that._updateItemHeight((0, _renderer2.default)(item), itemHeight, skipAnimation)
  238. })).done(function() {
  239. if (deferredAnimate) {
  240. deferredAnimate.resolveWith(that)
  241. }
  242. })
  243. },
  244. _updateItemHeight: function($item, itemHeight, skipAnimation) {
  245. var $title = $item.children("." + ACCORDION_ITEM_TITLE_CLASS);
  246. if (_fx2.default.isAnimating($item)) {
  247. _fx2.default.stop($item)
  248. }
  249. var startItemHeight = $item.outerHeight();
  250. var finalItemHeight = $item.hasClass(ACCORDION_ITEM_OPENED_CLASS) ? itemHeight + $title.outerHeight() || $item.height("auto").outerHeight() : $title.outerHeight();
  251. return this._animateItem($item, startItemHeight, finalItemHeight, skipAnimation, !!itemHeight)
  252. },
  253. _animateItem: function($element, startHeight, endHeight, skipAnimation, fixedHeight) {
  254. var d;
  255. if (skipAnimation || startHeight === endHeight) {
  256. $element.css("height", endHeight);
  257. d = (new _deferred.Deferred).resolve()
  258. } else {
  259. d = _fx2.default.animate($element, {
  260. type: "custom",
  261. from: {
  262. height: startHeight
  263. },
  264. to: {
  265. height: endHeight
  266. },
  267. duration: this.option("animationDuration"),
  268. easing: this.option("_animationEasing")
  269. })
  270. }
  271. return d.done(function() {
  272. if ($element.hasClass(ACCORDION_ITEM_OPENED_CLASS) && !fixedHeight) {
  273. $element.css("height", "")
  274. }
  275. $element.not("." + ACCORDION_ITEM_OPENED_CLASS).addClass(ACCORDION_ITEM_CLOSED_CLASS)
  276. })
  277. },
  278. _splitFreeSpace: function(freeSpace) {
  279. if (!freeSpace) {
  280. return freeSpace
  281. }
  282. return freeSpace / this.option("selectedItems").length
  283. },
  284. _calculateFreeSpace: function() {
  285. var height = this.option("height");
  286. if (void 0 === height || "auto" === height) {
  287. return
  288. }
  289. var $titles = this._itemTitles();
  290. var itemsHeight = 0;
  291. _iterator2.default.each($titles, function(_, title) {
  292. itemsHeight += (0, _renderer2.default)(title).outerHeight()
  293. });
  294. return this.$element().height() - itemsHeight
  295. },
  296. _visibilityChanged: function(visible) {
  297. if (visible) {
  298. this._dimensionChanged()
  299. }
  300. },
  301. _dimensionChanged: function() {
  302. this._updateItemHeights(true)
  303. },
  304. _clean: function() {
  305. clearTimeout(this._animationTimer);
  306. this.callBase()
  307. },
  308. _optionChanged: function(args) {
  309. switch (args.name) {
  310. case "animationDuration":
  311. case "onItemTitleClick":
  312. case "_animationEasing":
  313. break;
  314. case "collapsible":
  315. this.option("selectionRequired", !this.option("collapsible"));
  316. break;
  317. case "itemTitleTemplate":
  318. case "height":
  319. case "deferRendering":
  320. this._invalidate();
  321. break;
  322. case "multiple":
  323. this.option("selectionMode", args.value ? "multiple" : "single");
  324. break;
  325. default:
  326. this.callBase(args)
  327. }
  328. },
  329. expandItem: function(index) {
  330. this._deferredAnimate = new _deferred.Deferred;
  331. this.selectItem(index);
  332. return this._deferredAnimate.promise()
  333. },
  334. collapseItem: function(index) {
  335. this._deferredAnimate = new _deferred.Deferred;
  336. this.unselectItem(index);
  337. return this._deferredAnimate.promise()
  338. },
  339. updateDimensions: function() {
  340. return this._updateItemHeights(false)
  341. }
  342. });
  343. (0, _component_registrator2.default)("dxAccordion", Accordion);
  344. module.exports = Accordion;
  345. module.exports.default = module.exports;