【【面料标签】面料打标签分类及排序优化;】 https://www.tapd.cn/53459131/prong/stories/view/1153459131001000920
161 lines
4.9 KiB
TypeScript
161 lines
4.9 KiB
TypeScript
/**
|
|
* 注意:需要在父节点设置 position: relative;
|
|
*/
|
|
|
|
import { Text, View } from '@tarojs/components'
|
|
import Taro from '@tarojs/taro'
|
|
import type { Ref } from 'react'
|
|
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react'
|
|
import classNames from 'classnames'
|
|
import Iconfont from '../iconfont/iconfont'
|
|
import Popup from '../popup'
|
|
import Cell from '../cell'
|
|
import styles from './index.module.scss'
|
|
// 弹窗选择向上弹窗还是向下弹窗
|
|
type Direction = 'up' | 'down'
|
|
// 配置 菜单可选项
|
|
export interface DropDownOptions {
|
|
text: string
|
|
value: number
|
|
}
|
|
|
|
interface DropDownEvent {
|
|
change?: (value: DropDownOptions['value']) => void // value 变化时触发
|
|
onCloseOverlay?: () => void
|
|
}
|
|
|
|
export interface DropDownItemRef {
|
|
show: boolean
|
|
closePopup: () => void
|
|
showPopup: () => void
|
|
}
|
|
|
|
interface PropsType extends DropDownEvent {
|
|
direction?: Direction
|
|
options?: DropDownOptions[]
|
|
title?: string
|
|
value?: number | string // 当前选中的值
|
|
children?: React.ReactNode
|
|
activeColor?: string
|
|
showOverlay?: boolean
|
|
customClassName?: string
|
|
customStyle?: React.CSSProperties
|
|
hasBottomBtn?: boolean // 底部有按钮不允许点击蒙层关闭
|
|
titleSlot?: React.ReactNode
|
|
}
|
|
const DropDownItem = (props: PropsType, ref: Ref<DropDownItemRef>) => {
|
|
const { children, titleSlot, direction = 'down', title = '', value, options = [], change, activeColor, showOverlay = true, hasBottomBtn = false, customClassName, customStyle, onCloseOverlay } = props
|
|
|
|
const [showPopup, setShowPopup] = useState(false)
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
show: showPopup,
|
|
// 暴露出方法给有需要的用
|
|
closePopup() {
|
|
setShowPopup(false)
|
|
},
|
|
showPopup() {
|
|
setShowPopup(true)
|
|
},
|
|
}), [showPopup])
|
|
|
|
const handleClickOption = (value: DropDownOptions['value']) => {
|
|
change?.(value)
|
|
}
|
|
|
|
const [text, setText] = useState(options?.[0]?.text || '')
|
|
|
|
const defaultOptions = useMemo(() => {
|
|
const currentValue = value
|
|
return options?.map(({ text, value }, key) => {
|
|
currentValue === value && setText(text)
|
|
return <Cell key={key} title={text} desc="" isLink onClick={() => handleClickOption(value)}></Cell>
|
|
})
|
|
}, [value])
|
|
|
|
const getIconName = () => {
|
|
if (direction === 'up') {
|
|
return showPopup ? 'icon-zhankai1' : 'icon-shouqi1'
|
|
}
|
|
// down
|
|
return showPopup ? 'icon-shouqi1' : 'icon-zhankai1'
|
|
}
|
|
|
|
const handleClickTitle = () => {
|
|
console.log('handleClickTitle', showPopup)
|
|
onCloseOverlay?.()
|
|
setShowPopup(prev => !prev)
|
|
}
|
|
|
|
const handleClosePopup = () => {
|
|
if (hasBottomBtn) { return }
|
|
setShowPopup(false)
|
|
onCloseOverlay?.()
|
|
}
|
|
|
|
const [overlayOffsetTop, setOverlayOffsetTop] = useState('unset')
|
|
// 获取遮罩层的样式
|
|
const getOverlayStyle = (): React.CSSProperties => {
|
|
return { position: !showOverlay ? 'fixed' : 'absolute', top: 0 }
|
|
}
|
|
|
|
// 获取整个页面的完整高度
|
|
const [scrollHeight, setScrollHeight] = useState(0)
|
|
|
|
useEffect(() => {
|
|
const query = Taro.createSelectorQuery()
|
|
query.select('#DropDownItem').boundingClientRect()
|
|
query.selectViewport().scrollOffset()
|
|
query.exec((res) => {
|
|
console.log('res==>', res)
|
|
setScrollHeight(res[1].scrollHeight)
|
|
if (direction === 'down') {
|
|
setOverlayOffsetTop(`${res[0].bottom}px`)
|
|
}
|
|
else {
|
|
setOverlayOffsetTop(`${res[0].top}px`)
|
|
}
|
|
})
|
|
}, [])
|
|
|
|
// 获取样式
|
|
const getCustomStyle: React.CSSProperties = useMemo(() => {
|
|
if (direction === 'up') {
|
|
return { position: 'absolute', top: 0, height: overlayOffsetTop }
|
|
}
|
|
else {
|
|
return { position: 'absolute', top: overlayOffsetTop, height: `calc(${`${scrollHeight}px`} - ${overlayOffsetTop})` }
|
|
}
|
|
}, [overlayOffsetTop])
|
|
|
|
return (
|
|
<View className={styles.dropDownItem} id="DropDownItem">
|
|
<View className={classNames(customClassName, styles['dropDownItem--title'])} style={customStyle} onClick={handleClickTitle}>
|
|
{
|
|
titleSlot || <>
|
|
|
|
<Text className={styles['dropDownItem--title--text']} style={showPopup ? { color: activeColor } : {}}>
|
|
{title || text}
|
|
</Text>
|
|
<Iconfont name={getIconName()} size={20} color={showPopup ? activeColor : '#7f7f7f'}></Iconfont>
|
|
</>
|
|
}
|
|
</View>
|
|
<Popup
|
|
onClose={handleClosePopup}
|
|
showOverLay={showOverlay}
|
|
show={showPopup}
|
|
showTitle={false}
|
|
safeAreaInsetBottom={false}
|
|
customStyle={getCustomStyle}
|
|
overlayStyle={getOverlayStyle()}
|
|
position={direction === 'down' ? 'top' : 'bottom'}
|
|
>
|
|
{/* showPopup 为true才显示children 是因为真机环境下 children内的字体有穿透的情况 */}
|
|
{showPopup && (children || <View className={styles.dropDownItemOptions}>{defaultOptions}</View>)}
|
|
</Popup>
|
|
</View>
|
|
)
|
|
}
|
|
export default forwardRef(DropDownItem)
|