mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
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:
parent
fb1cafd105
commit
af986cc459
@ -44,6 +44,7 @@ export const dropdownMenuProps = {
|
|||||||
activeColor: String,
|
activeColor: String,
|
||||||
closeOnClickOutside: truthProp,
|
closeOnClickOutside: truthProp,
|
||||||
closeOnClickOverlay: truthProp,
|
closeOnClickOverlay: truthProp,
|
||||||
|
swipeThreshold: numericProp,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DropdownMenuProps = ExtractPropTypes<typeof dropdownMenuProps>;
|
export type DropdownMenuProps = ExtractPropTypes<typeof dropdownMenuProps>;
|
||||||
@ -68,6 +69,10 @@ export default defineComponent({
|
|||||||
children.some((item) => item.state.showWrapper),
|
children.some((item) => item.state.showWrapper),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const scrollable = computed(
|
||||||
|
() => props.swipeThreshold && children.length > +props.swipeThreshold,
|
||||||
|
);
|
||||||
|
|
||||||
const barStyle = computed<CSSProperties | undefined>(() => {
|
const barStyle = computed<CSSProperties | undefined>(() => {
|
||||||
if (opened.value && isDef(props.zIndex)) {
|
if (opened.value && isDef(props.zIndex)) {
|
||||||
return {
|
return {
|
||||||
@ -124,7 +129,10 @@ export default defineComponent({
|
|||||||
id={`${id}-${index}`}
|
id={`${id}-${index}`}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex={disabled ? undefined : 0}
|
tabindex={disabled ? undefined : 0}
|
||||||
class={[bem('item', { disabled }), { [HAPTICS_FEEDBACK]: !disabled }]}
|
class={[
|
||||||
|
bem('item', { disabled, grow: scrollable.value }),
|
||||||
|
{ [HAPTICS_FEEDBACK]: !disabled },
|
||||||
|
]}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
toggleItem(index);
|
toggleItem(index);
|
||||||
@ -160,7 +168,10 @@ export default defineComponent({
|
|||||||
<div
|
<div
|
||||||
ref={barRef}
|
ref={barRef}
|
||||||
style={barStyle.value}
|
style={barStyle.value}
|
||||||
class={bem('bar', { opened: opened.value })}
|
class={bem('bar', {
|
||||||
|
opened: opened.value,
|
||||||
|
scrollable: scrollable.value,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
{children.map(renderTitle)}
|
{children.map(renderTitle)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -126,6 +126,20 @@ Use `active-color` prop to custom active color of the title and options.
|
|||||||
</van-dropdown-menu>
|
</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
|
### Expand Direction
|
||||||
|
|
||||||
```html
|
```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` |
|
| overlay | Whether to show overlay | _boolean_ | `true` |
|
||||||
| close-on-click-overlay | Whether to close when overlay is clicked | _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` |
|
| 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
|
### DropdownItem Props
|
||||||
|
|
||||||
|
@ -128,6 +128,20 @@ export default {
|
|||||||
</van-dropdown-menu>
|
</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`,菜单即可向上展开。
|
将 `direction` 属性值设置为 `up`,菜单即可向上展开。
|
||||||
@ -161,6 +175,7 @@ export default {
|
|||||||
| overlay | 是否显示遮罩层 | _boolean_ | `true` |
|
| overlay | 是否显示遮罩层 | _boolean_ | `true` |
|
||||||
| close-on-click-overlay | 是否在点击遮罩层后关闭菜单 | _boolean_ | `true` |
|
| close-on-click-overlay | 是否在点击遮罩层后关闭菜单 | _boolean_ | `true` |
|
||||||
| close-on-click-outside | 是否在点击外部元素后关闭菜单 | _boolean_ | `true` |
|
| close-on-click-outside | 是否在点击外部元素后关闭菜单 | _boolean_ | `true` |
|
||||||
|
| swipe-threshold | 滚动阈值,选项数量超过阈值且总宽度超过菜单栏宽度时,可以横向滚动 | _number \| string_ | - |
|
||||||
|
|
||||||
### DropdownItem Props
|
### DropdownItem Props
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ const t = useTranslate({
|
|||||||
expandDirection: '向上展开',
|
expandDirection: '向上展开',
|
||||||
customContent: '自定义菜单内容',
|
customContent: '自定义菜单内容',
|
||||||
customActiveColor: '自定义选中态颜色',
|
customActiveColor: '自定义选中态颜色',
|
||||||
|
swipeItems: '横向滚动',
|
||||||
option1: [
|
option1: [
|
||||||
{ text: '全部商品', value: 0 },
|
{ text: '全部商品', value: 0 },
|
||||||
{ text: '新款商品', value: 1 },
|
{ text: '新款商品', value: 1 },
|
||||||
@ -36,6 +37,7 @@ const t = useTranslate({
|
|||||||
expandDirection: 'Expand Direction',
|
expandDirection: 'Expand Direction',
|
||||||
customContent: 'Custom Content',
|
customContent: 'Custom Content',
|
||||||
customActiveColor: 'Custom Active Color',
|
customActiveColor: 'Custom Active Color',
|
||||||
|
swipeItems: 'Swipe Items',
|
||||||
option1: [
|
option1: [
|
||||||
{ text: 'Option1', value: 0 },
|
{ text: 'Option1', value: 0 },
|
||||||
{ text: 'Option2', value: 1 },
|
{ text: 'Option2', value: 1 },
|
||||||
@ -108,6 +110,16 @@ const onConfirm = () => {
|
|||||||
</van-dropdown-menu>
|
</van-dropdown-menu>
|
||||||
</demo-block>
|
</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')">
|
<demo-block :title="t('expandDirection')">
|
||||||
<van-dropdown-menu direction="up">
|
<van-dropdown-menu direction="up">
|
||||||
<van-dropdown-item v-model="value1" :options="option1" />
|
<van-dropdown-item v-model="value1" :options="option1" />
|
||||||
|
@ -25,6 +25,18 @@
|
|||||||
&--opened {
|
&--opened {
|
||||||
z-index: calc(var(--van-dropdown-item-z-index) + 1);
|
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 {
|
&__item {
|
||||||
@ -39,6 +51,12 @@
|
|||||||
color: var(--van-dropdown-menu-title-disabled-text-color);
|
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 {
|
&__title {
|
||||||
|
@ -65,6 +65,42 @@ exports[`should render demo and match snapshot 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
<!--[-->
|
<!--[-->
|
||||||
<div class="van-dropdown-menu">
|
<div class="van-dropdown-menu">
|
||||||
|
@ -193,6 +193,157 @@ exports[`should render demo and match snapshot 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
<div class="van-dropdown-menu">
|
<div class="van-dropdown-menu">
|
||||||
<div class="van-dropdown-menu__bar">
|
<div class="van-dropdown-menu__bar">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { later, mount } from '../../../test';
|
import { later, mount } from '../../../test';
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted, computed } from 'vue';
|
||||||
import DropdownItem from '../../dropdown-item';
|
import DropdownItem from '../../dropdown-item';
|
||||||
import DropdownMenu, { DropdownMenuDirection } from '..';
|
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');
|
const item = root.querySelector('.van-dropdown-item');
|
||||||
expect(item?.classList.contains('foo')).toBeTruthy();
|
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');
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user