window.validationMixin = { methods: { // --- Helpers _depsArray() { // Support both `dependencies: [...]` and legacy single `dependency: {...}` if (this.fields && Array.isArray(this.fields.dependencies)) return this.fields.dependencies; if (this.fields && this.fields.dependency && typeof this.fields.dependency === 'object') return [this.fields.dependency]; return null; }, _isNotEmpty(val) { if (val === undefined || val === null) return false; if (typeof val === 'string') return val.trim() !== ''; if (Array.isArray(val)) return val.length > 0; // numbers: 0 should be considered not-empty (valid) for required numeric fields if (typeof val === 'number') return true; if (typeof val === 'boolean') return val === true; // objects: consider not empty if it has at least one key if (typeof val === 'object') return Object.keys(val).length > 0; return !!val; }, _equals(a, b) { // Loose compare for primitives, but normalize truthy/empty strings if (Array.isArray(a) || Array.isArray(b)) return JSON.stringify(a) === JSON.stringify(b); return String(a) == String(b); }, // --- Visibility according to dependencies isVisible() { const deps = this._depsArray(); if (!deps) return true; // Parent holds child refs by field key return deps.every(dep => { if (!dep || !dep.field) return true; // Prefer parent $refs value (set by Nuxy components) const depValue = this.$parent?.$refs?.[dep.field]?.value; // Operator const op = dep.operator || dep.op || (dep.value === 'not_empty' ? 'not_empty' : 'equals'); if (op === 'not_empty') { return this._isNotEmpty(depValue); } // Default equals return this._equals(depValue, dep.value); }); }, // --- Required validation by type validateField() { if (!this.fields) return true; const visible = this.isVisible(); const required = 'required' in this.fields && this.fields.required === true; const type = this.fields.type || ''; const value = this.field_value; let filled; if (!required) { filled = true; } else if (!visible) { // If not visible, treat as valid (global validator already skips hidden) filled = true; } else if (type === 'checkbox') { filled = value === 1 || value === true || value === '1' || value === 'true'; } else if (type === 'repeater' && required) { filled = this.fields.value && this.fields.value.length > 0; } else if (Array.isArray(value)) { filled = value.length > 0; } else if (typeof value === 'number') { // 0 is a valid number for required numeric fields filled = true; } else { filled = this._isNotEmpty(value); } const uniqueFieldId = this.field_id || (this.fields && this.fields.field_id); // Emit once per change/mount with stable payload if (uniqueFieldId) { this.$root.$emit('field-validation', { fieldId: uniqueFieldId, isValid: !!filled }); } return !!filled; }, }, watch: { field_value() { if (typeof this.validateField === 'function') { this.validateField(); } } }, mounted() { if (typeof this.validateField === 'function') { this.validateField(); } } };