chore(turbo): morph all the things
This commit is contained in:
parent
af6d167516
commit
5b9e534b3f
21 changed files with 81 additions and 106 deletions
19
Gemfile.lock
19
Gemfile.lock
|
@ -167,7 +167,7 @@ GEM
|
|||
coderay (1.1.3)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
concurrent-ruby (1.1.10)
|
||||
concurrent-ruby (1.2.0)
|
||||
content_disposition (1.0.0)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
|
@ -398,8 +398,11 @@ GEM
|
|||
loofah (2.19.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
mail (2.8.0.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
mailjet (1.6.0)
|
||||
activesupport (>= 3.1.0)
|
||||
rack (>= 1.4.0)
|
||||
|
@ -536,7 +539,7 @@ GEM
|
|||
activesupport (>= 4.2)
|
||||
choice (~> 0.2.0)
|
||||
ruby-graphviz (~> 1.2)
|
||||
rails-html-sanitizer (1.4.4)
|
||||
rails-html-sanitizer (1.5.0)
|
||||
loofah (~> 2.19, >= 2.19.1)
|
||||
rails-i18n (7.0.3)
|
||||
i18n (>= 0.7, < 2)
|
||||
|
@ -695,9 +698,9 @@ GEM
|
|||
spring (2.1.1)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
sprockets (4.1.1)
|
||||
sprockets (4.2.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
rack (>= 2.2.4, < 4)
|
||||
sprockets-rails (3.4.2)
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
|
@ -720,8 +723,10 @@ GEM
|
|||
timecop (0.9.4)
|
||||
timeout (0.1.1)
|
||||
ttfunk (1.7.0)
|
||||
turbo-rails (0.8.3)
|
||||
rails (>= 6.0.0)
|
||||
turbo-rails (1.3.2)
|
||||
actionpack (>= 6.0.0)
|
||||
activejob (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.5)
|
||||
|
|
|
@ -7,68 +7,48 @@ module TurboStreamHelper
|
|||
include ActionView::Helpers::TagHelper
|
||||
|
||||
def show(target, delay: nil)
|
||||
turbo_stream_simple_action_tag :show, target: target, delay: delay
|
||||
turbo_stream_action_tag :show, target:, delay:
|
||||
end
|
||||
|
||||
def show_all(targets, delay: nil)
|
||||
turbo_stream_simple_action_tag :show, targets: targets, delay: delay
|
||||
turbo_stream_action_tag :show, targets:, delay:
|
||||
end
|
||||
|
||||
def hide(target, delay: nil)
|
||||
turbo_stream_simple_action_tag :hide, target: target, delay: delay
|
||||
turbo_stream_action_tag :hide, target:, delay:
|
||||
end
|
||||
|
||||
def hide_all(targets, delay: nil)
|
||||
turbo_stream_simple_action_tag :hide, targets: targets, delay: delay
|
||||
turbo_stream_action_tag :hide, targets:, delay:
|
||||
end
|
||||
|
||||
def focus(target)
|
||||
turbo_stream_simple_action_tag :focus, target: target
|
||||
def focus(target, delay: nil)
|
||||
turbo_stream_action_tag :focus, target:, delay:
|
||||
end
|
||||
|
||||
def focus_all(targets)
|
||||
turbo_stream_simple_action_tag :focus, targets: targets
|
||||
def focus_all(targets, delay: nil)
|
||||
turbo_stream_action_tag :focus, targets:, delay:
|
||||
end
|
||||
|
||||
def enable(target)
|
||||
turbo_stream_simple_action_tag :enable, target: target
|
||||
def enable(target, delay: nil)
|
||||
turbo_stream_action_tag :enable, target:, delay:
|
||||
end
|
||||
|
||||
def enable_all(targets)
|
||||
turbo_stream_simple_action_tag :enable, targets: targets
|
||||
def enable_all(targets, delay: nil)
|
||||
turbo_stream_action_tag :enable, targets:, delay:
|
||||
end
|
||||
|
||||
def disable(target)
|
||||
turbo_stream_simple_action_tag :disable, target: target
|
||||
def disable(target, delay: nil)
|
||||
turbo_stream_action_tag :disable, target:, delay:
|
||||
end
|
||||
|
||||
def disable_all(targets)
|
||||
turbo_stream_simple_action_tag :disable, targets: targets
|
||||
end
|
||||
|
||||
def morph(target, content = nil, **rendering, &block)
|
||||
action :morph, target, content, **rendering, &block
|
||||
end
|
||||
|
||||
def morph_all(targets, content = nil, **rendering, &block)
|
||||
action_all :morph, targets, content, **rendering, &block
|
||||
def disable_all(targets, delay: nil)
|
||||
turbo_stream_action_tag :disable, targets:, delay:
|
||||
end
|
||||
|
||||
def dispatch(type, detail = nil)
|
||||
content = detail.present? ? tag.script(cdata_section(detail.to_json), type: 'application/json') : nil
|
||||
action_all :append, 'head', tag.dispatch_event(content, type:)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def turbo_stream_simple_action_tag(action, target: nil, targets: nil, **attributes)
|
||||
if (target = convert_to_turbo_stream_dom_id(target))
|
||||
tag.turbo_stream('', **attributes.merge(action: action, target: target))
|
||||
elsif (targets = convert_to_turbo_stream_dom_id(targets, include_selector: true))
|
||||
tag.turbo_stream('', **attributes.merge(action: action, targets: targets))
|
||||
else
|
||||
tag.turbo_stream('', **attributes.merge(action: action))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@ export class MenuButtonController extends ApplicationController {
|
|||
declare readonly buttonTarget: HTMLButtonElement;
|
||||
declare readonly menuTarget: HTMLElement;
|
||||
|
||||
#isOpen = false;
|
||||
#teardown?: () => void;
|
||||
|
||||
connect() {
|
||||
|
@ -17,6 +16,10 @@ export class MenuButtonController extends ApplicationController {
|
|||
this.#teardown?.();
|
||||
}
|
||||
|
||||
private get isOpen() {
|
||||
return (this.element as HTMLElement).classList.contains('open');
|
||||
}
|
||||
|
||||
private get isMenu() {
|
||||
return !(this.element as HTMLElement).dataset.popover;
|
||||
}
|
||||
|
@ -57,7 +60,7 @@ export class MenuButtonController extends ApplicationController {
|
|||
if (this.buttonTarget == target || this.buttonTarget.contains(target)) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.#isOpen) {
|
||||
if (this.isOpen) {
|
||||
this.close();
|
||||
} else {
|
||||
this.open();
|
||||
|
@ -98,7 +101,6 @@ export class MenuButtonController extends ApplicationController {
|
|||
document.body.addEventListener('click', onClickBody);
|
||||
});
|
||||
|
||||
this.#isOpen = true;
|
||||
this.#teardown = () =>
|
||||
document.body.removeEventListener('click', onClickBody);
|
||||
}
|
||||
|
@ -108,7 +110,6 @@ export class MenuButtonController extends ApplicationController {
|
|||
this.menuTarget.parentElement?.classList.remove('open');
|
||||
this.#teardown?.();
|
||||
this.setFocusToMenuitem(null);
|
||||
this.#isOpen = false;
|
||||
}
|
||||
|
||||
private isClickOutside(target: HTMLElement) {
|
||||
|
@ -116,7 +117,7 @@ export class MenuButtonController extends ApplicationController {
|
|||
target.isConnected &&
|
||||
!this.element.contains(target) &&
|
||||
!target.closest('reach-portal') &&
|
||||
this.#isOpen
|
||||
this.isOpen
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ export class TurboController extends ApplicationController {
|
|||
connect() {
|
||||
this.#actions = new Actions({
|
||||
element: document.documentElement,
|
||||
schema: { forceAttribute: 'data-turbo-force', hiddenClassName: 'hidden' }
|
||||
schema: { forceAttribute: 'data-turbo-force', hiddenClassName: 'hidden' },
|
||||
debug: false
|
||||
});
|
||||
|
||||
// actions#observe() is an interface over specialized mutation observers.
|
||||
|
@ -50,31 +51,11 @@ export class TurboController extends ApplicationController {
|
|||
|
||||
// see: https://turbo.hotwired.dev/handbook/streams#custom-actions
|
||||
this.onGlobal('turbo:before-stream-render', (event: StreamRenderEvent) => {
|
||||
const fallbackToDefaultActions = event.detail.render;
|
||||
event.detail.render = (streamElement: StreamElement) =>
|
||||
this.renderStreamElement(streamElement, fallbackToDefaultActions);
|
||||
this.actions.applyActions([parseTurboStream(streamElement)]);
|
||||
});
|
||||
}
|
||||
|
||||
private renderStreamElement(
|
||||
streamElement: StreamElement,
|
||||
fallbackRender: (streamElement: StreamElement) => void
|
||||
) {
|
||||
switch (streamElement.action) {
|
||||
// keep turbo default behavior to avoid risks going all in on coldwire
|
||||
case 'replace':
|
||||
case 'update':
|
||||
fallbackRender(streamElement);
|
||||
break;
|
||||
case 'morph':
|
||||
streamElement.setAttribute('action', 'replace');
|
||||
this.actions.applyActions([parseTurboStream(streamElement)]);
|
||||
break;
|
||||
default:
|
||||
this.actions.applyActions([parseTurboStream(streamElement)]);
|
||||
}
|
||||
}
|
||||
|
||||
private startSpinner() {
|
||||
this.#submitting = true;
|
||||
this.actions.show({ targets: this.spinnerTargets });
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
- rendered = render @condition_component
|
||||
|
||||
- if rendered.present?
|
||||
= turbo_stream.morph dom_id(@tdc.stable_self, :conditions) do
|
||||
= turbo_stream.replace dom_id(@tdc.stable_self, :conditions) do
|
||||
- rendered
|
||||
- else
|
||||
= turbo_stream.remove dom_id(@tdc.stable_self, :conditions)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
= form_for procedure.administrateurs.new(user: User.new),
|
||||
url: { controller: 'procedure_administrateurs' },
|
||||
html: { class: 'form', id: "new_administrateur" },
|
||||
data: { turbo: true } do |f|
|
||||
data: { turbo: true, turbo_force: true } do |f|
|
||||
= f.label :email do
|
||||
Ajouter un administrateur
|
||||
%p.notice Renseignez l’email d’un administrateur déjà enregistré sur #{APPLICATION_NAME} pour lui permettre de modifier « #{procedure.libelle} ».
|
||||
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true, disabled: disabled_as_super_admin
|
||||
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', autofocus: true, required: true, disabled: disabled_as_super_admin
|
||||
= f.submit 'Ajouter comme administrateur', class: 'button primary send', disabled: disabled_as_super_admin
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
- if @administrateur.present?
|
||||
= turbo_stream.update 'administrateurs',
|
||||
render(Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure))
|
||||
= turbo_stream.update 'administrateurs' do
|
||||
= render Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure)
|
||||
= turbo_stream.replace "new_administrateur", partial: 'add_admin_form', locals: { procedure: @procedure, disabled_as_super_admin: administrateur_as_manager? }
|
||||
= turbo_stream.focus 'administrateur_email'
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
= turbo_stream.update 'administrateurs',
|
||||
render(Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure))
|
||||
= turbo_stream.update 'administrateurs' do
|
||||
= render Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure)
|
||||
- if @procedure.administrateurs.one?
|
||||
= turbo_stream.focus 'administrateur_email'
|
||||
- else
|
||||
= turbo_stream.focus_all '#administrateurs tr:first-child input[type="submit"]'
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
- render @created
|
||||
|
||||
- @morphed&.each do |champ_component|
|
||||
= turbo_stream.morph dom_id(champ_component.coordinate, :type_de_champ_editor) do
|
||||
= turbo_stream.replace dom_id(champ_component.coordinate, :type_de_champ_editor) do
|
||||
- render champ_component
|
||||
|
||||
- if @coordinate.present?
|
||||
= turbo_stream.morph dom_id(@coordinate.revision, :estimated_fill_duration) do
|
||||
= turbo_stream.replace dom_id(@coordinate.revision, :estimated_fill_duration) do
|
||||
- render TypesDeChampEditor::EstimatedFillDurationComponent.new(revision: @coordinate.revision, is_annotation: @coordinate.private?)
|
||||
|
||||
= turbo_stream.dispatch 'sortable:sort'
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
= turbo_stream.morph dom_id(current_administrateur, :profil_api_token) do
|
||||
= turbo_stream.replace dom_id(current_administrateur, :profil_api_token) do
|
||||
= render Profile::APITokenCardComponent.new created_api_token: @api_token, created_packed_token: @packed_token
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
= fields_for @champ.input_name, @champ do |form|
|
||||
= turbo_stream.morph @champ.input_group_id do
|
||||
= turbo_stream.replace @champ.input_group_id do
|
||||
= render EditableChamp::EditableChampComponent.new champ: @champ, form: form
|
||||
|
||||
- @champ.piece_justificative_file.attachments.each do |attachment|
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
%br
|
||||
= t('.fill_in_password')
|
||||
|
||||
= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do
|
||||
= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true, turbo_force: true }, class: 'mt-2 form fconnect-form' do
|
||||
= hidden_field_tag :merge_token, merge_token
|
||||
= hidden_field_tag :email, email
|
||||
= label_tag :password, t('views.registrations.new.password_label', min_length: 8)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.dropdown{ data: { controller: 'menu-button', popover: 'true' } }
|
||||
.dropdown{ data: { controller: 'menu-button', popover: 'true', turbo_force: true } }
|
||||
-# Dropdown button title
|
||||
%button.fr-btn.dropdown-button{ class: button_or_label_class(dossier), data: { menu_button_target: 'button' } }
|
||||
= dossier_display_state dossier
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
= turbo_stream.morph 'filter-component' do
|
||||
= turbo_stream.replace 'filter-component' do
|
||||
= render Dossiers::FilterComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation, statut: @statut, field_id: @field)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
%p= t('views.invites.form.invite_to_edit_line1')
|
||||
%p= t('views.invites.form.invite_to_edit_line2')
|
||||
|
||||
= form_tag dossier_invites_path(dossier), data: { turbo: true }, method: :post, class: 'form' do
|
||||
= form_tag dossier_invites_path(dossier), data: { turbo: true, turbo_force: true }, method: :post, class: 'form' do
|
||||
.row
|
||||
.col
|
||||
%span
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
= turbo_stream.replace_all '.invite-user-action', partial: 'invites/dropdown', locals: { dossier: @dossier }
|
||||
= turbo_stream.focus_all '.invite-user-action > button'
|
||||
= turbo_stream.focus 'invite_email'
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
- if @dossier.present?
|
||||
= turbo_stream.replace_all '.invite-user-action', partial: 'invites/dropdown', locals: { dossier: @dossier }
|
||||
= turbo_stream.focus_all '.invite-user-action > button'
|
||||
- if @dossier.invites.empty?
|
||||
= turbo_stream.focus 'invite_email'
|
||||
- else
|
||||
= turbo_stream.focus_all '#invites-form ul a:first-child'
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
= turbo_stream.morph "dolist-report-form" do
|
||||
= turbo_stream.replace "dolist-report-form" do
|
||||
= @message
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@coldwired/actions": "^0.8.0",
|
||||
"@coldwired/turbo-stream": "^0.8.0",
|
||||
"@coldwired/actions": "^0.8.1",
|
||||
"@coldwired/turbo-stream": "^0.8.1",
|
||||
"@gouvfr/dsfr": "^1.7.2",
|
||||
"@graphiql/plugin-explorer": "^0.1.11",
|
||||
"@graphiql/toolkit": "^0.8.0",
|
||||
|
|
|
@ -32,7 +32,7 @@ describe Champs::PieceJustificativeController, type: :controller do
|
|||
it 'renders the attachment template as Javascript' do
|
||||
subject
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to include("<turbo-stream action=\"morph\" target=\"#{champ.input_group_id}\">")
|
||||
expect(response.body).to include("<turbo-stream action=\"replace\" target=\"#{champ.input_group_id}\">")
|
||||
end
|
||||
|
||||
it 'updates dossier.last_champ_updated_at' do
|
||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -511,28 +511,28 @@
|
|||
"@babel/helper-validator-identifier" "^7.19.1"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@coldwired/actions@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@coldwired/actions/-/actions-0.8.0.tgz#a5e92c0badbc37ffa7c9e70d2aa7982739fbba5f"
|
||||
integrity sha512-IbqyC2ToDv1JWBVkk33ywDrftD34r3WBLokub7Tlon8xS6FQQVejwmgqIqiQ3iLE0jiXOqKz2an3Y1e69mJ55A==
|
||||
"@coldwired/actions@^0.8.1":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@coldwired/actions/-/actions-0.8.1.tgz#c309a2a38f5a0af11c03c4cf8e16c6496c7f9894"
|
||||
integrity sha512-d+rgz+FHeKRcqPS8c8mPRuwYsbfDwIExJIOdpexAmhhW67c1V3xo4+oAwNSE395/qU5/JXmOnHPeqA6t6Fx9ew==
|
||||
dependencies:
|
||||
"@coldwired/utils" "^0.4.1"
|
||||
"@coldwired/utils" "^0.8.1"
|
||||
morphdom "^2.6.1"
|
||||
tiny-invariant "^1.3.1"
|
||||
|
||||
"@coldwired/turbo-stream@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@coldwired/turbo-stream/-/turbo-stream-0.8.0.tgz#596202ecbca88f9b264d5055bedcb94298db626b"
|
||||
integrity sha512-/ZKdAcgDkcH+YkW2RyxXGMM6OdpKp4nVpFGTYnor+4mR/jjEdnTRw+3wnZJQfgPzLMG39SXEmY4mp9xQM/qA3Q==
|
||||
"@coldwired/turbo-stream@^0.8.1":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@coldwired/turbo-stream/-/turbo-stream-0.8.1.tgz#3f2f52b8cd59906c2387b78949c66fad79cb0eae"
|
||||
integrity sha512-rxDzEObIZ4MQQzM2joXV2yjOLcveHBDkghDu6EiiCAMl95wV/O6dMTM1KW3+l6tjd0a5hTTKrhoNJKuWLTV5GA==
|
||||
dependencies:
|
||||
"@coldwired/actions" "^0.8.0"
|
||||
"@coldwired/utils" "^0.4.1"
|
||||
"@coldwired/actions" "^0.8.1"
|
||||
"@coldwired/utils" "^0.8.1"
|
||||
tiny-invariant "^1.3.1"
|
||||
|
||||
"@coldwired/utils@^0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@coldwired/utils/-/utils-0.4.1.tgz#50a615c81948754f4c7f118df05a44e078f28568"
|
||||
integrity sha512-a673URA77AUBm8u5bWGtEmMi9oMv8HBidPT1GtRsaYma1W23Hd2Emn+oD6vUyGLfL+hJGT91w+Y2Y3x9BgLqwQ==
|
||||
"@coldwired/utils@^0.8.1":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@coldwired/utils/-/utils-0.8.1.tgz#bd20e69d12d938b43ac03d4020416188e8c8107b"
|
||||
integrity sha512-pT3vLSNFKXAZUi3IgomLXNiDB/l4Qer3/uHP6I6XzLY/q7xIBarhSmcuYUsgxs4jlH8sIsgAjU+PP0qGXc3QAg==
|
||||
|
||||
"@esbuild/android-arm64@0.16.14":
|
||||
version "0.16.14"
|
||||
|
|
Loading…
Reference in a new issue