跳到主要内容

FloatButtonGroup 悬浮按钮组

悬浮在页面侧边的一列按钮集合,提供了"button""elevator""link""backTop"四种按钮类型

代码演示

基本使用

右侧悬浮按钮可操作此滚动盒子
欢迎点击回到顶部按钮上的icon进入我们的github
手机
电脑
收起源代码
import React from 'react'
import FloatButtonGroup from '../../FloatButtonGroup'
import { configItemType } from '../../FloatButtonGroup'
import { IconType } from '../../Icon'
import Icon from '../../Icon'

const TBconfigs: configItemType[] = [
{
description: '淘宝',
type: 'link',
action: {
href: 'www.taobao.com',
target: '_blank',
},
},
{
description: '按钮',
type: 'button',
action: {
onClick: () => {
alert('淘宝风格')
},
},
},
{
description: '电脑',
type: 'elevator',
action: {
top: 1300,
},
},
{
description: (
<>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<Icon
type={IconType.LOADING}
style={{ fontSize: 16, color: 'black' }}
href={'https://github.com/dancing-team/dance-ui'}
/>
<div>回到顶部</div>
</div>
</>
),
type: 'backTop',
action: {
visibleheight: 100,
},
},
]
const JDconfigs: configItemType[] = [
{
description: '京东',
type: 'link',
action: {
href: 'www.jd.com',
target: '_blank',
},
},
{
description: '按钮',
type: 'button',
action: {
onClick: () => {
alert('京东风格')
},
},
},
{
description: '手机',
type: 'elevator',
action: {
top: 1000,
},
},
{
description: (
<>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<Icon
type={IconType.LOADING}
style={{ fontSize: 16, color: 'black' }}
href={'https://github.com/dancing-team/dance-ui'}
/>
<div>回到顶部</div>
</div>
</>
),
type: 'backTop',
action: {
visibleheight: 300,
},
},
]
export default () => (
<div
style={{
overflowY: 'auto',
height: '500px',
position: 'relative',
}}>
<div
style={{
height: '2000px',
width: '300px',
background: 'green',
}}>
<div
style={{
height: '1000px',
}}>
右侧悬浮按钮可操作此滚动盒子
<div
style={{
color: '#fff',
}}>
欢迎点击回到顶部按钮上的icon进入我们的github
</div>
</div>
<div style={{ height: '300px' }}>手机</div>
<div>电脑</div>
<FloatButtonGroup configs={TBconfigs} right="90px" top="400px" platformStyle="TaoBao"></FloatButtonGroup>
<FloatButtonGroup configs={JDconfigs} right="165px" top="400px" platformStyle="JingDong"></FloatButtonGroup>
</div>
</div>
)

API

FloatButtonGroup 组件 props 属性

参数说明类型默认值可选择性
options结构对象数组optionItemType[]-必须
shadow是否添加阴影样式booleantrue可选
left位置相关:组件默认开启fixed定位,同定位属性leftnumber-可选
right位置相关:组件默认开启fixed定位,同定位属性rihgtnumber-可选
top位置相关:组件默认开启fixed定位,同定位属性topnumber-可选
bottom位置相关:组件默认开启fixed定位,同定位属性bottomnumber-可选
platformStyle提供现有知名电商平台样式"default" | "JingDong" | "TaoBao"default可选

optionItemType 对象配置属性

参数说明类型默认值可选择性
description按钮描述string"默认"可选
type按钮类型"link" | "elevator" | "button" | "backTop"-必须
action按钮行为配置对象linkActionOption | elevatorActionOption | buttonActionOption | backTopActionOption(与type属性的取值对应)-必须

type 值为"link"时对应的 action 属性

参数说明类型默认值可选择性
href类似于 a 标签的href属性string""可选
target类似于 a 标签的target属性'_blank' | '_self' | '_parent' | '_top' | 'framename''_blank'可选

type 值为"elevator"时对应的 action 属性

参数说明类型默认值可选择性
top点击后滚动至元素scrollTop的值为top的位置(滚动元素为离组件最近的滚动的父元素)number0可选

type 值为"button"时对应的 action 属性

参数说明类型默认值可选择性
onClick点击按钮后触发的回调函数(e: event) => void() => {}可选

type 值为"backTop"时对应的 action 属性

参数说明类型默认值可选择性
visbleheight滚动高度达到此参数值才出现 backTopnumber0可选

组件源码

