feat(FloatingPanel): add draggable prop (#13773)

Co-authored-by: Junwei Wang <junwei.wang@algento.com>
This commit is contained in:
Gavin
2026-02-18 14:13:16 +08:00
committed by GitHub
co-authored by Junwei Wang
parent b1283de219
commit 230ed26763
7 changed files with 124 additions and 0 deletions
@@ -29,6 +29,7 @@ export const floatingPanelProps = {
anchors: makeArrayProp<number>(), anchors: makeArrayProp<number>(),
duration: makeNumericProp(0.3), duration: makeNumericProp(0.3),
magnetic: truthProp, magnetic: truthProp,
draggable: truthProp,
contentDraggable: truthProp, contentDraggable: truthProp,
lockScroll: Boolean, lockScroll: Boolean,
safeAreaInsetBottom: truthProp, safeAreaInsetBottom: truthProp,
@@ -97,6 +98,8 @@ export default defineComponent({
const touch = useTouch(); const touch = useTouch();
const onTouchstart = (e: TouchEvent) => { const onTouchstart = (e: TouchEvent) => {
if (!props.draggable) return;
touch.start(e); touch.start(e);
dragging.value = true; dragging.value = true;
startY = -height.value; startY = -height.value;
@@ -104,6 +107,8 @@ export default defineComponent({
}; };
const onTouchmove = (e: TouchEvent) => { const onTouchmove = (e: TouchEvent) => {
if (!props.draggable) return;
touch.move(e); touch.move(e);
const target = e.target as Element; const target = e.target as Element;
@@ -130,8 +135,17 @@ export default defineComponent({
const onTouchend = () => { const onTouchend = () => {
maxScroll = -1; maxScroll = -1;
if (!dragging.value) {
return;
}
dragging.value = false; dragging.value = false;
if (!props.draggable) {
return;
}
if (props.magnetic) { if (props.magnetic) {
height.value = closest(anchors.value, height.value); height.value = closest(anchors.value, height.value);
} else { } else {
@@ -162,6 +176,10 @@ export default defineComponent({
return slots.header(); return slots.header();
} }
if (!props.draggable) {
return null;
}
return ( return (
<div class={bem('header')}> <div class={bem('header')}>
<div class={bem('header-bar')} /> <div class={bem('header-bar')} />
@@ -93,6 +93,18 @@ When `magnetic` is set to `false`, the panel will not automatically snap to anch
</van-floating-panel> </van-floating-panel>
``` ```
### Disable Dragging
You can disable the dragging functionality of the panel through the `draggable` attribute. When set to `false`, the panel will not be draggable, and the header drag bar will be hidden.
```html
<van-floating-panel :draggable="false">
<div style="text-align: center; padding: 15px">
<p>This panel cannot be dragged</p>
</div>
</van-floating-panel>
```
## API ## API
### Props ### Props
@@ -104,6 +116,7 @@ When `magnetic` is set to `false`, the panel will not automatically snap to anch
| duration | Transition duration, unit second | _number \| string_ | `0.3` | | duration | Transition duration, unit second | _number \| string_ | `0.3` |
| magnetic | Whether to enable magnetic adsorption to anchors. When disabled, panel can stop at any position within the anchor boundaries | _boolean_ | `true` | | magnetic | Whether to enable magnetic adsorption to anchors. When disabled, panel can stop at any position within the anchor boundaries | _boolean_ | `true` |
| content-draggable | Allow dragging content | _boolean_ | `true` | | content-draggable | Allow dragging content | _boolean_ | `true` |
| draggable | Whether to allow dragging the panel. When disabled, the header drag bar will be hidden | _boolean_ | `true` |
| lock-scroll `v4.6.4` | When not dragging, Whether to lock background scroll | _boolean_ | `false` | | lock-scroll `v4.6.4` | When not dragging, Whether to lock background scroll | _boolean_ | `false` |
| safe-area-inset-bottom | Whether to enable bottom safe area adaptation | _boolean_ | `true` | | safe-area-inset-bottom | Whether to enable bottom safe area adaptation | _boolean_ | `true` |
@@ -93,6 +93,18 @@ export default {
</van-floating-panel> </van-floating-panel>
``` ```
### 禁用拖拽
你可以通过 `draggable` 属性来禁用面板的拖拽功能。当设置为 `false` 时,面板将不可拖拽,同时头部拖拽栏也会被隐藏。
```html
<van-floating-panel :draggable="false">
<div style="text-align: center; padding: 15px">
<p>该面板不可拖拽</p>
</div>
</van-floating-panel>
```
## API ## API
### Props ### Props
@@ -104,6 +116,7 @@ export default {
| duration | 动画时长,单位秒,设置为 0 可以禁用动画 | _number \| string_ | `0.3` | | duration | 动画时长,单位秒,设置为 0 可以禁用动画 | _number \| string_ | `0.3` |
| magnetic | 是否启用磁力吸附到锚点。禁用后面板可在锚点边界范围内任意位置停留 | _boolean_ | `true` | | magnetic | 是否启用磁力吸附到锚点。禁用后面板可在锚点边界范围内任意位置停留 | _boolean_ | `true` |
| content-draggable | 允许拖拽内容容器 | _boolean_ | `true` | | content-draggable | 允许拖拽内容容器 | _boolean_ | `true` |
| draggable | 是否允许拖拽面板。禁用后头部拖拽栏会被隐藏 | _boolean_ | `true` |
| lock-scroll `v4.6.4` | 当不拖拽时,是否锁定背景滚动 | _boolean_ | `false` | | lock-scroll `v4.6.4` | 当不拖拽时,是否锁定背景滚动 | _boolean_ | `false` |
| safe-area-inset-bottom | 是否开启[底部安全区适配](#/zh-CN/advanced-usage#di-bu-an-quan-qu-gua-pei) | _boolean_ | `true` | | safe-area-inset-bottom | 是否开启[底部安全区适配](#/zh-CN/advanced-usage#di-bu-an-quan-qu-gua-pei) | _boolean_ | `true` |
@@ -15,6 +15,8 @@ const t = useTranslate({
customAnchors: '自定义锚点', customAnchors: '自定义锚点',
headDragOnly: '仅头部拖拽', headDragOnly: '仅头部拖拽',
disableMagnetic: '禁用吸附', disableMagnetic: '禁用吸附',
disableDragging: '禁用拖拽',
panelUnDrag: '该面板不可拖拽',
panelShowHeight: '面板显示高度', panelShowHeight: '面板显示高度',
contentUnDrag: '内容不可拖拽', contentUnDrag: '内容不可拖拽',
magneticDisabled: '已禁用磁力吸附,可在边界内任意停留', magneticDisabled: '已禁用磁力吸附,可在边界内任意停留',
@@ -23,6 +25,8 @@ const t = useTranslate({
customAnchors: 'Custom Anchors', customAnchors: 'Custom Anchors',
headDragOnly: 'Head Drag Only', headDragOnly: 'Head Drag Only',
disableMagnetic: 'Disable Magnetic', disableMagnetic: 'Disable Magnetic',
disableDragging: 'Disable Dragging',
panelUnDrag: 'This panel cannot be dragged',
panelShowHeight: 'Panel Show Height', panelShowHeight: 'Panel Show Height',
contentUnDrag: 'Content cannot be dragged', contentUnDrag: 'Content cannot be dragged',
magneticDisabled: 'Magnetic disabled, free positioning within boundaries', magneticDisabled: 'Magnetic disabled, free positioning within boundaries',
@@ -76,5 +80,13 @@ const height = ref(anchors[0]);
</div> </div>
</van-floating-panel> </van-floating-panel>
</van-tab> </van-tab>
<van-tab :title="t('disableDragging')">
<van-floating-panel :draggable="false">
<div style="text-align: center; padding: 15px">
<p>{{ t('panelUnDrag') }}</p>
</div>
</van-floating-panel>
</van-tab>
</van-tabs> </van-tabs>
</template> </template>
@@ -56,6 +56,16 @@ exports[`should render demo and match snapshot 1`] = `
style="display:none;" style="display:none;"
> >
</div> </div>
<div
id="van-tab"
role="tabpanel"
class="van-tab__panel"
tabindex="-1"
aria-labelledby="van-tabs-4"
data-allow-mismatch="attribute"
style="display:none;"
>
</div>
</div> </div>
</div> </div>
`; `;
@@ -60,6 +60,19 @@ exports[`should render demo and match snapshot 1`] = `
Disable Magnetic Disable Magnetic
</span> </span>
</div> </div>
<div
id="van-tabs-4"
role="tab"
class="van-tab van-tab--line van-tab--grow"
tabindex="-1"
aria-selected="false"
aria-controls="van-tab"
data-allow-mismatch="attribute"
>
<span class="van-tab__text">
Disable Dragging
</span>
</div>
<div <div
class="van-tabs__line" class="van-tabs__line"
style="transform: translateX(50px) translateX(-50%);" style="transform: translateX(50px) translateX(-50%);"
@@ -306,6 +319,16 @@ exports[`should render demo and match snapshot 1`] = `
style="display: none;" style="display: none;"
> >
</div> </div>
<div
id="van-tab"
role="tabpanel"
class="van-tab__panel"
tabindex="-1"
aria-labelledby="van-tabs-4"
data-allow-mismatch="attribute"
style="display: none;"
>
</div>
</div> </div>
</div> </div>
`; `;
@@ -189,3 +189,38 @@ test('should add padding bottom to content when panel is not fully expanded', as
await wrapper.setProps({ height: 400 }); await wrapper.setProps({ height: 400 });
expect(content.style.paddingBottom).toBe('0px'); expect(content.style.paddingBottom).toBe('0px');
}); });
test('should not allow dragging when draggable is false', async () => {
const wrapper = mount({
render() {
return (
<FloatingPanel
anchors={[100, 200, 400]}
draggable={false}
onHeightChange={(h) => this.$emit('change', h)}
>
Content
</FloatingPanel>
);
},
});
expect(wrapper.find('.van-floating-panel__header').exists()).toBe(false);
await triggerDrag(wrapper.find('.van-floating-panel__content'), 0, -199);
await later();
expect(wrapper.emitted('change')).toBeFalsy();
});
test('should render header slot even when draggable is false', () => {
const wrapper = mount(FloatingPanel, {
props: {
draggable: false,
},
slots: {
header: () => 'Custom Header',
},
});
expect(wrapper.html()).toContain('Custom Header');
});