@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 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[aria-selected=true] .canvas-react-cascader-chevron {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n');
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 selected = React.useMemo(() => resolvePath(options, value), [options, value]);
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 labels = value.map(
7310
- (part, index) => selected[index]?.label ?? String(part)
7311
- );
7312
- const text = displayRender?.(labels, selected) ?? labels.join(" / ");
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") choose(next, "keyboard", searching);
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;