@dynamic-field-kit/react 1.1.1 → 1.3.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.
package/README.md CHANGED
@@ -12,6 +12,10 @@ Demo app: https://github.com/vannt-dev/dynamic-field-kit-demo
12
12
  npm install @dynamic-field-kit/core @dynamic-field-kit/react react
13
13
  ```
14
14
 
15
+ Note: Core is shared runtime. Install core separately and ensure a single version is used across adapters to avoid duplicate registries.
16
+
17
+ - Install with core: `npm install @dynamic-field-kit/core @dynamic-field-kit/react`
18
+
15
19
  ## Exports
16
20
 
17
21
  - `DynamicInput`
@@ -25,6 +29,8 @@ npm install @dynamic-field-kit/core @dynamic-field-kit/react react
25
29
  - `FieldTypeKey`
26
30
  - `FieldRendererProps`
27
31
 
32
+ `FieldGroupInput` (repeatable field groups) is used internally by `FieldInput` and doesn't need to be imported directly - see "Repeatable field groups" below.
33
+
28
34
  Default layouts are registered automatically when you import the package root.
29
35
 
30
36
  Built-in layouts:
@@ -39,44 +45,44 @@ Built-in layouts:
39
45
  Register React components or function components through the React adapter:
40
46
 
41
47
  ```tsx
42
- import { fieldRegistry } from "@dynamic-field-kit/react"
48
+ import { fieldRegistry } from '@dynamic-field-kit/react';
43
49
 
44
- fieldRegistry.register("text", ({ value, onValueChange, label }) => (
45
- <label style={{ display: "grid", gap: 4 }}>
50
+ fieldRegistry.register('text', ({ value, onValueChange, label }) => (
51
+ <label style={{ display: 'grid', gap: 4 }}>
46
52
  <span>{label}</span>
47
53
  <input
48
- value={value ?? ""}
54
+ value={value ?? ''}
49
55
  onChange={(e) => onValueChange?.(e.target.value)}
50
56
  />
51
57
  </label>
52
- ))
58
+ ));
53
59
 
54
- fieldRegistry.register("number", ({ value, onValueChange, label }) => (
55
- <label style={{ display: "grid", gap: 4 }}>
60
+ fieldRegistry.register('number', ({ value, onValueChange, label }) => (
61
+ <label style={{ display: 'grid', gap: 4 }}>
56
62
  <span>{label}</span>
57
63
  <input
58
64
  type="number"
59
- value={value ?? ""}
65
+ value={value ?? ''}
60
66
  onChange={(e) => onValueChange?.(Number(e.target.value))}
61
67
  />
62
68
  </label>
63
- ))
69
+ ));
64
70
  ```
65
71
 
66
72
  ## Basic usage
67
73
 
68
74
  ```tsx
69
- import { useState } from "react"
70
- import { MultiFieldInput } from "@dynamic-field-kit/react"
71
- import type { FieldDescription } from "@dynamic-field-kit/core"
75
+ import { useState } from 'react';
76
+ import { MultiFieldInput } from '@dynamic-field-kit/react';
77
+ import type { FieldDescription } from '@dynamic-field-kit/core';
72
78
 
73
79
  const fields: FieldDescription[] = [
74
- { name: "name", type: "text", label: "Name" },
75
- { name: "age", type: "number", label: "Age" },
76
- ]
80
+ { name: 'name', type: 'text', label: 'Name' },
81
+ { name: 'age', type: 'number', label: 'Age' },
82
+ ];
77
83
 
78
84
  export function Example() {
79
- const [data, setData] = useState({})
85
+ const [data, setData] = useState({});
80
86
 
81
87
  return (
82
88
  <MultiFieldInput
@@ -84,7 +90,7 @@ export function Example() {
84
90
  properties={data}
85
91
  onChange={setData}
86
92
  />
87
- )
93
+ );
88
94
  }
89
95
  ```
90
96
 
@@ -101,7 +107,7 @@ Use a layout config object:
101
107
  ```tsx
