From 37a7027c6eaa30926ca77f2492369c178bde9c71 Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Thu, 12 Oct 2023 22:55:42 +0200 Subject: [PATCH] wip: add shoelace/alert --- app/assets/javascripts/alchemy/admin.js | 1 - .../alchemy/alchemy.growler.js.coffee | 24 ------- app/assets/stylesheets/alchemy/flash.scss | 26 -------- app/assets/stylesheets/alchemy/print.scss | 5 +- app/components/alchemy/admin/flash_message.rb | 56 ++++++++++++++++ app/helpers/alchemy/base_helper.rb | 11 +--- app/javascript/alchemy_admin.js | 2 + app/javascript/alchemy_admin/growler.js | 66 +++++++++++++++++++ app/javascript/alchemy_admin/initializer.js | 5 -- .../alchemy/admin/partials/_flash.html.erb | 5 +- .../admin/partials/_flash_notices.html.erb | 13 ++-- .../alchemy/admin/styleguide/index.html.erb | 4 ++ app/views/alchemy/base/error_notice.html.erb | 2 +- config/importmap.rb | 3 + 14 files changed, 145 insertions(+), 78 deletions(-) delete mode 100644 app/assets/javascripts/alchemy/alchemy.growler.js.coffee create mode 100644 app/components/alchemy/admin/flash_message.rb create mode 100644 app/javascript/alchemy_admin/growler.js diff --git a/app/assets/javascripts/alchemy/admin.js b/app/assets/javascripts/alchemy/admin.js index 0d12b3efcd..810266953d 100644 --- a/app/assets/javascripts/alchemy/admin.js +++ b/app/assets/javascripts/alchemy/admin.js @@ -15,7 +15,6 @@ //= require alchemy/alchemy.dragndrop //= 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 09c98ba49f..dd9e6d285c 100644 --- a/app/assets/stylesheets/alchemy/flash.scss +++ b/app/assets/stylesheets/alchemy/flash.scss @@ -1,29 +1,3 @@ -div#flash_notices { - position: fixed; - right: 0; - z-index: 400000; - width: 400px; - top: 0; - - .flash.error { - cursor: pointer; - padding-right: 32px; - - &:before { - display: flex; - position: absolute; - right: 2 * $default-padding; - top: 7px; - width: 20px; - height: 20px; - font-family: "remixicon"; - content: $ri-close-line; - align-items: center; - justify-content: center; - } - } -} - div.flash { border-radius: $default-border-radius; opacity: 0.95; 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..170d2f371b --- /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, + open: true + }.tap do |a| + a[:duration] = 3000 if auto_dismiss + a[:closable] = true if closable + end.compact! + end +end diff --git a/app/helpers/alchemy/base_helper.rb b/app/helpers/alchemy/base_helper.rb index 85474418ff..971c2313aa 100644 --- a/app/helpers/alchemy/base_helper.rb +++ b/app/helpers/alchemy/base_helper.rb @@ -54,19 +54,10 @@ def render_message(type = :info, msg = nil, &blk) if blk content_tag :div, render_icon(icon_class) + capture(&blk), class: "#{type} message" else - content_tag :div, render_icon(icon_class) + msg, class: "#{type} message" + render Alchemy::Admin::FlashMessage.new(msg, type: type, auto_dismiss: false, closable: false) 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 8ff76a60a1..ec99fdc40a 100644 --- a/app/javascript/alchemy_admin.js +++ b/app/javascript/alchemy_admin.js @@ -2,6 +2,7 @@ import "@ungap/custom-elements" import "@hotwired/turbo-rails" import Rails from "@rails/ujs" +import growl from "alchemy_admin/growler" import GUI from "alchemy_admin/gui" import { translate } from "alchemy_admin/i18n" import Dirty from "alchemy_admin/dirty" @@ -68,6 +69,7 @@ if (typeof window.Alchemy === "undefined") { // Enhance the global Alchemy object with imported features Object.assign(Alchemy, { ...Dirty, + growl, GUI, t: translate, // Global utility method for translating a given string ImageLoader: ImageLoader.init, 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 7527b7e01b..5311276c2f 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") .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/admin/styleguide/index.html.erb b/app/views/alchemy/admin/styleguide/index.html.erb index 1efa178c2e..fa12308483 100644 --- a/app/views/alchemy/admin/styleguide/index.html.erb +++ b/app/views/alchemy/admin/styleguide/index.html.erb @@ -41,6 +41,10 @@

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

<% end %> +

Inline Message

+ +<%= render_message "Lorem ipsum dolor sit amet, consectetur adipiscing elit." %> +

Warning Message

<%= render_message :warning do %> 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 ddcb5b5c50..61179dd308 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -4,6 +4,9 @@ pin "lodash-es/max", to: "https://ga.jspm.io/npm:lodash-es@4.17.21/max.js", preload: true pin "sortablejs", to: "https://ga.jspm.io/npm:sortablejs@1.15.0/modular/sortable.esm.js", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.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 "@shoelace/switch", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/switch/switch.js", preload: true 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