Splitter.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  4. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  5. /**
  6. * Licensed Materials - Property of IBM
  7. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2014, 2022
  8. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  9. */
  10. define(['../../lib/@waca/core-client/js/core-client/ui/core/View', '../../lib/@waca/core-client/js/core-client/utils/Utils', '../../lib/@waca/core-client/js/core-client/utils/BrowserUtils', 'underscore', 'jquery', 'hammerjs', 'text!./templates/Splitter.html'], function (View, CoreUtils, BrowserUtils, _, $, Hammer, Template) {
  11. var Splitter = function (_View) {
  12. _inherits(Splitter, _View);
  13. function Splitter() {
  14. _classCallCheck(this, Splitter);
  15. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  16. args[_key] = arguments[_key];
  17. }
  18. return _possibleConstructorReturn(this, _View.call.apply(_View, [this].concat(args)));
  19. }
  20. /**
  21. * Create a splitter pane. Support attributes:
  22. *
  23. * items:
  24. * A list of item objects containing View objects and handle details:
  25. * [{
  26. * handleIcon: <icon>,
  27. * handleClass: <string>,
  28. * handleOpenTitle: <text>,
  29. * handleCloseTitle: <text>,
  30. * view: <View>,
  31. * hidden: <boolean>
  32. * },...]
  33. *
  34. * When hidden is true the item is not shown
  35. *
  36. * @param options
  37. */
  38. Splitter.prototype.init = function init() {
  39. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  40. this.templateString = Template;
  41. if (options.$el && options.$el.length > 0) {
  42. //Note that this DOM node gets passed in so this view does not really own it
  43. //therefore ensure the remove function must not clean it up, not sure why it was done like thiss
  44. this.el = options.$el[0];
  45. }
  46. _View.prototype.init.call(this, options);
  47. this.content = options.content;
  48. this.handlers = options.handlers || {
  49. getParentSize: function getParentSize() {
  50. return {
  51. height: Infinity, // and beyond.. I had to
  52. width: Infinity
  53. };
  54. }
  55. };
  56. // Iterate over the items given to us and make sure we have some sane defaults
  57. this.items = (options.items || []).map(function (item) {
  58. if (item.defaultTabIndex === undefined) {
  59. item.defaultTabIndex = item.hidden ? '-1' : '0';
  60. }
  61. return item;
  62. });
  63. this.panel = options.panel;
  64. this.currentIndex = -1;
  65. this.isDragStartCalled = false;
  66. if (BrowserUtils.isIPad()) {
  67. //Add a touchmove handler to the document on iPad to kill default page scroll when dragging.
  68. document.addEventListener('touchmove', this.preventPageScrollDrag.bind(this), { passive: false });
  69. }
  70. // FIXME: Why pass instance property to instance method?
  71. this.overrideGlobalMaxSize = this.getOverrideGlobalMaxSize(this.items);
  72. this._setupEvent();
  73. this.$el.children().addClass('splitterPane');
  74. var sHtml = this.dotTemplate({ handles: this.items });
  75. this.$el.children(this.panel).before(sHtml);
  76. this.$el.find('.handle').each(function () {
  77. var icon = this.dataset.icon || '';
  78. CoreUtils.setIcon($(this), icon);
  79. });
  80. this.$splitterBar = this.$el.find('.splitterBar');
  81. var visibleItems = _.filter(this.items, function (item) {
  82. return !item.hidden;
  83. });
  84. if (visibleItems.length === 0) {
  85. this.$splitterBar.find('.splitterSizeHandle').hide();
  86. }
  87. if (this._shouldHideHandle()) {
  88. // The height of the splitter bar is 0px by default
  89. // Need to set the height in order to show splitterSizeHandle
  90. this.$splitterBar.addClass('showSplitterSizeHandle');
  91. }
  92. _.each(this.items, function (item) {
  93. if (item.hidden || this._shouldHideHandle()) {
  94. this.$splitterBar.find('.' + item.handleClass).hide();
  95. }
  96. }, this);
  97. this.$handles = this.$splitterBar.find('.handle');
  98. this.$handles.each(function (index, value) {
  99. $(value).attr('title', this.items[index].handleOpenTitle);
  100. $(value).attr('aria-label', this.items[index].handleOpenTitle);
  101. }.bind(this));
  102. this.renderedMap = {};
  103. $(window).off('resize.splitterResize' + this.viewId).on('resize.splitterResize' + this.viewId, this.onResize.bind(this));
  104. $.each(this.$splitterBar.find('.handle, .splitterSizeHandle'), function (i, e) {
  105. Hammer(e).on('dragstart', this.onHandleDragStart.bind(this)).on('dragup dragdown', this.onHandleDragDownUp.bind(this)).on('dragend', this.onHandleDragEnd.bind(this)).on('tap', this.onHandleClick.bind(this));
  106. $(e).on('keydown', this.onHandleKeyDown.bind(this));
  107. }.bind(this));
  108. };
  109. Splitter.prototype.preventPageScrollDrag = function preventPageScrollDrag(evt) {
  110. if (this.isDragStartCalled) {
  111. evt.preventDefault();
  112. }
  113. };
  114. Splitter.prototype._setupEvent = function _setupEvent() {
  115. //This class does not own the items views so no need to unregister handlers them during remove since the views unregister handlers themselves when they get remove
  116. (this.items || []).forEach(function (item, index) {
  117. var _this2 = this;
  118. var openPaneAction = this.toggleItem.bind(this, index, true);
  119. item.view.on('openPane', function () {
  120. openPaneAction();
  121. });
  122. item.view.on('closePane', function () {
  123. // TODO: stub, implement as needed
  124. });
  125. item.view.on('hidePane', function () {
  126. // TODO: stub, implement as needed
  127. });
  128. item.view.on('showPane', function () {
  129. // TODO: stub, implement as needed
  130. });
  131. item.view.on('disableHandle', function () {
  132. _this2._toggleItemHandle(index, true);
  133. });
  134. item.view.on('enableHandle', function () {
  135. _this2._toggleItemHandle(index, false);
  136. });
  137. // disableAndSwitchHandle is to be used when we want to automatically
  138. // switch off the current handle to the next or previous one
  139. // If we do switch we save where we came from to restore if/when
  140. // the 'restoreHandle' event is triggered
  141. item.view.on('disableAndSwitchHandle', function () {
  142. _this2._preservedIndex = null;
  143. item.view.trigger('disableHandle');
  144. var $handle = _this2.$handles.eq(index);
  145. if ($handle.hasClass('selected')) {
  146. $handle.removeClass('selected');
  147. // Find next viable handle to switch to here
  148. var nextHandleIndex = _this2._getNextAvailableHandle(index);
  149. _this2.toggleItem(nextHandleIndex, true);
  150. // preserve the handle index the user was on to automatically put them back if required
  151. _this2._preservedIndex = index;
  152. }
  153. });
  154. // re-enable the handle that was disabled and if there wasn't a use action
  155. // on the splitter handles between the 'disableAndSwitchHandle' and
  156. // 'restoreHandle' events then we set the user back to the preserved
  157. // index handle automatically.
  158. item.view.on('restoreHandle', function () {
  159. item.view.trigger('enableHandle');
  160. if (_this2._preservedIndex === index) {
  161. _this2.toggleItem(_this2._preservedIndex, true);
  162. _this2._preservedIndex = null;
  163. }
  164. });
  165. }, this);
  166. };
  167. Splitter.prototype._shouldHideHandle = function _shouldHideHandle() {
  168. return this.items && this.items.length === 1;
  169. };
  170. /**
  171. * @param {number} index An number value relating to the position the elemtn is in the DOM relative to its parent element
  172. * @param {boolean} [state=false] A value to pass to the jquery 'toggleClass' function. Used to determine whether the class
  173. * should be added or removed. Default set to false in case it is called in error we don't deny user access to parts of the UI
  174. */
  175. Splitter.prototype._toggleItemHandle = function _toggleItemHandle(index) {
  176. var state = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  177. this.$handles.eq(index).toggleClass('disabled blockOnExpandView', state);
  178. };
  179. Splitter.prototype._getNextAvailableHandle = function _getNextAvailableHandle(index) {
  180. if (index < 0) {
  181. index = 0;
  182. } else if (index >= this.items.length) {
  183. index = this.items.length - 1;
  184. }
  185. return index < this.items.length - 1 ? index + 1 : index - 1;
  186. };
  187. Splitter.prototype.getOverrideGlobalMaxSize = function getOverrideGlobalMaxSize(items) {
  188. var overrideGlobalMaxSize = Infinity;
  189. (items || []).forEach(function (item) {
  190. if (item.overrideGlobalMaxSize) {
  191. if (overrideGlobalMaxSize !== Infinity) {
  192. console.warn('Multiple modules are trying to override the global max splitter size.');
  193. }
  194. overrideGlobalMaxSize = item.overrideGlobalMaxSize;
  195. }
  196. }, this);
  197. return overrideGlobalMaxSize;
  198. };
  199. Splitter.prototype.remove = function remove() {
  200. //@todo jQuery does not seem to work with remove event listener here
  201. $(window).off('resize.splitterResize' + this.viewId);
  202. if (BrowserUtils.isIPad()) {
  203. //Add a touchmove handler to the document on iPad to kill default page scroll when dragging.
  204. document.removeEventListener('touchmove', this.preventPageScrollDrag.bind(this), { passive: false });
  205. }
  206. var sizeHandles = this.$splitterBar.find('.handle, .splitterSizeHandle');
  207. if (sizeHandles instanceof $ && sizeHandles.length > 0) {
  208. // Be aware of that sizeHandles is a JQuery object. It's not safe to use `for...of...`. `.each()` or `jQuery.each` would be safer.
  209. for (var i = 0; i < sizeHandles.length; i++) {
  210. var $handle = $(sizeHandles[i]);
  211. $handle.off('dragstart dragup dragdown dragend tap keydown');
  212. }
  213. }
  214. this.$el.children().removeClass('splitterPane');
  215. $('.splitterBar', this.$el).remove();
  216. this.content = null;
  217. this.handlers = null;
  218. this.items = null;
  219. this.panel = null;
  220. this.$splitterBar = null;
  221. //This view does not own this DOM node so just set it to null here
  222. //Because this don't call the base class remove since this base will invoke this.$el.remove() which is not what this
  223. //view wants since it does not not the this.$el node
  224. this.$el = null;
  225. if (this.off) {
  226. this.off();
  227. }
  228. };
  229. /**
  230. * Splitter bar click handler
  231. *
  232. * @param evt
  233. */
  234. Splitter.prototype.onHandleClick = function onHandleClick(evt) {
  235. var _this3 = this;
  236. var result = void 0;
  237. if (!this._isAnimating) {
  238. var index = 0;
  239. if (!this._shouldHideHandle()) {
  240. index = this.getHandleIndexFromNode($(evt.currentTarget));
  241. }
  242. result = this.toggleItem(index).then(function () {
  243. if (_this3.currentIndex != null) {
  244. _this3.items[_this3.currentIndex].view.trigger('splitterPanel:visibleChange');
  245. }
  246. });
  247. }
  248. return result ? result : Promise.resolve();
  249. };
  250. /**
  251. * Splitter bar keydown handler
  252. *
  253. * @param evt
  254. */
  255. Splitter.prototype.onHandleKeyDown = function onHandleKeyDown(evt) {
  256. var _map = {
  257. 13: this.onHandleClick.bind(this, evt),
  258. 40: this._handleUpDownKey.bind(this, evt, 1),
  259. 38: this._handleUpDownKey.bind(this, evt, -1)
  260. };
  261. var result;
  262. if (evt.keyCode in _map) {
  263. result = _map[evt.keyCode].call(this);
  264. }
  265. return result ? result : Promise.resolve();
  266. };
  267. Splitter.prototype._handleUpDownKey = function _handleUpDownKey(event, direction) {
  268. event.stopPropagation();
  269. var sizePropName = this._getSizePropName();
  270. this.prevSize = this.$splitterBar.prev()[sizePropName]();
  271. this.nextSize = this.panel[sizePropName]();
  272. this._adjustSize(direction, 10, this.currentIndex);
  273. };
  274. Splitter.prototype.isOpen = function isOpen() {
  275. if (this.$splitterBar && this.$splitterBar.hasClass) {
  276. return this.$splitterBar.hasClass('open');
  277. }
  278. return false;
  279. };
  280. /**
  281. * Toggle the specified handle open or closed.
  282. *
  283. * @param index - the index of the handle
  284. * @param toggle - an optional value of whether to show or hide the pane. Leaving this parameter blank will
  285. * simply toggle the pane between show or hide.
  286. * @param options - an optional object to carry extra options.
  287. */
  288. Splitter.prototype.toggleItem = function toggleItem(index, toggle, options) {
  289. var _this4 = this;
  290. // Do nothing when the index is out of bounds.
  291. if (index < 0 || index >= this.items.length) {
  292. return Promise.resolve(false);
  293. }
  294. var isOpen = this.isOpen();
  295. if (toggle === undefined) {
  296. if (index === this.currentIndex) {
  297. // Only toggle the current index.
  298. toggle = !isOpen;
  299. } else {
  300. // Always expand.
  301. toggle = true;
  302. }
  303. }
  304. var height = this._getHeight(index, toggle, isOpen, options);
  305. if (height <= 0 && this.items[index]) {
  306. this.items[index].view.trigger('splitterPanel:hide', options);
  307. }
  308. this.$handles.each(function (index, value) {
  309. $(value).attr('title', this.items[index] && this.items[index].handleOpenTitle);
  310. $(value).attr('aria-label', this.items[index] && this.items[index].handleOpenTitle);
  311. }.bind(this));
  312. if (height > 0) {
  313. var handle = this.$handles.eq(index);
  314. handle.attr('title', this.items[index].handleCloseTitle);
  315. handle.attr('aria-label', this.items[index].handleCloseTitle);
  316. } else {
  317. this.$handles.eq(index).removeClass('selected');
  318. this.currentIndex = null;
  319. this._preservedIndex = null;
  320. }
  321. this._isAnimating = true;
  322. this.$splitterBar.addClass('animating');
  323. return this.setPaneSize(height).finally(function () {
  324. _this4.$splitterBar.removeClass('animating');
  325. _this4._isAnimating = false;
  326. return height;
  327. });
  328. };
  329. Splitter.prototype._getHeight = function _getHeight(index, toggle, isOpen, options) {
  330. var height;
  331. if (toggle && index !== null) {
  332. if (isOpen && this.items[this.currentIndex]) {
  333. height = Math.max(this.items[this.currentIndex].size, this.items[index].view.minimumSize || -8);
  334. } else {
  335. height = this.items[index].size || this.items[index].initialSize;
  336. }
  337. // If no height is given, use max height.
  338. if (!height) {
  339. height = '100%';
  340. }
  341. //use the global override if the size hasn't been set by tbe user
  342. if (this.overrideGlobalMaxSize !== Infinity && !this.items[index].size) {
  343. height = this.overrideGlobalMaxSize;
  344. }
  345. // Toggling on, load the correct panel.
  346. this.selectHandle(index, options);
  347. } else {
  348. height = 0;
  349. }
  350. return height;
  351. };
  352. /**
  353. * Set the pane size
  354. *
  355. * @param $splitterBar - splitter bar that controls the pane
  356. * @param size
  357. */
  358. Splitter.prototype.setPaneSize = function setPaneSize(size) {
  359. var _this5 = this;
  360. var sizePropName = this._getSizePropName();
  361. if (size !== '100%' && !!this.isRestrictToParentSize) {
  362. var parentSize = this.handlers.getParentSize();
  363. size = Math.round(Math.min(size, parentSize[sizePropName]));
  364. }
  365. var open = parseInt(size, 10) > 0;
  366. var animation = {};
  367. animation[sizePropName] = size;
  368. return new Promise(function (resolve, reject) {
  369. try {
  370. _this5.panel.animate(animation, 300, function () {
  371. try {
  372. if (!open) {
  373. _this5.$splitterBar.toggleClass('open', false);
  374. } else {
  375. _this5.$splitterBar.toggleClass('open', true);
  376. if (_this5.items[_this5.currentIndex]) {
  377. // Store this height.
  378. _this5.items[_this5.currentIndex].size = size;
  379. }
  380. }
  381. $(window).resize();
  382. } catch (err) {
  383. console.error(err);
  384. }
  385. resolve(size);
  386. });
  387. } catch (error) {
  388. reject(error);
  389. }
  390. });
  391. };
  392. Splitter.prototype.selectHandle = function selectHandle(index, options) {
  393. if (this.currentIndex !== index) {
  394. this.loadPanel(index);
  395. var $handles = this.$splitterBar.find('.handle');
  396. var $handle = $handles.eq(index);
  397. if (!$handle.hasClass('selected')) {
  398. $handles.removeClass('selected');
  399. $handle.addClass('selected');
  400. this._preservedIndex = null;
  401. if (this.items[index] && this.items[index].view) {
  402. this.items[index].view.trigger('splitterPanel:show', options);
  403. }
  404. }
  405. }
  406. };
  407. Splitter.prototype.loadPanel = function loadPanel(index) {
  408. this.currentIndex = index;
  409. var panel = this.items[index];
  410. var panelView = panel.view;
  411. if (!this.renderedMap[index]) {
  412. panelView.render();
  413. panelView.$el.appendTo(this.panel);
  414. this.renderedMap[index] = true;
  415. } else {
  416. panelView.$el.css('visibility', 'visible');
  417. panelView.$el.css('z-index', '1');
  418. if (this.$splitterBar.hasClass('open')) {
  419. panelView.$el.css('opacity', 0);
  420. panelView.$el.animate({
  421. opacity: 1
  422. }, 300);
  423. } else {
  424. this.panel.children().not(panelView.$el).css('visibility', 'hidden');
  425. panelView.$el.css('opacity', 1);
  426. }
  427. }
  428. var children = this.panel.children().not(panelView.$el);
  429. children.animate({
  430. opacity: 0
  431. }, 300, function () {
  432. children.css('visibility', 'hidden');
  433. children.css('z-index', '-1');
  434. });
  435. };
  436. /**
  437. * @return The gripper handle index that matches the selected node
  438. */
  439. Splitter.prototype.getHandleIndexFromNode = function getHandleIndexFromNode(node) {
  440. return this.$splitterBar.find('.handle').index(node);
  441. };
  442. /**
  443. * Splitter bar dragStart handler
  444. * @param evt
  445. */
  446. Splitter.prototype.onHandleDragStart = function onHandleDragStart(evt) {
  447. this.isDragStartCalled = true;
  448. this.$el.addClass('resizing');
  449. var sizePropName = this._getSizePropName();
  450. this.prevSize = this.$splitterBar.prev()[sizePropName]();
  451. this.nextSize = this.panel[sizePropName]();
  452. // Sometimes the hammer event doesn't have a gesture member.
  453. if (evt.gesture) {
  454. evt.gesture.preventDefault();
  455. }
  456. var index = this.getHandleIndexFromNode($(evt.currentTarget));
  457. if (this._shouldHideHandle()) {
  458. index = 0;
  459. }
  460. if (index < 0 || index >= this.items.length) {
  461. index = this.currentIndex;
  462. }
  463. this.selectHandle(index);
  464. if (!this.$splitterBar.hasClass('open')) {
  465. this.$splitterBar.toggleClass('open', true);
  466. }
  467. };
  468. /**
  469. * Splitter bar drag up/down handler
  470. * @param evt
  471. */
  472. Splitter.prototype.onHandleDragDownUp = function onHandleDragDownUp(evt) {
  473. var isMovingBackward = evt.gesture.direction === 'up' || evt.gesture.direction === 'left';
  474. var direction = isMovingBackward ? -1 : 1;
  475. var index = this.getHandleIndexFromNode($(evt.currentTarget));
  476. if (this._shouldHideHandle()) {
  477. index = 0;
  478. }
  479. if (index < 0 || index >= this.items.length) {
  480. this._adjustSize(direction, evt.gesture.distance, this.currentIndex);
  481. } else {
  482. this._adjustSize(direction, evt.gesture.distance, index);
  483. }
  484. this.lastDragEvent = evt;
  485. };
  486. Splitter.prototype._adjustSize = function _adjustSize(direction, distance) {
  487. var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  488. if (!this.isOpen() && (direction === 1 || direction === -1)) {
  489. return;
  490. }
  491. var sizePropName = this._getSizePropName();
  492. var newPosition = this.nextSize - direction * distance;
  493. if (newPosition < this._calculateSizeLimit() && newPosition > 0) {
  494. //241557 using keyboard, navigating to data tray and using up/down keys to adjust height, results in blank data tray
  495. this.selectHandle(index);
  496. this.panel.css(sizePropName, Math.round(newPosition));
  497. } else if (newPosition <= 0 && !this._isAnimating) {
  498. this.close();
  499. }
  500. // RTC issue #50066 - this next line is needed to prevent a Firefox/JQuery issue where the splitter jumps to the top of the page when dragging.
  501. this.panel.css(sizePropName);
  502. $(window).resize();
  503. };
  504. Splitter.prototype._calculateSizeLimit = function _calculateSizeLimit() {
  505. var parentSize = this.handlers.getParentSize();
  506. var containerHeight = parentSize.height;
  507. var limit = containerHeight - 160; // FIXME: where does this magical number come from?
  508. return limit;
  509. };
  510. Splitter.prototype.onResize = function onResize() {
  511. if (this.isOpen()) {
  512. var sizePropName = this._getSizePropName();
  513. var sizeLimit = this._calculateSizeLimit();
  514. if (this.panel[sizePropName]() > sizeLimit) {
  515. this.panel.css(sizePropName, Math.round(sizeLimit));
  516. }
  517. }
  518. };
  519. /**
  520. * Splitter bar drag end handler
  521. * @param evt
  522. */
  523. Splitter.prototype.onHandleDragEnd = function onHandleDragEnd() {
  524. this.$el.removeClass('resizing');
  525. var sizePropName = this._getSizePropName();
  526. if (this.lastDragEvent && !this._isAnimating) {
  527. var gesture = this.lastDragEvent.gesture;
  528. var isResizingForward = gesture.direction === 'down' || gesture.direction === 'right';
  529. var velocity = gesture.velocityY;
  530. var isSwipe = this.enableSwipe && velocity > 1.5;
  531. if (isSwipe) {
  532. this._handleSwipe(this.$splitterBar, isResizingForward);
  533. } else {
  534. this._handleMinSize(this.$splitterBar, sizePropName, isResizingForward);
  535. // Save the current height.
  536. if (this.currentIndex !== null) {
  537. this.items[this.currentIndex].size = this.panel.height();
  538. if (this.items[this.currentIndex].size > 0) {
  539. this.items[this.currentIndex].view.trigger('splitterPanel:visibleChange');
  540. }
  541. }
  542. }
  543. }
  544. this.isDragStartCalled = false;
  545. };
  546. Splitter.prototype._handleSwipe = function _handleSwipe($splitterBar, isResizingForward) {
  547. this.setPaneSize($splitterBar, isResizingForward ? 0 : '100%');
  548. };
  549. Splitter.prototype._handleMinSize = function _handleMinSize($splitterBar, sizePropName, isResizingForward) {
  550. var _minSize = typeof this.minSize === 'function' ? this.minSize() : this.minSize;
  551. if ($splitterBar.prev()[sizePropName]() < _minSize) {
  552. // if previous pane ends up being less than our minimun size
  553. // set it to 0 if we are expanding 'next' pane or set it to minSize if we are reducing 'next' pane
  554. var parentSize = this.handlers.getParentSize();
  555. this.setPaneSize($splitterBar, isResizingForward ? Math.round(parentSize[sizePropName] - _minSize) : '100%');
  556. } else if (this.panel[sizePropName]() < _minSize) {
  557. // Pane is less than _minSize - if we are expanding the pane, set it to _minSize, otherwise we collapse it.
  558. this.setPaneSize($splitterBar, isResizingForward ? 0 : _minSize);
  559. } else {
  560. var size = this.panel.css(sizePropName);
  561. $splitterBar.toggleClass('open', parseInt(size, 10) > 1);
  562. }
  563. };
  564. Splitter.prototype._getSizePropName = function _getSizePropName() {
  565. return 'height';
  566. };
  567. Splitter.prototype.open = function open() {
  568. return this.toggleItem(this.currentIndex, true);
  569. };
  570. Splitter.prototype.close = function close() {
  571. var item = this.items[this.currentIndex];
  572. if (item) {
  573. item.size = 0;
  574. }
  575. return this.toggleItem(this.currentIndex, false);
  576. };
  577. Splitter.prototype.hideHandle = function hideHandle(handleClass) {
  578. var $handle = this.$splitterBar.find('.' + handleClass);
  579. var $splitterSizeHandle = this.$splitterBar.find('.splitterSizeHandle');
  580. if (!this._shouldHideHandle()) {
  581. $handle.hide();
  582. $handle.attr('tabindex', '-1');
  583. } else {
  584. $splitterSizeHandle.attr('tabindex', '-1');
  585. }
  586. var item = _.findWhere(this.items, { handleClass: handleClass }) || {};
  587. item.hidden = true;
  588. var visibleItems = _.filter(this.items, function (item) {
  589. return !item.hidden;
  590. });
  591. if (visibleItems.length === 0) {
  592. $splitterSizeHandle.hide();
  593. }
  594. };
  595. Splitter.prototype.hide = function hide() {
  596. var _this6 = this;
  597. return new Promise(function (resolve, reject) {
  598. try {
  599. _this6.$splitterBar.fadeOut(250, function () {
  600. resolve();
  601. });
  602. } catch (error) {
  603. reject(error);
  604. }
  605. });
  606. };
  607. Splitter.prototype.showHandle = function showHandle(handleClass) {
  608. var $splitterSizeHandle = this.$splitterBar.find('.splitterSizeHandle');
  609. $splitterSizeHandle.show();
  610. var $handle = this.$splitterBar.find('.' + handleClass);
  611. if (!this._shouldHideHandle()) {
  612. $handle.show();
  613. $handle.attr('tabindex', '0');
  614. } else {
  615. $splitterSizeHandle.attr('tabindex', '0');
  616. }
  617. var item = _.findWhere(this.items, { handleClass: handleClass }) || {};
  618. item.hidden = false;
  619. };
  620. Splitter.prototype.show = function show() {
  621. var _this7 = this;
  622. return new Promise(function (resolve, reject) {
  623. try {
  624. _this7.$splitterBar.fadeIn(250, function () {
  625. resolve();
  626. });
  627. } catch (error) {
  628. reject(error);
  629. }
  630. });
  631. };
  632. return Splitter;
  633. }(View);
  634. /**
  635. * A jQuery function that can be used to enable a splitter pane
  636. */
  637. $.fn.splitter = function (options) {
  638. this.destroySplitter();
  639. this.data('splitter', new Splitter(_.extend({ $el: this }, options)));
  640. return this;
  641. };
  642. /**
  643. * A jQuery function that can be destroy to remove a splitter pane
  644. */
  645. $.fn.destroySplitter = function () {
  646. var splitter = this.data('splitter');
  647. if (splitter) {
  648. splitter.remove();
  649. this.data('splitter', null);
  650. }
  651. return this;
  652. };
  653. return Splitter;
  654. });
  655. //# sourceMappingURL=Splitter.js.map