Skip to content

Commit

Permalink
Adds card and sidebar components.
Browse files Browse the repository at this point in the history
  • Loading branch information
mariochavez committed Jan 17, 2025
1 parent e9c3d45 commit c4706a3
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 7 deletions.
16 changes: 15 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
maquina-components (0.1.1)
maquina-components (0.1.2)
rails (>= 8.0.0)

GEM
Expand Down Expand Up @@ -121,8 +121,22 @@ GEM
timeout
net-smtp (0.5.0)
nio4r (2.7.4)
nokogiri (1.18.1-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.1-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.1-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.1-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.1-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.1-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.1-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.1-x86_64-linux-musl)
racc (~> 1.4)
parallel (1.26.3)
parser (3.3.6.0)
ast (~> 2.4.1)
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

Modern UI components for Ruby on Rails, powered by TailwindCSS and Stimulus

# Maquina Components

Modern UI components for Ruby on Rails, powered by TailwindCSS and Stimulus

![Dashboard Example](/imgs/home.png)

![Form Example](/imgs/new.png)

## Overview

Maquina Components provides a collection of ready-to-use UI components for Ruby on Rails applications. Built with ERB, TailwindCSS 4.0, and Stimulus, it offers a modern and maintainable approach to building beautiful user interfaces without the complexity of JavaScript frameworks.
Maquina Components provides a collection of ready-to-use UI components for Ruby on Rails applications. Built with ERB,
TailwindCSS 4.0, and Stimulus, it offers a modern and maintainable approach to building beautiful user interfaces
without the complexity of JavaScript frameworks.

### Key Features

Expand Down
99 changes: 99 additions & 0 deletions app/assets/stylesheets/maquina_components.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
@utility button-base {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
/* gap-2 */
white-space: nowrap;
border-radius: 0.375rem;
/* rounded-md */
font-size: 0.875rem;
/* text-sm */
font-weight: 500;
/* font-medium */
height: 2.25rem;
/* h-9 */
padding: 0.5rem 1rem;
/* px-4 py-2 */
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;

&:focus-visible {
outline: none;
ring-width: 1px;
ring-color: var(--color-ring);
}

&:disabled {
pointer-events: none;
opacity: 0.5;
}

& svg {
pointer-events: none;
width: 1rem;
/* size-4 */
height: 1rem;
/* size-4 */
flex-shrink: 0;
}
}

/* Form */
.form-base {
@apply flex flex-col w-full gap-6;
}

.form-group {
@apply flex flex-col gap-2;
}

.form-label {
@apply text-foreground text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70;
}

.form-input {
@apply flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm;
}

.form-select {
@apply col-start-1 row-start-1 w-full appearance-none rounded-md bg-transparent py-1.5 pl-3 pr-8 text-base text-foreground border border-input shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm/6;
}

.form-select-group {
@apply grid grid-cols-1;

& svg {
@apply pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-muted-foreground sm:size-4;
}
}

.form-help-text {
@apply text-sm text-muted-foreground;
}

.form-error-text {
@apply text-sm text-destructive;
}

/* Buttons */
.button-primary {
@apply button-base bg-primary text-primary-foreground hover:bg-primary/90;
}

.button-secondary {
@apply button-base bg-secondary text-secondary-foreground hover:bg-secondary/80;
}

.button-ghost {
@apply button-base hover:bg-secondary hover:text-secondary-foreground;
}

.button-outline {
@apply button-base border border-border bg-background shadow-sm hover:bg-secondary/40 hover:text-secondary-foreground;
}

.button-destructive {
@apply button-base bg-destructive text-destructive shadow-sm hover:bg-destructive/90;
}
124 changes: 124 additions & 0 deletions app/helpers/components/icons_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
module Components
module IconsHelper
def icon_for(name, options = {})
return nil unless name

svg = icon_svg_for(name.to_sym)
return nil unless svg

css_classes = options[:class]
svg = svg.gsub('class="', "class=\"#{css_classes} ")

if options[:stroke_width]
svg = svg.gsub('stroke-width="2"', "stroke-width=\"#{options[:stroke_width]}\"")
end

svg.html_safe
end

private

