first commit
This commit is contained in:
477
resources/assets/js/forms/formConvasHelper.js
Normal file
477
resources/assets/js/forms/formConvasHelper.js
Normal file
@ -0,0 +1,477 @@
|
||||
/**
|
||||
* FormCanvasHelper
|
||||
*
|
||||
* Clase para orquestar la interacción entre un formulario dentro de un Offcanvas
|
||||
* de Bootstrap y el estado de Livewire (modo create/edit/delete), además de
|
||||
* manipular ciertos componentes externos como Select2.
|
||||
*
|
||||
* Se diseñó teniendo en cuenta que el DOM del Offcanvas puede reconstruirse
|
||||
* (re-render) de manera frecuente, por lo que muchos getters reacceden al DOM
|
||||
* dinámicamente.
|
||||
*/
|
||||
export default class FormCanvasHelper {
|
||||
/**
|
||||
* @param {string} offcanvasId - ID del elemento Offcanvas en el DOM.
|
||||
* @param {object} liveWireInstance - Instancia de Livewire asociada al formulario.
|
||||
*/
|
||||
constructor(offcanvasId, liveWireInstance) {
|
||||
this.offcanvasId = offcanvasId;
|
||||
this.liveWireInstance = liveWireInstance;
|
||||
|
||||
// Validamos referencias mínimas para evitar errores tempranos
|
||||
// Si alguna falta, se mostrará un error en consola.
|
||||
this.validateInitialDomRefs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica la existencia básica de elementos en el DOM.
|
||||
* Muestra errores en consola si faltan elementos críticos.
|
||||
*/
|
||||
validateInitialDomRefs() {
|
||||
const offcanvasEl = document.getElementById(this.offcanvasId);
|
||||
|
||||
if (!offcanvasEl) {
|
||||
console.error(`❌ No se encontró el contenedor Offcanvas con ID: ${this.offcanvasId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const formEl = offcanvasEl.querySelector('form');
|
||||
if (!formEl) {
|
||||
console.error(`❌ No se encontró el formulario dentro de #${this.offcanvasId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const offcanvasTitle = offcanvasEl.querySelector('.offcanvas-title');
|
||||
const submitButtons = formEl.querySelectorAll('.btn-submit');
|
||||
const resetButtons = formEl.querySelectorAll('.btn-reset');
|
||||
|
||||
if (!offcanvasTitle || !submitButtons.length || !resetButtons.length) {
|
||||
console.error(`❌ Faltan el título, botones de submit o reset dentro de #${this.offcanvasId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter para el contenedor Offcanvas actual.
|
||||
* Retorna siempre la referencia más reciente del DOM.
|
||||
*/
|
||||
get offcanvasEl() {
|
||||
return document.getElementById(this.offcanvasId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter para el formulario dentro del Offcanvas.
|
||||
*/
|
||||
get formEl() {
|
||||
return this.offcanvasEl?.querySelector('form') ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter para el título del Offcanvas.
|
||||
*/
|
||||
get offcanvasTitleEl() {
|
||||
return this.offcanvasEl?.querySelector('.offcanvas-title') ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter para la instancia de Bootstrap Offcanvas.
|
||||
* Siempre retorna la instancia más reciente en caso de re-render.
|
||||
*/
|
||||
get offcanvasInstance() {
|
||||
if (!this.offcanvasEl) return null;
|
||||
return bootstrap.Offcanvas.getOrCreateInstance(this.offcanvasEl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna todos los botones de submit en el formulario.
|
||||
*/
|
||||
get submitButtons() {
|
||||
return this.formEl?.querySelectorAll('.btn-submit') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna todos los botones de reset en el formulario.
|
||||
*/
|
||||
get resetButtons() {
|
||||
return this.formEl?.querySelectorAll('.btn-reset') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Método principal para manejar la recarga del Offcanvas según los estados en Livewire.
|
||||
* Se encarga de resetear el formulario, limpiar errores y cerrar/abrir el Offcanvas
|
||||
* según sea necesario.
|
||||
*
|
||||
* @param {string|null} triggerMode - Forzar la acción (e.g., 'reset', 'create'). Si no se especifica, se verifica según Livewire.
|
||||
*/
|
||||
reloadOffcanvas(triggerMode = null) {
|
||||
setTimeout(() => {
|
||||
const mode = this.liveWireInstance.get('mode');
|
||||
const successProcess = this.liveWireInstance.get('successProcess');
|
||||
const validationError = this.liveWireInstance.get('validationError');
|
||||
|
||||
// Si se completa la acción o triggerMode = 'reset',
|
||||
// reseteamos completamente y cerramos el Offcanvas.
|
||||
if (triggerMode === 'reset' || successProcess) {
|
||||
this.resetFormAndState('create');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Forzar modo create si se solicita explícitamente
|
||||
if (triggerMode === 'create') {
|
||||
// Evitamos re-reset si ya estamos en 'create'
|
||||
if (mode === 'create') return;
|
||||
|
||||
this.resetFormAndState('create');
|
||||
|
||||
this.focusOnOpen();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Si no, simplemente preparamos la UI según el modo actual.
|
||||
this.prepareOffcanvasUI(mode);
|
||||
|
||||
// Si hay errores de validación, reabrimos el Offcanvas para mostrarlos.
|
||||
if (validationError) {
|
||||
this.liveWireInstance.set('validationError', null, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Si estamos en edit o delete, solo abrimos el Offcanvas.
|
||||
if (mode === 'edit' || mode === 'delete') {
|
||||
this.clearErrors();
|
||||
|
||||
if(mode === 'edit') {
|
||||
this.focusOnOpen();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reabre o fuerza la apertura del Offcanvas si hay errores de validación
|
||||
* o si el modo de Livewire es 'edit' o 'delete'.
|
||||
*
|
||||
* Normalmente se llama cuando hay un dispatch/evento de Livewire,
|
||||
* por ejemplo si el servidor devuelve un error de validación (para mostrarlo)
|
||||
* o si se acaba de cargar un registro para editar o eliminar.
|
||||
*
|
||||
* - Si hay `validationError`, forzamos la reapertura con `toggleOffcanvas(true, true)`
|
||||
* para que se refresque correctamente y el usuario vea los errores.
|
||||
* - Si el modo es 'edit' o 'delete', simplemente mostramos el Offcanvas sin forzar
|
||||
* un refresco de la interfaz.
|
||||
*/
|
||||
refresh() {
|
||||
setTimeout(() => {
|
||||
const mode = this.liveWireInstance.get('mode');
|
||||
const successProcess = this.liveWireInstance.get('successProcess');
|
||||
const validationError = this.liveWireInstance.get('validationError');
|
||||
|
||||
// cerramos el Offcanvas.
|
||||
if (successProcess) {
|
||||
this.toggleOffcanvas(false);
|
||||
|
||||
this.resetFormAndState('create');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (validationError) {
|
||||
// Forzamos la reapertura para que se rendericen
|
||||
this.toggleOffcanvas(true, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === 'edit' || mode === 'delete') {
|
||||
// Abrimos el Offcanvas para edición o eliminación
|
||||
this.toggleOffcanvas(true);
|
||||
|
||||
return;
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepara la UI del Offcanvas según el modo actual: cambia texto de botones, título,
|
||||
* habilita o deshabilita campos, etc.
|
||||
*
|
||||
* @param {string} mode - Modo actual en Livewire: 'create', 'edit' o 'delete'
|
||||
*/
|
||||
prepareOffcanvasUI(mode) {
|
||||
// Configura el texto y estilo de botones
|
||||
this.configureButtons(mode);
|
||||
|
||||
// Ajusta el título del Offcanvas
|
||||
this.configureTitle(mode);
|
||||
|
||||
// Activa o desactiva campos según el modo
|
||||
this.configureReadonlyMode(mode === 'delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cierra o muestra el Offcanvas.
|
||||
*
|
||||
* @param {boolean} show - true para mostrar, false para ocultar.
|
||||
* @param {boolean} force - true para forzar el refresco rápido del Offcanvas.
|
||||
*/
|
||||
toggleOffcanvas(show = false, force = false) {
|
||||
const instance = this.offcanvasInstance;
|
||||
|
||||
if (!instance) return;
|
||||
|
||||
if (show) {
|
||||
if (force) {
|
||||
// "Force" hace un hide + show para asegurar un nuevo render
|
||||
instance.hide();
|
||||
setTimeout(() => instance.show(), 10);
|
||||
|
||||
} else {
|
||||
instance.show();
|
||||
}
|
||||
|
||||
} else {
|
||||
instance.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resetea el formulario y el estado en Livewire (modo, id, errores).
|
||||
*
|
||||
* @param {string} targetMode - Modo al que queremos resetear, típicamente 'create'.
|
||||
*/
|
||||
resetFormAndState(targetMode) {
|
||||
if (!this.formEl) return;
|
||||
|
||||
// Restablecemos en Livewire
|
||||
this.liveWireInstance.set('successProcess', null, false);
|
||||
this.liveWireInstance.set('validationError', null, false);
|
||||
this.liveWireInstance.set('mode', targetMode, false);
|
||||
this.liveWireInstance.set('id', null, false);
|
||||
|
||||
// Limpiamos el formulario
|
||||
this.formEl.reset();
|
||||
this.clearErrors();
|
||||
|
||||
// Restablecemos valores por defecto del formulario
|
||||
const defaults = this.liveWireInstance.get('defaultValues');
|
||||
if (defaults && typeof defaults === 'object') {
|
||||
Object.entries(defaults).forEach(([key, value]) => {
|
||||
this.liveWireInstance.set(key, value, false);
|
||||
});
|
||||
}
|
||||
|
||||
// Limpiar select2 automáticamente
|
||||
$(this.formEl)
|
||||
.find('select.select2-hidden-accessible')
|
||||
.each(function () {
|
||||
$(this).val(null).trigger('change');
|
||||
});
|
||||
|
||||
// Desactivamos el modo lectura
|
||||
this.configureReadonlyMode(false);
|
||||
|
||||
// Reconfiguramos el Offcanvas UI
|
||||
this.prepareOffcanvasUI(targetMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configura el texto y estilo de los botones de submit y reset
|
||||
* según el modo de Livewire.
|
||||
*
|
||||
* @param {string} mode - 'create', 'edit' o 'delete'
|
||||
*/
|
||||
configureButtons(mode) {
|
||||
const singularName = this.liveWireInstance.get('singularName');
|
||||
|
||||
// Limpiar clases previas
|
||||
this.submitButtons.forEach(button => {
|
||||
button.classList.remove('btn-danger', 'btn-primary');
|
||||
});
|
||||
this.resetButtons.forEach(button => {
|
||||
button.classList.remove('btn-text-secondary', 'btn-label-secondary');
|
||||
});
|
||||
|
||||
// Configurar botón de submit según el modo
|
||||
this.submitButtons.forEach(button => {
|
||||
switch (mode) {
|
||||
case 'create':
|
||||
button.classList.add('btn-primary');
|
||||
button.textContent = `Crear ${singularName.toLowerCase()}`;
|
||||
break;
|
||||
case 'edit':
|
||||
button.classList.add('btn-primary');
|
||||
button.textContent = `Guardar cambios`;
|
||||
break;
|
||||
case 'delete':
|
||||
button.classList.add('btn-danger');
|
||||
button.textContent = `Eliminar ${singularName.toLowerCase()}`;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Configurar botones de reset según el modo
|
||||
this.resetButtons.forEach(button => {
|
||||
// Cambia la clase dependiendo si se trata de un modo 'delete' o no
|
||||
const buttonClass = (mode === 'delete') ? 'btn-text-secondary' : 'btn-label-secondary';
|
||||
button.classList.add(buttonClass);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajusta el título del Offcanvas según el modo y la propiedad configurada en Livewire.
|
||||
*
|
||||
* @param {string} mode - 'create', 'edit' o 'delete'
|
||||
*/
|
||||
configureTitle(mode) {
|
||||
if (!this.offcanvasTitleEl) return;
|
||||
|
||||
const capitalizeFirstLetter =(str) => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
const singularName = this.liveWireInstance.get('singularName');
|
||||
const columnNameLabel = this.liveWireInstance.get('columnNameLabel');
|
||||
const editName = this.liveWireInstance.get(columnNameLabel);
|
||||
|
||||
switch (mode) {
|
||||
case 'create':
|
||||
this.offcanvasTitleEl.innerHTML = `<i class="ti ti-plus ml-2"></i> ${capitalizeFirstLetter(singularName)} `;
|
||||
break;
|
||||
case 'edit':
|
||||
this.offcanvasTitleEl.innerHTML = `${editName} <i class="ti ti-lg ti-pencil ml-2 text-success"></i>`;
|
||||
break;
|
||||
case 'delete':
|
||||
this.offcanvasTitleEl.innerHTML = `${editName} <i class="ti ti-lg ti-eraser ml-2 text-danger"></i>`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configura el modo de solo lectura/edición en los campos del formulario.
|
||||
* Deshabilita inputs y maneja el "readonly" en checkboxes/radios.
|
||||
*
|
||||
* @param {boolean} readOnly - true si queremos modo lectura, false para edición.
|
||||
*/
|
||||
configureReadonlyMode(readOnly) {
|
||||
if (!this.formEl) return;
|
||||
|
||||
const inputs = this.formEl.querySelectorAll('input, textarea, select');
|
||||
|
||||
inputs.forEach(el => {
|
||||
// Saltar campos marcados como "data-always-enabled"
|
||||
if (el.hasAttribute('data-always-enabled')) return;
|
||||
|
||||
// Para selects
|
||||
if (el.tagName === 'SELECT') {
|
||||
if ($(el).hasClass('select2-hidden-accessible')) {
|
||||
// Deshabilitar select2
|
||||
$(el).prop('disabled', readOnly).trigger('change.select2');
|
||||
} else {
|
||||
this.toggleSelectReadonly(el, readOnly);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Para checkboxes / radios
|
||||
if (el.type === 'checkbox' || el.type === 'radio') {
|
||||
this.toggleCheckboxReadonly(el, readOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
// Para inputs de texto / textarea
|
||||
el.readOnly = readOnly;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Alterna modo "readonly" en un checkbox/radio simulando la inhabilitación
|
||||
* sin marcarlo como 'disabled' (para mantener su apariencia).
|
||||
*
|
||||
* @param {HTMLElement} checkbox - Elemento checkbox o radio.
|
||||
* @param {boolean} enabled - true si se quiere modo lectura, false en caso contrario.
|
||||
*/
|
||||
toggleCheckboxReadonly(checkbox, enabled) {
|
||||
if (enabled) {
|
||||
checkbox.setAttribute('readonly-mode', 'true');
|
||||
checkbox.style.pointerEvents = 'none';
|
||||
checkbox.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
} else {
|
||||
checkbox.removeAttribute('readonly-mode');
|
||||
checkbox.style.pointerEvents = '';
|
||||
checkbox.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alterna modo "readonly" para un <select> convencional.
|
||||
*
|
||||
* @param {HTMLElement} select - Elemento select.
|
||||
* @param {boolean} enabled - true si queremos readonly, false si editable.
|
||||
*/
|
||||
toggleSelectReadonly(select, enabled) {
|
||||
if (enabled) {
|
||||
select.setAttribute('readonly-mode', 'true');
|
||||
select.style.pointerEvents = 'none';
|
||||
select.tabIndex = -1;
|
||||
} else {
|
||||
select.removeAttribute('readonly-mode');
|
||||
select.style.pointerEvents = '';
|
||||
select.tabIndex = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hace focus en el elemento con el selector dado.
|
||||
*/
|
||||
focusOnOpen() {
|
||||
const focusSelector = this.liveWireInstance.get('focusOnOpen'); // Obtiene el selector de Livewire
|
||||
|
||||
if (!focusSelector) return;
|
||||
|
||||
setTimeout(() => {
|
||||
// Buscar el elemento real en el DOM
|
||||
const focusElement = document.getElementById(focusSelector);
|
||||
|
||||
// Si existe, hacer focus
|
||||
if (focusElement) {
|
||||
focusElement.focus();
|
||||
} else {
|
||||
console.warn(`Elemento no encontrado: ${focusSelector}`);
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia mensajes de error y la clase 'is-invalid' en el formulario.
|
||||
*/
|
||||
clearErrors() {
|
||||
if (!this.formEl) return;
|
||||
|
||||
// Remover mensajes de error en texto
|
||||
this.formEl.querySelectorAll('.text-danger').forEach(el => el.remove());
|
||||
|
||||
// Remover la clase 'is-invalid' de los inputs afectados
|
||||
this.formEl.querySelectorAll('.is-invalid').forEach(el => el.classList.remove('is-invalid'));
|
||||
|
||||
// Remover las notificaciones
|
||||
this.formEl.querySelectorAll('.notification-container').forEach(el => el.innerHTML = '');
|
||||
|
||||
// Removemos el checkbox de confirmación de eliminar
|
||||
const confirmDeletion = this.formEl.querySelector('.confirm-deletion');
|
||||
|
||||
if (confirmDeletion) {
|
||||
confirmDeletion.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exponemos la clase en window para acceso global (si fuese necesario)
|
||||
window.FormCanvasHelper = FormCanvasHelper;
|
245
resources/assets/js/forms/formCustomListener.js
Normal file
245
resources/assets/js/forms/formCustomListener.js
Normal file
@ -0,0 +1,245 @@
|
||||
export default class FormCustomListener {
|
||||
constructor(config = {}) {
|
||||
const defaultConfig = {
|
||||
formSelector: '.form-custom-listener', // Selector para formularios
|
||||
buttonSelectors: [], // Selectores específicos para botones
|
||||
callbacks: [], // Callbacks correspondientes a los botones específicos
|
||||
allowedInputTags: ['INPUT', 'SELECT', 'TEXTAREA'], // Tags permitidos para cambios
|
||||
validationConfig: null, // Nueva propiedad para la configuración de validación
|
||||
dispatchOnSubmit: null // Callback Livewire para disparar al enviar el formulario
|
||||
};
|
||||
|
||||
this.config = { ...defaultConfig, ...config };
|
||||
|
||||
// Aseguramos que los métodos que dependen de `this` estén vinculados al contexto correcto
|
||||
this.defaultButtonHandler = this.defaultButtonHandler.bind(this);
|
||||
this.formValidationInstance = null;
|
||||
|
||||
this.initForms();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicializa los formularios encontrados en el DOM.
|
||||
*/
|
||||
initForms() {
|
||||
const forms = document.querySelectorAll(this.config.formSelector);
|
||||
|
||||
if (forms.length === 0) {
|
||||
console.error(`No se encontraron formularios con el selector ${this.config.formSelector}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
forms.forEach(form => {
|
||||
if (form.dataset.initialized === 'true') {
|
||||
console.warn(`Formulario ya inicializado: ${form}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.initFormEvents(form);
|
||||
|
||||
// Si se pasó configuración de validación, inicialízala
|
||||
if (this.config.validationConfig) {
|
||||
this.initializeValidation(form);
|
||||
}
|
||||
|
||||
form.dataset.initialized = 'true'; // Marcar formulario como inicializado
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configura los eventos para un formulario individual.
|
||||
* @param {HTMLElement} form - El formulario que será manejado.
|
||||
*/
|
||||
initFormEvents(form) {
|
||||
const buttons = this.getButtons(form);
|
||||
|
||||
buttons.forEach(({ button, callback }, index) => {
|
||||
if (button) {
|
||||
button.addEventListener('click', () => {
|
||||
this.handleButtonClick(index, form, buttons, callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
form.addEventListener('input', event =>
|
||||
this.handleInputChange(
|
||||
event,
|
||||
form,
|
||||
buttons.map(b => b.button)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene los botones y sus callbacks según la configuración.
|
||||
* @param {HTMLElement} form - El formulario del cual obtener botones.
|
||||
* @returns {Array} Array de objetos con { button, callback }.
|
||||
*/
|
||||
getButtons(form) {
|
||||
const buttons = [];
|
||||
|
||||
this.config.buttonSelectors.forEach((selector, index) => {
|
||||
const buttonList = Array.from(form.querySelectorAll(selector));
|
||||
const callback = this.config.callbacks[index];
|
||||
|
||||
buttonList.forEach(button => {
|
||||
buttons.push({ button, callback });
|
||||
});
|
||||
});
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maneja los cambios en los campos de entrada.
|
||||
* @param {Event} event - El evento del cambio.
|
||||
* @param {HTMLElement} form - El formulario actual.
|
||||
* @param {HTMLElement[]} buttons - Array de botones en el formulario.
|
||||
*/
|
||||
handleInputChange(event, form, buttons) {
|
||||
const target = event.target;
|
||||
|
||||
if (['INPUT', 'SELECT', 'TEXTAREA'].includes(target.tagName)) {
|
||||
this.toggleButtonsState(buttons, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maneja el clic en un botón específico.
|
||||
* @param {number} index - Índice del botón.
|
||||
* @param {HTMLElement} form - El formulario actual.
|
||||
* @param {Array} buttons - Array de objetos { button, callback }.
|
||||
* @param {function|null} callback - Callback definido para el botón.
|
||||
*/
|
||||
handleButtonClick(index, form, buttons, callback) {
|
||||
if (typeof callback === 'function') {
|
||||
callback(
|
||||
form,
|
||||
buttons[index].button,
|
||||
buttons.map(b => b.button)
|
||||
);
|
||||
} else {
|
||||
this.defaultButtonHandler(
|
||||
form,
|
||||
buttons[index].button,
|
||||
buttons.map(b => b.button)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maneja la acción cuando el formulario es válido.
|
||||
* Este método puede ser sobreescrito para personalizar el comportamiento.
|
||||
*/
|
||||
handleFormValid(form) {
|
||||
// Ejecutar callback opcional (si lo proporcionaste)
|
||||
if (typeof this.config.handleValidForm === 'function') {
|
||||
this.config.handleValidForm(form);
|
||||
} else if (this.config.dispatchOnSubmit) {
|
||||
this.handleValidForm(form);
|
||||
} else {
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Método que maneja la acción cuando el formulario es válido.
|
||||
* Al ser un método de la clase, no necesitamos usar bind.
|
||||
*/
|
||||
handleValidForm(form) {
|
||||
const saveButton = form.querySelector('#save_website_button');
|
||||
const allButtons = Array.from(form.querySelectorAll('.btn'));
|
||||
|
||||
this.toggleButtonsState(allButtons, false); // Deshabilitar todos los botones
|
||||
this.toggleFormFields(form, false); // Deshabilitar todos los campos del formulario
|
||||
this.setButtonLoadingState(saveButton, true); // Poner en estado de carga al botón anfitrión
|
||||
|
||||
// Enviar la solicitud de Livewire correspondiente al enviar el formulario
|
||||
Livewire.dispatch(this.config.dispatchOnSubmit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manejador por defecto para los botones.
|
||||
* @param {HTMLElement} form - El formulario actual.
|
||||
* @param {HTMLElement} hostButton - El botón anfitrión que disparó el evento.
|
||||
* @param {HTMLElement[]} allButtons - Todos los botones relevantes del formulario.
|
||||
*/
|
||||
defaultButtonHandler(form, hostButton, allButtons) {
|
||||
this.toggleButtonsState(allButtons, false); // Deshabilitar todos los botones
|
||||
this.toggleFormFields(form, false); // Deshabilitar todos los campos del formulario
|
||||
this.setButtonLoadingState(hostButton, true); // Poner en estado de carga al botón anfitrión
|
||||
}
|
||||
|
||||
/**
|
||||
* Deshabilita o habilita los campos del formulario.
|
||||
* @param {HTMLElement} form - El formulario actual.
|
||||
* @param {boolean} isEnabled - Si los campos deben habilitarse.
|
||||
*/
|
||||
toggleFormFields(form, isEnabled) {
|
||||
form.querySelectorAll('input, select, textarea').forEach(field => {
|
||||
field.disabled = !isEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Habilita o deshabilita los botones.
|
||||
* @param {HTMLElement[]} buttons - Array de botones.
|
||||
* @param {boolean} isEnabled - Si los botones deben habilitarse.
|
||||
*/
|
||||
toggleButtonsState(buttons, isEnabled) {
|
||||
buttons.forEach(button => {
|
||||
if (button) button.disabled = !isEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cambia el estado de carga de un botón.
|
||||
* @param {HTMLElement} button - Botón que se manejará.
|
||||
* @param {boolean} isLoading - Si el botón está en estado de carga.
|
||||
*/
|
||||
setButtonLoadingState(button, isLoading) {
|
||||
if (!button) return;
|
||||
|
||||
const loadingText = button.getAttribute('data-loading-text');
|
||||
if (loadingText && isLoading) {
|
||||
button.setAttribute('data-original-text', button.innerHTML);
|
||||
button.innerHTML = loadingText;
|
||||
button.disabled = true;
|
||||
} else if (!isLoading) {
|
||||
button.innerHTML = button.getAttribute('data-original-text') || button.innerHTML;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicializa la validación del formulario con la configuración proporcionada.
|
||||
* @param {HTMLElement} form - El formulario que va a ser validado.
|
||||
*/
|
||||
initializeValidation(form) {
|
||||
if (this.config.validationConfig) {
|
||||
this.formValidationInstance = FormValidation.formValidation(form, this.config.validationConfig).on(
|
||||
'core.form.valid',
|
||||
() => this.handleFormValid(form)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
reloadValidation() {
|
||||
const form = document.querySelector(this.config.formSelector);
|
||||
|
||||
// Verificar si el formulario existe y si la validación está inicializada
|
||||
if (form && this.formValidationInstance) {
|
||||
try {
|
||||
// En lugar de destruir la validación, simplemente reiniciamos la validación.
|
||||
this.formValidationInstance.resetForm(); // Resetear el formulario, limpiando los errores
|
||||
|
||||
// Reinicializar la validación con la configuración actual
|
||||
this.initializeValidation(form);
|
||||
} catch (error) {
|
||||
console.error('Error al reiniciar la validación:', error);
|
||||
}
|
||||
} else {
|
||||
console.warn('Formulario no encontrado o instancia de validación no disponible.');
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user