Skip to content

Commit 66b0f37

Browse files
committed
[Feature] Add Empty component
Port the shadcn Empty component: a centered empty-state surface for when there is no data or content. Parts: Empty, EmptyHeader, EmptyMedia (default/icon variants), EmptyTitle, EmptyDescription, EmptyContent. Translates shadcn's cn-empty-* CSS layer to Tailwind v4 utilities. No JS. Docs page, route, controller, menu, site_files and MCP registry updated.
1 parent 43a75db commit 66b0f37

17 files changed

Lines changed: 370 additions & 0 deletions

File tree

docs/app/components/shared/components_list.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def components
3030
{name: "Date Picker", path: docs_date_picker_path},
3131
{name: "Dialog / Modal", path: docs_dialog_path},
3232
{name: "Dropdown Menu", path: docs_dropdown_menu_path},
33+
{name: "Empty", path: docs_empty_path},
3334
{name: "Form", path: docs_form_path},
3435
{name: "Hover Card", path: docs_hover_card_path},
3536
{name: "Input", path: docs_input_path},

docs/app/controllers/docs_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ def dropdown_menu
146146
render Views::Docs::DropdownMenu.new
147147
end
148148

149+
def empty
150+
render Views::Docs::Empty.new
151+
end
152+
149153
def form
150154
render Views::Docs::Form.new
151155
end

docs/app/lib/site_files.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class SiteFiles
103103
{title: "Date Picker", path: "/docs/date_picker", description: "Date picker component with input."},
104104
{title: "Dialog", path: "/docs/dialog", description: "Modal window that renders background content inert."},
105105
{title: "Dropdown Menu", path: "/docs/dropdown_menu", description: "Button-triggered menu for actions or functions."},
106+
{title: "Empty", path: "/docs/empty", description: "Empty state for when there is no data or content."},
106107
{title: "Form", path: "/docs/form", description: "Form fields with built-in client-side validations."},
107108
{title: "Hover Card", path: "/docs/hover_card", description: "Preview content exposed behind a link or trigger."},
108109
{title: "Input", path: "/docs/input", description: "Styled input field primitive."},

docs/app/views/docs/empty.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# frozen_string_literal: true
2+
3+
class Views::Docs::Empty < Views::Base
4+
def view_template
5+
component = "Empty"
6+
7+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
8+
render Docs::Header.new(title: "Empty", description: "Use the empty component to display a state when there is no data or content.")
9+
10+
Heading(level: 2) { "Usage" }
11+
12+
render Docs::VisualCodeExample.new(title: "Default", context: self) do
13+
<<~RUBY
14+
Empty do
15+
EmptyHeader do
16+
EmptyMedia(variant: :icon) do
17+
svg(xmlns: "http://www.w3.org/2000/svg", fill: "none", viewbox: "0 0 24 24", stroke_width: "1.5", stroke: "currentColor", class: "size-6") do |s|
18+
s.path(stroke_linecap: "round", stroke_linejoin: "round", d: "M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155")
19+
end
20+
end
21+
EmptyTitle { "No messages yet" }
22+
EmptyDescription { "Start a conversation to see your messages here." }
23+
end
24+
end
25+
RUBY
26+
end
27+
28+
render Docs::VisualCodeExample.new(title: "With action", context: self) do
29+
<<~RUBY
30+
Empty do
31+
EmptyHeader do
32+
EmptyMedia(variant: :icon) do
33+
svg(xmlns: "http://www.w3.org/2000/svg", fill: "none", viewbox: "0 0 24 24", stroke_width: "1.5", stroke: "currentColor", class: "size-6") do |s|
34+
s.path(stroke_linecap: "round", stroke_linejoin: "round", d: "M2.25 12.76c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 0 1 .865-.501 48.172 48.172 0 0 0 3.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z")
35+
end
36+
end
37+
EmptyTitle { "No projects" }
38+
EmptyDescription { "Get started by creating your first project." }
39+
end
40+
EmptyContent do
41+
Button { "Create project" }
42+
end
43+
end
44+
RUBY
45+
end
46+
47+
render Docs::VisualCodeExample.new(title: "Default media", context: self) do
48+
<<~RUBY
49+
Empty(class: "border-none") do
50+
EmptyHeader do
51+
EmptyMedia(variant: :default) do
52+
Avatar(size: :lg) do
53+
AvatarFallback { "RU" }
54+
end
55+
end
56+
EmptyTitle { "No team members" }
57+
EmptyDescription { "Invite your team to start collaborating." }
58+
end
59+
end
60+
RUBY
61+
end
62+
63+
render Components::ComponentSetup::Tabs.new(component_name: component)
64+
65+
# components
66+
render Docs::ComponentsTable.new(component_files(component))
67+
end
68+
end
69+
end

docs/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
get "date_picker", to: "docs#date_picker", as: :docs_date_picker
4848
get "dialog", to: "docs#dialog", as: :docs_dialog
4949
get "dropdown_menu", to: "docs#dropdown_menu", as: :docs_dropdown_menu
50+
get "empty", to: "docs#empty", as: :docs_empty
5051
get "form", to: "docs#form", as: :docs_form
5152
get "hover_card", to: "docs#hover_card", as: :docs_hover_card
5253
get "input", to: "docs#input", as: :docs_input

docs/public/llms-full.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,11 @@ This file expands the curated /llms.txt map into a compact reference that can be
203203
- URL: https://rubyui.com/docs/dropdown_menu
204204
- Summary: Button-triggered menu for actions or functions.
205205

206+
### Empty
207+
208+
- URL: https://rubyui.com/docs/empty
209+
- Summary: Empty state for when there is no data or content.
210+
206211
### Form
207212

208213
- URL: https://rubyui.com/docs/form

docs/public/llms.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Use the core docs first for installation, theming, dark mode, and customization
4545
- [Date Picker](https://rubyui.com/docs/date_picker): Date picker component with input.
4646
- [Dialog](https://rubyui.com/docs/dialog): Modal window that renders background content inert.
4747
- [Dropdown Menu](https://rubyui.com/docs/dropdown_menu): Button-triggered menu for actions or functions.
48+
- [Empty](https://rubyui.com/docs/empty): Empty state for when there is no data or content.
4849
- [Form](https://rubyui.com/docs/form): Form fields with built-in client-side validations.
4950
- [Hover Card](https://rubyui.com/docs/hover_card): Preview content exposed behind a link or trigger.
5051
- [Input](https://rubyui.com/docs/input): Styled input field primitive.

docs/public/sitemap.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@
180180
<changefreq>monthly</changefreq>
181181
<priority>0.7</priority>
182182
</url>
183+
<url>
184+
<loc>https://rubyui.com/docs/empty</loc>
185+
<changefreq>monthly</changefreq>
186+
<priority>0.7</priority>
187+
</url>
183188
<url>
184189
<loc>https://rubyui.com/docs/form</loc>
185190
<changefreq>monthly</changefreq>

gem/lib/ruby_ui/empty/empty.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module RubyUI
4+
class Empty < Base
5+
def view_template(&)
6+
div(**attrs, &)
7+
end
8+
9+
private
10+
11+
def default_attrs
12+
{
13+
data: {slot: "empty"},
14+
class: "flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-3xl border border-dashed p-12 text-center text-balance"
15+
}
16+
end
17+
end
18+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module RubyUI
4+
class EmptyContent < Base
5+
def view_template(&)
6+
div(**attrs, &)
7+
end
8+
9+
private
10+
11+
def default_attrs
12+
{
13+
data: {slot: "empty-content"},
14+
class: "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance"
15+
}
16+
end
17+
end
18+
end

0 commit comments

Comments
 (0)