@veltra/mobile 1.0.0 → 1.1.0

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 (245) hide show
  1. package/package.json +32 -6
  2. package/src/components/_internal/bottom-sheet/bottom-sheet.vue +81 -0
  3. package/src/components/_internal/bottom-sheet/index.ts +1 -0
  4. package/src/components/_internal/bottom-sheet/style.scss +116 -0
  5. package/src/components/_internal/bottom-sheet/style.ts +1 -0
  6. package/src/components/_internal/overlay-container.ts +34 -0
  7. package/src/components/avatar/avatar.vue +44 -0
  8. package/src/components/avatar/index.ts +1 -0
  9. package/src/components/avatar/style.scss +53 -0
  10. package/src/components/avatar/style.ts +1 -0
  11. package/src/components/badge/badge.vue +59 -0
  12. package/src/components/badge/index.ts +1 -0
  13. package/src/components/badge/style.scss +62 -0
  14. package/src/components/badge/style.ts +1 -0
  15. package/src/components/button/button.vue +76 -0
  16. package/src/components/button/index.ts +1 -0
  17. package/src/components/button/style.scss +207 -0
  18. package/src/components/button/style.ts +1 -0
  19. package/src/components/card/card.vue +24 -0
  20. package/src/components/card/index.ts +1 -0
  21. package/src/components/card/style.scss +35 -0
  22. package/src/components/card/style.ts +1 -0
  23. package/src/components/cascade/cascade.vue +595 -0
  24. package/src/components/cascade/index.ts +1 -0
  25. package/src/components/cascade/node.ts +29 -0
  26. package/src/components/cascade/style.scss +424 -0
  27. package/src/components/cascade/style.ts +2 -0
  28. package/src/components/check-tag/check-tag.vue +35 -0
  29. package/src/components/check-tag/index.ts +1 -0
  30. package/src/components/check-tag/style.scss +46 -0
  31. package/src/components/check-tag/style.ts +1 -0
  32. package/src/components/checkbox/checkbox.vue +67 -0
  33. package/src/components/checkbox/index.ts +1 -0
  34. package/src/components/checkbox/style.scss +132 -0
  35. package/src/components/checkbox/style.ts +2 -0
  36. package/src/components/checkbox-group/checkbox-group.vue +94 -0
  37. package/src/components/checkbox-group/index.ts +1 -0
  38. package/src/components/checkbox-group/style.scss +57 -0
  39. package/src/components/checkbox-group/style.ts +2 -0
  40. package/src/components/collapse/collapse-item.vue +134 -0
  41. package/src/components/collapse/collapse.vue +117 -0
  42. package/src/components/collapse/di.ts +18 -0
  43. package/src/components/collapse/index.ts +3 -0
  44. package/src/components/collapse/style.scss +157 -0
  45. package/src/components/collapse/style.ts +1 -0
  46. package/src/components/date-picker/date-picker.vue +326 -0
  47. package/src/components/date-picker/index.ts +1 -0
  48. package/src/components/date-picker/style.scss +236 -0
  49. package/src/components/date-picker/style.ts +2 -0
  50. package/src/components/descriptions/descriptions-item.vue +12 -0
  51. package/src/components/descriptions/descriptions.vue +50 -0
  52. package/src/components/descriptions/index.ts +2 -0
  53. package/src/components/descriptions/style.scss +120 -0
  54. package/src/components/descriptions/style.ts +1 -0
  55. package/src/components/dialog/dialog.vue +143 -0
  56. package/src/components/dialog/index.ts +1 -0
  57. package/src/components/dialog/style.scss +163 -0
  58. package/src/components/dialog/style.ts +4 -0
  59. package/src/components/divider/divider.vue +29 -0
  60. package/src/components/divider/index.ts +1 -0
  61. package/src/components/divider/style.scss +91 -0
  62. package/src/components/divider/style.ts +1 -0
  63. package/src/components/drawer/drawer.vue +168 -0
  64. package/src/components/drawer/index.ts +1 -0
  65. package/src/components/drawer/style.scss +240 -0
  66. package/src/components/drawer/style.ts +2 -0
  67. package/src/components/empty/empty.vue +22 -0
  68. package/src/components/empty/index.ts +1 -0
  69. package/src/components/empty/style.scss +36 -0
  70. package/src/components/empty/style.ts +1 -0
  71. package/src/components/form/form.vue +119 -0
  72. package/src/components/form/index.ts +1 -0
  73. package/src/components/form/is-field-modified.ts +16 -0
  74. package/src/components/form/style.scss +58 -0
  75. package/src/components/form/style.ts +2 -0
  76. package/src/components/form/use-form-fields.ts +115 -0
  77. package/src/components/form/use-node-interceptor.ts +101 -0
  78. package/src/components/form-item/form-item.vue +171 -0
  79. package/src/components/form-item/helper.ts +9 -0
  80. package/src/components/form-item/index.ts +1 -0
  81. package/src/components/form-item/style.scss +121 -0
  82. package/src/components/form-item/style.ts +1 -0
  83. package/src/components/form-item/validate.ts +131 -0
  84. package/src/components/icon/icon.vue +23 -0
  85. package/src/components/icon/index.ts +1 -0
  86. package/src/components/icon/style.scss +37 -0
  87. package/src/components/icon/style.ts +1 -0
  88. package/src/components/input/index.ts +1 -0
  89. package/src/components/input/input.vue +187 -0
  90. package/src/components/input/style.scss +121 -0
  91. package/src/components/input/style.ts +1 -0
  92. package/src/components/list/index.ts +3 -0
  93. package/src/components/list/list-item.vue +20 -0
  94. package/src/components/list/list.vue +20 -0
  95. package/src/components/list/style.scss +50 -0
  96. package/src/components/list/style.ts +1 -0
  97. package/src/components/loading/directive.ts +34 -0
  98. package/src/components/loading/index.ts +3 -0
  99. package/src/components/loading/loading.vue +25 -0
  100. package/src/components/loading/style.scss +179 -0
  101. package/src/components/loading/style.ts +2 -0
  102. package/src/components/message/helper.ts +28 -0
  103. package/src/components/message/index.ts +3 -0
  104. package/src/components/message/message-box.vue +31 -0
  105. package/src/components/message/message.ts +122 -0
  106. package/src/components/message/message.vue +54 -0
  107. package/src/components/message/style.scss +130 -0
  108. package/src/components/message/style.ts +1 -0
  109. package/src/components/message-confirm/index.ts +3 -0
  110. package/src/components/message-confirm/message-confirm-box.vue +36 -0
  111. package/src/components/message-confirm/message-confirm.ts +139 -0
  112. package/src/components/message-confirm/message-confirm.vue +53 -0
  113. package/src/components/message-confirm/style.scss +138 -0
  114. package/src/components/message-confirm/style.ts +1 -0
  115. package/src/components/multi-select/index.ts +1 -0
  116. package/src/components/multi-select/multi-select.vue +410 -0
  117. package/src/components/multi-select/style.scss +376 -0
  118. package/src/components/multi-select/style.ts +2 -0
  119. package/src/components/number-input/index.ts +1 -0
  120. package/src/components/number-input/number-input.vue +407 -0
  121. package/src/components/number-input/style.scss +118 -0
  122. package/src/components/number-input/style.ts +2 -0
  123. package/src/components/password-input/index.ts +1 -0
  124. package/src/components/password-input/password-input.vue +134 -0
  125. package/src/components/password-input/style.scss +40 -0
  126. package/src/components/password-input/style.ts +2 -0
  127. package/src/components/progress/index.ts +1 -0
  128. package/src/components/progress/progress.vue +90 -0
  129. package/src/components/progress/style.scss +73 -0
  130. package/src/components/progress/style.ts +1 -0
  131. package/src/components/radio/index.ts +1 -0
  132. package/src/components/radio/radio.vue +57 -0
  133. package/src/components/radio/style.scss +113 -0
  134. package/src/components/radio/style.ts +1 -0
  135. package/src/components/radio-group/index.ts +1 -0
  136. package/src/components/radio-group/radio-group.vue +60 -0
  137. package/src/components/radio-group/style.scss +29 -0
  138. package/src/components/radio-group/style.ts +2 -0
  139. package/src/components/segment/index.ts +1 -0
  140. package/src/components/segment/segment.vue +121 -0
  141. package/src/components/segment/style.scss +118 -0
  142. package/src/components/segment/style.ts +1 -0
  143. package/src/components/select/index.ts +1 -0
  144. package/src/components/select/select.vue +278 -0
  145. package/src/components/select/style.scss +237 -0
  146. package/src/components/select/style.ts +2 -0
  147. package/src/components/select/use-options.ts +195 -0
  148. package/src/components/skeleton/index.ts +1 -0
  149. package/src/components/skeleton/skeleton.vue +37 -0
  150. package/src/components/skeleton/style.scss +95 -0
  151. package/src/components/skeleton/style.ts +1 -0
  152. package/src/components/space/index.ts +1 -0
  153. package/src/components/space/space.vue +45 -0
  154. package/src/components/space/style.scss +41 -0
  155. package/src/components/space/style.ts +1 -0
  156. package/src/components/steps/index.ts +1 -0
  157. package/src/components/steps/steps.vue +114 -0
  158. package/src/components/steps/style.scss +231 -0
  159. package/src/components/steps/style.ts +1 -0
  160. package/src/components/switch/index.ts +1 -0
  161. package/src/components/switch/style.scss +137 -0
  162. package/src/components/switch/style.ts +1 -0
  163. package/src/components/switch/switch.vue +63 -0
  164. package/src/components/tabs/index.ts +1 -0
  165. package/src/components/tabs/style.scss +285 -0
  166. package/src/components/tabs/style.ts +1 -0
  167. package/src/components/tabs/tabs.vue +127 -0
  168. package/src/components/tag/index.ts +1 -0
  169. package/src/components/tag/style.scss +114 -0
  170. package/src/components/tag/style.ts +1 -0
  171. package/src/components/tag/tag.vue +49 -0
  172. package/src/components/text/index.ts +1 -0
  173. package/src/components/text/style.scss +63 -0
  174. package/src/components/text/style.ts +1 -0
  175. package/src/components/text/text.vue +63 -0
  176. package/src/components/textarea/index.ts +1 -0
  177. package/src/components/textarea/style.scss +124 -0
  178. package/src/components/textarea/style.ts +1 -0
  179. package/src/components/textarea/textarea.vue +121 -0
  180. package/src/components/textarea/utils.ts +11 -0
  181. package/src/components/time-picker/index.ts +1 -0
  182. package/src/components/time-picker/style.scss +216 -0
  183. package/src/components/time-picker/style.ts +2 -0
  184. package/src/components/time-picker/time-picker.vue +263 -0
  185. package/src/components/timeline/index.ts +2 -0
  186. package/src/components/timeline/style.scss +107 -0
  187. package/src/components/timeline/style.ts +1 -0
  188. package/src/components/timeline/timeline-item.vue +43 -0
  189. package/src/components/timeline/timeline.vue +16 -0
  190. package/src/components/uploader/index.ts +1 -0
  191. package/src/components/uploader/style.scss +220 -0
  192. package/src/components/uploader/style.ts +1 -0
  193. package/src/components/uploader/uploader.vue +306 -0
  194. package/src/env.d.ts +7 -0
  195. package/src/index.ts +99 -1
  196. package/src/shared/bem.ts +12 -0
  197. package/src/shared/use-scroll-lock.ts +111 -0
  198. package/src/shared/use-touch-gesture.ts +150 -0
  199. package/src/styles/_tokens.scss +129 -0
  200. package/src/types/avatar.ts +31 -0
  201. package/src/types/badge.ts +28 -0
  202. package/src/types/button.ts +48 -0
  203. package/src/types/card.ts +16 -0
  204. package/src/types/cascade.ts +87 -0
  205. package/src/types/check-tag.ts +24 -0
  206. package/src/types/checkbox-group.ts +20 -0
  207. package/src/types/checkbox.ts +15 -0
  208. package/src/types/collapse.ts +79 -0
  209. package/src/types/date-picker.ts +39 -0
  210. package/src/types/descriptions.ts +22 -0
  211. package/src/types/dialog.ts +54 -0
  212. package/src/types/divider.ts +14 -0
  213. package/src/types/drawer.ts +38 -0
  214. package/src/types/empty.ts +19 -0
  215. package/src/types/form-item.ts +22 -0
  216. package/src/types/form.ts +54 -0
  217. package/src/types/icon.ts +10 -0
  218. package/src/types/input.ts +51 -0
  219. package/src/types/list.ts +13 -0
  220. package/src/types/loading.ts +20 -0
  221. package/src/types/message-confirm.ts +72 -0
  222. package/src/types/message.ts +71 -0
  223. package/src/types/multi-select.ts +38 -0
  224. package/src/types/number-input.ts +32 -0
  225. package/src/types/password-input.ts +19 -0
  226. package/src/types/progress.ts +22 -0
  227. package/src/types/radio-group.ts +33 -0
  228. package/src/types/radio.ts +18 -0
  229. package/src/types/segment.ts +42 -0
  230. package/src/types/select.ts +63 -0
  231. package/src/types/skeleton.ts +29 -0
  232. package/src/types/space.ts +22 -0
  233. package/src/types/steps.ts +67 -0
  234. package/src/types/switch.ts +17 -0
  235. package/src/types/tabs.ts +63 -0
  236. package/src/types/tag.ts +19 -0
  237. package/src/types/text.ts +28 -0
  238. package/src/types/textarea.ts +76 -0
  239. package/src/types/time-picker.ts +40 -0
  240. package/src/types/timeline.ts +15 -0
  241. package/src/types/uploader.ts +50 -0
  242. package/.turbo/turbo-build.log +0 -2
  243. package/.turbo/turbo-check-types.log +0 -1
  244. package/tsconfig.json +0 -12
  245. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,111 @@
