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,218 @@
1
+ import { isFunction } from '@vue/shared';
2
+ import { capitalize, isEmpty, isUndefined } from '@vri/utils';
3
+ import type { VNode } from 'vue';
4
+
5
+ export type CascaderNodeValue = string | number
6
+ export type CascaderNodePathValue = CascaderNodeValue[]
7
+ export type CascaderValue =
8
+ | CascaderNodeValue
9
+ | CascaderNodePathValue
10
+ | (CascaderNodeValue | CascaderNodePathValue)[]
11
+ export type CascaderConfig = Required<CascaderProps>
12
+ export type ExpandTrigger = 'click' | 'hover'
13
+ export type isDisabled = (data: CascaderOption, node: Node) => boolean
14
+ export type isLeaf = (data: CascaderOption, node: Node) => boolean
15
+ export type Resolve = (dataList?: CascaderOption[]) => void
16
+ export type LazyLoad = (node: Node, resolve: Resolve) => void
17
+ export type RenderLabel = ({
18
+ node: Node,
19
+ data: CascaderOption
20
+ }) => VNode | VNode[]
21
+ export interface CascaderOption extends Record<string, unknown> {
22
+ label?: string
23
+ value?: CascaderNodeValue
24
+ children?: CascaderOption[]
25
+ disabled?: boolean
26
+ leaf?: boolean
27
+ }
28
+
29
+ export interface CascaderProps {
30
+ expandTrigger?: ExpandTrigger
31
+ multiple?: boolean
32
+ checkStrictly?: boolean
33
+ emitPath?: boolean
34
+ lazy?: boolean
35
+ lazyLoad?: LazyLoad
36
+ value?: string
37
+ label?: string
38
+ children?: string
39
+ disabled?: string | isDisabled
40
+ leaf?: string | isLeaf
41
+ hoverThreshold?: number
42
+ }
43
+
44
+ export type Nullable<T> = null | T
45
+
46
+ type ChildrenData = CascaderOption[] | undefined
47
+
48
+ let uid = 0;
49
+
50
+ const calculatePathNodes = (node: Node) => {
51
+ const nodes = [node];
52
+ let { parent } = node;
53
+
54
+ while (parent) {
55
+ nodes.unshift(parent);
56
+ parent = parent.parent;
57
+ }
58
+
59
+ return nodes;
60
+ };
61
+
62
+ class Node {
63
+ readonly uid: number = uid++;
64
+ readonly level: number;
65
+ readonly value: CascaderNodeValue;
66
+ readonly label: string;
67
+ readonly pathNodes: Node[];
68
+ readonly pathValues: CascaderNodePathValue;
69
+ readonly pathLabels: string[];
70
+
71
+ childrenData: ChildrenData;
72
+ children: Node[];
73
+ text: string;
74
+ loaded: boolean;
75
+ checked = false;
76
+ indeterminate = false;
77
+ loading = false;
78
+
79
+ constructor(
80
+ readonly data: Nullable<CascaderOption>,
81
+ readonly config: CascaderConfig,
82
+ readonly parent?: Node,
83
+ readonly root = false
84
+ ) {
85
+ const { value: valueKey, label: labelKey, children: childrenKey } = config;
86
+
87
+ const childrenData = data[childrenKey] as ChildrenData;
88
+ const pathNodes = calculatePathNodes(this);
89
+
90
+ this.level = root ? 0 : parent ? parent.level + 1 : 1;
91
+ this.value = data[valueKey] as CascaderNodeValue;
92
+ this.label = data[labelKey] as string;
93
+ this.pathNodes = pathNodes;
94
+ this.pathValues = pathNodes.map((node) => node.value);
95
+ this.pathLabels = pathNodes.map((node) => node.label);
96
+ this.childrenData = childrenData;
97
+ this.children = (childrenData || []).map(
98
+ (child) => new Node(child, config, this)
99
+ );
100
+ this.loaded = !config.lazy || this.isLeaf || !isEmpty(childrenData);
101
+ }
102
+
103
+ get isDisabled(): boolean {
104
+ const { data, parent, config } = this;
105
+ const { disabled, checkStrictly } = config;
106
+ const isDisabled = isFunction(disabled)
107
+ ? disabled(data, this)
108
+ : !!data[disabled];
109
+ return isDisabled || (!checkStrictly && parent?.isDisabled);
110
+ }
111
+
112
+ get isLeaf(): boolean {
113
+ const { data, config, childrenData, loaded } = this;
114
+ const { lazy, leaf } = config;
115
+ const isLeaf = isFunction(leaf) ? leaf(data, this) : data[leaf];
116
+
117
+ return isUndefined(isLeaf)
118
+ ? lazy && !loaded
119
+ ? false
120
+ : !(Array.isArray(childrenData) && childrenData.length)
121
+ : !!isLeaf;
122
+ }
123
+
124
+ get valueByOption() {
125
+ return this.config.emitPath ? this.pathValues : this.value;
126
+ }
127
+
128
+ appendChild(childData: CascaderOption) {
129
+ const { childrenData, children } = this;
130
+ const node = new Node(childData, this.config, this);
131
+
132
+ if (Array.isArray(childrenData)) {
133
+ childrenData.push(childData);
134
+ } else {
135
+ this.childrenData = [childData];
136
+ }
137
+
138
+ children.push(node);
139
+
140
+ return node;
141
+ }
142
+
143
+ calcText(allLevels: boolean, separator: string) {
144
+ const text = allLevels ? this.pathLabels.join(separator) : this.label;
145
+ this.text = text;
146
+ return text;
147
+ }
148
+
149
+ broadcast(event: string, ...args: unknown[]) {
150
+ const handlerName = `onParent${capitalize(event)}`;
151
+ this.children.forEach((child) => {
152
+ if (child) {
153
+ // bottom up
154
+ child.broadcast(event, ...args);
155
+ child[handlerName] && child[handlerName](...args);
156
+ }
157
+ });
158
+ }
159
+
160
+ emit(event: string, ...args: unknown[]) {
161
+ const { parent } = this;
162
+ const handlerName = `onChild${capitalize(event)}`;
163
+ if (parent) {
164
+ parent[handlerName] && parent[handlerName](...args);
165
+ parent.emit(event, ...args);
166
+ }
167
+ }
168
+
169
+ onParentCheck(checked: boolean) {
170
+ if (!this.isDisabled) {
171
+ this.setCheckState(checked);
172
+ }
173
+ }
174
+
175
+ onChildCheck() {
176
+ const { children } = this;
177
+ const validChildren = children.filter((child) => !child.isDisabled);
178
+ const checked = validChildren.length
179
+ ? validChildren.every((child) => child.checked)
180
+ : false;
181
+
182
+ this.setCheckState(checked);
183
+ }
184
+
185
+ setCheckState(checked: boolean) {
186
+ const totalNum = this.children.length;
187
+ const checkedNum = this.children.reduce((c, p) => {
188
+ const num = p.checked ? 1 : p.indeterminate ? 0.5 : 0;
189
+ return c + num;
190
+ }, 0);
191
+
192
+ this.checked =
193
+ this.loaded &&
194
+ this.children
195
+ .filter((child) => !child.isDisabled)
196
+ .every((child) => child.loaded && child.checked) &&
197
+ checked;
198
+ this.indeterminate =
199
+ this.loaded && checkedNum !== totalNum && checkedNum > 0;
200
+ }
201
+
202
+ doCheck(checked: boolean) {
203
+ if (this.checked === checked) return;
204
+
205
+ const { checkStrictly, multiple } = this.config;
206
+
207
+ if (checkStrictly || !multiple) {
208
+ this.checked = checked;
209
+ } else {
210
+ // bottom up to unify the calculation of the indeterminate state
211
+ this.broadcast('check', checked);
212
+ this.setCheckState(checked);
213
+ this.emit('check');
214
+ }
215
+ }
216
+ }
217
+
218
+ export default Node;
@@ -0,0 +1,197 @@
1
+ <template>
2
+ <li
3
+ :id="`${menuId}-${node.uid}`"
4
+ role="menuitem"
5
+ :aria-haspopup="!isLeaf"
6
+ :aria-owns="isLeaf ? null : menuId"
7
+ :aria-expanded="inExpandingPath"
8
+ :tabindex="expandable ? -1 : undefined"
9
+ :class="[
10
+ ns.b(),
11
+ ns.is('selectable', checkStrictly),
12
+ ns.is('active', node.checked),
13
+ ns.is('disabled', !expandable),
14
+ inExpandingPath && 'in-active-path',
15
+ inCheckedPath && 'in-checked-path',
16
+ ]"
17
+ @mouseenter="handleHoverExpand"
18
+ @focus="handleHoverExpand"
19
+ @click="handleClick"
20
+ >
21
+ <!-- prefix -->
22
+ <el-checkbox
23
+ v-if="multiple"
24
+ :model-value="node.checked"
25
+ :indeterminate="node.indeterminate"
26
+ :disabled="isDisabled"
27
+ @click.stop
28
+ @update:model-value="handleSelectCheck"
29
+ />
30
+ <el-radio
31
+ v-else-if="checkStrictly"
32
+ :model-value="checkedNodeId"
33
+ :label="node.uid"
34
+ :disabled="isDisabled"
35
+ @update:model-value="handleSelectCheck"
36
+ @click.stop
37
+ >
38
+ <!--
39
+ Add an empty element to avoid render label,
40
+ do not use empty fragment here for https://github.com/vuejs/vue-next/pull/2485
41
+ -->
42
+ <span />
43
+ </el-radio>
44
+ <v-icon v-else-if="isLeaf && node.checked" :class="ns.e('prefix')" icon="vi-success"
45
+ :size="14" color="rgb(64, 158, 255)"/>
46
+
47
+ <!-- content -->
48
+ <node-content />
49
+
50
+ <!-- postfix -->
51
+ <template v-if="!isLeaf">
52
+ <v-icon v-if="node.loading" :class="[ns.is('loading'), ns.e('postfix')]" icon="vi-loading" :size="16" />
53
+ <v-icon v-else :class="['arrow-right', ns.e('postfix')]" icon="vi-arrow-right"
54
+ :size="16" />
55
+ </template>
56
+ </li>
57
+ </template>
58
+
59
+ <script lang="ts">
60
+ import { computed, defineComponent, inject } from 'vue';
61
+ import {
62
+ ElCheckbox,
63
+ ElRadio
64
+ } from 'element-plus';
65
+ import { useNamespace } from '../../use';
66
+ import NodeContent from './node-content';
67
+ import { CASCADER_PANEL_INJECTION_KEY } from './types';
68
+ import type { default as CascaderNode } from './node';
69
+ import { Icon as VIcon } from '../../common';
70
+ import type { PropType } from 'vue';
71
+
72
+ export default defineComponent({
73
+ name: 'VriCascaderNode',
74
+ components: {
75
+ ElCheckbox,
76
+ ElRadio,
77
+ NodeContent,
78
+ VIcon
79
+ },
80
+ props: {
81
+ node: {
82
+ type: Object as PropType<CascaderNode>,
83
+ required: true
84
+ },
85
+ menuId: String
86
+ },
87
+ emits: ['expand'],
88
+ setup (props, { emit }) {
89
+ const panel = inject(CASCADER_PANEL_INJECTION_KEY)!;
90
+
91
+ const ns = useNamespace('cascader-node');
92
+ const isHoverMenu = computed(() => panel.isHoverMenu);
93
+ const multiple = computed(() => panel.config.multiple);
94
+ const checkStrictly = computed(() => panel.config.checkStrictly);
95
+ const checkedNodeId = computed(() => panel.checkedNodes[0]?.uid);
96
+ const isDisabled = computed(() => props.node.isDisabled);
97
+ const isLeaf = computed(() => props.node.isLeaf);
98
+ const expandable = computed(
99
+ () => (checkStrictly.value && !isLeaf.value) || !isDisabled.value
100
+ );
101
+ const inExpandingPath = computed(() => isInPath(panel.expandingNode!));
102
+ // only useful in check-strictly mode
103
+ const inCheckedPath = computed(
104
+ () => checkStrictly.value && panel.checkedNodes.some(isInPath)
105
+ );
106
+
107
+ const isInPath = (node: CascaderNode) => {
108
+ const { level, uid } = props.node;
109
+ return node?.pathNodes[level - 1]?.uid === uid;
110
+ };
111
+
112
+ const doExpand = () => {
113
+ if (inExpandingPath.value) return;
114
+ panel.expandNode(props.node);
115
+ };
116
+
117
+ const doCheck = (checked: boolean) => {
118
+ const { node } = props;
119
+ if (checked === node.checked) return;
120
+ panel.handleCheckChange(node, checked);
121
+ };
122
+
123
+ const doLoad = () => {
124
+ panel.lazyLoad(props.node, () => {
125
+ if (!isLeaf.value) doExpand();
126
+ });
127
+ };
128
+
129
+ const handleHoverExpand = (e: Event) => {
130
+ if (!isHoverMenu.value) return;
131
+ handleExpand();
132
+ !isLeaf.value && emit('expand', e);
133
+ };
134
+
135
+ const handleExpand = () => {
136
+ const { node } = props;
137
+ // do not exclude leaf node because the menus expanded might have to reset
138
+ if (!expandable.value || node.loading) return;
139
+ node.loaded ? doExpand() : doLoad();
140
+ };
141
+
142
+ const handleClick = () => {
143
+ if (isHoverMenu.value && !isLeaf.value) return;
144
+
145
+ if (
146
+ isLeaf.value &&
147
+ !isDisabled.value &&
148
+ !checkStrictly.value &&
149
+ !multiple.value
150
+ ) {
151
+ handleCheck(true);
152
+ } else {
153
+ handleExpand();
154
+ }
155
+ };
156
+
157
+ const handleSelectCheck = (checked: boolean) => {
158
+ if (checkStrictly.value) {
159
+ doCheck(checked);
160
+ if (props.node.loaded) {
161
+ doExpand();
162
+ }
163
+ } else {
164
+ handleCheck(checked);
165
+ }
166
+ };
167
+
168
+ const handleCheck = (checked: boolean) => {
169
+ if (!props.node.loaded) {
170
+ doLoad();
171
+ } else {
172
+ doCheck(checked);
173
+ !checkStrictly.value && doExpand();
174
+ }
175
+ };
176
+
177
+ return {
178
+ panel,
179
+ isHoverMenu,
180
+ multiple,
181
+ checkStrictly,
182
+ checkedNodeId,
183
+ isDisabled,
184
+ isLeaf,
185
+ expandable,
186
+ inExpandingPath,
187
+ inCheckedPath,
188
+ ns,
189
+ handleHoverExpand,
190
+ handleExpand,
191
+ handleClick,
192
+ handleCheck,
193
+ handleSelectCheck
194
+ };
195
+ }
196
+ });
197
+ </script>
@@ -0,0 +1,83 @@
1
+ import { isEqual } from 'lodash';
2
+ import Node from './node';
3
+
4
+ import type {
5
+ CascaderConfig,
6
+ CascaderNodePathValue,
7
+ CascaderNodeValue,
8
+ CascaderOption
9
+ } from './node';
10
+
11
+ const flatNodes = (nodes: Node[], leafOnly: boolean) => {
12
+ return nodes.reduce((res, node) => {
13
+ if (node.isLeaf) {
14
+ res.push(node);
15
+ } else {
16
+ !leafOnly && res.push(node);
17
+ res = res.concat(flatNodes(node.children, leafOnly));
18
+ }
19
+ return res;
20
+ }, [] as Node[]);
21
+ };
22
+
23
+ export default class Store {
24
+ readonly nodes: Node[];
25
+ readonly allNodes: Node[];
26
+ readonly leafNodes: Node[];
27
+
28
+ constructor(data: CascaderOption[], readonly config: CascaderConfig) {
29
+ const nodes = (data || []).map(
30
+ (nodeData) => new Node(nodeData, this.config)
31
+ );
32
+ this.nodes = nodes;
33
+ this.allNodes = flatNodes(nodes, false);
34
+ this.leafNodes = flatNodes(nodes, true);
35
+ }
36
+
37
+ getNodes() {
38
+ return this.nodes;
39
+ }
40
+
41
+ getFlattedNodes(leafOnly: boolean) {
42
+ return leafOnly ? this.leafNodes : this.allNodes;
43
+ }
44
+
45
+ appendNode(nodeData: CascaderOption, parentNode?: Node) {
46
+ const node = parentNode
47
+ ? parentNode.appendChild(nodeData)
48
+ : new Node(nodeData, this.config);
49
+
50
+ if (!parentNode) this.nodes.push(node);
51
+
52
+ this.allNodes.push(node);
53
+ node.isLeaf && this.leafNodes.push(node);
54
+ }
55
+
56
+ appendNodes(nodeDataList: CascaderOption[], parentNode: Node) {
57
+ nodeDataList.forEach((nodeData) => this.appendNode(nodeData, parentNode));
58
+ }
59
+
60
+ // when checkStrictly, leaf node first
61
+ getNodeByValue(
62
+ value: CascaderNodeValue | CascaderNodePathValue,
63
+ leafOnly = false
64
+ ): Nullable<Node> {
65
+ if (!value && value !== 0) return null;
66
+
67
+ const node = this.getFlattedNodes(leafOnly).find(
68
+ (node) => isEqual(node.value, value) || isEqual(node.pathValues, value)
69
+ );
70
+
71
+ return node || null;
72
+ }
73
+
74
+ getSameNode(node: Node): Nullable<Node> {
75
+ if (!node) return null;
76
+
77
+ const node_ = this.getFlattedNodes(false).find(
78
+ ({ value, level }) => isEqual(node.value, value) && node.level === level
79
+ );
80
+
81
+ return node_ || null;
82
+ }
83
+ }
@@ -0,0 +1,56 @@
1
+ import type { InjectionKey, VNode } from 'vue';
2
+ import type {
3
+ default as CascaderNode,
4
+ CascaderOption,
5
+ CascaderProps,
6
+ ExpandTrigger
7
+ } from './node';
8
+
9
+ export type { CascaderNode, CascaderOption, CascaderProps, ExpandTrigger };
10
+
11
+ export type CascaderNodeValue = string | number
12
+ export type CascaderNodePathValue = CascaderNodeValue[]
13
+ export type CascaderValue =
14
+ | CascaderNodeValue
15
+ | CascaderNodePathValue
16
+ | (CascaderNodeValue | CascaderNodePathValue)[]
17
+ export type CascaderConfig = Required<CascaderProps>
18
+ export type isDisabled = (data: CascaderOption, node: CascaderNode) => boolean
19
+ export type isLeaf = (data: CascaderOption, node: CascaderNode) => boolean
20
+ export type Resolve = (dataList?: CascaderOption[]) => void
21
+ export type LazyLoad = (node: CascaderNode, resolve: Resolve) => void
22
+ export type RenderLabel = ({
23
+ node: CascaderNode,
24
+ data: CascaderOption
25
+ }) => VNode | VNode[]
26
+
27
+ export interface Tag {
28
+ node?: CascaderNode
29
+ key: number
30
+ text: string
31
+ hitState?: boolean
32
+ closable: boolean
33
+ isCollapseTag: boolean
34
+ }
35
+
36
+ export interface VriCascaderPanelContext {
37
+ config: CascaderConfig
38
+ expandingNode: Nullable<CascaderNode>
39
+ checkedNodes: CascaderNode[]
40
+ isHoverMenu: boolean
41
+ initialLoaded: boolean
42
+ renderLabelFn: RenderLabel
43
+ lazyLoad: (
44
+ node?: CascaderNode,
45
+ cb?: (dataList: CascaderOption[]) => void
46
+ ) => void
47
+ expandNode: (node: CascaderNode, silent?: boolean) => void
48
+ handleCheckChange: (
49
+ node: CascaderNode,
50
+ checked: boolean,
51
+ emitClose?: boolean
52
+ ) => void
53
+ }
54
+
55
+ export const CASCADER_PANEL_INJECTION_KEY: InjectionKey<VriCascaderPanelContext> =
56
+ Symbol();
@@ -0,0 +1,40 @@
1
+ import { isLeaf } from '@vri/utils';
2
+ import type { default as CascaderNode } from './node';
3
+
4
+ export const getMenuIndex = (el: HTMLElement) => {
5
+ if (!el) return 0;
6
+ const pieces = el.id.split('-');
7
+ return Number(pieces[pieces.length - 2]);
8
+ };
9
+
10
+ export const checkNode = (el: HTMLElement) => {
11
+ if (!el) return;
12
+
13
+ const input = el.querySelector('input');
14
+ if (input) {
15
+ input.click();
16
+ } else if (isLeaf(el)) {
17
+ el.click();
18
+ }
19
+ };
20
+
21
+ export const sortByOriginalOrder = (
22
+ oldNodes: CascaderNode[],
23
+ newNodes: CascaderNode[]
24
+ ) => {
25
+ const newNodesCopy = newNodes.slice(0);
26
+ const newIds = newNodesCopy.map((node) => node.uid);
27
+ const res = oldNodes.reduce((acc, item) => {
28
+ const index = newIds.indexOf(item.uid);
29
+ if (index > -1) {
30
+ acc.push(item);
31
+ newNodesCopy.splice(index, 1);
32
+ newIds.splice(index, 1);
33
+ }
34
+ return acc;
35
+ }, [] as CascaderNode[]);
36
+
37
+ res.push(...newNodesCopy);
38
+
39
+ return res;
40
+ };
@@ -0,0 +1,31 @@
1
+ # cascader-select 级联选择组件
2
+
3
+ ## Usage
4
+
5
+ ```html
6
+ <vri-cascader-select title="地区" :options="cascaderOptions" @submit="onCascaderSubmit"></vri-cascader-select>
7
+ ```
8
+
9
+ ## Props
10
+
11
+ | 属性名 | 说明 | 类型 | 是否必传 | 默认值 |
12
+ | ----------- | ----------------------------- | --------------------- | -------- | -------- |
13
+ | title | 标题名称 | string | 是 | - |
14
+ | options | 级联选择列表 | Array\<option props\> | 是 | - |
15
+ | labelKey | 选择项名称对应的 key 名 | string | 否 | label |
16
+ | valueKey | 选择项值对应的 key 名 | string | 否 | value |
17
+ | childrenKey | 选择项 children 对应的 key 名 | string | 否 | children |
18
+
19
+ ### options props
20
+
21
+ | 属性名 | 说明 | 类型 | 是否必传 | 默认值 |
22
+ | --------------------------------------------- | --------------- | ------ | -------- | ------ |
23
+ | [key:string] (示例: label) | 选择项名 | string | 是 | - |
24
+ | [key:string] (示例: value) | 选择项值 | string | 是 | - |
25
+ | [key:string] (示例: children\<option props\>) | 选择项 children | Array | 否 | - |
26
+
27
+ ## Event
28
+
29
+ | 事件名 | 说明 | 参数 |
30
+ | ------ | ---------------- | ---------------------------------------------- |
31
+ | submit | 确认选择时的回调 | 选中项的值(对象,不同级的值整合到一个 key 中) |
@@ -0,0 +1,54 @@
1
+ $name: 'cascader-select';
2
+
3
+ @include b($name) {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: 4px;
7
+ height: 24px;
8
+ cursor: pointer;
9
+ @include e(title) {
10
+ font-size: 13px;
11
+ cursor: pointer;
12
+ @include when(show) {
13
+ color: #409eff;
14
+ }
15
+ }
16
+
17
+ @include e(icon-arrow-down) {
18
+ transition: all 0.2s;
19
+ @include when(rotate) {
20
+ position: relative;
21
+ top: -1px;
22
+ transform: rotate(180deg);
23
+ }
24
+ }
25
+ }
26
+
27
+ @include e(dropdown) {
28
+ :deep(.vri-cascader-menu__wrap.vri-scrollbar__wrap) {
29
+ height: unset;
30
+ min-height: 204px;
31
+ max-height: 330px;
32
+ }
33
+ :deep(.vri-cascader-node) {
34
+ padding: 0 16px;
35
+ height: 30px;
36
+ line-height: unset;
37
+ .vri-cascader-node__label {
38
+ padding-right: 24px;
39
+ font-size: 13px;
40
+ }
41
+ }
42
+ :deep(.vri-cascader-node.in-active-path) {
43
+ color: unset;
44
+ font-weight: normal;
45
+ }
46
+ :deep(.vri-cascader-node.is-selectable.in-checked-path),
47
+ :deep(.vri-cascader-node.is-active) {
48
+ color: #409eff;
49
+ font-weight: normal;
50
+ }
51
+ :deep(.vri-cascader-menu__empty-text) {
52
+ top: 40px;
53
+ }
54
+ }
@@ -0,0 +1,5 @@
1
+ import CascaderSelect from './index.vue';
2
+
3
+ export * from './types';
4
+
5
+ export { CascaderSelect };