Skip to content

Commit

Permalink
Merge pull request #2823 from tvdeyen/shoelace-confirm-dialog
Browse files Browse the repository at this point in the history
Use Shoelace Dialog for ConfirmDialog
  • Loading branch information
tvdeyen authored Apr 5, 2024
2 parents f0b0403 + 88454b5 commit e2f9520
Show file tree
Hide file tree
Showing 10 changed files with 441 additions and 168 deletions.
3 changes: 3 additions & 0 deletions app/assets/stylesheets/alchemy/_custom-properties.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
--color-grey_medium: hsl(0deg, 0%, 78%);
--color-grey_dark: hsl(0deg, 0%, 40%);
--color-grey_very_dark: hsl(0deg, 0%, 20%);

--color-white: hsl(0deg, 0%, 100%);

--color-text: hsla(224, 22.7%, 25.9%, 0.8);
--color-icon: hsla(224, 22.7%, 25.9%, 0.75);
}
Expand Down
15 changes: 15 additions & 0 deletions app/assets/stylesheets/alchemy/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,18 @@ a img {
.hidden {
display: none !important;
}

.mx-1 {
margin-left: var(--spacing-1);
margin-right: var(--spacing-1);
}

.mx-2 {
margin-left: var(--spacing-2);
margin-right: var(--spacing-2);
}

.my-0 {
margin-top: 0;
margin-bottom: 0;
}
35 changes: 33 additions & 2 deletions app/assets/stylesheets/alchemy/shoelace.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
--sl-shadow-small: 0 1px 2px hsl(240 3.8% 46.1% / 12%);
--sl-shadow-medium: 0 2px 4px hsl(240 3.8% 46.1% / 12%);
--sl-shadow-large: 0 2px 8px hsl(240 3.8% 46.1% / 12%);
--sl-shadow-x-large: 0 4px 16px hsl(240 3.8% 46.1% / 12%);
--sl-shadow-x-large: 0 8px 16px rgba(35, 35, 35, 0.5);

/*
* Spacings
Expand Down Expand Up @@ -255,7 +255,7 @@
* Overlays
*/

--sl-overlay-background-color: hsl(240 3.8% 46.1% / 33%);
--sl-overlay-background-color: hsl(0 0% 39.2% / 40%);

/*
* Panels
Expand Down Expand Up @@ -343,3 +343,34 @@ sl-tooltip {
box-shadow: 0 0 var(--spacing-1) var(--color-grey_medium);
}
}

sl-dialog {
&::part(panel) {
background-color: var(--color-grey_light);
--body-spacing: var(--spacing-4) var(--spacing-3);
--footer-spacing: var(--spacing-4) var(--spacing-3);
}

&::part(header) {
--header-spacing: var(--spacing-3);
background-color: var(--color-blue_dark);
border-top-left-radius: var(--border-radius_medium);
border-top-right-radius: var(--border-radius_medium);
}

&::part(header-actions) {
--header-spacing: var(--spacing-1);
}

&::part(title) {
--sl-font-size-large: var(--font-size_small);
color: var(--color-white);
}

&::part(close-button) {
--sl-color-primary-600: var(--color-white);
--sl-color-primary-700: var(--color-white);
color: var(--color-white);
fill: currentColor;
}
}
24 changes: 2 additions & 22 deletions app/javascript/alchemy_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,8 @@ import {
// Web Components
import "alchemy_admin/components"

import { setDefaultAnimation } from "shoelace"

// Change the default animation for all dialogs
setDefaultAnimation("tooltip.show", {
keyframes: [
{ transform: "translateY(10px)", opacity: "0" },
{ transform: "translateY(0)", opacity: "1" }
],
options: {
duration: 100
}
})

setDefaultAnimation("tooltip.hide", {
keyframes: [
{ transform: "translateY(0)", opacity: "1" },
{ transform: "translateY(10px)", opacity: "0" }
],
options: {
duration: 100
}
})
// Shoelace Setup
import "alchemy_admin/shoelace_theme"

// Global Alchemy object
if (typeof window.Alchemy === "undefined") {
Expand Down
82 changes: 51 additions & 31 deletions app/javascript/alchemy_admin/confirm_dialog.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,77 @@
import { growl } from "alchemy_admin/growler"
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
import { createHtmlElement } from "alchemy_admin/utils/dom_helpers"
import { translate } from "alchemy_admin/i18n"

Alchemy.Dialog = window.Alchemy.Dialog || class Dialog {}

class ConfirmDialog extends Alchemy.Dialog {
class ConfirmDialog {
constructor(message, options = {}) {
const DEFAULTS = {
size: "300x100",
title: "Please confirm",
ok_label: "Yes",
cancel_label: "No",
title: translate("Please confirm"),
ok_label: translate("Yes"),
cancel_label: translate("No"),
on_ok() {}
}

options = { ...DEFAULTS, ...options }

super("", options)
this.message = message
this.options = options
this.#build()
this.#bindEvents()
}

load() {
this.dialog_title.text(this.options.title)
this.dialog_body.html(`<p>${this.message}</p>`)
this.dialog_body.append(this.build_buttons())
this.bind_buttons()
open() {
requestAnimationFrame(() => {
this.dialog.show()
})
}

build_buttons() {
const $btn_container = $('<div class="alchemy-dialog-buttons" />')
this.cancel_button = $(
`<button class=\"cancel secondary\">${this.options.cancel_label}</button>`
)
this.ok_button = $(
`<button class=\"confirm\">${this.options.ok_label}</button>`
)
$btn_container.append(this.cancel_button)
$btn_container.append(this.ok_button)
return $btn_container
#build() {
const width = this.options.size.split("x")[0]
this.dialog = createHtmlElement(`
<sl-dialog label="${this.options.title}" style="--width: ${width}px">
${this.message}
<button slot="footer" type="reset" class="secondary mx-1 my-0" autofocus>
${this.options.cancel_label}
</button>
<button slot="footer" type="submit" class="mx-1 my-0">
${this.options.ok_label}
</button>
</sl-dialog>
`)
document.body.append(this.dialog)
}

bind_buttons() {
this.cancel_button.trigger("focus")
this.cancel_button.on("click", () => {
this.close()
return false
#bindEvents() {
this.cancelButton.addEventListener("click", (evt) => {
evt.preventDefault()
this.dialog.hide()
})
this.ok_button.on("click", () => {
this.close()
this.okButton.addEventListener("click", (evt) => {
evt.preventDefault()
this.options.on_ok()
return false
this.dialog.hide()
})
// Prevent the dialog from closing when the user clicks on the overlay
this.dialog.addEventListener("sl-request-close", (event) => {
if (event.detail.source === "overlay") {
event.preventDefault()
}
})
// Remove the dialog from the DOM after it has been hidden
this.dialog.addEventListener("sl-after-hide", () => {
this.dialog.remove()
})
}

get cancelButton() {
return this.dialog.querySelector("button[type=reset]")
}

get okButton() {
return this.dialog.querySelector("button[type=submit]")
}
}

// Opens a confirm dialog
Expand Down
60 changes: 60 additions & 0 deletions app/javascript/alchemy_admin/shoelace_theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { registerIconLibrary, setDefaultAnimation } from "shoelace"

// Change the default animation for all tooltips
setDefaultAnimation("tooltip.show", {
keyframes: [
{ transform: "translateY(10px)", opacity: "0" },
{ transform: "translateY(0)", opacity: "1" }
],
options: {
duration: 100
}
})

setDefaultAnimation("tooltip.hide", {
keyframes: [
{ transform: "translateY(0)", opacity: "1" },
{ transform: "translateY(10px)", opacity: "0" }
],
options: {
duration: 100
}
})

// Change the default animation for all dialogs
setDefaultAnimation("dialog.show", {
keyframes: [
{ transform: "scale(0.98)", opacity: "0" },
{ transform: "scale(1)", opacity: "1" }
],
options: {
duration: 150
}
})

setDefaultAnimation("dialog.hide", {
keyframes: [
{ transform: "scale(1)", opacity: "1" },
{ transform: "scale(0.98)", opacity: "0" }
],
options: {
duration: 150
}
})

const spriteUrl = document
.querySelector('meta[name="alchemy-icon-sprite"]')
.getAttribute("content")

const iconMap = {
"x-lg": "close"
}

const options = {
resolver: (name) => `${spriteUrl}#ri-${iconMap[name] || name}-line`,
mutator: (svg) => svg.setAttribute("fill", "currentColor"),
spriteSheet: true
}

registerIconLibrary("default", options)
registerIconLibrary("system", options)
4 changes: 3 additions & 1 deletion bundles/shoelace.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ import "@shoelace-style/shoelace/dist/components/tab-group/tab-group.js"
import "@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js"
import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js"
import "@shoelace-style/shoelace/dist/components/progress-bar/progress-bar.js"
import "@shoelace-style/shoelace/dist/components/dialog/dialog.js"
import { registerIconLibrary } from "@shoelace-style/shoelace/dist/utilities/icon-library.js"
import { setDefaultAnimation } from "@shoelace-style/shoelace/dist/utilities/animation-registry.js"
export { setDefaultAnimation }
export { registerIconLibrary, setDefaultAnimation }
4 changes: 2 additions & 2 deletions spec/features/admin/page_destroy_feature_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

page.find("a[href='#{admin_page_path(content_page.id)}']").click

within ".alchemy-dialog-buttons" do
within "sl-dialog" do
click_button "Yes"
end

Expand All @@ -30,7 +30,7 @@

page.find("a[href='#{admin_page_path(layout_page.id)}']").click

within ".alchemy-dialog-buttons" do
within "sl-dialog" do
click_button "Yes"
end

Expand Down
286 changes: 224 additions & 62 deletions vendor/javascript/shoelace.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit e2f9520

Please sign in to comment.