@fairys/valtio-form-basic 0.0.13 → 1.0.1

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.
@@ -3,7 +3,7 @@ import { FairysValtioFormInstance } from '../common/instance';
3
3
  import { type ReactNode } from 'react';
4
4
  import { FairysValtioFormLayoutAttrsProps } from './layout';
5
5
  import { RuleItem } from 'async-validator';
6
- export interface FairysValtioFormAttrsProps<T extends MObject<T> = object> extends FairysValtioFormLayoutAttrsProps {
6
+ export interface FairysValtioFormAttrsProps<T extends MObject<T> = Record<string, any>> extends FairysValtioFormLayoutAttrsProps {
7
7
  /**表单实例*/
8
8
  form?: FairysValtioFormInstance<T>;
9
9
  /**子元素*/
@@ -20,6 +20,12 @@ export interface FairysValtioFormAttrsProps<T extends MObject<T> = object> exten
20
20
  * - immutable:直接使用对象(注意:当传递的不是`valtio`的`proxy`对象时,会使用`valtio`中的`proxy`声明)
21
21
  */
22
22
  initFormDataType?: 'deepCopy' | 'immutable';
23
+ /**
24
+ * 表单值改变时回调
25
+ * @param path 表单项路径
26
+ * @param value 表单项值
27
+ */
28
+ onValuesChange?: (path: PropertyKey, value: any) => void;
23
29
  }
24
30
  /**
25
31
  * 表单属性处理
@@ -41,36 +47,6 @@ export const Form = (props: FormProps) => {
41
47
  }
42
48
  * ```
43
49
  */
44
- export declare function useFairysValtioForm<T extends MObject<T> = object>(props: FairysValtioFormAttrsProps<T>, ref: React.Ref<FairysValtioFormInstance<T>>): {
50
+ export declare function useFairysValtioForm<T extends MObject<T> = Record<string, any>>(props: FairysValtioFormAttrsProps<T>, ref: React.Ref<FairysValtioFormInstance<T>>): Omit<FairysValtioFormAttrsProps<T>, "initFormDataType" | "form" | "rules" | "formData" | "hideState" | "onValuesChange"> & {
45
51
  formInstance: FairysValtioFormInstance<T>;
46
- /**子元素*/
47
- children: ReactNode;
48
- gap?: string | number;
49
- title?: React.ReactNode;
50
- extra?: React.ReactNode;
51
- isAllColSpan?: boolean;
52
- className?: string;
53
- style?: React.CSSProperties;
54
- headerClassName?: string;
55
- headerStyle?: React.CSSProperties;
56
- bodyClassName?: string;
57
- bodyStyle?: React.CSSProperties;
58
- bordered?: boolean;
59
- boxShadow?: boolean;
60
- lastItemBordered?: boolean;
61
- platform?: "pc" | "rn" | "taro";
62
- colCount?: number;
63
- errorLayout?: "bottom-left" | "bottom-right" | "top-right" | "top-left" | "left-border-top" | "right-border-top";
64
- labelMode?: "left" | "top" | "between";
65
- formItemClassName?: string;
66
- formItemStyle?: React.CSSProperties;
67
- formItemLabelClassName?: string;
68
- formItemLabelStyle?: React.CSSProperties;
69
- formItemBodyClassName?: string;
70
- formItemBodyStyle?: React.CSSProperties;
71
- itemBorderType?: "bottom" | "body" | "none";
72
- itemBorderColor?: React.CSSProperties["borderColor"];
73
- isInvalidBorderRed?: boolean;
74
- isInvalidTextRed?: boolean;
75
- showColon?: boolean;
76
52
  };
@@ -5,7 +5,7 @@ import { FairysValtioFormInstance } from '../common/instance';
5
5
  import { FairysValtioFormLayoutContextOptions } from './layout';
6
6
  import { FairysValtioFormParentAttrs } from '../common/hooks';
7
7
  import { RuleItem } from 'async-validator';
8
- export interface FairysValtioFormItemAttrsProps<T extends MObject<T> = object> {
8
+ export interface FairysValtioFormItemAttrsProps<T extends MObject<T> = Record<string, any>> {
9
9
  /**平台*/
10
10
  platform?: 'pc' | 'rn' | 'taro';
11
11
  /**
@@ -77,6 +77,10 @@ export interface FairysValtioFormItemAttrsProps<T extends MObject<T> = object> {
77
77
  isJoinParentField?: boolean;
78
78
  /**校验规则*/
79
79
  rules?: RuleItem[];
80
+ /**卸载移除数据值
81
+ * @default true
82
+ */
83
+ isRemoveValueOnUnmount?: boolean;
80
84
  }
81
85
  /**
82
86
  * 处理表单表单项属性
@@ -121,8 +125,8 @@ export const FormItem = (props: FormItemProps) => {
121
125
  * ```
122
126
  *
123
127
  */
124
- export declare function useFairysValtioFormItemAttrs<T extends MObject<T> = object>(props: FairysValtioFormItemAttrsProps<T>): FairysValtioFormItemAttrsReturn<T>;
125
- export interface FairysValtioFormItemAttrsReturn<T extends MObject<T> = object> {
128
+ export declare function useFairysValtioFormItemAttrs<T extends MObject<T> = Record<string, any>>(props: FairysValtioFormItemAttrsProps<T>): FairysValtioFormItemAttrsReturn<T>;
129
+ export interface FairysValtioFormItemAttrsReturn<T extends MObject<T> = Record<string, any>> {
126
130
  /**表单项值*/
127
131
  value?: any;
128
132
  /**是否校验错误*/
@@ -207,18 +211,36 @@ export const FormItem = (props: FormItemProps) => {
207
211
  }
208
212
  * ```
209
213
  */
210
- export declare function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = object>(props: FairysValtioFormItemAttrsProps<T>): {
211
- value: unknown;
212
- error: string[];
214
+ export declare function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = Record<string, any>>(props: FairysValtioFormItemAttrsProps<T>): FairysValtioFormItemNoStyleAttrsReturn<T>;
215
+ export interface FairysValtioFormItemNoStyleAttrsReturn<T extends MObject<T> = Record<string, any>> {
216
+ /**表单项值*/
217
+ value?: any;
218
+ /**是否校验错误*/
219
+ isInvalid: boolean;
220
+ /**是否必填*/
221
+ isRequired: boolean;
222
+ /**错误信息*/
223
+ error?: string[];
224
+ /**值改变事件*/
213
225
  onValueChange: (event: any) => void;
226
+ /**表单状态*/
214
227
  state: T;
228
+ /**错误状态*/
215
229
  errorState: Record<PropertyKey, string[]>;
230
+ /**表单实例*/
216
231
  formInstance: FairysValtioFormInstance<T>;
217
- _name: string;
218
- name: string;
219
- id: string;
220
- paths: (number | symbol)[] | (string | number)[];
221
- parentName: string;
232
+ /**拼接父级字段名后得到的表单项名称*/
233
+ _name?: string;
234
+ /**表单项名称*/
235
+ name?: string;
236
+ /**表单项ID*/
237
+ id?: string;
238
+ /**表单项路径*/
239
+ paths?: (string | number)[];
240
+ /**父级字段名*/
241
+ parentName?: string;
242
+ /**表单属性名实例*/
222
243
  formAttrsNameInstance: FairysValtioFormParentAttrs;
223
- children: string | number | boolean | React.ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode>;
224
- };
244
+ /**子元素*/
245
+ children?: React.ReactNode;
246
+ }
@@ -61,7 +61,7 @@ function useFairysValtioFormItemAttrs(props) {
61
61
  const parent_isInvalidTextRed = layoutAttrs.isInvalidTextRed;
62
62
  const parent_showColon = layoutAttrs.showColon;
63
63
  const parent_platform = layoutAttrs.platform;
64
- const { name, valuePropName = 'value', getValuePath = valuePropName, getValueFromEvent, formatValue, onAfterUpdate, trigger = 'onChange', className, style, labelClassName, labelStyle, bodyClassName, bodyStyle, children, labelMode = parent_labelMode, errorLayout = parent_errorLayout, colSpan = 1, rowSpan = 1, isRequired: _isRequired, itemBorderType = parent_borderedType, attrs = {}, showColon = parent_showColon, itemBorderColor = parent_itemBorderColor, isInvalidBorderRed = parent_isInvalidBorderRed, isInvalidTextRed = parent_isInvalidTextRed, isJoinParentField = true, rules, platform = parent_platform } = props;
64
+ const { name, valuePropName = 'value', getValuePath = valuePropName, getValueFromEvent, formatValue, onAfterUpdate, trigger = 'onChange', className, style, labelClassName, labelStyle, bodyClassName, bodyStyle, children, labelMode = parent_labelMode, errorLayout = parent_errorLayout, colSpan = 1, rowSpan = 1, isRequired: _isRequired, itemBorderType = parent_borderedType, attrs = {}, showColon = parent_showColon, itemBorderColor = parent_itemBorderColor, isInvalidBorderRed = parent_isInvalidBorderRed, isInvalidTextRed = parent_isInvalidTextRed, isJoinParentField = true, rules, platform = parent_platform, isRemoveValueOnUnmount = true } = props;
65
65
  const { name: _name, paths, parentName, formAttrsNameInstance } = (0, hooks_index_js_namespaceObject.useFairysValtioFormAttrsName)({
66
66
  name,
67
67
  isJoinParentField
@@ -94,6 +94,9 @@ function useFairysValtioFormItemAttrs(props) {
94
94
  formInstance.updatedValueByPaths(_name, _value);
95
95
  if ('function' == typeof onAfterUpdate) onAfterUpdate(_value, formInstance, event);
96
96
  };
97
+ (0, external_react_namespaceObject.useEffect)(()=>()=>{
98
+ if (isRemoveValueOnUnmount) formInstance.removeValueByPaths(_name);
99
+ }, []);
97
100
  const baseControl = {
98
101
  ...attrs,
99
102
  name,
@@ -253,7 +256,7 @@ function useFairysValtioFormItemAttrs(props) {
253
256
  };
254
257
  }
255
258
  function useFairysValtioFormItemNoStyleAttrs(props) {
256
- const { name, valuePropName = 'value', getValuePath = valuePropName, getValueFromEvent, formatValue, onAfterUpdate, trigger = 'onChange', children, attrs = {}, isJoinParentField = true, rules } = props;
259
+ const { name, valuePropName = 'value', getValuePath = valuePropName, getValueFromEvent, formatValue, onAfterUpdate, trigger = 'onChange', children, attrs = {}, isJoinParentField = true, rules, isRemoveValueOnUnmount = true, isRequired: _isRequired } = props;
257
260
  const [state, errorState, formInstance] = (0, index_js_namespaceObject.useFairysValtioFormInstanceContextState)();
258
261
  const { name: _name, paths, parentName, formAttrsNameInstance } = (0, hooks_index_js_namespaceObject.useFairysValtioFormAttrsName)({
259
262
  name,
@@ -266,6 +269,7 @@ function useFairysValtioFormItemNoStyleAttrs(props) {
266
269
  ]);
267
270
  const error = errorState[_name];
268
271
  formInstance.nameToPaths[_name] = paths;
272
+ const _formItemRules = formInstance.rules?.[_name];
269
273
  (0, external_react_namespaceObject.useEffect)(()=>{
270
274
  if (Array.isArray(rules) && rules.length) formInstance.mountRules[_name] = rules;
271
275
  return ()=>{
@@ -285,6 +289,9 @@ function useFairysValtioFormItemNoStyleAttrs(props) {
285
289
  formInstance.updatedValueByPaths(_name, _value);
286
290
  if ('function' == typeof onAfterUpdate) onAfterUpdate(_value, formInstance, event);
287
291
  };
292
+ (0, external_react_namespaceObject.useEffect)(()=>()=>{
293
+ if (isRemoveValueOnUnmount) formInstance.removeValueByPaths(_name);
294
+ }, []);
288
295
  const baseControl = {
289
296
  ...attrs,
290
297
  name,
@@ -292,8 +299,26 @@ function useFairysValtioFormItemNoStyleAttrs(props) {
292
299
  [valuePropName]: value,
293
300
  [trigger]: onValueChange
294
301
  };
302
+ const isRequired = (0, external_react_namespaceObject.useMemo)(()=>{
303
+ if (_isRequired) return _isRequired;
304
+ if (Array.isArray(rules) && rules.length) return rules.some((rule)=>rule.required);
305
+ if (_formItemRules && Array.isArray(_formItemRules) && _formItemRules.length) return _formItemRules.some((rule)=>rule.required);
306
+ return false;
307
+ }, [
308
+ rules,
309
+ formInstance,
310
+ _formItemRules
311
+ ]);
312
+ const isInvalid = (0, external_react_namespaceObject.useMemo)(()=>{
313
+ if (Array.isArray(error) && error.length) return true;
314
+ return false;
315
+ }, [
316
+ error
317
+ ]);
295
318
  return {
296
319
  value,
320
+ isInvalid,
321
+ isRequired,
297
322
  error,
298
323
  onValueChange,
299
324
  state,
package/lib/form/form.js CHANGED
@@ -29,9 +29,10 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  const index_js_namespaceObject = require("../common/instance/index.js");
30
30
  const external_react_namespaceObject = require("react");
31
31
  function useFairysValtioForm(props, ref) {
32
- const { form, rules, formData, hideState, initFormDataType = 'deepCopy', ...rest } = props;
32
+ const { form, rules, formData, hideState, initFormDataType = 'deepCopy', onValuesChange, ...rest } = props;
33
33
  const formInstance = (0, index_js_namespaceObject.useFairysValtioFormInstance)(form);
34
34
  formInstance.rules = rules;
35
+ formInstance.onValuesChange = onValuesChange;
35
36
  (0, external_react_namespaceObject.useMemo)(()=>formInstance.ctor({
36
37
  formData,
37
38
  hideState,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "author": "SunLxy <1011771396@qq.com>",
4
4
  "description": "使用 valtio 实现的表单基础库, 使其更加便捷,同时支持`PC`、`H5`、`Taro`,同时也更加灵活。",
5
5
  "homepage": "https://github.com/autumn-fairy-tales/valtio-form-basic",
6
- "version": "0.0.13",
6
+ "version": "1.0.1",
7
7
  "main": "lib/index.js",
8
8
  "types": "esm/index.d.ts",
9
9
  "module": "esm/index.js",
@@ -3,11 +3,17 @@ import { createContext, useContext, useRef } from 'react';
3
3
  import { proxy, ref, snapshot, useSnapshot, unstable_getInternalStates } from 'valtio';
4
4
  import AsyncValidator, { RuleItem, ValidateFieldsError, Values } from 'async-validator';
5
5
  import { copy } from 'fast-copy';
6
- import { formatePath, get, set, isObject } from 'common/utils';
6
+ import { formatePath, get, set, isObject, removeValueByPaths } from 'common/utils';
7
7
  import { FairysValtioFormAttrsProps } from 'form/form';
8
8
 
9
9
  /**表单实例*/
10
10
  export class FairysValtioFormInstance<T extends MObject<T> = Record<string, any>> {
11
+ /**
12
+ * 表单值改变时回调
13
+ * @param path 表单项路径
14
+ * @param value 表单项值
15
+ */
16
+ onValuesChange?: (path: PropertyKey, value: any) => void;
11
17
  /***
12
18
  * 判断值是否为代理对象
13
19
  * @param value 值
@@ -46,25 +52,26 @@ export class FairysValtioFormInstance<T extends MObject<T> = Record<string, any>
46
52
  initFormDataType?: FairysValtioFormAttrsProps['initFormDataType'];
47
53
  }) => {
48
54
  const { formData, hideState, initFormDataType = 'deepCopy' } = options || {};
49
- this.mountRules = {}; //由表单项挂载规则,(根据表单项的字段存储路径对应校验规则)
50
- this.nameToPaths = {}; //表单项名称到路径映射
51
- // 如果是 isProxy,则直接赋值
52
- this.errorState = proxy<Record<PropertyKey, string[]>>({});
53
- this.hideState = proxy<Record<PropertyKey, boolean>>(hideState ? copy(hideState) : {});
55
+ this.clear();
56
+ this.updatedHideInfo(hideState ? copy(hideState) : {});
54
57
  // 判断是否是代理对象
55
58
  const isValtioProxy = this.isValtioProxy(formData);
56
59
  if (isValtioProxy) {
57
60
  if (initFormDataType === 'deepCopy') {
58
- this.state = proxy(copy(snapshot(formData)) as T);
61
+ // this.state = proxy(copy(snapshot(formData)) as T);
62
+ this.updated(copy(snapshot(formData)) as T, false);
59
63
  } else {
60
- this.state = formData as T;
64
+ // this.state = formData as T;
65
+ this.updated(formData as T, false);
61
66
  }
62
67
  } else {
63
68
  if (initFormDataType === 'deepCopy') {
64
- this.state = proxy(copy(formData || {}) as T);
69
+ // this.state = proxy(copy(formData || {}) as T);
70
+ this.updated(copy(formData || {}) as T, false);
65
71
  }
66
72
  {
67
- this.state = proxy((formData || {}) as T);
73
+ // this.state = proxy((formData || {}) as T);
74
+ this.updated((formData || {}) as T, false);
68
75
  }
69
76
  }
70
77
  };
@@ -91,7 +98,28 @@ export class FairysValtioFormInstance<T extends MObject<T> = Record<string, any>
91
98
  updatedValueByPaths = (path: PropertyKey, value: any) => {
92
99
  set(this.state, formatePath(path), value);
93
100
  this.validate([path], false);
101
+ this.onValuesChange?.(path, value);
102
+ };
103
+
104
+ /**表单项卸载移除保存值*/
105
+ removeValueByPaths = (path: PropertyKey) => {
106
+ removeValueByPaths(this.state, formatePath(path));
94
107
  };
108
+ /**
109
+ * 清理值
110
+ */
111
+ clearValue = (fields?: PropertyKey[]) => {
112
+ let _fields = fields;
113
+ if (!Array.isArray(fields)) {
114
+ _fields = Object.keys(this.state) as PropertyKey[];
115
+ }
116
+ for (let index = 0; index < _fields.length; index++) {
117
+ const field = _fields[index];
118
+ delete this.state[field];
119
+ }
120
+ return this;
121
+ };
122
+
95
123
  // ===================================================隐藏状态================================================================
96
124
  /**
97
125
  * 更新行数据的隐藏信息
@@ -154,9 +182,9 @@ export class FairysValtioFormInstance<T extends MObject<T> = Record<string, any>
154
182
  * 清理所有数据
155
183
  */
156
184
  clear = () => {
157
- this.state = proxy<T>({} as T); //表单值
158
- this.errorState = proxy<Record<PropertyKey, string[]>>({}); //错误信息
159
- this.hideState = proxy<Record<PropertyKey, boolean>>({}); //隐藏状态
185
+ this.clearValue();
186
+ this.clearHideInfo();
187
+ this.clearErrorInfo();
160
188
  this.mountRules = {}; //由表单项挂载规则,(根据表单项的字段存储路径对应校验规则)
161
189
  this.rules = {}; //表单项规则
162
190
  this.nameToPaths = {}; //表单项名称到路径映射
@@ -177,7 +205,10 @@ export class FairysValtioFormInstance<T extends MObject<T> = Record<string, any>
177
205
  * @param fields 要验证的字段(可选)
178
206
  * @param isReturn 是否返回验证结果(可选)
179
207
  */
180
- validate = async (fields?: PropertyKey[], isReturn: boolean = true): Promise<ValidateFieldsError | Values> => {
208
+ validate = async (
209
+ fields?: PropertyKey[],
210
+ isReturn: boolean = true,
211
+ ): Promise<ValidateFieldsError | Values | Partial<T>> => {
181
212
  const rules = {
182
213
  ...this.rules,
183
214
  ...this.mountRules,
@@ -251,7 +282,9 @@ export class FairysValtioFormInstance<T extends MObject<T> = Record<string, any>
251
282
  }
252
283
 
253
284
  /**声明实例*/
254
- export function useFairysValtioFormInstance<T extends MObject<T> = object>(instance?: FairysValtioFormInstance<T>) {
285
+ export function useFairysValtioFormInstance<T extends MObject<T> = Record<string, any>>(
286
+ instance?: FairysValtioFormInstance<T>,
287
+ ) {
255
288
  const ref = useRef<FairysValtioFormInstance<T>>();
256
289
  if (!ref.current) {
257
290
  if (instance) {
@@ -269,12 +302,12 @@ export const FairysValtioFormInstanceContext = createContext<FairysValtioFormIns
269
302
  );
270
303
 
271
304
  /**表单实例上下文*/
272
- export function useFairysValtioFormInstanceContext<T extends MObject<T> = object>() {
305
+ export function useFairysValtioFormInstanceContext<T extends MObject<T> = Record<string, any>>() {
273
306
  return useContext(FairysValtioFormInstanceContext) as FairysValtioFormInstance<T>;
274
307
  }
275
308
 
276
309
  /**状态取值*/
277
- export function useFairysValtioFormInstanceContextState<T extends MObject<T> = object>() {
310
+ export function useFairysValtioFormInstanceContextState<T extends MObject<T> = Record<string, any>>() {
278
311
  const instance = useFairysValtioFormInstanceContext<T>();
279
312
  const state = useSnapshot(instance.state) as T;
280
313
  const errorState = useSnapshot(instance.errorState) as Record<PropertyKey, string[]>;
@@ -288,7 +321,7 @@ export function useFairysValtioFormInstanceContextState<T extends MObject<T> = o
288
321
  }
289
322
 
290
323
  /**隐藏组件状态取值*/
291
- export function useFairysValtioFormInstanceContextHideState<T extends MObject<T> = object>() {
324
+ export function useFairysValtioFormInstanceContextHideState<T extends MObject<T> = Record<string, any>>() {
292
325
  const instance = useFairysValtioFormInstanceContext<T>();
293
326
  const hideState = useSnapshot(instance.hideState) as Record<PropertyKey, boolean>;
294
327
  return [hideState, instance, (hideState as any).__defaultValue] as [
@@ -297,3 +330,21 @@ export function useFairysValtioFormInstanceContextHideState<T extends MObject<T>
297
330
  any,
298
331
  ];
299
332
  }
333
+ /**
334
+ * 传递表单实例获取状态
335
+ */
336
+ export function useFairysValtioFormInstanceToState<T extends MObject<T> = Record<string, any>>(
337
+ formInstance: FairysValtioFormInstance<T>,
338
+ ) {
339
+ const state = useSnapshot(formInstance.state) as T;
340
+ return state as T;
341
+ }
342
+ /**
343
+ * 传递表单实例获取隐藏状态
344
+ */
345
+ export function useFairysValtioFormInstanceToHideState<T extends MObject<T> = Record<string, any>>(
346
+ formInstance: FairysValtioFormInstance<T>,
347
+ ) {
348
+ const hideState = useSnapshot(formInstance.hideState) as Record<PropertyKey, boolean>;
349
+ return hideState as Record<PropertyKey, boolean>;
350
+ }
@@ -50,6 +50,25 @@ export function get<TDefault = unknown>(value: any, segments: PropertyKey[]): TD
50
50
  return current;
51
51
  }
52
52
 
53
+ /***
54
+ * 移除值
55
+ * @param value 任意值
56
+ * @param segments 键路径
57
+ */
58
+ export function removeValueByPaths(value: any, segments: PropertyKey[]) {
59
+ // 移除字段
60
+ let current: any = value;
61
+ const lg = segments.length;
62
+ for (let index = 0; index < lg; index++) {
63
+ const key = segments[index];
64
+ if (index === lg - 1) {
65
+ delete current[key];
66
+ } else {
67
+ current = current?.[key];
68
+ }
69
+ }
70
+ }
71
+
53
72
  /***
54
73
  * 格式化路径,将路径中的数组索引转换为数字
55
74
  * @param path 路径
@@ -9,7 +9,7 @@ import { FairysValtioFormParentAttrs, useFairysValtioFormAttrsName, useId } from
9
9
  import { formatePath, get } from 'common/utils';
10
10
  import { RuleItem } from 'async-validator';
11
11
 
12
- export interface FairysValtioFormItemAttrsProps<T extends MObject<T> = object> {
12
+ export interface FairysValtioFormItemAttrsProps<T extends MObject<T> = Record<string, any>> {
13
13
  /**平台*/
14
14
  platform?: 'pc' | 'rn' | 'taro';
15
15
  /**
@@ -81,6 +81,10 @@ export interface FairysValtioFormItemAttrsProps<T extends MObject<T> = object> {
81
81
  isJoinParentField?: boolean;
82
82
  /**校验规则*/
83
83
  rules?: RuleItem[];
84
+ /**卸载移除数据值
85
+ * @default true
86
+ */
87
+ isRemoveValueOnUnmount?: boolean;
84
88
  }
85
89
 
86
90
  /**
@@ -126,7 +130,9 @@ export const FormItem = (props: FormItemProps) => {
126
130
  * ```
127
131
  *
128
132
  */
129
- export function useFairysValtioFormItemAttrs<T extends MObject<T> = object>(props: FairysValtioFormItemAttrsProps<T>) {
133
+ export function useFairysValtioFormItemAttrs<T extends MObject<T> = Record<string, any>>(
134
+ props: FairysValtioFormItemAttrsProps<T>,
135
+ ) {
130
136
  const [layoutAttrs] = useFairysValtioFormLayoutContext();
131
137
  const colCount = layoutAttrs.colCount || 1;
132
138
  const parent_borderedType = layoutAttrs.itemBorderType || 'bottom';
@@ -173,6 +179,7 @@ export function useFairysValtioFormItemAttrs<T extends MObject<T> = object>(prop
173
179
  isJoinParentField = true,
174
180
  rules,
175
181
  platform = parent_platform,
182
+ isRemoveValueOnUnmount = true,
176
183
  } = props;
177
184
 
178
185
  const {
@@ -217,6 +224,14 @@ export function useFairysValtioFormItemAttrs<T extends MObject<T> = object>(prop
217
224
  }
218
225
  };
219
226
 
227
+ useEffect(() => {
228
+ return () => {
229
+ if (isRemoveValueOnUnmount) {
230
+ formInstance.removeValueByPaths(_name);
231
+ }
232
+ };
233
+ }, []);
234
+
220
235
  /**基础组件参数*/
221
236
  const baseControl = {
222
237
  ...attrs,
@@ -420,7 +435,7 @@ export function useFairysValtioFormItemAttrs<T extends MObject<T> = object>(prop
420
435
  } as FairysValtioFormItemAttrsReturn<T>;
421
436
  }
422
437
 
423
- export interface FairysValtioFormItemAttrsReturn<T extends MObject<T> = object> {
438
+ export interface FairysValtioFormItemAttrsReturn<T extends MObject<T> = Record<string, any>> {
424
439
  /**表单项值*/
425
440
  value?: any;
426
441
  /**是否校验错误*/
@@ -507,7 +522,7 @@ export const FormItem = (props: FormItemProps) => {
507
522
  }
508
523
  * ```
509
524
  */
510
- export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = object>(
525
+ export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = Record<string, any>>(
511
526
  props: FairysValtioFormItemAttrsProps<T>,
512
527
  ) {
513
528
  const {
@@ -522,6 +537,8 @@ export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = objec
522
537
  attrs = {},
523
538
  isJoinParentField = true,
524
539
  rules,
540
+ isRemoveValueOnUnmount = true,
541
+ isRequired: _isRequired,
525
542
  } = props;
526
543
  const [state, errorState, formInstance] = useFairysValtioFormInstanceContextState<T>();
527
544
  const {
@@ -534,6 +551,8 @@ export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = objec
534
551
  const value = useMemo(() => get(state, paths), [state, paths]);
535
552
  const error = errorState[_name];
536
553
  formInstance.nameToPaths[_name] = paths;
554
+ // 使用从 Form 中设置的规则
555
+ const _formItemRules = formInstance.rules?.[_name];
537
556
 
538
557
  useEffect(() => {
539
558
  if (Array.isArray(rules) && rules.length) {
@@ -563,6 +582,15 @@ export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = objec
563
582
  onAfterUpdate(_value, formInstance, event);
564
583
  }
565
584
  };
585
+
586
+ useEffect(() => {
587
+ return () => {
588
+ if (isRemoveValueOnUnmount) {
589
+ formInstance.removeValueByPaths(_name);
590
+ }
591
+ };
592
+ }, []);
593
+
566
594
  /**基础组件参数*/
567
595
  const baseControl = {
568
596
  ...attrs,
@@ -571,8 +599,30 @@ export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = objec
571
599
  [valuePropName]: value,
572
600
  [trigger]: onValueChange,
573
601
  };
602
+
603
+ /**判断是否必填*/
604
+ const isRequired = useMemo(() => {
605
+ if (_isRequired) {
606
+ return _isRequired;
607
+ } else if (Array.isArray(rules) && rules.length) {
608
+ return rules.some((rule) => rule.required);
609
+ } else if (_formItemRules && Array.isArray(_formItemRules) && _formItemRules.length) {
610
+ return _formItemRules.some((rule) => rule.required);
611
+ }
612
+ return false;
613
+ }, [rules, formInstance, _formItemRules]);
614
+
615
+ /**校验是否存在错误信息*/
616
+ const isInvalid = useMemo(() => {
617
+ if (Array.isArray(error) && error.length) {
618
+ return true;
619
+ }
620
+ return false;
621
+ }, [error]);
574
622
  return {
575
623
  value,
624
+ isInvalid,
625
+ isRequired,
576
626
  error,
577
627
  onValueChange,
578
628
  state,
@@ -585,5 +635,38 @@ export function useFairysValtioFormItemNoStyleAttrs<T extends MObject<T> = objec
585
635
  parentName,
586
636
  formAttrsNameInstance,
587
637
  children: React.isValidElement(children) ? React.cloneElement(children, { ...baseControl }) : children,
588
- };
638
+ } as FairysValtioFormItemNoStyleAttrsReturn<T>;
639
+ }
640
+
641
+ export interface FairysValtioFormItemNoStyleAttrsReturn<T extends MObject<T> = Record<string, any>> {
642
+ /**表单项值*/
643
+ value?: any;
644
+ /**是否校验错误*/
645
+ isInvalid: boolean;
646
+ /**是否必填*/
647
+ isRequired: boolean;
648
+ /**错误信息*/
649
+ error?: string[];
650
+ /**值改变事件*/
651
+ onValueChange: (event: any) => void;
652
+ /**表单状态*/
653
+ state: T;
654
+ /**错误状态*/
655
+ errorState: Record<PropertyKey, string[]>;
656
+ /**表单实例*/
657
+ formInstance: FairysValtioFormInstance<T>;
658
+ /**拼接父级字段名后得到的表单项名称*/
659
+ _name?: string;
660
+ /**表单项名称*/
661
+ name?: string;
662
+ /**表单项ID*/
663
+ id?: string;
664
+ /**表单项路径*/
665
+ paths?: (string | number)[];
666
+ /**父级字段名*/
667
+ parentName?: string;
668
+ /**表单属性名实例*/
669
+ formAttrsNameInstance: FairysValtioFormParentAttrs;
670
+ /**子元素*/
671
+ children?: React.ReactNode;
589
672
  }
package/src/form/form.tsx CHANGED
@@ -4,7 +4,8 @@ import { useImperativeHandle, useMemo, type ReactNode, useEffect } from 'react';
4
4
  import { FairysValtioFormLayoutAttrsProps } from './layout';
5
5
  import { RuleItem } from 'async-validator';
6
6
 
7
- export interface FairysValtioFormAttrsProps<T extends MObject<T> = object> extends FairysValtioFormLayoutAttrsProps {
7
+ export interface FairysValtioFormAttrsProps<T extends MObject<T> = Record<string, any>>
8
+ extends FairysValtioFormLayoutAttrsProps {
8
9
  /**表单实例*/
9
10
  form?: FairysValtioFormInstance<T>;
10
11
  /**子元素*/
@@ -21,6 +22,12 @@ export interface FairysValtioFormAttrsProps<T extends MObject<T> = object> exten
21
22
  * - immutable:直接使用对象(注意:当传递的不是`valtio`的`proxy`对象时,会使用`valtio`中的`proxy`声明)
22
23
  */
23
24
  initFormDataType?: 'deepCopy' | 'immutable';
25
+ /**
26
+ * 表单值改变时回调
27
+ * @param path 表单项路径
28
+ * @param value 表单项值
29
+ */
30
+ onValuesChange?: (path: PropertyKey, value: any) => void;
24
31
  }
25
32
 
26
33
  /**
@@ -43,14 +50,16 @@ export const Form = (props: FormProps) => {
43
50
  }
44
51
  * ```
45
52
  */
46
- export function useFairysValtioForm<T extends MObject<T> = object>(
53
+ export function useFairysValtioForm<T extends MObject<T> = Record<string, any>>(
47
54
  props: FairysValtioFormAttrsProps<T>,
48
55
  ref: React.Ref<FairysValtioFormInstance<T>>,
49
56
  ) {
50
- const { form, rules, formData, hideState, initFormDataType = 'deepCopy', ...rest } = props;
57
+ const { form, rules, formData, hideState, initFormDataType = 'deepCopy', onValuesChange, ...rest } = props;
51
58
  const formInstance = useFairysValtioFormInstance(form);
52
59
  /**表单规则*/
53
60
  formInstance.rules = rules;
61
+ /**表单值改变时回调*/
62
+ formInstance.onValuesChange = onValuesChange;
54
63
  /**初始化表单值*/
55
64
  useMemo(() => formInstance.ctor({ formData, hideState, initFormDataType }), []);
56
65
  useImperativeHandle(ref, () => formInstance);
@@ -59,5 +68,10 @@ export function useFairysValtioForm<T extends MObject<T> = object>(
59
68
  return {
60
69
  ...rest,
61
70
  formInstance,
71
+ } as Omit<
72
+ FairysValtioFormAttrsProps<T>,
73
+ 'initFormDataType' | 'form' | 'rules' | 'formData' | 'hideState' | 'onValuesChange'
74
+ > & {
75
+ formInstance: FairysValtioFormInstance<T>;
62
76
  };
63
77
  }