1
+ import { getCurrentScope, onScopeDispose } from 'vue'
2
+
3
+ /**
4
+ * body / 任意容器滚动锁定(Dialog / Drawer / BottomSheet 等弹层共用)
5
+ *
6
+ * - body 用 `position: fixed` 定住页面(iOS Safari 下仅 `overflow: hidden`
7
+ * 阻不住橡皮筋滚动),其余容器(设备外壳视口等)用 `overflow: hidden`
8
+ * 并记录 `scrollTop`,解锁时还原滚动位置
9
+ * - 计数嵌套:多个弹层叠加(或对不同元素)各自 `lock()`/`unlock()`,
10
+ * 同一元素只有最外层解锁(计数归零)才还原样式与滚动位置
11
+ */
12
+
13
+ /** body 锁定期间改写的内联样式属性,解锁时逐项还原为锁定前的值 */
14
+ const BODY_PROPS = ['position', 'top', 'left', 'width', 'overflow'] as const
15
+
16
+ interface ElementLock {
17
+ count: number
18
+ restore: () => void
19
+ }
20
+
21
+ /** 逐元素持有状态;不同元素互不影响(body 与自定义容器可同时被锁) */
22
+ const locks = new WeakMap<HTMLElement, ElementLock>()
23
+
24
+ function lockElement(el: HTMLElement): void {
25
+ const held = locks.get(el)
26
+ if (held) {
27
+ held.count++
28
+ return
29
+ }
30
+
31
+ if (el === document.body) {
32
+ const prev = BODY_PROPS.map((prop) => [prop, el.style.getPropertyValue(prop)] as const)
33
+ const scrollY = window.scrollY
34
+
35
+ el.style.position = 'fixed'
36
+ el.style.top = `-${scrollY}px`
37
+ el.style.left = '0'
38
+ el.style.width = '100%'
39
+ el.style.overflow = 'hidden'
40
+
41
+ locks.set(el, {
42
+ count: 1,
43
+ restore: () => {
44
+ for (const [prop, value] of prev) el.style.setProperty(prop, value)
45
+ window.scrollTo(0, scrollY)
46
+ }
47
+ })
48
+ return
49
+ }
50
+
51
+ const prevOverflow = el.style.overflow
52
+ const scrollTop = el.scrollTop
53
+ el.style.overflow = 'hidden'
54
+
55
+ locks.set(el, {
56
+ count: 1,
57
+ restore: () => {
58
+ el.style.overflow = prevOverflow
59
+ el.scrollTop = scrollTop
60
+ }
61
+ })
62
+ }
63
+
64
+ function unlockElement(el: HTMLElement): void {
65
+ const held = locks.get(el)
66
+ if (!held) return
67
+
68
+ held.count--
69
+ if (held.count === 0) {
70
+ locks.delete(el)
71
+ held.restore()
72
+ }
73
+ }
74
+
75
+ export interface ScrollLock {
76
+ /** 锁定目标元素滚动;可嵌套调用 */
77
+ lock: () => void
78
+ /** 解锁一层;未持有时为空操作 */
79
+ unlock: () => void
80
+ }
81
+
82
+ /**
83
+ * 按需解析锁定目标:元素可能在锁定的瞬间才注册/切换(设备外壳重挂载)。
84
+ * 内部工厂,供弹层宿主(overlay-container)按各自目标派生锁,不进公开导出。
85
+ */
86
+ export function createScrollLock(resolve: () => HTMLElement): ScrollLock {
87
+ let target: HTMLElement | undefined
88
+
89
+ const lock = () => {
90
+ // 锁定期间目标变化(罕见)时先还原旧目标,避免永久锁死
91
+ if (target) unlockElement(target)
92
+ target = resolve()
93
+ lockElement(target)
94
+ }
95
+
96
+ const unlock = () => {
97
+ if (!target) return
98
+ unlockElement(target)
99
+ target = undefined
100
+ }
101
+
102
+ // 持锁期间组件被整体卸载(如外层 v-if 直接拔掉弹层)时兜底解锁
103
+ if (getCurrentScope()) onScopeDispose(unlock)
104
+
105
+ return { lock, unlock }
106
+ }
107
+
108
+ /** 锁定 body 滚动(公开 API),关闭后恢复原滚动位置 */
109
+ export function useScrollLock(): ScrollLock {
110
+ return createScrollLock(() => document.body)
111
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * 单指触摸手势基建(Drawer 四向边缘拖拽关闭、DatePicker 横滑翻月共用)
3
+ *
4
+ * - 只消费 `touch*` 事件,不依赖 pointer / hover
5
+ * - 判定逻辑不调用 `preventDefault`,监听是否 passive 均可工作;
6
+ * 需要阻断滚动链时由组件在模板上加 `.prevent` 修饰符
7
+ *
8
+ * 用法:返回的四个处理函数逐个绑到模板(或 `v-on` 整体展开),
9
+ * 位移跟随写在 `onMove`,释放判定用 `shouldCloseByDrag` / `horizontalSwipeDirection`
10
+ */
11
+
12
+ export interface TouchGestureState {
13
+ /** 相对起点的横向位移(px,向右为正) */
14
+ dx: number
15
+ /** 相对起点的纵向位移(px,向下为正) */
16
+ dy: number
17
+ /** 起触至今的时长(ms) */
18
+ duration: number
19
+ /** 横向速度(px/ms,向右为正,按最近一段位移计算) */
20
+ vx: number
21
+ /** 纵向速度(px/ms,向下为正,按最近一段位移计算) */
22
+ vy: number
23
+ }
24
+
25
+ export interface UseTouchGestureOptions {
26
+ /** 触摸开始(位移为 0 的首帧) */
27
+ onStart?: (state: TouchGestureState) => void
28
+ /** 触摸移动(每帧更新,供拖拽跟随) */
29
+ onMove?: (state: TouchGestureState) => void
30
+ /** 松手,在这里做释放判定 */
31
+ onEnd?: (state: TouchGestureState) => void
32
+ /** 手势被打断(来电、系统手势、落到多指等):拖拽中的组件应回弹而非关闭 */
33
+ onCancel?: (state: TouchGestureState) => void
34
+ }
35
+
36
+ /** 绑定到模板 `touch*` 事件的处理函数集,可直接 `v-on` 展开 */
37
+ export interface TouchGestureHandlers {
38
+ touchstart: (event: TouchEvent) => void
39
+ touchmove: (event: TouchEvent) => void
40
+ touchend: (event: TouchEvent) => void
41
+ touchcancel: () => void
42
+ }
43
+
44
+ export function useTouchGesture(options: UseTouchGestureOptions = {}): TouchGestureHandlers {
45
+ let startX = 0
46
+ let startY = 0
47
+ let startT = 0
48
+ let lastX = 0
49
+ let lastY = 0
50
+ let lastT = 0
51
+ let lastVX = 0
52
+ let lastVY = 0
53
+ let tracking = false
54
+
55
+ function stateAt(x: number, y: number, t: number): TouchGestureState {
56
+ const dt = t - lastT
57
+ // 速度按最近一段位移计算,长距离慢拖后再甩动时不被整体平均稀释
58
+ if (dt > 0) {
59
+ lastVX = (x - lastX) / dt
60
+ lastVY = (y - lastY) / dt
61
+ }
62
+
63
+ return { dx: x - startX, dy: y - startY, duration: t - startT, vx: lastVX, vy: lastVY }
64
+ }
65
+
66
+ function cancelTracking() {
67
+ if (!tracking) return
68
+ tracking = false
69
+ options.onCancel?.(stateAt(lastX, lastY, lastT))
70
+ }
71
+
72
+ return {
73
+ touchstart(e) {
74
+ // 只跟踪单指;第二指落下视为手势被打断
75
+ if (tracking || e.touches.length !== 1) {
76
+ cancelTracking()
77
+ return
78
+ }
79
+
80
+ const touch = e.touches[0]!
81
+ tracking = true
82
+ startX = lastX = touch.clientX
83
+ startY = lastY = touch.clientY
84
+ lastT = startT = e.timeStamp
85
+ lastVX = lastVY = 0
86
+ options.onStart?.({ dx: 0, dy: 0, duration: 0, vx: 0, vy: 0 })
87
+ },
88
+
89
+ touchmove(e) {
90
+ if (!tracking) return
91
+ if (e.touches.length !== 1) {
92
+ cancelTracking()
93
+ return
94
+ }
95
+
96
+ const touch = e.touches[0]!
97
+ options.onMove?.(stateAt(touch.clientX, touch.clientY, e.timeStamp))
98
+ lastX = touch.clientX
99
+ lastY = touch.clientY
100
+ lastT = e.timeStamp
101
+ },
102
+
103
+ touchend(e) {
104
+ if (!tracking) return
105
+ tracking = false
106
+ const touch = e.changedTouches[0]
107
+ options.onEnd?.(stateAt(touch?.clientX ?? lastX, touch?.clientY ?? lastY, e.timeStamp))
108
+ },
109
+
110
+ touchcancel: cancelTracking
111
+ }
112
+ }
113
+
114
+ export interface DragDismissOptions {
115
+ /** 拖拽轴:Drawer 的 bottom / top 形态用 y,left / right 形态用 x */
116
+ axis: 'x' | 'y'
117
+ /**
118
+ * 关闭方向:1 沿正方向(右 / 下)拖动关闭,-1 沿负方向(左 / 上)。
119
+ * Drawer 对应关系:bottom 向下拉 1、top 向上推 -1、left 向左拖 -1、right 向右拖 1
120
+ */
121
+ sign: 1 | -1
122
+ /** 判定为关闭的最小位移(px),默认 100 */
123
+ distance?: number
124
+ /** 判定为关闭的最小甩动速度(px/ms),默认 0.3 */
125
+ velocity?: number
126
+ }
127
+
128
+ /**
129
+ * 拖拽释放判定:沿关闭方向的位移或甩动速度任一达标即应关闭;
130
+ * 反方向(离开关闭方向)拖动永远不关闭
131
+ */
132
+ export function shouldCloseByDrag(state: TouchGestureState, options: DragDismissOptions): boolean {
133
+ const { axis, sign, distance = 100, velocity = 0.3 } = options
134
+ const delta = sign * (axis === 'x' ? state.dx : state.dy)
135
+
136
+ if (delta <= 0) return false
137
+
138
+ const speed = sign * (axis === 'x' ? state.vx : state.vy)
139
+ return delta >= distance || speed >= velocity
140
+ }
141
+
142
+ /** 横向滑动方向判定:位移达阈值且横向分量大于纵向时返回方向,否则 null(点按或纵向滚动) */
143
+ export function horizontalSwipeDirection(
144
+ state: TouchGestureState,
145
+ distance = 50
146
+ ): 'left' | 'right' | null {
147
+ const { dx, dy } = state
148
+ if (Math.abs(dx) < distance || Math.abs(dx) <= Math.abs(dy)) return null
149
+ return dx > 0 ? 'right' : 'left'
150
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * 移动端密度 token 层(`--um-*` 前缀,纯 SCSS,不经运行时注入)
3
+ *
4
+ * - 值表是编译期回退值的单一来源:组件样式一律经 `use-var()` 取密度值,
5
+ * 产出 `var(--um-<name>, 回退值)`,宿主可在任意层级用 `--um-*` 覆盖
6
+ * - 配色、圆角、阴影等观感 token 仍走 `@veltra/styles` 的 `--u-*` 预设体系
7
+ * - 本模块只提供值与函数,不产出任何 CSS;组件 SCSS 以
8
+ * `@use '../../styles/tokens' as t;` 引用(下划线 partial,禁止单独编译)
9
+ */
10
+ @use 'sass:map';
11
+
12
+ $namespace: 'um' !default;
13
+
14
+ // 密度值表:键为去掉 `--um-` 前缀的 token 名
15
+ $values: (
16
+ // 字号:正文主字号 16px(正文与输入类字号不小于 16px),辅助信息 14 / 12px
17
+ 'font-size-main': 16px,
18
+ 'font-size-secondary': 14px,
19
+ 'font-size-auxiliary': 12px,
20
+ // 面板标题档:底部弹层头部主标题、滚轮等大号主内容
21
+ 'font-size-title': 18px,
22
+ // 列表行式表单的行高(约 48px)
23
+ 'form-row-height': 48px,
24
+ // 表单 / 按钮等控件高度档(small / default / large)
25
+ 'control-height-small': 32px,
26
+ 'control-height-default': 40px,
27
+ 'control-height-large': 48px,
28
+ // 输入类控件圆角档(small / default / large)
29
+ 'control-radius-small': 4px,
30
+ 'control-radius-default': 6px,
31
+ 'control-radius-large': 8px,
32
+ // 弹层与浮层容器圆角(dialog / drawer / bottom-sheet / message 等面板级圆角)
33
+ 'panel-radius': 12px,
34
+ // 输入类正文行高
35
+ 'text-line-height': 1.5,
36
+ // 头像等组件尺寸档:整体上移的移动端密度,最小档 ≥32px
37
+ 'avatar-size-small': 32px,
38
+ 'avatar-size-default': 40px,
39
+ 'avatar-size-large': 48px,
40
+ // 展示组件密度基准:加载指示器边长(ring / dual-ring 直径,dot / bars 推导基准)、
41
+ // 环形进度兜底直径(progress 的 size 未传时生效)
42
+ 'loading-indicator': 36px,
43
+ 'progress-circle': 100px,
44
+ // 开关轨道高度档(宽度为高度两倍,手柄随轨道推导)
45
+ 'switch-track-small': 24px,
46
+ 'switch-track-default': 28px,
47
+ 'switch-track-large': 32px,
48
+ // 上传组件瓦片尺寸档(375 视口默认档约三列 + 间距)
49
+ 'uploader-size-small': 64px,
50
+ 'uploader-size-default': 80px,
51
+ 'uploader-size-large': 96px,
52
+ // 间距刻度(4px 基)
53
+ 'spacing-xs': 4px,
54
+ 'spacing-sm': 8px,
55
+ 'spacing-md': 12px,
56
+ 'spacing-lg': 16px,
57
+ 'spacing-xl': 24px,
58
+ // 触控热区下限
59
+ 'touch-target': 44px
60
+ );
61
+
62
+ // 引用移动端密度 token:`var(--um-<name>, 编译期回退值)`
63
+ @function use-var($name) {
64
+ @if not map.has-key($values, $name) {
65
+ @error '未知的移动端 token:--#{$namespace}-#{$name}';
66
+ }
67
+
68
+ @return var(--#{$namespace}-#{$name}, #{map.get($values, $name)});
69
+ }
70
+
71
+ // 按组件尺寸取控件高度:small / default / large → --um-control-height-*
72
+ @function control-height($size) {
73
+ @return use-var(map.get(
74
+ (
75
+ small: 'control-height-small',
76
+ default: 'control-height-default',
77
+ large: 'control-height-large'
78
+ ),
79
+ $size
80
+ ));
81
+ }
82
+
83
+ // 按组件尺寸取输入类控件圆角:small / default / large → --um-control-radius-*
84
+ @function control-radius($size) {
85
+ @return use-var(map.get(
86
+ (
87
+ small: 'control-radius-small',
88
+ default: 'control-radius-default',
89
+ large: 'control-radius-large'
90
+ ),
91
+ $size
92
+ ));
93
+ }
94
+
95
+ // 按组件尺寸取间距:small / default / large → --um-spacing-sm / md / lg
96
+ @function spacing($size) {
97
+ @return use-var(map.get(
98
+ (
99
+ small: 'spacing-sm',
100
+ default: 'spacing-md',
101
+ large: 'spacing-lg'
102
+ ),
103
+ $size
104
+ ));
105
+ }
106
+
107
+ // 按组件尺寸取开关轨道高度:small / default / large → --um-switch-track-*
108
+ @function switch-track($size) {
109
+ @return use-var(map.get(
110
+ (
111
+ small: 'switch-track-small',
112
+ default: 'switch-track-default',
113
+ large: 'switch-track-large'
114
+ ),
115
+ $size
116
+ ));
117
+ }
118
+
119
+ // 按组件尺寸取上传瓦片尺寸:small / default / large → --um-uploader-size-*
120
+ @function uploader-size($size) {
121
+ @return use-var(map.get(
122
+ (
123
+ small: 'uploader-size-small',
124
+ default: 'uploader-size-default',
125
+ large: 'uploader-size-large'
126
+ ),
127
+ $size
128
+ ));
129
+ }
@@ -0,0 +1,31 @@
1
+ import type { ComponentProps, DeconstructValue } from '@veltra/utils'
2
+
3
+ /** 头像形状:circle 圆形 / round 圆角方形 */
4
+ export type AvatarShape = 'circle' | 'round'
5
+
6
+ /** 头像组件属性 */
7
+ export interface AvatarProps extends ComponentProps {
8
+ /**
9
+ * 尺寸档位(继承自 ComponentProps):small / default / large,
10
+ * 宽高取移动端密度 token `--um-avatar-size-*`(32 / 40 / 48px),默认 default
11
+ */
12
+ size?: ComponentProps['size']
13
+
14
+ /** 图片地址;未传或加载失败时回退展示默认插槽内容 */
15
+ src?: string
16
+ /** 图片描述,作为 img 的 alt */
17
+ alt?: string
18
+ /** 形状,默认 circle */
19
+ shape?: AvatarShape
20
+ }
21
+
22
+ /** 头像组件定义的事件 */
23
+ export interface AvatarEmits {
24
+ (e: 'error', ev: Event): void
25
+ }
26
+
27
+ /** 头像组件暴露的属性和方法(组件内部使用) */
28
+ export interface _AvatarExposed {}
29
+
30
+ /** 头像组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
31
+ export type AvatarExposed = DeconstructValue<_AvatarExposed>
@@ -0,0 +1,28 @@
1
+ import type { ColorType, ComponentProps, DeconstructValue } from '@veltra/utils'
2
+
3
+ /** 徽章组件属性 */
4
+ export interface BadgeProps extends ComponentProps {
5
+ /** 显示值 */
6
+ value?: number | string
7
+ /** 类别 */
8
+ type?: ColorType
9
+ /** 自定义背景色 */
10
+ color?: string
11
+ /** 是否隐藏 Badge */
12
+ hidden?: boolean
13
+ /** 最大值 {{max}}+ */
14
+ max?: number
15
+ /** 是否显示小圆点 */
16
+ dot?: boolean
17
+ }
18
+
19
+ /** 徽章组件定义的事件 */
20
+ export interface BadgeEmits {
21
+ (e: 'update:modelValue', value: string): void
22
+ }
23
+
24
+ /** 徽章组件暴露的属性和方法(组件内部使用) */
25
+ export interface _BadgeExposed {}
26
+
27
+ /** 徽章组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
28
+ export type BadgeExposed = DeconstructValue<_BadgeExposed>
@@ -0,0 +1,48 @@
1
+ import type { ColorType, ComponentProps, DeconstructValue } from '@veltra/utils'
2
+ import type { Component, ShallowRef } from 'vue'
3
+
4
+ /** 按钮类型 */
5
+ export type ButtonType = ColorType
6
+
7
+ /**
8
+ * 按钮属性类型
9
+ * @description 组件不注入默认 `aria-label`:可访问名来自默认插槽文本,
10
+ * 或你自己传入的 `aria-label`(透传到根元素);只有图标的按钮必须自行传 `aria-label`
11
+ */
12
+ export interface ButtonProps extends ComponentProps {
13
+ /** 按钮类型 */
14
+ type?: ButtonType
15
+ /** 是否以文本形式展示 */
16
+ text?: boolean
17
+ /** 朴素模式 */
18
+ plain?: boolean
19
+ /** 加载中 */
20
+ loading?: boolean
21
+ /** 加载图标 */
22
+ loadingIcon?: Component
23
+ /** 圆形 */
24
+ circle?: boolean
25
+ /** 禁用 */
26
+ disabled?: boolean
27
+ /** 图标 */
28
+ icon?: Component
29
+ /** 图标大小, 单位px */
30
+ iconSize?: number
31
+ /** 图标位置 */
32
+ iconPosition?: 'left' | 'right'
33
+ /** 事件是否传播(冒泡或者捕获) */
34
+ propagate?: boolean
35
+ }
36
+
37
+ export interface ButtonEmits {
38
+ /** 点击事件 */
39
+ (name: 'click', e: MouseEvent): void
40
+ }
41
+
42
+ /** 在组件内部引用 */
43
+ export interface _ButtonExposed {
44
+ el: ShallowRef<HTMLButtonElement | undefined>
45
+ }
46
+
47
+ /** 按钮暴露的属性和方法 */
48
+ export type ButtonExposed = DeconstructValue<_ButtonExposed>
@@ -0,0 +1,16 @@
1
+ import type { ComponentProps, DeconstructValue } from '@veltra/utils'
2
+
3
+ /** 卡片组件属性 */
4
+ export interface CardProps extends ComponentProps {
5
+ /** 宽度 */
6
+ width?: string | number
7
+
8
+ /** 融合样式,卡片不再有阴影 */
9
+ integrate?: boolean
10
+ }
11
+
12
+ export interface CardEmits {}
13
+
14
+ export interface _CardExposed {}
15
+
16
+ export type CardExposed = DeconstructValue<_CardExposed>
@@ -0,0 +1,87 @@
1
+ import type { TreeNode } from '@cat-kit/core'
2
+ import type { DeconstructValue, FormComponentProps } from '@veltra/utils'
3
+ import type { CSSProperties } from 'vue'
4
+
5
+ export interface CascadeNode<
6
+ Data extends Record<string, any> = Record<string, any>
7
+ > extends TreeNode<Data, CascadeNode<Data>> {
8
+ visible: boolean
9
+ value: string
10
+ label: string
11
+ }
12
+
13
+ /** 级联选择器组件属性(移动端形态:面包屑 Tab + 底部面板) */
14
+ export interface CascadeProps extends FormComponentProps {
15
+ /**
16
+ * 分隔符
17
+ * @default '/'
18
+ */
19
+ separator?: string
20
+ /** 数据值 */
21
+ modelValue?: string[] | string
22
+ /** 级联数据项的标签字段 */
23
+ labelKey?: string
24
+ /**
25
+ * 兜底展示文案
26
+ * @description 单选 modelValue 未命中数据时展示 text(如回显路径已不在选项中),命中时展示节点 label 路径
27
+ */
28
+ text?: string
29
+ /** 级联数据项的值字段 */
30
+ valueKey?: string
31
+ /** 占位符 */
32
+ placeholder?: string
33
+ /** 是否可清除 */
34
+ clearable?: boolean
35
+ /** 子级字段 */
36
+ childrenKey?: string
37
+ /** 严格模式:仅叶子节点提交,非叶子点击只展开下级 */
38
+ strict?: boolean
39
+ /**
40
+ * 是否展示/提交完整路径
41
+ * @default true
42
+ * false 时显示、modelValue、update:label 均只体现选中叶子节点
43
+ */
44
+ showFullPath?: boolean
45
+ /**
46
+ * 数据项
47
+ */
48
+ data?: Record<string, any>[]
49
+ /**
50
+ * 多选
51
+ */
52
+ multiple?: boolean
53
+ /**
54
+ * 搜索
55
+ */
56
+ filterable?: boolean
57
+ /** 触发器上最多展示的标签数,超出折叠为 +N */
58
+ visibilityLimit?: number
59
+ /** 底部面板内容容器样式 */
60
+ contentStyle?: CSSProperties | string
61
+ /** 底部面板内容容器类名 */
62
+ contentClass?: unknown
63
+ }
64
+
65
+ /** 级联选择器组件定义的事件 */
66
+ export interface CascadeEmits {
67
+ /** 触发更新 label 事件 */
68
+ (e: 'update:label', label?: string | string[]): void
69
+ (e: 'update:modelValue', value?: string | string[]): void
70
+ (
71
+ e: 'change',
72
+ items: (Record<string, any> & { fullLabel?: string })[],
73
+ fullLabels?: string[]
74
+ ): void
75
+ (
76
+ e: 'change',
77
+ item: (Record<string, any> & { fullLabel?: string }) | undefined,
78
+ fullLabel?: string
79
+ ): void
80
+ (e: 'clear'): void
81
+ }
82
+
83
+ /** 级联选择器组件暴露的属性和方法(组件内部使用) */
84
+ export interface _CascadeExposed {}
85
+
86
+ /** 级联选择器组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
87
+ export type CascadeExposed = DeconstructValue<_CascadeExposed>
@@ -0,0 +1,24 @@
1
+ import type { DeconstructValue } from '@veltra/utils'
2
+
3
+ /** check-tag组件属性 */
4
+ export interface CheckTagProps {
5
+ modelValue?: boolean
6
+
7
+ checked?: boolean
8
+
9
+ /** 是否禁用;禁用后点击不切换选中态、不触发任何事件 */
10
+ disabled?: boolean
11
+ }
12
+
13
+ /** check-tag组件定义的事件 */
14
+ export interface CheckTagEmits {
15
+ (e: 'update:modelValue', value: boolean): void
16
+
17
+ (e: 'change', value: boolean): void
18
+ }
19
+
20
+ /** check-tag组件暴露的属性和方法(组件内部使用) */
21
+ export interface _CheckTagExposed {}
22
+
23
+ /** check-tag组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
24
+ export type CheckTagExposed = DeconstructValue<_CheckTagExposed>
@@ -0,0 +1,20 @@
1
+ import type { FormComponentProps } from '@veltra/utils'
2
+
3
+ /** 复选框组组件属性(移动端列表行式:选项纵向排列占满整行,v-model 为选中值数组) */
4
+ export interface CheckboxGroupProps extends FormComponentProps {
5
+ /** 值 */
6
+ modelValue?: Array<any>
7
+ /** 复选框项 */
8
+ items: Array<Record<string, any>>
9
+ /** 标签文本的key */
10
+ labelKey?: string
11
+ /** 值的key */
12
+ valueKey?: string
13
+ /** 选项行间细分隔线。默认 false */
14
+ divider?: boolean
15
+ }
16
+
17
+ /** 复选框组组件定义的事件 */
18
+ export interface CheckboxGroupEmits {
19
+ (e: 'update:modelValue', value: Array<any>): void
20
+ }
@@ -0,0 +1,15 @@
1
+ import type { FormComponentProps } from '@veltra/utils'
2
+
3
+ /** 复选框组件属性(与 @veltra/desktop UCheckbox 同名;呈移动端列表行式选项形态) */
4
+ export interface CheckboxProps extends FormComponentProps {
5
+ /** 部分选中 */
6
+ indeterminate?: boolean
7
+ /** 是否选中 */
8
+ modelValue?: boolean
9
+ }
10
+
11
+ /** 复选框组件定义的事件 */
12
+ export interface CheckboxEmits {
13
+ (name: 'update:modelValue', checked: boolean): void
14
+ (name: 'change', checked: boolean, e: MouseEvent): void
15
+ }