Skip to content

Commit e4789d2

Browse files
committed
feat: add form preserve demo
1 parent d7cfd27 commit e4789d2

File tree

7 files changed

+113
-43
lines changed

7 files changed

+113
-43
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use client';
2+
3+
import type { ComponentProps } from 'react';
4+
import { useState } from 'react';
5+
import { Button, Card, Form, FormField, Input, useForm } from 'soybean-react-ui';
6+
7+
const CustomInput = (props: ComponentProps<typeof Input>) => {
8+
console.log('props', props);
9+
return <Input {...props} />;
10+
};
11+
12+
const Preserve = () => {
13+
const [form] = useForm();
14+
15+
const [show, setShow] = useState(true);
16+
17+
function toggle() {
18+
setShow(!show);
19+
}
20+
21+
return (
22+
<Card title="Preserve">
23+
<Form
24+
className="w-[480px] max-sm:w-full space-y-4"
25+
form={form}
26+
>
27+
{show && (
28+
<FormField
29+
label="Username"
30+
name="username"
31+
>
32+
<CustomInput placeholder="Username" />
33+
</FormField>
34+
)}
35+
36+
<div className="flex gap-x-2">
37+
<Button onClick={toggle}>Toggle</Button>
38+
<Button type="submit">Reset</Button>
39+
</div>
40+
</Form>
41+
</Card>
42+
);
43+
};
44+
45+
export default Preserve;

playground/src/app/(demo)/form/modules/Reset.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import React from 'react';
4-
import { Button, ButtonGroup, Card, Form, FormField, Input, useForm } from 'soybean-react-ui';
4+
import { Button, Card, Form, FormField, Input, useForm } from 'soybean-react-ui';
55

66
type Inputs = {
77
info: { city: string; company: string };

playground/src/app/(demo)/form/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Default from './modules/Default';
22
import FieldChange from './modules/FieldChange';
3+
import Preserve from './modules/Preserve';
34
import Reset from './modules/Reset';
45
import UseWatch from './modules/UseWatch';
56
import Validate from './modules/validate';
@@ -19,6 +20,8 @@ const FormPage = () => {
1920
<UseWatch />
2021

2122
<Reset />
23+
24+
<Preserve />
2225
</div>
2326
);
2427
};

primitives/filed-form /src/Field.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,12 @@ function Field<Values = any>(props: InternalFieldProps<Values>) {
4646
validateTrigger: fieldValidateTrigger
4747
} = fieldContext as unknown as InternalFormInstance<Values>;
4848

49-
const { dispatch, getInitialValue, registerField } = getInternalHooks();
49+
const { dispatch, registerField } = getInternalHooks();
5050

5151
const isControlled = controlMode === 'controlled';
5252

5353
const mergedValidateTrigger = validateTrigger || fieldValidateTrigger;
5454

55-
const initiValue = getInitialValue(name) || initialValue;
56-
5755
const validateTriggerList: string[] = toArray(mergedValidateTrigger);
5856

5957
const make =
@@ -72,11 +70,9 @@ function Field<Values = any>(props: InternalFieldProps<Values>) {
7270
{} as Record<string, (...args: any[]) => void>
7371
);
7472

75-
const value = getFieldValue(name);
73+
const value = getFieldValue(name) || initialValue;
7674

77-
const valueProps = isControlled
78-
? { [valuePropName]: value }
79-
: { [`default${capitalize(valuePropName)}`]: initiValue };
75+
const valueProps = isControlled ? { [valuePropName]: value } : { [`default${capitalize(valuePropName)}`]: value };
8076

