feat(float-button): new component (#5627)

* feat: create template

* feat: add demo

* feat: change to css

* feat: add style

* feat: add demo

* feat: add demo

* feat: add group

* feat: add docs

* feat: add log

* fix: style error

* feat: add docs
This commit is contained in:
jahnli 2024-01-29 23:38:05 +08:00 committed by GitHub
parent 2bf5229543
commit da39759039
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1041 additions and 2 deletions

View File

@ -22,6 +22,7 @@
- `n-split` adds `size` prop and `on-update:size` prop.
- `n-split` adds `watch-props` prop, closes [#5526](https://github.com/tusen-ai/naive-ui/issues/5526).
- `n-drawer` adds `borderRadius` theme variable.
- adds `n-float-button` component.
### i18n

View File

@ -22,6 +22,7 @@
- `n-split` 新增 `size``on-update:size` 属性
- `n-split` 新增 `watch-props` 属性,关闭 [#5526](https://github.com/tusen-ai/naive-ui/issues/5526)
- `n-drawer` 新增 `borderRadius` 主题变量
- 新增 `n-float-button` 组件
### i18n

View File

@ -551,6 +551,11 @@ export const enComponentRoutes = [
{
path: 'split',
component: () => import('../../src/split/demos/enUS/index.demo-entry.md')
},
{
path: 'float-button',
component: () =>
import('../../src/float-button/demos/enUS/index.demo-entry.md')
}
]
@ -945,6 +950,11 @@ export const zhComponentRoutes = [
{
path: 'split',
component: () => import('../../src/split/demos/zhCN/index.demo-entry.md')
},
{
path: 'float-button',
component: () =>
import('../../src/float-button/demos/zhCN/index.demo-entry.md')
}
]

View File

@ -289,6 +289,13 @@ export function createComponentMenuOptions ({ lang, theme, mode }) {
zh: '水印',
enSuffix: true,
path: '/watermark'
},
{
en: 'Float Button',
zh: '浮动按钮',
enSuffix: true,
path: '/float-button',
isNew: true
}
]
}),

View File

@ -35,6 +35,8 @@ export * from './ellipsis'
export * from './empty'
export * from './flex'
export * from './form'
export * from './float-button'
export * from './float-button-group'
export * from './global-style'
export * from './gradient-text'
export * from './grid'

View File

