diff --git a/admin/app/components/solidus_admin/layout/confirm/component.html.erb b/admin/app/components/solidus_admin/layout/confirm/component.html.erb
new file mode 100644
index 00000000000..68ebe250857
--- /dev/null
+++ b/admin/app/components/solidus_admin/layout/confirm/component.html.erb
@@ -0,0 +1,12 @@
+<%= render component("ui/modal").new(
+ title: t(".title"),
+ open: false,
+ id: "confirm"
+) do |modal| %>
+ <% modal.with_actions do %>
+
+ <%= render component("ui/button").new(text: t(".confirm"), id: "confirm-accept", scheme: :danger) %>
+ <% end %>
+<% end %>
diff --git a/admin/app/components/solidus_admin/layout/confirm/component.rb b/admin/app/components/solidus_admin/layout/confirm/component.rb
new file mode 100644
index 00000000000..ea5a692da75
--- /dev/null
+++ b/admin/app/components/solidus_admin/layout/confirm/component.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# Component wrapper for confirmation dialog to use with rolemodel/turbo-confirm.
+#
+# The modal is rendered in the layout initially hidden.
+# To have it open to confirm user's action, place the "data-turbo-confirm" on the submitter (or any other element that
+# supports "data-turbo-confirm" attribute: form, link with "data-turbo-method") with the text you want to have in the
+# modal title:
+#
+#
+#
+#
+# You can add more details in the body of the modal using "data-confirm-details" attribute:
+#
+#
+# To customize "Confirm" button text use "data-confirm-button" attribute:
+#
+#
+# For more details see https://github.com/RoleModel/turbo-confirm.
+
+class SolidusAdmin::Layout::Confirm::Component < SolidusAdmin::BaseComponent
+end
diff --git a/admin/app/components/solidus_admin/layout/confirm/component.yml b/admin/app/components/solidus_admin/layout/confirm/component.yml
new file mode 100644
index 00000000000..88d249a4706
--- /dev/null
+++ b/admin/app/components/solidus_admin/layout/confirm/component.yml
@@ -0,0 +1,4 @@
+en:
+ cancel: "Cancel"
+ confirm: "Confirm"
+ title: "Are you sure?"
diff --git a/admin/app/components/solidus_admin/orders/cart/component.html.erb b/admin/app/components/solidus_admin/orders/cart/component.html.erb
index cc4eef3281a..d34a6f3e9bb 100644
--- a/admin/app/components/solidus_admin/orders/cart/component.html.erb
+++ b/admin/app/components/solidus_admin/orders/cart/component.html.erb
@@ -62,8 +62,7 @@
size: :s,
title: t("spree.delete"),
icon: 'close-line',
- "data-controller": "confirm",
- "data-confirm-text-value": t("spree.are_you_sure"),
+ "data-turbo-confirm": t("spree.are_you_sure")
) %>
<% end %>
diff --git a/admin/app/components/solidus_admin/products/show/component.html.erb b/admin/app/components/solidus_admin/products/show/component.html.erb
index f8095763f04..2614fba5b20 100644
--- a/admin/app/components/solidus_admin/products/show/component.html.erb
+++ b/admin/app/components/solidus_admin/products/show/component.html.erb
@@ -143,8 +143,7 @@
tag: :button,
text: t(".delete"),
scheme: :danger,
- "data-action": "click->#{stimulus_id}#confirmDelete",
- "data-#{stimulus_id}-message-param": t(".delete_confirmation"),
+ "data-turbo-confirm": t(".delete_confirmation")
) %>
<% end %>
<% end %>
diff --git a/admin/app/components/solidus_admin/products/show/component.js b/admin/app/components/solidus_admin/products/show/component.js
deleted file mode 100644
index fd490e3c1b7..00000000000
--- a/admin/app/components/solidus_admin/products/show/component.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Controller } from "@hotwired/stimulus"
-
-export default class extends Controller {
- confirmDelete(event) {
- if (!confirm(event.params.message)) {
- event.preventDefault()
- }
- }
-}
diff --git a/admin/app/components/solidus_admin/ui/modal/component.html.erb b/admin/app/components/solidus_admin/ui/modal/component.html.erb
index bf301c6cf51..4e6480dc5c9 100644
--- a/admin/app/components/solidus_admin/ui/modal/component.html.erb
+++ b/admin/app/components/solidus_admin/ui/modal/component.html.erb
@@ -11,7 +11,7 @@
-
+
<%= @title %>
-
- <%= content %>
-
+
<%= content %>
<% if actions? %>
diff --git a/admin/app/components/solidus_admin/users/edit/api_access/component.js b/admin/app/components/solidus_admin/users/edit/api_access/component.js
deleted file mode 100644
index 910294c5462..00000000000
--- a/admin/app/components/solidus_admin/users/edit/api_access/component.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Controller } from "@hotwired/stimulus"
-
-export default class extends Controller {
- confirm(event) {
- if (!confirm(event.params.message)) {
- event.preventDefault()
- }
- }
-}
diff --git a/admin/app/components/solidus_admin/users/edit/api_access/component.yml b/admin/app/components/solidus_admin/users/edit/api_access/component.yml
index e865ba47c30..820fa4832c2 100644
--- a/admin/app/components/solidus_admin/users/edit/api_access/component.yml
+++ b/admin/app/components/solidus_admin/users/edit/api_access/component.yml
@@ -6,5 +6,11 @@ en:
clear_key: Clear key
regenerate_key: Regenerate key
hidden: Hidden
- confirm_clear_key: Are you sure you want to clear this user's API key? It will invalidate the existing key.
- confirm_regenerate_key: Are you sure you want to regenerate this user's API key? It will invalidate the existing key.
+ confirm:
+ title: Are you sure?
+ clear:
+ details: Are you sure you want to clear this user's API key? It will invalidate the existing key.
+ button: Clear
+ regenerate:
+ details: Are you sure you want to regenerate this user's API key? It will invalidate the existing key.
+ button: Regenerate
diff --git a/admin/app/javascript/solidus_admin/application.js b/admin/app/javascript/solidus_admin/application.js
index 7e7a85ebe4a..6e5c0a253f5 100644
--- a/admin/app/javascript/solidus_admin/application.js
+++ b/admin/app/javascript/solidus_admin/application.js
@@ -2,3 +2,4 @@ import "@hotwired/turbo-rails"
import "vendor/custom_elements"
import "solidus_admin/controllers"
import "solidus_admin/web_components/solidus_select"
+import "solidus_admin/turbo-confirm"
diff --git a/admin/app/javascript/solidus_admin/controllers/confirm_controller.js b/admin/app/javascript/solidus_admin/controllers/confirm_controller.js
deleted file mode 100644
index 60e5f3c0a6f..00000000000
--- a/admin/app/javascript/solidus_admin/controllers/confirm_controller.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Controller } from "@hotwired/stimulus"
-
-export default class extends Controller {
- static values = {"text": String}
-
- connect() {
- this.element.addEventListener("click", this)
- this.element.addEventListener("submit", this)
- }
-
- disconnect() {
- this.element.removeEventListener("click", this)
- this.element.removeEventListener("submit", this)
- }
-
- handleEvent(event) {
- if (!confirm(this.textValue)) {
- event.preventDefault()
- }
- }
-}
diff --git a/admin/app/javascript/solidus_admin/rolemodel/turbo-confirm.js b/admin/app/javascript/solidus_admin/rolemodel/turbo-confirm.js
new file mode 100644
index 00000000000..fbbabad5c49
--- /dev/null
+++ b/admin/app/javascript/solidus_admin/rolemodel/turbo-confirm.js
@@ -0,0 +1,16 @@
+import TC from "@rolemodel/turbo-confirm"
+
+TC.start({
+ animationDuration: 0,
+ messageSlotSelector: ".modal-title",
+ contentSlots: {
+ body: {
+ contentAttribute: "confirm-details",
+ slotSelector: ".modal-body"
+ },
+ acceptText: {
+ contentAttribute: "confirm-button",
+ slotSelector: "#confirm-accept"
+ }
+ }
+});
diff --git a/admin/app/javascript/vendor/@rolemodel--turbo-confirm.js b/admin/app/javascript/vendor/@rolemodel--turbo-confirm.js
new file mode 100644
index 00000000000..b040dde8c5d
--- /dev/null
+++ b/admin/app/javascript/vendor/@rolemodel--turbo-confirm.js
@@ -0,0 +1,18 @@
+// @rolemodel/turbo-confirm@2.1.1 downloaded from https://ga.jspm.io/npm:@rolemodel/turbo-confirm@2.1.1/src/index.js
+
+const dispatch=(t,e=document,{bubbles:o=true,cancelable:n=true,prefix:i="rms",detail:r}={})=>{const s=new CustomEvent(`${i}:${t}`,{bubbles:o,cancelable:n,detail:r});e.dispatchEvent(s);return!s.defaultPrevented};class TurboConfirmError extends Error{name="TurboConfirmError";static missingDialog(t,e){return new this(`No element matching dialogSelector: '${t}'`,{cause:e})}static noTurbo(){return new this('Turbo is not defined. Be sure to import "@hotwired/turbo-rails" before calling the `start()` function')}}class ConfirmationController{initialContent;#t;constructor(t){this.delegate=t;this.accept=this.accept.bind(this);this.deny=this.deny.bind(this)}showConfirm(t){this.#e();for(const[e,o]of Object.entries(t)){const t=this.element.querySelector(e);t&&o&&(t.innerHTML=o)}this.#o();this.delegate.showConfirm(this.element);return new Promise((t=>this.#t=t))}accept(){this.#t(true);this.#n()}deny(){this.#t(false);this.#n()}get acceptButtons(){return this.element.querySelectorAll(this.delegate.acceptSelector)}get denyButtons(){return this.element.querySelectorAll(this.delegate.denySelector)}get element(){return document.querySelector(this.delegate.dialogSelector)}#n(){this.#t=null;this.delegate.hideConfirm(this.element);this.#i();setTimeout(this.#r.bind(this),this.delegate.animationDuration)}#o(){this.acceptButtons.forEach((t=>t.addEventListener("click",this.accept)));this.denyButtons.forEach((t=>t.addEventListener("click",this.deny)));this.element.addEventListener("cancel",this.deny)}#i(){this.acceptButtons.forEach((t=>t.removeEventListener("click",this.accept)));this.denyButtons.forEach((t=>t.removeEventListener("click",this.deny)));this.element.removeEventListener("cancel",this.deny)}#e(){try{this.initialContent=this.element.innerHTML}catch(t){throw TurboConfirmError.missingDialog(this.delegate.dialogSelector,t)}}#r(){try{this.element.innerHTML=this.initialContent}catch{}}}class TurboConfirm{#s;#c={dialogSelector:"#confirm",activeClass:"modal--active",acceptSelector:"#confirm-accept",denySelector:".confirm-cancel",animationDuration:300,showConfirmCallback:t=>t.showModal&&t.showModal(),hideConfirmCallback:t=>t.close&&t.close(),messageSlotSelector:"#confirm-title",contentSlots:{body:{contentAttribute:"confirm-details",slotSelector:"#confirm-body"},acceptText:{contentAttribute:"confirm-button",slotSelector:"#confirm-accept"}}};constructor(t={}){for(const[e,o]of Object.entries(t))this.#c[e]=o;this.#s=new ConfirmationController(this)}
+/**
+ * Present a confirmation challenge to the user.
+ * @public
+ * @param {string} [message] - The main challenge message; Value of `data-turbo-confirm` attribute.
+ * @param {HTMLFormElement} [_formElement] - (ignored) `form` element that contains the submitter.
+ * @param {HTMLElement} [submitter] - button of input of type submit that triggered the form submission.
+ * @returns {Promise} - A promise that resolves to true if the user accepts the challenge or false if they deny it.
+ */confirm(t,e,o){const n=this.#l(o);const i=this.#a(t,n);return this.confirmWithContent(i)}
+/**
+ * Present a confirmation challenge to the user.
+ * @public
+ * @param {Object} contentMap - A map of CSS selectors to HTML content to be inserted into the dialog.
+ * @returns {Promise} - A promise that resolves to true if the user accepts the challenge or false if they deny it.
+ */confirmWithContent(t){return this.#s.showConfirm(t)}showConfirm(t){t.classList.add(this.#c.activeClass);typeof this.#c.showConfirmCallback==="function"&&this.#c.showConfirmCallback(t)}hideConfirm(t){t.classList.remove(this.#c.activeClass);typeof this.#c.hideConfirmCallback==="function"&&this.#c.hideConfirmCallback(t)}get dialogSelector(){return this.#c.dialogSelector}get acceptSelector(){return this.#c.acceptSelector}get denySelector(){return this.#c.denySelector}get animationDuration(){return this.#c.animationDuration}#a(t,e){const o={};t&&(o[this.#c.messageSlotSelector]=t);if(e)for(const t of Object.keys(this.#c.contentSlots))o[this.#h(t)]=this.#f(t,e);return o}#h(t){return this.#c.contentSlots[t].slotSelector}#f(t,e){return e.getAttribute(`data-${this.#c.contentSlots[t].contentAttribute}`)}#l(t){const e=t??document.activeElement;return e.closest("[data-turbo-confirm]")}}const start=t=>{if(!window.Turbo)throw TurboConfirmError.noTurbo();const e=new TurboConfirm(t);const confirmationHandler=async(t,o,n)=>{const i=await e.confirm(t,o,n);dispatch(i?"confirm-accept":"confirm-reject",n);return i};window.Turbo.config?window.Turbo.config.forms.confirm=confirmationHandler:window.Turbo.setConfirmMethod(confirmationHandler)};var t={start:start};export{TurboConfirm,t as default};
+
diff --git a/admin/app/views/layouts/solidus_admin/application.html.erb b/admin/app/views/layouts/solidus_admin/application.html.erb
index 6755eea3c1d..924d44cc321 100644
--- a/admin/app/views/layouts/solidus_admin/application.html.erb
+++ b/admin/app/views/layouts/solidus_admin/application.html.erb
@@ -36,5 +36,7 @@
<%= render component("ui/toast").new(text: message, scheme: key.to_sym == :error ? :error : :default) %>
<% end %>
+
+ <%= render component("layout/confirm").new %>