Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

353 řádky
10 KiB

  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'jquery',
  7. 'jquery-ui-modules/widget',
  8. 'jquery/ui-modules/widgets/tabs',
  9. 'mage/mage',
  10. 'mage/collapsible'
  11. ], function ($) {
  12. 'use strict';
  13. $.widget('mage.tabs', {
  14. options: {
  15. active: 0,
  16. disabled: [],
  17. openOnFocus: true,
  18. collapsible: false,
  19. collapsibleElement: '[data-role=collapsible]',
  20. header: '[data-role=title]',
  21. content: '[data-role=content]',
  22. trigger: '[data-role=trigger]',
  23. closedState: null,
  24. openedState: null,
  25. disabledState: null,
  26. ajaxUrlElement: '[data-ajax=true]',
  27. ajaxContent: false,
  28. loadingClass: null,
  29. saveState: false,
  30. animate: false,
  31. icons: {
  32. activeHeader: null,
  33. header: null
  34. }
  35. },
  36. /**
  37. * @private
  38. */
  39. _create: function () {
  40. if (typeof this.options.disabled === 'string') {
  41. this.options.disabled = this.options.disabled.split(' ').map(function (item) {
  42. return parseInt(item, 10);
  43. });
  44. }
  45. this._processPanels();
  46. this._handleDeepLinking();
  47. this._processTabIndex();
  48. this._closeOthers();
  49. this._bind();
  50. },
  51. /**
  52. * @private
  53. */
  54. _destroy: function () {
  55. $.each(this.collapsibles, function () {
  56. $(this).collapsible('destroy');
  57. });
  58. },
  59. /**
  60. * If deep linking is used, all sections must be closed but the one that contains the anchor.
  61. * @private
  62. */
  63. _handleDeepLinking: function () {
  64. var self = this,
  65. anchor = window.location.hash,
  66. isValid = $.mage.isValidSelector(anchor),
  67. anchorId = anchor.replace('#', '');
  68. if (anchor && isValid) {
  69. $.each(self.contents, function (i) {
  70. if ($(this).attr('id') === anchorId || $(this).find('#' + anchorId).length) {
  71. self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate');
  72. return false;
  73. }
  74. });
  75. }
  76. },
  77. /**
  78. * When the widget gets instantiated, the first tab that is not disabled receive focusable property
  79. * All tabs receive tabIndex 0
  80. * @private
  81. */
  82. _processTabIndex: function () {
  83. var self = this;
  84. self.triggers.attr('tabIndex', 0);
  85. $.each(this.collapsibles, function (i) {
  86. self.triggers.attr('tabIndex', 0);
  87. self.triggers.eq(i).attr('tabIndex', 0);
  88. });
  89. },
  90. /**
  91. * Prepare the elements for instantiating the collapsible widget
  92. * @private
  93. */
  94. _processPanels: function () {
  95. var isNotNested = this._isNotNested.bind(this);
  96. this.contents = this.element
  97. .find(this.options.content)
  98. .filter(isNotNested);
  99. this.collapsibles = this.element
  100. .find(this.options.collapsibleElement)
  101. .filter(isNotNested);
  102. this.collapsibles
  103. .attr('role', 'presentation')
  104. .parent()
  105. .attr('role', 'tablist');
  106. this.headers = this.element
  107. .find(this.options.header)
  108. .filter(isNotNested);
  109. if (this.headers.length === 0) {
  110. this.headers = this.collapsibles;
  111. }
  112. this.triggers = this.element
  113. .find(this.options.trigger)
  114. .filter(isNotNested);
  115. if (this.triggers.length === 0) {
  116. this.triggers = this.headers;
  117. }
  118. this._callCollapsible();
  119. },
  120. /**
  121. * Checks if element is not in nested container to keep the correct scope of collapsible
  122. * @param {Number} index
  123. * @param {HTMLElement} element
  124. * @private
  125. * @return {Boolean}
  126. */
  127. _isNotNested: function (index, element) {
  128. var parentContent = $(element).parents(this.options.content);
  129. return !parentContent.length || !this.element.find(parentContent).length;
  130. },
  131. /**
  132. * Setting the disabled and active tabs and calling instantiation of collapsible
  133. * @private
  134. */
  135. _callCollapsible: function () {
  136. var self = this,
  137. disabled = false,
  138. active = false;
  139. $.each(this.collapsibles, function (i) {
  140. disabled = active = false;
  141. if ($.inArray(i, self.options.disabled) !== -1) {
  142. disabled = true;
  143. }
  144. if (i === self.options.active) {
  145. active = true;
  146. }
  147. self._instantiateCollapsible(this, i, active, disabled);
  148. });
  149. },
  150. /**
  151. * Instantiate collapsible.
  152. *
  153. * @param {HTMLElement} element
  154. * @param {Number} index
  155. * @param {*} active
  156. * @param {*} disabled
  157. * @private
  158. */
  159. _instantiateCollapsible: function (element, index, active, disabled) {
  160. $(element).collapsible(
  161. $.extend({}, this.options, {
  162. active: active,
  163. disabled: disabled,
  164. header: this.headers.eq(index),
  165. content: this.contents.eq(index),
  166. trigger: this.triggers.eq(index)
  167. })
  168. );
  169. },
  170. /**
  171. * Adding callback to close others tabs when one gets opened
  172. * @private
  173. */
  174. _closeOthers: function () {
  175. var self = this;
  176. $.each(this.collapsibles, function () {
  177. $(this).on('beforeOpen', function () {
  178. self.collapsibles.not(this).collapsible('forceDeactivate');
  179. });
  180. });
  181. },
  182. /**
  183. * @param {*} index
  184. */
  185. activate: function (index) {
  186. this._toggleActivate('activate', index);
  187. },
  188. /**
  189. * @param {*} index
  190. */
  191. deactivate: function (index) {
  192. this._toggleActivate('deactivate', index);
  193. },
  194. /**
  195. * @param {*} action
  196. * @param {*} index
  197. * @private
  198. */
  199. _toggleActivate: function (action, index) {
  200. this.collapsibles.eq(index).collapsible(action);
  201. },
  202. /**
  203. * @param {*} index
  204. */
  205. disable: function (index) {
  206. this._toggleEnable('disable', index);
  207. },
  208. /**
  209. * @param {*} index
  210. */
  211. enable: function (index) {
  212. this._toggleEnable('enable', index);
  213. },
  214. /**
  215. * @param {*} action
  216. * @param {*} index
  217. * @private
  218. */
  219. _toggleEnable: function (action, index) {
  220. var self = this;
  221. if (Array.isArray(index)) {
  222. $.each(index, function () {
  223. self.collapsibles.eq(this).collapsible(action);
  224. });
  225. } else if (index === undefined) {
  226. this.collapsibles.collapsible(action);
  227. } else {
  228. this.collapsibles.eq(index).collapsible(action);
  229. }
  230. },
  231. /**
  232. * @param {jQuery.Event} event
  233. * @private
  234. */
  235. _keydown: function (event) {
  236. var self = this,
  237. keyCode, toFocus, toFocusIndex, enabledTriggers, length, currentIndex, nextToFocus;
  238. if (event.altKey || event.ctrlKey) {
  239. return;
  240. }
  241. keyCode = $.ui.keyCode;
  242. toFocus = false;
  243. enabledTriggers = [];
  244. $.each(this.triggers, function () {
  245. if (!self.collapsibles.eq(self.triggers.index($(this))).collapsible('option', 'disabled')) {
  246. enabledTriggers.push(this);
  247. }
  248. });
  249. length = $(enabledTriggers).length;
  250. currentIndex = $(enabledTriggers).index(event.target);
  251. /**
  252. * @param {String} direction
  253. * @return {*}
  254. */
  255. nextToFocus = function (direction) {
  256. if (length > 0) {
  257. if (direction === 'right') {
  258. toFocusIndex = (currentIndex + 1) % length;
  259. } else {
  260. toFocusIndex = (currentIndex + length - 1) % length;
  261. }
  262. return enabledTriggers[toFocusIndex];
  263. }
  264. return event.target;
  265. };
  266. switch (event.keyCode) {
  267. case keyCode.RIGHT:
  268. case keyCode.DOWN:
  269. toFocus = nextToFocus('right');
  270. break;
  271. case keyCode.LEFT:
  272. case keyCode.UP:
  273. toFocus = nextToFocus('left');
  274. break;
  275. case keyCode.HOME:
  276. toFocus = enabledTriggers[0];
  277. break;
  278. case keyCode.END:
  279. toFocus = enabledTriggers[length - 1];
  280. break;
  281. }
  282. if (toFocus) {
  283. toFocusIndex = this.triggers.index(toFocus);
  284. $(event.target).attr('tabIndex', -1);
  285. $(toFocus).attr('tabIndex', 0);
  286. toFocus.focus();
  287. if (this.options.openOnFocus) {
  288. this.activate(toFocusIndex);
  289. }
  290. event.preventDefault();
  291. }
  292. },
  293. /**
  294. * @private
  295. */
  296. _bind: function () {
  297. var events = {
  298. keydown: '_keydown'
  299. };
  300. this._off(this.triggers);
  301. this._on(this.triggers, events);
  302. }
  303. });
  304. return $.mage.tabs;
  305. });