178 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // Utils
 | |
| import { createNamespace } from '../utils';
 | |
| import { isHidden } from '../utils/dom/style';
 | |
| import { getScroller } from '../utils/dom/scroll'; // Mixins
 | |
| 
 | |
| import { BindEventMixin } from '../mixins/bind-event'; // Components
 | |
| 
 | |
| import Loading from '../loading';
 | |
| 
 | |
| var _createNamespace = createNamespace('list'),
 | |
|     createComponent = _createNamespace[0],
 | |
|     bem = _createNamespace[1],
 | |
|     t = _createNamespace[2];
 | |
| 
 | |
| export default createComponent({
 | |
|   mixins: [BindEventMixin(function (bind) {
 | |
|     if (!this.scroller) {
 | |
|       this.scroller = getScroller(this.$el);
 | |
|     }
 | |
| 
 | |
|     bind(this.scroller, 'scroll', this.check);
 | |
|   })],
 | |
|   model: {
 | |
|     prop: 'loading'
 | |
|   },
 | |
|   props: {
 | |
|     error: Boolean,
 | |
|     loading: Boolean,
 | |
|     finished: Boolean,
 | |
|     errorText: String,
 | |
|     loadingText: String,
 | |
|     finishedText: String,
 | |
|     immediateCheck: {
 | |
|       type: Boolean,
 | |
|       default: true
 | |
|     },
 | |
|     offset: {
 | |
|       type: [Number, String],
 | |
|       default: 300
 | |
|     },
 | |
|     direction: {
 | |
|       type: String,
 | |
|       default: 'down'
 | |
|     }
 | |
|   },
 | |
|   data: function data() {
 | |
|     return {
 | |
|       // use sync innerLoading state to avoid repeated loading in some edge cases
 | |
|       innerLoading: this.loading
 | |
|     };
 | |
|   },
 | |
|   updated: function updated() {
 | |
|     this.innerLoading = this.loading;
 | |
|   },
 | |
|   mounted: function mounted() {
 | |
|     if (this.immediateCheck) {
 | |
|       this.check();
 | |
|     }
 | |
|   },
 | |
|   watch: {
 | |
|     loading: 'check',
 | |
|     finished: 'check'
 | |
|   },
 | |
|   methods: {
 | |
|     // @exposed-api
 | |
|     check: function check() {
 | |
|       var _this = this;
 | |
| 
 | |
|       this.$nextTick(function () {
 | |
|         if (_this.innerLoading || _this.finished || _this.error) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         var el = _this.$el,
 | |
|             scroller = _this.scroller,
 | |
|             offset = _this.offset,
 | |
|             direction = _this.direction;
 | |
|         var scrollerRect;
 | |
| 
 | |
|         if (scroller.getBoundingClientRect) {
 | |
|           scrollerRect = scroller.getBoundingClientRect();
 | |
|         } else {
 | |
|           scrollerRect = {
 | |
|             top: 0,
 | |
|             bottom: scroller.innerHeight
 | |
|           };
 | |
|         }
 | |
| 
 | |
|         var scrollerHeight = scrollerRect.bottom - scrollerRect.top;
 | |
|         /* istanbul ignore next */
 | |
| 
 | |
|         if (!scrollerHeight || isHidden(el)) {
 | |
|           return false;
 | |
|         }
 | |
| 
 | |
|         var isReachEdge = false;
 | |
| 
 | |
|         var placeholderRect = _this.$refs.placeholder.getBoundingClientRect();
 | |
| 
 | |
|         if (direction === 'up') {
 | |
|           isReachEdge = scrollerRect.top - placeholderRect.top <= offset;
 | |
|         } else {
 | |
|           isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset;
 | |
|         }
 | |
| 
 | |
|         if (isReachEdge) {
 | |
|           _this.innerLoading = true;
 | |
| 
 | |
|           _this.$emit('input', true);
 | |
| 
 | |
|           _this.$emit('load');
 | |
|         }
 | |
|       });
 | |
|     },
 | |
|     clickErrorText: function clickErrorText() {
 | |
|       this.$emit('update:error', false);
 | |
|       this.check();
 | |
|     },
 | |
|     genLoading: function genLoading() {
 | |
|       var h = this.$createElement;
 | |
| 
 | |
|       if (this.innerLoading && !this.finished) {
 | |
|         return h("div", {
 | |
|           "key": "loading",
 | |
|           "class": bem('loading')
 | |
|         }, [this.slots('loading') || h(Loading, {
 | |
|           "attrs": {
 | |
|             "size": "16"
 | |
|           }
 | |
|         }, [this.loadingText || t('loading')])]);
 | |
|       }
 | |
|     },
 | |
|     genFinishedText: function genFinishedText() {
 | |
|       var h = this.$createElement;
 | |
| 
 | |
|       if (this.finished) {
 | |
|         var text = this.slots('finished') || this.finishedText;
 | |
| 
 | |
|         if (text) {
 | |
|           return h("div", {
 | |
|             "class": bem('finished-text')
 | |
|           }, [text]);
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     genErrorText: function genErrorText() {
 | |
|       var h = this.$createElement;
 | |
| 
 | |
|       if (this.error) {
 | |
|         var text = this.slots('error') || this.errorText;
 | |
| 
 | |
|         if (text) {
 | |
|           return h("div", {
 | |
|             "on": {
 | |
|               "click": this.clickErrorText
 | |
|             },
 | |
|             "class": bem('error-text')
 | |
|           }, [text]);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   render: function render() {
 | |
|     var h = arguments[0];
 | |
|     var Placeholder = h("div", {
 | |
|       "ref": "placeholder",
 | |
|       "key": "placeholder",
 | |
|       "class": bem('placeholder')
 | |
|     });
 | |
|     return h("div", {
 | |
|       "class": bem(),
 | |
|       "attrs": {
 | |
|         "role": "feed",
 | |
|         "aria-busy": this.innerLoading
 | |
|       }
 | |
|     }, [this.direction === 'down' ? this.slots() : Placeholder, this.genLoading(), this.genFinishedText(), this.genErrorText(), this.direction === 'up' ? this.slots() : Placeholder]);
 | |
|   }
 | |
| }); | 
