mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(FloatingPanel): add new FloatingPanel component (#11832)
* fix(Swipe): props changed but component didn't * fix(Swipe): target watch windowWidth * Update packages/vant/src/swipe/Swipe.tsx * fix(Sticky): resize or orientationchange wrapper no reset width and height * fix(Sticky): resize or orientationchange wrapper no reset width and height * fix(Sticky): resize or orientationchange wrapper no reset width and height * fix(Sticky): resize or orientationchange wrapper no reset width and height * chore: sync * feat(FloatingPanel): add new FloatingPanel component * feat(FloatingPanel): add new FloatingPanel component * feat(FloatingPanel): optimized code * feat(FloatingPanel): optimized code --------- Co-authored-by: zhousg <345313727@qq.com> Co-authored-by: neverland <jait.chen@foxmail.com>
This commit is contained in:
parent
890ad11822
commit
b8424849f0
139
packages/vant/src/floating-panel/FloatingPanel.tsx
Normal file
139
packages/vant/src/floating-panel/FloatingPanel.tsx
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { computed, defineComponent, ref, type ExtractPropTypes } from 'vue';
|
||||||
|
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';
|
||||||
|
|
||||||
|
const { height: windowHeight } = useWindowSize();
|
||||||
|
|
||||||
|
export const floatingPanelProps = {
|
||||||
|
anchors: makeArrayProp<number>(),
|
||||||
|
contentDraggable: truthProp,
|
||||||
|
safeAreaInsetBottom: truthProp,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FloatingPanelProps = ExtractPropTypes<typeof floatingPanelProps>;
|
||||||
|
|
||||||
|
const [name, bem] = createNamespace('floating-panel');
|
||||||
|
|
||||||
|
const DAMP = 0.2;
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name,
|
||||||
|
|
||||||
|
props: floatingPanelProps,
|
||||||
|
|
||||||
|
emits: ['heightChange'],
|
||||||
|
|
||||||
|
setup(props, { emit, slots }) {
|
||||||
|
const rootRef = ref<HTMLDivElement>();
|
||||||
|
const contentRef = ref<HTMLDivElement>();
|
||||||
|
|
||||||
|
const boundary = computed(() => ({
|
||||||
|
min: props.anchors[0] ?? 100,
|
||||||
|
max:
|
||||||
|
props.anchors[props.anchors.length - 1] ??
|
||||||
|
Math.round(windowHeight.value * 0.6),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const anchors = computed(() =>
|
||||||
|
props.anchors.length >= 2
|
||||||
|
? props.anchors
|
||||||
|
: [boundary.value.min, boundary.value.max]
|
||||||
|
);
|
||||||
|
|
||||||
|
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)}))`,
|
||||||
|
transition: !dragging.value ? 'transform .3s' : 'none',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ease = (moveY: number): number => {
|
||||||
|
const absDistance = Math.abs(moveY);
|
||||||
|
const { min, max } = boundary.value;
|
||||||
|
|
||||||
|
if (absDistance > max) {
|
||||||
|
return -(max + (absDistance - max) * DAMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (absDistance < min) {
|
||||||
|
return -(min - (min - absDistance) * DAMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return moveY;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closest = (arr: number[], target: number) =>
|
||||||
|
arr.reduce((pre, cur) =>
|
||||||
|
Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
|
||||||
|
);
|
||||||
|
|
||||||
|
let startY = currentY.value;
|
||||||
|
const touch = useTouch();
|
||||||
|
|
||||||
|
const onTouchstart = (e: TouchEvent) => {
|
||||||
|
touch.start(e);
|
||||||
|
dragging.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTouchmove = (e: TouchEvent) => {
|
||||||
|
touch.move(e);
|
||||||
|
|
||||||
|
const target = e.target as Element;
|
||||||
|
if (contentRef.value === target || contentRef.value?.contains(target)) {
|
||||||
|
if (!props.contentDraggable) return;
|
||||||
|
|
||||||
|
if (-startY < boundary.value.max) {
|
||||||
|
if (e.cancelable) e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
} else if (
|
||||||
|
!(contentRef.value.scrollTop <= 0 && touch.deltaY.value > 0)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveY = touch.deltaY.value + startY;
|
||||||
|
|
||||||
|
currentY.value = ease(moveY);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTouchend = () => {
|
||||||
|
dragging.value = false;
|
||||||
|
|
||||||
|
const height = Math.abs(currentY.value);
|
||||||
|
const closestHeight = closest(anchors.value, height);
|
||||||
|
currentY.value = -closestHeight;
|
||||||
|
|
||||||
|
if (currentY.value !== startY) {
|
||||||
|
emit('heightChange', closestHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
startY = currentY.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
useLockScroll(rootRef, () => true);
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<div
|
||||||
|
class={[bem(), { 'van-safe-area-bottom': props.safeAreaInsetBottom }]}
|
||||||
|
ref={rootRef}
|
||||||
|
style={rootStyle.value}
|
||||||
|
onTouchstartPassive={onTouchstart}
|
||||||
|
onTouchmove={onTouchmove}
|
||||||
|
onTouchend={onTouchend}
|
||||||
|
onTouchcancel={onTouchend}
|
||||||
|
>
|
||||||
|
<div class={bem('header')}>
|
||||||
|
<div class={bem('header-bar')} />
|
||||||
|
</div>
|
||||||
|
<div class={bem('content')} ref={contentRef}>
|
||||||
|
{slots.default?.()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
109
packages/vant/src/floating-panel/README.md
Normal file
109
packages/vant/src/floating-panel/README.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# FloatingPanel
|
||||||
|
|
||||||
|
### Intro
|
||||||
|
|
||||||
|
A panel that floats at the bottom of a page, which can be dragged up and down to browse content, often used to provide additional functionality or information.
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
Register component globally via `app.use`, refer to [Component Registration](#/en-US/advanced-usage#zu-jian-zhu-ce) for more registration ways.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import { FloatingPanel } from 'vant';
|
||||||
|
|
||||||
|
const app = createApp();
|
||||||
|
app.use(FloatingPanel);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-floating-panel>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-cell
|
||||||
|
v-for="i in 26"
|
||||||
|
:key="i"
|
||||||
|
:title="String.fromCharCode(i + 64)"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</van-cell-group>
|
||||||
|
</van-floating-panel>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Anchors
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-floating-panel :anchors="anchors" @height-change="onHeightChange">
|
||||||
|
<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;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Head Drag Only
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-floating-panel :content-draggable="false">
|
||||||
|
<div style="text-align: center; padding: 15px">
|
||||||
|
<p>Content cannot be dragged</p>
|
||||||
|
</div>
|
||||||
|
</van-floating-panel>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| Attribute | Description | Type | Default |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 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_ |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------- | -------------------- |
|
||||||
|
| default | Custom panel content |
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
The component exports the following type definitions:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { FloatingPanelProps } from 'vant';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Theming
|
||||||
|
|
||||||
|
### CSS Variables
|
||||||
|
|
||||||
|
The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/config-provider).
|
||||||
|
|
||||||
|
| Name | Default Value | Description |
|
||||||
|
| ---------------------------------- | ------------------------- | ----------- |
|
||||||
|
| --van-floating-panel-border-radius | _16px_ | - |
|
||||||
|
| --van-floating-panel-header-height | _30px_ | - |
|
||||||
|
| --van-floating-panel-z-index | _999_ | - |
|
||||||
|
| --van-floating-panel-background | _var(--van-background-2)_ | - |
|
109
packages/vant/src/floating-panel/README.zh-CN.md
Normal file
109
packages/vant/src/floating-panel/README.zh-CN.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# FloatingPanel 浮动面板
|
||||||
|
|
||||||
|
### 介绍
|
||||||
|
|
||||||
|
浮动在页面底部的面板,可以上下拖动来浏览内容,常用于提供额外的功能或信息。
|
||||||
|
|
||||||
|
### 引入
|
||||||
|
|
||||||
|
通过以下方式来全局注册组件,更多注册方式请参考[组件注册](#/zh-CN/advanced-usage#zu-jian-zhu-ce)。
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import { FloatingPanel } from 'vant';
|
||||||
|
|
||||||
|
const app = createApp();
|
||||||
|
app.use(FloatingPanel);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 代码演示
|
||||||
|
|
||||||
|
### 基础用法
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-floating-panel>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-cell
|
||||||
|
v-for="i in 26"
|
||||||
|
:key="i"
|
||||||
|
:title="String.fromCharCode(i + 64)"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</van-cell-group>
|
||||||
|
</van-floating-panel>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义锚点
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-floating-panel :anchors="anchors" @height-change="onHeightChange">
|
||||||
|
<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;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 仅头部拖拽
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-floating-panel :content-draggable="false">
|
||||||
|
<div style="text-align: center; padding: 15px">
|
||||||
|
<p>内容不可拖拽</p>
|
||||||
|
</div>
|
||||||
|
</van-floating-panel>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 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_ |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------- | -------------- |
|
||||||
|
| default | 自定义面板内容 |
|
||||||
|
|
||||||
|
### 类型定义
|
||||||
|
|
||||||
|
组件导出以下类型定义:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { FloatingPanelProps } from 'vant';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 主题定制
|
||||||
|
|
||||||
|
### 样式变量
|
||||||
|
|
||||||
|
组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/config-provider)。
|
||||||
|
|
||||||
|
| Name | Default Value | Description |
|
||||||
|
| ---------------------------------- | ------------------------- | ----------- |
|
||||||
|
| --van-floating-panel-border-radius | _16px_ | - |
|
||||||
|
| --van-floating-panel-header-height | _30px_ | - |
|
||||||
|
| --van-floating-panel-z-index | _999_ | - |
|
||||||
|
| --van-floating-panel-background | _var(--van-background-2)_ | - |
|
69
packages/vant/src/floating-panel/demo/index.vue
Normal file
69
packages/vant/src/floating-panel/demo/index.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import VanTabs from '../../tabs';
|
||||||
|
import VanTab from '../../tab';
|
||||||
|
import VanCell from '../../cell';
|
||||||
|
import VanCellGroup from '../../cell-group';
|
||||||
|
import VanFloatingPanel from '..';
|
||||||
|
import { useTranslate } from '../../../docs/site';
|
||||||
|
import { useWindowSize } from '@vant/use';
|
||||||
|
|
||||||
|
const { height: windowHeight } = useWindowSize();
|
||||||
|
|
||||||
|
const t = useTranslate({
|
||||||
|
'zh-CN': {
|
||||||
|
customAnchors: '自定义锚点',
|
||||||
|
headDragOnly: '仅头部拖拽',
|
||||||
|
panelShowHeight: '面板显示高度',
|
||||||
|
contentUnDrag: '内容不可拖拽',
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
customAnchors: 'Custom Anchors',
|
||||||
|
headDragOnly: 'Head Drag Only',
|
||||||
|
panelShowHeight: 'Panel Show Height',
|
||||||
|
contentUnDrag: 'Content cannot be dragged',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const anchors = [
|
||||||
|
100,
|
||||||
|
Math.round(0.4 * windowHeight.value),
|
||||||
|
Math.round(0.7 * windowHeight.value),
|
||||||
|
];
|
||||||
|
|
||||||
|
const height = ref(anchors[0]);
|
||||||
|
const onHeightChange = (h: number) => {
|
||||||
|
height.value = h;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<van-tabs>
|
||||||
|
<van-tab :title="t('basicUsage')">
|
||||||
|
<van-floating-panel>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-cell
|
||||||
|
v-for="i in 26"
|
||||||
|
:key="i"
|
||||||
|
:title="String.fromCharCode(i + 64)"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</van-cell-group>
|
||||||
|
</van-floating-panel>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab :title="t('customAnchors')">
|
||||||
|
<van-floating-panel :anchors="anchors" @height-change="onHeightChange">
|
||||||
|
<div style="text-align: center; padding: 15px">
|
||||||
|
<p>{{ t('panelShowHeight') }} {{ height }} px</p>
|
||||||
|
</div>
|
||||||
|
</van-floating-panel>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab :title="t('headDragOnly')">
|
||||||
|
<van-floating-panel :content-draggable="false">
|
||||||
|
<div style="text-align: center; padding: 15px">
|
||||||
|
<p>{{ t('contentUnDrag') }}</p>
|
||||||
|
</div>
|
||||||
|
</van-floating-panel>
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</template>
|
52
packages/vant/src/floating-panel/index.less
Normal file
52
packages/vant/src/floating-panel/index.less
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
:root {
|
||||||
|
--van-floating-panel-border-radius: 16px;
|
||||||
|
--van-floating-panel-header-height: 30px;
|
||||||
|
--van-floating-panel-z-index: 999;
|
||||||
|
--van-floating-panel-background: var(--van-background-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-floating-panel {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100vw;
|
||||||
|
z-index: var(--van-floating-panel-z-index);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
touch-action: none;
|
||||||
|
border-top-left-radius: var(--van-floating-panel-border-radius);
|
||||||
|
border-top-right-radius: var(--van-floating-panel-border-radius);
|
||||||
|
background: var(--van-floating-panel-background);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -100vh;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
height: var(--van-floating-panel-header-height);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&-bar {
|
||||||
|
height: 3px;
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--van-gray-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: var(--van-floating-panel-background);
|
||||||
|
}
|
||||||
|
}
|
14
packages/vant/src/floating-panel/index.ts
Normal file
14
packages/vant/src/floating-panel/index.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { withInstall } from '../utils';
|
||||||
|
import _FloatingPanel, { FloatingPanelProps } from './FloatingPanel';
|
||||||
|
|
||||||
|
export const FloatingPanel = withInstall(_FloatingPanel);
|
||||||
|
|
||||||
|
export default FloatingPanel;
|
||||||
|
|
||||||
|
export type { FloatingPanelProps };
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
VanFloatingPanel: typeof FloatingPanel;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`should render demo and match snapshot 1`] = `
|
||||||
|
<div class="van-tabs van-tabs--line">
|
||||||
|
<!--[-->
|
||||||
|
<div class="van-tabs__wrap">
|
||||||
|
<div role="tablist"
|
||||||
|
class="van-tabs__nav van-tabs__nav--line"
|
||||||
|
style
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
>
|
||||||
|
<!--[-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-tabs__content">
|
||||||
|
<!--[-->
|
||||||
|
<!--[-->
|
||||||
|
<div id="van-tab"
|
||||||
|
role="tabpanel"
|
||||||
|
class="van-tab__panel"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="van-tabs-0"
|
||||||
|
style="display:none;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div id="van-tab"
|
||||||
|
role="tabpanel"
|
||||||
|
class="van-tab__panel"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="van-tabs-1"
|
||||||
|
style="display:none;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div id="van-tab"
|
||||||
|
role="tabpanel"
|
||||||
|
class="van-tab__panel"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="van-tabs-2"
|
||||||
|
style="display:none;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,270 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`should render demo and match snapshot 1`] = `
|
||||||
|
<div class="van-tabs van-tabs--line">
|
||||||
|
<div class="van-tabs__wrap">
|
||||||
|
<div role="tablist"
|
||||||
|
class="van-tabs__nav van-tabs__nav--line"
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
>
|
||||||
|
<div id="van-tabs-0"
|
||||||
|
role="tab"
|
||||||
|
class="van-tab van-tab--line van-tab--active"
|
||||||
|
tabindex="0"
|
||||||
|
aria-selected="true"
|
||||||
|
aria-controls="van-tab"
|
||||||
|
>
|
||||||
|
<span class="van-tab__text van-tab__text--ellipsis">
|
||||||
|
Basic Usage
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="van-tabs-1"
|
||||||
|
role="tab"
|
||||||
|
class="van-tab van-tab--line"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-selected="false"
|
||||||
|
aria-controls="van-tab"
|
||||||
|
>
|
||||||
|
<span class="van-tab__text van-tab__text--ellipsis">
|
||||||
|
Custom Anchors
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="van-tabs-2"
|
||||||
|
role="tab"
|
||||||
|
class="van-tab van-tab--line"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-selected="false"
|
||||||
|
aria-controls="van-tab"
|
||||||
|
>
|
||||||
|
<span class="van-tab__text van-tab__text--ellipsis">
|
||||||
|
Head Drag Only
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="van-tabs__line"
|
||||||
|
style="transform: translateX(50px) translateX(-50%);"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-tabs__content">
|
||||||
|
<div id="van-tab"
|
||||||
|
role="tabpanel"
|
||||||
|
class="van-tab__panel"
|
||||||
|
tabindex="0"
|
||||||
|
aria-labelledby="van-tabs-0"
|
||||||
|
style
|
||||||
|
>
|
||||||
|
<div class="van-floating-panel van-safe-area-bottom"
|
||||||
|
style="height: 461px; transform: translateY(calc(100% + -100px)); transition: transform .3s;"
|
||||||
|
>
|
||||||
|
<div class="van-floating-panel__header">
|
||||||
|
<div class="van-floating-panel__header-bar">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-floating-panel__content">
|
||||||
|
<div class="van-cell-group van-hairline--top-bottom">
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
A
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
B
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
C
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
D
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
E
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
F
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
G
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
H
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
I
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
J
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
K
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
L
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
M
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
N
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
O
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
P
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
Q
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
R
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
S
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
T
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
U
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
V
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
W
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
X
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
Y
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-cell van-cell--large">
|
||||||
|
<div class="van-cell__title">
|
||||||
|
<span>
|
||||||
|
Z
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="van-tab"
|
||||||
|
role="tabpanel"
|
||||||
|
class="van-tab__panel"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="van-tabs-1"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div id="van-tab"
|
||||||
|
role="tabpanel"
|
||||||
|
class="van-tab__panel"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="van-tabs-2"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,29 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`should drag adsorption effect when anchors props is [100, 200, 400] 1`] = `
|
||||||
|
<div class="van-floating-panel van-safe-area-bottom"
|
||||||
|
style="height: 400px; transform: translateY(calc(100% + -100px)); transition: transform .3s;"
|
||||||
|
>
|
||||||
|
<div class="van-floating-panel__header">
|
||||||
|
<div class="van-floating-panel__header-bar">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-floating-panel__content">
|
||||||
|
内容
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`should minHeight 100 and maxHeight 0.6 innerHeight when anchors props do not 1`] = `
|
||||||
|
<div class="van-floating-panel van-safe-area-bottom"
|
||||||
|
style="height: 461px; transform: translateY(calc(100% + -100px)); transition: transform .3s;"
|
||||||
|
>
|
||||||
|
<div class="van-floating-panel__header">
|
||||||
|
<div class="van-floating-panel__header-bar">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-floating-panel__content">
|
||||||
|
Content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
7
packages/vant/src/floating-panel/test/demo-ssr.spec.ts
Normal file
7
packages/vant/src/floating-panel/test/demo-ssr.spec.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment node
|
||||||
|
*/
|
||||||
|
import Demo from '../demo/index.vue';
|
||||||
|
import { snapshotDemo } from '../../../test/demo';
|
||||||
|
|
||||||
|
snapshotDemo(Demo, { ssr: true });
|
5
packages/vant/src/floating-panel/test/demo.spec.ts
Normal file
5
packages/vant/src/floating-panel/test/demo.spec.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Demo from '../demo/index.vue';
|
||||||
|
|
||||||
|
import { snapshotDemo } from '../../../test/demo';
|
||||||
|
|
||||||
|
snapshotDemo(Demo);
|
118
packages/vant/src/floating-panel/test/index.spec.tsx
Normal file
118
packages/vant/src/floating-panel/test/index.spec.tsx
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { later, mount, triggerDrag } from '../../../test';
|
||||||
|
import FloatingPanel from '..';
|
||||||
|
|
||||||
|
test('should minHeight 100 and maxHeight 0.6 innerHeight when anchors props do not', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return <FloatingPanel>Content</FloatingPanel>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.height).toBe(
|
||||||
|
`${Math.round(window.innerHeight * 0.6)}px`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-100px'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should drag adsorption effect when anchors props is [100, 200, 400]', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return <FloatingPanel anchors={[100, 200, 400]}>内容</FloatingPanel>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.height).toBe('400px');
|
||||||
|
|
||||||
|
// drag 10
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, 10);
|
||||||
|
await later();
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-100px'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-100px'
|
||||||
|
);
|
||||||
|
|
||||||
|
// drag -49
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, -49);
|
||||||
|
await later();
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-100px'
|
||||||
|
);
|
||||||
|
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, -199);
|
||||||
|
await later();
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-200px'
|
||||||
|
);
|
||||||
|
|
||||||
|
// drag -300
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, -200);
|
||||||
|
await later();
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-400px'
|
||||||
|
);
|
||||||
|
|
||||||
|
// drag -500
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, -500);
|
||||||
|
await later();
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-400px'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should emit height-change when height change in anchors', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<FloatingPanel
|
||||||
|
anchors={[100, 200, 400]}
|
||||||
|
onHeightChange={(h) => this.$emit('change', h)}
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</FloatingPanel>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, -199);
|
||||||
|
await later();
|
||||||
|
|
||||||
|
expect((wrapper.element as HTMLDivElement).style.transform).toContain(
|
||||||
|
'-200px'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wrapper.emitted('change')?.[0][0]).toEqual(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should only drag header when allowDraggingContent is false', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<FloatingPanel
|
||||||
|
anchors={[100, 200, 400]}
|
||||||
|
onHeightChange={(h) => this.$emit('change', h)}
|
||||||
|
contentDraggable={false}
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</FloatingPanel>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__content'), 0, -199);
|
||||||
|
await later();
|
||||||
|
expect(wrapper.emitted('change')).toBeFalsy();
|
||||||
|
|
||||||
|
await triggerDrag(wrapper.find('.van-floating-panel__header'), 0, -199);
|
||||||
|
await later();
|
||||||
|
expect(wrapper.emitted('change')).toBeTruthy();
|
||||||
|
});
|
@ -251,6 +251,10 @@ location.href = location.href.replace('youzan.github.io', 'vant-ui.github.io');
|
|||||||
path: 'dropdown-menu',
|
path: 'dropdown-menu',
|
||||||
title: 'DropdownMenu 下拉菜单',
|
title: 'DropdownMenu 下拉菜单',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'floating-panel',
|
||||||
|
title: 'FloatingPanel 浮动面板',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'loading',
|
path: 'loading',
|
||||||
title: 'Loading 加载',
|
title: 'Loading 加载',
|
||||||
@ -703,6 +707,10 @@ location.href = location.href.replace('youzan.github.io', 'vant-ui.github.io');
|
|||||||
path: 'dropdown-menu',
|
path: 'dropdown-menu',
|
||||||
title: 'DropdownMenu',
|
title: 'DropdownMenu',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'floating-panel',
|
||||||
|
title: 'FloatingPanel',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'loading',
|
path: 'loading',
|
||||||
title: 'Loading',
|
title: 'Loading',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user