Skip to content

Commit 7f07276

Browse files
committed
feat: replace "AllPaths" with "AllPathsKeys"
1 parent 45e70fe commit 7f07276

File tree

6 files changed

+92
-45
lines changed

6 files changed

+92
-45
lines changed

packages/ui/src/components/form/index.ts

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

33
export {
4+
ComputedField as FormComputedField,
45
Form,
56
List as FormList,
67
useFieldError,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const Reset = () => {
2424
};
2525

2626
const handleResetUsername = () => {
27-
form.resetFields('password');
27+
form.resetFields(['password']);
2828
};
2929

3030
return (

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Inputs = {
1414
const ValidateOnlyDemo = () => {
1515
const [form] = useForm<Inputs>();
1616

17-
const fieldsState = useFieldsState(form, ['email', 'username'], { warnings: true });
17+
const fieldsState = useFieldsState(form, ['email', 'username'], { mask: { warnings: true } });
1818

1919
async function validateOnly() {
2020
await form.validateFields();

primitives/filed-form /src/FieldContext.ts

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { createContext, useContext, useEffect, useState } from 'react';
55
import { flushSync } from 'react-dom';
6-
import type { AllPaths, AllPathsKeys, DeepPartial, PathToDeepType, ShapeFromPaths } from 'skyroc-type-utils';
6+
import type { AllPathsKeys, DeepPartial, PathToDeepType, ShapeFromPaths } from 'skyroc-type-utils';
77
import { isArray, toArray } from 'skyroc-utils';
88

99
import type { ChangeMask, SubscribeMaskOptions } from './form-core/event';
@@ -16,25 +16,27 @@ import type { FormState } from './types';
1616
import type { Meta } from './types/shared-types';
1717

1818
export interface ValuesOptions<Values = any> {
19-
getFieldsValue: <K extends AllPaths<Values, number>[]>(name?: K) => ShapeFromPaths<Values, K>;
20-
getFieldValue: <T extends AllPaths<Values>>(name: T) => PathToDeepType<Values, T>;
19+
getFieldsValue: <K extends AllPathsKeys<Values>[]>(name?: K) => ShapeFromPaths<Values, K>;
20+
getFieldValue: <T extends AllPathsKeys<Values>>(name: T) => PathToDeepType<Values, T>;
2121
setFieldsValue: (values: DeepPartial<Values>) => void;
22-
setFieldValue: <T extends AllPaths<Values>>(name: T, value: PathToDeepType<Values, T>) => void;
22+
setFieldValue: <T extends AllPathsKeys<Values>>(name: T, value: PathToDeepType<Values, T>) => void;
2323
}
2424

2525
export interface StateOptions<Values = any> {
26-
getField: <T extends AllPaths<Values>>(name: T) => Meta<T, PathToDeepType<Values, T>>;
27-
getFieldError: (name: AllPaths<Values>) => string[];
28-
getFields: (names?: AllPaths<Values>[]) => Meta<AllPaths<Values>, PathToDeepType<Values, AllPaths<Values>>>[];
29-
getFieldsError: (...name: AllPaths<Values>[]) => Record<AllPaths<Values>, string[]>;
30-
getFieldsTouched: (...name: AllPaths<Values>[]) => boolean;
31-
getFieldsValidated: (...name: AllPaths<Values>[]) => boolean;
32-
getFieldsValidating: (...name: AllPaths<Values>[]) => boolean;
33-
getFieldsWarning: (...name: AllPaths<Values>[]) => Record<AllPaths<Values>, string[]>;
34-
getFieldTouched: (name: AllPaths<Values>) => boolean;
35-
getFieldValidated: (name: AllPaths<Values>) => boolean;
36-
getFieldValidating: (name: AllPaths<Values>) => boolean;
37-
getFieldWarning: (name: AllPaths<Values>) => string[];
26+
getField: <T extends AllPathsKeys<Values>>(name: T) => Meta<T, PathToDeepType<Values, T>>;
27+
getFieldError: (name: AllPathsKeys<Values>) => string[];
28+
getFields: (
29+
names?: AllPathsKeys<Values>[]
30+
) => Meta<AllPathsKeys<Values>, PathToDeepType<Values, AllPathsKeys<Values>>>[];
31+
getFieldsError: (...name: AllPathsKeys<Values>[]) => Record<AllPathsKeys<Values>, string[]>;
32+
getFieldsTouched: (...name: AllPathsKeys<Values>[]) => boolean;
33+
getFieldsValidated: (...name: AllPathsKeys<Values>[]) => boolean;
34+
getFieldsValidating: (...name: AllPathsKeys<Values>[]) => boolean;
35+
getFieldsWarning: (...name: AllPathsKeys<Values>[]) => Record<AllPathsKeys<Values>, string[]>;
36+
getFieldTouched: (name: AllPathsKeys<Values>) => boolean;
37+
getFieldValidated: (name: AllPathsKeys<Values>) => boolean;
38+
getFieldValidating: (name: AllPathsKeys<Values>) => boolean;
39+
getFieldWarning: (name: AllPathsKeys<Values>) => string[];
3840
getFormState: () => FormState;
3941
}
4042

@@ -43,11 +45,11 @@ export interface ValidateFieldsOptions extends ValidateOptions {
4345
}
4446

4547
export interface OperationOptions<Values = any> {
46-
resetFields: (names?: AllPaths<Values>[]) => void;
48+
resetFields: (names?: AllPathsKeys<Values>[]) => void;
4749
submit: () => void;
4850
use: (mw: Middleware) => void;
49-
validateField: (name: AllPaths<Values>) => Promise<boolean>;
50-
validateFields: (names?: AllPaths<Values>[], opts?: ValidateFieldsOptions) => Promise<boolean>;
51+
validateField: (name: AllPathsKeys<Values>) => Promise<boolean>;
52+
validateFields: (names?: AllPathsKeys<Values>[], opts?: ValidateFieldsOptions) => Promise<boolean>;
5153
}
5254

5355
export interface ValidateErrorEntity<Values = any> {
@@ -63,8 +65,8 @@ export interface ValidateErrorEntity<Values = any> {
6365

6466
export interface RegisterCallbackOptions<Values = any> {
6567
onFieldsChange?: (
66-
changedFields: Meta<AllPaths<Values>, PathToDeepType<Values, AllPaths<Values>>>[],
67-
allFields: Meta<AllPaths<Values>, PathToDeepType<Values, AllPaths<Values>>>[]
68+
changedFields: Meta<AllPathsKeys<Values>, PathToDeepType<Values, AllPathsKeys<Values>>>[],
69+
allFields: Meta<AllPathsKeys<Values>, PathToDeepType<Values, AllPathsKeys<Values>>>[]
6870
) => void;
6971

7072
onFinish?: (values: Values) => void;
@@ -84,17 +86,22 @@ export interface InternalCallbacks<Values = any> {
8486

8587
export interface InternalFieldHooks<Values = any> {
8688
dispatch: (action: Action) => void;
87-
getInitialValue: <T extends AllPaths<Values>>(name: T) => PathToDeepType<Values, T>;
89+
getInitialValue: <T extends AllPathsKeys<Values>>(name: T) => PathToDeepType<Values, T>;
90+
registerComputed: <T extends AllPathsKeys<Values>>(
91+
name: T,
92+
deps: AllPathsKeys<Values>[],
93+
compute: (get: (n: AllPathsKeys<Values>) => any, all: Values) => PathToDeepType<Values, T>
94+
) => () => void;
8895
registerField: (entity: FieldEntity) => () => void;
89-
setFieldRules: (name: AllPaths<Values>, rules?: Rule[]) => void;
90-
setRules: (name: AllPaths<Values>, rules?: Rule[]) => void;
91-
subscribeField: (
92-
name: AllPaths<Values>,
93-
cb: () => void,
96+
setFieldRules: (name: AllPathsKeys<Values>, rules?: Rule[]) => void;
97+
setRules: (name: AllPathsKeys<Values>, rules?: Rule[]) => void;
98+
subscribeField: <T extends AllPathsKeys<Values>>(
99+
name: T,
100+
cb: (value: PathToDeepType<Values, T>, name: T, values: Values, mask: ChangeMask) => void,
94101
opt?: { includeChildren?: boolean; mask?: ChangeMask }
95102
) => () => void;
96103
subscribeFields: (
97-
names: AllPaths<Values>[],
104+
names: AllPathsKeys<Values>[],
98105
cb: () => void,
99106
opt?: { includeChildren?: boolean; mask?: ChangeMask }
100107
) => () => void;
@@ -131,7 +138,7 @@ export const useFieldContext = <Values = any>(): InternalFormContext<Values> =>
131138
};
132139

133140
export const useFieldState = <Values = any>(
134-
name: AllPaths<Values>,
141+
name: AllPathsKeys<Values>,
135142
mask: SubscribeMaskOptions = {
136143
errors: true,
137144
touched: true,
@@ -177,7 +184,7 @@ export const useFieldState = <Values = any>(
177184

178185
export const useFieldsState = <Values = any>(
179186
form: FormInstance<Values>,
180-
names: AllPaths<Values>[],
187+
names: AllPathsKeys<Values>[],
181188
opts?: {
182189
includeChildren?: boolean;
183190
mask?: SubscribeMaskOptions;
@@ -205,7 +212,7 @@ export const useFieldsState = <Values = any>(
205212

206213
useEffect(() => {
207214
const unregister = subscribeFields(
208-
names.length ? names : ([''] as AllPaths<Values>[]),
215+
names.length ? names : ([''] as AllPathsKeys<Values>[]),
209216
() => {
210217
flushSync(() => {
211218
forceUpdate({});
@@ -224,27 +231,27 @@ export const useFieldsState = <Values = any>(
224231
return state;
225232
};
226233

227-
export const useFieldError = <Values = any>(name: AllPaths<Values>) => {
234+
export const useFieldError = <Values = any>(name: AllPathsKeys<Values>) => {
228235
const state = useFieldState<Values>(name, { errors: true });
229236

230237
return state.errors;
231238
};
232239

233240
export const useFieldErrors = <Values = any>(
234241
form: FormInstance<Values>,
235-
names: AllPaths<Values>[] = []
236-
): Record<AllPaths<Values>, string[]> => {
242+
names: AllPathsKeys<Values>[] = []
243+
): Record<AllPathsKeys<Values>, string[]> => {
237244
const state = useFieldsState<Values>(form, names, { mask: { errors: true } });
238245

239246
const errors = state.reduce(
240247
(acc, field) => {
241248
acc[field.name] = field.errors;
242249
return acc;
243250
},
244-
{} as Record<AllPaths<Values>, string[]>
251+
{} as Record<AllPathsKeys<Values>, string[]>
245252
);
246253

247-
return errors as Record<AllPaths<Values>, string[]>;
254+
return errors as Record<AllPathsKeys<Values>, string[]>;
248255
};
249256

250257
function useWatch<Values, T extends AllPathsKeys<Values>>(
@@ -253,18 +260,18 @@ function useWatch<Values, T extends AllPathsKeys<Values>>(
253260
includeChildren?: boolean
254261
): PathToDeepType<Values, T>;
255262

256-
function useWatch<Values, const T extends AllPaths<Values>[]>(
263+
function useWatch<Values, const T extends AllPathsKeys<Values>[]>(
257264
form: FormInstance<Values>,
258265
names: T
259266
): ShapeFromPaths<Values, T>;
260267

261-
function useWatch<Values = any, const T extends AllPaths<Values>[] = AllPaths<Values>[]>(
268+
function useWatch<Values = any, const T extends AllPathsKeys<Values>[] = AllPathsKeys<Values>[]>(
262269
form: FormInstance<Values>
263270
): ShapeFromPaths<Values, T>;
264271

265272
function useWatch<Values = any>(
266273
form: FormInstance<Values>,
267-
names?: AllPaths<Values>[] | AllPaths<Values>,
274+
names?: AllPathsKeys<Values>[] | AllPathsKeys<Values>,
268275
includeChildren?: boolean
269276
) {
270277
const namesArray = names ? toArray(names) : [];

primitives/filed-form /src/createStore.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,11 @@ class FormStore {
362362

363363
// ========== 新增:Computed API ==========
364364
/** 注册 computed 字段 */
365-
private registerComputed(name: NamePath, deps: NamePath[], compute: (get: (n: NamePath) => any, all: Store) => any) {
365+
private registerComputed = (
366+
name: NamePath,
367+
deps: NamePath[],
368+
compute: (get: (n: NamePath) => any, all: Store) => any
369+
) => {
366370
const tgt = keyOfName(name);
367371
const depKeys = deps.map(keyOfName);
368372
this._computed.set(tgt, {
@@ -383,6 +387,38 @@ class FormStore {
383387
this._computed.delete(tgt);
384388
depKeys.forEach(d => this._depIndex.get(d)?.delete(tgt));
385389
};
390+
};
391+
392+
/** 注册副作用(watch effect) */
393+
private registerEffect(deps: NamePath[], effect: (get: (n: NamePath) => any, all: Store) => void) {
394+
const depKeys = deps.map(keyOfName);
395+
396+
const run = () => {
397+
effect(n => get(this._store, keyOfName(n)), this._store);
398+
};
399+
400+
// === 初始执行一次 ===
401+
this.transaction(run);
402+
403+
// === 建立依赖索引 ===
404+
const id = `__effect_${Math.random().toString(36).slice(2)}`;
405+
406+
depKeys.forEach(d => {
407+
if (!this._depIndex.has(d)) this._depIndex.set(d, new Set());
408+
this._depIndex.get(d)!.add(id);
409+
});
410+
411+
// === 存储一个特殊 effect 定义(不写 store,只执行副作用) ===
412+
this._computed.set(id, {
413+
compute: (getKey, all) => effect(n => getKey(keyOfName(n)), all),
414+
deps: depKeys
415+
});
416+
417+
// === 返回卸载函数 ===
418+
return () => {
419+
this._computed.delete(id);
420+
depKeys.forEach(d => this._depIndex.get(d)?.delete(id));
421+
};
386422
}
387423

388424
/** 由源变更触达:找出受影响的 computed 目标 */
@@ -1119,6 +1155,7 @@ class FormStore {
11191155
destroyForm: this.destroyForm,
11201156
dispatch: this.dispatch,
11211157
getInitialValue: this.getInitialValue,
1158+
registerComputed: this.registerComputed,
11221159
registerField: this.registerField,
11231160
setCallbacks: this.setCallbacks,
11241161
setFieldRules: this.setFieldRules,
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
export type { AllPaths, FieldElement } from 'skyroc-type-utils';
1+
export type { AllPathsKeys, FieldElement } from 'skyroc-type-utils';
22

33
export { default as Field } from './Field';
44

55
export { useFieldError, useFieldErrors, useFieldsState, useFieldState, useWatch } from './FieldContext';
66

7+
export type { FormInstance } from './FieldContext';
8+
79
export { default as Form } from './Form';
810

911
export { default as List } from './List';
1012

11-
export type { InternalFieldProps } from './types/field';
13+
export { default as ComputedField } from './react/components/ComputedField';
1214

13-
export type { FormInstance } from './FieldContext';
15+
export type { InternalFieldProps } from './types/field';
1416

1517
export { default as useForm } from './useForm';

0 commit comments

Comments
 (0)