List.mjs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { ref, watch, computed, nextTick, onUpdated, onMounted, defineComponent, createVNode as _createVNode } from "vue";
  2. import { isHidden, truthProp, makeStringProp, makeNumericProp, createNamespace } from "../utils/index.mjs";
  3. import { useRect, useScrollParent, useEventListener } from "@vant/use";
  4. import { useExpose } from "../composables/use-expose.mjs";
  5. import { useTabStatus } from "../composables/use-tab-status.mjs";
  6. import { Loading } from "../loading/index.mjs";
  7. const [name, bem, t] = createNamespace("list");
  8. const listProps = {
  9. error: Boolean,
  10. offset: makeNumericProp(300),
  11. loading: Boolean,
  12. disabled: Boolean,
  13. finished: Boolean,
  14. scroller: Object,
  15. errorText: String,
  16. direction: makeStringProp("down"),
  17. loadingText: String,
  18. finishedText: String,
  19. immediateCheck: truthProp
  20. };
  21. var stdin_default = defineComponent({
  22. name,
  23. props: listProps,
  24. emits: ["load", "update:error", "update:loading"],
  25. setup(props, {
  26. emit,
  27. slots
  28. }) {
  29. const loading = ref(props.loading);
  30. const root = ref();
  31. const placeholder = ref();
  32. const tabStatus = useTabStatus();
  33. const scrollParent = useScrollParent(root);
  34. const scroller = computed(() => props.scroller || scrollParent.value);
  35. const check = () => {
  36. nextTick(() => {
  37. if (loading.value || props.finished || props.disabled || props.error || // skip check when inside an inactive tab
  38. (tabStatus == null ? void 0 : tabStatus.value) === false) {
  39. return;
  40. }
  41. const {
  42. direction
  43. } = props;
  44. const offset = +props.offset;
  45. const scrollParentRect = useRect(scroller);
  46. if (!scrollParentRect.height || isHidden(root)) {
  47. return;
  48. }
  49. let isReachEdge = false;
  50. const placeholderRect = useRect(placeholder);
  51. if (direction === "up") {
  52. isReachEdge = scrollParentRect.top - placeholderRect.top <= offset;
  53. } else {
  54. isReachEdge = placeholderRect.bottom - scrollParentRect.bottom <= offset;
  55. }
  56. if (isReachEdge) {
  57. loading.value = true;
  58. emit("update:loading", true);
  59. emit("load");
  60. }
  61. });
  62. };
  63. const renderFinishedText = () => {
  64. if (props.finished) {
  65. const text = slots.finished ? slots.finished() : props.finishedText;
  66. if (text) {
  67. return _createVNode("div", {
  68. "class": bem("finished-text")
  69. }, [text]);
  70. }
  71. }
  72. };
  73. const clickErrorText = () => {
  74. emit("update:error", false);
  75. check();
  76. };
  77. const renderErrorText = () => {
  78. if (props.error) {
  79. const text = slots.error ? slots.error() : props.errorText;
  80. if (text) {
  81. return _createVNode("div", {
  82. "role": "button",
  83. "class": bem("error-text"),
  84. "tabindex": 0,
  85. "onClick": clickErrorText
  86. }, [text]);
  87. }
  88. }
  89. };
  90. const renderLoading = () => {
  91. if (loading.value && !props.finished && !props.disabled) {
  92. return _createVNode("div", {
  93. "class": bem("loading")
  94. }, [slots.loading ? slots.loading() : _createVNode(Loading, {
  95. "class": bem("loading-icon")
  96. }, {
  97. default: () => [props.loadingText || t("loading")]
  98. })]);
  99. }
  100. };
  101. watch(() => [props.loading, props.finished, props.error], check);
  102. if (tabStatus) {
  103. watch(tabStatus, (tabActive) => {
  104. if (tabActive) {
  105. check();
  106. }
  107. });
  108. }
  109. onUpdated(() => {
  110. loading.value = props.loading;
  111. });
  112. onMounted(() => {
  113. if (props.immediateCheck) {
  114. check();
  115. }
  116. });
  117. useExpose({
  118. check
  119. });
  120. useEventListener("scroll", check, {
  121. target: scroller,
  122. passive: true
  123. });
  124. return () => {
  125. var _a;
  126. const Content = (_a = slots.default) == null ? void 0 : _a.call(slots);
  127. const Placeholder = _createVNode("div", {
  128. "ref": placeholder,
  129. "class": bem("placeholder")
  130. }, null);
  131. return _createVNode("div", {
  132. "ref": root,
  133. "role": "feed",
  134. "class": bem(),
  135. "aria-busy": loading.value
  136. }, [props.direction === "down" ? Content : Placeholder, renderLoading(), renderFinishedText(), renderErrorText(), props.direction === "up" ? Content : Placeholder]);
  137. };
  138. }
  139. });
  140. export {
  141. stdin_default as default,
  142. listProps
  143. };