Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

attrify

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

attrify

  • 0.4.5
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Attrify

[!WARNING]
This a pre-release version of the gem. The API may change

:muscle: A powerful variant API for ruby components

  • Define component variants directly in your ruby
  • Framework agnostic
  • Seamlessly handle complex UI components

Build Status License: MIT

Installation

Add this line to your application's gemfile

gem "attrify"

Getting Started

Add it to your model

class BaseComponent
  include Attrify

  # Optional: pass keyword arguments from the initializer directly to the attributes API
  def initialize(**args)
    with_attributes(**args)
  end
end

Define attributes for your class:

class Button < BaseComponent
  attributes {
    base id: ->{ "button_#{SecureRandom.uuid}}"},
         class: %w[inline-flex items-center rounded], 
         data: { controller: "button_controller" }
    
    variant(:color) {
      primary   class: { append: %w[bg-blue-500 text-white] }
      secondary class: { append: %w[bg-gray-500 text-white shadow-sm] }
      danger    class: { append: %w[bg-red-500 text-white] }
    }

    variant(:size) {
      xs class: { append: %w[text-xs h-7 px-3 py-1] }
      sm class: { append: %w[text-sm h-8 px-3.5 py-1.5] }
      md class: { append: %w[text-sm h-9 px-4 py-2] }
      lg class: { append: %w[text-base h-10 px-5 py-2.5] }
      xl class: { append: %w[text-lg h-11 px-6 py-3] }
    }

    default color: :primary, size: :md
  }
end

Add the attributes to your component's html

  <button <%= attribs() %>> <%= content %> </button> 

Try it out!

  # Use the default attributes
  Button.new() { "Click Me" }

  # Use a predefined color and size 
  Button.new(color: :primary, size: :sm) { "Click Me" }

  # Override the attributes as needed
  Button.new(color: :primary, 
             id: "special_button",
             class: { remove: "border" }, 
             style: "width: 300px;", 
             href: "https://www.google.com") { "Click Me" }

Operations

To best leverage the variants API it is important to understand it's workings. Under the hood, attributes are parsed into operations.

List of operations:

{ set: value }
{ remove: value }
{ append: value }
{ prepend: value }

Let's take a look at some examples

# The default operation is SET so the following buttons are equivelant
Button.new(color: :primary, class: "bg-purple-500")
Button.new(color: :primary, class: { set: "bg-purple-500" })

# We can define multiple operations like this:
Button.new(color: :primary, class: [{ remove: "bg-blue-500" }, 
                                    { prepend: "text-black" }])

Slots

Slots allow you to define different attributes for different parts of a component.

class Card < BaseComponent
  attributes {
    base {
      slot :card, id: ->{"card_#{id}"}, class: %w[rounded-md border]
      slot :header, class: %w[bg-gray-100 border-b]
      slot :body, class: %w[]
      slot :footer, class: %w[border-t]
    }

    variant(:color) {
      primary {
        slot :header, class: { append: %w[bg-blue-500] }
        slot :footer, class: { append: %w[bg-blue-500] }
      }
      danger {
        slot :header, class: { append: %w[bg-red-500] }
        slot :footer, class: { append: %w[bg-red-500] }
      }
    }

    # Wildcard applies to all slots
    variant(:padding) {
      default {
        slot *, class: { append: %w[p-4] }
      }
      condensed {
        slot *, class: { append: %w[px-4 py-2] }
      }
      spacious {
        slot *, class: { append: %w[p-6] }
      }
    }

    default padding: :default
  }
end

Use it in your html

<div <%= attribs(slot: :card) %>>
  <div <%= attribs(slot: :header) %>> Header </div>
  <div <%= attribs(slot: :body) %>> Body </div>
  <div <%= attribs(slot: :footer) %>> Footer </div>
</div>

Override attributes like this:

Card.new(color: :primary, footer: { id: "footer_id" },
                          card: { class: { append: "border-3" }})

Child components

We can define variants of a child component inside a parent

class Alert < BaseComponent
  attributes {
    base {
      slot :main, class: %w[rounded-md border]
      slot :button, size: :md 
    }
    variant(:color) {
      primary {
        slot :main, class: { append: %w[bg-blue-100] }
        slot :button, color: :primary
      }
      danger {
        slot :main, class: { append: %w[bg-red-100] }
        slot :button, color: :danger, size: :lg, id: "alert_button_1"
      }
    }
  }
<div <%= attribs(slot: :main) %>>
  Hello World
  <%= render Button.new(**attribs(slot: :button)) %>
</div>

Note: You can override like this as well

<div <%= attribs(slot: :main, class: { append: "h-10" }) %>>
  Hello World
  <%= render Button.new(**attribs(slot: :button, size: :xl)) %>
</div>

Acknowledgements

Greatly inspired by Tailwind Variants, ViewComponentContrib::StyleVariants, and what GitHub is doing with Primer::Classify.

License

The gem is available as open source under the terms of the MIT License.

FAQs

Package last updated on 16 Oct 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc