@veltra/desktop 1.0.8 → 1.0.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.
Files changed (220) 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/batch-edit.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit.vue.d.ts +2 -2
  6. package/dist/components/batch-edit/use-edit.js.map +1 -1
  7. package/dist/components/button/button.js.map +1 -1
  8. package/dist/components/button/button.vue.d.ts +1 -1
  9. package/dist/components/cascade/cascade.js +15 -13
  10. package/dist/components/cascade/cascade.js.map +1 -1
  11. package/dist/components/cascade/cascade.vue.d.ts +7 -7
  12. package/dist/components/cascade/node.js.map +1 -1
  13. package/dist/components/cascade/use-check.js +3 -2
  14. package/dist/components/cascade/use-check.js.map +1 -1
  15. package/dist/components/cascade/use-select.js +3 -3
  16. package/dist/components/cascade/use-select.js.map +1 -1
  17. package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
  18. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  19. package/dist/components/date-panel/date-panel.js.map +1 -1
  20. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  21. package/dist/components/date-picker/date-picker.js +9 -9
  22. package/dist/components/date-picker/date-picker.js.map +1 -1
  23. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  24. package/dist/components/date-range-picker/date-range-picker.js +9 -9
  25. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  26. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  27. package/dist/components/drawer/drawer.js.map +1 -1
  28. package/dist/components/empty/empty.vue.d.ts +1 -1
  29. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  30. package/dist/components/expression-editor/internal/editor-runtime/create-runtime.js.map +1 -1
  31. package/dist/components/expression-editor/internal/features/commands/register-command-packs.js.map +1 -1
  32. package/dist/components/expression-editor/internal/features/insertion/insertion-service.js.map +1 -1
  33. package/dist/components/expression-editor/use-decorators.js.map +1 -1
  34. package/dist/components/expression-editor/use-expression-drag-drop.js.map +1 -1
  35. package/dist/components/file-picker/file-picker.js.map +1 -1
  36. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  37. package/dist/components/file-picker/helper.js +19 -7
  38. package/dist/components/file-picker/helper.js.map +1 -1
  39. package/dist/components/file-viewer/file-viewer.js +294 -0
  40. package/dist/components/file-viewer/file-viewer.js.map +1 -0
  41. package/dist/components/file-viewer/file-viewer.vue.d.ts +42 -0
  42. package/dist/components/file-viewer/helper.js +185 -0
  43. package/dist/components/file-viewer/helper.js.map +1 -0
  44. package/dist/components/file-viewer/index.d.ts +1 -0
  45. package/dist/components/file-viewer/previewers/docx-previewer.js +76 -0
  46. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -0
  47. package/dist/components/file-viewer/previewers/image-previewer.js +64 -0
  48. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -0
  49. package/dist/components/file-viewer/previewers/pdf-previewer.js +90 -0
  50. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -0
  51. package/dist/components/file-viewer/previewers/sheet-previewer.js +175 -0
  52. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -0
  53. package/dist/components/file-viewer/previewers/text-previewer.js +74 -0
  54. package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -0
  55. package/dist/components/file-viewer/previewers/video-previewer.js +62 -0
  56. package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -0
  57. package/dist/components/file-viewer/style.d.ts +1 -0
  58. package/dist/components/file-viewer/style.js +4 -0
  59. package/dist/components/file-viewer/style2.css +445 -0
  60. package/dist/components/form/form-model.js +2 -1
  61. package/dist/components/form/form-model.js.map +1 -1
  62. package/dist/components/grid/grid.js.map +1 -1
  63. package/dist/components/grid/use-responsive.js.map +1 -1
  64. package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
  65. package/dist/components/index.d.ts +12 -9
  66. package/dist/components/input/input.vue.d.ts +3 -3
  67. package/dist/components/list/list.js.map +1 -1
  68. package/dist/components/loading/directive.js.map +1 -1
  69. package/dist/components/menu/menu-item.js.map +1 -1
  70. package/dist/components/message/message2.js.map +1 -1
  71. package/dist/components/multi-select/multi-select-option.js +14 -10
  72. package/dist/components/multi-select/multi-select-option.js.map +1 -1
  73. package/dist/components/multi-select/multi-select.js +16 -19
  74. package/dist/components/multi-select/multi-select.js.map +1 -1
  75. package/dist/components/multi-select/multi-select.vue.d.ts +4 -4
  76. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +7 -7
  77. package/dist/components/notification/index.js.map +1 -1
  78. package/dist/components/notification/notification.vue.d.ts +1 -1
  79. package/dist/components/number-input/number-input.js.map +1 -1
  80. package/dist/components/number-input/number-input.vue.d.ts +1 -1
  81. package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
  82. package/dist/components/palette/color-transform.js.map +1 -1
  83. package/dist/components/palette/di.js.map +1 -1
  84. package/dist/components/palette/palette-alpha.js +7 -10
  85. package/dist/components/palette/palette-alpha.js.map +1 -1
  86. package/dist/components/palette/palette-hue.js +5 -7
  87. package/dist/components/palette/palette-hue.js.map +1 -1
  88. package/dist/components/palette/palette-sv.js +8 -10
  89. package/dist/components/palette/palette-sv.js.map +1 -1
  90. package/dist/components/palette/palette.js +22 -24
  91. package/dist/components/palette/palette.js.map +1 -1
  92. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  93. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +2 -2
  94. package/dist/components/progress/progress.js +1 -1
  95. package/dist/components/progress/progress.js.map +1 -1
  96. package/dist/components/progress-nodes/progress-nodes.js.map +1 -1
  97. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  98. package/dist/components/rich-text-editor/rich-text-editor.js.map +1 -1
  99. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
  100. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  101. package/dist/components/scroll/scroll.js.map +1 -1
  102. package/dist/components/select/select.js +14 -12
  103. package/dist/components/select/select.js.map +1 -1
  104. package/dist/components/select/select.vue.d.ts +3 -3
  105. package/dist/components/slider/slider-thumb.js +10 -13
  106. package/dist/components/slider/slider-thumb.js.map +1 -1
  107. package/dist/components/slider/slider.js +12 -15
  108. package/dist/components/slider/slider.js.map +1 -1
  109. package/dist/components/table/di.js.map +1 -1
  110. package/dist/components/table/node/row.d.ts +10 -1
  111. package/dist/components/table/node/row.js +9 -0
  112. package/dist/components/table/node/row.js.map +1 -1
  113. package/dist/components/table/table-body.js +25 -26
  114. package/dist/components/table/table-body.js.map +1 -1
  115. package/dist/components/table/table-cell.js +16 -9
  116. package/dist/components/table/table-cell.js.map +1 -1
  117. package/dist/components/table/table-row.js +77 -19
  118. package/dist/components/table/table-row.js.map +1 -1
  119. package/dist/components/table/table.js +75 -34
  120. package/dist/components/table/table.js.map +1 -1
  121. package/dist/components/table/table.vue.d.ts +2 -2
  122. package/dist/components/table/use-columns.js +4 -0
  123. package/dist/components/table/use-columns.js.map +1 -1
  124. package/dist/components/table/use-fixed-columns.js +27 -8
  125. package/dist/components/table/use-fixed-columns.js.map +1 -1
  126. package/dist/components/table/use-rows.js +2 -2
  127. package/dist/components/table/use-rows.js.map +1 -1
  128. package/dist/components/table/use-table.js +50 -3
  129. package/dist/components/table/use-table.js.map +1 -1
  130. package/dist/components/tabs/index.d.ts +3 -1
  131. package/dist/components/tabs/style2.css +170 -107
  132. package/dist/components/tabs/tabs-horizontal.js +133 -0
  133. package/dist/components/tabs/tabs-horizontal.js.map +1 -0
  134. package/dist/components/tabs/tabs-horizontal.vue.d.ts +40 -0
  135. package/dist/components/tabs/tabs-vertical.js +89 -0
  136. package/dist/components/tabs/tabs-vertical.js.map +1 -0
  137. package/dist/components/tabs/tabs-vertical.vue.d.ts +38 -0
  138. package/dist/components/tabs/tabs.js +75 -105
  139. package/dist/components/tabs/tabs.js.map +1 -1
  140. package/dist/components/tabs/tabs.vue.d.ts +9 -23
  141. package/dist/components/tabs/use-tabs-bar.js +105 -0
  142. package/dist/components/tabs/use-tabs-bar.js.map +1 -0
  143. package/dist/components/text/text.js.map +1 -1
  144. package/dist/components/textarea/textarea.js.map +1 -1
  145. package/dist/components/textarea/textarea.vue.d.ts +4 -4
  146. package/dist/components/theme/theme.js.map +1 -1
  147. package/dist/components/tip/tip.vue.d.ts +1 -1
  148. package/dist/components/tree/style2.css +45 -23
  149. package/dist/components/tree/tree-node.js +50 -35
  150. package/dist/components/tree/tree-node.js.map +1 -1
  151. package/dist/components/tree/tree-node2.js.map +1 -1
  152. package/dist/components/tree/tree.js +27 -20
  153. package/dist/components/tree/tree.js.map +1 -1
  154. package/dist/components/tree/tree.vue.d.ts +5 -5
  155. package/dist/components/tree/use-check.js +52 -14
  156. package/dist/components/tree/use-check.js.map +1 -1
  157. package/dist/components/tree/use-tree-nodes.js +51 -24
  158. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  159. package/dist/components/tree-select/tree-select.vue.d.ts +7 -7
  160. package/dist/index.d.ts +57 -53
  161. package/dist/index.js +51 -48
  162. package/dist/types/file-viewer.d.ts +71 -0
  163. package/dist/types/index.d.ts +3 -2
  164. package/dist/types/tabs.d.ts +106 -14
  165. package/dist/types/tree.d.ts +6 -1
  166. package/package.json +32 -31
  167. package/src/components/cascade/cascade.vue +18 -18
  168. package/src/components/cascade/node.ts +4 -1
  169. package/src/components/cascade/use-check.ts +5 -4
  170. package/src/components/cascade/use-select.ts +4 -5
  171. package/src/components/date-picker/date-picker.vue +10 -9
  172. package/src/components/date-range-picker/date-range-picker.vue +12 -12
  173. package/src/components/file-picker/helper.ts +21 -10
  174. package/src/components/file-viewer/file-viewer.vue +311 -0
  175. package/src/components/file-viewer/helper.ts +193 -0
  176. package/src/components/file-viewer/index.ts +1 -0
  177. package/src/components/file-viewer/previewers/docx-previewer.vue +78 -0
  178. package/src/components/file-viewer/previewers/image-previewer.vue +64 -0
  179. package/src/components/file-viewer/previewers/pdf-previewer.vue +90 -0
  180. package/src/components/file-viewer/previewers/sheet-previewer.vue +210 -0
  181. package/src/components/file-viewer/previewers/text-previewer.vue +76 -0
  182. package/src/components/file-viewer/previewers/video-previewer.vue +72 -0
  183. package/src/components/file-viewer/style.scss +504 -0
  184. package/src/components/file-viewer/style.ts +4 -0
  185. package/src/components/index.ts +2 -0
  186. package/src/components/multi-select/multi-select-option.vue +16 -3
  187. package/src/components/multi-select/multi-select.vue +17 -9
  188. package/src/components/palette/di.ts +5 -3
  189. package/src/components/palette/palette-alpha.vue +7 -10
  190. package/src/components/palette/palette-hue.vue +5 -7
  191. package/src/components/palette/palette-sv.vue +12 -14
  192. package/src/components/palette/palette.vue +19 -21
  193. package/src/components/progress/progress.vue +1 -1
  194. package/src/components/select/select.vue +21 -19
  195. package/src/components/slider/slider-thumb.vue +11 -14
  196. package/src/components/slider/slider.vue +15 -15
  197. package/src/components/table/di.ts +58 -43
  198. package/src/components/table/node/row.ts +10 -1
  199. package/src/components/table/table-body.vue +28 -33
  200. package/src/components/table/table-cell.vue +17 -9
  201. package/src/components/table/table-row.tsx +83 -17
  202. package/src/components/table/table.vue +72 -45
  203. package/src/components/table/use-fixed-columns.ts +46 -12
  204. package/src/components/table/use-rows.ts +14 -3
  205. package/src/components/table/use-table.ts +27 -3
  206. package/src/components/tabs/index.ts +2 -0
  207. package/src/components/tabs/style.scss +219 -133
  208. package/src/components/tabs/tabs-horizontal.vue +128 -0
  209. package/src/components/tabs/tabs-vertical.vue +77 -0
  210. package/src/components/tabs/tabs.vue +56 -127
  211. package/src/components/tabs/use-tabs-bar.ts +119 -0
  212. package/src/components/tree/style.scss +74 -29
  213. package/src/components/tree/tree-node.vue +10 -4
  214. package/src/components/tree/tree.vue +39 -12
  215. package/src/components/tree/use-check.ts +79 -23
  216. package/src/components/tree/use-tree-nodes.ts +85 -34
  217. package/src/types/file-viewer.ts +69 -0
  218. package/src/types/index.ts +1 -0
  219. package/src/types/tabs.ts +113 -19
  220. package/src/types/tree.ts +6 -1
