@rainbow-oh/yee-c 0.8.20 → 0.9.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/dist/cjs/Form/Field.js +6 -6
- package/dist/cjs/Form/demo/field-status.d.ts +3 -0
- package/dist/cjs/Form/demo/field-status.js +116 -0
- package/dist/cjs/Form/form-store.d.ts +6 -1
- package/dist/cjs/Form/form-store.js +74 -2
- package/dist/cjs/Form/index.d.ts +1 -1
- package/dist/cjs/Form/interface.d.ts +17 -1
- package/dist/cjs/Form/style/index.less +26 -0
- package/dist/cjs/style/themes.css +1 -1
- package/dist/cjs/style/variables.css +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Form/Field.js +6 -6
- package/dist/esm/Form/demo/field-status.d.ts +3 -0
- package/dist/esm/Form/demo/field-status.js +104 -0
- package/dist/esm/Form/form-store.d.ts +6 -1
- package/dist/esm/Form/form-store.js +88 -6
- package/dist/esm/Form/index.d.ts +1 -1
- package/dist/esm/Form/interface.d.ts +17 -1
- package/dist/esm/Form/style/index.less +26 -0
- package/dist/esm/style/themes.css +1 -1
- package/dist/esm/style/variables.css +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +1 -1
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +1 -1
- package/src/Form/Field.tsx +7 -8
- package/src/Form/demo/field-status.tsx +108 -0
- package/src/Form/form-store.ts +92 -2
- package/src/Form/index.en-US.md +6 -1
- package/src/Form/index.ts +2 -0
- package/src/Form/index.zh-CN.md +6 -1
- package/src/Form/interface.ts +19 -1
- package/src/Form/style/index.less +26 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
package/src/Form/Field.tsx
CHANGED
|
@@ -44,7 +44,7 @@ const Field: React.FC<FieldProps> = (props) => {
|
|
|
44
44
|
} = props;
|
|
45
45
|
const {
|
|
46
46
|
getCallbacks,
|
|
47
|
-
|
|
47
|
+
getFieldStatus,
|
|
48
48
|
getFieldValue,
|
|
49
49
|
getFieldsValue,
|
|
50
50
|
setFieldsValue,
|
|
@@ -88,7 +88,7 @@ const Field: React.FC<FieldProps> = (props) => {
|
|
|
88
88
|
);
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
const
|
|
91
|
+
const fieldStatus = getFieldStatus?.(normalizedName);
|
|
92
92
|
|
|
93
93
|
// Normalize tooltip into { icon, ...tooltipProps }, or null when not provided.
|
|
94
94
|
// Everything except `icon` is forwarded to <Tooltip>, so any Tooltip prop
|
|
@@ -152,8 +152,7 @@ const Field: React.FC<FieldProps> = (props) => {
|
|
|
152
152
|
`${prefixCls}-item`,
|
|
153
153
|
`${prefixCls}-item-${layout}`,
|
|
154
154
|
{
|
|
155
|
-
[`${prefixCls}-item-${
|
|
156
|
-
validate && validate.status,
|
|
155
|
+
[`${prefixCls}-item-${fieldStatus?.status}`]: fieldStatus?.status,
|
|
157
156
|
},
|
|
158
157
|
className,
|
|
159
158
|
)}
|
|
@@ -200,18 +199,18 @@ const Field: React.FC<FieldProps> = (props) => {
|
|
|
200
199
|
</div>
|
|
201
200
|
</div>
|
|
202
201
|
<AnimatePresence>
|
|
203
|
-
{
|
|
202
|
+
{fieldStatus?.message && (
|
|
204
203
|
<motion.small
|
|
205
204
|
initial={{ opacity: 0, y: -10 }}
|
|
206
205
|
animate={{ opacity: 1, y: 0 }}
|
|
207
206
|
exit={{ opacity: 0, y: -10 }}
|
|
208
207
|
transition={{ duration: 0.16 }}
|
|
209
208
|
className={clsx(`${prefixCls}-item-additional`, {
|
|
210
|
-
[`${prefixCls}-item-additional-${
|
|
211
|
-
|
|
209
|
+
[`${prefixCls}-item-additional-${fieldStatus.status}`]:
|
|
210
|
+
fieldStatus.status,
|
|
212
211
|
})}
|
|
213
212
|
>
|
|
214
|
-
{
|
|
213
|
+
{fieldStatus.message}
|
|
215
214
|
</motion.small>
|
|
216
215
|
)}
|
|
217
216
|
</AnimatePresence>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Form,
|
|
4
|
+
Input,
|
|
5
|
+
Space,
|
|
6
|
+
type FieldStatusData,
|
|
7
|
+
} from '@rainbow-oh/yee-c';
|
|
8
|
+
import React, { useState } from 'react';
|
|
9
|
+
|
|
10
|
+
export default () => {
|
|
11
|
+
const [form] = Form.useForm();
|
|
12
|
+
const [currentStatus, setCurrentStatus] = useState<FieldStatusData | null>(
|
|
13
|
+
null,
|
|
14
|
+
);
|
|
15
|
+
const [submitResult, setSubmitResult] = useState('');
|
|
16
|
+
|
|
17
|
+
const applyStatus = (status: FieldStatusData | null) => {
|
|
18
|
+
form.setFieldStatus('username', status);
|
|
19
|
+
setCurrentStatus(form.getFieldStatus('username'));
|
|
20
|
+
setSubmitResult('');
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Form
|
|
25
|
+
form={form}
|
|
26
|
+
initialValues={{ username: 'demo-user' }}
|
|
27
|
+
onFinish={() => setSubmitResult('Submit succeeded')}
|
|
28
|
+
onFinishFailed={() =>
|
|
29
|
+
setSubmitResult('Submit blocked by the blocking error')
|
|
30
|
+
}
|
|
31
|
+
>
|
|
32
|
+
<Form.Field
|
|
33
|
+
name="username"
|
|
34
|
+
label="Username"
|
|
35
|
+
rules={[{ required: true, message: 'Please input your username!' }]}
|
|
36
|
+
>
|
|
37
|
+
<Input placeholder="Please input your username" />
|
|
38
|
+
</Form.Field>
|
|
39
|
+
|
|
40
|
+
<Space style={{ marginTop: 28, flexWrap: 'wrap' }}>
|
|
41
|
+
<Button
|
|
42
|
+
onClick={() =>
|
|
43
|
+
applyStatus({
|
|
44
|
+
status: 'error',
|
|
45
|
+
message: 'This username is unavailable',
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
>
|
|
49
|
+
Error
|
|
50
|
+
</Button>
|
|
51
|
+
<Button
|
|
52
|
+
onClick={() =>
|
|
53
|
+
applyStatus({
|
|
54
|
+
status: 'error',
|
|
55
|
+
message: 'This error prevents submission',
|
|
56
|
+
blocking: true,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
>
|
|
60
|
+
Blocking error
|
|
61
|
+
</Button>
|
|
62
|
+
<Button
|
|
63
|
+
onClick={() =>
|
|
64
|
+
applyStatus({
|
|
65
|
+
status: 'warning',
|
|
66
|
+
message: 'This username is commonly used',
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
>
|
|
70
|
+
Warning
|
|
71
|
+
</Button>
|
|
72
|
+
<Button
|
|
73
|
+
onClick={() =>
|
|
74
|
+
applyStatus({
|
|
75
|
+
status: 'success',
|
|
76
|
+
message: 'This username is available',
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
>
|
|
80
|
+
Success
|
|
81
|
+
</Button>
|
|
82
|
+
<Button
|
|
83
|
+
onClick={() =>
|
|
84
|
+
applyStatus({
|
|
85
|
+
status: 'info',
|
|
86
|
+
message: 'The username can be changed later',
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
Info
|
|
91
|
+
</Button>
|
|
92
|
+
<Button onClick={() => applyStatus(null)}>Clear</Button>
|
|
93
|
+
<Button type="primary" htmlType="submit">
|
|
94
|
+
Submit
|
|
95
|
+
</Button>
|
|
96
|
+
</Space>
|
|
97
|
+
|
|
98
|
+
<pre style={{ marginTop: 16 }}>
|
|
99
|
+
Current status: {JSON.stringify(currentStatus, null, 2)}
|
|
100
|
+
</pre>
|
|
101
|
+
{submitResult ? <div>{submitResult}</div> : null}
|
|
102
|
+
<div style={{ marginTop: 8, color: '#999' }}>
|
|
103
|
+
Tip: set Success, then clear the input — the rule error takes display
|
|
104
|
+
precedence over the manually set status.
|
|
105
|
+
</div>
|
|
106
|
+
</Form>
|
|
107
|
+
);
|
|
108
|
+
};
|
package/src/Form/form-store.ts
CHANGED
|
@@ -9,6 +9,7 @@ import validate from './utils/validate';
|
|
|
9
9
|
import type {
|
|
10
10
|
Callbacks,
|
|
11
11
|
FieldEntity,
|
|
12
|
+
FieldStatusData,
|
|
12
13
|
FormInstance,
|
|
13
14
|
FormListField,
|
|
14
15
|
FormListOperation,
|
|
@@ -43,6 +44,7 @@ export class FormStore {
|
|
|
43
44
|
private fieldEntities: FieldEntity[] = [];
|
|
44
45
|
private fieldMap: Map<Name, FieldEntity> = new Map();
|
|
45
46
|
private validateMessages: Map<Name, ValidateMessage[]> = new Map();
|
|
47
|
+
private fieldStatuses: Map<Name, FieldStatusData> = new Map();
|
|
46
48
|
private initialized = false;
|
|
47
49
|
private initialValues: Store = {};
|
|
48
50
|
// List key manager
|
|
@@ -114,6 +116,76 @@ export class FormStore {
|
|
|
114
116
|
return null;
|
|
115
117
|
};
|
|
116
118
|
|
|
119
|
+
getFieldStatus = (name: NamePath): FieldStatusData | null => {
|
|
120
|
+
const normalizedName = stringifyPath(getNamePath(name));
|
|
121
|
+
const manualStatus = this.fieldStatuses.get(normalizedName);
|
|
122
|
+
const validateMessage = this.validateMessages.get(normalizedName)?.[0];
|
|
123
|
+
|
|
124
|
+
// Rule errors reflect the current value and auto-clear once validation
|
|
125
|
+
// passes, so they always take display precedence — a stale manually set
|
|
126
|
+
// status must never hide why submission failed.
|
|
127
|
+
if (validateMessage?.status === 'error') {
|
|
128
|
+
return {
|
|
129
|
+
status: 'error',
|
|
130
|
+
message: validateMessage.message,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (manualStatus) {
|
|
135
|
+
return { ...manualStatus };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (validateMessage) {
|
|
139
|
+
return {
|
|
140
|
+
status: validateMessage.status,
|
|
141
|
+
message: validateMessage.message,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return null;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
setFieldStatus = (name: NamePath, status: FieldStatusData | null) => {
|
|
149
|
+
const normalizedName = stringifyPath(getNamePath(name));
|
|
150
|
+
if (!normalizedName) return;
|
|
151
|
+
|
|
152
|
+
if (status) {
|
|
153
|
+
this.fieldStatuses.set(normalizedName, { ...status });
|
|
154
|
+
} else {
|
|
155
|
+
this.fieldStatuses.delete(normalizedName);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.fieldMap.get(normalizedName)?.onStoreChange();
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Delete entries whose key equals or nests under one of `names`
|
|
162
|
+
// (no names = delete all). Keeps rule messages and manual statuses in
|
|
163
|
+
// sync with the fields that reset/clear actually touched.
|
|
164
|
+
private deleteRelatedEntries = <T>(
|
|
165
|
+
entries: Map<Name, T>,
|
|
166
|
+
names?: NamePath[],
|
|
167
|
+
): void => {
|
|
168
|
+
if (!names) {
|
|
169
|
+
entries.clear();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const normalizedNames = names.map((name) =>
|
|
174
|
+
stringifyPath(getNamePath(name)),
|
|
175
|
+
);
|
|
176
|
+
Array.from(entries.keys()).forEach((fieldName) => {
|
|
177
|
+
if (
|
|
178
|
+
normalizedNames.some((name) => isRelatedNamePath(fieldName, name))
|
|
179
|
+
) {
|
|
180
|
+
entries.delete(fieldName);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
private clearFieldStatuses = (names?: NamePath[]) => {
|
|
186
|
+
this.deleteRelatedEntries(this.fieldStatuses, names);
|
|
187
|
+
};
|
|
188
|
+
|
|
117
189
|
setFieldsValue = (newStore: Store, trigger: TRIGGER = 'onChange') => {
|
|
118
190
|
if (trigger === 'reset' || trigger === 'clear') {
|
|
119
191
|
this.store = newStore;
|
|
@@ -196,7 +268,10 @@ export class FormStore {
|
|
|
196
268
|
});
|
|
197
269
|
}
|
|
198
270
|
this.callbacks?.onReset?.();
|
|
199
|
-
|
|
271
|
+
// A named reset only clears rule messages of the fields it touched;
|
|
272
|
+
// unrelated fields keep their (still valid) validation state.
|
|
273
|
+
this.deleteRelatedEntries(this.validateMessages, name);
|
|
274
|
+
this.clearFieldStatuses(name);
|
|
200
275
|
this.groupEntities.forEach((entity) => entity.reset());
|
|
201
276
|
this.refreshField();
|
|
202
277
|
};
|
|
@@ -208,6 +283,7 @@ export class FormStore {
|
|
|
208
283
|
acc[name] = undefined;
|
|
209
284
|
return acc;
|
|
210
285
|
}, {} as Store);
|
|
286
|
+
this.clearFieldStatuses(name);
|
|
211
287
|
this.setFieldsValue(clearStore, 'clear');
|
|
212
288
|
};
|
|
213
289
|
|
|
@@ -567,7 +643,19 @@ export class FormStore {
|
|
|
567
643
|
const { onFinish, onFinishFailed } = this.callbacks;
|
|
568
644
|
const fieldErrors = await this.validateFields();
|
|
569
645
|
const groupErrors = await this.validateGroups(true);
|
|
570
|
-
const
|
|
646
|
+
const statusErrors = Array.from(this.fieldStatuses.entries())
|
|
647
|
+
.filter(
|
|
648
|
+
([, status]) => status.status === 'error' && status.blocking === true,
|
|
649
|
+
)
|
|
650
|
+
.map(
|
|
651
|
+
([name, status]): ValidateMessage => ({
|
|
652
|
+
name,
|
|
653
|
+
message: status.message ?? '',
|
|
654
|
+
value: this.getFieldValue(name),
|
|
655
|
+
status: 'error',
|
|
656
|
+
}),
|
|
657
|
+
);
|
|
658
|
+
const allErrors = [...fieldErrors, ...groupErrors, ...statusErrors];
|
|
571
659
|
if (allErrors.length === 0) {
|
|
572
660
|
onFinish?.(this.getFieldsValue());
|
|
573
661
|
} else {
|
|
@@ -580,6 +668,8 @@ export class FormStore {
|
|
|
580
668
|
getForm(): FormInstance {
|
|
581
669
|
return {
|
|
582
670
|
getFieldValidate: this.getFieldValidate,
|
|
671
|
+
getFieldStatus: this.getFieldStatus,
|
|
672
|
+
setFieldStatus: this.setFieldStatus,
|
|
583
673
|
getFieldsValue: this.getFieldsValue,
|
|
584
674
|
getFieldValue: this.getFieldValue,
|
|
585
675
|
setFieldsValue: this.setFieldsValue,
|
package/src/Form/index.en-US.md
CHANGED
|
@@ -21,6 +21,7 @@ High performance form component with data scope management.
|
|
|
21
21
|
<code src="./demo/layout.tsx" title="Layout" description="Different form layouts"></code>
|
|
22
22
|
<code src="./demo/validate.tsx" title="Validation" description="Form validation"></code>
|
|
23
23
|
<code src="./demo/async-validate.tsx" title="Async Validation" description="async validator for server-side checks like uniqueness"></code>
|
|
24
|
+
<code src="./demo/field-status.tsx" title="Dynamic Field Status" description="Set error, warning, success, and info feedback dynamically with setFieldStatus"></code>
|
|
24
25
|
<code src="./demo/list.tsx" title="Form List" description="Form list field usage"></code>
|
|
25
26
|
<code src="./demo/form-table.tsx" title="Form Table" description="Form table field usage"></code>
|
|
26
27
|
<code src="./demo/dynamic.tsx" title="Dynamic Form" description="Dynamic form items"></code>
|
|
@@ -52,12 +53,14 @@ High performance form component with data scope management.
|
|
|
52
53
|
| Property | Type | Description |
|
|
53
54
|
| --------------------- | ------------------------------------------------------------------------------------------ | ------------------------- |
|
|
54
55
|
| getFieldValidate | `(name: Name) => ValidateMessage \| null` | Get field validation (may lag before async validation completes; updates via onStoreChange) |
|
|
56
|
+
| getFieldStatus | `(name: NamePath) => FieldStatusData \| null` | Get the displayed field status; rule validation errors take display precedence, otherwise a manually set status overrides validation |
|
|
57
|
+
| setFieldStatus | `(name: NamePath, status: FieldStatusData \| null) => void` | Set field status and message dynamically, or pass `null` to clear it; only blocking errors prevent submission |
|
|
55
58
|
| getFieldValue | `(name: Name) => StoreValue` | Get field value |
|
|
56
59
|
| submit | `() => Promise<ValidateMessage[]>` | Submit form (async, triggers onFinish only after validation completes) |
|
|
57
60
|
| getFieldsValue | `() => Values` | Get all field values |
|
|
58
61
|
| setFieldsValue | `(newStore: Store, trigger?: TRIGGER) => void` | Set field values |
|
|
59
62
|
| setCallbacks | `(callbacks: Callbacks) => void` | Set callbacks |
|
|
60
|
-
| resetFields | `(name?: Name[]) => void` | Reset fields
|
|
63
|
+
| resetFields | `(name?: Name[]) => void` | Reset fields (values and validation state; passing name only affects related fields) |
|
|
61
64
|
| clearFields | `(name?: Name[]) => void` | Clear fields |
|
|
62
65
|
| validateField | `(name: Name, trigger?: 'onChange' \| 'onBlur') => Promise<ValidateMessage[]>` | Validate single field |
|
|
63
66
|
| validateFields | `(names?: Name[], trigger?: TRIGGER) => Promise<ValidateMessage[]>` | Validate multiple fields |
|
|
@@ -129,6 +132,8 @@ High performance form component with data scope management.
|
|
|
129
132
|
| Store | `Record<string, StoreValue>` |
|
|
130
133
|
| Name | `string` |
|
|
131
134
|
| NamePath | `string \| string[] \| number \| (string \| number)[]` |
|
|
135
|
+
| FieldStatus | `'error' \| 'success' \| 'warning' \| 'info'` |
|
|
136
|
+
| FieldStatusData | `{ status: FieldStatus; message?: string; blocking?: boolean }` |
|
|
132
137
|
| ValidateMessage | `{ name: NamePath \| NamePath[] \| undefined; message: string; value: unknown; status: 'error' \| 'success' \| 'warning' \| 'info' }` |
|
|
133
138
|
| FieldEntity | `{ props: fieldProps & { name?: NamePath }; onStoreChange: () => void }` |
|
|
134
139
|
| TRIGGER | `'onChange' \| 'onBlur' \| 'onSubmit' \| 'reset' \| 'clear' \| 'update'` |
|
package/src/Form/index.ts
CHANGED
package/src/Form/index.zh-CN.md
CHANGED
|
@@ -23,6 +23,7 @@ toc: 'content'
|
|
|
23
23
|
<code src="./demo/layout.tsx" title="表单布局" description="不同的表单布局"></code>
|
|
24
24
|
<code src="./demo/validate.tsx" title="表单校验" description="表单校验功能"></code>
|
|
25
25
|
<code src="./demo/async-validate.tsx" title="异步校验" description="async validator 实现服务端唯一性校验等异步场景"></code>
|
|
26
|
+
<code src="./demo/field-status.tsx" title="动态字段状态" description="通过 setFieldStatus 动态设置 error、warning、success 和 info 状态及提示"></code>
|
|
26
27
|
|
|
27
28
|
## API
|
|
28
29
|
|
|
@@ -51,12 +52,14 @@ toc: 'content'
|
|
|
51
52
|
| 属性名 | 类型 | 描述 |
|
|
52
53
|
| --------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------ |
|
|
53
54
|
| getFieldValidate | `(name: Name) => ValidateMessage \| null` | 获取字段验证(异步校验未完成时可能读到旧值,最终会随 onStoreChange 更新) |
|
|
55
|
+
| getFieldStatus | `(name: NamePath) => FieldStatusData \| null` | 获取字段当前展示状态;规则校验错误优先展示,其余情况手动设置的状态优先于校验结果 |
|
|
56
|
+
| setFieldStatus | `(name: NamePath, status: FieldStatusData \| null) => void` | 动态设置字段状态和提示,传入 `null` 清除;仅 `blocking: true` 的 error 会阻止提交 |
|
|
54
57
|
| getFieldValue | `(name: Name) => StoreValue` | 获取字段值 |
|
|
55
58
|
| submit | `() => Promise<ValidateMessage[]>` | 提交表单(异步,校验完成后才触发 onFinish) |
|
|
56
59
|
| getFieldsValue | `() => Values` | 获取所有字段值 |
|
|
57
60
|
| setFieldsValue | `(newStore: Store, trigger?: TRIGGER) => void` | 设置字段值 |
|
|
58
61
|
| setCallbacks | `(callbacks: Callbacks) => void` | 设置回调 |
|
|
59
|
-
| resetFields | `(name?: Name[]) => void` |
|
|
62
|
+
| resetFields | `(name?: Name[]) => void` | 重置字段(值与校验状态;指定 name 时只影响相关字段) |
|
|
60
63
|
| clearFields | `(name?: Name[]) => void` | 清空字段 |
|
|
61
64
|
| validateField | `(name: Name, trigger?: 'onChange' \| 'onBlur') => Promise<ValidateMessage[]>` | 校验单个字段 |
|
|
62
65
|
| validateFields | `(names?: Name[], trigger?: TRIGGER) => Promise<ValidateMessage[]>` | 校验多个字段 |
|
|
@@ -128,6 +131,8 @@ toc: 'content'
|
|
|
128
131
|
| Store | `Record<string, StoreValue>` |
|
|
129
132
|
| Name | `string` |
|
|
130
133
|
| NamePath | `string \| string[] \| number \| (string \| number)[]` |
|
|
134
|
+
| FieldStatus | `'error' \| 'success' \| 'warning' \| 'info'` |
|
|
135
|
+
| FieldStatusData | `{ status: FieldStatus; message?: string; blocking?: boolean }` |
|
|
131
136
|
| ValidateMessage | `{ name: NamePath \| NamePath[] \| undefined; message: string; value: unknown; status: 'error' \| 'success' \| 'warning' \| 'info' }` |
|
|
132
137
|
| FieldEntity | `{ props: fieldProps & { name?: NamePath }; onStoreChange: () => void }` |
|
|
133
138
|
| TRIGGER | `'onChange' \| 'onBlur' \| 'onSubmit' \| 'reset' \| 'clear' \| 'update'` |
|
package/src/Form/interface.ts
CHANGED
|
@@ -106,6 +106,15 @@ export interface FormInstance<Values = any> {
|
|
|
106
106
|
* Get field validation info
|
|
107
107
|
*/
|
|
108
108
|
getFieldValidate: (name: Name) => ValidateMessage | null;
|
|
109
|
+
/**
|
|
110
|
+
* Get the displayed field status. Rule validation errors take display
|
|
111
|
+
* precedence; otherwise a manually set status overrides validation.
|
|
112
|
+
*/
|
|
113
|
+
getFieldStatus: (name: NamePath) => FieldStatusData | null;
|
|
114
|
+
/**
|
|
115
|
+
* Set or clear a field's displayed status. Pass null to clear it.
|
|
116
|
+
*/
|
|
117
|
+
setFieldStatus: (name: NamePath, status: FieldStatusData | null) => void;
|
|
109
118
|
/**
|
|
110
119
|
* Get field value
|
|
111
120
|
*/
|
|
@@ -253,11 +262,20 @@ export type Rule = {
|
|
|
253
262
|
| Array<'onBlur' | 'onChange' | 'onSubmit'>;
|
|
254
263
|
};
|
|
255
264
|
|
|
265
|
+
export type FieldStatus = 'error' | 'success' | 'warning' | 'info';
|
|
266
|
+
|
|
267
|
+
export type FieldStatusData = {
|
|
268
|
+
status: FieldStatus;
|
|
269
|
+
message?: string;
|
|
270
|
+
/** Whether an error status should prevent form submission. */
|
|
271
|
+
blocking?: boolean;
|
|
272
|
+
};
|
|
273
|
+
|
|
256
274
|
export type ValidateMessage = {
|
|
257
275
|
name: NamePath | NamePath[] | undefined;
|
|
258
276
|
message: string;
|
|
259
277
|
value: unknown;
|
|
260
|
-
status:
|
|
278
|
+
status: FieldStatus;
|
|
261
279
|
};
|
|
262
280
|
|
|
263
281
|
export type FieldConfigurableTooltip = Omit<TooltipProps, 'children'> & {
|
|
@@ -79,6 +79,18 @@
|
|
|
79
79
|
&-error {
|
|
80
80
|
color: var(--yee-error-color);
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
&-warning {
|
|
84
|
+
color: var(--yee-warning-color);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&-success {
|
|
88
|
+
color: var(--yee-success-color);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&-info {
|
|
92
|
+
color: var(--yee-info-color);
|
|
93
|
+
}
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
// 表单校验态:在此重定义控件读取的 border / box-shadow 变量,
|
|
@@ -97,5 +109,19 @@
|
|
|
97
109
|
--yee-input-focus-bs-color: var(--yee-warning-color-border);
|
|
98
110
|
--yee-checkbox-border-color: var(--yee-warning-color);
|
|
99
111
|
}
|
|
112
|
+
|
|
113
|
+
&&-success {
|
|
114
|
+
--yee-input-border-color: var(--yee-success-color);
|
|
115
|
+
--yee-input-hover-border-color: var(--yee-success-color-hover);
|
|
116
|
+
--yee-input-focus-bs-color: var(--yee-success-color-border);
|
|
117
|
+
--yee-checkbox-border-color: var(--yee-success-color);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&&-info {
|
|
121
|
+
--yee-input-border-color: var(--yee-info-color);
|
|
122
|
+
--yee-input-hover-border-color: var(--yee-info-color-hover);
|
|
123
|
+
--yee-input-focus-bs-color: var(--yee-info-color-border);
|
|
124
|
+
--yee-checkbox-border-color: var(--yee-info-color);
|
|
125
|
+
}
|
|
100
126
|
}
|
|
101
127
|
}
|