52066b6d7c3d7b8cc58f66a2224083fd4894db3c62c634aebe22e4c81537377c3e5ca7193d2f828968bb1372c31e04af12e941d82e0fc77b679b5238bdafb9 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var vue = require('vue');
  4. var core = require('@vueuse/core');
  5. var useCache = require('../hooks/use-cache.js');
  6. var useWheel = require('../hooks/use-wheel.js');
  7. var scrollbar = require('../components/scrollbar.js');
  8. var utils = require('../utils.js');
  9. var props = require('../props.js');
  10. var defaults = require('../defaults.js');
  11. var index = require('../../../../hooks/use-namespace/index.js');
  12. var types = require('../../../../utils/types.js');
  13. var shared = require('@vue/shared');
  14. const createList = ({
  15. name,
  16. getOffset,
  17. getItemSize,
  18. getItemOffset,
  19. getEstimatedTotalSize,
  20. getStartIndexForOffset,
  21. getStopIndexForStartIndex,
  22. initCache,
  23. clearCache,
  24. validateProps
  25. }) => {
  26. return vue.defineComponent({
  27. name: name != null ? name : "ElVirtualList",
  28. props: props.virtualizedListProps,
  29. emits: [defaults.ITEM_RENDER_EVT, defaults.SCROLL_EVT],
  30. setup(props, { emit, expose }) {
  31. validateProps(props);
  32. const instance = vue.getCurrentInstance();
  33. const ns = index.useNamespace("vl");
  34. const dynamicSizeCache = vue.ref(initCache(props, instance));
  35. const getItemStyleCache = useCache.useCache();
  36. const windowRef = vue.ref();
  37. const innerRef = vue.ref();
  38. const scrollbarRef = vue.ref();
  39. const states = vue.ref({
  40. isScrolling: false,
  41. scrollDir: "forward",
  42. scrollOffset: types.isNumber(props.initScrollOffset) ? props.initScrollOffset : 0,
  43. updateRequested: false,
  44. isScrollbarDragging: false,
  45. scrollbarAlwaysOn: props.scrollbarAlwaysOn
  46. });
  47. const itemsToRender = vue.computed(() => {
  48. const { total, cache } = props;
  49. const { isScrolling, scrollDir, scrollOffset } = vue.unref(states);
  50. if (total === 0) {
  51. return [0, 0, 0, 0];
  52. }
  53. const startIndex = getStartIndexForOffset(props, scrollOffset, vue.unref(dynamicSizeCache));
  54. const stopIndex = getStopIndexForStartIndex(props, startIndex, scrollOffset, vue.unref(dynamicSizeCache));
  55. const cacheBackward = !isScrolling || scrollDir === defaults.BACKWARD ? Math.max(1, cache) : 1;
  56. const cacheForward = !isScrolling || scrollDir === defaults.FORWARD ? Math.max(1, cache) : 1;
  57. return [
  58. Math.max(0, startIndex - cacheBackward),
  59. Math.max(0, Math.min(total - 1, stopIndex + cacheForward)),
  60. startIndex,
  61. stopIndex
  62. ];
  63. });
  64. const estimatedTotalSize = vue.computed(() => getEstimatedTotalSize(props, vue.unref(dynamicSizeCache)));
  65. const _isHorizontal = vue.computed(() => utils.isHorizontal(props.layout));
  66. const windowStyle = vue.computed(() => [
  67. {
  68. position: "relative",
  69. [`overflow-${_isHorizontal.value ? "x" : "y"}`]: "scroll",
  70. WebkitOverflowScrolling: "touch",
  71. willChange: "transform"
  72. },
  73. {
  74. direction: props.direction,
  75. height: types.isNumber(props.height) ? `${props.height}px` : props.height,
  76. width: types.isNumber(props.width) ? `${props.width}px` : props.width
  77. },
  78. props.style
  79. ]);
  80. const innerStyle = vue.computed(() => {
  81. const size = vue.unref(estimatedTotalSize);
  82. const horizontal = vue.unref(_isHorizontal);
  83. return {
  84. height: horizontal ? "100%" : `${size}px`,
  85. pointerEvents: vue.unref(states).isScrolling ? "none" : void 0,
  86. width: horizontal ? `${size}px` : "100%"
  87. };
  88. });
  89. const clientSize = vue.computed(() => _isHorizontal.value ? props.width : props.height);
  90. const { onWheel } = useWheel["default"]({
  91. atStartEdge: vue.computed(() => states.value.scrollOffset <= 0),
  92. atEndEdge: vue.computed(() => states.value.scrollOffset >= estimatedTotalSize.value),
  93. layout: vue.computed(() => props.layout)
  94. }, (offset) => {
  95. var _a, _b;
  96. (_b = (_a = scrollbarRef.value).onMouseUp) == null ? void 0 : _b.call(_a);
  97. scrollTo(Math.min(states.value.scrollOffset + offset, estimatedTotalSize.value - clientSize.value));
  98. });
  99. core.useEventListener(windowRef, "wheel", onWheel, {
  100. passive: false
  101. });
  102. const emitEvents = () => {
  103. const { total } = props;
  104. if (total > 0) {
  105. const [cacheStart, cacheEnd, visibleStart, visibleEnd] = vue.unref(itemsToRender);
  106. emit(defaults.ITEM_RENDER_EVT, cacheStart, cacheEnd, visibleStart, visibleEnd);
  107. }
  108. const { scrollDir, scrollOffset, updateRequested } = vue.unref(states);
  109. emit(defaults.SCROLL_EVT, scrollDir, scrollOffset, updateRequested);
  110. };
  111. const scrollVertically = (e) => {
  112. const { clientHeight, scrollHeight, scrollTop } = e.currentTarget;
  113. const _states = vue.unref(states);
  114. if (_states.scrollOffset === scrollTop) {
  115. return;
  116. }
  117. const scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight));
  118. states.value = {
  119. ..._states,
  120. isScrolling: true,
  121. scrollDir: utils.getScrollDir(_states.scrollOffset, scrollOffset),
  122. scrollOffset,
  123. updateRequested: false
  124. };
  125. vue.nextTick(resetIsScrolling);
  126. };
  127. const scrollHorizontally = (e) => {
  128. const { clientWidth, scrollLeft, scrollWidth } = e.currentTarget;
  129. const _states = vue.unref(states);
  130. if (_states.scrollOffset === scrollLeft) {
  131. return;
  132. }
  133. const { direction } = props;
  134. let scrollOffset = scrollLeft;
  135. if (direction === defaults.RTL) {
  136. switch (utils.getRTLOffsetType()) {
  137. case defaults.RTL_OFFSET_NAG: {
  138. scrollOffset = -scrollLeft;
  139. break;
  140. }
  141. case defaults.RTL_OFFSET_POS_DESC: {
  142. scrollOffset = scrollWidth - clientWidth - scrollLeft;
  143. break;
  144. }
  145. }
  146. }
  147. scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth));
  148. states.value = {
  149. ..._states,
  150. isScrolling: true,
  151. scrollDir: utils.getScrollDir(_states.scrollOffset, scrollOffset),
  152. scrollOffset,
  153. updateRequested: false
  154. };
  155. vue.nextTick(resetIsScrolling);
  156. };
  157. const onScroll = (e) => {
  158. vue.unref(_isHorizontal) ? scrollHorizontally(e) : scrollVertically(e);
  159. emitEvents();
  160. };
  161. const onScrollbarScroll = (distanceToGo, totalSteps) => {
  162. const offset = (estimatedTotalSize.value - clientSize.value) / totalSteps * distanceToGo;
  163. scrollTo(Math.min(estimatedTotalSize.value - clientSize.value, offset));
  164. };
  165. const scrollTo = (offset) => {
  166. offset = Math.max(offset, 0);
  167. if (offset === vue.unref(states).scrollOffset) {
  168. return;
  169. }
  170. states.value = {
  171. ...vue.unref(states),
  172. scrollOffset: offset,
  173. scrollDir: utils.getScrollDir(vue.unref(states).scrollOffset, offset),
  174. updateRequested: true
  175. };
  176. vue.nextTick(resetIsScrolling);
  177. };
  178. const scrollToItem = (idx, alignment = defaults.AUTO_ALIGNMENT) => {
  179. const { scrollOffset } = vue.unref(states);
  180. idx = Math.max(0, Math.min(idx, props.total - 1));
  181. scrollTo(getOffset(props, idx, alignment, scrollOffset, vue.unref(dynamicSizeCache)));
  182. };
  183. const getItemStyle = (idx) => {
  184. const { direction, itemSize, layout } = props;
  185. const itemStyleCache = getItemStyleCache.value(clearCache && itemSize, clearCache && layout, clearCache && direction);
  186. let style;
  187. if (shared.hasOwn(itemStyleCache, String(idx))) {
  188. style = itemStyleCache[idx];
  189. } else {
  190. const offset = getItemOffset(props, idx, vue.unref(dynamicSizeCache));
  191. const size = getItemSize(props, idx, vue.unref(dynamicSizeCache));
  192. const horizontal = vue.unref(_isHorizontal);
  193. const isRtl = direction === defaults.RTL;
  194. const offsetHorizontal = horizontal ? offset : 0;
  195. itemStyleCache[idx] = style = {
  196. position: "absolute",
  197. left: isRtl ? void 0 : `${offsetHorizontal}px`,
  198. right: isRtl ? `${offsetHorizontal}px` : void 0,
  199. top: !horizontal ? `${offset}px` : 0,
  200. height: !horizontal ? `${size}px` : "100%",
  201. width: horizontal ? `${size}px` : "100%"
  202. };
  203. }
  204. return style;
  205. };
  206. const resetIsScrolling = () => {
  207. states.value.isScrolling = false;
  208. vue.nextTick(() => {
  209. getItemStyleCache.value(-1, null, null);
  210. });
  211. };
  212. const resetScrollTop = () => {
  213. const window = windowRef.value;
  214. if (window) {
  215. window.scrollTop = 0;
  216. }
  217. };
  218. vue.onMounted(() => {
  219. if (!core.isClient)
  220. return;
  221. const { initScrollOffset } = props;
  222. const windowElement = vue.unref(windowRef);
  223. if (types.isNumber(initScrollOffset) && windowElement) {
  224. if (vue.unref(_isHorizontal)) {
  225. windowElement.scrollLeft = initScrollOffset;
  226. } else {
  227. windowElement.scrollTop = initScrollOffset;
  228. }
  229. }
  230. emitEvents();
  231. });
  232. vue.onUpdated(() => {
  233. const { direction, layout } = props;
  234. const { scrollOffset, updateRequested } = vue.unref(states);
  235. const windowElement = vue.unref(windowRef);
  236. if (updateRequested && windowElement) {
  237. if (layout === defaults.HORIZONTAL) {
  238. if (direction === defaults.RTL) {
  239. switch (utils.getRTLOffsetType()) {
  240. case defaults.RTL_OFFSET_NAG: {
  241. windowElement.scrollLeft = -scrollOffset;
  242. break;
  243. }
  244. case defaults.RTL_OFFSET_POS_ASC: {
  245. windowElement.scrollLeft = scrollOffset;
  246. break;
  247. }
  248. default: {
  249. const { clientWidth, scrollWidth } = windowElement;
  250. windowElement.scrollLeft = scrollWidth - clientWidth - scrollOffset;
  251. break;
  252. }
  253. }
  254. } else {
  255. windowElement.scrollLeft = scrollOffset;
  256. }
  257. } else {
  258. windowElement.scrollTop = scrollOffset;
  259. }
  260. }
  261. });
  262. vue.onActivated(() => {
  263. vue.unref(windowRef).scrollTop = vue.unref(states).scrollOffset;
  264. });
  265. const api = {
  266. ns,
  267. clientSize,
  268. estimatedTotalSize,
  269. windowStyle,
  270. windowRef,
  271. innerRef,
  272. innerStyle,
  273. itemsToRender,
  274. scrollbarRef,
  275. states,
  276. getItemStyle,
  277. onScroll,
  278. onScrollbarScroll,
  279. onWheel,
  280. scrollTo,
  281. scrollToItem,
  282. resetScrollTop
  283. };
  284. expose({
  285. windowRef,
  286. innerRef,
  287. getItemStyleCache,
  288. scrollTo,
  289. scrollToItem,
  290. resetScrollTop,
  291. states
  292. });
  293. return api;
  294. },
  295. render(ctx) {
  296. var _a;
  297. const {
  298. $slots,
  299. className,
  300. clientSize,
  301. containerElement,
  302. data,
  303. getItemStyle,
  304. innerElement,
  305. itemsToRender,
  306. innerStyle,
  307. layout,
  308. total,
  309. onScroll,
  310. onScrollbarScroll,
  311. states,
  312. useIsScrolling,
  313. windowStyle,
  314. ns
  315. } = ctx;
  316. const [start, end] = itemsToRender;
  317. const Container = vue.resolveDynamicComponent(containerElement);
  318. const Inner = vue.resolveDynamicComponent(innerElement);
  319. const children = [];
  320. if (total > 0) {
  321. for (let i = start; i <= end; i++) {
  322. children.push(vue.h(vue.Fragment, { key: i }, (_a = $slots.default) == null ? void 0 : _a.call($slots, {
  323. data,
  324. index: i,
  325. isScrolling: useIsScrolling ? states.isScrolling : void 0,
  326. style: getItemStyle(i)
  327. })));
  328. }
  329. }
  330. const InnerNode = [
  331. vue.h(Inner, {
  332. style: innerStyle,
  333. ref: "innerRef"
  334. }, !shared.isString(Inner) ? {
  335. default: () => children
  336. } : children)
  337. ];
  338. const scrollbar$1 = vue.h(scrollbar["default"], {
  339. ref: "scrollbarRef",
  340. clientSize,
  341. layout,
  342. onScroll: onScrollbarScroll,
  343. ratio: clientSize * 100 / this.estimatedTotalSize,
  344. scrollFrom: states.scrollOffset / (this.estimatedTotalSize - clientSize),
  345. total,
  346. alwaysOn: states.scrollbarAlwaysOn
  347. });
  348. const listContainer = vue.h(Container, {
  349. class: [ns.e("window"), className],
  350. style: windowStyle,
  351. onScroll,
  352. ref: "windowRef",
  353. key: 0
  354. }, !shared.isString(Container) ? { default: () => [InnerNode] } : [InnerNode]);
  355. return vue.h("div", {
  356. key: 0,
  357. class: [ns.e("wrapper"), states.scrollbarAlwaysOn ? "always-on" : ""]
  358. }, [listContainer, scrollbar$1]);
  359. }
  360. });
  361. };
  362. exports["default"] = createList;
  363. //# sourceMappingURL=build-list.js.map