@ -31,6 +31,7 @@ import type { EllipsisTheme } from '../../ellipsis/styles'
import type { EmptyTheme } from '../../empty/styles'
import type { EquationTheme } from '../../equation/styles'
import type { FormTheme } from '../../form/styles'
import type { FloatButtonTheme } from '../../float-button/styles'
import type { GradientTextTheme } from '../../gradient-text/styles'
import type { IconTheme } from '../../icon/styles'
import type { IconWrapperTheme } from '../../icon-wrapper/styles'
@ -100,6 +101,7 @@ import type { RowTheme } from '../../legacy-grid/styles'
import type { Katex } from './katex'
import type { SplitTheme } from '../../split/styles'
import type { FlexTheme } from '../../flex/styles'
import type { FloatButtonGroupTheme } from '../../float-button-group/styles'
export interface GlobalThemeWithoutCommon {
Alert?: AlertTheme
@ -136,6 +138,8 @@ export interface GlobalThemeWithoutCommon {
Equation?: EquationTheme
Flex?: FlexTheme
Form?: FormTheme
FloatButton?: FloatButtonTheme
FloatButtonGroup?: FloatButtonGroupTheme
GradientText?: GradientTextTheme
Icon?: IconTheme
IconWrapper?: IconWrapperTheme

View File

@ -0,0 +1,5 @@
export {
default as NFloatButtonGroup,
floatButtonGroupProps
} from './src/FloatButtonGroup'
export type { FloatButtonGroupProps } from './src/FloatButtonGroup'

View File

@ -0,0 +1,124 @@
import {
h,
type PropType,
defineComponent,
computed,
type CSSProperties
} from 'vue'
import type { Size } from '../../button/src/interface'
import { type ThemeProps, useConfig, useTheme } from '../../_mixins'
import type { ExtractPublicPropTypes } from '../../_utils'
import style from './styles/index.cssr'
import floatButtonGroupLight, {
type FloatButtonGroupTheme
} from '../styles/light'
export interface ButtonGroupInjection {
size?: Size | undefined
}
export const floatButtonGroupProps = {
...(useTheme.props as ThemeProps<FloatButtonGroupTheme>),
width: {
type: [Number, String] as PropType<string | number>,
default: 'auto'
},
height: {
type: [Number, String] as PropType<string | number>,
default: 'auto'
},
left: {
type: [Number, String] as PropType<string | number>,
default: undefined
},
right: {
type: [Number, String] as PropType<string | number>,
default: 40
},
top: {
type: [Number, String] as PropType<string | number>,
default: undefined
},
bottom: {
type: [Number, String] as PropType<string | number>,
default: 40
},
radius: {
type: [Number, String] as PropType<string | number>,
default: 22
},
backgroundColor: String,
vertical: Boolean
} as const
export type FloatButtonGroupProps = ExtractPublicPropTypes<
typeof floatButtonGroupProps
>
export default defineComponent({
name: 'FloatButtonGroup',
props: floatButtonGroupProps,
setup (props) {
const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props)
const themeRef = useTheme(
'FloatButtonGroup',
'-float-button-group',
style,
floatButtonGroupLight,
props,
mergedClsPrefixRef
)
const cssVarsRef = computed(() => {
const {
self: { color, textColor, boxShadow, boxShadowHover, boxShadowPressed },
common: { cubicBezierEaseInOut }
} = themeRef.value
return {
'--n-bezier': cubicBezierEaseInOut,
'--n-box-shadow': boxShadow,
'--n-box-shadow-hover': boxShadowHover,
'--n-box-shadow-pressed': boxShadowPressed,
'--n-color': color,
'--n-text-color': textColor,
left: formatNumber(props.left),
right: formatNumber(props.right),
top: formatNumber(props.top),
bottom: formatNumber(props.bottom),
width: formatNumber(props.width),
height: formatNumber(props.height),
borderRadius: formatNumber(props.radius),
backgroundColor: props.backgroundColor
}
})
const formatNumber = (
value: number | string | undefined
): string | undefined => {
if (typeof value === 'number') return `${value}px`
return value
}
return {
cssVars: inlineThemeDisabled ? undefined : cssVarsRef,
mergedClsPrefix: mergedClsPrefixRef,
formatNumber
}
},
render () {
const { mergedClsPrefix, cssVars } = this
return (
<div
class={[
`${mergedClsPrefix}-float-button-group`,
this.vertical && `${mergedClsPrefix}-float-button-group--vertical`
]}
style={cssVars as CSSProperties}
role="group"
>
{this.$slots}
</div>
)
}
})

View File

@ -0,0 +1,24 @@
import { cB, cM, cNotM } from '../../../_utils/cssr/index'
export default cB('float-button-group', `
position: fixed;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--n-text-color);
transition:
color .3s var(--n-bezier),
box-shadow .3s var(--n-bezier),
background-color .3s var(--n-bezier);
`, [
cNotM('vertical', {
flexDirection: 'row'
}, [
]),
cM('vertical', {
flexDirection: 'column'
}, [
])
])

View File

@ -0,0 +1,19 @@
import { commonDark } from '../../_styles/common'
import type { FloatButtonGroupTheme } from './light'
const floatButtonGroupDark: FloatButtonGroupTheme = {
name: 'FloatButtonGroup',
common: commonDark,
self (vars) {
const { popoverColor, textColor2 } = vars
return {
color: popoverColor,
textColor: textColor2,
boxShadow: '0 2px 8px 0px rgba(0, 0, 0, .12)',
boxShadowHover: '0 2px 12px 0px rgba(0, 0, 0, .18)',
boxShadowPressed: '0 2px 12px 0px rgba(0, 0, 0, .18)'
}
}
}
export default floatButtonGroupDark

View File

@ -0,0 +1,3 @@
export { default as floatButtonGroupDark } from './dark'
export { default as floatButtonGroupLight } from './light'
export type { FloatButtonGroupTheme } from './light'

View File

@ -0,0 +1,25 @@
import { type Theme } from '../../_mixins'
import { type ThemeCommonVars } from '../../config-provider'
import { commonLight } from '../../styles'
const self = (vars: ThemeCommonVars) => {
const { popoverColor, textColor2 } = vars
return {
color: popoverColor,
textColor: textColor2,
boxShadow: '0 2px 8px 0px rgba(0, 0, 0, .12)',
boxShadowHover: '0 2px 12px 0px rgba(0, 0, 0, .18)',
boxShadowPressed: '0 2px 12px 0px rgba(0, 0, 0, .18)'
}
}
export type FloatButtonGroupThemeVars = ReturnType<typeof self>
const themeLight: Theme<'FloatButtonGroup', FloatButtonGroupThemeVars> = {
name: 'FloatButtonGroup',
common: commonLight,
self
}
export default themeLight
export type FloatButtonGroupTheme = typeof themeLight

View File

@ -0,0 +1,64 @@
<markdown>
# Badge
You can put a hover button with the number of badges on any element.
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button :right="0" :bottom="0">
<n-badge :value="9" :offset="[6, -8]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="70" :bottom="0">
<n-badge :value="100" :max="99" :offset="[6, -8]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="142" :bottom="0">
<n-badge dot :offset="[4, -4]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="142" :bottom="0">
<n-badge dot :offset="[4, -4]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="0" :bottom="60">
<n-badge value="New" :offset="[10, -10]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="70" :bottom="60">
<n-badge :value="9" :offset="[6, -8]" color="grey">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
</div>
</template>
<script lang="ts">
import { AlarmOutline as AlarmOutlineIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
AlarmOutlineIcon
}
})
</script>

