ui.scroll_view.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /**
  2. * DevExtreme (ui/scroll_view/ui.scroll_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 devices = require("../../core/devices");
  12. var windowUtils = require("../../core/utils/window");
  13. var messageLocalization = require("../../localization/message");
  14. var registerComponent = require("../../core/component_registrator");
  15. var getPublicElement = require("../../core/utils/dom").getPublicElement;
  16. var extend = require("../../core/utils/extend").extend;
  17. var noop = require("../../core/utils/common").noop;
  18. var PullDownStrategy = require("./ui.scroll_view.native.pull_down");
  19. var SwipeDownStrategy = require("./ui.scroll_view.native.swipe_down");
  20. var SlideDownStrategy = require("./ui.scroll_view.native.slide_down");
  21. var SimulatedStrategy = require("./ui.scroll_view.simulated");
  22. var Scrollable = require("./ui.scrollable");
  23. var LoadIndicator = require("../load_indicator");
  24. var themes = require("./../themes");
  25. var LoadPanel = require("../load_panel");
  26. var SCROLLVIEW_CLASS = "dx-scrollview";
  27. var SCROLLVIEW_CONTENT_CLASS = SCROLLVIEW_CLASS + "-content";
  28. var SCROLLVIEW_TOP_POCKET_CLASS = SCROLLVIEW_CLASS + "-top-pocket";
  29. var SCROLLVIEW_BOTTOM_POCKET_CLASS = SCROLLVIEW_CLASS + "-bottom-pocket";
  30. var SCROLLVIEW_PULLDOWN_CLASS = SCROLLVIEW_CLASS + "-pull-down";
  31. var SCROLLVIEW_REACHBOTTOM_CLASS = SCROLLVIEW_CLASS + "-scrollbottom";
  32. var SCROLLVIEW_REACHBOTTOM_INDICATOR_CLASS = SCROLLVIEW_REACHBOTTOM_CLASS + "-indicator";
  33. var SCROLLVIEW_REACHBOTTOM_TEXT_CLASS = SCROLLVIEW_REACHBOTTOM_CLASS + "-text";
  34. var SCROLLVIEW_LOADPANEL = SCROLLVIEW_CLASS + "-loadpanel";
  35. var refreshStrategies = {
  36. pullDown: PullDownStrategy,
  37. swipeDown: SwipeDownStrategy,
  38. slideDown: SlideDownStrategy,
  39. simulated: SimulatedStrategy
  40. };
  41. var isServerSide = !windowUtils.hasWindow();
  42. var scrollViewServerConfig = {
  43. finishLoading: noop,
  44. release: noop,
  45. refresh: noop,
  46. _optionChanged: function(args) {
  47. if ("onUpdated" !== args.name) {
  48. return this.callBase.apply(this, arguments)
  49. }
  50. }
  51. };
  52. var ScrollView = Scrollable.inherit(isServerSide ? scrollViewServerConfig : {
  53. _getDefaultOptions: function() {
  54. return extend(this.callBase(), {
  55. pullingDownText: messageLocalization.format("dxScrollView-pullingDownText"),
  56. pulledDownText: messageLocalization.format("dxScrollView-pulledDownText"),
  57. refreshingText: messageLocalization.format("dxScrollView-refreshingText"),
  58. reachBottomText: messageLocalization.format("dxScrollView-reachBottomText"),
  59. onPullDown: null,
  60. onReachBottom: null,
  61. refreshStrategy: "pullDown"
  62. })
  63. },
  64. _defaultOptionsRules: function() {
  65. return this.callBase().concat([{
  66. device: function() {
  67. var realDevice = devices.real();
  68. return "android" === realDevice.platform
  69. },
  70. options: {
  71. refreshStrategy: "swipeDown"
  72. }
  73. }, {
  74. device: function() {
  75. return "win" === devices.real().platform
  76. },
  77. options: {
  78. refreshStrategy: "slideDown"
  79. }
  80. }, {
  81. device: function() {
  82. return themes.isMaterial()
  83. },
  84. options: {
  85. pullingDownText: "",
  86. pulledDownText: "",
  87. refreshingText: "",
  88. reachBottomText: ""
  89. }
  90. }])
  91. },
  92. _init: function() {
  93. this.callBase();
  94. this._loadingIndicatorEnabled = true
  95. },
  96. _initScrollableMarkup: function() {
  97. this.callBase();
  98. this.$element().addClass(SCROLLVIEW_CLASS);
  99. this._initContent();
  100. this._initTopPocket();
  101. this._initBottomPocket();
  102. this._initLoadPanel()
  103. },
  104. _initContent: function() {
  105. var $content = $("<div>").addClass(SCROLLVIEW_CONTENT_CLASS);
  106. this._$content.wrapInner($content)
  107. },
  108. _initTopPocket: function() {
  109. var $topPocket = this._$topPocket = $("<div>").addClass(SCROLLVIEW_TOP_POCKET_CLASS);
  110. var $pullDown = this._$pullDown = $("<div>").addClass(SCROLLVIEW_PULLDOWN_CLASS);
  111. $topPocket.append($pullDown);
  112. this._$content.prepend($topPocket)
  113. },
  114. _initBottomPocket: function() {
  115. var $bottomPocket = this._$bottomPocket = $("<div>").addClass(SCROLLVIEW_BOTTOM_POCKET_CLASS);
  116. var $reachBottom = this._$reachBottom = $("<div>").addClass(SCROLLVIEW_REACHBOTTOM_CLASS);
  117. var $loadContainer = $("<div>").addClass(SCROLLVIEW_REACHBOTTOM_INDICATOR_CLASS);
  118. var $loadIndicator = new LoadIndicator($("<div>")).$element();
  119. var $text = this._$reachBottomText = $("<div>").addClass(SCROLLVIEW_REACHBOTTOM_TEXT_CLASS);
  120. this._updateReachBottomText();
  121. $reachBottom.append($loadContainer.append($loadIndicator)).append($text);
  122. $bottomPocket.append($reachBottom);
  123. this._$content.append($bottomPocket)
  124. },
  125. _initLoadPanel: function() {
  126. var $loadPanelElement = $("<div>").addClass(SCROLLVIEW_LOADPANEL).appendTo(this.$element());
  127. var loadPanelOptions = {
  128. shading: false,
  129. delay: 400,
  130. message: this.option("refreshingText"),
  131. position: {
  132. of: this.$element()
  133. }
  134. };
  135. this._loadPanel = this._createComponent($loadPanelElement, LoadPanel, loadPanelOptions)
  136. },
  137. _updateReachBottomText: function() {
  138. this._$reachBottomText.text(this.option("reachBottomText"))
  139. },
  140. _createStrategy: function() {
  141. var strategyName = this.option("useNative") ? this.option("refreshStrategy") : "simulated";
  142. var strategyClass = refreshStrategies[strategyName];
  143. if (!strategyClass) {
  144. throw Error("E1030", this.option("refreshStrategy"))
  145. }
  146. this._strategy = new strategyClass(this);
  147. this._strategy.pullDownCallbacks.add(this._pullDownHandler.bind(this));
  148. this._strategy.releaseCallbacks.add(this._releaseHandler.bind(this));
  149. this._strategy.reachBottomCallbacks.add(this._reachBottomHandler.bind(this))
  150. },
  151. _createActions: function() {
  152. this.callBase();
  153. this._pullDownAction = this._createActionByOption("onPullDown");
  154. this._reachBottomAction = this._createActionByOption("onReachBottom");
  155. this._tryRefreshPocketState()
  156. },
  157. _tryRefreshPocketState: function() {
  158. this._pullDownEnable(this.hasActionSubscription("onPullDown"));
  159. this._reachBottomEnable(this.hasActionSubscription("onReachBottom"))
  160. },
  161. on: function(eventName) {
  162. var result = this.callBase.apply(this, arguments);
  163. if ("pullDown" === eventName || "reachBottom" === eventName) {
  164. this._tryRefreshPocketState()
  165. }
  166. return result
  167. },
  168. _pullDownEnable: function(enabled) {
  169. if (0 === arguments.length) {
  170. return this._pullDownEnabled
  171. }
  172. if (this._$pullDown && this._strategy) {
  173. this._$pullDown.toggle(enabled);
  174. this._strategy.pullDownEnable(enabled);
  175. this._pullDownEnabled = enabled
  176. }
  177. },
  178. _reachBottomEnable: function(enabled) {
  179. if (0 === arguments.length) {
  180. return this._reachBottomEnabled
  181. }
  182. if (this._$reachBottom && this._strategy) {
  183. this._$reachBottom.toggle(enabled);
  184. this._strategy.reachBottomEnable(enabled);
  185. this._reachBottomEnabled = enabled
  186. }
  187. },
  188. _pullDownHandler: function() {
  189. this._loadingIndicator(false);
  190. this._pullDownLoading()
  191. },
  192. _loadingIndicator: function(value) {
  193. if (arguments.length < 1) {
  194. return this._loadingIndicatorEnabled
  195. }
  196. this._loadingIndicatorEnabled = value
  197. },
  198. _pullDownLoading: function() {
  199. this.startLoading();
  200. this._pullDownAction()
  201. },
  202. _reachBottomHandler: function() {
  203. this._loadingIndicator(false);
  204. this._reachBottomLoading()
  205. },
  206. _reachBottomLoading: function() {
  207. this.startLoading();
  208. this._reachBottomAction()
  209. },
  210. _releaseHandler: function() {
  211. this.finishLoading();
  212. this._loadingIndicator(true)
  213. },
  214. _optionChanged: function(args) {
  215. switch (args.name) {
  216. case "onPullDown":
  217. case "onReachBottom":
  218. this._createActions();
  219. break;
  220. case "pullingDownText":
  221. case "pulledDownText":
  222. case "refreshingText":
  223. case "refreshStrategy":
  224. this._invalidate();
  225. break;
  226. case "reachBottomText":
  227. this._updateReachBottomText();
  228. break;
  229. default:
  230. this.callBase(args)
  231. }
  232. },
  233. isEmpty: function() {
  234. return !$(this.content()).children().length
  235. },
  236. content: function() {
  237. return getPublicElement(this._$content.children().eq(1))
  238. },
  239. release: function(preventReachBottom) {
  240. if (void 0 !== preventReachBottom) {
  241. this.toggleLoading(!preventReachBottom)
  242. }
  243. return this._strategy.release()
  244. },
  245. toggleLoading: function(showOrHide) {
  246. this._reachBottomEnable(showOrHide)
  247. },
  248. isFull: function() {
  249. return $(this.content()).height() > this._$container.height()
  250. },
  251. refresh: function() {
  252. if (!this.hasActionSubscription("onPullDown")) {
  253. return
  254. }
  255. this._strategy.pendingRelease();
  256. this._pullDownLoading()
  257. },
  258. startLoading: function() {
  259. if (this._loadingIndicator() && this.$element().is(":visible")) {
  260. this._loadPanel.show()
  261. }
  262. this._lock()
  263. },
  264. finishLoading: function() {
  265. this._loadPanel.hide();
  266. this._unlock()
  267. },
  268. _dispose: function() {
  269. this._strategy.dispose();
  270. this.callBase();
  271. if (this._loadPanel) {
  272. this._loadPanel.$element().remove()
  273. }
  274. }
  275. });
  276. registerComponent("dxScrollView", ScrollView);
  277. module.exports = ScrollView;