vft 0.0.1

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 (321) hide show
  1. package/package.json +54 -0
  2. package/src/app/index.ts +3 -0
  3. package/src/app/page-loading/assets/spin.gif +0 -0
  4. package/src/app/page-loading/index.less +10 -0
  5. package/src/app/page-loading/index.ts +3 -0
  6. package/src/app/page-loading/index.vue +38 -0
  7. package/src/app/table/assets/sort_triangle.png +0 -0
  8. package/src/app/table/demos/basic.vue +117 -0
  9. package/src/app/table/demos/complex.vue +2543 -0
  10. package/src/app/table/demos/index.vue +453 -0
  11. package/src/app/table/demos/rightAngle.png +0 -0
  12. package/src/app/table/header.vue +203 -0
  13. package/src/app/table/index.less +119 -0
  14. package/src/app/table/index.ts +5 -0
  15. package/src/app/table/index.vue +478 -0
  16. package/src/app/table/md/api.md +23 -0
  17. package/src/app/table/md/demo.md +3 -0
  18. package/src/app/table/types.ts +45 -0
  19. package/src/common/badge/index.scss +78 -0
  20. package/src/common/badge/index.ts +4 -0
  21. package/src/common/badge/index.vue +111 -0
  22. package/src/common/badge/types.ts +1 -0
  23. package/src/common/clamp/clamp-toggle.vue +91 -0
  24. package/src/common/clamp/index.ts +4 -0
  25. package/src/common/clamp/index.vue +247 -0
  26. package/src/common/code/index.less +321 -0
  27. package/src/common/code/index.ts +3 -0
  28. package/src/common/code/index.vue +60 -0
  29. package/src/common/config-provider/index.ts +4 -0
  30. package/src/common/config-provider/index.vue +94 -0
  31. package/src/common/config-provider/types.ts +29 -0
  32. package/src/common/icon/iconfont/iconfont.css +334 -0
  33. package/src/common/icon/index.scss +31 -0
  34. package/src/common/icon/index.ts +4 -0
  35. package/src/common/icon/index.vue +74 -0
  36. package/src/common/icon/types.ts +16 -0
  37. package/src/common/index.ts +6 -0
  38. package/src/common/message/index.ts +5 -0
  39. package/src/common/message/instance.ts +29 -0
  40. package/src/common/message/message.scss +108 -0
  41. package/src/common/message/message.ts +80 -0
  42. package/src/common/message/message.vue +162 -0
  43. package/src/common/message/method.ts +172 -0
  44. package/src/common/overlay/index.scss +14 -0
  45. package/src/common/overlay/index.ts +3 -0
  46. package/src/common/overlay/index.vue +117 -0
  47. package/src/constants/comp.ts +1 -0
  48. package/src/constants/index.ts +1 -0
  49. package/src/index.ts +3 -0
  50. package/src/page/index.ts +1 -0
  51. package/src/page/page-wrapper/index.scss +31 -0
  52. package/src/page/page-wrapper/index.ts +5 -0
  53. package/src/page/page-wrapper/index.vue +31 -0
  54. package/src/styles/vars.scss +500 -0
  55. package/src/use/index.ts +14 -0
  56. package/src/use/onPopupReopen.ts +15 -0
  57. package/src/use/use-delayed-toggle/index.ts +30 -0
  58. package/src/use/use-floating/index.ts +121 -0
  59. package/src/use/use-forward-ref.ts +35 -0
  60. package/src/use/use-global-config/index.ts +81 -0
  61. package/src/use/use-id/index.ts +43 -0
  62. package/src/use/use-model-toggle/index.ts +151 -0
  63. package/src/use/use-namespace/index.ts +91 -0
  64. package/src/use/use-ordered-children/index.ts +43 -0
  65. package/src/use/use-popper-container/index.ts +43 -0
  66. package/src/use/use-timeout/index.ts +18 -0
  67. package/src/use/use-z-index/index.ts +20 -0
  68. package/src/use/useGlobalZIndex.ts +24 -0
  69. package/src/use/useLazyRender.ts +17 -0
  70. package/src/use/useLockScroll.ts +66 -0
  71. package/src/utils/aria.ts +126 -0
  72. package/src/utils/arrays.ts +13 -0
  73. package/src/utils/error.ts +23 -0
  74. package/src/utils/event.ts +15 -0
  75. package/src/utils/helper.ts +7 -0
  76. package/src/utils/index.ts +8 -0
  77. package/src/utils/interceptor.ts +39 -0
  78. package/src/utils/mount-component.ts +65 -0
  79. package/src/utils/popper.ts +6 -0
  80. package/src/utils/rand.ts +12 -0
  81. package/src/utils/scroll.ts +101 -0
  82. package/src/utils/vnode.ts +169 -0
  83. package/src/web/avatar/index.scss +50 -0
  84. package/src/web/avatar/index.ts +3 -0
  85. package/src/web/avatar/index.vue +83 -0
  86. package/src/web/back-top/index.scss +35 -0
  87. package/src/web/back-top/index.ts +3 -0
  88. package/src/web/back-top/index.vue +72 -0
  89. package/src/web/back-top/types.ts +14 -0
  90. package/src/web/back-top/use-back-top.ts +65 -0
  91. package/src/web/cascader/index.scss +214 -0
  92. package/src/web/cascader/index.ts +5 -0
  93. package/src/web/cascader/index.vue +767 -0
  94. package/src/web/cascader-panel/config.ts +44 -0
  95. package/src/web/cascader-panel/index.scss +134 -0
  96. package/src/web/cascader-panel/index.ts +5 -0
  97. package/src/web/cascader-panel/index.vue +319 -0
  98. package/src/web/cascader-panel/menu.vue +135 -0
  99. package/src/web/cascader-panel/node-content.ts +23 -0
  100. package/src/web/cascader-panel/node.ts +218 -0
  101. package/src/web/cascader-panel/node.vue +197 -0
  102. package/src/web/cascader-panel/store.ts +83 -0
  103. package/src/web/cascader-panel/types.ts +56 -0
  104. package/src/web/cascader-panel/utils.ts +40 -0
  105. package/src/web/cascader-select/README.md +31 -0
  106. package/src/web/cascader-select/index.scss +54 -0
  107. package/src/web/cascader-select/index.ts +5 -0
  108. package/src/web/cascader-select/index.vue +132 -0
  109. package/src/web/cascader-select/types.ts +9 -0
  110. package/src/web/context-menu/createContextMenu.ts +72 -0
  111. package/src/web/context-menu/index.ts +4 -0
  112. package/src/web/context-menu/index.vue +89 -0
  113. package/src/web/context-menu/types.ts +27 -0
  114. package/src/web/context-menu/useContextMenu.ts +14 -0
  115. package/src/web/descriptions/description-item.vue +34 -0
  116. package/src/web/descriptions/description.vue +124 -0
  117. package/src/web/descriptions/descriptions-cell.ts +95 -0
  118. package/src/web/descriptions/descriptions-item.scss +68 -0
  119. package/src/web/descriptions/descriptions-row.vue +49 -0
  120. package/src/web/descriptions/descriptions.scss +153 -0
  121. package/src/web/descriptions/descriptions.type.ts +19 -0
  122. package/src/web/descriptions/index.ts +4 -0
  123. package/src/web/descriptions/token.ts +4 -0
  124. package/src/web/divider/index.scss +53 -0
  125. package/src/web/divider/index.ts +5 -0
  126. package/src/web/divider/index.vue +60 -0
  127. package/src/web/divider/types.ts +2 -0
  128. package/src/web/empty/assets/no-collect.png +0 -0
  129. package/src/web/empty/assets/no-data.png +0 -0
  130. package/src/web/empty/assets/no-filter.png +0 -0
  131. package/src/web/empty/assets/no-page-data.png +0 -0
  132. package/src/web/empty/assets/no-search.png +0 -0
  133. package/src/web/empty/constants.ts +12 -0
  134. package/src/web/empty/index.scss +57 -0
  135. package/src/web/empty/index.ts +5 -0
  136. package/src/web/empty/index.vue +96 -0
  137. package/src/web/exception/exception.png +0 -0
  138. package/src/web/exception/index.ts +3 -0
  139. package/src/web/exception/index.vue +44 -0
  140. package/src/web/filter/README.md +25 -0
  141. package/src/web/filter/index.scss +14 -0
  142. package/src/web/filter/index.ts +5 -0
  143. package/src/web/filter/index.vue +60 -0
  144. package/src/web/filter/type.ts +13 -0
  145. package/src/web/focus-trap/index.ts +6 -0
  146. package/src/web/focus-trap/index.vue +328 -0
  147. package/src/web/focus-trap/tokens.ts +23 -0
  148. package/src/web/focus-trap/utils.ts +178 -0
  149. package/src/web/full-screen/index.scss +22 -0
  150. package/src/web/full-screen/index.ts +3 -0
  151. package/src/web/full-screen/index.vue +24 -0
  152. package/src/web/icon-text/index.ts +3 -0
  153. package/src/web/icon-text/index.vue +77 -0
  154. package/src/web/image/index.scss +46 -0
  155. package/src/web/image/index.ts +5 -0
  156. package/src/web/image/index.vue +251 -0
  157. package/src/web/image/types.ts +1 -0
  158. package/src/web/index.ts +33 -0
  159. package/src/web/input/index.scss +473 -0
  160. package/src/web/input/index.ts +3 -0
  161. package/src/web/input/index.vue +533 -0
  162. package/src/web/input/utils.ts +102 -0
  163. package/src/web/layouts/blank.vue +4 -0
  164. package/src/web/layouts/footer/index.scss +31 -0
  165. package/src/web/layouts/footer/index.ts +3 -0
  166. package/src/web/layouts/footer/index.vue +38 -0
  167. package/src/web/layouts/header/index.scss +35 -0
  168. package/src/web/layouts/header/index.ts +3 -0
  169. package/src/web/layouts/header/index.vue +47 -0
  170. package/src/web/layouts/iframe/index.scss +18 -0
  171. package/src/web/layouts/iframe/index.vue +36 -0
  172. package/src/web/layouts/iframe/page.vue +30 -0
  173. package/src/web/layouts/index.ts +8 -0
  174. package/src/web/layouts/router-view-content/index.vue +70 -0
  175. package/src/web/link/index.scss +95 -0
  176. package/src/web/link/index.ts +3 -0
  177. package/src/web/link/index.vue +68 -0
  178. package/src/web/loading/directive.ts +104 -0
  179. package/src/web/loading/index.ts +6 -0
  180. package/src/web/loading/loading.scss +108 -0
  181. package/src/web/loading/loading.ts +156 -0
  182. package/src/web/loading/service.ts +145 -0
  183. package/src/web/loading/types.ts +29 -0
  184. package/src/web/logo/index.scss +31 -0
  185. package/src/web/logo/index.ts +5 -0
  186. package/src/web/logo/index.vue +45 -0
  187. package/src/web/logo/types.ts +6 -0
  188. package/src/web/menu/index.scss +336 -0
  189. package/src/web/menu/index.ts +8 -0
  190. package/src/web/menu/menu-collapse-transition.vue +62 -0
  191. package/src/web/menu/menu-item-group.vue +27 -0
  192. package/src/web/menu/menu-item.vue +126 -0
  193. package/src/web/menu/menu.vue +459 -0
  194. package/src/web/menu/sub-menu.vue +440 -0
  195. package/src/web/menu/types.ts +66 -0
  196. package/src/web/menu/use-menu-css-var.ts +11 -0
  197. package/src/web/menu/use-menu.ts +60 -0
  198. package/src/web/menu/utils/menu-bar.ts +19 -0
  199. package/src/web/menu/utils/menu-item.ts +55 -0
  200. package/src/web/menu/utils/submenu.ts +66 -0
  201. package/src/web/multiple-select-flat/index.ts +5 -0
  202. package/src/web/multiple-select-flat/index.vue +53 -0
  203. package/src/web/multiple-select-flat/types.ts +5 -0
  204. package/src/web/multiple-tabs/index.scss +16 -0
  205. package/src/web/multiple-tabs/index.ts +5 -0
  206. package/src/web/multiple-tabs/index.vue +193 -0
  207. package/src/web/multiple-tabs/tab-content.vue +40 -0
  208. package/src/web/multiple-tabs/types.ts +3 -0
  209. package/src/web/multiple-tabs/use/index.ts +2 -0
  210. package/src/web/multiple-tabs/use/use-multiple-tabs.ts +86 -0
  211. package/src/web/multiple-tabs/use/use-tab-dropdown.ts +101 -0
  212. package/src/web/nodata/README.md +42 -0
  213. package/src/web/nodata/fail.vue +13 -0
  214. package/src/web/nodata/img/100.png +0 -0
  215. package/src/web/nodata/img/101.png +0 -0
  216. package/src/web/nodata/img/102.png +0 -0
  217. package/src/web/nodata/img/103.png +0 -0
  218. package/src/web/nodata/img/104.png +0 -0
  219. package/src/web/nodata/img/105.png +0 -0
  220. package/src/web/nodata/img/106.png +0 -0
  221. package/src/web/nodata/img/107.png +0 -0
  222. package/src/web/nodata/img/200.png +0 -0
  223. package/src/web/nodata/img/201.png +0 -0
  224. package/src/web/nodata/img/202.png +0 -0
  225. package/src/web/nodata/img/203.png +0 -0
  226. package/src/web/nodata/index.scss +37 -0
  227. package/src/web/nodata/index.ts +6 -0
  228. package/src/web/nodata/index.vue +46 -0
  229. package/src/web/nodata/types.ts +17 -0
  230. package/src/web/only-child/index.tsx +69 -0
  231. package/src/web/pagination/components/jumper.vue +49 -0
  232. package/src/web/pagination/components/next.vue +40 -0
  233. package/src/web/pagination/components/pager.vue +215 -0
  234. package/src/web/pagination/components/prev.vue +35 -0
  235. package/src/web/pagination/components/sizes.vue +76 -0
  236. package/src/web/pagination/components/total.vue +21 -0
  237. package/src/web/pagination/index.scss +231 -0
  238. package/src/web/pagination/index.ts +5 -0
  239. package/src/web/pagination/pagination.ts +363 -0
  240. package/src/web/pagination/usePagination.ts +13 -0
  241. package/src/web/popover/directive.ts +21 -0
  242. package/src/web/popover/index.scss +58 -0
  243. package/src/web/popover/index.ts +3 -0
  244. package/src/web/popover/index.vue +161 -0
  245. package/src/web/popover/types.ts +26 -0
  246. package/src/web/popper/arrow.vue +45 -0
  247. package/src/web/popper/content.vue +311 -0
  248. package/src/web/popper/index.scss +108 -0
  249. package/src/web/popper/index.ts +11 -0
  250. package/src/web/popper/popper.vue +57 -0
  251. package/src/web/popper/tokens.ts +28 -0
  252. package/src/web/popper/trigger.vue +166 -0
  253. package/src/web/popper/types.ts +49 -0
  254. package/src/web/popper/utils.ts +81 -0
  255. package/src/web/qrcode/drawCanvas.ts +32 -0
  256. package/src/web/qrcode/drawLogo.ts +82 -0
  257. package/src/web/qrcode/index.ts +5 -0
  258. package/src/web/qrcode/index.vue +107 -0
  259. package/src/web/qrcode/qrcodePlus.ts +4 -0
  260. package/src/web/qrcode/toCanvas.ts +11 -0
  261. package/src/web/qrcode/types.ts +38 -0
  262. package/src/web/result/index.scss +69 -0
  263. package/src/web/result/index.ts +3 -0
  264. package/src/web/result/index.vue +63 -0
  265. package/src/web/scrollbar/bar.vue +48 -0
  266. package/src/web/scrollbar/index.scss +91 -0
  267. package/src/web/scrollbar/index.ts +5 -0
  268. package/src/web/scrollbar/index.vue +236 -0
  269. package/src/web/scrollbar/thumb.vue +183 -0
  270. package/src/web/scrollbar/tokens.ts +10 -0
  271. package/src/web/scrollbar/types.ts +7 -0
  272. package/src/web/scrollbar/util.ts +38 -0
  273. package/src/web/select/constants.ts +13 -0
  274. package/src/web/select/index.ts +11 -0
  275. package/src/web/select/index.vue +555 -0
  276. package/src/web/select/option-group.scss +49 -0
  277. package/src/web/select/option-group.vue +97 -0
  278. package/src/web/select/option-item.scss +66 -0
  279. package/src/web/select/option.scss +32 -0
  280. package/src/web/select/option.vue +110 -0
  281. package/src/web/select/select-dropdown.scss +86 -0
  282. package/src/web/select/select-dropdown.vue +51 -0
  283. package/src/web/select/select.scss +213 -0
  284. package/src/web/select/token.ts +56 -0
  285. package/src/web/select/useOption.ts +146 -0
  286. package/src/web/select/useSelect.ts +942 -0
  287. package/src/web/select/utils.ts +5 -0
  288. package/src/web/side-menu/index.scss +66 -0
  289. package/src/web/side-menu/index.ts +4 -0
  290. package/src/web/side-menu/index.vue +228 -0
  291. package/src/web/side-menu/types.ts +20 -0
  292. package/src/web/single-select/index.scss +60 -0
  293. package/src/web/single-select/index.ts +5 -0
  294. package/src/web/single-select/index.vue +70 -0
  295. package/src/web/single-select/select@2x.png +0 -0
  296. package/src/web/single-select/types.ts +5 -0
  297. package/src/web/svg/index.ts +3 -0
  298. package/src/web/svg/index.vue +22 -0
  299. package/src/web/tabs/index.scss +579 -0
  300. package/src/web/tabs/index.ts +6 -0
  301. package/src/web/tabs/index.vue +236 -0
  302. package/src/web/tabs/tab-bar.vue +90 -0
  303. package/src/web/tabs/tab-nav.vue +403 -0
  304. package/src/web/tabs/tab-pane.vue +90 -0
  305. package/src/web/tabs/types.ts +66 -0
  306. package/src/web/tag/index.scss +182 -0
  307. package/src/web/tag/index.ts +5 -0
  308. package/src/web/tag/index.vue +78 -0
  309. package/src/web/tag/types.ts +2 -0
  310. package/src/web/tooltip/content.vue +239 -0
  311. package/src/web/tooltip/index.ts +4 -0
  312. package/src/web/tooltip/tokens.ts +21 -0
  313. package/src/web/tooltip/tooltip.vue +270 -0
  314. package/src/web/tooltip/trigger.vue +119 -0
  315. package/src/web/tooltip/types.ts +56 -0
  316. package/src/web/tooltip/utils.ts +20 -0
  317. package/src/web/transition/collapse-transition.vue +73 -0
  318. package/src/web/transition/index.ts +5 -0
  319. package/tsconfig.json +8 -0
  320. package/types/component.ts +1 -0
  321. package/types/index.d.ts +286 -0