View File

@ -0,0 +1,39 @@
<markdown>
# Basic
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button :right="0" :bottom="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
<n-float-button :left="0" :bottom="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
<n-float-button :right="0" :top="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
<n-float-button :left="0" :top="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
</div>
</template>
<script lang="ts">
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
</script>

View File

@ -0,0 +1,49 @@
<markdown>
# Custom Dom
You can put any element you want.
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button
:right="0"
:bottom="60"
height="auto"
width="auto"
:radius="10"
>
<n-flex
vertical
:style="{ padding: '4px', textAlign: 'center' }"
align="center"
>
<n-icon :size="24">
<document-icon />
</n-icon>
<span> Document </span>
</n-flex>
</n-float-button>
<n-float-button :right="0" :left="0" :bottom="0" width="100%" :radius="10">
<n-flex align="center">
<n-icon :size="24">
<document-icon />
</n-icon>
<span> Document </span>
</n-flex>
</n-float-button>
</div>
</template>
<script lang="ts">
import { Document as DocumentIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
DocumentIcon
}
})
</script>

View File

@ -0,0 +1,81 @@
<markdown>
# Float Button Group
Elements can be arbitrarily combined to achieve more functionality.
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button-group
:left="0"
right="auto"
:bottom="0"
vertical
background-color="lightgrey"
>
<n-button
tertiary
circle
type="primary"
:style="{ marginBottom: '20px' }"
>
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle type="info" :style="{ marginBottom: '20px' }">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button
tertiary
circle
type="success"
:style="{ marginBottom: '20px' }"
>
<n-icon><cash-icon /></n-icon>
</n-button>
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
</n-float-button-group>
<n-float-button-group :right="0" :bottom="0">
<n-flex vertical>
<n-button tertiary circle type="primary">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle type="info">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle type="success">
<n-icon><cash-icon /></n-icon>
</n-button>
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
</n-flex>
</n-float-button-group>
</div>
</template>
<script lang="ts">
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
</script>

View File

@ -0,0 +1,44 @@
# Float Button
Like `Back Top`, more appearance level, more interaction.
Available since `NEXT_VERSION`.
## Demos
```demo
basic.vue
badge.vue
tooltip.vue
custom.vue
group.vue
```
## API
### Float Button Props
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| width | `number \| string` | `40` | The width. |
| height | `number \| string` | `40` | The height. |
| left | `number \| string` | `40` | The width from the left side of the page. |
| right | `number \| string` | `40` | The width from the right side of the page. |
| top | `number \| string` | `40` | The height from the top of the page. |
| bottom | `number \| string` | `40` | The height from the bottom of the page. |
| background-color | `string` | `#ffffff` | Background color. |
| radius | `number \| string` | `22` | The radius. |
### Float Button Group Props
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| width | `number \| string` | `40` | The width |
| height | `number \| string` | `40` | The height |
| left | `number \| string` | `40` | The width from the left side of the page |
| right | `number \| string` | `40` | The width from the right side of the page |
| top | `number \| string` | `40` | The height from the top of the page |
| bottom | `number \| string` | `40` | The height from the bottom of the page |
| radius | `number \| string` | `22` | The radius |
| background-color | `string` | `undefined` | Background color. |
| vertical | `boolean` | `undefined` | The direction of the child elements |

