feat(FloatingPanel): add v-model:height prop (#11947)

* feat(FloatingPanel): Optimize the timing of height-change event triggering and add dragging param

* feat(FloatingPanel): add v-model:height prop
This commit is contained in:
inottn 2023-06-10 12:12:48 +08:00 committed by GitHub
parent cfdf3a811e
commit 5c9ce177f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 48 deletions

View File

@ -1,14 +1,32 @@
import { computed, defineComponent, ref, type ExtractPropTypes } from 'vue';
import {
computed,
defineComponent,
ref,
watch,
type ExtractPropTypes,
} from 'vue';
// Utils
import {
addUnit,
createNamespace,
makeArrayProp,
makeNumericProp,
truthProp,
} from '../utils';
// Composables
import { useWindowSize } from '@vant/use';
import { useLockScroll } from '../composables/use-lock-scroll';
import { useTouch } from '../composables/use-touch';
import { addUnit, createNamespace, makeArrayProp, truthProp } from '../utils';
import { useWindowSize } from '@vant/use';
import { useSyncPropRef } from '../composables/use-sync-prop-ref';
const { height: windowHeight } = useWindowSize();
export const floatingPanelProps = {
anchors: makeArrayProp<number>(),
contentDraggable: truthProp,
height: makeNumericProp(0),
safeAreaInsetBottom: truthProp,
};
@ -23,11 +41,15 @@ export default defineComponent({
props: floatingPanelProps,
emits: ['heightChange'],
emits: ['heightChange', 'update:height'],
setup(props, { emit, slots }) {
const rootRef = ref<HTMLDivElement>();
const contentRef = ref<HTMLDivElement>();
const height = useSyncPropRef(
() => +props.height,
(value) => emit('update:height', value)
);
const boundary = computed(() => ({
min: props.anchors[0] ?? 100,
@ -43,11 +65,10 @@ export default defineComponent({
);
const dragging = ref(false);
const currentY = ref(-boundary.value.min);
const rootStyle = computed(() => ({
height: addUnit(boundary.value.max),
transform: `translateY(calc(100% + ${addUnit(currentY.value)}))`,
transform: `translateY(calc(100% + ${addUnit(-height.value)}))`,
transition: !dragging.value ? 'transform .3s' : 'none',
}));
@ -71,12 +92,13 @@ export default defineComponent({
Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
);
let startY = currentY.value;
let startY: number;
const touch = useTouch();
const onTouchstart = (e: TouchEvent) => {
touch.start(e);
dragging.value = true;
startY = -height.value;
};
const onTouchmove = (e: TouchEvent) => {
@ -97,24 +119,26 @@ export default defineComponent({
}
const moveY = touch.deltaY.value + startY;
currentY.value = ease(moveY);
height.value = -ease(moveY);
};
const onTouchend = () => {
dragging.value = false;
height.value = closest(anchors.value, height.value);
const height = Math.abs(currentY.value);
const closestHeight = closest(anchors.value, height);
currentY.value = -closestHeight;
if (currentY.value !== startY) {
emit('heightChange', closestHeight);
if (height.value !== -startY) {
emit('heightChange', { height: height.value });
}
startY = currentY.value;
};
watch(
boundary,
() => {
height.value = closest(anchors.value, height.value);
},
{ immediate: true }
);
useLockScroll(rootRef, () => true);
return () => (

View File

@ -36,22 +36,27 @@ app.use(FloatingPanel);
### Custom Anchors
```html
<van-floating-panel :anchors="anchors" @height-change="onHeightChange">
<van-floating-panel v-model:height="height" :anchors="anchors">
<div style="text-align: center; padding: 15px">
<p>Panel Show Height {{ height }} px</p>
</div>
</van-floating-panel>
```
```ts
const anchors = [
100,
Math.round(0.4 * window.innerHeight),
Math.round(0.7 * window.innerHeight),
];
const height = ref(anchors[0]);
const onHeightChange = (h: number) => {
height.value = h;
```js
import { ref } from 'vue';
export default {
setup() {
const anchors = [
100,
Math.round(0.4 * window.innerHeight),
Math.round(0.7 * window.innerHeight),
];
const height = ref(anchors[0]);
return { anchors, height };
},
};
```
@ -71,15 +76,16 @@ const onHeightChange = (h: number) => {
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| v-model:height | The current display height of the panel | _number \| string_ | `0` |
| anchors | Setting custom anchors, unit `px` | _number[]_ | `[100, window.innerWidth * 0.6]` |
| content-draggable | Allow dragging content | _boolean_ | `true` |
| safe-area-inset-bottom | Whether to enable bottom safe area adaptation | _boolean_ | `true` |
### Events
| Event | Description | Arguments |
| ------------- | ------------------------------------ | ---------------- |
| height-change | Emitted when panel height is changed | _height: number_ |
| Event | Description | Arguments |
| --- | --- | --- |
| height-change | Emitted when panel height is changed and the dragging is finished | _{ height: number }_ |
### Slots

View File

@ -36,22 +36,27 @@ app.use(FloatingPanel);
### 自定义锚点
```html
<van-floating-panel :anchors="anchors" @height-change="onHeightChange">
<van-floating-panel v-model:height="height" :anchors="anchors">
<div style="text-align: center; padding: 15px">
<p>面板显示高度 {{ height }} px</p>
</div>
</van-floating-panel>
```
```ts
const anchors = [
100,
Math.round(0.4 * window.innerHeight),
Math.round(0.7 * window.innerHeight),
];
const height = ref(anchors[0]);
const onHeightChange = (h: number) => {
height.value = h;
```js
import { ref } from 'vue';
export default {
setup() {
const anchors = [
100,
Math.round(0.4 * window.innerHeight),
Math.round(0.7 * window.innerHeight),
];
const height = ref(anchors[0]);
return { anchors, height };
},
};
```
@ -71,15 +76,16 @@ const onHeightChange = (h: number) => {
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| v-model:height | 当前面板的显示高度 | _number \| string_ | `0` |
| anchors | 设置自定义锚点, 单位 `px` | _number[]_ | `[100, window.innerWidth * 0.6]` |
| content-draggable | 允许拖拽内容容器 | _boolean_ | `true` |
| safe-area-inset-bottom | 是否开启[底部安全区适配](#/zh-CN/advanced-usage#di-bu-an-quan-qu-gua-pei) | _boolean_ | `true` |
### Events
| 事件名 | 说明 | 回调参数 |
| ------------- | ---------------------- | ---------------- |
| height-change | 面板显示高度改变时触发 | _height: number_ |
| 事件名 | 说明 | 回调参数 |
| ------------- | -------------------------------- | -------------------- |
| height-change | 面板显示高度改变且结束拖动后触发 | _{ height: number }_ |
### Slots

View File

@ -32,9 +32,6 @@ const anchors = [
];
const height = ref(anchors[0]);
const onHeightChange = (h: number) => {
height.value = h;
};
</script>
<template>
@ -52,7 +49,7 @@ const onHeightChange = (h: number) => {
</van-floating-panel>
</van-tab>
<van-tab :title="t('customAnchors')">
<van-floating-panel :anchors="anchors" @height-change="onHeightChange">
<van-floating-panel v-model:height="height" :anchors="anchors">
<div style="text-align: center; padding: 15px">
<p>{{ t('panelShowHeight') }} {{ height }} px</p>
</div>

View File

@ -90,7 +90,7 @@ test('should emit height-change when height change in anchors', async () => {
'-200px'
);
expect(wrapper.emitted('change')?.[0][0]).toEqual(200);
expect(wrapper.emitted('change')?.[0][0]).toEqual({ height: 200 });
});
test('should only drag header when allowDraggingContent is false', async () => {