组件源码
// @ts-nocheck
import classNames from 'classnames'
import React, { Fragment, ReactNode, useEffect, useRef, useState } from 'react'
import type { backTopActionConfig, buttonActionConfig, configItemType, elevatorActionConfig, linkActionConfig } from './Type'
import { judgeBackTopType, judgeLinkType } from './Type'
import { commonStyle, platformStyles } from './style'
import { findScrollDom } from './utils'
export type FloatButtonGroupPropsType = {
// ---- 结构相关 ----
/** 结构对象数组,具体配置详见README */
configs: configItemType[]

// ---- 样式相关 ----
/** 是否添加阴影效果 */
shadow?: boolean
/** 是否使用现有电商平台样式 */
platformStyle?: 'default' | 'JingDong' | 'TaoBao'

// ---- 定位相关 ----
/** 定位属性 */
left?: string
top?: string
bottom?: string
right?: string
}
// TODO: Fix ts
const FloatButtonGroup = (props: React.PropsWithChildren<FloatButtonGroupPropsType>): JSX.Element => {
const { configs, shadow = true, platformStyle = 'default', ...restProps } = props

// 预处理configs数组中的config对象:如给某些缺省值添加默认值
configs.map((config) => {
if (!('description' in config)) {
config.description = '默认'
}
// 根据type处理action
const type = config.type
const action = config.action
if (judgeLinkType(config, action)) {
if (action.hasOwnProperty('href')) {
if (action.href!.indexOf('http') !== 0) {
action.href = 'http://' + action.href
}
}
if (!action.hasOwnProperty('target')) {
action.target = '_blank'
}
} else if (judgeBackTopType(config, action)) {
if (!action.hasOwnProperty('visibleHeight')) {
action.visibleheight = 0
}
}
})

return (
<div>
<ul
className={classNames(
commonStyle.ulStyle,
shadow ? commonStyle.shadowStyle : null,
platformStyles[`${platformStyle}Ul`],
)}
style={restProps}>
{configs.map((config, index) => (
<Fragment key={index}>
{(() => {
switch (config.type) {
case 'link':
return linkContent(config.action, config.description, platformStyle)
case 'elevator':
return elevatorContent(config.action, config.description, platformStyle)
case 'button':
return buttonContent(config.action, config.description, platformStyle)
case 'backTop':
return backTopContent(config.action, config.description, platformStyle)
}
})()}
</Fragment>
))}
</ul>
</div>
)
}

function linkContent(action: linkActionConfig, description: ReactNode, platformStyle: 'default' | 'JingDong' | 'TaoBao') {
return (
<li className={classNames(commonStyle.liStyle, platformStyles[`${platformStyle}Li`])}>
<a className={classNames(commonStyle.aStyle, platformStyles[`${platformStyle}A`])} {...action}>
{description}
</a>
</li>
)
}

function elevatorContent(
action: elevatorActionConfig,
description: ReactNode,
platformStyle: 'default' | 'JingDong' | 'TaoBao',
) {
// 寻找按钮控制的滚动元素——最近的有滚动条的父级元素(类型问题)
const [scrollDom, setScrollDom] = useState<any>(null)
const refToFindScroll = useRef<any>(null)
useEffect(() => {
setScrollDom(findScrollDom(refToFindScroll.current))
})

return (
<li className={classNames(commonStyle.liStyle, platformStyles[`${platformStyle}Li`])}>
<a
ref={refToFindScroll}
className={classNames(commonStyle.aStyle, platformStyles[`${platformStyle}A`])}
onClick={() => {
// 滚动元素为body时需要用window对象调用scrollTo方法
if (scrollDom === document.body) {
window.scrollTo({
top: action.top || 0,
behavior: 'smooth',
})
} else {
scrollDom.scrollTo({
top: action.top || 0,
behavior: 'smooth',
})
}
}}>
{description}
</a>
</li>
)
}

function buttonContent(action: buttonActionConfig, description: ReactNode, platformStyle: 'default' | 'JingDong' | 'TaoBao') {
return (
<li className={classNames(commonStyle.liStyle, platformStyles[`${platformStyle}Li`])}>
<a className={classNames(commonStyle.aStyle, platformStyles[`${platformStyle}A`])} onClick={action.onClick}>
{description}
</a>
</li>
)
}

function backTopContent(action: backTopActionConfig, description: ReactNode, platformStyle: 'default' | 'JingDong' | 'TaoBao') {
// 寻找按钮控制的滚动元素——最近的有滚动条的父级元素(类型问题)
const [scrollDom, setScrollDom] = useState<any>(null)
const refToFindScroll = useRef<any>(null)
useEffect(() => {
setScrollDom(findScrollDom(refToFindScroll.current))
})

// backTop可视控制相关逻辑:
const [throttleTool, setThrottleTool] = useState<boolean>(false)
const [visible, setvisible] = useState<boolean>(action.visibleheight === 0)
const handleScroll = () => {
if (throttleTool) {
return
}
setThrottleTool(false)
setTimeout(() => {
const scrollTop = scrollDom === document.body ? document.documentElement.scrollTop : scrollDom.scrollTop
action.visibleheight && setvisible(scrollTop >= action.visibleheight)
})
}
useEffect(() => {
// 如果滚动元素为body,监听scroll事件需要用window对象调用addEventListener
if (scrollDom === document.body) {
window.addEventListener('scroll', handleScroll)
} else {
scrollDom?.addEventListener('scroll', handleScroll)
}
return () => {
if (scrollDom === document.body) {
window.removeEventListener('scroll', handleScroll)
} else {
scrollDom?.removeEventListener('scroll', handleScroll)
}
}
}, [scrollDom])

return (
<li
style={{ display: visible ? 'block' : 'none' }}
className={classNames(commonStyle.liStyle, platformStyles[`${platformStyle}Li`])}>
<a
ref={refToFindScroll}
className={classNames(commonStyle.aStyle, platformStyles[`${platformStyle}A`])}
onClick={() => {
if (scrollDom === document.body) {
window.scrollTo({
top: 0,
behavior: 'smooth',
})
} else {
scrollDom.scrollTo({
top: 0,
behavior: 'smooth',
})
}
}}>
{description}
</a>
</li>
)
}
FloatButtonGroup.defaultProps = {
shadow: true,
}

export default FloatButtonGroup