View File

@ -0,0 +1,29 @@
<markdown>
# Tooltip
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-tooltip trigger="hover" placement="left">
<template #trigger>
<n-float-button :right="0" :bottom="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
</template>
This story is not over.
</n-tooltip>
</div>
</template>
<script lang="ts">
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
</script>

View File

@ -0,0 +1,64 @@
<markdown>
# 徽章
带有徽章数的悬浮按钮
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button :right="0" :bottom="0">
<n-badge :value="9" :offset="[6, -8]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="70" :bottom="0">
<n-badge :value="100" :max="99" :offset="[6, -8]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="142" :bottom="0">
<n-badge dot :offset="[4, -4]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="142" :bottom="0">
<n-badge dot :offset="[4, -4]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="0" :bottom="60">
<n-badge value="新" :offset="[6, -6]">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
<n-float-button :right="70" :bottom="60">
<n-badge :value="9" :offset="[6, -8]" color="grey">
<n-icon :size="22" color="grey">
<alarm-outline-icon />
</n-icon>
</n-badge>
</n-float-button>
</div>
</template>
<script lang="ts">
import { AlarmOutline as AlarmOutlineIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
AlarmOutlineIcon
}
})
</script>

View File

@ -0,0 +1,39 @@
<markdown>
# 基础用法
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button :right="0" :bottom="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
<n-float-button :left="0" :bottom="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
<n-float-button :right="0" :top="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
<n-float-button :left="0" :top="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
</div>
</template>
<script lang="ts">
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
</script>

View File

@ -0,0 +1,49 @@
<markdown>
# 自定义元素
可以放任意元素
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button
:right="0"
:bottom="60"
height="auto"
width="auto"
:radius="10"
>
<n-flex
vertical
:style="{ padding: '4px', textAlign: 'center' }"
align="center"
>
<n-icon :size="24">
<document-icon />
</n-icon>
<span> 文档 </span>
</n-flex>
</n-float-button>
<n-float-button :right="0" :left="0" :bottom="0" width="100%" :radius="10">
<n-flex align="center">
<n-icon :size="24">
<document-icon />
</n-icon>
<span> 文档 </span>
</n-flex>
</n-float-button>
</div>
</template>
<script lang="ts">
import { Document as DocumentIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
DocumentIcon
}
})
</script>

View File

@ -0,0 +1,80 @@
<markdown>
# 浮动按钮组
可以任意组合元素实现更多的功能
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-float-button-group
:left="0"
right="auto"
:bottom="0"
vertical
background-color="lightgrey"
>
<n-button
tertiary
circle
type="primary"
:style="{ marginBottom: '20px' }"
>
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle type="info" :style="{ marginBottom: '20px' }">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button
tertiary
circle
type="success"
:style="{ marginBottom: '20px' }"
>
<n-icon><cash-icon /></n-icon>
</n-button>
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
</n-float-button-group>
<n-float-button-group :right="0" :bottom="0">
<n-flex vertical>
<n-button tertiary circle type="primary">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle type="info">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle type="success">
<n-icon><cash-icon /></n-icon>
</n-button>
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><cash-icon /></n-icon>
</template>
</n-button>
</n-flex>
</n-float-button-group>
</div>
</template>
<script lang="ts">
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
</script>

View File

@ -0,0 +1,44 @@
# 浮动按钮 Float Button
`Back Top` 很像,多一份颜值,多一份互动。
`NEXT_VERSION` 版本开始提供该组件。
## 演示
```demo
basic.vue
badge.vue
tooltip.vue
custom.vue
group.vue
```
## API
### Float Button Props
| 名称 | 类型 | 默认值 | 说明 |
| ---------------- | ------------------ | --------- | ------------------ |
| width | `number \| string` | `40` | 宽度 |
| height | `number \| string` | `40` | 高度 |
| left | `number \| string` | `40` | 距离页面左侧的宽度 |
| right | `number \| string` | `40` | 距离页面右侧的宽度 |
| top | `number \| string` | `40` | 距离页面顶部的高度 |
| bottom | `number \| string` | `40` | 距离页面底部的高度 |
| radius | `number \| string` | `22` | 圆角 |
| background-color | `string` | `#ffffff` | 背景色 |
### Float Button Group Props
| 名称 | 类型 | 默认值 | 说明 |
| ---------------- | ------------------ | ----------- | ------------------ |
| width | `number \| string` | `40` | 宽度 |
| height | `number \| string` | `40` | 高度 |
| left | `number \| string` | `40` | 距离页面左侧的宽度 |
| right | `number \| string` | `40` | 距离页面右侧的宽度 |
| top | `number \| string` | `40` | 距离页面顶部的高度 |
| bottom | `number \| string` | `40` | 距离页面底部的高度 |
| radius | `number \| string` | `22` | 圆角 |
| background-color | `string` | `undefined` | 背景色 |
| vertical | `boolean` | `undefined` | 子元素排列的方向 |

