UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

181 lines (180 loc) 7.22 kB
import { _ as __rest } from "./tslib.es6.js"; import React__default, { createContext, useRef, useState, useEffect } from "react"; import { useGesture } from "@use-gesture/react"; import { animated } from "@react-spring/web"; import classNames from "classnames"; import { C as ComponentDefaults } from "./typings.js"; const elevatorContext = createContext({}); const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { height: "200px", floorKey: "title", list: [], sticky: false, spaceHeight: 23, titleHeight: 35, showKeys: true }); const Elevator = (props) => { const _a = Object.assign(Object.assign({}, defaultProps), props), { height, floorKey, list, sticky, spaceHeight, titleHeight, showKeys, className, style, onItemClick, onIndexClick, children } = _a, rest = __rest(_a, ["height", "floorKey", "list", "sticky", "spaceHeight", "titleHeight", "showKeys", "className", "style", "onItemClick", "onIndexClick", "children"]); const classPrefix = "nut-elevator"; const listview = useRef(null); const initData = { anchorIndex: 0, listHeight: [], listGroup: [], scrollY: 0 }; const touchState = useRef({ y1: 0, y2: 0 }); const [scrollY, setScrollY] = useState(0); const [currentData, setCurrentData] = useState({}); const [currentKey, setCurrentKey] = useState(""); const [currentIndex, setCurrentIndex] = useState(0); const [codeIndex, setCodeIndex] = useState(0); const [scrollStart, setScrollStart] = useState(false); const state = useRef(initData); const resetScrollState = () => { setScrollStart(false); }; const getData = (el, name) => { const prefix = "data-"; return el.getAttribute(prefix + name); }; const calculateHeight = () => { let height2 = 0; state.current.listHeight.push(height2); for (let i = 0; i < state.current.listGroup.length; i++) { const item = state.current.listGroup[i]; height2 += item.clientHeight; state.current.listHeight.push(height2); } }; const scrollTo = (index) => { if (!index && index !== 0) { return; } if (!state.current.listHeight.length) { calculateHeight(); } let cacheIndex = index; if (index < 0) { cacheIndex = 0; } if (index > state.current.listHeight.length - 2) { cacheIndex = Math.max(0, state.current.listHeight.length - 2); } setCodeIndex(cacheIndex); if (listview.current) { listview.current.scrollTo(0, state.current.listHeight[cacheIndex]); } }; const bind = useGesture({ onDragStart: ({ target, offset }) => { setScrollStart(true); const index = Number(getData(target, "index")); touchState.current.y1 = offset[1]; state.current.anchorIndex = +index; setCodeIndex((codeIndex2) => codeIndex2 + index); scrollTo(index); }, onDragEnd: ({ offset }) => { touchState.current.y2 = offset[1]; const delta = (touchState.current.y2 - touchState.current.y1) / spaceHeight || 0; const cacheIndex = state.current.anchorIndex + Math.round(delta); setCodeIndex(cacheIndex); scrollTo(cacheIndex); resetScrollState(); } }); const handleClickItem = (key, item) => { onItemClick && onItemClick(key, item); setCurrentData(item); setCurrentKey(key); }; const handleClickIndex = (key) => { onIndexClick && onIndexClick(key); }; const setListGroup = () => { if (listview.current) { const els = listview.current.querySelectorAll(".nut-elevator-list-item"); els.forEach((el) => { if (el != null && !state.current.listGroup.includes(el)) { state.current.listGroup.push(el); } }); } }; const listViewScroll = (e) => { const { listHeight } = state.current; if (!listHeight.length) { calculateHeight(); } const target = e.target; let { scrollTop } = target; scrollTop = Math.ceil(scrollTop); state.current.scrollY = scrollTop; setScrollY(scrollTop); for (let i = 0; i < listHeight.length - 1; i++) { const height1 = listHeight[i]; const height2 = listHeight[i + 1]; if (state.current.scrollY >= height1 && state.current.scrollY < height2) { setCurrentIndex(i); return; } } setCurrentIndex(listHeight.length - 2); }; useEffect(() => { if (listview.current) { setListGroup(); listview.current.addEventListener("scroll", listViewScroll); } }, [listview]); return React__default.createElement( "div", Object.assign({ className: `${classPrefix} ${className}`, style }, rest), sticky && scrollY > 0 ? React__default.createElement( "div", { className: `${classPrefix}-list-fixed` }, React__default.createElement("span", { className: `${classPrefix}-list-fixed-title` }, list[currentIndex][floorKey]) ) : null, React__default.createElement( "div", { className: `${classPrefix}-list`, style: { height: Number.isNaN(+height) ? height : `${height}px` } }, React__default.createElement("div", { className: `${classPrefix}-list-inner`, ref: listview }, list.map((item, idx) => { return React__default.createElement( "div", { className: `${classPrefix}-list-item`, key: idx }, React__default.createElement("div", { className: `${classPrefix}-list-item-code` }, item[floorKey]), React__default.createElement(React__default.Fragment, null, item.list.map((subitem) => { return React__default.createElement("div", { className: classNames({ [`${classPrefix}-list-item-name`]: true, [`${classPrefix}-list-item-name-highcolor`]: currentData.id === subitem.id && currentKey === item[floorKey] }), key: subitem.id, onClick: () => handleClickItem(item[floorKey], subitem) }, children ? React__default.createElement( React__default.Fragment, null, React__default.createElement(elevatorContext.Provider, { value: subitem }, children) ) : subitem.name); })) ); })) ), showKeys ? React__default.createElement( React__default.Fragment, null, list.length && scrollStart ? React__default.createElement("div", { className: classNames({ [`${classPrefix}-code-current`]: true, [`${classPrefix}-code-current-current`]: true }) }, list[codeIndex][floorKey]) : null, React__default.createElement( "div", { className: `${classPrefix}-bars` }, React__default.createElement(animated.div, Object.assign({ className: `${classPrefix}-bars-inner` }, bind(), { style: { touchAction: "pan-y" } }), list.map((item, index) => { return React__default.createElement("div", { className: classNames({ [`${classPrefix}-bars-inner-item`]: true, [`${classPrefix}-bars-inner-item-active`]: item[floorKey] === list[currentIndex][floorKey] }), "data-index": index, key: index, onClick: () => handleClickIndex(item[floorKey]) }, item[floorKey]); })) ) ) : null ); }; Elevator.displayName = "NutElevator"; Elevator.Context = elevatorContext; export { Elevator as default };