defer_rendering.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /**
  2. * DevExtreme (ui/defer_rendering.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 domAdapter = require("../core/dom_adapter");
  12. var windowUtils = require("../core/utils/window");
  13. var window = windowUtils.getWindow();
  14. var eventsEngine = require("../events/core/events_engine");
  15. var registerComponent = require("../core/component_registrator");
  16. var commonUtils = require("../core/utils/common");
  17. var extend = require("../core/utils/extend").extend;
  18. var each = require("../core/utils/iterator").each;
  19. var domUtils = require("../core/utils/dom");
  20. var TransitionExecutorModule = require("../animation/transition_executor/transition_executor");
  21. var Widget = require("./widget/ui.widget");
  22. var LoadIndicator = require("./load_indicator");
  23. var isPromise = require("../core/utils/type").isPromise;
  24. var deferredUtils = require("../core/utils/deferred");
  25. var Deferred = deferredUtils.Deferred;
  26. var WIDGET_CLASS = "dx-widget";
  27. var DEFER_RENDERING_CLASS = "dx-deferrendering";
  28. var PENDING_RENDERING_CLASS = "dx-pending-rendering";
  29. var PENDING_RENDERING_MANUAL_CLASS = "dx-pending-rendering-manual";
  30. var PENDING_RENDERING_ACTIVE_CLASS = "dx-pending-rendering-active";
  31. var VISIBLE_WHILE_PENDING_RENDERING_CLASS = "dx-visible-while-pending-rendering";
  32. var INVISIBLE_WHILE_PENDING_RENDERING_CLASS = "dx-invisible-while-pending-rendering";
  33. var LOADINDICATOR_CONTAINER_CLASS = "dx-loadindicator-container";
  34. var DEFER_RENDERING_LOADINDICATOR_CONTAINER_CLASS = "dx-deferrendering-loadindicator-container";
  35. var DEFER_DEFER_RENDERING_LOAD_INDICATOR = "dx-deferrendering-load-indicator";
  36. var ANONYMOUS_TEMPLATE_NAME = "content";
  37. var ACTIONS = ["onRendered", "onShown"];
  38. var DeferRendering = Widget.inherit({
  39. _getDefaultOptions: function() {
  40. return extend(this.callBase(), {
  41. showLoadIndicator: false,
  42. renderWhen: void 0,
  43. animation: void 0,
  44. staggerItemSelector: void 0,
  45. onRendered: null,
  46. onShown: null
  47. })
  48. },
  49. _getAnonymousTemplateName: function() {
  50. return ANONYMOUS_TEMPLATE_NAME
  51. },
  52. _init: function() {
  53. this.transitionExecutor = new TransitionExecutorModule.TransitionExecutor;
  54. this._initElement();
  55. this._initRender();
  56. this._$initialContent = this.$element().clone().contents();
  57. this._initActions();
  58. this.callBase()
  59. },
  60. _initElement: function() {
  61. this.$element().addClass(DEFER_RENDERING_CLASS)
  62. },
  63. _initRender: function() {
  64. var that = this;
  65. var $element = this.$element();
  66. var renderWhen = this.option("renderWhen");
  67. var doRender = function() {
  68. return that._renderDeferredContent()
  69. };
  70. if (isPromise(renderWhen)) {
  71. deferredUtils.fromPromise(renderWhen).done(doRender)
  72. } else {
  73. $element.data("dx-render-delegate", doRender);
  74. if (void 0 === renderWhen) {
  75. $element.addClass(PENDING_RENDERING_MANUAL_CLASS)
  76. }
  77. }
  78. },
  79. _initActions: function() {
  80. this._actions = {};
  81. each(ACTIONS, function(_, action) {
  82. this._actions[action] = this._createActionByOption(action) || commonUtils.noop
  83. }.bind(this))
  84. },
  85. _initMarkup: function() {
  86. this.callBase();
  87. if (!this._initContent) {
  88. this._initContent = this._renderContent;
  89. this._renderContent = function() {}
  90. }
  91. this._initContent()
  92. },
  93. _renderContentImpl: function() {
  94. this.$element().removeClass(WIDGET_CLASS);
  95. this.$element().append(this._$initialContent);
  96. this._setLoadingState()
  97. },
  98. _renderDeferredContent: function() {
  99. var that = this;
  100. var $element = this.$element();
  101. var result = new Deferred;
  102. $element.removeClass(PENDING_RENDERING_MANUAL_CLASS);
  103. $element.addClass(PENDING_RENDERING_ACTIVE_CLASS);
  104. this._abortRenderTask();
  105. this._renderTask = commonUtils.executeAsync(function() {
  106. that._renderImpl().done(function() {
  107. var shownArgs = {
  108. element: $element
  109. };
  110. that._actions.onShown([shownArgs]);
  111. result.resolve(shownArgs)
  112. }).fail(function() {
  113. result.rejectWith(result, arguments)
  114. })
  115. });
  116. return result.promise()
  117. },
  118. _isElementInViewport: function(element) {
  119. var rect = element.getBoundingClientRect();
  120. return rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || domAdapter.getDocumentElement().clientHeight) && rect.left <= (window.innerWidth || domAdapter.getDocumentElement().clientWidth)
  121. },
  122. _animate: function() {
  123. var that = this;
  124. var $element = this.$element();
  125. var animation = windowUtils.hasWindow() && this.option("animation");
  126. var staggerItemSelector = this.option("staggerItemSelector");
  127. var animatePromise;
  128. that.transitionExecutor.stop();
  129. if (animation) {
  130. if (staggerItemSelector) {
  131. $element.find(staggerItemSelector).each(function() {
  132. if (that._isElementInViewport(this)) {
  133. that.transitionExecutor.enter($(this), animation)
  134. }
  135. })
  136. } else {
  137. that.transitionExecutor.enter($element, animation)
  138. }
  139. animatePromise = that.transitionExecutor.start()
  140. } else {
  141. animatePromise = (new Deferred).resolve().promise()
  142. }
  143. return animatePromise
  144. },
  145. _renderImpl: function() {
  146. var $element = this.$element();
  147. var renderedArgs = {
  148. element: $element
  149. };
  150. var contentTemplate = this._getTemplate(this._getAnonymousTemplateName());
  151. if (contentTemplate) {
  152. contentTemplate.render({
  153. container: $element.empty(),
  154. noModel: true
  155. })
  156. }
  157. this._setRenderedState($element);
  158. eventsEngine.trigger($element, "dxcontentrendered");
  159. this._actions.onRendered([renderedArgs]);
  160. this._isRendered = true;
  161. return this._animate()
  162. },
  163. _setLoadingState: function() {
  164. var $element = this.$element();
  165. var hasCustomLoadIndicator = !!$element.find("." + VISIBLE_WHILE_PENDING_RENDERING_CLASS).length;
  166. $element.addClass(PENDING_RENDERING_CLASS);
  167. if (!hasCustomLoadIndicator) {
  168. $element.children().addClass(INVISIBLE_WHILE_PENDING_RENDERING_CLASS)
  169. }
  170. if (this.option("showLoadIndicator")) {
  171. this._showLoadIndicator($element)
  172. }
  173. },
  174. _showLoadIndicator: function($container) {
  175. this._$loadIndicator = new LoadIndicator($("<div>"), {
  176. visible: true
  177. }).$element().addClass(DEFER_DEFER_RENDERING_LOAD_INDICATOR);
  178. $("<div>").addClass(LOADINDICATOR_CONTAINER_CLASS).addClass(DEFER_RENDERING_LOADINDICATOR_CONTAINER_CLASS).append(this._$loadIndicator).appendTo($container)
  179. },
  180. _setRenderedState: function() {
  181. var $element = this.$element();
  182. if (this._$loadIndicator) {
  183. this._$loadIndicator.remove()
  184. }
  185. $element.removeClass(PENDING_RENDERING_CLASS);
  186. $element.removeClass(PENDING_RENDERING_ACTIVE_CLASS);
  187. domUtils.triggerShownEvent($element.children())
  188. },
  189. _optionChanged: function(args) {
  190. var value = args.value;
  191. var previousValue = args.previousValue;
  192. switch (args.name) {
  193. case "renderWhen":
  194. if (false === previousValue && true === value) {
  195. this._renderOrAnimate()
  196. } else {
  197. if (true === previousValue && false === value) {
  198. this.transitionExecutor.stop();
  199. this._setLoadingState()
  200. }
  201. }
  202. break;
  203. case "showLoadIndicator":
  204. case "onRendered":
  205. case "onShown":
  206. break;
  207. default:
  208. this.callBase(args)
  209. }
  210. },
  211. _renderOrAnimate: function() {
  212. var result;
  213. if (this._isRendered) {
  214. this._setRenderedState();
  215. result = this._animate()
  216. } else {
  217. result = this._renderDeferredContent()
  218. }
  219. return result
  220. },
  221. renderContent: function() {
  222. return this._renderOrAnimate()
  223. },
  224. _abortRenderTask: function() {
  225. if (this._renderTask) {
  226. this._renderTask.abort();
  227. this._renderTask = void 0
  228. }
  229. },
  230. _dispose: function() {
  231. this.transitionExecutor.stop(true);
  232. this._abortRenderTask();
  233. this._actions = void 0;
  234. this._$initialContent = void 0;
  235. this.callBase()
  236. }
  237. });
  238. registerComponent("dxDeferRendering", DeferRendering);
  239. module.exports = DeferRendering;
  240. module.exports.default = module.exports;