diff --git a/app/assets/javascripts/alchemy/admin.js b/app/assets/javascripts/alchemy/admin.js index c54c6bc959..5f7519b138 100644 --- a/app/assets/javascripts/alchemy/admin.js +++ b/app/assets/javascripts/alchemy/admin.js @@ -18,7 +18,6 @@ //= require alchemy/alchemy.element_editors //= require alchemy/alchemy.elements_window //= require alchemy/alchemy.fixed_elements -//= require alchemy/alchemy.growler //= require alchemy/alchemy.hotkeys //= require alchemy/alchemy.image_overlay //= require alchemy/alchemy.string_extension diff --git a/app/assets/javascripts/alchemy/alchemy.growler.js.coffee b/app/assets/javascripts/alchemy/alchemy.growler.js.coffee deleted file mode 100644 index ad4283b0f8..0000000000 --- a/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +++ /dev/null @@ -1,24 +0,0 @@ -window.Alchemy = {} if typeof (window.Alchemy) is "undefined" - -Alchemy.Growler = - - build: (message, flash_type) -> - $flash_container = $("
") - $flash_container.append Alchemy.messageIcon(flash_type) - $flash_container.append message - $("#flash_notices").append $flash_container - $("#flash_notices").show() - Alchemy.Growler.fade() - - fade: -> - $(".flash:not(.error)", "#flash_notices").delay(5000).queue(-> Alchemy.Growler.dismiss(this)) - $(".flash", "#flash_notices").click((e) => @dismiss(e.currentTarget)) - return - - dismiss: (element) -> - $(element).on 'transitionend', => $(element).remove() - $(element).addClass('dismissed') - return - -Alchemy.growl = (message, style = "notice") -> - Alchemy.Growler.build message, style diff --git a/app/assets/stylesheets/alchemy/flash.scss b/app/assets/stylesheets/alchemy/flash.scss index d6fecfd327..049a1e43cd 100644 --- a/app/assets/stylesheets/alchemy/flash.scss +++ b/app/assets/stylesheets/alchemy/flash.scss @@ -1,25 +1,3 @@ -div#flash_notices { - position: fixed; - right: 0; - z-index: 400000; - width: 400px; - top: 0; - - .flash.error { - cursor: pointer; - padding-right: 24px; - - &:before { - content: fa-content($fa-var-times); - position: absolute; - right: 2*$default-padding; - top: 11px; - font-size: $small-font-size; - font-family: 'Font Awesome 5 Free'; - } - } -} - div.flash { border-radius: $default-border-radius; opacity: 0.95; @@ -28,7 +6,7 @@ div.flash { border-width: 1px; border-style: solid; z-index: 1000; - margin: $default-margin $default-margin 2*$default-margin; + margin: $default-margin $default-margin 2 * $default-margin; position: relative; min-height: 2.6em; word-break: break-all; @@ -66,7 +44,9 @@ div.flash { background-color: $info_background_color; } - &.warn, &.warning, &.alert { + &.warn, + &.warning, + &.alert { border-color: $warning_border_color; color: $warning_text_color; background-color: $warning_background_color; diff --git a/app/assets/stylesheets/alchemy/print.scss b/app/assets/stylesheets/alchemy/print.scss index 5bacf096c4..52644593a8 100644 --- a/app/assets/stylesheets/alchemy/print.scss +++ b/app/assets/stylesheets/alchemy/print.scss @@ -7,7 +7,6 @@ div#main_menu, div#top_menu, div#corner, -div#flash_notices, div.pagination span, div.pagination a { display: none; @@ -18,7 +17,7 @@ div#archive_all { } span.icon.true:before { - content: 'x'; + content: "x"; } div.pagination { @@ -31,5 +30,5 @@ div.pagination em.current { } div.pagination em:before { - content: 'Page '; + content: "Page "; } diff --git a/app/components/alchemy/admin/flash_message.rb b/app/components/alchemy/admin/flash_message.rb new file mode 100644 index 0000000000..af2ff7a252 --- /dev/null +++ b/app/components/alchemy/admin/flash_message.rb @@ -0,0 +1,56 @@ +class Alchemy::Admin::FlashMessage < ViewComponent::Base + def initialize(message, type: "notice", auto_dismiss: true, closable: true) + @message = message + @type = type + @auto_dismiss = (auto_dismiss == true) ? variant != "danger" : false + @closable = closable + end + + def call + content_tag("sl-alert", message, attributes) do + content_tag("sl-icon", nil, name: icon, slot: "icon") + message + end + end + + private + + attr_reader :message, :type, :auto_dismiss, :closable + + def icon + case type.to_s + when "warning", "warn", "alert" + "exclamation-triangle-fill" + when "notice" + "check-lg" + when "error" + "bug-fill" + else + "info-circle-fill" + end + end + + def variant + case type.to_s + when "warning", "warn", "alert" + "warning" + when "notice", "success" + "success" + when "error" + "danger" + when "info" + "primary" + else + "neutral" + end + end + + def attributes + { + variant: variant, + closable: closable, + open: true + }.tap do |a| + a[:duration] = 3000 if auto_dismiss + end + end +end diff --git a/app/controllers/alchemy/admin/elements_controller.rb b/app/controllers/alchemy/admin/elements_controller.rb index ca6dbcef28..1b0d36f8d9 100644 --- a/app/controllers/alchemy/admin/elements_controller.rb +++ b/app/controllers/alchemy/admin/elements_controller.rb @@ -53,6 +53,7 @@ def create # Updates the element and all ingredients in the element. # def update + lkjcreate @page = @element.page if @element.update(element_params) diff --git a/app/helpers/alchemy/base_helper.rb b/app/helpers/alchemy/base_helper.rb index 8196a62e9a..2d2354536a 100644 --- a/app/helpers/alchemy/base_helper.rb +++ b/app/helpers/alchemy/base_helper.rb @@ -58,15 +58,6 @@ def render_message(type = :info, msg = nil, &blk) end end - # Renders the flash partial (+alchemy/admin/partials/flash+) - # - # @param [String] notice The notice you want to display - # @param [Symbol] style The style of this flash. Valid values are +:notice+ (default), +:warn+ and +:error+ - # - def render_flash_notice(notice, style = :notice) - render("alchemy/admin/partials/flash", flash_type: style, message: notice) - end - # Checks if the given argument is a String or a Page object. # If a String is given, it tries to find the page via page_layout # Logs a warning if no page is given. diff --git a/app/javascript/alchemy_admin.js b/app/javascript/alchemy_admin.js index 59750c4166..00f9cb067c 100644 --- a/app/javascript/alchemy_admin.js +++ b/app/javascript/alchemy_admin.js @@ -1,6 +1,7 @@ import "@hotwired/turbo-rails" import Buttons from "alchemy_admin/buttons" +import growl from "alchemy_admin/growler" import GUI from "alchemy_admin/gui" import { translate } from "alchemy_admin/i18n" import Dirty from "alchemy_admin/dirty" @@ -41,6 +42,7 @@ if (typeof window.Alchemy === "undefined") { Object.assign(Alchemy, { Buttons, ...Dirty, + growl, GUI, t: translate, // Global utility method for translating a given string fileEditors, diff --git a/app/javascript/alchemy_admin/growler.js b/app/javascript/alchemy_admin/growler.js new file mode 100644 index 0000000000..2c3fa6c329 --- /dev/null +++ b/app/javascript/alchemy_admin/growler.js @@ -0,0 +1,66 @@ +import "@shoelace/alert" +import { registerIconLibrary } from "@shoelace/icon-library" + +registerIconLibrary("default", { + resolver: (name) => + `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/icons/${name}.svg` +}) + +function notify( + message, + variant = "primary", + icon = "info-circle-fill", + duration = 3000 +) { + const alert = Object.assign(document.createElement("sl-alert"), { + variant, + closable: true, + innerHTML: ` + + ${message} + ` + }) + if (variant !== "danger") { + alert.duration = duration + } + document.body.append(alert) + alert.toast() +} + +function messageIcon(messageType) { + switch (messageType) { + case "alert": + case "warn": + case "warning": + return "exclamation-triangle-fill" + case "notice": + return "check-lg" + case "error": + return "bug-fill" + default: + return "info-circle-fill" + } +} + +function messageStyle(messageType) { + switch (messageType) { + case "alert": + case "warn": + case "warning": + return "warning" + case "notice": + case "success": + return "success" + case "danger": + case "error": + return "danger" + case "info": + return "primary" + default: + return "neutral" + } +} + +export default function (message, style = "notice") { + notify(message, messageStyle(style), messageIcon(style)) +} diff --git a/app/javascript/alchemy_admin/initializer.js b/app/javascript/alchemy_admin/initializer.js index fb14bc327d..b467bed251 100644 --- a/app/javascript/alchemy_admin/initializer.js +++ b/app/javascript/alchemy_admin/initializer.js @@ -25,11 +25,6 @@ function Initialize() { // Initialize the GUI. Alchemy.GUI.init() - // Fade all growl notifications. - if ($("#flash_notices").length > 0) { - Alchemy.Growler.fade() - } - // Add observer for please wait overlay. $(".please_wait, .button_with_label form :submit") .not("*[data-alchemy-confirm]") diff --git a/app/views/alchemy/admin/partials/_flash.html.erb b/app/views/alchemy/admin/partials/_flash.html.erb index 7a94803849..c826f98e0f 100644 --- a/app/views/alchemy/admin/partials/_flash.html.erb +++ b/app/views/alchemy/admin/partials/_flash.html.erb @@ -1,4 +1 @@ -
- <%= render_icon message_icon_class(flash_type) %> - <%= message %> -
+<%= render Alchemy::Admin::FlashMessage.new(message, type: flash_type) %> diff --git a/app/views/alchemy/admin/partials/_flash_notices.html.erb b/app/views/alchemy/admin/partials/_flash_notices.html.erb index 38bb817077..e6004f969c 100644 --- a/app/views/alchemy/admin/partials/_flash_notices.html.erb +++ b/app/views/alchemy/admin/partials/_flash_notices.html.erb @@ -1,5 +1,10 @@ -
"> -<% flash.keys.each do |flash_type| %> - <%= render_flash_notice(flash[flash_type.to_sym], flash_type) if flash[flash_type.to_sym].present? %> -<% end %> +
+ <% flash.keys.each do |flash_type| %> + <% message = flash[flash_type.to_sym] %> + <% if message.present? %> + <%= render "alchemy/admin/partials/flash", + message: message, + flash_type: flash_type %> + <% end %> + <% end %>
diff --git a/app/views/alchemy/base/error_notice.html.erb b/app/views/alchemy/base/error_notice.html.erb index 649c79e1db..e18cd5a590 100644 --- a/app/views/alchemy/base/error_notice.html.erb +++ b/app/views/alchemy/base/error_notice.html.erb @@ -1 +1 @@ -<%= render_flash_notice @notice, :error %> +<%= render "alchemy/admin/partials/flash", flash_type: :error, message: @notice %> diff --git a/config/importmap.rb b/config/importmap.rb index 319e4cc656..9ec59f5c4d 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -6,6 +6,8 @@ pin "@shoelace/tab", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/tab/tab.js", preload: true pin "@shoelace/tab-group", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/tab-group/tab-group.js", preload: true pin "@shoelace/tab-panel", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/tab-panel/tab-panel.js", preload: true +pin "@shoelace/alert", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/alert/alert.js", preload: true +pin "@shoelace/icon-library", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/utilities/icon-library.js", preload: true pin "alchemy_admin", to: "alchemy_admin.js", preload: true pin_all_from File.expand_path("../app/javascript/alchemy_admin", __dir__), under: "alchemy_admin"