[improvement] rename packages dir to src (#3659)

This commit is contained in:
neverland
2019-06-27 11:25:57 +08:00
committed by GitHub
parent 8489918dca
commit 75c79b7044
619 changed files with 21 additions and 21 deletions
+79
View File
@@ -0,0 +1,79 @@
import { createNamespace } from '../utils';
import { TouchMixin } from '../mixins/touch';
const [createComponent, bem] = createNamespace('tabs');
const MIN_SWIPE_DISTANCE = 50;
export default createComponent({
mixins: [TouchMixin],
props: {
count: Number,
active: Number,
duration: Number,
animated: Boolean,
swipeable: Boolean
},
computed: {
style() {
if (this.animated) {
return {
transform: `translate3d(${-1 * this.active * 100}%, 0, 0)`,
transitionDuration: `${this.duration}s`
};
}
},
listeners() {
if (this.swipeable) {
return {
touchstart: this.touchStart,
touchmove: this.touchMove,
touchend: this.onTouchEnd,
touchcancel: this.onTouchEnd
};
}
}
},
methods: {
// watch swipe touch end
onTouchEnd() {
const { direction, deltaX, active } = this;
/* istanbul ignore else */
if (direction === 'horizontal' && this.offsetX >= MIN_SWIPE_DISTANCE) {
/* istanbul ignore else */
if (deltaX > 0 && active !== 0) {
this.$emit('change', active - 1);
} else if (deltaX < 0 && active !== this.count - 1) {
this.$emit('change', active + 1);
}
}
},
renderChildren() {
if (this.animated) {
return (
<div class={bem('track')} style={this.style}>
{this.slots()}
</div>
);
}
return this.slots();
}
},
render(h) {
return (
<div
class={bem('content', { animated: this.animated })}
{...{ on: this.listeners }}
>
{this.renderChildren()}
</div>
);
}
});
+82
View File
@@ -0,0 +1,82 @@
import { createNamespace } from '../utils';
const bem = createNamespace('tab')[1];
export default {
props: {
type: String,
color: String,
title: String,
active: Boolean,
ellipsis: Boolean,
disabled: Boolean,
scrollable: Boolean,
activeColor: String,
inactiveColor: String,
swipeThreshold: Number
},
computed: {
style() {
const style = {};
const { color, active } = this;
const isCard = this.type === 'card';
// card theme color
if (color && isCard) {
style.borderColor = color;
if (!this.disabled) {
if (active) {
style.backgroundColor = color;
} else {
style.color = color;
}
}
}
const titleColor = active ? this.activeColor : this.inactiveColor;
if (titleColor) {
style.color = titleColor;
}
if (this.scrollable && this.ellipsis) {
style.flexBasis = `${88 / this.swipeThreshold}%`;
}
return style;
}
},
methods: {
onClick() {
this.$emit('click');
},
renderTitle(el) {
const { title } = this.$refs;
title.innerHTML = '';
title.appendChild(el);
}
},
render(h) {
return (
<div
role="tab"
aria-selected={this.active}
class={bem({
active: this.active,
disabled: this.disabled,
complete: !this.ellipsis
})}
style={this.style}
onClick={this.onClick}
>
<span ref="title" class={{ 'van-ellipsis': this.ellipsis }}>
{this.title}
</span>
</div>
);
}
};
+353
View File
@@ -0,0 +1,353 @@
import { createNamespace, isDef, suffixPx } from '../utils';
import { scrollLeftTo } from './utils';
import { on, off } from '../utils/dom/event';
import { ParentMixin } from '../mixins/relation';
import { BindEventMixin } from '../mixins/bind-event';
import {
setScrollTop,
getScrollTop,
getElementTop,
getScrollEventTarget
} from '../utils/dom/scroll';
import Title from './Title';
import Content from './Content';
const [createComponent, bem] = createNamespace('tabs');
export default createComponent({
mixins: [
ParentMixin('vanTabs'),
BindEventMixin(function (bind, isBind) {
this.bindScrollEvent(isBind);
bind(window, 'resize', this.setLine, true);
})
],
model: {
prop: 'active'
},
props: {
color: String,
sticky: Boolean,
animated: Boolean,
offsetTop: Number,
swipeable: Boolean,
background: String,
lineWidth: [Number, String],
lineHeight: [Number, String],
titleActiveColor: String,
titleInactiveColor: String,
border: {
type: Boolean,
default: true
},
ellipsis: {
type: Boolean,
default: true
},
lazyRender: {
type: Boolean,
default: true
},
active: {
type: [Number, String],
default: 0
},
type: {
type: String,
default: 'line'
},
duration: {
type: Number,
default: 0.3
},
swipeThreshold: {
type: Number,
default: 4
}
},
data() {
this.scrollEvent = false;
return {
position: '',
curActive: null,
lineStyle: {
backgroundColor: this.color
}
};
},
computed: {
// whether the nav is scrollable
scrollable() {
return this.children.length > this.swipeThreshold || !this.ellipsis;
},
wrapStyle() {
switch (this.position) {
case 'top':
return {
top: this.offsetTop + 'px',
position: 'fixed'
};
case 'bottom':
return {
top: 'auto',
bottom: 0
};
default:
return null;
}
},
navStyle() {
return {
borderColor: this.color,
background: this.background
};
}
},
watch: {
active(val) {
if (val !== this.curActive) {
this.correctActive(val);
}
},
color() {
this.setLine();
},
children() {
this.correctActive(this.curActive || this.active);
this.scrollIntoView();
this.setLine();
},
curActive() {
this.scrollIntoView();
this.setLine();
// scroll to correct position
if (this.position === 'top' || this.position === 'bottom') {
setScrollTop(window, getElementTop(this.$el) - this.offsetTop);
}
},
sticky(val) {
this.bindScrollEvent(val);
}
},
mounted() {
this.onShow();
},
activated() {
this.onShow();
this.setLine();
},
methods: {
onShow() {
this.$nextTick(() => {
this.inited = true;
this.scrollIntoView(true);
});
},
bindScrollEvent(isBind) {
const sticky = this.sticky && isBind;
if (this.scrollEvent !== sticky) {
this.scrollEvent = sticky;
this.scrollEl = this.scrollEl || getScrollEventTarget(this.$el);
(sticky ? on : off)(this.scrollEl, 'scroll', this.onScroll, true);
this.onScroll();
}
},
// adjust tab position
onScroll() {
const scrollTop = getScrollTop(window) + this.offsetTop;
const elTopToPageTop = getElementTop(this.$el);
const elBottomToPageTop =
elTopToPageTop + this.$el.offsetHeight - this.$refs.wrap.offsetHeight;
if (scrollTop > elBottomToPageTop) {
this.position = 'bottom';
} else if (scrollTop > elTopToPageTop) {
this.position = 'top';
} else {
this.position = '';
}
const scrollParams = {
scrollTop,
isFixed: this.position === 'top'
};
this.$emit('scroll', scrollParams);
},
// update nav bar style
setLine() {
const shouldAnimate = this.inited;
this.$nextTick(() => {
const { titles } = this.$refs;
if (!titles || !titles[this.curActive] || this.type !== 'line') {
return;
}
const title = titles[this.curActive].$el;
const { lineWidth, lineHeight } = this;
const width = isDef(lineWidth) ? lineWidth : title.offsetWidth / 2;
const left = title.offsetLeft + title.offsetWidth / 2;
const lineStyle = {
width: suffixPx(width),
backgroundColor: this.color,
transform: `translateX(${left}px) translateX(-50%)`
};
if (shouldAnimate) {
lineStyle.transitionDuration = `${this.duration}s`;
}
if (isDef(lineHeight)) {
const height = suffixPx(lineHeight);
lineStyle.height = height;
lineStyle.borderRadius = height;
}
this.lineStyle = lineStyle;
});
},
// correct the value of active
correctActive(active) {
active = +active;
const exist = this.children.some(tab => tab.index === active);
const defaultActive = (this.children[0] || {}).index || 0;
this.setCurActive(exist ? active : defaultActive);
},
setCurActive(active) {
active = this.findAvailableTab(active, active < this.curActive);
if (isDef(active) && active !== this.curActive) {
this.$emit('input', active);
if (this.curActive !== null) {
this.$emit('change', active, this.children[active].title);
}
this.curActive = active;
}
},
findAvailableTab(active, reverse) {
const diff = reverse ? -1 : 1;
let index = active;
while (index >= 0 && index < this.children.length) {
if (!this.children[index].disabled) {
return index;
}
index += diff;
}
},
// emit event when clicked
onClick(index) {
const { title, disabled } = this.children[index];
if (disabled) {
this.$emit('disabled', index, title);
} else {
this.setCurActive(index);
this.$emit('click', index, title);
}
},
// scroll active tab into view
scrollIntoView(immediate) {
const { titles } = this.$refs;
if (!this.scrollable || !titles || !titles[this.curActive]) {
return;
}
const { nav } = this.$refs;
const title = titles[this.curActive].$el;
const to = title.offsetLeft - (nav.offsetWidth - title.offsetWidth) / 2;
scrollLeftTo(nav, to, immediate ? 0 : this.duration);
},
// render title slot of child tab
renderTitle(el, index) {
this.$nextTick(() => {
this.$refs.titles[index].renderTitle(el);
});
}
},
render(h) {
const { type, ellipsis, animated, scrollable } = this;
const Nav = this.children.map((item, index) => (
<Title
ref="titles"
refInFor
type={type}
title={item.title}
color={this.color}
active={index === this.curActive}
ellipsis={ellipsis}
disabled={item.disabled}
scrollable={scrollable}
activeColor={this.titleActiveColor}
inactiveColor={this.titleInactiveColor}
swipeThreshold={this.swipeThreshold}
onClick={() => {
this.onClick(index);
}}
/>
));
return (
<div class={bem([type])}>
<div
ref="wrap"
style={this.wrapStyle}
class={[
bem('wrap', { scrollable }),
{ 'van-hairline--top-bottom': type === 'line' && this.border }
]}
>
<div ref="nav" role="tablist" class={bem('nav', [type])} style={this.navStyle}>
{this.slots('nav-left')}
{Nav}
{type === 'line' && <div class={bem('line')} style={this.lineStyle} />}
{this.slots('nav-right')}
</div>
</div>
<Content
count={this.children.length}
active={this.curActive}
animated={animated}
duration={this.duration}
swipeable={this.swipeable}
onChange={this.setCurActive}
>
{this.slots()}
</Content>
</div>
);
}
});
+149
View File
@@ -0,0 +1,149 @@
@import '../style/var';
.van-tab {
position: relative;
flex: 1;
box-sizing: border-box;
min-width: 0; // hack for flex ellipsis
padding: 0 5px;
color: @tab-text-color;
font-size: @tab-font-size;
line-height: @tabs-line-height;
text-align: center;
cursor: pointer;
span {
display: block;
}
&--active {
color: @tab-active-text-color;
font-weight: 500;
}
&--disabled {
color: @tab-disabled-text-color;
}
}
.van-tabs {
position: relative;
&__wrap {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 99;
overflow: hidden;
&--page-top {
position: fixed;
}
&--content-bottom {
top: auto;
bottom: 0;
}
&--scrollable {
.van-tab {
flex: 0 0 22%;
&--complete {
flex: 1 0 auto;
}
}
.van-tabs__nav {
overflow: hidden;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
}
}
}
&__nav {
position: relative;
display: flex;
background-color: @tabs-nav-background-color;
user-select: none;
&--line {
box-sizing: content-box;
height: 100%;
padding-bottom: 15px; /* 15px padding to hide scrollbar in mobile safari */
}
&--card {
box-sizing: border-box;
height: @tabs-card-height;
margin: 0 15px;
border: 1px solid @tabs-default-color;
border-radius: 2px;
.van-tab {
color: @tabs-default-color;
line-height: @tabs-card-height - 2px;
border-right: 1px solid @tabs-default-color;
&:last-child {
border-right: none;
}
&.van-tab--active {
color: @white;
background-color: @tabs-default-color;
}
&--disabled {
color: @tab-disabled-text-color;
}
}
}
}
&__line {
position: absolute;
bottom: 15px;
left: 0;
z-index: 1;
height: @tabs-bottom-bar-height;
background-color: @tabs-bottom-bar-color;
border-radius: @tabs-bottom-bar-height;
}
&__track {
position: relative;
display: flex;
width: 100%;
height: 100%;
will-change: left;
}
&__content {
&--animated {
overflow: hidden;
}
}
&--line {
padding-top: @tabs-line-height;
.van-tabs__wrap {
height: @tabs-line-height;
}
}
&--card {
padding-top: @tabs-card-height;
> .van-tabs__wrap {
height: @tabs-card-height;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
import { raf } from '../utils/dom/raf';
export function scrollLeftTo(el: HTMLElement, to: number, duration: number) {
let count = 0;
const from = el.scrollLeft;
const frames = duration === 0 ? 1 : Math.round((duration * 1000) / 16);
function animate() {
el.scrollLeft += (to - from) / frames;
if (++count < frames) {
raf(animate);
}
}
animate();
}