View File

@ -0,0 +1,29 @@
<markdown>
# 气泡提示
</markdown>
<template>
<div style="height: 200px; transform: translate(0)">
<n-tooltip trigger="hover" placement="left">
<template #trigger>
<n-float-button :right="0" :bottom="0">
<n-icon :size="24">
<cash-icon />
</n-icon>
</n-float-button>
</template>
这个故事还没有完结
</n-tooltip>
</div>
</template>
<script lang="ts">
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
</script>

View File

@ -0,0 +1,2 @@
export { default as NFloatButton, floatButtonProps } from './src/FloatButton'
export type { FloatButtonProps } from './src/FloatButton'

View File

@ -0,0 +1,116 @@
import {
h,
defineComponent,
type PropType,
computed,
type CSSProperties
} from 'vue'
import { type ExtractPublicPropTypes } from '../../_utils'
import useConfig from '../../_mixins/use-config'
import style from './styles/index.cssr'
import { type ThemeProps, useTheme } from '../../_mixins'
import { type FloatButtonTheme, floatButtonLight } from '../styles'
export const floatButtonProps = {
...(useTheme.props as ThemeProps<FloatButtonTheme>),
width: {
type: [Number, String] as PropType<string | number>,
default: 40
},
height: {
type: [Number, String] as PropType<string | number>,
default: 40
},
left: {
type: [Number, String] as PropType<string | number>,
default: undefined
},
right: {
type: [Number, String] as PropType<string | number>,
default: 40
},
top: {
type: [Number, String] as PropType<string | number>,
default: undefined
},
bottom: {
type: [Number, String] as PropType<string | number>,
default: 40
},
radius: {
type: [Number, String] as PropType<string | number>,
default: 22
},
backgroundColor: {
type: String,
default: '#ffffff'
}
} as const
export type FloatButtonProps = ExtractPublicPropTypes<typeof floatButtonProps>
export default defineComponent({
name: 'FloatButton',
props: floatButtonProps,
setup (props) {
const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props)
const themeRef = useTheme(
'FloatButton',
'-float-button',
style,
floatButtonLight,
props,
mergedClsPrefixRef
)
const cssVarsRef = computed(() => {
const {
self: { color, textColor, boxShadow, boxShadowHover, boxShadowPressed },
common: { cubicBezierEaseInOut }
} = themeRef.value
return {
'--n-bezier': cubicBezierEaseInOut,
'--n-box-shadow': boxShadow,
'--n-box-shadow-hover': boxShadowHover,
'--n-box-shadow-pressed': boxShadowPressed,
'--n-color': color,
'--n-text-color': textColor,
left: formatNumber(props.left),
right: formatNumber(props.right),
top: formatNumber(props.top),
bottom: formatNumber(props.bottom),
width: formatNumber(props.width),
height: formatNumber(props.height),
borderRadius: formatNumber(props.radius),
backgroundColor: props.backgroundColor
}
})
const formatNumber = (
value: number | string | undefined
): string | undefined => {
if (typeof value === 'number') return `${value}px`
return value
}
return {
cssVars: inlineThemeDisabled ? undefined : cssVarsRef,
mergedClsPrefix: mergedClsPrefixRef,
formatNumber
}
},
render () {
const { mergedClsPrefix, cssVars, $slots } = this
return (
<div
class={`${mergedClsPrefix}-float-button`}
style={cssVars as CSSProperties}
>
{$slots.default?.()}
</div>
)
}
})

View File

