react-smart-fields 1.1.6 โ 2.2.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 +218 -129
- package/dist/index.cjs +595 -0
- package/dist/index.d.cts +129 -0
- package/dist/index.d.ts +129 -2
- package/dist/index.js +558 -1
- package/package.json +25 -14
- package/.gitattributes +0 -2
- package/dist/components/DynamicFields.d.ts +0 -31
- package/dist/components/DynamicFields.js +0 -97
- package/src/components/DynamicFields.tsx +0 -259
- package/src/index.ts +0 -2
- package/tsconfig.json +0 -13
package/README.md
CHANGED
|
@@ -1,190 +1,279 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Smart Fields
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
`react-smart-fields` is a powerful dynamic form renderer for React with:
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
- configurable field schemas
|
|
10
|
+
- built-in + custom validation
|
|
11
|
+
- conditional visibility/disable/required rules
|
|
12
|
+
- async options for selects
|
|
13
|
+
- nested field names (`user.addresses[0].city`)
|
|
14
|
+
- theme presets + headless mode + full render slots
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
## Installation
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* ๐
Fully customizable via Tailwind-compatible className props
|
|
16
|
-
* ๐ง Built-in required field validation
|
|
17
|
-
* ๐งฑ Extensible for advanced usage
|
|
18
|
-
* ๐ Full **dark mode** support
|
|
19
|
-
* ๐ Real-time `onChange` callback with live `formData` and `errors`
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## ๐ฆ Installation
|
|
24
|
-
|
|
25
|
-
Copy the `DynamicFields.tsx` file into your React project.
|
|
26
|
-
|
|
27
|
-
Make sure you have Tailwind CSS and React configured in your app.
|
|
18
|
+
```bash
|
|
19
|
+
npm install react-smart-fields
|
|
20
|
+
```
|
|
28
21
|
|
|
29
|
-
|
|
22
|
+
or
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
```bash
|
|
25
|
+
yarn add react-smart-fields
|
|
26
|
+
```
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
## Quick Start
|
|
34
29
|
|
|
35
30
|
```tsx
|
|
36
|
-
import
|
|
37
|
-
import DynamicFields from "./DynamicFields";
|
|
31
|
+
import { DynamicFields, FieldConfig } from "react-smart-fields";
|
|
38
32
|
|
|
39
|
-
const fields = [
|
|
40
|
-
{
|
|
41
|
-
name: "username",
|
|
42
|
-
label: "Username",
|
|
43
|
-
type: "text",
|
|
44
|
-
required: true,
|
|
45
|
-
},
|
|
33
|
+
const fields: FieldConfig[] = [
|
|
34
|
+
{ name: "name", label: "Name", type: "text", required: true },
|
|
46
35
|
{
|
|
47
36
|
name: "email",
|
|
48
37
|
label: "Email",
|
|
49
|
-
type: "
|
|
50
|
-
required: true,
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: "gender",
|
|
54
|
-
label: "Gender",
|
|
55
|
-
type: "radio",
|
|
38
|
+
type: "email",
|
|
56
39
|
required: true,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{ label: "Female", value: "female" },
|
|
60
|
-
],
|
|
40
|
+
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
41
|
+
patternMessage: "Please enter a valid email",
|
|
61
42
|
},
|
|
62
43
|
{
|
|
63
44
|
name: "country",
|
|
64
45
|
label: "Country",
|
|
65
46
|
type: "select",
|
|
47
|
+
placeholder: "Select country",
|
|
66
48
|
options: [
|
|
67
|
-
{ label: "USA", value: "us" },
|
|
68
49
|
{ label: "India", value: "in" },
|
|
50
|
+
{ label: "United States", value: "us" },
|
|
69
51
|
],
|
|
70
52
|
},
|
|
71
53
|
{
|
|
72
|
-
name: "
|
|
73
|
-
label: "Subscribe",
|
|
54
|
+
name: "newsletter",
|
|
55
|
+
label: "Subscribe to newsletter",
|
|
74
56
|
type: "checkbox",
|
|
75
|
-
|
|
57
|
+
defaultValue: true,
|
|
58
|
+
},
|
|
76
59
|
];
|
|
77
60
|
|
|
78
|
-
function App() {
|
|
79
|
-
const handleChange = (data) => {
|
|
80
|
-
console.log("Form Data:", data);
|
|
81
|
-
};
|
|
82
|
-
|
|
61
|
+
export default function App() {
|
|
83
62
|
return (
|
|
84
63
|
<DynamicFields
|
|
85
64
|
fields={fields}
|
|
86
|
-
title="
|
|
87
|
-
description="
|
|
88
|
-
|
|
65
|
+
title="Profile"
|
|
66
|
+
description="Update your profile details"
|
|
67
|
+
theme="default"
|
|
68
|
+
size="md"
|
|
69
|
+
onChange={(values, meta) => {
|
|
70
|
+
console.log(values, meta?.errors);
|
|
71
|
+
}}
|
|
89
72
|
/>
|
|
90
73
|
);
|
|
91
74
|
}
|
|
92
75
|
```
|
|
93
76
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
77
|
+
## Major Capabilities
|
|
78
|
+
|
|
79
|
+
- **Validation engine**: `required`, `min/max`, `minLength/maxLength`, `pattern`, and `validate`
|
|
80
|
+
- **Conditional behavior**: `showWhen`, `disableWhen`, `requiredWhen`
|
|
81
|
+
- **Nested paths**: dot/bracket field names are supported
|
|
82
|
+
- **Async select options**: `loadOptions` with `loadOptionsOn: "mount" | "change"`
|
|
83
|
+
- **Theme presets**: `default`, `minimal`, `filled`, `underline`
|
|
84
|
+
- **Headless mode**: set `headless` to fully control UI
|
|
85
|
+
- **Render slots**: `renderField`, `renderControl`, `renderLabel`, `renderDescription`, `renderError`
|
|
86
|
+
- **State styling hooks**: `stateClassNames` for `invalid`, `disabled`, `readonly`, `dirty`, `touched`
|
|
87
|
+
|
|
88
|
+
## Component Props
|
|
89
|
+
|
|
90
|
+
| Prop | Type | Required | Description |
|
|
91
|
+
| --- | --- | --- | --- |
|
|
92
|
+
| `fields` | `FieldConfig[]` | โ
| Field schema array |
|
|
93
|
+
| `onChange` | `(values, meta?) => void` | โ
| Fires on every value/meta update |
|
|
94
|
+
| `value` | `Record<string, any>` | โ | Controlled mode values |
|
|
95
|
+
| `defaultValues` | `Record<string, any>` | โ | Initial values (uncontrolled mode) |
|
|
96
|
+
| `resolver` | `(values) => errors \| Promise<errors>` | โ | External validation resolver |
|
|
97
|
+
| `validateMode` | `"change" \| "blur"` | โ | Validation trigger mode |
|
|
98
|
+
| `showErrorWhen` | `"always" \| "touched" \| "dirty"` | โ | Error visibility strategy |
|
|
99
|
+
| `headless` | `boolean` | โ | Disables built-in styling |
|
|
100
|
+
| `theme` | `"default" \| "minimal" \| "filled" \| "underline"` | โ | Built-in visual preset |
|
|
101
|
+
| `size` | `"sm" \| "md" \| "lg"` | โ | Input/label sizing |
|
|
102
|
+
| `ui` | `UiClassMap` | โ | Global slot class overrides |
|
|
103
|
+
| `stateClassNames` | `StateClassNames` | โ | State class hooks |
|
|
104
|
+
| `renderField` | `(ctx) => ReactNode` | โ | Full custom field renderer |
|
|
105
|
+
| `renderControl` | `(ctx) => ReactNode` | โ | Custom control renderer |
|
|
106
|
+
| `renderLabel` | `(ctx) => ReactNode` | โ | Custom label renderer |
|
|
107
|
+
| `renderDescription` | `(ctx) => ReactNode` | โ | Custom helper text renderer |
|
|
108
|
+
| `renderError` | `(ctx) => ReactNode` | โ | Custom error renderer |
|
|
109
|
+
| `title` | `string` | โ | Form heading |
|
|
110
|
+
| `description` | `string` | โ | Form subtitle |
|
|
111
|
+
|
|
112
|
+
Legacy class props are still supported for backward compatibility:
|
|
113
|
+
`className`, `inputClassName`, `labelClassName`, `mainFieldClassName`, `fieldClassName`, `errorClassName`, `selectClassName`, `optionClassName`, `checkboxClassName`, `radioClassName`.
|
|
114
|
+
|
|
115
|
+
## FieldConfig API
|
|
97
116
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
```ts
|
|
118
|
+
type FieldConfig = {
|
|
119
|
+
name: string;
|
|
120
|
+
label?: string;
|
|
121
|
+
type:
|
|
122
|
+
| "text"
|
|
123
|
+
| "number"
|
|
124
|
+
| "select"
|
|
125
|
+
| "radio"
|
|
126
|
+
| "checkbox"
|
|
127
|
+
| "textarea"
|
|
128
|
+
| "email"
|
|
129
|
+
| "password"
|
|
130
|
+
| "tel"
|
|
131
|
+
| "url"
|
|
132
|
+
| "date";
|
|
133
|
+
|
|
134
|
+
// values/options
|
|
135
|
+
defaultValue?: any;
|
|
136
|
+
placeholder?: string;
|
|
137
|
+
options?: { label: string; value: string | number | boolean | null | undefined; disabled?: boolean }[];
|
|
138
|
+
loadOptions?: (ctx: { values: Record<string, any>; field: FieldConfig }) => Promise<FieldOption[]>;
|
|
139
|
+
loadOptionsOn?: "mount" | "change";
|
|
140
|
+
|
|
141
|
+
// behavior
|
|
142
|
+
disabled?: boolean;
|
|
143
|
+
readOnly?: boolean;
|
|
144
|
+
showWhen?: (values: Record<string, any>) => boolean;
|
|
145
|
+
disableWhen?: (values: Record<string, any>) => boolean;
|
|
146
|
+
required?: boolean;
|
|
147
|
+
requiredWhen?: (values: Record<string, any>) => boolean;
|
|
148
|
+
requiredMessage?: string;
|
|
149
|
+
|
|
150
|
+
// validation
|
|
151
|
+
min?: number;
|
|
152
|
+
max?: number;
|
|
153
|
+
minLength?: number;
|
|
154
|
+
maxLength?: number;
|
|
155
|
+
pattern?: RegExp;
|
|
156
|
+
patternMessage?: string;
|
|
157
|
+
validate?: (value: any, values: Record<string, any>) => string | undefined | null;
|
|
158
|
+
|
|
159
|
+
// value transforms
|
|
160
|
+
transformIn?: (storedValue: any, values: Record<string, any>) => any;
|
|
161
|
+
transformOut?: (rawValue: any, values: Record<string, any>) => any;
|
|
162
|
+
|
|
163
|
+
// styling/rendering
|
|
164
|
+
className?: string;
|
|
165
|
+
inputClassName?: string;
|
|
166
|
+
labelClassName?: string;
|
|
167
|
+
errorClassName?: string;
|
|
168
|
+
ui?: UiClassMap;
|
|
169
|
+
renderControl?: (ctx: RenderControlContext) => ReactNode;
|
|
170
|
+
};
|
|
171
|
+
```
|
|
118
172
|
|
|
119
|
-
|
|
173
|
+
## Customization Examples
|
|
120
174
|
|
|
121
|
-
|
|
122
|
-
| ------------- | --------------------------------------------------------- | --------------------------------- | -------------------------------------- |
|
|
123
|
-
| `name` | `string` | โ
Yes | Unique key |
|
|
124
|
-
| `label` | `string` | โ No | Display label |
|
|
125
|
-
| `type` | `"text"`, `"number"`, `"select"`, `"radio"`, `"checkbox"` | โ
Yes | Field type |
|
|
126
|
-
| `required` | `boolean` | โ No | Show red asterisk and basic validation |
|
|
127
|
-
| `placeholder` | `string` | โ No | Placeholder (for inputs/selects) |
|
|
128
|
-
| `description` | `string` | โ No | Optional helper text |
|
|
129
|
-
| `options` | `{ label: string; value: any; }[]` | Required for `select` and `radio` | Dropdown/Radio values |
|
|
175
|
+
### 1) Conditional + Nested Fields
|
|
130
176
|
|
|
131
|
-
|
|
177
|
+
```tsx
|
|
178
|
+
const fields: FieldConfig[] = [
|
|
179
|
+
{ name: "accountType", label: "Account Type", type: "select", options: [
|
|
180
|
+
{ label: "Individual", value: "individual" },
|
|
181
|
+
{ label: "Business", value: "business" },
|
|
182
|
+
]},
|
|
183
|
+
{
|
|
184
|
+
name: "company.name",
|
|
185
|
+
label: "Company Name",
|
|
186
|
+
type: "text",
|
|
187
|
+
showWhen: (values) => values.accountType === "business",
|
|
188
|
+
requiredWhen: (values) => values.accountType === "business",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "addresses[0].city",
|
|
192
|
+
label: "Primary City",
|
|
193
|
+
type: "text",
|
|
194
|
+
required: true,
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
```
|
|
132
198
|
|
|
133
|
-
|
|
199
|
+
### 2) Async Select Options
|
|
134
200
|
|
|
135
|
-
|
|
201
|
+
```tsx
|
|
202
|
+
{
|
|
203
|
+
name: "state",
|
|
204
|
+
label: "State",
|
|
205
|
+
type: "select",
|
|
206
|
+
loadOptionsOn: "change",
|
|
207
|
+
loadOptions: async ({ values }) => {
|
|
208
|
+
if (!values.country) return [];
|
|
209
|
+
const response = await fetch(`/api/states?country=${values.country}`);
|
|
210
|
+
const data = await response.json();
|
|
211
|
+
return data.map((item: any) => ({ label: item.name, value: item.code }));
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
```
|
|
136
215
|
|
|
137
|
-
|
|
138
|
-
| ------------------- | ------------------- | ---------------------------------- |
|
|
139
|
-
| Wrapper div | `className` | `bg-white dark:bg-gray-900` |
|
|
140
|
-
| Label text | `labelClassName` | `text-gray-800 dark:text-gray-200` |
|
|
141
|
-
| Input fields | `inputClassName` | `bg-white dark:bg-gray-800` |
|
|
142
|
-
| Select dropdown | `selectClassName` | `rounded-lg` |
|
|
143
|
-
| Radio buttons | `radioClassName` | `rounded-full` |
|
|
144
|
-
| Checkbox | `checkboxClassName` | `rounded` |
|
|
145
|
-
| Field container div | `fieldClassName` | `w-full` |
|
|
146
|
-
| Dropdown options | `optionClassName` | `hover:bg-gray-100` |
|
|
147
|
-
| Error messages | `errorClassName` | `text-red-500` |
|
|
216
|
+
### 3) Headless + Slots
|
|
148
217
|
|
|
149
|
-
|
|
218
|
+
```tsx
|
|
219
|
+
<DynamicFields
|
|
220
|
+
fields={fields}
|
|
221
|
+
headless
|
|
222
|
+
renderField={({ field, labelNode, controlNode, errorNode, helpNode }) => (
|
|
223
|
+
<div key={field.name} className="mb-6">
|
|
224
|
+
{labelNode}
|
|
225
|
+
<div className="mt-2">{controlNode}</div>
|
|
226
|
+
<div className="mt-1">{helpNode}</div>
|
|
227
|
+
<div className="mt-1">{errorNode}</div>
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
onChange={(values) => console.log(values)}
|
|
231
|
+
/>
|
|
232
|
+
```
|
|
150
233
|
|
|
151
|
-
|
|
234
|
+
### 4) Resolver Validation
|
|
152
235
|
|
|
153
|
-
|
|
236
|
+
```tsx
|
|
237
|
+
<DynamicFields
|
|
238
|
+
fields={fields}
|
|
239
|
+
resolver={(values) => {
|
|
240
|
+
const errors: Record<string, string> = {};
|
|
241
|
+
if (values.password !== values.confirmPassword) {
|
|
242
|
+
errors.confirmPassword = "Passwords do not match";
|
|
243
|
+
}
|
|
244
|
+
return errors;
|
|
245
|
+
}}
|
|
246
|
+
onChange={(values, meta) => {
|
|
247
|
+
console.log(meta?.errors);
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
```
|
|
154
251
|
|
|
155
|
-
|
|
252
|
+
## Type Exports
|
|
156
253
|
|
|
157
254
|
```ts
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
255
|
+
import type {
|
|
256
|
+
FieldConfig,
|
|
257
|
+
FieldOption,
|
|
258
|
+
FieldType,
|
|
259
|
+
DynamicFieldsProps,
|
|
260
|
+
UiClassMap,
|
|
261
|
+
StateClassNames,
|
|
262
|
+
ChangeMeta,
|
|
263
|
+
RenderControlContext,
|
|
264
|
+
RenderFieldContext,
|
|
265
|
+
} from "react-smart-fields";
|
|
166
266
|
```
|
|
167
267
|
|
|
168
|
-
|
|
268
|
+
## Author
|
|
169
269
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
**Name:** Pratik Panchal
|
|
270
|
+
**Name:** Pratik Panchal
|
|
173
271
|
**GitHub:** [@Pratikpanchal25](https://github.com/Pratikpanchal25)
|
|
174
272
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
## ๐ License
|
|
273
|
+
## License
|
|
179
274
|
|
|
180
275
|
MIT โ Free to use, modify and distribute.
|
|
181
276
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
## ๐ Contributing
|
|
185
|
-
|
|
186
|
-
Pull requests are welcome! If you find bugs, feel free to [open an issue](https://github.com/ShubhamNakum/DynamicFields/issues).
|
|
187
|
-
|
|
188
|
-
---
|
|
277
|
+
## Contributing
|
|
189
278
|
|
|
190
|
-
|
|
279
|
+
Pull requests are welcome! If you find bugs, feel free to open an issue.
|