@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.
@@ -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