Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/app/components/shared/components_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def components
{name: "Date Picker", path: docs_date_picker_path},
{name: "Dialog / Modal", path: docs_dialog_path},
{name: "Dropdown Menu", path: docs_dropdown_menu_path},
{name: "Empty", path: docs_empty_path},
{name: "Form", path: docs_form_path},
{name: "Hover Card", path: docs_hover_card_path},
{name: "Input", path: docs_input_path},
Expand Down
4 changes: 4 additions & 0 deletions docs/app/controllers/docs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ def dropdown_menu
render Views::Docs::DropdownMenu.new
end

def empty
render Views::Docs::Empty.new
end

def form
render Views::Docs::Form.new
end
Expand Down
1 change: 1 addition & 0 deletions docs/app/lib/site_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class SiteFiles
{title: "Date Picker", path: "/docs/date_picker", description: "Date picker component with input."},
{title: "Dialog", path: "/docs/dialog", description: "Modal window that renders background content inert."},
{title: "Dropdown Menu", path: "/docs/dropdown_menu", description: "Button-triggered menu for actions or functions."},
{title: "Empty", path: "/docs/empty", description: "Empty state for when there is no data or content."},
{title: "Form", path: "/docs/form", description: "Form fields with built-in client-side validations."},
{title: "Hover Card", path: "/docs/hover_card", description: "Preview content exposed behind a link or trigger."},
{title: "Input", path: "/docs/input", description: "Styled input field primitive."},
Expand Down
69 changes: 69 additions & 0 deletions docs/app/views/docs/empty.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

class Views::Docs::Empty < Views::Base
def view_template
component = "Empty"

div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
render Docs::Header.new(title: "Empty", description: "Use the empty component to display a state when there is no data or content.")

Heading(level: 2) { "Usage" }

render Docs::VisualCodeExample.new(title: "Default", context: self) do
<<~RUBY
Empty do
EmptyHeader do
EmptyMedia(variant: :icon) do
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|
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")
end
end
EmptyTitle { "No messages yet" }
EmptyDescription { "Start a conversation to see your messages here." }
end
end
RUBY
end

render Docs::VisualCodeExample.new(title: "With action", context: self) do
<<~RUBY
Empty do
EmptyHeader do
EmptyMedia(variant: :icon) do
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|
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")
end
end
EmptyTitle { "No projects" }
EmptyDescription { "Get started by creating your first project." }
end
EmptyContent do
Button { "Create project" }
end
end
RUBY
end

render Docs::VisualCodeExample.new(title: "Default media", context: self) do
<<~RUBY
Empty(class: "border-none") do
EmptyHeader do
EmptyMedia(variant: :default) do
Avatar(size: :lg) do
AvatarFallback { "RU" }
end
end
EmptyTitle { "No team members" }
EmptyDescription { "Invite your team to start collaborating." }
end
end
RUBY
end

render Components::ComponentSetup::Tabs.new(component_name: component)

# components
render Docs::ComponentsTable.new(component_files(component))
end
end
end
1 change: 1 addition & 0 deletions docs/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
get "date_picker", to: "docs#date_picker", as: :docs_date_picker
get "dialog", to: "docs#dialog", as: :docs_dialog
get "dropdown_menu", to: "docs#dropdown_menu", as: :docs_dropdown_menu
get "empty", to: "docs#empty", as: :docs_empty
get "form", to: "docs#form", as: :docs_form
get "hover_card", to: "docs#hover_card", as: :docs_hover_card
get "input", to: "docs#input", as: :docs_input
Expand Down
5 changes: 5 additions & 0 deletions docs/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ This file expands the curated /llms.txt map into a compact reference that can be
- URL: https://rubyui.com/docs/dropdown_menu
- Summary: Button-triggered menu for actions or functions.

### Empty

- URL: https://rubyui.com/docs/empty
- Summary: Empty state for when there is no data or content.

### Form