8177
const controlledProps = omitUndefined({
8278
[trigger]: name

primitives/filed-form /src/Form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33

44
import type { ComponentPropsWithoutRef, ComponentRef, ElementType, HTMLProps, Ref } from 'react';
55
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
6+
import type { DeepPartial } from 'skyroc-type-utils';
67

78
import type { FormInstance, InternalFormContext, InternalFormInstance, RegisterCallbackOptions } from './FieldContext';
89
import { FieldContextProvider } from './FieldContext';
910
import type { ValidateMessages } from './form-core/validate';
1011
import useForm from './useForm';
11-
import { DeepPartial } from 'skyroc-type-utils';
1212

1313
interface FormBaseProps<Values = any> extends RegisterCallbackOptions<Values> {
1414
children?: React.ReactNode;

primitives/filed-form /src/createStore.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type { FieldEntity } from './types/field';
1414
import type { Callbacks, Store, StoreValue } from './types/formStore';
1515
import NameMap from './utils/NameMap';
1616
import { get } from './utils/get';
17-
import { set } from './utils/set';
17+
import { set, unset } from './utils/set';
1818
import type { NamePath, PathTuple } from './utils/util';
1919
import { anyOn, isOn, isUnderPrefix, keyOfName, microtask } from './utils/util';
2020

@@ -112,7 +112,7 @@ class FormStore {
112112
// ------------------------------------------------
113113
private isMergedPreserve = (fieldPreserve?: boolean) => {
114114
const mergedPreserve = fieldPreserve || this._preserve;
115-
return mergedPreserve || true;
115+
return mergedPreserve;
116116
};
117117

118118
private setCallbacks = (c: Callbacks) => {
@@ -287,17 +287,22 @@ class FormStore {
287287
private registerField = (entity: FieldEntity) => {
288288
const name = keyOfName(entity.name);
289289

290-
this._fieldEntities.push({
291-
...entity,
292-
name
293-
});
290+
const preserve = this.isMergedPreserve(entity.preserve);
291+
292+
const oldEntity = this._fieldEntities.find(e => e.name === name);
294293

295-
const initialValue = this.getInitialValue(name);
294+
if (!oldEntity) {
295+
this._fieldEntities.push({
296+
...entity,
297+
name
298+
});
296299

297-
if (isNil(initialValue)) {
298-
this.updateStore(set(this._store, name, entity.initialValue));
300+
const initialValue = this.getInitialValue(name);
299301

300-
this._initial = set(this._initial, name, entity.initialValue);
302+
if (isNil(initialValue)) {
303+
this.updateStore(set(this._store, name, entity.initialValue));
304+
this._initial = set(this._initial, name, entity.initialValue);
305+
}
301306
}
302307

303308
const listeners = this._exactListeners.get(name);
@@ -309,9 +314,19 @@ class FormStore {
309314
}
310315

311316
return () => {
312-
this._fieldEntities = this._fieldEntities.filter(e => e.name !== name);
317+
if (!preserve) {
318+
this._fieldEntities = this._fieldEntities.filter(e => e.name !== name);
319+
this._initial = unset(this._initial, name);
320+
this._store = unset(this._store, name);
321+
}
313322
this._exactListeners.delete(name);
314323
this._prefixListeners.delete(name);
324+
this._errors.delete(name);
325+
this._warnings.delete(name);
326+
this._touched.delete(name);
327+
this._dirty.delete(name);
328+
this._validating.delete(name);
329+
this._validated.delete(name);
315330
};
316331
};
317332

primitives/type-utils/src/utils.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,23 @@ type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...0[]];
4949
* type P1 = AllPaths<FormValues>;
5050
* // => "age" | "code" | "phone"|"info" | "info.age" | "info.city" | "info.name" | "info.pl" | "info.pl.age" | `info2.${number}.age` | `info2.${number}.city` | `info2.${number}.name`
5151
*/
52-
export type AllPaths<T, Index extends number = number, P extends string = '', Depth extends number = 6> =
53-
[Depth] extends [never] ? never :
54-
T extends Primitive ? (P extends '' ? never : P) :
55-
T extends readonly (infer U)[]
56-
? (P extends '' ? never : P) | AllPaths<U, Index, Join<P, `${Index}`>, Prev[Depth]>
57-
: T extends object
58-
? (P extends '' ? never : P) | {
59-
[K in Extract<keyof T, string>]: AllPaths<T[K], Index, Join<P, K>, Prev[Depth]>
60-
}[Extract<keyof T, string>]
61-
: never;
52+
export type AllPaths<T, Index extends number = number, P extends string = '', Depth extends number = 6> = [
53+
Depth
54+
] extends [never]
55+
? never
56+
: T extends Primitive
57+
? P extends ''
58+
? never
59+
: P
60+
: T extends readonly (infer U)[]
61+
? (P extends '' ? never : P) | AllPaths<U, Index, Join<P, `${Index}`>, Prev[Depth]>
62+
: T extends object
63+
?
64+
| (P extends '' ? never : P)
65+
| {
66+
[K in Extract<keyof T, string>]: AllPaths<T[K], Index, Join<P, K>, Prev[Depth]>;
67+
}[Extract<keyof T, string>]
68+
: never;
6269

6370
type OptionalIfObject<T> = T extends object ? (T extends readonly any[] ? T : { [K in keyof T]?: T[K] }) : T;
6471

@@ -140,9 +147,8 @@ type BuildShape<T, P extends string> = P extends `${infer K}.${infer R}`
140147
: never
141148
: P extends `${infer K}`
142149
? K extends keyof T
143-
? T[K] extends readonly (infer U)[] // ← 命中数组本身
144-
? // 元素若是对象 => 深可选对象数组;若是原始类型 => 原样元素数组
145-
Wrap<Extract<K, string>, Array<U extends object ? DeepOptional<U> : U>>
150+
? T[K] extends readonly (infer U)[]
151+
? Wrap<Extract<K, string>, Array<U extends object ? DeepOptional<U> : U>>
146152
: T[K] extends object
147153
? Wrap<Extract<K, string>, DeepOptional<T[K]>>
148154
: Wrap<Extract<K, string>, T[K]>
@@ -167,20 +173,25 @@ export type ShapeFromPaths<T, Ps extends readonly string[]> = Ps extends []
167173
? T
168174
: MergeUnion<Ps[number] extends infer P ? (P extends string ? BuildShape<T, P> : never) : never>;
169175

170-
type PathsShape<T, Index extends number = number, P extends string = '', Depth extends number = 6> =
171-
[Depth] extends [never] ? never :
172-
T extends Primitive
173-
? (P extends '' ? {} : { [K in P]: true })
176+
type PathsShape<T, Index extends number = number, P extends string = '', Depth extends number = 6> = [Depth] extends [
177+
never
178+
]
179+
? never
180+
: T extends Primitive
181+
? P extends ''
182+
? {}
183+
: { [K in P]: true }
174184
: T extends readonly (infer U)[]
175-
? (P extends '' ? {} : { [K in P]: true }) &
176-
PathsShape<U, Index, Join<P, `${Index}`>, Prev[Depth]>
185+
? (P extends '' ? {} : { [K in P]: true }) & PathsShape<U, Index, Join<P, `${Index}`>, Prev[Depth]>
177186
: T extends object
178187
? (P extends '' ? {} : { [K in P]: true }) &
179-
MergeUnion<{ [K in Extract<keyof T, string>]: PathsShape<T[K], Index, Join<P, K>, Prev[Depth]> }[Extract<keyof T, string>]>
188+
MergeUnion<
189+
{ [K in Extract<keyof T, string>]: PathsShape<T[K], Index, Join<P, K>, Prev[Depth]> }[Extract<
190+
keyof T,
191+
string
192+
>]
193+
>
180194
: {};
181195

182-
183196
export type AllPathsShape<T> = MergeUnion<PathsShape<T>>;
184197
export type AllPathsKeys<T> = keyof AllPathsShape<T> & string;
185-
186-

0 commit comments

Comments
 (0)