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,767 @@
1
+ <template>
2
+ <el-tooltip
3
+ ref="tooltipRef"
4
+ :visible="popperVisible"
5
+ :teleported="teleported"
6
+ :popper-class="[nsCascader.e('dropdown'), popperClass]"
7
+ :popper-options="popperOptions"
8
+ :fallback-placements="[
9
+ 'bottom-start',
10
+ 'bottom',
11
+ 'top-start',
12
+ 'top',
13
+ 'right',
14
+ 'left',
15
+ ]"
16
+ :stop-popper-mouse-event="false"
17
+ :gpu-acceleration="false"
18
+ placement="bottom-start"
19
+ :transition="`${nsCascader.namespace.value}-zoom-in-top`"
20
+ effect="light"
21
+ pure
22
+ persistent
23
+ @hide="hideSuggestionPanel"
24
+ >
25
+ <template #default>
26
+ <div
27
+ v-clickoutside:[popperPaneRef]="() => togglePopperVisible(false)"
28
+ :class="[
29
+ nsCascader.b(),
30
+ nsCascader.m(realSize),
31
+ nsCascader.is('disabled', isDisabled),
32
+ $attrs.class,
33
+ ]"
34
+ :style="$attrs.style"
35
+ @click="() => togglePopperVisible(readonly ? undefined : true)"
36
+ @keydown="handleKeyDown"
37
+ @mouseenter="inputHover = true"
38
+ @mouseleave="inputHover = false"
39
+ >
40
+ <v-input
41
+ ref="input"
42
+ v-model="inputValue"
43
+ :placeholder="currentPlaceholder"
44
+ :readonly="readonly"
45
+ :disabled="isDisabled"
46
+ :validate-event="false"
47
+ :size="realSize"
48
+ :class="nsCascader.is('focus', popperVisible)"
49
+ @compositionstart="handleComposition"
50
+ @compositionupdate="handleComposition"
51
+ @compositionend="handleComposition"
52
+ @focus="(e) => $emit('focus', e)"
53
+ @blur="(e) => $emit('blur', e)"
54
+ @input="handleInput"
55
+ >
56
+ <template #suffix>
57
+ <span v-if="clearBtnVisible"
58
+ key="clear"
59
+ :class="[nsInput.e('icon'), 'icon-circle-close']" @click.stop="handleClear">
60
+ <v-icon v-bind="{ icon: 'vi-close-full', size: 16 }" />
61
+ </span>
62
+ <span v-else key="arrow-down"
63
+ :class="[
64
+ nsInput.e('icon'),
65
+ 'icon-arrow-down',
66
+ nsCascader.is('reverse', popperVisible),
67
+ ]"
68
+ @click.stop="togglePopperVisible()">
69
+ <v-icon
70
+ v-bind="{ icon: 'vi-arrow-down', size: 16 }"
71
+ />
72
+ </span>
73
+
74
+ </template>
75
+ </v-input>
76
+
77
+ <div v-if="multiple" ref="tagWrapper" :class="nsCascader.e('tags')">
78
+ <el-tag
79
+ v-for="tag in presentTags"
80
+ :key="tag.key"
81
+ :type="tagType"
82
+ :size="tagSize"
83
+ :hit="tag.hitState"
84
+ :closable="tag.closable"
85
+ disable-transitions
86
+ @close="deleteTag(tag)"
87
+ >
88
+ <template v-if="tag.isCollapseTag === false">
89
+ <span>{{ tag.text }}</span>
90
+ </template>
91
+ <template v-else>
92
+ <el-tooltip
93
+ :disabled="popperVisible || !collapseTagsTooltip"
94
+ :fallback-placements="['bottom', 'top', 'right', 'left']"
95
+ placement="bottom"
96
+ effect="light"
97
+ >
98
+ <template #default>
99
+ <span>{{ tag.text }}</span>
100
+ </template>
101
+ <template #content>
102
+ <div :class="nsCascader.e('collapse-tags')">
103
+ <div
104
+ v-for="(tag2, idx) in allPresentTags.slice(1)"
105
+ :key="idx"
106
+ :class="nsCascader.e('collapse-tag')"
107
+ >
108
+ <el-tag
109
+ :key="tag2.key"
110
+ class="in-tooltip"
111
+ :type="tagType"
112
+ :size="tagSize"
113
+ :hit="tag2.hitState"
114
+ :closable="tag2.closable"
115
+ disable-transitions
116
+ @close="deleteTag(tag2)"
117
+ >
118
+ <span>{{ tag2.text }}</span>
119
+ </el-tag>
120
+ </div>
121
+ </div>
122
+ </template>
123
+ </el-tooltip>
124
+ </template>
125
+ </el-tag>
126
+ <input
127
+ v-if="filterable && !isDisabled"
128
+ v-model="searchInputValue"
129
+ type="text"
130
+ :class="nsCascader.e('search-input')"
131
+ :placeholder="presentText ? '' : inputPlaceholder"
132
+ @input="(e) => handleInput(searchInputValue, e)"
133
+ @click.stop="togglePopperVisible(true)"
134
+ @keydown.delete="handleDelete"
135
+ @compositionstart="handleComposition"
136
+ @compositionupdate="handleComposition"
137
+ @compositionend="handleComposition"
138
+ />
139
+ </div>
140
+ </div>
141
+ </template>
142
+
143
+ <template #content>
144
+ <cascader-panel
145
+ v-show="!filtering"
146
+ ref="panel"
147
+ v-model="checkedValue"
148
+ :options="options"
149
+ :props="props"
150
+ :border="false"
151
+ :render-label="$slots.default"
152
+ @expand-change="handleExpandChange"
153
+ @close="$nextTick(() => togglePopperVisible(false))"
154
+ />
155
+ <v-scrollbar
156
+ v-if="filterable"
157
+ v-show="filtering"
158
+ ref="suggestionPanel"
159
+ tag="ul"
160
+ :class="nsCascader.e('suggestion-panel')"
161
+ :view-class="nsCascader.e('suggestion-list')"
162
+ @keydown="handleSuggestionKeyDown"
163
+ >
164
+ <template v-if="suggestions.length">
165
+ <li
166
+ v-for="item in suggestions"
167
+ :key="item.uid"
168
+ :class="[
169
+ nsCascader.e('suggestion-item'),
170
+ nsCascader.is('checked', item.checked),
171
+ ]"
172
+ :tabindex="-1"
173
+ @click="handleSuggestionClick(item)"
174
+ >
175
+ <span>{{ item.text }}</span>
176
+ <v-icon v-if="item.checked" />
177
+ </li>
178
+ </template>
179
+ <slot v-else name="empty">
180
+ <li :class="nsCascader.e('empty-text')">
181
+ {{ t('el.cascader.noMatch') }}
182
+ </li>
183
+ </slot>
184
+ </v-scrollbar>
185
+ </template>
186
+ </el-tooltip>
187
+ </template>
188
+
189
+ <script lang="ts">
190
+ import { computed, defineComponent, nextTick, onMounted, ref, watch } from 'vue';
191
+ import { isPromise } from '@vue/shared';
192
+ import { cloneDeep, debounce } from 'lodash';
193
+ import { Input as VInput } from '../';
194
+ import { Scrollbar as VScrollbar } from '../';
195
+ import { Icon as VIcon } from '../../common';
196
+ import { isClient, useCssVar, useResizeObserver } from '@vueuse/core';
197
+ import CascaderPanel from '../cascader-panel/index.vue';
198
+ import {
199
+ ElTag,
200
+ ElTooltip,
201
+ ClickOutside as Clickoutside,
202
+ useFormItem,
203
+ useLocale,
204
+ useSize
205
+ } from 'element-plus';
206
+ import { useNamespace } from '../../use';
207
+ import { isKorean } from '@vri/utils';
208
+ import { debugWarn, focusNode, getSibling } from '../../utils';
209
+ import { CHANGE_EVENT, EVENT_CODE, UPDATE_MODEL_EVENT } from '@vri/constants';
210
+ import type { ComputedRef, PropType, Ref } from 'vue';
211
+ import type { CascaderOption, CascaderProps } from '../cascader-panel/node';
212
+ import type { CascaderNode, CascaderValue, Tag } from '../cascader-panel/types';
213
+ import { type ComponentSize, isValidComponentSize } from '../select/constants';
214
+
215
+ const popperOptions: any = {
216
+ modifiers: [
217
+ {
218
+ name: 'arrowPosition',
219
+ enabled: true,
220
+ phase: 'main',
221
+ fn: ({ state }) => {
222
+ const { modifiersData, placement } = state as any;
223
+ if (['right', 'left', 'bottom', 'top'].includes(placement)) return;
224
+ modifiersData.arrow.x = 35;
225
+ },
226
+ requires: ['arrow']
227
+ }
228
+ ]
229
+ };
230
+ const COMPONENT_NAME = 'VriCascader';
231
+ export default defineComponent({
232
+ name: COMPONENT_NAME,
233
+
234
+ components: {
235
+ CascaderPanel,
236
+ ElTooltip,
237
+ ElTag,
238
+ VInput,
239
+ VIcon,
240
+ VScrollbar
241
+ },
242
+
243
+ directives: {
244
+ Clickoutside
245
+ },
246
+
247
+ props: {
248
+ modelValue: [Number, String, Array] as PropType<CascaderValue>,
249
+ options: {
250
+ type: Array as PropType<CascaderOption[]>,
251
+ default: () => [] as CascaderOption[]
252
+ },
253
+ props: {
254
+ type: Object as PropType<CascaderProps>,
255
+ default: () => ({} as CascaderProps)
256
+ },
257
+ size: {
258
+ type: String as PropType<ComponentSize>,
259
+ validator: isValidComponentSize
260
+ },
261
+ placeholder: {
262
+ type: String
263
+ },
264
+ disabled: Boolean,
265
+ clearable: Boolean,
266
+ filterable: Boolean,
267
+ filterMethod: {
268
+ type: Function as PropType<(node: CascaderNode, keyword: string) => boolean>,
269
+ default: (node: CascaderNode, keyword: string) =>
270
+ node.text.includes(keyword)
271
+ },
272
+ separator: {
273
+ type: String,
274
+ default: ' / '
275
+ },
276
+ showAllLevels: {
277
+ type: Boolean,
278
+ default: true
279
+ },
280
+ collapseTags: Boolean,
281
+ collapseTagsTooltip: {
282
+ type: Boolean,
283
+ default: false
284
+ },
285
+ debounce: {
286
+ type: Number,
287
+ default: 300
288
+ },
289
+ beforeFilter: {
290
+ type: Function as PropType<(value: string) => boolean | Promise<any>>,
291
+ default: () => true
292
+ },
293
+ popperClass: {
294
+ type: String,
295
+ default: ''
296
+ },
297
+ teleported: {
298
+ type: Boolean,
299
+ default: true
300
+ },
301
+ tagType: {
302
+ type: String,
303
+ values: ['success', 'info', 'warning', 'danger', ''],
304
+ default: 'info'
305
+ },
306
+ validateEvent: {
307
+ type: Boolean,
308
+ default: true
309
+ }
310
+ },
311
+
312
+ emits: [
313
+ UPDATE_MODEL_EVENT,
314
+ CHANGE_EVENT,
315
+ 'focus',
316
+ 'blur',
317
+ 'visible-change',
318
+ 'expand-change',
319
+ 'remove-tag'
320
+ ],
321
+
322
+ setup (props, { emit }) {
323
+ let inputInitialHeight = 0;
324
+ let pressDeleteCount = 0;
325
+
326
+ const nsCascader = useNamespace('cascader');
327
+ const nsInput = useNamespace('input');
328
+
329
+ const { t } = useLocale();
330
+ const { form, formItem } = useFormItem();
331
+
332
+ const tooltipRef = ref(null);
333
+ const input = ref(null);
334
+ const tagWrapper = ref(null);
335
+ const panel = ref(null);
336
+ const suggestionPanel = ref(null);
337
+ const popperVisible = ref(false);
338
+ const inputHover = ref(false);
339
+ const filtering = ref(false);
340
+ const inputValue = ref('');
341
+ const searchInputValue = ref('');
342
+ const presentTags: Ref<Tag[]> = ref([]);
343
+ const allPresentTags: Ref<Tag[]> = ref([]);
344
+ const suggestions: Ref<CascaderNode[]> = ref([]);
345
+ const isOnComposition = ref(false);
346
+
347
+ const isDisabled = computed(() => props.disabled || form?.disabled);
348
+ const inputPlaceholder = computed(
349
+ () => props.placeholder || t('el.cascader.placeholder')
350
+ );
351
+ const currentPlaceholder = computed(() =>
352
+ searchInputValue.value || presentTags.value.length > 0
353
+ ? ''
354
+ : inputPlaceholder.value
355
+ );
356
+ const realSize = useSize();
357
+ const tagSize = computed(() =>
358
+ ['small'].includes(realSize.value) ? 'small' : 'default'
359
+ );
360
+ const multiple = computed(() => !!props.props.multiple);
361
+ const readonly = computed(() => !props.filterable || multiple.value);
362
+ const searchKeyword = computed(() =>
363
+ multiple.value ? searchInputValue.value : inputValue.value
364
+ );
365
+ const checkedNodes: ComputedRef<CascaderNode[]> = computed(
366
+ () => panel.value?.checkedNodes || []
367
+ );
368
+ const clearBtnVisible = computed(() => {
369
+ if (
370
+ !props.clearable ||
371
+ isDisabled.value ||
372
+ filtering.value ||
373
+ !inputHover.value
374
+ )
375
+ return false;
376
+
377
+ return !!checkedNodes.value.length;
378
+ });
379
+ const presentText = computed(() => {
380
+ const { showAllLevels, separator } = props;
381
+ const nodes = checkedNodes.value;
382
+ return nodes.length
383
+ ? multiple.value
384
+ ? ''
385
+ : nodes[0].calcText(showAllLevels, separator)
386
+ : '';
387
+ });
388
+
389
+ const checkedValue = computed<CascaderValue>({
390
+ get () {
391
+ return cloneDeep(props.modelValue) as CascaderValue;
392
+ },
393
+ set (val) {
394
+ emit(UPDATE_MODEL_EVENT, val);
395
+ emit(CHANGE_EVENT, val);
396
+ if (props.validateEvent) {
397
+ formItem?.validate('change').catch((err) => debugWarn(err));
398
+ }
399
+ }
400
+ });
401
+
402
+ const popperPaneRef = computed(() => {
403
+ return tooltipRef.value?.popperRef?.contentRef;
404
+ });
405
+
406
+ const togglePopperVisible = (visible?: boolean) => {
407
+ if (isDisabled.value) return;
408
+
409
+ visible = visible ?? !popperVisible.value;
410
+
411
+ if (visible !== popperVisible.value) {
412
+ popperVisible.value = visible;
413
+ input.value?.input?.setAttribute('aria-expanded', `${visible}`);
414
+
415
+ if (visible) {
416
+ updatePopperPosition();
417
+ nextTick(panel.value?.scrollToExpandingNode);
418
+ } else if (props.filterable) {
419
+ syncPresentTextValue();
420
+ }
421
+
422
+ emit('visible-change', visible);
423
+ }
424
+ };
425
+
426
+ const updatePopperPosition = () => {
427
+ nextTick(() => {
428
+ tooltipRef.value?.updatePopper();
429
+ });
430
+ };
431
+
432
+ const hideSuggestionPanel = () => {
433
+ filtering.value = false;
434
+ };
435
+
436
+ const genTag = (node: CascaderNode): Tag => {
437
+ const { showAllLevels, separator } = props;
438
+ return {
439
+ node,
440
+ key: node.uid,
441
+ text: node.calcText(showAllLevels, separator),
442
+ hitState: false,
443
+ closable: !isDisabled.value && !node.isDisabled,
444
+ isCollapseTag: false
445
+ };
446
+ };
447
+
448
+ const deleteTag = (tag: Tag) => {
449
+ const node = tag.node as CascaderNode;
450
+ node.doCheck(false);
451
+ panel.value?.calculateCheckedValue();
452
+ emit('remove-tag', node.valueByOption);
453
+ };
454
+
455
+ const calculatePresentTags = () => {
456
+ if (!multiple.value) return;
457
+
458
+ const nodes = checkedNodes.value;
459
+ const tags: Tag[] = [];
460
+
461
+ const allTags: Tag[] = [];
462
+ nodes.forEach((node) => allTags.push(genTag(node)));
463
+ allPresentTags.value = allTags;
464
+
465
+ if (nodes.length) {
466
+ const [first, ...rest] = nodes;
467
+ const restCount = rest.length;
468
+
469
+ tags.push(genTag(first));
470
+
471
+ if (restCount) {
472
+ if (props.collapseTags) {
473
+ tags.push({
474
+ key: -1,
475
+ text: `+ ${restCount}`,
476
+ closable: false,
477
+ isCollapseTag: true
478
+ });
479
+ } else {
480
+ rest.forEach((node) => tags.push(genTag(node)));
481
+ }
482
+ }
483
+ }
484
+
485
+ presentTags.value = tags;
486
+ };
487
+
488
+ const calculateSuggestions = () => {
489
+ const { filterMethod, showAllLevels, separator } = props;
490
+ const res = panel.value
491
+ ?.getFlattedNodes(!props.props.checkStrictly)
492
+ ?.filter((node) => {
493
+ if (node.isDisabled) return false;
494
+ node.calcText(showAllLevels, separator);
495
+ return filterMethod(node, searchKeyword.value);
496
+ });
497
+
498
+ if (multiple.value) {
499
+ presentTags.value.forEach((tag) => {
500
+ tag.hitState = false;
501
+ });
502
+ allPresentTags.value.forEach((tag) => {
503
+ tag.hitState = false;
504
+ });
505
+ }
506
+
507
+ filtering.value = true;
508
+ suggestions.value = res!;
509
+ updatePopperPosition();
510
+ };
511
+
512
+ const focusFirstNode = () => {
513
+ let firstNode!: HTMLElement;
514
+
515
+ if (filtering.value && suggestionPanel.value) {
516
+ firstNode = suggestionPanel.value.$el.querySelector(
517
+ `.${nsCascader.e('suggestion-item')}`
518
+ );
519
+ } else {
520
+ firstNode = panel.value?.$el.querySelector(
521
+ `.${nsCascader.b('node')}[tabindex="-1"]`
522
+ );
523
+ }
524
+
525
+ if (firstNode) {
526
+ firstNode.focus();
527
+ !filtering.value && firstNode.click();
528
+ }
529
+ };
530
+
531
+ const updateStyle = () => {
532
+ const inputInner = input.value?.input;
533
+ const tagWrapperEl = tagWrapper.value;
534
+ const suggestionPanelEl = suggestionPanel.value?.$el;
535
+
536
+ if (!isClient || !inputInner) return;
537
+
538
+ if (suggestionPanelEl) {
539
+ const suggestionList = suggestionPanelEl.querySelector(
540
+ `.${nsCascader.e('suggestion-list')}`
541
+ );
542
+ suggestionList.style.minWidth = `${inputInner.offsetWidth}px`;
543
+ }
544
+
545
+ if (tagWrapperEl) {
546
+ const { offsetHeight } = tagWrapperEl;
547
+ const height =
548
+ presentTags.value.length > 0
549
+ ? `${Math.max(offsetHeight + 6, inputInitialHeight)}px`
550
+ : `${inputInitialHeight}px`;
551
+ inputInner.style.height = height;
552
+ updatePopperPosition();
553
+ }
554
+ };
555
+
556
+ const getCheckedNodes = (leafOnly: boolean) => {
557
+ return panel.value?.getCheckedNodes(leafOnly);
558
+ };
559
+
560
+ const handleExpandChange = (value: CascaderValue) => {
561
+ updatePopperPosition();
562
+ emit('expand-change', value);
563
+ };
564
+
565
+ const handleComposition = (event: CompositionEvent) => {
566
+ const text = (event.target as HTMLInputElement)?.value;
567
+ if (event.type === 'compositionend') {
568
+ isOnComposition.value = false;
569
+ nextTick(() => handleInput(text));
570
+ } else {
571
+ const lastCharacter = text[text.length - 1] || '';
572
+ isOnComposition.value = !isKorean(lastCharacter);
573
+ }
574
+ };
575
+
576
+ const handleKeyDown = (e: KeyboardEvent) => {
577
+ if (isOnComposition.value) return;
578
+
579
+ switch (e.code) {
580
+ case EVENT_CODE.enter:
581
+ togglePopperVisible();
582
+ break;
583
+ case EVENT_CODE.down:
584
+ togglePopperVisible(true);
585
+ nextTick(focusFirstNode);
586
+ e.preventDefault();
587
+ break;
588
+ case EVENT_CODE.esc:
589
+ if (popperVisible.value === true) {
590
+ e.preventDefault();
591
+ e.stopPropagation();
592
+ togglePopperVisible(false);
593
+ }
594
+ break;
595
+ case EVENT_CODE.tab:
596
+ togglePopperVisible(false);
597
+ break;
598
+ }
599
+ };
600
+
601
+ const handleClear = () => {
602
+ panel.value?.clearCheckedNodes();
603
+ if (!popperVisible.value && props.filterable) {
604
+ syncPresentTextValue();
605
+ }
606
+ togglePopperVisible(false);
607
+ };
608
+
609
+ const syncPresentTextValue = () => {
610
+ const { value } = presentText;
611
+ inputValue.value = value;
612
+ searchInputValue.value = value;
613
+ };
614
+
615
+ const handleSuggestionClick = (node: CascaderNode) => {
616
+ const { checked } = node;
617
+
618
+ if (multiple.value) {
619
+ panel.value?.handleCheckChange(node, !checked, false);
620
+ } else {
621
+ !checked && panel.value?.handleCheckChange(node, true, false);
622
+ togglePopperVisible(false);
623
+ }
624
+ };
625
+
626
+ const handleSuggestionKeyDown = (e: KeyboardEvent) => {
627
+ const target = e.target as HTMLElement;
628
+ const { code } = e;
629
+
630
+ switch (code) {
631
+ case EVENT_CODE.up:
632
+ case EVENT_CODE.down: {
633
+ const distance = code === EVENT_CODE.up ? -1 : 1;
634
+ focusNode(
635
+ getSibling(
636
+ target,
637
+ distance,
638
+ `.${nsCascader.e('suggestion-item')}[tabindex="-1"]`
639
+ )
640
+ );
641
+ break;
642
+ }
643
+ case EVENT_CODE.enter:
644
+ target.click();
645
+ break;
646
+ }
647
+ };
648
+
649
+ const handleDelete = () => {
650
+ const tags = presentTags.value;
651
+ const lastTag = tags[tags.length - 1];
652
+ pressDeleteCount = searchInputValue.value ? 0 : pressDeleteCount + 1;
653
+
654
+ if (
655
+ !lastTag ||
656
+ !pressDeleteCount ||
657
+ (props.collapseTags && tags.length > 1)
658
+ )
659
+ return;
660
+
661
+ if (lastTag.hitState) {
662
+ deleteTag(lastTag);
663
+ } else {
664
+ lastTag.hitState = true;
665
+ }
666
+ };
667
+
668
+ const handleFilter = debounce(() => {
669
+ const { value } = searchKeyword;
670
+
671
+ if (!value) return;
672
+
673
+ const passed = props.beforeFilter(value);
674
+
675
+ if (isPromise(passed)) {
676
+ passed.then(calculateSuggestions).catch(() => {
677
+ /* prevent log error */
678
+ });
679
+ } else if (passed !== false) {
680
+ calculateSuggestions();
681
+ } else {
682
+ hideSuggestionPanel();
683
+ }
684
+ }, props.debounce);
685
+
686
+ const handleInput = (val: string, e?: KeyboardEvent) => {
687
+ !popperVisible.value && togglePopperVisible(true);
688
+
689
+ if (e?.isComposing) return;
690
+
691
+ val ? handleFilter() : hideSuggestionPanel();
692
+ };
693
+
694
+ watch(filtering, updatePopperPosition);
695
+
696
+ watch([checkedNodes, isDisabled], calculatePresentTags);
697
+
698
+ watch(presentTags, () => {
699
+ nextTick(() => updateStyle());
700
+ });
701
+
702
+ watch(presentText, syncPresentTextValue, { immediate: true });
703
+
704
+ onMounted(() => {
705
+ const inputInner = input.value!.input!;
706
+
707
+ const inputInnerHeight =
708
+ Number.parseFloat(
709
+ useCssVar(nsInput.cssVarName('input-height'), inputInner).value
710
+ ) - 2;
711
+
712
+ inputInitialHeight = inputInner.offsetHeight || inputInnerHeight;
713
+ useResizeObserver(inputInner, updateStyle);
714
+ });
715
+
716
+ return {
717
+ popperOptions,
718
+ tooltipRef,
719
+ popperPaneRef,
720
+ input,
721
+ tagWrapper,
722
+ panel,
723
+ suggestionPanel,
724
+ popperVisible,
725
+ inputHover,
726
+ inputPlaceholder,
727
+ currentPlaceholder,
728
+ filtering,
729
+ presentText,
730
+ checkedValue,
731
+ inputValue,
732
+ searchInputValue,
733
+ presentTags,
734
+ allPresentTags,
735
+ suggestions,
736
+ isDisabled,
737
+ isOnComposition,
738
+ realSize,
739
+ tagSize,
740
+ multiple,
741
+ readonly,
742
+ clearBtnVisible,
743
+
744
+ nsCascader,
745
+ nsInput,
746
+ t,
747
+ togglePopperVisible,
748
+ hideSuggestionPanel,
749
+ deleteTag,
750
+ focusFirstNode,
751
+ getCheckedNodes,
752
+ handleExpandChange,
753
+ handleKeyDown,
754
+ handleComposition,
755
+ handleClear,
756
+ handleSuggestionClick,
757
+ handleSuggestionKeyDown,
758
+ handleDelete,
759
+ handleInput
760
+ };
761
+ }
762
+ });
763
+ </script>
764
+
765
+ <style lang="scss">
766
+ @import "./index.scss";
767
+ </style>