@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,300 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Nuraly, Laabidi Aymen
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
7
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
8
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
9
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ };
12
+ import { property, state } from 'lit/decorators.js';
13
+ /**
14
+ * Mixin that provides standardized form field validation capabilities
15
+ * This should be used by input components that need to participate in forms
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * export class InputComponent extends FormFieldValidationMixin(NuralyUIBaseMixin(LitElement)) {
20
+ * // Component-specific validation logic
21
+ * protected validateValue(value: string): boolean {
22
+ * return value.length >= this.minLength;
23
+ * }
24
+ * }
25
+ * ```
26
+ */
27
+ export const FormFieldValidationMixin = (superClass) => {
28
+ class FormFieldValidationMixinClass extends superClass {
29
+ constructor() {
30
+ super(...arguments);
31
+ /** Current validation message */
32
+ this.validationMessage = '';
33
+ /** Validation state */
34
+ this._isValid = true;
35
+ /** Whether field has been touched by user */
36
+ this._isTouched = false;
37
+ /** Whether field value has changed from initial */
38
+ this._isDirty = false;
39
+ /**
40
+ * Handle focus event
41
+ */
42
+ this.handleFocus = () => {
43
+ this._isTouched = true;
44
+ };
45
+ /**
46
+ * Handle blur event
47
+ */
48
+ this.handleBlur = () => {
49
+ this.validateOnBlur();
50
+ };
51
+ /**
52
+ * Handle input event
53
+ */
54
+ this.handleInput = () => {
55
+ this.updateDirtyState();
56
+ this.validateOnInput();
57
+ };
58
+ /**
59
+ * Handle change event
60
+ */
61
+ this.handleChange = () => {
62
+ this.updateDirtyState();
63
+ this.validateOnChange();
64
+ };
65
+ }
66
+ connectedCallback() {
67
+ super.connectedCallback();
68
+ this._initialValue = this.value;
69
+ this.addValidationListeners();
70
+ }
71
+ disconnectedCallback() {
72
+ super.disconnectedCallback();
73
+ this.removeValidationListeners();
74
+ }
75
+ /**
76
+ * Add validation event listeners
77
+ */
78
+ addValidationListeners() {
79
+ this.addEventListener('focus', this.handleFocus);
80
+ this.addEventListener('blur', this.handleBlur);
81
+ this.addEventListener('input', this.handleInput);
82
+ this.addEventListener('change', this.handleChange);
83
+ }
84
+ /**
85
+ * Remove validation event listeners
86
+ */
87
+ removeValidationListeners() {
88
+ this.removeEventListener('focus', this.handleFocus);
89
+ this.removeEventListener('blur', this.handleBlur);
90
+ this.removeEventListener('input', this.handleInput);
91
+ this.removeEventListener('change', this.handleChange);
92
+ }
93
+ /**
94
+ * Update dirty state
95
+ */
96
+ updateDirtyState() {
97
+ this._isDirty = this.value !== this._initialValue;
98
+ }
99
+ /**
100
+ * Validate on blur (can be overridden)
101
+ */
102
+ validateOnBlur() {
103
+ // Only validate if the validation trigger is set to blur
104
+ const trigger = this.validationTrigger;
105
+ if (trigger === 'blur') {
106
+ this.validate();
107
+ }
108
+ }
109
+ /**
110
+ * Validate on input (can be overridden)
111
+ */
112
+ validateOnInput() {
113
+ // Override in component if needed
114
+ }
115
+ /**
116
+ * Validate on change (can be overridden)
117
+ */
118
+ validateOnChange() {
119
+ // Only validate if the validation trigger is set to change
120
+ const trigger = this.validationTrigger;
121
+ if (trigger === 'change') {
122
+ this.validate();
123
+ }
124
+ }
125
+ /**
126
+ * Main validation method - validates the field
127
+ */
128
+ validate() {
129
+ const isValid = this.performValidation();
130
+ this.setValidationState(isValid);
131
+ return isValid;
132
+ }
133
+ /**
134
+ * Perform the actual validation logic
135
+ * Override this method in components for specific validation
136
+ */
137
+ performValidation() {
138
+ // Required field validation
139
+ if (this.required && this.isEmpty()) {
140
+ this.validationMessage = 'This field is required';
141
+ return false;
142
+ }
143
+ // Component-specific validation
144
+ if (!this.isEmpty() && !this.validateValue(this.value)) {
145
+ return false;
146
+ }
147
+ this.validationMessage = '';
148
+ return true;
149
+ }
150
+ /**
151
+ * Component-specific value validation
152
+ * Override this method in components
153
+ */
154
+ validateValue(_value) {
155
+ return true; // Default: always valid
156
+ }
157
+ /**
158
+ * Check if value is empty
159
+ */
160
+ isEmpty() {
161
+ const value = this.value;
162
+ return value === null ||
163
+ value === undefined ||
164
+ value === '' ||
165
+ (Array.isArray(value) && value.length === 0);
166
+ }
167
+ /**
168
+ * Set validation state and dispatch events
169
+ */
170
+ setValidationState(isValid) {
171
+ const wasValid = this._isValid;
172
+ this._isValid = isValid;
173
+ // Dispatch validation event if state changed
174
+ if (wasValid !== isValid) {
175
+ this.dispatchValidationEvent(isValid);
176
+ }
177
+ // Update UI
178
+ this.requestUpdate();
179
+ }
180
+ /**
181
+ * HTML5 constraint validation API
182
+ */
183
+ checkValidity() {
184
+ return this.validate();
185
+ }
186
+ /**
187
+ * HTML5 constraint validation API with UI feedback
188
+ */
189
+ reportValidity() {
190
+ const isValid = this.validate();
191
+ if (!isValid) {
192
+ // Show validation UI (can be overridden)
193
+ this.showValidationUI();
194
+ // Dispatch invalid event
195
+ this.dispatchEvent(new CustomEvent('invalid', {
196
+ detail: { message: this.validationMessage },
197
+ bubbles: true,
198
+ composed: true
199
+ }));
200
+ }
201
+ return isValid;
202
+ }
203
+ /**
204
+ * Set custom validation message
205
+ */
206
+ setCustomValidity(message) {
207
+ this.validationMessage = message;
208
+ this._isValid = !message;
209
+ this.requestUpdate();
210
+ }
211
+ /**
212
+ * Reset field to initial state
213
+ */
214
+ reset() {
215
+ this.value = this._initialValue;
216
+ this._isValid = true;
217
+ this._isTouched = false;
218
+ this._isDirty = false;
219
+ this.validationMessage = '';
220
+ this.requestUpdate();
221
+ }
222
+ /**
223
+ * Show validation UI (override in components)
224
+ */
225
+ showValidationUI() {
226
+ // Default implementation - can be overridden
227
+ console.warn('Validation failed:', this.validationMessage);
228
+ }
229
+ /**
230
+ * Dispatch validation event
231
+ */
232
+ dispatchValidationEvent(isValid) {
233
+ const detail = {
234
+ isValid,
235
+ value: this.value,
236
+ message: this.validationMessage || '',
237
+ errors: isValid ? [] : [this.validationMessage || ''],
238
+ field: this.name,
239
+ timestamp: Date.now()
240
+ };
241
+ this.dispatchEvent(new CustomEvent('nr-validation', {
242
+ detail,
243
+ bubbles: true,
244
+ composed: true
245
+ }));
246
+ }
247
+ /**
248
+ * Get validation state
249
+ */
250
+ get isValid() {
251
+ return this._isValid;
252
+ }
253
+ /**
254
+ * Get touched state
255
+ */
256
+ get isTouched() {
257
+ return this._isTouched;
258
+ }
259
+ /**
260
+ * Get dirty state
261
+ */
262
+ get isDirty() {
263
+ return this._isDirty;
264
+ }
265
+ /**
266
+ * Get validation classes for styling
267
+ */
268
+ getValidationClasses() {
269
+ return {
270
+ 'valid': this._isValid,
271
+ 'invalid': !this._isValid,
272
+ 'touched': this._isTouched,
273
+ 'untouched': !this._isTouched,
274
+ 'dirty': this._isDirty,
275
+ 'pristine': !this._isDirty,
276
+ 'required': this.required || false
277
+ };
278
+ }
279
+ }
280
+ __decorate([
281
+ property({ type: String })
282
+ ], FormFieldValidationMixinClass.prototype, "name", void 0);
283
+ __decorate([
284
+ property({ type: Boolean })
285
+ ], FormFieldValidationMixinClass.prototype, "required", void 0);
286
+ __decorate([
287
+ state()
288
+ ], FormFieldValidationMixinClass.prototype, "validationMessage", void 0);
289
+ __decorate([
290
+ state()
291
+ ], FormFieldValidationMixinClass.prototype, "_isValid", void 0);
292
+ __decorate([
293
+ state()
294
+ ], FormFieldValidationMixinClass.prototype, "_isTouched", void 0);
295
+ __decorate([
296
+ state()
297
+ ], FormFieldValidationMixinClass.prototype, "_isDirty", void 0);
298
+ return FormFieldValidationMixinClass;
299
+ };
300
+ //# sourceMappingURL=form-field-validation.mixin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-field-validation.mixin.js","sourceRoot":"","sources":["../../../../src/components/form/mixins/form-field-validation.mixin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;;;;;;AAcH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAKpD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAoC,UAAa,EAAE,EAAE;IAC3F,MAAM,6BAA8B,SAAQ,UAAU;QAAtD;;YAUE,iCAAiC;YAEjC,sBAAiB,GAAY,EAAE,CAAC;YAEhC,uBAAuB;YAEf,aAAQ,GAAY,IAAI,CAAC;YAEjC,6CAA6C;YAErC,eAAU,GAAY,KAAK,CAAC;YAEpC,mDAAmD;YAE3C,aAAQ,GAAY,KAAK,CAAC;YAuClC;;eAEG;YACK,gBAAW,GAAG,GAAS,EAAE;gBAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACzB,CAAC,CAAC;YAEF;;eAEG;YACK,eAAU,GAAG,GAAS,EAAE;gBAC9B,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,CAAC,CAAC;YAEF;;eAEG;YACK,gBAAW,GAAG,GAAS,EAAE;gBAC/B,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACxB,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,CAAC,CAAC;YAEF;;eAEG;YACK,iBAAY,GAAG,GAAS,EAAE;gBAChC,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACxB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,CAAC,CAAC;QAsNJ,CAAC;QAjRU,iBAAiB;YACxB,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC1B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAChC,CAAC;QAEQ,oBAAoB;YAC3B,KAAK,CAAC,oBAAoB,EAAE,CAAC;YAC7B,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACnC,CAAC;QAED;;WAEG;QACK,sBAAsB;YAC5B,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACrD,CAAC;QAED;;WAEG;QACK,yBAAyB;YAC/B,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACpD,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAClD,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACpD,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACxD,CAAC;QAgCD;;WAEG;QACK,gBAAgB;YACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,aAAa,CAAC;QACpD,CAAC;QAED;;WAEG;QACO,cAAc;YACtB,yDAAyD;YACzD,MAAM,OAAO,GAAI,IAAY,CAAC,iBAAiB,CAAC;YAChD,IAAI,OAAO,KAAK,MAAM,EAAE;gBACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;aACjB;QACH,CAAC;QAED;;WAEG;QACO,eAAe;YACvB,kCAAkC;QACpC,CAAC;QAED;;WAEG;QACO,gBAAgB;YACxB,2DAA2D;YAC3D,MAAM,OAAO,GAAI,IAAY,CAAC,iBAAiB,CAAC;YAChD,IAAI,OAAO,KAAK,QAAQ,EAAE;gBACxB,IAAI,CAAC,QAAQ,EAAE,CAAC;aACjB;QACH,CAAC;QAED;;WAEG;QACH,QAAQ;YACN,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACjC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED;;;WAGG;QACO,iBAAiB;YACzB,4BAA4B;YAC5B,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;gBACnC,IAAI,CAAC,iBAAiB,GAAG,wBAAwB,CAAC;gBAClD,OAAO,KAAK,CAAC;aACd;YAED,gCAAgC;YAChC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;gBACtD,OAAO,KAAK,CAAC;aACd;YAED,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAED;;;WAGG;QACO,aAAa,CAAC,MAAW;YACjC,OAAO,IAAI,CAAC,CAAC,wBAAwB;QACvC,CAAC;QAED;;WAEG;QACO,OAAO;YACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACzB,OAAO,KAAK,KAAK,IAAI;gBACd,KAAK,KAAK,SAAS;gBACnB,KAAK,KAAK,EAAE;gBACZ,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;QACtD,CAAC;QAED;;WAEG;QACK,kBAAkB,CAAC,OAAgB;YACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;YAExB,6CAA6C;YAC7C,IAAI,QAAQ,KAAK,OAAO,EAAE;gBACxB,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;aACvC;YAED,YAAY;YACZ,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;QAED;;WAEG;QACH,aAAa;YACX,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;QAED;;WAEG;QACH,cAAc;YACZ,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAEhC,IAAI,CAAC,OAAO,EAAE;gBACZ,yCAAyC;gBACzC,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAExB,yBAAyB;gBACzB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,SAAS,EAAE;oBAC5C,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE;oBAC3C,OAAO,EAAE,IAAI;oBACb,QAAQ,EAAE,IAAI;iBACf,CAAC,CAAC,CAAC;aACL;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAED;;WAEG;QACH,iBAAiB,CAAC,OAAe;YAC/B,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC;YACjC,IAAI,CAAC,QAAQ,GAAG,CAAC,OAAO,CAAC;YACzB,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;QAED;;WAEG;QACH,KAAK;YACF,IAAY,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC;YACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;YACxB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;QAED;;WAEG;QACO,gBAAgB;YACxB,6CAA6C;YAC7C,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC7D,CAAC;QAED;;WAEG;QACK,uBAAuB,CAAC,OAAgB;YAC9C,MAAM,MAAM,GAA0B;gBACpC,OAAO;gBACP,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO,EAAE,IAAI,CAAC,iBAAiB,IAAI,EAAE;gBACrC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC;gBACrD,KAAK,EAAE,IAAI,CAAC,IAAI;gBAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;YAEF,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,eAAe,EAAE;gBAClD,MAAM;gBACN,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC,CAAC;QACN,CAAC;QAED;;WAEG;QACH,IAAI,OAAO;YACT,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED;;WAEG;QACH,IAAI,SAAS;YACX,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QAED;;WAEG;QACH,IAAI,OAAO;YACT,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED;;WAEG;QACO,oBAAoB;YAC5B,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,QAAQ;gBACtB,SAAS,EAAE,CAAC,IAAI,CAAC,QAAQ;gBACzB,SAAS,EAAE,IAAI,CAAC,UAAU;gBAC1B,WAAW,EAAE,CAAC,IAAI,CAAC,UAAU;gBAC7B,OAAO,EAAE,IAAI,CAAC,QAAQ;gBACtB,UAAU,EAAE,CAAC,IAAI,CAAC,QAAQ;gBAC1B,UAAU,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;aACnC,CAAC;QACJ,CAAC;KACF;IA7SC;QADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;+DACb;IAId;QADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;mEACT;IAInB;QADC,KAAK,EAAE;4EACwB;IAIhC;QADC,KAAK,EAAE;mEACyB;IAIjC;QADC,KAAK,EAAE;qEAC4B;IAIpC;QADC,KAAK,EAAE;mEAC0B;IA2RpC,OAAO,6BAAqE,CAAC;AAC/E,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Nuraly, Laabidi Aymen\n * SPDX-License-Identifier: MIT\n */\n\n/**\n * @deprecated This mixin is deprecated and no longer used.\n * \n * Form field validation is now handled by individual component validation controllers:\n * - Input validation: InputValidationController\n * - Form coordination: FormValidationController (listens to validation events)\n * \n * This approach eliminates duplication and provides better separation of concerns.\n * Components handle their own validation, and forms coordinate/listen to validation events.\n */\n\nimport { LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { FormFieldValidation, ValidationEventDetail } from '../interfaces/validation.interface.js';\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\n\n/**\n * Mixin that provides standardized form field validation capabilities\n * This should be used by input components that need to participate in forms\n * \n * @example\n * ```typescript\n * export class InputComponent extends FormFieldValidationMixin(NuralyUIBaseMixin(LitElement)) {\n * // Component-specific validation logic\n * protected validateValue(value: string): boolean {\n * return value.length >= this.minLength;\n * }\n * }\n * ```\n */\nexport const FormFieldValidationMixin = <T extends Constructor<LitElement>>(superClass: T) => {\n class FormFieldValidationMixinClass extends superClass implements FormFieldValidation {\n \n /** Field name for form submission */\n @property({ type: String })\n name?: string;\n \n /** Required field indicator */\n @property({ type: Boolean })\n required?: boolean;\n \n /** Current validation message */\n @state()\n validationMessage?: string = '';\n \n /** Validation state */\n @state()\n private _isValid: boolean = true;\n \n /** Whether field has been touched by user */\n @state()\n private _isTouched: boolean = false;\n \n /** Whether field value has changed from initial */\n @state()\n private _isDirty: boolean = false;\n \n /** Initial value for dirty state tracking */\n private _initialValue: any;\n \n /** Field value - must be implemented by component */\n declare value: any;\n \n override connectedCallback(): void {\n super.connectedCallback();\n this._initialValue = this.value;\n this.addValidationListeners();\n }\n \n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeValidationListeners();\n }\n \n /**\n * Add validation event listeners\n */\n private addValidationListeners(): void {\n this.addEventListener('focus', this.handleFocus);\n this.addEventListener('blur', this.handleBlur);\n this.addEventListener('input', this.handleInput);\n this.addEventListener('change', this.handleChange);\n }\n \n /**\n * Remove validation event listeners\n */\n private removeValidationListeners(): void {\n this.removeEventListener('focus', this.handleFocus);\n this.removeEventListener('blur', this.handleBlur);\n this.removeEventListener('input', this.handleInput);\n this.removeEventListener('change', this.handleChange);\n }\n \n /**\n * Handle focus event\n */\n private handleFocus = (): void => {\n this._isTouched = true;\n };\n \n /**\n * Handle blur event\n */\n private handleBlur = (): void => {\n this.validateOnBlur();\n };\n \n /**\n * Handle input event\n */\n private handleInput = (): void => {\n this.updateDirtyState();\n this.validateOnInput();\n };\n \n /**\n * Handle change event\n */\n private handleChange = (): void => {\n this.updateDirtyState();\n this.validateOnChange();\n };\n \n /**\n * Update dirty state\n */\n private updateDirtyState(): void {\n this._isDirty = this.value !== this._initialValue;\n }\n \n /**\n * Validate on blur (can be overridden)\n */\n protected validateOnBlur(): void {\n // Only validate if the validation trigger is set to blur\n const trigger = (this as any).validationTrigger;\n if (trigger === 'blur') {\n this.validate();\n }\n }\n \n /**\n * Validate on input (can be overridden)\n */\n protected validateOnInput(): void {\n // Override in component if needed\n }\n \n /**\n * Validate on change (can be overridden)\n */\n protected validateOnChange(): void {\n // Only validate if the validation trigger is set to change\n const trigger = (this as any).validationTrigger;\n if (trigger === 'change') {\n this.validate();\n }\n }\n \n /**\n * Main validation method - validates the field\n */\n validate(): boolean {\n const isValid = this.performValidation();\n this.setValidationState(isValid);\n return isValid;\n }\n \n /**\n * Perform the actual validation logic\n * Override this method in components for specific validation\n */\n protected performValidation(): boolean {\n // Required field validation\n if (this.required && this.isEmpty()) {\n this.validationMessage = 'This field is required';\n return false;\n }\n \n // Component-specific validation\n if (!this.isEmpty() && !this.validateValue(this.value)) {\n return false;\n }\n \n this.validationMessage = '';\n return true;\n }\n \n /**\n * Component-specific value validation\n * Override this method in components\n */\n protected validateValue(_value: any): boolean {\n return true; // Default: always valid\n }\n \n /**\n * Check if value is empty\n */\n protected isEmpty(): boolean {\n const value = this.value;\n return value === null || \n value === undefined || \n value === '' || \n (Array.isArray(value) && value.length === 0);\n }\n \n /**\n * Set validation state and dispatch events\n */\n private setValidationState(isValid: boolean): void {\n const wasValid = this._isValid;\n this._isValid = isValid;\n \n // Dispatch validation event if state changed\n if (wasValid !== isValid) {\n this.dispatchValidationEvent(isValid);\n }\n \n // Update UI\n this.requestUpdate();\n }\n \n /**\n * HTML5 constraint validation API\n */\n checkValidity(): boolean {\n return this.validate();\n }\n \n /**\n * HTML5 constraint validation API with UI feedback\n */\n reportValidity(): boolean {\n const isValid = this.validate();\n \n if (!isValid) {\n // Show validation UI (can be overridden)\n this.showValidationUI();\n \n // Dispatch invalid event\n this.dispatchEvent(new CustomEvent('invalid', {\n detail: { message: this.validationMessage },\n bubbles: true,\n composed: true\n }));\n }\n \n return isValid;\n }\n \n /**\n * Set custom validation message\n */\n setCustomValidity(message: string): void {\n this.validationMessage = message;\n this._isValid = !message;\n this.requestUpdate();\n }\n \n /**\n * Reset field to initial state\n */\n reset(): void {\n (this as any).value = this._initialValue;\n this._isValid = true;\n this._isTouched = false;\n this._isDirty = false;\n this.validationMessage = '';\n this.requestUpdate();\n }\n \n /**\n * Show validation UI (override in components)\n */\n protected showValidationUI(): void {\n // Default implementation - can be overridden\n console.warn('Validation failed:', this.validationMessage);\n }\n \n /**\n * Dispatch validation event\n */\n private dispatchValidationEvent(isValid: boolean): void {\n const detail: ValidationEventDetail = {\n isValid,\n value: this.value,\n message: this.validationMessage || '',\n errors: isValid ? [] : [this.validationMessage || ''],\n field: this.name,\n timestamp: Date.now()\n };\n \n this.dispatchEvent(new CustomEvent('nr-validation', {\n detail,\n bubbles: true,\n composed: true\n }));\n }\n \n /**\n * Get validation state\n */\n get isValid(): boolean {\n return this._isValid;\n }\n \n /**\n * Get touched state\n */\n get isTouched(): boolean {\n return this._isTouched;\n }\n \n /**\n * Get dirty state\n */\n get isDirty(): boolean {\n return this._isDirty;\n }\n \n /**\n * Get validation classes for styling\n */\n protected getValidationClasses(): Record<string, boolean> {\n return {\n 'valid': this._isValid,\n 'invalid': !this._isValid,\n 'touched': this._isTouched,\n 'untouched': !this._isTouched,\n 'dirty': this._isDirty,\n 'pristine': !this._isDirty,\n 'required': this.required || false\n };\n }\n }\n \n return FormFieldValidationMixinClass as Constructor<FormFieldValidation> & T;\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@nuralyui/form",
3
+ "version": "0.1.0",
4
+ "description": "NuralyUI Form - A comprehensive form component with validation coordination, submission handling, and state management",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "module": "./index.js",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "default": "./index.js"
13
+ },
14
+ "./form.component.js": {
15
+ "types": "./form.component.d.ts",
16
+ "default": "./form.component.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "*.js",
21
+ "*.d.ts",
22
+ "controllers/",
23
+ "interfaces/",
24
+ "mixins/",
25
+ "utils/",
26
+ "demo/",
27
+ "test/"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "test": "wtr \"**/*.test.js\" --node-resolve",
32
+ "test:watch": "wtr \"**/*.test.js\" --node-resolve --watch",
33
+ "demo": "wds --open demo/ --node-resolve"
34
+ },
35
+ "keywords": [
36
+ "form",
37
+ "validation",
38
+ "form-control",
39
+ "web-components",
40
+ "lit",
41
+ "nuraly",
42
+ "ui-components",
43
+ "form-submission",
44
+ "form-state"
45
+ ],
46
+ "author": "Nuraly Team <team@nuraly.io>",
47
+ "license": "MIT",
48
+ "dependencies": {
49
+ "lit": "^3.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "@nuraly/shared": "^1.0.0"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/Nuralyio/NuralyUI.git",
57
+ "directory": "src/components/form"
58
+ },
59
+ "bugs": {
60
+ "url": "https://github.com/Nuralyio/NuralyUI/issues"
61
+ },
62
+ "homepage": "https://nuralyui.com/components/form"
63
+ }