@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.
- package/package.json +32 -6
- package/src/components/_internal/bottom-sheet/bottom-sheet.vue +81 -0
- package/src/components/_internal/bottom-sheet/index.ts +1 -0
- package/src/components/_internal/bottom-sheet/style.scss +116 -0
- package/src/components/_internal/bottom-sheet/style.ts +1 -0
- package/src/components/_internal/overlay-container.ts +34 -0
- package/src/components/avatar/avatar.vue +44 -0
- package/src/components/avatar/index.ts +1 -0
- package/src/components/avatar/style.scss +53 -0
- package/src/components/avatar/style.ts +1 -0
- package/src/components/badge/badge.vue +59 -0
- package/src/components/badge/index.ts +1 -0
- package/src/components/badge/style.scss +62 -0
- package/src/components/badge/style.ts +1 -0
- package/src/components/button/button.vue +76 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/button/style.scss +207 -0
- package/src/components/button/style.ts +1 -0
- package/src/components/card/card.vue +24 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/card/style.scss +35 -0
- package/src/components/card/style.ts +1 -0
- package/src/components/cascade/cascade.vue +595 -0
- package/src/components/cascade/index.ts +1 -0
- package/src/components/cascade/node.ts +29 -0
- package/src/components/cascade/style.scss +424 -0
- package/src/components/cascade/style.ts +2 -0
- package/src/components/check-tag/check-tag.vue +35 -0
- package/src/components/check-tag/index.ts +1 -0
- package/src/components/check-tag/style.scss +46 -0
- package/src/components/check-tag/style.ts +1 -0
- package/src/components/checkbox/checkbox.vue +67 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/checkbox/style.scss +132 -0
- package/src/components/checkbox/style.ts +2 -0
- package/src/components/checkbox-group/checkbox-group.vue +94 -0
- package/src/components/checkbox-group/index.ts +1 -0
- package/src/components/checkbox-group/style.scss +57 -0
- package/src/components/checkbox-group/style.ts +2 -0
- package/src/components/collapse/collapse-item.vue +134 -0
- package/src/components/collapse/collapse.vue +117 -0
- package/src/components/collapse/di.ts +18 -0
- package/src/components/collapse/index.ts +3 -0
- package/src/components/collapse/style.scss +157 -0
- package/src/components/collapse/style.ts +1 -0
- package/src/components/date-picker/date-picker.vue +326 -0
- package/src/components/date-picker/index.ts +1 -0
- package/src/components/date-picker/style.scss +236 -0
- package/src/components/date-picker/style.ts +2 -0
- package/src/components/descriptions/descriptions-item.vue +12 -0
- package/src/components/descriptions/descriptions.vue +50 -0
- package/src/components/descriptions/index.ts +2 -0
- package/src/components/descriptions/style.scss +120 -0
- package/src/components/descriptions/style.ts +1 -0
- package/src/components/dialog/dialog.vue +143 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/dialog/style.scss +163 -0
- package/src/components/dialog/style.ts +4 -0
- package/src/components/divider/divider.vue +29 -0
- package/src/components/divider/index.ts +1 -0
- package/src/components/divider/style.scss +91 -0
- package/src/components/divider/style.ts +1 -0
- package/src/components/drawer/drawer.vue +168 -0
- package/src/components/drawer/index.ts +1 -0
- package/src/components/drawer/style.scss +240 -0
- package/src/components/drawer/style.ts +2 -0
- package/src/components/empty/empty.vue +22 -0
- package/src/components/empty/index.ts +1 -0
- package/src/components/empty/style.scss +36 -0
- package/src/components/empty/style.ts +1 -0
- package/src/components/form/form.vue +119 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/form/is-field-modified.ts +16 -0
- package/src/components/form/style.scss +58 -0
- package/src/components/form/style.ts +2 -0
- package/src/components/form/use-form-fields.ts +115 -0
- package/src/components/form/use-node-interceptor.ts +101 -0
- package/src/components/form-item/form-item.vue +171 -0
- package/src/components/form-item/helper.ts +9 -0
- package/src/components/form-item/index.ts +1 -0
- package/src/components/form-item/style.scss +121 -0
- package/src/components/form-item/style.ts +1 -0
- package/src/components/form-item/validate.ts +131 -0
- package/src/components/icon/icon.vue +23 -0
- package/src/components/icon/index.ts +1 -0
- package/src/components/icon/style.scss +37 -0
- package/src/components/icon/style.ts +1 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.vue +187 -0
- package/src/components/input/style.scss +121 -0
- package/src/components/input/style.ts +1 -0
- package/src/components/list/index.ts +3 -0
- package/src/components/list/list-item.vue +20 -0
- package/src/components/list/list.vue +20 -0
- package/src/components/list/style.scss +50 -0
- package/src/components/list/style.ts +1 -0
- package/src/components/loading/directive.ts +34 -0
- package/src/components/loading/index.ts +3 -0
- package/src/components/loading/loading.vue +25 -0
- package/src/components/loading/style.scss +179 -0
- package/src/components/loading/style.ts +2 -0
- package/src/components/message/helper.ts +28 -0
- package/src/components/message/index.ts +3 -0
- package/src/components/message/message-box.vue +31 -0
- package/src/components/message/message.ts +122 -0
- package/src/components/message/message.vue +54 -0
- package/src/components/message/style.scss +130 -0
- package/src/components/message/style.ts +1 -0
- package/src/components/message-confirm/index.ts +3 -0
- package/src/components/message-confirm/message-confirm-box.vue +36 -0
- package/src/components/message-confirm/message-confirm.ts +139 -0
- package/src/components/message-confirm/message-confirm.vue +53 -0
- package/src/components/message-confirm/style.scss +138 -0
- package/src/components/message-confirm/style.ts +1 -0
- package/src/components/multi-select/index.ts +1 -0
- package/src/components/multi-select/multi-select.vue +410 -0
- package/src/components/multi-select/style.scss +376 -0
- package/src/components/multi-select/style.ts +2 -0
- package/src/components/number-input/index.ts +1 -0
- package/src/components/number-input/number-input.vue +407 -0
- package/src/components/number-input/style.scss +118 -0
- package/src/components/number-input/style.ts +2 -0
- package/src/components/password-input/index.ts +1 -0
- package/src/components/password-input/password-input.vue +134 -0
- package/src/components/password-input/style.scss +40 -0
- package/src/components/password-input/style.ts +2 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/progress/progress.vue +90 -0
- package/src/components/progress/style.scss +73 -0
- package/src/components/progress/style.ts +1 -0
- package/src/components/radio/index.ts +1 -0
- package/src/components/radio/radio.vue +57 -0
- package/src/components/radio/style.scss +113 -0
- package/src/components/radio/style.ts +1 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.vue +60 -0
- package/src/components/radio-group/style.scss +29 -0
- package/src/components/radio-group/style.ts +2 -0
- package/src/components/segment/index.ts +1 -0
- package/src/components/segment/segment.vue +121 -0
- package/src/components/segment/style.scss +118 -0
- package/src/components/segment/style.ts +1 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/select.vue +278 -0
- package/src/components/select/style.scss +237 -0
- package/src/components/select/style.ts +2 -0
- package/src/components/select/use-options.ts +195 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.vue +37 -0
- package/src/components/skeleton/style.scss +95 -0
- package/src/components/skeleton/style.ts +1 -0
- package/src/components/space/index.ts +1 -0
- package/src/components/space/space.vue +45 -0
- package/src/components/space/style.scss +41 -0
- package/src/components/space/style.ts +1 -0
- package/src/components/steps/index.ts +1 -0
- package/src/components/steps/steps.vue +114 -0
- package/src/components/steps/style.scss +231 -0
- package/src/components/steps/style.ts +1 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/style.scss +137 -0
- package/src/components/switch/style.ts +1 -0
- package/src/components/switch/switch.vue +63 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/style.scss +285 -0
- package/src/components/tabs/style.ts +1 -0
- package/src/components/tabs/tabs.vue +127 -0
- package/src/components/tag/index.ts +1 -0
- package/src/components/tag/style.scss +114 -0
- package/src/components/tag/style.ts +1 -0
- package/src/components/tag/tag.vue +49 -0
- package/src/components/text/index.ts +1 -0
- package/src/components/text/style.scss +63 -0
- package/src/components/text/style.ts +1 -0
- package/src/components/text/text.vue +63 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/style.scss +124 -0
- package/src/components/textarea/style.ts +1 -0
- package/src/components/textarea/textarea.vue +121 -0
- package/src/components/textarea/utils.ts +11 -0
- package/src/components/time-picker/index.ts +1 -0
- package/src/components/time-picker/style.scss +216 -0
- package/src/components/time-picker/style.ts +2 -0
- package/src/components/time-picker/time-picker.vue +263 -0
- package/src/components/timeline/index.ts +2 -0
- package/src/components/timeline/style.scss +107 -0
- package/src/components/timeline/style.ts +1 -0
- package/src/components/timeline/timeline-item.vue +43 -0
- package/src/components/timeline/timeline.vue +16 -0
- package/src/components/uploader/index.ts +1 -0
- package/src/components/uploader/style.scss +220 -0
- package/src/components/uploader/style.ts +1 -0
- package/src/components/uploader/uploader.vue +306 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +99 -1
- package/src/shared/bem.ts +12 -0
- package/src/shared/use-scroll-lock.ts +111 -0
- package/src/shared/use-touch-gesture.ts +150 -0
- package/src/styles/_tokens.scss +129 -0
- package/src/types/avatar.ts +31 -0
- package/src/types/badge.ts +28 -0
- package/src/types/button.ts +48 -0
- package/src/types/card.ts +16 -0
- package/src/types/cascade.ts +87 -0
- package/src/types/check-tag.ts +24 -0
- package/src/types/checkbox-group.ts +20 -0
- package/src/types/checkbox.ts +15 -0
- package/src/types/collapse.ts +79 -0
- package/src/types/date-picker.ts +39 -0
- package/src/types/descriptions.ts +22 -0
- package/src/types/dialog.ts +54 -0
- package/src/types/divider.ts +14 -0
- package/src/types/drawer.ts +38 -0
- package/src/types/empty.ts +19 -0
- package/src/types/form-item.ts +22 -0
- package/src/types/form.ts +54 -0
- package/src/types/icon.ts +10 -0
- package/src/types/input.ts +51 -0
- package/src/types/list.ts +13 -0
- package/src/types/loading.ts +20 -0
- package/src/types/message-confirm.ts +72 -0
- package/src/types/message.ts +71 -0
- package/src/types/multi-select.ts +38 -0
- package/src/types/number-input.ts +32 -0
- package/src/types/password-input.ts +19 -0
- package/src/types/progress.ts +22 -0
- package/src/types/radio-group.ts +33 -0
- package/src/types/radio.ts +18 -0
- package/src/types/segment.ts +42 -0
- package/src/types/select.ts +63 -0
- package/src/types/skeleton.ts +29 -0
- package/src/types/space.ts +22 -0
- package/src/types/steps.ts +67 -0
- package/src/types/switch.ts +17 -0
- package/src/types/tabs.ts +63 -0
- package/src/types/tag.ts +19 -0
- package/src/types/text.ts +28 -0
- package/src/types/textarea.ts +76 -0
- package/src/types/time-picker.ts +40 -0
- package/src/types/timeline.ts +15 -0
- package/src/types/uploader.ts +50 -0
- package/.turbo/turbo-build.log +0 -2
- package/.turbo/turbo-check-types.log +0 -1
- package/tsconfig.json +0 -12
- 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
|
+
}
|