@canvas-components/react-components 0.3.8 → 0.3.10

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