@@ -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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"}
@@ -7,25 +7,25 @@ type __VLS_Slots = {
7
7
  default: (props: TreeSlotsScope) => any;
8
8
  };
9
9
  declare const __VLS_base: _$vue.DefineComponent<TreeProps, _TreeExposed, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {} & {
10
- "update:checked": (checked: any[], checkedData: Record<string, any>[]) => any;
11
- "update:selected": (selected?: any, selectedData?: Record<string, any> | undefined, node?: TreeNode<Record<string, any>> | undefined) => any;
12
10
  expand: (node: TreeNode<Record<string, any>>) => any;
13
11
  "node-click": (node: TreeNode<Record<string, any>>) => any;
12
+ "update:selected": (selected?: any, selectedData?: Record<string, any> | undefined, node?: TreeNode<Record<string, any>> | undefined) => any;
13
+ "update:checked": (checked: any[], checkedData: Record<string, any>[]) => any;
14
14
  "node-contextmenu": (event: MouseEvent, node: TreeNode<Record<string, any>>) => any;
15
15
  "selected-synced": (selected?: Record<string, any> | undefined) => any;
16
16
  }, string, _$vue.PublicProps, Readonly<TreeProps> & Readonly<{
17
- "onUpdate:checked"?: ((checked: any[], checkedData: Record<string, any>[]) => any) | undefined;
18
- "onUpdate:selected"?: ((selected?: any, selectedData?: Record<string, any> | undefined, node?: TreeNode<Record<string, any>> | undefined) => any) | undefined;
19
17
  onExpand?: ((node: TreeNode<Record<string, any>>) => any) | undefined;
20
18
  "onNode-click"?: ((node: TreeNode<Record<string, any>>) => any) | undefined;
19
+ "onUpdate:selected"?: ((selected?: any, selectedData?: Record<string, any> | undefined, node?: TreeNode<Record<string, any>> | undefined) => any) | undefined;
20
+ "onUpdate:checked"?: ((checked: any[], checkedData: Record<string, any>[]) => any) | undefined;
21
21
  "onNode-contextmenu"?: ((event: MouseEvent, node: TreeNode<Record<string, any>>) => any) | undefined;
22
22
  "onSelected-synced"?: ((selected?: Record<string, any> | undefined) => any) | undefined;
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,MAGlB;EAAE;EAAiB,UAFxB,eAAe,YAAY,MAEK;EAAE;EAAc"}
@@ -15,24 +15,24 @@ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
15
15
  declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {
16
16
  "update:modelValue": (value: string | number | undefined) => any;
17
17
  } & {
18
- clear: () => any;
19
18
  change: (value?: string | number | undefined, selectedData?: Record<string, any> | undefined) => any;
19
+ clear: () => any;
20
20
  "update:text": (text?: string | undefined) => any;
21
21
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
22
22
  "onUpdate:modelValue"?: ((value: string | number | undefined) => any) | undefined;
23
- onClear?: (() => any) | undefined;
24
23
  onChange?: ((value?: string | number | undefined, selectedData?: Record<string, any> | undefined) => any) | undefined;
24
+ onClear?: (() => any) | undefined;
25
25
  "onUpdate:text"?: ((text?: string | undefined) => any) | undefined;
26
26
  }>, {
27
- disabled: boolean;
28
- placeholder: string;
29
- clearable: boolean;
30
- readonly: boolean;
31
27
  labelKey: string;
32
28
  valueKey: string;
33
- filterable: boolean;
29
+ disabled: boolean;
30
+ readonly: boolean;
34
31
  minWidth: string;
32
+ placeholder: string;
33
+ clearable: boolean;
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;