diff --git a/app/components/types_de_champ_editor/block_component/block_component.html.haml b/app/components/types_de_champ_editor/block_component/block_component.html.haml index 388cbf6ff..c2c8d9d7a 100644 --- a/app/components/types_de_champ_editor/block_component/block_component.html.haml +++ b/app/components/types_de_champ_editor/block_component/block_component.html.haml @@ -1,3 +1,6 @@ -%ul.types-de-champ-block{ id: block_id, data: sortable_options } +- c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @block, coordinates: @coordinates) +%ul.types-de-champ-block{ id: block_id, data: sortable_options.merge(controller: 'select-champ-position-template', 'select-champ-position-template-template-id-value': c.block_id ) } + %li.hidden= render c - @coordinates.each do |coordinate| - = render TypesDeChampEditor::ChampComponent.new(coordinate: coordinate, upper_coordinates: coordinate.upper_coordinates) + = render TypesDeChampEditor::ChampComponent.new(coordinate:, upper_coordinates: coordinate.upper_coordinates) + diff --git a/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml b/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml index d4db64799..6c2635d92 100644 --- a/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml +++ b/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml @@ -1,3 +1,3 @@ .fr-ml-3w.flex.select-position = label_tag :target_stable_id, "Déplacer le champ", for: describedby_id, class: 'flex align-center flex-no-shrink fr-mr-3w' - = select_tag :target_stable_id, options_for_select(options), id: describedby_id, class: 'fr-select', aria: { discribedby: describedby_id }, data: { action: 'change->type-de-champ-editor#onMoveAndMorphChange', selected: @coordinate.stable_id } \ No newline at end of file + = select_tag :target_stable_id, options_for_select(options), id: describedby_id, class: 'fr-select', aria: { discribedby: describedby_id }, data: { 'select-champ-position-template-target': 'select', action: 'change->type-de-champ-editor#onMoveAndMorphChange', selected: @coordinate.stable_id } \ No newline at end of file diff --git a/app/components/types_de_champ_editor/select_champ_template_position_component.rb b/app/components/types_de_champ_editor/select_champ_template_position_component.rb new file mode 100644 index 000000000..23d87e927 --- /dev/null +++ b/app/components/types_de_champ_editor/select_champ_template_position_component.rb @@ -0,0 +1,10 @@ +class TypesDeChampEditor::SelectChampTemplatePositionComponent < ApplicationComponent + def initialize(block:, coordinates:) + @block = block + @coordinates = coordinates + end + + def block_id + dom_id(@block, :types_de_champ_editor_select_champ_template) + end +end diff --git a/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml b/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml new file mode 100644 index 000000000..5615c0dfa --- /dev/null +++ b/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml @@ -0,0 +1,5 @@ +%div{ id: block_id, data: { 'select-champ-position-template-target': 'template', turbo_force: :server } } + %select + %option{ selected: :selected } Selectionner une option + - @coordinates.each do |coordinate| + %option{ value: coordinate.stable_id }= "#{coordinate.position} #{coordinate.libelle}" \ No newline at end of file diff --git a/app/javascript/controllers/select_champ_position_template_controller.ts b/app/javascript/controllers/select_champ_position_template_controller.ts new file mode 100644 index 000000000..73ca19d74 --- /dev/null +++ b/app/javascript/controllers/select_champ_position_template_controller.ts @@ -0,0 +1,74 @@ +import { ApplicationController } from './application_controller'; + +export class SelectChampPositionTemplateController extends ApplicationController { + static targets = ['select', 'template']; + static values = { + templateId: String + }; + // this element is updated via turbostream as the source of truth for all select + declare readonly templateIdValue: string; + + declare readonly selectTargets: HTMLSelectElement[]; + + selectTargetConnected(selectElement: HTMLSelectElement) { + selectElement.addEventListener('blur', this); + selectElement.addEventListener('focus', this); + selectElement.addEventListener('change', this); + } + + selectTargetDisconnected(selectElement: HTMLSelectElement) { + console.log('selectTargetDisconnected'); + selectElement.removeEventListener('blur', this); + selectElement.removeEventListener('focus', this); + selectElement.removeEventListener('change', this); + } + + handleEvent(event: Event) { + switch (event.type) { + case 'focus': + this.onFocus(event); + break; + case 'change': + this.onChange(event); + break; + } + } + + private onFocus(event: Event): void { + const focusedSelect = event.target as HTMLSelectElement; + const focusedSelectStableId = this.getStableIdForSelect(focusedSelect); + const template = this.element.querySelector( + `#${this.templateIdValue}` + ); + + if (template) { + const fragment = template.cloneNode(true) as HTMLSelectElement; + + const options = Array.from(fragment.querySelectorAll('option')) + .map((option) => { + if (option.value == focusedSelectStableId) { + option.setAttribute('selected', 'selected'); + } + + return option.outerHTML; + }) + .join(''); + focusedSelect.innerHTML = options; + } + } + + private onChange(event: Event): void { + const changedSelectTarget = event.target as HTMLSelectElement; + const stableIdDidChange = + changedSelectTarget.value != + this.getStableIdForSelect(changedSelectTarget); + if (!stableIdDidChange) { + // prevent to bulble up + event.stopImmediatePropagation(); + } + } + + private getStableIdForSelect(select: HTMLSelectElement): string | null { + return select.getAttribute('data-selected'); + } +} diff --git a/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml b/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml index 85407ef6d..19dd15f0d 100644 --- a/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml +++ b/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml @@ -1,3 +1,11 @@ +- if @coordinate.present? + - if @coordinate.parent.present? + - c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @coordinate.parent, coordinates: @coordinate.parent.revision_types_de_champ) + - else + - c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @coordinate.revision, coordinates: @coordinate.private? ? @coordinate.revision.revision_types_de_champ_private : @coordinate.revision.revision_types_de_champ_public) + + = turbo_stream.replace(c.block_id) do + = render c = turbo_stream.replace 'breadcrumbs' , render(partial: 'administrateurs/breadcrumbs', locals: { steps: [['Démarches', admin_procedures_path], [@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],