UNPKG

@nutui/nutui-react-taro

Version:

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

273 lines (272 loc) 9.71 kB
import { a as __awaiter } from "./tslib.es6-iWu3F_1J.js"; import React__default, { useState, useRef, useEffect, useMemo } from "react"; import classNames from "classnames"; import { View } from "@tarojs/components"; import { u as useTouch } from "./use-touch-BeiLHDO9.js"; import { C as ComponentDefaults } from "./typings-DV9RBfhj.js"; import { u as usePropsValue } from "./use-props-value-SH9krhkx.js"; import { g as getRectByTaro } from "./get-rect-by-taro-D0h6aiDr.js"; import { a as useRtl } from "./configprovider.taro-DpK4IiCE.js"; const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { range: false, min: 0, max: 100, step: 1, vertical: false, marks: {} }); const Range = (props) => { const rtl = useRtl(); const { className, range, disabled, button, vertical, marks, minDescription, maxDescription, currentDescription, min, max, step, value, defaultValue, onChange, onStart, onEnd } = Object.assign(Object.assign({}, defaultProps), props); const classPrefix = "nut-range"; const [buttonIndex, setButtonIndex] = useState(0); const [dragStatus, setDragStatus] = useState("start"); const touch = useTouch(); const root = useRef(null); const [marksList, setMarksList] = useState([]); const [startValue, setStartValue] = useState(0); const handleChange = (value2) => { onChange && onChange(value2); }; const [current, setCurrent] = usePropsValue({ value, defaultValue, finalValue: 0, onChange: handleChange }); const [exactValue, setExactValue] = useState(() => value || defaultValue || 0); const marksRef = useRef({}); useEffect(() => { if (marks) { if (Array.isArray(marks)) { const list = marks.sort((a, b) => a.value - b.value).filter((point) => point.value >= min && point.value <= max); setMarksList(list.map((mark) => mark.value)); list.forEach((mark) => { marksRef.current[mark.value] = mark.label !== void 0 ? mark.label : mark.value; }); } else { const marksKeys = Object.keys(marks); const list = marksKeys.map(parseFloat).sort((a, b) => a - b).filter((point) => point >= min && point <= max); setMarksList(list); } } }, [marks, max, min]); const classes = classNames(classPrefix, { [`${classPrefix}-disabled`]: disabled, [`${classPrefix}-vertical`]: vertical }); const containerClasses = classNames(`${classPrefix}-container`, { [`${classPrefix}-container-vertical`]: vertical }, className); const markClassName = (mark) => { const classPrefix2 = "nut-range-mark"; let lowerBound = min; let upperBound = max; if (range && Array.isArray(current)) { lowerBound = current[0]; upperBound = current[1]; } else { upperBound = current; } const isActive = mark <= upperBound && mark >= lowerBound; return [ `${classPrefix2}-text`, `${isActive ? `${classPrefix2}-text-active` : ""}` ].join(" "); }; const isRange = (val) => { return !!range && Array.isArray(val); }; const scope = useMemo(() => { if (max < min || max === min) { console.log("max 的值需要大于 min的值"); } return max - min; }, [max, min]); const calcMainAxis = () => { const modelVal = current; return isRange(modelVal) ? `${(modelVal[1] - modelVal[0]) * 100 / scope}%` : `${(modelVal - min) * 100 / scope}%`; }; const calcOffset = () => { const modelVal = current; return isRange(modelVal) ? `${(modelVal[0] - min) * 100 / scope}%` : `0%`; }; const barStyle = () => { if (vertical) { return { height: calcMainAxis(), top: calcOffset(), transition: dragStatus ? "none" : void 0 }; } const dir = rtl ? "right" : "left"; return { width: calcMainAxis(), [dir]: calcOffset(), transition: dragStatus ? "none" : void 0 }; }; const marksStyle = (mark) => { const dir = rtl ? "right" : "left"; let style = { [dir]: `${(mark - min) / scope * 100}%` }; if (vertical) { style = { top: `${(mark - min) / scope * 100}%` }; } return style; }; const tickClass = (mark) => { if (range && Array.isArray(current)) { return mark <= current[1] && mark >= current[0]; } return mark <= current; }; const format = (value2) => { value2 = Math.max(+min, Math.min(value2, +max)); return Math.round(value2 / +step) * +step; }; const isSameValue = (newValue, oldValue) => { return JSON.stringify(newValue) === JSON.stringify(oldValue); }; const handleOverlap = (value2) => { if (value2[0] > value2[1]) { return value2.slice(0).reverse(); } return value2; }; const updateValue = (value2, end) => { if (isRange(value2)) { value2 = handleOverlap(value2).map(format); } else { value2 = format(value2); } if (!isSameValue(value2, current)) { setCurrent(value2); } end && onEnd && onEnd(value2); }; const click = (event) => __awaiter(void 0, void 0, void 0, function* () { if (disabled || !root.current) { return; } setDragStatus(""); const rect = yield getRectByTaro(root.current); let delta = (event.detail.x ? event.detail.x : event.clientX) - rect.left; let total = rect.width; if (vertical) { delta = (event.detail.y ? event.detail.y : event.clientY) - rect.top; total = rect.height; } const value2 = min + delta / total * scope; setExactValue(current); if (isRange(current)) { const [left, right] = current; const middle = (left + right) / 2; if (value2 <= middle) { updateValue([value2, right], true); } else { updateValue([left, value2], true); } } else { updateValue(value2, true); } }); const onTouchStart = (event) => { if (disabled) { return; } touch.start(event); setExactValue(current); if (isRange(current)) { setStartValue(current.map(format)); } else { setStartValue(format(current)); } setDragStatus("start"); }; const onTouchMove = (event) => __awaiter(void 0, void 0, void 0, function* () { if (disabled || !root.current) { return; } if (dragStatus === "start") { onStart && onStart(); } touch.move(event); setDragStatus("draging"); const rect = yield getRectByTaro(root.current); if (!rect) return; let delta = touch.deltaX.current; let total = rect.width; let diff = delta / total * scope; diff = rtl ? -diff : diff; if (vertical) { delta = touch.deltaY.current; total = rect.height; diff = delta / total * scope; } let newValue; if (isRange(startValue)) { newValue = exactValue.slice(); newValue[buttonIndex] = startValue[buttonIndex] + diff; } else { newValue = startValue + diff; } setExactValue(newValue); updateValue(newValue); }); const onTouchEnd = () => { if (disabled) { return; } if (dragStatus === "draging") { updateValue(current, true); } setDragStatus(""); }; const curValue = (idx) => { const modelVal = current; const value2 = typeof idx === "number" ? modelVal[idx] : modelVal; return value2; }; const renderButton = (index) => { return React__default.createElement(React__default.Fragment, null, button || React__default.createElement("div", { className: "nut-range-button" }, currentDescription !== null && React__default.createElement("div", { className: "number" }, currentDescription ? currentDescription(curValue(index)) : curValue(index)))); }; const renderRangeButton = () => { return [0, 1].map((item, index) => { const cls = `${index === 0 ? "nut-range-button-wrapper-left" : ""} ${index === 1 ? "nut-range-button-wrapper-right" : ""}`; return React__default.createElement("div", { key: index, className: cls, onTouchStart: (e) => { setButtonIndex(index); onTouchStart(e); }, onTouchMove, onTouchEnd, onTouchCancel: onTouchEnd, onClick: (e) => e.stopPropagation() }, renderButton(index)); }); }; const renderSingleButton = () => { return React__default.createElement(View, { catchMove: true, className: "nut-range-button-wrapper", onTouchStart, onTouchMove, onTouchEnd, onTouchCancel: onTouchEnd, onClick: (e) => e.stopPropagation() }, renderButton()); }; const renderMark = () => { return React__default.createElement(React__default.Fragment, null, marksList.length > 0 && React__default.createElement("div", { className: "nut-range-mark" }, marksList.map((mark) => { return React__default.createElement( "span", { key: mark, className: markClassName(mark), style: marksStyle(mark) }, Array.isArray(marks) ? marksRef.current[mark] : marks[mark], React__default.createElement("span", { className: classNames("nut-range-tick", { active: tickClass(mark) }) }) ); }))); }; return React__default.createElement( "div", { className: containerClasses }, minDescription !== null && React__default.createElement("div", { className: "min" }, minDescription || min), React__default.createElement( "div", { ref: root, className: classes, onClick: (e) => click(e) }, renderMark(), React__default.createElement("div", { className: "nut-range-bar", style: barStyle() }, range ? renderRangeButton() : renderSingleButton()) ), maxDescription !== null && React__default.createElement("div", { className: "max" }, maxDescription || max) ); }; Range.displayName = "NutRange"; export { Range as R };