slide_out_view.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /**
  2. * DevExtreme (ui/slide_out_view.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 $ = require("../core/renderer");
  11. var eventsEngine = require("../events/core/events_engine");
  12. var noop = require("../core/utils/common").noop;
  13. var fx = require("../animation/fx");
  14. var clickEvent = require("../events/click");
  15. var translator = require("../animation/translator");
  16. var getPublicElement = require("../core/utils/dom").getPublicElement;
  17. var hideTopOverlayCallback = require("../mobile/hide_top_overlay").hideCallback;
  18. var registerComponent = require("../core/component_registrator");
  19. var extend = require("../core/utils/extend").extend;
  20. var AsyncTemplateMixin = require("./shared/async_template_mixin");
  21. var Widget = require("./widget/ui.widget");
  22. var Swipeable = require("../events/gesture/swipeable");
  23. var EmptyTemplate = require("./widget/empty_template");
  24. var Deferred = require("../core/utils/deferred").Deferred;
  25. var windowUtils = require("../core/utils/window");
  26. var SLIDEOUTVIEW_CLASS = "dx-slideoutview";
  27. var SLIDEOUTVIEW_WRAPPER_CLASS = "dx-slideoutview-wrapper";
  28. var SLIDEOUTVIEW_MENU_CONTENT_CLASS = "dx-slideoutview-menu-content";
  29. var SLIDEOUTVIEW_CONTENT_CLASS = "dx-slideoutview-content";
  30. var SLIDEOUTVIEW_SHIELD_CLASS = "dx-slideoutview-shield";
  31. var INVISIBLE_STATE_CLASS = "dx-state-invisible";
  32. var ANONYMOUS_TEMPLATE_NAME = "content";
  33. var ANIMATION_DURATION = 400;
  34. var animation = {
  35. moveTo: function($element, position, completeAction) {
  36. fx.animate($element, {
  37. type: "slide",
  38. to: {
  39. left: position
  40. },
  41. duration: ANIMATION_DURATION,
  42. complete: completeAction
  43. })
  44. },
  45. complete: function($element) {
  46. fx.stop($element, true)
  47. }
  48. };
  49. var SlideOutView = Widget.inherit({
  50. _getDefaultOptions: function() {
  51. return extend(this.callBase(), {
  52. menuPosition: "normal",
  53. menuVisible: false,
  54. swipeEnabled: true,
  55. menuTemplate: "menu",
  56. contentTemplate: "content",
  57. contentOffset: 45
  58. })
  59. },
  60. _defaultOptionsRules: function() {
  61. return this.callBase().concat([{
  62. device: {
  63. android: true
  64. },
  65. options: {
  66. contentOffset: 54
  67. }
  68. }, {
  69. device: function(_device) {
  70. return "generic" === _device.platform && "desktop" !== _device.deviceType
  71. },
  72. options: {
  73. contentOffset: 56
  74. }
  75. }, {
  76. device: {
  77. win: true,
  78. phone: false
  79. },
  80. options: {
  81. contentOffset: 76
  82. }
  83. }])
  84. },
  85. _getAnonymousTemplateName: function() {
  86. return ANONYMOUS_TEMPLATE_NAME
  87. },
  88. _init: function() {
  89. this.callBase();
  90. this.$element().addClass(SLIDEOUTVIEW_CLASS);
  91. this._whenAnimationComplete = void 0;
  92. this._whenMenuRendered = void 0;
  93. this._initHideTopOverlayHandler()
  94. },
  95. _initHideTopOverlayHandler: function() {
  96. this._hideMenuHandler = this.hideMenu.bind(this)
  97. },
  98. _initTemplates: function() {
  99. this.callBase();
  100. this._defaultTemplates.menu = new EmptyTemplate(this);
  101. this._defaultTemplates.content = new EmptyTemplate(this)
  102. },
  103. _initMarkup: function() {
  104. var _this = this;
  105. this.callBase();
  106. this._renderMarkup();
  107. this._whenMenuRendered = new Deferred;
  108. var menuTemplate = this._getTemplate(this.option("menuTemplate"));
  109. menuTemplate && menuTemplate.render({
  110. container: this.menuContent(),
  111. onRendered: function() {
  112. _this._whenMenuRendered.resolve()
  113. }
  114. });
  115. var contentTemplateOption = this.option("contentTemplate");
  116. var contentTemplate = this._getTemplate(contentTemplateOption);
  117. var transclude = this._getAnonymousTemplateName() === contentTemplateOption;
  118. contentTemplate && contentTemplate.render({
  119. container: this.content(),
  120. noModel: true,
  121. transclude: transclude
  122. });
  123. this._renderShield();
  124. this._toggleMenuPositionClass()
  125. },
  126. _render: function() {
  127. var _this2 = this;
  128. this.callBase();
  129. this._whenMenuRendered.always(function() {
  130. _this2._initSwipeHandlers();
  131. _this2._dimensionChanged()
  132. })
  133. },
  134. _renderMarkup: function() {
  135. var $wrapper = $("<div>").addClass(SLIDEOUTVIEW_WRAPPER_CLASS);
  136. this._$menu = $("<div>").addClass(SLIDEOUTVIEW_MENU_CONTENT_CLASS);
  137. this._$container = $("<div>").addClass(SLIDEOUTVIEW_CONTENT_CLASS);
  138. $wrapper.append(this._$menu);
  139. $wrapper.append(this._$container);
  140. this.$element().append($wrapper);
  141. eventsEngine.on(this._$container, "MSPointerDown", noop)
  142. },
  143. _renderShield: function() {
  144. this._$shield = this._$shield || $("<div>").addClass(SLIDEOUTVIEW_SHIELD_CLASS);
  145. this._$shield.appendTo(this.content());
  146. eventsEngine.off(this._$shield, clickEvent.name);
  147. eventsEngine.on(this._$shield, clickEvent.name, this.hideMenu.bind(this));
  148. this._toggleShieldVisibility(this.option("menuVisible"))
  149. },
  150. _initSwipeHandlers: function() {
  151. this._createComponent($(this.content()), Swipeable, {
  152. disabled: !this.option("swipeEnabled"),
  153. elastic: false,
  154. itemSizeFunc: this._getMenuWidth.bind(this),
  155. onStart: this._swipeStartHandler.bind(this),
  156. onUpdated: this._swipeUpdateHandler.bind(this),
  157. onEnd: this._swipeEndHandler.bind(this)
  158. })
  159. },
  160. _isRightMenuPosition: function() {
  161. var invertedPosition = "inverted" === this.option("menuPosition");
  162. var rtl = this.option("rtlEnabled");
  163. return rtl && !invertedPosition || !rtl && invertedPosition
  164. },
  165. _swipeStartHandler: function(e) {
  166. animation.complete($(this.content()));
  167. var event = e.event;
  168. var menuVisible = this.option("menuVisible");
  169. var rtl = this._isRightMenuPosition();
  170. event.maxLeftOffset = +(rtl ? !menuVisible : menuVisible);
  171. event.maxRightOffset = +(rtl ? menuVisible : !menuVisible);
  172. this._toggleShieldVisibility(true)
  173. },
  174. _swipeUpdateHandler: function(e) {
  175. var event = e.event;
  176. var offset = this.option("menuVisible") ? event.offset + 1 * this._getRTLSignCorrection() : event.offset;
  177. offset *= this._getRTLSignCorrection();
  178. this._renderPosition(offset, false)
  179. },
  180. _swipeEndHandler: function(e) {
  181. var targetOffset = e.event.targetOffset * this._getRTLSignCorrection() + this.option("menuVisible");
  182. var menuVisible = 0 !== targetOffset;
  183. if (this.option("menuVisible") === menuVisible) {
  184. this._renderPosition(this.option("menuVisible"), true)
  185. } else {
  186. this.option("menuVisible", menuVisible)
  187. }
  188. },
  189. _toggleMenuPositionClass: function() {
  190. var left = SLIDEOUTVIEW_CLASS + "-left";
  191. var right = SLIDEOUTVIEW_CLASS + "-right";
  192. var menuPosition = this._isRightMenuPosition() ? "right" : "left";
  193. this._$menu.removeClass(left + " " + right);
  194. this._$menu.addClass(SLIDEOUTVIEW_CLASS + "-" + menuPosition)
  195. },
  196. _renderPosition: function(offset, animate) {
  197. if (!windowUtils.hasWindow()) {
  198. return
  199. }
  200. var pos = this._calculatePixelOffset(offset) * this._getRTLSignCorrection();
  201. this._toggleHideMenuCallback(offset);
  202. if (animate) {
  203. this._toggleShieldVisibility(true);
  204. animation.moveTo($(this.content()), pos, this._animationCompleteHandler.bind(this))
  205. } else {
  206. translator.move($(this.content()), {
  207. left: pos
  208. })
  209. }
  210. },
  211. _calculatePixelOffset: function(offset) {
  212. offset = offset || 0;
  213. return offset * this._getMenuWidth()
  214. },
  215. _getMenuWidth: function() {
  216. if (!this._menuWidth) {
  217. var maxMenuWidth = this.$element().width() - this.option("contentOffset");
  218. var menuContent = $(this.menuContent());
  219. menuContent.css("maxWidth", maxMenuWidth < 0 ? 0 : maxMenuWidth);
  220. var currentMenuWidth = menuContent.width();
  221. this._menuWidth = Math.min(currentMenuWidth, maxMenuWidth)
  222. }
  223. return this._menuWidth
  224. },
  225. _animationCompleteHandler: function() {
  226. this._toggleShieldVisibility(this.option("menuVisible"));
  227. if (this._whenAnimationComplete) {
  228. this._whenAnimationComplete.resolveWith(this)
  229. }
  230. },
  231. _toggleHideMenuCallback: function(subscribe) {
  232. if (subscribe) {
  233. hideTopOverlayCallback.add(this._hideMenuHandler)
  234. } else {
  235. hideTopOverlayCallback.remove(this._hideMenuHandler)
  236. }
  237. },
  238. _getRTLSignCorrection: function() {
  239. return this._isRightMenuPosition() ? -1 : 1
  240. },
  241. _dispose: function() {
  242. animation.complete($(this.content()));
  243. this._toggleHideMenuCallback(false);
  244. this.callBase()
  245. },
  246. _visibilityChanged: function(visible) {
  247. if (visible) {
  248. this._dimensionChanged()
  249. }
  250. },
  251. _dimensionChanged: function() {
  252. delete this._menuWidth;
  253. this._renderPosition(this.option("menuVisible"), false)
  254. },
  255. _toggleShieldVisibility: function(visible) {
  256. this._$shield.toggleClass(INVISIBLE_STATE_CLASS, !visible)
  257. },
  258. _optionChanged: function(args) {
  259. switch (args.name) {
  260. case "width":
  261. this.callBase(args);
  262. this._dimensionChanged();
  263. break;
  264. case "contentOffset":
  265. this._dimensionChanged();
  266. break;
  267. case "menuVisible":
  268. this._renderPosition(args.value, true);
  269. break;
  270. case "menuPosition":
  271. this._renderPosition(this.option("menuVisible"), true);
  272. this._toggleMenuPositionClass();
  273. break;
  274. case "swipeEnabled":
  275. this._initSwipeHandlers();
  276. break;
  277. case "contentTemplate":
  278. case "menuTemplate":
  279. this._invalidate();
  280. break;
  281. default:
  282. this.callBase(args)
  283. }
  284. },
  285. menuContent: function() {
  286. return getPublicElement(this._$menu)
  287. },
  288. content: function() {
  289. return getPublicElement(this._$container)
  290. },
  291. showMenu: function() {
  292. return this.toggleMenuVisibility(true)
  293. },
  294. hideMenu: function() {
  295. return this.toggleMenuVisibility(false)
  296. },
  297. toggleMenuVisibility: function(showing) {
  298. showing = void 0 === showing ? !this.option("menuVisible") : showing;
  299. this._whenAnimationComplete = new Deferred;
  300. this.option("menuVisible", showing);
  301. return this._whenAnimationComplete.promise()
  302. }
  303. }).include(AsyncTemplateMixin);
  304. registerComponent("dxSlideOutView", SlideOutView);
  305. module.exports = SlideOutView;
  306. module.exports.default = module.exports;