index.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Utils
  2. import { createNamespace } from '../utils';
  3. import { isHidden } from '../utils/dom/style';
  4. import { getScroller } from '../utils/dom/scroll'; // Mixins
  5. import { BindEventMixin } from '../mixins/bind-event'; // Components
  6. import Loading from '../loading';
  7. var _createNamespace = createNamespace('list'),
  8. createComponent = _createNamespace[0],
  9. bem = _createNamespace[1],
  10. t = _createNamespace[2];
  11. export default createComponent({
  12. mixins: [BindEventMixin(function (bind) {
  13. if (!this.scroller) {
  14. this.scroller = getScroller(this.$el);
  15. }
  16. bind(this.scroller, 'scroll', this.check);
  17. })],
  18. model: {
  19. prop: 'loading'
  20. },
  21. props: {
  22. error: Boolean,
  23. loading: Boolean,
  24. finished: Boolean,
  25. errorText: String,
  26. loadingText: String,
  27. finishedText: String,
  28. immediateCheck: {
  29. type: Boolean,
  30. default: true
  31. },
  32. offset: {
  33. type: [Number, String],
  34. default: 300
  35. },
  36. direction: {
  37. type: String,
  38. default: 'down'
  39. }
  40. },
  41. data: function data() {
  42. return {
  43. // use sync innerLoading state to avoid repeated loading in some edge cases
  44. innerLoading: this.loading
  45. };
  46. },
  47. updated: function updated() {
  48. this.innerLoading = this.loading;
  49. },
  50. mounted: function mounted() {
  51. if (this.immediateCheck) {
  52. this.check();
  53. }
  54. },
  55. watch: {
  56. loading: 'check',
  57. finished: 'check'
  58. },
  59. methods: {
  60. // @exposed-api
  61. check: function check() {
  62. var _this = this;
  63. this.$nextTick(function () {
  64. if (_this.innerLoading || _this.finished || _this.error) {
  65. return;
  66. }
  67. var el = _this.$el,
  68. scroller = _this.scroller,
  69. offset = _this.offset,
  70. direction = _this.direction;
  71. var scrollerRect;
  72. if (scroller.getBoundingClientRect) {
  73. scrollerRect = scroller.getBoundingClientRect();
  74. } else {
  75. scrollerRect = {
  76. top: 0,
  77. bottom: scroller.innerHeight
  78. };
  79. }
  80. var scrollerHeight = scrollerRect.bottom - scrollerRect.top;
  81. /* istanbul ignore next */
  82. if (!scrollerHeight || isHidden(el)) {
  83. return false;
  84. }
  85. var isReachEdge = false;
  86. var placeholderRect = _this.$refs.placeholder.getBoundingClientRect();
  87. if (direction === 'up') {
  88. isReachEdge = scrollerRect.top - placeholderRect.top <= offset;
  89. } else {
  90. isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset;
  91. }
  92. if (isReachEdge) {
  93. _this.innerLoading = true;
  94. _this.$emit('input', true);
  95. _this.$emit('load');
  96. }
  97. });
  98. },
  99. clickErrorText: function clickErrorText() {
  100. this.$emit('update:error', false);
  101. this.check();
  102. },
  103. genLoading: function genLoading() {
  104. var h = this.$createElement;
  105. if (this.innerLoading && !this.finished) {
  106. return h("div", {
  107. "key": "loading",
  108. "class": bem('loading')
  109. }, [this.slots('loading') || h(Loading, {
  110. "attrs": {
  111. "size": "16"
  112. }
  113. }, [this.loadingText || t('loading')])]);
  114. }
  115. },
  116. genFinishedText: function genFinishedText() {
  117. var h = this.$createElement;
  118. if (this.finished) {
  119. var text = this.slots('finished') || this.finishedText;
  120. if (text) {
  121. return h("div", {
  122. "class": bem('finished-text')
  123. }, [text]);
  124. }
  125. }
  126. },
  127. genErrorText: function genErrorText() {
  128. var h = this.$createElement;
  129. if (this.error) {
  130. var text = this.slots('error') || this.errorText;
  131. if (text) {
  132. return h("div", {
  133. "on": {
  134. "click": this.clickErrorText
  135. },
  136. "class": bem('error-text')
  137. }, [text]);
  138. }
  139. }
  140. }
  141. },
  142. render: function render() {
  143. var h = arguments[0];
  144. var Placeholder = h("div", {
  145. "ref": "placeholder",
  146. "key": "placeholder",
  147. "class": bem('placeholder')
  148. });
  149. return h("div", {
  150. "class": bem(),
  151. "attrs": {
  152. "role": "feed",
  153. "aria-busy": this.innerLoading
  154. }
  155. }, [this.direction === 'down' ? this.slots() : Placeholder, this.genLoading(), this.genFinishedText(), this.genErrorText(), this.direction === 'up' ? this.slots() : Placeholder]);
  156. }
  157. });