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