@page-speed/forms 0.7.9 → 0.8.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.
Files changed (2) hide show
  1. package/README.md +217 -116
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
- ![Page Speed React Forms](https://octane.cdn.ing/api/v1/images/transform?url=https://cdn.ing/assets/i/r/286339/nwqgw37pigfluhcmmjmpql3yj9y4/github.png&q=90)
1
+ # @page-speed/forms
2
2
 
3
- # `@page-speed/forms`
3
+ ## Type-safe, high-performance React form state and input components for OpenSite/DashTrack workloads
4
4
 
5
- Type-safe, high-performance React form state and input components for OpenSite/DashTrack workloads.
5
+ ![Page Speed React Forms](https://octane.cdn.ing/api/v1/images/transform?url=https://cdn.ing/assets/i/r/286339/nwqgw37pigfluhcmmjmpql3yj9y4/github.png&f=webp&q=90)
6
+
7
+ <br />
6
8
 
7
9
  [![npm version](https://img.shields.io/npm/v/@page-speed/forms?style=flat-square)](https://www.npmjs.com/package/@page-speed/forms)
8
10
  [![npm downloads](https://img.shields.io/npm/dm/@page-speed/forms?style=flat-square)](https://www.npmjs.com/package/@page-speed/forms)
@@ -10,8 +12,8 @@ Type-safe, high-performance React form state and input components for OpenSite/D
10
12
 
11
13
  ## Highlights
12
14
 
15
+ - **`FormEngine`** — declarative form component with built-in API integration
13
16
  - Field-level reactivity via `@legendapp/state/react`
14
- - Typed `useForm` and `useField` APIs
15
17
  - Built-in input library (text, select, date, time, upload, rich text)
16
18
  - Tree-shakable subpath exports (`/core`, `/inputs`, `/validation`, `/upload`, `/integration`)
17
19
  - Validation rules and utilities (sync + async)
@@ -30,112 +32,151 @@ Peer dependencies:
30
32
  - `react >= 16.8.0`
31
33
  - `react-dom >= 16.8.0`
32
34
 
33
- ## Quick Start
35
+ ## Quick Start with FormEngine
36
+
37
+ `FormEngine` is the recommended entry point for most use cases. It provides a declarative API for rendering forms with built-in API integration, validation, file uploads, and styling.
34
38
 
35
39
  ```tsx
36
40
  import * as React from "react";
37
- import { Form, Field, useForm } from "@page-speed/forms";
38
- import { TextInput, Select } from "@page-speed/forms/inputs";
39
- import { required, email } from "@page-speed/forms/validation/rules";
41
+ import {
42
+ FormEngine,
43
+ type FormFieldConfig,
44
+ } from "@page-speed/forms/integration";
40
45
 
41
- export function ContactForm() {
42
- const form = useForm({
43
- initialValues: {
44
- fullName: "",
45
- email: "",
46
- inquiryType: "",
47
- },
48
- validationSchema: {
49
- fullName: required(),
50
- email: [required(), email()],
51
- inquiryType: required(),
52
- },
53
- onSubmit: async (values) => {
54
- console.log(values);
55
- },
56
- });
46
+ const fields: FormFieldConfig[] = [
47
+ {
48
+ name: "full_name",
49
+ type: "text",
50
+ label: "Full Name",
51
+ required: true,
52
+ placeholder: "Your name",
53
+ columnSpan: 12,
54
+ },
55
+ {
56
+ name: "email",
57
+ type: "email",
58
+ label: "Email",
59
+ required: true,
60
+ placeholder: "you@example.com",
61
+ columnSpan: 6,
62
+ },
63
+ {
64
+ name: "phone",
65
+ type: "tel",
66
+ label: "Phone",
67
+ columnSpan: 6,
68
+ },
69
+ {
70
+ name: "content",
71
+ type: "textarea",
72
+ label: "Message",
73
+ required: true,
74
+ columnSpan: 12,
75
+ },
76
+ ];
57
77
 
78
+ export function ContactForm() {
58
79
  return (
59
- <Form form={form}>
60
- <Field name="fullName" label="Full Name" required>
61
- {({ field, meta }) => (
62
- <TextInput
63
- {...field}
64
- placeholder="Your name"
65
- error={Boolean(meta.touched && meta.error)}
66
- />
67
- )}
68
- </Field>
80
+ <FormEngine
81
+ api={{
82
+ endpoint: "/api/contact",
83
+ method: "post",
84
+ submissionConfig: { behavior: "showConfirmation" },
85
+ }}
86
+ fields={fields}
87
+ successMessage="Thanks for reaching out!"
88
+ formLayoutSettings={{
89
+ submitButtonSetup: {
90
+ submitLabel: "Send Message",
91
+ },
92
+ }}
93
+ />
94
+ );
95
+ }
96
+ ```
69
97
 
70
- <Field name="email" label="Email" required>
71
- {({ field, meta }) => (
72
- <TextInput
73
- {...field}
74
- type="email"
75
- placeholder="you@example.com"
76
- error={Boolean(meta.touched && meta.error)}
77
- />
78
- )}
79
- </Field>
98
+ ### FormEngine Props
80
99
 
81
- <Field name="inquiryType" label="Inquiry Type" required>
82
- {({ field, meta }) => (
83
- <Select
84
- {...field}
85
- options={[
86
- { label: "General", value: "general" },
87
- { label: "Sales", value: "sales" },
88
- { label: "Support", value: "support" },
89
- ]}
90
- error={Boolean(meta.touched && meta.error)}
91
- />
92
- )}
93
- </Field>
100
+ | Prop | Type | Description |
101
+ |------|------|-------------|
102
+ | `api` | `PageSpeedFormConfig` | API endpoint and submission configuration |
103
+ | `fields` | `FormFieldConfig[]` | Array of field definitions |
104
+ | `formLayoutSettings` | `FormEngineLayoutSettings` | Layout, style, and submit button settings |
105
+ | `successMessage` | `ReactNode` | Message shown after successful submission |
106
+ | `onSubmit` | `(values) => void \| Promise<void>` | Custom submit handler |
107
+ | `onSuccess` | `(data) => void` | Called after successful submission |
108
+ | `onError` | `(error) => void` | Called when submission fails |
109
+ | `resetOnSuccess` | `boolean` | Reset form after success (default: `true`) |
94
110
 
95
- <button type="submit" disabled={form.isSubmitting}>
96
- Submit
97
- </button>
98
- </Form>
99
- );
111
+ ### Field Configuration
112
+
113
+ Each field in the `fields` array supports:
114
+
115
+ ```tsx
116
+ interface FormFieldConfig {
117
+ name: string;
118
+ type: "text" | "email" | "tel" | "textarea" | "select" | "multiselect" |
119
+ "date" | "daterange" | "time" | "file" | "checkbox" | "radio";
120
+ label?: string;
121
+ placeholder?: string;
122
+ required?: boolean;
123
+ columnSpan?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
124
+ className?: string;
125
+ options?: { label: string; value: string }[]; // For select/multiselect/radio
126
+ // File-specific props
127
+ accept?: string;
128
+ maxFiles?: number;
129
+ maxFileSize?: number;
100
130
  }
101
131
  ```
102
132
 
103
- ### Grouped Form Configuration
133
+ ### Layout Options
104
134
 
105
- `Form` supports both classic props and grouped config objects for cleaner callsites.
135
+ #### Standard Layout (default)
106
136
 
107
- ```tsx
108
- import * as React from "react";
109
- import { Form } from "@page-speed/forms";
137
+ Multi-column grid with a submit button below the fields:
110
138
 
111
- <Form
112
- form={form}
113
- notificationConfig={{
114
- successMessage: "Thanks, submission received.",
115
- submissionError,
116
- }}
117
- styleConfig={{
118
- formClassName: "space-y-6",
119
- successMessageClassName: "bg-emerald-600 text-white",
120
- errorMessageClassName: "bg-red-600 text-white",
139
+ ```tsx
140
+ <FormEngine
141
+ fields={fields}
142
+ formLayoutSettings={{
143
+ formLayout: "standard",
144
+ submitButtonSetup: {
145
+ submitLabel: "Submit",
146
+ submitVariant: "default", // | "destructive" | "outline" | "secondary" | "ghost" | "link"
147
+ },
148
+ styleRules: {
149
+ formContainer: "max-w-2xl mx-auto",
150
+ fieldsContainer: "gap-6",
151
+ formClassName: "space-y-4",
152
+ },
121
153
  }}
122
- formConfig={{
123
- endpoint: "/api/contact",
124
- method: "post",
125
- submissionConfig: {
126
- behavior: "showConfirmation",
154
+ />
155
+ ```
156
+
157
+ #### Button-Group Layout
158
+
159
+ Inline input with submit button (e.g., newsletter signup):
160
+
161
+ ```tsx
162
+ <FormEngine
163
+ fields={[{ name: "email", type: "email", label: "Email", required: true }]}
164
+ formLayoutSettings={{
165
+ formLayout: "button-group",
166
+ buttonGroupSetup: {
167
+ size: "lg", // | "xs" | "sm" | "default"
168
+ submitLabel: "Subscribe",
169
+ submitVariant: "default",
127
170
  },
128
171
  }}
129
- />;
172
+ />
130
173
  ```
131
174
 
132
- ### `FormEngine` Defaults + Wrapper Setup
175
+ ### Using formEngineSetup Wrapper
133
176
 
134
- `FormEngine` supports both direct props and a wrapper setup object. This is useful
135
- for block/component libraries that want to provide local default fields and styles.
177
+ For block/component libraries that provide default configurations:
136
178
 
137
179
  ```tsx
138
- import * as React from "react";
139
180
  import {
140
181
  FormEngine,
141
182
  type FormEngineSetup,
@@ -151,68 +192,78 @@ const defaultStyleRules: FormEngineStyleRules = {
151
192
  formClassName: "space-y-6",
152
193
  };
153
194
 
154
- const setup: FormEngineSetup = {
155
- api: { endpoint: "/api/contact", method: "post" },
156
- formLayoutSettings: { formLayout: "standard" },
157
- };
158
-
159
- <FormEngine
160
- formEngineSetup={setup}
161
- defaultFields={defaultFields}
162
- defaultStyleRules={defaultStyleRules}
163
- />;
195
+ // Consumer passes setup, component provides defaults
196
+ function ContactBlock({ formEngineSetup }: { formEngineSetup?: FormEngineSetup }) {
197
+ return (
198
+ <FormEngine
199
+ formEngineSetup={formEngineSetup}
200
+ defaultFields={defaultFields}
201
+ defaultStyleRules={defaultStyleRules}
202
+ />
203
+ );
204
+ }
164
205
  ```
165
206
 
166
207
  ## Package Entry Points
167
208
 
168
209
  ### Main
210
+
169
211
  - `@page-speed/forms`
170
212
 
171
- Exports:
213
+ #### Exports:
214
+
172
215
  - `useForm`, `useField`, `Form`, `Field`, `FormContext`
173
- - core form/types interfaces
216
+ - Core form/types interfaces
217
+
218
+ ### Integration (Recommended)
219
+
220
+ - `@page-speed/forms/integration`
221
+
222
+ #### Exports:
223
+
224
+ - `FormEngine`, `FormEngineSetup`, `FormEngineProps`
225
+ - `FormFieldConfig`, `FormEngineStyleRules`, `FormEngineLayoutSettings`
226
+ - `DynamicFormField`, `useContactForm`, `useFileUpload`
174
227
 
175
228
  ### Inputs
229
+
176
230
  - `@page-speed/forms/inputs`
177
231
 
178
- Exports:
179
- - `TextInput`
180
- - `TextArea`
181
- - `Checkbox`
182
- - `CheckboxGroup`
183
- - `Radio`
184
- - `Select`
185
- - `MultiSelect`
186
- - `DatePicker`
187
- - `DateRangePicker`
188
- - `TimePicker`
232
+ #### Exports:
233
+
234
+ - `TextInput`, `TextArea`, `Checkbox`, `CheckboxGroup`, `Radio`
235
+ - `Select`, `MultiSelect`, `DatePicker`, `DateRangePicker`, `TimePicker`
189
236
  - `FileInput`
190
237
 
191
238
  ### Validation
239
+
192
240
  - `@page-speed/forms/validation`
193
241
  - `@page-speed/forms/validation/rules`
194
242
  - `@page-speed/forms/validation/utils`
195
243
  - `@page-speed/forms/validation/valibot`
196
244
 
197
- ### Upload and Integration
245
+ ### Upload
246
+
198
247
  - `@page-speed/forms/upload`
199
- - `@page-speed/forms/integration`
200
248
 
201
249
  ## Input Notes
202
250
 
203
251
  ### `TimePicker`
204
- `TimePicker` now uses a native `input[type="time"]` UX internally.
252
+
253
+ `TimePicker` uses a native `input[type="time"]` UX internally.
205
254
 
206
255
  - Accepts controlled values in `HH:mm` (24-hour) or `h:mm AM/PM` (12-hour)
207
256
  - Emits `HH:mm` when `use24Hour` is `true`
208
257
  - Emits `h:mm AM/PM` when `use24Hour` is `false`
209
258
 
210
259
  ### `DatePicker` and `DateRangePicker`
260
+
211
261
  - Calendar popovers close on outside click
212
262
  - Compact month/day layout using tokenized Tailwind classes
213
263
  - `DateRangePicker` renders two months and highlights endpoints + in-range dates
214
264
 
215
265
  ### `Select` and `MultiSelect`
266
+
216
267
  - Close on outside click
217
268
  - Search support
218
269
  - Option groups
@@ -222,8 +273,6 @@ Exports:
222
273
 
223
274
  This library ships with Tailwind utility classes and semantic token class names.
224
275
 
225
- It is **not** a BEM-only unstyled package anymore.
226
-
227
276
  ### Base conventions
228
277
 
229
278
  - Inputs/triggers are transparent shells with semantic borders/rings
@@ -231,6 +280,19 @@ It is **not** a BEM-only unstyled package anymore.
231
280
  - Error states use destructive border/ring
232
281
  - Dropdown selected rows use muted backgrounds
233
282
 
283
+ ### FormEngine Style Rules
284
+
285
+ ```tsx
286
+ interface FormEngineStyleRules {
287
+ formContainer?: string; // Wrapper around <form>
288
+ fieldsContainer?: string; // Grid wrapper for fields
289
+ fieldClassName?: string; // Fallback className for fields
290
+ formClassName?: string; // Applied to <form> element
291
+ successMessageClassName?: string;
292
+ errorMessageClassName?: string;
293
+ }
294
+ ```
295
+
234
296
  ### Autofill normalization
235
297
 
236
298
  Text-like controls apply autofill reset classes to avoid browser-injected background/text colors breaking your theme contrast.
@@ -240,6 +302,7 @@ See `INPUT_AUTOFILL_RESET_CLASSES` in `src/utils.ts`.
240
302
  ### Token requirements
241
303
 
242
304
  Ensure your app defines semantic tokens used in classes such as:
305
+
243
306
  - `background`, `foreground`, `border`, `input`, `ring`
244
307
  - `primary`, `primary-foreground`
245
308
  - `muted`, `muted-foreground`
@@ -249,9 +312,45 @@ Ensure your app defines semantic tokens used in classes such as:
249
312
 
250
313
  For complete styling guidance, see [`docs/STYLES.md`](./docs/STYLES.md).
251
314
 
315
+ ## Advanced: Low-Level APIs
316
+
317
+ For custom form implementations, the lower-level `useForm`, `Form`, and `Field` APIs are available:
318
+
319
+ ```tsx
320
+ import { Form, Field, useForm } from "@page-speed/forms";
321
+ import { TextInput } from "@page-speed/forms/inputs";
322
+
323
+ function CustomForm() {
324
+ const form = useForm({
325
+ initialValues: { email: "" },
326
+ validationSchema: {
327
+ email: (value) => (!value ? "Required" : undefined),
328
+ },
329
+ onSubmit: async (values) => {
330
+ console.log(values);
331
+ },
332
+ });
333
+
334
+ return (
335
+ <Form form={form}>
336
+ <Field name="email" label="Email">
337
+ {({ field, meta }) => (
338
+ <TextInput
339
+ {...field}
340
+ error={Boolean(meta.touched && meta.error)}
341
+ />
342
+ )}
343
+ </Field>
344
+ <button type="submit">Submit</button>
345
+ </Form>
346
+ );
347
+ }
348
+ ```
349
+
252
350
  ## Validation Utilities
253
351
 
254
352
  Use built-in rules:
353
+
255
354
  - `required`, `email`, `url`, `phone`
256
355
  - `minLength`, `maxLength`, `min`, `max`
257
356
  - `pattern`, `matches`, `oneOf`
@@ -259,14 +358,16 @@ Use built-in rules:
259
358
  - `compose`
260
359
 
261
360
  Use utilities from `/validation/utils`:
361
+
262
362
  - `debounce`, `asyncValidator`, `crossFieldValidator`, `when`
263
363
  - `setErrorMessages`, `getErrorMessage`, `resetErrorMessages`
264
364
 
265
365
  ## File Uploads
266
366
 
267
- `FileInput` supports validation, drag/drop, preview, and crop workflows.
367
+ `FileInput` and `FormEngine` support validation, drag/drop, preview, and crop workflows.
268
368
 
269
369
  For full two-phase upload patterns and serializer usage, see:
370
+
270
371
  - [`docs/FILE_UPLOADS.md`](./docs/FILE_UPLOADS.md)
271
372
  - `@page-speed/forms/integration`
272
373
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@page-speed/forms",
3
- "version": "0.7.9",
3
+ "version": "0.8.1",
4
4
  "description": "Ultra-high-performance React form library with field-level reactivity and tree-shakable architecture",
5
5
  "keywords": [
6
6
  "react",