@page-speed/forms 0.1.0 → 0.1.2

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
@@ -1,469 +1,96 @@
1
+ <img width="1200" height="330" alt="page-speed-forms-npm-module" src="https://github.com/user-attachments/assets/4dd21311-9de6-4c42-be75-bbc8fe5a0192" />
2
+
1
3
  # @page-speed/forms
2
4
 
3
- Ultra-high-performance React form library with field-level reactivity and tree-shakable architecture.
5
+ Type-safe form state management and validation for React applications.
4
6
 
5
- ## Features
7
+ ## Overview
6
8
 
7
- - =� **Field-Level Reactivity**: Only re-render the specific field that changed (~1 re-render per change vs ~10 for traditional hooks)
8
- - =� **Tree-Shakable**: Import only what you need - Core module starts at 13 KB gzipped
9
- - � **Built on @legendapp/state**: Observable-based state management for optimal performance
10
- -  **Valibot Integration**: Lightweight validation (95% smaller than Zod)
11
- - <� **TypeScript-First**: Full type safety with comprehensive type definitions
12
- -  **Accessible**: ARIA attributes and semantic HTML out of the box
13
- - = **Progressive Enhancement**: Forms work without JavaScript
14
- - <� **Unstyled**: Bring your own styles - no CSS to override
9
+ OpenSite Page Speed Forms is a high-performance library designed to streamline form state management, validation, and submission handling in React applications. This library is part of OpenSite AI's open-source ecosystem, built for performance and open collaboration. By emphasizing type safety and modularity, it aligns with OpenSite's goal to create scalable, open, and developer-friendly performance tooling.
15
10
 
