提交 61490b03 编写于 作者: 宋宋

feat: elevator组件开发完成

上级 312638ec
...@@ -232,15 +232,15 @@ ...@@ -232,15 +232,15 @@
"show": true, "show": true,
"author": "swag~jun" "author": "swag~jun"
}, },
{ {
"version": "1.0.0", "version": "1.0.0",
"name": "CircleProgress", "name": "CircleProgress",
"type": "component", "type": "component",
"cName": "进度条", "cName": "进度条",
"desc": "展示操作或任务的当前进度。", "desc": "展示操作或任务的当前进度。",
"sort": 7, "sort": 7,
"show": true, "show": true,
"author": "swag~jun" "author": "swag~jun"
} }
] ]
}, },
...@@ -276,6 +276,16 @@ ...@@ -276,6 +276,16 @@
"sort": 3, "sort": 3,
"show": true, "show": true,
"author": "swag~jun" "author": "swag~jun"
},
{
"version": "1.0.0",
"name": "Elevator",
"type": "component",
"cName": "电梯楼层",
"desc": "用于列表快速定位以及索引的显示",
"sort": 4,
"show": true,
"author": "songsong"
} }
] ]
}, },
......
import React from 'react'
import { Elevator } from './elevator'
const ElevatorDemo = () => {
const dataList = [
{
title: 'A',
list: [
{
name: '安徽',
id: 1,
},
],
},
{
title: 'B',
list: [
{
name: '北京',
id: 2,
},
],
},
{
title: 'G',
list: [
{
name: '广西',
id: 3,
},
{
name: '广东',
id: 4,
},
],
},
{
title: 'H',
list: [
{
name: '湖南',
id: 5,
},
{
name: '湖北',
id: 6,
},
,
{
name: '琥珀',
id: 7,
},
],
},
]
const dataList2 = [
{
num: '',
list: [
{
name: '北京',
id: 1,
},
{
name: '上海',
id: 2,
},
{
name: '深圳',
id: 3,
},
{
name: '广州',
id: 4,
},
{
name: '杭州',
id: 5,
},
],
},
{
num: '',
list: [
{
name: '成都',
id: 6,
},
{
name: '西安',
id: 7,
},
{
name: '天津',
id: 8,
},
{
name: '武汉',
id: 9,
},
{
name: '长沙',
id: 10,
},
{
name: '重庆',
id: 11,
},
{
name: '苏州',
id: 12,
},
{
name: '南京',
id: 13,
},
],
},
{
num: '',
list: [
{
name: '西宁',
id: 14,
},
{
name: '兰州',
id: 15,
},
{
name: '石家庄',
id: 16,
},
{
name: '秦皇岛',
id: 17,
},
{
name: '大连',
id: 18,
},
{
name: '哈尔滨',
id: 19,
},
{
name: '长春',
id: 20,
},
{
name: '太原',
id: 21,
},
],
},
]
const clickItem = (key: string, item: any) => {
console.log(key, JSON.stringify(item))
}
const clickIndex = (key: string) => {
console.log(key)
}
return (
<>
<div className="demo">
<h2>基础用法</h2>
<Elevator
indexList={dataList}
height="260"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
<h2>自定义索引key</h2>
<Elevator
indexList={dataList2}
height="220"
acceptKey="num"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
</div>
</>
)
}
export default ElevatorDemo
# Elevator 组件
### 介绍
用于列表快速定位以及索引的显示
### 安装
## 代码演示
### 基础用法
```tsx
const dataList = [
{
title: 'A',
list: [
{
name: '安徽',
id: 1,
},
],
},
{
title: 'B',
list: [
{
name: '北京',
id: 2,
},
],
},
{
title: 'G',
list: [
{
name: '广西',
id: 3,
},
{
name: '广东',
id: 4,
},
],
},
{
title: 'H',
list: [
{
name: '湖南',
id: 5,
},
{
name: '湖北',
id: 6,
},
,
{
name: '琥珀',
id: 7,
},
],
},
]
const clickItem = (key: string, item: any) => {
console.log(key, JSON.stringify(item))
}
const clickIndex = (key: string) => {
console.log(key)
}
```
```tsx
<Elevator
indexList={dataList}
height="260"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
```
### 自定义索引
## API
```tsx
const dataList = [
{
num: '',
list: [
{
name: '北京',
id: 1,
},
{
name: '上海',
id: 2,
},
{
name: '深圳',
id: 3,
},
{
name: '广州',
id: 4,
},
{
name: '杭州',
id: 5,
},
],
},
{
num: '',
list: [
{
name: '成都',
id: 6,
},
{
name: '西安',
id: 7,
},
{
name: '天津',
id: 8,
},
{
name: '武汉',
id: 9,
},
{
name: '长沙',
id: 10,
},
{
name: '重庆',
id: 11,
},
{
name: '苏州',
id: 12,
},
{
name: '南京',
id: 13,
},
],
},
{
num: '',
list: [
{
name: '西宁',
id: 14,
},
{
name: '兰州',
id: 15,
},
{
name: '石家庄',
id: 16,
},
{
name: '秦皇岛',
id: 17,
},
{
name: '大连',
id: 18,
},
{
name: '哈尔滨',
id: 19,
},
{
name: '长春',
id: 20,
},
{
name: '太原',
id: 21,
},
],
},
]
const clickItem = (key: string, item: any) => {
console.log(key, JSON.stringify(item))
}
const clickIndex = (key: string) => {
console.log(key)
}
```
```tsx
<Elevator
indexList={dataList2}
height="220"
acceptKey="num"
clickItem={(key: string, item: any) => clickItem(key, item)}
clickIndex={(key: string) => clickIndex(key)}
></Elevator>
```
### Props
| 字段 | 说明 | 类型 | 默认值 |
| --------- | -------------- | ----------------------------------------------------------- | --------------------- |
| height | 电梯区域的高度 | Number、String | `200px` |
| acceptKey | 索引 key 值 | String | `title` |
| indexList | 索引列表 | Array(item 需包含 id、name 属性, name 支持传入 html 结构) | `[{id: 0, name: ''}]` |
### Event
| 名称 | 说明 | 回调参数 |
| ---------- | -------- | -------------------------------------- |
| clickItem | 点击内容 | key: string, item: { id: 0, name: '' } |
| clickIndex | 点击索引 | key: string |
.nut-elevator {
width: 100%;
display: block;
position: relative;
&__list {
display: block;
overflow: auto;
&__item {
display: block;
font-size: 12px;
color: #333;
&__code {
display: inline-flex;
position: relative;
height: 35px;
line-height: 35px;
font-size: 14px;
color: #1a1a1a;
padding: 0 20px;
font-weight: 500;
&::after {
content: ' ';
width: 100%;
height: 1px;
position: absolute;
left: 0;
bottom: 7px;
background-color: #eeeff2;
}
}
&__name {
display: flex;
align-items: center;
padding: 0 20px;
height: 30px;
line-height: 30px;
}
}
}
&__code--current {
position: absolute;
right: 60px;
top: 50%;
transform: translateY(-50%);
width: 45px;
height: 45px;
line-height: 45px;
border-radius: 50%;
background: $white;
box-shadow: 0 3px 3px 1px rgba(240, 240, 240, 1);
text-align: center;
}
&__bars {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
padding: 15px 0;
background-color: #eeeff2;
border-radius: 6px;
text-align: center;
z-index: 10;
&__inner {
&__item {
display: block;
padding: 3px;
font-size: 10px;
}
}
}
}
import React, { FunctionComponent, useRef, useEffect, useState } from 'react'
import './elevator.scss'
import bem from '@/utils/bem'
export interface ElevatorProps {
height: number | string
acceptKey: string
indexList: any
className: string
style: React.CSSProperties
clickItem: (key: string, item: ElevatorData) => void
clickIndex: (key: string) => void
}
const defaultProps = {
height: '200px',
acceptKey: 'title',
indexList: [],
className: '',
} as ElevatorProps
interface ElevatorData {
name: string
id: number | string
[key: string]: string | number
}
export const Elevator: FunctionComponent<
Partial<ElevatorProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { height, acceptKey, indexList, className, clickItem, clickIndex, ...rest } = {
...defaultProps,
...props,
}
const b = bem('elevator')
const spaceHeight = 23
const listview = useRef(null)
const initData = {
anchorIndex: 0,
listHeight: [] as number[],
listGroup: [] as HTMLElement[],
}
const touchState = useRef({
y1: 0,
y2: 0,
})
const [currentIndex, setCurrentIndex] = useState<number>(0)
const [scrollStart, setScrollStart] = useState<boolean>(false)
const state = useRef(initData)
//重置滚动参数
const resetScrollState = () => {
state.current.anchorIndex = 0
setCurrentIndex(0)
setScrollStart(false)
touchState.current = {
y1: 0,
y2: 0,
}
}
const getData = (el: HTMLElement, name: string): string | void => {
const prefix = 'data-'
return el.getAttribute(prefix + name) as string
}
const calculateHeight = () => {
let height = 0
state.current.listHeight.push(height)
for (let i = 0; i < state.current.listGroup.length; i++) {
let item = state.current.listGroup[i]
height += item.clientHeight
state.current.listHeight.push(height)
}
}
const scrollTo = (index: number) => {
if (!index && index !== 0) {
return
}
if (!state.current.listHeight.length) {
calculateHeight()
}
if (index < 0) index = 0
if (index > state.current.listHeight.length - 2) index = state.current.listHeight.length - 2
setCurrentIndex(index)
if (listview.current) {
listview.current.scrollTo(0, state.current.listHeight[index])
}
}
const touchMove = (e: React.TouchEvent<HTMLDivElement>) => {
let firstTouch = e.touches[0]
touchState.current.y2 = firstTouch.pageY
let delta = ((touchState.current.y2 - touchState.current.y1) / spaceHeight) | 0
const cacheIndex = state.current.anchorIndex + delta
setCurrentIndex(cacheIndex)
scrollTo(cacheIndex)
}
const touchEnd = () => {
resetScrollState()
}
const touchStart = (e: React.TouchEvent<HTMLDivElement>) => {
setScrollStart(true)
let index = Number(getData(e.target as HTMLElement, 'index'))
let firstTouch = e.touches[0]
touchState.current.y1 = firstTouch.pageY
state.current.anchorIndex = +index
setCurrentIndex((currentIndex) => currentIndex + index)
scrollTo(index)
const target = e.currentTarget as HTMLElement
target.removeEventListener('touchmove', touchMove, false)
target.removeEventListener('touchend', touchEnd, false)
target.addEventListener('touchmove', touchMove, false)
target.addEventListener('touchend', touchEnd, false)
}
const handleClickItem = (key: string, item: ElevatorData) => {
clickItem && clickItem(key, item)
}
const handleClickIndex = (key: string) => {
clickIndex && clickIndex(key)
}
const setListGroup = () => {
const els = listview.current.querySelectorAll('.nut-elevator__list__item')
els.forEach((el: HTMLLIElement) => {
if (el != null && !state.current.listGroup.includes(el)) {
state.current.listGroup.push(el)
}
})
}
useEffect(() => {
if (listview.current) {
setListGroup()
}
}, [listview.current])
return (
<div className={`${b()} ${className}`} {...rest}>
<div
className={b('list')}
ref={listview}
style={{ height: isNaN(+height) ? height : `${height}px` }}
>
{indexList.map((item: any) => {
return (
<div className={b('list__item')} key={item[acceptKey]}>
<div className={b('list__item__code')}>{item[acceptKey]}</div>
<>
{item.list.map((subitem: any) => {
return (
<div
className={b('list__item__name')}
key={subitem['id']}
onClick={() => handleClickItem(item[acceptKey], subitem)}
>
{subitem.name}
</div>
)
})}
</>
</div>
)
})}
</div>
{indexList.length && scrollStart ? (
<div className={b('code', { current: true })}> {indexList[currentIndex][acceptKey]}</div>
) : null}
<div className={b('bars')} onTouchStart={(event) => touchStart(event)}>
<div className={b('bars__inner')}>
{indexList.map((item: any, index: number) => {
return (
<div
className={b('bars__inner__item')}
data-index={index}
key={item[acceptKey]}
onClick={() => handleClickIndex(item[acceptKey])}
>
{item[acceptKey]}
</div>
)
})}
</div>
</div>
</div>
)
}
Elevator.defaultProps = defaultProps
Elevator.displayName = 'NutElevator'
import { Elevator } from './elevator'
export default Elevator
...@@ -15,6 +15,7 @@ import Steps from './steps' ...@@ -15,6 +15,7 @@ import Steps from './steps'
import NavBar from './navbar' import NavBar from './navbar'
import Tabbar from './tabbar' import Tabbar from './tabbar'
import InputNumber from './inputnumber' import InputNumber from './inputnumber'
import Elevator from './elevator'
import Rate from './rate' import Rate from './rate'
import Uploader from './uploader' import Uploader from './uploader'
import Input from './input' import Input from './input'
...@@ -40,6 +41,7 @@ export { ...@@ -40,6 +41,7 @@ export {
NavBar, NavBar,
Tabbar, Tabbar,
InputNumber, InputNumber,
Elevator,
Rate, Rate,
Uploader, Uploader,
Input, Input,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册