@coherent.js/forms 1.0.0-beta.5 → 1.0.0-beta.6

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/package.json +3 -3
  2. package/types/index.d.ts +361 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/forms",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "SSR + Hydration form system for Coherent.js applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,8 +22,8 @@
22
22
  "author": "Coherent.js Team",
23
23
  "license": "MIT",
24
24
  "peerDependencies": {
25
- "@coherent.js/core": "1.0.0-beta.5",
26
- "@coherent.js/state": "1.0.0-beta.5"
25
+ "@coherent.js/core": "1.0.0-beta.6",
26
+ "@coherent.js/state": "1.0.0-beta.6"
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
package/types/index.d.ts CHANGED
@@ -3,132 +3,455 @@
3
3
  * @module @coherent.js/forms
4
4
  */
5
5
 
6
- // ===== Form Builder Types =====
6
+ import type { CoherentNode, CoherentElement, StrictCoherentElement } from '@coherent.js/core';
7
7
 
8
- export interface FormField {
9
- type: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'date' | 'time' | 'datetime-local' | 'checkbox' | 'radio' | 'select' | 'textarea';
8
+ // ============================================================================
9
+ // Form Field Types
10
+ // ============================================================================
11
+
12
+ /**
13
+ * Available form field input types
14
+ */
15
+ export type FormFieldType =
16
+ | 'text'
17
+ | 'email'
18
+ | 'password'
19
+ | 'number'
20
+ | 'tel'
21
+ | 'url'
22
+ | 'date'
23
+ | 'time'
24
+ | 'datetime-local'
25
+ | 'checkbox'
26
+ | 'radio'
27
+ | 'select'
28
+ | 'textarea'
29
+ | 'file'
30
+ | 'hidden'
31
+ | 'color'
32
+ | 'range'
33
+ | 'search'
34
+ | 'month'
35
+ | 'week';
36
+
37
+ /**
38
+ * Option for select and radio fields
39
+ */
40
+ export interface SelectOption {
41
+ value: string | number;
42
+ label: string;
43
+ disabled?: boolean;
44
+ }
45
+
46
+ /**
47
+ * Typed form field definition with generic value type
48
+ * @template T - The type of the field value
49
+ */
50
+ export interface FormField<T = unknown> {
51
+ /** Field input type */
52
+ type: FormFieldType;
53
+ /** Field name (used as form data key) */
10
54
  name: string;
55
+ /** Human-readable label */
11
56
  label?: string;
57
+ /** Placeholder text */
12
58
  placeholder?: string;
59
+ /** Whether field is required */
13
60
  required?: boolean;
14
- value?: any;
15
- options?: Array<{ value: string; label: string }>;
61
+ /** Whether field is disabled */
62
+ disabled?: boolean;
63
+ /** Whether field is readonly */
64
+ readonly?: boolean;
65
+ /** Default/initial value */
66
+ defaultValue?: T;
67
+ /** Current value */
68
+ value?: T;
69
+ /** Options for select/radio fields */
70
+ options?: SelectOption[];
71
+ /** Validation rules */
16
72
  validators?: Validator[];
17
- attributes?: Record<string, any>;
73
+ /** Field-specific validation configuration */
74
+ validation?: FieldValidation<T>;
75
+ /** Additional HTML attributes */
76
+ attributes?: Record<string, unknown>;
77
+ /** Transform function to convert raw input to typed value */
78
+ transform?: (value: unknown) => T;
18
79
  }
19
80
 
81
+ /**
82
+ * Field validation configuration with typed value
83
+ * @template T - The type of the field value
84
+ */
85
+ export interface FieldValidation<T = unknown> {
86
+ /** Required validation with optional custom message */
87
+ required?: boolean | string;
88
+ /** Minimum length for string values */
89
+ minLength?: number | { value: number; message: string };
90
+ /** Maximum length for string values */
91
+ maxLength?: number | { value: number; message: string };
92
+ /** Minimum value for number values */
93
+ min?: number | { value: number; message: string };
94
+ /** Maximum value for number values */
95
+ max?: number | { value: number; message: string };
96
+ /** Regular expression pattern validation */
97
+ pattern?: RegExp | { value: RegExp; message: string };
98
+ /** Custom validation function */
99
+ custom?: (value: T, formData: Record<string, unknown>) => boolean | string | Promise<boolean | string>;
100
+ /** Validate on change (real-time) */
101
+ validateOnChange?: boolean;
102
+ /** Validate on blur */
103
+ validateOnBlur?: boolean;
104
+ /** Debounce time in milliseconds for validation */
105
+ debounce?: number;
106
+ }
107
+
108
+ // ============================================================================
109
+ // Form Builder Types
110
+ // ============================================================================
111
+
112
+ /**
113
+ * Form configuration options
114
+ */
20
115
  export interface FormConfig {
21
- fields: FormField[];
116
+ /** Form fields */
117
+ fields?: FormField[];
118
+ /** Form action URL */
22
119
  action?: string;
120
+ /** Form submission method */
23
121
  method?: 'get' | 'post';
122
+ /** Form CSS class name */
24
123
  className?: string;
124
+ /** Submit button text */
25
125
  submitText?: string;
126
+ /** Form submit handler */
26
127
  onSubmit?: (data: FormData) => void | Promise<void>;
128
+ /** Form encoding type */
129
+ enctype?: 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain';
130
+ /** Whether to disable browser validation */
131
+ novalidate?: boolean;
132
+ /** Form ID */
133
+ id?: string;
27
134
  }
28
135
 
29
- export class FormBuilder {
30
- constructor(config: FormConfig);
31
- addField(field: FormField): this;
32
- removeField(name: string): this;
33
- build(): object;
34
- render(): object;
136
+ /**
137
+ * Typed form builder with generic form data shape
138
+ * @template T - The shape of the form data
139
+ */
140
+ export interface FormBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
141
+ /**
142
+ * Add a field to the form
143
+ * @template K - The key in the form data
144
+ */
145
+ addField<K extends keyof T>(name: K, field: Omit<FormField<T[K]>, 'name'>): FormBuilder<T>;
146
+
147
+ /**
148
+ * Remove a field from the form
149
+ */
150
+ removeField(name: keyof T): FormBuilder<T>;
151
+
152
+ /**
153
+ * Set the form action URL
154
+ */
155
+ setAction(action: string): FormBuilder<T>;
156
+
157
+ /**
158
+ * Set the form submission method
159
+ */
160
+ setMethod(method: 'get' | 'post'): FormBuilder<T>;
161
+
162
+ /**
163
+ * Set the form submit handler
164
+ */
165
+ onSubmit(handler: (data: T) => void | Promise<void>): FormBuilder<T>;
166
+
167
+ /**
168
+ * Build the form as a CoherentNode
169
+ */
170
+ build(): CoherentNode;
171
+
172
+ /**
173
+ * Render the form (alias for build)
174
+ */
175
+ render(): CoherentNode;
176
+
177
+ /**
178
+ * Validate form data
179
+ */
180
+ validate(data: unknown): { valid: boolean; errors: Record<keyof T, string[]> };
181
+
182
+ /**
183
+ * Get current field definitions
184
+ */
185
+ getFields(): FormField[];
35
186
  }
36
187
 
37
- export function createFormBuilder(config: FormConfig): FormBuilder;
38
- export function buildForm(config: FormConfig): object;
188
+ /**
189
+ * Create a typed form builder
190
+ * @template T - The shape of the form data
191
+ */
192
+ export function createFormBuilder<T extends Record<string, unknown> = Record<string, unknown>>(
193
+ config?: FormConfig
194
+ ): FormBuilder<T>;
195
+
196
+ /**
197
+ * Build a form from configuration
198
+ */
199
+ export function buildForm(config: FormConfig): CoherentNode;
200
+
201
+ /**
202
+ * Render a single form field
203
+ */
204
+ export function renderField<T = unknown>(field: FormField<T>): CoherentNode;
205
+
206
+ /**
207
+ * Validate a single field value
208
+ * @returns Error message or null if valid
209
+ */
210
+ export function validateField<T>(field: FormField<T>, value: unknown): string | null;
211
+
212
+ // ============================================================================
213
+ // Form Builder Class
214
+ // ============================================================================
215
+
216
+ /**
217
+ * Form builder class implementation
218
+ */
219
+ export class FormBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
220
+ constructor(config?: FormConfig);
221
+ addField<K extends keyof T>(name: K, field: Omit<FormField<T[K]>, 'name'>): this;
222
+ removeField(name: keyof T): this;
223
+ setAction(action: string): this;
224
+ setMethod(method: 'get' | 'post'): this;
225
+ onSubmit(handler: (data: T) => void | Promise<void>): this;
226
+ build(): CoherentNode;
227
+ render(): CoherentNode;
228
+ validate(data: unknown): { valid: boolean; errors: Record<keyof T, string[]> };
229
+ getFields(): FormField[];
230
+ }
39
231
 
40
- // ===== Form Hydration Types =====
232
+ // ============================================================================
233
+ // Form Hydration Types
234
+ // ============================================================================
41
235
 
236
+ /**
237
+ * Options for hydrating a form on the client
238
+ */
42
239
  export interface HydrationOptions {
240
+ /** Enable validation */
43
241
  validation?: boolean;
242
+ /** Enable real-time validation as user types */
44
243
  realTimeValidation?: boolean;
244
+ /** Form submit handler */
45
245
  onSubmit?: (event: Event, data: FormData) => void | Promise<void>;
246
+ /** Validation error handler */
46
247
  onValidate?: (errors: ValidationErrors) => void;
248
+ /** Prevent default form submission */
249
+ preventSubmit?: boolean;
47
250
  }
48
251
 
252
+ /**
253
+ * Hydrated form interface for client-side interaction
254
+ */
49
255
  export interface HydratedForm {
256
+ /** The form DOM element */
50
257
  element: HTMLFormElement;
258
+ /** Validate all form fields */
51
259
  validate(): ValidationResult;
260
+ /** Reset form to initial values */
52
261
  reset(): void;
262
+ /** Get current form data */
53
263
  getData(): FormData;
54
- setData(data: Record<string, any>): void;
264
+ /** Get form data as object */
265
+ getValues<T = Record<string, unknown>>(): T;
266
+ /** Set form field values */
267
+ setData(data: Record<string, unknown>): void;
268
+ /** Set a single field value */
269
+ setValue(name: string, value: unknown): void;
270
+ /** Destroy hydration and clean up event listeners */
55
271
  destroy(): void;
272
+ /** Check if form is valid */
273
+ isValid(): boolean;
274
+ /** Get validation errors */
275
+ getErrors(): ValidationErrors;
56
276
  }
57
277
 
58
- export function hydrateForm(formElement: HTMLFormElement | string, options?: HydrationOptions): HydratedForm;
278
+ /**
279
+ * Hydrate a form element for client-side interactivity
280
+ */
281
+ export function hydrateForm(
282
+ formElement: HTMLFormElement | string,
283
+ options?: HydrationOptions
284
+ ): HydratedForm;
59
285
 
60
- // ===== Validation Types =====
286
+ // ============================================================================
287
+ // Validation Types
288
+ // ============================================================================
61
289
 
290
+ /**
291
+ * Validation result
292
+ */
62
293
  export interface ValidationResult {
294
+ /** Whether all fields are valid */
63
295
  valid: boolean;
296
+ /** Validation errors by field name */
64
297
  errors: ValidationErrors;
65
298
  }
66
299
 
300
+ /**
301
+ * Validation errors mapped by field name
302
+ */
67
303
  export interface ValidationErrors {
68
304
  [fieldName: string]: string[];
69
305
  }
70
306
 
307
+ /**
308
+ * Validator function type
309
+ */
71
310
  export interface Validator {
72
- (value: any): boolean | string | Promise<boolean | string>;
311
+ (value: unknown): boolean | string | Promise<boolean | string>;
73
312
  }
74
313
 
314
+ /**
315
+ * Form validator class
316
+ */
75
317
  export class FormValidator {
76
318
  constructor(rules: Record<string, Validator[]>);
77
- validate(data: Record<string, any>): ValidationResult;
78
- validateAsync(data: Record<string, any>): Promise<ValidationResult>;
319
+ /** Synchronous validation */
320
+ validate(data: Record<string, unknown>): ValidationResult;
321
+ /** Asynchronous validation */
322
+ validateAsync(data: Record<string, unknown>): Promise<ValidationResult>;
323
+ /** Add a validation rule */
79
324
  addRule(field: string, validator: Validator): void;
325
+ /** Remove a validation rule */
80
326
  removeRule(field: string, validator: Validator): void;
327
+ /** Clear all rules for a field */
328
+ clearRules(field: string): void;
81
329
  }
82
330
 
331
+ /**
332
+ * Create a form validator
333
+ */
83
334
  export function createValidator(rules: Record<string, Validator[]>): FormValidator;
84
- export function validate(data: Record<string, any>, rules: Record<string, Validator[]>): ValidationResult;
85
335
 
86
- // ===== Built-in Validators =====
336
+ /**
337
+ * Validate data against rules
338
+ */
339
+ export function validate(
340
+ data: Record<string, unknown>,
341
+ rules: Record<string, Validator[]>
342
+ ): ValidationResult;
343
+
344
+ // ============================================================================
345
+ // Built-in Validators
346
+ // ============================================================================
87
347
 
348
+ /**
349
+ * Built-in validator functions
350
+ */
88
351
  export const validators: {
352
+ /** Require a value to be present */
89
353
  required(message?: string): Validator;
354
+ /** Validate email format */
90
355
  email(message?: string): Validator;
356
+ /** Minimum string length */
91
357
  minLength(length: number, message?: string): Validator;
358
+ /** Maximum string length */
92
359
  maxLength(length: number, message?: string): Validator;
360
+ /** Minimum numeric value */
93
361
  min(value: number, message?: string): Validator;
362
+ /** Maximum numeric value */
94
363
  max(value: number, message?: string): Validator;
364
+ /** Pattern matching */
95
365
  pattern(regex: RegExp, message?: string): Validator;
366
+ /** Match another field's value */
96
367
  matches(field: string, message?: string): Validator;
368
+ /** Validate URL format */
97
369
  url(message?: string): Validator;
370
+ /** Validate as number */
98
371
  number(message?: string): Validator;
372
+ /** Validate as integer */
99
373
  integer(message?: string): Validator;
374
+ /** Validate as positive number */
100
375
  positive(message?: string): Validator;
376
+ /** Validate as negative number */
101
377
  negative(message?: string): Validator;
378
+ /** Validate date format */
102
379
  date(message?: string): Validator;
103
- custom(fn: (value: any) => boolean | string, message?: string): Validator;
104
- async(fn: (value: any) => Promise<boolean | string>): Validator;
380
+ /** Custom validation function */
381
+ custom(fn: (value: unknown) => boolean | string, message?: string): Validator;
382
+ /** Async validation function */
383
+ async(fn: (value: unknown) => Promise<boolean | string>): Validator;
105
384
  };
106
385
 
386
+ /** Alias for validators */
107
387
  export const formValidators: typeof validators;
108
388
 
109
- // ===== Advanced Validation =====
389
+ // ============================================================================
390
+ // Advanced Validation
391
+ // ============================================================================
110
392
 
393
+ /**
394
+ * Validation rule configuration
395
+ */
111
396
  export interface ValidationRule {
397
+ /** The validator function */
112
398
  validator: Validator;
399
+ /** Custom error message */
113
400
  message?: string;
401
+ /** Whether this is an async validator */
114
402
  async?: boolean;
115
403
  }
116
404
 
117
- export interface FieldValidation {
118
- rules: ValidationRule[];
119
- validateOnChange?: boolean;
120
- validateOnBlur?: boolean;
121
- debounce?: number;
122
- }
405
+ /**
406
+ * Create an async validator
407
+ */
408
+ export function createAsyncValidator(
409
+ fn: (value: unknown) => Promise<boolean | string>
410
+ ): Validator;
123
411
 
124
- export function createAsyncValidator(fn: (value: any) => Promise<boolean | string>): Validator;
412
+ /**
413
+ * Combine multiple validators into one
414
+ */
125
415
  export function combineValidators(...validators: Validator[]): Validator;
126
- export function conditionalValidator(condition: (data: Record<string, any>) => boolean, validator: Validator): Validator;
127
416
 
128
- // ===== Deprecated SPA-only Functions (for backward compatibility) =====
417
+ /**
418
+ * Create a conditional validator
419
+ */
420
+ export function conditionalValidator(
421
+ condition: (data: Record<string, unknown>) => boolean,
422
+ validator: Validator
423
+ ): Validator;
424
+
425
+ // ============================================================================
426
+ // Form Utilities
427
+ // ============================================================================
428
+
429
+ /**
430
+ * Parse FormData to typed object
431
+ */
432
+ export function parseFormData<T extends Record<string, unknown>>(formData: FormData): T;
433
+
434
+ /**
435
+ * Serialize object to FormData
436
+ */
437
+ export function toFormData(data: Record<string, unknown>): FormData;
438
+
439
+ /**
440
+ * Create a form group (fieldset)
441
+ */
442
+ export function createFieldGroup(
443
+ legend: string,
444
+ fields: FormField[]
445
+ ): CoherentNode;
446
+
447
+ // ============================================================================
448
+ // Deprecated Functions (Backward Compatibility)
449
+ // ============================================================================
129
450
 
130
451
  /** @deprecated Use createFormBuilder() on server + hydrateForm() on client */
131
- export function createForm(config: FormConfig): object;
452
+ export function createForm(config: FormConfig): CoherentNode;
132
453
 
133
454
  /** @deprecated Use validators with hydrateForm() instead */
134
- export function enhancedForm(config: FormConfig & { validation?: Record<string, Validator[]> }): object;
455
+ export function enhancedForm(
456
+ config: FormConfig & { validation?: Record<string, Validator[]> }
457
+ ): CoherentNode;