def icon_svg_for(name)
case name
when :dollar
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<line x1="12" y1="2" x2="12" y2="22"></line>
<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
</svg>
SVG
when :users
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
SVG
when :credit_card
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect width="20" height="14" x="2" y="5" rx="2"></rect>
<line x1="2" y1="10" x2="22" y2="10"></line>
</svg>
SVG
when :activity
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
</svg>
SVG
when :trend_up
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<polyline points="22 7 13.5 15.5 8.5 10.5 2 17"></polyline>
<polyline points="16 7 22 7 22 13"></polyline>
</svg>
SVG
when :trend_down
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<polyline points="22 17 13.5 8.5 8.5 13.5 2 7"></polyline>
<polyline points="16 17 22 17 22 11"></polyline>
</svg>
SVG
when :clock
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
SVG
when :money
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<rect width="20" height="12" x="2" y="6" rx="2"></rect>
<circle cx="12" cy="12" r="2"></circle>
<path d="M6 12h.01M18 12h.01"></path>
</svg>
SVG
when :line_chart
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<path d="M3 3v18h18"/><path d="m19 9-5 5-4-4-3 3"/>
</svg>
SVG
when :piggy_bank
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<path d="M19 5c-1.5 0-2.8 1.4-3 2-3.5-1.5-11-.3-11 5 0 1.8 0 3 2 4.5V20h4v-2h3v2h4v-4c1-.5 1.7-1 2-2h2v-4h-2c0-1-.5-1.5-1-2h0V5z"/>
<path d="M2 9v1c0 1.1.9 2 2 2h1"/>
<path d="M16 11h0"/>
</svg>
SVG
when :arrow_left
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<line x1="19" y1="12" x2="5" y2="12"></line>
<polyline points="12 19 5 12 12 5"></polyline>
</svg>
SVG
when :select_chevron
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="" aria-hidden="true">
<path d="m6 9 6 6 6-6"></path>
</svg>
SVG
when :check
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<path d="M20 6 9 17l-5-5"/>
</svg>
SVG
when :circle_alert
<<~SVG.freeze
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="">
<circle cx="12" cy="12" r="10"/>
<line x1="12" x2="12" y1="8" y2="12"/>
<line x1="12" x2="12.01" y1="16" y2="16"/>
</svg>
SVG
end
end
end
end
15 changes: 15 additions & 0 deletions app/helpers/components/pagination_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Components
module PaginationHelper
def paginated_page_param(pagy, page)
@page_param ||= pagy.vars[:page_param] || Pagy::VARS[:page_param]
@query_parameters ||= request.query_parameters

@query_parameters.merge(@page_param => page)
end

def paginated_path(path_helper, pagy, page, param)
page_query = paginated_page_param(pagy, page)
send(path_helper, param, page_query)
end
end
end
10 changes: 10 additions & 0 deletions app/views/components/_card.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<%# locals: (custom_classes: "") -%>

<div
class="<%= class_names(
"rounded-xl border border-border bg-card text-card-foreground shadow",
custom_classes
) %>"
>
<%= yield %>
</div>
5 changes: 5 additions & 0 deletions app/views/components/_card_content.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%# locals: (custom_classes: "") -%>

<div class="<%= class_names("p-6 pt-0", custom_classes) %>">
<%= yield %>
</div>
8 changes: 8 additions & 0 deletions app/views/components/_card_header.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<%# locals: (title:, custom_classes: "", icon: nil, icon_classes: "h-4 w-4 text-muted-foreground") -%>

<div
class="<%= class_names("p-6 flex flex-row items-center justify-between space-y-0 pb-2", custom_classes) %>"
>
<h3 class="tracking-tight text-sm font-medium"><%= title %></h3>
<%= icon_for(icon, class: icon_classes) if icon.present? %>
</div>
34 changes: 34 additions & 0 deletions app/views/components/_pagination.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<%# locals: (pagy:, route_helper:, param: nil, data: {turbo_action: :replace}) %>
<nav class="mx-auto flex w-full justify-center mt-4">
<ul class="flex flex-row items-center gap-1">
<li>
<% if pagy.prev.present? %>
<%= link_to "Previo",
paginated_path(route_helper, pagy, pagy.prev, param),
class: "button-ghost",
data: data %>
<% else %>
<button class="button-ghost" disabled>Previo</button>
<% end %>
</li>
<% pagy.series.each do |page| %>
<li>
<%= link_to page,
paginated_path(route_helper, pagy, page, param),
class: class_names("button-ghost", "button-outline": page.is_a?(String)),
data: data %>
</li>
<% end %>

<li>
<% if pagy.next.present? %>
<%= link_to "Siguiente",
paginated_path(route_helper, pagy, pagy.next, param),
class: "button-ghost",
data: data %>
<% else %>
<button class="button-ghost" disabled>Siguiente</button>
<% end %>
</li>
</ul>
</nav>
30 changes: 30 additions & 0 deletions app/views/components/_sidebar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<aside
class="group peer hidden md:block text-sidebar-foreground"
data-controller="sidebars"
data-state="expanded"
data-collapsible=""
id="sidebar"
>
<!-- Mobile overlay -->
<div
class="
inset-0 bg-black/80 lg:hidden transition-opacity duration-300 hidden
"
data-sidebar-target="overlay"
data-action="click->sidebar#toggleMobile"
>
</div>
<!-- Sidebar container -->
<div
class="
duration-200 fixed inset-y-0 z-10 h-svh w-(--sidebar-width)
transition-[left,right,width] ease-linear md:flex left-0 border-r
border-sidebar-border group-data-[collapsible=icon]:w-[--sidebar-width-icon]
"
data-sidebar-target="container"
>
<div class=" flex h-full w-full flex-col bg-sidebar">
<%= yield %>
</div>
</div>
</aside>
8 changes: 8 additions & 0 deletions app/views/components/_sidebar_content.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div
class="
flex min-h-0 flex-1 flex-col gap-2 overflow-auto
group-data-[collapsible=icon]:overflow-hidden
"
>
<%= yield %>
</div>
Loading

0 comments on commit c4706a3

Please sign in to comment.