mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
[Improvement] Popup: support lazy render (#1138)
This commit is contained in:
parent
7f10d99d3d
commit
cb992ce979
@ -65,7 +65,7 @@
|
|||||||
{{ $t('deleteAddress') }}
|
{{ $t('deleteAddress') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
<popup v-model="showArea" position="bottom">
|
<popup v-model="showArea" position="bottom" :lazy-render="false">
|
||||||
<van-area
|
<van-area
|
||||||
ref="area"
|
ref="area"
|
||||||
:loading="!areaListLoaded"
|
:loading="!areaListLoaded"
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
@click="showList = true"
|
@click="showList = true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<van-popup v-model="showList" position="bottom">
|
<van-popup v-model="showList" position="bottom" :lazy-render="false">
|
||||||
<van-contact-list
|
<van-contact-list
|
||||||
v-model="chosenContactId"
|
v-model="chosenContactId"
|
||||||
:list="list"
|
:list="list"
|
||||||
@ -18,7 +18,7 @@
|
|||||||
/>
|
/>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
|
|
||||||
<van-popup v-model="showEdit" position="bottom">
|
<van-popup v-model="showEdit" position="bottom" :lazy-render="false">
|
||||||
<van-contact-edit
|
<van-contact-edit
|
||||||
:contact-info="editingContact"
|
:contact-info="editingContact"
|
||||||
:is-edit="isEdit"
|
:is-edit="isEdit"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
@click="showList = true"
|
@click="showList = true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<van-popup v-model="showList" position="bottom">
|
<van-popup v-model="showList" position="bottom" :lazy-render="false">
|
||||||
<van-coupon-list
|
<van-coupon-list
|
||||||
:coupons="coupons"
|
:coupons="coupons"
|
||||||
:chosen-coupon="chosenCoupon"
|
:chosen-coupon="chosenCoupon"
|
||||||
|
@ -119,7 +119,6 @@ export default {
|
|||||||
move() {
|
move() {
|
||||||
if (this.getContainer) {
|
if (this.getContainer) {
|
||||||
this.getContainer().appendChild(this.$el);
|
this.getContainer().appendChild(this.$el);
|
||||||
/* istanbul ignore if */
|
|
||||||
} else if (this.$parent) {
|
} else if (this.$parent) {
|
||||||
this.$parent.$el.appendChild(this.$el);
|
this.$parent.$el.appendChild(this.$el);
|
||||||
}
|
}
|
||||||
@ -160,7 +159,10 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
manager.close(this._popupId);
|
manager.close(this._popupId);
|
||||||
}
|
}
|
||||||
this.$el.style.zIndex = context.plusKey('zIndex');
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$el.style.zIndex = context.plusKey('zIndex');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -61,6 +61,7 @@ export default {
|
|||||||
|
|
||||||
// close popup when click modal && closeOnClickOverlay is true
|
// close popup when click modal && closeOnClickOverlay is true
|
||||||
onClick() {
|
onClick() {
|
||||||
|
/* istanbul ignore else */
|
||||||
if (context.top) {
|
if (context.top) {
|
||||||
const { vm } = context.top;
|
const { vm } = context.top;
|
||||||
vm.$emit('click-overlay');
|
vm.$emit('click-overlay');
|
||||||
|
@ -47,6 +47,7 @@ Use `position` prop to set popup display position
|
|||||||
| close-on-click-overlay | Close popup when click overlay | `Boolean` | `true` |
|
| close-on-click-overlay | Close popup when click overlay | `Boolean` | `true` |
|
||||||
| transition | Transition | `String` | `popup-slide` |
|
| transition | Transition | `String` | `popup-slide` |
|
||||||
| lock-scroll | Whether to lock background scroll | `Boolean` | `true` |
|
| lock-scroll | Whether to lock background scroll | `Boolean` | `true` |
|
||||||
|
| lazy-render | Whether to lazy render util appeared | `Boolean` | `true` |
|
||||||
| get-container | Return the mount node for Popup | `() => HTMLElement` | - |
|
| get-container | Return the mount node for Popup | `() => HTMLElement` | - |
|
||||||
|
|
||||||
### Event
|
### Event
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition :name="currentTransition">
|
<transition :name="currentTransition">
|
||||||
<div v-show="value" :class="b({ [position]: position })">
|
<div v-if="inited || !lazyRender" v-show="value" :class="b({ [position]: position })">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
@ -17,6 +17,10 @@ export default create({
|
|||||||
|
|
||||||
props: {
|
props: {
|
||||||
transition: String,
|
transition: String,
|
||||||
|
lazyRender: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
overlay: {
|
overlay: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
@ -31,10 +35,22 @@ export default create({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inited: this.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
currentTransition() {
|
currentTransition() {
|
||||||
return this.transition || (this.position === '' ? 'van-fade' : `popup-slide-${this.position}`);
|
return this.transition || (this.position === '' ? 'van-fade' : `popup-slide-${this.position}`);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value() {
|
||||||
|
this.inited = this.inited || this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -5,50 +5,18 @@ exports[`renders demo correctly 1`] = `
|
|||||||
<div>
|
<div>
|
||||||
<button class="van-button van-button--default van-button--normal">
|
<button class="van-button van-button--default van-button--normal">
|
||||||
<!----><span class="van-button__text">弹出 Popup</span></button>
|
<!----><span class="van-button__text">弹出 Popup</span></button>
|
||||||
<div class="van-popup" style="display:none;">内容</div>
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="van-button van-button--default van-button--normal">
|
<button class="van-button van-button--default van-button--normal">
|
||||||
<!----><span class="van-button__text">底部弹出</span></button>
|
<!----><span class="van-button__text">底部弹出</span></button>
|
||||||
<div class="van-popup van-popup--bottom" style="display:none;">
|
<!---->
|
||||||
<div class="van-picker">
|
|
||||||
<div class="van-hairline--top-bottom van-picker__toolbar">
|
|
||||||
<div class="van-picker__cancel">取消</div>
|
|
||||||
<!---->
|
|
||||||
<div class="van-picker__confirm">确认</div>
|
|
||||||
</div>
|
|
||||||
<!---->
|
|
||||||
<div class="van-picker__columns" style="height:220px;">
|
|
||||||
<div class="van-picker-column" style="height:220px;">
|
|
||||||
<ul style="transition:0ms;transform:translate3d(0, 88px, 0);line-height:44px;">
|
|
||||||
<li class="van-ellipsis van-picker-column__item van-picker-column__item--selected">杭州</li>
|
|
||||||
<li class="van-ellipsis van-picker-column__item">宁波</li>
|
|
||||||
<li class="van-ellipsis van-picker-column__item">温州</li>
|
|
||||||
<li class="van-ellipsis van-picker-column__item">嘉兴</li>
|
|
||||||
<li class="van-ellipsis van-picker-column__item">湖州</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="van-hairline--top-bottom van-picker__frame" style="height:44px;"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="van-button van-button--default van-button--normal">
|
<button class="van-button van-button--default van-button--normal">
|
||||||
<!----><span class="van-button__text">顶部弹出</span></button>
|
<!----><span class="van-button__text">顶部弹出</span></button>
|
||||||
<div class="van-popup van-popup--top" style="display:none;">
|
<!---->
|
||||||
内容
|
|
||||||
</div>
|
|
||||||
<button class="van-button van-button--default van-button--normal">
|
<button class="van-button van-button--default van-button--normal">
|
||||||
<!----><span class="van-button__text">右侧弹出</span></button>
|
<!----><span class="van-button__text">右侧弹出</span></button>
|
||||||
<div class="van-popup van-popup--right" style="display:none;">
|
<!---->
|
||||||
<button class="van-button van-button--default van-button--normal">
|
|
||||||
<!----><span class="van-button__text">关闭弹层</span></button>
|
|
||||||
<button class="van-button van-button--default van-button--normal">
|
|
||||||
<!----><span class="van-button__text">右侧弹出</span></button>
|
|
||||||
<div class="van-popup van-popup--right" style="display:none;">
|
|
||||||
<button class="van-button van-button--default van-button--normal">
|
|
||||||
<!----><span class="van-button__text">关闭弹层</span></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
3
packages/popup/test/__snapshots__/index.spec.js.snap
Normal file
3
packages/popup/test/__snapshots__/index.spec.js.snap
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`reset z-index 1`] = `<div class="van-popup" name="van-fade"></div>`;
|
126
packages/popup/test/index.spec.js
Normal file
126
packages/popup/test/index.spec.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import Popup from '../';
|
||||||
|
import { mount, TransitionStub } from '@vue/test-utils';
|
||||||
|
import { triggerDrag } from '../../../test/touch-utils';
|
||||||
|
|
||||||
|
Vue.component('transition', TransitionStub);
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.vm.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('lazy render', () => {
|
||||||
|
wrapper = mount(Popup);
|
||||||
|
expect(wrapper.vm.$el.tagName).toBeFalsy();
|
||||||
|
wrapper.vm.value = true;
|
||||||
|
expect(wrapper.vm.$el.tagName).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reset z-index', () => {
|
||||||
|
wrapper = mount(Popup, {
|
||||||
|
propsData: {
|
||||||
|
value: true,
|
||||||
|
zIndex: 10,
|
||||||
|
lockScroll: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('popup lock scroll', () => {
|
||||||
|
const wrapper1 = mount(Popup, {
|
||||||
|
propsData: {
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(document.body.classList.contains('van-overflow-hidden')).toBeTruthy();
|
||||||
|
triggerDrag(document, 0, 100);
|
||||||
|
triggerDrag(document, 0, -150);
|
||||||
|
|
||||||
|
const wrapper2 = mount(Popup, {
|
||||||
|
propsData: {
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wrapper1.vm.$destroy();
|
||||||
|
expect(document.body.classList.contains('van-overflow-hidden')).toBeTruthy();
|
||||||
|
|
||||||
|
wrapper2.vm.$destroy();
|
||||||
|
expect(document.body.classList.contains('van-overflow-hidden')).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get container with parent', () => {
|
||||||
|
const div1 = document.createElement('div');
|
||||||
|
const div2 = document.createElement('div');
|
||||||
|
wrapper = mount({
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<popup :value="true" :get-container="getContainer" />
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
components: {
|
||||||
|
Popup
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
getContainer: () => div1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const popup = wrapper.find('.van-popup').element;
|
||||||
|
|
||||||
|
expect(popup.parentNode).toEqual(div1);
|
||||||
|
wrapper.vm.getContainer = () => div2;
|
||||||
|
expect(popup.parentNode).toEqual(div2);
|
||||||
|
wrapper.vm.getContainer = null;
|
||||||
|
expect(popup.parentNode).toEqual(wrapper.element);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get container without parent', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
wrapper = mount(Popup, {
|
||||||
|
propsData: {
|
||||||
|
getContainer: () => div
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const popup = wrapper.element;
|
||||||
|
expect(popup.parentNode).toEqual(div);
|
||||||
|
wrapper.vm.getContainer = null;
|
||||||
|
expect(popup.parentNode).toEqual(div);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('render overlay', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
wrapper = mount(Popup, {
|
||||||
|
propsData: {
|
||||||
|
value: true,
|
||||||
|
overlay: false,
|
||||||
|
getContainer: () => div
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(div.querySelector('.van-modal')).toBeFalsy();
|
||||||
|
wrapper.vm.overlay = true;
|
||||||
|
expect(div.querySelector('.van-modal')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('close on click modal', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
wrapper = mount(Popup, {
|
||||||
|
propsData: {
|
||||||
|
value: true,
|
||||||
|
getContainer: () => div
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.$on('input', val => {
|
||||||
|
wrapper.vm.value = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
const modal = div.querySelector('.van-modal');
|
||||||
|
triggerDrag(modal, 0, -30);
|
||||||
|
modal.click();
|
||||||
|
expect(wrapper.vm.value).toBeFalsy();
|
||||||
|
});
|
@ -47,6 +47,7 @@ export default {
|
|||||||
| overlay-style | 自定义蒙层样式 | `Object` | `` |
|
| overlay-style | 自定义蒙层样式 | `Object` | `` |
|
||||||
| close-on-click-overlay | 点击蒙层是否关闭 Popup | `Boolean` | `true` |
|
| close-on-click-overlay | 点击蒙层是否关闭 Popup | `Boolean` | `true` |
|
||||||
| transition | transition 名称 | `String` | `popup-slide` |
|
| transition | transition 名称 | `String` | `popup-slide` |
|
||||||
|
| lazy-render | 是否在首次显示弹层时才渲染 DOM 节点 | `Boolean` | `true` |
|
||||||
| get-container | 指定弹出层挂载的 HTML 节点 | `() => HTMLElement` | - |
|
| get-container | 指定弹出层挂载的 HTML 节点 | `() => HTMLElement` | - |
|
||||||
|
|
||||||
### Event
|
### Event
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`renders demo correctly 1`] = `""`;
|
exports[`renders demo correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div class="sku-container">
|
||||||
|
<!---->
|
||||||
|
<button class="van-button van-button--primary van-button--normal van-button--block">
|
||||||
|
<!----><span class="van-button__text">基础用法</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="sku-container">
|
||||||
|
<!---->
|
||||||
|
<button class="van-button van-button--primary van-button--normal van-button--block">
|
||||||
|
<!----><span class="van-button__text">自定义步进器相关配置</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="sku-container">
|
||||||
|
<!---->
|
||||||
|
<button class="van-button van-button--primary van-button--normal van-button--block">
|
||||||
|
<!----><span class="van-button__text">高级用法</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="b('pane')" v-show="isSelected">
|
<div :class="b('pane')" v-show="isSelected">
|
||||||
<slot v-if="slotInited" />
|
<slot v-if="inited" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ export default create({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
slotInited: false
|
inited: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -36,9 +36,7 @@ export default create({
|
|||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'parent.curActive'() {
|
'parent.curActive'() {
|
||||||
if (this.isSelected) {
|
this.inited = this.inited || this.isSelected;
|
||||||
this.slotInited = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
34
test/touch-utils.js
Normal file
34
test/touch-utils.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Trigger touch event
|
||||||
|
export function triggerTouch(wrapper, eventName, x = 0, y = 0) {
|
||||||
|
const el = wrapper.element ? wrapper.element : wrapper;
|
||||||
|
const touch = {
|
||||||
|
identifier: Date.now(),
|
||||||
|
target: el,
|
||||||
|
pageX: x,
|
||||||
|
pageY: y,
|
||||||
|
clientX: x,
|
||||||
|
clientY: y,
|
||||||
|
radiusX: 2.5,
|
||||||
|
radiusY: 2.5,
|
||||||
|
rotationAngle: 10,
|
||||||
|
force: 0.5
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = document.createEvent('CustomEvent');
|
||||||
|
event.initCustomEvent(eventName, true, true, {});
|
||||||
|
event.touches = [touch];
|
||||||
|
event.targetTouches = [touch];
|
||||||
|
event.changedTouches = [touch];
|
||||||
|
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// simulate drag gesture
|
||||||
|
export function triggerDrag(el, x = 0, y = 0) {
|
||||||
|
triggerTouch(el, 'touchstart', 0, 0);
|
||||||
|
triggerTouch(el, 'touchmove', x / 4, y / 4);
|
||||||
|
triggerTouch(el, 'touchmove', x / 3, y / 3);
|
||||||
|
triggerTouch(el, 'touchmove', x / 2, y / 2);
|
||||||
|
triggerTouch(el, 'touchmove', x, y);
|
||||||
|
triggerTouch(el, 'touchend', x, y);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user