@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
@@ -24,7 +24,7 @@ defineOptions({
24
24
  name: 'PaletteSV'
25
25
  })
26
26
 
27
- const { cls, hueRGB, HSV, updateSV, updater } = inject(PaletteDIKey)!
27
+ const { cls, hueRGB, HSV, updateSV, userAction } = inject(PaletteDIKey)!
28
28
 
29
29
  const svRef = shallowRef<HTMLDivElement>()
30
30
 
@@ -58,21 +58,19 @@ const svThumbStyle = computed(() => {
58
58
  }
59
59
  })
60
60
 
61
- function updateThumb(offsetX: number, offsetY: number) {
62
- updater.updateAndLock(() => {
63
- transform.x = offsetX
64
- transform.y = offsetY
61
+ const updateThumb = userAction((offsetX: number, offsetY: number) => {
62
+ transform.x = offsetX
63
+ transform.y = offsetY
65
64
 
66
- // 根据画布位置计算饱和度和亮度
67
- // 水平方向表示饱和度,从左到右饱和度逐渐增高
68
- // 垂直方向表示亮度,上亮下暗
69
- const s = Math.max(0, Math.min(1, offsetX / canvasSize.width))
70
- const v = Math.max(0, Math.min(1, 1 - offsetY / canvasSize.height))
65
+ // 根据画布位置计算饱和度和亮度
66
+ // 水平方向表示饱和度,从左到右饱和度逐渐增高
67
+ // 垂直方向表示亮度,上亮下暗
68
+ const s = Math.max(0, Math.min(1, offsetX / canvasSize.width))
69
+ const v = Math.max(0, Math.min(1, 1 - offsetY / canvasSize.height))
71
70
 
72
- // 更新饱和度和亮度
73
- updateSV({ s, v })
74
- })
75
- }
71
+ // 更新饱和度和亮度
72
+ updateSV({ s, v })
73
+ })
76
74
 
