You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
5.9 KiB
224 lines
5.9 KiB
// 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);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scrollToElement: function scrollToElement(element) {
|
|
var index = element.dataset.index;
|
|
|
|
if (!index) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
},
|
|
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')]);
|
|
}
|
|
}); |