16
- ## Installation
11
+ Learn more at [OpenSite.ai Developers](https://opensite.ai/developers).
17
12
 
18
- ```bash
19
- # Using pnpm (recommended)
20
- pnpm add @page-speed/forms
13
+ ## Key Features
21
14
 
22
- # Optional: Add validation library
23
- pnpm add valibot
15
+ - Type-safe form state management with TypeScript.
16
+ - Flexible validation schemas supporting both synchronous and asynchronous validation.
17
+ - Modular useForm and useField hooks for complete form and field control.
18
+ - Built-in support for form submission and error handling.
19
+ - Configurable validation modes: `onChange`, `onBlur`, and `onSubmit`.
24
20
 
25
- # Optional: Add state management (peer dependency)
26
- pnpm add @legendapp/state
27
- ```
21
+ ## Installation
28
22
 
29
- ## Bundle Sizes
23
+ To install OpenSite Page Speed Forms, ensure you have Node.js and npm installed, then run:
30
24
 
31
- All sizes shown are **minified + gzipped** with dependencies:
25
+ ```
26
+ npm install @page-speed/forms
27
+ ```
32
28
 
33
- - **Core** (useForm, Form, Field, useField): 13.11 KB
34
- - **TextInput**: 502 B
35
- - **Valibot Adapter**: 392 B
36
- - **Full Bundle**: 13.25 KB
29
+ Dependencies:
30
+ - React
37
31
 
38
32
  ## Quick Start
39
33
 
40
- ### Basic Form
34
+ Here is a basic example to get started with OpenSite Page Speed Forms in your React application:
41
35
 
42
- ```tsx
43
- import { useForm, Form, Field } from '@page-speed/forms';
44
- import { TextInput } from '@page-speed/forms/inputs';
36
+ ```typescript
37
+ import React from 'react';
38
+ import { useForm, Form } from '@page-speed/forms';
45
39
 
46
- function LoginForm() {
40
+ function MyForm() {
47
41
  const form = useForm({
48
- initialValues: {
49
- email: '',
50
- password: '',
51
- },
52
- validationSchema: {
53
- email: (value) => !value ? 'Required' : undefined,
54
- password: (value) => value.length < 8 ? 'Too short' : undefined,
55
- },
56
- onSubmit: async (values) => {
57
- await login(values);
58
- },
42
+ initialValues: { email: '' },
43
+ onSubmit: (values) => {
44
+ console.log('Form Submitted:', values);
45
+ }
59
46
  });
60
47
 
61
48
  return (
62
49
  <Form form={form}>
63
- <Field name="email" label="Email">
64
- {({ field, meta }) => (
65
- <>
66
- <TextInput {...field} type="email" error={!!meta.error} />
67
- {meta.error && <span>{meta.error}</span>}
68
- </>
69
- )}
70
- </Field>
71
-
72
- <Field name="password" label="Password">
73
- {({ field, meta }) => (
74
- <>
75
- <TextInput {...field} type="password" error={!!meta.error} />
76
- {meta.error && <span>{meta.error}</span>}
77
- </>
78
- )}
79
- </Field>
80
-
81
- <button type="submit" disabled={form.isSubmitting}>
82
- Submit
83
- </button>
50
+ <input
51
+ name="email"
52
+ value={form.values.email}
53
+ onChange={(e) => form.setFieldValue('email', e.target.value)}
54
+ onBlur={() => form.setFieldTouched('email', true)}
55
+ />
56
+ <button type="submit">Submit</button>
84
57
  </Form>
85
58
  );
86
59
  }
87
60
  ```
88
61
 
89
- ### With Valibot Validation
90
-
91
- ```tsx
92
- import { useForm } from '@page-speed/forms';
93
- import { createValibotSchema } from '@page-speed/forms/validation/valibot';
94
- import * as v from 'valibot';
95
-
96
- const LoginSchema = v.object({
97
- email: v.pipe(v.string(), v.email('Invalid email')),
98
- password: v.pipe(v.string(), v.minLength(8, 'Too short')),
99
- });
100
-
101
- function LoginForm() {
102
- const form = useForm({
103
- initialValues: { email: '', password: '' },
104
- validationSchema: createValibotSchema(LoginSchema),
105
- onSubmit: async (values) => {
106
- await login(values);
107
- },
108
- });
109
-
110
- // ... rest of form
111
- }
112
- ```
113
-
114
- ## API Reference
115
-
116
- ### `useForm(options)`
117
-
118
- Main hook for creating form state.
119
-
120
- **Options:**
62
+ ## Configuration or Advanced Usage
121
63
 
122
- - `initialValues` (required): Initial form values
123
- - `validationSchema`: Schema mapping field names to validators
124
- - `validateOn`: When to validate - `"onBlur"` (default) | `"onChange"` | `"onSubmit"`
125
- - `revalidateOn`: When to revalidate after first validation - `"onChange"` (default) | `"onBlur"`
126
- - `onSubmit` (required): Submit handler function
127
- - `onError`: Error handler for validation failures
128
- - `debug`: Enable debug logging
129
-
130
- **Returns:**
64
+ OpenSite Page Speed Forms can be customized with various options:
131
65
 
132
66
  ```typescript
133
- {
134
- // State
135
- values: T;
136
- errors: FormErrors<T>;
137
- touched: TouchedFields<T>;
138
- isSubmitting: boolean;
139
- isValid: boolean;
140
- isDirty: boolean;
141
- status: 'idle' | 'submitting' | 'success' | 'error';
142
-
143
- // Actions
144
- handleSubmit: (e?: React.FormEvent) => Promise<void>;
145
- setFieldValue: (field: keyof T, value: T[field]) => void;
146
- setFieldError: (field: keyof T, error: string) => void;
147
- setFieldTouched: (field: keyof T, touched: boolean) => void;
148
- validateForm: () => Promise<FormErrors<T>>;
149
- validateField: (field: keyof T) => Promise<string | undefined>;
150
- resetForm: () => void;
151
- getFieldProps: (field: keyof T) => FieldInputProps;
152
- getFieldMeta: (field: keyof T) => FieldMeta;
153
- }
154
- ```
155
-
156
- ### `<Form>`
157
-
158
- Progressive enhancement wrapper component.
159
-
160
- ```tsx
161
- <Form
162
- form={form}
163
- action="/api/endpoint" // Fallback for no-JS
164
- method="post" // Fallback for no-JS
165
- className="my-form"
166
- >
167
- {/* fields */}
168
- </Form>
169
- ```
170
-
171
- ### `<Field>`
172
-
173
- Field wrapper with label, description, and error display.
174
-
175
- ```tsx
176
- <Field
177
- name="email"
178
- label="Email Address"
179
- description="We'll never share your email"
180
- validate={(value) => !value ? 'Required' : undefined}
181
- >
182
- {({ field, meta, helpers }) => (
183
- <TextInput {...field} error={!!meta.error} />
184
- )}
185
- </Field>
186
- ```
187
-
188
- ### `useField(options)`
189
-
190
- Field-level hook for accessing field state.
191
-
192
- ```tsx
193
- const { field, meta, helpers } = useField({
194
- name: 'email',
195
- validate: (value) => !value ? 'Required' : undefined,
196
- transform: (value) => value.toLowerCase(),
197
- });
198
- ```
199
-
200
- ### `<TextInput>`
201
-
202
- Lightweight, accessible text input component.
203
-
204
- ```tsx
205
- <TextInput
206
- name="email"
207
- value={value}
208
- onChange={onChange}
209
- onBlur={onBlur}
210
- type="email"
211
- placeholder="you@example.com"
212
- error={hasError}
213
- disabled={false}
214
- required={true}
215
- />
216
- ```
217
-
218
- ## Validation
219
-
220
- ### Inline Validators
221
-
222
- ```tsx
223
67
  const form = useForm({
224
68
  initialValues: { email: '' },
225
69
  validationSchema: {
226
- email: (value) => {
227
- if (!value) return 'Required';
228
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
229
- return 'Invalid email';
230
- }
231
- return undefined;
232
- },
233
- },
234
- onSubmit: async (values) => { /* ... */ },
235
- });
236
- ```
237
-
238
- ### Async Validation
239
-
240
- ```tsx
241
- const form = useForm({
242
- initialValues: { username: '' },
243
- validationSchema: {
244
- username: async (value) => {
245
- const exists = await checkUsernameExists(value);
246
- return exists ? 'Username taken' : undefined;
247
- },
248
- },
249
- onSubmit: async (values) => { /* ... */ },
250
- });
251
- ```
252
-
253
- ### Multiple Validators per Field
254
-
255
- ```tsx
256
- const form = useForm({
257
- initialValues: { password: '' },
258
- validationSchema: {
259
- password: [
260
- (value) => !value ? 'Required' : undefined,
261
- (value) => value.length < 8 ? 'Too short' : undefined,
262
- (value) => !/[A-Z]/.test(value) ? 'Needs uppercase' : undefined,
263
- ],
264
- },
265
- onSubmit: async (values) => { /* ... */ },
266
- });
267
- ```
268
-
269
- ### Valibot Integration
270
-
271
- ```tsx
272
- import { createValibotSchema } from '@page-speed/forms/validation/valibot';
273
- import * as v from 'valibot';
274
-
275
- const schema = v.object({
276
- email: v.pipe(
277
- v.string(),
278
- v.email('Invalid email'),
279
- v.endsWith('@company.com', 'Must be company email')
280
- ),
281
- age: v.pipe(
282
- v.number(),
283
- v.minValue(18, 'Must be 18+')
284
- ),
285
- });
286
-
287
- const form = useForm({
288
- initialValues: { email: '', age: 0 },
289
- validationSchema: createValibotSchema(schema),
290
- onSubmit: async (values) => { /* ... */ },
291
- });
292
- ```
293
-
294
- ## Advanced Usage
295
-
296
- ### Conditional Validation
297
-
298
- ```tsx
299
- const form = useForm({
300
- initialValues: {
301
- contactMethod: 'email',
302
- email: '',
303
- phone: '',
304
- },
305
- validationSchema: {
306
- email: (value, allValues) => {
307
- if (allValues.contactMethod === 'email' && !value) {
308
- return 'Email required when email is selected';
309
- }
310
- return undefined;
311
- },
312
- phone: (value, allValues) => {
313
- if (allValues.contactMethod === 'phone' && !value) {
314
- return 'Phone required when phone is selected';
315
- }
316
- return undefined;
317
- },
318
- },
319
- onSubmit: async (values) => { /* ... */ },
320
- });
321
- ```
322
-
323
- ### Dynamic Forms
324
-
325
- ```tsx
326
- function DynamicForm() {
327
- const form = useForm({
328
- initialValues: {
329
- contacts: [{ name: '', email: '' }],
330
- },
331
- onSubmit: async (values) => { /* ... */ },
332
- });
333
-
334
- return (
335
- <Form form={form}>
336
- {form.values.contacts.map((contact, index) => (
337
- <div key={index}>
338
- <Field name={`contacts.${index}.name`}>
339
- {({ field }) => <TextInput {...field} />}
340
- </Field>
341
- <Field name={`contacts.${index}.email`}>
342
- {({ field }) => <TextInput {...field} type="email" />}
343
- </Field>
344
- </div>
345
- ))}
346
- <button
347
- type="button"
348
- onClick={() => {
349
- form.setFieldValue('contacts', [
350
- ...form.values.contacts,
351
- { name: '', email: '' },
352
- ]);
353
- }}
354
- >
355
- Add Contact
356
- </button>
357
- </Form>
358
- );
359
- }
360
- ```
361
-
362
- ### Form Helpers in onSubmit
363
-
364
- ```tsx
365
- const form = useForm({
366
- initialValues: { email: '' },
367
- onSubmit: async (values, helpers) => {
368
- try {
369
- await api.submit(values);
370
- helpers.resetForm();
371
- } catch (error) {
372
- if (error.code === 'EMAIL_EXISTS') {
373
- helpers.setFieldError('email', 'Email already exists');
374
- } else {
375
- helpers.setErrors({ email: 'Unexpected error' });
376
- }
377
- }
70
+ email: (value) => value.includes('@') ? undefined : 'Invalid email'
378
71
  },
72
+ validateOn: 'onBlur',
73
+ revalidateOn: 'onChange',
74
+ onSubmit: (values) => console.log(values),
75
+ onError: (errors) => console.error(errors),
76
+ debug: true
379
77
  });
380
78
  ```
381
79
 
382
- ## Tree-Shaking
383
-
384
- The library is designed for optimal tree-shaking. Import only what you need:
385
-
386
- ```tsx
387
- // Import core functionality (13.11 KB)
388
- import { useForm, Form, Field } from '@page-speed/forms/core';
389
-
390
- // Import input components separately (502 B)
391
- import { TextInput } from '@page-speed/forms/inputs';
392
-
393
- // Import validation adapters separately (392 B)
394
- import { createValibotSchema } from '@page-speed/forms/validation/valibot';
395
-
396
- // Or import from main entry point
397
- import { useForm } from '@page-speed/forms';
398
- ```
399
-
400
- ## Performance
401
-
402
- The library uses @legendapp/state for optimal performance:
403
-
404
- - **~1 re-render per change** vs ~10 for traditional hooks
405
- - **Observable-based state**: Fine-grained reactivity at the field level
406
- - **No unnecessary re-renders**: Parent form doesn't re-render when child field changes
407
- - **Efficient validation**: Debounced and memoized by default
408
-
409
- ## Progressive Enhancement
410
-
411
- Forms work without JavaScript by using native HTML form submission:
412
-
413
- ```tsx
414
- <Form
415
- form={form}
416
- action="/api/endpoint" // Used when JS disabled
417
- method="post"
418
- >
419
- {/* fields */}
420
- </Form>
421
- ```
422
-
423
- When JavaScript is available, `onSubmit` handles the submission. When JavaScript is disabled, the native HTML form submission takes over.
424
-
425
- ## Browser Support
426
-
427
- - Modern browsers (Chrome, Firefox, Safari, Edge)
428
- - Requires ES2020 support
429
- - No IE11 support
80
+ ## Performance Notes
430
81
 
431
- ## TypeScript
82
+ Performance is a core facet of everything we build at OpenSite AI. The library is optimized for minimal re-renders and efficient form state updates, ensuring your applications remain responsive and fast.
432
83
 
433
- Fully typed with comprehensive type definitions:
434
-
435
- ```tsx
436
- import type {
437
- FormValues,
438
- FormErrors,
439
- TouchedFields,
440
- ValidationSchema,
441
- FieldValidator,
442
- UseFormOptions,
443
- UseFormReturn,
444
- } from '@page-speed/forms/core';
445
- ```
446
-
447
- ## Examples
84
+ ## Contributing
448
85
 
449
- See the [`examples/`](./examples) directory for more complete examples including:
450
- - Basic forms with inline validation
451
- - Valibot schema integration
452
- - Dynamic forms with conditional fields
453
- - Progressive enhancement
454
- - Async validation
86
+ We welcome contributions from the community to enhance OpenSite Page Speed Forms. Please refer to our [GitHub repository](https://github.com/opensite-ai) for guidelines and more information on how to get involved.
455
87
 
456
88
  ## License
457
89
 
458
- MIT
459
-
460
- ## Contributing
461
-
462
- Contributions welcome! Please read our contributing guidelines first.
90
+ Licensed under the BSD 3-Clause License. See the [LICENSE](./LICENSE) file for details.
463
91
 
464
- ## Credits
92
+ ## Related Projects
465
93
 
466
- Built with:
467
- - [@legendapp/state](https://legendapp.com/open-source/state/) - Observable state management
468
- - [Valibot](https://valibot.dev/) - Lightweight validation library
469
- - [tsup](https://tsup.egoist.dev/) - TypeScript bundler
94
+ - [Domain Extractor](https://github.com/opensite-ai/domain_extractor)
95
+ - [Page Speed Hooks](https://github.com/opensite-ai/page-speed-hooks)
96
+ - Visit [opensite.ai](https://opensite.ai) for more tools and information.