mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
refactor(Popup): refactor with composition api
This commit is contained in:
parent
0a6ead093e
commit
9465653f42
@ -1,11 +1,18 @@
|
|||||||
// Utils
|
// Utils
|
||||||
import { Teleport, Transition } from 'vue';
|
import {
|
||||||
|
watch,
|
||||||
|
computed,
|
||||||
|
reactive,
|
||||||
|
Teleport,
|
||||||
|
onMounted,
|
||||||
|
Transition,
|
||||||
|
onActivated,
|
||||||
|
onBeforeMount,
|
||||||
|
onDeactivated,
|
||||||
|
} from 'vue';
|
||||||
import { createNamespace, isDef } from '../utils';
|
import { createNamespace, isDef } from '../utils';
|
||||||
import { on, off, preventDefault } from '../utils/dom/event';
|
|
||||||
import { getScroller } from '../utils/dom/scroll';
|
|
||||||
|
|
||||||
// Mixins
|
// Mixins
|
||||||
import { TouchMixin } from '../mixins/touch';
|
|
||||||
import { CloseOnPopstateMixin } from '../mixins/close-on-popstate';
|
import { CloseOnPopstateMixin } from '../mixins/close-on-popstate';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@ -59,7 +66,7 @@ export const popupSharedProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default createComponent({
|
export default createComponent({
|
||||||
mixins: [TouchMixin, CloseOnPopstateMixin],
|
mixins: [CloseOnPopstateMixin],
|
||||||
|
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
|
||||||
@ -93,93 +100,118 @@ export default createComponent({
|
|||||||
'click-overlay',
|
'click-overlay',
|
||||||
],
|
],
|
||||||
|
|
||||||
data() {
|
setup(props, { emit, attrs, slots }) {
|
||||||
return {
|
let opened;
|
||||||
inited: this.show,
|
let shouldReopen;
|
||||||
currentZIndex: null,
|
|
||||||
|
const state = reactive({
|
||||||
|
inited: props.show,
|
||||||
|
zIndex: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const shouldRender = computed(() => state.inited || !props.lazyRender);
|
||||||
|
|
||||||
|
const lockScroll = () => {
|
||||||
|
if (props.lockScroll) {
|
||||||
|
if (!context.lockCount) {
|
||||||
|
document.body.classList.add('van-overflow-hidden');
|
||||||
|
}
|
||||||
|
context.lockCount++;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const unlockScroll = () => {
|
||||||
shouldRender() {
|
if (props.lockScroll && context.lockCount) {
|
||||||
return this.inited || !this.lazyRender;
|
context.lockCount--;
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
if (!context.lockCount) {
|
||||||
show(val) {
|
document.body.classList.remove('van-overflow-hidden');
|
||||||
const type = val ? 'open' : 'close';
|
}
|
||||||
this.inited = this.inited || this.show;
|
}
|
||||||
this[type]();
|
};
|
||||||
this.$emit(type);
|
|
||||||
},
|
|
||||||
|
|
||||||
overlay: 'renderOverlay',
|
const updateZIndex = () => {
|
||||||
},
|
state.zIndex = ++context.zIndex;
|
||||||
|
};
|
||||||
|
|
||||||
beforeCreate() {
|
const open = () => {
|
||||||
const createEmitter = (eventName) => (event) =>
|
if (opened) {
|
||||||
this.$emit(eventName, event);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.onClick = createEmitter('click');
|
if (props.zIndex !== undefined) {
|
||||||
this.onOpened = createEmitter('opened');
|
context.zIndex = props.zIndex;
|
||||||
this.onClosed = createEmitter('closed');
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
opened = true;
|
||||||
if (this.show) {
|
updateZIndex();
|
||||||
this.open();
|
lockScroll();
|
||||||
}
|
};
|
||||||
},
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
const close = () => {
|
||||||
activated() {
|
if (opened) {
|
||||||
if (this.shouldReopen) {
|
opened = false;
|
||||||
this.$emit('update:show', true);
|
unlockScroll();
|
||||||
this.shouldReopen = false;
|
emit('update:show', false);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
beforeUnmount() {
|
const onClickOverlay = () => {
|
||||||
if (this.opened) {
|
emit('click-overlay');
|
||||||
this.removeLock();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
if (props.closeOnClickOverlay) {
|
||||||
deactivated() {
|
close();
|
||||||
if (this.show) {
|
}
|
||||||
this.close();
|
};
|
||||||
this.shouldReopen = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const renderOverlay = () => {
|
||||||
genOverlay() {
|
if (props.overlay) {
|
||||||
if (this.overlay) {
|
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay
|
||||||
show={this.show}
|
show={props.show}
|
||||||
class={this.overlayClass}
|
class={props.overlayClass}
|
||||||
style={this.overlayStyle}
|
style={props.overlayStyle}
|
||||||
zIndex={this.currentZIndex}
|
zIndex={state.zIndex}
|
||||||
duration={this.duration}
|
duration={props.duration}
|
||||||
onClick={this.onClickOverlay}
|
onClick={onClickOverlay}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
genPopup() {
|
const renderCloseIcon = () => {
|
||||||
const { round, position, duration } = this;
|
if (props.closeable) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
name={props.closeIcon}
|
||||||
|
class={bem('close-icon', props.closeIconPosition)}
|
||||||
|
onClick={close}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClick = (event) => emit('click', event);
|
||||||
|
const onOpened = () => emit('opened');
|
||||||
|
const onClosed = () => emit('closed');
|
||||||
|
|
||||||
|
const renderPopup = () => {
|
||||||
|
const {
|
||||||
|
round,
|
||||||
|
position,
|
||||||
|
duration,
|
||||||
|
transition,
|
||||||
|
safeAreaInsetBottom,
|
||||||
|
} = props;
|
||||||
const isCenter = position === 'center';
|
const isCenter = position === 'center';
|
||||||
|
|
||||||
const transitionName =
|
const transitionName =
|
||||||
this.transition ||
|
transition || (isCenter ? 'van-fade' : `van-popup-slide-${position}`);
|
||||||
(isCenter ? 'van-fade' : `van-popup-slide-${position}`);
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
zIndex: this.currentZIndex,
|
zIndex: state.zIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isDef(duration)) {
|
if (isDef(duration)) {
|
||||||
@ -190,149 +222,85 @@ export default createComponent({
|
|||||||
return (
|
return (
|
||||||
<Transition
|
<Transition
|
||||||
name={transitionName}
|
name={transitionName}
|
||||||
onAfterEnter={this.onOpened}
|
onAfterEnter={onOpened}
|
||||||
onAfterLeave={this.onClosed}
|
onAfterLeave={onClosed}
|
||||||
>
|
>
|
||||||
{this.shouldRender ? (
|
{shouldRender.value ? (
|
||||||
<div
|
<div
|
||||||
vShow={this.show}
|
vShow={props.show}
|
||||||
style={style}
|
style={style}
|
||||||
class={bem({
|
class={bem({
|
||||||
round,
|
round,
|
||||||
[position]: position,
|
[position]: position,
|
||||||
'safe-area-inset-bottom': this.safeAreaInsetBottom,
|
'safe-area-inset-bottom': safeAreaInsetBottom,
|
||||||
})}
|
})}
|
||||||
onClick={this.onClick}
|
onClick={onClick}
|
||||||
{...this.$attrs}
|
{...attrs}
|
||||||
>
|
>
|
||||||
{this.$slots.default?.()}
|
{slots.default?.()}
|
||||||
{this.closeable && (
|
{renderCloseIcon()}
|
||||||
<Icon
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
name={this.closeIcon}
|
|
||||||
class={bem('close-icon', this.closeIconPosition)}
|
|
||||||
onClick={this.close}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</Transition>
|
</Transition>
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
|
|
||||||
open() {
|
watch(
|
||||||
/* istanbul ignore next */
|
() => props.show,
|
||||||
if (this.$isServer || this.opened) {
|
(value) => {
|
||||||
return;
|
if (value) {
|
||||||
}
|
state.inited = true;
|
||||||
|
open();
|
||||||
// cover default zIndex
|
emit('open');
|
||||||
if (this.zIndex !== undefined) {
|
} else {
|
||||||
context.zIndex = this.zIndex;
|
close();
|
||||||
}
|
emit('close');
|
||||||
|
|
||||||
this.opened = true;
|
|
||||||
this.renderOverlay();
|
|
||||||
this.addLock();
|
|
||||||
},
|
|
||||||
|
|
||||||
addLock() {
|
|
||||||
if (this.lockScroll) {
|
|
||||||
on(document, 'touchstart', this.touchStart);
|
|
||||||
on(document, 'touchmove', this.onTouchMove);
|
|
||||||
|
|
||||||
if (!context.lockCount) {
|
|
||||||
document.body.classList.add('van-overflow-hidden');
|
|
||||||
}
|
|
||||||
context.lockCount++;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
removeLock() {
|
|
||||||
if (this.lockScroll && context.lockCount) {
|
|
||||||
context.lockCount--;
|
|
||||||
off(document, 'touchstart', this.touchStart);
|
|
||||||
off(document, 'touchmove', this.onTouchMove);
|
|
||||||
|
|
||||||
if (!context.lockCount) {
|
|
||||||
document.body.classList.remove('van-overflow-hidden');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
|
||||||
if (!this.opened) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.opened = false;
|
|
||||||
this.removeLock();
|
|
||||||
this.$emit('update:show', false);
|
|
||||||
},
|
|
||||||
|
|
||||||
onTouchMove(event) {
|
|
||||||
this.touchMove(event);
|
|
||||||
const direction = this.deltaY > 0 ? '10' : '01';
|
|
||||||
const el = getScroller(event.target, this.$refs.root);
|
|
||||||
const { scrollHeight, offsetHeight, scrollTop } = el;
|
|
||||||
let status = '11';
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (scrollTop === 0) {
|
|
||||||
status = offsetHeight >= scrollHeight ? '00' : '01';
|
|
||||||
} else if (scrollTop + offsetHeight >= scrollHeight) {
|
|
||||||
status = '10';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (
|
|
||||||
status !== '11' &&
|
|
||||||
this.direction === 'vertical' &&
|
|
||||||
!(parseInt(status, 2) & parseInt(direction, 2))
|
|
||||||
) {
|
|
||||||
preventDefault(event, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onClickOverlay() {
|
|
||||||
this.$emit('click-overlay');
|
|
||||||
|
|
||||||
if (this.closeOnClickOverlay) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
renderOverlay() {
|
|
||||||
if (this.$isServer || !this.show) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateZIndex(this.overlay ? 1 : 0);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateZIndex(value = 0) {
|
|
||||||
this.currentZIndex = ++context.zIndex + value;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.teleport) {
|
|
||||||
return (
|
|
||||||
<Teleport to={this.teleport}>
|
|
||||||
{this.genOverlay()}
|
|
||||||
{this.genPopup()}
|
|
||||||
</Teleport>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.genOverlay()}
|
|
||||||
{this.genPopup()}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.show) {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
if (shouldReopen) {
|
||||||
|
emit('update:show', true);
|
||||||
|
shouldReopen = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onDeactivated(() => {
|
||||||
|
if (props.show) {
|
||||||
|
close();
|
||||||
|
shouldReopen = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (opened) {
|
||||||
|
unlockScroll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (props.teleport) {
|
||||||
|
return (
|
||||||
|
<Teleport to={props.teleport}>
|
||||||
|
{renderOverlay()}
|
||||||
|
{renderPopup()}
|
||||||
|
</Teleport>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderOverlay()}
|
||||||
|
{renderPopup()}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user