Skip to content

Commit

Permalink
feat(DropdownMenu): add auto-locate prop
Browse files Browse the repository at this point in the history
  • Loading branch information
inottn committed Dec 30, 2023
1 parent 2f9f961 commit dbe6463
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 7 deletions.
30 changes: 24 additions & 6 deletions packages/vant/src/dropdown-item/DropdownItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
reactive,
Teleport,
defineComponent,
ref,
type PropType,
type TeleportProps,
type CSSProperties,
Expand All @@ -15,11 +16,12 @@ import {
getZIndexStyle,
createNamespace,
makeArrayProp,
getContainingBlock,
} from '../utils';
import { DROPDOWN_KEY } from '../dropdown-menu/DropdownMenu';

// Composables
import { useParent } from '@vant/use';
import { useParent, useRect } from '@vant/use';
import { useExpose } from '../composables/use-expose';

// Components
Expand Down Expand Up @@ -59,6 +61,7 @@ export default defineComponent({
transition: true,
showWrapper: false,
});
const wrapperRef = ref<HTMLElement>();

const { parent, index } = useParent(DROPDOWN_KEY);

Expand Down Expand Up @@ -160,20 +163,35 @@ export default defineComponent({

const renderContent = () => {
const { offset } = parent;
const { zIndex, overlay, duration, direction, closeOnClickOverlay } =
parent.props;

const {
autoLocate,
zIndex,
overlay,
duration,
direction,
closeOnClickOverlay,
} = parent.props;
const style: CSSProperties = getZIndexStyle(zIndex);
let offsetValue = offset.value;

if (autoLocate && wrapperRef.value) {
const offsetParent = getContainingBlock(wrapperRef.value);

if (offsetParent) {
offsetValue -= useRect(offsetParent).top;
}
}

if (direction === 'down') {
style.top = `${offset.value}px`;
style.top = `${offsetValue}px`;
} else {
style.bottom = `${offset.value}px`;
style.bottom = `${offsetValue}px`;
}

return (
<div
v-show={state.showWrapper}
ref={wrapperRef}
style={style}
class={bem([direction])}
onClick={onClickWrapper}
Expand Down
1 change: 1 addition & 0 deletions packages/vant/src/dropdown-menu/DropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const dropdownMenuProps = {
duration: makeNumericProp(0.2),
direction: makeStringProp<DropdownMenuDirection>('down'),
activeColor: String,
autoLocate: Boolean,
closeOnClickOutside: truthProp,
closeOnClickOverlay: truthProp,
swipeThreshold: numericProp,
Expand Down
1 change: 1 addition & 0 deletions packages/vant/src/dropdown-menu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ You can set `swipe-threshold` prop to customize threshold number.
| 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_ | - |
| auto-locate | When the ancestor element is set with a transform, the position of the dropdown menu will be automatically adjusted. | _boolean_ | `false` |

### DropdownItem Props

Expand Down
12 changes: 11 additions & 1 deletion packages/vant/src/dropdown-menu/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export default {
| close-on-click-overlay | 是否在点击遮罩层后关闭菜单 | _boolean_ | `true` |
| close-on-click-outside | 是否在点击外部元素后关闭菜单 | _boolean_ | `true` |
| swipe-threshold | 滚动阈值,选项数量超过阈值且总宽度超过菜单栏宽度时,可以横向滚动 | _number \| string_ | - |
| auto-locate | 当祖先元素设置了 transform 时,自动调整下拉菜单的位置 | _boolean_ | `false` |

### DropdownItem Props

Expand Down Expand Up @@ -283,7 +284,7 @@ dropdownItemRef.value?.toggle();

### 父元素设置 transform 后,下拉菜单的位置错误?

`DropdownMenu` 嵌套在 `Tabs` 等组件内部使用时,可能会遇到下拉菜单位置错误的问题。这是因为在 Chrome 浏览器中,transform 元素内部的 fixed 布局会降级成 absolute 布局,导致下拉菜单的布局异常
`DropdownMenu` 嵌套在 `Tabs` 等组件内部使用时,可能会遇到下拉菜单位置错误的问题。这是因为 transform 元素内部的 fixed 定位会相对于该元素进行计算,而不是相对于整个文档,从而导致下拉菜单的布局异常

`DropdownItem``teleport` 属性设置为 `body` 即可避免此问题:

Expand All @@ -293,3 +294,12 @@ dropdownItemRef.value?.toggle();
<van-dropdown-item teleport="body" />
</van-dropdown-menu>
```

也可以将 `DropdownMenu``auto-locate` 属性设置为 `true`

```html
<van-dropdown-menu auto-locate>
<van-dropdown-item />
<van-dropdown-item />
</van-dropdown-menu>
```
31 changes: 31 additions & 0 deletions packages/vant/src/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,34 @@ export function isHidden(
}

export const { width: windowWidth, height: windowHeight } = useWindowSize();

function isContainingBlock(el: Element) {
const css = window.getComputedStyle(el);

return (
css.transform !== 'none' ||
css.perspective !== 'none' ||
['transform', 'perspective', 'filter'].some((value) =>
(css.willChange || '').includes(value),
)
);
}

export function getContainingBlock(el: Element) {
let node = el.parentElement;

while (node) {
if (
node &&
node.tagName !== 'HTML' &&
node.tagName !== 'BODY' &&
isContainingBlock(node)
) {
return node;
}

node = node.parentElement;
}

return null;
}

0 comments on commit dbe6463

Please sign in to comment.