UNPKG

@nutui/nutui-react

Version:

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

423 lines (422 loc) 14.9 kB
import { _ as __rest, a as __awaiter } from "./tslib.es6.js"; import React__default, { useState, useImperativeHandle, useEffect, isValidElement } from "react"; import classNames from "classnames"; import { Loading, Checklist } from "@nutui/icons-react"; import { P as Popup } from "./popup2.js"; import { T as Tabs } from "./tabs2.js"; import { C as ComponentDefaults } from "./typings.js"; import { u as usePropsValue } from "./use-props-value.js"; import { u as useConfig } from "./configprovider.taro.js"; const formatTree = (tree, parent, config) => tree.map((node) => { const { value: valueKey = "value", text: textKey = "text", children: childrenKey = "children" } = config; const _a = node, _b = valueKey, value = _a[_b], _c = textKey, text = _a[_c], _d = childrenKey, children = _a[_d], others = __rest(_a, [typeof _b === "symbol" ? _b : _b + "", typeof _c === "symbol" ? _c : _c + "", typeof _d === "symbol" ? _d : _d + ""]); const newNode = Object.assign(Object.assign({ loading: false }, others), { level: parent ? (parent && parent.level || 0) + 1 : 0, value, text, children, _parent: parent }); if (newNode.children && newNode.children.length) { newNode.children = formatTree(newNode.children, newNode, config); } return newNode; }); const eachTree = (tree, cb) => { let i = 0; let node; while (node = tree[i++]) { if (cb(node) === true) { break; } if (node.children && node.children.length) { eachTree(node.children, cb); } } }; const defaultConvertConfig = { topId: null, idKey: "id", pidKey: "pid", sortKey: "" }; const convertListToOptions = (list, options) => { const mergedOptions = Object.assign(Object.assign({}, defaultConvertConfig), options || {}); const { topId, idKey, pidKey, sortKey } = mergedOptions; let result = []; let map = {}; list.forEach((node) => { node = Object.assign({}, node); const { [idKey]: id, [pidKey]: pid } = node; const children = map[pid] = map[pid] || []; if (!result.length && pid === topId) { result = children; } children.push(node); node.children = map[id] || (map[id] = []); }); if (sortKey) { Object.keys(map).forEach((i) => { if (map[i].length > 1) { map[i].sort((a, b) => a[sortKey] - b[sortKey]); } }); } map = null; return result; }; class Tree { constructor(nodes, config) { this.isLeaf = (node, lazy) => { const { leaf, children } = node; const hasChildren = Array.isArray(children) && Boolean(children.length); return leaf || !hasChildren && !lazy; }; this.hasChildren = (node, lazy) => { const isLeaf = this.isLeaf(node, lazy); if (isLeaf) { return false; } const { children } = node; return Array.isArray(children) && Boolean(children.length); }; this.config = Object.assign({ value: "value", text: "text", children: "children" }, config || {}); this.nodes = formatTree(nodes, null, this.config); } updateChildren(nodes, parent) { if (!parent) { this.nodes = formatTree(nodes, null, this.config); } else { parent.children = formatTree(nodes, parent, this.config); } } // for test getNodeByValue(value) { let foundNode; eachTree(this.nodes, (node) => { if (node.value === value) { foundNode = node; return true; } return null; }); return foundNode; } getPathNodesByValue(value) { if (!value.length) { return []; } const pathNodes = []; let currentNodes = this.nodes; while (currentNodes && currentNodes.length) { const foundNode = currentNodes.find((node) => node.value === value[node.level]); if (!foundNode) { break; } pathNodes.push(foundNode); currentNodes = foundNode.children; } return pathNodes; } } const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { activeColor: "", activeIcon: "checklist", popup: true, options: [], optionKey: { textKey: "text", valueKey: "value", childrenKey: "children" }, format: {}, closeable: false, closeIconPosition: "top-right", closeIcon: "close", lazy: false, onLoad: () => { }, onClose: () => { }, onChange: () => { }, onPathChange: () => { } }); const InternalCascader = (props, ref) => { const { locale } = useConfig(); const { className, style, activeColor, activeIcon, popup, popupProps = {}, visible, options, value, defaultValue, optionKey, format, closeable, closeIconPosition, closeIcon, lazy, title, left, onLoad, onClose, onChange, onPathChange } = Object.assign(Object.assign({}, defaultProps), props); const [tabvalue, setTabvalue] = useState("c1"); const [optionsData, setOptionsData] = useState([]); const isLazy = () => state.configs.lazy && Boolean(state.configs.onLoad); const [innerValue, setInnerValue] = usePropsValue({ value, defaultValue, finalValue: defaultValue }); const [innerVisible, setInnerVisible] = usePropsValue({ value: visible, defaultValue: void 0, finalValue: false }); const actions = { open: () => { setInnerVisible(true); }, close: () => { setInnerVisible(false); } }; useImperativeHandle(ref, () => actions); const [state] = useState({ optionsData: [], panes: [ { nodes: [], selectedNode: [], paneKey: "" } ], tree: new Tree([], {}), tabsCursor: 0, // 选中的tab项 initLoading: false, currentProcessNode: [], configs: { lazy, onLoad, optionKey, format }, lazyLoadMap: /* @__PURE__ */ new Map() }); const classPrefix = classNames(`nut-cascader`); const classesPane = classNames({ [`${classPrefix}-pane`]: true }); useEffect(() => { initData(); }, [options, format]); useEffect(() => { syncValue(); }, [value]); const initData = () => __awaiter(void 0, void 0, void 0, function* () { state.lazyLoadMap.clear(); if (format && Object.keys(format).length > 0) { state.optionsData = convertListToOptions(options, format); } else { state.optionsData = options; } state.tree = new Tree(state.optionsData, { value: state.configs.optionKey.valueKey, text: state.configs.optionKey.textKey, children: state.configs.optionKey.childrenKey }); if (isLazy() && !state.tree.nodes.length) { yield invokeLazyLoad({ root: true, loading: true, text: "", value: "" }); } state.panes = [ { nodes: state.tree.nodes, selectedNode: null, paneKey: "c1" } ]; syncValue(); setOptionsData(state.panes); }); const syncValue = () => __awaiter(void 0, void 0, void 0, function* () { const currentValue = innerValue; if (currentValue === void 0 || ![defaultValue, value].includes(currentValue) || !state.tree.nodes.length) { return; } if (currentValue.length === 0) { state.tabsCursor = 0; return; } let needToSync = currentValue; if (isLazy() && Array.isArray(currentValue) && currentValue.length) { needToSync = []; const parent = state.tree.nodes.find((node) => node.value === currentValue[0]); if (parent) { needToSync = [parent.value]; state.initLoading = true; const last = yield currentValue.slice(1).reduce((p, value2) => __awaiter(void 0, void 0, void 0, function* () { var _a; const parent2 = yield p; yield invokeLazyLoad(parent2); const node = (_a = parent2 === null || parent2 === void 0 ? void 0 : parent2.children) === null || _a === void 0 ? void 0 : _a.find((item) => item.value === value2); if (node) { needToSync.push(value2); } return Promise.resolve(node); }), Promise.resolve(parent)); yield invokeLazyLoad(last); state.initLoading = false; } } if (needToSync.length && [defaultValue, value].includes(currentValue)) { const pathNodes = state.tree.getPathNodesByValue(needToSync); pathNodes.forEach((node, index) => { state.tabsCursor = index; chooseItem(node, true); }); } }); const invokeLazyLoad = (node) => __awaiter(void 0, void 0, void 0, function* () { if (!node) { return; } if (!state.configs.onLoad) { node.leaf = true; return; } if (state.tree.isLeaf(node, isLazy()) || state.tree.hasChildren(node, isLazy())) { return; } node.loading = true; const parent = node.root ? null : node; let lazyLoadPromise = state.lazyLoadMap.get(node); if (!lazyLoadPromise) { lazyLoadPromise = new Promise((resolve) => { var _a, _b; (_b = (_a = state.configs).onLoad) === null || _b === void 0 ? void 0 : _b.call(_a, node, resolve); }); state.lazyLoadMap.set(node, lazyLoadPromise); } const nodes = yield lazyLoadPromise; if (Array.isArray(nodes) && nodes.length > 0) { state.tree.updateChildren(nodes, parent); } else { node.leaf = true; } node.loading = false; state.lazyLoadMap.delete(node); }); const close = () => { setInnerVisible(false); onClose && onClose(); }; const closePopup = () => { close(); }; const chooseItem = (node, type) => __awaiter(void 0, void 0, void 0, function* () { if (!type && node.disabled || !state.panes[state.tabsCursor]) { return; } if (state.tree.isLeaf(node, isLazy())) { node.leaf = true; state.panes[state.tabsCursor].selectedNode = node; state.panes = state.panes.slice(0, node.level + 1); if (!type) { const pathNodes = state.panes.map((item) => item.selectedNode); const optionParams = pathNodes.map((item) => item.value); onChange(optionParams, pathNodes); onPathChange === null || onPathChange === void 0 ? void 0 : onPathChange(optionParams, pathNodes); setInnerValue(optionParams); } setOptionsData(state.panes); close(); return; } if (state.tree.hasChildren(node, isLazy())) { const level = node.level + 1; state.panes[state.tabsCursor].selectedNode = node; state.panes = state.panes.slice(0, level); state.tabsCursor = level; state.panes.push({ nodes: node.children || [], selectedNode: null, paneKey: `c${state.tabsCursor + 1}` }); setOptionsData(state.panes); setTabvalue(`c${state.tabsCursor + 1}`); if (!type) { const pathNodes = state.panes.map((item) => item.selectedNode); const optionParams = pathNodes.map((item) => item === null || item === void 0 ? void 0 : item.value); onPathChange === null || onPathChange === void 0 ? void 0 : onPathChange(optionParams, pathNodes); } return; } state.currentProcessNode = node; if (node.loading) { return; } yield invokeLazyLoad(node); if (state.currentProcessNode === node) { state.panes[state.tabsCursor].selectedNode = node; chooseItem(node, type); } setOptionsData(state.panes); }); const renderItem = (pane, node, index) => { var _a; const classPrefix2 = "nut-cascader-item"; const checked = ((_a = pane.selectedNode) === null || _a === void 0 ? void 0 : _a.value) === node.value; const classes = classNames({ active: checked, disabled: node.disabled }, classPrefix2); const classesTitle = classNames({ [`${classPrefix2}-title`]: true }); const renderIcon = () => { if (checked) { if (isValidElement(activeIcon)) { return activeIcon; } return React__default.createElement(Checklist, { className: `${checked ? `${classPrefix}-icon-check` : ""}` }); } return null; }; return React__default.createElement( "div", { style: { color: checked ? activeColor : "" }, className: classes, key: index, onClick: () => { chooseItem(node, false); } }, React__default.createElement("div", { className: classesTitle }, node.text), node.loading ? React__default.createElement(Loading, { color: "#969799", className: "nut-cascader-item-icon-loading" }) : renderIcon() ); }; const renderTabs = () => { return React__default.createElement( "div", { className: `${classPrefix} ${className}`, style }, React__default.createElement(Tabs, { value: tabvalue, title: () => { return optionsData.map((pane, index) => { var _a, _b; return React__default.createElement( "div", { onClick: () => { setTabvalue(pane.paneKey); state.tabsCursor = index; }, className: `nut-tabs-titles-item ${tabvalue === pane.paneKey ? "nut-tabs-titles-item-active" : ""}`, key: pane.paneKey }, React__default.createElement( "span", { className: "nut-tabs-titles-item-text" }, !state.initLoading && state.panes.length && ((_a = pane === null || pane === void 0 ? void 0 : pane.selectedNode) === null || _a === void 0 ? void 0 : _a.text), !state.initLoading && state.panes.length && !((_b = pane === null || pane === void 0 ? void 0 : pane.selectedNode) === null || _b === void 0 ? void 0 : _b.text) && `${locale.select}`, !(!state.initLoading && state.panes.length) && "Loading..." ), React__default.createElement("span", { className: "nut-tabs-titles-item-line" }) ); }); } }, !state.initLoading && state.panes.length ? optionsData.map((pane) => { var _a; return React__default.createElement( Tabs.TabPane, { key: pane.paneKey, value: pane.paneKey }, React__default.createElement("div", { className: classesPane }, (_a = pane.nodes) === null || _a === void 0 ? void 0 : _a.map((node, index) => renderItem(pane, node, index))) ); }) : React__default.createElement( Tabs.TabPane, null, React__default.createElement("div", { className: classesPane }) )) ); }; return React__default.createElement(React__default.Fragment, null, popup ? React__default.createElement(Popup, Object.assign({}, popupProps, { visible: innerVisible, position: "bottom", round: true, closeIcon, closeable, closeIconPosition, title: popup && title, left, // todo 只关闭,不处理逻辑。和popup的逻辑不一致。关闭时需要增加是否要处理回调 onOverlayClick: closePopup, onCloseIconClick: closePopup }), renderTabs()) : renderTabs()); }; const Cascader = React__default.forwardRef(InternalCascader); Cascader.displayName = "NutCascader"; export { Cascader as C };