@dynamic-field-kit/core 1.0.0 → 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.
- package/README.md +276 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Dynamic Field Kit
|
|
2
|
+
|
|
3
|
+
A lightweight, extensible **dynamic form engine** for React, built for scalable applications and design systems.
|
|
4
|
+
|
|
5
|
+
`dynamic-field-kit` lets you define forms using **configuration objects** instead of hard-coded UI, and allows applications to **freely extend field types** (`text`, `number`, `checkbox`, `select`, `date`, `custom`, …) without modifying the library.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- Schema-driven dynamic forms
|
|
12
|
+
- Extensible field types (no enums, no hard-coded unions)
|
|
13
|
+
- Pluggable field renderers via registry
|
|
14
|
+
- Runtime conditional fields (`appearCondition`)
|
|
15
|
+
- Clean TypeScript declarations (DTS-safe)
|
|
16
|
+
- Core logic separated from React rendering
|
|
17
|
+
- Ideal for form builders & design systems
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📦 Packages
|
|
22
|
+
|
|
23
|
+
| Package | Description |
|
|
24
|
+
|------|------------|
|
|
25
|
+
| `@dynamic-field-kit/core` | Core types and field registry |
|
|
26
|
+
| `@dynamic-field-kit/react` | React components (FieldInput, MultiFieldInput, DynamicInput) |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 📥 Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @dynamic-field-kit/core @dynamic-field-kit/react
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Peer dependency**
|
|
37
|
+
|
|
38
|
+
```txt
|
|
39
|
+
react >= 17
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🧱 Core Concepts
|
|
45
|
+
|
|
46
|
+
The library **does NOT define field types** like:
|
|
47
|
+
```ts
|
|
48
|
+
"text" | "number" | "select"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Instead, it exposes an **extendable interface** that applications can augment:
|
|
52
|
+
```ts
|
|
53
|
+
export interface FieldTypeMap {}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This allows:
|
|
57
|
+
- Unlimited custom field types
|
|
58
|
+
- Strong typing without locking consumers
|
|
59
|
+
- No need to rebuild the library
|
|
60
|
+
|
|
61
|
+
This pattern is used by mature libraries like **MUI, React Hook Form,** and **Redux Toolkit**.
|
|
62
|
+
|
|
63
|
+
## 🧩 Defining Field Types (App Side)
|
|
64
|
+
|
|
65
|
+
Create a `.d.ts` file in your app (e.g. src/types/dynamic-field.d.ts):
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import "@dynamic-field-kit/react"
|
|
69
|
+
|
|
70
|
+
declare module "@dynamic-field-kit/react" {
|
|
71
|
+
interface FieldTypeMap {
|
|
72
|
+
text: string
|
|
73
|
+
number: number
|
|
74
|
+
checkbox: boolean
|
|
75
|
+
select: string
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
⚠️ Make sure this file is included in tsconfig.json.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### FieldDescription
|
|
84
|
+
|
|
85
|
+
A `FieldDescription` defines **what a field is**, not **how it looks**.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { FieldDescription } from "@dynamic-field-kit/core"
|
|
89
|
+
|
|
90
|
+
const fields: FieldDescription[] = [
|
|
91
|
+
{
|
|
92
|
+
name: "username",
|
|
93
|
+
type: "text",
|
|
94
|
+
label: "Username"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "age",
|
|
98
|
+
type: "number",
|
|
99
|
+
label: "Age",
|
|
100
|
+
appearCondition: (data) => data.username !== ""
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Key properties**
|
|
106
|
+
- `name` – field key in form data
|
|
107
|
+
- `type` – renderer key
|
|
108
|
+
- `appearCondition` – runtime visibility condition
|
|
109
|
+
- No domain-specific typing enforced
|
|
110
|
+
|
|
111
|
+
**Field Registry (Render Layer)**
|
|
112
|
+
The library does **not** ship UI components.
|
|
113
|
+
|
|
114
|
+
Instead, applications register their own renderers.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { fieldRegistry } from "@dynamic-field-kit/core"
|
|
118
|
+
|
|
119
|
+
fieldRegistry.register("text", ({ value, onValueChange, label }) => (
|
|
120
|
+
<div>
|
|
121
|
+
<label>{label}</label>
|
|
122
|
+
<input
|
|
123
|
+
value={value ?? ""}
|
|
124
|
+
onChange={(e) => onValueChange?.(e.target.value)}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
))
|
|
128
|
+
|
|
129
|
+
fieldRegistry.register("checkbox", ({ value, onValueChange, label }) => (
|
|
130
|
+
<label>
|
|
131
|
+
<input
|
|
132
|
+
type="checkbox"
|
|
133
|
+
checked={!!value}
|
|
134
|
+
onChange={(e) => onValueChange?.(e.target.checked)}
|
|
135
|
+
/>
|
|
136
|
+
{label}
|
|
137
|
+
</label>
|
|
138
|
+
))
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## ⚛️ React Usage
|
|
145
|
+
|
|
146
|
+
**MultiFieldInput (Main Form Engine)**
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { MultiFieldInput } from "@dynamic-field-kit/react"
|
|
150
|
+
import { FieldDescription } from "@dynamic-field-kit/core"
|
|
151
|
+
|
|
152
|
+
const fields: FieldDescription[] = [
|
|
153
|
+
{ name: "email", type: "text", label: "Email" },
|
|
154
|
+
{ name: "age", type: "number", label: "Age" }
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
const Example = () => {
|
|
158
|
+
return (
|
|
159
|
+
<MultiFieldInput
|
|
160
|
+
fieldDescriptions={fields}
|
|
161
|
+
onChange={(data) => {
|
|
162
|
+
console.log("Form data:", data)
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Controlled Form**
|
|
170
|
+
```tsx
|
|
171
|
+
const [formData, setFormData] = useState({})
|
|
172
|
+
|
|
173
|
+
<MultiFieldInput
|
|
174
|
+
fieldDescriptions={fields}
|
|
175
|
+
properties={formData}
|
|
176
|
+
onChange={setFormData}
|
|
177
|
+
/>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## ➕ Adding a New Field Type
|
|
183
|
+
|
|
184
|
+
You **do not** need to modify the library.
|
|
185
|
+
|
|
186
|
+
Just extend `FieldTypeMap`:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
declare module "@dynamic-field-kit/react" {
|
|
190
|
+
interface FieldTypeMap {
|
|
191
|
+
date: Date
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Then register a renderer:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
fieldRegistry.register("date", ({ value, onValueChange }) => (
|
|
200
|
+
<input
|
|
201
|
+
type="date"
|
|
202
|
+
value={value ? value.toISOString().slice(0, 10) : ""}
|
|
203
|
+
onChange={(e) =>
|
|
204
|
+
onValueChange?.(new Date(e.target.value))
|
|
205
|
+
}
|
|
206
|
+
/>
|
|
207
|
+
))
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Now `"date"` is fully type-safe everywhere.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
## 🧠 Domain Typing (Optional)
|
|
214
|
+
The library intentionally avoids enforcing domain types.
|
|
215
|
+
If you want strict typing, cast inside your app:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
interface UserForm {
|
|
219
|
+
age: number
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const fields: FieldDescription[] = [
|
|
223
|
+
{
|
|
224
|
+
name: "age",
|
|
225
|
+
type: "number",
|
|
226
|
+
appearCondition: (data) =>
|
|
227
|
+
(data as UserForm).age > 18
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
```
|
|
231
|
+
This keeps the library generic while allowing strict typing in the app.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 🧩 Components API
|
|
236
|
+
|
|
237
|
+
**<DynamicInput />**
|
|
238
|
+
|
|
239
|
+
Resolves and renders a field based on its type.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
<DynamicInput type="text" value="hello" />
|
|
243
|
+
```
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
**<FieldInput />**
|
|
247
|
+
|
|
248
|
+
Renders a single field with value binding.
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<FieldInput
|
|
252
|
+
fieldDescription={field}
|
|
253
|
+
renderInfos={formData}
|
|
254
|
+
onChange={(value, key) => {}}
|
|
255
|
+
/>
|
|
256
|
+
```
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 🏗 Architecture
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
dynamic-field-kit
|
|
263
|
+
├─ packages/
|
|
264
|
+
│ ├─ core # framework-agnostic types
|
|
265
|
+
│ └─ react # React renderer + registry
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 🚫 Non-Goals
|
|
271
|
+
This library intentionally does not include:
|
|
272
|
+
-Built-in UI components
|
|
273
|
+
-Built-in UI components
|
|
274
|
+
-Form state management library
|
|
275
|
+
|
|
276
|
+
It is a **form engine**, not a full form framework.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-field-kit/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Core types and field registry for dynamic-field-kit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
"build": "tsup",
|
|
22
22
|
"clean": "rm -rf dist"
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
}
|