@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,207 @@
|
|
|
1
|
+
import { useConfig } from "../config/context.js";
|
|
2
|
+
import { dataAttrs, hasContent, mergeAttrs } from "../utils/props.js";
|
|
3
|
+
import { Icon } from "../base/Icon.js";
|
|
4
|
+
import { Loading } from "../feedback/Loading.js";
|
|
5
|
+
import { useHost } from "../utils/host.js";
|
|
6
|
+
import { Image } from "../display/Image.js";
|
|
7
|
+
import { useForm } from "./context.js";
|
|
8
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
9
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
|
+
import { createVNode, reactive } from "vue";
|
|
11
|
+
import { Camera, CircleX, X } from "@lucide/icons";
|
|
12
|
+
//#region src/form/Uploader.tsx
|
|
13
|
+
/**
|
|
14
|
+
* 图片上传:已选的图片排成方格(可删除、显示上传中与失败),最后一格点了选图(H5 是文件选择,小程序是相册或拍照)。
|
|
15
|
+
* 只管选图与展示;上传由使用方在 onRead 里做,改文件的 status 显示进度。
|
|
16
|
+
*
|
|
17
|
+
* <Uploader _value={photos} maxCount={6} onRead={(files) => files.forEach(upload)} />
|
|
18
|
+
*
|
|
19
|
+
* async function upload(file: UploaderFile) {
|
|
20
|
+
* file.status = 'uploading';
|
|
21
|
+
* try { file.url = await api.upload(file.file!); file.status = 'done'; }
|
|
22
|
+
* catch { file.status = 'failed'; file.message = '上传失败'; }
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
/** 一个文件 */
|
|
26
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
27
|
+
/** Uploader 的样式:root、item、mask、remove、upload */
|
|
28
|
+
var uploaderStyles = css.create({
|
|
29
|
+
root: {
|
|
30
|
+
display: "flex",
|
|
31
|
+
flexWrap: "wrap",
|
|
32
|
+
gap: "0.5em"
|
|
33
|
+
},
|
|
34
|
+
item: {
|
|
35
|
+
position: "relative",
|
|
36
|
+
flex: "none",
|
|
37
|
+
overflow: "hidden",
|
|
38
|
+
borderRadius: "_md"
|
|
39
|
+
},
|
|
40
|
+
mask: {
|
|
41
|
+
position: "absolute",
|
|
42
|
+
inset: 0,
|
|
43
|
+
display: "flex",
|
|
44
|
+
flexDirection: "column",
|
|
45
|
+
alignItems: "center",
|
|
46
|
+
justifyContent: "center",
|
|
47
|
+
gap: "0.25em",
|
|
48
|
+
backgroundColor: "_overlay",
|
|
49
|
+
color: "#fff",
|
|
50
|
+
fontSize: "_xs"
|
|
51
|
+
},
|
|
52
|
+
remove: {
|
|
53
|
+
position: "absolute",
|
|
54
|
+
insetBlockStart: 0,
|
|
55
|
+
insetInlineEnd: 0,
|
|
56
|
+
display: "inline-flex",
|
|
57
|
+
alignItems: "center",
|
|
58
|
+
justifyContent: "center",
|
|
59
|
+
width: "1.4em",
|
|
60
|
+
height: "1.4em",
|
|
61
|
+
margin: 0,
|
|
62
|
+
padding: 0,
|
|
63
|
+
borderWidth: 0,
|
|
64
|
+
borderEndStartRadius: "_md",
|
|
65
|
+
backgroundColor: "_overlay",
|
|
66
|
+
color: "#fff",
|
|
67
|
+
fontSize: "_xs",
|
|
68
|
+
cursor: "pointer"
|
|
69
|
+
},
|
|
70
|
+
upload: {
|
|
71
|
+
boxSizing: "border-box",
|
|
72
|
+
flex: "none",
|
|
73
|
+
display: "inline-flex",
|
|
74
|
+
flexDirection: "column",
|
|
75
|
+
alignItems: "center",
|
|
76
|
+
justifyContent: "center",
|
|
77
|
+
gap: "0.3em",
|
|
78
|
+
margin: 0,
|
|
79
|
+
padding: "0.25em",
|
|
80
|
+
borderWidth: 0,
|
|
81
|
+
borderRadius: "_md",
|
|
82
|
+
backgroundColor: {
|
|
83
|
+
default: "_fill",
|
|
84
|
+
":active:not([disabled])": "_fillActive"
|
|
85
|
+
},
|
|
86
|
+
color: "_textMuted",
|
|
87
|
+
fontFamily: "inherit",
|
|
88
|
+
fontSize: "_xs",
|
|
89
|
+
cursor: {
|
|
90
|
+
default: "pointer",
|
|
91
|
+
"[disabled]": "not-allowed"
|
|
92
|
+
},
|
|
93
|
+
opacity: {
|
|
94
|
+
default: 1,
|
|
95
|
+
"[disabled]": "_disabled"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
uploadIcon: { fontSize: "1.9em" }
|
|
99
|
+
});
|
|
100
|
+
/** 图片上传(div > 预览的方格 + 选图的一格) */
|
|
101
|
+
var Uploader = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
102
|
+
const value = bindable(_props, "_value", () => [], _ctx.emit);
|
|
103
|
+
const maxCount = readonlyProp(_props, "maxCount", () => Number.POSITIVE_INFINITY);
|
|
104
|
+
const maxSize = readonlyProp(_props, "maxSize");
|
|
105
|
+
const multiple = readonlyProp(_props, "multiple", true);
|
|
106
|
+
const deletable = readonlyProp(_props, "deletable", true);
|
|
107
|
+
const disabled = readonlyProp(_props, "disabled");
|
|
108
|
+
const previewSize = readonlyProp(_props, "previewSize", "5em");
|
|
109
|
+
const uploadText = readonlyProp(_props, "uploadText");
|
|
110
|
+
const onRead = readonlyProp(_props, "onRead");
|
|
111
|
+
const onOversize = readonlyProp(_props, "onOversize");
|
|
112
|
+
const onDelete = readonlyProp(_props, "onDelete");
|
|
113
|
+
const onPreview = readonlyProp(_props, "onPreview");
|
|
114
|
+
const propsItem = readonlyProp(_props, "propsItem");
|
|
115
|
+
const propsUpload = readonlyProp(_props, "propsUpload");
|
|
116
|
+
const rest = restProps(_props, _ctx, true);
|
|
117
|
+
const config = useConfig();
|
|
118
|
+
const host = useHost();
|
|
119
|
+
const form = useForm();
|
|
120
|
+
const isDisabled = () => disabled.value ?? form?.disabled ?? false;
|
|
121
|
+
const choose = async () => {
|
|
122
|
+
const room = maxCount.value - value.value.length;
|
|
123
|
+
if (room <= 0) return;
|
|
124
|
+
const files = (await host.chooseImages({ count: multiple.value ? Math.min(room, 9) : 1 })).slice(0, room).map((image) => reactive({
|
|
125
|
+
url: image.url,
|
|
126
|
+
file: image.file,
|
|
127
|
+
size: image.size
|
|
128
|
+
}));
|
|
129
|
+
const oversize = maxSize.value === void 0 ? [] : files.filter((file) => (file.size ?? 0) > maxSize.value);
|
|
130
|
+
const accepted = files.filter((file) => !oversize.includes(file));
|
|
131
|
+
if (oversize.length) onOversize.value?.(oversize);
|
|
132
|
+
if (!accepted.length) return;
|
|
133
|
+
value.value = [...value.value, ...accepted];
|
|
134
|
+
onRead.value?.(accepted);
|
|
135
|
+
};
|
|
136
|
+
const remove = (index) => {
|
|
137
|
+
const file = value.value[index];
|
|
138
|
+
value.value = value.value.filter((_, position) => position !== index);
|
|
139
|
+
onDelete.value?.(file, index);
|
|
140
|
+
};
|
|
141
|
+
return () => {
|
|
142
|
+
const off = isDisabled();
|
|
143
|
+
const size = {
|
|
144
|
+
width: previewSize.value,
|
|
145
|
+
height: previewSize.value
|
|
146
|
+
};
|
|
147
|
+
return createVNode("div", mergeAttrs({ sx: uploaderStyles.root }, rest, dataAttrs({ "data-disabled": off })), [value.value.map((file, index) => createVNode("div", mergeAttrs({
|
|
148
|
+
sx: uploaderStyles.item,
|
|
149
|
+
style: size
|
|
150
|
+
}, propsItem.value, {
|
|
151
|
+
onClick: () => onPreview.value?.(file, index),
|
|
152
|
+
...dataAttrs({ "data-status": file.status })
|
|
153
|
+
}), [
|
|
154
|
+
createVNode(Image, {
|
|
155
|
+
"src": file.url,
|
|
156
|
+
"width": previewSize.value,
|
|
157
|
+
"height": previewSize.value,
|
|
158
|
+
"fit": "cover"
|
|
159
|
+
}, null),
|
|
160
|
+
(file.status === "uploading" || file.status === "failed") && createVNode("div", mergeAttrs({ sx: uploaderStyles.mask }), [file.status === "uploading" ? createVNode(Loading, { "size": 20 }, null) : createVNode(Icon, {
|
|
161
|
+
"icon": CircleX,
|
|
162
|
+
"size": 20
|
|
163
|
+
}, null), hasContent(file.message) && createVNode("span", null, [file.message])]),
|
|
164
|
+
deletable.value && !off && file.status !== "uploading" && createVNode("button", mergeJsxProps(mergeAttrs({ sx: uploaderStyles.remove }), {
|
|
165
|
+
"type": "button",
|
|
166
|
+
"aria-label": config.lang.remove,
|
|
167
|
+
"onClick": (event) => {
|
|
168
|
+
event.stopPropagation();
|
|
169
|
+
remove(index);
|
|
170
|
+
}
|
|
171
|
+
}), [createVNode(Icon, { "icon": X }, null)])
|
|
172
|
+
])), value.value.length < maxCount.value && createVNode("button", mergeAttrs({
|
|
173
|
+
sx: uploaderStyles.upload,
|
|
174
|
+
style: size
|
|
175
|
+
}, propsUpload.value, {
|
|
176
|
+
type: "button",
|
|
177
|
+
disabled: off,
|
|
178
|
+
onClick: () => void choose()
|
|
179
|
+
}), [createVNode(Icon, {
|
|
180
|
+
"icon": Camera,
|
|
181
|
+
"sx": uploaderStyles.uploadIcon
|
|
182
|
+
}, null), uploadText.value ?? config.lang.upload])]);
|
|
183
|
+
};
|
|
184
|
+
}, {
|
|
185
|
+
name: "Uploader",
|
|
186
|
+
props: [
|
|
187
|
+
"_value",
|
|
188
|
+
"onUpdate:_value",
|
|
189
|
+
"maxCount",
|
|
190
|
+
"maxSize",
|
|
191
|
+
"multiple",
|
|
192
|
+
"deletable",
|
|
193
|
+
"disabled",
|
|
194
|
+
"previewSize",
|
|
195
|
+
"uploadText",
|
|
196
|
+
"onRead",
|
|
197
|
+
"onOversize",
|
|
198
|
+
"onDelete",
|
|
199
|
+
"onPreview",
|
|
200
|
+
"propsItem",
|
|
201
|
+
"propsUpload"
|
|
202
|
+
]
|
|
203
|
+
});
|
|
204
|
+
//#endregion
|
|
205
|
+
export { Uploader, uploaderStyles };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=Uploader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Uploader.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/form/Uploader.tsx"],"sourcesContent":["/**\n * 图片上传:已选的图片排成方格(可删除、显示上传中与失败),最后一格点了选图(H5 是文件选择,小程序是相册或拍照)。\n * 只管选图与展示;上传由使用方在 onRead 里做,改文件的 status 显示进度。\n *\n * <Uploader _value={photos} maxCount={6} onRead={(files) => files.forEach(upload)} />\n *\n * async function upload(file: UploaderFile) {\n * file.status = 'uploading';\n * try { file.url = await api.upload(file.file!); file.status = 'done'; }\n * catch { file.status = 'failed'; file.message = '上传失败'; }\n * }\n */\nimport { Camera, CircleX, X } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { reactive, type VNodeChild } from 'vue';\nimport { Icon } from '../base/Icon';\nimport { useConfig } from '../config/context';\nimport { Image } from '../display/Image';\nimport { Loading } from '../feedback/Loading';\nimport { useHost } from '../utils/host';\nimport { dataAttrs, hasContent, mergeAttrs, type PartProps } from '../utils/props';\nimport { useForm } from './context';\n\n/** 一个文件 */\nexport interface UploaderFile {\n /** 预览(及上传完)的地址 */\n url: string;\n /** H5 里选到的 File,上传时用;小程序里没有(用 url 这个临时路径上传) */\n file?: File;\n /** 字节数 */\n size?: number;\n /** 上传的状态;不给时是已完成 */\n status?: 'uploading' | 'failed' | 'done';\n /** 状态下面的文字(“上传中”“上传失败”) */\n message?: string;\n}\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface UploaderProps extends Omit<PartProps<'div'>, 'onChange'> {\n /** 已选的文件;双向绑定写 `_value={x}` */\n _value?: UploaderFile[];\n /** 最多几张 */\n maxCount?: number;\n /** 单张最大字节数;超过的不加进来,交给 onOversize */\n maxSize?: number;\n /**\n * 一次可以选多张\n * @default true\n */\n multiple?: boolean;\n /**\n * 能删除\n * @default true\n */\n deletable?: boolean;\n /** 禁用 */\n disabled?: boolean;\n /**\n * 方格的边长(CSS 长度)\n * @default '5em'\n */\n previewSize?: string;\n /** 选图那一格的文字;不给时用语言包的“上传图片” */\n uploadText?: VNodeChild;\n /** 选好了:新加的文件(已经在 _value 里,是响应式的,改 status 会刷新) */\n onRead?: (files: UploaderFile[]) => void;\n /** 有文件超过 maxSize */\n onOversize?: (files: UploaderFile[]) => void;\n /** 删除了一张 */\n onDelete?: (file: UploaderFile, index: number) => void;\n /** 点了一张预览 */\n onPreview?: (file: UploaderFile, index: number) => void;\n /** 每一格预览的部件属性 */\n propsItem?: PartProps<'div'>;\n /** 选图那一格的部件属性 */\n propsUpload?: PartProps<'button'>;\n}\n\n/** Uploader 的样式:root、item、mask、remove、upload */\nexport const uploaderStyles = css.create({\n root: { display: 'flex', flexWrap: 'wrap', gap: '0.5em' },\n item: { position: 'relative', flex: 'none', overflow: 'hidden', borderRadius: '_md' },\n mask: {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '0.25em',\n // 盖在图片上的深色层:两套主题里都是深底白字\n backgroundColor: '_overlay',\n color: '#fff',\n fontSize: '_xs',\n },\n remove: {\n position: 'absolute',\n insetBlockStart: 0,\n insetInlineEnd: 0,\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1.4em',\n height: '1.4em',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n borderEndStartRadius: '_md',\n backgroundColor: '_overlay',\n color: '#fff',\n fontSize: '_xs',\n cursor: 'pointer',\n },\n upload: {\n boxSizing: 'border-box',\n flex: 'none',\n display: 'inline-flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '0.3em',\n margin: 0,\n padding: '0.25em',\n borderWidth: 0,\n borderRadius: '_md',\n backgroundColor: { default: '_fill', ':active:not([disabled])': '_fillActive' },\n color: '_textMuted',\n fontFamily: 'inherit',\n fontSize: '_xs',\n cursor: { default: 'pointer', '[disabled]': 'not-allowed' },\n opacity: { default: 1, '[disabled]': '_disabled' },\n },\n uploadIcon: { fontSize: '1.9em' },\n});\n\n/** 图片上传(div > 预览的方格 + 选图的一格) */\nexport const Uploader = _component(\n ({\n _value: value = _bindable<UploaderFile[]>([]),\n maxCount = Number.POSITIVE_INFINITY,\n maxSize,\n multiple = true,\n deletable = true,\n disabled,\n previewSize = '5em',\n uploadText,\n onRead,\n onOversize,\n onDelete,\n onPreview,\n propsItem,\n propsUpload,\n ...rest\n }: UploaderProps) => {\n const config = useConfig();\n const host = useHost();\n const form = useForm();\n const isDisabled = () => disabled ?? form?.disabled ?? false;\n\n const choose = async () => {\n const room = maxCount - value.length;\n if (room <= 0) return;\n const chosen = await host.chooseImages({ count: multiple ? Math.min(room, 9) : 1 });\n // 响应式:使用方在 onRead 里改 status、url 会刷新\n const files = chosen\n .slice(0, room)\n .map((image) =>\n reactive<UploaderFile>({ url: image.url, file: image.file, size: image.size }),\n );\n const oversize =\n maxSize === undefined ? [] : files.filter((file) => (file.size ?? 0) > maxSize);\n const accepted = files.filter((file) => !oversize.includes(file));\n if (oversize.length) onOversize?.(oversize);\n if (!accepted.length) return;\n value = [...value, ...accepted];\n onRead?.(accepted);\n };\n const remove = (index: number) => {\n const file = value[index]!;\n value = value.filter((_, position) => position !== index);\n onDelete?.(file, index);\n };\n\n return () => {\n const off = isDisabled();\n const size = { width: previewSize, height: previewSize };\n return (\n <div\n {...mergeAttrs<'div'>(\n { sx: uploaderStyles.root },\n rest,\n dataAttrs({ 'data-disabled': off }),\n )}\n >\n {value.map((file, index) => (\n <div\n {...mergeAttrs<'div'>({ sx: uploaderStyles.item, style: size }, propsItem, {\n onClick: () => onPreview?.(file, index),\n ...dataAttrs({ 'data-status': file.status }),\n })}\n >\n <Image src={file.url} width={previewSize} height={previewSize} fit=\"cover\" />\n {(file.status === 'uploading' || file.status === 'failed') && (\n <div {...mergeAttrs<'div'>({ sx: uploaderStyles.mask })}>\n {file.status === 'uploading' ? (\n <Loading size={20} />\n ) : (\n <Icon icon={CircleX} size={20} />\n )}\n {hasContent(file.message) && <span>{file.message}</span>}\n </div>\n )}\n {deletable && !off && file.status !== 'uploading' && (\n <button\n {...mergeAttrs<'button'>({ sx: uploaderStyles.remove })}\n type=\"button\"\n aria-label={config.lang.remove}\n onClick={(event: Event) => {\n // 不当成点了预览\n event.stopPropagation();\n remove(index);\n }}\n >\n <Icon icon={X} />\n </button>\n )}\n </div>\n ))}\n {value.length < maxCount && (\n <button\n {...mergeAttrs<'button'>({ sx: uploaderStyles.upload, style: size }, propsUpload, {\n type: 'button',\n disabled: off,\n onClick: () => void choose(),\n })}\n >\n <Icon icon={Camera} sx={uploaderStyles.uploadIcon} />\n {uploadText ?? config.lang.upload}\n </button>\n )}\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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 interface ChoiceShapes {
|
|
6
|
+
/** 圆(默认) */
|
|
7
|
+
round: unknown;
|
|
8
|
+
/** 圆角方块 */
|
|
9
|
+
square: unknown;
|
|
10
|
+
}
|
|
11
|
+
/** 选项的排列 */
|
|
12
|
+
export interface ChoiceDirections {
|
|
13
|
+
/** 竖着一行一个(默认) */
|
|
14
|
+
vertical: unknown;
|
|
15
|
+
/** 横着排,放不下时换行 */
|
|
16
|
+
horizontal: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** 选框、分组的样式:root、box、square、checked、label、group、horizontal */
|
|
19
|
+
export declare const choiceStyles: Readonly<{
|
|
20
|
+
box: css.CompiledStyles;
|
|
21
|
+
checked: css.CompiledStyles;
|
|
22
|
+
group: css.CompiledStyles;
|
|
23
|
+
horizontal: css.CompiledStyles;
|
|
24
|
+
label: css.CompiledStyles;
|
|
25
|
+
root: css.CompiledStyles;
|
|
26
|
+
square: css.CompiledStyles;
|
|
27
|
+
}>;
|
|
28
|
+
/** 一个选框的状态 */
|
|
29
|
+
export interface ChoiceState {
|
|
30
|
+
role: 'checkbox' | 'radio';
|
|
31
|
+
checked: boolean;
|
|
32
|
+
/** 半选(复选框的全选在部分选中时) */
|
|
33
|
+
indeterminate?: boolean;
|
|
34
|
+
disabled: boolean;
|
|
35
|
+
shape: keyof ChoiceShapes;
|
|
36
|
+
onToggle: () => void;
|
|
37
|
+
}
|
|
38
|
+
/** 画一个选框:button[role] > 框 + 文字 */
|
|
39
|
+
export declare function renderChoice(state: ChoiceState, attrs: Record<string, unknown> | undefined, label: VNodeChild, parts: {
|
|
40
|
+
propsBox?: PartProps<'span'>;
|
|
41
|
+
propsLabel?: PartProps<'span'>;
|
|
42
|
+
}): import("@zerodep-dev/vue-tsx-pro-tsx/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { dataAttrs, hasContent, mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { Icon } from "../base/Icon.js";
|
|
3
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
4
|
+
import { createVNode } from "vue";
|
|
5
|
+
import { Check, Minus } from "@lucide/icons";
|
|
6
|
+
//#region src/form/choice.tsx
|
|
7
|
+
/**
|
|
8
|
+
* 复选框与单选框共用的外观:前面一个圆(或方)框,选中时填上主色、画对勾,后面是文字。
|
|
9
|
+
* Checkbox、Radio 只在角色、状态与分组的逻辑上不同(内部模块,不从包导出)。
|
|
10
|
+
*/
|
|
11
|
+
/** 选框的形状 */
|
|
12
|
+
/** 选项的排列 */
|
|
13
|
+
/** 选框、分组的样式:root、box、square、checked、label、group、horizontal */
|
|
14
|
+
var choiceStyles = css.create({
|
|
15
|
+
root: {
|
|
16
|
+
display: "inline-flex",
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
gap: "0.5em",
|
|
19
|
+
margin: 0,
|
|
20
|
+
padding: 0,
|
|
21
|
+
borderWidth: 0,
|
|
22
|
+
backgroundColor: "transparent",
|
|
23
|
+
color: {
|
|
24
|
+
default: "_text",
|
|
25
|
+
"[data-disabled]": "_textDisabled"
|
|
26
|
+
},
|
|
27
|
+
fontFamily: "inherit",
|
|
28
|
+
fontSize: "inherit",
|
|
29
|
+
lineHeight: "1.5",
|
|
30
|
+
textAlign: "start",
|
|
31
|
+
cursor: {
|
|
32
|
+
default: "pointer",
|
|
33
|
+
"[data-disabled]": "not-allowed"
|
|
34
|
+
},
|
|
35
|
+
userSelect: "none"
|
|
36
|
+
},
|
|
37
|
+
box: {
|
|
38
|
+
boxSizing: "border-box",
|
|
39
|
+
flex: "none",
|
|
40
|
+
display: "inline-flex",
|
|
41
|
+
alignItems: "center",
|
|
42
|
+
justifyContent: "center",
|
|
43
|
+
width: "1.3em",
|
|
44
|
+
height: "1.3em",
|
|
45
|
+
borderWidth: "_xs",
|
|
46
|
+
borderStyle: "solid",
|
|
47
|
+
borderColor: "_borderStrong",
|
|
48
|
+
borderRadius: "_full",
|
|
49
|
+
backgroundColor: {
|
|
50
|
+
default: "_surface",
|
|
51
|
+
"[data-disabled]": "_fill"
|
|
52
|
+
},
|
|
53
|
+
color: "_accentContrast",
|
|
54
|
+
fontSize: "1em",
|
|
55
|
+
transitionProperty: "background-color, border-color",
|
|
56
|
+
transitionDuration: "_sm"
|
|
57
|
+
},
|
|
58
|
+
square: { borderRadius: "_sm" },
|
|
59
|
+
checked: {
|
|
60
|
+
borderColor: {
|
|
61
|
+
default: "_accent",
|
|
62
|
+
"[data-disabled]": "_borderStrong"
|
|
63
|
+
},
|
|
64
|
+
backgroundColor: {
|
|
65
|
+
default: "_accent",
|
|
66
|
+
"[data-disabled]": "_fillActive"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
label: { overflowWrap: "anywhere" },
|
|
70
|
+
group: {
|
|
71
|
+
display: "flex",
|
|
72
|
+
flexDirection: "column",
|
|
73
|
+
alignItems: "flex-start",
|
|
74
|
+
gap: "_sm"
|
|
75
|
+
},
|
|
76
|
+
horizontal: {
|
|
77
|
+
flexDirection: "row",
|
|
78
|
+
flexWrap: "wrap",
|
|
79
|
+
alignItems: "center",
|
|
80
|
+
columnGap: "_lg"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
/** 一个选框的状态 */
|
|
84
|
+
/** 画一个选框:button[role] > 框 + 文字 */
|
|
85
|
+
function renderChoice(state, attrs, label, parts) {
|
|
86
|
+
const marked = state.checked || state.indeterminate;
|
|
87
|
+
return createVNode("button", mergeAttrs({ sx: choiceStyles.root }, attrs, {
|
|
88
|
+
type: "button",
|
|
89
|
+
role: state.role,
|
|
90
|
+
"aria-checked": state.indeterminate ? "mixed" : state.checked ? "true" : "false",
|
|
91
|
+
disabled: state.disabled,
|
|
92
|
+
onClick: state.onToggle,
|
|
93
|
+
...dataAttrs({
|
|
94
|
+
"data-disabled": state.disabled,
|
|
95
|
+
"data-checked": marked
|
|
96
|
+
})
|
|
97
|
+
}), [createVNode("span", mergeAttrs({ sx: [
|
|
98
|
+
choiceStyles.box,
|
|
99
|
+
state.shape === "square" && choiceStyles.square,
|
|
100
|
+
marked && choiceStyles.checked
|
|
101
|
+
] }, parts.propsBox, dataAttrs({ "data-disabled": state.disabled })), [marked && createVNode(Icon, {
|
|
102
|
+
"icon": state.indeterminate ? Minus : Check,
|
|
103
|
+
"strokeWidth": 3
|
|
104
|
+
}, null)]), hasContent(label) && createVNode("span", mergeAttrs({ sx: choiceStyles.label }, parts.propsLabel), [label])]);
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { choiceStyles, renderChoice };
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=choice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice.js","names":["_createVNode"],"sources":["../../src/form/choice.tsx"],"sourcesContent":["/**\n * 复选框与单选框共用的外观:前面一个圆(或方)框,选中时填上主色、画对勾,后面是文字。\n * Checkbox、Radio 只在角色、状态与分组的逻辑上不同(内部模块,不从包导出)。\n */\nimport { Check, Minus } from '@lucide/icons';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Icon } from '../base/Icon';\nimport { dataAttrs, hasContent, mergeAttrs, type PartProps } from '../utils/props';\n\n/** 选框的形状 */\nexport interface ChoiceShapes {\n /** 圆(默认) */\n round: unknown;\n /** 圆角方块 */\n square: unknown;\n}\n\n/** 选项的排列 */\nexport interface ChoiceDirections {\n /** 竖着一行一个(默认) */\n vertical: unknown;\n /** 横着排,放不下时换行 */\n horizontal: unknown;\n}\n\n/** 选框、分组的样式:root、box、square、checked、label、group、horizontal */\nexport const choiceStyles = css.create({\n root: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '0.5em',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n backgroundColor: 'transparent',\n color: { default: '_text', '[data-disabled]': '_textDisabled' },\n fontFamily: 'inherit',\n fontSize: 'inherit',\n lineHeight: '1.5',\n textAlign: 'start',\n cursor: { default: 'pointer', '[data-disabled]': 'not-allowed' },\n userSelect: 'none',\n },\n box: {\n boxSizing: 'border-box',\n flex: 'none',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1.3em',\n height: '1.3em',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderStrong',\n borderRadius: '_full',\n backgroundColor: { default: '_surface', '[data-disabled]': '_fill' },\n color: '_accentContrast',\n fontSize: '1em',\n transitionProperty: 'background-color, border-color',\n transitionDuration: '_sm',\n },\n square: { borderRadius: '_sm' },\n checked: {\n borderColor: { default: '_accent', '[data-disabled]': '_borderStrong' },\n backgroundColor: { default: '_accent', '[data-disabled]': '_fillActive' },\n },\n label: { overflowWrap: 'anywhere' },\n group: { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '_sm' },\n horizontal: { flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', columnGap: '_lg' },\n});\n\n/** 一个选框的状态 */\nexport interface ChoiceState {\n role: 'checkbox' | 'radio';\n checked: boolean;\n /** 半选(复选框的全选在部分选中时) */\n indeterminate?: boolean;\n disabled: boolean;\n shape: keyof ChoiceShapes;\n onToggle: () => void;\n}\n\n/** 画一个选框:button[role] > 框 + 文字 */\nexport function renderChoice(\n state: ChoiceState,\n attrs: Record<string, unknown> | undefined,\n label: VNodeChild,\n parts: { propsBox?: PartProps<'span'>; propsLabel?: PartProps<'span'> },\n) {\n const marked = state.checked || state.indeterminate;\n return (\n <button\n {...mergeAttrs<'button'>({ sx: choiceStyles.root }, attrs, {\n type: 'button',\n role: state.role,\n 'aria-checked': state.indeterminate ? 'mixed' : state.checked ? 'true' : 'false',\n disabled: state.disabled,\n onClick: state.onToggle,\n ...dataAttrs({ 'data-disabled': state.disabled, 'data-checked': marked }),\n })}\n >\n <span\n {...mergeAttrs<'span'>(\n {\n sx: [\n choiceStyles.box,\n state.shape === 'square' && choiceStyles.square,\n marked && choiceStyles.checked,\n ],\n },\n parts.propsBox,\n dataAttrs({ 'data-disabled': state.disabled }),\n )}\n >\n {marked && <Icon icon={state.indeterminate ? Minus : Check} strokeWidth={3} />}\n </span>\n {hasContent(label) && (\n <span {...mergeAttrs<'span'>({ sx: choiceStyles.label }, parts.propsLabel)}>{label}</span>\n )}\n </button>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,IAAa,eAAe,IAAI,OAAO;CACrC,MAAM;EACJ,SAAS;EACT,YAAY;EACZ,KAAK;EACL,QAAQ;EACR,SAAS;EACT,aAAa;EACb,iBAAiB;EACjB,OAAO;GAAE,SAAS;GAAS,mBAAmB;EAAgB;EAC9D,YAAY;EACZ,UAAU;EACV,YAAY;EACZ,WAAW;EACX,QAAQ;GAAE,SAAS;GAAW,mBAAmB;EAAc;EAC/D,YAAY;CACd;CACA,KAAK;EACH,WAAW;EACX,MAAM;EACN,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,OAAO;EACP,QAAQ;EACR,aAAa;EACb,aAAa;EACb,aAAa;EACb,cAAc;EACd,iBAAiB;GAAE,SAAS;GAAY,mBAAmB;EAAQ;EACnE,OAAO;EACP,UAAU;EACV,oBAAoB;EACpB,oBAAoB;CACtB;CACA,QAAQ,EAAE,cAAc,MAAM;CAC9B,SAAS;EACP,aAAa;GAAE,SAAS;GAAW,mBAAmB;EAAgB;EACtE,iBAAiB;GAAE,SAAS;GAAW,mBAAmB;EAAc;CAC1E;CACA,OAAO,EAAE,cAAc,WAAW;CAClC,OAAO;EAAE,SAAS;EAAQ,eAAe;EAAU,YAAY;EAAc,KAAK;CAAM;CACxF,YAAY;EAAE,eAAe;EAAO,UAAU;EAAQ,YAAY;EAAU,WAAW;CAAM;AAC/F,CAAC;;;AAcD,SAAgB,aACd,OACA,OACA,OACA,OACA;CACA,MAAM,SAAS,MAAM,WAAW,MAAM;CACtC,OAAAA,YAAA,UAEQ,WAAqB,EAAE,IAAI,aAAa,KAAK,GAAG,OAAO;EACzD,MAAM;EACN,MAAM,MAAM;EACZ,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,UAAU,SAAS;EACzE,UAAU,MAAM;EAChB,SAAS,MAAM;EACf,GAAG,UAAU;GAAE,iBAAiB,MAAM;GAAU,gBAAgB;EAAO,CAAC;CAC1E,CAAC,GAAC,CAAAA,YAAA,QAGI,WACF,EACE,IAAI;EACF,aAAa;EACb,MAAM,UAAU,YAAY,aAAa;EACzC,UAAU,aAAa;CAAO,EAElC,GACA,MAAM,UACN,UAAU,EAAE,iBAAiB,MAAM,SAAS,CAAC,CAC/C,GAAC,CAEA,UAAMA,YAAA,MAAA;EAAA,QAAgB,MAAM,gBAAgB,QAAQ;EAAK,eAAe;CAAC,GAAA,IAAA,CAAI,CAAA,GAE/E,WAAW,KAAK,KAACA,YAAA,QACN,WAAmB,EAAE,IAAI,aAAa,MAAM,GAAG,MAAM,UAAU,GAAC,CAAG,KAAK,CAAA,CACnF,CAAA;AAGP"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type VNodeChild } from 'vue';
|
|
2
|
+
/** 标题的位置 */
|
|
3
|
+
export interface FormLabelPositions {
|
|
4
|
+
/** 左边,固定宽度(默认) */
|
|
5
|
+
left: unknown;
|
|
6
|
+
/** 上面,控件占满一行 */
|
|
7
|
+
top: unknown;
|
|
8
|
+
}
|
|
9
|
+
/** 什么时候校验 */
|
|
10
|
+
export interface FormValidateTriggers {
|
|
11
|
+
/** 失去焦点时(输入框的默认) */
|
|
12
|
+
blur: unknown;
|
|
13
|
+
/** 值变了就校验 */
|
|
14
|
+
change: unknown;
|
|
15
|
+
/** 只在提交时 */
|
|
16
|
+
submit: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** 一条校验规则;按顺序检查,第一条不通过的给出消息 */
|
|
19
|
+
export interface FormRule {
|
|
20
|
+
/** 必填:空字符串、空数组、null、undefined、false 算没填 */
|
|
21
|
+
required?: boolean;
|
|
22
|
+
/** 字符串要匹配的格式 */
|
|
23
|
+
pattern?: RegExp;
|
|
24
|
+
/** 自定义:返回 false 或消息表示不通过,可以是异步的(查重名) */
|
|
25
|
+
validator?: (value: unknown) => boolean | string | Promise<boolean | string>;
|
|
26
|
+
/** 不通过时的消息;不给时必填用语言包的“请填写…” */
|
|
27
|
+
message?: string;
|
|
28
|
+
/**
|
|
29
|
+
* 什么时候检查这一条;不给时按 Form 的 validateTrigger
|
|
30
|
+
*/
|
|
31
|
+
trigger?: keyof FormValidateTriggers;
|
|
32
|
+
}
|
|
33
|
+
/** 登记到表单里的一项 */
|
|
34
|
+
export interface FormItemHandle {
|
|
35
|
+
/** 字段名:提交时作为值的键 */
|
|
36
|
+
readonly name: string | undefined;
|
|
37
|
+
/** 当前的值 */
|
|
38
|
+
readonly value: unknown;
|
|
39
|
+
/** 当前的错误消息 */
|
|
40
|
+
readonly error: string | undefined;
|
|
41
|
+
/** 校验;trigger 为 submit 时检查所有规则,否则只检查这个时机的 */
|
|
42
|
+
validate(trigger?: keyof FormValidateTriggers): Promise<string | undefined>;
|
|
43
|
+
/** 清掉错误消息 */
|
|
44
|
+
resetValidation(): void;
|
|
45
|
+
/** 滚到这一项(出错时) */
|
|
46
|
+
scrollIntoView(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/** 表单提供给各项的 */
|
|
49
|
+
export interface FormState {
|
|
50
|
+
readonly disabled: boolean;
|
|
51
|
+
readonly readonly: boolean;
|
|
52
|
+
readonly labelWidth: string | undefined;
|
|
53
|
+
readonly labelPosition: keyof FormLabelPositions;
|
|
54
|
+
readonly inputAlign: 'left' | 'right';
|
|
55
|
+
readonly validateTrigger: keyof FormValidateTriggers;
|
|
56
|
+
register(item: FormItemHandle): () => void;
|
|
57
|
+
/** 提交:校验所有项,通过时调用 onSubmit */
|
|
58
|
+
submit(): void;
|
|
59
|
+
}
|
|
60
|
+
/** 表单的上下文 */
|
|
61
|
+
export declare const FormContext: import("@zerodep-dev/vue-tsx-pro-tsx").Context<FormState>;
|
|
62
|
+
/** 不在 Form 里也能单独用的项:读不到上下文时为 null */
|
|
63
|
+
export declare const useForm: () => FormState | null;
|
|
64
|
+
/** 值是不是“没填” */
|
|
65
|
+
export declare function isEmptyValue(value: unknown): boolean;
|
|
66
|
+
/** useFormItem 的参数:都是 getter,跟着组件的 props 变 */
|
|
67
|
+
export interface FormItemOptions {
|
|
68
|
+
name: () => string | undefined;
|
|
69
|
+
rules: () => readonly FormRule[] | undefined;
|
|
70
|
+
value: () => unknown;
|
|
71
|
+
/** 标题:必填消息里用 */
|
|
72
|
+
label: () => VNodeChild;
|
|
73
|
+
/** 滚到这一项要量的元素 */
|
|
74
|
+
element?: () => unknown;
|
|
75
|
+
}
|
|
76
|
+
/** 一个表单项的校验状态;在 Form 里时登记进去,提交时一起校验 */
|
|
77
|
+
export declare function useFormItem(options: FormItemOptions): {
|
|
78
|
+
readonly error: string | undefined;
|
|
79
|
+
readonly required: boolean;
|
|
80
|
+
form: FormState | null;
|
|
81
|
+
validate: (trigger?: keyof FormValidateTriggers) => Promise<string | undefined>;
|
|
82
|
+
resetValidation: () => void;
|
|
83
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useConfig } from "../config/context.js";
|
|
2
|
+
import { useHost } from "../utils/host.js";
|
|
3
|
+
import { createContext } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
4
|
+
import { onBeforeUnmount, shallowRef, watch } from "vue";
|
|
5
|
+
//#region src/form/context.ts
|
|
6
|
+
/**
|
|
7
|
+
* 表单的上下文与校验:Form 提供,Field、FormItem 用 `useFormItem` 登记自己(名字、规则、取值),
|
|
8
|
+
* 提交时按顺序校验,出错的项显示消息。单独一个模块(不带组件),Form、FormItem、Field 都要导入。
|
|
9
|
+
*
|
|
10
|
+
* const item = useFormItem({ name: () => name, rules: () => rules, value: () => value, label: () => label });
|
|
11
|
+
* item.error // 当前的错误消息(没有时 undefined)
|
|
12
|
+
* await item.validate('blur')
|
|
13
|
+
*/
|
|
14
|
+
/** 表单的上下文 */
|
|
15
|
+
var FormContext = createContext("Form");
|
|
16
|
+
/** 不在 Form 里也能单独用的项:读不到上下文时为 null */
|
|
17
|
+
var useForm = () => FormContext.inject(null);
|
|
18
|
+
/** 值是不是“没填” */
|
|
19
|
+
function isEmptyValue(value) {
|
|
20
|
+
return value === void 0 || value === null || value === false || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0;
|
|
21
|
+
}
|
|
22
|
+
/** 用一条规则检查值:通过时 undefined,不通过时消息 */
|
|
23
|
+
async function check(rule, value, label, requiredText) {
|
|
24
|
+
if (rule.required && isEmptyValue(value)) return rule.message ?? requiredText(label);
|
|
25
|
+
if (isEmptyValue(value)) return void 0;
|
|
26
|
+
if (rule.pattern && !rule.pattern.test(String(value))) return rule.message ?? "";
|
|
27
|
+
if (rule.validator) {
|
|
28
|
+
const result = await rule.validator(value);
|
|
29
|
+
if (result === false) return rule.message ?? "";
|
|
30
|
+
if (typeof result === "string" && result) return result;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** 一个表单项的校验状态;在 Form 里时登记进去,提交时一起校验 */
|
|
34
|
+
function useFormItem(options) {
|
|
35
|
+
const form = useForm();
|
|
36
|
+
const config = useConfig();
|
|
37
|
+
const host = useHost();
|
|
38
|
+
const error = shallowRef();
|
|
39
|
+
let version = 0;
|
|
40
|
+
const handle = {
|
|
41
|
+
get name() {
|
|
42
|
+
return options.name();
|
|
43
|
+
},
|
|
44
|
+
get value() {
|
|
45
|
+
return options.value();
|
|
46
|
+
},
|
|
47
|
+
get error() {
|
|
48
|
+
return error.value;
|
|
49
|
+
},
|
|
50
|
+
async validate(trigger = "submit") {
|
|
51
|
+
const current = ++version;
|
|
52
|
+
const fallback = form?.validateTrigger ?? "blur";
|
|
53
|
+
const label = options.label();
|
|
54
|
+
for (const rule of options.rules() ?? []) {
|
|
55
|
+
if (trigger !== "submit" && (rule.trigger ?? fallback) !== trigger) continue;
|
|
56
|
+
const message = await check(rule, options.value(), typeof label === "string" ? label : "", config.lang.fieldRequired);
|
|
57
|
+
if (current !== version) return error.value;
|
|
58
|
+
if (message !== void 0) return error.value = message;
|
|
59
|
+
}
|
|
60
|
+
error.value = void 0;
|
|
61
|
+
},
|
|
62
|
+
resetValidation() {
|
|
63
|
+
version++;
|
|
64
|
+
error.value = void 0;
|
|
65
|
+
},
|
|
66
|
+
async scrollIntoView() {
|
|
67
|
+
const rect = await host.measure(options.element?.());
|
|
68
|
+
if (rect) host.scrollTo(host.scrollTop() + rect.top - 16, { smooth: true });
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
watch(options.value, () => void handle.validate(error.value === void 0 ? "change" : "submit"));
|
|
72
|
+
const unregister = form?.register(handle);
|
|
73
|
+
if (unregister) onBeforeUnmount(unregister);
|
|
74
|
+
return {
|
|
75
|
+
get error() {
|
|
76
|
+
return error.value;
|
|
77
|
+
},
|
|
78
|
+
get required() {
|
|
79
|
+
return options.rules()?.some((rule) => rule.required) ?? false;
|
|
80
|
+
},
|
|
81
|
+
form,
|
|
82
|
+
validate: (trigger) => handle.validate(trigger),
|
|
83
|
+
resetValidation: () => handle.resetValidation()
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { FormContext, isEmptyValue, useForm, useFormItem };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../../src/form/context.ts"],"sourcesContent":["/**\n * 表单的上下文与校验:Form 提供,Field、FormItem 用 `useFormItem` 登记自己(名字、规则、取值),\n * 提交时按顺序校验,出错的项显示消息。单独一个模块(不带组件),Form、FormItem、Field 都要导入。\n *\n * const item = useFormItem({ name: () => name, rules: () => rules, value: () => value, label: () => label });\n * item.error // 当前的错误消息(没有时 undefined)\n * await item.validate('blur')\n */\nimport { createContext } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { onBeforeUnmount, shallowRef, watch, type VNodeChild } from 'vue';\nimport { useConfig } from '../config/context';\nimport { useHost } from '../utils/host';\n\n/** 标题的位置 */\nexport interface FormLabelPositions {\n /** 左边,固定宽度(默认) */\n left: unknown;\n /** 上面,控件占满一行 */\n top: unknown;\n}\n\n/** 什么时候校验 */\nexport interface FormValidateTriggers {\n /** 失去焦点时(输入框的默认) */\n blur: unknown;\n /** 值变了就校验 */\n change: unknown;\n /** 只在提交时 */\n submit: unknown;\n}\n\n/** 一条校验规则;按顺序检查,第一条不通过的给出消息 */\nexport interface FormRule {\n /** 必填:空字符串、空数组、null、undefined、false 算没填 */\n required?: boolean;\n /** 字符串要匹配的格式 */\n pattern?: RegExp;\n /** 自定义:返回 false 或消息表示不通过,可以是异步的(查重名) */\n validator?: (value: unknown) => boolean | string | Promise<boolean | string>;\n /** 不通过时的消息;不给时必填用语言包的“请填写…” */\n message?: string;\n /**\n * 什么时候检查这一条;不给时按 Form 的 validateTrigger\n */\n trigger?: keyof FormValidateTriggers;\n}\n\n/** 登记到表单里的一项 */\nexport interface FormItemHandle {\n /** 字段名:提交时作为值的键 */\n readonly name: string | undefined;\n /** 当前的值 */\n readonly value: unknown;\n /** 当前的错误消息 */\n readonly error: string | undefined;\n /** 校验;trigger 为 submit 时检查所有规则,否则只检查这个时机的 */\n validate(trigger?: keyof FormValidateTriggers): Promise<string | undefined>;\n /** 清掉错误消息 */\n resetValidation(): void;\n /** 滚到这一项(出错时) */\n scrollIntoView(): Promise<void>;\n}\n\n/** 表单提供给各项的 */\nexport interface FormState {\n readonly disabled: boolean;\n readonly readonly: boolean;\n readonly labelWidth: string | undefined;\n readonly labelPosition: keyof FormLabelPositions;\n readonly inputAlign: 'left' | 'right';\n readonly validateTrigger: keyof FormValidateTriggers;\n register(item: FormItemHandle): () => void;\n /** 提交:校验所有项,通过时调用 onSubmit */\n submit(): void;\n}\n\n/** 表单的上下文 */\nexport const FormContext = createContext<FormState>('Form');\n\n/** 不在 Form 里也能单独用的项:读不到上下文时为 null */\nexport const useForm = (): FormState | null => FormContext.inject(null);\n\n/** 值是不是“没填” */\nexport function isEmptyValue(value: unknown): boolean {\n return (\n value === undefined ||\n value === null ||\n value === false ||\n (typeof value === 'string' && value.trim() === '') ||\n (Array.isArray(value) && value.length === 0)\n );\n}\n\n/** 用一条规则检查值:通过时 undefined,不通过时消息 */\nasync function check(\n rule: FormRule,\n value: unknown,\n label: string,\n requiredText: (label: string) => string,\n) {\n if (rule.required && isEmptyValue(value)) return rule.message ?? requiredText(label);\n // 没填的非必填项不查格式\n if (isEmptyValue(value)) return undefined;\n if (rule.pattern && !rule.pattern.test(String(value as string))) return rule.message ?? '';\n if (rule.validator) {\n const result = await rule.validator(value);\n if (result === false) return rule.message ?? '';\n if (typeof result === 'string' && result) return result;\n }\n return undefined;\n}\n\n/** useFormItem 的参数:都是 getter,跟着组件的 props 变 */\nexport interface FormItemOptions {\n name: () => string | undefined;\n rules: () => readonly FormRule[] | undefined;\n value: () => unknown;\n /** 标题:必填消息里用 */\n label: () => VNodeChild;\n /** 滚到这一项要量的元素 */\n element?: () => unknown;\n}\n\n/** 一个表单项的校验状态;在 Form 里时登记进去,提交时一起校验 */\nexport function useFormItem(options: FormItemOptions) {\n const form = useForm();\n const config = useConfig();\n const host = useHost();\n const error = shallowRef<string>();\n // 异步校验可能交错:只认最后一次\n let version = 0;\n const handle: FormItemHandle = {\n get name() {\n return options.name();\n },\n get value() {\n return options.value();\n },\n get error() {\n return error.value;\n },\n async validate(trigger = 'submit') {\n const current = ++version;\n const fallback = form?.validateTrigger ?? 'blur';\n const label = options.label();\n for (const rule of options.rules() ?? []) {\n if (trigger !== 'submit' && (rule.trigger ?? fallback) !== trigger) continue;\n const message = await check(\n rule,\n options.value(),\n typeof label === 'string' ? label : '',\n config.lang.fieldRequired,\n );\n if (current !== version) return error.value;\n if (message !== undefined) return (error.value = message);\n }\n return (error.value = undefined);\n },\n resetValidation() {\n version++;\n error.value = undefined;\n },\n async scrollIntoView() {\n const rect = await host.measure(options.element?.());\n // 顶部留一点空,不贴着导航栏\n if (rect) host.scrollTo(host.scrollTop() + rect.top - 16, { smooth: true });\n },\n };\n // 值变了:已经报错的项整个重查(改对了消息就消失),否则只查 change 时机的规则\n watch(options.value, () => void handle.validate(error.value === undefined ? 'change' : 'submit'));\n const unregister = form?.register(handle);\n if (unregister) onBeforeUnmount(unregister);\n return {\n get error() {\n return error.value;\n },\n get required() {\n return options.rules()?.some((rule) => rule.required) ?? false;\n },\n form,\n validate: (trigger?: keyof FormValidateTriggers) => handle.validate(trigger),\n resetValidation: () => handle.resetValidation(),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA6EA,IAAa,cAAc,cAAyB,MAAM;;AAG1D,IAAa,gBAAkC,YAAY,OAAO,IAAI;;AAGtE,SAAgB,aAAa,OAAyB;CACpD,OACE,UAAU,KAAA,KACV,UAAU,QACV,UAAU,SACT,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,MAC9C,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAE9C;;AAGA,eAAe,MACb,MACA,OACA,OACA,cACA;CACA,IAAI,KAAK,YAAY,aAAa,KAAK,GAAG,OAAO,KAAK,WAAW,aAAa,KAAK;CAEnF,IAAI,aAAa,KAAK,GAAG,OAAO,KAAA;CAChC,IAAI,KAAK,WAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,KAAe,CAAC,GAAG,OAAO,KAAK,WAAW;CACxF,IAAI,KAAK,WAAW;EAClB,MAAM,SAAS,MAAM,KAAK,UAAU,KAAK;EACzC,IAAI,WAAW,OAAO,OAAO,KAAK,WAAW;EAC7C,IAAI,OAAO,WAAW,YAAY,QAAQ,OAAO;CACnD;AAEF;;AAcA,SAAgB,YAAY,SAA0B;CACpD,MAAM,OAAO,QAAQ;CACrB,MAAM,SAAS,UAAU;CACzB,MAAM,OAAO,QAAQ;CACrB,MAAM,QAAQ,WAAmB;CAEjC,IAAI,UAAU;CACd,MAAM,SAAyB;EAC7B,IAAI,OAAO;GACT,OAAO,QAAQ,KAAK;EACtB;EACA,IAAI,QAAQ;GACV,OAAO,QAAQ,MAAM;EACvB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,MAAM,SAAS,UAAU,UAAU;GACjC,MAAM,UAAU,EAAE;GAClB,MAAM,WAAW,MAAM,mBAAmB;GAC1C,MAAM,QAAQ,QAAQ,MAAM;GAC5B,KAAK,MAAM,QAAQ,QAAQ,MAAM,KAAK,CAAC,GAAG;IACxC,IAAI,YAAY,aAAa,KAAK,WAAW,cAAc,SAAS;IACpE,MAAM,UAAU,MAAM,MACpB,MACA,QAAQ,MAAM,GACd,OAAO,UAAU,WAAW,QAAQ,IACpC,OAAO,KAAK,aACd;IACA,IAAI,YAAY,SAAS,OAAO,MAAM;IACtC,IAAI,YAAY,KAAA,GAAW,OAAQ,MAAM,QAAQ;GACnD;GACQ,MAAM,QAAQ,KAAA;EACxB;EACA,kBAAkB;GAChB;GACA,MAAM,QAAQ,KAAA;EAChB;EACA,MAAM,iBAAiB;GACrB,MAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ,UAAU,CAAC;GAEnD,IAAI,MAAM,KAAK,SAAS,KAAK,UAAU,IAAI,KAAK,MAAM,IAAI,EAAE,QAAQ,KAAK,CAAC;EAC5E;CACF;CAEA,MAAM,QAAQ,aAAa,KAAK,OAAO,SAAS,MAAM,UAAU,KAAA,IAAY,WAAW,QAAQ,CAAC;CAChG,MAAM,aAAa,MAAM,SAAS,MAAM;CACxC,IAAI,YAAY,gBAAgB,UAAU;CAC1C,OAAO;EACL,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,WAAW;GACb,OAAO,QAAQ,MAAM,CAAC,EAAE,MAAM,SAAS,KAAK,QAAQ,KAAK;EAC3D;EACA;EACA,WAAW,YAAyC,OAAO,SAAS,OAAO;EAC3E,uBAAuB,OAAO,gBAAgB;CAChD;AACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zerodep-dev/vue-tsx-pro-mobile:移动端组件库。H5 直接用;小程序经 @zerodep-dev/vue-tsx-pro-mini 渲染。
|
|
3
|
+
* 规范见 MOBILE.md。
|
|
4
|
+
*/
|
|
5
|
+
export * from './theme/tokens';
|
|
6
|
+
export * from './theme/dark';
|
|
7
|
+
import './theme/token-names';
|
|
8
|
+
export * from './config/lang';
|
|
9
|
+
export * from './config/context';
|
|
10
|
+
export * from './config/ConfigProvider';
|
|
11
|
+
export * from './utils/props';
|
|
12
|
+
export * from './utils/host';
|
|
13
|
+
export * from './capability/types';
|
|
14
|
+
export * from './capability/usePermission';
|
|
15
|
+
export * from './capability/useStorage';
|
|
16
|
+
export * from './capability/PrivacyPrompt';
|
|
17
|
+
export * from './utils/scales';
|
|
18
|
+
export * from './base/Icon';
|
|
19
|
+
export * from './base/Button';
|
|
20
|
+
export * from './layout/Divider';
|
|
21
|
+
export * from './layout/Space';
|
|
22
|
+
export * from './display/Cell';
|
|
23
|
+
export * from './display/Tag';
|
|
24
|
+
export * from './display/Badge';
|
|
25
|
+
export * from './display/Image';
|
|
26
|
+
export * from './display/SwipeCell';
|
|
27
|
+
export * from './display/Empty';
|
|
28
|
+
export * from './display/NoticeBar';
|
|
29
|
+
export * from './display/Grid';
|
|
30
|
+
export * from './display/Collapse';
|
|
31
|
+
export * from './display/Steps';
|
|
32
|
+
export * from './display/List';
|
|
33
|
+
export * from './display/Swiper';
|
|
34
|
+
export * from './display/IndexBar';
|
|
35
|
+
export * from './navigation/NavBar';
|
|
36
|
+
export * from './navigation/TabBar';
|
|
37
|
+
export * from './navigation/Tabs';
|
|
38
|
+
export * from './navigation/Sticky';
|
|
39
|
+
export * from './navigation/BackTop';
|
|
40
|
+
export * from './picker/Picker';
|
|
41
|
+
export * from './picker/DatePicker';
|
|
42
|
+
export * from './picker/Cascader';
|
|
43
|
+
export * from './picker/Calendar';
|
|
44
|
+
export * from './picker/DropdownMenu';
|
|
45
|
+
export * from './overlay/Overlay';
|
|
46
|
+
export * from './overlay/Popup';
|
|
47
|
+
export * from './overlay/ActionSheet';
|
|
48
|
+
export * from './form/context';
|
|
49
|
+
export * from './form/Form';
|
|
50
|
+
export * from './form/FormItem';
|
|
51
|
+
export * from './form/Field';
|
|
52
|
+
export * from './form/Search';
|
|
53
|
+
export * from './form/Switch';
|
|
54
|
+
export * from './form/Checkbox';
|
|
55
|
+
export * from './form/Radio';
|
|
56
|
+
export * from './form/Stepper';
|
|
57
|
+
export * from './form/Rate';
|
|
58
|
+
export * from './form/Slider';
|
|
59
|
+
export * from './form/Uploader';
|
|
60
|
+
export type { ChoiceDirections, ChoiceShapes } from './form/choice';
|
|
61
|
+
export * from './feedback/Loading';
|
|
62
|
+
export * from './feedback/Toast';
|
|
63
|
+
export * from './feedback/Dialog';
|
|
64
|
+
export * from './feedback/Notify';
|
|
65
|
+
export * from './feedback/Skeleton';
|
|
66
|
+
export * from './feedback/PullRefresh';
|