update multicurrencies functionality on global level
This commit is contained in:
@@ -1,14 +1,22 @@
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
const Repeater = Swal.mixin({
|
||||
customClass: {
|
||||
const Repeater = (window.Swal && typeof window.Swal.mixin === 'function')
|
||||
? window.Swal.mixin({
|
||||
customClass: {
|
||||
container: 'wpcfto-settings',
|
||||
confirmButton: 'button'
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
: {
|
||||
// Fallback that mimics Swal.fire returning a Promise
|
||||
fire: (opts) => Promise.resolve({
|
||||
isConfirmed: window.confirm((opts && (opts.text || opts.html || opts.title)) || 'Are you sure?')
|
||||
})
|
||||
};
|
||||
|
||||
Vue.component('wpcfto_repeater', {
|
||||
mixins: (window.validationMixin ? [window.validationMixin] : []),
|
||||
props: ['fields', 'field_label', 'field_name', 'field_id', 'field_value'],
|
||||
data: function data() {
|
||||
return {
|
||||
@@ -18,8 +26,23 @@ Vue.component('wpcfto_repeater', {
|
||||
},
|
||||
template: `
|
||||
<div class="wpcfto_generic_field wpcfto_generic_field_repeater wpcfto-repeater unflex_fields">
|
||||
<wpcfto_fields_aside_before :fields="fields" :field_label="field_label"></wpcfto_fields_aside_before>
|
||||
<wpcfto_fields_aside_before
|
||||
:fields="fields"
|
||||
:field_label="field_label"
|
||||
:required="fields && fields.required === true"
|
||||
/>
|
||||
<div class="wpcfto-field-content">
|
||||
<!-- Required proxy: forces validation when repeater is empty -->
|
||||
<input
|
||||
class="wpcfto-required-proxy"
|
||||
type="text"
|
||||
:name="field_name + '__required__'"
|
||||
:value="(repeater && repeater.length ? 'ok' : '')"
|
||||
:required="fields && fields.required === true && (!repeater || repeater.length === 0)"
|
||||
:disabled="!(fields && fields.required === true && (!repeater || repeater.length === 0))"
|
||||
tabindex="-1" readonly
|
||||
style="position:absolute; left:-9999px; top:auto; width:1px; height:1px; opacity:0; pointer-events:none;"
|
||||
/>
|
||||
<div v-for="(area, area_key) in repeater" class="wpcfto-repeater-single" :class="'wpcfto-repeater_' + field_name + '_' + area_key">
|
||||
<div class="wpcfto_group_title" @click="toggleArea(area)" v-html="getGroupTitle(area, area_key)"></div>
|
||||
<div class="repeater_inner" :class="{ closed: area.closed_tab }">
|
||||
@@ -71,10 +94,101 @@ Vue.component('wpcfto_repeater', {
|
||||
item.closed_tab = true;
|
||||
});
|
||||
}
|
||||
// Initial validation state
|
||||
if (typeof this.validateField === 'function') this.validateField();
|
||||
// Block Save when repeater is required but empty (length check only)
|
||||
const handler = (e) => {
|
||||
// Only enforce when this component is in DOM
|
||||
if (!this.$el || !document.body.contains(this.$el)) return;
|
||||
const emptyRequired = !!(this.fields && this.fields.required === true && (!this.repeater || this.repeater.length === 0));
|
||||
if (emptyRequired) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.focusSelf();
|
||||
}
|
||||
};
|
||||
try {
|
||||
const btns = document.querySelectorAll('.wpcfto_save_settings, .wpcfto_save_metabox');
|
||||
btns.forEach(btn => btn.addEventListener('click', handler, true));
|
||||
this.__wpcftoSaveHandler = handler;
|
||||
} catch(e) {}
|
||||
// Also prevent <form> submission if invalid (works for both settings + metabox)
|
||||
try {
|
||||
const form = this.$el.closest('form');
|
||||
if (form) {
|
||||
const onSubmit = (e) => {
|
||||
const emptyRequired = !!(this.fields && this.fields.required === true && (!this.repeater || this.repeater.length === 0));
|
||||
if (emptyRequired) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.focusSelf();
|
||||
}
|
||||
};
|
||||
form.addEventListener('submit', onSubmit, true);
|
||||
this.__wpcftoFormSubmitHandler = onSubmit;
|
||||
}
|
||||
} catch(e) {}
|
||||
if (typeof this.validateField === 'function') this.validateField();
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
if (this.__teardown) this.__teardown();
|
||||
},
|
||||
methods: {
|
||||
focusSelf() {
|
||||
try {
|
||||
const rect = this.$el.getBoundingClientRect();
|
||||
const top = window.pageYOffset + rect.top - 60;
|
||||
window.scrollTo({ top, behavior: 'smooth' });
|
||||
} catch(e) {}
|
||||
try { this.$el.classList.add('wpcfto-invalid'); } catch(e) {}
|
||||
},
|
||||
// scrollIntoViewIfNeeded() {
|
||||
// try {
|
||||
// const top = window.pageYOffset + this.$el.getBoundingClientRect().top - 60;
|
||||
// window.scrollTo({ top, behavior: 'smooth' });
|
||||
// } catch(e) {}
|
||||
// },
|
||||
formatLabelPath(parts) {
|
||||
return parts.filter(Boolean).map(function (s) {
|
||||
return String(s).trim();
|
||||
}).filter(Boolean).join(' → ');
|
||||
},
|
||||
validateField() {
|
||||
let isValid = true;
|
||||
if ('required' in this.fields && this.fields.required === true) {
|
||||
isValid = !!(this.fields.value.length > 0);
|
||||
}
|
||||
if (!isValid) {
|
||||
try { this.$el.classList.add('wpcfto-invalid'); } catch(e){}
|
||||
try { this.$el.setAttribute('aria-invalid','true'); } catch(e){}
|
||||
// Tell the global collector (validationMixin) about our state
|
||||
try {
|
||||
const fid = this.field_id || (this.fields && this.fields.field_id);
|
||||
if (fid) this.$root.$emit('field-validation', { fieldId: fid, isValid: false });
|
||||
} catch(e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
try { this.$el.classList.remove('wpcfto-invalid'); } catch(e){}
|
||||
try { this.$el.removeAttribute('aria-invalid'); } catch(e){}
|
||||
try {
|
||||
const fid = this.field_id || (this.fields && this.fields.field_id);
|
||||
if (fid) this.$root.$emit('field-validation', { fieldId: fid, isValid: true });
|
||||
} catch(e) {}
|
||||
|
||||
return true;
|
||||
},
|
||||
// This method validates all visible required fields in all rows before adding a new row
|
||||
validateAndAddArea: function() {
|
||||
const fieldsInRepeater = this._props.fields.fields;
|
||||
let group_title = null;
|
||||
Object.values(fieldsInRepeater).forEach(field => {
|
||||
if ('is_group_title' in field) {
|
||||
if (field.is_group_title !== false) {
|
||||
group_title = field.label;
|
||||
}
|
||||
}
|
||||
});
|
||||
var invalidFields = [];
|
||||
// Loop through all repeater rows and all fields
|
||||
this.repeater.forEach((area, area_key) => {
|
||||
@@ -91,6 +205,8 @@ Vue.component('wpcfto_repeater', {
|
||||
if (childComponent.isVisible && childComponent.fields && childComponent.fields.required) {
|
||||
if (!childComponent.validateField()) {
|
||||
invalidFields.push({
|
||||
repeater_label: this._props.field_label,
|
||||
group_title: group_title,
|
||||
label: childComponent.fields.label || field_name_inner,
|
||||
area: area_key + 1
|
||||
});
|
||||
@@ -102,20 +218,25 @@ Vue.component('wpcfto_repeater', {
|
||||
}
|
||||
});
|
||||
if (invalidFields.length > 0) {
|
||||
// Compose error message
|
||||
var msg = 'Please fill all required fields before adding a new item:\n\n';
|
||||
msg += invalidFields.map(f => 'Item #' + f.area + ': ' + f.label).join('\n');
|
||||
// Use SweetAlert2 if available, otherwise alert()
|
||||
if (typeof window.Swal === 'function') {
|
||||
Repeater.fire({
|
||||
icon: 'warning',
|
||||
title: 'Required fields missing',
|
||||
html: msg.replace(/\n/g, '<br>')
|
||||
});
|
||||
} else {
|
||||
alert(msg);
|
||||
}
|
||||
return;
|
||||
// Compose error message
|
||||
var msg = 'Please fill all required fields before adding a new item:\n\n';
|
||||
const firstInvalidItem = invalidFields[0];
|
||||
msg += 'Check item #'+firstInvalidItem.area+': <b>'+this.formatLabelPath([firstInvalidItem.group_title, firstInvalidItem.label])+'</b>';
|
||||
// Use SweetAlert2 if available, otherwise alert()
|
||||
if (typeof window.Swal === 'function') {
|
||||
Repeater.fire({
|
||||
icon: 'warning',
|
||||
title: 'Required fields missing',
|
||||
html: msg.replace(/\n/g, '<br>'),
|
||||
width: 'fit-content',
|
||||
customClass: {
|
||||
confirmButton: 'btn text-bg-primary'
|
||||
},
|
||||
});
|
||||
} else {
|
||||
alert(msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If all required fields are valid, add a new row
|
||||
this.addArea();
|
||||
@@ -134,6 +255,9 @@ Vue.component('wpcfto_repeater', {
|
||||
}
|
||||
});
|
||||
this.validateField();
|
||||
if (typeof jQuery !== 'undefined') {
|
||||
jQuery(document).trigger('repeater-item-added', [this._props]);
|
||||
}
|
||||
},
|
||||
toggleArea: function toggleArea(area) {
|
||||
// Close all other rows
|
||||
@@ -159,6 +283,10 @@ Vue.component('wpcfto_repeater', {
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
this.repeater.splice(areaIndex, 1);
|
||||
// jQuery action hook trigger example:
|
||||
if (typeof jQuery !== 'undefined') {
|
||||
jQuery(document).trigger('repeater-item-removed', [this._props, areaIndex]);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -243,9 +371,10 @@ Vue.component('wpcfto_repeater', {
|
||||
return true;
|
||||
},
|
||||
saveChanges() {
|
||||
if (!this.validateAllRows()) {
|
||||
return; // Prevent save if validation fails
|
||||
}
|
||||
// Block if repeater required but empty OR if any child invalid
|
||||
const selfValid = this.validateField();
|
||||
// const rowsValid = this.validateAllRows();
|
||||
if (!selfValid) { return; }
|
||||
// Proceed with save logic here...
|
||||
}
|
||||
},
|
||||
@@ -253,7 +382,8 @@ Vue.component('wpcfto_repeater', {
|
||||
repeater: {
|
||||
deep: true,
|
||||
handler: function handler(repeater) {
|
||||
this.$emit('wpcfto-get-value', repeater);
|
||||
this.$emit('wpcfto-get-value', repeater);
|
||||
this.validateField();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user