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,60 @@
1
+ <template>
2
+ <div :class="ns.b()" :style="filterStyle">
3
+ <div :class="ns.e('filter-menu')" v-for="(menu, index) in menus" :key="menu.title">
4
+ <!-- 单选、多选、级联、更多 -->
5
+ <template v-if="menu.component !== ComponentType.DATE">
6
+ <component
7
+ :is="filterComponents[index]"
8
+ :title="menu.title"
9
+ :options="menu.options"
10
+ :type="menu?.type"
11
+ :label-key="menu?.labelKey"
12
+ :value-key="menu?.valueKey"
13
+ :children-key="menu?.childrenKey"
14
+ :has-all="menu?.hasAll"
15
+ :hide-search="menu?.hideSearch"
16
+ :value="menu?.value"
17
+ :required="menu?.required"
18
+ @submit="menu.submitFn"
19
+ />
20
+ </template>
21
+ <!-- 日期 -->
22
+ <template v-else>
23
+ <component :is="filterComponents[index]" :title="menu.title" :type="menu?.type" :shortcuts="menu?.shortcuts" @submit="menu.submitFn"/>
24
+ </template>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { type CSSProperties, defineAsyncComponent, computed } from 'vue';
31
+ import { useNamespace } from '../../use';
32
+ import { ComponentType, type FilterMenuProps } from './type';
33
+
34
+ interface FilterProps {
35
+ menus: Array<FilterMenuProps>;
36
+ gap?: string | number;
37
+ }
38
+
39
+ const { menus, gap = 40 } = defineProps<FilterProps>();
40
+
41
+ const ns = /* hoist-static*/ useNamespace('filter');
42
+ defineOptions({ name: ns.b() });
43
+
44
+ const filterStyle = computed(() => {
45
+ return {
46
+ ...ns.cssVarBlock({
47
+ gap: isNaN(+gap) ? `${gap}` : `${gap}px`
48
+ })
49
+ } as CSSProperties;
50
+ });
51
+
52
+ let filterComponents: any = {};
53
+ menus.forEach((menu, index) => {
54
+ filterComponents[index] = defineAsyncComponent(() => import(`../${menu.component}/index.vue`));
55
+ });
56
+ </script>
57
+
58
+ <style lang="scss" scoped>
59
+ @import './index.scss';
60
+ </style>
@@ -0,0 +1,13 @@
1
+ export enum ComponentType {
2
+ CASCADER = 'cascader-select',
3
+ SINGLE = 'single-select',
4
+ MULTIPLE = 'multiple-select',
5
+ DATE = 'datepicker',
6
+ TILED = 'tiled-select'
7
+ }
8
+
9
+ export interface FilterMenuProps {
10
+ component: ComponentType;
11
+ submitFn: Function;
12
+ [key: string]: any;
13
+ }
@@ -0,0 +1,6 @@
1
+ import FocusTrap from './index.vue';
2
+
3
+ export { FocusTrap };
4
+
5
+ export * from './tokens';
6
+ export * from './utils';
@@ -0,0 +1,328 @@
1
+ <template>
2
+ <slot :handle-keydown="onKeydown" />
3
+ </template>
4
+
5
+ <script lang="ts">
6
+ import { EVENT_CODE } from '@vri/constants';
7
+ import { useEscapeKeydown } from '@vri/use';
8
+ import { isString } from '@vri/utils';
9
+ import { isNil } from 'lodash';
10
+ import type { PropType } from 'vue';
11
+ import {
12
+ defineComponent,
13
+ nextTick,
14
+ onBeforeUnmount,
15
+ onMounted,
16
+ provide,
17
+ ref,
18
+ unref,
19
+ watch
20
+ } from 'vue';
21
+ import {
22
+ FOCUS_AFTER_RELEASED,
23
+ FOCUS_AFTER_TRAPPED,
24
+ FOCUS_AFTER_TRAPPED_OPTS,
25
+ FOCUS_TRAP_INJECTION_KEY,
26
+ ON_RELEASE_FOCUS_EVT,
27
+ ON_TRAP_FOCUS_EVT
28
+ } from './tokens';
29
+ import type { FocusLayer } from './utils';
30
+ import {
31
+ createFocusOutPreventedEvent,
32
+ focusableStack,
33
+ focusFirstDescendant,
34
+ getEdges,
35
+ isFocusCausedByUserEvent,
36
+ obtainAllFocusableElements,
37
+ tryFocus,
38
+ useFocusReason
39
+ } from './utils';
40
+
41
+ export default defineComponent({
42
+ name: 'VriFocusTrap',
43
+ inheritAttrs: false,
44
+ props: {
45
+ loop: Boolean,
46
+ trapped: Boolean,
47
+ focusTrapEl: Object as PropType<HTMLElement>,
48
+ focusStartEl: {
49
+ type: [Object, String] as PropType<'container' | 'first' | HTMLElement>,
50
+ default: 'first'
51
+ }
52
+ },
53
+ emits: [
54
+ ON_TRAP_FOCUS_EVT,
55
+ ON_RELEASE_FOCUS_EVT,
56
+ 'focusin',
57
+ 'focusout',
58
+ 'focusout-prevented',
59
+ 'release-requested'
60
+ ],
61
+ setup (props, { emit }) {
62
+ const forwardRef = ref<HTMLElement | undefined>();
63
+ let lastFocusBeforeTrapped: HTMLElement | null;
64
+ let lastFocusAfterTrapped: HTMLElement | null;
65
+
66
+ const { focusReason } = useFocusReason();
67
+
68
+ useEscapeKeydown((event) => {
69
+ if (props.trapped && !focusLayer.paused) {
70
+ emit('release-requested', event);
71
+ }
72
+ });
73
+
74
+ const focusLayer: FocusLayer = {
75
+ paused: false,
76
+ pause () {
77
+ this.paused = true;
78
+ },
79
+ resume () {
80
+ this.paused = false;
81
+ }
82
+ };
83
+
84
+ const onKeydown = (e: KeyboardEvent) => {
85
+ if (!props.loop && !props.trapped) return;
86
+ if (focusLayer.paused) return;
87
+
88
+ const { key, altKey, ctrlKey, metaKey, currentTarget, shiftKey } = e;
89
+ const { loop } = props;
90
+ const isTabbing =
91
+ key === EVENT_CODE.tab && !altKey && !ctrlKey && !metaKey;
92
+
93
+ const currentFocusingEl = document.activeElement;
94
+ if (isTabbing && currentFocusingEl) {
95
+ const container = currentTarget as HTMLElement;
96
+ const [first, last] = getEdges(container);
97
+ const isTabbable = first && last;
98
+ if (!isTabbable) {
99
+ if (currentFocusingEl === container) {
100
+ const focusoutPreventedEvent = createFocusOutPreventedEvent({
101
+ focusReason: focusReason.value
102
+ });
103
+ emit('focusout-prevented', focusoutPreventedEvent);
104
+ if (!focusoutPreventedEvent.defaultPrevented) {
105
+ e.preventDefault();
106
+ }
107
+ }
108
+ } else {
109
+ if (!shiftKey && currentFocusingEl === last) {
110
+ const focusoutPreventedEvent = createFocusOutPreventedEvent({
111
+ focusReason: focusReason.value
112
+ });
113
+ emit('focusout-prevented', focusoutPreventedEvent);
114
+ if (!focusoutPreventedEvent.defaultPrevented) {
115
+ e.preventDefault();
116
+ if (loop) tryFocus(first, true);
117
+ }
118
+ } else if (
119
+ shiftKey &&
120
+ [first, container].includes(currentFocusingEl as HTMLElement)
121
+ ) {
122
+ const focusoutPreventedEvent = createFocusOutPreventedEvent({
123
+ focusReason: focusReason.value
124
+ });
125
+ emit('focusout-prevented', focusoutPreventedEvent);
126
+ if (!focusoutPreventedEvent.defaultPrevented) {
127
+ e.preventDefault();
128
+ if (loop) tryFocus(last, true);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ };
134
+
135
+ provide(FOCUS_TRAP_INJECTION_KEY, {
136
+ focusTrapRef: forwardRef,
137
+ onKeydown
138
+ });
139
+
140
+ watch(
141
+ () => props.focusTrapEl,
142
+ (focusTrapEl) => {
143
+ if (focusTrapEl) {
144
+ forwardRef.value = focusTrapEl;
145
+ }
146
+ },
147
+ { immediate: true }
148
+ );
149
+
150
+ watch([forwardRef], ([forwardRef], [oldForwardRef]) => {
151
+ if (forwardRef) {
152
+ forwardRef.addEventListener('keydown', onKeydown);
153
+ forwardRef.addEventListener('focusin', onFocusIn);
154
+ forwardRef.addEventListener('focusout', onFocusOut);
155
+ }
156
+ if (oldForwardRef) {
157
+ oldForwardRef.removeEventListener('keydown', onKeydown);
158
+ oldForwardRef.removeEventListener('focusin', onFocusIn);
159
+ oldForwardRef.removeEventListener('focusout', onFocusOut);
160
+ }
161
+ });
162
+
163
+ const trapOnFocus = (e: Event) => {
164
+ emit(ON_TRAP_FOCUS_EVT, e);
165
+ };
166
+ const releaseOnFocus = (e: Event) => emit(ON_RELEASE_FOCUS_EVT, e);
167
+
168
+ const onFocusIn = (e: FocusEvent) => {
169
+ const trapContainer = unref(forwardRef);
170
+ if (!trapContainer) return;
171
+
172
+ const target = e.target as HTMLElement | null;
173
+ const relatedTarget = e.relatedTarget as HTMLElement | null;
174
+ const isFocusedInTrap = target && trapContainer.contains(target);
175
+
176
+ if (!props.trapped) {
177
+ const isPrevFocusedInTrap =
178
+ relatedTarget && trapContainer.contains(relatedTarget);
179
+ if (!isPrevFocusedInTrap) {
180
+ lastFocusBeforeTrapped = relatedTarget;
181
+ }
182
+ }
183
+
184
+ if (isFocusedInTrap) emit('focusin', e);
185
+
186
+ if (focusLayer.paused) return;
187
+
188
+ if (props.trapped) {
189
+ if (isFocusedInTrap) {
190
+ lastFocusAfterTrapped = target;
191
+ } else {
192
+ tryFocus(lastFocusAfterTrapped, true);
193
+ }
194
+ }
195
+ };
196
+
197
+ const onFocusOut = (e: Event) => {
198
+ const trapContainer = unref(forwardRef);
199
+ if (focusLayer.paused || !trapContainer) return;
200
+
201
+ if (props.trapped) {
202
+ const relatedTarget = (e as FocusEvent)
203
+ .relatedTarget as HTMLElement | null;
204
+ if (!isNil(relatedTarget) && !trapContainer.contains(relatedTarget)) {
205
+ // Give embedded focus layer time to pause this layer before reclaiming focus
206
+ // And only reclaim focus if it should currently be trapping
207
+ setTimeout(() => {
208
+ if (!focusLayer.paused && props.trapped) {
209
+ const focusoutPreventedEvent = createFocusOutPreventedEvent({
210
+ focusReason: focusReason.value
211
+ });
212
+ emit('focusout-prevented', focusoutPreventedEvent);
213
+ if (!focusoutPreventedEvent.defaultPrevented) {
214
+ tryFocus(lastFocusAfterTrapped, true);
215
+ }
216
+ }
217
+ }, 0);
218
+ }
219
+ } else {
220
+ const target = e.target as HTMLElement | null;
221
+ const isFocusedInTrap = target && trapContainer.contains(target);
222
+ if (!isFocusedInTrap) emit('focusout', e);
223
+ }
224
+ };
225
+
226
+ async function startTrap () {
227
+ // Wait for forwardRef to resolve
228
+ await nextTick();
229
+ const trapContainer = unref(forwardRef);
230
+ if (trapContainer) {
231
+ focusableStack.push(focusLayer);
232
+ const prevFocusedElement = trapContainer.contains(
233
+ document.activeElement
234
+ )
235
+ ? lastFocusBeforeTrapped
236
+ : document.activeElement;
237
+ lastFocusBeforeTrapped = prevFocusedElement as HTMLElement | null;
238
+ const isPrevFocusContained = trapContainer.contains(prevFocusedElement);
239
+ if (!isPrevFocusContained) {
240
+ const focusEvent = new Event(
241
+ FOCUS_AFTER_TRAPPED,
242
+ FOCUS_AFTER_TRAPPED_OPTS
243
+ );
244
+ trapContainer.addEventListener(FOCUS_AFTER_TRAPPED, trapOnFocus);
245
+ trapContainer.dispatchEvent(focusEvent);
246
+ if (!focusEvent.defaultPrevented) {
247
+ nextTick(() => {
248
+ let focusStartEl = props.focusStartEl;
249
+ if (!isString(focusStartEl)) {
250
+ tryFocus(focusStartEl);
251
+ if (document.activeElement !== focusStartEl) {
252
+ focusStartEl = 'first';
253
+ }
254
+ }
255
+ if (focusStartEl === 'first') {
256
+ focusFirstDescendant(
257
+ obtainAllFocusableElements(trapContainer),
258
+ true
259
+ );
260
+ }
261
+ if (
262
+ document.activeElement === prevFocusedElement ||
263
+ focusStartEl === 'container'
264
+ ) {
265
+ tryFocus(trapContainer);
266
+ }
267
+ });
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ function stopTrap () {
274
+ const trapContainer = unref(forwardRef);
275
+
276
+ if (trapContainer) {
277
+ trapContainer.removeEventListener(FOCUS_AFTER_TRAPPED, trapOnFocus);
278
+
279
+ const releasedEvent = new CustomEvent(FOCUS_AFTER_RELEASED, {
280
+ ...FOCUS_AFTER_TRAPPED_OPTS,
281
+ detail: {
282
+ focusReason: focusReason.value
283
+ }
284
+ });
285
+ trapContainer.addEventListener(FOCUS_AFTER_RELEASED, releaseOnFocus);
286
+ trapContainer.dispatchEvent(releasedEvent);
287
+
288
+ if (
289
+ !releasedEvent.defaultPrevented &&
290
+ (focusReason.value === 'keyboard' || !isFocusCausedByUserEvent())
291
+ ) {
292
+ tryFocus(lastFocusBeforeTrapped ?? document.body, true);
293
+ }
294
+
295
+ trapContainer.removeEventListener(FOCUS_AFTER_RELEASED, trapOnFocus);
296
+ focusableStack.remove(focusLayer);
297
+ }
298
+ }
299
+
300
+ onMounted(() => {
301
+ if (props.trapped) {
302
+ startTrap();
303
+ }
304
+
305
+ watch(
306
+ () => props.trapped,
307
+ (trapped) => {
308
+ if (trapped) {
309
+ startTrap();
310
+ } else {
311
+ stopTrap();
312
+ }
313
+ }
314
+ );
315
+ });
316
+
317
+ onBeforeUnmount(() => {
318
+ if (props.trapped) {
319
+ stopTrap();
320
+ }
321
+ });
322
+
323
+ return {
324
+ onKeydown
325
+ };
326
+ }
327
+ });
328
+ </script>
@@ -0,0 +1,23 @@
1
+ import type { InjectionKey, Ref } from 'vue';
2
+
3
+ export const FOCUS_AFTER_TRAPPED = 'focus-trap.focus-after-trapped';
4
+ export const FOCUS_AFTER_RELEASED = 'focus-trap.focus-after-released';
5
+ export const FOCUSOUT_PREVENTED = 'focus-trap.focusout-prevented';
6
+ export const FOCUS_AFTER_TRAPPED_OPTS: EventInit = {
7
+ cancelable: true,
8
+ bubbles: false
9
+ };
10
+ export const FOCUSOUT_PREVENTED_OPTS: EventInit = {
11
+ cancelable: true,
12
+ bubbles: false
13
+ };
14
+
15
+ export const ON_TRAP_FOCUS_EVT = 'focusAfterTrapped';
16
+ export const ON_RELEASE_FOCUS_EVT = 'focusAfterReleased';
17
+
18
+ export type FocusTrapInjectionContext = {
19
+ focusTrapRef: Ref<HTMLElement | undefined>;
20
+ onKeydown: (e: KeyboardEvent) => void;
21
+ };
22
+
23
+ export const FOCUS_TRAP_INJECTION_KEY: InjectionKey<FocusTrapInjectionContext> = Symbol('elFocusTrap');
@@ -0,0 +1,178 @@
1
+ import { onBeforeUnmount, onMounted, ref } from 'vue';
2
+ import { FOCUSOUT_PREVENTED, FOCUSOUT_PREVENTED_OPTS } from './tokens';
3
+
4
+ const focusReason = ref<'pointer' | 'keyboard'>();
5
+ const lastUserFocusTimestamp = ref<number>(0);
6
+ const lastAutomatedFocusTimestamp = ref<number>(0);
7
+ let focusReasonUserCount = 0;
8
+
9
+ export type FocusLayer = {
10
+ paused: boolean;
11
+ pause: () => void;
12
+ resume: () => void;
13
+ };
14
+
15
+ export type FocusStack = FocusLayer[];
16
+
17
+ export const obtainAllFocusableElements = (element: HTMLElement): HTMLElement[] => {
18
+ const nodes: HTMLElement[] = [];
19
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, {
20
+ acceptNode: (
21
+ node: Element & {
22
+ disabled: boolean;
23
+ hidden: boolean;
24
+ type: string;
25
+ tabIndex: number;
26
+ }
27
+ ) => {
28
+ const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden';
29
+ if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;
30
+ return node.tabIndex >= 0 || node === document.activeElement ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
31
+ }
32
+ });
33
+ while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement);
34
+
35
+ return nodes;
36
+ };
37
+
38
+ export const getVisibleElement = (elements: HTMLElement[], container: HTMLElement) => {
39
+ for (const element of elements) {
40
+ if (!isHidden(element, container)) return element;
41
+ }
42
+ };
43
+
44
+ export const isHidden = (element: HTMLElement, container: HTMLElement) => {
45
+ if (process.env.NODE_ENV === 'test') return false;
46
+ if (getComputedStyle(element).visibility === 'hidden') return true;
47
+
48
+ while (element) {
49
+ if (container && element === container) return false;
50
+ if (getComputedStyle(element).display === 'none') return true;
51
+ element = element.parentElement as HTMLElement;
52
+ }
53
+
54
+ return false;
55
+ };
56
+
57
+ export const getEdges = (container: HTMLElement) => {
58
+ const focusable = obtainAllFocusableElements(container);
59
+ const first = getVisibleElement(focusable, container);
60
+ const last = getVisibleElement(focusable.reverse(), container);
61
+ return [first, last];
62
+ };
63
+
64
+ const isSelectable = (element: any): element is HTMLInputElement & { select: () => void } => {
65
+ return element instanceof HTMLInputElement && 'select' in element;
66
+ };
67
+
68
+ export const tryFocus = (element?: HTMLElement | { focus: () => void } | null, shouldSelect?: boolean) => {
69
+ if (element && element.focus) {
70
+ const prevFocusedElement = document.activeElement;
71
+ element.focus({ preventScroll: true });
72
+ lastAutomatedFocusTimestamp.value = window.performance.now();
73
+ if (element !== prevFocusedElement && isSelectable(element) && shouldSelect) {
74
+ if (element.tagName === 'INPUT') {
75
+ element.setSelectionRange(element.value.length, element.value.length);
76
+ return;
77
+ }
78
+ element.select();
79
+ }
80
+ }
81
+ };
82
+
83
+ function removeFromStack<T>(list: T[], item: T) {
84
+ const copy = [...list];
85
+
86
+ const idx = list.indexOf(item);
87
+
88
+ if (idx !== -1) {
89
+ copy.splice(idx, 1);
90
+ }
91
+ return copy;
92
+ }
93
+
94
+ const createFocusableStack = () => {
95
+ let stack = [] as FocusStack;
96
+
97
+ const push = (layer: FocusLayer) => {
98
+ const currentLayer = stack[0];
99
+
100
+ if (currentLayer && layer !== currentLayer) {
101
+ currentLayer.pause();
102
+ }
103
+
104
+ stack = removeFromStack(stack, layer);
105
+ stack.unshift(layer);
106
+ };
107
+
108
+ const remove = (layer: FocusLayer) => {
109
+ stack = removeFromStack(stack, layer);
110
+ stack[0]?.resume?.();
111
+ };
112
+
113
+ return {
114
+ push,
115
+ remove
116
+ };
117
+ };
118
+
119
+ export const focusFirstDescendant = (elements: HTMLElement[], shouldSelect = false) => {
120
+ const prevFocusedElement = document.activeElement;
121
+ for (const element of elements) {
122
+ tryFocus(element, shouldSelect);
123
+ if (document.activeElement !== prevFocusedElement) return;
124
+ }
125
+ };
126
+
127
+ export const focusableStack = createFocusableStack();
128
+
129
+ export const isFocusCausedByUserEvent = (): boolean => {
130
+ return lastUserFocusTimestamp.value > lastAutomatedFocusTimestamp.value;
131
+ };
132
+
133
+ const notifyFocusReasonPointer = () => {
134
+ focusReason.value = 'pointer';
135
+ lastUserFocusTimestamp.value = window.performance.now();
136
+ };
137
+
138
+ const notifyFocusReasonKeydown = () => {
139
+ focusReason.value = 'keyboard';
140
+ lastUserFocusTimestamp.value = window.performance.now();
141
+ };
142
+
143
+ export const useFocusReason = (): {
144
+ focusReason: typeof focusReason;
145
+ lastUserFocusTimestamp: typeof lastUserFocusTimestamp;
146
+ lastAutomatedFocusTimestamp: typeof lastAutomatedFocusTimestamp;
147
+ } => {
148
+ onMounted(() => {
149
+ if (focusReasonUserCount === 0) {
150
+ document.addEventListener('mousedown', notifyFocusReasonPointer);
151
+ document.addEventListener('touchstart', notifyFocusReasonPointer);
152
+ document.addEventListener('keydown', notifyFocusReasonKeydown);
153
+ }
154
+ focusReasonUserCount++;
155
+ });
156
+
157
+ onBeforeUnmount(() => {
158
+ focusReasonUserCount--;
159
+ if (focusReasonUserCount <= 0) {
160
+ document.removeEventListener('mousedown', notifyFocusReasonPointer);
161
+ document.removeEventListener('touchstart', notifyFocusReasonPointer);
162
+ document.removeEventListener('keydown', notifyFocusReasonKeydown);
163
+ }
164
+ });
165
+
166
+ return {
167
+ focusReason,
168
+ lastUserFocusTimestamp,
169
+ lastAutomatedFocusTimestamp
170
+ };
171
+ };
172
+
173
+ export const createFocusOutPreventedEvent = (detail: CustomEventInit['detail']) => {
174
+ return new CustomEvent(FOCUSOUT_PREVENTED, {
175
+ ...FOCUSOUT_PREVENTED_OPTS,
176
+ detail
177
+ });
178
+ };
@@ -0,0 +1,22 @@
1
+ $name: full-screen;
2
+
3
+ @include set-root-css-vars($name, $full-screen);
4
+
5
+ @function getCompCssVar($value) {
6
+ @return getCssVar($name, $value);
7
+ }
8
+
9
+ @include b($name) {
10
+ color: getCompCssVar('color');
11
+ font-size: getCompCssVar('font-size');
12
+ transition: color 0.2s;
13
+ i {
14
+ margin-right: 2px;
15
+ }
16
+ &:hover {
17
+ color: getCompCssVar('hover-color');
18
+ i {
19
+ color: getCompCssVar('hover-color');
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,3 @@
1
+ import FullScreen from './index.vue';
2
+
3
+ export { FullScreen };
@@ -0,0 +1,24 @@
1
+ <script lang="ts" setup>
2
+ import { useFullscreen } from '@vri/use';
3
+ import { Icon } from '../../';
4
+ import { useNamespace } from '../../use';
5
+
6
+ const ns = /* hoist-static*/ useNamespace('full-screen');
7
+
8
+ defineOptions({
9
+ name: ns.b()
10
+ });
11
+
12
+ const { toggle, isFullscreen } = useFullscreen();
13
+ </script>
14
+
15
+ <template>
16
+ <span :class="ns.b()" @click="toggle">
17
+ <icon :icon="!isFullscreen ? 'vi-full-screen' : 'vi-exit-screen'" />
18
+ {{ !isFullscreen ? '全屏' : '退出全屏' }}
19
+ </span>
20
+ </template>
21
+
22
+ <style lang="scss">
23
+ @import "./index.scss";
24
+ </style>
@@ -0,0 +1,3 @@
1
+ import IconText from './index.vue';
2
+
3
+ export { IconText };