Skip to content

Commit

Permalink
Add alchemy-message component
Browse files Browse the repository at this point in the history
And use this for Rails `flash` messages, `render_message` helper
and `Alchemy.growl` notices.
  • Loading branch information
tvdeyen committed Mar 19, 2024
1 parent a053600 commit bdb76ce
Show file tree
Hide file tree
Showing 19 changed files with 513 additions and 254 deletions.
19 changes: 4 additions & 15 deletions app/assets/javascripts/alchemy/alchemy.dialog.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ class window.Alchemy.Dialog
else
error_header = "#{xhr.statusText} (#{xhr.status})"
error_body = "Please check log and try again."
$errorDiv = $("<div class=\"message #{error_type}\" />")
$errorDiv.append Alchemy.messageIcon(error_type)
$errorDiv.append "<h1>#{error_header}</h1>"
$errorDiv.append "<p>#{error_body}</p>"
$errorDiv = $("<alchemy-message type=\"#{error_type}\">
<h1>#{error_header}</h1>
<p>#{error_body}</p>
</alchemy-message>")
$container.html $errorDiv

# Binds close events on:
Expand Down Expand Up @@ -265,14 +265,3 @@ window.Alchemy.watchForDialogs = (scope = '#alchemy') ->
return
event.preventDefault()
return

# Returns a Remix icon for given message type
#
window.Alchemy.messageIcon = (messageType) ->
icon_name = switch messageType
when "warning", "warn", "alert" then "alert"
when "notice" then "check"
when "info" then "information"
when "error" then "bug"
else messageType
"<alchemy-icon name=\"#{icon_name}\"></alchemy-icon>"
84 changes: 19 additions & 65 deletions app/assets/stylesheets/alchemy/flash.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,28 @@ div#flash_notices {
width: 400px;
top: 0;

.flash.error {
cursor: pointer;
padding-right: 32px;

alchemy-icon[name="close"] {
position: absolute;
left: initial;
right: 2 * $default-padding;
alchemy-message {
font-weight: bold;
margin: $default-margin $default-margin 2 * $default-margin;
opacity: 0.95;
transition-property: opacity, transform;
transition-duration: 0.4s;

&.dismissed {
display: block;
opacity: 0;
transform: translate3d(0, -100%, 0);
}
}
}

div.flash {
border-radius: $default-border-radius;
opacity: 0.95;
padding: 8px 16px 8px 32px;
font-weight: bold;
border-width: 1px;
border-style: solid;
z-index: 1000;
margin: $default-margin $default-margin 2 * $default-margin;
position: relative;
min-height: 2.6em;
word-break: break-word;
transition-property: opacity, transform;
transition-duration: 0.4s;
line-height: 1.5;

alchemy-icon {
position: absolute;
top: 9px;
left: 2 * $default-padding;
vertical-align: bottom;
&[type="error"] {
cursor: pointer;
padding-right: 32px;

.icon {
fill: currentColor;
alchemy-icon[name="close"] {
position: absolute;
left: initial;
right: 2 * $default-padding;
}
}
}

&.dismissed {
display: block;
opacity: 0;
transform: translate3d(0, -100%, 0);
}

&.notice {
border-color: $success_border_color;
color: $success_text_color;
background-color: $success_background_color;
}

&.error {
border-color: $error_border_color;
color: $error_text_color;
background-color: $error_background_color;
}

&.info {
border-color: $info_border_color;
color: $info_text_color;
background-color: $info_background_color;
}

&.warn,
&.warning,
&.alert {
border-color: $warning_border_color;
color: $warning_text_color;
background-color: $warning_background_color;
}
}
87 changes: 50 additions & 37 deletions app/assets/stylesheets/alchemy/notices.scss
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
// Some table columns also have .message class
div.message {
padding: 8px 8px 8px 32px;
line-height: 17px;
alchemy-message {
display: block;
border-width: $default-border-width;
border-style: $default-border-style;
border-radius: $default-border-radius;
position: relative;
padding: 8px 16px 8px 32px;
margin-bottom: 8px;
text-align: left;
min-height: 2.6em;
word-break: break-word;
line-height: 1.5;

h1,
h2,
h3 {
margin-top: 0;
&[type="footnote"] {
font-size: $small-font-size;
margin: 16px;
}

&[type="notice"] {
border-color: $success_border_color;
color: $success_text_color;
background-color: $success_background_color;
}

&[type="hint"] {
background-color: $hint-background-color;
border-color: $hint-background-color;
color: $hint-text-color;
}

&[type="info"] {
background-color: $info_background_color;
border-color: $info_border_color;
color: $info_text_color;
}

&[type="error"] {
background-color: $error_background_color;
border-color: $error_border_color;
color: $error_text_color;
}

&[type="warning"],
&[type="warn"],
&[type="alert"] {
background-color: $warning_background_color;
border-color: $warning_border_color;
color: $warning_text_color;
}

alchemy-icon {
Expand All @@ -26,9 +59,15 @@ div.message {
}
}

&.footnote {
font-size: $small-font-size;
margin: 16px;
h1 {
font-size: 1.3rem;
line-height: 1.1;
}

h1,
h2,
h3 {
margin-top: 0;
}

a {
Expand All @@ -49,30 +88,4 @@ div.message {
margin-bottom: 4px;
}
}

&.hint {
background-color: $hint-background-color;
border-color: $hint-background-color;
color: $hint-text-color;
}

&.info {
background-color: $info_background_color;
border-color: $info_border_color;
color: $info_text_color;
}

&.error {
background-color: $error_background_color;
border-color: $error_border_color;
color: $error_text_color;
}

&.warning,
&.warn,
&.alert {
background-color: $warning_background_color;
border-color: $warning_border_color;
color: $warning_text_color;
}
}
19 changes: 19 additions & 0 deletions app/components/alchemy/admin/message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Alchemy
module Admin
class Message < ViewComponent::Base
attr_reader :message, :type, :dismissable

erb_template <<~ERB
<alchemy-message type="<%= type %>"<%= dismissable ? ' dismissable' : '' %>>
<%= message || content %>
</alchemy-message>
ERB

def initialize(message = nil, type: :info, dismissable: false)
@message = message
@dismissable = dismissable
@type = type
end
end
end
end
32 changes: 6 additions & 26 deletions app/helpers/alchemy/base_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,16 @@ def render_icon(icon_name, options = {})
# <% end %>
#
def render_message(type = :info, msg = nil, &blk)
icon_class = message_icon_class(type)
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"
end
render Alchemy::Admin::Message.new(msg || capture(&blk), type: type)
end

# Renders the flash partial (+alchemy/admin/partials/flash+)
# Renders a dismissable growl message.
#
# @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+
# @param [String] notice - The notice you want to display
# @param [Symbol] type - The type of this flash. Valid values are +:notice+ (default), +:warn+, +:info+ and +:error+
#
def render_flash_notice(notice, style = :notice)
render("alchemy/admin/partials/flash", flash_type: style, message: notice)
def render_flash_notice(notice, type = :notice)
render Alchemy::Admin::Message.new(notice, type: type, dismissable: true)
end

# Checks if the given argument is a String or a Page object.
Expand All @@ -77,20 +72,5 @@ def page_or_find(page)
page
end
end

# Returns the icon name for given message type
#
# @param message_type [String] The message type. One of +warning+, +info+, +notice+, +error+
# @return [String] The icon name
def message_icon_class(message_type)
case message_type.to_s
when "warning", "warn", "alert" then "exclamation"
when "notice" then "check"
when "error" then "bug"
when "hint" then "info"
else
message_type
end
end
end
end
1 change: 1 addition & 0 deletions app/javascript/alchemy_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import "alchemy_admin/components/clipboard_button"
import "alchemy_admin/components/datepicker"
import "alchemy_admin/components/dialog_link"
import "alchemy_admin/components/element_editor"
import "alchemy_admin/components/message"
import "alchemy_admin/components/growl"
import "alchemy_admin/components/icon"
import "alchemy_admin/components/ingredient_group"
Expand Down
69 changes: 69 additions & 0 deletions app/javascript/alchemy_admin/components/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const DISMISS_DELAY = 5000

class Message extends HTMLElement {
#message

constructor() {
super()
this.#message = this.innerHTML
if (this.dismissable || this.type === "error") {
this.addEventListener("click", this)
}
}

handleEvent(event) {
if (event.type === "click") {
this.dismiss()
}
}

connectedCallback() {
this.innerHTML = `
<alchemy-icon name="${this.iconName}"></alchemy-icon>
${this.dismissable && this.type === "error" ? '<alchemy-icon name="close"></alchemy-icon>' : ""}
${this.#message}
`
if (this.dismissable && this.type !== "error") {
setTimeout(() => {
this.dismiss()
}, this.delay)
}
}

dismiss() {
this.addEventListener("transitionend", () => this.remove())
this.classList.add("dismissed")
}

get dismissable() {
return this.hasAttribute("dismissable")
}

get type() {
return this.getAttribute("type") || "notice"
}

get delay() {
return parseInt(this.getAttribute("delay") || DISMISS_DELAY)
}

get iconName() {
switch (this.type) {
case "warning":
case "warn":
case "alert":
return "alert"
case "notice":
return "check"
case "info":
case "hint":
return "information"
case "error":
return "bug"
default:
return this.type
}
}
}

customElements.define("alchemy-message", Message)
Loading

0 comments on commit bdb76ce

Please sign in to comment.