feat(Popup): add destroy-on-close prop (#13223)

This commit is contained in:
inottn 2024-11-24 19:52:49 +08:00 committed by GitHub
parent b41f8cd14c
commit 6dc6b4b5ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 152 additions and 28 deletions

View File

@ -13,5 +13,6 @@ export function useLazyRender(show: WatchSource<boolean | undefined>) {
{ immediate: true }, { immediate: true },
); );
return (render: () => JSX.Element) => () => (inited.value ? render() : null); return (render: () => JSX.Element | undefined) => () =>
inited.value ? render() : null;
} }

View File

@ -328,8 +328,9 @@ export default {
placeholder="Select city" placeholder="Select city"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" position="bottom"> <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
<van-picker <van-picker
:model-value="pickerValue"
:columns="columns" :columns="columns"
@confirm="onConfirm" @confirm="onConfirm"
@cancel="showPicker = false" @cancel="showPicker = false"
@ -343,6 +344,7 @@ import { ref } from 'vue';
export default { export default {
setup() { setup() {
const result = ref(''); const result = ref('');
const pickerValue = ref([]);
const showPicker = ref(false); const showPicker = ref(false);
const columns = [ const columns = [
{ text: 'Delaware', value: 'Delaware' }, { text: 'Delaware', value: 'Delaware' },
@ -352,13 +354,15 @@ export default {
{ text: 'Maine', value: 'Maine' }, { text: 'Maine', value: 'Maine' },
]; ];
const onConfirm = ({ selectedOptions }) => { const onConfirm = ({ selectedValues, selectedOptions }) => {
result.value = selectedOptions[0]?.text; result.value = selectedOptions[0]?.text;
pickerValue.value = selectedValues;
showPicker.value = false; showPicker.value = false;
}; };
return { return {
result, result,
pickerValue,
columns, columns,
onConfirm, onConfirm,
showPicker, showPicker,
@ -379,8 +383,12 @@ export default {
placeholder="Select date" placeholder="Select date"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" position="bottom"> <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
<van-date-picker @confirm="onConfirm" @cancel="showPicker = false" /> <van-date-picker
:model-value="pickerValue"
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</van-popup> </van-popup>
``` ```
@ -391,13 +399,16 @@ export default {
setup() { setup() {
const result = ref(''); const result = ref('');
const showPicker = ref(false); const showPicker = ref(false);
const pickerValue = ref<string[]>([]);
const onConfirm = ({ selectedValues }) => { const onConfirm = ({ selectedValues }) => {
result.value = selectedValues.join('/'); result.value = selectedValues.join('/');
pickerValue.value = selectedValues;
showPicker.value = false; showPicker.value = false;
}; };
return { return {
result, result,
pickerValue,
onConfirm, onConfirm,
showPicker, showPicker,
}; };
@ -417,9 +428,10 @@ export default {
placeholder="Select area" placeholder="Select area"
@click="showArea = true" @click="showArea = true"
/> />
<van-popup v-model:show="showArea" position="bottom"> <van-popup v-model:show="showArea" destroy-on-close position="bottom">
<van-area <van-area
:area-list="areaList" :area-list="areaList"
:model-value="pickerValue"
@confirm="onConfirm" @confirm="onConfirm"
@cancel="showArea = false" @cancel="showArea = false"
/> />
@ -434,7 +446,11 @@ export default {
setup() { setup() {
const result = ref(''); const result = ref('');
const showArea = ref(false); const showArea = ref(false);
const onConfirm = ({ selectedOptions }) => { const pickerValue = ref('');
const onConfirm = ({ selectedValues, selectedOptions }) => {
pickerValue.value = selectedValues.length
? selectedValues[selectedValues.length - 1]
: '';
showArea.value = false; showArea.value = false;
result.value = selectedOptions.map((item) => item.text).join('/'); result.value = selectedOptions.map((item) => item.text).join('/');
}; };
@ -442,6 +458,7 @@ export default {
return { return {
result, result,
areaList, areaList,
pickerValue,
showArea, showArea,
onConfirm, onConfirm,
}; };

View File

@ -354,9 +354,10 @@ export default {
placeholder="点击选择城市" placeholder="点击选择城市"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" position="bottom"> <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
<van-picker <van-picker
:columns="columns" :columns="columns"
:model-value="pickerValue"
@confirm="onConfirm" @confirm="onConfirm"
@cancel="showPicker = false" @cancel="showPicker = false"
/> />
@ -369,6 +370,7 @@ import { ref } from 'vue';
export default { export default {
setup() { setup() {
const result = ref(''); const result = ref('');
const pickerValue = ref([]);
const showPicker = ref(false); const showPicker = ref(false);
const columns = [ const columns = [
{ text: '杭州', value: 'Hangzhou' }, { text: '杭州', value: 'Hangzhou' },
@ -378,13 +380,15 @@ export default {
{ text: '湖州', value: 'Huzhou' }, { text: '湖州', value: 'Huzhou' },
]; ];
const onConfirm = ({ selectedOptions }) => { const onConfirm = ({ selectedValues, selectedOptions }) => {
result.value = selectedOptions[0]?.text; result.value = selectedOptions[0]?.text;
pickerValue.value = selectedValues;
showPicker.value = false; showPicker.value = false;
}; };
return { return {
result, result,
pickerValue,
columns, columns,
onConfirm, onConfirm,
showPicker, showPicker,
@ -407,8 +411,12 @@ export default {
placeholder="点击选择时间" placeholder="点击选择时间"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" position="bottom"> <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
<van-date-picker @confirm="onConfirm" @cancel="showPicker = false" /> <van-date-picker
:model-value="pickerValue"
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</van-popup> </van-popup>
``` ```
@ -419,13 +427,16 @@ export default {
setup() { setup() {
const result = ref(''); const result = ref('');
const showPicker = ref(false); const showPicker = ref(false);
const pickerValue = ref([]);
const onConfirm = ({ selectedValues }) => { const onConfirm = ({ selectedValues }) => {
result.value = selectedValues.join('/'); result.value = selectedValues.join('/');
pickerValue.value = selectedValues;
showPicker.value = false; showPicker.value = false;
}; };
return { return {
result, result,
pickerValue,
onConfirm, onConfirm,
showPicker, showPicker,
}; };
@ -447,9 +458,10 @@ export default {
placeholder="点击选择省市区" placeholder="点击选择省市区"
@click="showArea = true" @click="showArea = true"
/> />
<van-popup v-model:show="showArea" position="bottom"> <van-popup v-model:show="showArea" destroy-on-close position="bottom">
<van-area <van-area
:area-list="areaList" :area-list="areaList"
:model-value="pickerValue"
@confirm="onConfirm" @confirm="onConfirm"
@cancel="showArea = false" @cancel="showArea = false"
/> />
@ -464,7 +476,11 @@ export default {
setup() { setup() {
const result = ref(''); const result = ref('');
const showArea = ref(false); const showArea = ref(false);
const onConfirm = ({ selectedOptions }) => { const pickerValue = ref([]);
const onConfirm = ({ selectedValues, selectedOptions }) => {
pickerValue.value = selectedValues.length
? selectedValues[selectedValues.length - 1]
: '';
showArea.value = false; showArea.value = false;
result.value = selectedOptions.map((item) => item.text).join('/'); result.value = selectedOptions.map((item) => item.text).join('/');
}; };
@ -472,6 +488,7 @@ export default {
return { return {
result, result,
areaList, areaList,
pickerValue,
showArea, showArea,
onConfirm, onConfirm,
}; };

View File

@ -22,10 +22,17 @@ const t = useTranslate({
}); });
const areaCode = ref(''); const areaCode = ref('');
const pickerValue = ref('');
const showArea = ref(false); const showArea = ref(false);
const onConfirm = ({ selectedOptions }: PickerConfirmEventParams) => { const onConfirm = ({
selectedValues,
selectedOptions,
}: PickerConfirmEventParams) => {
areaCode.value = selectedOptions.map((item) => item!.text).join('/'); areaCode.value = selectedOptions.map((item) => item!.text).join('/');
pickerValue.value = selectedValues.length
? (selectedValues[selectedValues.length - 1] as string)
: '';
showArea.value = false; showArea.value = false;
}; };
@ -44,9 +51,16 @@ const onCancel = () => {
:placeholder="t('placeholder')" :placeholder="t('placeholder')"
@click="showArea = true" @click="showArea = true"
/> />
<van-popup v-model:show="showArea" round position="bottom" teleport="body"> <van-popup
v-model:show="showArea"
destroy-on-close
round
position="bottom"
teleport="body"
>
<van-area <van-area
:area-list="t('areaList')" :area-list="t('areaList')"
:model-value="pickerValue"
@confirm="onConfirm" @confirm="onConfirm"
@cancel="onCancel" @cancel="onCancel"
/> />

View File

@ -18,10 +18,12 @@ const t = useTranslate({
}); });
const result = ref(''); const result = ref('');
const pickerValue = ref<string[]>([]);
const showPicker = ref(false); const showPicker = ref(false);
const onConfirm = ({ selectedValues }: PickerConfirmEventParams) => { const onConfirm = ({ selectedValues }: PickerConfirmEventParams) => {
result.value = selectedValues.join('/'); result.value = selectedValues.join('/');
pickerValue.value = selectedValues as string[];
showPicker.value = false; showPicker.value = false;
}; };
@ -40,7 +42,17 @@ const onCancel = () => {
:placeholder="t('placeholder')" :placeholder="t('placeholder')"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" round position="bottom" teleport="body"> <van-popup
<van-date-picker @confirm="onConfirm" @cancel="onCancel" /> v-model:show="showPicker"
destroy-on-close
round
position="bottom"
teleport="body"
>
<van-date-picker
:model-value="pickerValue"
@confirm="onConfirm"
@cancel="onCancel"
/>
</van-popup> </van-popup>
</template> </template>

View File

@ -21,10 +21,15 @@ const t = useTranslate({
}); });
const result = ref<Numeric>(''); const result = ref<Numeric>('');
const pickerValue = ref<Numeric[]>([]);
const showPicker = ref(false); const showPicker = ref(false);
const onConfirm = ({ selectedOptions }: PickerConfirmEventParams) => { const onConfirm = ({
selectedValues,
selectedOptions,
}: PickerConfirmEventParams) => {
result.value = selectedOptions[0]?.text || ''; result.value = selectedOptions[0]?.text || '';
pickerValue.value = selectedValues;
showPicker.value = false; showPicker.value = false;
}; };
@ -43,8 +48,15 @@ const onCancel = () => {
:placeholder="t('placeholder')" :placeholder="t('placeholder')"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" round position="bottom" teleport="body"> <van-popup
v-model:show="showPicker"
destroy-on-close
round
position="bottom"
teleport="body"
>
<van-picker <van-picker
:model-value="pickerValue"
:columns="t('textColumns')" :columns="t('textColumns')"
@confirm="onConfirm" @confirm="onConfirm"
@cancel="onCancel" @cancel="onCancel"

View File

@ -71,8 +71,9 @@ export default {
placeholder="Choose City" placeholder="Choose City"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" round position="bottom"> <van-popup v-model:show="showPicker" destroy-on-close round position="bottom">
<van-picker <van-picker
:model-value="pickerValue"
title="Title" title="Title"
:columns="columns" :columns="columns"
@cancel="showPicker = false" @cancel="showPicker = false"
@ -94,10 +95,12 @@ export default {
{ text: 'Maine', value: 'Maine' }, { text: 'Maine', value: 'Maine' },
]; ];
const fieldValue = ref(''); const fieldValue = ref('');
const pickerValue = ref<Numeric[]>([]);
const showPicker = ref(false); const showPicker = ref(false);
const onConfirm = ({ selectedOptions }) => { const onConfirm = ({ selectedValues, selectedOptions }) => {
showPicker.value = false; showPicker.value = false;
pickerValue.value = selectedValues;
fieldValue.value = selectedOptions[0].text; fieldValue.value = selectedOptions[0].text;
}; };

View File

@ -81,8 +81,9 @@ export default {
placeholder="选择城市" placeholder="选择城市"
@click="showPicker = true" @click="showPicker = true"
/> />
<van-popup v-model:show="showPicker" round position="bottom"> <van-popup v-model:show="showPicker" destroy-on-close round position="bottom">
<van-picker <van-picker
:model-value="pickerValue"
:columns="columns" :columns="columns"
@cancel="showPicker = false" @cancel="showPicker = false"
@confirm="onConfirm" @confirm="onConfirm"
@ -104,9 +105,10 @@ export default {
]; ];
const fieldValue = ref(''); const fieldValue = ref('');
const showPicker = ref(false); const showPicker = ref(false);
const pickerValue = ref<Numeric[]>([]);
const onConfirm = ({ selectedOptions }) => { const onConfirm = ({ selectedValues, selectedOptions }) => {
showPicker.value = false; showPicker.value = false;
pickerValue.value = selectedValues;
fieldValue.value = selectedOptions[0].text; fieldValue.value = selectedOptions[0].text;
}; };

View File

@ -6,6 +6,7 @@ import VanPopup from '../../popup';
import { basicColumns } from './data'; import { basicColumns } from './data';
import { useTranslate } from '../../../docs/site'; import { useTranslate } from '../../../docs/site';
import type { PickerConfirmEventParams } from '../types'; import type { PickerConfirmEventParams } from '../types';
import type { Numeric } from '../../utils';
const t = useTranslate({ const t = useTranslate({
'zh-CN': { 'zh-CN': {
@ -24,6 +25,7 @@ const t = useTranslate({
const showPicker = ref(false); const showPicker = ref(false);
const fieldValue = ref(''); const fieldValue = ref('');
const pickerValue = ref<Numeric[]>([]);
const onClickField = () => { const onClickField = () => {
showPicker.value = true; showPicker.value = true;
@ -31,8 +33,12 @@ const onClickField = () => {
const onCancel = () => { const onCancel = () => {
showPicker.value = false; showPicker.value = false;
}; };
const onConfirm = ({ selectedOptions }: PickerConfirmEventParams) => { const onConfirm = ({
selectedValues,
selectedOptions,
}: PickerConfirmEventParams) => {
showPicker.value = false; showPicker.value = false;
pickerValue.value = selectedValues;
fieldValue.value = selectedOptions[0]!.text as string; fieldValue.value = selectedOptions[0]!.text as string;
}; };
</script> </script>
@ -47,8 +53,14 @@ const onConfirm = ({ selectedOptions }: PickerConfirmEventParams) => {
:placeholder="t('chooseCity')" :placeholder="t('chooseCity')"
@click="onClickField" @click="onClickField"
/> />
<van-popup v-model:show="showPicker" round position="bottom"> <van-popup
v-model:show="showPicker"
destroy-on-close
round
position="bottom"
>
<van-picker <van-picker
:model-value="pickerValue"
:title="t('title')" :title="t('title')"
:columns="t('basicColumns')" :columns="t('basicColumns')"
@cancel="onCancel" @cancel="onCancel"

View File

@ -50,6 +50,7 @@ export const popupProps = extend({}, popupSharedProps, {
iconPrefix: String, iconPrefix: String,
closeOnPopstate: Boolean, closeOnPopstate: Boolean,
closeIconPosition: makeStringProp<PopupCloseIconPosition>('top-right'), closeIconPosition: makeStringProp<PopupCloseIconPosition>('top-right'),
destroyOnClose: Boolean,
safeAreaInsetTop: Boolean, safeAreaInsetTop: Boolean,
safeAreaInsetBottom: Boolean, safeAreaInsetBottom: Boolean,
}); });
@ -186,11 +187,22 @@ export default defineComponent({
const onKeydown = (event: KeyboardEvent) => emit('keydown', event); const onKeydown = (event: KeyboardEvent) => emit('keydown', event);
const renderPopup = lazyRender(() => { const renderPopup = lazyRender(() => {
const { round, position, safeAreaInsetTop, safeAreaInsetBottom } = props; const {
destroyOnClose,
round,
position,
safeAreaInsetTop,
safeAreaInsetBottom,
show,
} = props;
if (!show && destroyOnClose) {
return;
}
return ( return (
<div <div
v-show={props.show} v-show={show}
ref={popupRef} ref={popupRef}
style={style.value} style={style.value}
role="dialog" role="dialog"

View File

@ -226,6 +226,7 @@ Use `teleport` prop to specify mount location.
| duration | Transition duration, unit second | _number \| string_ | `0.3` | | duration | Transition duration, unit second | _number \| string_ | `0.3` |
| z-index | Set the z-index to a fixed value | _number \| string_ | `2000+` | | z-index | Set the z-index to a fixed value | _number \| string_ | `2000+` |
| round | Whether to show round corner | _boolean_ | `false` | | round | Whether to show round corner | _boolean_ | `false` |
| destroy-on-close `v4.9.10` | Whether to destroy content when closed | _boolean_ | `false` |
| 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` | | lazy-render | Whether to lazy render util appeared | _boolean_ | `true` |
| close-on-popstate | Whether to close when popstate | _boolean_ | `false` | | close-on-popstate | Whether to close when popstate | _boolean_ | `false` |

View File

@ -228,6 +228,7 @@ export default {
| duration | 动画时长,单位秒,设置为 0 可以禁用动画 | _number \| string_ | `0.3` | | duration | 动画时长,单位秒,设置为 0 可以禁用动画 | _number \| string_ | `0.3` |
| z-index | 将弹窗的 z-index 层级设置为一个固定值 | _number \| string_ | `2000+` | | z-index | 将弹窗的 z-index 层级设置为一个固定值 | _number \| string_ | `2000+` |
| round | 是否显示圆角 | _boolean_ | `false` | | round | 是否显示圆角 | _boolean_ | `false` |
| destroy-on-close `v4.9.10` | 是否在关闭时销毁内容 | _boolean_ | `false` |
| lock-scroll | 是否锁定背景滚动 | _boolean_ | `true` | | lock-scroll | 是否锁定背景滚动 | _boolean_ | `true` |
| lazy-render | 是否在显示弹层时才渲染节点 | _boolean_ | `true` | | lazy-render | 是否在显示弹层时才渲染节点 | _boolean_ | `true` |
| close-on-popstate | 是否在页面回退时自动关闭 | _boolean_ | `false` | | close-on-popstate | 是否在页面回退时自动关闭 | _boolean_ | `false` |

View File

@ -283,3 +283,23 @@ test('should have safe-area-inset-bottom class when using safe-area-inset-bottom
'van-safe-area-bottom', 'van-safe-area-bottom',
); );
}); });
test('should destroy content when using destroyOnClose prop', async () => {
const wrapper = mount(Popup, {
props: {
show: false,
destroyOnClose: true,
},
slots: {
default: () => <div class="foo" />,
},
});
expect(wrapper.find('.foo').exists()).toBeFalsy();
await wrapper.setProps({ show: true });
expect(wrapper.find('.foo').exists()).toBeTruthy();
await wrapper.setProps({ show: false });
expect(wrapper.find('.foo').exists()).toBeFalsy();
});