mcpField
V
viewcomponent-coder

viewcomponent-coder

github.com/majesticlabs-dev开发5052 次安装7 周前更新
代码仓库

关于 viewcomponent-coder

viewcomponent-coder 是使用 ViewComponent 和 view_component-contrib 模式构建及维护 Rails UI 的技能,涵盖组件设计、预览、插槽、样式变体与隔离测试。

主要功能

  1. 01组件化 UI 模式
  2. 02插槽与样式变体
  3. 03Lookbook 预览
  4. 04组件隔离测试
  5. 05Rails 辅助方法与 SVG 指引

适用场景

  1. 01创建可复用的 Rails UI 组件
  2. 02添加插槽和视觉变体
  3. 03在 Lookbook 中预览组件
  4. 04独立测试组件
  5. 05将 partial 重构为组件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill viewcomponent-coder

安装后在 Claude Code 中输入/viewcomponent-coder确认已加载。

附带文件references/patterns.mdreferences/style-variants.md

详细介绍

ViewComponent 模式

使用 Evil Martians 的 view_component-contrib 模式,通过 ViewComponent 构建现代组件化 UI。

何时使用此技能

  • 创建 ViewComponent 类
  • 实现插槽和样式变体
  • 构建 Lookbook 预览
  • 单独测试组件
  • 将局部模板重构为组件

核心原则:组件优于局部模板

对于可复用 UI,优先使用 ViewComponent,而不是局部模板。

为什么使用 ViewComponent?
  • 比局部模板更好地封装逻辑
  • 可单独测试
  • 面向对象的方式,具有明确的契约
  • 支持 IDE 和类型安全
  • 性能优势(已编译的模板)

设置

# Gemfile
gem "view_component"
gem "view_component-contrib"  # Evil Martians patterns
gem "dry-initializer"         # Declarative initialization
gem "lookbook"                # Component previews
gem "inline_svg"              # SVG icons

使用 Rails 模板安装:

rails app:template LOCATION="https://railsbytes.com/script/zJosO5"

基类

# app/components/application_view_component.rb
class ApplicationViewComponent < ViewComponentContrib::Base
  extend Dry::Initializer
end

# spec/components/previews/application_view_component_preview.rb
class ApplicationViewComponentPreview < ViewComponentContrib::Preview::Base
  self.abstract_class = true
end

使用 dry-initializer 的基本组件

# app/components/button_component.rb
class ButtonComponent < ApplicationViewComponent
  option :text
  option :variant, default: -> { :primary }
  option :size, default: -> { :md }
end
<%# app/components/button_component.html.erb %>
<button class="btn btn-<%= variant %> btn-<%= size %>">
  <%= text %>
</button>

样式变体 DSL

使用 Style Variants DSL 替代手动编写 VARIANTS 哈希:

class ButtonComponent < ApplicationViewComponent
  include ViewComponentContrib::StyleVariants

  option :text
  option :color, default: -> { :primary }
  option :size, default: -> { :md }

  style do
    base { %w[font-medium rounded-full] }

    variants {
      color {
        primary { %w[bg-blue-500 text-white] }
        secondary { %w[bg-gray-500 text-white] }
        danger { %w[bg-red-500 text-white] }
      }
      size {
        sm { "text-sm px-2 py-1" }
        md { "text-base px-4 py-2" }
        lg { "text-lg px-6 py-3" }
      }
    }

    # Apply when multiple conditions match
    compound(size: :lg, color: :primary) { "uppercase" }

    defaults { { color: :primary, size: :md } }
  end
end
<button class="<%= style(color:, size:) %>">
  <%= text %>
</button>

带插槽的组件

class CardComponent < ApplicationViewComponent
  renders_one :header
  renders_one :footer
  renders_many :actions
end
<%= render CardComponent.new do |card| %>
  <% card.with_header do %>
    <h3>Title</h3>
  <% end %>

  <p>Body content</p>

  <% card.with_action do %>
    <%= helpers.link_to "Edit", edit_path %>
  <% end %>
<% end %>

重要规则

1. 使用 helpers. 作为 Rails 辅助方法的前缀

<%# CORRECT %>
<%= helpers.link_to "Home", root_path %>
<%= helpers.image_tag "logo.png" %>
<%= helpers.inline_svg_tag "icons/user.svg" %>

<%# WRONG - will fail in component context %>
<%= link_to "Home", root_path %>

例外:t() i18n 辅助方法不需要前缀:

<%= t('.title') %>

2. 将 SVG 图标存为单独文件

将 SVG 存放在 app/assets/images/icons/ 中,并使用 inline_svg gem 渲染:

<%= helpers.inline_svg_tag "icons/user.svg", class: "w-5 h-5" %>

不要在 Ruby 代码中内嵌 SVG 标记,请改用单独的文件。

条件渲染

class AlertComponent < ApplicationViewComponent
  option :message
  option :type, default: -> { :info }
  option :dismissible, default: -> { true }

  # Skip rendering if no message
  def render?
    message.present?
  end
end

Lookbook 预览

# spec/components/previews/button_component_preview.rb
class ButtonComponentPreview < ApplicationViewComponentPreview
  def default
    render ButtonComponent.new(text: "Click me")
  end

  def primary
    render ButtonComponent.new(text: "Primary", color: :primary)
  end

  def all_sizes
    render_with(wrapper: :flex_row) do
      safe_join([
        render(ButtonComponent.new(text: "Small", size: :sm)),
        render(ButtonComponent.new(text: "Medium", size: :md)),
        render(ButtonComponent.new(text: "Large", size: :lg))
      ])
    end
  end
end

访问地址:http://localhost:3000/lookbook

测试组件

RSpec.describe ButtonComponent, type: :component do
  it "renders button text" do
    render_inline(ButtonComponent.new(text: "Click me"))
    expect(page).to have_button("Click me")
  end

  it "applies style variant classes" do
    render_inline(ButtonComponent.new(text: "Save", color: :primary, size: :lg))
    expect(page).to have_css("button.bg-blue-500.text-lg")
  end
end

详细参考资料

如需了解高级模式和示例:

  • references/patterns.md - 插槽、集合、多态组件、Turbo 集成
  • references/style-variants.md - 完整的 Style Variants DSL、复合变体、TailwindMerge

评论

支持 Markdown