Merge pull request #9445 from tchak/feat-add-champ-buttons
feat(type_de_champ): insert an add champ button after each type de champ
This commit is contained in:
commit
70b57257eb
8 changed files with 140 additions and 157 deletions
|
@ -13,13 +13,18 @@
|
|||
}
|
||||
|
||||
.type-de-champ {
|
||||
width: 100%;
|
||||
margin-bottom: $default-padding;
|
||||
overflow: hidden;
|
||||
|
||||
.type-de-champ-container {
|
||||
width: 100%;
|
||||
background-color: #FAFDFF;
|
||||
border: 1px solid $border-grey;
|
||||
border-radius: 5px;
|
||||
margin-bottom: $default-padding * 2;
|
||||
margin-bottom: $default-padding;
|
||||
box-shadow: 0px 2px 4px -4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.handle.icon {
|
||||
width: 32px;
|
||||
|
@ -71,6 +76,10 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
&.last .type-de-champ-add-button.root {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.head {
|
||||
background-color: #FAFDFF;
|
||||
|
||||
|
@ -91,10 +100,6 @@
|
|||
&.section {
|
||||
padding: $default-spacer $default-spacer 0;
|
||||
margin-bottom: 8px;
|
||||
|
||||
input {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&.hr {
|
||||
|
|
|
@ -2,13 +2,6 @@ class ApplicationComponent < ViewComponent::Base
|
|||
include ViewComponent::Translatable
|
||||
include FlipperHelper
|
||||
|
||||
# Takes a Hash of { class_name: boolean }.
|
||||
# Returns truthy class names in an array. Array can be passed as-it in rails helpers,
|
||||
# and is still manipulable if needed.
|
||||
def class_names(class_names)
|
||||
class_names.filter { _2 }.keys
|
||||
end
|
||||
|
||||
def current_user
|
||||
controller.current_user
|
||||
end
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
class TypesDeChampEditor::AddChampButtonComponent < ApplicationComponent
|
||||
def initialize(revision:, parent: nil, is_annotation: false)
|
||||
def initialize(revision:, parent: nil, is_annotation: false, after_stable_id: nil)
|
||||
@revision = revision
|
||||
@parent = parent
|
||||
@is_annotation = is_annotation
|
||||
@after_stable_id = after_stable_id
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -25,8 +26,7 @@ class TypesDeChampEditor::AddChampButtonComponent < ApplicationComponent
|
|||
|
||||
def button_options
|
||||
{
|
||||
class: "button",
|
||||
form: { class: @parent ? "add-to-block" : "add-to-root" },
|
||||
class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-line",
|
||||
method: :post,
|
||||
params: {
|
||||
type_de_champ: {
|
||||
|
@ -34,7 +34,7 @@ class TypesDeChampEditor::AddChampButtonComponent < ApplicationComponent
|
|||
type_champ: TypeDeChamp.type_champs.fetch(:text),
|
||||
private: annotations? ? true : nil,
|
||||
parent_stable_id: @parent&.stable_id,
|
||||
after_stable_id: ''
|
||||
after_stable_id: @after_stable_id
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent
|
|||
controller: 'type-de-champ-editor',
|
||||
type_de_champ_editor_move_url_value: move_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
|
||||
type_de_champ_editor_move_up_url_value: move_up_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
|
||||
type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
|
||||
type_de_champ_editor_type_de_champ_stable_id_value: type_de_champ.stable_id
|
||||
type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
%li.type-de-champ.flex.column.justify-start{ html_options }
|
||||
.type-de-champ-container
|
||||
.flex.justify-between.section.head.hr
|
||||
.handle.small.icon-only.icon.move-handle{ title: "Déplacer le champ vers le haut ou vers le bas" }
|
||||
|
||||
|
@ -9,7 +10,7 @@
|
|||
= link_to('le routage', admin_procedure_groupe_instructeurs_path(revision.procedure_id, anchor: 'routing-rules'))
|
||||
- else
|
||||
.flex.justify-start.delete
|
||||
= button_to type_de_champ_path, class: 'button small icon-only danger', method: :delete, form: { data: { turbo_confirm: 'Êtes vous sûr de vouloir supprimer ce champ ?' } } do
|
||||
= button_to type_de_champ_path, class: 'fr-btn fr-btn--sm fr-btn--secondary fr-icon-delete-line', title: "Supprimer le champ", method: :delete, form: { data: { turbo_confirm: 'Êtes vous sûr de vouloir supprimer ce champ ?' } } do
|
||||
.icon.delete
|
||||
%span.sr-only Supprimer
|
||||
|
||||
|
@ -30,7 +31,7 @@
|
|||
%span.sr-only Déplacer le champ vers le bas
|
||||
.cell.flex.justify-start.column.flex-grow
|
||||
= form.label :type_champ, "Type de champ", for: dom_id(type_de_champ, :type_champ)
|
||||
= form.select :type_champ, grouped_options_for_select(types_of_type_de_champ, type_de_champ.type_champ), {}, class: 'small-margin small inline width-100', id: dom_id(type_de_champ, :type_champ), disabled: coordinate.used_by_routing_rules?
|
||||
= form.select :type_champ, grouped_options_for_select(types_of_type_de_champ, type_de_champ.type_champ), {}, class: 'fr-select small-margin small inline width-100', id: dom_id(type_de_champ, :type_champ), disabled: coordinate.used_by_routing_rules?
|
||||
.flex.column.justify-start.flex-grow
|
||||
.cell
|
||||
.flex.align-center
|
||||
|
@ -39,7 +40,7 @@
|
|||
.cell.flex.align-center
|
||||
= form.check_box :mandatory, class: 'small-margin small', id: dom_id(type_de_champ, :mandatory)
|
||||
= form.label :mandatory, "Champ obligatoire", for: dom_id(type_de_champ, :mandatory)
|
||||
= form.text_field :libelle, class: 'small-margin small width-100', id: dom_id(type_de_champ, :libelle), data: input_autofocus
|
||||
= form.text_field :libelle, class: 'fr-input small-margin small width-100', id: dom_id(type_de_champ, :libelle), data: input_autofocus
|
||||
- if type_de_champ.header_section?
|
||||
%p
|
||||
%small Nous numérotons automatiquement les titres lorsqu’aucun de vos titres ne commence par un chiffre.
|
||||
|
@ -47,7 +48,7 @@
|
|||
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
|
||||
.cell.mt-1
|
||||
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
|
||||
= form.text_area :description, class: 'small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :description)
|
||||
= form.text_area :description, class: 'fr-input small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :description)
|
||||
- if type_de_champ.header_section?
|
||||
.cell.mt-1
|
||||
= render TypesDeChampEditor::HeaderSectionComponent.new(form: form, tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ))
|
||||
|
@ -59,7 +60,7 @@
|
|||
.flex.column.justify-start.width-33
|
||||
.cell
|
||||
= form.label :drop_down_list_value, "Options de la liste", for: dom_id(type_de_champ, :drop_down_list_value)
|
||||
= form.text_area :drop_down_list_value, class: 'small-margin small width-100', rows: 7, id: dom_id(type_de_champ, :drop_down_list_value)
|
||||
= form.text_area :drop_down_list_value, class: 'fr-input small-margin small width-100', rows: 7, id: dom_id(type_de_champ, :drop_down_list_value)
|
||||
- if type_de_champ.simple_drop_down_list?
|
||||
.cell
|
||||
= form.label :drop_down_other, for: dom_id(type_de_champ, :drop_down_other) do
|
||||
|
@ -70,10 +71,10 @@
|
|||
.flex.column.justify-start.flex-grow
|
||||
.cell
|
||||
= form.label :drop_down_secondary_libelle, "Libellé du champ secondaire", class: 'flex-grow', for: dom_id(type_de_champ, :drop_down_secondary_libelle)
|
||||
= form.text_field :drop_down_secondary_libelle, class: 'small-margin small width-100', id: dom_id(type_de_champ, :drop_down_secondary_libelle)
|
||||
= form.text_field :drop_down_secondary_libelle, class: 'fr-input small-margin small width-100', id: dom_id(type_de_champ, :drop_down_secondary_libelle)
|
||||
.cell.mt-1
|
||||
= form.label :drop_down_secondary_description, "Description du champ secondaire (optionnel)", for: dom_id(type_de_champ, :drop_down_secondary_description)
|
||||
= form.text_area :drop_down_secondary_description, class: 'small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :drop_down_secondary_description)
|
||||
= form.text_area :drop_down_secondary_description, class: 'fr-input small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :drop_down_secondary_description)
|
||||
- if type_de_champ.piece_justificative?
|
||||
.cell
|
||||
= form.label :piece_justificative_template, "Modèle", for: dom_id(type_de_champ, :piece_justificative_template)
|
||||
|
@ -100,18 +101,22 @@
|
|||
- if form.object.collapsible_explanation_enabled?
|
||||
= form.label :collapsible_explanation_text, for: dom_id(type_de_champ, :collapsible_explanation_text) do
|
||||
= "Texte à afficher quand l'utiliser a choisi de l'afficher"
|
||||
= form.text_area :collapsible_explanation_text, class: "small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_text)
|
||||
= form.text_area :collapsible_explanation_text, class: "fr-input small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_text)
|
||||
- if type_de_champ.textarea?
|
||||
.cell
|
||||
= form.label :character_limit, for: dom_id(type_de_champ, :character_limit) do
|
||||
Spécifier un nombre maximal conseillé de caractères :
|
||||
= form.select :character_limit, options_for_character_limit, id: dom_id(type_de_champ, :character_limit)
|
||||
= form.select :character_limit, options_for_character_limit, id: dom_id(type_de_champ, :character_limit), class: 'fr-select'
|
||||
|
||||
- if type_de_champ.block?
|
||||
.flex.justify-start.section.ml-1
|
||||
.editor-block.flex-grow.cell
|
||||
= render TypesDeChampEditor::BlockComponent.new(block: coordinate, coordinates: coordinate.revision_types_de_champ, upper_coordinates: @upper_coordinates)
|
||||
.type-de-champ-add-button{ id: dom_id(coordinate, :type_de_champ_add_button), class: class_names(hidden: !coordinate.empty?) }
|
||||
= render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate, is_annotation: coordinate.private?)
|
||||
|
||||
- if conditional_enabled?
|
||||
= render(TypesDeChampEditor::ConditionsComponent.new(tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ), procedure_id: procedure.id))
|
||||
|
||||
.type-de-champ-add-button{ class: class_names(root: !coordinate.child?) }
|
||||
= render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate&.parent, is_annotation: coordinate.private?, after_stable_id: type_de_champ.stable_id)
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
isTextInputElement,
|
||||
getConfig
|
||||
} from '@utils';
|
||||
import { useIntersection } from 'stimulus-use';
|
||||
import { AutoUpload } from '../shared/activestorage/auto-upload';
|
||||
import { ApplicationController } from './application_controller';
|
||||
|
||||
|
@ -28,7 +27,6 @@ export class TypeDeChampEditorController extends ApplicationController {
|
|||
declare readonly moveUrlValue: string;
|
||||
declare readonly moveUpUrlValue: string;
|
||||
declare readonly moveDownUrlValue: string;
|
||||
declare readonly typeDeChampStableIdValue: string;
|
||||
declare readonly isVisible: boolean;
|
||||
|
||||
#latestPromise = Promise.resolve();
|
||||
|
@ -36,8 +34,6 @@ export class TypeDeChampEditorController extends ApplicationController {
|
|||
#inFlightForms: Map<HTMLFormElement, AbortController> = new Map();
|
||||
|
||||
connect() {
|
||||
useIntersection(this, { threshold: 0.6 });
|
||||
|
||||
this.#latestPromise = Promise.resolve();
|
||||
this.on('change', (event) => this.onChange(event));
|
||||
this.on('input', (event) => this.onInput(event));
|
||||
|
@ -62,10 +58,6 @@ export class TypeDeChampEditorController extends ApplicationController {
|
|||
this.requestSubmitForm(form);
|
||||
}
|
||||
|
||||
appear() {
|
||||
this.updateAfterId();
|
||||
}
|
||||
|
||||
private onChange(event: Event) {
|
||||
const target = event.target as HTMLElement & { form?: HTMLFormElement };
|
||||
|
||||
|
@ -144,27 +136,7 @@ export class TypeDeChampEditorController extends ApplicationController {
|
|||
this.#inFlightForms.set(form, controller);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private updateAfterId() {
|
||||
const parent = this.element.closest<HTMLElement>(
|
||||
'.editor-block, .editor-root'
|
||||
);
|
||||
if (parent) {
|
||||
const selector = parent.classList.contains('editor-block')
|
||||
? '.add-to-block'
|
||||
: '.add-to-root';
|
||||
const input = parent.querySelector<HTMLInputElement>(
|
||||
`${selector} ${AFTER_STABLE_ID_INPUT_SELECTOR}`
|
||||
);
|
||||
if (input) {
|
||||
input.value = this.typeDeChampStableIdValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AFTER_STABLE_ID_INPUT_SELECTOR =
|
||||
'input[name="type_de_champ[after_stable_id]"]';
|
||||
|
||||
function createForm(action: string, method: string) {
|
||||
const form = document.createElement('form');
|
||||
|
|
|
@ -25,6 +25,10 @@ class ProcedureRevisionTypeDeChamp < ApplicationRecord
|
|||
siblings.last == self
|
||||
end
|
||||
|
||||
def empty?
|
||||
revision_types_de_champ.empty?
|
||||
end
|
||||
|
||||
def siblings
|
||||
if parent_id.present?
|
||||
revision.revision_types_de_champ.where(parent_id: parent_id).ordered
|
||||
|
|
|
@ -26,3 +26,8 @@
|
|||
- render TypesDeChampEditor::EstimatedFillDurationComponent.new(revision: @coordinate.revision, is_annotation: @coordinate.private?)
|
||||
|
||||
= turbo_stream.dispatch 'sortable:sort'
|
||||
|
||||
- if @created&.coordinate&.child?
|
||||
= turbo_stream.hide dom_id(@created.coordinate.parent, :type_de_champ_add_button)
|
||||
- elsif @destroyed&.child? && @destroyed.parent.empty?
|
||||
= turbo_stream.show dom_id(@destroyed.parent, :type_de_champ_add_button)
|
||||
|
|
Loading…
Reference in a new issue