@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,50 @@
|
|
|
1
|
+
import type { VNodeChild } from 'vue';
|
|
2
|
+
import { type PartProps } from '../utils/props';
|
|
3
|
+
import { type FormLabelPositions, type FormValidateTriggers } from './context';
|
|
4
|
+
/** 一个出错的项 */
|
|
5
|
+
export interface FormError {
|
|
6
|
+
/** 字段名 */
|
|
7
|
+
name: string | undefined;
|
|
8
|
+
/** 消息 */
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
12
|
+
export interface FormProps extends Omit<PartProps<'div'>, 'onSubmit'> {
|
|
13
|
+
/** 全部项禁用 */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** 全部项只读 */
|
|
16
|
+
readonly?: boolean;
|
|
17
|
+
/** 标题的宽度(CSS 长度) */
|
|
18
|
+
labelWidth?: string;
|
|
19
|
+
/**
|
|
20
|
+
* 标题的位置
|
|
21
|
+
* @default 'left'
|
|
22
|
+
*/
|
|
23
|
+
labelPosition?: keyof FormLabelPositions;
|
|
24
|
+
/**
|
|
25
|
+
* 输入框里文字的对齐
|
|
26
|
+
* @default 'left'
|
|
27
|
+
*/
|
|
28
|
+
inputAlign?: 'left' | 'right';
|
|
29
|
+
/**
|
|
30
|
+
* 规则没写 trigger 时什么时候校验
|
|
31
|
+
* @default 'blur'
|
|
32
|
+
*/
|
|
33
|
+
validateTrigger?: keyof FormValidateTriggers;
|
|
34
|
+
/**
|
|
35
|
+
* 校验不通过时滚到第一个出错的项
|
|
36
|
+
* @default true
|
|
37
|
+
*/
|
|
38
|
+
scrollToError?: boolean;
|
|
39
|
+
/** 校验通过:各项的值,以字段名为键 */
|
|
40
|
+
onSubmit?: (values: Record<string, unknown>) => void;
|
|
41
|
+
/** 校验不通过 */
|
|
42
|
+
onFailed?: (details: {
|
|
43
|
+
values: Record<string, unknown>;
|
|
44
|
+
errors: FormError[];
|
|
45
|
+
}) => void;
|
|
46
|
+
/** 表单项与提交按钮 */
|
|
47
|
+
children?: VNodeChild;
|
|
48
|
+
}
|
|
49
|
+
/** 表单(div) */
|
|
50
|
+
export declare const Form: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<FormProps>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { FormContext } from "./context.js";
|
|
3
|
+
import { _component, childrenProp, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
4
|
+
import { createVNode } from "vue";
|
|
5
|
+
//#region src/form/Form.tsx
|
|
6
|
+
/** 一个出错的项 */
|
|
7
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
8
|
+
/** 节点是不是提交按钮(H5 的元素与小程序的节点都有 getAttribute) */
|
|
9
|
+
var isSubmit = (node) => {
|
|
10
|
+
const element = node;
|
|
11
|
+
return element?.getAttribute?.("type") === "submit" && element.getAttribute("aria-busy") !== "true";
|
|
12
|
+
};
|
|
13
|
+
/** 表单(div) */
|
|
14
|
+
var Form = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
15
|
+
const disabled = readonlyProp(_props, "disabled", false);
|
|
16
|
+
const readonly = readonlyProp(_props, "readonly", false);
|
|
17
|
+
const labelWidth = readonlyProp(_props, "labelWidth");
|
|
18
|
+
const labelPosition = readonlyProp(_props, "labelPosition", "left");
|
|
19
|
+
const inputAlign = readonlyProp(_props, "inputAlign", "left");
|
|
20
|
+
const validateTrigger = readonlyProp(_props, "validateTrigger", "blur");
|
|
21
|
+
const scrollToError = readonlyProp(_props, "scrollToError", true);
|
|
22
|
+
const onSubmit = readonlyProp(_props, "onSubmit");
|
|
23
|
+
const onFailed = readonlyProp(_props, "onFailed");
|
|
24
|
+
const children = childrenProp(_props);
|
|
25
|
+
const rest = restProps(_props, _ctx, false);
|
|
26
|
+
const items = [];
|
|
27
|
+
const values = () => {
|
|
28
|
+
const result = {};
|
|
29
|
+
for (const item of items) if (item.name !== void 0) result[item.name] = item.value;
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
const submit = async () => {
|
|
33
|
+
const errors = [];
|
|
34
|
+
let first;
|
|
35
|
+
(await Promise.all(items.map((item) => item.validate("submit")))).forEach((message, index) => {
|
|
36
|
+
if (message === void 0) return;
|
|
37
|
+
first ??= items[index];
|
|
38
|
+
errors.push({
|
|
39
|
+
name: items[index].name,
|
|
40
|
+
message
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
if (!errors.length) return onSubmit.value?.(values());
|
|
44
|
+
if (scrollToError.value) first?.scrollIntoView();
|
|
45
|
+
onFailed.value?.({
|
|
46
|
+
values: values(),
|
|
47
|
+
errors
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
FormContext.provide({
|
|
51
|
+
get disabled() {
|
|
52
|
+
return disabled.value;
|
|
53
|
+
},
|
|
54
|
+
get readonly() {
|
|
55
|
+
return readonly.value;
|
|
56
|
+
},
|
|
57
|
+
get labelWidth() {
|
|
58
|
+
return labelWidth.value;
|
|
59
|
+
},
|
|
60
|
+
get labelPosition() {
|
|
61
|
+
return labelPosition.value;
|
|
62
|
+
},
|
|
63
|
+
get inputAlign() {
|
|
64
|
+
return inputAlign.value;
|
|
65
|
+
},
|
|
66
|
+
get validateTrigger() {
|
|
67
|
+
return validateTrigger.value;
|
|
68
|
+
},
|
|
69
|
+
register(item) {
|
|
70
|
+
items.push(item);
|
|
71
|
+
return () => items.splice(items.indexOf(item), 1);
|
|
72
|
+
},
|
|
73
|
+
submit: () => void submit()
|
|
74
|
+
});
|
|
75
|
+
const onClick = (event) => {
|
|
76
|
+
for (let node = event.target; node && node !== event.currentTarget; node = node.parentNode ?? node.parent) if (isSubmit(node)) return void submit();
|
|
77
|
+
};
|
|
78
|
+
return () => createVNode("div", mergeAttrs({}, rest, { onClick }), [children.value]);
|
|
79
|
+
}, {
|
|
80
|
+
name: "Form",
|
|
81
|
+
props: [
|
|
82
|
+
"disabled",
|
|
83
|
+
"readonly",
|
|
84
|
+
"labelWidth",
|
|
85
|
+
"labelPosition",
|
|
86
|
+
"inputAlign",
|
|
87
|
+
"validateTrigger",
|
|
88
|
+
"scrollToError",
|
|
89
|
+
"onSubmit",
|
|
90
|
+
"onFailed"
|
|
91
|
+
]
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
export { Form };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=Form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Form.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode"],"sources":["../../src/form/Form.tsx"],"sourcesContent":["/**\n * 表单:收集里面各项(Field、FormItem)的值,点 `type=\"submit\"` 的按钮(或在输入框里按键盘的确认键)时\n * 按顺序校验,都通过时 onSubmit 收到 { 字段名: 值 },否则滚到第一个出错的项、onFailed 收到错误列表。\n *\n * <Form onSubmit={(values) => save(values)}>\n * <CellGroup inset>\n * <Field name=\"phone\" label=\"手机号\" _value={phone} type=\"tel\" rules={[{ required: true }, { pattern: /^1\\d{10}$/, message: '手机号格式不对' }]} />\n * <Field name=\"code\" label=\"验证码\" _value={code} rules={[{ required: true }]} />\n * </CellGroup>\n * <Button type=\"submit\" fullWidth>登录</Button>\n * </Form>\n *\n * 不渲染原生 form(小程序里没有):提交按钮由这里认出来,H5 与小程序一样。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport type { VNodeChild } from 'vue';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport {\n FormContext,\n type FormItemHandle,\n type FormLabelPositions,\n type FormValidateTriggers,\n} from './context';\n\n/** 一个出错的项 */\nexport interface FormError {\n /** 字段名 */\n name: string | undefined;\n /** 消息 */\n message: string;\n}\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface FormProps extends Omit<PartProps<'div'>, 'onSubmit'> {\n /** 全部项禁用 */\n disabled?: boolean;\n /** 全部项只读 */\n readonly?: boolean;\n /** 标题的宽度(CSS 长度) */\n labelWidth?: string;\n /**\n * 标题的位置\n * @default 'left'\n */\n labelPosition?: keyof FormLabelPositions;\n /**\n * 输入框里文字的对齐\n * @default 'left'\n */\n inputAlign?: 'left' | 'right';\n /**\n * 规则没写 trigger 时什么时候校验\n * @default 'blur'\n */\n validateTrigger?: keyof FormValidateTriggers;\n /**\n * 校验不通过时滚到第一个出错的项\n * @default true\n */\n scrollToError?: boolean;\n /** 校验通过:各项的值,以字段名为键 */\n onSubmit?: (values: Record<string, unknown>) => void;\n /** 校验不通过 */\n onFailed?: (details: { values: Record<string, unknown>; errors: FormError[] }) => void;\n /** 表单项与提交按钮 */\n children?: VNodeChild;\n}\n\n/** 节点是不是提交按钮(H5 的元素与小程序的节点都有 getAttribute) */\nconst isSubmit = (node: unknown) => {\n const element = node as { getAttribute?: (name: string) => string | null } | null;\n return (\n element?.getAttribute?.('type') === 'submit' && element.getAttribute('aria-busy') !== 'true'\n );\n};\n\n/** 表单(div) */\nexport const Form = _component(\n ({\n disabled = false,\n readonly = false,\n labelWidth,\n labelPosition = 'left',\n inputAlign = 'left',\n validateTrigger = 'blur',\n scrollToError = true,\n onSubmit,\n onFailed,\n children,\n ...rest\n }: FormProps) => {\n const items: FormItemHandle[] = [];\n const values = () => {\n const result: Record<string, unknown> = {};\n for (const item of items) if (item.name !== undefined) result[item.name] = item.value;\n return result;\n };\n const submit = async () => {\n const errors: FormError[] = [];\n let first: FormItemHandle | undefined;\n // 按登记的顺序(即页面上的顺序)一起校验\n const messages = await Promise.all(items.map((item) => item.validate('submit')));\n messages.forEach((message, index) => {\n if (message === undefined) return;\n first ??= items[index];\n errors.push({ name: items[index]!.name, message });\n });\n if (!errors.length) return onSubmit?.(values());\n if (scrollToError) void first?.scrollIntoView();\n onFailed?.({ values: values(), errors });\n };\n FormContext.provide({\n get disabled() {\n return disabled;\n },\n get readonly() {\n return readonly;\n },\n get labelWidth() {\n return labelWidth;\n },\n get labelPosition() {\n return labelPosition;\n },\n get inputAlign() {\n return inputAlign;\n },\n get validateTrigger() {\n return validateTrigger;\n },\n register(item) {\n items.push(item);\n return () => items.splice(items.indexOf(item), 1);\n },\n submit: () => void submit(),\n });\n // 点了提交按钮:从点到的元素往上找,到表单为止\n const onClick = (event: Event) => {\n for (\n let node = event.target as { parentNode?: unknown; parent?: unknown } | null;\n node && node !== event.currentTarget;\n node = (node.parentNode ?? node.parent) as typeof node\n )\n if (isSubmit(node)) return void submit();\n };\n return () => <div {...mergeAttrs<'div'>({}, rest, { onClick })}>{children}</div>;\n },\n);\n"],"mappings":";;;;;;;;AAqEA,IAAM,YAAY,SAAkB;CAClC,MAAM,UAAU;CAChB,OACE,SAAS,eAAe,MAAM,MAAM,YAAY,QAAQ,aAAa,WAAW,MAAM;AAE1F;;AAGA,IAAa,OAAO,0BAClB,QAAA,SAYiB;CAAA,MAAA,WAAAA,aAAA,QAAA,YAXJ,KAAK;CAAA,MAAA,WAAAA,aAAA,QAAA,YACL,KAAK;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,gBAAAA,aAAA,QAAA,iBAEA,MAAM;CAAA,MAAA,aAAAA,aAAA,QAAA,cACT,MAAM;CAAA,MAAA,kBAAAA,aAAA,QAAA,mBACD,MAAM;CAAA,MAAA,gBAAAA,aAAA,QAAA,iBACR,IAAI;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MAIjB,OAAIC,UAAA,QAAA,MAAA,KAAA;CAEP,MAAM,QAA0B,CAAA;CAChC,MAAM,eAAe;EACnB,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,KAAK,QAAQ,KAAK;EAChF,OAAO;CACT;CACA,MAAM,SAAS,YAAY;EACzB,MAAM,SAAsB,CAAA;EAC5B,IAAI;EAGJ,CAAA,MADuB,QAAQ,IAAI,MAAM,KAAK,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,EAAA,CACtE,SAAS,SAAS,UAAU;GACnC,IAAI,YAAY,KAAA,GAAW;GAC3B,UAAU,MAAM;GAChB,OAAO,KAAK;IAAE,MAAM,MAAM,MAAM,CAAE;IAAM;GAAQ,CAAC;EACnD,CAAC;EACD,IAAI,CAAC,OAAO,QAAQ,OAAO,SAAA,QAAW,OAAO,CAAC;EAC9C,IAAA,cAAA,OAAmB,OAAY,eAAe;EAC9C,SAAA,QAAW;GAAE,QAAQ,OAAO;GAAG;EAAO,CAAC;CACzC;CACA,YAAY,QAAQ;EAClB,IAAI,WAAW;GACb,OAAA,SAAA;EACF;EACA,IAAI,WAAW;GACb,OAAA,SAAA;EACF;EACA,IAAI,aAAa;GACf,OAAA,WAAA;EACF;EACA,IAAI,gBAAgB;GAClB,OAAA,cAAA;EACF;EACA,IAAI,aAAa;GACf,OAAA,WAAA;EACF;EACA,IAAI,kBAAkB;GACpB,OAAA,gBAAA;EACF;EACA,SAAS,MAAM;GACb,MAAM,KAAK,IAAI;GACf,aAAa,MAAM,OAAO,MAAM,QAAQ,IAAI,GAAG,CAAC;EAClD;EACA,cAAc,KAAK,OAAO;CAC5B,CAAC;CAED,MAAM,WAAW,UAAiB;EAChC,KACE,IAAI,OAAO,MAAM,QACjB,QAAQ,SAAS,MAAM,eACvB,OAAQ,KAAK,cAAc,KAAK,QAEhC,IAAI,SAAS,IAAI,GAAG,OAAO,KAAK,OAAO;CAC3C;CACA,aAAOC,YAAA,OAAe,WAAkB,CAAC,GAAG,MAAM,EAAE,QAAQ,CAAC,GAAC,CAAA,SAAA,KAAA,CAAA;AAChE,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
import { type FormLabelPositions, type FormRule } from './context';
|
|
5
|
+
/** 表单项的布局(Field 共用) */
|
|
6
|
+
export interface FormItemLayout {
|
|
7
|
+
/** 标题 */
|
|
8
|
+
label?: VNodeChild;
|
|
9
|
+
/** 标题前加红色星号;有必填规则时自动加 */
|
|
10
|
+
required?: boolean;
|
|
11
|
+
/** 控件下面的说明(没有错误时显示) */
|
|
12
|
+
help?: VNodeChild;
|
|
13
|
+
/** 错误消息:给了就显示(优先于校验的结果) */
|
|
14
|
+
error?: string;
|
|
15
|
+
/** 标题的位置;不给时随 Form */
|
|
16
|
+
labelPosition?: keyof FormLabelPositions;
|
|
17
|
+
/** 标题的宽度(CSS 长度);不给时随 Form,默认 6.2em */
|
|
18
|
+
labelWidth?: string;
|
|
19
|
+
/** 标题与控件垂直居中(默认顶端对齐,说明多行时标题仍在第一行) */
|
|
20
|
+
center?: boolean;
|
|
21
|
+
/** 标题的部件属性 */
|
|
22
|
+
propsLabel?: PartProps<'div'>;
|
|
23
|
+
/** 控件与消息那一栏的部件属性 */
|
|
24
|
+
propsBody?: PartProps<'div'>;
|
|
25
|
+
/** 错误消息(或说明)的部件属性 */
|
|
26
|
+
propsMessage?: PartProps<'div'>;
|
|
27
|
+
}
|
|
28
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
29
|
+
export interface FormItemProps extends FormItemLayout, PartProps<'div', 'title'> {
|
|
30
|
+
/** 字段名:在 Form 里时提交的值以它为键 */
|
|
31
|
+
name?: string;
|
|
32
|
+
/** 校验规则 */
|
|
33
|
+
rules?: FormRule[];
|
|
34
|
+
/** 当前的值:校验与提交时读它(控件自己的绑定照常写在控件上) */
|
|
35
|
+
value?: unknown;
|
|
36
|
+
/** 控件 */
|
|
37
|
+
children?: VNodeChild;
|
|
38
|
+
}
|
|
39
|
+
/** FormItem 的样式:root、top、center、label、required、body、control、help、error */
|
|
40
|
+
export declare const formItemStyles: Readonly<{
|
|
41
|
+
body: css.CompiledStyles;
|
|
42
|
+
center: css.CompiledStyles;
|
|
43
|
+
control: css.CompiledStyles;
|
|
44
|
+
error: css.CompiledStyles;
|
|
45
|
+
help: css.CompiledStyles;
|
|
46
|
+
label: css.CompiledStyles;
|
|
47
|
+
required: css.CompiledStyles;
|
|
48
|
+
root: css.CompiledStyles;
|
|
49
|
+
top: css.CompiledStyles;
|
|
50
|
+
}>;
|
|
51
|
+
/** 表单项的外层:标题 + 控件 + 消息;Field 也用它画 */
|
|
52
|
+
export declare function renderFormItem(layout: FormItemLayout & {
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
required: boolean;
|
|
55
|
+
}, attrs: Record<string, unknown> | undefined, control: VNodeChild, ref?: (element: unknown) => void): import("@zerodep-dev/vue-tsx-pro-tsx/jsx-runtime").JSX.Element;
|
|
56
|
+
/** 表单项(div > 标题 + 控件 + 消息) */
|
|
57
|
+
export declare const FormItem: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<FormItemProps>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { dataAttrs, hasContent, mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { dividerImage, dividerLayout } from "../theme/divider.js";
|
|
3
|
+
import { useForm, useFormItem } from "./context.js";
|
|
4
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
5
|
+
import { _component, childrenProp, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
6
|
+
import { createTextVNode, createVNode } from "vue";
|
|
7
|
+
//#region src/form/FormItem.tsx
|
|
8
|
+
/** 表单项的布局(Field 共用) */
|
|
9
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
10
|
+
/** FormItem 的样式:root、top、center、label、required、body、control、help、error */
|
|
11
|
+
var formItemStyles = css.create({
|
|
12
|
+
root: {
|
|
13
|
+
boxSizing: "border-box",
|
|
14
|
+
display: "flex",
|
|
15
|
+
alignItems: "flex-start",
|
|
16
|
+
gap: "_md",
|
|
17
|
+
width: "100%",
|
|
18
|
+
minHeight: "_control",
|
|
19
|
+
paddingBlock: "0.7em",
|
|
20
|
+
paddingInline: "_lg",
|
|
21
|
+
fontSize: "_md",
|
|
22
|
+
lineHeight: "1.5",
|
|
23
|
+
color: "_text",
|
|
24
|
+
backgroundColor: "_surface",
|
|
25
|
+
backgroundImage: {
|
|
26
|
+
default: dividerImage,
|
|
27
|
+
":last-child": "none"
|
|
28
|
+
},
|
|
29
|
+
...dividerLayout
|
|
30
|
+
},
|
|
31
|
+
top: {
|
|
32
|
+
flexDirection: "column",
|
|
33
|
+
alignItems: "stretch",
|
|
34
|
+
gap: "0.4em"
|
|
35
|
+
},
|
|
36
|
+
center: { alignItems: "center" },
|
|
37
|
+
label: {
|
|
38
|
+
flex: "none",
|
|
39
|
+
overflowWrap: "anywhere",
|
|
40
|
+
color: {
|
|
41
|
+
default: "_text",
|
|
42
|
+
"[data-disabled]": "_textDisabled"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
required: {
|
|
46
|
+
color: "_dangerText",
|
|
47
|
+
marginInlineEnd: "0.15em"
|
|
48
|
+
},
|
|
49
|
+
body: {
|
|
50
|
+
flex: "1 1 auto",
|
|
51
|
+
minWidth: 0
|
|
52
|
+
},
|
|
53
|
+
control: {
|
|
54
|
+
display: "flex",
|
|
55
|
+
alignItems: "center",
|
|
56
|
+
gap: "_sm",
|
|
57
|
+
minHeight: "1.5em"
|
|
58
|
+
},
|
|
59
|
+
help: {
|
|
60
|
+
marginBlockStart: "0.25em",
|
|
61
|
+
fontSize: "_xs",
|
|
62
|
+
color: "_textMuted"
|
|
63
|
+
},
|
|
64
|
+
error: { color: "_dangerText" }
|
|
65
|
+
});
|
|
66
|
+
/** 表单项的外层:标题 + 控件 + 消息;Field 也用它画 */
|
|
67
|
+
function renderFormItem(layout, attrs, control, ref) {
|
|
68
|
+
const top = layout.labelPosition === "top";
|
|
69
|
+
const message = layout.error ?? layout.help;
|
|
70
|
+
return createVNode("div", mergeJsxProps(mergeAttrs({ sx: [
|
|
71
|
+
formItemStyles.root,
|
|
72
|
+
top && formItemStyles.top,
|
|
73
|
+
layout.center && !top && formItemStyles.center
|
|
74
|
+
] }, attrs, dataAttrs({
|
|
75
|
+
"data-error": layout.error !== void 0,
|
|
76
|
+
"data-disabled": layout.disabled
|
|
77
|
+
})), { "ref": ref }), [hasContent(layout.label) && createVNode("div", mergeAttrs({
|
|
78
|
+
sx: formItemStyles.label,
|
|
79
|
+
style: top ? void 0 : { width: layout.labelWidth ?? "6.2em" }
|
|
80
|
+
}, layout.propsLabel, dataAttrs({ "data-disabled": layout.disabled })), [layout.required && createVNode("span", mergeJsxProps(mergeAttrs({ sx: formItemStyles.required }), { "aria-hidden": "true" }), [createTextVNode("*")]), layout.label]), createVNode("div", mergeAttrs({ sx: formItemStyles.body }, layout.propsBody), [createVNode("div", mergeAttrs({ sx: formItemStyles.control }), [control]), hasContent(message) && createVNode("div", mergeAttrs({ sx: [formItemStyles.help, layout.error !== void 0 && formItemStyles.error] }, layout.propsMessage, { role: layout.error !== void 0 ? "alert" : void 0 }), [message])])]);
|
|
81
|
+
}
|
|
82
|
+
/** 表单项(div > 标题 + 控件 + 消息) */
|
|
83
|
+
var FormItem = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
84
|
+
const name = readonlyProp(_props, "name");
|
|
85
|
+
const rules = readonlyProp(_props, "rules");
|
|
86
|
+
const value = readonlyProp(_props, "value");
|
|
87
|
+
const label = readonlyProp(_props, "label");
|
|
88
|
+
const required = readonlyProp(_props, "required");
|
|
89
|
+
const help = readonlyProp(_props, "help");
|
|
90
|
+
const error = readonlyProp(_props, "error");
|
|
91
|
+
const labelPosition = readonlyProp(_props, "labelPosition");
|
|
92
|
+
const labelWidth = readonlyProp(_props, "labelWidth");
|
|
93
|
+
const center = readonlyProp(_props, "center", false);
|
|
94
|
+
const children = childrenProp(_props);
|
|
95
|
+
const propsLabel = readonlyProp(_props, "propsLabel");
|
|
96
|
+
const propsBody = readonlyProp(_props, "propsBody");
|
|
97
|
+
const propsMessage = readonlyProp(_props, "propsMessage");
|
|
98
|
+
const rest = restProps(_props, _ctx, false);
|
|
99
|
+
const form = useForm();
|
|
100
|
+
let element = null;
|
|
101
|
+
const item = useFormItem({
|
|
102
|
+
name: () => name.value,
|
|
103
|
+
rules: () => rules.value,
|
|
104
|
+
value: () => value.value,
|
|
105
|
+
label: () => label.value,
|
|
106
|
+
element: () => element
|
|
107
|
+
});
|
|
108
|
+
return () => renderFormItem({
|
|
109
|
+
label: label.value,
|
|
110
|
+
required: required.value ?? item.required,
|
|
111
|
+
help: help.value,
|
|
112
|
+
error: error.value ?? item.error,
|
|
113
|
+
labelPosition: labelPosition.value ?? form?.labelPosition,
|
|
114
|
+
labelWidth: labelWidth.value ?? form?.labelWidth,
|
|
115
|
+
center: center.value,
|
|
116
|
+
disabled: form?.disabled,
|
|
117
|
+
propsLabel: propsLabel.value,
|
|
118
|
+
propsBody: propsBody.value,
|
|
119
|
+
propsMessage: propsMessage.value
|
|
120
|
+
}, rest, children.value, (node) => element = node);
|
|
121
|
+
}, {
|
|
122
|
+
name: "FormItem",
|
|
123
|
+
props: [
|
|
124
|
+
"name",
|
|
125
|
+
"rules",
|
|
126
|
+
"value",
|
|
127
|
+
"label",
|
|
128
|
+
"required",
|
|
129
|
+
"help",
|
|
130
|
+
"error",
|
|
131
|
+
"labelPosition",
|
|
132
|
+
"labelWidth",
|
|
133
|
+
"center",
|
|
134
|
+
"propsLabel",
|
|
135
|
+
"propsBody",
|
|
136
|
+
"propsMessage"
|
|
137
|
+
]
|
|
138
|
+
});
|
|
139
|
+
//#endregion
|
|
140
|
+
export { FormItem, formItemStyles, renderFormItem };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=FormItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormItem.js","names":["_createVNode","_mergeProps","_createTextVNode","_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps"],"sources":["../../src/form/FormItem.tsx"],"sourcesContent":["/**\n * 表单项:左边(或上面)标题,右边任意控件,下面是说明或错误消息。输入框用 Field;开关、单选、步进器这类控件放进 FormItem。\n *\n * <FormItem label=\"消息通知\" name=\"notify\" value={notify}>\n * <Switch _checked={notify} />\n * </FormItem>\n * <FormItem label=\"配送方式\" name=\"delivery\" value={delivery} rules={[{ required: true, message: '请选择配送方式' }]}>\n * <RadioGroup _value={delivery} direction=\"horizontal\">…</RadioGroup>\n * </FormItem>\n *\n * 有 name 时登记到 Form:提交时值以 name 为键,按 rules 校验;单独用时 error 由使用方给。\n */\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 { dividerImage, dividerLayout } from '../theme/divider';\nimport { dataAttrs, hasContent, mergeAttrs, type PartProps } from '../utils/props';\nimport { useForm, useFormItem, type FormLabelPositions, type FormRule } from './context';\n\n/** 表单项的布局(Field 共用) */\nexport interface FormItemLayout {\n /** 标题 */\n label?: VNodeChild;\n /** 标题前加红色星号;有必填规则时自动加 */\n required?: boolean;\n /** 控件下面的说明(没有错误时显示) */\n help?: VNodeChild;\n /** 错误消息:给了就显示(优先于校验的结果) */\n error?: string;\n /** 标题的位置;不给时随 Form */\n labelPosition?: keyof FormLabelPositions;\n /** 标题的宽度(CSS 长度);不给时随 Form,默认 6.2em */\n labelWidth?: string;\n /** 标题与控件垂直居中(默认顶端对齐,说明多行时标题仍在第一行) */\n center?: boolean;\n /** 标题的部件属性 */\n propsLabel?: PartProps<'div'>;\n /** 控件与消息那一栏的部件属性 */\n propsBody?: PartProps<'div'>;\n /** 错误消息(或说明)的部件属性 */\n propsMessage?: PartProps<'div'>;\n}\n\n/** 未声明的属性落到根元素(div)上 */\nexport interface FormItemProps extends FormItemLayout, PartProps<'div', 'title'> {\n /** 字段名:在 Form 里时提交的值以它为键 */\n name?: string;\n /** 校验规则 */\n rules?: FormRule[];\n /** 当前的值:校验与提交时读它(控件自己的绑定照常写在控件上) */\n value?: unknown;\n /** 控件 */\n children?: VNodeChild;\n}\n\n/** FormItem 的样式:root、top、center、label、required、body、control、help、error */\nexport const formItemStyles = css.create({\n root: {\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'flex-start',\n gap: '_md',\n width: '100%',\n minHeight: '_control',\n paddingBlock: '0.7em',\n paddingInline: '_lg',\n fontSize: '_md',\n lineHeight: '1.5',\n color: '_text',\n backgroundColor: '_surface',\n // 细线同 Cell:从左边内边距处画到右边,最后一项不画\n backgroundImage: {\n default: dividerImage,\n ':last-child': 'none',\n },\n ...dividerLayout,\n },\n top: { flexDirection: 'column', alignItems: 'stretch', gap: '0.4em' },\n center: { alignItems: 'center' },\n label: {\n flex: 'none',\n overflowWrap: 'anywhere',\n color: { default: '_text', '[data-disabled]': '_textDisabled' },\n },\n required: { color: '_dangerText', marginInlineEnd: '0.15em' },\n body: { flex: '1 1 auto', minWidth: 0 },\n control: { display: 'flex', alignItems: 'center', gap: '_sm', minHeight: '1.5em' },\n help: { marginBlockStart: '0.25em', fontSize: '_xs', color: '_textMuted' },\n error: { color: '_dangerText' },\n});\n\n/** 表单项的外层:标题 + 控件 + 消息;Field 也用它画 */\nexport function renderFormItem(\n layout: FormItemLayout & { disabled?: boolean; required: boolean },\n attrs: Record<string, unknown> | undefined,\n control: VNodeChild,\n ref?: (element: unknown) => void,\n) {\n const top = layout.labelPosition === 'top';\n const message = layout.error ?? layout.help;\n return (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n formItemStyles.root,\n top && formItemStyles.top,\n layout.center && !top && formItemStyles.center,\n ],\n },\n attrs,\n dataAttrs({ 'data-error': layout.error !== undefined, 'data-disabled': layout.disabled }),\n )}\n ref={ref}\n >\n {hasContent(layout.label) && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: formItemStyles.label,\n style: top ? undefined : { width: layout.labelWidth ?? '6.2em' },\n },\n layout.propsLabel,\n dataAttrs({ 'data-disabled': layout.disabled }),\n )}\n >\n {layout.required && (\n <span {...mergeAttrs<'span'>({ sx: formItemStyles.required })} aria-hidden=\"true\">\n *\n </span>\n )}\n {layout.label}\n </div>\n )}\n <div {...mergeAttrs<'div'>({ sx: formItemStyles.body }, layout.propsBody)}>\n <div {...mergeAttrs<'div'>({ sx: formItemStyles.control })}>{control}</div>\n {hasContent(message) && (\n <div\n {...mergeAttrs<'div'>(\n { sx: [formItemStyles.help, layout.error !== undefined && formItemStyles.error] },\n layout.propsMessage,\n { role: layout.error !== undefined ? 'alert' : undefined },\n )}\n >\n {message}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/** 表单项(div > 标题 + 控件 + 消息) */\nexport const FormItem = _component(\n ({\n name,\n rules,\n value,\n label,\n required,\n help,\n error,\n labelPosition,\n labelWidth,\n center = false,\n children,\n propsLabel,\n propsBody,\n propsMessage,\n ...rest\n }: FormItemProps) => {\n const form = useForm();\n let element: unknown = null;\n const item = useFormItem({\n name: () => name,\n rules: () => rules,\n value: () => value,\n label: () => label,\n element: () => element,\n });\n return () =>\n renderFormItem(\n {\n label,\n required: required ?? item.required,\n help,\n error: error ?? item.error,\n labelPosition: labelPosition ?? form?.labelPosition,\n labelWidth: labelWidth ?? form?.labelWidth,\n center,\n disabled: form?.disabled,\n propsLabel,\n propsBody,\n propsMessage,\n },\n rest,\n children,\n (node) => (element = node),\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { VNodeChild } from 'vue';
|
|
2
|
+
import { type PartProps } from '../utils/props';
|
|
3
|
+
import type { ChoiceManaged, ChoiceValue } from './Checkbox';
|
|
4
|
+
import { type ChoiceDirections, type ChoiceShapes } from './choice';
|
|
5
|
+
/** 未声明的属性落到按钮上 */
|
|
6
|
+
export interface RadioProps extends PartProps<'button', ChoiceManaged> {
|
|
7
|
+
/** 这一项的值 */
|
|
8
|
+
name: ChoiceValue;
|
|
9
|
+
/** 形状;不给时随组,默认圆 */
|
|
10
|
+
shape?: keyof ChoiceShapes;
|
|
11
|
+
/** 禁用 */
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
/** 文字 */
|
|
14
|
+
children?: VNodeChild;
|
|
15
|
+
/** 框的部件属性 */
|
|
16
|
+
propsBox?: PartProps<'span'>;
|
|
17
|
+
/** 文字的部件属性 */
|
|
18
|
+
propsLabel?: PartProps<'span'>;
|
|
19
|
+
}
|
|
20
|
+
/** 单选框(button[role=radio] > 框 + 文字);放在 RadioGroup 里 */
|
|
21
|
+
export declare const Radio: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<RadioProps>;
|
|
22
|
+
/** 未声明的属性落到组的 div 上 */
|
|
23
|
+
export interface RadioGroupProps extends Omit<PartProps<'div', 'role'>, 'onChange'> {
|
|
24
|
+
/** 选中的值;双向绑定写 `_value={x}` */
|
|
25
|
+
_value?: ChoiceValue;
|
|
26
|
+
/**
|
|
27
|
+
* 排列
|
|
28
|
+
* @default 'vertical'
|
|
29
|
+
*/
|
|
30
|
+
direction?: keyof ChoiceDirections;
|
|
31
|
+
/** 全部禁用 */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
/** 各项的形状 */
|
|
34
|
+
shape?: keyof ChoiceShapes;
|
|
35
|
+
/** 选了另一项 */
|
|
36
|
+
onChange?: (value: ChoiceValue) => void;
|
|
37
|
+
/** 各个 Radio */
|
|
38
|
+
children?: VNodeChild;
|
|
39
|
+
}
|
|
40
|
+
/** 单选组(div[role=radiogroup]) */
|
|
41
|
+
export declare const RadioGroup: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<RadioGroupProps>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { useForm } from "./context.js";
|
|
3
|
+
import { choiceStyles, renderChoice } from "./choice.js";
|
|
4
|
+
import { _component, bindable, childrenProp, createContext, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
+
import { createVNode } from "vue";
|
|
6
|
+
//#region src/form/Radio.tsx
|
|
7
|
+
/** 单选组提供给各项的 */
|
|
8
|
+
var RadioGroupContext = createContext("RadioGroup");
|
|
9
|
+
/** 未声明的属性落到按钮上 */
|
|
10
|
+
/** 单选框(button[role=radio] > 框 + 文字);放在 RadioGroup 里 */
|
|
11
|
+
var Radio = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
12
|
+
const name = readonlyProp(_props, "name");
|
|
13
|
+
const shape = readonlyProp(_props, "shape");
|
|
14
|
+
const disabled = readonlyProp(_props, "disabled");
|
|
15
|
+
const children = childrenProp(_props);
|
|
16
|
+
const propsBox = readonlyProp(_props, "propsBox");
|
|
17
|
+
const propsLabel = readonlyProp(_props, "propsLabel");
|
|
18
|
+
const rest = restProps(_props, _ctx, false);
|
|
19
|
+
const form = useForm();
|
|
20
|
+
const group = RadioGroupContext.inject();
|
|
21
|
+
return () => {
|
|
22
|
+
const off = disabled.value ?? (group.disabled || form?.disabled || false);
|
|
23
|
+
return renderChoice({
|
|
24
|
+
role: "radio",
|
|
25
|
+
checked: group.value === name.value,
|
|
26
|
+
disabled: off,
|
|
27
|
+
shape: shape.value ?? group.shape ?? "round",
|
|
28
|
+
onToggle: () => !form?.readonly && group.select(name.value)
|
|
29
|
+
}, rest, children.value, {
|
|
30
|
+
propsBox: propsBox.value,
|
|
31
|
+
propsLabel: propsLabel.value
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
}, {
|
|
35
|
+
name: "Radio",
|
|
36
|
+
props: [
|
|
37
|
+
"name",
|
|
38
|
+
"shape",
|
|
39
|
+
"disabled",
|
|
40
|
+
"propsBox",
|
|
41
|
+
"propsLabel"
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
/** 未声明的属性落到组的 div 上 */
|
|
45
|
+
/** 单选组(div[role=radiogroup]) */
|
|
46
|
+
var RadioGroup = /*#__PURE__*/ _component((_props2, _ctx2) => {
|
|
47
|
+
const value = bindable(_props2, "_value", () => void 0, _ctx2.emit);
|
|
48
|
+
const direction = readonlyProp(_props2, "direction", "vertical");
|
|
49
|
+
const disabled = readonlyProp(_props2, "disabled", false);
|
|
50
|
+
const shape = readonlyProp(_props2, "shape");
|
|
51
|
+
const onChange = readonlyProp(_props2, "onChange");
|
|
52
|
+
const children = childrenProp(_props2);
|
|
53
|
+
const rest = restProps(_props2, _ctx2, false);
|
|
54
|
+
RadioGroupContext.provide({
|
|
55
|
+
get value() {
|
|
56
|
+
return value.value;
|
|
57
|
+
},
|
|
58
|
+
get disabled() {
|
|
59
|
+
return disabled.value;
|
|
60
|
+
},
|
|
61
|
+
get shape() {
|
|
62
|
+
return shape.value;
|
|
63
|
+
},
|
|
64
|
+
select(name) {
|
|
65
|
+
if (name === value.value) return;
|
|
66
|
+
value.value = name;
|
|
67
|
+
onChange.value?.(name);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return () => createVNode("div", mergeAttrs({ sx: [choiceStyles.group, direction.value === "horizontal" && choiceStyles.horizontal] }, rest, { role: "radiogroup" }), [children.value]);
|
|
71
|
+
}, {
|
|
72
|
+
name: "RadioGroup",
|
|
73
|
+
props: [
|
|
74
|
+
"_value",
|
|
75
|
+
"onUpdate:_value",
|
|
76
|
+
"direction",
|
|
77
|
+
"disabled",
|
|
78
|
+
"shape",
|
|
79
|
+
"onChange"
|
|
80
|
+
]
|
|
81
|
+
});
|
|
82
|
+
//#endregion
|
|
83
|
+
export { Radio, RadioGroup };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=Radio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Radio.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vtp_bindable","_createVNode"],"sources":["../../src/form/Radio.tsx"],"sourcesContent":["/**\n * 单选框与单选组:从几个互斥的选项里选一个(配送方式、支付方式)。\n *\n * <RadioGroup _value={delivery} direction=\"horizontal\">\n * <Radio name=\"express\">快递</Radio>\n * <Radio name=\"pickup\">到店自提</Radio>\n * </RadioGroup>\n *\n * 外观同复选框(圆框里打勾);name 是选项的值。\n */\nimport { _component, createContext } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport type { VNodeChild } from 'vue';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { ChoiceManaged, ChoiceValue } from './Checkbox';\nimport { choiceStyles, renderChoice, type ChoiceDirections, type ChoiceShapes } from './choice';\nimport { useForm } from './context';\n\n/** 单选组提供给各项的 */\ninterface RadioGroupState {\n readonly value: ChoiceValue | undefined;\n readonly disabled: boolean;\n readonly shape: keyof ChoiceShapes | undefined;\n select(name: ChoiceValue): void;\n}\n\nconst RadioGroupContext = createContext<RadioGroupState>('RadioGroup');\n\n/** 未声明的属性落到按钮上 */\nexport interface RadioProps extends PartProps<'button', ChoiceManaged> {\n /** 这一项的值 */\n name: ChoiceValue;\n /** 形状;不给时随组,默认圆 */\n shape?: keyof ChoiceShapes;\n /** 禁用 */\n disabled?: boolean;\n /** 文字 */\n children?: VNodeChild;\n /** 框的部件属性 */\n propsBox?: PartProps<'span'>;\n /** 文字的部件属性 */\n propsLabel?: PartProps<'span'>;\n}\n\n/** 单选框(button[role=radio] > 框 + 文字);放在 RadioGroup 里 */\nexport const Radio = _component(\n ({ name, shape, disabled, children, propsBox, propsLabel, ...rest }: RadioProps) => {\n const form = useForm();\n const group = RadioGroupContext.inject();\n return () => {\n const off = disabled ?? (group.disabled || form?.disabled || false);\n return renderChoice(\n {\n role: 'radio',\n checked: group.value === name,\n disabled: off,\n shape: shape ?? group.shape ?? 'round',\n onToggle: () => !form?.readonly && group.select(name),\n },\n rest,\n children,\n { propsBox, propsLabel },\n );\n };\n },\n);\n\n/** 未声明的属性落到组的 div 上 */\nexport interface RadioGroupProps extends Omit<PartProps<'div', 'role'>, 'onChange'> {\n /** 选中的值;双向绑定写 `_value={x}` */\n _value?: ChoiceValue;\n /**\n * 排列\n * @default 'vertical'\n */\n direction?: keyof ChoiceDirections;\n /** 全部禁用 */\n disabled?: boolean;\n /** 各项的形状 */\n shape?: keyof ChoiceShapes;\n /** 选了另一项 */\n onChange?: (value: ChoiceValue) => void;\n /** 各个 Radio */\n children?: VNodeChild;\n}\n\n/** 单选组(div[role=radiogroup]) */\nexport const RadioGroup = _component(\n ({\n _value: value = _bindable<ChoiceValue | undefined>(undefined),\n direction = 'vertical',\n disabled = false,\n shape,\n onChange,\n children,\n ...rest\n }: RadioGroupProps) => {\n RadioGroupContext.provide({\n get value() {\n return value;\n },\n get disabled() {\n return disabled;\n },\n get shape() {\n return shape;\n },\n select(name) {\n if (name === value) return;\n value = name;\n onChange?.(name);\n },\n });\n return () => (\n <div\n {...mergeAttrs<'div'>(\n { sx: [choiceStyles.group, direction === 'horizontal' && choiceStyles.horizontal] },\n rest,\n { role: 'radiogroup' },\n )}\n >\n {children}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;AAyBA,IAAM,oBAAoB,cAA+B,YAAY;;;AAmBrE,IAAa,QAAQ,0BACnB,QAAA,SAAoF;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MAAA,WAAAD,aAAA,QAAA,UAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAvB,OAAIE,UAAA,QAAA,MAAA,KAAA;CAC/D,MAAM,OAAO,QAAQ;CACrB,MAAM,QAAQ,kBAAkB,OAAO;CACvC,aAAa;EACX,MAAM,MAAM,SAAA,UAAa,MAAM,YAAY,MAAM,YAAY;EAC7D,OAAO,aACL;GACE,MAAM;GACN,SAAS,MAAM,UAAK,KAAA;GACpB,UAAU;GACV,OAAO,MAAA,SAAS,MAAM,SAAS;GAC/B,gBAAgB,CAAC,MAAM,YAAY,MAAM,OAAM,KAAA,KAAK;EACtD,GACA,MAAI,SAAA,OAEJ;GAAE,UAAQ,SAAA;GAAE,YAAU,WAAA;EAAC,CACzB;CACF;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH;;;AAsBA,IAAa,aAAa,0BACxB,SAAA,UAQuB;CAAA,MAAA,QAAAC,SAAA,SAAA,gBAP8B,KAAA,GAAS,MAAA,IAAA;CAAA,MAAA,YAAAH,aAAA,SAAA,aAChD,UAAU;CAAA,MAAA,WAAAA,aAAA,SAAA,YACX,KAAK;CAAA,MAAA,QAAAA,aAAA,SAAA,OAAA;CAAA,MAAA,WAAAA,aAAA,SAAA,UAAA;CAAA,MAAA,WAAAC,aAAA,OAAA;CAAA,MAIb,OAAIC,UAAA,SAAA,OAAA,KAAA;CAEP,kBAAkB,QAAQ;EACxB,IAAI,QAAQ;GACV,OAAA,MAAA;EACF;EACA,IAAI,WAAW;GACb,OAAA,SAAA;EACF;EACA,IAAI,QAAQ;GACV,OAAA,MAAA;EACF;EACA,OAAO,MAAM;GACX,IAAI,SAAI,MAAA,OAAY;GACpB,MAAA,QAAQ;GACR,SAAA,QAAW,IAAI;EACjB;CACF,CAAC;CACD,aAAOE,YAAA,OAEC,WACF,EAAE,IAAI,CAAC,aAAa,OAAO,UAAA,UAAc,gBAAgB,aAAa,UAAU,EAAE,GAClF,MACA,EAAE,MAAM,aAAa,CACvB,GAAC,CAAA,SAAA,KAAA,CAAA;AAKP,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import { type IconData } from '../base/Icon';
|
|
3
|
+
import { type PartProps } from '../utils/props';
|
|
4
|
+
/** 未声明的属性落到根元素(div)上 */
|
|
5
|
+
export interface RateProps extends Omit<PartProps<'div', 'role' | 'tabindex'>, 'onChange'> {
|
|
6
|
+
/** 分数;双向绑定写 `_value={x}` */
|
|
7
|
+
_value?: number;
|
|
8
|
+
/**
|
|
9
|
+
* 几颗星
|
|
10
|
+
* @default 5
|
|
11
|
+
*/
|
|
12
|
+
count?: number;
|
|
13
|
+
/** 可以选半颗 */
|
|
14
|
+
allowHalf?: boolean;
|
|
15
|
+
/** 只读:只展示 */
|
|
16
|
+
readonly?: boolean;
|
|
17
|
+
/** 禁用 */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* 图标
|
|
21
|
+
* @default Star
|
|
22
|
+
*/
|
|
23
|
+
icon?: IconData;
|
|
24
|
+
/**
|
|
25
|
+
* 选中部分的色调
|
|
26
|
+
* @default 'warning'
|
|
27
|
+
*/
|
|
28
|
+
tone?: 'warning' | 'danger' | 'accent';
|
|
29
|
+
/** 分数变了 */
|
|
30
|
+
onChange?: (value: number) => void;
|
|
31
|
+
/** 每颗星的部件属性 */
|
|
32
|
+
propsItem?: PartProps<'span'>;
|
|
33
|
+
}
|
|
34
|
+
/** Rate 的样式:root、item、void、各色调、half */
|
|
35
|
+
export declare const rateStyles: Readonly<{
|
|
36
|
+
accent: css.CompiledStyles;
|
|
37
|
+
danger: css.CompiledStyles;
|
|
38
|
+
half: css.CompiledStyles;
|
|
39
|
+
item: css.CompiledStyles;
|
|
40
|
+
root: css.CompiledStyles;
|
|
41
|
+
void: css.CompiledStyles;
|
|
42
|
+
warning: css.CompiledStyles;
|
|
43
|
+
}>;
|
|
44
|
+
/** 评分(div[role=slider] > 各颗星) */
|
|
45
|
+
export declare const Rate: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<RateProps>;
|