@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.js CHANGED
@@ -7239,8 +7239,84 @@ function clamp3(value, min, max) {
7239
7239
  return Math.min(Math.max(value, min), max);
7240
7240
  }
7241
7241
 
7242
+ // src/components/cascader/cascader-selection.ts
7243
+ function createCascaderSelection(options) {
7244
+ const entries = /* @__PURE__ */ new Map();
7245
+ const visit = (items, parents, disabled = false) => items.map((item) => {
7246
+ const nodes = [...parents, item];
7247
+ const path = nodes.map((node) => node.value);
7248
+ const key = JSON.stringify(path);
7249
+ const entry = {
7250
+ path,
7251
+ children: [],
7252
+ leaves: [],
7253
+ disabled: disabled || !!item.disabled
7254
+ };
7255
+ entries.set(key, entry);
7256
+ entry.children = visit(item.children ?? [], nodes, entry.disabled);
7257
+ entry.leaves = entry.disabled ? [] : entry.children.length ? entry.children.flatMap((child) => child.leaves) : [key];
7258
+ return entry;
7259
+ });
7260
+ const roots = visit(options, []);
7261
+ const expand = (value) => {
7262
+ const checked = /* @__PURE__ */ new Set();
7263
+ const preserved = /* @__PURE__ */ new Map();
7264
+ for (const path of value) {
7265
+ const key = JSON.stringify(path);
7266
+ const entry = entries.get(key);
7267
+ if (!entry || entry.disabled || !entry.leaves.length)
7268
+ preserved.set(key, path);
7269
+ else entry.leaves.forEach((leaf) => checked.add(leaf));
7270
+ }
7271
+ return { checked, preserved };
7272
+ };
7273
+ const state = (entry, checked) => {
7274
+ const count = entry?.leaves.filter((leaf) => checked.has(leaf)).length ?? 0;
7275
+ return {
7276
+ checked: count > 0 && count === entry?.leaves.length,
7277
+ indeterminate: count > 0 && count < (entry?.leaves.length ?? 0)
7278
+ };
7279
+ };
7280
+ const collect = (checked, preserved, strategy) => {
7281
+ const paths = [];
7282
+ const walk = (entry) => {
7283
+ if (entry.disabled) return;
7284
+ if (state(entry, checked).checked && (strategy === "SHOW_PARENT" || !entry.children.length))
7285
+ paths.push(entry.path);
7286
+ else entry.children.forEach(walk);
7287
+ };
7288
+ roots.forEach(walk);
7289
+ return [...paths, ...preserved.values()];
7290
+ };
7291
+ return {
7292
+ /** 归一化回填值,去重并应用父级/末级策略。 */
7293
+ normalize(value, strategy) {
7294
+ const { checked, preserved } = expand(value);
7295
+ return collect(checked, preserved, strategy);
7296
+ },
7297
+ /** 计算某一路径的全选和半选状态。 */
7298
+ getState(value, path) {
7299
+ const { checked, preserved } = expand(value);
7300
+ if (preserved.has(JSON.stringify(path)))
7301
+ return { checked: true, indeterminate: false };
7302
+ return state(entries.get(JSON.stringify(path)), checked);
7303
+ },
7304
+ /** 切换某个节点以及全部可用后代,不修改入参。 */
7305
+ toggle(value, path, strategy) {
7306
+ const entry = entries.get(JSON.stringify(path));
7307
+ if (!entry || entry.disabled || !entry.leaves.length) return value;
7308
+ const { checked, preserved } = expand(value);
7309
+ const remove = state(entry, checked).checked;
7310
+ entry.leaves.forEach(
7311
+ (leaf) => remove ? checked.delete(leaf) : checked.add(leaf)
7312
+ );
7313
+ return collect(checked, preserved, strategy);
7314
+ }
7315
+ };
7316
+ }
7317
+
7242
7318
  // src/components/cascader/style.css
