[![license](https://img.shields.io/badge/License-MIT-purple.svg)](LICENSE) [![Gem Version](https://img.shields.io/gem/v/amber_component.svg?style=flat)](https://rubygems.org/gems/amber_component) [![Maintainability](https://api.codeclimate.com/v1/badges/ad84af499e9791933a87/maintainability)](https://codeclimate.com/github/amber-ruby/amber_component/maintainability) [![CI badge](https://github.com/amber-ruby/amber_component/actions/workflows/ci_ruby.yml/badge.svg)](https://github.com/amber-ruby/amber_component/actions/workflows/ci_ruby.yml) [![Coverage Badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/Verseth/6a095c79278b074d79feaa4f8ceeb2a8/raw/amber_component__heads_main.json)](https://github.com/amber-ruby/amber_component/actions/workflows/ci_ruby.yml) [![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/amber_component)]((https://rubygems.org/gems/amber_component)) # AmberComponent AmberComponent is a simple component library which seamlessly hooks into your Rails project and allows you to create simple backend components. They work like mini controllers which are bound with their view and stylesheet. Created by [Garbus Beach](https://github.com/garbusbeach) and [Mateusz Drewniak](https://github.com/Verseth). ## Getting started You can read a lot more about AmberComponent in its [official docs](https://ambercomponent.com). ## Installation Install the gem and add to the application's Gemfile by executing: $ bundle add amber_component If bundler is not being used to manage dependencies, install the gem by executing: $ gem install amber_component If you're using a Rails application there's an installation generator that you should run: ```sh $ bin/rails generate amber_component:install ``` ## Usage ## Components Components are located under `app/components`. Every component consists of: - a Ruby file which defines its properties, encapsulates logic and may implement helper methods (like a controller) - a view/template file (html.erb, haml, slim etc.) - a style file (css, scss, sass etc.) An individual component which implements a button may look like this. ```ruby # app/components/button_component.rb class ButtonComponent < AmberComponent::Base prop :label, required: true end ``` ```html
<%= label %>
``` ```scss // app/components/button_component/style.scss .button_component { background-color: indigo; border-radius: 1rem; transition-duration: 500ms; &:hover { background-color: blue; } } ``` You can render this component in other components or in a Rails view. ```html

We're inside FooController

<%= button_component label: 'Click me!' %> <%= ButtonComponent.call label: 'Click me!' %> ``` Or even directly in Ruby ```ruby # Calling a method on the component class. Outputs an HTML string. ButtonComponent.call label: 'Click me!' #=> '
Click me!
' ``` ### Rails helpers inside component templates Component views/template files can make use of all ActionView helpers and Rails route helpers. This makes component views very flexible and convenient. ```erb <%= form_with url: sign_up_path, class: "login_form_component" do |f| %> <%= f.label :first_name %> <%= f.text_field :first_name %> <%= f.label :last_name %> <%= f.text_field :last_name %> <%= f.label :email, "Email Address" %> <%= f.text_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirm Password" %> <%= f.password_field :password_confirmation %> <%= f.submit "Create account" %> <% end %> ``` ### Component properties There is a neat prop DSL. ```ruby # app/components/comment_component.rb class CommentComponent < ApplicationComponent # will raise an error when not present prop :body, required: true # will raise an error when an object of a different # class is received (uses `is_a?`) prop :author, type: User, allow_nil: true # the default value prop :date, default: -> { DateTime.now } end ``` Props can be passed as keyword arguments to the `::call` method of the component class or the helper method. ```ruby CommentComponent.call body: 'Foo bar', author: User.first # only in views and other components comment_component body: 'Foo bar', author: User.first ``` ### Overriding prop getters and setters Getters and setters for properties are defined in a module which means that you can override them and call them with `super`. ```ruby # app/components/priority_icon_component.rb class PriorityIconComponent < ApplicationComponent PriorityStruct = Struct.new :icon, :color PRIORITY_MAP = { low: PriorityStruct.new('fa-solid fa-chevrons-down', 'green'), medium: PriorityStruct.new('fa-solid fa-chevron-up', 'yellow'), high: PriorityStruct.new('fa-solid fa-chevrons-up', 'red') } prop :severity, default: -> { :low } def severity=(val) # super will call the original # implementation of the setter super(PRIORITY_MAP[val]) end end ``` ```html ``` ### Helper methods Defining helper methods which are available in the template is extremely easy. Just define a method on the component class. ```ruby # app/components/comment_component.rb class CommentComponent < ApplicationComponent # you can also include helper modules include SomeHelper prop :body, required: true prop :author, type: Author, allow_nil: true prop :date, default: -> { DateTime.now } private def humanized_date date.strftime '%Y-%m-%d %H:%M' end def author_name author&.name || 'Unknown' end def author_avatar author&.avatar_url || User.placeholder_avatar_url end end ``` ```html
<%= author_name %> avatar
<%= author_name %>
<%= humanized_date %>
<%= body %>
``` ### Nested components It's possible to nest components or provide custom HTML to a component. This works similarly to React's `props.children`. To render the passed nested content call `yield.html_safe` somewhere inside the template/view. ```ruby # app/components/modal_component.rb class ModalComponent < ApplicationComponent prop :id, required: true prop :title, required: true end ``` ```html