@veltra/desktop 1.0.8 → 1.0.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.
Files changed (183) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +3 -3
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +1 -1
  4. package/dist/components/batch-edit/style.js +2 -2
  5. package/dist/components/button/button.vue.d.ts +1 -1
  6. package/dist/components/cascade/cascade.js +15 -13
  7. package/dist/components/cascade/cascade.js.map +1 -1
  8. package/dist/components/cascade/cascade.vue.d.ts +4 -4
  9. package/dist/components/cascade/node.js.map +1 -1
  10. package/dist/components/cascade/use-check.js +3 -2
  11. package/dist/components/cascade/use-check.js.map +1 -1
  12. package/dist/components/cascade/use-select.js +3 -3
  13. package/dist/components/cascade/use-select.js.map +1 -1
  14. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  15. package/dist/components/date-picker/date-picker.js +9 -9
  16. package/dist/components/date-picker/date-picker.js.map +1 -1
  17. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  18. package/dist/components/date-range-picker/date-range-picker.js +9 -9
  19. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  20. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  21. package/dist/components/empty/empty.vue.d.ts +1 -1
  22. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  23. package/dist/components/file-picker/helper.js +19 -7
  24. package/dist/components/file-picker/helper.js.map +1 -1
  25. package/dist/components/file-viewer/file-viewer.js +294 -0
  26. package/dist/components/file-viewer/file-viewer.js.map +1 -0
  27. package/dist/components/file-viewer/file-viewer.vue.d.ts +42 -0
  28. package/dist/components/file-viewer/helper.js +185 -0
  29. package/dist/components/file-viewer/helper.js.map +1 -0
  30. package/dist/components/file-viewer/index.d.ts +1 -0
  31. package/dist/components/file-viewer/previewers/docx-previewer.js +76 -0
  32. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -0
  33. package/dist/components/file-viewer/previewers/image-previewer.js +64 -0
  34. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -0
  35. package/dist/components/file-viewer/previewers/pdf-previewer.js +90 -0
  36. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -0
  37. package/dist/components/file-viewer/previewers/sheet-previewer.js +175 -0
  38. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -0
  39. package/dist/components/file-viewer/previewers/text-previewer.js +74 -0
  40. package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -0
  41. package/dist/components/file-viewer/previewers/video-previewer.js +62 -0
  42. package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -0
  43. package/dist/components/file-viewer/style.d.ts +1 -0
  44. package/dist/components/file-viewer/style.js +4 -0
  45. package/dist/components/file-viewer/style2.css +445 -0
  46. package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
  47. package/dist/components/index.d.ts +12 -9
  48. package/dist/components/input/input.vue.d.ts +1 -1
  49. package/dist/components/multi-select/multi-select-option.js +14 -10
  50. package/dist/components/multi-select/multi-select-option.js.map +1 -1
  51. package/dist/components/multi-select/multi-select.js +16 -19
  52. package/dist/components/multi-select/multi-select.js.map +1 -1
  53. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  54. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
  55. package/dist/components/notification/notification.vue.d.ts +1 -1
  56. package/dist/components/number-input/number-input.vue.d.ts +1 -1
  57. package/dist/components/number-range-input/number-range-input.vue.d.ts +2 -2
  58. package/dist/components/palette/di.js.map +1 -1
  59. package/dist/components/palette/palette-alpha.js +7 -10
  60. package/dist/components/palette/palette-alpha.js.map +1 -1
  61. package/dist/components/palette/palette-hue.js +5 -7
  62. package/dist/components/palette/palette-hue.js.map +1 -1
  63. package/dist/components/palette/palette-sv.js +8 -10
  64. package/dist/components/palette/palette-sv.js.map +1 -1
  65. package/dist/components/palette/palette.js +22 -24
  66. package/dist/components/palette/palette.js.map +1 -1
  67. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  68. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  69. package/dist/components/progress-nodes/progress-nodes.vue.d.ts +3 -3
  70. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +2 -2
  71. package/dist/components/select/select.js +14 -12
  72. package/dist/components/select/select.js.map +1 -1
  73. package/dist/components/select/select.vue.d.ts +2 -2
  74. package/dist/components/slider/slider-thumb.js +10 -13
  75. package/dist/components/slider/slider-thumb.js.map +1 -1
  76. package/dist/components/slider/slider.js +12 -15
  77. package/dist/components/slider/slider.js.map +1 -1
  78. package/dist/components/table/di.js.map +1 -1
  79. package/dist/components/table/node/row.d.ts +10 -1
  80. package/dist/components/table/node/row.js +9 -0
  81. package/dist/components/table/node/row.js.map +1 -1
  82. package/dist/components/table/style.js +1 -1
  83. package/dist/components/table/table-body.js +25 -26
  84. package/dist/components/table/table-body.js.map +1 -1
  85. package/dist/components/table/table-cell.js +16 -9
  86. package/dist/components/table/table-cell.js.map +1 -1
  87. package/dist/components/table/table-row.js +78 -19
  88. package/dist/components/table/table-row.js.map +1 -1
  89. package/dist/components/table/table.js +76 -34
  90. package/dist/components/table/table.js.map +1 -1
  91. package/dist/components/table/use-fixed-columns.js +27 -8
  92. package/dist/components/table/use-fixed-columns.js.map +1 -1
  93. package/dist/components/table/use-rows.js +2 -2
  94. package/dist/components/table/use-rows.js.map +1 -1
  95. package/dist/components/table/use-table.js +25 -3
  96. package/dist/components/table/use-table.js.map +1 -1
  97. package/dist/components/tabs/index.d.ts +3 -1
  98. package/dist/components/tabs/style2.css +169 -107
  99. package/dist/components/tabs/tabs-horizontal.js +133 -0
  100. package/dist/components/tabs/tabs-horizontal.js.map +1 -0
  101. package/dist/components/tabs/tabs-horizontal.vue.d.ts +29 -0
  102. package/dist/components/tabs/tabs-vertical.js +89 -0
  103. package/dist/components/tabs/tabs-vertical.js.map +1 -0
  104. package/dist/components/tabs/tabs-vertical.vue.d.ts +28 -0
  105. package/dist/components/tabs/tabs.js +75 -105
  106. package/dist/components/tabs/tabs.js.map +1 -1
  107. package/dist/components/tabs/tabs.vue.d.ts +9 -23
  108. package/dist/components/tabs/use-tabs-bar.js +105 -0
  109. package/dist/components/tabs/use-tabs-bar.js.map +1 -0
  110. package/dist/components/textarea/textarea.vue.d.ts +1 -1
  111. package/dist/components/tip/tip.vue.d.ts +2 -2
  112. package/dist/components/tree/style2.css +45 -23
  113. package/dist/components/tree/tree-node.js +50 -35
  114. package/dist/components/tree/tree-node.js.map +1 -1
  115. package/dist/components/tree/tree.js +27 -20
  116. package/dist/components/tree/tree.js.map +1 -1
  117. package/dist/components/tree/tree.vue.d.ts +1 -1
  118. package/dist/components/tree/use-check.js +52 -14
  119. package/dist/components/tree/use-check.js.map +1 -1
  120. package/dist/components/tree/use-tree-nodes.js +51 -24
  121. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  122. package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
  123. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  124. package/dist/index.d.ts +57 -53
  125. package/dist/index.js +51 -48
  126. package/dist/types/file-viewer.d.ts +71 -0
  127. package/dist/types/index.d.ts +3 -2
  128. package/dist/types/tabs.d.ts +106 -14
  129. package/dist/types/tree.d.ts +6 -1
  130. package/package.json +32 -31
  131. package/src/components/cascade/cascade.vue +18 -18
  132. package/src/components/cascade/node.ts +4 -1
  133. package/src/components/cascade/use-check.ts +5 -4
  134. package/src/components/cascade/use-select.ts +4 -5
  135. package/src/components/date-picker/date-picker.vue +10 -9
  136. package/src/components/date-range-picker/date-range-picker.vue +12 -12
  137. package/src/components/file-picker/helper.ts +21 -10
  138. package/src/components/file-viewer/file-viewer.vue +311 -0
  139. package/src/components/file-viewer/helper.ts +193 -0
  140. package/src/components/file-viewer/index.ts +1 -0
  141. package/src/components/file-viewer/previewers/docx-previewer.vue +78 -0
  142. package/src/components/file-viewer/previewers/image-previewer.vue +64 -0
  143. package/src/components/file-viewer/previewers/pdf-previewer.vue +90 -0
  144. package/src/components/file-viewer/previewers/sheet-previewer.vue +210 -0
  145. package/src/components/file-viewer/previewers/text-previewer.vue +76 -0
  146. package/src/components/file-viewer/previewers/video-previewer.vue +72 -0
  147. package/src/components/file-viewer/style.scss +504 -0
  148. package/src/components/file-viewer/style.ts +4 -0
  149. package/src/components/index.ts +2 -0
  150. package/src/components/multi-select/multi-select-option.vue +16 -3
  151. package/src/components/multi-select/multi-select.vue +17 -9
  152. package/src/components/palette/di.ts +5 -3
  153. package/src/components/palette/palette-alpha.vue +7 -10
  154. package/src/components/palette/palette-hue.vue +5 -7
  155. package/src/components/palette/palette-sv.vue +12 -14
  156. package/src/components/palette/palette.vue +19 -21
  157. package/src/components/select/select.vue +21 -19
  158. package/src/components/slider/slider-thumb.vue +11 -14
  159. package/src/components/slider/slider.vue +15 -15
  160. package/src/components/table/di.ts +20 -3
  161. package/src/components/table/node/row.ts +10 -1
  162. package/src/components/table/table-body.vue +28 -33
  163. package/src/components/table/table-cell.vue +17 -9
  164. package/src/components/table/table-row.tsx +83 -17
  165. package/src/components/table/table.vue +73 -45
  166. package/src/components/table/use-fixed-columns.ts +46 -12
  167. package/src/components/table/use-rows.ts +14 -3
  168. package/src/components/table/use-table.ts +27 -3
  169. package/src/components/tabs/index.ts +2 -0
  170. package/src/components/tabs/style.scss +217 -134
  171. package/src/components/tabs/tabs-horizontal.vue +125 -0
  172. package/src/components/tabs/tabs-vertical.vue +75 -0
  173. package/src/components/tabs/tabs.vue +56 -127
  174. package/src/components/tabs/use-tabs-bar.ts +119 -0
  175. package/src/components/tree/style.scss +74 -29
  176. package/src/components/tree/tree-node.vue +10 -4
  177. package/src/components/tree/tree.vue +39 -12
  178. package/src/components/tree/use-check.ts +79 -23
  179. package/src/components/tree/use-tree-nodes.ts +85 -34
  180. package/src/types/file-viewer.ts +69 -0
  181. package/src/types/index.ts +1 -0
  182. package/src/types/tabs.ts +113 -19
  183. package/src/types/tree.ts +6 -1