@ -0,0 +1,30 @@
import { c, cB } from '../../../_utils/cssr'
// vars:
// --n-bezier
// --n-box-shadow
// --n-box-shadow-hover
// --n-box-shadow-pressed
// --n-color
// --n-text-color
export default cB('float-button', `
position: fixed;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--n-text-color);
transition:
color .3s var(--n-bezier),
box-shadow .3s var(--n-bezier),
background-color .3s var(--n-bezier);
box-shadow: var(--n-box-shadow);
`, [
c('&:hover', {
boxShadow: 'var(--n-box-shadow-hover)'
}),
c('&:active', {
boxShadow: 'var(--n-box-shadow-pressed)'
})
])

View File

@ -0,0 +1,19 @@
import { commonDark } from '../../_styles/common'
import type { FloatButtonTheme } from './light'
const floatButtonDark: FloatButtonTheme = {
name: 'FloatButton',
common: commonDark,
self (vars) {
const { popoverColor, textColor2 } = vars
return {
color: popoverColor,
textColor: textColor2,
boxShadow: '0 2px 8px 0px rgba(0, 0, 0, .12)',
boxShadowHover: '0 2px 12px 0px rgba(0, 0, 0, .18)',
boxShadowPressed: '0 2px 12px 0px rgba(0, 0, 0, .18)'
}
}
}
export default floatButtonDark

View File

@ -0,0 +1,3 @@
export { default as floatButtonDark } from './dark'
export { default as floatButtonLight } from './light'
export type { FloatButtonTheme, FloatButtonThemeVars } from './light'

View File

@ -0,0 +1,25 @@
import { commonLight } from '../../_styles/common'
import type { ThemeCommonVars } from '../../_styles/common'
import { type Theme } from '../../_mixins'
const self = (vars: ThemeCommonVars) => {
const { popoverColor, textColor2 } = vars
return {
color: popoverColor,
textColor: textColor2,
boxShadow: '0 2px 8px 0px rgba(0, 0, 0, .12)',
boxShadowHover: '0 2px 12px 0px rgba(0, 0, 0, .18)',
boxShadowPressed: '0 2px 12px 0px rgba(0, 0, 0, .18)'
}
}
export type FloatButtonThemeVars = ReturnType<typeof self>
const themeLight: Theme<'FloatButton', FloatButtonThemeVars> = {
name: 'FloatButton',
common: commonLight,
self
}
export default themeLight
export type FloatButtonTheme = typeof themeLight

View File

@ -32,6 +32,7 @@ import { ellipsisDark } from '../ellipsis/styles'
import { emptyDark } from '../empty/styles'
import { equationDark } from '../equation/styles'
import { formDark } from '../form/styles'
import { floatButtonDark } from '../float-button/styles'
import { gradientTextDark } from '../gradient-text/styles'
import { iconDark } from '../icon/styles'
import { iconWrapperDark } from '../icon-wrapper/styles'
@ -84,6 +85,7 @@ import { watermarkDark } from '../watermark/styles'
import { splitDark } from '../split/styles'
import type { BuiltInGlobalTheme } from './interface'
import { flexDark } from '../styles'
import { floatButtonGroupDark } from '../float-button-group/styles'
export const darkTheme: BuiltInGlobalTheme = {
name: 'dark',
@ -171,5 +173,7 @@ export const darkTheme: BuiltInGlobalTheme = {
Typography: typographyDark,
Upload: uploadDark,
Watermark: watermarkDark,
Split: splitDark
Split: splitDark,
FloatButton: floatButtonDark,
FloatButtonGroup: floatButtonGroupDark
}

View File

@ -34,6 +34,7 @@ import { ellipsisLight } from '../ellipsis/styles'
import { emptyLight } from '../empty/styles'
import { equationLight } from '../equation/styles'
import { formLight } from '../form/styles'
import { floatButtonLight } from '../float-button/styles'
import { gradientTextLight } from '../gradient-text/styles'
import { iconLight } from '../icon/styles'
import { iconWrapperLight } from '../icon-wrapper/styles'
@ -86,6 +87,7 @@ import { watermarkLight } from '../watermark/styles'
import { splitLight } from '../split/styles'
import type { BuiltInGlobalTheme } from './interface'
import { flexLight } from '../flex/styles'
import { floatButtonGroupLight } from '../float-button-group/styles'
export const lightTheme: BuiltInGlobalTheme = {
name: 'light',
@ -173,5 +175,7 @@ export const lightTheme: BuiltInGlobalTheme = {
Typography: typographyLight,
Upload: uploadLight,
Watermark: watermarkLight,
Split: splitLight
Split: splitLight,
FloatButton: floatButtonLight,
FloatButtonGroup: floatButtonGroupLight
}