102
108
  <MultiFieldInput
103
109
  fieldDescriptions={fields}
104
- layout={{ type: "grid", columns: 3, gap: 16 }}
110
+ layout={{ type: 'grid', columns: 3, gap: 16 }}
105
111
  />
106
112
  ```
107
113
 
@@ -111,9 +117,9 @@ Use the built-in responsive layout:
111
117
  <MultiFieldInput
112
118
  fieldDescriptions={fields}
113
119
  layout={{
114
- type: "responsive",
115
- mobile: "column",
116
- desktop: { type: "grid", columns: 2, gap: 12 },
120
+ type: 'responsive',
121
+ mobile: 'column',
122
+ desktop: { type: 'grid', columns: 2, gap: 12 },
117
123
  }}
118
124
  />
119
125
  ```
@@ -121,11 +127,51 @@ Use the built-in responsive layout:
121
127
  Register a custom layout:
122
128
 
123
129
  ```tsx
124
- import { layoutRegistry } from "@dynamic-field-kit/react"
130
+ import { layoutRegistry } from '@dynamic-field-kit/react';
131
+
132
+ layoutRegistry.register('stack-tight', ({ children }) => (
133
+ <div style={{ display: 'grid', gap: 8 }}>{children}</div>
134
+ ));
135
+ ```
136
+
137
+ ## Derived fields with `computeValue`
125
138
 
126
- layoutRegistry.register("stack-tight", ({ children }) => (
127
- <div style={{ display: "grid", gap: 8 }}>{children}</div>
128
- ))
139
+ Give a field a `computeValue` to derive its value from the rest of the form data whenever any field changes:
140
+
141
+ ```tsx
142
+ const fields: FieldDescription[] = [
143
+ { name: 'firstName', type: 'text' },
144
+ { name: 'lastName', type: 'text' },
145
+ {
146
+ name: 'fullName',
147
+ type: 'text',
148
+ computeValue: (data) =>
149
+ `${data.firstName ?? ''} ${data.lastName ?? ''}`.trim(),
150
+ },
151
+ ];
152
+ ```
153
+
154
+ ## Repeatable field groups
155
+
156
+ A field with `fields` renders as a repeatable group: `data[name]` becomes an array of items, each shaped by the nested `fields`, with "Add"/"Remove" controls rendered automatically.
157
+
158
+ ```tsx
159
+ const fields: FieldDescription[] = [
160
+ {
161
+ name: 'contacts',
162
+ type: 'group',
163
+ label: 'Contacts',
164
+ fields: [
165
+ { name: 'email', type: 'text', label: 'Email' },
166
+ { name: 'phone', type: 'text', label: 'Phone' },
167
+ ],
168
+ defaultItem: { email: '', phone: '' },
169
+ minItems: 1,
170
+ maxItems: 5,
171
+ },
172
+ ];
173
+
174
+ <MultiFieldInput fieldDescriptions={fields} />;
129
175
  ```
130
176
 
131
177
  ## Type augmentation