@@ -2,8 +2,8 @@ import _sfc_main$1 from "../icon/icon.js";
2
2
  import NodeRender from "../node-render/node-render.js";
3
3
  import _sfc_main$2 from "../checkbox/checkbox.js";
4
4
  import { TreeDIKey } from "./di.js";
5
- import { createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, inject, normalizeClass, normalizeStyle, openBlock, unref, withCtx, withDirectives, withModifiers } from "vue";
6
- import { bem, withUnit } from "@veltra/utils";
5
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, inject, normalizeClass, openBlock, renderList, unref, withCtx, withDirectives, withModifiers } from "vue";
6
+ import { bem } from "@veltra/utils";
7
7
  import { vRipple } from "@veltra/directives";
8
8
  import { ArrowRight } from "@veltra/icons/normal";
9
9
  //#region src/components/tree/tree-node.vue
@@ -12,11 +12,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
12
12
  __name: "tree-node",
13
13
  props: {
14
14
  node: {},
15
+ index: {},
15
16
  measureElement: { type: Function }
16
17
  },
17
18
  setup(__props) {
18
19
  const props = __props;
19
20
  const { treeProps, treeEmit, cls, getTreeSlotsNode, getFlattedNodes, toggleCheck, handleSelect } = inject(TreeDIKey);
21
+ function measureRef(el) {
22
+ if (typeof props.index !== "number" || !props.measureElement) return;
23
+ props.measureElement(props.index, el);
24
+ }
20
25
  function toggleExpand() {
21
26
  const { node } = props;
22
27
  node.expanded = !node.expanded;
@@ -40,39 +45,49 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
40
45
  unref(bem).is("expanded", __props.node.expanded),
41
46
  unref(bem).is("disabled", __props.node.disabled)
42
47
  ]),
43
- style: normalizeStyle({ paddingLeft: unref(withUnit)(__props.node.depth * 20 - 20, "px") }),
44
- ref: __props.measureElement
45
- }, [!__props.node.isLeaf ? (openBlock(), createBlock(unref(_sfc_main$1), {
46
- key: 0,
47
- class: normalizeClass(unref(cls).e("expand-icon")),
48
- onClick: withModifiers(toggleExpand, ["stop"])
49
- }, {
50
- default: withCtx(() => [createVNode(unref(ArrowRight))]),
51
- _: 1
52
- }, 8, ["class"])) : (openBlock(), createElementBlock("i", {
53
- key: 1,
54
- class: normalizeClass(unref(cls).e("icon-placeholder"))
55
- }, null, 2)), withDirectives((openBlock(), createElementBlock("div", {
56
- class: normalizeClass(unref(cls).e("node-content")),
57
- onClick: handleClick,
58
- onContextmenu: _cache[1] || (_cache[1] = ($event) => unref(treeEmit)("node-contextmenu", $event, __props.node))
59
- }, [unref(treeProps).checkable ? (openBlock(), createBlock(_sfc_main$2, {
60
- key: 0,
61
- class: normalizeClass(unref(cls).e("checkbox")),
62
- "model-value": __props.node.checked,
63
- indeterminate: __props.node.indeterminate,
64
- disabled: __props.node.disabled,
65
- onChange: handleChange,
66
- onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
67
- }, null, 8, [
68
- "class",
69
- "model-value",
70
- "indeterminate",
71
- "disabled"
72
- ])) : createCommentVNode("v-if", true), createVNode(unref(NodeRender), { content: unref(getTreeSlotsNode)({
73
- node: __props.node,
74
- data: __props.node.data
75
- }) }, null, 8, ["content"])], 34)), [[unref(vRipple), (unref(treeProps).checkable || unref(treeProps).selectable) && !__props.node.disabled ? unref(cls).e("ripple") : false]])], 6);
48
+ ref: measureRef
49
+ }, [
50
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.node.depth, (i) => {
51
+ return openBlock(), createElementBlock("span", {
52
+ key: i,
53
+ class: normalizeClass(unref(cls).e("indent")),
54
+ "aria-hidden": "true"
55
+ }, null, 2);
56
+ }), 128)),
57
+ !__props.node.isLeaf ? (openBlock(), createBlock(unref(_sfc_main$1), {
58
+ key: 0,
59
+ class: normalizeClass(unref(cls).e("expand-icon")),
60
+ onClick: withModifiers(toggleExpand, ["stop"])
61
+ }, {
62
+ default: withCtx(() => [createVNode(unref(ArrowRight))]),
63
+ _: 1
64
+ }, 8, ["class"])) : (openBlock(), createElementBlock("i", {
65
+ key: 1,
66
+ class: normalizeClass([unref(cls).e("indent"), unref(cls).em("indent", "leaf")]),
67
+ "aria-hidden": "true"
68
+ }, null, 2)),
69
+ withDirectives((openBlock(), createElementBlock("div", {
70
+ class: normalizeClass(unref(cls).e("node-content")),
71
+ onClick: handleClick,
72
+ onContextmenu: _cache[1] || (_cache[1] = ($event) => unref(treeEmit)("node-contextmenu", $event, __props.node))
73
+ }, [unref(treeProps).checkable ? (openBlock(), createBlock(_sfc_main$2, {
74
+ key: 0,
75
+ class: normalizeClass(unref(cls).e("checkbox")),
76
+ "model-value": __props.node.checked,
77
+ indeterminate: __props.node.indeterminate,
78
+ disabled: __props.node.disabled,
79
+ onChange: handleChange,
80
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
81
+ }, null, 8, [
82
+ "class",
83
+ "model-value",
84
+ "indeterminate",
85
+ "disabled"
86
+ ])) : createCommentVNode("v-if", true), createVNode(unref(NodeRender), { content: unref(getTreeSlotsNode)({
87
+ node: __props.node,
88
+ data: __props.node.data
89
+ }) }, null, 8, ["content"])], 34)), [[unref(vRipple), (unref(treeProps).checkable || unref(treeProps).selectable) && !__props.node.disabled ? unref(cls).e("ripple") : false]])
90
+ ], 2);
76
91
  };
77
92
  }
78
93
  });
