@canvas-components/react-components 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +516 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -4
- package/dist/index.d.ts +43 -4
- package/dist/index.js +516 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7260,8 +7260,84 @@ function clamp3(value, min, max) {
|
|
|
7260
7260
|
return Math.min(Math.max(value, min), max);
|
|
7261
7261
|
}
|
|
7262
7262
|
|
|
7263
|
+
// src/components/cascader/cascader-selection.ts
|
|
7264
|
+
function createCascaderSelection(options) {
|
|
7265
|
+
const entries = /* @__PURE__ */ new Map();
|
|
7266
|
+
const visit = (items, parents, disabled = false) => items.map((item) => {
|
|
7267
|
+
const nodes = [...parents, item];
|
|
7268
|
+
const path = nodes.map((node) => node.value);
|
|
7269
|
+
const key = JSON.stringify(path);
|
|
7270
|
+
const entry = {
|
|
7271
|
+
path,
|
|
7272
|
+
children: [],
|
|
7273
|
+
leaves: [],
|
|
7274
|
+
disabled: disabled || !!item.disabled
|
|
7275
|
+
};
|
|
7276
|
+
entries.set(key, entry);
|
|
7277
|
+
entry.children = visit(item.children ?? [], nodes, entry.disabled);
|
|
7278
|
+
entry.leaves = entry.disabled ? [] : entry.children.length ? entry.children.flatMap((child) => child.leaves) : [key];
|
|
7279
|
+
return entry;
|
|
7280
|
+
});
|
|
7281
|
+
const roots = visit(options, []);
|
|
7282
|
+
const expand = (value) => {
|
|
7283
|
+
const checked = /* @__PURE__ */ new Set();
|
|
7284
|
+
const preserved = /* @__PURE__ */ new Map();
|
|
7285
|
+
for (const path of value) {
|
|
7286
|
+
const key = JSON.stringify(path);
|
|
7287
|
+
const entry = entries.get(key);
|
|
7288
|
+
if (!entry || entry.disabled || !entry.leaves.length)
|
|
7289
|
+
preserved.set(key, path);
|
|
7290
|
+
else entry.leaves.forEach((leaf) => checked.add(leaf));
|
|
7291
|
+
}
|
|
7292
|
+
return { checked, preserved };
|
|
7293
|
+
};
|
|
7294
|
+
const state = (entry, checked) => {
|
|
7295
|
+
const count = entry?.leaves.filter((leaf) => checked.has(leaf)).length ?? 0;
|
|
7296
|
+
return {
|
|
7297
|
+
checked: count > 0 && count === entry?.leaves.length,
|
|
7298
|
+
indeterminate: count > 0 && count < (entry?.leaves.length ?? 0)
|
|
7299
|
+
};
|
|
7300
|
+
};
|
|
7301
|
+
const collect = (checked, preserved, strategy) => {
|
|
7302
|
+
const paths = [];
|
|
7303
|
+
const walk = (entry) => {
|
|
7304
|
+
if (entry.disabled) return;
|
|
7305
|
+
if (state(entry, checked).checked && (strategy === "SHOW_PARENT" || !entry.children.length))
|
|
7306
|
+
paths.push(entry.path);
|
|
7307
|
+
else entry.children.forEach(walk);
|
|
7308
|
+
};
|
|
7309
|
+
roots.forEach(walk);
|
|
7310
|
+
return [...paths, ...preserved.values()];
|
|
7311
|
+
};
|
|
7312
|
+
return {
|
|
7313
|
+
/** 归一化回填值,去重并应用父级/末级策略。 */
|
|
7314
|
+
normalize(value, strategy) {
|
|
7315
|
+
const { checked, preserved } = expand(value);
|
|
7316
|
+
return collect(checked, preserved, strategy);
|
|
7317
|
+
},
|
|
7318
|
+
/** 计算某一路径的全选和半选状态。 */
|
|
7319
|
+
getState(value, path) {
|
|
7320
|
+
const { checked, preserved } = expand(value);
|
|
7321
|
+
if (preserved.has(JSON.stringify(path)))
|
|
7322
|
+
return { checked: true, indeterminate: false };
|
|
7323
|
+
return state(entries.get(JSON.stringify(path)), checked);
|
|
7324
|
+
},
|
|
7325
|
+
/** 切换某个节点以及全部可用后代,不修改入参。 */
|
|
7326
|
+
toggle(value, path, strategy) {
|
|
7327
|
+
const entry = entries.get(JSON.stringify(path));
|
|
7328
|
+
if (!entry || entry.disabled || !entry.leaves.length) return value;
|
|
7329
|
+
const { checked, preserved } = expand(value);
|
|
7330
|
+
const remove = state(entry, checked).checked;
|
|
7331
|
+
entry.leaves.forEach(
|
|
7332
|
+
(leaf) => remove ? checked.delete(leaf) : checked.add(leaf)
|
|
7333
|
+
);
|
|
7334
|
+
return collect(checked, preserved, strategy);
|
|
7335
|
+
}
|
|
7336
|
+
};
|
|
7337
|
+
}
|
|
7338
|
+
|
|
7263
7339
|
// src/components/cascader/style.css
|
|
7264
|
-
styleInject('@import "../../styles/variables.css";\n.canvas-react-cascader {\n width: 100%;\n min-width: 0;\n}\n.canvas-react-cascader > .canvas-react-input-affix-wrapper {\n width: 100%;\n}\n.canvas-react-cascader-menus {\n display: flex;\n max-width: calc(100vw - 24px);\n overflow: auto;\n color: var(--canvas-react-color-text);\n font-size: var(--canvas-react-font-size);\n}\n.canvas-react-cascader-menu {\n flex: 0 0 auto;\n min-width: 120px;\n max-width: 320px;\n max-height: min(256px, calc(100vh - 48px));\n overflow: auto;\n padding: 4px;\n}\n.canvas-react-cascader-menu + .canvas-react-cascader-menu {\n border-left: 1px solid var(--canvas-react-color-border);\n}\n.canvas-react-cascader-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 20px;\n padding: 5px 10px;\n line-height: 22px;\n border-radius: var(--canvas-react-radius-sm);\n cursor: pointer;\n white-space: nowrap;\n}\n.canvas-react-cascader-option[aria-selected=true] {\n color: var(--canvas-react-color-primary);\n background: var(--canvas-react-color-bg-active);\n}\n.canvas-react-cascader-option[data-active=true] {\n background: var(--canvas-react-color-bg-hover, #f5f5f5);\n}\n.canvas-react-cascader-option[aria-disabled=true] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.canvas-react-cascader-empty {\n padding: 8px 10px;\n color: var(--canvas-react-color-text-muted);\n}\n.canvas-react-cascader-chevron {\n width: 7px;\n height: 7px;\n flex: 0 0 auto;\n margin-left: 8px;\n border-top: 1.5px solid #9ca3af;\n border-right: 1.5px solid #9ca3af;\n transform: rotate(45deg);\n transform-origin: center;\n transition: transform 0.18s ease, border-color 0.18s ease;\n}\n.canvas-react-cascader-option:not([aria-disabled=true]):hover .canvas-react-cascader-chevron,\n.canvas-react-cascader-option[
|
|
7340
|
+
styleInject('@import "../../styles/variables.css";\n.canvas-react-cascader {\n width: 100%;\n min-width: 0;\n}\n.canvas-react-cascader > .canvas-react-input-affix-wrapper {\n width: 100%;\n}\n.canvas-react-cascader-menus {\n display: flex;\n max-width: calc(100vw - 24px);\n overflow: auto;\n color: var(--canvas-react-color-text);\n font-size: var(--canvas-react-font-size);\n}\n.canvas-react-cascader-menu {\n flex: 0 0 auto;\n min-width: 120px;\n max-width: 320px;\n max-height: min(256px, calc(100vh - 48px));\n overflow: auto;\n padding: 4px;\n}\n.canvas-react-cascader-menu + .canvas-react-cascader-menu {\n border-left: 1px solid var(--canvas-react-color-border);\n}\n.canvas-react-cascader-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 20px;\n padding: 5px 10px;\n line-height: 22px;\n border-radius: var(--canvas-react-radius-sm);\n cursor: pointer;\n white-space: nowrap;\n}\n.canvas-react-cascader-check {\n display: inline-flex;\n flex: 0 0 auto;\n margin-right: -12px;\n}\n.canvas-react-cascader-check > * {\n pointer-events: none;\n}\n.canvas-react-cascader-option > span:not(.canvas-react-cascader-check):not(.canvas-react-cascader-chevron) {\n flex: 1;\n}\n.canvas-react-cascader-option[aria-selected=true] {\n color: var(--canvas-react-color-primary);\n background: var(--canvas-react-color-bg-active);\n}\n.canvas-react-cascader-option[data-active=true] {\n background: var(--canvas-react-color-bg-hover, #f5f5f5);\n}\n.canvas-react-cascader-option[aria-disabled=true] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.canvas-react-cascader-empty {\n padding: 8px 10px;\n color: var(--canvas-react-color-text-muted);\n}\n.canvas-react-cascader-chevron {\n width: 7px;\n height: 7px;\n flex: 0 0 auto;\n margin-left: 8px;\n border-top: 1.5px solid #9ca3af;\n border-right: 1.5px solid #9ca3af;\n transform: rotate(45deg);\n transform-origin: center;\n transition: transform 0.18s ease, border-color 0.18s ease;\n}\n.canvas-react-cascader-option:not([aria-disabled=true]):hover .canvas-react-cascader-chevron,\n.canvas-react-cascader-option[data-expanded=true] .canvas-react-cascader-chevron {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n');
|
|
7265
7341
|
function resolvePath(options, value) {
|
|
7266
7342
|
const path = [];
|
|
7267
7343
|
for (const part of value) {
|
|
@@ -7279,6 +7355,8 @@ function ReactCascader(props) {
|
|
|
7279
7355
|
value = EMPTY_VALUE,
|
|
7280
7356
|
options = EMPTY_OPTIONS,
|
|
7281
7357
|
showSearch = false,
|
|
7358
|
+
multiple = false,
|
|
7359
|
+
showCheckedStrategy = "SHOW_PARENT",
|
|
7282
7360
|
changeOnSelect = false,
|
|
7283
7361
|
expandTrigger = "click",
|
|
7284
7362
|
displayRender,
|
|
@@ -7296,7 +7374,13 @@ function ReactCascader(props) {
|
|
|
7296
7374
|
const inputRef = React.useRef(null);
|
|
7297
7375
|
const popupRef = React.useRef(null);
|
|
7298
7376
|
const popupId = React.useId();
|
|
7299
|
-
const
|
|
7377
|
+
const selection = React.useMemo(() => createCascaderSelection(options), [options]);
|
|
7378
|
+
const singleValue = multiple ? EMPTY_VALUE : value;
|
|
7379
|
+
const multipleValue = multiple ? value.filter(Array.isArray) : [];
|
|
7380
|
+
const selected = React.useMemo(
|
|
7381
|
+
() => resolvePath(options, singleValue),
|
|
7382
|
+
[options, singleValue]
|
|
7383
|
+
);
|
|
7300
7384
|
const [path, setPath] = React.useState(selected);
|
|
7301
7385
|
const [open, setOpen] = React.useState(false);
|
|
7302
7386
|
const [keyword, setKeyword] = React.useState("");
|
|
@@ -7306,10 +7390,14 @@ function ReactCascader(props) {
|
|
|
7306
7390
|
React.useEffect(() => {
|
|
7307
7391
|
setPath(selected);
|
|
7308
7392
|
}, [selected]);
|
|
7309
|
-
const
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7393
|
+
const displayPath = (valuePath) => {
|
|
7394
|
+
const nodes = resolvePath(options, valuePath);
|
|
7395
|
+
const labels = valuePath.map(
|
|
7396
|
+
(part, index) => nodes[index]?.label ?? String(part)
|
|
7397
|
+
);
|
|
7398
|
+
return displayRender?.(labels, nodes) ?? labels.join(" / ");
|
|
7399
|
+
};
|
|
7400
|
+
const text = multiple ? selection.normalize(multipleValue, showCheckedStrategy).map(displayPath).join("\uFF1B") : displayPath(singleValue);
|
|
7313
7401
|
const columns = [options];
|
|
7314
7402
|
for (const item of path) {
|
|
7315
7403
|
if (!item.children?.length) break;
|
|
@@ -7323,21 +7411,37 @@ function ReactCascader(props) {
|
|
|
7323
7411
|
for (const item of items) {
|
|
7324
7412
|
if (item.disabled) continue;
|
|
7325
7413
|
const next = [...parents, item];
|
|
7326
|
-
if ((!item.children?.length || changeOnSelect) && next.map((part) => part.label).join(" / ").toLocaleLowerCase().includes(keyword.trim().toLocaleLowerCase()))
|
|
7414
|
+
if ((!item.children?.length || changeOnSelect || multiple) && next.map((part) => part.label).join(" / ").toLocaleLowerCase().includes(keyword.trim().toLocaleLowerCase()))
|
|
7327
7415
|
matches.push(next);
|
|
7328
7416
|
if (item.children?.length) visit(item.children, next);
|
|
7329
7417
|
}
|
|
7330
7418
|
};
|
|
7331
7419
|
visit(options, []);
|
|
7332
7420
|
return matches;
|
|
7333
|
-
}, [options, keyword, changeOnSelect]);
|
|
7421
|
+
}, [options, keyword, changeOnSelect, multiple]);
|
|
7334
7422
|
React.useEffect(() => {
|
|
7335
7423
|
popupRef.current?.querySelector("[data-active='true']")?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
|
|
7336
7424
|
}, [focus, open]);
|
|
7425
|
+
const toggle = (next, trigger) => {
|
|
7426
|
+
const updated = selection.toggle(
|
|
7427
|
+
multipleValue,
|
|
7428
|
+
next.map((item) => item.value),
|
|
7429
|
+
showCheckedStrategy
|
|
7430
|
+
);
|
|
7431
|
+
onChange?.(
|
|
7432
|
+
updated,
|
|
7433
|
+
updated.map((parts) => resolvePath(options, parts)),
|
|
7434
|
+
{ trigger, complete: false }
|
|
7435
|
+
);
|
|
7436
|
+
};
|
|
7337
7437
|
const choose = (next, trigger, fromSearch = false) => {
|
|
7338
7438
|
const item = next[next.length - 1];
|
|
7339
7439
|
if (!item || next.some((option) => option.disabled)) return;
|
|
7340
7440
|
setPath(next);
|
|
7441
|
+
if (multiple) {
|
|
7442
|
+
if (!item.children?.length || fromSearch) toggle(next, trigger);
|
|
7443
|
+
return;
|
|
7444
|
+
}
|
|
7341
7445
|
const complete = !item.children?.length || fromSearch;
|
|
7342
7446
|
if (complete || changeOnSelect)
|
|
7343
7447
|
onChange?.(
|
|
@@ -7354,7 +7458,7 @@ function ReactCascader(props) {
|
|
|
7354
7458
|
const show = () => {
|
|
7355
7459
|
if (!interactive) return;
|
|
7356
7460
|
setOpen(true);
|
|
7357
|
-
setPath(selected);
|
|
7461
|
+
if (!multiple) setPath(selected);
|
|
7358
7462
|
};
|
|
7359
7463
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7360
7464
|
"div",
|
|
@@ -7406,7 +7510,7 @@ function ReactCascader(props) {
|
|
|
7406
7510
|
setTyping(false);
|
|
7407
7511
|
return;
|
|
7408
7512
|
}
|
|
7409
|
-
if (![
|
|
7513
|
+
if (!(multiple && event.key === " " && (!showSearch || !typing)) && ![
|
|
7410
7514
|
"ArrowDown",
|
|
7411
7515
|
"ArrowUp",
|
|
7412
7516
|
"ArrowRight",
|
|
@@ -7439,7 +7543,9 @@ function ReactCascader(props) {
|
|
|
7439
7543
|
const item = items[focus.index];
|
|
7440
7544
|
if (!item || item.disabled) return;
|
|
7441
7545
|
const next = searching ? results[focus.index] : [...path.slice(0, column), item];
|
|
7442
|
-
if (event.key === "Enter"
|
|
7546
|
+
if (multiple && (event.key === "Enter" || event.key === " "))
|
|
7547
|
+
toggle(next, "keyboard");
|
|
7548
|
+
else if (event.key === "Enter") choose(next, "keyboard", searching);
|
|
7443
7549
|
else if (event.key !== "ArrowRight") return;
|
|
7444
7550
|
if (!searching && item.children?.length) {
|
|
7445
7551
|
setPath(next);
|
|
@@ -7536,20 +7642,26 @@ function ReactCascader(props) {
|
|
|
7536
7642
|
"div",
|
|
7537
7643
|
{
|
|
7538
7644
|
role: "listbox",
|
|
7645
|
+
"aria-multiselectable": multiple || void 0,
|
|
7539
7646
|
"aria-label": searching ? "\u641C\u7D22\u7ED3\u679C" : `\u7B2C ${column + 1} \u7EA7\u9009\u9879`,
|
|
7540
7647
|
className: "canvas-react-cascader-menu",
|
|
7541
7648
|
children: items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-react-cascader-empty", children: "\u6682\u65E0\u6570\u636E" }) : items.map((item, index) => {
|
|
7542
7649
|
const next = searching ? results[index] : [...path.slice(0, column), item];
|
|
7543
7650
|
const active = focus.column === column && focus.index === index;
|
|
7651
|
+
const checkedState = multiple ? selection.getState(
|
|
7652
|
+
multipleValue,
|
|
7653
|
+
next.map((node) => node.value)
|
|
7654
|
+
) : { checked: false, indeterminate: false };
|
|
7544
7655
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7545
7656
|
"div",
|
|
7546
7657
|
{
|
|
7547
7658
|
id: `${popupId}-${column}-${index}`,
|
|
7548
7659
|
role: "option",
|
|
7549
|
-
"aria-selected": path[column] === item,
|
|
7660
|
+
"aria-selected": multiple ? checkedState.checked : path[column] === item,
|
|
7550
7661
|
"aria-disabled": !!item.disabled,
|
|
7551
7662
|
"data-active": active,
|
|
7552
7663
|
className: "canvas-react-cascader-option",
|
|
7664
|
+
"data-expanded": !!item.children?.length && path[column] === item,
|
|
7553
7665
|
onMouseDown: (event) => event.preventDefault(),
|
|
7554
7666
|
onMouseEnter: () => {
|
|
7555
7667
|
if (item.disabled) return;
|
|
@@ -7562,6 +7674,32 @@ function ReactCascader(props) {
|
|
|
7562
7674
|
choose(next, "mouse", searching);
|
|
7563
7675
|
},
|
|
7564
7676
|
children: [
|
|
7677
|
+
multiple ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7678
|
+
"span",
|
|
7679
|
+
{
|
|
7680
|
+
className: "canvas-react-cascader-check",
|
|
7681
|
+
role: "checkbox",
|
|
7682
|
+
"aria-checked": checkedState.indeterminate ? "mixed" : checkedState.checked,
|
|
7683
|
+
"aria-label": `\u9009\u62E9 ${next.map((node) => node.label).join(" / ")}`,
|
|
7684
|
+
"aria-disabled": !!item.disabled,
|
|
7685
|
+
onClick: (event) => {
|
|
7686
|
+
event.preventDefault();
|
|
7687
|
+
event.stopPropagation();
|
|
7688
|
+
if (!item.disabled) toggle(next, "mouse");
|
|
7689
|
+
},
|
|
7690
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7691
|
+
ReactCheckbox,
|
|
7692
|
+
{
|
|
7693
|
+
tabIndex: -1,
|
|
7694
|
+
"aria-hidden": "true",
|
|
7695
|
+
readOnly: true,
|
|
7696
|
+
checked: checkedState.checked,
|
|
7697
|
+
indeterminate: checkedState.indeterminate,
|
|
7698
|
+
disabled: item.disabled
|
|
7699
|
+
}
|
|
7700
|
+
)
|
|
7701
|
+
}
|
|
7702
|
+
) : null,
|
|
7565
7703
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: searching ? next.map((part) => part.label).join(" / ") : item.label }),
|
|
7566
7704
|
!searching && item.children?.length ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7567
7705
|
"span",
|
|
@@ -7586,6 +7724,371 @@ function ReactCascader(props) {
|
|
|
7586
7724
|
}
|
|
7587
7725
|
);
|
|
7588
7726
|
}
|
|
7727
|
+
|
|
7728
|
+
// src/components/tree-select/tree-select-utils.ts
|
|
7729
|
+
function createTreeSelectIndex(treeData) {
|
|
7730
|
+
const entries = [];
|
|
7731
|
+
const byValue = /* @__PURE__ */ new Map();
|
|
7732
|
+
const visit = (nodes, parents) => {
|
|
7733
|
+
for (const node of nodes) {
|
|
7734
|
+
const entry = {
|
|
7735
|
+
node,
|
|
7736
|
+
key: node.key ?? node.value,
|
|
7737
|
+
parents,
|
|
7738
|
+
depth: parents.length
|
|
7739
|
+
};
|
|
7740
|
+
entries.push(entry);
|
|
7741
|
+
byValue.set(node.value, entry);
|
|
7742
|
+
if (node.children?.length) visit(node.children, [...parents, entry]);
|
|
7743
|
+
}
|
|
7744
|
+
};
|
|
7745
|
+
visit(treeData, []);
|
|
7746
|
+
return { entries, byValue };
|
|
7747
|
+
}
|
|
7748
|
+
function normalizeTreeSelectValue(value, multiple) {
|
|
7749
|
+
const values = multiple ? Array.isArray(value) ? value : [] : [value];
|
|
7750
|
+
return [
|
|
7751
|
+
...new Set(
|
|
7752
|
+
values.filter(
|
|
7753
|
+
(part) => typeof part === "string" || typeof part === "number"
|
|
7754
|
+
)
|
|
7755
|
+
)
|
|
7756
|
+
];
|
|
7757
|
+
}
|
|
7758
|
+
|
|
7759
|
+
// src/components/tree-select/style.css
|
|
7760
|
+
styleInject('@import "../../styles/variables.css";\n.canvas-react-tree-select {\n width: 100%;\n min-width: 0;\n}\n.canvas-react-tree-select-tree {\n max-height: min(280px, calc(100vh - 48px));\n overflow: auto;\n padding: 4px;\n color: var(--canvas-react-color-text);\n font-size: var(--canvas-react-font-size);\n}\n.canvas-react-tree-select-node {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 30px;\n padding: 2px 10px 2px 6px;\n box-sizing: border-box;\n line-height: 22px;\n border-radius: var(--canvas-react-radius-sm);\n cursor: pointer;\n}\n.canvas-react-tree-select-node[data-active=true] {\n background: var(--canvas-react-color-bg-hover);\n}\n.canvas-react-tree-select-node[aria-selected=true] {\n background: var(--canvas-react-color-bg-active);\n color: var(--canvas-react-color-primary);\n}\n.canvas-react-tree-select-node[aria-disabled=true] .canvas-react-tree-select-title {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.canvas-react-tree-select-switcher {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 18px;\n width: 18px;\n height: 24px;\n border: 0;\n padding: 0;\n background: transparent;\n cursor: pointer;\n}\n.canvas-react-tree-select-chevron {\n width: 7px;\n height: 7px;\n border-top: 1.5px solid #9ca3af;\n border-right: 1.5px solid #9ca3af;\n transform: rotate(45deg);\n transition: transform 0.18s ease, border-color 0.18s ease;\n pointer-events: none;\n}\n.canvas-react-tree-select-chevron-open {\n transform: rotate(135deg);\n border-color: #6b7280;\n}\n.canvas-react-tree-select-title {\n flex: 1;\n white-space: nowrap;\n}\n.canvas-react-tree-select-check {\n color: var(--canvas-react-color-primary);\n}\n.canvas-react-tree-select-empty {\n padding: 8px 10px;\n color: var(--canvas-react-color-text-muted);\n}\n.canvas-react-tree-select:hover .canvas-react-select-clear,\n.canvas-react-tree-select-open .canvas-react-select-clear,\n.canvas-react-tree-select:focus-within .canvas-react-select-clear {\n opacity: 1;\n pointer-events: auto;\n}\n.canvas-react-tree-select:hover .canvas-react-select-actions-clearable .canvas-react-select-arrow,\n.canvas-react-tree-select-open .canvas-react-select-actions-clearable .canvas-react-select-arrow,\n.canvas-react-tree-select:focus-within .canvas-react-select-actions-clearable .canvas-react-select-arrow {\n opacity: 0;\n}\n');
|
|
7761
|
+
var EMPTY_TREE = [];
|
|
7762
|
+
function ReactTreeSelect(props) {
|
|
7763
|
+
const {
|
|
7764
|
+
value = null,
|
|
7765
|
+
treeData = EMPTY_TREE,
|
|
7766
|
+
multiple = false,
|
|
7767
|
+
showSearch = multiple,
|
|
7768
|
+
treeDefaultExpandAll = false,
|
|
7769
|
+
treeDefaultExpandedKeys,
|
|
7770
|
+
allowClear = true,
|
|
7771
|
+
onChange,
|
|
7772
|
+
disabled,
|
|
7773
|
+
readOnly,
|
|
7774
|
+
onFocus,
|
|
7775
|
+
onBlur,
|
|
7776
|
+
onClick,
|
|
7777
|
+
onKeyDown,
|
|
7778
|
+
...inputProps
|
|
7779
|
+
} = props;
|
|
7780
|
+
const index = React.useMemo(() => createTreeSelectIndex(treeData), [treeData]);
|
|
7781
|
+
const selected = React.useMemo(
|
|
7782
|
+
() => normalizeTreeSelectValue(value, multiple),
|
|
7783
|
+
[value, multiple]
|
|
7784
|
+
);
|
|
7785
|
+
const selectedSet = React.useMemo(() => new Set(selected), [selected]);
|
|
7786
|
+
const [expanded, setExpanded] = React.useState(
|
|
7787
|
+
() => /* @__PURE__ */ new Set([
|
|
7788
|
+
...treeDefaultExpandAll ? index.entries.filter((entry) => entry.node.children?.length).map((entry) => entry.key) : treeDefaultExpandedKeys ?? [],
|
|
7789
|
+
...selected.flatMap(
|
|
7790
|
+
(part) => index.byValue.get(part)?.parents.map((parent) => parent.key) ?? []
|
|
7791
|
+
)
|
|
7792
|
+
])
|
|
7793
|
+
);
|
|
7794
|
+
const [open, setOpen] = React.useState(false);
|
|
7795
|
+
const [keyword, setKeyword] = React.useState(null);
|
|
7796
|
+
const [active, setActive] = React.useState(
|
|
7797
|
+
selected[0] ?? null
|
|
7798
|
+
);
|
|
7799
|
+
const anchorRef = React.useRef(null);
|
|
7800
|
+
const inputRef = React.useRef(null);
|
|
7801
|
+
const treeRef = React.useRef(null);
|
|
7802
|
+
const treeId = React.useId();
|
|
7803
|
+
const interactive = !disabled && !readOnly;
|
|
7804
|
+
const query = (keyword ?? "").trim().toLocaleLowerCase();
|
|
7805
|
+
const text = selected.map((part) => index.byValue.get(part)?.node.title ?? String(part)).join("\uFF1B");
|
|
7806
|
+
const visible = React.useMemo(() => {
|
|
7807
|
+
if (!query)
|
|
7808
|
+
return index.entries.filter(
|
|
7809
|
+
(entry) => entry.parents.every((parent) => expanded.has(parent.key))
|
|
7810
|
+
);
|
|
7811
|
+
const matches = /* @__PURE__ */ new Set();
|
|
7812
|
+
for (const entry of index.entries) {
|
|
7813
|
+
if (!entry.node.title.toLocaleLowerCase().includes(query)) continue;
|
|
7814
|
+
matches.add(entry);
|
|
7815
|
+
entry.parents.forEach((parent) => matches.add(parent));
|
|
7816
|
+
}
|
|
7817
|
+
return index.entries.filter((entry) => matches.has(entry));
|
|
7818
|
+
}, [index, query, expanded]);
|
|
7819
|
+
React.useEffect(() => {
|
|
7820
|
+
setExpanded((previous) => {
|
|
7821
|
+
const next = new Set(previous);
|
|
7822
|
+
selected.forEach(
|
|
7823
|
+
(part) => index.byValue.get(part)?.parents.forEach((parent) => next.add(parent.key))
|
|
7824
|
+
);
|
|
7825
|
+
return next;
|
|
7826
|
+
});
|
|
7827
|
+
}, [index, selected]);
|
|
7828
|
+
React.useEffect(() => {
|
|
7829
|
+
treeRef.current?.querySelector('[data-active="true"]')?.scrollIntoView?.({ block: "nearest" });
|
|
7830
|
+
}, [active, open]);
|
|
7831
|
+
const expand = (entry, next) => {
|
|
7832
|
+
if (!entry.node.children?.length || query) return;
|
|
7833
|
+
setExpanded((previous) => {
|
|
7834
|
+
const keys = new Set(previous);
|
|
7835
|
+
if (next ?? !keys.has(entry.key)) keys.add(entry.key);
|
|
7836
|
+
else keys.delete(entry.key);
|
|
7837
|
+
return keys;
|
|
7838
|
+
});
|
|
7839
|
+
};
|
|
7840
|
+
const choose = (entry, trigger) => {
|
|
7841
|
+
if (entry.node.disabled) return;
|
|
7842
|
+
if (entry.node.selectable === false) {
|
|
7843
|
+
expand(entry);
|
|
7844
|
+
return;
|
|
7845
|
+
}
|
|
7846
|
+
const next = multiple ? selectedSet.has(entry.node.value) ? selected.filter((part) => part !== entry.node.value) : [...selected, entry.node.value] : [entry.node.value];
|
|
7847
|
+
onChange?.(
|
|
7848
|
+
multiple ? next : entry.node.value,
|
|
7849
|
+
next.flatMap((part) => index.byValue.get(part)?.node ?? []),
|
|
7850
|
+
{ trigger, complete: !multiple }
|
|
7851
|
+
);
|
|
7852
|
+
if (!multiple) {
|
|
7853
|
+
setOpen(false);
|
|
7854
|
+
setKeyword(null);
|
|
7855
|
+
}
|
|
7856
|
+
};
|
|
7857
|
+
const show = () => {
|
|
7858
|
+
if (interactive) setOpen(true);
|
|
7859
|
+
};
|
|
7860
|
+
const activeIndex = visible.findIndex((entry) => entry.node.value === active);
|
|
7861
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7862
|
+
"div",
|
|
7863
|
+
{
|
|
7864
|
+
ref: anchorRef,
|
|
7865
|
+
className: `canvas-react-tree-select${open && interactive ? " canvas-react-tree-select-open" : ""}`,
|
|
7866
|
+
children: [
|
|
7867
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7868
|
+
ReactInput,
|
|
7869
|
+
{
|
|
7870
|
+
...inputProps,
|
|
7871
|
+
ref: inputRef,
|
|
7872
|
+
disabled,
|
|
7873
|
+
readOnly: readOnly || !showSearch,
|
|
7874
|
+
allowClear: false,
|
|
7875
|
+
role: "combobox",
|
|
7876
|
+
"aria-haspopup": "tree",
|
|
7877
|
+
"aria-expanded": open && interactive,
|
|
7878
|
+
"aria-controls": open ? treeId : void 0,
|
|
7879
|
+
"aria-activedescendant": open && activeIndex >= 0 ? `${treeId}-${activeIndex}` : void 0,
|
|
7880
|
+
value: open && keyword !== null ? keyword : text,
|
|
7881
|
+
onFocus: (event) => {
|
|
7882
|
+
show();
|
|
7883
|
+
onFocus?.(event);
|
|
7884
|
+
},
|
|
7885
|
+
onClick: (event) => {
|
|
7886
|
+
show();
|
|
7887
|
+
onClick?.(event);
|
|
7888
|
+
},
|
|
7889
|
+
onBlur: (event) => {
|
|
7890
|
+
setOpen(false);
|
|
7891
|
+
setKeyword(null);
|
|
7892
|
+
onBlur?.(event);
|
|
7893
|
+
},
|
|
7894
|
+
onChange: (event) => {
|
|
7895
|
+
setKeyword(event.target.value);
|
|
7896
|
+
setActive(null);
|
|
7897
|
+
show();
|
|
7898
|
+
},
|
|
7899
|
+
onKeyDown: (event) => {
|
|
7900
|
+
onKeyDown?.(event);
|
|
7901
|
+
if (event.defaultPrevented || !interactive) return;
|
|
7902
|
+
if (event.key === "Escape") {
|
|
7903
|
+
event.preventDefault();
|
|
7904
|
+
setOpen(false);
|
|
7905
|
+
setKeyword(null);
|
|
7906
|
+
return;
|
|
7907
|
+
}
|
|
7908
|
+
const space = event.key === " " && (!showSearch || keyword === null);
|
|
7909
|
+
if (!space && ![
|
|
7910
|
+
"ArrowDown",
|
|
7911
|
+
"ArrowUp",
|
|
7912
|
+
"ArrowLeft",
|
|
7913
|
+
"ArrowRight",
|
|
7914
|
+
"Enter",
|
|
7915
|
+
"Home",
|
|
7916
|
+
"End"
|
|
7917
|
+
].includes(event.key))
|
|
7918
|
+
return;
|
|
7919
|
+
event.preventDefault();
|
|
7920
|
+
show();
|
|
7921
|
+
if (!visible.length) return;
|
|
7922
|
+
const entry = visible[activeIndex];
|
|
7923
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
7924
|
+
const next = activeIndex < 0 ? event.key === "ArrowDown" ? 0 : visible.length - 1 : (activeIndex + (event.key === "ArrowDown" ? 1 : -1) + visible.length) % visible.length;
|
|
7925
|
+
setActive(visible[next].node.value);
|
|
7926
|
+
} else if (event.key === "Home" || event.key === "End")
|
|
7927
|
+
setActive(
|
|
7928
|
+
visible[event.key === "Home" ? 0 : visible.length - 1].node.value
|
|
7929
|
+
);
|
|
7930
|
+
else if (entry) {
|
|
7931
|
+
if (event.key === "ArrowRight") {
|
|
7932
|
+
if (entry.node.children?.length && !expanded.has(entry.key) && !query)
|
|
7933
|
+
expand(entry, true);
|
|
7934
|
+
else if (visible[activeIndex + 1]?.depth > entry.depth)
|
|
7935
|
+
setActive(visible[activeIndex + 1].node.value);
|
|
7936
|
+
} else if (event.key === "ArrowLeft") {
|
|
7937
|
+
if (expanded.has(entry.key) && !query) expand(entry, false);
|
|
7938
|
+
else
|
|
7939
|
+
setActive(
|
|
7940
|
+
entry.parents[entry.parents.length - 1]?.node.value ?? entry.node.value
|
|
7941
|
+
);
|
|
7942
|
+
} else choose(entry, "keyboard");
|
|
7943
|
+
}
|
|
7944
|
+
},
|
|
7945
|
+
suffix: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7946
|
+
"span",
|
|
7947
|
+
{
|
|
7948
|
+
className: `canvas-react-select-actions${allowClear && interactive && selected.length ? " canvas-react-select-actions-clearable" : ""}`,
|
|
7949
|
+
onPointerDown: (event) => {
|
|
7950
|
+
if (interactive) {
|
|
7951
|
+
event.preventDefault();
|
|
7952
|
+
event.stopPropagation();
|
|
7953
|
+
}
|
|
7954
|
+
},
|
|
7955
|
+
onMouseDown: (event) => {
|
|
7956
|
+
if (interactive) {
|
|
7957
|
+
event.preventDefault();
|
|
7958
|
+
event.stopPropagation();
|
|
7959
|
+
}
|
|
7960
|
+
},
|
|
7961
|
+
onClick: (event) => {
|
|
7962
|
+
event.stopPropagation();
|
|
7963
|
+
if (interactive) {
|
|
7964
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
7965
|
+
show();
|
|
7966
|
+
}
|
|
7967
|
+
},
|
|
7968
|
+
children: [
|
|
7969
|
+
allowClear && interactive && selected.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7970
|
+
"button",
|
|
7971
|
+
{
|
|
7972
|
+
type: "button",
|
|
7973
|
+
tabIndex: -1,
|
|
7974
|
+
"aria-label": "\u6E05\u7A7A\u6811\u9009\u62E9",
|
|
7975
|
+
className: "canvas-react-select-clear",
|
|
7976
|
+
onClick: (event) => {
|
|
7977
|
+
event.preventDefault();
|
|
7978
|
+
event.stopPropagation();
|
|
7979
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
7980
|
+
setKeyword(null);
|
|
7981
|
+
setActive(null);
|
|
7982
|
+
setOpen(true);
|
|
7983
|
+
onChange?.(multiple ? [] : null, [], {
|
|
7984
|
+
trigger: "clear",
|
|
7985
|
+
complete: false
|
|
7986
|
+
});
|
|
7987
|
+
},
|
|
7988
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7989
|
+
"span",
|
|
7990
|
+
{
|
|
7991
|
+
className: "canvas-react-select-clear-icon",
|
|
7992
|
+
"aria-hidden": "true"
|
|
7993
|
+
}
|
|
7994
|
+
)
|
|
7995
|
+
}
|
|
7996
|
+
) : null,
|
|
7997
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7998
|
+
"span",
|
|
7999
|
+
{
|
|
8000
|
+
className: `canvas-react-select-arrow${open ? " canvas-react-select-arrow-open" : ""}`,
|
|
8001
|
+
"aria-hidden": "true"
|
|
8002
|
+
}
|
|
8003
|
+
)
|
|
8004
|
+
]
|
|
8005
|
+
}
|
|
8006
|
+
)
|
|
8007
|
+
}
|
|
8008
|
+
),
|
|
8009
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8010
|
+
ReactPopup,
|
|
8011
|
+
{
|
|
8012
|
+
open: open && interactive,
|
|
8013
|
+
anchorRef,
|
|
8014
|
+
onOpenChange: setOpen,
|
|
8015
|
+
matchAnchorWidth: true,
|
|
8016
|
+
constrainHeight: true,
|
|
8017
|
+
surfaceClassName: "canvas-react-tree-select-popup",
|
|
8018
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8019
|
+
"div",
|
|
8020
|
+
{
|
|
8021
|
+
ref: treeRef,
|
|
8022
|
+
id: treeId,
|
|
8023
|
+
role: "tree",
|
|
8024
|
+
"aria-label": "\u6811\u9009\u62E9\u9009\u9879",
|
|
8025
|
+
"aria-multiselectable": multiple || void 0,
|
|
8026
|
+
className: "canvas-react-tree-select-tree",
|
|
8027
|
+
children: visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-react-tree-select-empty", children: "\u6682\u65E0\u6570\u636E" }) : visible.map((entry, position) => {
|
|
8028
|
+
const isExpanded = !!query || expanded.has(entry.key);
|
|
8029
|
+
const hasChildren = !!entry.node.children?.length;
|
|
8030
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8031
|
+
"div",
|
|
8032
|
+
{
|
|
8033
|
+
id: `${treeId}-${position}`,
|
|
8034
|
+
role: "treeitem",
|
|
8035
|
+
"aria-level": entry.depth + 1,
|
|
8036
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
8037
|
+
"aria-selected": selectedSet.has(entry.node.value),
|
|
8038
|
+
"aria-disabled": !!entry.node.disabled,
|
|
8039
|
+
"data-active": entry.node.value === active,
|
|
8040
|
+
className: "canvas-react-tree-select-node",
|
|
8041
|
+
style: { paddingLeft: 6 + entry.depth * 18 },
|
|
8042
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
8043
|
+
onMouseEnter: () => setActive(entry.node.value),
|
|
8044
|
+
onClick: () => {
|
|
8045
|
+
setActive(entry.node.value);
|
|
8046
|
+
choose(entry, "mouse");
|
|
8047
|
+
},
|
|
8048
|
+
children: [
|
|
8049
|
+
hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8050
|
+
"button",
|
|
8051
|
+
{
|
|
8052
|
+
type: "button",
|
|
8053
|
+
tabIndex: -1,
|
|
8054
|
+
"aria-label": `${isExpanded ? "\u6536\u8D77" : "\u5C55\u5F00"} ${entry.node.title}`,
|
|
8055
|
+
className: "canvas-react-tree-select-switcher",
|
|
8056
|
+
onClick: (event) => {
|
|
8057
|
+
event.preventDefault();
|
|
8058
|
+
event.stopPropagation();
|
|
8059
|
+
expand(entry);
|
|
8060
|
+
},
|
|
8061
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8062
|
+
"span",
|
|
8063
|
+
{
|
|
8064
|
+
className: `canvas-react-tree-select-chevron${isExpanded ? " canvas-react-tree-select-chevron-open" : ""}`,
|
|
8065
|
+
"aria-hidden": "true"
|
|
8066
|
+
}
|
|
8067
|
+
)
|
|
8068
|
+
}
|
|
8069
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "canvas-react-tree-select-switcher" }),
|
|
8070
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "canvas-react-tree-select-title", children: entry.node.title }),
|
|
8071
|
+
multiple && selectedSet.has(entry.node.value) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8072
|
+
"span",
|
|
8073
|
+
{
|
|
8074
|
+
className: "canvas-react-tree-select-check",
|
|
8075
|
+
"aria-hidden": "true",
|
|
8076
|
+
children: "\u2713"
|
|
8077
|
+
}
|
|
8078
|
+
) : null
|
|
8079
|
+
]
|
|
8080
|
+
},
|
|
8081
|
+
JSON.stringify(entry.node.value)
|
|
8082
|
+
);
|
|
8083
|
+
})
|
|
8084
|
+
}
|
|
8085
|
+
)
|
|
8086
|
+
}
|
|
8087
|
+
)
|
|
8088
|
+
]
|
|
8089
|
+
}
|
|
8090
|
+
);
|
|
8091
|
+
}
|
|
7589
8092
|
var ReactAutoComplete = React.forwardRef(function ReactAutoComplete2(props, ref) {
|
|
7590
8093
|
const {
|
|
7591
8094
|
onChange,
|
|
@@ -8908,6 +9411,7 @@ exports.ReactTimePicker = ReactTimePicker;
|
|
|
8908
9411
|
exports.ReactTimeRangePicker = ReactTimeRangePicker;
|
|
8909
9412
|
exports.ReactTooltip = ReactTooltip;
|
|
8910
9413
|
exports.ReactTransition = ReactTransition;
|
|
9414
|
+
exports.ReactTreeSelect = ReactTreeSelect;
|
|
8911
9415
|
exports.ReactVirtualList = ReactVirtualList;
|
|
8912
9416
|
exports.ReactYearPicker = ReactYearPicker;
|
|
8913
9417
|
exports.reactMessage = reactMessage;
|