- URL: https://rubyui.com/docs/form
Expand Down
1 change: 1 addition & 0 deletions docs/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Use the core docs first for installation, theming, dark mode, and customization
- [Date Picker](https://rubyui.com/docs/date_picker): Date picker component with input.
- [Dialog](https://rubyui.com/docs/dialog): Modal window that renders background content inert.
- [Dropdown Menu](https://rubyui.com/docs/dropdown_menu): Button-triggered menu for actions or functions.
- [Empty](https://rubyui.com/docs/empty): Empty state for when there is no data or content.
- [Form](https://rubyui.com/docs/form): Form fields with built-in client-side validations.
- [Hover Card](https://rubyui.com/docs/hover_card): Preview content exposed behind a link or trigger.
- [Input](https://rubyui.com/docs/input): Styled input field primitive.
Expand Down
5 changes: 5 additions & 0 deletions docs/public/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://rubyui.com/docs/empty</loc>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://rubyui.com/docs/form</loc>
<changefreq>monthly</changefreq>
Expand Down
18 changes: 18 additions & 0 deletions gem/lib/ruby_ui/empty/empty.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module RubyUI
class Empty < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
data: {slot: "empty"},
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"
}
end
end
end
18 changes: 18 additions & 0 deletions gem/lib/ruby_ui/empty/empty_content.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module RubyUI
class EmptyContent < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
data: {slot: "empty-content"},
class: "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance"
}
end
end
end
18 changes: 18 additions & 0 deletions gem/lib/ruby_ui/empty/empty_description.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module RubyUI
class EmptyDescription < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
data: {slot: "empty-description"},
class: "text-sm leading-relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary"
}
end
end
end
69 changes: 69 additions & 0 deletions gem/lib/ruby_ui/empty/empty_docs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

class Views::Docs::Empty < Views::Base
def view_template
component = "Empty"

div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
render Docs::Header.new(title: "Empty", description: "Use the empty component to display a state when there is no data or content.")

Heading(level: 2) { "Usage" }

render Docs::VisualCodeExample.new(title: "Default", context: self) do
<<~RUBY
Empty do
EmptyHeader do
EmptyMedia(variant: :icon) do
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|
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")
end
end
EmptyTitle { "No messages yet" }
EmptyDescription { "Start a conversation to see your messages here." }
end
end
RUBY
end

render Docs::VisualCodeExample.new(title: "With action", context: self) do
<<~RUBY
Empty do
EmptyHeader do
EmptyMedia(variant: :icon) do
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|
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")
end
end
EmptyTitle { "No projects" }
EmptyDescription { "Get started by creating your first project." }
end
EmptyContent do
Button { "Create project" }
end
end
RUBY
end

render Docs::VisualCodeExample.new(title: "Default media", context: self) do
<<~RUBY
Empty(class: "border-none") do
EmptyHeader do
EmptyMedia(variant: :default) do
Avatar(size: :lg) do
AvatarFallback { "RU" }
end
end
EmptyTitle { "No team members" }
EmptyDescription { "Invite your team to start collaborating." }
end
end
RUBY
end

render Components::ComponentSetup::Tabs.new(component_name: component)

# components
render Docs::ComponentsTable.new(component_files(component))
end
end
end
18 changes: 18 additions & 0 deletions gem/lib/ruby_ui/empty/empty_header.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module RubyUI
class EmptyHeader < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
data: {slot: "empty-header"},
class: "flex max-w-sm flex-col items-center gap-2"
}
end
end
end
31 changes: 31 additions & 0 deletions gem/lib/ruby_ui/empty/empty_media.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module RubyUI
class EmptyMedia < Base
VARIANTS = {
default: "bg-transparent",
icon: "size-10 rounded-xl bg-muted text-foreground [&_svg:not([class*='size-'])]:size-5"
}

def initialize(variant: :default, **attrs)
@variant = variant
super(**attrs)
end

def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
data: {slot: "empty-icon", variant: @variant},
class: [
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
VARIANTS[@variant]
]
}
end
end
end
18 changes: 18 additions & 0 deletions gem/lib/ruby_ui/empty/empty_title.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module RubyUI
class EmptyTitle < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
data: {slot: "empty-title"},
class: "text-lg font-medium tracking-tight"
}
end
end
end
39 changes: 39 additions & 0 deletions gem/test/ruby_ui/empty_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

require "test_helper"

class RubyUI::EmptyTest < ComponentTest
def test_renders_full_structure
output = phlex do
RubyUI.Empty do
RubyUI.EmptyHeader do
RubyUI.EmptyMedia(variant: :icon) { "icon" }
RubyUI.EmptyTitle { "Nothing here" }
RubyUI.EmptyDescription { "No content yet." }
end
RubyUI.EmptyContent { "action" }
end
end

assert_match(/data-slot="empty"/, output)
assert_match(/data-slot="empty-header"/, output)
assert_match(/data-slot="empty-icon"/, output)
assert_match(/data-slot="empty-title"/, output)
assert_match(/data-slot="empty-description"/, output)
assert_match(/data-slot="empty-content"/, output)
assert_match(/Nothing here/, output)
end

def test_media_default_variant
output = phlex { RubyUI.EmptyMedia { "x" } }

assert_match(/data-variant="default"/, output)
end

def test_media_icon_variant
output = phlex { RubyUI.EmptyMedia(variant: :icon) { "x" } }

assert_match(/data-variant="icon"/, output)
assert_match(/bg-muted/, output)
end
end
Loading