mirror of https://github.com/ElemeFE/element
infiniteScroll: add infiniteScroll component (#15567)
parent
c42716000c
commit
5fea8b46f2
|
@ -27,6 +27,7 @@ const install = function(Vue, opts = {}) {
|
||||||
Vue.component(component.name, component);
|
Vue.component(component.name, component);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Vue.use(InfiniteScroll);
|
||||||
Vue.use(Loading.directive);
|
Vue.use(Loading.directive);
|
||||||
|
|
||||||
Vue.prototype.$ELEMENT = {
|
Vue.prototype.$ELEMENT = {
|
||||||
|
@ -76,7 +77,7 @@ ComponentNames.forEach(name => {
|
||||||
package: name
|
package: name
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (['Loading', 'MessageBox', 'Notification', 'Message'].indexOf(componentName) === -1) {
|
if (['Loading', 'MessageBox', 'Notification', 'Message', 'InfiniteScroll'].indexOf(componentName) === -1) {
|
||||||
installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
|
installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
|
||||||
name: componentName,
|
name: componentName,
|
||||||
component: name
|
component: name
|
||||||
|
|
|
@ -73,5 +73,6 @@
|
||||||
"link": "./packages/link/index.js",
|
"link": "./packages/link/index.js",
|
||||||
"divider": "./packages/divider/index.js",
|
"divider": "./packages/divider/index.js",
|
||||||
"image": "./packages/image/index.js",
|
"image": "./packages/image/index.js",
|
||||||
"calendar": "./packages/calendar/index.js"
|
"calendar": "./packages/calendar/index.js",
|
||||||
|
"infiniteScroll": "./packages/infiniteScroll/index.js"
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,3 +41,4 @@
|
||||||
@import "./upload.scss";
|
@import "./upload.scss";
|
||||||
@import "./divider.scss";
|
@import "./divider.scss";
|
||||||
@import "./image.scss";
|
@import "./image.scss";
|
||||||
|
@import "./infiniteScroll.scss";
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
.infinite-list {
|
||||||
|
height: 300px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.infinite-list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 50px;
|
||||||
|
background: #e8f3fe;
|
||||||
|
margin: 10px;
|
||||||
|
color: lighten(#1989fa, 20%);
|
||||||
|
& + .list-item {
|
||||||
|
margin-top: 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.infinite-list-wrapper {
|
||||||
|
height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.list{
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.list-item{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 50px;
|
||||||
|
background: #fff6f6;
|
||||||
|
color: #ff8484;
|
||||||
|
& + .list-item {
|
||||||
|
margin-top: 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
## InfiniteScroll
|
||||||
|
|
||||||
|
Load more data while reach bottom of the page
|
||||||
|
|
||||||
|
### Basic usage
|
||||||
|
Add `v-infinite-scroll` to the list to automatically execute loading method when scrolling to the bottom.
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<ul class="infinite-list" v-infinite-scroll="load">
|
||||||
|
<li v-for="i in count" class="infinite-list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.count += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Disable Loading
|
||||||
|
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="infinite-list-wrapper">
|
||||||
|
<ul
|
||||||
|
class="list"
|
||||||
|
v-infinite-scroll="load"
|
||||||
|
infinite-scroll-disabled="disabled">
|
||||||
|
<li v-for="i in count" class="list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
<p v-if="loading">loading...</p>
|
||||||
|
<p v-if="noMore">Mo more</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 10,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
noMore () {
|
||||||
|
return this.count >= 20
|
||||||
|
},
|
||||||
|
disabled () {
|
||||||
|
return this.loading || this.noMore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.loading = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.count += 2
|
||||||
|
this.loading = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
|
| Attribute | Description | Type | Accepted values | Default |
|
||||||
|
| -------------- | ------------------------------ | --------- | ------------------------------------ | ------- |
|
||||||
|
| infinite-scroll-disabled | is disabled | boolean | - |false |
|
||||||
|
| infinite-scroll-delay | throttle delay (ms) | number | - |200 |
|
||||||
|
| infinite-scroll-distance| trigger distance (px) | number |- |0 |
|
||||||
|
| infinite-scroll-immediate |Whether to execute the loading method immediately, in case the content cannot be filled up in the initial state. | boolean | - |true |
|
|
@ -0,0 +1,87 @@
|
||||||
|
## InfiniteScroll
|
||||||
|
|
||||||
|
Load more data while reach bottom of the page
|
||||||
|
|
||||||
|
### Basic usage
|
||||||
|
Add `v-infinite-scroll` to the list to automatically execute loading method when scrolling to the bottom.
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<ul class="infinite-list" v-infinite-scroll="load">
|
||||||
|
<li v-for="i in count" class="infinite-list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.count += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Disable Loading
|
||||||
|
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="infinite-list-wrapper">
|
||||||
|
<ul
|
||||||
|
class="list"
|
||||||
|
v-infinite-scroll="load"
|
||||||
|
infinite-scroll-disabled="disabled">
|
||||||
|
<li v-for="i in count" class="list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
<p v-if="loading">loading...</p>
|
||||||
|
<p v-if="noMore">Mo more</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 10,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
noMore () {
|
||||||
|
return this.count >= 20
|
||||||
|
},
|
||||||
|
disabled () {
|
||||||
|
return this.loading || this.noMore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.loading = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.count += 2
|
||||||
|
this.loading = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
|
| Attribute | Description | Type | Accepted values | Default |
|
||||||
|
| -------------- | ------------------------------ | --------- | ------------------------------------ | ------- |
|
||||||
|
| infinite-scroll-disabled | is disabled | boolean | - |false |
|
||||||
|
| infinite-scroll-delay | throttle delay (ms) | number | - |200 |
|
||||||
|
| infinite-scroll-distance| trigger distance (px) | number |- |0 |
|
||||||
|
| infinite-scroll-immediate |Whether to execute the loading method immediately, in case the content cannot be filled up in the initial state. | boolean | - |true |
|
|
@ -0,0 +1,87 @@
|
||||||
|
## InfiniteScroll
|
||||||
|
|
||||||
|
Load more data while reach bottom of the page
|
||||||
|
|
||||||
|
### Basic usage
|
||||||
|
Add `v-infinite-scroll` to the list to automatically execute loading method when scrolling to the bottom.
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<ul class="infinite-list" v-infinite-scroll="load">
|
||||||
|
<li v-for="i in count" class="infinite-list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.count += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Disable Loading
|
||||||
|
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="infinite-list-wrapper">
|
||||||
|
<ul
|
||||||
|
class="list"
|
||||||
|
v-infinite-scroll="load"
|
||||||
|
infinite-scroll-disabled="disabled">
|
||||||
|
<li v-for="i in count" class="list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
<p v-if="loading">loading...</p>
|
||||||
|
<p v-if="noMore">Mo more</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 10,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
noMore () {
|
||||||
|
return this.count >= 20
|
||||||
|
},
|
||||||
|
disabled () {
|
||||||
|
return this.loading || this.noMore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.loading = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.count += 2
|
||||||
|
this.loading = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
|
| Attribute | Description | Type | Accepted values | Default |
|
||||||
|
| -------------- | ------------------------------ | --------- | ------------------------------------ | ------- |
|
||||||
|
| infinite-scroll-disabled | is disabled | boolean | - |false |
|
||||||
|
| infinite-scroll-delay | throttle delay (ms) | number | - |200 |
|
||||||
|
| infinite-scroll-distance| trigger distance (px) | number |- |0 |
|
||||||
|
| infinite-scroll-immediate |Whether to execute the loading method immediately, in case the content cannot be filled up in the initial state. | boolean | - |true |
|
|
@ -0,0 +1,87 @@
|
||||||
|
## InfiniteScroll 无限滚动
|
||||||
|
|
||||||
|
滚动至底部时,加载更多数据。
|
||||||
|
|
||||||
|
### 基础用法
|
||||||
|
在要实现滚动加载的列表上上添加`v-infinite-scroll`,并赋值相应的加载方法,可实现滚动到底部时自动执行加载方法。
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<ul class="infinite-list" v-infinite-scroll="load">
|
||||||
|
<li v-for="i in count" class="infinite-list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.count += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 禁用加载
|
||||||
|
|
||||||
|
:::demo
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="infinite-list-wrapper">
|
||||||
|
<ul
|
||||||
|
class="list"
|
||||||
|
v-infinite-scroll="load"
|
||||||
|
infinite-scroll-disabled="disabled">
|
||||||
|
<li v-for="i in count" class="list-item">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
<p v-if="loading">加载中...</p>
|
||||||
|
<p v-if="noMore">没有更多了</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
count: 10,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
noMore () {
|
||||||
|
return this.count >= 20
|
||||||
|
},
|
||||||
|
disabled () {
|
||||||
|
return this.loading || this.noMore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load () {
|
||||||
|
this.loading = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.count += 2
|
||||||
|
this.loading = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||||
|
| -------------- | ------------------------------ | --------- | ------------------------------------ | ------- |
|
||||||
|
| infinite-scroll-disabled | 是否禁用 | boolean | - |false |
|
||||||
|
| infinite-scroll-delay | 节流时延,单位为ms | number | - |200 |
|
||||||
|
| infinite-scroll-distance| 触发加载的距离阈值,单位为px | number |- |0 |
|
||||||
|
| infinite-scroll-immediate | 是否立即执行加载方法,以防初始状态下内容无法撑满容器。| boolean | - |true |
|
|
@ -267,6 +267,10 @@
|
||||||
{
|
{
|
||||||
"path": "/image",
|
"path": "/image",
|
||||||
"title": "Image 图片"
|
"title": "Image 图片"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/infiniteScroll",
|
||||||
|
"title": "InfiniteScroll 无限滚动"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -541,6 +545,10 @@
|
||||||
{
|
{
|
||||||
"path": "/image",
|
"path": "/image",
|
||||||
"title": "Image"
|
"title": "Image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/infiniteScroll",
|
||||||
|
"title": "InfiniteScroll"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -815,6 +823,10 @@
|
||||||
{
|
{
|
||||||
"path": "/image",
|
"path": "/image",
|
||||||
"title": "Image"
|
"title": "Image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/infiniteScroll",
|
||||||
|
"title": "InfiniteScroll"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1089,6 +1101,10 @@
|
||||||
{
|
{
|
||||||
"path": "/image",
|
"path": "/image",
|
||||||
"title": "Image"
|
"title": "Image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/infiniteScroll",
|
||||||
|
"title": "InfiniteScroll"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import InfiniteScroll from './src/main.js';
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
InfiniteScroll.install = function(Vue) {
|
||||||
|
Vue.directive(InfiniteScroll.name, InfiniteScroll);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InfiniteScroll;
|
|
@ -0,0 +1,147 @@
|
||||||
|
import throttle from 'throttle-debounce/debounce';
|
||||||
|
import {
|
||||||
|
isHtmlElement,
|
||||||
|
isFunction,
|
||||||
|
isUndefined,
|
||||||
|
isDefined
|
||||||
|
} from 'element-ui/src/utils/types';
|
||||||
|
import {
|
||||||
|
getScrollContainer
|
||||||
|
} from 'element-ui/src/utils/dom';
|
||||||
|
|
||||||
|
const getStyleComputedProperty = (element, property) => {
|
||||||
|
if (element === window) {
|
||||||
|
element = document.documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.nodeType !== 1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// NOTE: 1 DOM access here
|
||||||
|
const css = window.getComputedStyle(element, null);
|
||||||
|
return property ? css[property] : css;
|
||||||
|
};
|
||||||
|
|
||||||
|
const entries = (obj) => {
|
||||||
|
return Object.keys(obj || {})
|
||||||
|
.map(key => ([key, obj[key]]));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPositionSize = (el, prop) => {
|
||||||
|
return el === window || el === document
|
||||||
|
? document.documentElement[prop]
|
||||||
|
: el[prop];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOffsetHeight = el => {
|
||||||
|
return getPositionSize(el, 'offsetHeight');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getClientHeight = el => {
|
||||||
|
return getPositionSize(el, 'clientHeight');
|
||||||
|
};
|
||||||
|
|
||||||
|
const scope = 'ElInfiniteScroll';
|
||||||
|
const attributes = {
|
||||||
|
delay: {
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
distance: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
immediate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScrollOptions = (el, vm) => {
|
||||||
|
if (!isHtmlElement(el)) return {};
|
||||||
|
|
||||||
|
return entries(attributes).reduce((map, [key, option]) => {
|
||||||
|
const { type, default: defaultValue } = option;
|
||||||
|
let value = el.getAttribute(`infinite-scroll-${key}`);
|
||||||
|
value = isUndefined(vm[value]) ? value : vm[value];
|
||||||
|
switch (type) {
|
||||||
|
case Number:
|
||||||
|
value = Number(value);
|
||||||
|
value = Number.isNaN(value) ? defaultValue : value;
|
||||||
|
break;
|
||||||
|
case Boolean:
|
||||||
|
value = isDefined(value) ? value === 'false' ? false : Boolean(value) : defaultValue;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = type(value);
|
||||||
|
}
|
||||||
|
map[key] = value;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getElementTop = el => el.getBoundingClientRect().top;
|
||||||
|
|
||||||
|
const handleScroll = function(cb) {
|
||||||
|
const { el, vm, container, observer } = this[scope];
|
||||||
|
const { distance, disabled } = getScrollOptions(el, vm);
|
||||||
|
|
||||||
|
if (disabled) return;
|
||||||
|
|
||||||
|
let shouldTrigger = false;
|
||||||
|
|
||||||
|
if (container === el) {
|
||||||
|
// be aware of difference between clientHeight & offsetHeight & window.getComputedStyle().height
|
||||||
|
const scrollBottom = container.scrollTop + getClientHeight(container);
|
||||||
|
shouldTrigger = container.scrollHeight - scrollBottom <= distance;
|
||||||
|
} else {
|
||||||
|
const heightBelowTop = getOffsetHeight(el) + getElementTop(el) - getElementTop(container);
|
||||||
|
const offsetHeight = getOffsetHeight(container);
|
||||||
|
const borderBottom = Number.parseFloat(getStyleComputedProperty(container, 'borderBottomWidth'));
|
||||||
|
shouldTrigger = heightBelowTop - offsetHeight + borderBottom <= distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldTrigger && isFunction(cb)) {
|
||||||
|
cb.call(vm);
|
||||||
|
} else if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
this[scope].observer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'InfiniteScroll',
|
||||||
|
inserted(el, binding, vnode) {
|
||||||
|
const cb = binding.value;
|
||||||
|
|
||||||
|
const vm = vnode.context;
|
||||||
|
// only include vertical scroll
|
||||||
|
const container = getScrollContainer(el, true);
|
||||||
|
const { delay, immediate } = getScrollOptions(el, vm);
|
||||||
|
const onScroll = throttle(delay, handleScroll.bind(el, cb));
|
||||||
|
|
||||||
|
el[scope] = { el, vm, container, onScroll };
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
container.addEventListener('scroll', onScroll);
|
||||||
|
|
||||||
|
if (immediate) {
|
||||||
|
const observer = el[scope].observer = new MutationObserver(onScroll);
|
||||||
|
observer.observe(container, { childList: true, subtree: true });
|
||||||
|
onScroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unbind(el) {
|
||||||
|
const { container, onScroll } = el[scope];
|
||||||
|
if (container) {
|
||||||
|
container.removeEventListener('scroll', onScroll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -75,6 +75,7 @@ import Link from '../packages/link/index.js';
|
||||||
import Divider from '../packages/divider/index.js';
|
import Divider from '../packages/divider/index.js';
|
||||||
import Image from '../packages/image/index.js';
|
import Image from '../packages/image/index.js';
|
||||||
import Calendar from '../packages/calendar/index.js';
|
import Calendar from '../packages/calendar/index.js';
|
||||||
|
import InfiniteScroll from '../packages/infiniteScroll/index.js';
|
||||||
import locale from 'element-ui/src/locale';
|
import locale from 'element-ui/src/locale';
|
||||||
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
|
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
|
||||||
|
|
||||||
|
@ -161,6 +162,7 @@ const install = function(Vue, opts = {}) {
|
||||||
Vue.component(component.name, component);
|
Vue.component(component.name, component);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Vue.use(InfiniteScroll);
|
||||||
Vue.use(Loading.directive);
|
Vue.use(Loading.directive);
|
||||||
|
|
||||||
Vue.prototype.$ELEMENT = {
|
Vue.prototype.$ELEMENT = {
|
||||||
|
@ -263,5 +265,6 @@ export default {
|
||||||
Link,
|
Link,
|
||||||
Divider,
|
Divider,
|
||||||
Image,
|
Image,
|
||||||
Calendar
|
Calendar,
|
||||||
|
InfiniteScroll
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,3 +9,16 @@ export function isObject(obj) {
|
||||||
export function isHtmlElement(node) {
|
export function isHtmlElement(node) {
|
||||||
return node && node.nodeType === Node.ELEMENT_NODE;
|
return node && node.nodeType === Node.ELEMENT_NODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isFunction = (functionToCheck) => {
|
||||||
|
var getType = {};
|
||||||
|
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isUndefined = (val)=> {
|
||||||
|
return val === void 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isDefined = (val) => {
|
||||||
|
return val !== undefined && val !== null;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { createVue, wait, destroyVM } from '../util';
|
||||||
|
|
||||||
|
describe('InfiniteScroll', () => {
|
||||||
|
let vm;
|
||||||
|
afterEach(() => {
|
||||||
|
destroyVM(vm);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create', async() => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<ul ref="scrollTarget" v-infinite-scroll="load" style="height: 300px;overflow: auto;">
|
||||||
|
<li v-for="i in count" style="display: flex;height: 50px;">{{ i }}</li>
|
||||||
|
</ul>
|
||||||
|
`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load() {
|
||||||
|
this.count += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
vm.$refs.scrollTarget.scrollTop = 2000;
|
||||||
|
await wait();
|
||||||
|
expect(vm.$el.innerText.indexOf('2') > -1).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -74,6 +74,7 @@ import { ElDivider } from './divider'
|
||||||
import { ElIcon } from './icon'
|
import { ElIcon } from './icon'
|
||||||
import { ElCalendar } from './calendar'
|
import { ElCalendar } from './calendar'
|
||||||
import { ElImage } from './image'
|
import { ElImage } from './image'
|
||||||
|
import { ElInfiniteScroll } from './infiniteScroll'
|
||||||
|
|
||||||
export interface InstallationOptions {
|
export interface InstallationOptions {
|
||||||
locale: any,
|
locale: any,
|
||||||
|
@ -320,3 +321,6 @@ export class Icon extends ElIcon {}
|
||||||
|
|
||||||
/** Calendar Component */
|
/** Calendar Component */
|
||||||
export class Calendar extends ElCalendar {}
|
export class Calendar extends ElCalendar {}
|
||||||
|
|
||||||
|
/** InfiniteScroll Component */
|
||||||
|
export class InfiniteScroll extends ElInfiniteScroll {}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { VNodeDirective } from 'vue'
|
||||||
|
|
||||||
|
export interface ElLoadingDirective extends VNodeDirective {
|
||||||
|
name: 'infinite-scroll',
|
||||||
|
value: Function
|
||||||
|
}
|
Loading…
Reference in New Issue