mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-24 02:16:12 +08:00
refactor(List): refactor with composition api
This commit is contained in:
parent
67f418ab07
commit
ecb319625b
@ -9,7 +9,7 @@ import {
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
export function useGlobalEvent(
|
export function useGlobalEvent(
|
||||||
target: EventTarget,
|
target: Ref<EventTarget>,
|
||||||
event: string,
|
event: string,
|
||||||
handler: EventListener,
|
handler: EventListener,
|
||||||
passive = false,
|
passive = false,
|
||||||
@ -18,23 +18,23 @@ export function useGlobalEvent(
|
|||||||
let binded: boolean;
|
let binded: boolean;
|
||||||
|
|
||||||
function add() {
|
function add() {
|
||||||
if (binded || (flag && !flag.value)) {
|
if (binded || (flag && !flag.value) || !target.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
on(target, event, handler, passive);
|
on(target.value, event, handler, passive);
|
||||||
binded = true;
|
binded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove() {
|
function remove() {
|
||||||
if (binded) {
|
if (binded && target.value) {
|
||||||
off(target, event, handler);
|
off(target.value, event, handler);
|
||||||
binded = false;
|
binded = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag) {
|
if (flag) {
|
||||||
watch(() => {
|
watch(flag, () => {
|
||||||
flag.value ? add() : remove();
|
flag.value ? add() : remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
|
import {
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
nextTick,
|
||||||
|
onUpdated,
|
||||||
|
onMounted,
|
||||||
|
getCurrentInstance,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { createNamespace } from '../utils';
|
import { createNamespace } from '../utils';
|
||||||
import { isHidden } from '../utils/dom/style';
|
import { isHidden } from '../utils/dom/style';
|
||||||
import { getScroller } from '../utils/dom/scroll';
|
import { getScroller } from '../utils/dom/scroll';
|
||||||
|
|
||||||
// Mixins
|
// Composition
|
||||||
import { BindEventMixin } from '../mixins/bind-event';
|
import { useRect } from '../composition/use-rect';
|
||||||
|
import { useGlobalEvent } from '../composition/use-global-event';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Loading from '../loading';
|
import Loading from '../loading';
|
||||||
@ -12,16 +22,6 @@ import Loading from '../loading';
|
|||||||
const [createComponent, bem, t] = createNamespace('list');
|
const [createComponent, bem, t] = createNamespace('list');
|
||||||
|
|
||||||
export default createComponent({
|
export default createComponent({
|
||||||
mixins: [
|
|
||||||
BindEventMixin(function (bind) {
|
|
||||||
if (!this.scroller) {
|
|
||||||
this.scroller = getScroller(this.$el);
|
|
||||||
}
|
|
||||||
|
|
||||||
bind(this.scroller, 'scroll', this.check);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
error: Boolean,
|
error: Boolean,
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
@ -45,37 +45,21 @@ export default createComponent({
|
|||||||
|
|
||||||
emits: ['load', 'update:error', 'update:loading'],
|
emits: ['load', 'update:error', 'update:loading'],
|
||||||
|
|
||||||
data() {
|
setup(props, { emit, slots }) {
|
||||||
return {
|
// use sync innerLoading state to avoid repeated loading in some edge cases
|
||||||
// use sync innerLoading state to avoid repeated loading in some edge cases
|
const loading = ref(false);
|
||||||
innerLoading: this.loading,
|
const rootRef = ref();
|
||||||
};
|
const scrollerRef = ref();
|
||||||
},
|
const placeholderRef = ref();
|
||||||
|
|
||||||
updated() {
|
const check = () => {
|
||||||
this.innerLoading = this.loading;
|
nextTick(() => {
|
||||||
},
|
if (loading.value || props.finished || props.error) {
|
||||||
|
|
||||||
mounted() {
|
|
||||||
if (this.immediateCheck) {
|
|
||||||
this.check();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
loading: 'check',
|
|
||||||
finished: 'check',
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// @exposed-api
|
|
||||||
check() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.innerLoading || this.finished || this.error) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { $el: el, scroller, offset, direction } = this;
|
const scroller = scrollerRef.value;
|
||||||
|
const { offset, direction } = props;
|
||||||
let scrollerRect;
|
let scrollerRect;
|
||||||
|
|
||||||
if (scroller.getBoundingClientRect) {
|
if (scroller.getBoundingClientRect) {
|
||||||
@ -90,12 +74,12 @@ export default createComponent({
|
|||||||
const scrollerHeight = scrollerRect.bottom - scrollerRect.top;
|
const scrollerHeight = scrollerRect.bottom - scrollerRect.top;
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (!scrollerHeight || isHidden(el)) {
|
if (!scrollerHeight || isHidden(rootRef.value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isReachEdge = false;
|
let isReachEdge = false;
|
||||||
const placeholderRect = this.$refs.placeholder.getBoundingClientRect();
|
const placeholderRect = useRect(placeholderRef);
|
||||||
|
|
||||||
if (direction === 'up') {
|
if (direction === 'up') {
|
||||||
isReachEdge = scrollerRect.top - placeholderRect.top <= offset;
|
isReachEdge = scrollerRect.top - placeholderRect.top <= offset;
|
||||||
@ -104,71 +88,89 @@ export default createComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isReachEdge) {
|
if (isReachEdge) {
|
||||||
this.innerLoading = true;
|
loading.value = true;
|
||||||
this.$emit('update:loading', true);
|
emit('update:loading', true);
|
||||||
this.$emit('load');
|
emit('load');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
clickErrorText() {
|
|
||||||
this.$emit('update:error', false);
|
|
||||||
this.check();
|
|
||||||
},
|
|
||||||
|
|
||||||
genLoading() {
|
|
||||||
if (this.innerLoading && !this.finished) {
|
|
||||||
return (
|
|
||||||
<div class={bem('loading')} key="loading">
|
|
||||||
{this.$slots.loading ? (
|
|
||||||
this.$slots.loading()
|
|
||||||
) : (
|
|
||||||
<Loading size="16">{this.loadingText || t('loading')}</Loading>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
genFinishedText() {
|
|
||||||
if (this.finished) {
|
|
||||||
const text = this.$slots.finished
|
|
||||||
? this.$slots.finished()
|
|
||||||
: this.finishedText;
|
|
||||||
|
|
||||||
|
const renderFinishedText = () => {
|
||||||
|
if (props.finished) {
|
||||||
|
const text = slots.finished ? slots.finished() : props.finishedText;
|
||||||
if (text) {
|
if (text) {
|
||||||
return <div class={bem('finished-text')}>{text}</div>;
|
return <div class={bem('finished-text')}>{text}</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
genErrorText() {
|
const clickErrorText = () => {
|
||||||
if (this.error) {
|
emit('update:error', false);
|
||||||
const text = this.$slots.error ? this.$slots.error() : this.errorText;
|
check();
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderErrorText = () => {
|
||||||
|
if (props.error) {
|
||||||
|
const text = slots.error ? slots.error() : props.errorText;
|
||||||
if (text) {
|
if (text) {
|
||||||
return (
|
return (
|
||||||
<div onClick={this.clickErrorText} class={bem('error-text')}>
|
<div onClick={clickErrorText} class={bem('error-text')}>
|
||||||
{text}
|
{text}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
const renderLoading = () => {
|
||||||
const Placeholder = <div ref="placeholder" class={bem('placeholder')} />;
|
if (loading.value && !props.finished) {
|
||||||
const Content = this.$slots.default?.();
|
return (
|
||||||
|
<div class={bem('loading')} key="loading">
|
||||||
|
{slots.loading ? (
|
||||||
|
slots.loading()
|
||||||
|
) : (
|
||||||
|
<Loading size="16">{props.loadingText || t('loading')}</Loading>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
watch([() => props.loading, () => props.finished], check);
|
||||||
<div class={bem()} role="feed" aria-busy={this.innerLoading}>
|
|
||||||
{this.direction === 'down' ? Content : Placeholder}
|
onUpdated(() => {
|
||||||
{this.genLoading()}
|
loading.value = props.loading;
|
||||||
{this.genFinishedText()}
|
});
|
||||||
{this.genErrorText()}
|
|
||||||
{this.direction === 'up' ? Content : Placeholder}
|
onMounted(() => {
|
||||||
</div>
|
scrollerRef.value = getScroller(rootRef.value);
|
||||||
);
|
|
||||||
|
if (props.immediateCheck) {
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useGlobalEvent(scrollerRef, 'scroll', check);
|
||||||
|
|
||||||
|
// @exposed-api
|
||||||
|
const vm = getCurrentInstance().proxy;
|
||||||
|
vm.check = check;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const Content = slots.default?.();
|
||||||
|
const Placeholder = (
|
||||||
|
<div ref={placeholderRef} class={bem('placeholder')} />
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={rootRef} role="feed" class={bem()} aria-busy={loading.value}>
|
||||||
|
{props.direction === 'down' ? Content : Placeholder}
|
||||||
|
{renderLoading()}
|
||||||
|
{renderFinishedText()}
|
||||||
|
{renderErrorText()}
|
||||||
|
{props.direction === 'up' ? Content : Placeholder}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user