[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
+146
View File
@@ -0,0 +1,146 @@
<template>
<demo-section>
<van-tabs>
<van-tab :title="$t('basicUsage')">
<van-pull-refresh
v-model="list[0].refreshing"
@refresh="onRefresh(0)"
>
<van-list
v-model="list[0].loading"
:finished="list[0].finished"
:finished-text="$t('finishedText')"
@load="onLoad(0)"
>
<van-cell
v-for="item in list[0].items"
:key="item"
:title="item"
/>
</van-list>
</van-pull-refresh>
</van-tab>
<van-tab :title="$t('errorInfo')">
<van-pull-refresh
v-model="list[1].refreshing"
@refresh="onRefresh(1)"
>
<van-list
v-model="list[1].loading"
:finished="list[1].finished"
:error.sync="list[1].error"
:error-text="$t('errorText')"
@load="onLoad(1)"
>
<van-cell
v-for="item in list[1].items"
:key="item"
:title="item"
/>
</van-list>
</van-pull-refresh>
</van-tab>
</van-tabs>
</demo-section>
</template>
<script>
export default {
i18n: {
'zh-CN': {
errorInfo: '错误提示',
finishedText: '没有更多了',
errorText: '请求失败,点击重新加载'
},
'en-US': {
errorInfo: 'Error Info',
finishedText: 'Finished',
errorText: 'Request failed. Click to reload'
}
},
data() {
return {
list: [{
items: [],
refreshing: false,
loading: false,
error: false,
finished: false
}, {
items: [],
refreshing: false,
loading: false,
error: false,
finished: false
}]
};
},
methods: {
onLoad(index) {
const list = this.list[index];
setTimeout(() => {
for (let i = 0; i < 10; i++) {
const text = list.items.length + 1;
list.items.push(text < 10 ? '0' + text : text);
}
list.loading = false;
// show error info in second demo
if (index === 1 && list.items.length === 10 && !list.error) {
list.error = true;
} else {
list.error = false;
}
if (list.items.length >= 40) {
list.finished = true;
}
}, 500);
},
onRefresh(index) {
const list = this.list[index];
setTimeout(() => {
list.items = [];
list.error = false;
list.finished = false;
list.refreshing = false;
window.scrollTo(0, 10);
}, 1000);
}
}
};
</script>
<style lang="less">
@import '../../style/var';
.demo-list {
.van-cell {
text-align: center;
}
.page-desc {
margin: 0;
padding: 5px 0;
color: @gray-darker;
font-size: 14px;
text-align: center;
&--text {
margin: 0;
}
&--option {
margin: 12px;
}
}
.van-checkbox__label {
color: @gray-darker;
}
}
</style>
+133
View File
@@ -0,0 +1,133 @@
# List
### Intro
A list component to show items and control loading status.
### Install
``` javascript
import { List } from 'vant';
Vue.use(List);
```
## Usage
### Basic Usage
```html
<van-list
v-model="loading"
:finished="finished"
finished-text="Finished"
@load="onLoad"
>
<van-cell
v-for="item in list"
:key="item"
:title="item"
/>
</van-list>
```
```js
export default {
data() {
return {
list: [],
loading: false,
finished: false
};
},
methods: {
onLoad() {
setTimeout(() => {
for (let i = 0; i < 10; i++) {
this.list.push(this.list.length + 1);
}
this.loading = false;
if (this.list.length >= 40) {
this.finished = true;
}
}, 500);
}
}
}
```
### Error Info
```html
<van-list
v-model="loading"
:error.sync="error"
error-text="Request failed. Click to reload"
@load="onLoad"
>
<van-cell
v-for="item in list"
:key="item"
:title="item"
/>
</van-list>
```
```js
export default {
data() {
return {
list: [],
error: false,
loading: false
};
},
methods: {
onLoad() {
fetchSomeThing().catch(() => {
this.error = true;
})
}
}
}
```
## API
### Props
| Attribute | Description | Type | Default |
|------|------|------|------|
| v-model | Whether to show loading infothe `load` event will not be triggered when loading | `Boolean` | `false` |
| finished | Whether loading is finishedthe `load` event will not be triggered when finished | `Boolean` | `false` |
| error | Whether loading is errorthe `load` event will be triggered only when error text clicked, the `sync` modifier is needed | `Boolean` | `false` |
| offset | The load event will be triggered when the distance between the scrollbar and the bottom is less than offset | `Number` | `300` |
| loading-text | Loading text | `String` | `Loading...` |
| finished-text | Finished text | `String` | - |
| error-text | Error loaded text | `String` | - |
| immediate-check | Whether to check loading position immediately after mounted | `Boolean` | `true` |
| direction | Scroll directioncan be set to `up` | `String` | `down` |
### Events
| Event | Description | Arguments |
|------|------|------|
| load | Triggered when the distance between the scrollbar and the bottom is less than offset | - |
### Methods
Use ref to get list instance and call instance methods
| Name | Attribute | Return value | Description |
|------|------|------|------|
| check | - | - | Check scroll position |
### Slots
| Name | Description |
|------|------|
| default | List content |
| loading | Custom loading tips |
+135
View File
@@ -0,0 +1,135 @@
import { createNamespace } from '../utils';
import Loading from '../loading';
import { BindEventMixin } from '../mixins/bind-event';
import { getScrollEventTarget } from '../utils/dom/scroll';
const [createComponent, bem, t] = createNamespace('list');
export default createComponent({
mixins: [
BindEventMixin(function (bind) {
if (!this.scroller) {
this.scroller = getScrollEventTarget(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,
default: 300
},
direction: {
type: String,
default: 'down'
}
},
mounted() {
if (this.immediateCheck) {
this.$nextTick(this.check);
}
},
watch: {
loading() {
this.$nextTick(this.check);
},
finished() {
this.$nextTick(this.check);
}
},
methods: {
check() {
if (this.loading || this.finished || this.error) {
return;
}
const { $el: el, scroller, offset, direction } = this;
let scrollerRect;
if (scroller.getBoundingClientRect) {
scrollerRect = scroller.getBoundingClientRect();
} else {
scrollerRect = {
top: 0,
bottom: scroller.innerHeight
};
}
const scrollerHeight = scrollerRect.bottom - scrollerRect.top;
/* istanbul ignore next */
if (
!scrollerHeight ||
window.getComputedStyle(el).display === 'none' ||
el.offsetParent === null
) {
return false;
}
let isReachEdge = false;
const placeholderRect = this.$refs.placeholder.getBoundingClientRect();
if (direction === 'up') {
isReachEdge = placeholderRect.top - scrollerRect.top <= offset;
} else {
isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset;
}
if (isReachEdge) {
this.$emit('input', true);
this.$emit('load');
}
},
clickErrorText() {
this.$emit('update:error', false);
this.$nextTick(this.check);
}
},
render(h) {
const Placeholder = <div ref="placeholder" class={bem('placeholder')}/>;
return (
<div class={bem()} role="feed" aria-busy={this.loading}>
{this.direction === 'down' ? this.slots() : Placeholder}
{this.loading && (
<div class={bem('loading')} key="loading">
{this.slots('loading') || (
<Loading size="16">{this.loadingText || t('loading')}</Loading>
)}
</div>
)}
{this.finished && this.finishedText && (
<div class={bem('finished-text')}>{this.finishedText}</div>
)}
{this.error && this.errorText && (
<div onClick={this.clickErrorText} class={bem('error-text')}>
{this.errorText}
</div>
)}
{this.direction === 'up' ? this.slots() : Placeholder}
</div>
);
}
});
+17
View File
@@ -0,0 +1,17 @@
@import '../style/var';
.van-list {
&__loading,
&__finished-text,
&__error-text {
color: @list-text-color;
font-size: @list-text-font-size;
line-height: @list-text-line-height;
text-align: center;
}
&__placeholder {
height: 0;
pointer-events: none;
}
}
@@ -0,0 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders demo correctly 1`] = `
<div>
<div class="van-tabs van-tabs--line">
<div class="van-tabs__wrap van-hairline--top-bottom">
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
<div role="tab" aria-selected="true" class="van-tab van-tab--active"><span class="van-ellipsis">基础用法</span></div>
<div role="tab" class="van-tab"><span class="van-ellipsis">错误提示</span></div>
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
</div>
</div>
<div class="van-tabs__content">
<div role="tabpanel" class="van-tab__pane" style="">
<div class="van-pull-refresh">
<div class="van-pull-refresh__track" style="transition: 0ms;">
<div class="van-pull-refresh__head"></div>
<div role="feed" class="van-list">
<div class="van-list__placeholder"></div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="van-tab__pane" style="display: none;">
<!---->
</div>
</div>
</div>
</div>
`;
+4
View File
@@ -0,0 +1,4 @@
import Demo from '../demo';
import demoTest from '../../../test/demo-test';
demoTest(Demo);
+153
View File
@@ -0,0 +1,153 @@
import List from '..';
import { mount, later, mockGetBoundingClientRect } from '../../../test/utils';
function mockOffsetParent(el) {
Object.defineProperty(el, 'offsetParent', {
get() {
return {};
}
});
}
test('load event', async () => {
const wrapper = mount(List);
wrapper.vm.$on('input', value => {
wrapper.vm.loading = value;
});
mockOffsetParent(wrapper.vm.$el);
await later();
expect(wrapper.emitted('load')).toBeTruthy();
expect(wrapper.emitted('input')).toBeTruthy();
wrapper.vm.loading = false;
await later();
expect(wrapper.emitted('input')[1]).toBeTruthy();
wrapper.destroy();
});
test('error loaded, click error-text and reload', async () => {
const wrapper = mount(List, {
propsData: {
errorText: 'Request failed. Click to reload...',
error: true,
}
});
mockOffsetParent(wrapper.vm.$el);
await later();
expect(wrapper.emitted('load')).toBeFalsy();
expect(wrapper.emitted('input')).toBeFalsy();
// 模拟点击error-text的行为
wrapper.vm.$on('update:error', val => {
wrapper.setProps({
error: val
});
});
wrapper.find('.van-list__error-text').trigger('click');
await later();
expect(wrapper.vm.$props.error).toBeFalsy();
expect(wrapper.emitted('load')).toBeTruthy();
expect(wrapper.emitted('input')).toBeTruthy();
wrapper.destroy();
});
test('finished', async () => {
const wrapper = mount(List, {
propsData: {
finished: true,
finishedText: 'Finished'
}
});
mockOffsetParent(wrapper.vm.$el);
await later();
expect(wrapper.emitted('load')).toBeFalsy();
expect(wrapper.emitted('input')).toBeFalsy();
expect(wrapper.contains('.van-list__finished-text')).toBeTruthy();
wrapper.vm.finished = false;
await later();
expect(wrapper.emitted('load')).toBeTruthy();
expect(wrapper.emitted('input')).toBeTruthy();
expect(wrapper.contains('.van-list__finished-text')).toBeFalsy();
});
test('immediate check false', async () => {
const wrapper = mount(List, {
propsData: {
immediateCheck: false
}
});
await later();
expect(wrapper.emitted('load')).toBeFalsy();
expect(wrapper.emitted('input')).toBeFalsy();
});
test('check the case that scroller is not window', async () => {
const restoreMock = mockGetBoundingClientRect({
top: 0,
bottom: 200
});
const wrapper = mount({
template: `
<div style="overflow-y: scroll;">
<list ref="list"/>
</div>
`,
components: { List }
});
const listRef = wrapper.find({
ref: 'list'
});
mockOffsetParent(listRef.vm.$el);
await later();
expect(listRef.emitted('load')).toBeTruthy();
expect(listRef.emitted('input')).toBeTruthy();
restoreMock();
});
test('check the direction props', async () => {
const wrapper = mount(List, {
slots: {
default: '<div class="list-item">list item</div>'
},
propsData: {
direction: 'up'
}
});
mockOffsetParent(wrapper.vm.$el);
await later();
let children = wrapper.findAll('.van-list > div');
expect(children.at(0).is('.van-list__placeholder')).toBeTruthy();
expect(children.at(1).is('.list-item')).toBeTruthy();
// change the direction's value
wrapper.setProps({
direction: 'down'
});
await later();
children = wrapper.findAll('.van-list > div');
expect(children.at(0).is('.list-item')).toBeTruthy();
expect(children.at(1).is('.van-list__placeholder')).toBeTruthy();
});
+150
View File
@@ -0,0 +1,150 @@
# List 列表
### 介绍
瀑布流滚动加载,用于展示长列表,当列表即将滚动到底部时,会触发事件并加载更多列表项。
### 引入
``` javascript
import { List } from 'vant';
Vue.use(List);
```
## 代码演示
### 基础用法
List 组件通过`loading`和`finished`两个变量控制加载状态,当组件滚动到底部时,会触发`load`事件并将`loading`设置成`true`。此时可以发起异步操作并更新数据,数据更新完毕后,将`loading`设置成`false`即可。若数据已全部加载完毕,则直接将`finished`设置成`true`即可。
```html
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell
v-for="item in list"
:key="item"
:title="item"
/>
</van-list>
```
```js
export default {
data() {
return {
list: [],
loading: false,
finished: false
};
},
methods: {
onLoad() {
// 异步更新数据
setTimeout(() => {
for (let i = 0; i < 10; i++) {
this.list.push(this.list.length + 1);
}
// 加载状态结束
this.loading = false;
// 数据全部加载完成
if (this.list.length >= 40) {
this.finished = true;
}
}, 500);
}
}
}
```
### 错误提示
若列表数据加载失败,将`error`设置成`true`即可显示错误提示,用户点击错误提示后会重新触发 load 事件。
```html
<van-list
v-model="loading"
:error.sync="error"
error-text="请求失败,点击重新加载"
@load="onLoad"
>
<van-cell
v-for="item in list"
:key="item"
:title="item"
/>
</van-list>
```
```js
export default {
data() {
return {
list: [],
error: false,
loading: false
};
},
methods: {
onLoad() {
fetchSomeThing().catch(() => {
this.error = true;
})
}
}
}
```
### 状态变化
`List`有以下三种状态,理解这些状态有助于你正确地使用`List`组件:
- 非加载中,`loading`为`false`,此时会根据列表滚动位置判断是否触发`load`事件(列表内容不足一屏幕时,会直接触发)
- 加载中,`loading`为`true`,表示正在发送异步请求,此时不会触发`load`事件
- 加载完成,`finished`为`true`,此时不会触发`load`事件
在每次请求完毕后,需要手动将`loading`设置为`false`,表示加载结束
## API
### Props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|------|------|------|------|------|
| v-model | 是否处于加载状态,加载过程中不触发`load`事件 | `Boolean` | `false` | - |
| finished | 是否已加载完成,加载完成后不再触发`load`事件 | `Boolean` | `false` | - |
| error | 是否加载失败,加载失败后点击错误提示可以重新<br>触发`load`事件,必须使用`sync`修饰符 | `Boolean` | `false` | - |
| offset | 滚动条与底部距离小于 offset 时触发`load`事件 | `Number` | `300` | - |
| loading-text | 加载过程中的提示文案 | `String` | `加载中...` | 1.1.1 |
| finished-text | 加载完成后的提示文案 | `String` | - | 1.4.7 |
| error-text | 加载失败后的提示文案 | `String` | - | 1.5.3 |
| immediate-check | 是否在初始化时立即执行滚动位置检查 | `Boolean` | `true` | - |
| direction | 滚动触发加载的方向,可选值为`up` | `String` | `down` | 1.6.16 |
### Events
| 事件名 | 说明 | 回调参数 |
|------|------|------|
| load | 滚动条与底部距离小于 offset 时触发 | - |
### 方法
通过 ref 可以获取到 list 实例并调用实例方法
| 方法名 | 参数 | 返回值 | 介绍 |
|------|------|------|------|
| check | - | - | 检查当前的滚动位置,若已滚动至底部,则会触发 load 事件 |
### Slots
| 名称 | 说明 |
|------|------|
| default | 列表内容 |
| loading | 自定义底部加载中提示 |