222 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // Utils
 | |
| import { createNamespace, isDef } from '../utils';
 | |
| import { isHidden } from '../utils/dom/style';
 | |
| import { preventDefault } from '../utils/dom/event';
 | |
| import { getScroller, getScrollTop, getRootScrollTop, setRootScrollTop } from '../utils/dom/scroll'; // Mixins
 | |
| 
 | |
| import { TouchMixin } from '../mixins/touch';
 | |
| import { ParentMixin } from '../mixins/relation';
 | |
| import { BindEventMixin } from '../mixins/bind-event';
 | |
| 
 | |
| function genAlphabet() {
 | |
|   var indexList = [];
 | |
|   var charCodeOfA = 'A'.charCodeAt(0);
 | |
| 
 | |
|   for (var i = 0; i < 26; i++) {
 | |
|     indexList.push(String.fromCharCode(charCodeOfA + i));
 | |
|   }
 | |
| 
 | |
|   return indexList;
 | |
| }
 | |
| 
 | |
| var _createNamespace = createNamespace('index-bar'),
 | |
|     createComponent = _createNamespace[0],
 | |
|     bem = _createNamespace[1];
 | |
| 
 | |
| export default createComponent({
 | |
|   mixins: [TouchMixin, ParentMixin('vanIndexBar'), BindEventMixin(function (bind) {
 | |
|     if (!this.scroller) {
 | |
|       this.scroller = getScroller(this.$el);
 | |
|     }
 | |
| 
 | |
|     bind(this.scroller, 'scroll', this.onScroll);
 | |
|   })],
 | |
|   props: {
 | |
|     zIndex: [Number, String],
 | |
|     highlightColor: String,
 | |
|     sticky: {
 | |
|       type: Boolean,
 | |
|       default: true
 | |
|     },
 | |
|     stickyOffsetTop: {
 | |
|       type: Number,
 | |
|       default: 0
 | |
|     },
 | |
|     indexList: {
 | |
|       type: Array,
 | |
|       default: genAlphabet
 | |
|     }
 | |
|   },
 | |
|   data: function data() {
 | |
|     return {
 | |
|       activeAnchorIndex: null
 | |
|     };
 | |
|   },
 | |
|   computed: {
 | |
|     sidebarStyle: function sidebarStyle() {
 | |
|       if (isDef(this.zIndex)) {
 | |
|         return {
 | |
|           zIndex: this.zIndex + 1
 | |
|         };
 | |
|       }
 | |
|     },
 | |
|     highlightStyle: function highlightStyle() {
 | |
|       var highlightColor = this.highlightColor;
 | |
| 
 | |
|       if (highlightColor) {
 | |
|         return {
 | |
|           color: highlightColor
 | |
|         };
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   watch: {
 | |
|     indexList: function indexList() {
 | |
|       this.$nextTick(this.onScroll);
 | |
|     },
 | |
|     activeAnchorIndex: function activeAnchorIndex(value) {
 | |
|       if (value) {
 | |
|         this.$emit('change', value);
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     onScroll: function onScroll() {
 | |
|       var _this = this;
 | |
| 
 | |
|       if (isHidden(this.$el)) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       var scrollTop = getScrollTop(this.scroller);
 | |
|       var scrollerRect = this.getScrollerRect();
 | |
|       var rects = this.children.map(function (item) {
 | |
|         return item.getRect(_this.scroller, scrollerRect);
 | |
|       });
 | |
|       var active = this.getActiveAnchorIndex(scrollTop, rects);
 | |
|       this.activeAnchorIndex = this.indexList[active];
 | |
| 
 | |
|       if (this.sticky) {
 | |
|         this.children.forEach(function (item, index) {
 | |
|           if (index === active || index === active - 1) {
 | |
|             var rect = item.$el.getBoundingClientRect();
 | |
|             item.left = rect.left;
 | |
|             item.width = rect.width;
 | |
|           } else {
 | |
|             item.left = null;
 | |
|             item.width = null;
 | |
|           }
 | |
| 
 | |
|           if (index === active) {
 | |
|             item.active = true;
 | |
|             item.top = Math.max(_this.stickyOffsetTop, rects[index].top - scrollTop) + scrollerRect.top;
 | |
|           } else if (index === active - 1) {
 | |
|             var activeItemTop = rects[active].top - scrollTop;
 | |
|             item.active = activeItemTop > 0;
 | |
|             item.top = activeItemTop + scrollerRect.top - rects[index].height;
 | |
|           } else {
 | |
|             item.active = false;
 | |
|           }
 | |
|         });
 | |
|       }
 | |
|     },
 | |
|     getScrollerRect: function getScrollerRect() {
 | |
|       if (this.scroller.getBoundingClientRect) {
 | |
|         return this.scroller.getBoundingClientRect();
 | |
|       }
 | |
| 
 | |
|       return {
 | |
|         top: 0,
 | |
|         left: 0
 | |
|       };
 | |
|     },
 | |
|     getActiveAnchorIndex: function getActiveAnchorIndex(scrollTop, rects) {
 | |
|       for (var i = this.children.length - 1; i >= 0; i--) {
 | |
|         var prevHeight = i > 0 ? rects[i - 1].height : 0;
 | |
|         var reachTop = this.sticky ? prevHeight + this.stickyOffsetTop : 0;
 | |
| 
 | |
|         if (scrollTop + reachTop >= rects[i].top) {
 | |
|           return i;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return -1;
 | |
|     },
 | |
|     onClick: function onClick(event) {
 | |
|       this.scrollToElement(event.target);
 | |
|     },
 | |
|     onTouchMove: function onTouchMove(event) {
 | |
|       this.touchMove(event);
 | |
| 
 | |
|       if (this.direction === 'vertical') {
 | |
|         preventDefault(event);
 | |
|         var _event$touches$ = event.touches[0],
 | |
|             clientX = _event$touches$.clientX,
 | |
|             clientY = _event$touches$.clientY;
 | |
|         var target = document.elementFromPoint(clientX, clientY);
 | |
| 
 | |
|         if (target) {
 | |
|           var index = target.dataset.index;
 | |
|           /* istanbul ignore else */
 | |
| 
 | |
|           if (this.touchActiveIndex !== index) {
 | |
|             this.touchActiveIndex = index;
 | |
|             this.scrollToElement(target);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     scrollTo: function scrollTo(index) {
 | |
|       var match = this.children.filter(function (item) {
 | |
|         return String(item.index) === index;
 | |
|       });
 | |
| 
 | |
|       if (match[0]) {
 | |
|         match[0].scrollIntoView();
 | |
| 
 | |
|         if (this.sticky && this.stickyOffsetTop) {
 | |
|           setRootScrollTop(getRootScrollTop() - this.stickyOffsetTop);
 | |
|         }
 | |
| 
 | |
|         this.$emit('select', match[0].index);
 | |
|       }
 | |
|     },
 | |
|     scrollToElement: function scrollToElement(element) {
 | |
|       var index = element.dataset.index;
 | |
|       this.scrollTo(index);
 | |
|     },
 | |
|     onTouchEnd: function onTouchEnd() {
 | |
|       this.active = null;
 | |
|     }
 | |
|   },
 | |
|   render: function render() {
 | |
|     var _this2 = this;
 | |
| 
 | |
|     var h = arguments[0];
 | |
|     var Indexes = this.indexList.map(function (index) {
 | |
|       var active = index === _this2.activeAnchorIndex;
 | |
|       return h("span", {
 | |
|         "class": bem('index', {
 | |
|           active: active
 | |
|         }),
 | |
|         "style": active ? _this2.highlightStyle : null,
 | |
|         "attrs": {
 | |
|           "data-index": index
 | |
|         }
 | |
|       }, [index]);
 | |
|     });
 | |
|     return h("div", {
 | |
|       "class": bem()
 | |
|     }, [h("div", {
 | |
|       "class": bem('sidebar'),
 | |
|       "style": this.sidebarStyle,
 | |
|       "on": {
 | |
|         "click": this.onClick,
 | |
|         "touchstart": this.touchStart,
 | |
|         "touchmove": this.onTouchMove,
 | |
|         "touchend": this.onTouchEnd,
 | |
|         "touchcancel": this.onTouchEnd
 | |
|       }
 | |
|     }, [Indexes]), this.slots('default')]);
 | |
|   }
 | |
| }); | 
