Skip to content

Instantly share code, notes, and snippets.

@obie
Created October 15, 2020 03:29
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save obie/5c56d87c7b7e4e343ef7504349a69515 to your computer and use it in GitHub Desktop.
Save obie/5c56d87c7b7e4e343ef7504349a69515 to your computer and use it in GitHub Desktop.
Alerts in Reactive Rails style (with Shoelace web components)
%sl-alert.popup-toast{id: id, type: type, open: false, duration: duration, closable: true}
%sl-icon{slot: "icon", name: icon}
- if title.present?
%strong= title
%br
= text
// app/javascript/channels/alerts_channel.js
import CableReady from 'cable_ready'
import consumer from "./consumer"
consumer.subscriptions.create("AlertsChannel", {
connected() {
console.log("AlertsChannel connected")
},
disconnected() {
console.log("AlertsChannel disconnected")
},
received(data) {
if (data.cableReady) {
CableReady.perform(data.operations)
document.querySelectorAll('sl-alert.popup-toast').forEach(function (a) {
a.toast()
})
}
}
});
# app/channels/alerts_channel.rb
class AlertsChannel < ApplicationCable::Channel
def subscribed
stream_from "alerts"
stream_for current_user
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
<!DOCTYPE html>
<html lang="en">
<%= render partial: "head" %>
<body class="sl-theme-dark" data-reflex-root="#site-content">
<%= render partial: "header" %>
<div id="site-content">
<%= yield %>
<%= render Audio::Player.new(track: @track_to_play) %>
<div id="dialogs"></div>
</div>
<div id="alerts"></div>
<%= render partial: "footer" %>
<%= render partial: "flash" %>
</body>
</html>
# app/reflexes/application_reflex.rb
class ApplicationReflex < StimulusReflex::Reflex
include ApplicationHelper
include CableReady::Broadcaster
delegate :current_user, to: :connection
delegate :render, to: ApplicationController
# todo: extract to a concern. bonus points if it works for both reflexes and controllers
around_reflex :broadcast_alerts
def alert(opts)
@alerts << opts.merge(type: "primary", icon: "info-circle")
end
def success(opts)
@alerts << opts.merge(type: "success", icon: "check2-circle")
end
def info(opts)
@alerts << opts.merge(type: "info", icon: "gear")
end
def warning(opts)
@alerts << opts.merge(type: "warning", icon: "exclamation-triangle", duration: nil)
end
def danger(opts)
@alerts << opts.merge(type: "danger", icon: "exclamation-octagon", duration: nil)
end
private
def broadcast_alerts
@alerts = []
yield
@alerts.each { |opts| send_alert(opts) }
end
def send_alert(opts)
opts = { id: random_id, title: "", duration: 3000 }.merge(opts)
cable_ready["alerts"].insert_adjacent_html(selector: "#alerts", html: render(partial: "alert", locals: opts))
cable_ready.broadcast_to(current_user)
end
end
@elissonmichael
Copy link

Thanks for sharing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment