From 61490b0383ce55b287fd47f0905ce72e8adee96d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=AE=8B=E5=AE=8B?= <353833373@qq.com>
Date: Thu, 28 Oct 2021 17:42:27 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20elevator=E7=BB=84=E4=BB=B6=E5=BC=80?=
=?UTF-8?q?=E5=8F=91=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/config.json | 28 ++--
src/packages/elevator/demo.tsx | 187 +++++++++++++++++++++++
src/packages/elevator/doc.md | 221 ++++++++++++++++++++++++++++
src/packages/elevator/elevator.scss | 71 +++++++++
src/packages/elevator/elevator.tsx | 198 +++++++++++++++++++++++++
src/packages/elevator/index.ts | 2 +
src/packages/nutui.react.ts | 2 +
7 files changed, 700 insertions(+), 9 deletions(-)
create mode 100644 src/packages/elevator/demo.tsx
create mode 100644 src/packages/elevator/doc.md
create mode 100644 src/packages/elevator/elevator.scss
create mode 100644 src/packages/elevator/elevator.tsx
create mode 100644 src/packages/elevator/index.ts
diff --git a/src/config.json b/src/config.json
index 301ea16..9ac31ab 100644
--- a/src/config.json
+++ b/src/config.json
@@ -232,15 +232,15 @@
"show": true,
"author": "swag~jun"
},
- {
- "version": "1.0.0",
- "name": "CircleProgress",
- "type": "component",
- "cName": "进度条",
- "desc": "展示操作或任务的当前进度。",
- "sort": 7,
- "show": true,
- "author": "swag~jun"
+ {
+ "version": "1.0.0",
+ "name": "CircleProgress",
+ "type": "component",
+ "cName": "进度条",
+ "desc": "展示操作或任务的当前进度。",
+ "sort": 7,
+ "show": true,
+ "author": "swag~jun"
}
]
},
@@ -276,6 +276,16 @@
"sort": 3,
"show": true,
"author": "swag~jun"
+ },
+ {
+ "version": "1.0.0",
+ "name": "Elevator",
+ "type": "component",
+ "cName": "电梯楼层",
+ "desc": "用于列表快速定位以及索引的显示",
+ "sort": 4,
+ "show": true,
+ "author": "songsong"
}
]
},
diff --git a/src/packages/elevator/demo.tsx b/src/packages/elevator/demo.tsx
new file mode 100644
index 0000000..0881b66
--- /dev/null
+++ b/src/packages/elevator/demo.tsx
@@ -0,0 +1,187 @@
+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 (
+ <>
+
+
基础用法
+ clickItem(key, item)}
+ clickIndex={(key: string) => clickIndex(key)}
+ >
+ 自定义索引key
+ clickItem(key, item)}
+ clickIndex={(key: string) => clickIndex(key)}
+ >
+
+ >
+ )
+}
+
+export default ElevatorDemo
diff --git a/src/packages/elevator/doc.md b/src/packages/elevator/doc.md
new file mode 100644
index 0000000..5e41f16
--- /dev/null
+++ b/src/packages/elevator/doc.md
@@ -0,0 +1,221 @@
+# 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
+ clickItem(key, item)}
+ clickIndex={(key: string) => clickIndex(key)}
+>
+```
+
+### 自定义索引
+
+## 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
+ clickItem(key, item)}
+ clickIndex={(key: string) => clickIndex(key)}
+>
+```
+
+### 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 |
diff --git a/src/packages/elevator/elevator.scss b/src/packages/elevator/elevator.scss
new file mode 100644
index 0000000..da6cd73
--- /dev/null
+++ b/src/packages/elevator/elevator.scss
@@ -0,0 +1,71 @@
+.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;
+ }
+ }
+ }
+}
diff --git a/src/packages/elevator/elevator.tsx b/src/packages/elevator/elevator.tsx
new file mode 100644
index 0000000..ae8e5b8
--- /dev/null
+++ b/src/packages/elevator/elevator.tsx
@@ -0,0 +1,198 @@
+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 & React.HTMLAttributes
+> = (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(0)
+ const [scrollStart, setScrollStart] = useState(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) => {
+ 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) => {
+ 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 (
+
+
+ {indexList.map((item: any) => {
+ return (
+
+
{item[acceptKey]}
+ <>
+ {item.list.map((subitem: any) => {
+ return (
+
handleClickItem(item[acceptKey], subitem)}
+ >
+ {subitem.name}
+
+ )
+ })}
+ >
+
+ )
+ })}
+
+ {indexList.length && scrollStart ? (
+
{indexList[currentIndex][acceptKey]}
+ ) : null}
+
touchStart(event)}>
+
+ {indexList.map((item: any, index: number) => {
+ return (
+
handleClickIndex(item[acceptKey])}
+ >
+ {item[acceptKey]}
+
+ )
+ })}
+
+
+
+ )
+}
+
+Elevator.defaultProps = defaultProps
+Elevator.displayName = 'NutElevator'
diff --git a/src/packages/elevator/index.ts b/src/packages/elevator/index.ts
new file mode 100644
index 0000000..0cb817a
--- /dev/null
+++ b/src/packages/elevator/index.ts
@@ -0,0 +1,2 @@
+import { Elevator } from './elevator'
+export default Elevator
diff --git a/src/packages/nutui.react.ts b/src/packages/nutui.react.ts
index d75f7a2..b6a1c51 100644
--- a/src/packages/nutui.react.ts
+++ b/src/packages/nutui.react.ts
@@ -15,6 +15,7 @@ import Steps from './steps'
import NavBar from './navbar'
import Tabbar from './tabbar'
import InputNumber from './inputnumber'
+import Elevator from './elevator'
import Rate from './rate'
import Uploader from './uploader'
import Input from './input'
@@ -40,6 +41,7 @@ export {
NavBar,
Tabbar,
InputNumber,
+ Elevator,
Rate,
Uploader,
Input,
--
GitLab