FloatButtonGroup 悬浮按钮组
悬浮在页面侧边的一列按钮集合,提供了"button"、"elevator"、"link"、"backTop"四种按钮类型
代码演示
基本使用
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 | 是否添加阴影样式 | boolean | true | 可选 |
left | 位置相关:组件默认开启fixed 定位,同定位属性left | number | - | 可选 |
right | 位置相关:组件默认开启fixed 定位,同定位属性rihgt | number | - | 可选 |
top | 位置相关:组件默认开启fixed 定位,同定位属性top | number | - | 可选 |
bottom | 位置相关:组件默认开启fixed 定位,同定位属性bottom | number | - | 可选 |
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 的位置(滚动元素为离组件最近的滚动的父元素) | number | 0 | 可选 |
type 值为"button"时对应的 action 属性
参数 | 说明 | 类型 | 默认值 | 可选择性 |
---|---|---|---|---|
onClick | 点击按钮后触发的回调函数 | (e: event) => void | () => {} | 可选 |
type 值为"backTop"时对应的 action 属性
参数 | 说明 | 类型 | 默认值 | 可选择性 |
---|---|---|---|---|
visbleheight | 滚动高度达到此参数值才出现 backTop | number | 0 | 可选 |
组件源码
组件源码
// @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