UNPKG

@mijadesign/mjui-react-taro

Version:

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

488 lines (487 loc) 16 kB
import { _ as __rest, a as __awaiter } from "./tslib.es6-iWu3F_1J.js"; import { Loading } from "@nutui/icons-react-taro"; import classNames from "classnames"; import React, { useState, useImperativeHandle, useEffect, isValidElement } from "react"; /* empty css */ import { u as usePropsValue } from "./use-props-value-DuhZMy4U.js"; import { C as ComponentDefaults } from "./typings-DV9RBfhj.js"; import { P as Popup } from "./popup.taro-EAlqxR4w.js"; import { T as Tabs } from "./tabs.taro-BDUlIoKI.js"; const n = { size: "", classPrefix: "icon", fontClassName: "iconfont", color: "", tag: "i", onClick: (e) => { }, className: "" }; function h(e) { return e === "" ? "" : Number.isNaN(Number(e)) ? String(e) : `${e}px`; } function a(e) { const l = "selected", { size: i, classPrefix: r, color: f, tag: m, children: d, className: p, fontClassName: N, style: u, logValue: o, onClick: s, ...k } = { ...n, ...e }, C = (g) => { s && s(g); }, t = {}, c = h(i); return c && (t.fontSize = c), React.createElement( m, { className: classNames(`${N} ${r}-${l} ${p || ""}`, { "auto-track": !!o }), style: { color: f, ...t, ...u }, ...k, onClick: C, "data-log-name": o }, d ); } a.defaultProps = n; a.displayName = "FiresoonMobileIcon-selected"; 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((a2, b) => a2[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: React.createElement(a, null), popup: true, visible: false, options: [], optionKey: { textKey: "text", valueKey: "value", childrenKey: "children" }, format: {}, closeable: true, closeIconPosition: "top-right", lazy: false, onLoad: () => { }, onClose: () => { }, onChange: () => { }, onPathChange: () => { }, closeIcon: React.createElement("div", { style: { color: "#999" } }, "取消") }); const InternalCascader = (props, ref) => { const { className, style = {}, activeColor, activeIcon, popup, popupProps = {}, visible, options, value, defaultValue, optionKey, format, closeable, closeIconPosition, closeIcon, lazy, footer, 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(`mj-cascader`); const classesPane = classNames({ [`${classPrefix}-pane`]: true }); useEffect(() => { initData(); }, [options, format]); 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 || !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) { const pathNodes = state.tree.getPathNodesByValue(needToSync); if (pathNodes.length === 0) { setTabvalue(`c1`); state.tabsCursor = 0; } pathNodes.forEach((node, index) => { state.tabsCursor = index; chooseItem(node, true, false); }); } }); 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 chooseItem = (node_1, type_1, ...args_1) => __awaiter(void 0, [node_1, type_1, ...args_1], void 0, function* (node, type, isClose = true) { 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(optionParams, pathNodes); setInnerValue(optionParams); } setOptionsData(state.panes); isClose && (close === null || close === void 0 ? void 0 : close()); return; } if (state.tree.hasChildren(node, isLazy())) { const level = node.level + 1; state.panes[node.level].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(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 close = () => { setInnerVisible(false); onClose === null || onClose === void 0 ? void 0 : onClose(); }; const onClosePopup = () => { initData(); if (!(innerValue === null || innerValue === void 0 ? void 0 : innerValue.length)) { setTabvalue("c1"); } close(); }; useEffect(() => { if (!(innerValue === null || innerValue === void 0 ? void 0 : innerValue.length)) { initData(); setTabvalue("c1"); } }, [innerValue]); const renderItem = (pane, node, index) => { var _a, _b, _c; const classPrefix2 = "mj-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 null; }; return React.createElement( "div", { style: { color: checked ? activeColor : "" }, className: classes, key: index, onClick: () => { chooseItem(node, false); } }, React.createElement("div", { className: classesTitle }, (_c = (_b = node === null || node === void 0 ? void 0 : node.render) === null || _b === void 0 ? void 0 : _b.call(node, node.text, node)) !== null && _c !== void 0 ? _c : node.text), node.loading ? React.createElement(Loading, { color: "#969799", className: "mj-cascader-item__icon-loading" }) : renderIcon() ); }; const renderTabs = () => { const isLoading = !(!state.initLoading && state.panes.length); return React.createElement( "div", { className: `${classPrefix} ${className}`, style: Object.assign({}, style) }, React.createElement(Tabs, { value: tabvalue, title: () => { return optionsData.map((pane, index) => React.createElement( "div", { className: classNames("nut-tabs-titles-item", { "nut-tabs-titles-item-active": tabvalue === pane.paneKey }), key: pane.paneKey, onClick: () => { setTabvalue(pane.paneKey); state.tabsCursor = index; } }, React.createElement("span", { className: "nut-tabs-titles-item-text" }, (() => { const { text } = (pane === null || pane === void 0 ? void 0 : pane.selectedNode) || {}; if (isLoading) return "Loading..."; if (text) return text; return "请选择"; })()), React.createElement("span", { className: "nut-tabs-titles-item-line" }) )); } }, !isLoading ? optionsData.map((pane) => { var _a; return React.createElement( Tabs.TabPane, { key: pane.paneKey, value: pane.paneKey }, React.createElement("div", { className: classesPane }, (_a = pane.nodes) === null || _a === void 0 ? void 0 : _a.map((node, index) => renderItem(pane, node, index))) ); }) : React.createElement( Tabs.TabPane, null, React.createElement("div", { className: classesPane }) )) ); }; return React.createElement(React.Fragment, null, popup ? React.createElement( Popup, Object.assign({}, popupProps, { visible, position: "bottom", round: true, closeIcon, closeable, closeIconPosition, title: popup && props.title, left: props.left, // todo 只关闭,不处理逻辑。和popup的逻辑不一致。关闭时需要增加是否要处理回调 onOverlayClick: onClosePopup, onCloseIconClick: onClosePopup }), renderTabs(), !!footer && footer ) : renderTabs()); }; const Cascader = React.forwardRef(InternalCascader); Cascader.displayName = "NutCascader"; export { Cascader as C };