@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
181 lines (180 loc) • 7.22 kB
JavaScript
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
};