@@ -1 +1 @@
1
- {"version":3,"file":"tree-node.js","names":[],"sources":["../../../src/components/tree/tree-node.vue"],"sourcesContent":["<template>\n <div\n :class=\"[cls.e('node'), bem.is('expanded', node.expanded), bem.is('disabled', node.disabled)]\"\n :style=\"{ paddingLeft: withUnit(node.depth * 20 - 20, 'px') }\"\n :ref=\"measureElement\"\n >\n <u-icon v-if=\"!node.isLeaf\" :class=\"cls.e('expand-icon')\" @click.stop=\"toggleExpand\">\n <ArrowRight />\n </u-icon>\n <i v-else :class=\"cls.e('icon-placeholder')\"> </i>\n\n <div\n :class=\"cls.e('node-content')\"\n @click=\"handleClick\"\n @contextmenu=\"treeEmit('node-contextmenu', $event, node)\"\n v-ripple=\"\n (treeProps.checkable || treeProps.selectable) && !node.disabled ? cls.e('ripple') : false\n \"\n >\n <u-checkbox\n v-if=\"treeProps.checkable\"\n :class=\"cls.e('checkbox')\"\n :model-value=\"node.checked\"\n :indeterminate=\"node.indeterminate\"\n :disabled=\"node.disabled\"\n @change=\"handleChange\"\n @click.stop\n />\n\n <u-node-render :content=\"getTreeSlotsNode({ node, data: node.data })\" />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { vRipple } from '@veltra/directives'\nimport { ArrowRight } from '@veltra/icons/normal'\nimport { bem, withUnit } from '@veltra/utils'\nimport { inject } from 'vue'\n\nimport type { TreeNodeProps } from '../../types'\nimport UCheckbox from '../checkbox/checkbox.vue'\nimport { UIcon } from '../icon'\nimport { UNodeRender } from '../node-render'\nimport { TreeDIKey } from './di'\n\ndefineOptions({\n name: 'TreeNode'\n})\n\nconst props = defineProps<TreeNodeProps>()\n\nconst { treeProps, treeEmit, cls, getTreeSlotsNode, getFlattedNodes, toggleCheck, handleSelect } =\n inject(TreeDIKey)!\n\nfunction toggleExpand() {\n const { node } = props\n node.expanded = !node.expanded\n getFlattedNodes()\n\n treeEmit('expand', node)\n}\n\nfunction handleChange(checked: boolean, e: MouseEvent) {\n toggleCheck(props.node, checked, e.ctrlKey)\n}\n\nfunction handleClick(e: MouseEvent) {\n const { node } = props\n treeEmit('node-click', node)\n\n treeProps.selectable && handleSelect(node)\n treeProps.expandOnClickNode && toggleExpand()\n treeProps.checkable &&\n !node.disabled &&\n !treeProps.expandOnClickNode &&\n toggleCheck(node, !node.checked, e.ctrlKey)\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;EAkDA,MAAM,QAAQ;EAEd,MAAM,EAAE,WAAW,UAAU,KAAK,kBAAkB,iBAAiB,aAAa,iBAChF,OAAO,UAAU;EAEnB,SAAS,eAAe;GACtB,MAAM,EAAE,SAAS;AACjB,QAAK,WAAW,CAAC,KAAK;AACtB,oBAAgB;AAEhB,YAAS,UAAU,KAAI;;EAGzB,SAAS,aAAa,SAAkB,GAAe;AACrD,eAAY,MAAM,MAAM,SAAS,EAAE,QAAO;;EAG5C,SAAS,YAAY,GAAe;GAClC,MAAM,EAAE,SAAS;AACjB,YAAS,cAAc,KAAI;AAE3B,aAAU,cAAc,aAAa,KAAI;AACzC,aAAU,qBAAqB,cAAa;AAC5C,aAAU,aACR,CAAC,KAAK,YACN,CAAC,UAAU,qBACX,YAAY,MAAM,CAAC,KAAK,SAAS,EAAE,QAAO;;;uBA3E5C,mBA8BM,OAAA;IA7BH,OAAK,eAAA;KAAG,MAAA,IAAG,CAAC,EAAC,OAAA;KAAU,MAAA,IAAG,CAAC,GAAE,YAAa,QAAA,KAAK,SAAQ;KAAG,MAAA,IAAG,CAAC,GAAE,YAAa,QAAA,KAAK,SAAQ;KAAA,CAAA;IAC1F,OAAK,eAAA,EAAA,aAAiB,MAAA,SAAQ,CAAC,QAAA,KAAK,QAAK,KAAA,IAAA,KAAA,EAAA,CAAA;IACzC,KAAK,QAAA;QAES,QAAA,KAAK,UAAA,WAAA,EAApB,YAES,MAAA,YAAA,EAAA;;IAFoB,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,cAAA,CAAA;IAAkB,SAAK,cAAO,cAAY,CAAA,OAAA,CAAA;;2BACnE,CAAd,YAAc,MAAA,WAAA,CAAA,CAAA,CAAA;;qCAEhB,mBAAkD,KAAA;;IAAvC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,mBAAA,CAAA;8CAEvB,mBAmBM,OAAA;IAlBH,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,eAAA,CAAA;IACZ,SAAO;IACP,eAAW,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,SAAQ,CAAA,oBAAqB,QAAQ,QAAA,KAAI;OAM/C,MAAA,UAAS,CAAC,aAAA,WAAA,EADlB,YAQE,aAAA;;IANC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,WAAA,CAAA;IACZ,eAAa,QAAA,KAAK;IAClB,eAAe,QAAA,KAAK;IACpB,UAAU,QAAA,KAAK;IACf,UAAQ;IACR,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;;;;;;2CAGb,YAAwE,MAAA,WAAA,EAAA,EAAxD,SAAS,MAAA,iBAAgB,CAAA;IAAA,MAAG,QAAA;IAAI,MAAQ,QAAA,KAAK;IAAI,CAAA,EAAA,EAAA,MAAA,GAAA,CAAA,UAAA,CAAA,CAAA,EAAA,GAAA,GAAA,CAAA,CAAA,MAAA,QAAA,GAd7C,MAAA,UAAS,CAAC,aAAa,MAAA,UAAS,CAAC,eAAU,CAAM,QAAA,KAAK,WAAW,MAAA,IAAG,CAAC,EAAC,SAAA,GAAA,MAAA,CAAA,CAAA,CAAA,EAAA,EAAA"}
1
+ {"version":3,"file":"tree-node.js","names":[],"sources":["../../../src/components/tree/tree-node.vue"],"sourcesContent":["<template>\n <div\n :class=\"[cls.e('node'), bem.is('expanded', node.expanded), bem.is('disabled', node.disabled)]\"\n :ref=\"measureRef\"\n >\n <span v-for=\"i in node.depth\" :key=\"i\" :class=\"cls.e('indent')\" aria-hidden=\"true\"></span>\n\n <u-icon v-if=\"!node.isLeaf\" :class=\"cls.e('expand-icon')\" @click.stop=\"toggleExpand\">\n <ArrowRight />\n </u-icon>\n <i v-else :class=\"[cls.e('indent'), cls.em('indent', 'leaf')]\" aria-hidden=\"true\"></i>\n\n <div\n :class=\"cls.e('node-content')\"\n @click=\"handleClick\"\n @contextmenu=\"treeEmit('node-contextmenu', $event, node)\"\n v-ripple=\"\n (treeProps.checkable || treeProps.selectable) && !node.disabled ? cls.e('ripple') : false\n \"\n >\n <u-checkbox\n v-if=\"treeProps.checkable\"\n :class=\"cls.e('checkbox')\"\n :model-value=\"node.checked\"\n :indeterminate=\"node.indeterminate\"\n :disabled=\"node.disabled\"\n @change=\"handleChange\"\n @click.stop\n />\n\n <u-node-render :content=\"getTreeSlotsNode({ node, data: node.data })\" />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { vRipple } from '@veltra/directives'\nimport { ArrowRight } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { inject } from 'vue'\n\nimport type { TreeNodeProps } from '../../types'\nimport UCheckbox from '../checkbox/checkbox.vue'\nimport { UIcon } from '../icon'\nimport { UNodeRender } from '../node-render'\nimport { TreeDIKey } from './di'\n\ndefineOptions({\n name: 'TreeNode'\n})\n\nconst props = defineProps<TreeNodeProps>()\n\nconst { treeProps, treeEmit, cls, getTreeSlotsNode, getFlattedNodes, toggleCheck, handleSelect } =\n inject(TreeDIKey)!\n\nfunction measureRef(el: unknown) {\n if (typeof props.index !== 'number' || !props.measureElement) return\n props.measureElement(props.index, el as Element | null)\n}\n\nfunction toggleExpand() {\n const { node } = props\n node.expanded = !node.expanded\n getFlattedNodes()\n\n treeEmit('expand', node)\n}\n\nfunction handleChange(checked: boolean, e: MouseEvent) {\n toggleCheck(props.node, checked, e.ctrlKey)\n}\n\nfunction handleClick(e: MouseEvent) {\n const { node } = props\n treeEmit('node-click', node)\n\n treeProps.selectable && handleSelect(node)\n treeProps.expandOnClickNode && toggleExpand()\n treeProps.checkable &&\n !node.disabled &&\n !treeProps.expandOnClickNode &&\n toggleCheck(node, !node.checked, e.ctrlKey)\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAmDA,MAAM,QAAQ;EAEd,MAAM,EAAE,WAAW,UAAU,KAAK,kBAAkB,iBAAiB,aAAa,iBAChF,OAAO,UAAU;EAEnB,SAAS,WAAW,IAAa;AAC/B,OAAI,OAAO,MAAM,UAAU,YAAY,CAAC,MAAM,eAAgB;AAC9D,SAAM,eAAe,MAAM,OAAO,GAAoB;;EAGxD,SAAS,eAAe;GACtB,MAAM,EAAE,SAAS;AACjB,QAAK,WAAW,CAAC,KAAK;AACtB,oBAAgB;AAEhB,YAAS,UAAU,KAAI;;EAGzB,SAAS,aAAa,SAAkB,GAAe;AACrD,eAAY,MAAM,MAAM,SAAS,EAAE,QAAO;;EAG5C,SAAS,YAAY,GAAe;GAClC,MAAM,EAAE,SAAS;AACjB,YAAS,cAAc,KAAI;AAE3B,aAAU,cAAc,aAAa,KAAI;AACzC,aAAU,qBAAqB,cAAa;AAC5C,aAAU,aACR,CAAC,KAAK,YACN,CAAC,UAAU,qBACX,YAAY,MAAM,CAAC,KAAK,SAAS,EAAE,QAAO;;;uBAjF5C,mBA+BM,OAAA;IA9BH,OAAK,eAAA;KAAG,MAAA,IAAG,CAAC,EAAC,OAAA;KAAU,MAAA,IAAG,CAAC,GAAE,YAAa,QAAA,KAAK,SAAQ;KAAG,MAAA,IAAG,CAAC,GAAE,YAAa,QAAA,KAAK,SAAQ;KAAA,CAAA;IAC1F,KAAK;;sBAEN,mBAA0F,UAAA,MAAA,WAAxE,QAAA,KAAK,QAAV,MAAC;yBAAd,mBAA0F,QAAA;MAA3D,KAAK;MAAI,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,SAAA,CAAA;MAAY,eAAY;;;KAE7D,QAAA,KAAK,UAAA,WAAA,EAApB,YAES,MAAA,YAAA,EAAA;;KAFoB,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,cAAA,CAAA;KAAkB,SAAK,cAAO,cAAY,CAAA,OAAA,CAAA;;4BACnE,CAAd,YAAc,MAAA,WAAA,CAAA,CAAA,CAAA;;sCAEhB,mBAAsF,KAAA;;KAA3E,OAAK,eAAA,CAAG,MAAA,IAAG,CAAC,EAAC,SAAA,EAAY,MAAA,IAAG,CAAC,GAAE,UAAA,OAAA,CAAA,CAAA;KAAqB,eAAY;;iCAE3E,mBAmBM,OAAA;KAlBH,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,eAAA,CAAA;KACZ,SAAO;KACP,eAAW,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,SAAQ,CAAA,oBAAqB,QAAQ,QAAA,KAAI;QAM/C,MAAA,UAAS,CAAC,aAAA,WAAA,EADlB,YAQE,aAAA;;KANC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,WAAA,CAAA;KACZ,eAAa,QAAA,KAAK;KAClB,eAAe,QAAA,KAAK;KACpB,UAAU,QAAA,KAAK;KACf,UAAQ;KACR,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;;;;;;4CAGb,YAAwE,MAAA,WAAA,EAAA,EAAxD,SAAS,MAAA,iBAAgB,CAAA;KAAA,MAAG,QAAA;KAAI,MAAQ,QAAA,KAAK;KAAI,CAAA,EAAA,EAAA,MAAA,GAAA,CAAA,UAAA,CAAA,CAAA,EAAA,GAAA,GAAA,CAAA,CAAA,MAAA,QAAA,GAd7C,MAAA,UAAS,CAAC,aAAa,MAAA,UAAS,CAAC,eAAU,CAAM,QAAA,KAAK,WAAW,MAAA,IAAG,CAAC,EAAC,SAAA,GAAA,MAAA,CAAA,CAAA"}
@@ -7,8 +7,8 @@ import { useFilter } from "./use-filter.js";
7
7
  import { useSelect } from "./use-select.js";
8
8
  import { useTreeNodes } from "./use-tree-nodes.js";
9
9
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, provide, renderList, shallowRef, unref, useSlots, watch, watchEffect, withCtx } from "vue";
10
- import { bem, nextFrame, scrollIntoContainerView, withUnit } from "@veltra/utils";
11
- import { useFormComponent, useFormFallbackProps, useVirtual } from "@veltra/compositions";
10
+ import { bem, nextFrame, scrollIntoContainerView } from "@veltra/utils";
11
+ import { useFormComponent, useFormFallbackProps, useVirtualizer } from "@veltra/compositions";
12
12
  //#region src/components/tree/tree.vue
13
13
  const _sfc_main = /* @__PURE__ */ defineComponent({
14
14
  name: "Tree",
@@ -87,25 +87,38 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
87
87
  nodeDict,
88
88
  getFlattedNodes
89
89
  });
90
- const { totalHeight, virtualList, scrollTo, virtualEnabled, measureElement } = useVirtual({
90
+ const estimateSize = () => {
91
+ switch (size.value) {
92
+ case "small": return 32;
93
+ case "large": return 44;
94
+ default: return 36;
95
+ }
96
+ };
97
+ const virtualEnabled = computed(() => nodes.value.length > 80);
98
+ const { virtualizer, items } = useVirtualizer({
91
99
  count: computed(() => nodes.value.length),
92
- estimateSize: () => 40,
100
+ scrollEl: () => scrollRef.value?.containerRef ?? null,
101
+ contentEl: () => virtualEnabled.value ? scrollRef.value?.contentRef ?? null : null,
102
+ estimateSize,
93
103
  gap: 2,
94
- virtualThreshold: 80,
95
- scrollEl: computed(() => scrollRef.value?.containerRef ?? null)
104
+ getItemKey: (i) => nodes.value[i]?.key ?? i
96
105
  });
97
106
  const virtualNodes = computed(() => {
98
107
  const _nodes = nodes.value;
99
- return virtualList.value.map((item) => {
108
+ return items.value.map((item) => {
100
109
  const node = _nodes[item.index];
101
110
  return {
102
111
  node,
103
- key: node.key || item.key,
112
+ key: node?.key ?? item.index,
104
113
  offset: item.start,
105
114
  index: item.index
106
115
  };
107
116
  });
108
117
  });
118
+ const measureElement = (index, el) => virtualizer.measureElement(index, el);
119
+ function scrollTo(index) {
120
+ virtualizer.scrollToIndex(index, { align: "center" });
121
+ }
109
122
  function scrollIntoView() {
110
123
  const { selectable, checkable, scrollToView } = props;
111
124
  if (!scrollToView || !selectable && !checkable || !nodes.value) return;
@@ -176,22 +189,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
176
189
  class: normalizeClass(className.value),
177
190
  ref_key: "scrollRef",
178
191
  ref: scrollRef,
179
- "content-style": { height: unref(virtualEnabled) ? unref(withUnit)(unref(totalHeight), "px") : void 0 },
180
- "content-class": [unref(cls).e("wrap"), unref(bem).is("virtual", unref(virtualEnabled))]
192
+ "content-class": [unref(cls).e("wrap"), unref(bem).is("virtual", virtualEnabled.value)]
181
193
  }, {
182
- default: withCtx(() => [unref(virtualEnabled) ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(virtualNodes.value, ({ node, key, offset, index }) => {
194
+ default: withCtx(() => [virtualEnabled.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(virtualNodes.value, ({ node, key, offset, index }) => {
183
195
  return openBlock(), createBlock(_sfc_main$3, {
184
196
  node,
185
197
  key,
186
198
  class: normalizeClass(unref(bem).is("selected", node.data === unref(selectedData))),
187
- "data-index": index,
188
- "measure-element": unref(measureElement),
199
+ index,
200
+ "measure-element": measureElement,
189
201
  style: normalizeStyle({ transform: `translateY(${offset}px)` })
190
202
  }, null, 8, [
191
203
  "node",
192
204
  "class",
193
- "data-index",
194
- "measure-element",
205
+ "index",
195
206
  "style"
196
207
  ]);
197
208
  }), 128)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(nodes), (node) => {
@@ -205,11 +216,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
205
216
  class: normalizeClass(unref(cls).e("empty"))
206
217
  }, [createVNode(unref(_sfc_main$2))], 2)) : createCommentVNode("v-if", true)]),
207
218
  _: 1
208
- }, 8, [
209
- "class",
210
- "content-style",
211
- "content-class"
212
- ]);
219
+ }, 8, ["class", "content-class"]);
213
220
  };
214
221
  }
215
222
  });
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":[],"sources":["../../../src/components/tree/tree.vue"],"sourcesContent":["<template>\n <u-scroll\n :class=\"className\"\n ref=\"scrollRef\"\n :content-style=\"{ height: virtualEnabled ? withUnit(totalHeight, 'px') : undefined }\"\n :content-class=\"[cls.e('wrap'), bem.is('virtual', virtualEnabled)]\"\n >\n <template v-if=\"virtualEnabled\">\n <UTreeNode\n v-for=\"{ node, key, offset, index } of virtualNodes\"\n :node=\"node\"\n :key=\"key\"\n :class=\"bem.is('selected', node.data === selectedData)\"\n :data-index=\"index\"\n :measure-element=\"measureElement\"\n :style=\"{ transform: `translateY(${offset}px)` }\"\n />\n </template>\n <template v-else>\n <UTreeNode\n v-for=\"node of nodes\"\n :node=\"node\"\n :key=\"node.key\"\n :class=\"bem.is('selected', node.data === selectedData)\"\n />\n </template>\n\n <div :class=\"cls.e('empty')\" v-if=\"!nodes.length\">\n <UEmpty />\n </div>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFormComponent, useFormFallbackProps } from '@veltra/compositions'\nimport { useVirtual } from '@veltra/compositions'\nimport { bem, nextFrame, withUnit, scrollIntoContainerView } from '@veltra/utils'\nimport { computed, provide, shallowRef, useSlots, watch, watchEffect, type VNode } from 'vue'\n\nimport type { TreeProps, TreeEmit, _TreeExposed } from '../../types'\nimport type { ScrollExposed } from '../../types'\nimport { UEmpty } from '../empty'\nimport { UScroll } from '../scroll'\nimport { TreeDIKey, type TreeConText, type TreeSlotsScope } from './di'\nimport UTreeNode from './tree-node.vue'\nimport { useCheck } from './use-check'\nimport { useFilter } from './use-filter'\nimport { useSelect } from './use-select'\nimport { useTreeNodes } from './use-tree-nodes'\n\ndefineOptions({\n name: 'Tree'\n})\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n childrenKey: 'children',\n expandOnClickNode: false,\n checkStrictly: false,\n data: () => []\n})\n\nconst emit = defineEmits<TreeEmit>()\n\nconst cls = bem('tree')\n\nconst { formProps } = useFormComponent()\n\nconst { size } = useFormFallbackProps([formProps ?? {}, props], {\n size: 'default'\n})\n\nconst scrollRef = shallowRef<ScrollExposed>()\n\nconst className = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n bem.is('selectable', props.selectable),\n bem.is('checkable', props.checkable)\n ]\n})\n\ndefineSlots<{\n default: (props: TreeSlotsScope) => any\n}>()\n\nconst slots = useSlots()\n\nfunction getTreeSlotsNode(ctx: TreeSlotsScope): VNode[] | string | undefined {\n return (props.slots?.default ?? slots.default)?.(ctx) ?? ctx.node.label\n}\n\nconst { nodes, forest, getFlattedNodes, nodeDict } = useTreeNodes({\n props\n})\n\nwatch(\n [() => props.selectable, () => props.checkable, forest],\n ([s, c]) => {\n // 如果选择或勾选,则不更新节点\n if (s || c) return\n getFlattedNodes()\n },\n { immediate: true }\n)\n\nconst { filter } = useFilter({ forest, getFlattedNodes })\n\nconst { handleSelect, selectedData } = useSelect({\n props,\n emit,\n nodeDict,\n getFlattedNodes\n})\n\nconst { checkedData, toggleCheck } = useCheck({\n props,\n emit,\n nodeDict,\n getFlattedNodes\n})\n\nconst { totalHeight, virtualList, scrollTo, virtualEnabled, measureElement } = useVirtual({\n count: computed(() => nodes.value.length),\n estimateSize: () => 40,\n gap: 2,\n virtualThreshold: 80,\n scrollEl: computed(() => scrollRef.value?.containerRef ?? null)\n})\n\nconst virtualNodes = computed(() => {\n const _nodes = nodes.value\n\n return virtualList.value.map((item) => {\n const node = _nodes[item.index]!\n return {\n node,\n key: node.key || item.key,\n offset: item.start,\n index: item.index\n }\n })\n})\n\nfunction scrollIntoView() {\n const { selectable, checkable, scrollToView } = props\n if (!scrollToView || (!selectable && !checkable) || !nodes.value) return\n let index = -1\n\n if (selectable) {\n if (!selectedData.value) return\n index = nodes.value.findIndex((node) => node.data === selectedData.value)\n } else if (checkable) {\n if (!checkedData.size) return\n index = nodes.value.findIndex((node) => checkedData.has(node.data))\n }\n\n if (virtualEnabled.value) {\n scrollTo(index)\n } else {\n const { contentRef, containerRef } = scrollRef.value ?? {}\n const el = contentRef?.children[index]\n el && scrollIntoContainerView(el as HTMLElement, containerRef ?? null)\n }\n}\n\nwatchEffect(() => {\n nextFrame(() => {\n scrollIntoView()\n })\n})\n\nprovide(TreeDIKey, {\n cls,\n selectedData,\n checkedData,\n getFlattedNodes,\n getTreeSlotsNode,\n treeEmit: emit as TreeEmit,\n treeProps: props as TreeProps,\n toggleCheck: toggleCheck as TreeConText['toggleCheck'],\n handleSelect: handleSelect as TreeConText['handleSelect']\n})\n\ndefineExpose<_TreeExposed>({\n filter,\n forest,\n nodes,\n checkNode: toggleCheck,\n selectNode: handleSelect,\n checkAll(check: boolean) {\n forest.value.roots.forEach((node) => {\n toggleCheck(node, check)\n })\n },\n getSelected() {\n return selectedData\n },\n getChecked() {\n return Array.from(checkedData)\n },\n scrollTo,\n\n expandAll() {\n forest.value.dfs((node) => {\n node.expanded = true\n })\n getFlattedNodes()\n },\n collapseAll() {\n forest.value.dfs((node) => {\n node.expanded = false\n })\n getFlattedNodes()\n }\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree.js","names":[],"sources":["../../../src/components/tree/tree.vue"],"sourcesContent":["<template>\n <u-scroll\n :class=\"className\"\n ref=\"scrollRef\"\n :content-class=\"[cls.e('wrap'), bem.is('virtual', virtualEnabled)]\"\n >\n <template v-if=\"virtualEnabled\">\n <UTreeNode\n v-for=\"{ node, key, offset, index } of virtualNodes\"\n :node=\"node\"\n :key=\"key\"\n :class=\"bem.is('selected', node.data === selectedData)\"\n :index=\"index\"\n :measure-element=\"measureElement\"\n :style=\"{ transform: `translateY(${offset}px)` }\"\n />\n </template>\n <template v-else>\n <UTreeNode\n v-for=\"node of nodes\"\n :node=\"node\"\n :key=\"node.key\"\n :class=\"bem.is('selected', node.data === selectedData)\"\n />\n </template>\n\n <div :class=\"cls.e('empty')\" v-if=\"!nodes.length\">\n <UEmpty />\n </div>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFormComponent, useFormFallbackProps, useVirtualizer } from '@veltra/compositions'\nimport { bem, nextFrame, scrollIntoContainerView } from '@veltra/utils'\nimport { computed, provide, shallowRef, useSlots, watch, watchEffect, type VNode } from 'vue'\n\nimport type { TreeProps, TreeEmit, _TreeExposed } from '../../types'\nimport type { ScrollExposed } from '../../types'\nimport { UEmpty } from '../empty'\nimport { UScroll } from '../scroll'\nimport { TreeDIKey, type TreeConText, type TreeSlotsScope } from './di'\nimport UTreeNode from './tree-node.vue'\nimport { useCheck } from './use-check'\nimport { useFilter } from './use-filter'\nimport { useSelect } from './use-select'\nimport { useTreeNodes } from './use-tree-nodes'\n\ndefineOptions({\n name: 'Tree'\n})\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n childrenKey: 'children',\n expandOnClickNode: false,\n checkStrictly: false,\n data: () => []\n})\n\nconst emit = defineEmits<TreeEmit>()\n\nconst cls = bem('tree')\n\nconst { formProps } = useFormComponent()\n\nconst { size } = useFormFallbackProps([formProps ?? {}, props], {\n size: 'default'\n})\n\nconst scrollRef = shallowRef<ScrollExposed>()\n\nconst className = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n bem.is('selectable', props.selectable),\n bem.is('checkable', props.checkable)\n ]\n})\n\ndefineSlots<{\n default: (props: TreeSlotsScope) => any\n}>()\n\nconst slots = useSlots()\n\nfunction getTreeSlotsNode(ctx: TreeSlotsScope): VNode[] | string | undefined {\n return (props.slots?.default ?? slots.default)?.(ctx) ?? ctx.node.label\n}\n\nconst { nodes, forest, getFlattedNodes, nodeDict } = useTreeNodes({\n props\n})\n\nwatch(\n [() => props.selectable, () => props.checkable, forest],\n ([s, c]) => {\n // 如果选择或勾选,则不更新节点\n if (s || c) return\n getFlattedNodes()\n },\n { immediate: true }\n)\n\nconst { filter } = useFilter({ forest, getFlattedNodes })\n\nconst { handleSelect, selectedData } = useSelect({\n props,\n emit,\n nodeDict,\n getFlattedNodes\n})\n\nconst { checkedData, toggleCheck } = useCheck({\n props,\n emit,\n nodeDict,\n getFlattedNodes\n})\n\nconst estimateSize = (): number => {\n // 基于 style.scss 中节点稳态高度推算的默认值;\n // 实际高度仍由 `measureElement` 通过 ResizeObserver 回填覆盖。\n switch (size.value) {\n case 'small':\n return 32\n case 'large':\n return 44\n default:\n return 36\n }\n}\n\n// 80 阈值内化为消费者侧语义;低于此值时不启用虚拟化。\nconst virtualEnabled = computed(() => nodes.value.length > 80)\n\nconst { virtualizer, items } = useVirtualizer({\n count: computed(() => nodes.value.length),\n scrollEl: () => scrollRef.value?.containerRef ?? null,\n // 仅在虚拟化启用时把内容容器的 height 撑开到 totalSize;\n // 关闭时传 null,hook 会清除此前写入的内联 height,回到 CSS 默认。\n contentEl: () => (virtualEnabled.value ? (scrollRef.value?.contentRef ?? null) : null),\n estimateSize,\n gap: 2,\n // 以节点稳定 key 作为虚拟项身份,展开 / 收起 / 过滤引起的 nodes 数组变更\n // 保留未移动节点的真实测量值,避免再次滚动首屏抖动。\n getItemKey: (i) => nodes.value[i]?.key ?? i\n})\n\n// 身份稳定性已由底层 `getItemKey` 保证;直接以 `node.key` 作为 Vue 层 key。\n// `item.index` 仅作为 nodes / snapshot 竞态边缘的 fallback。\nconst virtualNodes = computed(() => {\n const _nodes = nodes.value\n return items.value.map((item) => {\n const node = _nodes[item.index]!\n return {\n node,\n key: node?.key ?? item.index,\n offset: item.start,\n index: item.index\n }\n })\n})\n\nconst measureElement: (index: number, el: Element | null) => void = (index, el) =>\n virtualizer.measureElement(index, el)\n\nfunction scrollTo(index: number): void {\n virtualizer.scrollToIndex(index, { align: 'center' })\n}\n\nfunction scrollIntoView() {\n const { selectable, checkable, scrollToView } = props\n if (!scrollToView || (!selectable && !checkable) || !nodes.value) return\n let index = -1\n\n if (selectable) {\n if (!selectedData.value) return\n index = nodes.value.findIndex((node) => node.data === selectedData.value)\n } else if (checkable) {\n if (!checkedData.size) return\n index = nodes.value.findIndex((node) => checkedData.has(node.data))\n }\n\n if (virtualEnabled.value) {\n scrollTo(index)\n } else {\n const { contentRef, containerRef } = scrollRef.value ?? {}\n const el = contentRef?.children[index]\n el && scrollIntoContainerView(el as HTMLElement, containerRef ?? null)\n }\n}\n\nwatchEffect(() => {\n nextFrame(() => {\n scrollIntoView()\n })\n})\n\nprovide(TreeDIKey, {\n cls,\n selectedData,\n checkedData,\n getFlattedNodes,\n getTreeSlotsNode,\n treeEmit: emit as TreeEmit,\n treeProps: props as TreeProps,\n toggleCheck: toggleCheck as TreeConText['toggleCheck'],\n handleSelect: handleSelect as TreeConText['handleSelect']\n})\n\ndefineExpose<_TreeExposed>({\n filter,\n forest,\n nodes,\n checkNode: toggleCheck,\n selectNode: handleSelect,\n checkAll(check: boolean) {\n forest.value.roots.forEach((node) => {\n toggleCheck(node, check)\n })\n },\n getSelected() {\n return selectedData\n },\n getChecked() {\n return Array.from(checkedData)\n },\n scrollTo,\n\n expandAll() {\n forest.value.dfs((node) => {\n node.expanded = true\n })\n getFlattedNodes()\n },\n collapseAll() {\n forest.value.dfs((node) => {\n node.expanded = false\n })\n getFlattedNodes()\n }\n})\n</script>\n"],"mappings":"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"}
@@ -23,9 +23,9 @@ declare const __VLS_base: _$vue.DefineComponent<TreeProps, _TreeExposed, {}, {},
23
23
  }>, {
24
24
  labelKey: string;
25
25
  valueKey: string;
26
- childrenKey: string;
27
26
  data: Record<string, any>[];
28
27
  expandOnClickNode: boolean;
28
+ childrenKey: string;
29
29
  checkStrictly: boolean;
30
30
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
31
31
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,15 +1,38 @@
1
1
  import { nextTick, watch } from "vue";
2
2
  import { dfs, o } from "@cat-kit/core";
3
3
  //#region src/components/tree/use-check.ts
4
+ /**
5
+ * 展开目标节点的所有祖先。返回是否发生了实际展开。
6
+ */
7
+ function expandAncestors(node) {
8
+ let changed = false;
9
+ node.bubbleSet((n) => {
10
+ if (n.parent) {
11
+ if (n.parent.expanded) return false;
12
+ n.parent.expanded = true;
13
+ changed = true;
14
+ }
15
+ });
16
+ return changed;
17
+ }
4
18
  function useCheck(options) {
5
19
  const { emit, props, nodeDict, getFlattedNodes } = options;
6
20
  const checkedData = /* @__PURE__ */ new Set();
21
+ /**
22
+ * 上一次回显使用的 value 集合(仅限 props.checked)。
23
+ * 用于在相同 `nodeDict` 下走差集回显,避免每次全量清空再重建。
24
+ */
25
+ let lastCheckedSet = /* @__PURE__ */ new Set();
26
+ /** 最近一次参与差集比较的 nodeDict 引用;引用变化时视为整棵树重建。 */
27
+ let lastDict = null;
7
28
  function checkNode(node) {
29
+ if (node.checked) return;
8
30
  node.checked = true;
9
31
  if (node.parent) node.parent.childrenCheckCount++;
10
32
  checkedData.add(node.data);
11
33
  }
12
34
  function uncheckNode(node) {
35
+ if (!node.checked) return;
13
36
  node.checked = false;
14
37
  if (node.parent) node.parent.childrenCheckCount--;
15
38
  checkedData.delete(node.data);
@@ -18,23 +41,35 @@ function useCheck(options) {
18
41
  * 模型值是否由事件触发
19
42
  */
20
43
  let checkedByEvent = false;
21
- watch([() => props.checked, nodeDict], ([c, nodeDict]) => {
44
+ watch([() => props.checked, nodeDict], ([c, dict]) => {
22
45
  if (checkedByEvent || !props.checkable) return;
23
- if (!nodeDict.size) return;
24
- checkedData.clear();
25
- c?.forEach((v) => {
26
- const node = nodeDict.get(v);
46
+ if (!dict.size) return;
47
+ const nextSet = new Set(c ?? []);
48
+ const dictChanged = dict !== lastDict;
49
+ if (dictChanged) {
50
+ checkedData.clear();
51
+ lastCheckedSet = /* @__PURE__ */ new Set();
52
+ }
53
+ let mutated = false;
54
+ for (const v of lastCheckedSet) if (!nextSet.has(v)) {
55
+ const node = dict.get(v);
56
+ if (node) {
57
+ uncheckNode(node);
58
+ mutated = true;
59
+ }
60
+ }
61
+ let ancestorsExpanded = false;
62
+ for (const v of nextSet) if (!lastCheckedSet.has(v) || dictChanged) {
63
+ const node = dict.get(v);
27
64
  if (node) {
28
65
  checkNode(node);
29
- node.bubbleSet((node) => {
30
- if (node.parent) {
31
- if (node.parent.expanded) return false;
32
- node.parent.expanded = true;
33
- }
34
- });
66
+ mutated = true;
67
+ if (expandAncestors(node)) ancestorsExpanded = true;
35
68
  }
36
- });
37
- getFlattedNodes();
69
+ }
70
+ lastCheckedSet = nextSet;
71
+ lastDict = dict;
72
+ if (mutated || ancestorsExpanded) getFlattedNodes();
38
73
  }, { immediate: true });
39
74
  function handleCheck(node, ctrlKey) {
40
75
  const { checkStrictly } = props;
@@ -64,7 +99,10 @@ function useCheck(options) {
64
99
  checkedByEvent = true;
65
100
  check ? handleCheck(node, ctrlKey) : handleUncheck(node, ctrlKey);
66
101
  const checkedArr = Array.from(checkedData);
67
- emit("update:checked", checkedArr.map((item) => o(item).get(props.valueKey)), checkedArr);
102
+ const valueKey = props.valueKey;
103
+ const nextValues = checkedArr.map((item) => o(item).get(valueKey));
104
+ lastCheckedSet = new Set(nextValues);
105
+ emit("update:checked", nextValues, checkedArr);
68
106
  nextTick(() => {
69
107
  checkedByEvent = false;
70
108
  });
@@ -1 +1 @@
1
- {"version":3,"file":"use-check.js","names":[],"sources":["../../../src/components/tree/use-check.ts"],"sourcesContent":["import { dfs, o } from '@cat-kit/core'\nimport { nextTick, watch, type ComputedRef } from 'vue'\n\nimport type { TreeEmit, TreeProps } from '../../types'\nimport type { TreeNode } from './tree-node'\n\ninterface Options {\n emit: TreeEmit\n props: TreeProps\n nodeDict: ComputedRef<Map<any, TreeNode>>\n getFlattedNodes: () => void\n}\n\ninterface UseCheckReturned {\n checkedData: Set<Record<string, any>>\n toggleCheck: (node: TreeNode, check: boolean, ctrlKey?: boolean) => void\n}\n\nexport function useCheck(options: Options): UseCheckReturned {\n const { emit, props, nodeDict, getFlattedNodes } = options\n\n const checkedData = new Set<Record<string, any>>()\n\n function checkNode(node: TreeNode) {\n node.checked = true\n if (node.parent) {\n node.parent.childrenCheckCount++\n }\n checkedData.add(node.data)\n }\n\n function uncheckNode(node: TreeNode) {\n node.checked = false\n if (node.parent) {\n node.parent.childrenCheckCount--\n }\n checkedData.delete(node.data)\n }\n\n /**\n * 模型值是否由事件触发\n */\n let checkedByEvent = false\n\n // 回显\n watch(\n [() => props.checked, nodeDict],\n ([c, nodeDict]) => {\n // 事件已经触发模型变更了,所以不再进行下面的计算\n if (checkedByEvent || !props.checkable) return\n\n if (!nodeDict.size) return\n checkedData.clear()\n\n c?.forEach((v) => {\n const node = nodeDict.get(v)\n if (node) {\n checkNode(node)\n node.bubbleSet((node) => {\n if (node.parent) {\n if (node.parent.expanded) return false\n node.parent.expanded = true\n }\n })\n }\n })\n\n getFlattedNodes()\n },\n { immediate: true }\n )\n\n function handleCheck(node: TreeNode, ctrlKey?: boolean) {\n const { checkStrictly } = props\n\n if (ctrlKey) {\n checkNode(node)\n } else {\n dfs(\n node as unknown as Record<string, unknown>,\n (n) => {\n const tn = n as unknown as TreeNode\n !tn.disabled && checkNode(tn)\n },\n props.childrenKey ?? 'children'\n )\n }\n\n // 非严格选择时还需要更新祖先节点,\n // 一旦子节点全部选中,父节点也要设置为选中状态\n if (!checkStrictly) {\n node.bubbleSet((node) => {\n const { parent } = node\n\n if (parent) {\n const parentChecked = parent.childrenCheckCount === parent.children!.length\n\n parentChecked ? checkNode(parent) : uncheckNode(parent)\n }\n })\n }\n }\n\n function handleUncheck(node: TreeNode, ctrlKey?: boolean) {\n const { checkStrictly } = props\n if (ctrlKey) {\n uncheckNode(node)\n } else {\n dfs(\n node as unknown as Record<string, unknown>,\n (n) => {\n const tn = n as unknown as TreeNode\n !tn.disabled && uncheckNode(tn)\n },\n props.childrenKey ?? 'children'\n )\n }\n\n // 非严格模式下,取消选中时,需要更新父节点\n if (!checkStrictly) {\n node.bubbleSet((node) => {\n const { parent } = node\n if (parent) {\n uncheckNode(parent)\n }\n })\n }\n }\n\n function toggleCheck(node: TreeNode, check: boolean, ctrlKey?: boolean) {\n checkedByEvent = true\n\n check ? handleCheck(node, ctrlKey) : handleUncheck(node, ctrlKey)\n\n const checkedArr = Array.from(checkedData)\n\n emit(\n 'update:checked',\n checkedArr.map((item) => o(item).get(props.valueKey!)),\n checkedArr\n )\n\n nextTick(() => {\n checkedByEvent = false\n })\n }\n\n return { checkedData, toggleCheck }\n}\n"],"mappings":";;;AAkBA,SAAgB,SAAS,SAAoC;CAC3D,MAAM,EAAE,MAAM,OAAO,UAAU,oBAAoB;CAEnD,MAAM,8BAAc,IAAI,KAA0B;CAElD,SAAS,UAAU,MAAgB;AACjC,OAAK,UAAU;AACf,MAAI,KAAK,OACP,MAAK,OAAO;AAEd,cAAY,IAAI,KAAK,KAAK;;CAG5B,SAAS,YAAY,MAAgB;AACnC,OAAK,UAAU;AACf,MAAI,KAAK,OACP,MAAK,OAAO;AAEd,cAAY,OAAO,KAAK,KAAK;;;;;CAM/B,IAAI,iBAAiB;AAGrB,OACE,OAAO,MAAM,SAAS,SAAS,GAC9B,CAAC,GAAG,cAAc;AAEjB,MAAI,kBAAkB,CAAC,MAAM,UAAW;AAExC,MAAI,CAAC,SAAS,KAAM;AACpB,cAAY,OAAO;AAEnB,KAAG,SAAS,MAAM;GAChB,MAAM,OAAO,SAAS,IAAI,EAAE;AAC5B,OAAI,MAAM;AACR,cAAU,KAAK;AACf,SAAK,WAAW,SAAS;AACvB,SAAI,KAAK,QAAQ;AACf,UAAI,KAAK,OAAO,SAAU,QAAO;AACjC,WAAK,OAAO,WAAW;;MAEzB;;IAEJ;AAEF,mBAAiB;IAEnB,EAAE,WAAW,MAAM,CACpB;CAED,SAAS,YAAY,MAAgB,SAAmB;EACtD,MAAM,EAAE,kBAAkB;AAE1B,MAAI,QACF,WAAU,KAAK;MAEf,KACE,OACC,MAAM;GACL,MAAM,KAAK;AACX,IAAC,GAAG,YAAY,UAAU,GAAG;KAE/B,MAAM,eAAe,WACtB;AAKH,MAAI,CAAC,cACH,MAAK,WAAW,SAAS;GACvB,MAAM,EAAE,WAAW;AAEnB,OAAI,OACoB,QAAO,uBAAuB,OAAO,SAAU,SAErD,UAAU,OAAO,GAAG,YAAY,OAAO;IAEzD;;CAIN,SAAS,cAAc,MAAgB,SAAmB;EACxD,MAAM,EAAE,kBAAkB;AAC1B,MAAI,QACF,aAAY,KAAK;MAEjB,KACE,OACC,MAAM;GACL,MAAM,KAAK;AACX,IAAC,GAAG,YAAY,YAAY,GAAG;KAEjC,MAAM,eAAe,WACtB;AAIH,MAAI,CAAC,cACH,MAAK,WAAW,SAAS;GACvB,MAAM,EAAE,WAAW;AACnB,OAAI,OACF,aAAY,OAAO;IAErB;;CAIN,SAAS,YAAY,MAAgB,OAAgB,SAAmB;AACtE,mBAAiB;AAEjB,UAAQ,YAAY,MAAM,QAAQ,GAAG,cAAc,MAAM,QAAQ;EAEjE,MAAM,aAAa,MAAM,KAAK,YAAY;AAE1C,OACE,kBACA,WAAW,KAAK,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,SAAU,CAAC,EACtD,WACD;AAED,iBAAe;AACb,oBAAiB;IACjB;;AAGJ,QAAO;EAAE;EAAa;EAAa"}
1
+ {"version":3,"file":"use-check.js","names":[],"sources":["../../../src/components/tree/use-check.ts"],"sourcesContent":["import { dfs, o } from '@cat-kit/core'\nimport { nextTick, watch, type ComputedRef } from 'vue'\n\nimport type { TreeEmit, TreeProps } from '../../types'\nimport type { TreeNode } from './tree-node'\n\ninterface Options {\n emit: TreeEmit\n props: TreeProps\n nodeDict: ComputedRef<Map<any, TreeNode>>\n getFlattedNodes: () => void\n}\n\ninterface UseCheckReturned {\n checkedData: Set<Record<string, any>>\n toggleCheck: (node: TreeNode, check: boolean, ctrlKey?: boolean) => void\n}\n\n/**\n * 展开目标节点的所有祖先。返回是否发生了实际展开。\n */\nfunction expandAncestors(node: TreeNode): boolean {\n let changed = false\n node.bubbleSet((n) => {\n if (n.parent) {\n if (n.parent.expanded) return false\n n.parent.expanded = true\n changed = true\n }\n })\n return changed\n}\n\nexport function useCheck(options: Options): UseCheckReturned {\n const { emit, props, nodeDict, getFlattedNodes } = options\n\n const checkedData = new Set<Record<string, any>>()\n\n /**\n * 上一次回显使用的 value 集合(仅限 props.checked)。\n * 用于在相同 `nodeDict` 下走差集回显,避免每次全量清空再重建。\n */\n let lastCheckedSet = new Set<unknown>()\n /** 最近一次参与差集比较的 nodeDict 引用;引用变化时视为整棵树重建。 */\n let lastDict: Map<any, TreeNode> | null = null\n\n function checkNode(node: TreeNode): void {\n if (node.checked) return\n node.checked = true\n if (node.parent) {\n node.parent.childrenCheckCount++\n }\n checkedData.add(node.data)\n }\n\n function uncheckNode(node: TreeNode): void {\n if (!node.checked) return\n node.checked = false\n if (node.parent) {\n node.parent.childrenCheckCount--\n }\n checkedData.delete(node.data)\n }\n\n /**\n * 模型值是否由事件触发\n */\n let checkedByEvent = false\n\n // 回显\n watch(\n [() => props.checked, nodeDict],\n ([c, dict]) => {\n // 事件已经触发模型变更了,所以不再进行下面的计算\n if (checkedByEvent || !props.checkable) return\n if (!dict.size) return\n\n const nextSet = new Set<unknown>(c ?? [])\n\n // 森林重建(dict 引用变化)→ 走全量清空重建路径,\n // 因为所有 TreeNode 实例都是新的,上一轮缓存的 lastCheckedSet 失效。\n const dictChanged = dict !== lastDict\n if (dictChanged) {\n checkedData.clear()\n lastCheckedSet = new Set()\n }\n\n let mutated = false\n\n // 取消:上一轮有但本轮没有的 value\n for (const v of lastCheckedSet) {\n if (!nextSet.has(v)) {\n const node = dict.get(v)\n if (node) {\n uncheckNode(node)\n mutated = true\n }\n }\n }\n\n // 新增:本轮有但上一轮没有的 value\n let ancestorsExpanded = false\n for (const v of nextSet) {\n if (!lastCheckedSet.has(v) || dictChanged) {\n const node = dict.get(v)\n if (node) {\n checkNode(node)\n mutated = true\n if (expandAncestors(node)) {\n ancestorsExpanded = true\n }\n }\n }\n }\n\n lastCheckedSet = nextSet\n lastDict = dict\n\n // 只在真实发生变化时才触发扁平化,避免空转。\n if (mutated || ancestorsExpanded) {\n getFlattedNodes()\n }\n },\n { immediate: true }\n )\n\n function handleCheck(node: TreeNode, ctrlKey?: boolean) {\n const { checkStrictly } = props\n\n if (ctrlKey) {\n checkNode(node)\n } else {\n dfs(\n node as unknown as Record<string, unknown>,\n (n) => {\n const tn = n as unknown as TreeNode\n !tn.disabled && checkNode(tn)\n },\n props.childrenKey ?? 'children'\n )\n }\n\n // 非严格选择时还需要更新祖先节点,\n // 一旦子节点全部选中,父节点也要设置为选中状态\n if (!checkStrictly) {\n node.bubbleSet((node) => {\n const { parent } = node\n\n if (parent) {\n const parentChecked = parent.childrenCheckCount === parent.children!.length\n\n parentChecked ? checkNode(parent) : uncheckNode(parent)\n }\n })\n }\n }\n\n function handleUncheck(node: TreeNode, ctrlKey?: boolean) {\n const { checkStrictly } = props\n if (ctrlKey) {\n uncheckNode(node)\n } else {\n dfs(\n node as unknown as Record<string, unknown>,\n (n) => {\n const tn = n as unknown as TreeNode\n !tn.disabled && uncheckNode(tn)\n },\n props.childrenKey ?? 'children'\n )\n }\n\n // 非严格模式下,取消选中时,需要更新父节点\n if (!checkStrictly) {\n node.bubbleSet((node) => {\n const { parent } = node\n if (parent) {\n uncheckNode(parent)\n }\n })\n }\n }\n\n function toggleCheck(node: TreeNode, check: boolean, ctrlKey?: boolean) {\n checkedByEvent = true\n\n check ? handleCheck(node, ctrlKey) : handleUncheck(node, ctrlKey)\n\n const checkedArr = Array.from(checkedData)\n\n const valueKey = props.valueKey!\n const nextValues = checkedArr.map((item) => o(item).get(valueKey) as unknown)\n\n // 与外部 checked 保持差集缓存同步,避免下一次 watch 回调把刚勾选的值又误判为“新增”。\n lastCheckedSet = new Set(nextValues)\n\n emit('update:checked', nextValues as any[], checkedArr)\n\n nextTick(() => {\n checkedByEvent = false\n })\n }\n\n return { checkedData, toggleCheck }\n}\n"],"mappings":"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"}
@@ -1,14 +1,19 @@
1
1
  import { TreeNode as TreeNode$1 } from "./tree-node2.js";
2
- import { computed, shallowRef } from "vue";
2
+ import { computed, shallowRef, triggerRef, watch } from "vue";
3
3
  import { Forest } from "@cat-kit/core";
4
4
  //#region src/components/tree/use-tree-nodes.ts
5
5
  function useTreeNodes(options) {
6
6
  const { props } = options;
7
- /** 森林 */
8
- const forest = computed(() => {
7
+ const forestRef = shallowRef(new Forest({
8
+ data: [],
9
+ childrenKey: props.childrenKey ?? "children"
10
+ }));
11
+ const nodeDictRef = shallowRef(/* @__PURE__ */ new Map());
12
+ const nodes = shallowRef([]);
13
+ function buildForest() {
9
14
  const { disabledNode, expandAll = false, valueKey, labelKey } = props;
10
15
  const childrenKey = props.childrenKey ?? "children";
11
- function createNode(data, index, depth, _f, parent) {
16
+ function createNode(data, index, depth, parent) {
12
17
  const node = new TreeNode$1({
13
18
  data,
14
19
  index,
@@ -21,36 +26,58 @@ function useTreeNodes(options) {
21
26
  return node;
22
27
  }
23
28
  return new Forest({
24
- data: props.data,
29
+ data: props.data ?? [],
25
30
  childrenKey,
26
- createNode: disabledNode ? (data, index, depth, f, parent) => {
27
- const node = createNode(data, index, depth, f, parent);
31
+ createNode: disabledNode ? (data, index, depth, _f, parent) => {
32
+ const node = createNode(data, index, depth, parent);
28
33
  if (data) node.disabled = disabledNode(data, node) ?? false;
29
34
  return node;
30
- } : (data, index, depth, f, parent) => createNode(data, index, depth, f, parent)
35
+ } : (data, index, depth, _f, parent) => createNode(data, index, depth, parent)
31
36
  });
32
- });
33
- /** 碾平后的节点 */
34
- const nodes = shallowRef([]);
35
- /**
36
- * 节点的字典,key为指定的valueKey的值
37
- */
38
- const nodeDict = computed(() => {
39
- const dict = /* @__PURE__ */ new Map();
40
- forest.value.dfs((node) => {
41
- dict.set(node.key, node);
37
+ }
38
+ function rebuildForest() {
39
+ const nextForest = buildForest();
40
+ const nextDict = /* @__PURE__ */ new Map();
41
+ nextForest.dfs((node) => {
42
+ nextDict.set(node.key, node);
42
43
  });
43
- return dict;
44
- });
45
- /** 获取碾平后的节点 */
44
+ forestRef.value = nextForest;
45
+ nodeDictRef.value = nextDict;
46
+ getFlattedNodes();
47
+ }
48
+ /** 按展开与可见性扁平化:仅一次 DFS。 */
46
49
  function getFlattedNodes() {
47
- nodes.value = forest.value.flattenVisible((n) => n.expanded).filter((n) => n.visible);
50
+ const result = [];
51
+ const roots = forestRef.value.roots;
52
+ const stack = [];
53
+ for (let i = roots.length - 1; i >= 0; i--) stack.push(roots[i]);
54
+ while (stack.length) {
55
+ const node = stack.pop();
56
+ if (!node.visible) continue;
57
+ result.push(node);
58
+ if (node.expanded && node.children && node.children.length) for (let i = node.children.length - 1; i >= 0; i--) stack.push(node.children[i]);
59
+ }
60
+ nodes.value = result;
61
+ }
62
+ function triggerNodes() {
63
+ triggerRef(nodes);
48
64
  }
65
+ watch([
66
+ () => props.data,
67
+ () => props.disabledNode,
68
+ () => props.childrenKey,
69
+ () => props.valueKey,
70
+ () => props.labelKey,
71
+ () => props.expandAll
72
+ ], () => {
73
+ rebuildForest();
74
+ }, { immediate: true });
49
75
  return {
50
76
  nodes,
51
- forest,
77
+ forest: computed(() => forestRef.value),
52
78
  getFlattedNodes,
53
- nodeDict
79
+ nodeDict: computed(() => nodeDictRef.value),
80
+ triggerNodes
54
81
  };
55
82
  }
56
83
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"use-tree-nodes.js","names":["TreeNode"],"sources":["../../../src/components/tree/use-tree-nodes.ts"],"sourcesContent":["import { Forest } from '@cat-kit/core'\nimport { computed, shallowRef, type ShallowRef, type ComputedRef } from 'vue'\n\nimport type { TreeProps } from '../../types'\nimport { TreeNode } from './tree-node'\n\ninterface Options {\n props: TreeProps\n}\n\ninterface UseTreeNodesReturned {\n nodes: ShallowRef<TreeNode[]>\n forest: ComputedRef<Forest<Record<string, unknown>, any>>\n getFlattedNodes: () => void\n nodeDict: ComputedRef<Map<string | number, TreeNode>>\n}\nexport function useTreeNodes(options: Options): UseTreeNodesReturned {\n const { props } = options\n\n /** 森林 */\n const forest = computed(() => {\n const { disabledNode, expandAll = false, valueKey, labelKey } = props\n const childrenKey = props.childrenKey ?? 'children'\n\n function createNode(\n data: Record<string, any>,\n index: number,\n depth: number,\n _f: Forest<Record<string, unknown>, any>,\n parent?: TreeNode\n ) {\n const node = new TreeNode({\n data,\n index,\n depth,\n parent,\n valueKey: valueKey!,\n labelKey: labelKey!\n })\n node.expanded = expandAll\n return node\n }\n\n return new Forest<Record<string, unknown>, any>({\n data: props.data! as Record<string, unknown>[],\n childrenKey,\n createNode: disabledNode\n ? (data, index, depth, f, parent) => {\n const node = createNode(\n data as Record<string, any>,\n index,\n depth,\n f,\n parent as TreeNode\n )\n if (data) {\n node.disabled = disabledNode(data, node) ?? false\n }\n return node\n }\n : (data, index, depth, f, parent) =>\n createNode(data as Record<string, any>, index, depth, f, parent as TreeNode)\n })\n })\n\n /** 碾平后的节点 */\n const nodes = shallowRef<TreeNode[]>([])\n\n /**\n * 节点的字典,key为指定的valueKey的值\n */\n const nodeDict = computed(() => {\n const dict = new Map<string | number, TreeNode>()\n\n forest.value.dfs((node) => {\n dict.set(node.key, node)\n })\n\n return dict\n })\n\n /** 获取碾平后的节点 */\n function getFlattedNodes() {\n nodes.value = forest.value.flattenVisible((n) => n.expanded).filter((n) => n.visible)\n }\n\n return { nodes, forest, getFlattedNodes, nodeDict }\n}\n"],"mappings":";;;;AAgBA,SAAgB,aAAa,SAAwC;CACnE,MAAM,EAAE,UAAU;;CAGlB,MAAM,SAAS,eAAe;EAC5B,MAAM,EAAE,cAAc,YAAY,OAAO,UAAU,aAAa;EAChE,MAAM,cAAc,MAAM,eAAe;EAEzC,SAAS,WACP,MACA,OACA,OACA,IACA,QACA;GACA,MAAM,OAAO,IAAIA,WAAS;IACxB;IACA;IACA;IACA;IACU;IACA;IACX,CAAC;AACF,QAAK,WAAW;AAChB,UAAO;;AAGT,SAAO,IAAI,OAAqC;GAC9C,MAAM,MAAM;GACZ;GACA,YAAY,gBACP,MAAM,OAAO,OAAO,GAAG,WAAW;IACjC,MAAM,OAAO,WACX,MACA,OACA,OACA,GACA,OACD;AACD,QAAI,KACF,MAAK,WAAW,aAAa,MAAM,KAAK,IAAI;AAE9C,WAAO;QAER,MAAM,OAAO,OAAO,GAAG,WACtB,WAAW,MAA6B,OAAO,OAAO,GAAG,OAAmB;GACnF,CAAC;GACF;;CAGF,MAAM,QAAQ,WAAuB,EAAE,CAAC;;;;CAKxC,MAAM,WAAW,eAAe;EAC9B,MAAM,uBAAO,IAAI,KAAgC;AAEjD,SAAO,MAAM,KAAK,SAAS;AACzB,QAAK,IAAI,KAAK,KAAK,KAAK;IACxB;AAEF,SAAO;GACP;;CAGF,SAAS,kBAAkB;AACzB,QAAM,QAAQ,OAAO,MAAM,gBAAgB,MAAM,EAAE,SAAS,CAAC,QAAQ,MAAM,EAAE,QAAQ;;AAGvF,QAAO;EAAE;EAAO;EAAQ;EAAiB;EAAU"}
1
+ {"version":3,"file":"use-tree-nodes.js","names":["TreeNode"],"sources":["../../../src/components/tree/use-tree-nodes.ts"],"sourcesContent":["import { Forest } from '@cat-kit/core'\nimport { computed, shallowRef, triggerRef, watch, type ShallowRef, type ComputedRef } from 'vue'\n\nimport type { TreeProps } from '../../types'\nimport { TreeNode } from './tree-node'\n\ninterface Options {\n props: TreeProps\n}\n\ninterface UseTreeNodesReturned {\n nodes: ShallowRef<TreeNode[]>\n forest: ComputedRef<Forest<Record<string, unknown>, any>>\n getFlattedNodes: () => void\n nodeDict: ComputedRef<Map<string | number, TreeNode>>\n /** 强制刷新 `nodes` 的订阅者(供字段级别变化但列表引用不变时手动触发)。 */\n triggerNodes: () => void\n}\n\nexport function useTreeNodes(options: Options): UseTreeNodesReturned {\n const { props } = options\n\n // forest / nodeDict 保持为 shallowRef 而非 computed:\n // - computed 会因为任一 prop 变化(尤其是内联 disabledNode 函数)而整棵重建,\n // 丢失 expanded/checked/visible 等运行时状态;\n // - 显式 watch 仅在真正影响结构的输入变化时才重建,行为可控、可测量。\n const forestRef = shallowRef<Forest<Record<string, unknown>, any>>(\n new Forest<Record<string, unknown>, any>({\n data: [],\n childrenKey: props.childrenKey ?? 'children'\n })\n )\n\n const nodeDictRef = shallowRef<Map<string | number, TreeNode>>(new Map())\n\n const nodes = shallowRef<TreeNode[]>([])\n\n function buildForest(): Forest<Record<string, unknown>, any> {\n const { disabledNode, expandAll = false, valueKey, labelKey } = props\n const childrenKey = props.childrenKey ?? 'children'\n\n function createNode(\n data: Record<string, any>,\n index: number,\n depth: number,\n parent?: TreeNode\n ): TreeNode {\n const node = new TreeNode({\n data,\n index,\n depth,\n parent,\n valueKey: valueKey!,\n labelKey: labelKey!\n })\n node.expanded = expandAll\n return node\n }\n\n return new Forest<Record<string, unknown>, any>({\n data: (props.data ?? []) as Record<string, unknown>[],\n childrenKey,\n createNode: disabledNode\n ? (data, index, depth, _f, parent) => {\n const node = createNode(data as Record<string, any>, index, depth, parent as TreeNode)\n if (data) {\n node.disabled = disabledNode(data as Record<string, any>, node) ?? false\n }\n return node\n }\n : (data, index, depth, _f, parent) =>\n createNode(data as Record<string, any>, index, depth, parent as TreeNode)\n })\n }\n\n function rebuildForest(): void {\n const nextForest = buildForest()\n const nextDict = new Map<string | number, TreeNode>()\n\n // 构建森林的同时一次 DFS 同步填充字典,\n // 避免旧版 “forest computed → nodeDict computed” 两次完整遍历。\n nextForest.dfs((node) => {\n nextDict.set(node.key, node)\n })\n\n forestRef.value = nextForest\n nodeDictRef.value = nextDict\n\n getFlattedNodes()\n }\n\n /** 按展开与可见性扁平化:仅一次 DFS。 */\n function getFlattedNodes(): void {\n const result: TreeNode[] = []\n const roots = forestRef.value.roots as TreeNode[]\n\n const stack: TreeNode[] = []\n for (let i = roots.length - 1; i >= 0; i--) {\n stack.push(roots[i]!)\n }\n\n while (stack.length) {\n const node = stack.pop()!\n if (!node.visible) continue\n result.push(node)\n if (node.expanded && node.children && node.children.length) {\n for (let i = node.children.length - 1; i >= 0; i--) {\n stack.push(node.children[i]!)\n }\n }\n }\n\n nodes.value = result\n }\n\n function triggerNodes(): void {\n triggerRef(nodes)\n }\n\n watch(\n [\n () => props.data,\n () => props.disabledNode,\n () => props.childrenKey,\n () => props.valueKey,\n () => props.labelKey,\n () => props.expandAll\n ],\n () => {\n rebuildForest()\n },\n { immediate: true }\n )\n\n const forest = computed(() => forestRef.value)\n const nodeDict = computed(() => nodeDictRef.value)\n\n return { nodes, forest, getFlattedNodes, nodeDict, triggerNodes }\n}\n"],"mappings":";;;;AAmBA,SAAgB,aAAa,SAAwC;CACnE,MAAM,EAAE,UAAU;CAMlB,MAAM,YAAY,WAChB,IAAI,OAAqC;EACvC,MAAM,EAAE;EACR,aAAa,MAAM,eAAe;EACnC,CAAC,CACH;CAED,MAAM,cAAc,2BAA2C,IAAI,KAAK,CAAC;CAEzE,MAAM,QAAQ,WAAuB,EAAE,CAAC;CAExC,SAAS,cAAoD;EAC3D,MAAM,EAAE,cAAc,YAAY,OAAO,UAAU,aAAa;EAChE,MAAM,cAAc,MAAM,eAAe;EAEzC,SAAS,WACP,MACA,OACA,OACA,QACU;GACV,MAAM,OAAO,IAAIA,WAAS;IACxB;IACA;IACA;IACA;IACU;IACA;IACX,CAAC;AACF,QAAK,WAAW;AAChB,UAAO;;AAGT,SAAO,IAAI,OAAqC;GAC9C,MAAO,MAAM,QAAQ,EAAE;GACvB;GACA,YAAY,gBACP,MAAM,OAAO,OAAO,IAAI,WAAW;IAClC,MAAM,OAAO,WAAW,MAA6B,OAAO,OAAO,OAAmB;AACtF,QAAI,KACF,MAAK,WAAW,aAAa,MAA6B,KAAK,IAAI;AAErE,WAAO;QAER,MAAM,OAAO,OAAO,IAAI,WACvB,WAAW,MAA6B,OAAO,OAAO,OAAmB;GAChF,CAAC;;CAGJ,SAAS,gBAAsB;EAC7B,MAAM,aAAa,aAAa;EAChC,MAAM,2BAAW,IAAI,KAAgC;AAIrD,aAAW,KAAK,SAAS;AACvB,YAAS,IAAI,KAAK,KAAK,KAAK;IAC5B;AAEF,YAAU,QAAQ;AAClB,cAAY,QAAQ;AAEpB,mBAAiB;;;CAInB,SAAS,kBAAwB;EAC/B,MAAM,SAAqB,EAAE;EAC7B,MAAM,QAAQ,UAAU,MAAM;EAE9B,MAAM,QAAoB,EAAE;AAC5B,OAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,IACrC,OAAM,KAAK,MAAM,GAAI;AAGvB,SAAO,MAAM,QAAQ;GACnB,MAAM,OAAO,MAAM,KAAK;AACxB,OAAI,CAAC,KAAK,QAAS;AACnB,UAAO,KAAK,KAAK;AACjB,OAAI,KAAK,YAAY,KAAK,YAAY,KAAK,SAAS,OAClD,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,IAC7C,OAAM,KAAK,KAAK,SAAS,GAAI;;AAKnC,QAAM,QAAQ;;CAGhB,SAAS,eAAqB;AAC5B,aAAW,MAAM;;AAGnB,OACE;QACQ,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;EACb,QACK;AACJ,iBAAe;IAEjB,EAAE,WAAW,MAAM,CACpB;AAKD,QAAO;EAAE;EAAO,QAHD,eAAe,UAAU,MAAM;EAGtB;EAAiB,UAFxB,eAAe,YAAY,MAAM;EAEC;EAAc"}
@@ -25,14 +25,14 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
25
25
  "onUpdate:text"?: ((text?: string | undefined) => any) | undefined;
26
26
  }>, {
27
27
  disabled: boolean;
28
- placeholder: string;
29
- clearable: boolean;
30
28
  readonly: boolean;
31
29
  labelKey: string;
32
30
  valueKey: string;
33
- filterable: boolean;
31
+ placeholder: string;
32
+ clearable: boolean;
34
33
  minWidth: string;
35
34
  expandAll: boolean;
35
+ filterable: boolean;
36
36
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
37
37
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
38
  declare const _default: typeof __VLS_export;
@@ -7,8 +7,8 @@ type __VLS_Slots = {} & {
7
7
  default?: (props: typeof __VLS_7) => any;
8
8
  };
9
9
  declare const __VLS_base: _$vue.DefineComponent<WatermarkProps, {}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<WatermarkProps> & Readonly<{}>, {
10
- fontSize: number;
11
10
  route: number;
11
+ fontSize: number;
12
12
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
13
13
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
14
14
  declare const _default: typeof __VLS_export;