77
75
  const svDragger = useDrag({
78
76
  target: svRef,
@@ -24,7 +24,7 @@
24
24
  </template>
25
25
 
26
26
  <script lang="ts" setup>
27
- import { useFormComponent, useFormFallbackProps, useUpdateLock } from '@veltra/compositions'
27
+ import { useFormComponent, useFormFallbackProps, useUserAction } from '@veltra/compositions'
28
28
  import { bem } from '@veltra/utils'
29
29
  import { computed, provide, shallowRef, useTemplateRef, watch } from 'vue'
30
30
 
@@ -61,7 +61,7 @@ const { HSV, alpha, ...rest } = useHSV()
61
61
 
62
62
  const color = defineModel<string>()
63
63
  const RGB = computed(() => HSV2RGB(HSV))
64
- const updater = useUpdateLock()
64
+ const { userAction, isUserActive } = useUserAction()
65
65
 
66
66
  watch([alpha, RGB], ([alpha, RGB]) => {
67
67
  color.value = `#${RGB2HEX(RGB, alpha)}`
@@ -69,13 +69,11 @@ watch([alpha, RGB], ([alpha, RGB]) => {
69
69
 
70
70
  const visible = shallowRef(false)
71
71
 
72
- function handleClear() {
73
- updater.updateAndLock(() => {
74
- color.value = ''
75
- rest.updateAlpha(1)
76
- visible.value = false
77
- })
78
- }
72
+ const handleClear = userAction(() => {
73
+ color.value = ''
74
+ rest.updateAlpha(1)
75
+ visible.value = false
76
+ })
79
77
 
80
78
  const paletteSVRef = useTemplateRef('palette-sv')
81
79
  const paletteHueRef = useTemplateRef('palette-hue')
@@ -85,18 +83,17 @@ watch(
85
83
  color,
86
84
  (color) => {
87
85
  if (!color) return
86
+ if (isUserActive()) return
87
+
88
+ const { RGB, alpha } = HEX2RGBA(color)
89
+ const hsv = RGB2HSV(RGB)
90
+ rest.updateHue(hsv.h)
91
+ rest.updateSV({ s: hsv.s, v: hsv.v })
92
+ rest.updateAlpha(alpha)
88
93
 
89
- updater.update(() => {
90
- const { RGB, alpha } = HEX2RGBA(color)
91
- const hsv = RGB2HSV(RGB)
92
- rest.updateHue(hsv.h)
93
- rest.updateSV({ s: hsv.s, v: hsv.v })
94
- rest.updateAlpha(alpha)
95
-
96
- paletteSVRef.value?.init()
97
- paletteHueRef.value?.init()
98
- paletteAlphaRef.value?.init()
99
- })
94
+ paletteSVRef.value?.init()
95
+ paletteHueRef.value?.init()
96
+ paletteAlphaRef.value?.init()
100
97
  },
101
98
  { immediate: true }
102
99
  )
@@ -106,7 +103,8 @@ provide(PaletteDIKey, {
106
103
  HSV,
107
104
  RGB,
108
105
  alpha,
109
- updater,
106
+ userAction,
107
+ isUserActive,
110
108
  ...rest
111
109
  })
112
110
  </script>
@@ -78,7 +78,7 @@ const className = computed(() => {
78
78
 
79
79
  /** 描边 */
80
80
  const stroke = computed(() => {
81
- return `var(--color-${type.value})`
81
+ return `var(--u-color-${type.value})`
82
82
  })
83
83
 
84
84
  const r = 44
@@ -50,7 +50,10 @@
50
50
  </u-input>
51
51
  </div>
52
52
 
53
- <!-- 单选列表 -->
53
+ <!--
54
+ 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;
55
+ 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。
56
+ -->
54
57
  <u-scroll
55
58
  v-if="options.length"
56
59
  tag="ul"
@@ -62,7 +65,6 @@
62
65
  bem.is('grid', !!grid)
63
66
  ]"
64
67
  :content-style="{
65
- height: virtualEnabled ? withUnit(totalHeight, 'px') : undefined,
66
68
  gridTemplateColumns: grid ? `repeat(${grid.cols}, minmax(0px, 1fr))` : undefined,
67
69
  gridGap: grid ? withUnit(grid.gap, 'px') : undefined
68
70
  }"
@@ -76,7 +78,7 @@
76
78
  :key="key"
77
79
  :style="{ transform: `translateY(${offset}px)` }"
78
80
  :data-index="index"
79
- :ref="measureElement"
81
+ :ref="(el) => measureElement(index, el as Element | null)"
80
82
  >
81
83
  <slot v-bind="{ option, index }">
82
84
  {{ label }}
@@ -113,7 +115,7 @@
113
115
 
114
116
  <script lang="ts" setup>
115
117
  import { o } from '@cat-kit/core'
116
- import { useFormComponent, useFormFallbackProps, useVirtual } from '@veltra/compositions'
118
+ import { useFormComponent, useFormFallbackProps, useVirtualizer } from '@veltra/compositions'
117
119
  import { vFocus } from '@veltra/directives'
118
120
  import { ArrowDown, Search } from '@veltra/icons/normal'
119
121
  import { bem, FORM_EMPTY_CONTENT, scrollIntoContainerView, withUnit } from '@veltra/utils'
@@ -212,40 +214,40 @@ watch(
212
214
  { immediate: true }
213
215
  )
214
216
 
215
- const {
216
- virtualList,
217
- totalHeight,
218
- virtualEnabled: _virtualEnabled,
219
- scrollTo,
220
- measureElement
221
- } = useVirtual({
217
+ const baseVirtualEnabled = computed(() => options.value.length > 80)
218
+ const virtualEnabled = computed(() => baseVirtualEnabled.value && !props.grid)
219
+
220
+ const { virtualizer, items } = useVirtualizer({
222
221
  count: computed(() => options.value.length),
223
- virtualThreshold: 80,
224
- scrollEl: computed(() => scrollRef.value?.containerRef ?? null),
222
+ scrollEl: () => scrollRef.value?.containerRef ?? null,
223
+ contentEl: () => (virtualEnabled.value ? (scrollRef.value?.contentRef ?? null) : null),
225
224
  gap: 4,
226
225
  estimateSize: () => 40
227
226
  })
228
227
 
229
- const virtualEnabled = computed(() => {
230
- return _virtualEnabled.value && !props.grid
231
- })
232
-
233
228
  const virtualOptions = computed(() => {
234
229
  const _options = options.value
235
230
  const { labelKey, valueKey } = props
236
- return virtualList.value.map((item) => {
231
+ return items.value.map((item) => {
237
232
  const option = _options[item.index]!
238
233
  return {
239
234
  option,
240
235
  index: item.index,
241
236
  label: o(option).get(labelKey),
242
237
  val: o(option).get(valueKey),
243
- key: item.key,
238
+ key: item.index,
244
239
  offset: item.start
245
240
  }
246
241
  })
247
242
  })
248
243
 
244
+ const measureElement: (index: number, el: Element | null) => void = (index, el) =>
245
+ virtualizer.measureElement(index, el)
246
+
247
+ function scrollTo(index: number): void {
248
+ virtualizer.scrollToIndex(index, { align: 'center' })
249
+ }
250
+
249
251
  watch([scrollRef, virtualEnabled], ([scroll, virtualEnabled]) => {
250
252
  if (!scroll || !props.modelValue) return
251
253
 
@@ -2,7 +2,7 @@
2
2
  <div ref="thumb" :class="cls.e('thumb')" :style></div>
3
3
  </template>
4
4
  <script lang="ts" setup>
5
- import { useDrag, useUpdateLock } from '@veltra/compositions'
5
+ import { useDrag, useUserAction } from '@veltra/compositions'
6
6
  import { computed, inject, useTemplateRef, watch } from 'vue'
7
7
 
8
8
  import { sliderContextKey } from './di'
@@ -30,7 +30,7 @@ const style = computed(() => {
30
30
  }
31
31
  })
32
32
 
33
- const { updateAndLock, update } = useUpdateLock()
33
+ const { userAction, isUserActive } = useUserAction()
34
34
 
35
35
  function updateModel(offset: number) {
36
36
  offset = getOffsetByStep(offset)
@@ -41,19 +41,15 @@ function updateModel(offset: number) {
41
41
  const dragger = useDrag({
42
42
  target: computed(() => (disabled.value ? null : thumbRef.value)),
43
43
 
44
- onDrag({ offsetX, offsetY }) {
45
- updateAndLock(() => {
46
- updateModel(sliderProps.vertical ? offsetY : offsetX)
47
- })
48
- },
44
+ onDrag: userAction(({ offsetX, offsetY }) => {
45
+ updateModel(sliderProps.vertical ? offsetY : offsetX)
46
+ }),
49
47
  rangeX: range,
50
48
  rangeY: range,
51
- onDragEnd({ offsetX, offsetY }) {
52
- updateAndLock(() => {
53
- const offset = updateModel(sliderProps.vertical ? offsetY : offsetX)
54
- updateDragger(offset)
55
- })
56
- }
49
+ onDragEnd: userAction(({ offsetX, offsetY }) => {
50
+ const offset = updateModel(sliderProps.vertical ? offsetY : offsetX)
51
+ updateDragger(offset)
52
+ })
57
53
  })
58
54
 
59
55
  function updateDragger(offset: number) {
@@ -67,7 +63,8 @@ function updateDragger(offset: number) {
67
63
  watch(
68
64
  () => props.modelValue,
69
65
  (offset) => {
70
- update(() => updateDragger(offset))
66
+ if (isUserActive()) return
67
+ updateDragger(offset)
71
68
  }
72
69
  )
73
70
  </script>
@@ -20,7 +20,7 @@
20
20
  </template>
21
21
 
22
22
  <script lang="ts" setup generic="T extends number | [number, number]">
23
- import { useFormComponent, useFormFallbackProps, useUpdateLock } from '@veltra/compositions'
23
+ import { useFormComponent, useFormFallbackProps, useUserAction } from '@veltra/compositions'
24
24
  import { bem } from '@veltra/utils'
25
25
  import { computed, provide, watch } from 'vue'
26
26
 
@@ -87,21 +87,20 @@ const barStyles = computed(() => {
87
87
  }
88
88
  })
89
89
 
90
- const { updateAndLock, update } = useUpdateLock()
90
+ const { userAction, isUserActive } = useUserAction()
91
91
 
92
92
  watch(
93
93
  [sliderSize, ...['modelValue', 'range', 'max', 'min', 'vertical'].map((k) => () => props[k])],
94
94
  ([size, value, range]) => {
95
- update(() => {
96
- if (value === undefined || size === 0) return
97
-
98
- if (range) {
99
- offset1.value = value2SliderOffset((value as [number, number])[0])
100
- offset2.value = value2SliderOffset((value as [number, number])[1])
101
- } else {
102
- offset1.value = value2SliderOffset(value as number)
103
- }
104
- })
95
+ if (isUserActive()) return
96
+ if (value === undefined || size === 0) return
97
+
98
+ if (range) {
99
+ offset1.value = value2SliderOffset((value as [number, number])[0])
100
+ offset2.value = value2SliderOffset((value as [number, number])[1])
101
+ } else {
102
+ offset1.value = value2SliderOffset(value as number)
103
+ }
105
104
  },
106
105
  { immediate: true }
107
106
  )
@@ -115,15 +114,16 @@ const ticks = computed(() => {
115
114
  return [...Array.from({ length: tickLength }, (_, i) => i * offsetPercent), 100]
116
115
  })
117
116
 
118
- watch([offset1, offset2], (v) => {
119
- updateAndLock(() => {
117
+ watch(
118
+ [offset1, offset2],
119
+ userAction((v) => {
120
120
  if (props.range) {
121
121
  emit('update:modelValue', v.map(sliderOffset2Value).sort() as T)
122
122
  } else {
123
123
  emit('update:modelValue', sliderOffset2Value(v[0]) as T)
124
124
  }
125
125
  })
126
- })
126
+ )
127
127
 
128
128
  function handleClickSlider(e: MouseEvent) {
129
129
  if (disabled.value || props.range) return
@@ -1,6 +1,6 @@
1
- import type { VirtualReturned } from '@veltra/compositions'
1
+ import type { VirtualItem } from '@veltra/compositions'
2
2
  import type { BEM } from '@veltra/utils'
3
- import type { InjectionKey, ShallowRef, Slots } from 'vue'
3
+ import type { ComputedRef, InjectionKey, ShallowRef, Slots } from 'vue'
4
4
 
5
5
  import type {
6
6
  TableProps,
@@ -14,47 +14,62 @@ import type {
14
14
  import type { ColumnNode } from './node/col'
15
15
  import type { ColumnConfig } from './use-columns'
16
16
 
17
- export const TableDIKey: InjectionKey<
18
- {
19
- /** 表格属性 */
20
- tableProps: TableProps
21
- /** 表格插槽 */
22
- tableSlots: Slots
23
- /** 类 */
24
- cls: BEM<'table'>
25
- /** 行 */
26
- rows: ShallowRef<TableRow[]>
27
- /** 选中行 */
28
- checkedRows: ShallowRef<Set<TableRow>>
29
- /** 结构化列 */
30
- columnConfig: ColumnConfig
31
- /** 事件处理方法 */
32
- handleRowClick: (row: TableRow, ev: MouseEvent) => void
33
- /** 单元格点击 */
34
- handleCellClick: (row: TableRow, column: TableColumn, ev: MouseEvent) => void
35
- /** 表格列插槽node */
36
- getColumnSlotsNode: (ctx: TableColumnSlotsScope | TableColumnRenderContext) => RenderReturn
37
- /** 展开行插槽node */
38
- getExpandRowSlotsNode: (ctx: TableRowSlotsScope) => RenderReturn
39
- /** 表头插槽node */
40
- getHeaderSlotsNode: (ctx: { column: ColumnNode }) => RenderReturn
41
-
42
- /** 展开/隐藏子节点 */
43
- toggleTreeRowExpand: (row: TableRow) => void
44
-
45
- /** 获取单元格的类 */
46
- getCellClass: (column: ColumnNode) => string
47
-
48
- /** 获取表头单元格的类 */
49
- getHeaderCellClass: (column: ColumnNode) => string
50
-
51
- /** 获取单元格上下文 */
52
- getCellCtx: (
53
- row: TableRow,
54
- column: ColumnNode
55
- ) => TableColumnSlotsScope | TableColumnRenderContext
56
- } & VirtualReturned
57
- > = Symbol('TableDIKey')
17
+ export const TableDIKey: InjectionKey<{
18
+ /** 表格属性 */
19
+ tableProps: TableProps
20
+ /** 表格插槽 */
21
+ tableSlots: Slots
22
+ /** 类 */
23
+ cls: BEM<'table'>
24
+ /** 行 */
25
+ rows: ShallowRef<TableRow[]>
26
+ /** 选中行 */
27
+ checkedRows: ShallowRef<Set<TableRow>>
28
+ /** 结构化列 */
29
+ columnConfig: ColumnConfig
30
+ /** 事件处理方法 */
31
+ handleRowClick: (row: TableRow, ev: MouseEvent) => void
32
+ /** 单元格点击 */
33
+ handleCellClick: (row: TableRow, column: TableColumn, ev: MouseEvent) => void
34
+ /** 表格列插槽node */
35
+ getColumnSlotsNode: (ctx: TableColumnSlotsScope | TableColumnRenderContext) => RenderReturn
36
+ /** 展开行插槽node */
37
+ getExpandRowSlotsNode: (ctx: TableRowSlotsScope) => RenderReturn
38
+ /** 表头插槽node */
39
+ getHeaderSlotsNode: (ctx: { column: ColumnNode }) => RenderReturn
40
+
41
+ /** 展开/隐藏子节点 */
42
+ toggleTreeRowExpand: (row: TableRow) => void
43
+
44
+ /** 获取单元格的类 */
45
+ getCellClass: (column: ColumnNode) => string
46
+
47
+ /** 获取表头单元格的类 */
48
+ getHeaderCellClass: (column: ColumnNode) => string
49
+
50
+ /** 获取单元格上下文 */
51
+ getCellCtx: (
52
+ row: TableRow,
53
+ column: ColumnNode
54
+ ) => TableColumnSlotsScope | TableColumnRenderContext
55
+
56
+ /**
57
+ * 当前渲染窗口的虚拟项列表。
58
+ *
59
+ * 由 `useVirtualizer` 拆分出的 `items` shallowRef:仅在底层 `items`
60
+ * 引用变化时才更新,`isScrolling` / 纯尺寸变化不会导致此 ref 更新。
61
+ */
62
+ virtualList: ShallowRef<VirtualItem[]>
63
+ /** 是否启用虚拟列表 */
64
+ virtualEnabled: ComputedRef<boolean>
65
+ /**
66
+ * 测量行元素尺寸(参数顺序与底层 `Virtualizer.measureElement` 一致)。
67
+ *
68
+ * 元素卸载(`el === null`)时会从底层解绑,避免被移除的 `<tr>` 继续触发
69
+ * size=0 测量回调。
70
+ */
71
+ measureElement: (index: number, el: Element | null) => void
72
+ }> = Symbol('TableDIKey')
58
73
 
59
74
  export const TableResizeKey: InjectionKey<{
60
75
  handleResizeMousedown: (e: MouseEvent, resizeColumn: ColumnNode) => void
@@ -28,7 +28,16 @@ export class TableRowNode<Data extends Record<string, any> = Record<string, any>
28
28
  /** 是否选中 */
29
29
  checked = false
30
30
 
31
- uid: number | string
31
+ /**
32
+ * 行的稳定唯一标识。
33
+ *
34
+ * E2 不变式:`uid` 在单个 `TableRowNode` 实例的生命周期内不可变更。
35
+ * `useVirtualizer` 的 `getItemKey: i => rows[i].uid` 依赖该不变式来复用测量缓存;
36
+ * 若 uid 在运行时变更,虚拟列表的尺寸表会错配导致滚动抖动。
37
+ * 当前代码路径(`createRow` / `copy`)始终为每个新节点分配一次 uid,
38
+ * 所以该不变式天然成立;此处使用 `readonly` 做编译期锁定。
39
+ */
40
+ readonly uid: number | string
32
41
 
33
42
  override children?: TableRowNode<Data>[] = undefined
34
43
 
@@ -1,20 +1,15 @@
1
1
  <template>
2
- <tbody :class="cls.e('body')" ref="bodyRef">
2
+ <tbody :class="bodyCls">
3
3
  <template v-for="{ row, index, stripeIndex } of tableRows" :key="row.uid">
4
- <UExpandTableRow v-if="row.isExpandRow" :row="row" :data-index="index" />
5
- <UTableRow
6
- v-else
7
- :row="row"
8
- :data-index="index"
9
- :class="[bem.is('current', row.isCurrent), getStripeCls(stripeIndex)]"
10
- />
4
+ <UExpandTableRow v-if="row.isExpandRow" :row="row" :index="index" />
5
+ <UTableRow v-else :row="row" :index="index" :class="getStripeCls(stripeIndex)" />
11
6
  </template>
12
7
 
13
8
  <!-- 空 -->
14
- <tr v-if="!rows.length" :class="cls.e('row')">
9
+ <tr v-if="!rows.length" :class="emptyRowCls">
15
10
  <td :colspan="leafColumns.length">
16
11
  <slot name="empty">
17
- <UEmpty :class="cls.e('empty')" />
12
+ <UEmpty :class="emptyCls" />
18
13
  </slot>
19
14
  </td>
20
15
  </tr>
@@ -24,8 +19,8 @@
24
19
  </template>
25
20
 
26
21
  <script lang="ts" setup>
27
- import { bem, setStyles } from '@veltra/utils'
28
- import { computed, inject, shallowRef, watch } from 'vue'
22
+ import { bem } from '@veltra/utils'
23
+ import { computed, inject } from 'vue'
29
24
 
30
25
  import { UEmpty } from '../empty'
31
26
  import { TableDIKey } from './di'
@@ -38,9 +33,26 @@ defineOptions({
38
33
  const { cls, rows, virtualList, columnConfig, tableProps, virtualEnabled } = inject(TableDIKey)!
39
34
  const { leafColumns } = columnConfig
40
35
 
41
- const getStripeCls = computed(() => {
42
- return tableProps.stripe ? (index: number) => bem.is('stripe', index % 2 === 1) : () => ''
43
- })
36
+ // A1 的同族优化:把 body / empty / empty-row 等静态 class 一次性取出,
37
+ // 避免每次 render 触发 `cls.e` 的字符串拼接。
38
+ const bodyCls = cls.e('body')
39
+ const emptyRowCls = cls.e('row')
40
+ const emptyCls = cls.e('empty')
41
+
42
+ /**
43
+ * C1:stripe class 查找表。
44
+ *
45
+ * `bem.is('stripe', true) === 'is-stripe'`,`bem.is('stripe', false) === ''`。
46
+ * 两种输出是全局常量,无需每次调用 `bem.is`;按奇偶直接查表即可。
47
+ * 非 stripe 模式下两个位置均返回空串。
48
+ */
49
+ const STRIPE_CLS_ON: readonly [string, string] = [bem.is('stripe', false), bem.is('stripe', true)]
50
+ const STRIPE_CLS_OFF: readonly [string, string] = ['', '']
51
+
52
+ const stripeTable = computed<readonly [string, string]>(() =>
53
+ tableProps.stripe ? STRIPE_CLS_ON : STRIPE_CLS_OFF
54
+ )
55
+ const getStripeCls = (index: number): string => stripeTable.value[index & 1]!
44
56
 
45
57
  const tableRows = computed(() => {
46
58
  if (!virtualEnabled.value) {
@@ -53,7 +65,7 @@ const tableRows = computed(() => {
53
65
  })
54
66
  }
55
67
 
56
- // 虚拟列表
68
+ // 虚拟列表:仅渲染可视窗口内的行,位置由 table.vue 中的 before/after 占位 tbody 撑开
57
69
  return virtualList.value.map((item) => {
58
70
  return {
59
71
  row: rows.value[item.index]!,
@@ -62,21 +74,4 @@ const tableRows = computed(() => {
62
74
  }
63
75
  })
64
76
  })
65
-
66
- const bodyRef = shallowRef<HTMLElement>()
67
-
68
- function setBodyTransform(transformY: number) {
69
- bodyRef.value &&
70
- setStyles(bodyRef.value, {
71
- transform: `translate3d(0, ${transformY}px, 0)`
72
- })
73
- }
74
-
75
- watch(virtualList, (list) => {
76
- setBodyTransform(list[0]?.start ?? 0)
77
- })
78
-
79
- defineExpose({
80
- setBodyTransform
81
- })
82
77
  </script>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <td
3
- :class="getCellClass(column)"
4
- :style="{ left: withUnit(left, 'px'), right: withUnit(right, 'px') }"
3
+ :class="cellClass"
4
+ :style="style"
5
5
  :rowspan="rowspan"
6
6
  :colspan="colspan"
7
7
  v-if="rowspan !== 0 && colspan !== 0"
@@ -12,22 +12,30 @@
12
12
 
13
13
  <script lang="ts" setup>
14
14
  import { withUnit } from '@veltra/utils'
15
- import { inject } from 'vue'
16
-
17
- import { TableDIKey } from './di'
18
- import type { ColumnNode } from './node/col'
15
+ import { computed } from 'vue'
19
16
 
20
17
  defineOptions({
21
18
  name: 'TableCell'
22
19
  })
23
20
 
24
- const { column } = defineProps<{
25
- column: ColumnNode
21
+ /**
22
+ * B2: TableCell 去 inject 化。
23
+ *
24
+ * 原先每个可见单元格都会执行 `inject(TableDIKey)` 并在模板中调用 `getCellClass(column)`,
25
+ * 在 30 列场景下每次滚动每行产生 30 次 inject / 30 次类名拼接。
26
+ * 父组件 `TableRow` 已经持有注入上下文,直接把 `cellClass` 传下来即可;
27
+ * 这里变成「完全受控」的纯渲染单元。
28
+ */
29
+ const { left, right } = defineProps<{
30
+ cellClass: string
26
31
  left?: number
27
32
  right?: number
28
33
  rowspan?: number
29
34
  colspan?: number
30
35
  }>()
31
36
 
32
- const { getCellClass } = inject(TableDIKey)!
37
+ const style = computed(() => ({
38
+ left: withUnit(left, 'px'),
39
+ right: withUnit(right, 'px')
40
+ }))
33
41
  </script>