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,57 @@
1
+ <template>
2
+ <slot />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import type { Instance as PopperInstance } from '@popperjs/core';
7
+ import { computed, provide, ref } from 'vue';
8
+ import './index.scss';
9
+ import type { VriPopperInjectionContext } from './tokens';
10
+ import { POPPER_INJECTION_KEY } from './tokens';
11
+
12
+ defineOptions({
13
+ name: 'VriPopperRoot',
14
+ inheritAttrs: false
15
+ });
16
+
17
+ interface Props {
18
+ role?: 'dialog' | 'grid' | 'group' | 'listbox' | 'menu' | 'navigation' | 'tooltip' | 'tree';
19
+ }
20
+
21
+ const {
22
+ role = 'tooltip'
23
+ } = defineProps<Props>();
24
+
25
+ const triggerRef = ref<HTMLElement>();
26
+ const popperInstanceRef = ref<PopperInstance>();
27
+ const contentRef = ref<HTMLElement>();
28
+ const referenceRef = ref<HTMLElement>();
29
+ const _role = computed(() => role);
30
+
31
+ const popperProvides = {
32
+ /**
33
+ * @description trigger element
34
+ */
35
+ triggerRef,
36
+ /**
37
+ * @description popperjs instance
38
+ */
39
+ popperInstanceRef,
40
+ /**
41
+ * @description popper content element
42
+ */
43
+ contentRef,
44
+ /**
45
+ * @description popper reference element
46
+ */
47
+ referenceRef,
48
+ /**
49
+ * @description role determines how aria attributes are distributed
50
+ */
51
+ role: _role
52
+ } as VriPopperInjectionContext;
53
+
54
+ defineExpose(popperProvides);
55
+
56
+ provide(POPPER_INJECTION_KEY, popperProvides);
57
+ </script>
@@ -0,0 +1,28 @@
1
+ import type { ComputedRef, InjectionKey, Ref } from 'vue';
2
+ import type { Instance } from '@popperjs/core';
3
+
4
+ export type Measurable = {
5
+ getBoundingClientRect: () => DOMRect;
6
+ };
7
+
8
+ /**
9
+ * triggerRef indicates the element that triggers popper
10
+ * contentRef indicates the element of popper content
11
+ * referenceRef indicates the element that popper content relative with
12
+ */
13
+ export type VriPopperInjectionContext = {
14
+ triggerRef: Ref<Measurable | undefined>;
15
+ contentRef: Ref<HTMLElement | undefined>;
16
+ popperInstanceRef: Ref<Instance | undefined>;
17
+ referenceRef: Ref<Measurable | undefined>;
18
+ role: ComputedRef<string>;
19
+ };
20
+
21
+ export type ElPopperContentInjectionContext = {
22
+ arrowRef: Ref<HTMLElement | undefined>;
23
+ arrowOffset: Ref<number | undefined>;
24
+ };
25
+
26
+ export const POPPER_INJECTION_KEY: InjectionKey<VriPopperInjectionContext> = Symbol('popper');
27
+
28
+ export const POPPER_CONTENT_INJECTION_KEY: InjectionKey<ElPopperContentInjectionContext> = Symbol('popperContent');
@@ -0,0 +1,166 @@
1
+ <template>
2
+ <only-child
3
+ v-if="!virtualTriggering"
4
+ v-bind="$attrs"
5
+ :aria-controls="ariaControls"
6
+ :aria-describedby="ariaDescribedby"
7
+ :aria-expanded="ariaExpanded"
8
+ :aria-haspopup="ariaHaspopup"
9
+ >
10
+ <slot />
11
+ </only-child>
12
+ </template>
13
+
14
+ <script lang="ts" setup>
15
+ import { computed, inject, onBeforeUnmount, onMounted, watch, getCurrentInstance } from 'vue';
16
+ import { isNil } from 'lodash';
17
+ import { unrefElement } from '@vueuse/core';
18
+ import { OnlyChild } from '../only-child';
19
+ import { useForwardRef } from '../../use';
20
+ import { POPPER_INJECTION_KEY } from './tokens';
21
+ import { isElement } from '@vri/utils';
22
+
23
+ import type { WatchStopHandle } from 'vue';
24
+ import type { Measurable } from './types';
25
+
26
+ defineOptions({
27
+ name: 'VriPopperTrigger',
28
+ inheritAttrs: false
29
+ });
30
+
31
+ interface Props {
32
+ virtualRef?: Measurable
33
+ virtualTriggering?: boolean
34
+ onMouseenter?: Function
35
+ onMouseleave?: Function
36
+ onClick?: Function
37
+ onKeydown?: Function
38
+ onFocus?: Function
39
+ onBlur?: Function
40
+ onContextmenu?: Function
41
+ id?: string
42
+ open?: boolean
43
+ }
44
+
45
+ const {
46
+ id,
47
+ open,
48
+ virtualRef
49
+ } = defineProps<Props>();
50
+
51
+ const instance = getCurrentInstance()!;
52
+
53
+ const { role, triggerRef } = inject(POPPER_INJECTION_KEY, undefined)!;
54
+
55
+ useForwardRef(triggerRef);
56
+
57
+ const ariaControls = computed<string | undefined>(() => {
58
+ return ariaHaspopup.value ? id : undefined;
59
+ });
60
+
61
+ const ariaDescribedby = computed<string | undefined>(() => {
62
+ if (role && role.value === 'tooltip') {
63
+ return open && id ? id : undefined;
64
+ }
65
+ return undefined;
66
+ });
67
+
68
+ const ariaHaspopup = computed<string | undefined>(() => {
69
+ if (role && role.value !== 'tooltip') {
70
+ return role.value;
71
+ }
72
+ return undefined;
73
+ });
74
+
75
+ const ariaExpanded = computed<string | undefined>(() => {
76
+ return ariaHaspopup.value ? `${open}` : undefined;
77
+ });
78
+
79
+ let virtualTriggerAriaStopWatch: WatchStopHandle | undefined = undefined;
80
+
81
+ onMounted(() => {
82
+ watch(
83
+ () => virtualRef,
84
+ (virtualEl) => {
85
+ if (virtualEl) {
86
+ triggerRef.value = unrefElement(virtualEl as HTMLElement);
87
+ }
88
+ },
89
+ {
90
+ immediate: true
91
+ }
92
+ );
93
+
94
+ watch(
95
+ triggerRef,
96
+ (el, prevEl) => {
97
+ virtualTriggerAriaStopWatch?.();
98
+ virtualTriggerAriaStopWatch = undefined;
99
+ if (isElement(el)) {
100
+ ;(
101
+ [
102
+ 'onMouseenter',
103
+ 'onMouseleave',
104
+ 'onClick',
105
+ 'onKeydown',
106
+ 'onFocus',
107
+ 'onBlur',
108
+ 'onContextmenu'
109
+ ] as const
110
+ ).forEach((eventName) => {
111
+ const handler: any = instance.props[eventName];
112
+ if (handler) {
113
+ ;(el as HTMLElement).addEventListener(
114
+ eventName.slice(2).toLowerCase(),
115
+ handler
116
+ )
117
+ ;(prevEl as HTMLElement)?.removeEventListener?.(
118
+ eventName.slice(2).toLowerCase(),
119
+ handler
120
+ );
121
+ }
122
+ });
123
+ virtualTriggerAriaStopWatch = watch(
124
+ [ariaControls, ariaDescribedby, ariaHaspopup, ariaExpanded],
125
+ (watches) => {
126
+ ;[
127
+ 'aria-controls',
128
+ 'aria-describedby',
129
+ 'aria-haspopup',
130
+ 'aria-expanded'
131
+ ].forEach((key, idx) => {
132
+ isNil(watches[idx])
133
+ ? el.removeAttribute(key)
134
+ : el.setAttribute(key, watches[idx]!);
135
+ });
136
+ },
137
+ { immediate: true }
138
+ );
139
+ }
140
+ if (isElement(prevEl)) {
141
+ ;[
142
+ 'aria-controls',
143
+ 'aria-describedby',
144
+ 'aria-haspopup',
145
+ 'aria-expanded'
146
+ ].forEach((key) => prevEl.removeAttribute(key));
147
+ }
148
+ },
149
+ {
150
+ immediate: true
151
+ }
152
+ );
153
+ });
154
+
155
+ onBeforeUnmount(() => {
156
+ virtualTriggerAriaStopWatch?.();
157
+ virtualTriggerAriaStopWatch = undefined;
158
+ });
159
+
160
+ defineExpose({
161
+ /**
162
+ * @description trigger element
163
+ */
164
+ triggerRef
165
+ });
166
+ </script>
@@ -0,0 +1,49 @@
1
+ import type { Options, Placement } from '@popperjs/core';
2
+ import { type StyleValue } from 'vue';
3
+
4
+ export type Measurable = {
5
+ getBoundingClientRect: () => DOMRect;
6
+ };
7
+
8
+ export interface CreatePopperInstanceParams {
9
+ referenceEl: Measurable;
10
+ popperContentEl: HTMLElement;
11
+ arrowEl: HTMLElement | undefined;
12
+ }
13
+
14
+ export interface PopperCoreConfigProps {
15
+ boundariesPadding?: number;
16
+ fallbackPlacements?: Placement[];
17
+ gpuAcceleration?: boolean;
18
+ offset?: number;
19
+ placement?: Placement;
20
+ popperOptions?: Partial<Options>;
21
+ strategy?: 'fixed' | 'absolute';
22
+ }
23
+
24
+ export interface PopperContentProps {
25
+ boundariesPadding?: number;
26
+ fallbackPlacements?: Placement[];
27
+ gpuAcceleration?: boolean;
28
+ offset?: number;
29
+ placement?: Placement;
30
+ popperOptions?: Partial<Options>;
31
+ strategy?: 'fixed' | 'absolute';
32
+ id?: string;
33
+ style?: StyleValue;
34
+ className?: ClassType;
35
+ effect?: string;
36
+ visible?: boolean;
37
+ enterable?: boolean;
38
+ pure?: boolean;
39
+ focusOnShow?: boolean;
40
+ trapping?: boolean;
41
+ popperClass?: ClassType;
42
+ popperStyle?: StyleValue;
43
+ referenceEl?: HTMLElement;
44
+ triggerTargetEl?: HTMLElement;
45
+ stopPopperMouseEvent?: boolean;
46
+ ariaLabel?: string;
47
+ virtualTriggering?: boolean;
48
+ zIndex?: number;
49
+ }
@@ -0,0 +1,81 @@
1
+ import { isClient, unrefElement } from '@vueuse/core';
2
+ import type { ComponentPublicInstance } from 'vue';
3
+ import type { MaybeRef } from '@vueuse/core';
4
+ import type { Measurable } from './tokens';
5
+ import type { PopperCoreConfigProps } from './types';
6
+
7
+ type ArrowProps = {
8
+ arrowEl: HTMLElement | undefined;
9
+ arrowOffset: number | undefined;
10
+ };
11
+
12
+ export const buildPopperOptions = (props: PopperCoreConfigProps, arrowProps: ArrowProps) => {
13
+ const { placement, strategy, popperOptions } = props;
14
+ const options = {
15
+ placement,
16
+ strategy,
17
+ ...popperOptions,
18
+ modifiers: genModifiers(props)
19
+ };
20
+
21
+ attachArrow(options, arrowProps);
22
+ deriveExtraModifiers(options, popperOptions?.modifiers);
23
+ return options;
24
+ };
25
+
26
+ export const unwrapMeasurableEl = ($el: MaybeRef<Measurable | undefined | ComponentPublicInstance>) => {
27
+ if (!isClient) return;
28
+ return unrefElement($el as HTMLElement);
29
+ };
30
+
31
+ function genModifiers(options: PopperCoreConfigProps) {
32
+ const { offset, gpuAcceleration, fallbackPlacements } = options;
33
+ return [
34
+ {
35
+ name: 'offset',
36
+ options: {
37
+ offset: [0, offset ?? 12]
38
+ }
39
+ },
40
+ {
41
+ name: 'preventOverflow',
42
+ options: {
43
+ padding: {
44
+ top: 2,
45
+ bottom: 2,
46
+ left: 5,
47
+ right: 5
48
+ }
49
+ }
50
+ },
51
+ {
52
+ name: 'flip',
53
+ options: {
54
+ padding: 5,
55
+ fallbackPlacements
56
+ }
57
+ },
58
+ {
59
+ name: 'computeStyles',
60
+ options: {
61
+ gpuAcceleration
62
+ }
63
+ }
64
+ ];
65
+ }
66
+
67
+ function attachArrow(options: any, { arrowEl, arrowOffset }: ArrowProps) {
68
+ options.modifiers.push({
69
+ name: 'arrow',
70
+ options: {
71
+ element: arrowEl,
72
+ padding: arrowOffset ?? 5
73
+ }
74
+ } as any);
75
+ }
76
+
77
+ function deriveExtraModifiers(options: any, modifiers: PopperCoreConfigProps['popperOptions']['modifiers']) {
78
+ if (modifiers) {
79
+ options.modifiers = [...options.modifiers, ...(modifiers ?? [])];
80
+ }
81
+ }
@@ -0,0 +1,32 @@
1
+ import { toCanvas } from 'qrcode';
2
+ import type { QRCodeRenderersOptions } from 'qrcode';
3
+ import type { RenderQrCodeParams, ContentType } from './types';
4
+ import { cloneDeep } from 'lodash';
5
+
6
+ export const renderQrCode = ({ canvas, content, width = 0, options: params = {} }: RenderQrCodeParams) => {
7
+ const options = cloneDeep(params);
8
+ // 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
9
+ options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);
10
+
11
+ return getOriginWidth(content, options).then((_width: number) => {
12
+ options.scale = width === 0 ? undefined : (width / _width) * 4;
13
+ return toCanvas(canvas, content, options);
14
+ });
15
+ };
16
+
17
+ // 得到原QrCode的大小,以便缩放得到正确的QrCode大小
18
+ function getOriginWidth(content: ContentType, options: QRCodeRenderersOptions) {
19
+ const _canvas = document.createElement('canvas');
20
+ return toCanvas(_canvas, content, options).then(() => _canvas.width);
21
+ }
22
+
23
+ // 对于内容少的QrCode,增大容错率
24
+ function getErrorCorrectionLevel(content: ContentType) {
25
+ if (content.length > 36) {
26
+ return 'M';
27
+ } else if (content.length > 16) {
28
+ return 'Q';
29
+ } else {
30
+ return 'H';
31
+ }
32
+ }
@@ -0,0 +1,82 @@
1
+ import { isString } from '@vri/utils';
2
+ import type { LogoType, RenderQrCodeParams } from './types';
3
+
4
+ export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => {
5
+ if (!logo) {
6
+ return new Promise((resolve) => {
7
+ resolve((canvas as HTMLCanvasElement).toDataURL());
8
+ });
9
+ }
10
+ const canvasWidth = (canvas as HTMLCanvasElement).width;
11
+ const { logoSize = 0.15, bgColor = '#fff', borderSize = 0.05, crossOrigin, borderRadius = 8, logoRadius = 0 } = logo as LogoType;
12
+
13
+ const logoSrc: string = isString(logo) ? logo : logo.src;
14
+ const logoWidth = canvasWidth * logoSize;
15
+ const logoXY = (canvasWidth * (1 - logoSize)) / 2;
16
+ const logoBgWidth = canvasWidth * (logoSize + borderSize);
17
+ const logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2;
18
+
19
+ const ctx = canvas.getContext('2d');
20
+ if (!ctx) return;
21
+
22
+ // logo 底色
23
+ canvasRoundRect(ctx)(logoBgXY, logoBgXY, logoBgWidth, logoBgWidth, borderRadius);
24
+ ctx.fillStyle = bgColor;
25
+ ctx.fill();
26
+
27
+ // logo
28
+ const image = new Image();
29
+ if (crossOrigin || logoRadius) {
30
+ image.setAttribute('crossOrigin', crossOrigin || 'anonymous');
31
+ }
32
+ image.src = logoSrc;
33
+
34
+ // 使用image绘制可以避免某些跨域情况
35
+ const drawLogoWithImage = (image: CanvasImageSource) => {
36
+ ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
37
+ };
38
+
39
+ // 使用canvas绘制以获得更多的功能
40
+ const drawLogoWithCanvas = (image: HTMLImageElement) => {
41
+ const canvasImage = document.createElement('canvas');
42
+ canvasImage.width = logoXY + logoWidth;
43
+ canvasImage.height = logoXY + logoWidth;
44
+ const imageCanvas = canvasImage.getContext('2d');
45
+ if (!imageCanvas || !ctx) return;
46
+ imageCanvas.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
47
+
48
+ canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius);
49
+ if (!ctx) return;
50
+ const fillStyle = ctx.createPattern(canvasImage, 'no-repeat');
51
+ if (fillStyle) {
52
+ ctx.fillStyle = fillStyle;
53
+ ctx.fill();
54
+ }
55
+ };
56
+
57
+ // 将 logo绘制到 canvas上
58
+ return new Promise((resolve) => {
59
+ image.onload = () => {
60
+ logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image);
61
+ resolve((canvas as HTMLCanvasElement).toDataURL());
62
+ };
63
+ });
64
+ };
65
+
66
+ // copy来的方法,用于绘制圆角
67
+ function canvasRoundRect(ctx: CanvasRenderingContext2D) {
68
+ return (x: number, y: number, w: number, h: number, r: number) => {
69
+ const minSize = Math.min(w, h);
70
+ if (r > minSize / 2) {
71
+ r = minSize / 2;
72
+ }
73
+ ctx.beginPath();
74
+ ctx.moveTo(x + r, y);
75
+ ctx.arcTo(x + w, y, x + w, y + h, r);
76
+ ctx.arcTo(x + w, y + h, x, y + h, r);
77
+ ctx.arcTo(x, y + h, x, y, r);
78
+ ctx.arcTo(x, y, x + w, y, r);
79
+ ctx.closePath();
80
+ return ctx;
81
+ };
82
+ }
@@ -0,0 +1,5 @@
1
+ import Qrcode from './index.vue';
2
+
3
+ export * from './types';
4
+
5
+ export { Qrcode };
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <div>
3
+ <component :is="tag" ref="wrapRef" />
4
+ </div>
5
+ </template>
6
+ <script setup lang="ts">
7
+ import { toDataURL } from 'qrcode';
8
+ import { onMounted, ref, unref } from 'vue';
9
+ import { useNamespace } from '../../use';
10
+ import { type LogoType, type QRCodeRenderersOptions, toCanvas } from './qrcodePlus';
11
+ import { downloadByUrl } from '@vri/utils';
12
+
13
+ interface Props {
14
+ /** 二维码资源 */
15
+ value?: string | any[];
16
+ /** 二维码配置 */
17
+ options?: QRCodeRenderersOptions;
18
+ /** 宽度 */
19
+ width?: number;
20
+ /** 中间的 logo */
21
+ logo?: LogoType;
22
+ /** img 不支持内嵌logo */
23
+ tag?: 'canvas' | 'img';
24
+ }
25
+
26
+ const {
27
+ value,
28
+ options,
29
+ width = 200,
30
+ logo,
31
+ tag = 'canvas'
32
+ } = defineProps<Props>();
33
+
34
+ const emit = defineEmits(['done', 'error']);
35
+
36
+ const ns = /* hoist-static*/ useNamespace('qrcode');
37
+
38
+ defineOptions({
39
+ name: ns.b()
40
+ });
41
+
42
+ const wrapRef = ref<HTMLCanvasElement | HTMLImageElement | null>(null);
43
+
44
+ async function createQrcode () {
45
+ try {
46
+ const renderValue = String(value);
47
+ const wrapEl = unref(wrapRef);
48
+
49
+ if (!wrapEl) return;
50
+
51
+ if (tag === 'canvas') {
52
+ const url: string = await toCanvas({
53
+ canvas: wrapEl,
54
+ width,
55
+ logo,
56
+ content: renderValue,
57
+ options: options || {}
58
+ });
59
+ emit('done', { url, ctx: (wrapEl as HTMLCanvasElement).getContext('2d') });
60
+ return;
61
+ }
62
+
63
+ if (tag === 'img') {
64
+ const url = await toDataURL(renderValue, {
65
+ errorCorrectionLevel: 'H',
66
+ width,
67
+ ...options
68
+ });
69
+ (unref(wrapRef) as HTMLImageElement).src = url;
70
+ emit('done', { url });
71
+ }
72
+ } catch (error) {
73
+ emit('error', error);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * file download
79
+ */
80
+ function download (fileName?: string) {
81
+ let url = '';
82
+ const wrapEl = unref(wrapRef);
83
+ if (wrapEl instanceof HTMLCanvasElement) {
84
+ url = wrapEl.toDataURL();
85
+ } else if (wrapEl instanceof HTMLImageElement) {
86
+ url = wrapEl.src;
87
+ }
88
+ if (!url) return;
89
+ downloadByUrl({
90
+ url,
91
+ fileName
92
+ });
93
+ }
94
+
95
+ onMounted(createQrcode);
96
+
97
+ const instance = getCurrentInstance()!;
98
+
99
+ // 监听参数变化重新生成二维码
100
+ watch(instance.props, () => {
101
+ createQrcode();
102
+ }, { deep: true });
103
+
104
+ defineExpose({
105
+ download
106
+ });
107
+ </script>
@@ -0,0 +1,4 @@
1
+ // 参考 qr-code-with-logo 进行ts版本修改
2
+ import { toCanvas } from './toCanvas';
3
+ export * from './types';
4
+ export { toCanvas };
@@ -0,0 +1,11 @@
1
+ import { renderQrCode } from './drawCanvas';
2
+ import { drawLogo } from './drawLogo';
3
+ import type { RenderQrCodeParams } from './types';
4
+
5
+ export const toCanvas = (options: RenderQrCodeParams) => {
6
+ return renderQrCode(options)
7
+ .then(() => {
8
+ return options;
9
+ })
10
+ .then(drawLogo) as Promise<string>;
11
+ };
@@ -0,0 +1,38 @@
1
+ import type { QRCodeSegment, QRCodeRenderersOptions } from 'qrcode';
2
+
3
+ export type ContentType = string | QRCodeSegment[];
4
+
5
+ export type { QRCodeRenderersOptions };
6
+
7
+ export type LogoType = {
8
+ src: string;
9
+ logoSize: number;
10
+ borderColor: string;
11
+ bgColor: string;
12
+ borderSize: number;
13
+ crossOrigin: string;
14
+ borderRadius: number;
15
+ logoRadius: number;
16
+ };
17
+
18
+ export interface RenderQrCodeParams {
19
+ canvas: any;
20
+ content: ContentType;
21
+ width?: number;
22
+ options?: QRCodeRenderersOptions;
23
+ logo?: LogoType | string;
24
+ image?: HTMLImageElement;
25
+ downloadName?: string;
26
+ download?: boolean | Fn;
27
+ }
28
+
29
+ export type ToCanvasFn = (options: RenderQrCodeParams) => Promise<unknown>;
30
+
31
+ export interface QrCodeActionType {
32
+ download: (fileName?: string) => void;
33
+ }
34
+
35
+ export interface QrcodeDoneEventParams {
36
+ url: string;
37
+ ctx?: CanvasRenderingContext2D | null;
38
+ }