What front-end frameworks are you using? What are you using it for? #1156
Replies: 10 comments 11 replies
-
Here is a Cookie Consent widget from our sales site using some alpine sprinkles. This is a completely independent component containing everything it needs except CSS (but I'm working on that as well :) class Shared::CookieConsentWidget < BaseComponent
include Mixins::CookiePolicyResourceHelper
needs context : HTTP::Server::Context
def render
if visitor_consent_required?
div class: css_class,
x_data: x_data,
"x-show.transition": "visible" do
text t(".text")
link t(".link"), to: Legal::Show.with(cookie_policy_page_slug)
button class: "#{css_class}__agree-button",
title: t(".ok"),
"@click": x_on_click do
text "+"
end
end
end
end
private quick_def agreed, "AGREE"
private quick_def cookie, "#{cookie_name}=#{agreed};expires=#{expires};path=/"
private quick_def css_class, "cookie-consent"
private quick_def expires, HTTP.format_time(6.months.from_now)
private quick_def x_data, %({"visible": true, "cookie": "#{cookie}" })
private quick_def x_on_click, "document.cookie=cookie;visible=false"
private def visitor_consent_required? : Bool
cookie = context.cookies.get_raw?(cookie_name)
!cookie || cookie.value != agreed
end
end I'll gather some other examples as well. But they are all fairly basic, I've not yet done more complicated stuff. What I love here is that I don't have to switch context while coding. Everything is in there to make it work. |
Beta Was this translation helpful? Give feedback.
-
Lots of discussion here: https://gitter.im/luckyframework/Lobby?at=5ecd1fa6778fad0b1328c89c Some interesting parts:
|
Beta Was this translation helpful? Give feedback.
-
As I mentioned in the chat, I am using Stimulus. Let me show my loader controller in Stimulus. It is simple and need some polish but it works: import { Controller } from "stimulus"
export default class extends Controller {
setData(response) {
this.element.innerHTML = response
}
getData() {
this.element.innerHTML = this.loader()
const period = this.data.get("period") ? this.data.get("period") : "7d"
fetch(this.data.get("url") + "?period=" + period).then(response => {
return response.text()
}).then(response => {
return this.setData(response)
})
}
connect() {
this.getData();
}
loader() {
return "<div class=\"w-1/6 m-auto\"><div class=\"lds-ring\"><div></div><div></div><div></div><div></div></div></div>"
}
} And my HTML dom for that loader looks like this: <div data-controller="loader" data-loader-period="<%=@period%>" data-loader-url="/domains/<%=@domain.id%>/data/countries"> |
Beta Was this translation helpful? Give feedback.
-
We use Stimulus, but all of the markup is generated in Lucky. Here's a quick tutorial. Basically Stimulus controller has a |
Beta Was this translation helpful? Give feedback.
-
Another one is our preregistration page. It depends on alpinejs for the following functions:
class Preregistrations::NewPage < MainLayout
def content
render_hero
render_preregistration
end
private def render_preregistration
section class: "section band band--preregister" do
div class: "section__lining grid grid--centered" do
div x_data: x_data, x_init: x_init,
class: "grid__unit grid__unit--one-of-two" do
render_form
render_messages
end
end
end
end
private def render_form
tag "template", x_if: "/^pending|ready$/.test(status)" do
form_for Preregistrations::Create,
data_remote: true,
"x-on:ajax:success": "status = 'success'",
"x-on:ajax:error": "status = 'error'" do
div class: "preregistration" do
h2 class: "band__heading text--large" do
raw t(".form.title")
end
render_input("name", "text", attrs: [:required])
tag "template", x_if: "status == 'pending'" do
render_input("email_1", "email", "email", attrs: [:required])
end
tag "template", x_if: "status == 'ready'" do
render_input("email_2", "email", "email", attrs: [:required])
end
label t("labels.webshop"), for: "webshop", class: "label"
render_input("webshop", "text")
para t(".form.agreement"), class: "preregistration__agreement"
button(t("buttons.preregister"),
role: "submit",
class: "button button--full-width")
end
end
end
end
private def render_messages
tag "template", x_if: "status == 'success'" do
div class: "preregistration__message" do
h2 t(".thank_you.title"), class: "preregistration__message-title"
para t(".thank_you.message")
end
end
tag "template", x_if: "status == 'error'" do
div class: "preregistration__message" do
h2 t(".error.title"), class: "preregistration__message-title"
para t(".error.message")
end
end
end
private def x_data
{status: "pending"}.to_s
end
private def x_init
"function(){ setTimeout(function() {status='ready'}, 2000) }"
end
private def render_input(
name : String,
type : String,
key : String? = nil,
attrs : Array(Symbol) = %i[]
)
input(
type: type,
name: name,
placeholder: t("inputs.#{key || name}"),
class: "input",
id: name,
attrs: attrs)
end
end
|
Beta Was this translation helpful? Give feedback.
-
From @matthewmcgarvey https://github.com/matthewmcgarvey/validon/commit/00784892432d8404abc22537a7c9172ae15d23aa def render
nav class: "navbar has-shadow", role: "navigation", aria_label: "main navigation" do
div class: "container", x_data: "{ active: false }" do
div class: "navbar-brand" do
a href: "/", class: "navbar-item" do
text "Validon"
end
a role: "button",
class: "navbar-burger burger",
aria_label: "menu",
aria_expanded: false,
":class": "{ 'is-active': active }",
"@click": "active = !active",
flow_id: "nav-hamburger" do
span aria_hidden: true
span aria_hidden: true
span aria_hidden: true
end
end
div class: "navbar-menu", ":class": "{ 'is-active': active }" do
div class: "navbar-end" do
auth_buttons
end
end
end
end
end |
Beta Was this translation helpful? Give feedback.
-
Here is another one. It's an element showing our product price different currencies: class Shared::PricingSwitcher < BaseComponent
private quick_def price, Price.new
def render
div class: "pricing", "x-data": price.to_data do
div class: "pricing__disk" do
para class: "pricing__price" do
span price.sign, class: "pricing__sign", "x-text": "sign"
span price.per_month.to_i.to_s,
"x-text": "Math.ceil(price * rates[sign])"
end
para t(".per_month"), class: "pricing__per-month"
para "subject to exchange rates",
class: "pricing__info",
"x-show": "sign != '£'"
button_class = "pricing__sign-button"
price.rates.keys.each.with_index do |sign, index|
button sign.to_s,
class: "#{button_class} #{button_class}--currency-#{index}",
"x-bind:class": "{'#{button_class}--active': sign == '#{sign}'}",
"@click": "sign = '#{sign}'"
end
end
end
end
end Where record Price, sign : String = "£" do
quick_def per_month, 19
quick_def per_year, per_month * 10
quick_def rates, {"€": 1.15, "£": 1.0, "$": 1.25}
def to_data
%({sign: '#{sign}', rates: #{rates}, price: #{per_month}})
end
end |
Beta Was this translation helpful? Give feedback.
-
Here's one StimulusJS controller I'm using regularly. One thing I love about Stimulus that's really easy to encourage is not writing page-specific functionality ("This controller toggles the navbar"), and instead baking in more generalized functionality ("When you click some element, toggle these elements on and off, respectively"). Here's a controller I made for Accordion functionality: import { Controller } from "stimulus";
class AccordionBaseController extends Controller {
readonly showWhenClosedTargets!: Element[];
readonly showWhenOpenTargets!: Element[];
}
export default class extends (Controller as typeof AccordionBaseController) {
static targets = ["showWhenClosed", "showWhenOpen"];
public toggle(): void {
this.showWhenClosedTargets.forEach((target) => {
target.classList.toggle("hidden");
});
this.showWhenOpenTargets.forEach((target) => {
target.classList.toggle("hidden");
});
}
} <%= tag.div data: { controller: "accordion" } do %>
<%= tag.button "Expand!", data: { target: "accordion.showWhenClosed" } %>
<%= tag.button "Collapse!", data: { target: "accordion.showWhenOpen" } %>
<%= div "Some expanded content", data: { target: "accordion.showWhenOpen" } %>
<% end %> The thing I love about it is that I don't have to remember my implementation, StimulusJS specifics, or anything else in the future. I can open this file, look at the target names, and grok immediately what they do and how I'd need to change things in the HTML without touching any JS. I've used this for anything from expandable content sections in cards all the way to a side drawer. |
Beta Was this translation helpful? Give feedback.
-
And one more StimulusJS controller I use for about a million things is my "list" controller: import { Controller } from "stimulus";
class ListBaseController extends Controller {
readonly placeholderTarget!: Element;
}
// Controller for handling lists elegantly.
//
// Usage:
// Add the "list" controller to the element containing the list.
// Add a "placeholder" target within the "list" that will be displayed when no other sibling elements are present.
export default class extends (Controller as typeof ListBaseController) {
static targets = ["content", "placeholder"];
public initialize() {
this.updateVisibility();
const contentObserver = new MutationObserver(() => {
this.updateVisibility();
});
contentObserver.observe(this.element, { childList: true });
}
private updateVisibility() {
const childrenArray = Array.from(this.element.children);
const contentArray = childrenArray.filter(this.isNotPlaceholderElement);
if (contentArray.length === 0) {
this.showPlaceholder();
} else {
this.hidePlaceholder();
}
}
private isNotPlaceholderElement(element: Element): Boolean {
const placeholderTargetName = "list.placeholder";
return element.getAttribute("data-target") !== placeholderTargetName;
}
private showPlaceholder(): void {
this.placeholderTarget.classList.remove("hidden");
}
private hidePlaceholder(): void {
this.placeholderTarget.classList.add("hidden");
}
} Lots more code, but this tackles that super-boilerplate code of always checking <%= tag.ul id: "task-list", data: { controller: "list" } do %>
<%= render(TaskComponent.with_collection(@tasks)) %>
<%= tag.p "No tasks", data: { target: "list.placeholder" } %>
<% end %> |
Beta Was this translation helpful? Give feedback.
-
Ah, and the last one I'll share is a short and sweet import { Controller } from "stimulus";
// This controller adds the ability to remove any arbitrary object in the DOM.
export default class extends Controller {
public remove(): void {
const parentElement = this.element.parentElement;
if (parentElement !== null) {
parentElement.removeChild(this.element);
}
}
} Great for something like a flash message: <% @flash_messages.each do |key, value| %>
<%= tag.div class: flash_class(key), data: { controller: "removable" } do %>
<span><%= value %></span>
<span data-action="click->removable#remove">
<i class="fas fa-times"></i>
</span>
<% end %>
<% end %> |
Beta Was this translation helpful? Give feedback.
-
I'm working on a super secret project called "Fusion". To make sure it is as flexible and robust as possible, I'd love to hear what kinds of things you are using on the front-end, what you are building, what you like/dislike, and maybe even some sample Lucky and JS code (bonus points for some code 💚)
Beta Was this translation helpful? Give feedback.
All reactions