feat(DropdownMenu): add swipe-threshold prop (#12117)

* feat(DropdownMenu): add swipe-threshold prop

* docs: add prop description

* test: add test case

* docs: add demo

* test: update snapshots
This commit is contained in:
inottn 2023-08-13 15:53:14 +08:00 committed by GitHub
parent fb1cafd105
commit af986cc459
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 289 additions and 3 deletions

View File

@ -44,6 +44,7 @@ export const dropdownMenuProps = {
activeColor: String,
closeOnClickOutside: truthProp,
closeOnClickOverlay: truthProp,
swipeThreshold: numericProp,
};
export type DropdownMenuProps = ExtractPropTypes<typeof dropdownMenuProps>;
@ -68,6 +69,10 @@ export default defineComponent({
children.some((item) => item.state.showWrapper),
);
const scrollable = computed(
() => props.swipeThreshold && children.length > +props.swipeThreshold,
);
const barStyle = computed<CSSProperties | undefined>(() => {
if (opened.value && isDef(props.zIndex)) {
return {
@ -124,7 +129,10 @@ export default defineComponent({
id={`${id}-${index}`}
role="button"
tabindex={disabled ? undefined : 0}
class={[bem('item', { disabled }), { [HAPTICS_FEEDBACK]: !disabled }]}
class={[
bem('item', { disabled, grow: scrollable.value }),
{ [HAPTICS_FEEDBACK]: !disabled },
]}
onClick={() => {
if (!disabled) {
toggleItem(index);
@ -160,7 +168,10 @@ export default defineComponent({
<div
ref={barRef}
style={barStyle.value}
class={bem('bar', { opened: opened.value })}
class={bem('bar', {
opened: opened.value,
scrollable: scrollable.value,
})}
>
{children.map(renderTitle)}
</div>

View File

@ -126,6 +126,20 @@ Use `active-color` prop to custom active color of the title and options.
</van-dropdown-menu>
```
### Swipe Items
You can set `swipe-threshold` prop to customize threshold number.
```html
<van-dropdown-menu swipe-threshold="4">
<van-dropdown-item v-model="value1" :options="option1" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
</van-dropdown-menu>
```
### Expand Direction
```html
@ -157,6 +171,7 @@ Use `active-color` prop to custom active color of the title and options.
| overlay | Whether to show overlay | _boolean_ | `true` |
| close-on-click-overlay | Whether to close when overlay is clicked | _boolean_ | `true` |
| close-on-click-outside | Whether to close when outside is clicked | _boolean_ | `true` |
| swipe-threshold | Horizontal scrolling is allowed when the number of items exceeds the threshold and the total width exceeds the width of the menu. | _number \| string_ | - |
### DropdownItem Props

View File

@ -128,6 +128,20 @@ export default {
</van-dropdown-menu>
```
### 横向滚动
通过 `swipe-threshold` 属性可以自定义滚动阈值。
```html
<van-dropdown-menu swipe-threshold="4">
<van-dropdown-item v-model="value1" :options="option1" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
</van-dropdown-menu>
```
### 向上展开
`direction` 属性值设置为 `up`,菜单即可向上展开。
@ -161,6 +175,7 @@ export default {
| overlay | 是否显示遮罩层 | _boolean_ | `true` |
| close-on-click-overlay | 是否在点击遮罩层后关闭菜单 | _boolean_ | `true` |
| close-on-click-outside | 是否在点击外部元素后关闭菜单 | _boolean_ | `true` |
| swipe-threshold | 滚动阈值,选项数量超过阈值且总宽度超过菜单栏宽度时,可以横向滚动 | _number \| string_ | - |
### DropdownItem Props

View File

@ -17,6 +17,7 @@ const t = useTranslate({
expandDirection: '向上展开',
customContent: '自定义菜单内容',
customActiveColor: '自定义选中态颜色',
swipeItems: '横向滚动',
option1: [
{ text: '全部商品', value: 0 },
{ text: '新款商品', value: 1 },
@ -36,6 +37,7 @@ const t = useTranslate({
expandDirection: 'Expand Direction',
customContent: 'Custom Content',
customActiveColor: 'Custom Active Color',
swipeItems: 'Swipe Items',
option1: [
{ text: 'Option1', value: 0 },
{ text: 'Option2', value: 1 },
@ -108,6 +110,16 @@ const onConfirm = () => {
</van-dropdown-menu>
</demo-block>
<demo-block :title="t('swipeItems')">
<van-dropdown-menu swipe-threshold="4">
<van-dropdown-item v-model="value1" :options="option1" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
<van-dropdown-item v-model="value2" :options="option2" />
</van-dropdown-menu>
</demo-block>
<demo-block :title="t('expandDirection')">
<van-dropdown-menu direction="up">
<van-dropdown-item v-model="value1" :options="option1" />

View File

@ -25,6 +25,18 @@
&--opened {
z-index: calc(var(--van-dropdown-item-z-index) + 1);
}
&--scrollable {
padding-left: var(--van-padding-base);
padding-right: var(--van-padding-xs);
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
}
}
&__item {
@ -39,6 +51,12 @@
color: var(--van-dropdown-menu-title-disabled-text-color);
}
}
&--grow {
flex: 1 0 auto;
padding-left: var(--van-padding-base);
padding-right: var(--van-padding-sm);
}
}
&__title {

View File

@ -65,6 +65,42 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div>
<!--[-->
<div class="van-dropdown-menu">
<div style
class="van-dropdown-menu__bar"
>
<!--[-->
</div>
<!--[-->
<div style="top:0px;display:none;"
class="van-dropdown-item van-dropdown-item--down"
>
<!--[-->
</div>
<div style="top:0px;display:none;"
class="van-dropdown-item van-dropdown-item--down"
>
<!--[-->
</div>
<div style="top:0px;display:none;"
class="van-dropdown-item van-dropdown-item--down"
>
<!--[-->
</div>
<div style="top:0px;display:none;"
class="van-dropdown-item van-dropdown-item--down"
>
<!--[-->
</div>
<div style="top:0px;display:none;"
class="van-dropdown-item van-dropdown-item--down"
>
<!--[-->
</div>
</div>
</div>
<div>
<!--[-->
<div class="van-dropdown-menu">

View File

@ -193,6 +193,157 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div>
<div class="van-dropdown-menu">
<div class="van-dropdown-menu__bar van-dropdown-menu__bar--scrollable">
<div id="van-dropdown-menu-0"
role="button"
tabindex="0"
class="van-dropdown-menu__item van-dropdown-menu__item--grow van-haptics-feedback"
>
<span class="van-dropdown-menu__title">
<div class="van-ellipsis">
Option1
</div>
</span>
</div>
<div id="van-dropdown-menu-1"
role="button"
tabindex="0"
class="van-dropdown-menu__item van-dropdown-menu__item--grow van-haptics-feedback"
>
<span class="van-dropdown-menu__title">
<div class="van-ellipsis">
Option A
</div>
</span>
</div>
<div id="van-dropdown-menu-2"
role="button"
tabindex="0"
class="van-dropdown-menu__item van-dropdown-menu__item--grow van-haptics-feedback"
>
<span class="van-dropdown-menu__title">
<div class="van-ellipsis">
Option A
</div>
</span>
</div>
<div id="van-dropdown-menu-3"
role="button"
tabindex="0"
class="van-dropdown-menu__item van-dropdown-menu__item--grow van-haptics-feedback"
>
<span class="van-dropdown-menu__title">
<div class="van-ellipsis">
Option A
</div>
</span>
</div>
<div id="van-dropdown-menu-4"
role="button"
tabindex="0"
class="van-dropdown-menu__item van-dropdown-menu__item--grow van-haptics-feedback"
>
<span class="van-dropdown-menu__title">
<div class="van-ellipsis">
Option A
</div>
</span>
</div>
</div>
<div style="top: 0px; display: none;"
class="van-dropdown-item van-dropdown-item--down"
>
<transition-stub name="van-fade"
appear="true"
persisted="false"
css="true"
role="button"
tabindex="0"
>
</transition-stub>
<transition-stub name="van-popup-slide-top"
appear="false"
persisted="false"
css="true"
>
</transition-stub>
</div>
<div style="top: 0px; display: none;"
class="van-dropdown-item van-dropdown-item--down"
>
<transition-stub name="van-fade"
appear="true"
persisted="false"
css="true"
role="button"
tabindex="0"
>
</transition-stub>
<transition-stub name="van-popup-slide-top"
appear="false"
persisted="false"
css="true"
>
</transition-stub>
</div>
<div style="top: 0px; display: none;"
class="van-dropdown-item van-dropdown-item--down"
>
<transition-stub name="van-fade"
appear="true"
persisted="false"
css="true"
role="button"
tabindex="0"
>
</transition-stub>
<transition-stub name="van-popup-slide-top"
appear="false"
persisted="false"
css="true"
>
</transition-stub>
</div>
<div style="top: 0px; display: none;"
class="van-dropdown-item van-dropdown-item--down"
>
<transition-stub name="van-fade"
appear="true"
persisted="false"
css="true"
role="button"
tabindex="0"
>
</transition-stub>
<transition-stub name="van-popup-slide-top"
appear="false"
persisted="false"
css="true"
>
</transition-stub>
</div>
<div style="top: 0px; display: none;"
class="van-dropdown-item van-dropdown-item--down"
>
<transition-stub name="van-fade"
appear="true"
persisted="false"
css="true"
role="button"
tabindex="0"
>
</transition-stub>
<transition-stub name="van-popup-slide-top"
appear="false"
persisted="false"
css="true"
>
</transition-stub>
</div>
</div>
</div>
<div>
<div class="van-dropdown-menu">
<div class="van-dropdown-menu__bar">

View File

@ -1,5 +1,5 @@
import { later, mount } from '../../../test';
import { reactive, ref, onMounted } from 'vue';
import { reactive, ref, onMounted, computed } from 'vue';
import DropdownItem from '../../dropdown-item';
import DropdownMenu, { DropdownMenuDirection } from '..';
@ -295,3 +295,31 @@ test('DropdownItem should inherit attrs when using teleport prop', async () => {
const item = root.querySelector('.van-dropdown-item');
expect(item?.classList.contains('foo')).toBeTruthy();
});
test('scrolling is allowed when the number of items exceeds the threshold', async () => {
const itemCounts = ref(4);
const wrapper = mount({
setup() {
const options = [
{ text: 'A', value: 0 },
{ text: 'B', value: 1 },
];
const dropdownItems = computed(() =>
new Array(itemCounts.value)
.fill(0)
.map(() => <DropdownItem modelValue={0} options={options} />),
);
return () => (
<DropdownMenu swipeThreshold={4}>{dropdownItems.value}</DropdownMenu>
);
},
});
const bar = wrapper.find('.van-dropdown-menu__bar');
expect(bar.classes()).not.toContain('van-dropdown-menu__bar--scrollable');
itemCounts.value = 5;
await later();
expect(bar.classes()).toContain('van-dropdown-menu__bar--scrollable');
});