@zerodep-dev/vue-tsx-pro-mobile 0.0.0-stage → 0.4.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/LICENSE +21 -0
- package/README.md +29 -2
- package/dist/area/data.d.ts +10 -0
- package/dist/area/data.js +3899 -0
- package/dist/area/data.js.map +1 -0
- package/dist/area/index.d.ts +22 -0
- package/dist/area/index.js +41 -0
- package/dist/area/index.js.map +1 -0
- package/dist/base/Button.d.ts +170 -0
- package/dist/base/Button.js +537 -0
- package/dist/base/Button.js.map +1 -0
- package/dist/base/Icon.d.ts +38 -0
- package/dist/base/Icon.js +65 -0
- package/dist/base/Icon.js.map +1 -0
- package/dist/capability/PrivacyPrompt.d.ts +1 -0
- package/dist/capability/PrivacyPrompt.js +73 -0
- package/dist/capability/PrivacyPrompt.js.map +1 -0
- package/dist/capability/types.d.ts +92 -0
- package/dist/capability/usePermission.d.ts +23 -0
- package/dist/capability/usePermission.js +52 -0
- package/dist/capability/usePermission.js.map +1 -0
- package/dist/capability/useStorage.d.ts +8 -0
- package/dist/capability/useStorage.js +19 -0
- package/dist/capability/useStorage.js.map +1 -0
- package/dist/capability/web.d.ts +18 -0
- package/dist/capability/web.js +109 -0
- package/dist/capability/web.js.map +1 -0
- package/dist/capacitor/index.d.ts +9 -0
- package/dist/capacitor/index.js +143 -0
- package/dist/capacitor/index.js.map +1 -0
- package/dist/config/ConfigProvider.d.ts +37 -0
- package/dist/config/ConfigProvider.js +64 -0
- package/dist/config/ConfigProvider.js.map +1 -0
- package/dist/config/context.d.ts +16 -0
- package/dist/config/context.js +20 -0
- package/dist/config/context.js.map +1 -0
- package/dist/config/lang.d.ts +85 -0
- package/dist/config/lang.js +138 -0
- package/dist/config/lang.js.map +1 -0
- package/dist/display/Badge.d.ts +39 -0
- package/dist/display/Badge.js +108 -0
- package/dist/display/Badge.js.map +1 -0
- package/dist/display/Cell.d.ts +92 -0
- package/dist/display/Cell.js +204 -0
- package/dist/display/Cell.js.map +1 -0
- package/dist/display/Collapse.d.ts +47 -0
- package/dist/display/Collapse.js +173 -0
- package/dist/display/Collapse.js.map +1 -0
- package/dist/display/Empty.d.ts +42 -0
- package/dist/display/Empty.js +79 -0
- package/dist/display/Empty.js.map +1 -0
- package/dist/display/Grid.d.ts +49 -0
- package/dist/display/Grid.js +136 -0
- package/dist/display/Grid.js.map +1 -0
- package/dist/display/Image.d.ts +70 -0
- package/dist/display/Image.js +138 -0
- package/dist/display/Image.js.map +1 -0
- package/dist/display/IndexBar.d.ts +34 -0
- package/dist/display/IndexBar.js +158 -0
- package/dist/display/IndexBar.js.map +1 -0
- package/dist/display/List.d.ts +34 -0
- package/dist/display/List.js +105 -0
- package/dist/display/List.js.map +1 -0
- package/dist/display/NoticeBar.d.ts +57 -0
- package/dist/display/NoticeBar.js +172 -0
- package/dist/display/NoticeBar.js.map +1 -0
- package/dist/display/Steps.d.ts +59 -0
- package/dist/display/Steps.js +158 -0
- package/dist/display/Steps.js.map +1 -0
- package/dist/display/SwipeCell.d.ts +37 -0
- package/dist/display/SwipeCell.js +147 -0
- package/dist/display/SwipeCell.js.map +1 -0
- package/dist/display/Swiper.d.ts +47 -0
- package/dist/display/Swiper.js +173 -0
- package/dist/display/Swiper.js.map +1 -0
- package/dist/display/Tag.d.ts +99 -0
- package/dist/display/Tag.js +320 -0
- package/dist/display/Tag.js.map +1 -0
- package/dist/feedback/Dialog.d.ts +79 -0
- package/dist/feedback/Dialog.js +186 -0
- package/dist/feedback/Dialog.js.map +1 -0
- package/dist/feedback/FeedbackOutlet.d.ts +2 -0
- package/dist/feedback/FeedbackOutlet.js +31 -0
- package/dist/feedback/FeedbackOutlet.js.map +1 -0
- package/dist/feedback/Loading.d.ts +26 -0
- package/dist/feedback/Loading.js +68 -0
- package/dist/feedback/Loading.js.map +1 -0
- package/dist/feedback/Notify.d.ts +48 -0
- package/dist/feedback/Notify.js +116 -0
- package/dist/feedback/Notify.js.map +1 -0
- package/dist/feedback/PullRefresh.d.ts +58 -0
- package/dist/feedback/PullRefresh.js +178 -0
- package/dist/feedback/PullRefresh.js.map +1 -0
- package/dist/feedback/Skeleton.d.ts +56 -0
- package/dist/feedback/Skeleton.js +99 -0
- package/dist/feedback/Skeleton.js.map +1 -0
- package/dist/feedback/Toast.d.ts +89 -0
- package/dist/feedback/Toast.js +186 -0
- package/dist/feedback/Toast.js.map +1 -0
- package/dist/form/Checkbox.d.ts +52 -0
- package/dist/form/Checkbox.js +107 -0
- package/dist/form/Checkbox.js.map +1 -0
- package/dist/form/Field.d.ts +91 -0
- package/dist/form/Field.js +296 -0
- package/dist/form/Field.js.map +1 -0
- package/dist/form/Form.d.ts +50 -0
- package/dist/form/Form.js +96 -0
- package/dist/form/Form.js.map +1 -0
- package/dist/form/FormItem.d.ts +57 -0
- package/dist/form/FormItem.js +142 -0
- package/dist/form/FormItem.js.map +1 -0
- package/dist/form/Radio.d.ts +41 -0
- package/dist/form/Radio.js +85 -0
- package/dist/form/Radio.js.map +1 -0
- package/dist/form/Rate.d.ts +45 -0
- package/dist/form/Rate.js +162 -0
- package/dist/form/Rate.js.map +1 -0
- package/dist/form/Search.d.ts +50 -0
- package/dist/form/Search.js +152 -0
- package/dist/form/Search.js.map +1 -0
- package/dist/form/Slider.d.ts +43 -0
- package/dist/form/Slider.js +213 -0
- package/dist/form/Slider.js.map +1 -0
- package/dist/form/Stepper.d.ts +61 -0
- package/dist/form/Stepper.js +222 -0
- package/dist/form/Stepper.js.map +1 -0
- package/dist/form/Switch.d.ts +33 -0
- package/dist/form/Switch.js +116 -0
- package/dist/form/Switch.js.map +1 -0
- package/dist/form/Uploader.d.ts +67 -0
- package/dist/form/Uploader.js +207 -0
- package/dist/form/Uploader.js.map +1 -0
- package/dist/form/choice.d.ts +42 -0
- package/dist/form/choice.js +109 -0
- package/dist/form/choice.js.map +1 -0
- package/dist/form/context.d.ts +83 -0
- package/dist/form/context.js +89 -0
- package/dist/form/context.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +59 -0
- package/dist/layout/Divider.d.ts +34 -0
- package/dist/layout/Divider.js +55 -0
- package/dist/layout/Divider.js.map +1 -0
- package/dist/layout/Space.d.ts +66 -0
- package/dist/layout/Space.js +65 -0
- package/dist/layout/Space.js.map +1 -0
- package/dist/navigation/BackTop.d.ts +29 -0
- package/dist/navigation/BackTop.js +89 -0
- package/dist/navigation/BackTop.js.map +1 -0
- package/dist/navigation/NavBar.d.ts +46 -0
- package/dist/navigation/NavBar.js +141 -0
- package/dist/navigation/NavBar.js.map +1 -0
- package/dist/navigation/Sticky.d.ts +19 -0
- package/dist/navigation/Sticky.js +28 -0
- package/dist/navigation/Sticky.js.map +1 -0
- package/dist/navigation/TabBar.d.ts +54 -0
- package/dist/navigation/TabBar.js +138 -0
- package/dist/navigation/TabBar.js.map +1 -0
- package/dist/navigation/Tabs.d.ts +45 -0
- package/dist/navigation/Tabs.js +206 -0
- package/dist/navigation/Tabs.js.map +1 -0
- package/dist/overlay/ActionSheet.d.ts +80 -0
- package/dist/overlay/ActionSheet.js +164 -0
- package/dist/overlay/ActionSheet.js.map +1 -0
- package/dist/overlay/Overlay.d.ts +35 -0
- package/dist/overlay/Overlay.js +85 -0
- package/dist/overlay/Overlay.js.map +1 -0
- package/dist/overlay/Popup.d.ts +76 -0
- package/dist/overlay/Popup.js +191 -0
- package/dist/overlay/Popup.js.map +1 -0
- package/dist/picker/Calendar.d.ts +69 -0
- package/dist/picker/Calendar.js +213 -0
- package/dist/picker/Calendar.js.map +1 -0
- package/dist/picker/Cascader.d.ts +50 -0
- package/dist/picker/Cascader.js +199 -0
- package/dist/picker/Cascader.js.map +1 -0
- package/dist/picker/DatePicker.d.ts +52 -0
- package/dist/picker/DatePicker.js +135 -0
- package/dist/picker/DatePicker.js.map +1 -0
- package/dist/picker/DropdownMenu.d.ts +39 -0
- package/dist/picker/DropdownMenu.js +242 -0
- package/dist/picker/DropdownMenu.js.map +1 -0
- package/dist/picker/Picker.d.ts +67 -0
- package/dist/picker/Picker.js +185 -0
- package/dist/picker/Picker.js.map +1 -0
- package/dist/picker/PickerColumn.d.ts +36 -0
- package/dist/picker/PickerColumn.js +175 -0
- package/dist/picker/PickerColumn.js.map +1 -0
- package/dist/theme/dark.d.ts +121 -0
- package/dist/theme/dark.js +77 -0
- package/dist/theme/dark.js.map +1 -0
- package/dist/theme/divider.d.ts +6 -0
- package/dist/theme/divider.js +18 -0
- package/dist/theme/divider.js.map +1 -0
- package/dist/theme/token-names.d.ts +1389 -0
- package/dist/theme/tokens.d.ts +549 -0
- package/dist/theme/tokens.js +312 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/utils/host.d.ts +97 -0
- package/dist/utils/host.js +98 -0
- package/dist/utils/host.js.map +1 -0
- package/dist/utils/presence.d.ts +11 -0
- package/dist/utils/presence.js +34 -0
- package/dist/utils/presence.js.map +1 -0
- package/dist/utils/props.d.ts +38 -0
- package/dist/utils/props.js +115 -0
- package/dist/utils/props.js.map +1 -0
- package/dist/utils/scales.d.ts +48 -0
- package/package.json +106 -3
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { t } from "../theme/tokens.js";
|
|
2
|
+
import { dataAttrs, hasContent, mergeAttrs } from "../utils/props.js";
|
|
3
|
+
import { Icon } from "../base/Icon.js";
|
|
4
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
5
|
+
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
6
|
+
import { createVNode } from "vue";
|
|
7
|
+
import { Check, X } from "@lucide/icons";
|
|
8
|
+
//#region src/display/Steps.tsx
|
|
9
|
+
/**
|
|
10
|
+
* 步骤条:一件事分几步,标出已完成、进行中、未开始(下单流程、物流进度)。横排放标题;竖排可带说明与时间,常用于物流。
|
|
11
|
+
*
|
|
12
|
+
* <Steps active={1} items={[{ title: '下单' }, { title: '付款' }, { title: '发货' }, { title: '收货' }]} />
|
|
13
|
+
* <Steps direction="vertical" active={0} items={[{ title: '已签收', description: '2026-10-30 09:12' }, …]} />
|
|
14
|
+
*/
|
|
15
|
+
/** 一步的状态 */
|
|
16
|
+
/** 一步 */
|
|
17
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
18
|
+
/** Steps 的样式 */
|
|
19
|
+
var stepsStyles = css.create({
|
|
20
|
+
root: {
|
|
21
|
+
display: "flex",
|
|
22
|
+
paddingBlock: "_md",
|
|
23
|
+
paddingInline: "_sm",
|
|
24
|
+
backgroundColor: "_surface"
|
|
25
|
+
},
|
|
26
|
+
vertical: {
|
|
27
|
+
flexDirection: "column",
|
|
28
|
+
paddingInline: "_lg"
|
|
29
|
+
},
|
|
30
|
+
item: {
|
|
31
|
+
position: "relative",
|
|
32
|
+
flex: "1 1 0",
|
|
33
|
+
minWidth: 0,
|
|
34
|
+
display: "flex",
|
|
35
|
+
flexDirection: "column",
|
|
36
|
+
alignItems: "center",
|
|
37
|
+
gap: "0.4em",
|
|
38
|
+
fontSize: "_xs",
|
|
39
|
+
textAlign: "center",
|
|
40
|
+
color: {
|
|
41
|
+
default: "_textMuted",
|
|
42
|
+
"[data-status=\"process\"]": "_accentText",
|
|
43
|
+
"[data-status=\"done\"]": "_text",
|
|
44
|
+
"[data-status=\"error\"]": "_dangerText"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
itemVertical: {
|
|
48
|
+
flexDirection: "row",
|
|
49
|
+
alignItems: "flex-start",
|
|
50
|
+
gap: "_md",
|
|
51
|
+
paddingBlockEnd: "1.2em",
|
|
52
|
+
textAlign: "start",
|
|
53
|
+
fontSize: "_sm"
|
|
54
|
+
},
|
|
55
|
+
line: {
|
|
56
|
+
position: "absolute",
|
|
57
|
+
insetBlockStart: "0.65em",
|
|
58
|
+
insetInlineStart: "50%",
|
|
59
|
+
width: "100%",
|
|
60
|
+
height: t.borderWidths.hairline,
|
|
61
|
+
backgroundColor: {
|
|
62
|
+
default: "_border",
|
|
63
|
+
"[data-done]": "_accent"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
lineVertical: {
|
|
67
|
+
insetBlockStart: "1.4em",
|
|
68
|
+
insetBlockEnd: "-0.1em",
|
|
69
|
+
insetInlineStart: "0.65em",
|
|
70
|
+
width: t.borderWidths.hairline,
|
|
71
|
+
height: "auto"
|
|
72
|
+
},
|
|
73
|
+
dot: {
|
|
74
|
+
boxSizing: "border-box",
|
|
75
|
+
position: "relative",
|
|
76
|
+
zIndex: 1,
|
|
77
|
+
flex: "none",
|
|
78
|
+
display: "inline-flex",
|
|
79
|
+
alignItems: "center",
|
|
80
|
+
justifyContent: "center",
|
|
81
|
+
width: "1.3em",
|
|
82
|
+
height: "1.3em",
|
|
83
|
+
borderRadius: "_full",
|
|
84
|
+
color: "_accentContrast",
|
|
85
|
+
fontSize: "1em"
|
|
86
|
+
},
|
|
87
|
+
wait: {
|
|
88
|
+
borderWidth: "_xs",
|
|
89
|
+
borderStyle: "solid",
|
|
90
|
+
borderColor: "_borderStrong",
|
|
91
|
+
backgroundColor: "_surface"
|
|
92
|
+
},
|
|
93
|
+
process: {
|
|
94
|
+
backgroundColor: "_accent",
|
|
95
|
+
boxShadow: `0 0 0 0.25em ${t.colors.accentSoft}`
|
|
96
|
+
},
|
|
97
|
+
done: { backgroundColor: "_accent" },
|
|
98
|
+
error: {
|
|
99
|
+
backgroundColor: "_danger",
|
|
100
|
+
color: "_dangerContrast"
|
|
101
|
+
},
|
|
102
|
+
body: {
|
|
103
|
+
display: "flex",
|
|
104
|
+
flexDirection: "column",
|
|
105
|
+
gap: "0.2em",
|
|
106
|
+
minWidth: 0,
|
|
107
|
+
overflowWrap: "anywhere"
|
|
108
|
+
},
|
|
109
|
+
title: { lineHeight: "1.4" },
|
|
110
|
+
description: {
|
|
111
|
+
fontSize: "_xs",
|
|
112
|
+
color: "_textMuted"
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
/** 步骤条(div[role=list] > 各步) */
|
|
116
|
+
var Steps = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
117
|
+
const items = readonlyProp(_props, "items");
|
|
118
|
+
const active = readonlyProp(_props, "active", 0);
|
|
119
|
+
const direction = readonlyProp(_props, "direction", "horizontal");
|
|
120
|
+
const propsItem = readonlyProp(_props, "propsItem");
|
|
121
|
+
const rest = restProps(_props, _ctx, true);
|
|
122
|
+
return () => {
|
|
123
|
+
const vertical = direction.value === "vertical";
|
|
124
|
+
return createVNode("div", mergeAttrs({ sx: [stepsStyles.root, vertical && stepsStyles.vertical] }, rest, { role: "list" }), [items.value.map((item, index) => {
|
|
125
|
+
const status = item.status ?? (index < active.value ? "done" : index === active.value ? "process" : "wait");
|
|
126
|
+
const last = index === items.value.length - 1;
|
|
127
|
+
return createVNode("div", mergeAttrs({ sx: [stepsStyles.item, vertical && stepsStyles.itemVertical] }, propsItem.value, {
|
|
128
|
+
role: "listitem",
|
|
129
|
+
"aria-current": status === "process" ? "step" : void 0,
|
|
130
|
+
...dataAttrs({ "data-status": status })
|
|
131
|
+
}), [
|
|
132
|
+
!last && createVNode("span", mergeAttrs({ sx: [stepsStyles.line, vertical && stepsStyles.lineVertical] }, dataAttrs({ "data-done": status === "done" })), null),
|
|
133
|
+
createVNode("span", mergeAttrs({ sx: [stepsStyles.dot, stepsStyles[status]] }), [status === "done" && createVNode(Icon, {
|
|
134
|
+
"icon": Check,
|
|
135
|
+
"strokeWidth": 3,
|
|
136
|
+
"size": 11
|
|
137
|
+
}, null), status === "error" && createVNode(Icon, {
|
|
138
|
+
"icon": X,
|
|
139
|
+
"strokeWidth": 3,
|
|
140
|
+
"size": 11
|
|
141
|
+
}, null)]),
|
|
142
|
+
createVNode("span", mergeAttrs({ sx: stepsStyles.body }), [createVNode("span", mergeAttrs({ sx: stepsStyles.title }), [item.title]), vertical && hasContent(item.description) && createVNode("span", mergeAttrs({ sx: stepsStyles.description }), [item.description])])
|
|
143
|
+
]);
|
|
144
|
+
})]);
|
|
145
|
+
};
|
|
146
|
+
}, {
|
|
147
|
+
name: "Steps",
|
|
148
|
+
props: [
|
|
149
|
+
"items",
|
|
150
|
+
"active",
|
|
151
|
+
"direction",
|
|
152
|
+
"propsItem"
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
//#endregion
|
|
156
|
+
export { Steps, stepsStyles };
|
|
157
|
+
|
|
158
|
+
//# sourceMappingURL=Steps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Steps.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode"],"sources":["../../src/display/Steps.tsx"],"sourcesContent":["/**\n * 步骤条:一件事分几步,标出已完成、进行中、未开始(下单流程、物流进度)。横排放标题;竖排可带说明与时间,常用于物流。\n *\n * <Steps active={1} items={[{ title: '下单' }, { title: '付款' }, { title: '发货' }, { title: '收货' }]} />\n * <Steps direction=\"vertical\" active={0} items={[{ title: '已签收', description: '2026-10-30 09:12' }, …]} />\n */\nimport { Check, X } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Icon } from '../base/Icon';\nimport { t } from '../theme/tokens';\nimport { dataAttrs, hasContent, mergeAttrs, type PartProps } from '../utils/props';\n\n/** 一步的状态 */\nexport interface StepStatuses {\n /** 已完成:对勾 */\n done: unknown;\n /** 进行中:实心的主色圆点 */\n process: unknown;\n /** 未开始:灰色圆点 */\n wait: unknown;\n /** 出错:叉 */\n error: unknown;\n}\n\n/** 一步 */\nexport interface StepItem {\n /** 标题 */\n title: VNodeChild;\n /** 说明(竖排时显示在标题下面) */\n description?: VNodeChild;\n /** 状态;不给时按 active 算 */\n status?: keyof StepStatuses;\n}\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface StepsProps extends PartProps<'div', 'role'> {\n /** 各步 */\n items: StepItem[];\n /**\n * 当前是第几步(从 0 开始):之前的是已完成,这一步是进行中\n * @default 0\n */\n active?: number;\n /**\n * 方向\n * @default 'horizontal'\n */\n direction?: 'horizontal' | 'vertical';\n /** 每一步的部件属性 */\n propsItem?: PartProps<'div'>;\n}\n\n/** Steps 的样式 */\nexport const stepsStyles = css.create({\n root: { display: 'flex', paddingBlock: '_md', paddingInline: '_sm', backgroundColor: '_surface' },\n vertical: { flexDirection: 'column', paddingInline: '_lg' },\n item: {\n position: 'relative',\n flex: '1 1 0',\n minWidth: 0,\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: '0.4em',\n fontSize: '_xs',\n textAlign: 'center',\n color: {\n default: '_textMuted',\n '[data-status=\"process\"]': '_accentText',\n '[data-status=\"done\"]': '_text',\n '[data-status=\"error\"]': '_dangerText',\n },\n },\n itemVertical: {\n flexDirection: 'row',\n alignItems: 'flex-start',\n gap: '_md',\n paddingBlockEnd: '1.2em',\n textAlign: 'start',\n fontSize: '_sm',\n },\n // 连接线:横排在圆点中线上、从这一步的中心连到下一步的中心;竖排在圆点下面\n line: {\n position: 'absolute',\n insetBlockStart: '0.65em',\n insetInlineStart: '50%',\n width: '100%',\n height: t.borderWidths.hairline,\n backgroundColor: { default: '_border', '[data-done]': '_accent' },\n },\n lineVertical: {\n insetBlockStart: '1.4em',\n insetBlockEnd: '-0.1em',\n insetInlineStart: '0.65em',\n width: t.borderWidths.hairline,\n height: 'auto',\n },\n dot: {\n boxSizing: 'border-box',\n position: 'relative',\n zIndex: 1,\n flex: 'none',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1.3em',\n height: '1.3em',\n borderRadius: '_full',\n color: '_accentContrast',\n fontSize: '1em',\n },\n wait: {\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderStrong',\n backgroundColor: '_surface',\n },\n process: { backgroundColor: '_accent', boxShadow: `0 0 0 0.25em ${t.colors.accentSoft}` },\n done: { backgroundColor: '_accent' },\n error: { backgroundColor: '_danger', color: '_dangerContrast' },\n body: {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.2em',\n minWidth: 0,\n overflowWrap: 'anywhere',\n },\n title: { lineHeight: '1.4' },\n description: { fontSize: '_xs', color: '_textMuted' },\n});\n\n/** 步骤条(div[role=list] > 各步) */\nexport const Steps = _component(\n ({ items, active = 0, direction = 'horizontal', propsItem, ...rest }: StepsProps) =>\n () => {\n const vertical = direction === 'vertical';\n return (\n <div\n {...mergeAttrs<'div'>(\n { sx: [stepsStyles.root, vertical && stepsStyles.vertical] },\n rest,\n {\n role: 'list',\n },\n )}\n >\n {items.map((item, index) => {\n const status =\n item.status ?? (index < active ? 'done' : index === active ? 'process' : 'wait');\n const last = index === items.length - 1;\n return (\n <div\n {...mergeAttrs<'div'>(\n { sx: [stepsStyles.item, vertical && stepsStyles.itemVertical] },\n propsItem,\n {\n role: 'listitem',\n 'aria-current': status === 'process' ? 'step' : undefined,\n ...dataAttrs({ 'data-status': status }),\n },\n )}\n >\n {!last && (\n <span\n {...mergeAttrs<'span'>(\n { sx: [stepsStyles.line, vertical && stepsStyles.lineVertical] },\n dataAttrs({ 'data-done': status === 'done' }),\n )}\n />\n )}\n <span {...mergeAttrs<'span'>({ sx: [stepsStyles.dot, stepsStyles[status]] })}>\n {status === 'done' && <Icon icon={Check} strokeWidth={3} size={11} />}\n {status === 'error' && <Icon icon={X} strokeWidth={3} size={11} />}\n </span>\n <span {...mergeAttrs<'span'>({ sx: stepsStyles.body })}>\n <span {...mergeAttrs<'span'>({ sx: stepsStyles.title })}>{item.title}</span>\n {vertical && hasContent(item.description) && (\n <span {...mergeAttrs<'span'>({ sx: stepsStyles.description })}>\n {item.description}\n </span>\n )}\n </span>\n </div>\n );\n })}\n </div>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type VNodeChild } from 'vue';
|
|
3
|
+
import { type PartProps } from '../utils/props';
|
|
4
|
+
/** 打开的一边 */
|
|
5
|
+
export type SwipeCellSide = 'left' | 'right';
|
|
6
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
7
|
+
export interface SwipeCellProps extends PartProps<'div'> {
|
|
8
|
+
/** 打开的一边,false 是收起;双向绑定写 `_open={x}` */
|
|
9
|
+
_open?: SwipeCellSide | false;
|
|
10
|
+
/** 左边藏着的内容(往右滑露出) */
|
|
11
|
+
left?: VNodeChild;
|
|
12
|
+
/** 右边藏着的内容(往左滑露出) */
|
|
13
|
+
right?: VNodeChild;
|
|
14
|
+
/** 不能滑 */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** 打开或收起 */
|
|
17
|
+
onOpenChange?: (open: SwipeCellSide | false) => void;
|
|
18
|
+
/** 内容 */
|
|
19
|
+
children?: VNodeChild;
|
|
20
|
+
/** 滑动层(内容与两边)的部件属性 */
|
|
21
|
+
propsTrack?: PartProps<'div'>;
|
|
22
|
+
/** 左边的部件属性 */
|
|
23
|
+
propsLeft?: PartProps<'div'>;
|
|
24
|
+
/** 右边的部件属性 */
|
|
25
|
+
propsRight?: PartProps<'div'>;
|
|
26
|
+
}
|
|
27
|
+
/** SwipeCell 的样式:root、track、dragging、side、left、right */
|
|
28
|
+
export declare const swipeCellStyles: Readonly<{
|
|
29
|
+
dragging: css.CompiledStyles;
|
|
30
|
+
left: css.CompiledStyles;
|
|
31
|
+
right: css.CompiledStyles;
|
|
32
|
+
root: css.CompiledStyles;
|
|
33
|
+
side: css.CompiledStyles;
|
|
34
|
+
track: css.CompiledStyles;
|
|
35
|
+
}>;
|
|
36
|
+
/** 滑动单元格(div > 滑动层:左边 + 内容 + 右边) */
|
|
37
|
+
export declare const SwipeCell: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<SwipeCellProps>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { dataAttrs, hasContent, mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { useHost } from "../utils/host.js";
|
|
3
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
4
|
+
import { _component, bindable, childrenProp, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
+
import { createVNode, onMounted, ref } from "vue";
|
|
6
|
+
//#region src/display/SwipeCell.tsx
|
|
7
|
+
/** 打开的一边 */
|
|
8
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
9
|
+
/** SwipeCell 的样式:root、track、dragging、side、left、right */
|
|
10
|
+
var swipeCellStyles = css.create({
|
|
11
|
+
root: {
|
|
12
|
+
position: "relative",
|
|
13
|
+
overflow: "hidden",
|
|
14
|
+
touchAction: "pan-y",
|
|
15
|
+
userSelect: "none"
|
|
16
|
+
},
|
|
17
|
+
track: {
|
|
18
|
+
position: "relative",
|
|
19
|
+
transitionProperty: "transform",
|
|
20
|
+
transitionDuration: "_md",
|
|
21
|
+
transitionTimingFunction: "_standard"
|
|
22
|
+
},
|
|
23
|
+
dragging: { transitionDuration: "0s" },
|
|
24
|
+
side: {
|
|
25
|
+
position: "absolute",
|
|
26
|
+
insetBlock: 0,
|
|
27
|
+
display: "flex",
|
|
28
|
+
alignItems: "stretch"
|
|
29
|
+
},
|
|
30
|
+
left: { insetInlineEnd: "100%" },
|
|
31
|
+
right: { insetInlineStart: "100%" }
|
|
32
|
+
});
|
|
33
|
+
var LOCK_DISTANCE = 8;
|
|
34
|
+
/** 滑动单元格(div > 滑动层:左边 + 内容 + 右边) */
|
|
35
|
+
var SwipeCell = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
36
|
+
const _cache = [];
|
|
37
|
+
const open = bindable(_props, "_open", () => false, _ctx.emit);
|
|
38
|
+
const left = readonlyProp(_props, "left");
|
|
39
|
+
const right = readonlyProp(_props, "right");
|
|
40
|
+
const disabled = readonlyProp(_props, "disabled", false);
|
|
41
|
+
const onOpenChange = readonlyProp(_props, "onOpenChange");
|
|
42
|
+
const children = childrenProp(_props);
|
|
43
|
+
const propsTrack = readonlyProp(_props, "propsTrack");
|
|
44
|
+
const propsLeft = readonlyProp(_props, "propsLeft");
|
|
45
|
+
const propsRight = readonlyProp(_props, "propsRight");
|
|
46
|
+
const rest = restProps(_props, _ctx, false);
|
|
47
|
+
const host = useHost();
|
|
48
|
+
const widths = ref({
|
|
49
|
+
left: 0,
|
|
50
|
+
right: 0
|
|
51
|
+
});
|
|
52
|
+
let leftNode = null;
|
|
53
|
+
let rightNode = null;
|
|
54
|
+
const measure = async () => {
|
|
55
|
+
const [l, r] = await Promise.all([host.measure(leftNode), host.measure(rightNode)]);
|
|
56
|
+
widths.value.left = l?.width ?? 0;
|
|
57
|
+
widths.value.right = r?.width ?? 0;
|
|
58
|
+
};
|
|
59
|
+
onMounted(measure);
|
|
60
|
+
let drag = ref(null);
|
|
61
|
+
let horizontal = ref(false);
|
|
62
|
+
let start = {
|
|
63
|
+
x: 0,
|
|
64
|
+
y: 0,
|
|
65
|
+
offset: 0
|
|
66
|
+
};
|
|
67
|
+
let direction = null;
|
|
68
|
+
const restingOffset = () => open.value === "left" ? widths.value.left : open.value === "right" ? -widths.value.right : 0;
|
|
69
|
+
const setOpen = (next) => {
|
|
70
|
+
if (next === open.value) return;
|
|
71
|
+
open.value = next;
|
|
72
|
+
onOpenChange.value?.(next);
|
|
73
|
+
};
|
|
74
|
+
const onTouchstart = (event) => {
|
|
75
|
+
if (disabled.value) return;
|
|
76
|
+
measure();
|
|
77
|
+
const touch = event.touches[0];
|
|
78
|
+
start = {
|
|
79
|
+
x: touch.clientX,
|
|
80
|
+
y: touch.clientY,
|
|
81
|
+
offset: restingOffset()
|
|
82
|
+
};
|
|
83
|
+
direction = null;
|
|
84
|
+
};
|
|
85
|
+
const onTouchmove = (event) => {
|
|
86
|
+
if (disabled.value) return;
|
|
87
|
+
const touch = event.touches[0];
|
|
88
|
+
const dx = touch.clientX - start.x;
|
|
89
|
+
const dy = touch.clientY - start.y;
|
|
90
|
+
if (!direction) {
|
|
91
|
+
if (Math.abs(dx) < LOCK_DISTANCE && Math.abs(dy) < LOCK_DISTANCE) return;
|
|
92
|
+
direction = Math.abs(dx) > Math.abs(dy) ? "h" : "v";
|
|
93
|
+
horizontal.value = direction === "h";
|
|
94
|
+
}
|
|
95
|
+
if (direction !== "h") return;
|
|
96
|
+
drag.value = Math.min(widths.value.left, Math.max(-widths.value.right, start.offset + dx));
|
|
97
|
+
};
|
|
98
|
+
const onTouchend = () => {
|
|
99
|
+
horizontal.value = false;
|
|
100
|
+
if (drag.value === null) return;
|
|
101
|
+
const offset = drag.value;
|
|
102
|
+
drag.value = null;
|
|
103
|
+
const threshold = (width) => open.value ? width * .7 : width * .3;
|
|
104
|
+
if (offset > 0 && widths.value.left && offset > threshold(widths.value.left)) setOpen("left");
|
|
105
|
+
else if (offset < 0 && widths.value.right && -offset > threshold(widths.value.right)) setOpen("right");
|
|
106
|
+
else setOpen(false);
|
|
107
|
+
};
|
|
108
|
+
const onClick = () => open.value && setOpen(false);
|
|
109
|
+
return () => {
|
|
110
|
+
const offset = drag.value ?? restingOffset();
|
|
111
|
+
return createVNode("div", mergeAttrs({ sx: swipeCellStyles.root }, rest, {
|
|
112
|
+
onTouchstart,
|
|
113
|
+
onTouchmove,
|
|
114
|
+
onTouchend,
|
|
115
|
+
onTouchcancel: onTouchend,
|
|
116
|
+
onClick,
|
|
117
|
+
...dataAttrs({
|
|
118
|
+
"data-touch-move": horizontal.value ? "catch" : true,
|
|
119
|
+
"data-open": open.value || void 0
|
|
120
|
+
})
|
|
121
|
+
}), [createVNode("div", mergeAttrs({
|
|
122
|
+
sx: [swipeCellStyles.track, drag.value !== null && swipeCellStyles.dragging],
|
|
123
|
+
style: { transform: `translateX(${offset}px)` }
|
|
124
|
+
}, propsTrack.value), [
|
|
125
|
+
hasContent(left.value) && createVNode("div", mergeJsxProps(mergeAttrs({ sx: [swipeCellStyles.side, swipeCellStyles.left] }, propsLeft.value), { "ref": _cache[0] || (_cache[0] = (node) => leftNode = node) }), [left.value]),
|
|
126
|
+
children.value,
|
|
127
|
+
hasContent(right.value) && createVNode("div", mergeJsxProps(mergeAttrs({ sx: [swipeCellStyles.side, swipeCellStyles.right] }, propsRight.value), { "ref": _cache[1] || (_cache[1] = (node) => rightNode = node) }), [right.value])
|
|
128
|
+
])]);
|
|
129
|
+
};
|
|
130
|
+
}, {
|
|
131
|
+
name: "SwipeCell",
|
|
132
|
+
props: [
|
|
133
|
+
"_open",
|
|
134
|
+
"onUpdate:_open",
|
|
135
|
+
"left",
|
|
136
|
+
"right",
|
|
137
|
+
"disabled",
|
|
138
|
+
"onOpenChange",
|
|
139
|
+
"propsTrack",
|
|
140
|
+
"propsLeft",
|
|
141
|
+
"propsRight"
|
|
142
|
+
]
|
|
143
|
+
});
|
|
144
|
+
//#endregion
|
|
145
|
+
export { SwipeCell, swipeCellStyles };
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=SwipeCell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SwipeCell.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_ref","_createVNode","_mergeProps"],"sources":["../../src/display/SwipeCell.tsx"],"sourcesContent":["/**\n * 滑动单元格:内容往左(右)滑,露出藏在另一边的操作按钮(删除、收藏……)。列表里一行一个。\n *\n * <SwipeCell right={<Button tone=\"danger\" radius=\"none\" onClick={remove}>删除</Button>}>\n * <Cell title=\"订单 1024\" value=\"待付款\" />\n * </SwipeCell>\n * <SwipeCell _open={side} left={…} right={…} /> 双向绑定:'left' | 'right' | false\n *\n * 横滑才接管手势,竖滑照常滚动页面(H5 靠 touch-action,小程序在横滑期间把 touchmove 换成 catch)。\n * 打开时点任意位置(包括露出的按钮,在它自己的点击之后)收起。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { onMounted, type VNodeChild } from 'vue';\nimport { useHost } from '../utils/host';\nimport { dataAttrs, hasContent, mergeAttrs, type PartProps } from '../utils/props';\n\n/** 打开的一边 */\nexport type SwipeCellSide = 'left' | 'right';\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface SwipeCellProps extends PartProps<'div'> {\n /** 打开的一边,false 是收起;双向绑定写 `_open={x}` */\n _open?: SwipeCellSide | false;\n /** 左边藏着的内容(往右滑露出) */\n left?: VNodeChild;\n /** 右边藏着的内容(往左滑露出) */\n right?: VNodeChild;\n /** 不能滑 */\n disabled?: boolean;\n /** 打开或收起 */\n onOpenChange?: (open: SwipeCellSide | false) => void;\n /** 内容 */\n children?: VNodeChild;\n /** 滑动层(内容与两边)的部件属性 */\n propsTrack?: PartProps<'div'>;\n /** 左边的部件属性 */\n propsLeft?: PartProps<'div'>;\n /** 右边的部件属性 */\n propsRight?: PartProps<'div'>;\n}\n\n/** SwipeCell 的样式:root、track、dragging、side、left、right */\nexport const swipeCellStyles = css.create({\n root: {\n position: 'relative',\n overflow: 'hidden',\n // 竖滑交给浏览器滚动页面,横滑交给组件\n touchAction: 'pan-y',\n userSelect: 'none',\n },\n track: {\n position: 'relative',\n transitionProperty: 'transform',\n transitionDuration: '_md',\n transitionTimingFunction: '_standard',\n },\n // 拖动时跟手,不要过渡\n dragging: { transitionDuration: '0s' },\n side: { position: 'absolute', insetBlock: 0, display: 'flex', alignItems: 'stretch' },\n left: { insetInlineEnd: '100%' },\n right: { insetInlineStart: '100%' },\n});\n\n// 横竖方向的判定:移动超过这个距离(px)才判定\nconst LOCK_DISTANCE = 8;\n\n/** 滑动单元格(div > 滑动层:左边 + 内容 + 右边) */\nexport const SwipeCell = _component(\n ({\n _open: open = _bindable<SwipeCellSide | false>(false),\n left,\n right,\n disabled = false,\n onOpenChange,\n children,\n propsTrack,\n propsLeft,\n propsRight,\n ...rest\n }: SwipeCellProps) => {\n const host = useHost();\n // 两边的宽度:挂上时量一次,每次按下时再量(内容可能变了;小程序里只能异步量,按下到开始滑动之间来得及)\n const widths = _state({ left: 0, right: 0 });\n let leftNode: unknown = null;\n let rightNode: unknown = null;\n const measure = async () => {\n const [l, r] = await Promise.all([host.measure(leftNode), host.measure(rightNode)]);\n widths.left = l?.width ?? 0;\n widths.right = r?.width ?? 0;\n };\n onMounted(measure);\n\n // 拖动中的偏移;不拖动时由 open 决定\n let drag = _state<number | null>(null);\n let horizontal = _state(false);\n let start = { x: 0, y: 0, offset: 0 };\n let direction: 'h' | 'v' | null = null;\n\n const restingOffset = () =>\n open === 'left' ? widths.left : open === 'right' ? -widths.right : 0;\n const setOpen = (next: SwipeCellSide | false) => {\n if (next === open) return;\n open = next;\n onOpenChange?.(next);\n };\n\n const onTouchstart = (event: TouchEvent) => {\n if (disabled) return;\n void measure();\n const touch = event.touches[0]!;\n start = { x: touch.clientX, y: touch.clientY, offset: restingOffset() };\n direction = null;\n };\n const onTouchmove = (event: TouchEvent) => {\n if (disabled) return;\n const touch = event.touches[0]!;\n const dx = touch.clientX - start.x;\n const dy = touch.clientY - start.y;\n if (!direction) {\n if (Math.abs(dx) < LOCK_DISTANCE && Math.abs(dy) < LOCK_DISTANCE) return;\n direction = Math.abs(dx) > Math.abs(dy) ? 'h' : 'v';\n horizontal = direction === 'h';\n }\n if (direction !== 'h') return;\n drag = Math.min(widths.left, Math.max(-widths.right, start.offset + dx));\n };\n const onTouchend = () => {\n horizontal = false;\n if (drag === null) return;\n const offset = drag;\n drag = null;\n // 从收起往外拉过三成就打开;从打开往回推过三成就收起\n const threshold = (width: number) => (open ? width * 0.7 : width * 0.3);\n if (offset > 0 && widths.left && offset > threshold(widths.left)) setOpen('left');\n else if (offset < 0 && widths.right && -offset > threshold(widths.right)) setOpen('right');\n else setOpen(false);\n };\n // 滑动不会触发点击(浏览器与小程序都是),所以这里的点击都是点按:打开时收起\n const onClick = () => open && setOpen(false);\n\n return () => {\n const offset = drag ?? restingOffset();\n return (\n <div\n {...mergeAttrs<'div'>({ sx: swipeCellStyles.root }, rest, {\n onTouchstart,\n onTouchmove,\n onTouchend,\n onTouchcancel: onTouchend,\n onClick,\n ...dataAttrs({\n // 小程序:收 touchmove;横滑期间 catch,页面不跟着滚\n 'data-touch-move': horizontal ? 'catch' : true,\n 'data-open': open || undefined,\n }),\n })}\n >\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [swipeCellStyles.track, drag !== null && swipeCellStyles.dragging],\n style: { transform: `translateX(${offset}px)` },\n },\n propsTrack,\n )}\n >\n {hasContent(left) && (\n <div\n {...mergeAttrs<'div'>(\n { sx: [swipeCellStyles.side, swipeCellStyles.left] },\n propsLeft,\n )}\n ref={(node: unknown) => (leftNode = node)}\n >\n {left}\n </div>\n )}\n {children}\n {hasContent(right) && (\n <div\n {...mergeAttrs<'div'>(\n { sx: [swipeCellStyles.side, swipeCellStyles.right] },\n propsRight,\n )}\n ref={(node: unknown) => (rightNode = node)}\n >\n {right}\n </div>\n )}\n </div>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type VNodeChild } from 'vue';
|
|
3
|
+
import { type PartProps } from '../utils/props';
|
|
4
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
5
|
+
export interface SwiperProps extends Omit<PartProps<'div'>, 'onChange'> {
|
|
6
|
+
/** 当前是第几张(从 0 开始);双向绑定写 `_value={x}` */
|
|
7
|
+
_value?: number;
|
|
8
|
+
/** 自动切换的间隔(ms);0 不自动切换 */
|
|
9
|
+
autoplay?: number;
|
|
10
|
+
/**
|
|
11
|
+
* 首尾相连:最后一张往后是第一张
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
loop?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* 显示指示的圆点
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
indicators?: boolean;
|
|
20
|
+
/** 高度(CSS 长度);不给时随内容 */
|
|
21
|
+
height?: string;
|
|
22
|
+
/** 换了一张 */
|
|
23
|
+
onChange?: (index: number) => void;
|
|
24
|
+
/** 各个 SwiperItem */
|
|
25
|
+
children?: VNodeChild;
|
|
26
|
+
/** 指示圆点那一排的部件属性 */
|
|
27
|
+
propsIndicators?: PartProps<'div'>;
|
|
28
|
+
}
|
|
29
|
+
/** Swiper 的样式 */
|
|
30
|
+
export declare const swiperStyles: Readonly<{
|
|
31
|
+
active: css.CompiledStyles;
|
|
32
|
+
dot: css.CompiledStyles;
|
|
33
|
+
dragging: css.CompiledStyles;
|
|
34
|
+
indicators: css.CompiledStyles;
|
|
35
|
+
item: css.CompiledStyles;
|
|
36
|
+
root: css.CompiledStyles;
|
|
37
|
+
track: css.CompiledStyles;
|
|
38
|
+
}>;
|
|
39
|
+
/** 轮播(div > 滑动层:各项 + 指示圆点) */
|
|
40
|
+
export declare const Swiper: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<SwiperProps>;
|
|
41
|
+
/** 未声明的属性落到这一项(div)上 */
|
|
42
|
+
export interface SwiperItemProps extends PartProps<'div'> {
|
|
43
|
+
/** 内容 */
|
|
44
|
+
children?: VNodeChild;
|
|
45
|
+
}
|
|
46
|
+
/** 轮播的一项(div) */
|
|
47
|
+
export declare const SwiperItem: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<SwiperItemProps>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { dataAttrs, mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { useHost } from "../utils/host.js";
|
|
3
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
4
|
+
import { _component, bindable, childrenProp, createContext, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
+
import { createVNode, onBeforeUnmount, onMounted, ref, shallowReactive, watch } from "vue";
|
|
6
|
+
//#region src/display/Swiper.tsx
|
|
7
|
+
/** 轮播提供给各项的 */
|
|
8
|
+
var SwiperContext = createContext("Swiper");
|
|
9
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
10
|
+
/** Swiper 的样式 */
|
|
11
|
+
var swiperStyles = css.create({
|
|
12
|
+
root: {
|
|
13
|
+
position: "relative",
|
|
14
|
+
overflow: "hidden",
|
|
15
|
+
touchAction: "pan-y",
|
|
16
|
+
userSelect: "none"
|
|
17
|
+
},
|
|
18
|
+
track: {
|
|
19
|
+
display: "flex",
|
|
20
|
+
height: "100%",
|
|
21
|
+
transitionProperty: "transform",
|
|
22
|
+
transitionDuration: "_lg",
|
|
23
|
+
transitionTimingFunction: "_standard"
|
|
24
|
+
},
|
|
25
|
+
dragging: { transitionDuration: "0s" },
|
|
26
|
+
item: {
|
|
27
|
+
flex: "none",
|
|
28
|
+
width: "100%",
|
|
29
|
+
height: "100%"
|
|
30
|
+
},
|
|
31
|
+
indicators: {
|
|
32
|
+
position: "absolute",
|
|
33
|
+
insetInline: 0,
|
|
34
|
+
insetBlockEnd: "0.6em",
|
|
35
|
+
display: "flex",
|
|
36
|
+
justifyContent: "center",
|
|
37
|
+
gap: "0.35em",
|
|
38
|
+
pointerEvents: "none"
|
|
39
|
+
},
|
|
40
|
+
dot: {
|
|
41
|
+
width: "0.4em",
|
|
42
|
+
height: "0.4em",
|
|
43
|
+
borderRadius: "_full",
|
|
44
|
+
backgroundColor: "rgba(255, 255, 255, 0.55)",
|
|
45
|
+
transitionProperty: "width, background-color",
|
|
46
|
+
transitionDuration: "_md"
|
|
47
|
+
},
|
|
48
|
+
active: {
|
|
49
|
+
width: "1em",
|
|
50
|
+
backgroundColor: "#fff"
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
var LOCK_DISTANCE = 6;
|
|
54
|
+
var SWITCH_RATIO = .2;
|
|
55
|
+
/** 轮播(div > 滑动层:各项 + 指示圆点) */
|
|
56
|
+
var Swiper = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
57
|
+
const _cache = [];
|
|
58
|
+
const value = bindable(_props, "_value", () => 0, _ctx.emit);
|
|
59
|
+
const autoplay = readonlyProp(_props, "autoplay", 0);
|
|
60
|
+
const loop = readonlyProp(_props, "loop", true);
|
|
61
|
+
const indicators = readonlyProp(_props, "indicators", true);
|
|
62
|
+
const height = readonlyProp(_props, "height");
|
|
63
|
+
const onChange = readonlyProp(_props, "onChange");
|
|
64
|
+
const children = childrenProp(_props);
|
|
65
|
+
const propsIndicators = readonlyProp(_props, "propsIndicators");
|
|
66
|
+
const rest = restProps(_props, _ctx, false);
|
|
67
|
+
const host = useHost();
|
|
68
|
+
const items = shallowReactive([]);
|
|
69
|
+
SwiperContext.provide({ register(item) {
|
|
70
|
+
items.push(item);
|
|
71
|
+
return () => items.splice(items.indexOf(item), 1);
|
|
72
|
+
} });
|
|
73
|
+
const go = (index) => {
|
|
74
|
+
const count = items.length;
|
|
75
|
+
if (!count) return;
|
|
76
|
+
const next = loop.value ? (index + count) % count : Math.min(count - 1, Math.max(0, index));
|
|
77
|
+
if (next === value.value) return;
|
|
78
|
+
value.value = next;
|
|
79
|
+
onChange.value?.(next);
|
|
80
|
+
};
|
|
81
|
+
let timer;
|
|
82
|
+
const startAutoplay = () => {
|
|
83
|
+
clearInterval(timer);
|
|
84
|
+
if (autoplay.value > 0) timer = setInterval(() => go(value.value + 1), autoplay.value);
|
|
85
|
+
};
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
startAutoplay();
|
|
88
|
+
measure();
|
|
89
|
+
});
|
|
90
|
+
watch(() => autoplay.value, startAutoplay);
|
|
91
|
+
onBeforeUnmount(() => clearInterval(timer));
|
|
92
|
+
let root = null;
|
|
93
|
+
let width = 0;
|
|
94
|
+
const measure = async () => {
|
|
95
|
+
width = (await host.measure(root))?.width ?? width;
|
|
96
|
+
};
|
|
97
|
+
let drag = ref(null);
|
|
98
|
+
let horizontal = ref(false);
|
|
99
|
+
let start = {
|
|
100
|
+
x: 0,
|
|
101
|
+
y: 0
|
|
102
|
+
};
|
|
103
|
+
let direction = null;
|
|
104
|
+
const onTouchstart = (event) => {
|
|
105
|
+
const touch = event.touches[0];
|
|
106
|
+
start = {
|
|
107
|
+
x: touch.clientX,
|
|
108
|
+
y: touch.clientY
|
|
109
|
+
};
|
|
110
|
+
direction = null;
|
|
111
|
+
clearInterval(timer);
|
|
112
|
+
measure();
|
|
113
|
+
};
|
|
114
|
+
const onTouchmove = (event) => {
|
|
115
|
+
const touch = event.touches[0];
|
|
116
|
+
const dx = touch.clientX - start.x;
|
|
117
|
+
const dy = touch.clientY - start.y;
|
|
118
|
+
if (!direction) {
|
|
119
|
+
if (Math.abs(dx) < LOCK_DISTANCE && Math.abs(dy) < LOCK_DISTANCE) return;
|
|
120
|
+
direction = Math.abs(dx) > Math.abs(dy) ? "h" : "v";
|
|
121
|
+
horizontal.value = direction === "h";
|
|
122
|
+
}
|
|
123
|
+
if (direction === "h") drag.value = dx;
|
|
124
|
+
};
|
|
125
|
+
const onTouchend = () => {
|
|
126
|
+
const dx = drag.value ?? 0;
|
|
127
|
+
drag.value = null;
|
|
128
|
+
horizontal.value = false;
|
|
129
|
+
if (width && Math.abs(dx) > width * SWITCH_RATIO) go(value.value + (dx < 0 ? 1 : -1));
|
|
130
|
+
startAutoplay();
|
|
131
|
+
};
|
|
132
|
+
return () => {
|
|
133
|
+
const offset = drag.value === null ? "" : ` + ${drag.value}px`;
|
|
134
|
+
return createVNode("div", mergeJsxProps(mergeAttrs({
|
|
135
|
+
sx: swiperStyles.root,
|
|
136
|
+
style: height.value ? { height: height.value } : void 0
|
|
137
|
+
}, rest, {
|
|
138
|
+
onTouchstart,
|
|
139
|
+
onTouchmove,
|
|
140
|
+
onTouchend,
|
|
141
|
+
onTouchcancel: onTouchend,
|
|
142
|
+
...dataAttrs({ "data-touch-move": horizontal.value ? "catch" : true })
|
|
143
|
+
}), { "ref": _cache[0] || (_cache[0] = (node) => root = node) }), [createVNode("div", mergeAttrs({
|
|
144
|
+
sx: [swiperStyles.track, drag.value !== null && swiperStyles.dragging],
|
|
145
|
+
style: { transform: `translateX(calc(${-value.value * 100}%${offset}))` }
|
|
146
|
+
}), [children.value]), indicators.value && items.length > 1 && createVNode("div", mergeJsxProps(mergeAttrs({ sx: swiperStyles.indicators }, propsIndicators.value), { "aria-hidden": "true" }), [items.map((_, index) => createVNode("span", mergeAttrs({ sx: [swiperStyles.dot, index === value.value && swiperStyles.active] }), null))])]);
|
|
147
|
+
};
|
|
148
|
+
}, {
|
|
149
|
+
name: "Swiper",
|
|
150
|
+
props: [
|
|
151
|
+
"_value",
|
|
152
|
+
"onUpdate:_value",
|
|
153
|
+
"autoplay",
|
|
154
|
+
"loop",
|
|
155
|
+
"indicators",
|
|
156
|
+
"height",
|
|
157
|
+
"onChange",
|
|
158
|
+
"propsIndicators"
|
|
159
|
+
]
|
|
160
|
+
});
|
|
161
|
+
/** 未声明的属性落到这一项(div)上 */
|
|
162
|
+
/** 轮播的一项(div) */
|
|
163
|
+
var SwiperItem = /*#__PURE__*/ _component((_props2, _ctx2) => {
|
|
164
|
+
const children = childrenProp(_props2);
|
|
165
|
+
const rest = restProps(_props2, _ctx2, false);
|
|
166
|
+
const swiper = SwiperContext.inject();
|
|
167
|
+
onBeforeUnmount(swiper.register({}));
|
|
168
|
+
return () => createVNode("div", mergeAttrs({ sx: swiperStyles.item }, rest), [children.value]);
|
|
169
|
+
}, { name: "SwiperItem" });
|
|
170
|
+
//#endregion
|
|
171
|
+
export { Swiper, SwiperItem, swiperStyles };
|
|
172
|
+
|
|
173
|
+
//# sourceMappingURL=Swiper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Swiper.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_ref","_createVNode","_mergeProps"],"sources":["../../src/display/Swiper.tsx"],"sourcesContent":["/**\n * 轮播:一组横排的卡片(首页的广告图、商品的多张图),左右滑或自动切换,下面一排圆点指示当前是第几张。\n *\n * <Swiper autoplay={3000} height=\"10em\">\n * <SwiperItem><Image src={banner1} width=\"100%\" height=\"100%\" fit=\"cover\" /></SwiperItem>\n * <SwiperItem>…</SwiperItem>\n * </Swiper>\n *\n * 横滑才接管手势(竖滑照常滚动页面);loop 时最后一张再往后回到第一张(倒着滑回去,不复制首尾)。\n */\nimport { _component, createContext } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { onBeforeUnmount, onMounted, shallowReactive, watch, type VNodeChild } from 'vue';\nimport { useHost } from '../utils/host';\nimport { dataAttrs, mergeAttrs, type PartProps } from '../utils/props';\n\n/** 轮播提供给各项的 */\ninterface SwiperState {\n register(item: object): () => void;\n}\n\nconst SwiperContext = createContext<SwiperState>('Swiper');\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface SwiperProps extends Omit<PartProps<'div'>, 'onChange'> {\n /** 当前是第几张(从 0 开始);双向绑定写 `_value={x}` */\n _value?: number;\n /** 自动切换的间隔(ms);0 不自动切换 */\n autoplay?: number;\n /**\n * 首尾相连:最后一张往后是第一张\n * @default true\n */\n loop?: boolean;\n /**\n * 显示指示的圆点\n * @default true\n */\n indicators?: boolean;\n /** 高度(CSS 长度);不给时随内容 */\n height?: string;\n /** 换了一张 */\n onChange?: (index: number) => void;\n /** 各个 SwiperItem */\n children?: VNodeChild;\n /** 指示圆点那一排的部件属性 */\n propsIndicators?: PartProps<'div'>;\n}\n\n/** Swiper 的样式 */\nexport const swiperStyles = css.create({\n root: { position: 'relative', overflow: 'hidden', touchAction: 'pan-y', userSelect: 'none' },\n track: {\n display: 'flex',\n height: '100%',\n transitionProperty: 'transform',\n transitionDuration: '_lg',\n transitionTimingFunction: '_standard',\n },\n dragging: { transitionDuration: '0s' },\n item: { flex: 'none', width: '100%', height: '100%' },\n indicators: {\n position: 'absolute',\n insetInline: 0,\n insetBlockEnd: '0.6em',\n display: 'flex',\n justifyContent: 'center',\n gap: '0.35em',\n pointerEvents: 'none',\n },\n dot: {\n width: '0.4em',\n height: '0.4em',\n borderRadius: '_full',\n // 圆点画在图片上:两套主题里都是白的\n backgroundColor: 'rgba(255, 255, 255, 0.55)',\n transitionProperty: 'width, background-color',\n transitionDuration: '_md',\n },\n active: { width: '1em', backgroundColor: '#fff' },\n});\n\n// 判定横竖的移动距离(px);松手时滑过宽度的这个比例就换一张\nconst LOCK_DISTANCE = 6;\nconst SWITCH_RATIO = 0.2;\n\n/** 轮播(div > 滑动层:各项 + 指示圆点) */\nexport const Swiper = _component(\n ({\n _value: value = _bindable(0),\n autoplay = 0,\n loop = true,\n indicators = true,\n height,\n onChange,\n children,\n propsIndicators,\n ...rest\n }: SwiperProps) => {\n const host = useHost();\n const items = shallowReactive<object[]>([]);\n SwiperContext.provide({\n register(item) {\n items.push(item);\n return () => items.splice(items.indexOf(item), 1);\n },\n });\n\n const go = (index: number) => {\n const count = items.length;\n if (!count) return;\n const next = loop ? (index + count) % count : Math.min(count - 1, Math.max(0, index));\n if (next === value) return;\n value = next;\n onChange?.(next);\n };\n\n // 自动切换:拖动时停下,松手后重新计时\n let timer: ReturnType<typeof setInterval> | undefined;\n const startAutoplay = () => {\n clearInterval(timer);\n if (autoplay > 0) timer = setInterval(() => go(value + 1), autoplay);\n };\n onMounted(() => {\n startAutoplay();\n void measure();\n });\n watch(() => autoplay, startAutoplay);\n onBeforeUnmount(() => clearInterval(timer));\n\n // 拖动:挂上时量一次宽度,每次按下时再量(转屏、布局变了)\n let root: unknown = null;\n let width = 0;\n const measure = async () => {\n width = (await host.measure(root))?.width ?? width;\n };\n let drag = _state<number | null>(null);\n let horizontal = _state(false);\n let start = { x: 0, y: 0 };\n let direction: 'h' | 'v' | null = null;\n const onTouchstart = (event: TouchEvent) => {\n const touch = event.touches[0]!;\n start = { x: touch.clientX, y: touch.clientY };\n direction = null;\n clearInterval(timer);\n void measure();\n };\n const onTouchmove = (event: TouchEvent) => {\n const touch = event.touches[0]!;\n const dx = touch.clientX - start.x;\n const dy = touch.clientY - start.y;\n if (!direction) {\n if (Math.abs(dx) < LOCK_DISTANCE && Math.abs(dy) < LOCK_DISTANCE) return;\n direction = Math.abs(dx) > Math.abs(dy) ? 'h' : 'v';\n horizontal = direction === 'h';\n }\n if (direction === 'h') drag = dx;\n };\n const onTouchend = () => {\n const dx = drag ?? 0;\n drag = null;\n horizontal = false;\n if (width && Math.abs(dx) > width * SWITCH_RATIO) go(value + (dx < 0 ? 1 : -1));\n startAutoplay();\n };\n\n return () => {\n const offset = drag === null ? '' : ` + ${drag}px`;\n return (\n <div\n {...mergeAttrs<'div'>(\n { sx: swiperStyles.root, style: height ? { height } : undefined },\n rest,\n {\n onTouchstart,\n onTouchmove,\n onTouchend,\n onTouchcancel: onTouchend,\n ...dataAttrs({ 'data-touch-move': horizontal ? 'catch' : true }),\n },\n )}\n ref={(node: unknown) => (root = node)}\n >\n <div\n {...mergeAttrs<'div'>({\n sx: [swiperStyles.track, drag !== null && swiperStyles.dragging],\n style: { transform: `translateX(calc(${-value * 100}%${offset}))` },\n })}\n >\n {children}\n </div>\n {indicators && items.length > 1 && (\n <div\n {...mergeAttrs<'div'>({ sx: swiperStyles.indicators }, propsIndicators)}\n aria-hidden=\"true\"\n >\n {items.map((_, index) => (\n <span\n {...mergeAttrs<'span'>({\n sx: [swiperStyles.dot, index === value && swiperStyles.active],\n })}\n />\n ))}\n </div>\n )}\n </div>\n );\n };\n },\n);\n\n/** 未声明的属性落到这一项(div)上 */\nexport interface SwiperItemProps extends PartProps<'div'> {\n /** 内容 */\n children?: VNodeChild;\n}\n\n/** 轮播的一项(div) */\nexport const SwiperItem = _component(({ children, ...rest }: SwiperItemProps) => {\n const swiper = SwiperContext.inject();\n onBeforeUnmount(swiper.register({}));\n return () => <div {...mergeAttrs<'div'>({ sx: swiperStyles.item }, rest)}>{children}</div>;\n});\n"],"mappings":"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"}
|