@flowgram-vue/form 0.2.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.
Files changed (47) hide show
  1. package/LICENSE +22 -0
  2. package/dist/index.cjs +0 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +0 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +65 -0
  8. package/src/composables/index.ts +13 -0
  9. package/src/composables/use-current-field-state.ts +29 -0
  10. package/src/composables/use-current-field.ts +31 -0
  11. package/src/composables/use-field-validate.ts +25 -0
  12. package/src/composables/use-field.ts +47 -0
  13. package/src/composables/use-form-model.ts +17 -0
  14. package/src/composables/use-form-state.ts +25 -0
  15. package/src/composables/use-form.ts +16 -0
  16. package/src/composables/use-watch.ts +34 -0
  17. package/src/constants.ts +34 -0
  18. package/src/core/create-form.ts +58 -0
  19. package/src/core/field-array-model.ts +333 -0
  20. package/src/core/field-model.ts +396 -0
  21. package/src/core/form-model.ts +362 -0
  22. package/src/core/index.ts +14 -0
  23. package/src/core/path.ts +125 -0
  24. package/src/core/store.ts +33 -0
  25. package/src/core/to-field-array.ts +52 -0
  26. package/src/core/to-field.ts +80 -0
  27. package/src/core/to-form.ts +64 -0
  28. package/src/core/utils.ts +123 -0
  29. package/src/env.d.ts +10 -0
  30. package/src/index.ts +30 -0
  31. package/src/types/common.ts +6 -0
  32. package/src/types/field.ts +193 -0
  33. package/src/types/form.ts +145 -0
  34. package/src/types/index.ts +8 -0
  35. package/src/types/validate.ts +88 -0
  36. package/src/utils/dom.ts +31 -0
  37. package/src/utils/event.ts +32 -0
  38. package/src/utils/glob.ts +279 -0
  39. package/src/utils/index.ts +8 -0
  40. package/src/utils/object.ts +107 -0
  41. package/src/utils/validate.ts +42 -0
  42. package/src/vue/context.ts +12 -0
  43. package/src/vue/field-array.vue +117 -0
  44. package/src/vue/field.vue +127 -0
  45. package/src/vue/form.vue +61 -0
  46. package/src/vue/index.ts +11 -0
  47. package/src/vue/types.ts +45 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Form, FormModelState, FormState } from '../types/form';