@@ -133,12 +179,12 @@ layoutRegistry.register("stack-tight", ({ children }) => (
133
179
  Add your app's field types through module augmentation:
134
180
 
135
181
  ```ts
136
- import "@dynamic-field-kit/core"
182
+ import '@dynamic-field-kit/core';
137
183
 
138
- declare module "@dynamic-field-kit/core" {
184
+ declare module '@dynamic-field-kit/core' {
139
185
  interface FieldTypeMap {
140
- text: string
141
- number: number
186
+ text: string;
187
+ number: number;
142
188
  }
143
189
  }
144
190
  ```
@@ -147,8 +193,9 @@ declare module "@dynamic-field-kit/core" {
147
193
 
148
194
  - `@dynamic-field-kit/core` stays framework-agnostic and does not export React-specific JSX types.
149
195
  - `@dynamic-field-kit/react` narrows the shared registry to React component types so `fieldRegistry.get(type)` can be rendered safely in TSX.
150
- - `MultiFieldInput` filters fields using `appearCondition`.
196
+ - `MultiFieldInput` filters fields using `appearCondition` and derives fields using `computeValue`.
151
197
  - `DynamicInput` renders `Unknown field type: ...` when a renderer is missing.
198
+ - Fields with `fields` render as repeatable groups instead of going through `fieldRegistry`.
152
199
 
153
200
  ## License
154
201
 
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { FieldTypeKey, Properties, FieldDescription, FieldTypeMap, FieldRendererProps } from '@dynamic-field-kit/core';
4
4
  export { FieldDescription, FieldRendererProps, FieldTypeKey } from '@dynamic-field-kit/core';
5
5
 
6
- type LayoutRenderer<C = any> = (props: {
6
+ type LayoutRenderer<C = unknown> = (props: {
7
7
  children: React.ReactNode;
8
8
  config?: C;
9
9
  }) => React.ReactElement;
@@ -14,34 +14,35 @@ declare class LayoutRegistry {
14
14
  }
15
15
  declare const layoutRegistry: LayoutRegistry;
16
16
 
17
- type BaseLayout = "column" | "row" | {
18
- type: "grid";
17
+ type BaseLayout = 'column' | 'row' | {
18
+ type: 'grid';
19
19
  columns?: number;
20
20
  gap?: number;
21
21
  };
22
22
  type LayoutConfig = BaseLayout | {
23
- type: "responsive";
23
+ type: 'responsive';
24
24
  mobile: BaseLayout;
25
25
  desktop: BaseLayout;
26
26
  };
27
27
 
28
28
  interface Props$2<T extends FieldTypeKey> {
29
29
  type: T;
30
- value?: any;
31
- onChange?: (value: any) => void;
30
+ value?: unknown;
31
+ onChange?: (value: unknown) => void;
32
32
  label?: string;
33
33
  options?: Properties[];
34
34
  className?: string;
35
35
  description?: ReactNode;
36
36
  }
37
- declare const DynamicInput: <T extends FieldTypeKey>({ type, value, onChange, label, options, className, description }: Props$2<T>) => react_jsx_runtime.JSX.Element;
37
+ declare const DynamicInputInner: <T extends FieldTypeKey>({ type, value, onChange, label, options, className, description, }: Props$2<T>) => react_jsx_runtime.JSX.Element;
38
+ declare const DynamicInput: typeof DynamicInputInner;
38
39
 
39
40
  interface Props$1 {
40
41
  fieldDescription: FieldDescription;
41
42
  renderInfos: Properties;
42
- onValueChangeField: (value: any, key: string) => void;
43
+ onValueChangeField: (value: unknown, key: string) => void;
43
44
  }
44
- declare const FieldInput: ({ fieldDescription, renderInfos, onValueChangeField }: Props$1) => react_jsx_runtime.JSX.Element;
45
+ declare const FieldInput: React.MemoExoticComponent<({ fieldDescription, renderInfos, onValueChangeField, }: Props$1) => react_jsx_runtime.JSX.Element>;
45
46
 
46
47
  interface Props {
47
48
  fieldDescriptions: FieldDescription[];
@@ -49,9 +50,9 @@ interface Props {
49
50
  onChange?: (data: Properties) => void;
50
51
  layout?: LayoutConfig;
51
52
  }
52
- declare const MultiFieldInput: ({ fieldDescriptions, properties, onChange, layout }: Props) => react_jsx_runtime.JSX.Element;
53
+ declare const MultiFieldInput: ({ fieldDescriptions, properties, onChange, layout, }: Props) => react_jsx_runtime.JSX.Element;
53
54
 
54
- type ReactFieldRenderer<T = any> = ComponentType<FieldRendererProps<T>>;
55
+ type ReactFieldRenderer<T = unknown> = ComponentType<FieldRendererProps<T>>;
55
56
  interface ReactFieldRegistry {
56
57
  register<K extends keyof FieldTypeMap>(type: K, renderer: ReactFieldRenderer<FieldTypeMap[K]>): void;
57
58
  get<K extends keyof FieldTypeMap>(type: K): ReactFieldRenderer<FieldTypeMap[K]> | undefined;
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { FieldTypeKey, Properties, FieldDescription, FieldTypeMap, FieldRendererProps } from '@dynamic-field-kit/core';
4
4
  export { FieldDescription, FieldRendererProps, FieldTypeKey } from '@dynamic-field-kit/core';
5
5
 
6
- type LayoutRenderer<C = any> = (props: {
6
+ type LayoutRenderer<C = unknown> = (props: {
7
7
  children: React.ReactNode;
8
8
  config?: C;
9
9
  }) => React.ReactElement;
@@ -14,34 +14,35 @@ declare class LayoutRegistry {
14
14
  }
15
15
  declare const layoutRegistry: LayoutRegistry;
16
16
 
17
- type BaseLayout = "column" | "row" | {
18
- type: "grid";
17
+ type BaseLayout = 'column' | 'row' | {
18
+ type: 'grid';
19
19
  columns?: number;
20
20
  gap?: number;
21
21
  };
22
22
  type LayoutConfig = BaseLayout | {
23
- type: "responsive";
23
+ type: 'responsive';
24
24
  mobile: BaseLayout;
25
25
  desktop: BaseLayout;
26
26
  };
27
27
 
28
28
  interface Props$2<T extends FieldTypeKey> {
29
29
  type: T;
30
- value?: any;
31
- onChange?: (value: any) => void;
30
+ value?: unknown;
31
+ onChange?: (value: unknown) => void;
32
32
  label?: string;
33
33
  options?: Properties[];
34
34
  className?: string;
35
35
  description?: ReactNode;
36
36
  }
37
- declare const DynamicInput: <T extends FieldTypeKey>({ type, value, onChange, label, options, className, description }: Props$2<T>) => react_jsx_runtime.JSX.Element;
37
+ declare const DynamicInputInner: <T extends FieldTypeKey>({ type, value, onChange, label, options, className, description, }: Props$2<T>) => react_jsx_runtime.JSX.Element;
38
+ declare const DynamicInput: typeof DynamicInputInner;
38
39
 
39
40
  interface Props$1 {
40
41
  fieldDescription: FieldDescription;
41
42
  renderInfos: Properties;
42
- onValueChangeField: (value: any, key: string) => void;
43
+ onValueChangeField: (value: unknown, key: string) => void;
43
44
  }
44
- declare const FieldInput: ({ fieldDescription, renderInfos, onValueChangeField }: Props$1) => react_jsx_runtime.JSX.Element;
45
+ declare const FieldInput: React.MemoExoticComponent<({ fieldDescription, renderInfos, onValueChangeField, }: Props$1) => react_jsx_runtime.JSX.Element>;
45
46
 
46
47
  interface Props {
47
48
  fieldDescriptions: FieldDescription[];
@@ -49,9 +50,9 @@ interface Props {
49
50
  onChange?: (data: Properties) => void;
50
51
  layout?: LayoutConfig;
51
52
  }
52
- declare const MultiFieldInput: ({ fieldDescriptions, properties, onChange, layout }: Props) => react_jsx_runtime.JSX.Element;
53
+ declare const MultiFieldInput: ({ fieldDescriptions, properties, onChange, layout, }: Props) => react_jsx_runtime.JSX.Element;
53
54
 
54
- type ReactFieldRenderer<T = any> = ComponentType<FieldRendererProps<T>>;
55
+ type ReactFieldRenderer<T = unknown> = ComponentType<FieldRendererProps<T>>;
55
56
  interface ReactFieldRegistry {
56
57
  register<K extends keyof FieldTypeMap>(type: K, renderer: ReactFieldRenderer<FieldTypeMap[K]>): void;
57
58
  get<K extends keyof FieldTypeMap>(type: K): ReactFieldRenderer<FieldTypeMap[K]> | undefined;