介绍
弹出式的气泡菜单。
引入
js
import Popover from 'sard-uniapp/components/popover/popover.vue'
import PopoverReference from 'sard-uniapp/components/popover-reference/popover-reference.vue'代码演示
基础使用
使用 options 属性设置弹出框菜单; 在 Popover 组件里面放置 PopoverReference 组件,以便控制弹出框的显示和位置计算。
vue
<template>
<sar-popover :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="ts">
import { toast, type MenuOption } from 'sard-uniapp'
const options = [
{
text: '选项1',
},
{
text: '选项2',
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text as string)
}
</script>vue
<template>
<sar-popover :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const options = [
{
text: "\u9009\u98791"
},
{
text: "\u9009\u98792"
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
</script>暗黑模式
设置 theme="dark" 会显示为暗黑模式。
vue
<template>
<sar-popover theme="dark" :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="ts">
import { toast, type MenuOption } from 'sard-uniapp'
const options = [
{
text: '选项1',
disabled: true,
},
{
text: '选项2',
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text)
}
</script>vue
<template>
<sar-popover theme="dark" :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const options = [
{
text: "\u9009\u98791",
disabled: true
},
{
text: "\u9009\u98792"
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
</script>展示图标
通过 options 属性的可以在菜单左边展示图标。
vue
<template>
<sar-popover theme="dark" :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="ts">
import { toast, type MenuOption } from 'sard-uniapp'
const options = [
{
text: '选项1',
icon: 'upc-scan',
iconFamily: 'demo-icons',
},
{
text: '选项2',
icon: 'camera',
iconFamily: 'demo-icons',
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text)
}
</script>vue
<template>
<sar-popover theme="dark" :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const options = [
{
text: "\u9009\u98791",
icon: "upc-scan",
iconFamily: "demo-icons"
},
{
text: "\u9009\u98792",
icon: "camera",
iconFamily: "demo-icons"
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
</script>禁用选项
禁用的选项无法点击。
vue
<template>
<sar-popover :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="ts">
import { toast, type MenuOption } from 'sard-uniapp'
const options = [
{
text: '选项1',
disabled: true,
},
{
text: '选项2',
disabled: true,
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text)
}
</script>vue
<template>
<sar-popover :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const options = [
{
text: "\u9009\u98791",
disabled: true
},
{
text: "\u9009\u98792",
disabled: true
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
</script>水平排列
配置 direction="horizontal" 可以水平排列菜单。
vue
<template>
<sar-popover direction="horizontal" :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="ts">
import { toast, type MenuOption } from 'sard-uniapp'
const options = [
{
text: '选项1',
icon: 'upc-scan',
iconFamily: 'demo-icons',
},
{
text: '选项2',
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text)
}
</script>vue
<template>
<sar-popover direction="horizontal" :options="options" @select="onSelect">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
</sar-popover>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const options = [
{
text: "\u9009\u98791",
icon: "upc-scan",
iconFamily: "demo-icons"
},
{
text: "\u9009\u98792"
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
</script>自定义内容
弹出框可以放置任何内容,而不仅仅是菜单。通过 v-model:visible 可以控制弹出框的隐藏。
vue
<template>
<sar-popover v-model:visible="visible">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
<template #content>
<sar-grid clickable root-style="width: 560rpx">
<sar-grid-item
v-for="n in 8"
:key="n"
:text="`选项${n}`"
icon="image"
@click="onSelect(n)"
/>
</sar-grid>
</template>
</sar-popover>
</template>
<script setup lang="ts">
import { toast } from 'sard-uniapp'
import { ref } from 'vue'
const visible = ref(false)
const onSelect = (n: number) => {
toast(`选项${n}`)
visible.value = false
}
</script>vue
<template>
<sar-popover v-model:visible="visible">
<sar-popover-reference>
<sar-button>显示气泡弹出框</sar-button>
</sar-popover-reference>
<template #content>
<sar-grid clickable root-style="width: 560rpx">
<sar-grid-item
v-for="n in 8"
:key="n"
:text="`选项${n}`"
icon="image"
@click="onSelect(n)"
/>
</sar-grid>
</template>
</sar-popover>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
import { ref } from "vue";
const visible = ref(false);
const onSelect = (n) => {
toast(`\u9009\u9879${n}`);
visible.value = false;
};
</script>弹出位置
气泡弹出框会尽量在视窗中匹配各个位置以便可以完整展示,默认底部居中展示。
vue
<template>
<view style="margin: 0 120rpx">
<sar-grid :columns="3" clickable>
<sar-grid-item @click="show('top-start')">TS</sar-grid-item>
<sar-grid-item @click="show('top')">top</sar-grid-item>
<sar-grid-item @click="show('top-end')">TE</sar-grid-item>
</sar-grid>
</view>
<view
style="display: flex; flex-direction: row; justify-content: space-between"
>
<sar-grid :columns="1" clickable root-style="width: 120rpx">
<sar-grid-item @click="show('left-start')">LS</sar-grid-item>
<sar-grid-item @click="show('left')">left</sar-grid-item>
<sar-grid-item @click="show('left-end')">LE</sar-grid-item>
</sar-grid>
<sar-popover
:options="options"
v-model:visible="visible"
:position="position"
@select="onSelect"
>
<sar-popover-reference
:root-style="{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '200rpx',
height: '200rpx',
margin: 'auto',
background: 'var(--sar-orange)',
}"
>
box
</sar-popover-reference>
</sar-popover>
<sar-grid :columns="1" clickable root-style="width: 120rpx">
<sar-grid-item @click="show('right-start')">RS</sar-grid-item>
<sar-grid-item @click="show('right')">right</sar-grid-item>
<sar-grid-item @click="show('right-end')">RE</sar-grid-item>
</sar-grid>
</view>
<view style="margin: 0 120rpx">
<sar-grid :columns="3" clickable>
<sar-grid-item @click="show('bottom-start')">BS</sar-grid-item>
<sar-grid-item @click="show('bottom')">bottom</sar-grid-item>
<sar-grid-item @click="show('bottom-end')">BE</sar-grid-item>
</sar-grid>
</view>
</template>
<script setup lang="ts">
import { toast, type MenuOption, type PopoverPosition } from 'sard-uniapp'
import { ref } from 'vue'
const position = ref<PopoverPosition>('bottom')
const visible = ref(false)
const options = [
{
text: '选项1',
},
{
text: '选项2',
},
{
text: '选项3',
},
]
const show = (pos: PopoverPosition) => {
position.value = pos
visible.value = true
}
const onSelect = (option: MenuOption) => {
toast(option.text)
}
</script>
<style lang="scss" scoped>
:deep() {
.sar-grid {
font-size: var(--sar-text-base);
}
}
</style>vue
<template>
<view style="margin: 0 120rpx">
<sar-grid :columns="3" clickable>
<sar-grid-item @click="show('top-start')">TS</sar-grid-item>
<sar-grid-item @click="show('top')">top</sar-grid-item>
<sar-grid-item @click="show('top-end')">TE</sar-grid-item>
</sar-grid>
</view>
<view
style="display: flex; flex-direction: row; justify-content: space-between"
>
<sar-grid :columns="1" clickable root-style="width: 120rpx">
<sar-grid-item @click="show('left-start')">LS</sar-grid-item>
<sar-grid-item @click="show('left')">left</sar-grid-item>
<sar-grid-item @click="show('left-end')">LE</sar-grid-item>
</sar-grid>
<sar-popover
:options="options"
v-model:visible="visible"
:position="position"
@select="onSelect"
>
<sar-popover-reference
:root-style="{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '200rpx',
height: '200rpx',
margin: 'auto',
background: 'var(--sar-orange)',
}"
>
box
</sar-popover-reference>
</sar-popover>
<sar-grid :columns="1" clickable root-style="width: 120rpx">
<sar-grid-item @click="show('right-start')">RS</sar-grid-item>
<sar-grid-item @click="show('right')">right</sar-grid-item>
<sar-grid-item @click="show('right-end')">RE</sar-grid-item>
</sar-grid>
</view>
<view style="margin: 0 120rpx">
<sar-grid :columns="3" clickable>
<sar-grid-item @click="show('bottom-start')">BS</sar-grid-item>
<sar-grid-item @click="show('bottom')">bottom</sar-grid-item>
<sar-grid-item @click="show('bottom-end')">BE</sar-grid-item>
</sar-grid>
</view>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
import { ref } from "vue";
const position = ref("bottom");
const visible = ref(false);
const options = [
{
text: "\u9009\u98791"
},
{
text: "\u9009\u98792"
},
{
text: "\u9009\u98793"
}
];
const show = (pos) => {
position.value = pos;
visible.value = true;
};
const onSelect = (option) => {
toast(option.text);
};
</script>
<style lang="scss" scoped>
:deep() {
.sar-grid {
font-size: var(--sar-text-base);
}
}
</style>自定义 reference
如果因 DOM 结构限制,无法在 Popover 组件里面放置 PopoverReference 组件, 可以使用 usePopover 钩子函数关联 Popover 组件和自定义的 reference 组件。
usePopover 钩子函数接收一个 reference 的选择器,并返回一个控制器对象, 将此对象绑定到 Popover 组件的 controller 属性,便可以使用控制器对象的 show 方法显示弹出框。
vue
<template>
<sar-popover
:options="options"
@select="onSelect"
:controller="popover"
position="right"
/>
<view @click="onClick" class="popover-box" id="refId">box</view>
</template>
<script setup lang="ts">
import { toast, type MenuOption, usePopover } from 'sard-uniapp'
const popover = usePopover('#refId')
const options = [
{
text: '选项1',
},
{
text: '选项2',
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text)
}
const onClick = () => {
popover.show()
}
</script>
<style lang="scss" scoped>
.popover-box {
display: flex;
justify-content: center;
align-items: center;
width: 200rpx;
height: 200rpx;
background-color: var(--sar-orange);
}
</style>vue
<template>
<sar-popover
:options="options"
@select="onSelect"
:controller="popover"
position="right"
/>
<view @click="onClick" class="popover-box" id="refId">box</view>
</template>
<script setup lang="js">
import { toast, usePopover } from "sard-uniapp";
const popover = usePopover("#refId");
const options = [
{
text: "\u9009\u98791"
},
{
text: "\u9009\u98792"
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
const onClick = () => {
popover.show();
};
</script>
<style lang="scss" scoped>
.popover-box {
display: flex;
justify-content: center;
align-items: center;
width: 200rpx;
height: 200rpx;
background-color: var(--sar-orange);
}
</style>手动定义位置
借助 usePopover 钩子函数,你甚至不需要 reference 组件来提供位置, 控制器的 show 方法可以接收返回 NodeRect 或者 Promise<NodeRect> 的函数, 可以让弹出框显示在屏幕任意位置。
vue
<template>
<sar-popover
:options="options"
@select="onSelect"
:position="position"
:controller="popover"
/>
<sar-list card>
<sar-list-item arrow hover title="屏幕中下" @click="onBottomCenter" />
<sar-list-item arrow hover title="屏幕右下" @click="onBottomEnd" />
<sar-list-item arrow hover title="屏幕中上" @click="onTopCenter" />
</sar-list>
</template>
<script setup lang="ts">
import {
getWindowInfo,
toast,
usePopover,
type MenuOption,
type PopoverPosition,
} from 'sard-uniapp'
import { ref } from 'vue'
const position = ref<PopoverPosition>('bottom')
const popover = usePopover()
const onBottomCenter = () => {
position.value = 'bottom'
popover.show(() => {
const { windowWidth, windowHeight } = getWindowInfo()
return {
width: windowWidth,
height: 10,
left: 0,
right: windowWidth,
top: windowHeight - 10,
bottom: windowHeight,
}
})
}
const onBottomEnd = () => {
position.value = 'bottom-end'
popover.show(() => {
const { windowWidth, windowHeight } = getWindowInfo()
return {
width: 10,
height: 10,
left: windowWidth - 10,
right: windowWidth,
top: windowHeight - 10,
bottom: windowHeight,
}
})
}
const onTopCenter = () => {
position.value = 'top'
popover.show(() => {
const { windowWidth } = getWindowInfo()
return {
width: windowWidth,
height: 10,
left: 0,
right: windowWidth,
top: 0,
bottom: 10,
}
})
}
const options = [
{
text: '选项1',
},
{
text: '选项2',
},
{
text: '选项3',
},
]
const onSelect = (option: MenuOption) => {
toast(option.text)
}
</script>vue
<template>
<sar-popover
:options="options"
@select="onSelect"
:position="position"
:controller="popover"
/>
<sar-list card>
<sar-list-item arrow hover title="屏幕中下" @click="onBottomCenter" />
<sar-list-item arrow hover title="屏幕右下" @click="onBottomEnd" />
<sar-list-item arrow hover title="屏幕中上" @click="onTopCenter" />
</sar-list>
</template>
<script setup lang="js">
import {
getWindowInfo,
toast,
usePopover
} from "sard-uniapp";
import { ref } from "vue";
const position = ref("bottom");
const popover = usePopover();
const onBottomCenter = () => {
position.value = "bottom";
popover.show(() => {
const { windowWidth, windowHeight } = getWindowInfo();
return {
width: windowWidth,
height: 10,
left: 0,
right: windowWidth,
top: windowHeight - 10,
bottom: windowHeight
};
});
};
const onBottomEnd = () => {
position.value = "bottom-end";
popover.show(() => {
const { windowWidth, windowHeight } = getWindowInfo();
return {
width: 10,
height: 10,
left: windowWidth - 10,
right: windowWidth,
top: windowHeight - 10,
bottom: windowHeight
};
});
};
const onTopCenter = () => {
position.value = "top";
popover.show(() => {
const { windowWidth } = getWindowInfo();
return {
width: windowWidth,
height: 10,
left: 0,
right: windowWidth,
top: 0,
bottom: 10
};
});
};
const options = [
{
text: "\u9009\u98791"
},
{
text: "\u9009\u98792"
},
{
text: "\u9009\u98793"
}
];
const onSelect = (option) => {
toast(option.text);
};
</script>API
PopoverProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| root-class | 组件根元素类名 | string | - |
| root-style | 组件根元素样式 | StyleValue | - |
| visible | 是否显示气泡弹出框 | boolean | - |
| options | 菜单选项列表 | MenuOption[] | [] |
| position | 弹出位置 | PopoverPosition | 'bottom' |
| direction | 菜单选项排列方向 | 'vertical' | 'horizontal' | 'vertical' |
| theme | 主题风格 | 'dark' | 'light' | 'light' |
| refGap | 气泡弹出框与reference元素的间距,单位 px | number | 10 |
| viewport-gap | 气泡弹出框距与视窗的间距,单位 px | number | 10 |
| transparent | 遮罩是否透明 | boolean | true |
| duration | 显隐动画时长,单位 ms | number | 150 |
PopoverSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 放置 PopoverReference 组件 | - |
| content | 自定义弹出框内容 | - |
PopoverEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| update:visible | 弹出框显隐时触发 | (visible: boolean) => void |
| select | 选择菜单项时触发 | (option: MenuOption) => void |
MenuOption
| 属性 | 描述 | 类型 |
|---|---|---|
| text | 菜单项文本 | string |
| disabled | 是否禁用菜单项 | boolean |
| icon | 图标名称 | string |
| iconFamily | 图标字体 | string |
PopoverPosition
tsx
type PopoverPosition =
| 'top'
| 'top-start'
| 'top-end'
| 'right'
| 'right-start'
| 'right-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'PopoverReferenceProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| root-class | 组件根元素类名 | string | - |
| root-style | 组件根元素样式 | StyleValue | - |
PopoverReferenceSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义默认内容 | - |
PopoverReferenceEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| click | 点击组件时触发 | (event: any) => void |
usePopover
ts
function usePopover(selector?: string): PopoverControllerPopoverController
ts
interface PopoverController {
show: (getRect?: () => NodeRect | Promise<NodeRect>) => void
}NodeRect
ts
interface NodeRect {
top: number
right: number
bottom: number
left: number
height: number
width: number
}主题定制
CSS 变量
scss
page,
.sar-portal {
--sar-popover-duration: var(--sar-duration-fast);
--sar-popover-bg: var(--sar-emphasis-bg);
--sar-popover-box-shadow: var(--sar-shadow-xl);
--sar-popover-min-width: 160rpx;
--sar-popover-border-radius: var(--sar-rounded);
--sar-popover-dark-bg: var(--sar-gray-800);
--sar-popover-dark-color: var(--sar-white);
--sar-popover-arrow-size: 20rpx;
}