first commit
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
:: BASE_DOC ::
|
||||
|
||||
## API
|
||||
|
||||
### Popover Props
|
||||
|
||||
name | type | default | description | required
|
||||
-- | -- | -- | -- | --
|
||||
custom-style | Object | - | CSS(Cascading Style Sheets) | N
|
||||
close-on-click-outside | Boolean | true | \- | N
|
||||
content | String | - | \- | N
|
||||
fixed | Boolean | false | \- | N
|
||||
placement | String | top | options: top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
|
||||
show-arrow | Boolean | true | \- | N
|
||||
theme | String | dark | options: dark/light/brand/success/warning/error | N
|
||||
visible | Boolean | undefined | `v-model:visible` is supported | N
|
||||
|
||||
### Popover Events
|
||||
|
||||
name | params | description
|
||||
-- | -- | --
|
||||
visible-change | `(visible: boolean)` | \-
|
||||
|
||||
### Popover Slots
|
||||
|
||||
name | Description
|
||||
-- | --
|
||||
\- | \-
|
||||
content | \-
|
||||
|
||||
### Popover External Classes
|
||||
|
||||
className | Description
|
||||
-- | --
|
||||
t-class | \-
|
||||
t-class-content | \-
|
||||
|
||||
### CSS Variables
|
||||
|
||||
The component provides the following CSS variables, which can be used to customize styles.
|
||||
Name | Default Value | Description
|
||||
-- | -- | --
|
||||
--td-popover-brand-bg-color | @primary-color-1 | -
|
||||
--td-popover-brand-color | @primary-color-7 | -
|
||||
--td-popover-dark-bg-color | @font-gray-1 | -
|
||||
--td-popover-dark-color | @text-color-anti | -
|
||||
--td-popover-error-bg-color | @error-color-1 | -
|
||||
--td-popover-error-color | @error-color-6 | -
|
||||
--td-popover-light-bg-color | @bg-color-container | -
|
||||
--td-popover-light-color | @text-color-primary | -
|
||||
--td-popover-padding | 24rpx | -
|
||||
--td-popover-success-bg-color | @success-color-1 | -
|
||||
--td-popover-success-color | @success-color-5 | -
|
||||
--td-popover-warning-bg-color | @warning-color-1 | -
|
||||
--td-popover-warning-color | @warning-color-5 | -
|
||||
85
uni_modules/tdesign-uniapp/components/popover/README.md
Normal file
85
uni_modules/tdesign-uniapp/components/popover/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Popover 弹出气泡
|
||||
description: 用于文字提示的气泡框。
|
||||
spline: data
|
||||
isComponent: true
|
||||
---
|
||||
|
||||
|
||||
## 引入
|
||||
|
||||
可在 `main.ts` 或在需要使用的页面或组件中引入。
|
||||
|
||||
```js
|
||||
import TPopover from '@tdesign/uniapp/popover/popover.vue';
|
||||
```
|
||||
|
||||
### 组件类型
|
||||
|
||||
#### 带箭头的弹出气泡
|
||||
|
||||
{{ base }}
|
||||
|
||||
### 组件样式
|
||||
|
||||
#### 气泡主题
|
||||
|
||||
{{ theme }}
|
||||
|
||||
#### 气泡位置
|
||||
|
||||
{{ placement }}
|
||||
|
||||
## API
|
||||
|
||||
### Popover Props
|
||||
|
||||
名称 | 类型 | 默认值 | 描述 | 必传
|
||||
-- | -- | -- | -- | --
|
||||
custom-style | Object | - | 自定义样式 | N
|
||||
close-on-click-outside | Boolean | true | 是否在点击外部元素后关闭菜单 | N
|
||||
content | String | - | 确认框内容 | N
|
||||
fixed | Boolean | false | 如果触发元素为 `fixed` 场景,需要显示指定 `fixed` 属性为 `true`,同时需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素 | N
|
||||
placement | String | top | 浮层出现位置。可选项:top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
|
||||
show-arrow | Boolean | true | 是否显示浮层箭头 | N
|
||||
theme | String | dark | 弹出气泡主题。可选项:dark/light/brand/success/warning/error | N
|
||||
visible | Boolean | undefined | 是否显示气泡确认框。支持语法糖 `v-model:visible` | N
|
||||
|
||||
### Popover Events
|
||||
|
||||
名称 | 参数 | 描述
|
||||
-- | -- | --
|
||||
visible-change | `(visible: boolean)` | 确认框显示或隐藏时触发
|
||||
|
||||
### Popover Slots
|
||||
|
||||
名称 | 描述
|
||||
-- | --
|
||||
\- | 默认插槽,用于自定义触发元素
|
||||
content | 自定义 `content` 显示内容
|
||||
|
||||
### Popover External Classes
|
||||
|
||||
类名 | 描述
|
||||
-- | --
|
||||
t-class | 根节点样式类
|
||||
t-class-content | 内容样式类
|
||||
|
||||
### CSS Variables
|
||||
|
||||
组件提供了下列 CSS 变量,可用于自定义样式。
|
||||
名称 | 默认值 | 描述
|
||||
-- | -- | --
|
||||
--td-popover-brand-bg-color | @primary-color-1 | -
|
||||
--td-popover-brand-color | @primary-color-7 | -
|
||||
--td-popover-dark-bg-color | @font-gray-1 | -
|
||||
--td-popover-dark-color | @text-color-anti | -
|
||||
--td-popover-error-bg-color | @error-color-1 | -
|
||||
--td-popover-error-color | @error-color-6 | -
|
||||
--td-popover-light-bg-color | @bg-color-container | -
|
||||
--td-popover-light-color | @text-color-primary | -
|
||||
--td-popover-padding | 24rpx | -
|
||||
--td-popover-success-bg-color | @success-color-1 | -
|
||||
--td-popover-success-color | @success-color-5 | -
|
||||
--td-popover-warning-bg-color | @warning-color-1 | -
|
||||
--td-popover-warning-color | @warning-color-5 | -
|
||||
271
uni_modules/tdesign-uniapp/components/popover/popover.css
Normal file
271
uni_modules/tdesign-uniapp/components/popover/popover.css
Normal file
@@ -0,0 +1,271 @@
|
||||
.t-popover__wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
.t-popover {
|
||||
position: absolute;
|
||||
z-index: 11500;
|
||||
overflow: visible;
|
||||
transition: 0.2s ease-in-out all;
|
||||
}
|
||||
.t-popover--fixed {
|
||||
position: fixed;
|
||||
}
|
||||
.t-popover__content {
|
||||
position: relative;
|
||||
padding: var(--td-popover-padding, 24rpx);
|
||||
border-radius: 12rpx;
|
||||
box-shadow: var(--td-shadow-3, 0 6px 30px 5px rgba(0, 0, 0, 0.05), 0 16px 24px 2px rgba(0, 0, 0, 0.04), 0 8px 10px -5px rgba(0, 0, 0, 0.08));
|
||||
font-size: var(--td-font-size-m, 32rpx);
|
||||
line-height: 48rpx;
|
||||
border-radius: 6px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
word-break: break-all;
|
||||
}
|
||||
.t-popover__arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-width: 16rpx;
|
||||
}
|
||||
.t-popover .t-popover--dark {
|
||||
color: var(--td-popover-dark-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff)));
|
||||
background: var(--td-popover-dark-bg-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
|
||||
}
|
||||
.t-popover .t-popover--dark .t-popover__arrow {
|
||||
color: var(--td-popover-dark-bg-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
|
||||
}
|
||||
.t-popover .t-popover--light {
|
||||
color: var(--td-popover-light-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))));
|
||||
background: var(--td-popover-light-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff)));
|
||||
}
|
||||
.t-popover .t-popover--light .t-popover__arrow {
|
||||
color: var(--td-popover-light-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff)));
|
||||
}
|
||||
.t-popover .t-popover--brand {
|
||||
color: var(--td-popover-brand-color, var(--td-primary-color-7, #0052d9));
|
||||
background: var(--td-popover-brand-bg-color, var(--td-primary-color-1, #f2f3ff));
|
||||
}
|
||||
.t-popover .t-popover--brand .t-popover__arrow {
|
||||
color: var(--td-popover-brand-bg-color, var(--td-primary-color-1, #f2f3ff));
|
||||
}
|
||||
.t-popover .t-popover--success {
|
||||
color: var(--td-popover-success-color, var(--td-success-color-5, #2ba471));
|
||||
background: var(--td-popover-success-bg-color, var(--td-success-color-1, #e3f9e9));
|
||||
}
|
||||
.t-popover .t-popover--success .t-popover__arrow {
|
||||
color: var(--td-popover-success-bg-color, var(--td-success-color-1, #e3f9e9));
|
||||
}
|
||||
.t-popover .t-popover--warning {
|
||||
color: var(--td-popover-warning-color, var(--td-warning-color-5, #e37318));
|
||||
background: var(--td-popover-warning-bg-color, var(--td-warning-color-1, #fff1e9));
|
||||
}
|
||||
.t-popover .t-popover--warning .t-popover__arrow {
|
||||
color: var(--td-popover-warning-bg-color, var(--td-warning-color-1, #fff1e9));
|
||||
}
|
||||
.t-popover .t-popover--error {
|
||||
color: var(--td-popover-error-color, var(--td-error-color-6, #d54941));
|
||||
background: var(--td-popover-error-bg-color, var(--td-error-color-1, #fff0ed));
|
||||
}
|
||||
.t-popover .t-popover--error .t-popover__arrow {
|
||||
color: var(--td-popover-error-bg-color, var(--td-error-color-1, #fff0ed));
|
||||
}
|
||||
.t-popover.t-fade-enter-to {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
.t-popover.t-fade-enter,
|
||||
.t-popover.t-fade-leave-to {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.t-popover.t-popover--placement-top .t-popover__content,
|
||||
.t-popover.t-popover--placement-top-start .t-popover__content,
|
||||
.t-popover.t-popover--placement-top-end .t-popover__content {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-top .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-top-start .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-top-end .t-popover__content--arrow {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom .t-popover__content,
|
||||
.t-popover.t-popover--placement-bottom-start .t-popover__content,
|
||||
.t-popover.t-popover--placement-bottom-end .t-popover__content {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-bottom-start .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-bottom-end .t-popover__content--arrow {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-left .t-popover__content,
|
||||
.t-popover.t-popover--placement-left-start .t-popover__content,
|
||||
.t-popover.t-popover--placement-left-end .t-popover__content {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-left .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-left-start .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-left-end .t-popover__content--arrow {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-right .t-popover__content,
|
||||
.t-popover.t-popover--placement-right-start .t-popover__content,
|
||||
.t-popover.t-popover--placement-right-end .t-popover__content {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-right .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-right-start .t-popover__content--arrow,
|
||||
.t-popover.t-popover--placement-right-end .t-popover__content--arrow {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
.t-popover.t-popover--placement-top .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-top-start .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-top-end .t-popover__arrow {
|
||||
bottom: 0;
|
||||
border-top-color: currentColor;
|
||||
border-bottom-width: 0;
|
||||
margin-bottom: calc(16rpx * -1);
|
||||
}
|
||||
.t-popover.t-popover--placement-top {
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
.t-popover.t-popover--placement-top .t-popover__arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.t-popover.t-popover--placement-top-start {
|
||||
transform-origin: 0 100%;
|
||||
}
|
||||
.t-popover.t-popover--placement-top-start .t-popover__arrow {
|
||||
left: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-top-end {
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
.t-popover.t-popover--placement-top-end .t-popover__arrow {
|
||||
right: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-bottom-start .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-bottom-end .t-popover__arrow {
|
||||
top: 0;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: currentColor;
|
||||
margin-top: calc(16rpx * -1);
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom {
|
||||
transform-origin: 50% 0;
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom .t-popover__arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom-start {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom-start .t-popover__arrow {
|
||||
left: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom-end {
|
||||
transform-origin: 100% 0;
|
||||
}
|
||||
.t-popover.t-popover--placement-bottom-end .t-popover__arrow {
|
||||
right: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-left .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-left-start .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-left-end .t-popover__arrow {
|
||||
right: 0;
|
||||
border-right-width: 0;
|
||||
border-left-color: currentColor;
|
||||
margin-right: calc(16rpx * -1);
|
||||
}
|
||||
.t-popover.t-popover--placement-left {
|
||||
transform-origin: 100% 50%;
|
||||
}
|
||||
.t-popover.t-popover--placement-left .t-popover__arrow {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.t-popover.t-popover--placement-left-start {
|
||||
transform-origin: 100% 0;
|
||||
}
|
||||
.t-popover.t-popover--placement-left-start .t-popover__arrow {
|
||||
top: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-left-end {
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
.t-popover.t-popover--placement-left-end .t-popover__arrow {
|
||||
bottom: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-right .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-right-start .t-popover__arrow,
|
||||
.t-popover.t-popover--placement-right-end .t-popover__arrow {
|
||||
left: 0;
|
||||
border-right-color: currentColor;
|
||||
border-left-width: 0;
|
||||
margin-left: calc(16rpx * -1);
|
||||
}
|
||||
.t-popover.t-popover--placement-right {
|
||||
transform-origin: 0 50%;
|
||||
}
|
||||
.t-popover.t-popover--placement-right .t-popover__arrow {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.t-popover.t-popover--placement-right-start {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
.t-popover.t-popover--placement-right-start .t-popover__arrow {
|
||||
top: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.t-popover.t-popover--placement-right-end {
|
||||
transform-origin: 0 100%;
|
||||
}
|
||||
.t-popover.t-popover--placement-right-end .t-popover__arrow {
|
||||
bottom: var(--td-popover-padding, 24rpx);
|
||||
}
|
||||
.content-placement-top .t-popover.t-popover--placement-top .t-popover__content,
|
||||
.content-placement-top .t-popover.t-popover--placement-top-start .t-popover__content,
|
||||
.content-placement-top .t-popover.t-popover--placement-top-end .t-popover__content {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.content-placement-top .t-popover.t-popover--placement-top .t-popover__content--arrow,
|
||||
.content-placement-top .t-popover.t-popover--placement-top-start .t-popover__content--arrow,
|
||||
.content-placement-top .t-popover.t-popover--placement-top-end .t-popover__content--arrow {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.content-placement-bottom .t-popover.t-popover--placement-bottom .t-popover__content,
|
||||
.content-placement-bottom .t-popover.t-popover--placement-bottom-start .t-popover__content,
|
||||
.content-placement-bottom .t-popover.t-popover--placement-bottom-end .t-popover__content {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
.content-placement-bottom .t-popover.t-popover--placement-bottom .t-popover__content--arrow,
|
||||
.content-placement-bottom .t-popover.t-popover--placement-bottom-start .t-popover__content--arrow,
|
||||
.content-placement-bottom .t-popover.t-popover--placement-bottom-end .t-popover__content--arrow {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.content-placement-left .t-popover.t-popover--placement-left .t-popover__content,
|
||||
.content-placement-left .t-popover.t-popover--placement-left-start .t-popover__content,
|
||||
.content-placement-left .t-popover.t-popover--placement-left-end .t-popover__content {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.content-placement-left .t-popover.t-popover--placement-left .t-popover__content--arrow,
|
||||
.content-placement-left .t-popover.t-popover--placement-left-start .t-popover__content--arrow,
|
||||
.content-placement-left .t-popover.t-popover--placement-left-end .t-popover__content--arrow {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.content-placement-right .t-popover.t-popover--placement-right .t-popover__content,
|
||||
.content-placement-right .t-popover.t-popover--placement-right-start .t-popover__content,
|
||||
.content-placement-right .t-popover.t-popover--placement-right-end .t-popover__content {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
.content-placement-right .t-popover.t-popover--placement-right .t-popover__content--arrow,
|
||||
.content-placement-right .t-popover.t-popover--placement-right-start .t-popover__content--arrow,
|
||||
.content-placement-right .t-popover.t-popover--placement-right-end .t-popover__content--arrow {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
352
uni_modules/tdesign-uniapp/components/popover/popover.vue
Normal file
352
uni_modules/tdesign-uniapp/components/popover/popover.vue
Normal file
@@ -0,0 +1,352 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
:id="classPrefix + '-wrapper'"
|
||||
:class="classPrefix + '__wrapper'"
|
||||
>
|
||||
<slot />
|
||||
</view>
|
||||
<t-overlay
|
||||
v-if="realVisible && closeOnClickOutside"
|
||||
id="popover-overlay"
|
||||
:visible="true"
|
||||
:z-index="11000"
|
||||
:duration="0"
|
||||
background-color="rgba(0,0,0,0)"
|
||||
@click="onOverlayTap($event, { tagId: 'popover-overlay' })"
|
||||
/>
|
||||
<view
|
||||
v-if="realVisible"
|
||||
:id="classPrefix + '-content'"
|
||||
:style="contentStyle + ' ' + customStyle"
|
||||
:class="classPrefix + ' ' + transitionClass + ' ' + tClass + ' ' + classPrefix + '--placement-' + innerPlacement + ' ' + (fixed ? classPrefix + '--fixed' : '')"
|
||||
>
|
||||
<view :class="classPrefix + '__content ' + classPrefix + '--' + theme + ' ' + tClassContent + ' ' + (showArrow ? classPrefix + '__content--arrow' : '')">
|
||||
<slot name="content" />
|
||||
<view v-if="content">
|
||||
{{ content }}
|
||||
</view>
|
||||
<view
|
||||
v-if="showArrow"
|
||||
:class="classPrefix + '__arrow'"
|
||||
:style="arrowStyle"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TOverlay from '../overlay/overlay';
|
||||
import { getWindowInfo } from '../common/wechat';
|
||||
import { uniComponent } from '../common/src/index';
|
||||
import { prefix } from '../common/config';
|
||||
import props from './props';
|
||||
import { debounce, nextTick, coalesce } from '../common/utils';
|
||||
import { transitionMixins } from '../mixins/transition';
|
||||
import pageScrollMixin from '../mixins/page-scroll';
|
||||
|
||||
delete props.visible;
|
||||
|
||||
const name = `${prefix}-popover`;
|
||||
|
||||
|
||||
export default uniComponent({
|
||||
name,
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
|
||||
controlledProps: [
|
||||
{
|
||||
key: 'visible',
|
||||
event: 'visible-change',
|
||||
},
|
||||
],
|
||||
|
||||
externalClasses: [
|
||||
`${prefix}-class`,
|
||||
`${prefix}-class-content`,
|
||||
`${prefix}-class-trigger`,
|
||||
],
|
||||
|
||||
mixins: [
|
||||
transitionMixins,
|
||||
pageScrollMixin(),
|
||||
],
|
||||
|
||||
components: {
|
||||
TOverlay,
|
||||
},
|
||||
|
||||
props: {
|
||||
...props,
|
||||
},
|
||||
|
||||
emits: [
|
||||
'visible-change',
|
||||
'leaved',
|
||||
'update:visible',
|
||||
],
|
||||
|
||||
data() {
|
||||
return {
|
||||
prefix,
|
||||
classPrefix: name,
|
||||
|
||||
dataVisible: coalesce(this.visible, this.defaultVisible),
|
||||
|
||||
innerPlacement: 'top',
|
||||
contentStyle: '',
|
||||
arrowStyle: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val === undefined || val === null) return;
|
||||
this.updateVisible(val);
|
||||
},
|
||||
'placement'(v) {
|
||||
if (v) {
|
||||
nextTick().then(() => {
|
||||
this.computePosition();
|
||||
});
|
||||
}
|
||||
},
|
||||
realVisible(v) {
|
||||
if (v) {
|
||||
nextTick().then(() => {
|
||||
this.computePosition();
|
||||
nextTick().then(() => {
|
||||
this.computePosition();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
onScroll() {
|
||||
if (this.realVisible) {
|
||||
debounce(() => this.computePosition(), 100);
|
||||
}
|
||||
},
|
||||
|
||||
updateVisible(visible) {
|
||||
if (visible === this.dataVisible) return;
|
||||
this.dataVisible = visible;
|
||||
nextTick().then(() => {
|
||||
this._trigger('visible-change', { visible });
|
||||
this.$emit('update:visible', visible);
|
||||
});
|
||||
},
|
||||
|
||||
onOverlayTap() {
|
||||
if (this.closeOnClickOutside) {
|
||||
this.updateVisible(false);
|
||||
}
|
||||
},
|
||||
|
||||
getToward(placement) {
|
||||
const horizontal = ['top', 'bottom'];
|
||||
const vertical = ['left', 'right'];
|
||||
const isHorizontal = horizontal.find(item => placement.includes(item));
|
||||
const isVertical = vertical.find(item => placement.includes(item));
|
||||
const isBase = [...horizontal, ...vertical].find(item => item === placement);
|
||||
const isEnd = placement.includes('end');
|
||||
return {
|
||||
isHorizontal,
|
||||
isVertical,
|
||||
isBase,
|
||||
isEnd,
|
||||
};
|
||||
},
|
||||
|
||||
calcArrowStyle(placement, contentDom, popoverDom) {
|
||||
const { isHorizontal, isVertical, isBase, isEnd } = this.getToward(placement);
|
||||
|
||||
if (isBase) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const { width, left } = contentDom;
|
||||
const { width: popperWidth, height: popperHeight } = popoverDom;
|
||||
const { windowWidth } = getWindowInfo();
|
||||
|
||||
if (isHorizontal) {
|
||||
const padding = isEnd ? Math.min(width + left, popperWidth) : Math.min(windowWidth - left, popperWidth);
|
||||
if (isEnd) {
|
||||
return `left:${padding - 28}px;`;
|
||||
}
|
||||
return `right:${padding - 28}px;`;
|
||||
}
|
||||
if (isVertical) {
|
||||
const offset = popperHeight - 28;
|
||||
if (isEnd) {
|
||||
return `top:${offset}px;`;
|
||||
}
|
||||
return `bottom:${offset}px;top:unset;`;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
calcContentPosition(placement, triggerRect, contentRect) {
|
||||
let top = 0;
|
||||
let left = 0;
|
||||
|
||||
const isTopBase = placement.startsWith('top');
|
||||
const isBottomBase = placement.startsWith('bottom');
|
||||
const isLeftBase = placement.startsWith('left');
|
||||
const isRightBase = placement.startsWith('right');
|
||||
|
||||
if (isTopBase) {
|
||||
top = triggerRect.top - contentRect.height;
|
||||
} else if (isBottomBase) {
|
||||
top = triggerRect.top + triggerRect.height;
|
||||
} else if (isLeftBase) {
|
||||
left = triggerRect.left - contentRect.width;
|
||||
} else if (isRightBase) {
|
||||
left = triggerRect.left + triggerRect.width;
|
||||
} else {
|
||||
top = triggerRect.top - contentRect.height;
|
||||
}
|
||||
|
||||
const isStart = placement.includes('start');
|
||||
const isEnd = placement.includes('end');
|
||||
|
||||
let align;
|
||||
if (isStart) align = 'start';
|
||||
else if (isEnd) align = 'end';
|
||||
else align = 'center';
|
||||
|
||||
if (isTopBase || isBottomBase) {
|
||||
left = this.alignCrossAxis(triggerRect.left, triggerRect.width, contentRect.width, align);
|
||||
}
|
||||
|
||||
if (isLeftBase || isRightBase) {
|
||||
top = this.alignCrossAxis(triggerRect.top, triggerRect.height, contentRect.height, align);
|
||||
}
|
||||
|
||||
return { top, left };
|
||||
},
|
||||
|
||||
alignCrossAxis(start, triggerSize, contentSize, align) {
|
||||
if (align === 'start') return start;
|
||||
if (align === 'end') return start + triggerSize - contentSize;
|
||||
return start + triggerSize / 2 - contentSize / 2;
|
||||
},
|
||||
|
||||
calcPlacement(isFixed, placement, triggerRect, contentRect) {
|
||||
return new Promise((resolve) => {
|
||||
const owner = uni.createSelectorQuery().in(this);
|
||||
owner.select(`.${name}-wrapper--fixed`).boundingClientRect();
|
||||
owner.exec((b) => {
|
||||
const [triggerChildRect] = b;
|
||||
if (triggerChildRect && isFixed) {
|
||||
triggerRect = triggerChildRect;
|
||||
}
|
||||
|
||||
const {
|
||||
isHorizontal,
|
||||
isVertical,
|
||||
} = this.getToward(placement);
|
||||
const {
|
||||
width: contentWidth,
|
||||
height: contentHeight,
|
||||
} = contentRect;
|
||||
const {
|
||||
left: triggerLeft,
|
||||
top: triggerTop,
|
||||
right: triggerRight,
|
||||
bottom: triggerBottom,
|
||||
} = triggerRect;
|
||||
let canPlace = true;
|
||||
const {
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
} = getWindowInfo();
|
||||
let finalPlacement = placement;
|
||||
if (isHorizontal) {
|
||||
if (placement.startsWith('top')) {
|
||||
canPlace = triggerTop - contentHeight >= 0;
|
||||
} else if (placement.startsWith('bottom')) {
|
||||
canPlace = triggerBottom + contentHeight <= windowHeight;
|
||||
}
|
||||
} else if (isVertical) {
|
||||
if (placement.startsWith('left')) {
|
||||
canPlace = triggerLeft - contentWidth >= 0;
|
||||
} else if (placement.startsWith('right')) {
|
||||
canPlace = triggerRight + contentWidth <= windowWidth;
|
||||
}
|
||||
}
|
||||
if (!canPlace) {
|
||||
// 反向
|
||||
if (isHorizontal) {
|
||||
finalPlacement = placement.startsWith('top')
|
||||
? placement.replace('top', 'bottom')
|
||||
: placement.replace('bottom', 'top');
|
||||
} else if (isVertical) {
|
||||
finalPlacement = placement.startsWith('left')
|
||||
? placement.replace('left', 'right')
|
||||
: placement.replace('right', 'left');
|
||||
}
|
||||
}
|
||||
const basePos = this.calcContentPosition(finalPlacement, triggerRect, contentRect);
|
||||
|
||||
resolve({
|
||||
placement: finalPlacement,
|
||||
...basePos,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
async computePosition() {
|
||||
const { placement } = this;
|
||||
const innerPlacement = placement
|
||||
.replace(/-(left|top)$/, '-start')
|
||||
.replace(/-(right|bottom)$/, '-end');
|
||||
// 此处必须要设置,否则计算的位置会出错
|
||||
this.innerPlacement = innerPlacement;
|
||||
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
|
||||
query.select(`#${name}-wrapper`).boundingClientRect();
|
||||
query.select(`#${name}-content`).boundingClientRect();
|
||||
query.selectViewport().scrollOffset();
|
||||
query.exec(async (res) => {
|
||||
const [triggerRect, contentRect, viewportOffset] = res;
|
||||
if (!triggerRect || !contentRect) return;
|
||||
const isFixed = this.fixed;
|
||||
// 最终放置位置
|
||||
const { placement: finalPlacement, ...basePos } = await this.calcPlacement(
|
||||
isFixed,
|
||||
innerPlacement,
|
||||
triggerRect,
|
||||
contentRect,
|
||||
);
|
||||
// TODO 优化:滚动时可能导致箭头闪烁
|
||||
this.innerPlacement = finalPlacement;
|
||||
|
||||
const {
|
||||
scrollTop = 0,
|
||||
scrollLeft = 0,
|
||||
} = viewportOffset || {};
|
||||
const top = isFixed ? basePos.top : basePos.top + scrollTop;
|
||||
const left = isFixed ? basePos.left : basePos.left + scrollLeft;
|
||||
const style = `top:${Math.max(top, 0)}px;left:${Math.max(left, 0)}px;`;
|
||||
const arrowStyle = this.calcArrowStyle(innerPlacement, triggerRect, contentRect);
|
||||
|
||||
this.contentStyle = style;
|
||||
this.arrowStyle = arrowStyle;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
@import './popover.css';
|
||||
</style>
|
||||
58
uni_modules/tdesign-uniapp/components/popover/props.ts
Normal file
58
uni_modules/tdesign-uniapp/components/popover/props.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
|
||||
* */
|
||||
|
||||
import type { TdPopoverProps } from './type';
|
||||
export default {
|
||||
/** 是否在点击外部元素后关闭菜单 */
|
||||
closeOnClickOutside: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/** 确认框内容 */
|
||||
content: {
|
||||
type: String,
|
||||
},
|
||||
/** 如果触发元素为 `fixed` 场景,需要显示指定 `fixed` 属性为 `true`,同时需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素 */
|
||||
fixed: Boolean,
|
||||
/** 浮层出现位置 */
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'top' as TdPopoverProps['placement'],
|
||||
validator(val: TdPopoverProps['placement']): boolean {
|
||||
if (!val) return true;
|
||||
return ['top', 'left', 'right', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'left-top', 'left-bottom', 'right-top', 'right-bottom'].includes(val);
|
||||
},
|
||||
},
|
||||
/** 是否显示浮层箭头 */
|
||||
showArrow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/** 弹出气泡主题 */
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'dark' as TdPopoverProps['theme'],
|
||||
validator(val: TdPopoverProps['theme']): boolean {
|
||||
if (!val) return true;
|
||||
return ['dark', 'light', 'brand', 'success', 'warning', 'error'].includes(val);
|
||||
},
|
||||
},
|
||||
/** 是否显示气泡确认框 */
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
/** 是否显示气泡确认框,非受控属性 */
|
||||
defaultVisible: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
/** 确认框显示或隐藏时触发 */
|
||||
onVisibleChange: {
|
||||
type: Function,
|
||||
default: () => ({}),
|
||||
},
|
||||
};
|
||||
61
uni_modules/tdesign-uniapp/components/popover/type.ts
Normal file
61
uni_modules/tdesign-uniapp/components/popover/type.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
|
||||
* */
|
||||
|
||||
export interface TdPopoverProps {
|
||||
/**
|
||||
* 是否在点击外部元素后关闭菜单
|
||||
* @default true
|
||||
*/
|
||||
closeOnClickOutside?: boolean;
|
||||
/**
|
||||
* 确认框内容
|
||||
*/
|
||||
content?: string;
|
||||
/**
|
||||
* 如果触发元素为 `fixed` 场景,需要显示指定 `fixed` 属性为 `true`,同时需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素
|
||||
* @default false
|
||||
*/
|
||||
fixed?: boolean;
|
||||
/**
|
||||
* 浮层出现位置
|
||||
* @default top
|
||||
*/
|
||||
placement?:
|
||||
| 'top'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'bottom'
|
||||
| 'top-left'
|
||||
| 'top-right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-right'
|
||||
| 'left-top'
|
||||
| 'left-bottom'
|
||||
| 'right-top'
|
||||
| 'right-bottom';
|
||||
/**
|
||||
* 是否显示浮层箭头
|
||||
* @default true
|
||||
*/
|
||||
showArrow?: boolean;
|
||||
/**
|
||||
* 弹出气泡主题
|
||||
* @default dark
|
||||
*/
|
||||
theme?: 'dark' | 'light' | 'brand' | 'success' | 'warning' | 'error';
|
||||
/**
|
||||
* 是否显示气泡确认框
|
||||
*/
|
||||
visible?: boolean;
|
||||
/**
|
||||
* 是否显示气泡确认框,非受控属性
|
||||
*/
|
||||
defaultVisible?: boolean;
|
||||
/**
|
||||
* 确认框显示或隐藏时触发
|
||||
*/
|
||||
onVisibleChange?: (visible: boolean) => void;
|
||||
}
|
||||
Reference in New Issue
Block a user