7
+ import { FieldName, FieldValue } from '../types/field';
8
+ import { FormModel } from './form-model';
9
+
10
+ export function toForm<TValue>(model: FormModel): Form<TValue> {
11
+ const res = {
12
+ initialValues: model.initialValues,
13
+ get values() {
14
+ return model.values;
15
+ },
16
+ set values(v) {
17
+ model.values = v;
18
+ },
19
+ state: toFormState(model.state),
20
+ getValueIn: <TValue = FieldValue>(name: FieldName) => model.getValueIn(name),
21
+ setValueIn: <TValue>(name: FieldName, value: TValue) => model.setValueIn(name, value),
22
+ validate: model.validate.bind(model),
23
+ };
24
+
25
+ Object.defineProperty(res, '_formModel', {
26
+ enumerable: false,
27
+ get() {
28
+ return model;
29
+ },
30
+ });
31
+ return res as Form<TValue>;
32
+ }
33
+
34
+ export function toFormState(modelState: FormModelState): FormState {
35
+ return {
36
+ get isTouched() {
37
+ return modelState.isTouched;
38
+ },
39
+ get invalid() {
40
+ return modelState.invalid;
41
+ },
42
+ get isDirty() {
43
+ return modelState.isDirty;
44
+ },
45
+ get isValidating() {
46
+ return modelState.isValidating;
47
+ },
48
+ // get dirtyFields() {
49
+ // return modelState.dirtyFields;
50
+ // },
51
+ // get isLoading() {
52
+ // return modelState.isLoading;
53
+ // },
54
+ // get touchedFields() {
55
+ // return modelState.touchedFields;
56
+ // },
57
+ get errors() {
58
+ return modelState.errors;
59
+ },
60
+ get warnings() {
61
+ return modelState.warnings;
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { isEmpty, isEqual } from 'lodash-es';
7
+
8
+ import { Glob } from '../utils';
9
+ import { Errors, Feedback, OnFormValuesChangePayload, ValidateTrigger, Warnings } from '../types';
10
+ import { Path } from './path';
11
+
12
+ export function updateFeedbacksName(feedbacks: Feedback<any>[], name: string) {
13
+ return (feedbacks || []).map((f) => ({
14
+ ...f,
15
+ name,
16
+ }));
17
+ }
18
+
19
+ export function mergeFeedbacks<T extends Errors | Warnings>(origin?: T, source?: T) {
20
+ if (!source) {
21
+ return origin;
22
+ }
23
+ if (!origin) {
24
+ return { ...source };
25
+ }
26
+ const changed = Object.keys(source).some(
27
+ (sourceKey) => !isEqual(origin[sourceKey], source[sourceKey])
28
+ );
29
+
30
+ if (changed) {
31
+ return {
32
+ ...origin,
33
+ ...source,
34
+ };
35
+ }
36
+ return origin;
37
+ }
38
+
39
+ export function clearFeedbacks<T extends Errors | Warnings>(name: string, origin?: T) {
40
+ if (!origin) {
41
+ return origin;
42
+ }
43
+ if (name in origin) {
44
+ delete origin[name];
45
+ }
46
+ return origin;
47
+ }
48
+
49
+ export function shouldValidate(currentTrigger: ValidateTrigger, formTrigger?: ValidateTrigger) {
50
+ return currentTrigger === formTrigger;
51
+ }
52
+
53
+ export function getValidByErrors(errors: Errors | undefined) {
54
+ return errors ? Object.keys(errors).every((name) => isEmpty(errors[name])) : true;
55
+ }
56
+
57
+ export namespace FieldEventUtils {
58
+ export function shouldTriggerFieldChangeEvent(
59
+ payload: OnFormValuesChangePayload,
60
+ fieldName: string
61
+ ) {
62
+ const { name: changedName, options } = payload;
63
+
64
+ // 如果 Field 是 变更path 的 ancestor 则触发
65
+ if (Glob.isMatchOrParent(fieldName, changedName)) {
66
+ return true;
67
+ }
68
+
69
+ // 如果 Field 是 变更path 的 child 或 grandchild 有条件触发
70
+ if (new Path(changedName).isChildOrGrandChild(fieldName)) {
71
+ // 数组情况下部分子项不触发变更
72
+
73
+ // 1. 数组 append 触发的FormValuesChange 不需要触发其子 Field 的 onValueChange
74
+ if (options?.action === 'array-append') {
75
+ return !new Path(changedName).isChildOrGrandChild(fieldName);
76
+ }
77
+ // 2. 数组 splice 触发的FormValuesChange 无需触发第一个删除项前的所有子 Field 的 onValueChange
78
+ else if (options?.action === 'array-splice' && options?.indexes?.length) {
79
+ return (
80
+ (Path.compareArrayPath(
81
+ new Path(fieldName),
82
+ new Path(changedName).concat(options.indexes[0])
83
+ ) as number) >= 0
84
+ );
85
+ }
86
+
87
+ // 其余情况都需要触发
88
+ return true;
89
+ }
90
+ return false;
91
+ }
92
+
93
+ export function shouldTriggerFieldValidateWhenChange(
94
+ payload: OnFormValuesChangePayload,
95
+ fieldName: string
96
+ ) {
97
+ const { name: changedName, options } = payload;
98
+
99
+ if (options?.action === 'array-splice' || options?.action === 'array-swap') {
100
+ // const splicedIndexes = options?.indexes || [];
101
+ //
102
+ // const splicedPaths = splicedIndexes.map(index => new Path(changedName).concat(index));
103
+ // const removedPaths = Array.from({ length: splicedIndexes.length }, (_, i) =>
104
+ // new Path(changedName).concat(prevValues[changedName].length - i - 1),
105
+ // );
106
+ //
107
+ // const ignoredPathOrParentPaths = [...splicedPaths, ...removedPaths];
108
+ // // const ignoredPathOrParentPaths = splicedPaths;
109
+ // if (
110
+ // ignoredPathOrParentPaths.some(
111
+ // path => path.toString() === fieldName || path.isChildOrGrandChild(fieldName),
112
+ // )
113
+ // ) {
114
+ // return false;
115
+ // }
116
+
117
+ // splice 和 swap 都属于数组跟级别的变更,仅需触发数组field的校验, 无需校验子项
118
+ return fieldName === changedName;
119
+ }
120
+
121
+ return FieldEventUtils.shouldTriggerFieldChangeEvent(payload, fieldName);
122
+ }
123
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ declare module '*.vue' {
7
+ import type { DefineComponent } from 'vue';
8
+ const component: DefineComponent<object, object, unknown>;
9
+ export default component;
10
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './vue';
7
+ export type {
8
+ FormRenderProps,
9
+ FieldRenderProps,
10
+ FieldArrayRenderProps,
11
+ FieldState,
12
+ FormState,
13
+ Validate,
14
+ FormControl,
15
+ FieldName,
16
+ FieldError,
17
+ FieldWarning,
18
+ FormValidateReturn,
19
+ FieldValue,
20
+ FieldArray as IFieldArray,
21
+ Field as IField,
22
+ Form as IForm,
23
+ Errors,
24
+ Warnings,
25
+ } from './types';
26
+
27
+ export { ValidateTrigger, FeedbackLevel } from './types';
28
+ export { createForm, type CreateFormOptions } from './core/create-form';
29
+ export { Glob } from './utils';
30
+ export * from './core';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export type Context = any;
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import type { VNode } from 'vue';
7
+
8
+ import { Errors, FieldError, FieldWarning, Warnings } from './validate';
9
+ import { FormState } from './form';
10
+
11
+ export type NativeFieldValue = string | number | boolean | null | undefined | unknown[];
12
+
13
+ export type FieldValue = any;
14
+ export type FieldArrayValue = Array<any> | undefined;
15
+ export type FieldName = string;
16
+
17
+ export type CustomElement = Partial<HTMLElement> & {
18
+ name: FieldName;
19
+ type?: string;
20
+ value?: any;
21
+ disabled?: boolean;
22
+ checked?: boolean;
23
+ options?: HTMLOptionsCollection;
24
+ files?: FileList | null;
25
+ focus?: () => void;
26
+ };
27
+
28
+ export type FieldElement =
29
+ | HTMLInputElement
30
+ | HTMLSelectElement
31
+ | HTMLTextAreaElement
32
+ | CustomElement;
33
+
34
+ export type Ref = FieldElement;
35
+
36
+ /**
37
+ * Field render model, it's only available when Field is rendered
38
+ */
39
+ export interface Field<
40
+ TFieldValue extends FieldValue = FieldValue,
41
+ E = Event | TFieldValue
42
+ > {
43
+ /**
44
+ * Uniq key for the Field, you can use it for the child Vue component's uniq key.
45
+ */
46
+ key: string;
47
+ /**
48
+ * A function which sends the input's value to Field.
49
+ * It should be assigned to the onChange prop of the input component
50
+ * @param e It can be the new value of the field or the event sent by original dom input or checkbox component.
51
+ */
52
+ onChange: (e: E) => void;
53
+ /**
54
+ * The current value of Field
55
+ */
56
+ value: TFieldValue;
57
+ /**
58
+ * Field's name (path)
59
+ */
60
+ name: FieldName;
61
+ /**
62
+ * A function which sends the input's onFocus event to Field. It should be assigned to the input's onFocus prop.
63
+ */
64
+ onFocus?: () => void;
65
+ /**
66
+ * A function which sends the input's onBlur event to Field. It should be assigned to the input's onBlur prop.
67
+ */
68
+ onBlur?: () => void;
69
+ }
70
+
71
+ /**
72
+ * FieldArray render model, it's only available when FieldArray is rendered
73
+ */
74
+ export interface FieldArray<TFieldValue extends FieldValue = FieldValue>
75
+ extends Field<Array<TFieldValue> | undefined, Array<TFieldValue> | undefined> {
76
+ /**
77
+ * Same as native Array.map, the first param of the callback function is the child field of this FieldArray.
78
+ * @param cb callback function
79
+ */
80
+ map: <T = any>(cb: (f: Field<TFieldValue>, index: number) => T) => T[];
81
+ /**
82
+ * Append a value at the end of the array, it will create a new Field for this value as well.
83
+ * @param value the value to append
84
+ */
85
+ append: (value: TFieldValue) => Field<TFieldValue>;
86
+ /**
87
+ * @deprecated use remove instead
88
+ * Delete the value and the related field at certain index of the array.
89
+ * @param index the index of the element to delete
90
+ */
91
+ delete: (index: number) => void;
92
+ /**
93
+ * Delete the value and the related field at certain index of the array.
94
+ * @param index the index of the element to delete
95
+ */
96
+ remove: (index: number) => void;
97
+ /**
98
+ * Move an array element from one position to another.
99
+ * @param from from position
100
+ * @param to to position
101
+ */
102
+ move: (from: number, to: number) => void;
103
+ /**
104
+ * Swap the position of two elements of the array.
105
+ * @param from
106
+ * @param to
107
+ */
108
+ swap: (from: number, to: number) => void;
109
+ }
110
+
111
+ export interface FieldOptions<TValue, TFormValues = any> {
112
+ /**
113
+ * Field's name(path), it should be uniq within a form instance.
114
+ * Two Fields Rendered with the same name will link to the same part of data and field status such as errors is shared.
115
+ */
116
+ name: FieldName;
117
+ /**
118
+ * Default value of the field. Please notice that Field is a render model, so this default value will only be set when
119
+ * the field is rendered. If you want to give a default value before field rendering, please set it in the Form's defaultValue.
120
+ */
121
+ defaultValue?: TValue;
122
+ /**
123
+ * This is a render prop. A function that returns a Vue VNode and provides the ability to attach events and value into the component.
124
+ * This simplifies integrating with external controlled components with non-standard prop names. Provides field、fieldState and formState, to the child component.
125
+ * @param props
126
+ */
127
+ render?: (props: FieldRenderProps<TValue>) => VNode;
128
+ }
129
+
130
+ export interface FieldRenderProps<TValue> {
131
+ field: Field<TValue>;
132
+ fieldState: Readonly<FieldState>;
133
+ formState: Readonly<FormState>;
134
+ }
135
+
136
+ export interface FieldArrayOptions<TValue> {
137
+ /**
138
+ * Field's name(path), it should be uniq within a form instance.
139
+ * Two Fields Rendered with the same name will link to the same part of data and field status such as errors is shared.
140
+ */
141
+ name: FieldName;
142
+ /**
143
+ * Default value of the field. Please notice that Field is a render model, so this default value will only be set when
144
+ * the field is rendered. If you want to give a default value before field rendering, please set it in the Form's initialValues.
145
+ */
146
+ defaultValue?: TValue[];
147
+ /**
148
+ * This is a render prop. A function that returns a Vue VNode and provides the ability to attach events and value into the component.
149
+ * This simplifies integrating with external controlled components with non-standard prop names. Provides field、fieldState and formState, to the child component.
150
+ * @param props
151
+ */
152
+ render?: (props: FieldArrayRenderProps<TValue>) => VNode;
153
+ }
154
+
155
+ export interface FieldArrayRenderProps<TValue> {
156
+ field: FieldArray<TValue>;
157
+ fieldState: Readonly<FieldState>;
158
+ formState: Readonly<FormState>;
159
+ }
160
+
161
+ export interface UseFieldReturn {}
162
+
163
+ export interface FieldState {
164
+ /**
165
+ * If field value is invalid
166
+ */
167
+ invalid: boolean;
168
+ /**
169
+ * If field input component is touched by user
170
+ */
171
+ isTouched: boolean;
172
+ /**
173
+ * If field current value is different from the initialValue.
174
+ */
175
+ isDirty: boolean;
176
+ /**
177
+ * If field is validating.
178
+ */
179
+ isValidating: boolean;
180
+ /**
181
+ * Field errors, empty array means there is no errors.
182
+ */
183
+ errors?: FieldError[];
184
+ /**
185
+ * Field warnings, empty array means there is no warnings.
186
+ */
187
+ warnings?: FieldWarning[];
188
+ }
189
+
190
+ export interface FieldModelState extends Omit<FieldState, 'errors' | 'warnings'> {
191
+ errors?: Errors;
192
+ warnings?: Warnings;
193
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { FormModel } from '../core/form-model';
7
+ import { Errors, FormValidateReturn, Validate, ValidateTrigger, Warnings } from './validate';
8
+ import { Field, FieldArray, FieldName, FieldValue } from './field';
9
+ import { Context } from './common';
10
+
11
+ export interface FormState {
12
+ // isLoading: boolean;
13
+ /**
14
+ * If the form data is valid
15
+ */
16
+ invalid: boolean;
17
+ /**
18
+ * If the form data is different from the intialValues
19
+ */
20
+ isDirty: boolean;
21
+ /**
22
+ * If the form fields have been touched
23
+ */
24
+ isTouched: boolean;
25
+ /**
26
+ * If the form is during validation
27
+ */
28
+ isValidating: boolean;
29
+ /**
30
+ * Form errors
31
+ */
32
+ errors?: Errors;
33
+ /**
34
+ * Form warnings
35
+ */
36
+ warnings?: Warnings;
37
+ }
38
+
39
+ export interface FormModelState extends Omit<FormState, 'errors' | 'warnings'> {
40
+ errors?: Errors;
41
+ warnings?: Warnings;
42
+ }
43
+
44
+ export interface FormOptions<TValues = any> {
45
+ /**
46
+ * InitialValues of the form.
47
+ */
48
+ initialValues?: TValues;
49
+ /**
50
+ * When should the validation trigger, for example onChange or onBlur.
51
+ */
52
+ validateTrigger?: ValidateTrigger;
53
+ /**
54
+ * Form data's validation rules. It's a key value map, where the key is a pattern of data's path (or field name), the value is a validate function.
55
+ */
56
+ validate?:
57
+ | Record<string, Validate>
58
+ | ((value: TValues, ctx: Context) => Record<string, Validate>);
59
+ /**
60
+ * Custom context. It will be accessible via form instance or in validate function.
61
+ */
62
+ context?: Context;
63
+ }
64
+
65
+ export interface Form<TValues = any> {
66
+ /**
67
+ * The initialValues of the form.
68
+ */
69
+ initialValues: TValues;
70
+ /**
71
+ * Form values. Returns a deep copy of the data in the store.
72
+ */
73
+ values: TValues;
74
+ /**
75
+ * Form state
76
+ */
77
+ state: FormState;
78
+
79
+ /**
80
+ * Get value in certain path
81
+ * @param name path
82
+ */
83
+ getValueIn<TValue = FieldValue>(name: FieldName): TValue;
84
+
85
+ /**
86
+ * Set value in certain path.
87
+ * It will trigger the re-rendering of the Field Component if a Field is related to this path
88
+ * @param name path
89
+ */
90
+ setValueIn<TValue>(name: FieldName, value: TValue): void;
91
+
92
+ /**
93
+ * Trigger validate for the whole form.
94
+ */
95
+ validate: () => Promise<FormValidateReturn>;
96
+ }
97
+
98
+ export interface FormRenderProps<TValues> {
99
+ /**
100
+ * Form instance.
101
+ */
102
+ form: Form<TValues>;
103
+ }
104
+
105
+ export interface FormControl<TValues> {
106
+ _formModel: FormModel<TValues>;
107
+ getField: <
108
+ TValue = FieldValue,
109
+ TField extends Field<TValue> | FieldArray<TValue> = Field<TValue>
110
+ >(
111
+ name: FieldName
112
+ ) => Field<TValue> | FieldArray<TValue> | undefined;
113
+ /** 手动初始化form */
114
+ init: () => void;
115
+ }
116
+
117
+ export interface CreateFormReturn<TValues> {
118
+ form: Form<TValues>;
119
+ control: FormControl<TValues>;
120
+ }
121
+
122
+ export interface OnFormValuesChangeOptions {
123
+ action?: 'array-append' | 'array-splice' | 'array-swap';
124
+ indexes?: number[];
125
+ }
126
+
127
+ export interface OnFormValuesChangePayload {
128
+ values: FieldValue;
129
+ prevValues: FieldValue;
130
+ name: FieldName;
131
+ options?: OnFormValuesChangeOptions;
132
+ }
133
+
134
+ export interface OnFormValuesInitPayload {
135
+ values: FieldValue;
136
+ prevValues: FieldValue;
137
+ name: FieldName;
138
+ }
139
+
140
+ export interface OnFormValuesUpdatedPayload {
141
+ values: FieldValue;
142
+ prevValues: FieldValue;
143
+ name: FieldName;
144
+ options?: OnFormValuesChangeOptions;
145
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './field';
7
+ export * from './form';
8
+ export * from './validate';
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import type { VNode } from 'vue';
7
+ import { MaybePromise } from '@flowgram-vue/utils';
8
+
9
+ import { FieldName } from './field';
10
+ import { Context } from './common';
11
+
12
+ export enum FeedbackLevel {
13
+ Error = 'error',
14
+ Warning = 'warning',
15
+ }
16
+
17
+ export interface Feedback<FeedbackLevel> {
18
+ /**
19
+ * The data path (or field path) that generate this feedback
20
+ */
21
+ name: string;
22
+ /**
23
+ * The type of the feedback
24
+ */
25
+ type?: string;
26
+ /**
27
+ * Feedback level
28
+ */
29
+ level: FeedbackLevel;
30
+ /**
31
+ * Feedback message
32
+ */
33
+ message: string | VNode;
34
+ }
35
+
36
+ export type FieldError = Feedback<FeedbackLevel.Error>;
37
+ export type FieldWarning = Feedback<FeedbackLevel.Warning>;
38
+
39
+ export type FormErrorOptions = Omit<FieldError, 'name'>;
40
+ export type FormWarningOptions = Omit<FieldWarning, 'name'>;
41
+ export type FeedbackOptions<FeedbackLevel> = Omit<Feedback<FeedbackLevel>, 'name'>;
42
+
43
+ export type Validate<TFieldValue = any, TFormValues = any> = (props: {
44
+ /**
45
+ * Value of the data to validate
46
+ */
47
+ value: TFieldValue;
48
+ /**
49
+ * Complete form values
50
+ */
51
+ formValues: TFormValues;
52
+ /**
53
+ * The path of the data we are validating
54
+ */
55
+ name: FieldName;
56
+ /**
57
+ * The custom context set when init form
58
+ */
59
+ context: Context;
60
+ }) =>
61
+ | MaybePromise<string>
62
+ | MaybePromise<FormErrorOptions>
63
+ | MaybePromise<FormWarningOptions>
64
+ | MaybePromise<undefined>;
65
+
66
+ export function isFieldError(f: Feedback<any>): f is FieldError {
67
+ if (f.level === FeedbackLevel.Error) {
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+
73
+ export function isFieldWarning(f: Feedback<any>): f is FieldWarning {
74
+ if (f.level === FeedbackLevel.Warning) {
75
+ return true;
76
+ }
77
+ return false;
78
+ }
79
+
80
+ export type Errors = Record<FieldName, FieldError[]>;
81
+ export type Warnings = Record<FieldName, FieldWarning[]>;
82
+
83
+ export enum ValidateTrigger {
84
+ onChange = 'onChange',
85
+ onBlur = 'onBlur',
86
+ }
87
+
88
+ export type FormValidateReturn = (FieldError | FieldWarning)[];
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export interface NativeChangeEvent {
7
+ target: EventTarget & {
8
+ value?: any;
9
+ type?: string;
10
+ checked?: boolean;
11
+ };
12
+ }
13
+
14
+ export function isNativeChangeEvent(e: unknown): e is NativeChangeEvent {
15
+ return (
16
+ typeof e === 'object' &&
17
+ e !== null &&
18
+ 'target' in e &&
19
+ typeof (e as NativeChangeEvent).target === 'object'
20
+ );
21
+ }
22
+
23
+ export function isCheckBoxEvent(e: unknown): e is NativeChangeEvent {
24
+ return (
25
+ typeof e === 'object' &&
26
+ e !== null &&
27
+ 'target' in e &&
28
+ typeof (e as NativeChangeEvent).target === 'object' &&
29
+ (e as NativeChangeEvent).target.type === 'checkbox'
30
+ );
31
+ }