-# Genera un listado de checkboxes entre los que se puede elegir para guardar. Podemos elegir entre los artículos ya cargados o agregar uno nuevo. Al agregar uno nuevo, se abre un segundo modal que carga el formulario correspondiente vía HTMX. El formulario tiene que cargarse por fuera del formulario principal porque no se pueden anidar. :ruby id = id_for(base, attribute) name = "#{base}[#{attribute}][]" form_id = random_id modal_id = random_id post_id = random_id post_form_id = random_id post_modal_id = random_id post_form_loaded_id = random_id value_list_id = random_id controllers = %w[modal array] controllers << 'required-checkbox' if metadata.required %div{ id: modal_id, data: { controller: controllers.join(' '), 'array-original-value': metadata.value.to_json, 'array-new-array-value': site_posts_new_related_post_path(site) } } %template{ data: { 'array-target': 'placeholder' } } .col.p-3{ 'aria-hidden': 'true' } %span.placeholder.w-100 .form-group = hidden_field_tag name, '' .d-flex.align-items-center.justify-content-between %div = label_tag id, post_label_t(attribute, post: post) = render 'posts/required_checkbox', required: metadata.required, name: name, initial: metadata.empty? = render 'bootstrap/btn', content: t('.edit'), action: 'modal#show' -# Mostramos la lista de valores actuales. Al aceptar el modal, se vacía el listado y se completa en base a renderizaciones con HTMX. Para poder hacer eso, tenemos que poder acceder a todos los items dentro del modal (como array.item) y enviar el valor al endpoint que devuelve uno por uno. Esto lo tenemos disponible en Stimulus, pero queremos usar HTMX o técnica similar para poder renderizar del lado del servidor. Para poder cancelar, mantenemos el estado original y desactivamos o activamos los ítemes según estén incluidos en esa lista o no. .row.no-gutters.placeholder-glow{ data: { 'array-target': 'current' } } -# @todo issue-7537 - metadata.value.each do |uuid| - if (indexed_post = site.indexed_posts.find_by(post_id: uuid)) = render 'posts/new_related_post', post: indexed_post, attribute: metadata.type = render 'bootstrap/modal', id: id, modal_content_attributes: { class: 'h-100' }, hide_actions: ['array#cancel'], keydown_actions: %w[keydown->array#cancelWithEscape] do - content_for :"#{id}_header" do .form-group.flex-grow-1.mb-0 = label_tag id, post_label_t(attribute, post: post) %input.form-control{ data: { 'array-target': 'search', action: 'input->array#search' }, type: 'search', placeholder: t('.filter') } - content_for :"#{id}_body" do .form-group.mb-0{ id: value_list_id } - metadata.values.each_pair do |value, uuid| = render 'targets/array/item', value: uuid, 'send-value': uuid, 'human-value': value, class: 'mb-2' do = render 'bootstrap/custom_checkbox', name: name, id: random_id, value: uuid, checked: metadata.value.include?(uuid), content: value, data: { action: 'required-checkbox#change', 'required-checkbox-target': 'checkbox' } -# Según la definición del campo, si hay un filtro, tenemos que poder elegir qué tipo de esquema queremos o si hay uno solo, siempre vamos a enviar ese. Si no hay ninguno, tendríamos que poder elegir entre todos los esquemas. - content_for :"#{id}_footer" do - layout = metadata.filter[:layout] - if layout.is_a?(String) %input{ type: 'hidden', name: 'layout', value: layout, form: post_form_id } = render 'bootstrap/btn', content: t('.add', layout: site.layouts[layout].humanized_name), form: post_form_id, type: 'submit', class: 'm-0 mr-1' - else - layouts = layout&.map { |x| site.layouts[x] } - layouts ||= site.layouts.values .input-group.w-auto.flex-grow-1.my-0 %select.form-control{ form: post_form_id, name: 'layout' } - layouts.each do |layout| %option{ value: layout.name }= layout.humanized_name .input-group-append = render 'bootstrap/btn', content: t('.add', layout: ''), form: post_form_id, type: 'submit', class: 'mb-0 mr-0' = render 'bootstrap/btn', content: t('.accept'), action: 'array#accept modal#hide', class: 'm-0 mr-1' = render 'bootstrap/btn', content: t('.cancel'), action: 'array#cancel modal#hide', class: 'm-0' -# Este segundo modal es el que carga los formularios de creación/modificación de artículos relacionados. Se envía a post_form para que sea externo al formulario actual. - content_for :post_form do %form{ id: post_form_id, 'hx-get': site_posts_form_path(site), 'hx-target': "##{post_form_loaded_id}" } %input{ type: 'hidden', name: 'show', value: post_modal_id } %input{ type: 'hidden', name: 'hide', value: modal_id } %input{ type: 'hidden', name: 'target', value: value_list_id } %input{ type: 'hidden', name: 'swap', value: 'beforeend' } %input{ type: 'hidden', name: 'base', value: id } %input{ type: 'hidden', name: 'name', value: name } %input{ type: 'hidden', name: 'form', value: form_id } %input{ type: 'hidden', name: 'attribute', value: metadata.type } -# @todo Forma genérica de arrastrar valores desde un formulario al siguiente - if metadata.inverse? %input{ type: 'hidden', name: 'inverse', value: metadata.inverse } %input{ type: 'hidden', name: metadata.inverse, value: post.uuid.value } %div{ id: post_modal_id, data: { controller: 'modal' } } = render 'bootstrap/modal', id: post_id, modal_content_attributes: { class: 'h-100' } do - content_for :"#{post_id}_body" do %div{ id: post_form_loaded_id } - content_for :"#{post_id}_footer" do = render 'bootstrap/btn', form: form_id, content: t('.save'), type: 'submit' -# @todo: Volver al otro modal = render 'bootstrap/btn', content: t('.cancel'), action: 'modal#hide'