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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
* Button component (first component, wee!)
* Input components
* InputField components
* Basic Card component

### Changes

Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,33 @@ Add Flowbite to your Tailwind CSS configuration. In your `app/assets/tailwind/ap
) %>
```

## How to customize components

### Add specific CSS classes

A common use case for customizing a component is to add more CSS classes when
rendering it, fx to change the size or spacing. flowbite-components is optimized
for this case and all you need to do is specify the extra classes:

```erb
<%= render(Flowbite::Card.new(class: "w-full my-8")) { "Content" } %>
```
renders
```html
<div class="... all the usual classes... w-full my-8">
```

If you want to fully replace the existing classes, you can pass an entirely new
`class` attribute via options:

```erb
<%= render(Flowbite::Card.new(options: {class: "w-full my-8"})) { "Content" } %>
```
renders
```html
<div class="w-full my-8">
```

## Available Components

### Form Components
Expand Down
43 changes: 43 additions & 0 deletions app/components/flowbite/card.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Flowbite
# Renders a card element.
#
# See https://flowbite.com/docs/components/cards/
class Card < ViewComponent::Base
class << self
def classes(state: :default, style: :default)
style = styles.fetch(style)
style.fetch(state)
end

# rubocop:disable Layout/LineLength
def styles
{
default: Flowbite::Style.new(
default: ["max-w-sm", "p-6", "bg-white", "border", "border-gray-200", "rounded-lg", "shadow-sm", "dark:bg-gray-800", "dark:border-gray-700"]
)
}.freeze
end
# rubocop:enable Layout/LineLength
end

def call
card_options = {}
card_options[:class] = self.class.classes + @class

content_tag(:div, card_options.merge(@options)) do
concat(content_tag(:div, content, class: "font-normal text-gray-700 dark:text-gray-400"))
end
end

# @param class [Array<String>] Additional CSS classes for the card
# container.
#
# @param options [Hash] Additional HTML options for the card container
# (e.g., custom classes, data attributes). These options are merged into
# the card's root element.
def initialize(class: [], options: {})
@class = Array(binding.local_variable_get(:class)) || []
@options = options || {}
end
end
end
12 changes: 6 additions & 6 deletions demo/app/views/pages/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</section>

<div class="flex flex-col gap-6 sm:flex-row">
<div class="p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700 sm:flex-1">
<%= render(Flowbite::Card.new(:class => "sm:flex-1")) do %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-7 text-gray-500 dark:text-gray-400 mb-3">
<path fill-rule="evenodd" d="M20.599 1.5c-.376 0-.743.111-1.055.32l-5.08 3.385a18.747 18.747 0 0 0-3.471 2.987 10.04 10.04 0 0 1 4.815 4.815 18.748 18.748 0 0 0 2.987-3.472l3.386-5.079A1.902 1.902 0 0 0 20.599 1.5Zm-8.3 14.025a18.76 18.76 0 0 0 1.896-1.207 8.026 8.026 0 0 0-4.513-4.513A18.75 18.75 0 0 0 8.475 11.7l-.278.5a5.26 5.26 0 0 1 3.601 3.602l.502-.278ZM6.75 13.5A3.75 3.75 0 0 0 3 17.25a1.5 1.5 0 0 1-1.601 1.497.75.75 0 0 0-.7 1.123 5.25 5.25 0 0 0 9.8-2.62 3.75 3.75 0 0 0-3.75-3.75Z" clip-rule="evenodd" />
</svg>
Expand All @@ -30,9 +30,9 @@
<svg class="w-3 h-3 ms-2.5 rtl:rotate-[270deg]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11v4.833A1.166 1.166 0 0 1 13.833 17H2.167A1.167 1.167 0 0 1 1 15.833V4.167A1.166 1.166 0 0 1 2.167 3h4.618m4.447-2H17v5.768M9.111 8.889l7.778-7.778"/></svg>
<% end %>
</div>
<% end %>

<div class="p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700 sm:flex-1">
<%= render(Flowbite::Card.new(:class => "sm:flex-1")) do %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-7 text-gray-500 dark:text-gray-400 mb-3">
<path d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z" />
</svg>
Expand All @@ -46,9 +46,9 @@
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11v4.833A1.166 1.166 0 0 1 13.833 17H2.167A1.167 1.167 0 0 1 1 15.833V4.167A1.166 1.166 0 0 1 2.167 3h4.618m4.447-2H17v5.768M9.111 8.889l7.778-7.778"/>
</svg>
<% end %>
</div>
<% end %>

<div class="p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700 sm:flex-1">
<%= render(Flowbite::Card.new(:class => "sm:flex-1")) do %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-7 text-gray-500 dark:text-gray-400 mb-3">
<path fill-rule="evenodd" d="M12 6.75a5.25 5.25 0 0 1 6.775-5.025.75.75 0 0 1 .313 1.248l-3.32 3.319c.063.475.276.934.641 1.299.365.365.824.578 1.3.64l3.318-3.319a.75.75 0 0 1 1.248.313 5.25 5.25 0 0 1-5.472 6.756c-1.018-.086-1.87.1-2.309.634L7.344 21.3A3.298 3.298 0 1 1 2.7 16.657l8.684-7.151c.533-.44.72-1.291.634-2.309A5.342 5.342 0 0 1 12 6.75ZM4.117 19.125a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75h-.008a.75.75 0 0 1-.75-.75v-.008Z" clip-rule="evenodd" />
<path d="m10.076 8.64-2.201-2.2V4.874a.75.75 0 0 0-.364-.643l-3.75-2.25a.75.75 0 0 0-.916.113l-.75.75a.75.75 0 0 0-.113.916l2.25 3.75a.75.75 0 0 0 .643.364h1.564l2.062 2.062 1.575-1.297Z" />
Expand All @@ -64,5 +64,5 @@
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11v4.833A1.166 1.166 0 0 1 13.833 17H2.167A1.167 1.167 0 0 1 1 15.833V4.167A1.166 1.166 0 0 1 2.167 3h4.618m4.447-2H17v5.768M9.111 8.889l7.778-7.778"/>
</svg>
<% end %>
</div>
<% end %>
</div>
8 changes: 8 additions & 0 deletions demo/test/components/previews/card_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

class CardPreview < Lookbook::Preview
# Use the following simple card component with a title and description.
def default
render(Flowbite::Card.new) { "Use the following simple card component with a title and description." }
end
end
30 changes: 30 additions & 0 deletions test/components/flowbite/card_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require "test_helper"

class Flowbite::CardTest < Minitest::Test
include ViewComponent::TestHelpers

def test_renders_a_default_card
render_inline(Flowbite::Card.new) { "Card Content" }

assert_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm")
end

def test_passes_options_to_the_card_as_attributes
render_inline(Flowbite::Card.new(options: {id: "card-1"})) { "Card Content" }

assert_selector("div#card-1")
end

def test_adds_the_classes_to_the_default_classes
render_inline(Flowbite::Card.new(class: "custom-class another")) { "Card Content" }

assert_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm.custom-class.another")
end

def test_overrides_the_default_classes
render_inline(Flowbite::Card.new(options: {class: "custom-class another"})) { "Card Content" }

assert_no_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm")
assert_selector("div.custom-class.another")
end
end