diff --git a/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee b/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee index 3d9fbfe62a..3a5d569279 100644 --- a/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee @@ -1,5 +1,5 @@ # Dialog windows -# +# @deprecated use dialog.js instead class window.Alchemy.Dialog DEFAULTS: @@ -229,6 +229,11 @@ window.Alchemy.closeCurrentDialog = (callback) -> dialog.options.closed = callback dialog.close() + # this is a intermediate solution to use the same method also with the new dialog + new_dialog = document.querySelector("sl-dialog") + if new_dialog + new_dialog.connectedDialogInstance.close() + # Utility function to open a new Dialog window.Alchemy.openDialog = (url, options) -> if !url diff --git a/app/assets/stylesheets/alchemy/shoelace.scss b/app/assets/stylesheets/alchemy/shoelace.scss index 00bae2bb4d..79c55127a4 100644 --- a/app/assets/stylesheets/alchemy/shoelace.scss +++ b/app/assets/stylesheets/alchemy/shoelace.scss @@ -347,6 +347,7 @@ sl-tooltip { sl-dialog { &::part(panel) { background-color: var(--color-grey_light); + min-height: var(--height); --body-spacing: var(--spacing-4) var(--spacing-3); --footer-spacing: var(--spacing-4) var(--spacing-3); } diff --git a/app/javascript/alchemy_admin/components/dialog_link.js b/app/javascript/alchemy_admin/components/dialog_link.js index 779018c530..916a83a8be 100644 --- a/app/javascript/alchemy_admin/components/dialog_link.js +++ b/app/javascript/alchemy_admin/components/dialog_link.js @@ -1,13 +1,4 @@ -export const DEFAULTS = { - header_height: 36, - size: "400x300", - padding: true, - title: "", - modal: true, - overflow: "visible", - ready: () => {}, - closed: () => {} -} +import { Dialog } from "alchemy_admin/dialog" export class DialogLink extends HTMLAnchorElement { connectedCallback() { @@ -20,21 +11,14 @@ export class DialogLink extends HTMLAnchorElement { } openDialog() { - this.dialog = new Alchemy.Dialog( - this.getAttribute("href"), - this.dialogOptions - ) + this.dialog = new Dialog(this.getAttribute("href"), this.dialogOptions) this.dialog.open() } get dialogOptions() { - const options = this.dataset.dialogOptions + return this.dataset.dialogOptions ? JSON.parse(this.dataset.dialogOptions) : {} - return { - ...DEFAULTS, - ...options - } } get disabled() { diff --git a/app/javascript/alchemy_admin/dialog.js b/app/javascript/alchemy_admin/dialog.js new file mode 100644 index 0000000000..727a2a1500 --- /dev/null +++ b/app/javascript/alchemy_admin/dialog.js @@ -0,0 +1,119 @@ +import { createHtmlElement } from "alchemy_admin/utils/dom_helpers" + +export class Dialog { + #dialog + #onReject + #onResolve + + /** + * @param {string} url + * @param {object} options + */ + constructor(url, options = {}) { + this.url = url + this.options = { title: "", size: "300x400", padding: true, ...options } + } + + /** + * load the content of given url and than open the dialog + * @returns {Promise} + */ + open() { + this.#loadContent().then((content) => { + // create the dialog markup and show the dialog + this.#build(content) + this.#subscribeFormSubmit() + this.#dialog.show() + + // bind the current class instance to the DOM - element + // this should be an intermediate solution + // the main goal, is to close the dialog with the turbo:submit-end - event + this.#dialog.connectedDialogInstance = this + + // the dialog is closing with the overlay, esc - key, or close - button + this.#dialog.addEventListener("sl-request-close", () => { + this.#dialog.remove() + this.#onReject() + }) + }) + + return new Promise((resolve, reject) => { + this.#onResolve = resolve + this.#onReject = reject + }) + } + + /** + * hide and remove dialog + * the open - promise will be resolved + */ + close() { + this.#dialog.hide().then(() => { + this.#dialog.remove() + this.#onResolve() + }) + } + + /** + * load content of the given url + * @returns {Promise} + */ + async #loadContent() { + const response = await fetch(this.url, { + headers: { "X-Requested-With": "XMLHttpRequest" } + }) + return await response.text() + } + + /** + * create and append the dialog container to the DOM + * @param {string} content + */ + #build(content) { + this.#dialog = createHtmlElement(` + + ${content} + + `) + document.body.append(this.#dialog) + } + + /** + * add event listeners to forms inside the dialog + * + * listen to turbo:submit-end events if a remote form was submitted + * only forms with the attribute `data-close-dialog-on-success` + */ + #subscribeFormSubmit() { + this.#dialog + .querySelectorAll("form[data-close-dialog-on-success]") + .forEach((form) => + form.addEventListener("turbo:submit-end", (evt) => { + if (evt.detail.success) { + this.close() + } + }) + ) + } + + /** + * provide the custom properties for the dialog settings + * @returns {string} + */ + get styles() { + const sizes = this.options.size.split("x") + let styles = `--width: ${sizes[0]}px; --height: ${sizes[1]}px;` + if (!this.options.padding) { + styles += " --body-spacing: 0;" + } + return styles + } + + /** + * get the title of the dialog + * @returns {string} + */ + get title() { + return this.options.title + } +}