From af986cc4592d6ea1d4e9ebd0846785f067b71c02 Mon Sep 17 00:00:00 2001 From: inottn Date: Sun, 13 Aug 2023 15:53:14 +0800 Subject: [PATCH] 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 --- .../vant/src/dropdown-menu/DropdownMenu.tsx | 15 +- packages/vant/src/dropdown-menu/README.md | 15 ++ .../vant/src/dropdown-menu/README.zh-CN.md | 15 ++ .../vant/src/dropdown-menu/demo/index.vue | 12 ++ packages/vant/src/dropdown-menu/index.less | 18 +++ .../test/__snapshots__/demo-ssr.spec.ts.snap | 36 +++++ .../test/__snapshots__/demo.spec.ts.snap | 151 ++++++++++++++++++ .../src/dropdown-menu/test/index.spec.tsx | 30 +++- 8 files changed, 289 insertions(+), 3 deletions(-) diff --git a/packages/vant/src/dropdown-menu/DropdownMenu.tsx b/packages/vant/src/dropdown-menu/DropdownMenu.tsx index 64b009779..66c80fc31 100644 --- a/packages/vant/src/dropdown-menu/DropdownMenu.tsx +++ b/packages/vant/src/dropdown-menu/DropdownMenu.tsx @@ -44,6 +44,7 @@ export const dropdownMenuProps = { activeColor: String, closeOnClickOutside: truthProp, closeOnClickOverlay: truthProp, + swipeThreshold: numericProp, }; export type DropdownMenuProps = ExtractPropTypes; @@ -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(() => { 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({
{children.map(renderTitle)}
diff --git a/packages/vant/src/dropdown-menu/README.md b/packages/vant/src/dropdown-menu/README.md index 4a9e717e4..28af7af37 100644 --- a/packages/vant/src/dropdown-menu/README.md +++ b/packages/vant/src/dropdown-menu/README.md @@ -126,6 +126,20 @@ Use `active-color` prop to custom active color of the title and options. ``` +### Swipe Items + +You can set `swipe-threshold` prop to customize threshold number. + +```html + + + + + + + +``` + ### 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 diff --git a/packages/vant/src/dropdown-menu/README.zh-CN.md b/packages/vant/src/dropdown-menu/README.zh-CN.md index 975771a5a..86e3215db 100644 --- a/packages/vant/src/dropdown-menu/README.zh-CN.md +++ b/packages/vant/src/dropdown-menu/README.zh-CN.md @@ -128,6 +128,20 @@ export default { ``` +### 横向滚动 + +通过 `swipe-threshold` 属性可以自定义滚动阈值。 + +```html + + + + + + + +``` + ### 向上展开 将 `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 diff --git a/packages/vant/src/dropdown-menu/demo/index.vue b/packages/vant/src/dropdown-menu/demo/index.vue index 975641fd5..123a39556 100644 --- a/packages/vant/src/dropdown-menu/demo/index.vue +++ b/packages/vant/src/dropdown-menu/demo/index.vue @@ -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 = () => { + + + + + + + + + + diff --git a/packages/vant/src/dropdown-menu/index.less b/packages/vant/src/dropdown-menu/index.less index a6a7bb564..87ff64bf6 100644 --- a/packages/vant/src/dropdown-menu/index.less +++ b/packages/vant/src/dropdown-menu/index.less @@ -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 { diff --git a/packages/vant/src/dropdown-menu/test/__snapshots__/demo-ssr.spec.ts.snap b/packages/vant/src/dropdown-menu/test/__snapshots__/demo-ssr.spec.ts.snap index d346d7a3c..b5d2e576d 100644 --- a/packages/vant/src/dropdown-menu/test/__snapshots__/demo-ssr.spec.ts.snap +++ b/packages/vant/src/dropdown-menu/test/__snapshots__/demo-ssr.spec.ts.snap @@ -65,6 +65,42 @@ exports[`should render demo and match snapshot 1`] = ` +
+ +
+
+ +
+ + + + + + +
+
diff --git a/packages/vant/src/dropdown-menu/test/__snapshots__/demo.spec.ts.snap b/packages/vant/src/dropdown-menu/test/__snapshots__/demo.spec.ts.snap index f9f538085..fbd56f6e4 100644 --- a/packages/vant/src/dropdown-menu/test/__snapshots__/demo.spec.ts.snap +++ b/packages/vant/src/dropdown-menu/test/__snapshots__/demo.spec.ts.snap @@ -193,6 +193,157 @@ exports[`should render demo and match snapshot 1`] = `
+
+
+
+
+ +
+ Option1 +
+
+
+
+ +
+ Option A +
+
+
+
+ +
+ Option A +
+
+
+
+ +
+ Option A +
+
+
+
+ +
+ Option A +
+
+
+
+ + + + + +
+
diff --git a/packages/vant/src/dropdown-menu/test/index.spec.tsx b/packages/vant/src/dropdown-menu/test/index.spec.tsx index 2d7f9a77c..a241d8fef 100644 --- a/packages/vant/src/dropdown-menu/test/index.spec.tsx +++ b/packages/vant/src/dropdown-menu/test/index.spec.tsx @@ -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(() => ), + ); + + return () => ( + {dropdownItems.value} + ); + }, + }); + + 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'); +});