feat(PickerGroup): add next-step-text prop (#11487)

This commit is contained in:
neverland 2023-01-15 10:53:00 +08:00 committed by GitHub
parent 082a596568
commit f5f4ae6997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1584 additions and 15 deletions

View File

@ -1,4 +1,9 @@
import { defineComponent, type InjectionKey, type ExtractPropTypes } from 'vue';
import {
ref,
defineComponent,
type InjectionKey,
type ExtractPropTypes,
} from 'vue';
// Utils
import { extend, makeArrayProp, createNamespace } from '../utils';
@ -20,6 +25,7 @@ export const PICKER_GROUP_KEY: InjectionKey<PickerGroupProvide> = Symbol(name);
export const pickerGroupProps = extend(
{
tabs: makeArrayProp<string>(),
nextStepText: String,
},
pickerToolbarProps
);
@ -34,26 +40,47 @@ export default defineComponent({
emits: ['confirm', 'cancel'],
setup(props, { emit, slots }) {
const activeTab = ref(0);
const { children, linkChildren } = useChildren(PICKER_GROUP_KEY);
linkChildren();
const showNextButton = () =>
activeTab.value < props.tabs.length - 1 && props.nextStepText;
const onConfirm = () => {
emit(
'confirm',
children.map((item) => item.confirm())
);
if (showNextButton()) {
activeTab.value++;
} else {
emit(
'confirm',
children.map((item) => item.confirm())
);
}
};
const onCancel = () => emit('cancel');
return () => {
const childNodes = slots.default?.();
const confirmButtonText = showNextButton()
? props.nextStepText
: props.confirmButtonText;
return (
<div class={bem()}>
<Toolbar {...props} onConfirm={onConfirm} onCancel={onCancel} />
<Tabs shrink class={bem('tabs')} animated>
<Toolbar
{...props}
confirmButtonText={confirmButtonText}
onConfirm={onConfirm}
onCancel={onCancel}
/>
<Tabs
v-model:active={activeTab.value}
class={bem('tabs')}
shrink
animated
>
{props.tabs.map((title, index) => (
<Tab title={title} titleClass={bem('tab-title')}>
{childNodes?.[index]}

View File

@ -75,6 +75,54 @@ export default {
};
```
### Next Step Button
In some scenarios, in order to ensure that users can select all Pickers in turn, you can set the `next-step-text` prop of PickerGroup. After setting the `next-step-text` prop, if the user has not switched to the last tab, the button in the upper right corner will become "Next Step", and automatically switch to the next Picker after clicking. When the user switches to the last tab, the button in the upper right corner changes to "Confirm".
```html
<van-picker-group
title="Title"
:tabs="['Date', 'Time']"
next-step-text="Next Step"
@confirm="onConfirm"
@cancel="onCancel"
>
<van-date-picker
v-model="currentDate"
:min-date="minDate"
:max-date="maxDate"
/>
<van-time-picker v-model="currentTime" />
</van-picker-group>
```
```js
import { ref } from 'vue';
import { showToast } from 'vant';
export default {
setup() {
const currentDate = ref(['2022', '06', '01']);
const currentTime = ref(['12', '00']);
const onConfirm = () => {
showToast(
`${currentDate.value.join('/')} ${currentTime.value.join(':')}`
);
};
const onCancel = () => {
showToast('cancel');
};
return {
minDate: new Date(2020, 0, 1),
maxDate: new Date(2025, 5, 1),
currentDate,
currentTime,
};
},
};
```
### Select Date Range
Place two `DatePicker` components in the default slot of `PickerGroup` to select the time range.
@ -165,12 +213,13 @@ export default {
### Props
| Attribute | Description | Type | Default |
| ------------------- | ---------------------- | ---------- | --------- |
| tabs | Titles of tabs | _string[]_ | `[]` |
| title | Toolbar title | _string_ | `''` |
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
| Attribute | Description | Type | Default |
| ------------------- | ------------------------ | ---------- | --------- |
| tabs | Titles of tabs | _string[]_ | `[]` |
| title | Toolbar title | _string_ | `''` |
| next-step-text | Text of next step button | _string_ | `''` |
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
### Slots

View File

@ -75,6 +75,54 @@ export default {
};
```
### 下一步按钮
部分场景下,为了保证用户能够依次选择所有的 Picker你可以设置 PickerGroup 的 `next-step-text` 属性。在设置 `next-step-text` 属性后,如果用户未切换到最后一个标签页,那么右上角的按钮会变成「下一步」,点击后自动切换到下一个 Picker。当用户切换到最后一个标签页时右上角的按钮会变为「确认」。
```html
<van-picker-group
title="预约日期"
:tabs="['选择日期', '选择时间']"
next-step-text="下一步"
@confirm="onConfirm"
@cancel="onCancel"
>
<van-date-picker
v-model="currentDate"
:min-date="minDate"
:max-date="maxDate"
/>
<van-time-picker v-model="currentTime" />
</van-picker-group>
```
```js
import { ref } from 'vue';
import { showToast } from 'vant';
export default {
setup() {
const currentDate = ref(['2022', '06', '01']);
const currentTime = ref(['12', '00']);
const onConfirm = () => {
showToast(
`${currentDate.value.join('/')} ${currentTime.value.join(':')}`
);
};
const onCancel = () => {
showToast('cancel');
};
return {
minDate: new Date(2020, 0, 1),
maxDate: new Date(2025, 5, 1),
currentDate,
currentTime,
};
},
};
```
### 选择日期范围
`PickerGroup` 的默认插槽中放置两个 `DatePicker` 组件,可以实现选择日期范围的交互效果。
@ -169,8 +217,9 @@ export default {
| ------------------- | ---------------- | ---------- | ------ |
| tabs | 设置标签页的标题 | _string[]_ | `[]` |
| title | 顶部栏标题 | _string_ | `''` |
| confirm-button-text | 确认按钮文字 | _string_ | `确认` |
| cancel-button-text | 取消按钮文字 | _string_ | `取消` |
| next-step-text | 下一步按钮的文字 | _string_ | `''` |
| confirm-button-text | 确认按钮的文字 | _string_ | `确认` |
| cancel-button-text | 取消按钮的文字 | _string_ | `取消` |
### Slots

View File

@ -0,0 +1,52 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useTranslate } from '../../../docs/site';
import VanPickerGroup from '..';
import VanTimePicker from '../../time-picker';
import VanDatePicker from '../../date-picker';
import { showToast } from '../../toast';
const t = useTranslate({
'zh-CN': {
date: '选择日期',
time: '选择时间',
title: '预约日期',
nextStep: '下一步',
},
'en-US': {
date: 'Date',
time: 'Time',
title: 'Title',
nextStep: 'Next Step',
},
});
const currentTime = ref(['12', '00']);
const currentDate = ref(['2022', '06', '01']);
const minDate = new Date(2020, 0, 1);
const maxDate = new Date(2025, 5, 1);
const onConfirm = () => {
showToast(`${currentDate.value.join('/')} ${currentTime.value.join(':')}`);
};
const onCancel = () => {
showToast('cancel');
};
</script>
<template>
<van-picker-group
:title="t('title')"
:tabs="[t('date'), t('time')]"
:next-step-text="t('nextStep')"
@confirm="onConfirm"
@cancel="onCancel"
>
<van-date-picker
v-model="currentDate"
:min-date="minDate"
:max-date="maxDate"
/>
<van-time-picker v-model="currentTime" />
</van-picker-group>
</template>

View File

@ -2,6 +2,7 @@
import SelectDateTime from './SelectDateTime.vue';
import SelectTimeRange from './SelectTimeRange.vue';
import SelectDateRange from './SelectDateRange.vue';
import NextStepButton from './NextStepButton.vue';
import { useTranslate } from '../../../docs/site';
const t = useTranslate({
@ -9,11 +10,13 @@ const t = useTranslate({
selectDateTime: '选择日期时间',
selectDateRange: '选择日期范围',
selectTimeRange: '选择时间范围',
nextStepButton: '下一步按钮',
},
'en-US': {
selectDateTime: 'Select Date Time',
selectDateRange: 'Select Date Range',
selectTimeRange: 'Select Time Range',
nextStepButton: 'Next Step Button',
},
});
</script>
@ -23,6 +26,10 @@ const t = useTranslate({
<select-date-time />
</demo-block>
<demo-block card :title="t('nextStepButton')">
<next-step-button />
</demo-block>
<demo-block card :title="t('selectDateRange')">
<select-date-range />
</demo-block>

View File

@ -0,0 +1,54 @@
import { ref } from 'vue';
import { mount } from '../../../test';
import { Picker, PickerConfirmEventParams } from '../../picker';
import { PickerGroup } from '..';
test('should emit confirm event after clicking the confirm button', async () => {
const value1 = ref(['1']);
const value2 = ref(['2']);
const wrapper = mount({
setup(props, { emit }) {
const onConfirm = (params: PickerConfirmEventParams) => {
emit('confirm', params);
};
return () => (
<PickerGroup
title="Title"
tabs={['Tab1', 'Tab2']}
nextStepText="Next Step"
onConfirm={onConfirm}
>
<Picker
v-model={value1.value}
columns={[{ text: '1', value: '1' }]}
/>
<Picker
v-model={value2.value}
columns={[{ text: '2', value: '2' }]}
/>
</PickerGroup>
);
},
});
await wrapper.find('.van-picker__confirm').trigger('click');
expect(wrapper.emitted('confirm')).toBeFalsy();
await wrapper.find('.van-picker__confirm').trigger('click');
expect(wrapper.emitted('confirm')![0]).toEqual([
[
{
selectedIndexes: [0],
selectedOptions: [{ text: '1', value: '1' }],
selectedValues: ['1'],
},
{
selectedIndexes: [0],
selectedOptions: [{ text: '2', value: '2' }],
selectedValues: ['2'],
},
],
]);
});