7243
- 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');
7319
+ 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');
7244
7320
  function resolvePath(options, value) {
7245
7321
  const path = [];
7246
7322
  for (const part of value) {
@@ -7258,6 +7334,8 @@ function ReactCascader(props) {
7258
7334
  value = EMPTY_VALUE,
7259
7335
  options = EMPTY_OPTIONS,
7260
7336
  showSearch = false,
7337
+ multiple = false,
7338
+ showCheckedStrategy = "SHOW_PARENT",
7261
7339
  changeOnSelect = false,
7262
7340
  expandTrigger = "click",
7263
7341
  displayRender,
@@ -7275,7 +7353,13 @@ function ReactCascader(props) {
7275
7353
  const inputRef = useRef(null);
7276
7354
  const popupRef = useRef(null);
7277
7355
  const popupId = useId();
7278
- const selected = useMemo(() => resolvePath(options, value), [options, value]);
7356
+ const selection = useMemo(() => createCascaderSelection(options), [options]);
7357
+ const singleValue = multiple ? EMPTY_VALUE : value;
7358
+ const multipleValue = multiple ? value.filter(Array.isArray) : [];
7359
+ const selected = useMemo(
7360
+ () => resolvePath(options, singleValue),
7361
+ [options, singleValue]
7362
+ );
7279
7363
  const [path, setPath] = useState(selected);
7280
7364
  const [open, setOpen] = useState(false);
7281
7365
  const [keyword, setKeyword] = useState("");
@@ -7285,10 +7369,14 @@ function ReactCascader(props) {
7285
7369
  useEffect(() => {
7286
7370
  setPath(selected);
7287
7371
  }, [selected]);
7288
- const labels = value.map(
7289
- (part, index) => selected[index]?.label ?? String(part)
7290
- );
7291
- const text = displayRender?.(labels, selected) ?? labels.join(" / ");
7372
+ const displayPath = (valuePath) => {
7373
+ const nodes = resolvePath(options, valuePath);
7374
+ const labels = valuePath.map(
7375
+ (part, index) => nodes[index]?.label ?? String(part)
7376
+ );
7377
+ return displayRender?.(labels, nodes) ?? labels.join(" / ");
7378
+ };
7379
+ const text = multiple ? selection.normalize(multipleValue, showCheckedStrategy).map(displayPath).join("\uFF1B") : displayPath(singleValue);
7292
7380
  const columns = [options];
7293
7381
  for (const item of path) {
7294
7382
  if (!item.children?.length) break;
@@ -7302,21 +7390,37 @@ function ReactCascader(props) {
7302
7390
  for (const item of items) {
7303
7391
  if (item.disabled) continue;
7304
7392
  const next = [...parents, item];
7305
- if ((!item.children?.length || changeOnSelect) && next.map((part) => part.label).join(" / ").toLocaleLowerCase().includes(keyword.trim().toLocaleLowerCase()))
7393
+ if ((!item.children?.length || changeOnSelect || multiple) && next.map((part) => part.label).join(" / ").toLocaleLowerCase().includes(keyword.trim().toLocaleLowerCase()))
7306
7394
  matches.push(next);
7307
7395
  if (item.children?.length) visit(item.children, next);
7308
7396
  }
7309
7397
  };
7310
7398
  visit(options, []);
7311
7399
  return matches;
7312
- }, [options, keyword, changeOnSelect]);
7400
+ }, [options, keyword, changeOnSelect, multiple]);
7313
7401
  useEffect(() => {
7314
7402
  popupRef.current?.querySelector("[data-active='true']")?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
7315
7403
  }, [focus, open]);
7404
+ const toggle = (next, trigger) => {
7405
+ const updated = selection.toggle(
7406
+ multipleValue,
7407
+ next.map((item) => item.value),
7408
+ showCheckedStrategy
7409
+ );
7410
+ onChange?.(
7411
+ updated,
7412
+ updated.map((parts) => resolvePath(options, parts)),
7413
+ { trigger, complete: false }
7414
+ );
7415
+ };
7316
7416
  const choose = (next, trigger, fromSearch = false) => {
7317
7417
  const item = next[next.length - 1];
7318
7418
  if (!item || next.some((option) => option.disabled)) return;
7319
7419
  setPath(next);
7420
+ if (multiple) {
7421
+ if (!item.children?.length || fromSearch) toggle(next, trigger);
7422
+ return;
7423
+ }
7320
7424
  const complete = !item.children?.length || fromSearch;
7321
7425
  if (complete || changeOnSelect)
7322
7426
  onChange?.(
@@ -7333,7 +7437,7 @@ function ReactCascader(props) {
7333
7437
  const show = () => {
7334
7438
  if (!interactive) return;
7335
7439
  setOpen(true);
7336
- setPath(selected);
7440
+ if (!multiple) setPath(selected);
7337
7441
  };
7338
7442
  return /* @__PURE__ */ jsxs(
7339
7443
  "div",
@@ -7385,7 +7489,7 @@ function ReactCascader(props) {
7385
7489
  setTyping(false);
7386
7490
  return;
7387
7491
  }
7388
- if (![
7492
+ if (!(multiple && event.key === " " && (!showSearch || !typing)) && ![
7389
7493
  "ArrowDown",
7390
7494
  "ArrowUp",
7391
7495
  "ArrowRight",
@@ -7418,7 +7522,9 @@ function ReactCascader(props) {
7418
7522
  const item = items[focus.index];
7419
7523
  if (!item || item.disabled) return;
7420
7524
  const next = searching ? results[focus.index] : [...path.slice(0, column), item];
7421
- if (event.key === "Enter") choose(next, "keyboard", searching);
7525
+ if (multiple && (event.key === "Enter" || event.key === " "))
7526
+ toggle(next, "keyboard");
7527
+ else if (event.key === "Enter") choose(next, "keyboard", searching);
7422
7528
  else if (event.key !== "ArrowRight") return;
7423
7529
  if (!searching && item.children?.length) {
7424
7530
  setPath(next);
@@ -7515,20 +7621,26 @@ function ReactCascader(props) {
7515
7621
  "div",
7516
7622
  {
7517
7623
  role: "listbox",
7624
+ "aria-multiselectable": multiple || void 0,
7518
7625
  "aria-label": searching ? "\u641C\u7D22\u7ED3\u679C" : `\u7B2C ${column + 1} \u7EA7\u9009\u9879`,
7519
7626
  className: "canvas-react-cascader-menu",
7520
7627
  children: items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "canvas-react-cascader-empty", children: "\u6682\u65E0\u6570\u636E" }) : items.map((item, index) => {
7521
7628
  const next = searching ? results[index] : [...path.slice(0, column), item];
7522
7629
  const active = focus.column === column && focus.index === index;
7630
+ const checkedState = multiple ? selection.getState(
7631
+ multipleValue,
7632
+ next.map((node) => node.value)
7633
+ ) : { checked: false, indeterminate: false };
7523
7634
  return /* @__PURE__ */ jsxs(
7524
7635
  "div",
7525
7636
  {
7526
7637
  id: `${popupId}-${column}-${index}`,
7527
7638
  role: "option",
7528
- "aria-selected": path[column] === item,
7639
+ "aria-selected": multiple ? checkedState.checked : path[column] === item,
7529
7640
  "aria-disabled": !!item.disabled,
7530
7641
  "data-active": active,
7531
7642
  className: "canvas-react-cascader-option",
7643
+ "data-expanded": !!item.children?.length && path[column] === item,
7532
7644
  onMouseDown: (event) => event.preventDefault(),
7533
7645
  onMouseEnter: () => {
7534
7646
  if (item.disabled) return;
@@ -7541,6 +7653,32 @@ function ReactCascader(props) {
7541
7653
  choose(next, "mouse", searching);
7542
7654
  },
7543
7655
  children: [
7656
+ multiple ? /* @__PURE__ */ jsx(
7657
+ "span",
7658
+ {
7659
+ className: "canvas-react-cascader-check",
7660
+ role: "checkbox",
7661
+ "aria-checked": checkedState.indeterminate ? "mixed" : checkedState.checked,
7662
+ "aria-label": `\u9009\u62E9 ${next.map((node) => node.label).join(" / ")}`,
7663
+ "aria-disabled": !!item.disabled,
7664
+ onClick: (event) => {
7665
+ event.preventDefault();
7666
+ event.stopPropagation();
7667
+ if (!item.disabled) toggle(next, "mouse");
7668
+ },
7669
+ children: /* @__PURE__ */ jsx(
7670
+ ReactCheckbox,
7671
+ {
7672
+ tabIndex: -1,
7673
+ "aria-hidden": "true",
7674
+ readOnly: true,
7675
+ checked: checkedState.checked,
7676
+ indeterminate: checkedState.indeterminate,
7677
+ disabled: item.disabled
7678
+ }
7679
+ )
7680
+ }
7681
+ ) : null,
7544
7682
  /* @__PURE__ */ jsx("span", { children: searching ? next.map((part) => part.label).join(" / ") : item.label }),
7545
7683
  !searching && item.children?.length ? /* @__PURE__ */ jsx(
7546
7684
  "span",
@@ -7565,6 +7703,371 @@ function ReactCascader(props) {
7565
7703
  }
7566
7704
  );
7567
7705
  }
7706
+
7707
+ // src/components/tree-select/tree-select-utils.ts
7708
+ function createTreeSelectIndex(treeData) {
7709
+ const entries = [];
7710
+ const byValue = /* @__PURE__ */ new Map();
7711
+ const visit = (nodes, parents) => {
7712
+ for (const node of nodes) {
7713
+ const entry = {
7714
+ node,
7715
+ key: node.key ?? node.value,
7716
+ parents,
7717
+ depth: parents.length
7718
+ };
7719
+ entries.push(entry);
7720
+ byValue.set(node.value, entry);
7721
+ if (node.children?.length) visit(node.children, [...parents, entry]);
7722
+ }
7723
+ };
7724
+ visit(treeData, []);
7725
+ return { entries, byValue };
7726
+ }
7727
+ function normalizeTreeSelectValue(value, multiple) {
7728
+ const values = multiple ? Array.isArray(value) ? value : [] : [value];
7729
+ return [
7730
+ ...new Set(
7731
+ values.filter(
7732
+ (part) => typeof part === "string" || typeof part === "number"
7733
+ )
7734
+ )
7735
+ ];
7736
+ }
7737
+
7738
+ // src/components/tree-select/style.css
7739
+ 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');
7740
+ var EMPTY_TREE = [];
7741
+ function ReactTreeSelect(props) {
7742
+ const {
7743
+ value = null,
7744
+ treeData = EMPTY_TREE,
7745
+ multiple = false,
7746
+ showSearch = multiple,
7747
+ treeDefaultExpandAll = false,
7748
+ treeDefaultExpandedKeys,
7749
+ allowClear = true,
7750
+ onChange,
7751
+ disabled,
7752
+ readOnly,
7753
+ onFocus,
7754
+ onBlur,
7755
+ onClick,
7756
+ onKeyDown,
7757
+ ...inputProps
7758
+ } = props;
7759
+ const index = useMemo(() => createTreeSelectIndex(treeData), [treeData]);
7760
+ const selected = useMemo(
7761
+ () => normalizeTreeSelectValue(value, multiple),
7762
+ [value, multiple]
7763
+ );
7764
+ const selectedSet = useMemo(() => new Set(selected), [selected]);
7765
+ const [expanded, setExpanded] = useState(
7766
+ () => /* @__PURE__ */ new Set([
7767
+ ...treeDefaultExpandAll ? index.entries.filter((entry) => entry.node.children?.length).map((entry) => entry.key) : treeDefaultExpandedKeys ?? [],
7768
+ ...selected.flatMap(
7769
+ (part) => index.byValue.get(part)?.parents.map((parent) => parent.key) ?? []
7770
+ )
7771
+ ])
7772
+ );
7773
+ const [open, setOpen] = useState(false);
7774
+ const [keyword, setKeyword] = useState(null);
7775
+ const [active, setActive] = useState(
7776
+ selected[0] ?? null
7777
+ );
7778
+ const anchorRef = useRef(null);
7779
+ const inputRef = useRef(null);
7780
+ const treeRef = useRef(null);
7781
+ const treeId = useId();
7782
+ const interactive = !disabled && !readOnly;
7783
+ const query = (keyword ?? "").trim().toLocaleLowerCase();
7784
+ const text = selected.map((part) => index.byValue.get(part)?.node.title ?? String(part)).join("\uFF1B");
7785
+ const visible = useMemo(() => {
7786
+ if (!query)
7787
+ return index.entries.filter(
7788
+ (entry) => entry.parents.every((parent) => expanded.has(parent.key))
7789
+ );
7790
+ const matches = /* @__PURE__ */ new Set();
7791
+ for (const entry of index.entries) {
7792
+ if (!entry.node.title.toLocaleLowerCase().includes(query)) continue;
7793
+ matches.add(entry);
7794
+ entry.parents.forEach((parent) => matches.add(parent));
7795
+ }
7796
+ return index.entries.filter((entry) => matches.has(entry));
7797
+ }, [index, query, expanded]);
7798
+ useEffect(() => {
7799
+ setExpanded((previous) => {
7800
+ const next = new Set(previous);
7801
+ selected.forEach(
7802
+ (part) => index.byValue.get(part)?.parents.forEach((parent) => next.add(parent.key))
7803
+ );
7804
+ return next;
7805
+ });
7806
+ }, [index, selected]);
7807
+ useEffect(() => {
7808
+ treeRef.current?.querySelector('[data-active="true"]')?.scrollIntoView?.({ block: "nearest" });
7809
+ }, [active, open]);
7810
+ const expand = (entry, next) => {
7811
+ if (!entry.node.children?.length || query) return;
7812
+ setExpanded((previous) => {
7813
+ const keys = new Set(previous);
7814
+ if (next ?? !keys.has(entry.key)) keys.add(entry.key);
7815
+ else keys.delete(entry.key);
7816
+ return keys;
7817
+ });
7818
+ };
7819
+ const choose = (entry, trigger) => {
7820
+ if (entry.node.disabled) return;
7821
+ if (entry.node.selectable === false) {
7822
+ expand(entry);
7823
+ return;
7824
+ }
7825
+ const next = multiple ? selectedSet.has(entry.node.value) ? selected.filter((part) => part !== entry.node.value) : [...selected, entry.node.value] : [entry.node.value];
7826
+ onChange?.(
7827
+ multiple ? next : entry.node.value,
7828
+ next.flatMap((part) => index.byValue.get(part)?.node ?? []),
7829
+ { trigger, complete: !multiple }
7830
+ );
7831
+ if (!multiple) {
7832
+ setOpen(false);
7833
+ setKeyword(null);
7834
+ }
7835
+ };
7836
+ const show = () => {
7837
+ if (interactive) setOpen(true);
7838
+ };
7839
+ const activeIndex = visible.findIndex((entry) => entry.node.value === active);
7840
+ return /* @__PURE__ */ jsxs(
7841
+ "div",
7842
+ {
7843
+ ref: anchorRef,
7844
+ className: `canvas-react-tree-select${open && interactive ? " canvas-react-tree-select-open" : ""}`,
7845
+ children: [
7846
+ /* @__PURE__ */ jsx(
7847
+ ReactInput,
7848
+ {
7849
+ ...inputProps,
7850
+ ref: inputRef,
7851
+ disabled,
7852
+ readOnly: readOnly || !showSearch,
7853
+ allowClear: false,
7854
+ role: "combobox",
7855
+ "aria-haspopup": "tree",
7856
+ "aria-expanded": open && interactive,
7857
+ "aria-controls": open ? treeId : void 0,
7858
+ "aria-activedescendant": open && activeIndex >= 0 ? `${treeId}-${activeIndex}` : void 0,
7859
+ value: open && keyword !== null ? keyword : text,
7860
+ onFocus: (event) => {
7861
+ show();
7862
+ onFocus?.(event);
7863
+ },
7864
+ onClick: (event) => {
7865
+ show();
7866
+ onClick?.(event);
7867
+ },
7868
+ onBlur: (event) => {
7869
+ setOpen(false);
7870
+ setKeyword(null);
7871
+ onBlur?.(event);
7872
+ },
7873
+ onChange: (event) => {
7874
+ setKeyword(event.target.value);
7875
+ setActive(null);
7876
+ show();
7877
+ },
7878
+ onKeyDown: (event) => {
7879
+ onKeyDown?.(event);
7880
+ if (event.defaultPrevented || !interactive) return;
7881
+ if (event.key === "Escape") {
7882
+ event.preventDefault();
7883
+ setOpen(false);
7884
+ setKeyword(null);
7885
+ return;
7886
+ }
7887
+ const space = event.key === " " && (!showSearch || keyword === null);
7888
+ if (!space && ![
7889
+ "ArrowDown",
7890
+ "ArrowUp",
7891
+ "ArrowLeft",
7892
+ "ArrowRight",
7893
+ "Enter",
7894
+ "Home",
7895
+ "End"
7896
+ ].includes(event.key))
7897
+ return;
7898
+ event.preventDefault();
7899
+ show();
7900
+ if (!visible.length) return;
7901
+ const entry = visible[activeIndex];
7902
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
7903
+ const next = activeIndex < 0 ? event.key === "ArrowDown" ? 0 : visible.length - 1 : (activeIndex + (event.key === "ArrowDown" ? 1 : -1) + visible.length) % visible.length;
7904
+ setActive(visible[next].node.value);
7905
+ } else if (event.key === "Home" || event.key === "End")
7906
+ setActive(
7907
+ visible[event.key === "Home" ? 0 : visible.length - 1].node.value
7908
+ );
7909
+ else if (entry) {
7910
+ if (event.key === "ArrowRight") {
7911
+ if (entry.node.children?.length && !expanded.has(entry.key) && !query)
7912
+ expand(entry, true);
7913
+ else if (visible[activeIndex + 1]?.depth > entry.depth)
7914
+ setActive(visible[activeIndex + 1].node.value);
7915
+ } else if (event.key === "ArrowLeft") {
7916
+ if (expanded.has(entry.key) && !query) expand(entry, false);
7917
+ else
7918
+ setActive(
7919
+ entry.parents[entry.parents.length - 1]?.node.value ?? entry.node.value
7920
+ );
7921
+ } else choose(entry, "keyboard");
7922
+ }
7923
+ },
7924
+ suffix: /* @__PURE__ */ jsxs(
7925
+ "span",
7926
+ {
7927
+ className: `canvas-react-select-actions${allowClear && interactive && selected.length ? " canvas-react-select-actions-clearable" : ""}`,
7928
+ onPointerDown: (event) => {
7929
+ if (interactive) {
7930
+ event.preventDefault();
7931
+ event.stopPropagation();
7932
+ }
7933
+ },
7934
+ onMouseDown: (event) => {
7935
+ if (interactive) {
7936
+ event.preventDefault();
7937
+ event.stopPropagation();
7938
+ }
7939
+ },
7940
+ onClick: (event) => {
7941
+ event.stopPropagation();
7942
+ if (interactive) {
7943
+ inputRef.current?.focus({ preventScroll: true });
7944
+ show();
7945
+ }
7946
+ },
7947
+ children: [
7948
+ allowClear && interactive && selected.length > 0 ? /* @__PURE__ */ jsx(
7949
+ "button",
7950
+ {
7951
+ type: "button",
7952
+ tabIndex: -1,
7953
+ "aria-label": "\u6E05\u7A7A\u6811\u9009\u62E9",
7954
+ className: "canvas-react-select-clear",
7955
+ onClick: (event) => {
7956
+ event.preventDefault();
7957
+ event.stopPropagation();
7958
+ inputRef.current?.focus({ preventScroll: true });
7959
+ setKeyword(null);
7960
+ setActive(null);
7961
+ setOpen(true);
7962
+ onChange?.(multiple ? [] : null, [], {
7963
+ trigger: "clear",
7964
+ complete: false
7965
+ });
7966
+ },
7967
+ children: /* @__PURE__ */ jsx(
7968
+ "span",
7969
+ {
7970
+ className: "canvas-react-select-clear-icon",
7971
+ "aria-hidden": "true"
7972
+ }
7973
+ )
7974
+ }
7975
+ ) : null,
7976
+ /* @__PURE__ */ jsx(
7977
+ "span",
7978
+ {
7979
+ className: `canvas-react-select-arrow${open ? " canvas-react-select-arrow-open" : ""}`,
7980
+ "aria-hidden": "true"
7981
+ }
7982
+ )
7983
+ ]
7984
+ }
7985
+ )
7986
+ }
7987
+ ),
7988
+ /* @__PURE__ */ jsx(
7989
+ ReactPopup,
7990
+ {
7991
+ open: open && interactive,
7992
+ anchorRef,
7993
+ onOpenChange: setOpen,
7994
+ matchAnchorWidth: true,
7995
+ constrainHeight: true,
7996
+ surfaceClassName: "canvas-react-tree-select-popup",
7997
+ children: /* @__PURE__ */ jsx(
7998
+ "div",
7999
+ {
8000
+ ref: treeRef,
8001
+ id: treeId,
8002
+ role: "tree",
8003
+ "aria-label": "\u6811\u9009\u62E9\u9009\u9879",
8004
+ "aria-multiselectable": multiple || void 0,
8005
+ className: "canvas-react-tree-select-tree",
8006
+ children: visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "canvas-react-tree-select-empty", children: "\u6682\u65E0\u6570\u636E" }) : visible.map((entry, position) => {
8007
+ const isExpanded = !!query || expanded.has(entry.key);
8008
+ const hasChildren = !!entry.node.children?.length;
8009
+ return /* @__PURE__ */ jsxs(
8010
+ "div",
8011
+ {
8012
+ id: `${treeId}-${position}`,
8013
+ role: "treeitem",
8014
+ "aria-level": entry.depth + 1,
8015
+ "aria-expanded": hasChildren ? isExpanded : void 0,
8016
+ "aria-selected": selectedSet.has(entry.node.value),
8017
+ "aria-disabled": !!entry.node.disabled,
8018
+ "data-active": entry.node.value === active,
8019
+ className: "canvas-react-tree-select-node",
8020
+ style: { paddingLeft: 6 + entry.depth * 18 },
8021
+ onMouseDown: (event) => event.preventDefault(),
8022
+ onMouseEnter: () => setActive(entry.node.value),
8023
+ onClick: () => {
8024
+ setActive(entry.node.value);
8025
+ choose(entry, "mouse");
8026
+ },
8027
+ children: [
8028
+ hasChildren ? /* @__PURE__ */ jsx(
8029
+ "button",
8030
+ {
8031
+ type: "button",
8032
+ tabIndex: -1,
8033
+ "aria-label": `${isExpanded ? "\u6536\u8D77" : "\u5C55\u5F00"} ${entry.node.title}`,
8034
+ className: "canvas-react-tree-select-switcher",
8035
+ onClick: (event) => {
8036
+ event.preventDefault();
8037
+ event.stopPropagation();
8038
+ expand(entry);
8039
+ },
8040
+ children: /* @__PURE__ */ jsx(
8041
+ "span",
8042
+ {
8043
+ className: `canvas-react-tree-select-chevron${isExpanded ? " canvas-react-tree-select-chevron-open" : ""}`,
8044
+ "aria-hidden": "true"
8045
+ }
8046
+ )
8047
+ }
8048
+ ) : /* @__PURE__ */ jsx("span", { className: "canvas-react-tree-select-switcher" }),
8049
+ /* @__PURE__ */ jsx("span", { className: "canvas-react-tree-select-title", children: entry.node.title }),
8050
+ multiple && selectedSet.has(entry.node.value) ? /* @__PURE__ */ jsx(
8051
+ "span",
8052
+ {
8053
+ className: "canvas-react-tree-select-check",
8054
+ "aria-hidden": "true",
8055
+ children: "\u2713"
8056
+ }
8057
+ ) : null
8058
+ ]
8059
+ },
8060
+ JSON.stringify(entry.node.value)
8061
+ );
8062
+ })
8063
+ }
8064
+ )
8065
+ }
8066
+ )
8067
+ ]
8068
+ }
8069
+ );
8070
+ }
7568
8071
  var ReactAutoComplete = forwardRef(function ReactAutoComplete2(props, ref) {
7569
8072
  const {
7570
8073
  onChange,
@@ -8852,6 +9355,6 @@ var reactMessage = {
8852
9355
  error: (content) => showMessage("error", content)
8853
9356
  };
8854
9357
 
8855
- export { REACT_COMPONENT_PREFIX, ReactAutoComplete, ReactButton, ReactCascader, ReactCheckbox, ReactCol, ReactColorPicker, ReactContentScaleProvider, ReactDatePicker, ReactDateRangePicker, ReactDateTimePicker, ReactDateTimeRangePicker, ReactFilterPanel, ReactFlex, ReactForm, ReactImagePreview, ReactInput, ReactInputNumber, ReactInputRange, ReactModal, ReactMonthPicker, ReactNumberInput, ReactPagination, ReactPopconfirm, ReactPopup, ReactQuarterPicker, ReactRadio, ReactResizablePanels, ReactRow, ReactSelect, ReactTextArea, ReactTimePicker, ReactTimeRangePicker, ReactTooltip, ReactTransition, ReactVirtualList, ReactYearPicker, reactMessage, resolveResizablePanelSizes, useReactContentScale, useReactForm, useReactFormWatch, useReactTransition };
9358
+ export { REACT_COMPONENT_PREFIX, ReactAutoComplete, ReactButton, ReactCascader, ReactCheckbox, ReactCol, ReactColorPicker, ReactContentScaleProvider, ReactDatePicker, ReactDateRangePicker, ReactDateTimePicker, ReactDateTimeRangePicker, ReactFilterPanel, ReactFlex, ReactForm, ReactImagePreview, ReactInput, ReactInputNumber, ReactInputRange, ReactModal, ReactMonthPicker, ReactNumberInput, ReactPagination, ReactPopconfirm, ReactPopup, ReactQuarterPicker, ReactRadio, ReactResizablePanels, ReactRow, ReactSelect, ReactTextArea, ReactTimePicker, ReactTimeRangePicker, ReactTooltip, ReactTransition, ReactTreeSelect, ReactVirtualList, ReactYearPicker, reactMessage, resolveResizablePanelSizes, useReactContentScale, useReactForm, useReactFormWatch, useReactTransition };
8856
9359
  //# sourceMappingURL=index.js.map
8857
9360
  //# sourceMappingURL=index.js.map