@@ -0,0 +1,440 @@
1
+ <script lang="tsx" setup>
2
+ import { renderTNode, singleAttrToObj } from '@vri/utils';
3
+ import { useTimeoutFn } from '@vueuse/core';
4
+ import { Tooltip, type ToolTipProps } from '../tooltip';
5
+ import type { Placement } from 'element-plus/es/components/popper';
6
+ import { Icon, type IconProps } from '../../common';
7
+ import { useNamespace } from '../../use';
8
+ import { throwError } from '../../utils';
9
+ import type { CSSProperties, VNodeArrayChildren } from 'vue';
10
+ import {
11
+ computed,
12
+ Fragment,
13
+ getCurrentInstance,
14
+ h,
15
+ inject,
16
+ onBeforeUnmount,
17
+ onMounted,
18
+ provide,
19
+ reactive,
20
+ ref,
21
+ vShow,
22
+ watch,
23
+ withDirectives,
24
+ useSlots
25
+ } from 'vue';
26
+ import CollapseTransition from '../transition/collapse-transition.vue';
27
+ import type { MenuProvider, SubMenuProvider } from './types';
28
+ import useMenu from './use-menu';
29
+ import { useMenuCssVar } from './use-menu-css-var';
30
+
31
+ interface Props {
32
+ /** 唯一标志 */
33
+ index: string;
34
+ /** 展开 sub-menu 的延时 */
35
+ showTimeout?: number;
36
+ /** 收起 sub-menu 的延时 */
37
+ hideTimeout?: number;
38
+ /** 为 popper 添加类名 */
39
+ popperClass?: ClassType;
40
+ /** 是否禁用 */
41
+ disabled?: boolean;
42
+ popperAppendToBody?: boolean;
43
+ /** 弹出窗口偏移 */
44
+ popperOffset?: number;
45
+ expandIcon?: IconProps;
46
+ collapseIcon?: IconProps;
47
+ title?: string;
48
+ icon?: IconProps | string;
49
+ showArrow?: boolean;
50
+ toolTipCfg?: ToolTipProps;
51
+ }
52
+
53
+ const {
54
+ index,
55
+ showTimeout = 300,
56
+ hideTimeout = 300,
57
+ popperClass,
58
+ disabled,
59
+ popperAppendToBody = undefined,
60
+ popperOffset = 6,
61
+ expandIcon = {},
62
+ collapseIcon = {},
63
+ icon,
64
+ showArrow = true,
65
+ toolTipCfg
66
+ } = defineProps<Props>();
67
+
68
+ const ns = /* hoist-static*/ useNamespace('sub-menu');
69
+
70
+ defineOptions({
71
+ name: ns.b()
72
+ });
73
+
74
+ const instance = getCurrentInstance()!;
75
+
76
+ // 返回当前 sub-menu 对应的 indexPath 页面中对 submenu 定义的 index parentMenu -> vri-menu 组件
77
+ const { indexPath, parentMenu } = useMenu(instance, computed(() => index));
78
+
79
+ const nsMenu = useNamespace('menu');
80
+ const nsSubMenu = useNamespace('sub-menu');
81
+
82
+ // 接受 vri-menu 组件中注入的数据
83
+ const rootMenu = inject<MenuProvider>('rootMenu');
84
+ if (!rootMenu) throwError(ns.b(), 'can not inject root menu');
85
+
86
+ // 接受 vri-menu 组件中注入的数据 parentMenu.value!.uid 当前 sub-menu 组件对应的 menu.vue 组件
87
+ const subMenu = inject<SubMenuProvider>(`subMenu:${parentMenu.value!.uid}`);
88
+ if (!subMenu) throwError(ns.b(), 'can not inject sub menu');
89
+
90
+ /**
91
+ * 格式如下
92
+ * ```
93
+ * {
94
+ * 1-1:{index: '1-1', indexPath: ['mio', '1-1'], active: false},
95
+ * 1-2:{index: '1-2', indexPath: ['mio', '1-2'], active: false}
96
+ * }
97
+ * ```
98
+ * @param {MenuItemRegistered} item
99
+ */
100
+ const subMenus = ref<MenuProvider['subMenus']>({});
101
+
102
+ let timeout: (() => void) | undefined;
103
+ const mouseInChild = ref(false);
104
+
105
+ // sub-menu-title 的 ref
106
+ const verticalTitleRef = ref<HTMLDivElement>();
107
+ // tooltip 的实例
108
+ const vPopper = ref(null);
109
+
110
+ // toolTop 的展示位置
111
+ const currentPlacement = computed<Placement>(
112
+ () => (mode.value === 'horizontal' && isFirstLevel.value ? 'bottom-start' : 'right-start'));
113
+
114
+ // 是否为第一级的 subMenu
115
+ const isFirstLevel = computed(() => {
116
+ return subMenu.level === 0;
117
+ });
118
+
119
+ const appendToBody = computed(() => {
120
+ return popperAppendToBody === undefined ? isFirstLevel.value : Boolean(popperAppendToBody);
121
+ });
122
+
123
+ // toolTip 弹出的动画名称
124
+ const menuTransitionName = computed(
125
+ () => (rootMenu.props.collapse ? `${nsMenu.namespace.value}-zoom-in-left` : `${nsMenu.namespace.value}-zoom-in-top`));
126
+
127
+ const fallbackPlacements = computed<Placement[]>(() =>
128
+ mode.value === 'horizontal' && isFirstLevel.value
129
+ ? ['bottom-start', 'bottom-end', 'top-start', 'top-end', 'right-start', 'left-start']
130
+ : ['right-start', 'left-start', 'bottom-start', 'bottom-end', 'top-start', 'top-end']
131
+ );
132
+
133
+ // 获取当前的 index 是否包含在 openedMenus 中,表示当前 sub-menu 是否已经展开,子节点就是通过这个使用 v-show 来显隐
134
+ const opened = computed(() => rootMenu.openedMenus.includes(index));
135
+
136
+ const active = computed(() => {
137
+ let isActive = false;
138
+ Object.values(subMenus.value).forEach((subItem) => {
139
+ if (subItem.active) {
140
+ isActive = true;
141
+ }
142
+ });
143
+ if (index === rootMenu.activeIndex) {
144
+ isActive = true;
145
+ }
146
+ return isActive;
147
+ });
148
+
149
+ const backgroundColor = computed(() => rootMenu.props.backgroundColor || '');
150
+
151
+ const activeTextColor = computed(() => rootMenu.props.activeTextColor || '');
152
+
153
+ const textColor = computed(() => rootMenu.props.textColor || '');
154
+
155
+ const mode = computed(() => rootMenu.props.mode);
156
+
157
+ const item = reactive({
158
+ index,
159
+ indexPath,
160
+ active
161
+ });
162
+
163
+ //
164
+ const titleStyle = computed<CSSProperties>(() => {
165
+ if (mode.value !== 'horizontal') {
166
+ return {
167
+ color: textColor.value
168
+ };
169
+ }
170
+ return {
171
+ borderBottomColor: active.value ? (rootMenu.props.activeTextColor ? activeTextColor.value : '') : 'transparent',
172
+ color: active.value ? activeTextColor.value : textColor.value
173
+ };
174
+ });
175
+
176
+ // 销毁 toolTip
177
+ const doDestroy = () => vPopper.value?.popperRef?.popperInstanceRef?.destroy();
178
+
179
+ // 当菜单收缩展开时执行销毁 toolTip
180
+ const handleCollapseToggle = (value: boolean) => {
181
+ if (!value) {
182
+ doDestroy();
183
+ }
184
+ };
185
+
186
+ // sub-menu 菜单点击事件
187
+ const handleClick = () => {
188
+ // 以下几种情况不执行点击事件操作
189
+ if ((rootMenu.props.menuTrigger === 'hover' && rootMenu.props.mode === 'horizontal') || (rootMenu.props.collapse && rootMenu.props.mode === 'vertical') || disabled) return;
190
+
191
+ // 调用 menn.vue 中的 handleSubMenuClick 点击事件,传入参数
192
+ rootMenu.handleSubMenuClick({
193
+ index: index,
194
+ indexPath: indexPath.value,
195
+ active: active.value
196
+ });
197
+ };
198
+
199
+ // 滑入 sub-menu 菜单事件
200
+ const handleMouseenter = (event: MouseEvent | FocusEvent, _showTimeout = showTimeout) => {
201
+ if (event.type === 'focus') {
202
+ return;
203
+ }
204
+ if ((rootMenu.props.menuTrigger === 'click' && rootMenu.props.mode === 'horizontal') || (!rootMenu.props.collapse && rootMenu.props.mode === 'vertical') || disabled) {
205
+ return;
206
+ }
207
+ subMenu.mouseInChild.value = true;
208
+
209
+ // 根据 _showTimeout 展开滑入的菜单子项
210
+ timeout?.();
211
+ ({ stop: timeout } = useTimeoutFn(() => {
212
+ rootMenu.openMenu(index, indexPath.value);
213
+ }, _showTimeout));
214
+
215
+ if (appendToBody.value) {
216
+ parentMenu.value.vnode.el?.dispatchEvent(new MouseEvent('mouseenter'));
217
+ }
218
+ };
219
+
220
+ // 离开滑入的菜单
221
+ const handleMouseleave = (deepDispatch = false) => {
222
+ if ((rootMenu.props.menuTrigger === 'click' && rootMenu.props.mode === 'horizontal') || (!rootMenu.props.collapse && rootMenu.props.mode === 'vertical')) {
223
+ return;
224
+ }
225
+ timeout?.();
226
+ subMenu.mouseInChild.value = false;
227
+ ({ stop: timeout } = useTimeoutFn(
228
+ () => !mouseInChild.value && rootMenu.closeMenu(index, indexPath.value),
229
+ hideTimeout));
230
+
231
+ if (appendToBody.value && deepDispatch) {
232
+ if (instance.parent?.type.name === 'vri-sub-menu') {
233
+ subMenu.handleMouseleave?.(true);
234
+ }
235
+ }
236
+ };
237
+
238
+ // 监听菜单的收缩展开决定是否销毁 ToolTip
239
+ watch(
240
+ () => rootMenu.props.collapse,
241
+ (value) => handleCollapseToggle(Boolean(value))
242
+ );
243
+
244
+ // 以下代码在 menu-item.vue 中触发
245
+ {
246
+ /**
247
+ * 此函数在 menu-item 中触发,items.value 默认为空对象
248
+ * 最后添加后的 items 格式如下
249
+ * ```
250
+ * {
251
+ * 1-1:{index: '1-1', indexPath: ['mio', '1-1'], active: false},
252
+ * 1-2:{index: '1-2', indexPath: ['mio', '1-2'], active: false}
253
+ * }
254
+ * ```
255
+ * @param {MenuItemRegistered} item
256
+ */
257
+ const addSubMenu: SubMenuProvider['addSubMenu'] = (item) => {
258
+ subMenus.value[item.index] = item;
259
+ };
260
+ const removeSubMenu: SubMenuProvider['removeSubMenu'] = (item) => {
261
+ delete subMenus.value[item.index];
262
+ };
263
+ provide<SubMenuProvider>(`subMenu:${instance.uid}`, {
264
+ addSubMenu,
265
+ removeSubMenu,
266
+ handleMouseleave,
267
+ mouseInChild,
268
+ level: subMenu.level + 1
269
+ });
270
+ }
271
+
272
+ defineExpose({
273
+ opened
274
+ });
275
+
276
+ onMounted(() => {
277
+ rootMenu.addSubMenu(item);
278
+ subMenu.addSubMenu(item);
279
+ });
280
+
281
+ onBeforeUnmount(() => {
282
+ subMenu.removeSubMenu(item);
283
+ rootMenu.removeSubMenu(item);
284
+ });
285
+
286
+ const slots = useSlots();
287
+
288
+ // 箭头设置
289
+ const subMenuIcon = computed(() => {
290
+ // 水平模式第一级 或者 竖直非折叠
291
+ return (mode.value === 'horizontal' && isFirstLevel.value) || (mode.value === 'vertical' && !rootMenu.props.collapse)
292
+ ? { icon: 'vi-arrow-down', size: 20, ...expandIcon } :
293
+ { icon: 'vi-arrow-right', size: 18, ...collapseIcon };
294
+ });
295
+
296
+ const _icon = computed(() => {
297
+ return singleAttrToObj(icon, 'icon') as IconProps;
298
+ });
299
+
300
+ const titleCon = computed(() => {
301
+ return renderTNode(instance, 'title');
302
+ });
303
+
304
+ defineRender(() => {
305
+ // sub-menu 的标题以及展开箭头 icon 相关
306
+ const titleTag: VNodeArrayChildren = [
307
+ _icon.value?.icon ? h(Icon, { ..._icon.value }) : null,
308
+ h(
309
+ 'span',
310
+ {
311
+ class: showArrow ? nsSubMenu.e('text') : '',
312
+ title: instance.props.title
313
+ },
314
+ titleCon.value
315
+ ),
316
+ showArrow ? h(
317
+ Icon,
318
+ {
319
+ class: nsSubMenu.e('icon-arrow'),
320
+ ...subMenuIcon.value,
321
+ style: { transform: opened.value ? 'rotateZ(180deg)' : 'none' }
322
+ }
323
+ ) : false
324
+ ];
325
+
326
+ // 给 vri-menu class 这一栏加入 level 等级,以此来区分当前的 level: --vri-menu-level:2;
327
+ const ulStyle = useMenuCssVar(rootMenu.props, subMenu.level + 1);
328
+
329
+ /**
330
+ * rootMenu.isMenuPopup 是否需要 tooltip 1.水平模式的菜单需要 2.竖直类型的菜单在收缩时需要
331
+ * 如果需要 tooltip 用 h 函数渲染时,则采用 ElTooltip 组件
332
+ * @type {VNode}
333
+ */
334
+ const child = rootMenu.isMenuPopup
335
+ ? h(
336
+ Tooltip,
337
+ {
338
+ ref: vPopper,
339
+ visible: opened.value,
340
+ effect: 'light',
341
+ pure: true,
342
+ offset: popperOffset,
343
+ showArrow: false,
344
+ persistent: true,
345
+ popperClass,
346
+ placement: currentPlacement.value,
347
+ teleported: appendToBody.value,
348
+ fallbackPlacements: fallbackPlacements.value,
349
+ transition: menuTransitionName.value,
350
+ gpuAcceleration: false,
351
+ ...toolTipCfg
352
+ },
353
+ {
354
+ content: () =>
355
+ h(
356
+ 'div',
357
+ {
358
+ class: [nsMenu.m(mode.value), nsMenu.m('popup-container'), popperClass],
359
+ onMouseenter: (evt: MouseEvent) => handleMouseenter(evt, 100),
360
+ onMouseleave: () => handleMouseleave(true),
361
+ onFocus: (evt: FocusEvent) => handleMouseenter(evt, 100)
362
+ },
363
+ [
364
+ h(
365
+ 'ul',
366
+ {
367
+ class: [nsMenu.b(), nsMenu.m('popup'), nsMenu.m(
368
+ `popup-${currentPlacement.value}`)],
369
+ style: ulStyle.value
370
+ },
371
+ [slots.default?.()]
372
+ )
373
+ ]
374
+ ),
375
+ default: () =>
376
+ h(
377
+ 'div',
378
+ {
379
+ class: nsSubMenu.e('title'),
380
+ style: [titleStyle.value, { backgroundColor: backgroundColor.value }],
381
+ onClick: handleClick
382
+ },
383
+ titleTag
384
+ )
385
+ }
386
+ )
387
+ :
388
+ /**
389
+ * Fragment 碎片化节点,可以让 dom 没有根节点
390
+ *
391
+ * 以下是无需 tooltip 渲染的组件,通常是侧边栏没有折叠的时候
392
+ */
393
+ h(Fragment, {}, [
394
+ titleCon.value ? h(
395
+ 'div',
396
+ {
397
+ class: nsSubMenu.e('title'),
398
+ style: [titleStyle.value, { backgroundColor: backgroundColor.value }],
399
+ ref: verticalTitleRef,
400
+ onClick: handleClick
401
+ },
402
+ titleTag
403
+ ) : null,
404
+ h(
405
+ CollapseTransition,
406
+ {},
407
+ {
408
+ default: () =>
409
+ withDirectives(
410
+ h(
411
+ 'ul',
412
+ {
413
+ role: 'menu',
414
+ class: [nsMenu.b(), nsMenu.m('inline')],
415
+ style: ulStyle.value
416
+ },
417
+ [slots.default?.()]
418
+ ),
419
+ [[vShow, opened.value]]
420
+ )
421
+ }
422
+ )
423
+ ]);
424
+
425
+ return h(
426
+ 'li',
427
+ {
428
+ class: [nsSubMenu.b(), nsSubMenu.is('active', active.value),
429
+ nsSubMenu.is('opened', opened.value), nsSubMenu.is('disabled', disabled)],
430
+ role: 'menuitem',
431
+ ariaHaspopup: true,
432
+ ariaExpanded: opened.value,
433
+ onMouseenter: handleMouseenter,
434
+ onMouseleave: () => handleMouseleave(true),
435
+ onFocus: handleMouseenter
436
+ },
437
+ [child]
438
+ );
439
+ });
440
+ </script>
@@ -0,0 +1,66 @@
1
+ import type { Ref } from 'vue';
2
+ import type { RouteLocationRaw } from 'vue-router';
3
+
4
+ export interface MenuItemRegistered {
5
+ index: string;
6
+ indexPath: string[];
7
+ active: boolean;
8
+ }
9
+ export interface MenuItemClicked {
10
+ index: string;
11
+ indexPath: string[];
12
+ route?: RouteLocationRaw;
13
+ }
14
+
15
+ export interface MenuProps {
16
+ /** 菜单展示模式 */
17
+ mode?: 'horizontal' | 'vertical';
18
+ /** 页面加载时默认激活菜单的 index(menu-item 的index) */
19
+ defaultActive?: string;
20
+ /** 默认打开的 sub-menu 的 index 的数组 */
21
+ defaultOpeneds?: string[];
22
+ /** 是否只保持一个子菜单的展开 */
23
+ uniqueOpened?: boolean;
24
+ /** 是否启用 vue-router 模式。 启用该模式会在激活导航时以 index 作为 path 进行路由跳转 使用 default-active 来设置加载时的激活项 */
25
+ router?: boolean;
26
+ /** 子菜单打开的触发方式,只在 mode 为 horizontal 时有效 */
27
+ menuTrigger?: 'hover' | 'click';
28
+ collapse?: boolean;
29
+ /** 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用) */
30
+ backgroundColor?: string;
31
+ textColor?: string;
32
+ activeTextColor?: string;
33
+ /** 是否开启折叠动画 */
34
+ collapseTransition?: boolean;
35
+ /** 是否省略多余的子项(仅在横向模式生效) */
36
+ ellipsis?: boolean;
37
+ }
38
+
39
+ export interface MenuProvider {
40
+ openedMenus: string[];
41
+ items: Record<string, MenuItemRegistered>;
42
+ subMenus: Record<string, MenuItemRegistered>;
43
+ activeIndex?: string;
44
+ /** 是否需要 tooltip 1.水平模式的菜单需要 2.竖直类型的菜单在收缩时需要 */
45
+ isMenuPopup: boolean;
46
+ props: MenuProps;
47
+
48
+ addMenuItem: (item: MenuItemRegistered) => void;
49
+ removeMenuItem: (item: MenuItemRegistered) => void;
50
+ addSubMenu: (item: MenuItemRegistered) => void;
51
+ removeSubMenu: (item: MenuItemRegistered) => void;
52
+
53
+ openMenu: (index: string, indexPath: string[]) => void;
54
+ closeMenu: (index: string, indexPath: string[]) => void;
55
+
56
+ handleMenuItemClick: (item: MenuItemClicked) => void;
57
+ handleSubMenuClick: (subMenu: MenuItemRegistered) => void;
58
+ }
59
+
60
+ export interface SubMenuProvider {
61
+ addSubMenu: (item: MenuItemRegistered) => void;
62
+ removeSubMenu: (item: MenuItemRegistered) => void;
63
+ handleMouseleave?: (deepDispatch: boolean) => void;
64
+ mouseInChild: Ref<boolean>;
65
+ level: number;
66
+ }
@@ -0,0 +1,11 @@
1
+ import { computed } from 'vue';
2
+ import { useNamespace } from '../../use';
3
+
4
+ export const useMenuCssVar = (props: any, level: number) => {
5
+ const ns = useNamespace('menu');
6
+ return computed(() => {
7
+ return ns.cssVarBlock({
8
+ level: `${level}`
9
+ });
10
+ });
11
+ };
@@ -0,0 +1,60 @@
1
+ import { computed } from 'vue';
2
+
3
+ import type { ComponentInternalInstance, Ref } from 'vue';
4
+
5
+ /**
6
+ * @description menu-item 与 sub-menu 都会调用此方法
7
+ * @author wfd
8
+ * @date 2022/11/8 14:02
9
+ * @example
10
+ * @param {ComponentInternalInstance} instance
11
+ * @param {Ref<string>} currentIndex
12
+ * @returns {{indexPath: ComputedRef<string[]>, parentMenu: ComputedRef<ComponentInternalInstance>}}
13
+ */
14
+ export default function useMenu(
15
+ instance: ComponentInternalInstance,
16
+ currentIndex: Ref<string>
17
+ ) {
18
+
19
+ /**
20
+ * 这个 indexPath 是如何查找的,一般我们会给 vri-sub-menu 组件一个 index 属性,由于这个文件只有 menu-item 与 sub-menu 调用,
21
+ * 由于 sub-menu 向上查询,不会有 index 属性了,主要是不断去找 menu-item 的父组件的 props 中的 index 属性
22
+ *
23
+ * 所以最终 indexPath 返回 menu-item:[自身的 index,sub-menu 的 index],sub-menu: [自身的index]
24
+ *
25
+ * sub-menu:['mio'] menu-item:['mio', '1-2']
26
+ * @type {ComputedRef<string[]>}
27
+ */
28
+ const indexPath = computed(() => {
29
+ let parent = instance.parent!;
30
+
31
+ const path = [currentIndex.value];
32
+ while (parent.type.name !== 'vri-menu') {
33
+ if (parent.props.index) {
34
+ path.unshift(parent.props.index as string);
35
+ }
36
+ parent = parent.parent!;
37
+ }
38
+ return path;
39
+ });
40
+
41
+ /**
42
+ * @description 如果是 sub-menu 文件最终返回的 parent 是 vri-menu 组件,如果是 menu-item 最终返回的是 vri-sub-menu 组件
43
+ * @author wfd
44
+ * @date 2022/11/8 16:23
45
+ * @example
46
+ * @type {ComputedRef<ComponentInternalInstance>}
47
+ */
48
+ const parentMenu = computed(() => {
49
+ let parent = instance.parent;
50
+ while (parent && !['vri-menu', 'vri-sub-menu'].includes(parent.type.name!)) {
51
+ parent = parent.parent;
52
+ }
53
+ return parent!;
54
+ });
55
+
56
+ return {
57
+ parentMenu,
58
+ indexPath
59
+ };
60
+ }
@@ -0,0 +1,19 @@
1
+ import MenuItem from './menu-item';
2
+
3
+ import type { RendererNode } from 'vue';
4
+
5
+ class Menu {
6
+ constructor(public domNode: RendererNode, namespace: string) {
7
+ this.init(namespace);
8
+ }
9
+ init(namespace: string): void {
10
+ const menuChildren = this.domNode.childNodes;
11
+ Array.from<Node>(menuChildren).forEach((child) => {
12
+ if (child.nodeType === 1) {
13
+ new MenuItem(child as HTMLElement, namespace);
14
+ }
15
+ });
16
+ }
17
+ }
18
+
19
+ export default Menu;
@@ -0,0 +1,55 @@
1
+ import { triggerEvent } from '@vri/utils';
2
+ import { EVENT_CODE } from '@vri/constants';
3
+ import SubMenu from './submenu';
4
+
5
+ class MenuItem {
6
+ public submenu: SubMenu | null = null;
7
+ constructor(public domNode: HTMLElement, namespace: string) {
8
+ this.submenu = null;
9
+ this.init(namespace);
10
+ }
11
+
12
+ init(namespace: string): void {
13
+ this.domNode.setAttribute('tabindex', '0');
14
+ const menuChild = this.domNode.querySelector(`.${namespace}-menu`);
15
+ if (menuChild) {
16
+ this.submenu = new SubMenu(this, menuChild);
17
+ }
18
+ this.addListeners();
19
+ }
20
+
21
+ addListeners(): void {
22
+ this.domNode.addEventListener('keydown', (event: KeyboardEvent) => {
23
+ let prevDef = false;
24
+ switch (event.code) {
25
+ case EVENT_CODE.down: {
26
+ triggerEvent(event.currentTarget as HTMLElement, 'mouseenter');
27
+ this.submenu && this.submenu.gotoSubIndex(0);
28
+ prevDef = true;
29
+ break;
30
+ }
31
+ case EVENT_CODE.up: {
32
+ triggerEvent(event.currentTarget as HTMLElement, 'mouseenter');
33
+ this.submenu && this.submenu.gotoSubIndex(this.submenu.subMenuItems.length - 1);
34
+ prevDef = true;
35
+ break;
36
+ }
37
+ case EVENT_CODE.tab: {
38
+ triggerEvent(event.currentTarget as HTMLElement, 'mouseleave');
39
+ break;
40
+ }
41
+ case EVENT_CODE.enter:
42
+ case EVENT_CODE.space: {
43
+ prevDef = true;
44
+ (event.currentTarget as HTMLElement).click();
45
+ break;
46
+ }
47
+ }
48
+ if (prevDef) {
49
+ event.preventDefault();
50
+ }
51
+ });
52
+ }
53
+ }
54
+
55
+ export default MenuItem;