From 66b0f376850dd5c47e30b102e4db33cb12a961a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Djalma=20Ara=C3=BAjo?= Date: Tue, 30 Jun 2026 17:40:20 -0300 Subject: [PATCH] [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. --- docs/app/components/shared/components_list.rb | 1 + docs/app/controllers/docs_controller.rb | 4 ++ docs/app/lib/site_files.rb | 1 + docs/app/views/docs/empty.rb | 69 +++++++++++++++++++ docs/config/routes.rb | 1 + docs/public/llms-full.txt | 5 ++ docs/public/llms.txt | 1 + docs/public/sitemap.xml | 5 ++ gem/lib/ruby_ui/empty/empty.rb | 18 +++++ gem/lib/ruby_ui/empty/empty_content.rb | 18 +++++ gem/lib/ruby_ui/empty/empty_description.rb | 18 +++++ gem/lib/ruby_ui/empty/empty_docs.rb | 69 +++++++++++++++++++ gem/lib/ruby_ui/empty/empty_header.rb | 18 +++++ gem/lib/ruby_ui/empty/empty_media.rb | 31 +++++++++ gem/lib/ruby_ui/empty/empty_title.rb | 18 +++++ gem/test/ruby_ui/empty_test.rb | 39 +++++++++++ mcp/data/registry.json | 54 +++++++++++++++ 17 files changed, 370 insertions(+) create mode 100644 docs/app/views/docs/empty.rb create mode 100644 gem/lib/ruby_ui/empty/empty.rb create mode 100644 gem/lib/ruby_ui/empty/empty_content.rb create mode 100644 gem/lib/ruby_ui/empty/empty_description.rb create mode 100644 gem/lib/ruby_ui/empty/empty_docs.rb create mode 100644 gem/lib/ruby_ui/empty/empty_header.rb create mode 100644 gem/lib/ruby_ui/empty/empty_media.rb create mode 100644 gem/lib/ruby_ui/empty/empty_title.rb create mode 100644 gem/test/ruby_ui/empty_test.rb diff --git a/docs/app/components/shared/components_list.rb b/docs/app/components/shared/components_list.rb index eae5c84d..28112700 100644 --- a/docs/app/components/shared/components_list.rb +++ b/docs/app/components/shared/components_list.rb @@ -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}, diff --git a/docs/app/controllers/docs_controller.rb b/docs/app/controllers/docs_controller.rb index d5e8473d..6b9d5a83 100644 --- a/docs/app/controllers/docs_controller.rb +++ b/docs/app/controllers/docs_controller.rb @@ -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 diff --git a/docs/app/lib/site_files.rb b/docs/app/lib/site_files.rb index b0984020..7eda2dfa 100644 --- a/docs/app/lib/site_files.rb +++ b/docs/app/lib/site_files.rb @@ -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."}, diff --git a/docs/app/views/docs/empty.rb b/docs/app/views/docs/empty.rb new file mode 100644 index 00000000..c69b3f43 --- /dev/null +++ b/docs/app/views/docs/empty.rb @@ -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 diff --git a/docs/config/routes.rb b/docs/config/routes.rb index 59702c7f..d739ebe2 100644 --- a/docs/config/routes.rb +++ b/docs/config/routes.rb @@ -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 diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 3bd2e280..4ab58847 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -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 diff --git a/docs/public/llms.txt b/docs/public/llms.txt index 722ad3ac..f2269efb 100644 --- a/docs/public/llms.txt +++ b/docs/public/llms.txt @@ -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. diff --git a/docs/public/sitemap.xml b/docs/public/sitemap.xml index e3f5c942..518381a2 100644 --- a/docs/public/sitemap.xml +++ b/docs/public/sitemap.xml @@ -180,6 +180,11 @@ monthly 0.7 + + https://rubyui.com/docs/empty + monthly + 0.7 + https://rubyui.com/docs/form monthly diff --git a/gem/lib/ruby_ui/empty/empty.rb b/gem/lib/ruby_ui/empty/empty.rb new file mode 100644 index 00000000..1c315f8f --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty.rb @@ -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 diff --git a/gem/lib/ruby_ui/empty/empty_content.rb b/gem/lib/ruby_ui/empty/empty_content.rb new file mode 100644 index 00000000..eae0b46d --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty_content.rb @@ -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 diff --git a/gem/lib/ruby_ui/empty/empty_description.rb b/gem/lib/ruby_ui/empty/empty_description.rb new file mode 100644 index 00000000..147bcafb --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty_description.rb @@ -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 diff --git a/gem/lib/ruby_ui/empty/empty_docs.rb b/gem/lib/ruby_ui/empty/empty_docs.rb new file mode 100644 index 00000000..c69b3f43 --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty_docs.rb @@ -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 diff --git a/gem/lib/ruby_ui/empty/empty_header.rb b/gem/lib/ruby_ui/empty/empty_header.rb new file mode 100644 index 00000000..8207b0cc --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty_header.rb @@ -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 diff --git a/gem/lib/ruby_ui/empty/empty_media.rb b/gem/lib/ruby_ui/empty/empty_media.rb new file mode 100644 index 00000000..231d110b --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty_media.rb @@ -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 diff --git a/gem/lib/ruby_ui/empty/empty_title.rb b/gem/lib/ruby_ui/empty/empty_title.rb new file mode 100644 index 00000000..26570c1b --- /dev/null +++ b/gem/lib/ruby_ui/empty/empty_title.rb @@ -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 diff --git a/gem/test/ruby_ui/empty_test.rb b/gem/test/ruby_ui/empty_test.rb new file mode 100644 index 00000000..75205f50 --- /dev/null +++ b/gem/test/ruby_ui/empty_test.rb @@ -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 diff --git a/mcp/data/registry.json b/mcp/data/registry.json index 4922db45..aff6f5a4 100644 --- a/mcp/data/registry.json +++ b/mcp/data/registry.json @@ -1435,6 +1435,60 @@ } ] }, + "empty": { + "name": "Empty", + "description": "Use the empty component to display a state when there is no data or content.", + "files": [ + { + "path": "empty.rb", + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class Empty < Base\n def view_template(&)\n div(**attrs, &)\n end\n\n private\n\n def default_attrs\n {\n data: {slot: \"empty\"},\n 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\"\n }\n end\n end\nend\n" + }, + { + "path": "empty_content.rb", + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class EmptyContent < Base\n def view_template(&)\n div(**attrs, &)\n end\n\n private\n\n def default_attrs\n {\n data: {slot: \"empty-content\"},\n class: \"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance\"\n }\n end\n end\nend\n" + }, + { + "path": "empty_description.rb", + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class EmptyDescription < Base\n def view_template(&)\n div(**attrs, &)\n end\n\n private\n\n def default_attrs\n {\n data: {slot: \"empty-description\"},\n class: \"text-sm leading-relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary\"\n }\n end\n end\nend\n" + }, + { + "path": "empty_header.rb", + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class EmptyHeader < Base\n def view_template(&)\n div(**attrs, &)\n end\n\n private\n\n def default_attrs\n {\n data: {slot: \"empty-header\"},\n class: \"flex max-w-sm flex-col items-center gap-2\"\n }\n end\n end\nend\n" + }, + { + "path": "empty_media.rb", + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class EmptyMedia < Base\n VARIANTS = {\n default: \"bg-transparent\",\n icon: \"size-10 rounded-xl bg-muted text-foreground [&_svg:not([class*='size-'])]:size-5\"\n }\n\n def initialize(variant: :default, **attrs)\n @variant = variant\n super(**attrs)\n end\n\n def view_template(&)\n div(**attrs, &)\n end\n\n private\n\n def default_attrs\n {\n data: {slot: \"empty-icon\", variant: @variant},\n class: [\n \"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n VARIANTS[@variant]\n ]\n }\n end\n end\nend\n" + }, + { + "path": "empty_title.rb", + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class EmptyTitle < Base\n def view_template(&)\n div(**attrs, &)\n end\n\n private\n\n def default_attrs\n {\n data: {slot: \"empty-title\"},\n class: \"text-lg font-medium tracking-tight\"\n }\n end\n end\nend\n" + } + ], + "dependencies": { + "components": [], + "js_packages": [], + "gems": [] + }, + "install_command": "rails g ruby_ui:component Empty", + "docs_markdown": "# Empty\n\nUse the empty component to display a state when there is no data or content.\n\n## Usage\n\n### Default\n\n```ruby\nEmpty do\n EmptyHeader do\n EmptyMedia(variant: :icon) do\n 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|\n 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\")\n end\n end\n EmptyTitle { \"No messages yet\" }\n EmptyDescription { \"Start a conversation to see your messages here.\" }\n end\nend\n```\n\n### With action\n\n```ruby\nEmpty do\n EmptyHeader do\n EmptyMedia(variant: :icon) do\n 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|\n 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\")\n end\n end\n EmptyTitle { \"No projects\" }\n EmptyDescription { \"Get started by creating your first project.\" }\n end\n EmptyContent do\n Button { \"Create project\" }\n end\nend\n```\n\n### Default media\n\n```ruby\nEmpty(class: \"border-none\") do\n EmptyHeader do\n EmptyMedia(variant: :default) do\n Avatar(size: :lg) do\n AvatarFallback { \"RU\" }\n end\n end\n EmptyTitle { \"No team members\" }\n EmptyDescription { \"Invite your team to start collaborating.\" }\n end\nend\n```", + "examples": [ + { + "title": "Default", + "code": "Empty do\n EmptyHeader do\n EmptyMedia(variant: :icon) do\n 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|\n 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\")\n end\n end\n EmptyTitle { \"No messages yet\" }\n EmptyDescription { \"Start a conversation to see your messages here.\" }\n end\nend\n", + "language": "ruby" + }, + { + "title": "With action", + "code": "Empty do\n EmptyHeader do\n EmptyMedia(variant: :icon) do\n 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|\n 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\")\n end\n end\n EmptyTitle { \"No projects\" }\n EmptyDescription { \"Get started by creating your first project.\" }\n end\n EmptyContent do\n Button { \"Create project\" }\n end\nend\n", + "language": "ruby" + }, + { + "title": "Default media", + "code": "Empty(class: \"border-none\") do\n EmptyHeader do\n EmptyMedia(variant: :default) do\n Avatar(size: :lg) do\n AvatarFallback { \"RU\" }\n end\n end\n EmptyTitle { \"No team members\" }\n EmptyDescription { \"Invite your team to start collaborating.\" }\n end\nend\n", + "language": "ruby" + } + ] + }, "form": { "name": "Form", "description": "Building forms with built-in client-side validations.",