use-carousel.mjs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { getCurrentInstance, useSlots, ref, computed, unref, isVNode, watch, shallowRef, onMounted, onBeforeUnmount, provide } from 'vue';
  2. import { throttle } from 'lodash-unified';
  3. import { useResizeObserver } from '@vueuse/core';
  4. import '../../../utils/index.mjs';
  5. import '../../../hooks/index.mjs';
  6. import { carouselContextKey } from './constants.mjs';
  7. import { useOrderedChildren } from '../../../hooks/use-ordered-children/index.mjs';
  8. import { isString } from '@vue/shared';
  9. import { debugWarn } from '../../../utils/error.mjs';
  10. import { flattedChildren } from '../../../utils/vue/vnode.mjs';
  11. const THROTTLE_TIME = 300;
  12. const useCarousel = (props, emit, componentName) => {
  13. const {
  14. children: items,
  15. addChild: addItem,
  16. removeChild: removeItem
  17. } = useOrderedChildren(getCurrentInstance(), "ElCarouselItem");
  18. const slots = useSlots();
  19. const activeIndex = ref(-1);
  20. const timer = ref(null);
  21. const hover = ref(false);
  22. const root = ref();
  23. const containerHeight = ref(0);
  24. const isItemsTwoLength = ref(true);
  25. const isFirstCall = ref(true);
  26. const isTransitioning = ref(false);
  27. const arrowDisplay = computed(() => props.arrow !== "never" && !unref(isVertical));
  28. const hasLabel = computed(() => {
  29. return items.value.some((item) => item.props.label.toString().length > 0);
  30. });
  31. const isCardType = computed(() => props.type === "card");
  32. const isVertical = computed(() => props.direction === "vertical");
  33. const containerStyle = computed(() => {
  34. if (props.height !== "auto") {
  35. return {
  36. height: props.height
  37. };
  38. }
  39. return {
  40. height: `${containerHeight.value}px`,
  41. overflow: "hidden"
  42. };
  43. });
  44. const throttledArrowClick = throttle((index) => {
  45. setActiveItem(index);
  46. }, THROTTLE_TIME, { trailing: true });
  47. const throttledIndicatorHover = throttle((index) => {
  48. handleIndicatorHover(index);
  49. }, THROTTLE_TIME);
  50. const isTwoLengthShow = (index) => {
  51. if (!isItemsTwoLength.value)
  52. return true;
  53. return activeIndex.value <= 1 ? index <= 1 : index > 1;
  54. };
  55. function pauseTimer() {
  56. if (timer.value) {
  57. clearInterval(timer.value);
  58. timer.value = null;
  59. }
  60. }
  61. function startTimer() {
  62. if (props.interval <= 0 || !props.autoplay || timer.value)
  63. return;
  64. timer.value = setInterval(() => playSlides(), props.interval);
  65. }
  66. const playSlides = () => {
  67. if (!isFirstCall.value) {
  68. isTransitioning.value = true;
  69. }
  70. isFirstCall.value = false;
  71. if (activeIndex.value < items.value.length - 1) {
  72. activeIndex.value = activeIndex.value + 1;
  73. } else if (props.loop) {
  74. activeIndex.value = 0;
  75. }
  76. };
  77. function setActiveItem(index) {
  78. if (!isFirstCall.value) {
  79. isTransitioning.value = true;
  80. }
  81. isFirstCall.value = false;
  82. if (isString(index)) {
  83. const filteredItems = items.value.filter((item) => item.props.name === index);
  84. if (filteredItems.length > 0) {
  85. index = items.value.indexOf(filteredItems[0]);
  86. }
  87. }
  88. index = Number(index);
  89. if (Number.isNaN(index) || index !== Math.floor(index)) {
  90. debugWarn(componentName, "index must be integer.");
  91. return;
  92. }
  93. const itemCount = items.value.length;
  94. const oldIndex = activeIndex.value;
  95. if (index < 0) {
  96. activeIndex.value = props.loop ? itemCount - 1 : 0;
  97. } else if (index >= itemCount) {
  98. activeIndex.value = props.loop ? 0 : itemCount - 1;
  99. } else {
  100. activeIndex.value = index;
  101. }
  102. if (oldIndex === activeIndex.value) {
  103. resetItemPosition(oldIndex);
  104. }
  105. resetTimer();
  106. }
  107. function resetItemPosition(oldIndex) {
  108. items.value.forEach((item, index) => {
  109. item.translateItem(index, activeIndex.value, oldIndex);
  110. });
  111. }
  112. function itemInStage(item, index) {
  113. var _a, _b, _c, _d;
  114. const _items = unref(items);
  115. const itemCount = _items.length;
  116. if (itemCount === 0 || !item.states.inStage)
  117. return false;
  118. const nextItemIndex = index + 1;
  119. const prevItemIndex = index - 1;
  120. const lastItemIndex = itemCount - 1;
  121. const isLastItemActive = _items[lastItemIndex].states.active;
  122. const isFirstItemActive = _items[0].states.active;
  123. const isNextItemActive = (_b = (_a = _items[nextItemIndex]) == null ? void 0 : _a.states) == null ? void 0 : _b.active;
  124. const isPrevItemActive = (_d = (_c = _items[prevItemIndex]) == null ? void 0 : _c.states) == null ? void 0 : _d.active;
  125. if (index === lastItemIndex && isFirstItemActive || isNextItemActive) {
  126. return "left";
  127. } else if (index === 0 && isLastItemActive || isPrevItemActive) {
  128. return "right";
  129. }
  130. return false;
  131. }
  132. function handleMouseEnter() {
  133. hover.value = true;
  134. if (props.pauseOnHover) {
  135. pauseTimer();
  136. }
  137. }
  138. function handleMouseLeave() {
  139. hover.value = false;
  140. startTimer();
  141. }
  142. function handleTransitionEnd() {
  143. isTransitioning.value = false;
  144. }
  145. function handleButtonEnter(arrow) {
  146. if (unref(isVertical))
  147. return;
  148. items.value.forEach((item, index) => {
  149. if (arrow === itemInStage(item, index)) {
  150. item.states.hover = true;
  151. }
  152. });
  153. }
  154. function handleButtonLeave() {
  155. if (unref(isVertical))
  156. return;
  157. items.value.forEach((item) => {
  158. item.states.hover = false;
  159. });
  160. }
  161. function handleIndicatorClick(index) {
  162. if (index !== activeIndex.value) {
  163. if (!isFirstCall.value) {
  164. isTransitioning.value = true;
  165. }
  166. }
  167. activeIndex.value = index;
  168. }
  169. function handleIndicatorHover(index) {
  170. if (props.trigger === "hover" && index !== activeIndex.value) {
  171. activeIndex.value = index;
  172. if (!isFirstCall.value) {
  173. isTransitioning.value = true;
  174. }
  175. }
  176. }
  177. function prev() {
  178. setActiveItem(activeIndex.value - 1);
  179. }
  180. function next() {
  181. setActiveItem(activeIndex.value + 1);
  182. }
  183. function resetTimer() {
  184. pauseTimer();
  185. if (!props.pauseOnHover)
  186. startTimer();
  187. }
  188. function setContainerHeight(height) {
  189. if (props.height !== "auto")
  190. return;
  191. containerHeight.value = height;
  192. }
  193. function PlaceholderItem() {
  194. var _a;
  195. const defaultSlots = (_a = slots.default) == null ? void 0 : _a.call(slots);
  196. if (!defaultSlots)
  197. return null;
  198. const flatSlots = flattedChildren(defaultSlots);
  199. const carouselItemsName = "ElCarouselItem";
  200. const normalizeSlots = flatSlots.filter((slot) => {
  201. return isVNode(slot) && slot.type.name === carouselItemsName;
  202. });
  203. if ((normalizeSlots == null ? void 0 : normalizeSlots.length) === 2 && props.loop && !isCardType.value) {
  204. isItemsTwoLength.value = true;
  205. return normalizeSlots;
  206. }
  207. isItemsTwoLength.value = false;
  208. return null;
  209. }
  210. watch(() => activeIndex.value, (current, prev2) => {
  211. resetItemPosition(prev2);
  212. if (isItemsTwoLength.value) {
  213. current = current % 2;
  214. prev2 = prev2 % 2;
  215. }
  216. if (prev2 > -1) {
  217. emit("change", current, prev2);
  218. }
  219. });
  220. watch(() => props.autoplay, (autoplay) => {
  221. autoplay ? startTimer() : pauseTimer();
  222. });
  223. watch(() => props.loop, () => {
  224. setActiveItem(activeIndex.value);
  225. });
  226. watch(() => props.interval, () => {
  227. resetTimer();
  228. });
  229. const resizeObserver = shallowRef();
  230. onMounted(() => {
  231. watch(() => items.value, () => {
  232. if (items.value.length > 0)
  233. setActiveItem(props.initialIndex);
  234. }, {
  235. immediate: true
  236. });
  237. resizeObserver.value = useResizeObserver(root.value, () => {
  238. resetItemPosition();
  239. });
  240. startTimer();
  241. });
  242. onBeforeUnmount(() => {
  243. pauseTimer();
  244. if (root.value && resizeObserver.value)
  245. resizeObserver.value.stop();
  246. });
  247. provide(carouselContextKey, {
  248. root,
  249. isCardType,
  250. isVertical,
  251. items,
  252. loop: props.loop,
  253. addItem,
  254. removeItem,
  255. setActiveItem,
  256. setContainerHeight
  257. });
  258. return {
  259. root,
  260. activeIndex,
  261. arrowDisplay,
  262. hasLabel,
  263. hover,
  264. isCardType,
  265. isTransitioning,
  266. items,
  267. isVertical,
  268. containerStyle,
  269. isItemsTwoLength,
  270. handleButtonEnter,
  271. handleTransitionEnd,
  272. handleButtonLeave,
  273. handleIndicatorClick,
  274. handleMouseEnter,
  275. handleMouseLeave,
  276. setActiveItem,
  277. prev,
  278. next,
  279. PlaceholderItem,
  280. isTwoLengthShow,
  281. throttledArrowClick,
  282. throttledIndicatorHover
  283. };
  284. };
  285. export { useCarousel };
  286. //# sourceMappingURL=use-carousel.mjs.map