@nuralyui/form 0.1.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/bundle.js +154 -0
- package/controllers/submission.controller.d.ts +60 -0
- package/controllers/submission.controller.d.ts.map +1 -0
- package/controllers/submission.controller.js +220 -0
- package/controllers/submission.controller.js.map +1 -0
- package/controllers/validation.controller.d.ts +87 -0
- package/controllers/validation.controller.d.ts.map +1 -0
- package/controllers/validation.controller.js +236 -0
- package/controllers/validation.controller.js.map +1 -0
- package/form.component.d.ts +279 -0
- package/form.component.js +644 -0
- package/form.style.d.ts +7 -0
- package/form.style.js +68 -0
- package/form.types.d.ts +92 -0
- package/form.types.js +38 -0
- package/index.d.ts +10 -0
- package/index.js +13 -0
- package/interfaces/validation.interface.d.ts +118 -0
- package/interfaces/validation.interface.d.ts.map +1 -0
- package/interfaces/validation.interface.js +7 -0
- package/interfaces/validation.interface.js.map +1 -0
- package/mixins/form-field-integration.mixin.d.ts +22 -0
- package/mixins/form-field-integration.mixin.d.ts.map +1 -0
- package/mixins/form-field-integration.mixin.js +78 -0
- package/mixins/form-field-integration.mixin.js.map +1 -0
- package/mixins/form-field-validation.mixin.d.ts +35 -0
- package/mixins/form-field-validation.mixin.d.ts.map +1 -0
- package/mixins/form-field-validation.mixin.js +300 -0
- package/mixins/form-field-validation.mixin.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
9
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
10
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
11
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
12
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
import { FormValidationState, FORM_EVENTS } from '../form.types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Controller that coordinates validation across all form fields
|
|
18
|
+
* This does NOT perform validation - it coordinates existing component validations
|
|
19
|
+
*/
|
|
20
|
+
export class FormValidationController {
|
|
21
|
+
constructor(host) {
|
|
22
|
+
this.host = host;
|
|
23
|
+
this.fields = new Map();
|
|
24
|
+
this.validationState = FormValidationState.Pristine;
|
|
25
|
+
this.validationPromise = null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Register a form field for validation coordination
|
|
29
|
+
*/
|
|
30
|
+
registerField(element) {
|
|
31
|
+
var _a;
|
|
32
|
+
const name = element.name || element.getAttribute('name') || `field-${Date.now()}`;
|
|
33
|
+
const field = {
|
|
34
|
+
element,
|
|
35
|
+
name,
|
|
36
|
+
value: element.value,
|
|
37
|
+
isValid: element.getValidationStatus().isValid,
|
|
38
|
+
validationMessage: element.validationMessage || '',
|
|
39
|
+
required: (_a = element.required) !== null && _a !== void 0 ? _a : false,
|
|
40
|
+
touched: false,
|
|
41
|
+
dirty: false
|
|
42
|
+
};
|
|
43
|
+
this.fields.set(name, field);
|
|
44
|
+
// Listen for field changes
|
|
45
|
+
this.addFieldListeners(element, name);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Unregister a form field
|
|
49
|
+
*/
|
|
50
|
+
unregisterField(name) {
|
|
51
|
+
const field = this.fields.get(name);
|
|
52
|
+
if (field) {
|
|
53
|
+
this.removeFieldListeners(field.element);
|
|
54
|
+
this.fields.delete(name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add event listeners to a form field
|
|
59
|
+
*/
|
|
60
|
+
addFieldListeners(element, fieldName) {
|
|
61
|
+
element.addEventListener('nr-validation', (event) => this.handleFieldValidation(fieldName, event));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Remove event listeners from a form field
|
|
65
|
+
*/
|
|
66
|
+
removeFieldListeners(_element) {
|
|
67
|
+
// Remove all event listeners (implementation depends on your needs)
|
|
68
|
+
// You might want to store listener references for proper cleanup
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Handle field validation event
|
|
72
|
+
*/
|
|
73
|
+
handleFieldValidation(fieldName, event) {
|
|
74
|
+
const field = this.fields.get(fieldName);
|
|
75
|
+
if (!field)
|
|
76
|
+
return;
|
|
77
|
+
if (event && event.detail) {
|
|
78
|
+
// Use the detailed validation information from the nr-validation event
|
|
79
|
+
field.isValid = event.detail.isValid || false;
|
|
80
|
+
field.validationMessage = event.detail.validationMessage || '';
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// Fallback to ValidatableComponent validation
|
|
84
|
+
const status = field.element.getValidationStatus();
|
|
85
|
+
field.isValid = status.isValid;
|
|
86
|
+
field.validationMessage = field.element.validationMessage || '';
|
|
87
|
+
}
|
|
88
|
+
// Update form validation state based on all fields
|
|
89
|
+
this.updateFormValidationState();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Update form validation state based on current field states
|
|
93
|
+
*/
|
|
94
|
+
updateFormValidationState() {
|
|
95
|
+
const invalidFields = this.getInvalidFields();
|
|
96
|
+
const isFormValid = invalidFields.length === 0;
|
|
97
|
+
this.validationState = isFormValid ? FormValidationState.Valid : FormValidationState.Invalid;
|
|
98
|
+
const result = {
|
|
99
|
+
isValid: isFormValid,
|
|
100
|
+
invalidFields,
|
|
101
|
+
validationErrors: this.buildValidationErrors(),
|
|
102
|
+
summary: isFormValid ? 'Form is valid' : `${invalidFields.length} field(s) have errors`
|
|
103
|
+
};
|
|
104
|
+
this.dispatchValidationEvent(result);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Build validation errors object from current field states
|
|
108
|
+
*/
|
|
109
|
+
buildValidationErrors() {
|
|
110
|
+
const errors = {};
|
|
111
|
+
for (const [name, field] of this.fields) {
|
|
112
|
+
if (!field.isValid && field.validationMessage) {
|
|
113
|
+
errors[name] = field.validationMessage;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return errors;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate all form fields (coordinates existing validations)
|
|
120
|
+
*/
|
|
121
|
+
validateForm() {
|
|
122
|
+
// Return existing promise if validation is in progress
|
|
123
|
+
if (this.validationPromise) {
|
|
124
|
+
return this.validationPromise;
|
|
125
|
+
}
|
|
126
|
+
this.validationState = FormValidationState.Pending;
|
|
127
|
+
this.validationPromise = this.performValidation();
|
|
128
|
+
return this.validationPromise;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Perform the actual validation coordination
|
|
132
|
+
*/
|
|
133
|
+
performValidation() {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
const invalidFields = [];
|
|
136
|
+
const validationErrors = {};
|
|
137
|
+
// Check each field's validation state
|
|
138
|
+
for (const [name, field] of this.fields) {
|
|
139
|
+
// Trigger validation on the field component itself
|
|
140
|
+
const status = field.element.getValidationStatus();
|
|
141
|
+
const isValid = status.isValid;
|
|
142
|
+
field.isValid = isValid;
|
|
143
|
+
field.validationMessage = field.element.validationMessage || '';
|
|
144
|
+
if (!isValid) {
|
|
145
|
+
invalidFields.push(field);
|
|
146
|
+
validationErrors[name] = field.validationMessage;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const isFormValid = invalidFields.length === 0;
|
|
150
|
+
this.validationState = isFormValid ? FormValidationState.Valid : FormValidationState.Invalid;
|
|
151
|
+
const result = {
|
|
152
|
+
isValid: isFormValid,
|
|
153
|
+
invalidFields,
|
|
154
|
+
validationErrors,
|
|
155
|
+
summary: isFormValid ? 'Form is valid' : `${invalidFields.length} field(s) have errors`
|
|
156
|
+
};
|
|
157
|
+
this.dispatchValidationEvent(result);
|
|
158
|
+
this.validationPromise = null;
|
|
159
|
+
return result;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get current validation state
|
|
164
|
+
*/
|
|
165
|
+
getValidationState() {
|
|
166
|
+
return this.validationState;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get all registered fields
|
|
170
|
+
*/
|
|
171
|
+
getFields() {
|
|
172
|
+
return Array.from(this.fields.values());
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get invalid fields
|
|
176
|
+
*/
|
|
177
|
+
getInvalidFields() {
|
|
178
|
+
return Array.from(this.fields.values()).filter(field => !field.isValid);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check if form is valid
|
|
182
|
+
*/
|
|
183
|
+
isValid() {
|
|
184
|
+
return this.validationState === FormValidationState.Valid &&
|
|
185
|
+
Array.from(this.fields.values()).every(field => field.isValid);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Focus first invalid field
|
|
189
|
+
*/
|
|
190
|
+
focusFirstInvalidField() {
|
|
191
|
+
const invalidField = Array.from(this.fields.values()).find(field => !field.isValid);
|
|
192
|
+
if (invalidField && typeof invalidField.element.focus === 'function') {
|
|
193
|
+
invalidField.element.focus();
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Reset all fields
|
|
200
|
+
*/
|
|
201
|
+
reset() {
|
|
202
|
+
for (const field of this.fields.values()) {
|
|
203
|
+
// Reset using ValidatableComponent interface if available
|
|
204
|
+
if ('clearValidation' in field.element && typeof field.element.clearValidation === 'function') {
|
|
205
|
+
field.element.clearValidation();
|
|
206
|
+
}
|
|
207
|
+
// Reset value if component supports it
|
|
208
|
+
if ('value' in field.element) {
|
|
209
|
+
field.element.value = '';
|
|
210
|
+
}
|
|
211
|
+
field.touched = false;
|
|
212
|
+
field.dirty = false;
|
|
213
|
+
field.isValid = true;
|
|
214
|
+
field.validationMessage = '';
|
|
215
|
+
}
|
|
216
|
+
this.validationState = FormValidationState.Pristine;
|
|
217
|
+
this.dispatchResetEvent();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Dispatch validation event
|
|
221
|
+
*/
|
|
222
|
+
dispatchValidationEvent(result) {
|
|
223
|
+
var _a, _b;
|
|
224
|
+
(_b = (_a = this.host).dispatchCustomEvent) === null || _b === void 0 ? void 0 : _b.call(_a, FORM_EVENTS.VALIDATION_CHANGED, {
|
|
225
|
+
validationResult: result
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Dispatch reset event
|
|
230
|
+
*/
|
|
231
|
+
dispatchResetEvent() {
|
|
232
|
+
var _a, _b;
|
|
233
|
+
(_b = (_a = this.host).dispatchCustomEvent) === null || _b === void 0 ? void 0 : _b.call(_a, FORM_EVENTS.RESET, {});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=validation.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.controller.js","sourceRoot":"","sources":["../../../../src/components/form/controllers/validation.controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Nuraly, Laabidi Aymen\n * SPDX-License-Identifier: MIT\n */\n\nimport {\n FormField,\n FormValidationResult,\n FormValidationState,\n FORM_EVENTS\n} from '../form.types.js';\nimport { ValidatableComponent } from '../../../shared/validation.types.js';\n\n/**\n * Controller that coordinates validation across all form fields\n * This does NOT perform validation - it coordinates existing component validations\n */\nexport class FormValidationController {\n private fields: Map<string, FormField> = new Map();\n private validationState: FormValidationState = FormValidationState.Pristine;\n private validationPromise: Promise<FormValidationResult> | null = null;\n\n constructor(private host: any) {}\n\n /**\n * Register a form field for validation coordination\n */\n registerField(element: HTMLElement & ValidatableComponent): void {\n const name = element.name || element.getAttribute('name') || `field-${Date.now()}`;\n \n const field: FormField = {\n element,\n name,\n value: element.value,\n isValid: element.getValidationStatus().isValid,\n validationMessage: element.validationMessage || '',\n required: element.required ?? false,\n touched: false,\n dirty: false\n };\n\n this.fields.set(name, field);\n\n // Listen for field changes\n this.addFieldListeners(element, name);\n }\n\n /**\n * Unregister a form field\n */\n unregisterField(name: string): void {\n const field = this.fields.get(name);\n if (field) {\n this.removeFieldListeners(field.element);\n this.fields.delete(name);\n }\n }\n\n /**\n * Add event listeners to a form field\n */\n private addFieldListeners(element: HTMLElement, fieldName: string): void {\n element.addEventListener('nr-validation', (event) => this.handleFieldValidation(fieldName, event as CustomEvent));\n }\n\n /**\n * Remove event listeners from a form field\n */\n private removeFieldListeners(_element: HTMLElement): void {\n // Remove all event listeners (implementation depends on your needs)\n // You might want to store listener references for proper cleanup\n }\n\n\n /**\n * Handle field validation event\n */\n private handleFieldValidation(fieldName: string, event?: CustomEvent): void {\n const field = this.fields.get(fieldName);\n if (!field) return;\n\n if (event && event.detail) {\n // Use the detailed validation information from the nr-validation event\n field.isValid = event.detail.isValid || false;\n field.validationMessage = event.detail.validationMessage || '';\n } else {\n // Fallback to ValidatableComponent validation\n const status = field.element.getValidationStatus();\n field.isValid = status.isValid;\n field.validationMessage = field.element.validationMessage || '';\n }\n\n // Update form validation state based on all fields\n this.updateFormValidationState();\n }\n\n /**\n * Update form validation state based on current field states\n */\n private updateFormValidationState(): void {\n const invalidFields = this.getInvalidFields();\n const isFormValid = invalidFields.length === 0;\n \n this.validationState = isFormValid ? FormValidationState.Valid : FormValidationState.Invalid;\n \n const result: FormValidationResult = {\n isValid: isFormValid,\n invalidFields,\n validationErrors: this.buildValidationErrors(),\n summary: isFormValid ? 'Form is valid' : `${invalidFields.length} field(s) have errors`\n };\n \n this.dispatchValidationEvent(result);\n }\n\n /**\n * Build validation errors object from current field states\n */\n private buildValidationErrors(): Record<string, string> {\n const errors: Record<string, string> = {};\n for (const [name, field] of this.fields) {\n if (!field.isValid && field.validationMessage) {\n errors[name] = field.validationMessage;\n }\n }\n return errors;\n }\n\n \n\n /**\n * Validate all form fields (coordinates existing validations)\n */\n validateForm(): Promise<FormValidationResult> {\n // Return existing promise if validation is in progress\n if (this.validationPromise) {\n return this.validationPromise;\n }\n\n this.validationState = FormValidationState.Pending;\n\n this.validationPromise = this.performValidation();\n \n return this.validationPromise;\n }\n\n /**\n * Perform the actual validation coordination\n */\n private async performValidation(): Promise<FormValidationResult> {\n const invalidFields: FormField[] = [];\n const validationErrors: Record<string, string> = {};\n\n // Check each field's validation state\n for (const [name, field] of this.fields) {\n // Trigger validation on the field component itself\n const status = field.element.getValidationStatus();\n const isValid = status.isValid;\n \n field.isValid = isValid;\n field.validationMessage = field.element.validationMessage || '';\n\n if (!isValid) {\n invalidFields.push(field);\n validationErrors[name] = field.validationMessage;\n }\n }\n\n const isFormValid = invalidFields.length === 0;\n this.validationState = isFormValid ? FormValidationState.Valid : FormValidationState.Invalid;\n\n const result: FormValidationResult = {\n isValid: isFormValid,\n invalidFields,\n validationErrors,\n summary: isFormValid ? 'Form is valid' : `${invalidFields.length} field(s) have errors`\n };\n\n this.dispatchValidationEvent(result);\n\n this.validationPromise = null;\n\n return result;\n }\n\n /**\n * Get current validation state\n */\n getValidationState(): FormValidationState {\n return this.validationState;\n }\n\n /**\n * Get all registered fields\n */\n getFields(): FormField[] {\n return Array.from(this.fields.values());\n }\n\n /**\n * Get invalid fields\n */\n getInvalidFields(): FormField[] {\n return Array.from(this.fields.values()).filter(field => !field.isValid);\n }\n\n /**\n * Check if form is valid\n */\n isValid(): boolean {\n return this.validationState === FormValidationState.Valid && \n Array.from(this.fields.values()).every(field => field.isValid);\n }\n\n /**\n * Focus first invalid field\n */\n focusFirstInvalidField(): boolean {\n const invalidField = Array.from(this.fields.values()).find(field => !field.isValid);\n if (invalidField && typeof invalidField.element.focus === 'function') {\n invalidField.element.focus();\n return true;\n }\n return false;\n }\n\n /**\n * Reset all fields\n */\n reset(): void {\n for (const field of this.fields.values()) {\n // Reset using ValidatableComponent interface if available\n if ('clearValidation' in field.element && typeof field.element.clearValidation === 'function') {\n field.element.clearValidation();\n }\n // Reset value if component supports it\n if ('value' in field.element) {\n field.element.value = '';\n }\n field.touched = false;\n field.dirty = false;\n field.isValid = true;\n field.validationMessage = '';\n }\n \n this.validationState = FormValidationState.Pristine;\n this.dispatchResetEvent();\n }\n\n \n\n /**\n * Dispatch validation event\n */\n private dispatchValidationEvent(result: FormValidationResult): void {\n (this.host as any).dispatchCustomEvent?.(FORM_EVENTS.VALIDATION_CHANGED, {\n validationResult: result\n });\n }\n\n /**\n * Dispatch reset event\n */\n private dispatchResetEvent(): void {\n (this.host as any).dispatchCustomEvent?.(FORM_EVENTS.RESET, {});\n }\n}\n"]}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { LitElement, PropertyValues } from 'lit';
|
|
7
|
+
import { FormConfig, FormValidationState, FormSubmissionState } from './form.types.js';
|
|
8
|
+
declare const NrFormElement_base: (new (...args: any[]) => import("../../shared/dependency-mixin.js").DependencyAware) & (new (...args: any[]) => import("../../shared/theme-mixin.js").ThemeAware) & (new (...args: any[]) => import("../../shared/event-handler-mixin.js").EventHandlerCapable) & typeof LitElement;
|
|
9
|
+
/**
|
|
10
|
+
* Comprehensive form component with field management and validation API
|
|
11
|
+
*
|
|
12
|
+
* Key Features:
|
|
13
|
+
* - Coordinates validation across all form fields (does NOT validate itself)
|
|
14
|
+
* - Handles form submission with built-in validation checks
|
|
15
|
+
* - Provides form state management and events
|
|
16
|
+
* - Integrates with existing component validation controllers
|
|
17
|
+
* - Supports both programmatic and user-driven interactions
|
|
18
|
+
* - Comprehensive API for field manipulation and validation
|
|
19
|
+
*
|
|
20
|
+
* @example Basic Usage
|
|
21
|
+
* ```html
|
|
22
|
+
* <nr-form @nr-form-submit-success="${handleSuccess}" validate-on-change>
|
|
23
|
+
* <nr-input name="username" required></nr-input>
|
|
24
|
+
* <nr-input name="email" type="email" required></nr-input>
|
|
25
|
+
* <nr-button type="submit">Submit</nr-button>
|
|
26
|
+
* </nr-form>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example Programmatic Usage
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const form = document.querySelector('nr-form');
|
|
32
|
+
*
|
|
33
|
+
* // Set field values
|
|
34
|
+
* form.setFieldsValue({ username: 'john', email: 'john@example.com' });
|
|
35
|
+
*
|
|
36
|
+
* // Get field values
|
|
37
|
+
* const values = form.getFieldsValue();
|
|
38
|
+
*
|
|
39
|
+
* // Validate and submit
|
|
40
|
+
* try {
|
|
41
|
+
* const values = await form.finish();
|
|
42
|
+
* console.log('Form submitted:', values);
|
|
43
|
+
* } catch (errors) {
|
|
44
|
+
* console.log('Validation failed:', errors);
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* // Reset specific fields
|
|
48
|
+
* form.resetFields(['username']);
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @fires nr-form-validation-changed - Validation state changes
|
|
52
|
+
* @fires nr-form-field-changed - Individual field changes
|
|
53
|
+
* @fires nr-form-submit-attempt - Form submission attempted
|
|
54
|
+
* @fires nr-form-submit-success - Form submitted successfully
|
|
55
|
+
* @fires nr-form-submit-error - Form submission failed
|
|
56
|
+
* @fires nr-form-reset - Form was reset
|
|
57
|
+
*
|
|
58
|
+
* @slot default - Form content (inputs, buttons, etc.)
|
|
59
|
+
*/
|
|
60
|
+
export declare class NrFormElement extends NrFormElement_base {
|
|
61
|
+
static styles: import("lit").CSSResult;
|
|
62
|
+
/** Form configuration */
|
|
63
|
+
config: FormConfig;
|
|
64
|
+
/** Enable real-time validation on field changes */
|
|
65
|
+
validateOnChange: boolean;
|
|
66
|
+
/** Enable validation on field blur */
|
|
67
|
+
validateOnBlur: boolean;
|
|
68
|
+
/** Prevent form submission if validation fails */
|
|
69
|
+
preventInvalidSubmission: boolean;
|
|
70
|
+
/** Reset form after successful submission */
|
|
71
|
+
resetOnSuccess: boolean;
|
|
72
|
+
/** Form action URL for native submission */
|
|
73
|
+
action?: string;
|
|
74
|
+
/** Form method for native submission */
|
|
75
|
+
method: 'GET' | 'POST';
|
|
76
|
+
/** Form encoding type */
|
|
77
|
+
enctype: string;
|
|
78
|
+
/** Target for form submission */
|
|
79
|
+
target?: string;
|
|
80
|
+
/** Disable the entire form */
|
|
81
|
+
disabled: boolean;
|
|
82
|
+
/** Form validation state */
|
|
83
|
+
private _validationState;
|
|
84
|
+
/** Form submission state */
|
|
85
|
+
private _submissionState;
|
|
86
|
+
/** Validation controller */
|
|
87
|
+
private validationController;
|
|
88
|
+
/** Submission controller */
|
|
89
|
+
private submissionController;
|
|
90
|
+
/** Get current validation state */
|
|
91
|
+
get validationState(): FormValidationState;
|
|
92
|
+
/** Get current submission state */
|
|
93
|
+
get submissionState(): FormSubmissionState;
|
|
94
|
+
connectedCallback(): void;
|
|
95
|
+
disconnectedCallback(): void;
|
|
96
|
+
willUpdate(changedProperties: PropertyValues): void;
|
|
97
|
+
firstUpdated(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Setup mutation observer to detect new form fields
|
|
100
|
+
*/
|
|
101
|
+
private setupFormObserver;
|
|
102
|
+
/**
|
|
103
|
+
* Cleanup mutation observer
|
|
104
|
+
*/
|
|
105
|
+
private cleanupFormObserver;
|
|
106
|
+
/**
|
|
107
|
+
* Register existing form fields
|
|
108
|
+
*/
|
|
109
|
+
private registerExistingFields;
|
|
110
|
+
/**
|
|
111
|
+
* Register form fields in an element
|
|
112
|
+
*/
|
|
113
|
+
private registerFieldsInElement;
|
|
114
|
+
/**
|
|
115
|
+
* Setup form events
|
|
116
|
+
*/
|
|
117
|
+
private setupFormEvents;
|
|
118
|
+
/**
|
|
119
|
+
* Handle form submission
|
|
120
|
+
*/
|
|
121
|
+
private handleFormSubmit;
|
|
122
|
+
/**
|
|
123
|
+
* Handle form reset
|
|
124
|
+
*/
|
|
125
|
+
private handleFormReset;
|
|
126
|
+
/**
|
|
127
|
+
* Handle validation state changes
|
|
128
|
+
*/
|
|
129
|
+
private handleValidationChanged;
|
|
130
|
+
/**
|
|
131
|
+
* Perform native form submission
|
|
132
|
+
*/
|
|
133
|
+
private performNativeSubmission;
|
|
134
|
+
/**
|
|
135
|
+
* Validate the form
|
|
136
|
+
*/
|
|
137
|
+
validate(): Promise<boolean>;
|
|
138
|
+
/**
|
|
139
|
+
* Submit the form programmatically
|
|
140
|
+
*/
|
|
141
|
+
submit(customData?: Record<string, any>): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Reset the form
|
|
144
|
+
*/
|
|
145
|
+
reset(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Check if form is valid
|
|
148
|
+
*/
|
|
149
|
+
get isValid(): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Check if form is submitting
|
|
152
|
+
*/
|
|
153
|
+
get isSubmitting(): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Get form data
|
|
156
|
+
*/
|
|
157
|
+
getFormData(): import("./form.types.js").FormSubmissionData;
|
|
158
|
+
/**
|
|
159
|
+
* Get invalid fields
|
|
160
|
+
*/
|
|
161
|
+
getInvalidFields(): import("./form.types.js").FormField[];
|
|
162
|
+
/**
|
|
163
|
+
* Get values of all fields
|
|
164
|
+
* @returns Object containing all field values
|
|
165
|
+
*/
|
|
166
|
+
getFieldsValue(nameList?: string[]): Record<string, any>;
|
|
167
|
+
/**
|
|
168
|
+
* Get value of specific field
|
|
169
|
+
* @param name Field name
|
|
170
|
+
* @returns Field value
|
|
171
|
+
*/
|
|
172
|
+
getFieldValue(name: string): any;
|
|
173
|
+
/**
|
|
174
|
+
* Set values of fields
|
|
175
|
+
* @param values Object containing field values to set
|
|
176
|
+
*/
|
|
177
|
+
setFieldsValue(values: Record<string, any>): void;
|
|
178
|
+
/**
|
|
179
|
+
* Set value of specific field
|
|
180
|
+
* @param name Field name
|
|
181
|
+
* @param value Field value
|
|
182
|
+
*/
|
|
183
|
+
setFieldValue(name: string, value: any): void;
|
|
184
|
+
/**
|
|
185
|
+
* Validate specific fields
|
|
186
|
+
* @param nameList Array of field names to validate, if empty validates all
|
|
187
|
+
* @returns Promise with validation result
|
|
188
|
+
*/
|
|
189
|
+
validateFields(nameList?: string[]): Promise<Record<string, any>>;
|
|
190
|
+
/**
|
|
191
|
+
* Reset specific fields
|
|
192
|
+
* @param nameList Array of field names to reset, if empty resets all
|
|
193
|
+
*/
|
|
194
|
+
resetFields(nameList?: string[]): void;
|
|
195
|
+
/**
|
|
196
|
+
* Get field error
|
|
197
|
+
* @param name Field name
|
|
198
|
+
* @returns Field error message or null
|
|
199
|
+
*/
|
|
200
|
+
getFieldError(name: string): string | null;
|
|
201
|
+
/**
|
|
202
|
+
* Get all field errors
|
|
203
|
+
* @param nameList Array of field names, if empty returns all
|
|
204
|
+
* @returns Object containing field errors
|
|
205
|
+
*/
|
|
206
|
+
getFieldsError(nameList?: string[]): Record<string, string | null>;
|
|
207
|
+
/**
|
|
208
|
+
* Check if field has been touched
|
|
209
|
+
* @param name Field name
|
|
210
|
+
* @returns Whether field has been touched
|
|
211
|
+
*/
|
|
212
|
+
isFieldTouched(name: string): boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Check if any fields have been touched
|
|
215
|
+
* @param nameList Array of field names, if empty checks all
|
|
216
|
+
* @returns Whether any of the specified fields have been touched
|
|
217
|
+
*/
|
|
218
|
+
isFieldsTouched(nameList?: string[]): boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Check if field value has been modified
|
|
221
|
+
* @param name Field name
|
|
222
|
+
* @returns Whether field has been modified
|
|
223
|
+
*/
|
|
224
|
+
isFieldDirty(name: string): boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Check if any fields have been modified
|
|
227
|
+
* @param nameList Array of field names, if empty checks all
|
|
228
|
+
* @returns Whether any of the specified fields have been modified
|
|
229
|
+
*/
|
|
230
|
+
isFieldsDirty(nameList?: string[]): boolean;
|
|
231
|
+
/**
|
|
232
|
+
* Get field instance
|
|
233
|
+
* @param name Field name
|
|
234
|
+
* @returns Field element or null
|
|
235
|
+
*/
|
|
236
|
+
getFieldInstance(name: string): HTMLElement | null;
|
|
237
|
+
/**
|
|
238
|
+
* Scroll to first error field
|
|
239
|
+
* @returns Whether scrolled to a field
|
|
240
|
+
*/
|
|
241
|
+
scrollToField(name?: string): boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Submit form and validate
|
|
244
|
+
* @returns Promise with form values
|
|
245
|
+
*/
|
|
246
|
+
finish(): Promise<Record<string, any>>;
|
|
247
|
+
/**
|
|
248
|
+
* Get field names that have validation errors
|
|
249
|
+
* @returns Array of field names with errors
|
|
250
|
+
*/
|
|
251
|
+
getFieldsWithErrors(): string[];
|
|
252
|
+
/**
|
|
253
|
+
* Check if form has any validation errors
|
|
254
|
+
* @returns Whether form has errors
|
|
255
|
+
*/
|
|
256
|
+
hasErrors(): boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Get summary of form state
|
|
259
|
+
* @returns Object with form state information
|
|
260
|
+
*/
|
|
261
|
+
getFormState(): {
|
|
262
|
+
isValid: boolean;
|
|
263
|
+
isSubmitting: boolean;
|
|
264
|
+
hasErrors: boolean;
|
|
265
|
+
errorCount: number;
|
|
266
|
+
fieldCount: number;
|
|
267
|
+
touchedFields: string[];
|
|
268
|
+
dirtyFields: string[];
|
|
269
|
+
invalidFields: string[];
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Set form loading state (useful for async operations)
|
|
273
|
+
* @param loading Whether form is in loading state
|
|
274
|
+
*/
|
|
275
|
+
setLoading(loading: boolean): void;
|
|
276
|
+
render(): import("lit").TemplateResult<1>;
|
|
277
|
+
}
|
|
278
|
+
export {};
|
|
279
|
+
//# sourceMappingURL=form.component.d.ts.map
|