diff --git a/app/javascript/controllers/form_validation_controller.js b/app/javascript/controllers/form_validation_controller.js index c9817e27..85e7bb86 100644 --- a/app/javascript/controllers/form_validation_controller.js +++ b/app/javascript/controllers/form_validation_controller.js @@ -3,6 +3,16 @@ import { Controller } from "stimulus"; export default class extends Controller { static targets = ["invalid", "submitting"]; + // @todo Stimulus >1 + get submittingIdValue() { + return this.element.dataset?.formValidationSubmittingIdValue; + } + + // @todo Stimulus >1 + get invalidIdValue() { + return this.element.dataset?.formValidationInvalidIdValue; + } + connect() { this.element.setAttribute("novalidate", true); @@ -19,34 +29,19 @@ export default class extends Controller { submit(event = undefined) { event?.preventDefault(); - event?.stopPropagation(); if (this.element.reportValidity()) { this.element.classList.remove("was-validated"); - this.element.submit(); - this.show(this.submittingTargets); - this.hide(this.invalidTargets); + if (!this.element.hasAttribute("hx-post")) this.element.submit(); + + window.dispatchEvent(new CustomEvent("notification:show", { detail: { id: this.submittingIdValue } })); } else { + event?.stopPropagation(); + this.element.classList.add("was-validated"); - this.hide(this.submittingTargets); - this.show(this.invalidTargets); - } - } - show(elements) { - for (const element of elements) { - element.classList.remove("d-none"); - - setTimeout(() => element.classList.add("show"), 1); - } - } - - hide(elements) { - for (const element of elements) { - element.classList.remove("show"); - - setTimeout(() => element.classList.add("d-none"), 2000); + window.dispatchEvent(new CustomEvent("notification:show", { detail: { id: this.invalidIdValue } })); } } } diff --git a/app/javascript/controllers/notification_controller.js b/app/javascript/controllers/notification_controller.js new file mode 100644 index 00000000..7fbe3b5a --- /dev/null +++ b/app/javascript/controllers/notification_controller.js @@ -0,0 +1,43 @@ +import { Controller } from "stimulus"; + +/* + * Solo se puede mostrar una notificación a la vez + */ +export default class extends Controller { + // @todo Stimulus >1 + get showClasses() { + return (this.element.dataset?.notificationShowClass || "").split(" ").filter(x => x); + } + + // @todo Stimulus >1 + get hideClasses() { + return (this.element.dataset?.notificationHideClass || "").split(" ").filter(x => x); + } + + /* + * Al recibir el evento de mostrar, si no está dirigido al elemento + * actual, se oculta. + */ + show(event = undefined) { + if (event?.detail?.id !== this.element.id) { + this.hide({ detail: { id: this.element.id } }); + return; + } + + this.element.classList.remove("d-none"); + + setTimeout(() => { + this.element.classList.remove(...this.hideClasses); + this.element.classList.add(...this.showClasses); + }, 1); + } + + hide(event = undefined) { + if (event?.detail?.id !== this.element.id) return; + + this.element.classList.remove(...this.showClasses); + this.element.classList.add(...this.hideClasses); + + setTimeout(() => this.element.classList.add("d-none"), 150); + } +} diff --git a/app/views/posts/_form.haml b/app/views/posts/_form.haml index aa8f3d1d..3e09cb72 100644 --- a/app/views/posts/_form.haml +++ b/app/views/posts/_form.haml @@ -31,11 +31,19 @@ end - dir = t("locales.#{@locale}.dir") +- submitting_id = random_id +- invalid_id = random_id +- data = {} +- data[:controller] = 'unsaved-changes form-validation' +- data[:action] = 'unsaved-changes#submit form-validation#submit beforeunload@window->unsaved-changes#unsaved turbolinks:before-visit@window->unsaved-changes#unsavedTurbolinks' +- data[:'unsaved-changes-confirm-value'] = t('.confirm') +- data[:'form-validation-submitting-id-value'] = submitting_id +- data[:'form-validation-invalid-id-value'] = invalid_id -# Comienza el formulario -= form_tag url, method: method, class: "form post #{extra_class}", multipart: true, data: { controller: 'unsaved-changes form-validation', action: 'unsaved-changes#submit form-validation#submit beforeunload@window->unsaved-changes#unsaved turbolinks:before-visit@window->unsaved-changes#unsavedTurbolinks', 'unsaved-changes-confirm-value': t('.confirm') } do += form_tag url, method: method, class: "form post #{extra_class}", multipart: true, data: data do -# Botones de guardado - = render 'posts/submit', site: site, post: post + = render 'posts/submit', site: site, post: post, invalid: invalid_id, submitting: submitting_id = hidden_field_tag 'post[layout]', post.layout.name @@ -43,7 +51,7 @@ = render 'posts/attributes', site: site, post: post, dir: dir, base: 'post', locale: @locale -# Botones de guardado - = render 'posts/submit', site: site, post: post + = render 'posts/submit', site: site, post: post, invalid: invalid_id, submitting: submitting_id -# Formularios usados por los modales = yield(:post_form) diff --git a/app/views/posts/_submit.haml b/app/views/posts/_submit.haml index c3064c53..41d6f420 100644 --- a/app/views/posts/_submit.haml +++ b/app/views/posts/_submit.haml @@ -1,11 +1,3 @@ -- invalid_help = site.config.fetch('invalid_help', t('.invalid_help')) -- submitting_help = site.config.fetch('submitting_help', t('.submitting_help')) - .d-flex.flex-column.flex-md-row.align-items-start.mb-3 - %div - = submit_tag t('.save'), class: 'btn btn-secondary submit-post' - .d-flex.flex-column.position-relative - = render 'bootstrap/alert', class: 'm-0 d-none fade', data: { target: 'form-validation.invalid' } do - = invalid_help - = render 'bootstrap/alert', class: 'm-0 d-none fade position-absolute top-0 left-0', data: { target: 'form-validation.submitting' } do - = submitting_help + %div= submit_tag t('.save'), class: 'btn btn-secondary submit-post' + = render 'posts/validation', site: site, submitting: { id: submitting }, invalid: { id: invalid } diff --git a/app/views/posts/_validation.haml b/app/views/posts/_validation.haml new file mode 100644 index 00000000..c28a743a --- /dev/null +++ b/app/views/posts/_validation.haml @@ -0,0 +1,16 @@ +- invalid = site.config.fetch('invalid', t('.invalid')) +- submitting = site.config.fetch('submitting', t('.submitting')) +- %i[invalid submitting].each do |key| + - local_assigns[key] ||= {} + - local_assigns[key][:data] ||= {} + - local_assigns[key][:data][:target] ||= "form-validation.#{key}" + - local_assigns[key][:data][:action] ||= 'notification:show@window->notification#show' + - local_assigns[key][:data][:controller] ||= 'notification' + - local_assigns[key][:data][:'notification-hide-class'] ||= 'hide' + - local_assigns[key][:data][:'notification-show-class'] ||= 'show' + +.d-flex.flex-column + = render 'bootstrap/alert', class: 'm-0 d-none fade', **local_assigns[:invalid] do + = invalid + = render 'bootstrap/alert', class: 'm-0 d-none fade', **local_assigns[:submitting] do + = submitting diff --git a/config/locales/en.yml b/config/locales/en.yml index 3815f5de..c0ad36d2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -719,8 +719,9 @@ en: submit: save: 'Save' save_draft: 'Save as draft' - invalid_help: "Some fields need attention! Please search for the fields marked as not valid." - submitting_help: "Saving changes, please wait..." + validation: + invalid: "Some fields need attention! Please search for the fields marked as not valid." + submitting: "Saving changes, please wait..." new_array: remove: "Remove" attributes: diff --git a/config/locales/es.yml b/config/locales/es.yml index 7b981b1d..1fd397e4 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -727,8 +727,9 @@ es: submit: save: 'Guardar' save_draft: 'Guardar como borrador' - invalid_help: "¡Te falta completar algunos campos! Busca los que estén marcados como no válidos." - submitting_help: "Guardando, por favor espera..." + validation: + invalid: "¡Te falta completar algunos campos! Busca los que estén marcados como no válidos." + submitting: "Guardando, por favor espera..." new_array: remove: "Eliminar" attributes: @@ -770,6 +771,8 @@ es: edit: "Editar" new_predefined_array: edit: "Editar" + new_predefined_value: + edit: "Editar" new_array: edit: "Editar" required: "Seleccioná al menos una opción."