@rxdrag/form 0.1.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.
@@ -0,0 +1,44 @@
1
+ import { CSSProperties, ReactNode } from 'react';
2
+ import { ButtonProps } from '@rxdrag/shadcn';
3
+ import { AnyAppForm } from './context';
4
+
5
+ /**
6
+ * TanStack Form 表单层(替代 fieldy 的 VirtualForm/EntityForm 内核)。
7
+ *
8
+ * - useAppForm:包 TanStack useForm——defaultValues 即初始值(编辑=实体对象,新建=空对象/预填);
9
+ * onSubmit 在校验全部通过后拿到整表单值。
10
+ * - AppForm:渲染 <form> + 提供上下文;**脏判定用 `!form.state.isDefaultValue`**(TanStack 内置
11
+ * 逐字段深比较 defaultValue,改回去即不脏——语义正确,无 fieldy modified 黑盒/整对象结构噪音)。
12
+ * - SubmitButton:type=submit + 提交中 loading。
13
+ */
14
+ export interface UseAppFormOptions<T> {
15
+ defaultValues: T;
16
+ onSubmit?: (value: T) => void | Promise<void>;
17
+ }
18
+ export declare function useAppForm<T extends object>(opts: UseAppFormOptions<T>): AnyAppForm;
19
+ /**
20
+ * refillForm —— 表单重灌的唯一入口:值 + 脏基线(defaultValues) 一起换新。
21
+ * 语义(form-core 源码确认):reset(next) = options.defaultValues←next + 全字段 meta 清空
22
+ * (isTouched=false、isDefaultValue=true)。配合 useAppForm 已关闭 options 同步,重灌后
23
+ * 任何渲染都不会把值拉回旧默认值。用于「编辑态数据到达/重新加载整表/手动重置到基线」。
24
+ */
25
+ export declare function refillForm<T extends object>(form: AnyAppForm, next: T): void;
26
+ /**
27
+ * commitForm —— 保存成功后的基线提交:把「当前表单值 +(可选)服务端补丁」设为新值与新脏基线。
28
+ * 替代业务手拼 form.reset({...values,...serverPatch,id}) 的易错写法;patch 只应含服务端
29
+ * 生成字段(id/code/createdAt/updatedAt 等白名单),用户填的值一律以表单现值为准。
30
+ */
31
+ export declare function commitForm<T extends object>(form: AnyAppForm, patch?: Partial<T>): void;
32
+ export interface AppFormProps {
33
+ form: AnyAppForm;
34
+ children?: ReactNode;
35
+ className?: string;
36
+ style?: CSSProperties;
37
+ /** 表单名(= 实体名,调试标识)。 */
38
+ name?: string;
39
+ /** 未保存脏态上报(= !isDefaultValue,供 useUnsavedGuard)。 */
40
+ onDirtyChange?: (dirty: boolean) => void;
41
+ }
42
+ export declare function AppForm(props: AppFormProps): import("react").JSX.Element;
43
+ /** 提交按钮:submit 类型 + 提交中 loading/禁用。放在 AppForm 内使用。 */
44
+ export declare function SubmitButton(props: ButtonProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ /**
4
+ * 嵌套对象字段:把 name 作为前缀下发给子字段(TanStack Form 用点号路径表达嵌套)。
5
+ * 例如 <ObjectField name="meta"><InputField name="title" /></ObjectField>
6
+ * 内部字段实际绑定到 "meta.title"。可多层嵌套。
7
+ */
8
+ export declare function ObjectField(props: {
9
+ name: string;
10
+ children?: ReactNode;
11
+ }): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { default as React } from 'react';
2
+ import { FieldValidateProps } from './validators';
3
+
4
+ /**
5
+ * bindField —— fieldy 的 toField 的 TanStack 等价 HOC:
6
+ * 把「受控展示组件(value/onValueChange/isInvalid/errorMessage)」绑定到 TanStack Field。
7
+ *
8
+ * 关键约定:**显示层转换不污染 store**——组件渲染用 value ?? displayDefault(如 ''),
9
+ * 但 store 里保持原始值(DB 的 null 不会被规范化写值弄脏,脏判定 isDefaultValue 因此可靠)。
10
+ */
11
+ export type ControlProps = {
12
+ id?: string;
13
+ name?: string;
14
+ label?: React.ReactNode;
15
+ placeholder?: string;
16
+ value?: any;
17
+ isInvalid?: boolean;
18
+ errorMessage?: React.ReactNode;
19
+ onValueChange?: (value: any) => void;
20
+ };
21
+ export type BoundFieldProps = FieldValidateProps & {
22
+ /** 字段路径(XxxFields.* 常量)。可选:无 name 时不绑定,直接渲染展示组件(对齐 fieldy)。 */
23
+ name?: string;
24
+ /** 值写回前的转换(如 number 字段把输入串转数字)。 */
25
+ coerce?: (raw: any) => any;
26
+ /** 以下为 fieldy 遗留 props:类型上接受、运行时忽略(不写回 store、不透传 DOM)。 */
27
+ initialValue?: unknown;
28
+ defaultValue?: unknown;
29
+ value?: unknown;
30
+ valueComputer?: unknown;
31
+ };
32
+ export type BindFieldOptions = {
33
+ /** 渲染层空值兜底(不写回 store)。 */
34
+ displayDefault?: unknown;
35
+ /** 登记到字段元数据表的控件类型。 */
36
+ controlType?: string;
37
+ };
38
+ export declare function bindField<P extends ControlProps = ControlProps>(Component: React.ComponentType<P>, options?: BindFieldOptions): (props: BoundFieldProps & Omit<P, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export type CheckboxProps = {
3
+ id?: string;
4
+ children?: React.ReactNode;
5
+ value?: boolean;
6
+ onValueChange?: (value: boolean) => void;
7
+ isInvalid?: boolean;
8
+ errorMessage?: React.ReactNode;
9
+ };
10
+ export declare function Checkbox(props: CheckboxProps): React.JSX.Element;
@@ -0,0 +1,5 @@
1
+ export type ColorInputProps = {
2
+ value?: string;
3
+ onValueChange?: (value?: string) => void;
4
+ };
5
+ export declare function ColorInput(props: ColorInputProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import * as React from "react";
2
+ type ShadcnInputSize = "sm" | "md" | "lg";
3
+ type ShadcnInputVariant = "default" | "filled" | "flat";
4
+ type BaseInputProps = Omit<React.ComponentProps<"input">, "size"> & {
5
+ size?: ShadcnInputSize;
6
+ variant?: ShadcnInputVariant;
7
+ };
8
+ export type InputProps = BaseInputProps & {
9
+ label?: React.ReactNode;
10
+ labelPlacement?: "outside" | "inside" | "outside-left";
11
+ description?: React.ReactNode;
12
+ isInvalid?: boolean;
13
+ errorMessage?: React.ReactNode;
14
+ onValueChange?: (value: string) => void;
15
+ startContent?: React.ReactNode;
16
+ endContent?: React.ReactNode;
17
+ required?: boolean;
18
+ requried?: boolean;
19
+ classNames?: Partial<{
20
+ base: string;
21
+ label: string;
22
+ inputWrapper: string;
23
+ input: string;
24
+ description: string;
25
+ errorMessage: string;
26
+ }>;
27
+ };
28
+ export declare function Input(props: InputProps): React.JSX.Element;
29
+ export {};
@@ -0,0 +1,6 @@
1
+ import { InputProps } from './Input';
2
+ import * as React from "react";
3
+ export type PasswordProps = Omit<InputProps, "type" | "endContent"> & {
4
+ endContent?: React.ReactNode;
5
+ };
6
+ export declare function Password(props: PasswordProps): React.JSX.Element;
@@ -0,0 +1,24 @@
1
+ import * as React from "react";
2
+ export type SelectProps = {
3
+ id?: string;
4
+ label?: React.ReactNode;
5
+ labelPlacement?: "outside" | "inside" | "outside-left";
6
+ placeholder?: string;
7
+ description?: React.ReactNode;
8
+ isInvalid?: boolean;
9
+ errorMessage?: React.ReactNode;
10
+ value?: string | number;
11
+ onValueChange?: (value?: string | number) => void;
12
+ disabled?: boolean;
13
+ className?: string;
14
+ classNames?: Partial<{
15
+ base: string;
16
+ label: string;
17
+ trigger: string;
18
+ content: string;
19
+ description: string;
20
+ errorMessage: string;
21
+ }>;
22
+ children?: React.ReactNode;
23
+ };
24
+ export declare function Select(props: SelectProps): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { SelectProps } from './Select';
2
+
3
+ export type SingleSelectProps = Omit<SelectProps, "value" | "onValueChange"> & {
4
+ value?: string;
5
+ onValueChange?: (value?: string) => void;
6
+ };
7
+ export declare function SingleSelect(props: SingleSelectProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ export type SwitchProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onChange" | "value"> & {
3
+ value?: boolean;
4
+ onValueChange?: (value: boolean) => void;
5
+ size?: "sm" | "md" | "lg";
6
+ children?: React.ReactNode;
7
+ disabled?: boolean;
8
+ };
9
+ export declare function Switch(props: SwitchProps): React.JSX.Element;
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ export type TextareaProps = React.ComponentProps<"textarea"> & {
3
+ id?: string;
4
+ label?: React.ReactNode;
5
+ labelPlacement?: "outside" | "inside" | "outside-left";
6
+ description?: React.ReactNode;
7
+ isInvalid?: boolean;
8
+ errorMessage?: React.ReactNode;
9
+ onValueChange?: (value: string) => void;
10
+ required?: boolean;
11
+ requried?: boolean;
12
+ minRows?: number;
13
+ classNames?: Partial<{
14
+ base: string;
15
+ label: string;
16
+ textarea: string;
17
+ description: string;
18
+ errorMessage: string;
19
+ }>;
20
+ };
21
+ export declare function Textarea(props: TextareaProps): React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ export * from './Input';
2
+ export * from './Textarea';
3
+ export * from './Select';
4
+ export * from './SingleSelect';
5
+ export * from './Switch';
6
+ export * from './Checkbox';
7
+ export * from './ColorInput';
8
+ export * from './Password';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * AppForm 上下文:向字段组件提供 TanStack form 实例 + 字段元数据登记表。
3
+ *
4
+ * form 类型收敛为 any:TanStack Form 的泛型链按字面 name 做类型级校验,而本项目字段组件
5
+ * 以动态字符串 name(XxxFields.name 常量)工作——功能兼容优先,类型宽松收敛在包内部,
6
+ * 业务层拿到的字段组件 props 仍是强类型。
7
+ */
8
+ export type AnyAppForm = any;
9
+ /** 已挂载字段的元数据(供枚举/调试/未来 agent 工作台)。 */
10
+ export interface RegisteredFieldMeta {
11
+ name: string;
12
+ label?: string;
13
+ type?: string;
14
+ }
15
+ export interface AppFormContextValue {
16
+ /** 表单名(= 实体名)。 */
17
+ name?: string;
18
+ form: AnyAppForm;
19
+ /** 字段挂载时登记元数据,返回注销函数。 */
20
+ registerFieldMeta: (meta: RegisteredFieldMeta) => () => void;
21
+ /** 枚举当前挂载字段(按登记序)。 */
22
+ getFieldMetas: () => RegisteredFieldMeta[];
23
+ }
24
+ export declare const AppFormContext: import('react').Context<AppFormContextValue | undefined>;
25
+ export declare function useAppFormContext(): AppFormContextValue;
26
+ /** 宽松版:不在 AppForm 内返回 undefined(供可选场景探测)。 */
27
+ export declare function useMaybeAppFormContext(): AppFormContextValue | undefined;
28
+ /**
29
+ * 字段名前缀上下文(ObjectField 用)。TanStack Form 用点号路径(如 "meta.title")表达嵌套,
30
+ * ObjectField 把自己的 name 作为前缀下发,内部字段 name="title" 解析成 "meta.title"。
31
+ */
32
+ export declare const FieldNamePrefixContext: import('react').Context<string>;
33
+ export declare function useFieldNamePrefix(): string;
34
+ /** 拼接前缀与字段名(空前缀直接返回 name)。 */
35
+ export declare function joinFieldName(prefix: string, name: string): string;
@@ -0,0 +1,112 @@
1
+ import { default as React } from 'react';
2
+ import { BindFieldOptions, ControlProps } from './bindField';
3
+ import { InputProps } from './components/Input';
4
+ import { TextareaProps } from './components/Textarea';
5
+ import { SelectProps } from './components/Select';
6
+ import { SingleSelectProps } from './components/SingleSelect';
7
+ import { SwitchProps } from './components/Switch';
8
+ import { CheckboxProps } from './components/Checkbox';
9
+ import { ColorInputProps } from './components/ColorInput';
10
+ import { PasswordProps } from './components/Password';
11
+
12
+ /**
13
+ * 业务字段组件(TanStack 版)——沿用 fieldy-ui 的同名 props 心智,功能对齐。
14
+ * name 用 codegen 出的 XxxFields.* 常量;校验支持 required / zodSchema / 旧版 validateSchema。
15
+ */
16
+ declare const BaseInputField: (props: import('./validators').FieldValidateProps & {
17
+ name?: string;
18
+ coerce?: (raw: any) => any;
19
+ initialValue?: unknown;
20
+ defaultValue?: unknown;
21
+ value?: unknown;
22
+ valueComputer?: unknown;
23
+ } & Omit<InputProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
24
+ /** 文本/数字输入。type="number" 时写回 store 的是 number(空串→undefined)。 */
25
+ export declare function InputField(props: Parameters<typeof BaseInputField>[0] & {
26
+ type?: string;
27
+ }): React.JSX.Element;
28
+ export declare const TextareaField: (props: import('./validators').FieldValidateProps & {
29
+ name?: string;
30
+ coerce?: (raw: any) => any;
31
+ initialValue?: unknown;
32
+ defaultValue?: unknown;
33
+ value?: unknown;
34
+ valueComputer?: unknown;
35
+ } & Omit<TextareaProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
36
+ export declare const SelectField: (props: import('./validators').FieldValidateProps & {
37
+ name?: string;
38
+ coerce?: (raw: any) => any;
39
+ initialValue?: unknown;
40
+ defaultValue?: unknown;
41
+ value?: unknown;
42
+ valueComputer?: unknown;
43
+ } & Omit<SelectProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
44
+ export declare const SingleSelectField: (props: import('./validators').FieldValidateProps & {
45
+ name?: string;
46
+ coerce?: (raw: any) => any;
47
+ initialValue?: unknown;
48
+ defaultValue?: unknown;
49
+ value?: unknown;
50
+ valueComputer?: unknown;
51
+ } & Omit<SingleSelectProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
52
+ export declare const SwitchField: (props: import('./validators').FieldValidateProps & {
53
+ name?: string;
54
+ coerce?: (raw: any) => any;
55
+ initialValue?: unknown;
56
+ defaultValue?: unknown;
57
+ value?: unknown;
58
+ valueComputer?: unknown;
59
+ } & Omit<SwitchProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
60
+ export declare const CheckboxField: (props: import('./validators').FieldValidateProps & {
61
+ name?: string;
62
+ coerce?: (raw: any) => any;
63
+ initialValue?: unknown;
64
+ defaultValue?: unknown;
65
+ value?: unknown;
66
+ valueComputer?: unknown;
67
+ } & Omit<CheckboxProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
68
+ export declare const PasswordField: (props: import('./validators').FieldValidateProps & {
69
+ name?: string;
70
+ coerce?: (raw: any) => any;
71
+ initialValue?: unknown;
72
+ defaultValue?: unknown;
73
+ value?: unknown;
74
+ valueComputer?: unknown;
75
+ } & Omit<PasswordProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
76
+ export declare const ColorField: (props: import('./validators').FieldValidateProps & {
77
+ name?: string;
78
+ coerce?: (raw: any) => any;
79
+ initialValue?: unknown;
80
+ defaultValue?: unknown;
81
+ value?: unknown;
82
+ valueComputer?: unknown;
83
+ } & Omit<ColorInputProps, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
84
+ /**
85
+ * toField —— fieldy-ui 同名 API 的兼容别名(供 glue 字段沿用)。
86
+ * 旧签名是 toField(Component, { defaultValue }),这里把 defaultValue 映射为 displayDefault。
87
+ * 保留「显式传 undefined」语义(如 select 存对象/undefined 而非 "")。
88
+ */
89
+ export declare function toField<P extends ControlProps = ControlProps>(Component: React.ComponentType<P>, options?: {
90
+ defaultValue?: unknown;
91
+ } & BindFieldOptions): (props: import('./validators').FieldValidateProps & {
92
+ name?: string;
93
+ coerce?: (raw: any) => any;
94
+ initialValue?: unknown;
95
+ defaultValue?: unknown;
96
+ value?: unknown;
97
+ valueComputer?: unknown;
98
+ } & Omit<P, "value" | "onValueChange" | "isInvalid" | "errorMessage">) => React.JSX.Element;
99
+ /**
100
+ * fieldy-ui 的 FieldProps 兼容类型:字段包装组件的公共 props(供 glue 字段手写签名沿用)。
101
+ */
102
+ export type FieldProps = {
103
+ name?: string;
104
+ label?: React.ReactNode;
105
+ defaultValue?: unknown;
106
+ initialValue?: unknown;
107
+ value?: unknown;
108
+ required?: boolean | string;
109
+ validateSchema?: import('./validators').LegacyValidateSchema;
110
+ valueComputer?: any;
111
+ };
112
+ export {};
@@ -0,0 +1,30 @@
1
+ import { AnyAppForm } from './context';
2
+
3
+ /** 订阅整表单当前值(TanStack store 切片)。 */
4
+ export declare function useFormValues<T = any>(): T;
5
+ /** fieldy useFormValue 兼容别名(无参,返回整表单值)。 */
6
+ export declare const useFormValue: typeof useFormValues;
7
+ /** 订阅某字段当前值(尊重 ObjectField 前缀)。 */
8
+ export declare function useFieldValue<T = any>(name: string): T;
9
+ /**
10
+ * fieldy useFormModified 兼容:脏 = !isDefaultValue。
11
+ * setModified(false):以当前值为新基线(保存成功后调用 → 变“未修改”,禁用保存按钮)。
12
+ * setModified(true):无需处理(TanStack 由值变化自动置脏)。
13
+ */
14
+ export declare function useFormModified(): {
15
+ modified: boolean;
16
+ setModified: (v: boolean) => void;
17
+ };
18
+ /**
19
+ * 命令式校验整表单并取值(替代 fieldy 的 form.validate())。
20
+ * 返回 { ok, value }:ok=全字段校验通过;value=当前表单值(重组出的 entity 形状)。
21
+ */
22
+ export declare function validateForm<T = any>(form: AnyAppForm): Promise<{
23
+ ok: boolean;
24
+ value: T;
25
+ }>;
26
+ /**
27
+ * 命令式设置某字段值(供加载后回填等场景,尊重 ObjectField 前缀)。
28
+ * 用法:const setField = useSetFieldValue(); setField("schemaJson", text)
29
+ */
30
+ export declare function useSetFieldValue(): (name: string, value: unknown) => any;
@@ -0,0 +1,9 @@
1
+ export * from './context';
2
+ export * from './AppForm';
3
+ export * from './bindField';
4
+ export * from './validators';
5
+ export * from './fields';
6
+ export * from './ObjectField';
7
+ export * from './hooks';
8
+ export * from './components';
9
+ export { useStore } from '@tanstack/react-form';