flowbite-components 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2641783b14422d1a71ae1bc126308ce749e54ab33971baa9f2c590e7368a3d18
4
- data.tar.gz: 919b625a46a74adafea39e617906d3da0c79f44876aaaba80c202fbae6ad9f0a
3
+ metadata.gz: 0013a87a4e4924d4ca2fe6f3ba8eff3f9f266be23a15b07cbddd25164d436805
4
+ data.tar.gz: d50dd418f1403dc2375754f4b0d295c9c2d4f1a09c0904bffce88fc76268135b
5
5
  SHA512:
6
- metadata.gz: 436530353e5ae17c78af2a1d84a3125677d4fbc23a66191ff3b355f205a65307ddbc908d5c53dccef5806bd4151d6f6842f5e53f2079290179b37b96585940d5
7
- data.tar.gz: 9031664132054b141c7937dfa0794cde6924c86907d0b3c544364f101b1937de5700f86cf89f6350bb159769be35095e3f3cf59572521052941b0f1f4f922155
6
+ metadata.gz: e410ab1f18d6264c18cae8e5c0322c612422e33aa4136ab99ff21e6b0564ceaeb95decd262a265d72b9029022ff76965c19eeb0438d3d295c85cd2962986a3ab
7
+ data.tar.gz: 168b101505fbddfc952c334cc169c1e12e5f129beb8c3f884ffd32047c0548bd22655fd03a6da3589f9ff5071c94850d727761c7aacb96b2712c09420657147c
data/CHANGELOG.md CHANGED
@@ -7,15 +7,28 @@ This project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
8
  ### Added
9
9
 
10
- *
10
+ ### Changed
11
+
12
+ ### Removed
13
+
14
+
15
+ ## [0.1.4]
16
+
17
+ ### Added
18
+
19
+ * Flowbite::Link component to render links.
20
+ * Flowbite::Card now displays a title via the title argument/slot.
21
+ * Improved error message when an unknown style is requested.
22
+ * Input-elements now support forms without an object; ie forms built with `form_tag` - not `form_for` or `form_with`.
11
23
 
12
24
  ### Changed
13
25
 
14
- *
26
+ * [BREAKING] All components now use Flowbite 4 style classes. This adds the option for easier styling, uses semantic variant names, and keeps us up to date with Flowbite proper.
27
+ * Extra CSS classes passed to components in the `class` argument are now added to the default classes from the component. This optimizes for minor tweaks and additions, which is likely to be the most common use case. If you need to replace all classes on the root element of the component, pass them in `options[:class]` instead.
15
28
 
16
29
  ### Removed
17
30
 
18
- *
31
+ * [BREAKING] Color-specific styles from Flowbite::Button, ie `alternative`, `green`, `light`, `purple`, `red`, `yellow`.
19
32
 
20
33
 
21
34
  ## [0.1.3]
data/README.md CHANGED
@@ -22,7 +22,7 @@ Flowbite Components provides a comprehensive library of UI components following
22
22
  Add the gem to your application's Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'flowbite-components'
25
+ gem "flowbite-components"
26
26
  ```
27
27
 
28
28
  Then execute:
@@ -31,9 +31,9 @@ Then execute:
31
31
  bundle install
32
32
  ```
33
33
 
34
- ### Configuration
34
+ ### tailwindcss-rails
35
35
 
36
- Make sure you have Tailwind CSS installed in your Rails application. We recommend using the [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) gem:
36
+ Tailwind needs to be able to look through your code in order to generate the final CSS file with the class names you actually use. To allow Tailwind to find CSS class names inside flowbite-components you need to use [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) gem:
37
37
 
38
38
  ```ruby
39
39
  gem "tailwindcss-rails", ">= 4.3.0"
@@ -44,17 +44,19 @@ gem "tailwindcss-rails", ">= 4.3.0"
44
44
  Install Flowbite as an npm dependency:
45
45
 
46
46
  ```bash
47
- npm install flowbite
47
+ yarn add flowbite
48
48
  ```
49
49
 
50
50
  Add Flowbite to your Tailwind CSS configuration. In your `app/assets/tailwind/application.css`:
51
51
 
52
52
  ```css
53
- @import "flowbite/src/themes/default";
54
53
  @plugin "flowbite/plugin";
54
+ @import "flowbite/src/themes/default";
55
55
  @import "../builds/tailwind/flowbite_components";
56
56
  ```
57
57
 
58
+ If you want to use one of the other [Flowbite themes](https://flowbite.com/docs/customize/theming/), change `@import "flowbite/src/themes/default";` accordingly.
59
+
58
60
  ## Usage examples
59
61
 
60
62
  ### Basic Form Field
@@ -217,6 +219,33 @@ renders
217
219
  - **Outline Button**: `Flowbite::Button::Outline`
218
220
  - **Pill Button**: `Flowbite::Button::Pill`
219
221
 
222
+ #### Cards
223
+ - **Card**: `Flowbite::Card` (default card with content and title)
224
+
225
+ #### Navigation
226
+ - **Link**: `Flowbite::Link` (default link styling)
227
+
228
+
229
+ ## Principles
230
+
231
+ ### CSS classes are additive
232
+
233
+ Passing classes via the `class` argument to a component adds the classes to the
234
+ default ones instead of replacing them.
235
+
236
+ ```ruby
237
+ render(Component.new(class: "these are added"))
238
+ ```
239
+
240
+ This makes for easier customization of components, where you don't have to
241
+ recreate the entire classlist, ie in order to increase sizes or add margins or
242
+ whatever.
243
+
244
+ If you want to replace the entire class attribute for a component, pass it as part of the `options` hash, ie
245
+
246
+ ```ruby
247
+ render(Component.new(options: {class: "these replace the classes"}))
248
+ ```
220
249
 
221
250
  ## Development
222
251
 
@@ -4,18 +4,33 @@ module Flowbite
4
4
  class Button
5
5
  class Outline < Flowbite::Button
6
6
  class << self
7
- # rubocop:disable Layout/LineLength
7
+ # rubocop:disable Layout/LineLength, Metrics/MethodLength
8
8
  def styles
9
- {
10
- default: Flowbite::Style.new(
11
- default: ["text-blue-700", "hover:text-white", "border", "border-blue-700", "hover:bg-blue-800", "focus:ring-4", "focus:outline-none", "focus:ring-blue-300", "font-medium", "rounded-lg", "text-center", "me-2", "mb-2", "dark:border-blue-500", "dark:text-blue-500", "dark:hover:text-white", "dark:hover:bg-blue-500", "dark:focus:ring-blue-800"]
12
- ),
13
- green: Flowbite::Style.new(
14
- default: ["text-green-700", "hover:text-white", "border", "border-green-700", "hover:bg-green-800", "focus:ring-4", "focus:outline-none", "focus:ring-green-300", "font-medium", "rounded-lg", "text-center", "me-2", "mb-2", "dark:border-green-500", "dark:text-green-500", "dark:hover:text-white", "dark:hover:bg-green-600", "dark:focus:ring-green-800"]
15
- )
16
- }
9
+ Flowbite::Styles.from_hash({
10
+ danger: {
11
+ default: ["focus:outline-none", "text-danger", "bg-transparent", "box-border", "border", "border-danger", "hover:text-white", "hover:bg-danger-strong", "focus:ring-4", "focus:ring-danger-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
12
+ },
13
+ dark: {
14
+ default: ["focus:outline-none", "text-dark", "bg-transparent", "box-border", "border", "border-dark", "hover:text-white", "hover:bg-dark-strong", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
15
+ },
16
+ default: {
17
+ default: ["focus:outline-none", "text-brand", "bg-transparent", "box-border", "border", "border-brand", "hover:text-white", "hover:bg-brand-strong", "focus:ring-4", "focus:ring-brand-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
18
+ },
19
+ secondary: {
20
+ default: ["focus:outline-none", "text-body", "bg-transparent", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
21
+ },
22
+ success: {
23
+ default: ["focus:outline-none", "text-success", "bg-transparent", "box-border", "border", "border-success", "hover:text-white", "hover:bg-success-strong", "focus:ring-4", "focus:ring-success-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
24
+ },
25
+ tertiary: {
26
+ default: ["focus:outline-none", "text-body", "bg-transparent", "box-border", "border", "border-default", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary-soft", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
27
+ },
28
+ warning: {
29
+ default: ["focus:outline-none", "text-warning", "bg-transparent", "box-border", "border", "border-warning", "hover:text-white", "hover:bg-warning-strong", "focus:ring-4", "focus:ring-warning-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
30
+ }
31
+ }.freeze)
17
32
  end
18
- # rubocop:enable Layout/LineLength
33
+ # rubocop:enable Layout/LineLength, Metrics/MethodLength
19
34
  end
20
35
  end
21
36
  end
@@ -6,32 +6,32 @@ module Flowbite
6
6
  class << self
7
7
  # rubocop:disable Layout/LineLength, Metrics/MethodLength
8
8
  def styles
9
- {
10
- alternative: Flowbite::Style.new(
11
- default: ["text-sm", "font-medium", "text-gray-900", "focus:outline-none", "bg-white", "rounded-full", "border", "border-gray-200", "hover:bg-gray-100", "hover:text-blue-700", "focus:z-10", "focus:ring-4", "focus:ring-gray-100", "dark:focus:ring-gray-700", "dark:bg-gray-800", "dark:text-gray-400", "dark:border-gray-600", "dark:hover:text-white", "dark:hover:bg-gray-700"]
12
- ),
13
- dark: Flowbite::Style.new(
14
- default: ["text-white", "bg-gray-800", "hover:bg-gray-900", "focus:outline-none", "focus:ring-4", "focus:ring-gray-300", "font-medium", "rounded-full", "dark:bg-gray-800", "dark:hover:bg-gray-700", "dark:focus:ring-gray-700", "dark:border-gray-700"]
15
- ),
16
- default: Flowbite::Style.new(
17
- default: ["text-white", "bg-blue-700", "hover:bg-blue-800", "focus:outline-none", "focus:ring-4", "focus:ring-blue-300", "font-medium", "rounded-full", "text-center", "dark:bg-blue-600", "dark:hover:bg-blue-700", "dark:focus:ring-blue-800"]
18
- ),
19
- green: Flowbite::Style.new(
20
- default: ["text-white", "bg-green-700", "hover:bg-green-800", "focus:outline-none", "focus:ring-4", "focus:ring-green-300", "font-medium", "rounded-full", "text-center", "dark:bg-green-600", "dark:hover:bg-green-700", "dark:focus:ring-green-800"]
21
- ),
22
- light: Flowbite::Style.new(
23
- default: ["text-gray-900", "bg-white", "border", "border-gray-300", "focus:outline-none", "hover:bg-gray-100", "focus:ring-4", "focus:ring-gray-100", "font-medium", "rounded-full", "dark:bg-gray-800", "dark:text-white", "dark:border-gray-600", "dark:hover:bg-gray-700", "dark:hover:border-gray-600", "dark:focus:ring-gray-700"]
24
- ),
25
- purple: Flowbite::Style.new(
26
- default: ["text-white", "bg-purple-700", "hover:bg-purple-800", "focus:outline-none", "focus:ring-4", "focus:ring-purple-300", "font-medium", "rounded-full", "text-center", "dark:bg-purple-600", "dark:hover:bg-purple-700", "dark:focus:ring-purple-900"]
27
- ),
28
- red: Flowbite::Style.new(
29
- default: ["text-white", "bg-red-700", "hover:bg-red-800", "focus:outline-none", "focus:ring-4", "focus:ring-red-300", "font-medium", "rounded-full", "text-center", "dark:bg-red-600", "dark:hover:bg-red-700", "dark:focus:ring-red-900"]
30
- ),
31
- yellow: Flowbite::Style.new(
32
- default: ["text-white", "bg-yellow-400", "hover:bg-yellow-500", "focus:outline-none", "focus:ring-4", "focus:ring-yellow-300", "font-medium", "rounded-full", "text-center", "dark:focus:ring-yellow-900"]
33
- )
34
- }
9
+ Flowbite::Styles.from_hash({
10
+ danger: {
11
+ default: ["focus:outline-none", "text-white", "bg-danger", "box-border", "border", "border-transparent", "hover:bg-danger-strong", "focus:ring-4", "focus:ring-danger-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
12
+ },
13
+ dark: {
14
+ default: ["focus:outline-none", "text-white", "bg-dark", "box-border", "border", "border-transparent", "hover:bg-dark-strong", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
15
+ },
16
+ default: {
17
+ default: ["focus:outline-none", "text-white", "bg-brand", "box-border", "border", "border-transparent", "hover:bg-brand-strong", "focus:ring-4", "focus:ring-brand-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
18
+ },
19
+ ghost: {
20
+ default: ["focus:outline-none", "text-heading", "bg-transparent", "box-border", "border", "border-transparent", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "font-medium", "leading-5", "rounded-full", "text-center"]
21
+ },
22
+ secondary: {
23
+ default: ["focus:outline-none", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
24
+ },
25
+ success: {
26
+ default: ["focus:outline-none", "text-white", "bg-success", "box-border", "border", "border-transparent", "hover:bg-success-strong", "focus:ring-4", "focus:ring-success-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
27
+ },
28
+ tertiary: {
29
+ default: ["focus:outline-none", "text-body", "bg-neutral-primary-soft", "box-border", "border", "border-default", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary-soft", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
30
+ },
31
+ warning: {
32
+ default: ["focus:outline-none", "text-white", "bg-warning", "box-border", "border", "border-transparent", "hover:bg-warning-strong", "focus:ring-4", "focus:ring-warning-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
33
+ }
34
+ }.freeze)
35
35
  end
36
36
  # rubocop:enable Layout/LineLength, Metrics/MethodLength
37
37
  end
@@ -11,9 +11,9 @@ module Flowbite
11
11
  # as HTML attributes.
12
12
  class Button < ViewComponent::Base
13
13
  SIZES = {
14
- xs: ["text-xs", "px-3", "py-2"],
14
+ xs: ["text-xs", "px-3", "py-1.5"],
15
15
  sm: ["text-sm", "px-3", "py-2"],
16
- default: ["text-sm", "px-5", "py-2.5"],
16
+ default: ["text-sm", "px-4", "py-2.5"],
17
17
  lg: ["text-base", "px-5", "py-3"],
18
18
  xl: ["text-base", "px-6", "py-3.5"]
19
19
  }.freeze
@@ -31,39 +31,42 @@ module Flowbite
31
31
 
32
32
  # rubocop:disable Layout/LineLength
33
33
  def styles
34
- {
35
- alternative: Flowbite::Style.new(
36
- default: ["font-medium", "text-gray-900", "focus:outline-none", "bg-white", "rounded-lg", "border", "border-gray-200", "hover:bg-gray-100", "hover:text-blue-700", "focus:z-10", "focus:ring-4", "focus:ring-gray-100", "dark:focus:ring-gray-700", "dark:bg-gray-800", "dark:text-gray-400", "dark:border-gray-600", "dark:hover:text-white", "dark:hover:bg-gray-700"]
37
- ),
38
- dark: Flowbite::Style.new(
39
- default: ["text-white", "bg-gray-800", "hover:bg-gray-900", "focus:ring-4", "focus:ring-gray-300", "font-medium", "rounded-lg", "dark:bg-gray-800", "dark:hover:bg-gray-700", "dark:focus:ring-gray-700", "dark:border-gray-700"]
40
- ),
41
- default: Flowbite::Style.new(
42
- default: ["text-white", "bg-blue-700", "hover:bg-blue-800", "focus:ring-4", "focus:ring-blue-300", "font-medium", "rounded-lg", "dark:bg-blue-600", "dark:hover:bg-blue-700", "focus:outline-none", "dark:focus:ring-blue-800"]
43
- ),
44
- green: Flowbite::Style.new(
45
- default: ["focus:outline-none", "text-white", "bg-green-700", "hover:bg-green-800", "focus:ring-4", "focus:ring-green-300", "font-medium", "rounded-lg", "dark:bg-green-600", "dark:hover:bg-green-700", "dark:focus:ring-green-800"]
46
- ),
47
- light: Flowbite::Style.new(
48
- default: ["text-gray-900", "bg-white", "border", "border-gray-300", "hover:bg-gray-100", "focus:ring-4", "focus:ring-gray-100", "font-medium", "rounded-lg", "dark:bg-gray-800", "dark:text-white", "dark:border-gray-600", "dark:hover:bg-gray-700", "dark:hover:border-gray-600", "dark:focus:ring-gray-700"]
49
- ),
50
- purple: Flowbite::Style.new(
51
- default: ["focus:outline-none", "text-white", "bg-purple-700", "hover:bg-purple-800", "focus:ring-4", "focus:ring-purple-300", "font-medium", "rounded-lg", "dark:bg-purple-600", "dark:hover:bg-purple-700", "dark:focus:ring-purple-900"]
52
- ),
53
- red: Flowbite::Style.new(
54
- default: ["focus:outline-none", "text-white", "bg-red-700", "hover:bg-red-800", "focus:ring-4", "focus:ring-red-300", "font-medium", "rounded-lg", "dark:bg-red-600", "dark:hover:bg-red-700", "dark:focus:ring-red-900"]
55
- ),
56
- yellow: Flowbite::Style.new(
57
- default: ["focus:outline-none", "text-white", "bg-yellow-400", "hover:bg-yellow-500", "focus:ring-4", "focus:ring-yellow-300", "font-medium", "rounded-lg", "dark:focus:ring-yellow-900"]
58
- )
59
- }.freeze
34
+ Flowbite::Styles.from_hash(
35
+ {
36
+ danger: {
37
+ default: ["focus:outline-none", "text-white", "bg-danger", "box-border", "border", "border-transparent", "hover:bg-danger-strong", "focus:ring-4", "focus:ring-danger-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
38
+ },
39
+ dark: {
40
+ default: ["focus:outline-none", "text-white", "bg-dark", "box-border", "border", "border-transparent", "hover:bg-dark-strong", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
41
+ },
42
+ default: {
43
+ default: ["focus:outline-none", "text-white", "bg-brand", "box-border", "border", "border-transparent", "hover:bg-brand-strong", "focus:ring-4", "focus:ring-brand-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
44
+ },
45
+ ghost: {
46
+ default: ["focus:outline-none", "text-heading", "bg-transparent", "box-border", "border", "border-transparent", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "font-medium", "leading-5", "rounded-base"]
47
+ },
48
+ secondary: {
49
+ default: ["focus:outline-none", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
50
+ },
51
+ success: {
52
+ default: ["focus:outline-none", "text-white", "bg-success", "box-border", "border", "border-transparent", "hover:bg-success-strong", "focus:ring-4", "focus:ring-success-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
53
+ },
54
+ tertiary: {
55
+ default: ["focus:outline-none", "text-body", "bg-neutral-primary-soft", "box-border", "border", "border-default", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary-soft", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
56
+ },
57
+ warning: {
58
+ default: ["focus:outline-none", "text-white", "bg-warning", "box-border", "border", "border-transparent", "hover:bg-warning-strong", "focus:ring-4", "focus:ring-warning-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
59
+ }
60
+ }.freeze
61
+ )
60
62
  end
61
63
  # rubocop:enable Layout/LineLength
62
64
  end
63
65
 
64
66
  attr_reader :button_attributes, :size, :style
65
67
 
66
- def initialize(size: :default, style: :default, **button_attributes)
68
+ def initialize(class: nil, size: :default, style: :default, **button_attributes)
69
+ @class = Array.wrap(binding.local_variable_get(:class))
67
70
  @size = size
68
71
  @style = style
69
72
  @button_attributes = button_attributes
@@ -80,7 +83,7 @@ module Flowbite
80
83
  private
81
84
 
82
85
  def classes
83
- self.class.classes(size: size, state: :default, style: style)
86
+ self.class.classes(size: size, state: :default, style: style) + @class
84
87
  end
85
88
 
86
89
  def options
@@ -0,0 +1,7 @@
1
+ <%= content_tag(:div, card_options) do %>
2
+ <%= title %>
3
+
4
+ <% if content.present? %>
5
+ <div class="font-normal text-body"><%= content %></div>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flowbite
4
+ class Card
5
+ # Renders the title of a card element.
6
+ class Title < ViewComponent::Base
7
+ class << self
8
+ def classes(state: :default, style: :default)
9
+ style = styles.fetch(style)
10
+ style.fetch(state)
11
+ end
12
+
13
+ # rubocop:disable Layout/LineLength
14
+ def styles
15
+ Flowbite::Styles.from_hash(
16
+ {
17
+ default: {
18
+ default: ["mb-2", "text-2xl", "font-semibold", "tracking-tight", "text-heading"]
19
+ }
20
+ }.freeze
21
+ )
22
+ end
23
+ # rubocop:enable Layout/LineLength
24
+ end
25
+
26
+ def call
27
+ title_options = {
28
+ class: self.class.classes
29
+ }.merge(@options)
30
+
31
+ content_tag(:h5, content, **title_options)
32
+ end
33
+
34
+ def initialize(**options)
35
+ @options = options || {}
36
+ end
37
+ end
38
+ end
39
+ end
@@ -5,6 +5,8 @@ module Flowbite
5
5
  #
6
6
  # See https://flowbite.com/docs/components/cards/
7
7
  class Card < ViewComponent::Base
8
+ renders_one :title
9
+
8
10
  class << self
9
11
  def classes(state: :default, style: :default)
10
12
  style = styles.fetch(style)
@@ -13,33 +15,72 @@ module Flowbite
13
15
 
14
16
  # rubocop:disable Layout/LineLength
15
17
  def styles
16
- {
17
- default: Flowbite::Style.new(
18
- default: ["p-6", "bg-white", "border", "border-gray-200", "rounded-lg", "shadow-sm", "dark:bg-gray-800", "dark:border-gray-700"]
19
- )
20
- }.freeze
18
+ Flowbite::Styles.from_hash(
19
+ {
20
+ default: {
21
+ default: ["p-6", "bg-neutral-primary-soft", "border", "border-default", "rounded-base", "shadow-xs"]
22
+ }
23
+ }.freeze
24
+ )
21
25
  end
22
26
  # rubocop:enable Layout/LineLength
23
27
  end
24
28
 
25
- def call
26
- card_options = {}
27
- card_options[:class] = self.class.classes + @class
28
-
29
- content_tag(:div, card_options.merge(@options)) do
30
- concat(content_tag(:div, content, class: "font-normal text-gray-700 dark:text-gray-400"))
31
- end
32
- end
33
-
34
29
  # @param class [Array<String>] Additional CSS classes for the card
35
30
  # container.
36
31
  #
37
32
  # @param options [Hash] Additional HTML options for the card container
38
33
  # (e.g., custom classes, data attributes). These options are merged into
39
34
  # the card's root element.
40
- def initialize(class: [], options: {})
35
+ #
36
+ # @param title [Hash] An optional title for the card. If provided,
37
+ # it will be rendered at the top of the card in a h5 tag using the
38
+ # Card::Title component. The hash can contain:
39
+ # - `content`: The text content of the title
40
+ # - `options`: Additional HTML options to pass to the title element
41
+ # Alternatively, you can use the `title` slot to provide the entire
42
+ # title element yourself.
43
+ def initialize(class: [], options: {}, title: {})
41
44
  @class = Array(binding.local_variable_get(:class)) || []
42
45
  @options = options || {}
46
+ @title = title
47
+ end
48
+
49
+ protected
50
+
51
+ def card_options
52
+ card_options = {}
53
+ card_options[:class] = self.class.classes + @class
54
+ card_options.merge(@options)
55
+ end
56
+
57
+ # Returns the HTML to use for the title element if any
58
+ def default_title
59
+ component = Flowbite::Card::Title.new(**default_title_options)
60
+
61
+ if default_title_content
62
+ component.with_content(default_title_content)
63
+ else
64
+ component
65
+ end
66
+
67
+ render(component)
68
+ end
69
+
70
+ def default_title_content
71
+ return nil unless @title
72
+
73
+ @title[:content]
74
+ end
75
+
76
+ # @return [Hash] The options to pass to the default title component
77
+ def default_title_options
78
+ title_options = @title.dup
79
+ title_options[:options] || {}
80
+ end
81
+
82
+ def title?
83
+ @title.present?
43
84
  end
44
85
  end
45
86
  end
@@ -22,13 +22,15 @@ module Flowbite
22
22
 
23
23
  # rubocop:disable Layout/LineLength
24
24
  def styles
25
- {
26
- default: Flowbite::Style.new(
27
- default: ["text-blue-600", "bg-gray-100", "border-gray-300", "rounded-sm", "focus:ring-blue-500", "dark:focus:ring-blue-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-gray-600"],
28
- disabled: ["text-blue-600", "bg-gray-100", "border-gray-300", "rounded-sm", "focus:ring-blue-500", "dark:focus:ring-blue-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-gray-600"],
29
- error: ["text-red-600", "bg-red-50", "border-red-500", "rounded-sm", "focus:ring-red-500", "dark:focus:ring-red-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-red-500"]
30
- )
31
- }.freeze
25
+ Flowbite::Styles.from_hash(
26
+ {
27
+ default: {
28
+ default: ["text-brand", "bg-neutral-secondary-medium", "border-default-medium", "rounded-sm", "focus:ring-brand", "focus:ring-2"],
29
+ disabled: ["text-brand", "bg-neutral-secondary-medium", "border-default-medium", "rounded-sm", "focus:ring-brand", "focus:ring-2", "cursor-not-allowed"],
30
+ error: ["text-danger", "bg-danger-soft", "border-danger-subtle", "rounded-sm", "focus:ring-danger", "focus:ring-2"]
31
+ }
32
+ }.freeze
33
+ )
32
34
  end
33
35
  end
34
36
 
@@ -43,8 +45,8 @@ module Flowbite
43
45
  )
44
46
  end
45
47
 
46
- def initialize(attribute:, form:, disabled: false, options: {}, size: :default, unchecked_value: DEFAULT_UNCHECKED_VALUE, value: DEFAULT_CHECKED_VALUE)
47
- super(attribute: attribute, form: form, disabled: disabled, options: options, size: size)
48
+ def initialize(attribute:, form:, class: nil, disabled: false, options: {}, size: :default, unchecked_value: DEFAULT_UNCHECKED_VALUE, value: DEFAULT_CHECKED_VALUE)
49
+ super(attribute: attribute, class: binding.local_variable_get(:class), form: form, disabled: disabled, options: options, size: size)
48
50
  @unchecked_value = unchecked_value
49
51
  @value = value
50
52
  end
@@ -13,9 +13,9 @@ module Flowbite
13
13
  # `Flowbite::InputField` instead.
14
14
  class Field < ViewComponent::Base
15
15
  SIZES = {
16
- sm: ["p-2", "text-xs"],
17
- default: ["p-2.5", "text-sm"],
18
- lg: ["p-4", "text-base"]
16
+ sm: ["px-2.5", "py-2", "text-sm"],
17
+ default: ["px-3", "py-2.5", "text-sm"],
18
+ lg: ["px-3.5", "py-3", "text-base"]
19
19
  }.freeze
20
20
 
21
21
  STATES = [
@@ -46,19 +46,22 @@ module Flowbite
46
46
 
47
47
  # rubocop:disable Layout/LineLength
48
48
  def styles
49
- {
50
- default: Flowbite::Style.new(
51
- default: ["bg-gray-50", "border", "border-gray-300", "text-gray-900", "rounded-lg", "focus:ring-blue-500", "focus:border-blue-500", "block", "w-full", "dark:bg-gray-700", "dark:border-gray-600", "dark:placeholder-gray-400", "dark:text-white", "dark:focus:ring-blue-500", "dark:focus:border-blue-500"],
52
- disabled: ["bg-gray-100", "border", "border-gray-300", "text-gray-900", "text-sm", "rounded-lg", "focus:ring-blue-500", "focus:border-blue-500", "block", "w-full", "p-2.5", "cursor-not-allowed", "dark:bg-gray-700", "dark:border-gray-600", "dark:placeholder-gray-400", "dark:text-gray-400", "dark:focus:ring-blue-500", "dark:focus:border-blue-500"],
53
- error: ["bg-red-50", "border", "border-red-500", "text-red-900", "placeholder-red-700", "rounded-lg", "focus:ring-red-500", "dark:bg-gray-700", "focus:border-red-500", "block", "w-full", "dark:text-red-500", "dark:placeholder-red-500", "dark:border-red-500"]
54
- )
55
- }.freeze
49
+ Flowbite::Styles.from_hash(
50
+ {
51
+ default: {
52
+ default: ["bg-neutral-secondary-medium", "border", "border-default-medium", "text-heading", "rounded-base", "focus:ring-brand", "focus:border-brand", "block", "w-full", "shadow-xs", "placeholder:text-body"],
53
+ disabled: ["bg-neutral-secondary-medium", "border", "border-default-medium", "text-fg-disabled", "rounded-base", "focus:ring-brand", "focus:border-brand", "block", "w-full", "shadow-xs", "cursor-not-allowed", "placeholder:text-fg-disabled"],
54
+ error: ["bg-danger-soft", "border", "border-danger-subtle", "text-fg-danger-strong", "rounded-base", "focus:ring-danger", "focus:border-danger", "block", "w-full", "shadow-xs", "placeholder:text-fg-danger-strong"]
55
+ }
56
+ }.freeze
57
+ )
56
58
  end
57
59
  # rubocop:enable Layout/LineLength
58
60
  end
59
61
 
60
- def initialize(attribute:, form:, disabled: false, options: {}, size: :default)
62
+ def initialize(attribute:, form:, class: nil, disabled: false, options: {}, size: :default)
61
63
  @attribute = attribute
64
+ @class = Array.wrap(binding.local_variable_get(:class))
62
65
  @disabled = disabled
63
66
  @form = form
64
67
  @options = options || {}
@@ -77,7 +80,7 @@ module Flowbite
77
80
 
78
81
  # Returns the CSS classes to apply to the input field
79
82
  def classes
80
- self.class.classes(size: size, state: state)
83
+ self.class.classes(size: size, state: state) + @class
81
84
  end
82
85
 
83
86
  # Returns the name of the method used to generate HTML for the input field
@@ -92,7 +95,13 @@ module Flowbite
92
95
  !!@disabled
93
96
  end
94
97
 
98
+ # Returns true if the object has errors. Returns false if there is no
99
+ # object.
100
+ #
101
+ # @return [Boolean] true if there are errors, false otherwise.
95
102
  def errors?
103
+ return false unless @object
104
+
96
105
  @object.errors.include?(@attribute.intern)
97
106
  end
98
107
 
@@ -4,9 +4,9 @@ module Flowbite
4
4
  module Input
5
5
  class File < Field
6
6
  SIZES = {
7
- sm: ["text-xs"],
7
+ sm: ["text-sm"],
8
8
  default: ["text-sm"],
9
- lg: ["text-lg"]
9
+ lg: ["text-base"]
10
10
  }.freeze
11
11
 
12
12
  # Returns the name of the method used to generate HTML for the input field
@@ -16,13 +16,15 @@ module Flowbite
16
16
 
17
17
  # rubocop:disable Layout/LineLength
18
18
  def self.styles
19
- {
20
- default: Flowbite::Style.new(
21
- default: ["block", "w-full", "text-gray-900", "border", "border-gray-300", "rounded-lg", "cursor-pointer", "bg-gray-50", "focus:outline-none", "dark:text-gray-400", "dark:bg-gray-700", "dark:border-gray-600"],
22
- disabled: ["block", "w-full", "text-gray-400", "border", "border-gray-300", "rounded-lg", "cursor-not-allowed", "bg-gray-100", "dark:text-gray-500", "dark:bg-gray-600", "dark:border-gray-500"],
23
- error: ["block", "w-full", "text-red-900", "border", "border-red-500", "rounded-lg", "cursor-pointer", "bg-red-50", "focus:outline-none", "dark:text-red-400", "dark:bg-gray-700", "dark:border-red-500"]
24
- )
25
- }.freeze
19
+ Flowbite::Styles.from_hash(
20
+ {
21
+ default: {
22
+ default: ["block", "w-full", "text-heading", "border", "border-default-medium", "rounded-base", "cursor-pointer", "bg-neutral-secondary-medium", "focus:outline-none"],
23
+ disabled: ["block", "w-full", "text-fg-disabled", "border", "border-default-medium", "rounded-base", "cursor-not-allowed", "bg-neutral-secondary-medium"],
24
+ error: ["block", "w-full", "text-fg-danger-strong", "border", "border-danger-subtle", "rounded-base", "cursor-pointer", "bg-danger-soft", "focus:outline-none"]
25
+ }
26
+ }.freeze
27
+ )
26
28
  end
27
29
  # rubocop:enable Layout/LineLength
28
30
  end
@@ -14,11 +14,13 @@ module Flowbite
14
14
  end
15
15
 
16
16
  def styles
17
- {
18
- default: Flowbite::Style.new(
19
- default: ["mt-2", "text-sm", "text-gray-500", "dark:text-gray-400"]
20
- )
21
- }.freeze
17
+ Flowbite::Styles.from_hash(
18
+ {
19
+ default: {
20
+ default: ["mt-2.5", "text-sm", "text-body"]
21
+ }
22
+ }.freeze
23
+ )
22
24
  end
23
25
  end
24
26
 
@@ -30,8 +32,9 @@ module Flowbite
30
32
  )
31
33
  end
32
34
 
33
- def initialize(attribute:, form:, options: {})
35
+ def initialize(attribute:, form:, class: nil, options: {})
34
36
  @attribute = attribute
37
+ @class = Array.wrap(binding.local_variable_get(:class))
35
38
  @form = form
36
39
  @options = options
37
40
  @object = form.object
@@ -39,7 +42,7 @@ module Flowbite
39
42
 
40
43
  # Returns an array with the CSS classes to apply to the label element
41
44
  def classes
42
- self.class.classes(state: state)
45
+ self.class.classes(state: state) + @class
43
46
  end
44
47
 
45
48
  protected
@@ -17,13 +17,15 @@ module Flowbite
17
17
  end
18
18
 
19
19
  def styles
20
- {
21
- default: Flowbite::Style.new(
22
- default: ["block", "mb-2", "text-sm", "font-medium", "text-gray-900", "dark:text-white"],
23
- disabled: ["block", "mb-2", "text-sm", "font-medium", "text-gray-400", "dark:text-gray-500"],
24
- error: ["block", "mb-2", "text-sm", "font-medium", "text-red-700", "dark:text-red-500"]
25
- )
26
- }.freeze
20
+ Flowbite::Styles.from_hash(
21
+ {
22
+ default: {
23
+ default: ["block", "mb-2.5", "text-sm", "font-medium", "text-heading"],
24
+ disabled: ["block", "mb-2.5", "text-sm", "font-medium", "text-fg-disabled"],
25
+ error: ["block", "mb-2.5", "text-sm", "font-medium", "text-fg-danger-strong"]
26
+ }
27
+ }.freeze
28
+ )
27
29
  end
28
30
  end
29
31
 
@@ -36,11 +38,14 @@ module Flowbite
36
38
  end
37
39
 
38
40
  def errors?
41
+ return false unless @object
42
+
39
43
  @object.errors.include?(@attribute.intern)
40
44
  end
41
45
 
42
- def initialize(attribute:, form:, disabled: false, options: {})
46
+ def initialize(attribute:, form:, class: nil, disabled: false, options: {})
43
47
  @attribute = attribute
48
+ @class = Array.wrap(binding.local_variable_get(:class))
44
49
  @disabled = disabled
45
50
  @form = form
46
51
  @object = form.object
@@ -49,7 +54,7 @@ module Flowbite
49
54
 
50
55
  # Returns an array with the CSS classes to apply to the label element
51
56
  def classes
52
- self.class.classes(state: state)
57
+ self.class.classes(state: state) + @class
53
58
  end
54
59
 
55
60
  protected
@@ -17,13 +17,15 @@ module Flowbite
17
17
 
18
18
  # rubocop:disable Layout/LineLength
19
19
  def styles
20
- {
21
- default: Flowbite::Style.new(
22
- default: ["text-blue-600", "bg-gray-100", "border-gray-300", "focus:ring-blue-500", "dark:focus:ring-blue-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-gray-600"],
23
- disabled: ["text-blue-600", "bg-gray-100", "border-gray-300", "focus:ring-blue-500", "dark:focus:ring-blue-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-gray-600"],
24
- error: ["text-red-600", "bg-red-50", "border-red-500", "focus:ring-red-500", "dark:focus:ring-red-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-red-500"]
25
- )
26
- }.freeze
20
+ Flowbite::Styles.from_hash(
21
+ {
22
+ default: {
23
+ default: ["text-brand", "bg-neutral-secondary-medium", "border-default-medium", "focus:ring-brand", "focus:ring-2"],
24
+ disabled: ["text-brand", "bg-neutral-secondary-medium", "border-default-medium", "focus:ring-brand", "focus:ring-2", "cursor-not-allowed"],
25
+ error: ["text-danger", "bg-danger-soft", "border-danger-subtle", "focus:ring-danger", "focus:ring-2"]
26
+ }
27
+ }.freeze
28
+ )
27
29
  end
28
30
  end
29
31
 
@@ -37,8 +39,8 @@ module Flowbite
37
39
  )
38
40
  end
39
41
 
40
- def initialize(attribute:, form:, value:, disabled: false, options: {})
41
- super(attribute: attribute, disabled: disabled, form: form, options: options)
42
+ def initialize(attribute:, form:, value:, class: nil, disabled: false, options: {})
43
+ super(attribute: attribute, class: binding.local_variable_get(:class), disabled: disabled, form: form, options: options)
42
44
  @value = value
43
45
  end
44
46
 
@@ -9,13 +9,13 @@ module Flowbite
9
9
  # Wraps `ActionView::Helpers::FormOptionsHelper#select`: https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-select
10
10
  class Select < Field
11
11
  SIZES = {
12
- sm: ["p-2", "text-xs"],
13
- default: ["p-2.5", "text-sm"],
14
- lg: ["px-4", "py-3", "text-base"]
12
+ sm: ["px-2.5", "py-2", "text-sm"],
13
+ default: ["px-3", "py-2.5", "text-sm"],
14
+ lg: ["px-3.5", "py-3", "text-base"]
15
15
  }.freeze
16
16
 
17
- def initialize(form:, attribute:, collection: [], disabled: false, include_blank: false, multiple: false, options: {}, size: :default)
18
- super(form: form, attribute: attribute, disabled: disabled, options: options, size: size)
17
+ def initialize(form:, attribute:, class: nil, collection: [], disabled: false, include_blank: false, multiple: false, options: {}, size: :default)
18
+ super(form: form, attribute: attribute, class: binding.local_variable_get(:class), disabled: disabled, options: options, size: size)
19
19
  @collection = collection
20
20
  @include_blank = include_blank
21
21
  @multiple = multiple
@@ -6,16 +6,18 @@ module Flowbite
6
6
  class << self
7
7
  # rubocop:disable Layout/LineLength
8
8
  def styles
9
- {
10
- default: Flowbite::Style.new(
11
- default: ["block", "w-full", "text-gray-900", "bg-gray-50", "rounded-lg", "border", "border-gray-300", "focus:ring-blue-500", "focus:border-blue-500", "dark:bg-gray-700", "dark:border-gray-600", "dark:placeholder-gray-400", "dark:text-white", "dark:focus:ring-blue-500", "dark:focus:border-blue-500"],
12
- disabled: ["block", "w-full", "bg-gray-100", "rounded-lg", "border", "border-gray-300", "text-gray-900", "cursor-not-allowed"],
13
- error: ["block", "w-full", "bg-red-50", "border", "border-red-500", "text-red-900", "placeholder-red-700", "rounded-lg", "focus:ring-red-500", "focus:border-red-500", "dark:bg-gray-700", "dark:text-red-500", "dark:placeholder-red-500", "dark:border-red-500"]
14
- )
15
- }.freeze
9
+ Flowbite::Styles.from_hash(
10
+ {
11
+ default: {
12
+ default: ["block", "w-full", "text-heading", "bg-neutral-secondary-medium", "rounded-base", "border", "border-default-medium", "focus:ring-brand", "focus:border-brand", "shadow-xs", "placeholder:text-body"],
13
+ disabled: ["block", "w-full", "bg-neutral-secondary-medium", "rounded-base", "border", "border-default-medium", "text-fg-disabled", "cursor-not-allowed", "shadow-xs", "placeholder:text-fg-disabled"],
14
+ error: ["block", "w-full", "bg-danger-soft", "border", "border-danger-subtle", "text-fg-danger-strong", "placeholder:text-fg-danger-strong", "rounded-base", "focus:ring-danger", "focus:border-danger", "shadow-xs"]
15
+ }
16
+ }.freeze
17
+ )
16
18
  end
17
- # rubocop:enable Layout/LineLength
18
19
  end
20
+ # rubocop:enable Layout/LineLength
19
21
 
20
22
  # Returns the HTML to use for the actual input field element.
21
23
  def call
@@ -30,7 +32,7 @@ module Flowbite
30
32
 
31
33
  # Returns the CSS classes to apply to the input field
32
34
  def classes
33
- self.class.classes(size: size, state: state)
35
+ self.class.classes(size: size, state: state) + @class
34
36
  end
35
37
 
36
38
  # Returns the name of the method used to generate HTML for the input field
@@ -3,8 +3,38 @@
3
3
  module Flowbite
4
4
  module Input
5
5
  class ValidationError < ViewComponent::Base
6
+ class << self
7
+ def classes(state: :default, style: :default)
8
+ style = styles.fetch(style)
9
+ style.fetch(state)
10
+ end
11
+
12
+ # rubocop:disable Layout/LineLength
13
+ def styles
14
+ Flowbite::Styles.from_hash(
15
+ {
16
+ default: {
17
+ default: ["mt-2", "text-sm", "text-red-600", "dark:text-red-500"]
18
+ }
19
+ }.freeze
20
+ )
21
+ end
22
+ # rubocop:enable Layout/LineLength
23
+ end
24
+
6
25
  def call
7
- tag.p(content, class: "mt-2 text-sm text-red-600 dark:text-red-500")
26
+ tag.p(content, class: classes)
27
+ end
28
+
29
+ def initialize(class: nil)
30
+ @class = Array.wrap(binding.local_variable_get(:class))
31
+ end
32
+
33
+ protected
34
+
35
+ # Returns the CSS classes to apply to the validation error
36
+ def classes
37
+ self.class.classes + @class
8
38
  end
9
39
  end
10
40
  end
@@ -1,7 +1,5 @@
1
- <div class="flex">
2
- <div class="flex items-center h-5">
3
- <%= input %>
4
- </div>
1
+ <div class="<%= classes.join(" ") %>">
2
+ <div class="flex items-center h-5"><%= input %></div>
5
3
 
6
4
  <div class="ms-2 text-sm">
7
5
  <%= label %>
@@ -5,6 +5,10 @@ module Flowbite
5
5
  class Checkbox < InputField
6
6
  protected
7
7
 
8
+ def default_container_classes
9
+ ["flex"]
10
+ end
11
+
8
12
  def default_hint_options
9
13
  return {} unless @hint
10
14
 
@@ -29,9 +33,9 @@ module Flowbite
29
33
 
30
34
  def hint_classes
31
35
  if disabled?
32
- "text-xs font-normal text-gray-400 dark:text-gray-500"
36
+ "text-xs font-normal text-fg-disabled"
33
37
  else
34
- "text-xs font-normal text-gray-500 dark:text-gray-300"
38
+ "text-xs font-normal text-body"
35
39
  end
36
40
  end
37
41
 
@@ -44,9 +48,9 @@ module Flowbite
44
48
 
45
49
  def label_classes
46
50
  if disabled?
47
- "font-medium text-gray-400 dark:text-gray-500"
51
+ "font-medium text-fg-disabled"
48
52
  else
49
- "font-medium text-gray-900 dark:text-gray-300"
53
+ "font-medium text-heading"
50
54
  end
51
55
  end
52
56
  end
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="<%= classes.join(" ") %>">
2
2
  <%= label %>
3
3
  <%= input %>
4
4
  <%= hint %>
@@ -1,7 +1,5 @@
1
- <div class="flex">
2
- <div class="flex items-center h-5">
3
- <%= input %>
4
- </div>
1
+ <div class="<%= classes.join(" ") %>">
2
+ <div class="flex items-center h-5"><%= input %></div>
5
3
 
6
4
  <div class="ms-2 text-sm">
7
5
  <%= label %>
@@ -3,13 +3,17 @@
3
3
  module Flowbite
4
4
  class InputField
5
5
  class RadioButton < InputField
6
- def initialize(attribute:, form:, value:, disabled: false, hint: nil, input: {}, label: {})
7
- super(attribute: attribute, form: form, disabled: disabled, hint: hint, input: input, label: label)
6
+ def initialize(attribute:, form:, value:, class: nil, disabled: false, hint: nil, input: {}, label: {}, options: {})
7
+ super(attribute: attribute, class: binding.local_variable_get(:class), form: form, disabled: disabled, hint: hint, input: input, label: label, options: options)
8
8
  @value = value
9
9
  end
10
10
 
11
11
  protected
12
12
 
13
+ def default_container_classes
14
+ ["flex"]
15
+ end
16
+
13
17
  def default_input
14
18
  args = {
15
19
  attribute: @attribute,
@@ -56,9 +60,9 @@ module Flowbite
56
60
 
57
61
  def hint_classes
58
62
  if disabled?
59
- "text-xs font-normal text-gray-400 dark:text-gray-500"
63
+ "text-xs font-normal text-fg-disabled"
60
64
  else
61
- "text-xs font-normal text-gray-500 dark:text-gray-300"
65
+ "text-xs font-normal text-body"
62
66
  end
63
67
  end
64
68
 
@@ -76,9 +80,9 @@ module Flowbite
76
80
 
77
81
  def label_classes
78
82
  if disabled?
79
- "font-medium text-gray-400 dark:text-gray-500"
83
+ "font-medium text-fg-disabled"
80
84
  else
81
- "font-medium text-gray-900 dark:text-gray-300"
85
+ "font-medium text-heading"
82
86
  end
83
87
  end
84
88
  end
@@ -67,18 +67,24 @@ module Flowbite
67
67
  renders_one :label
68
68
 
69
69
  # Returns the errors for attribute
70
+ #
71
+ # @return [Array<String>] An array of error messages for the attribute.
70
72
  def errors
73
+ return [] unless @object
74
+
71
75
  @object.errors[@attribute] || []
72
76
  end
73
77
 
74
- def initialize(attribute:, form:, disabled: false, hint: nil, input: {}, label: {}, size: :default)
78
+ def initialize(attribute:, form:, class: nil, disabled: false, hint: nil, input: {}, label: {}, options: {}, size: :default)
75
79
  @attribute = attribute
80
+ @class = Array.wrap(binding.local_variable_get(:class))
76
81
  @disabled = disabled
77
82
  @form = form
78
83
  @hint = hint
79
84
  @input = input
80
85
  @label = label
81
86
  @object = form.object
87
+ @options = options || {}
82
88
  @size = size
83
89
  end
84
90
 
@@ -88,6 +94,18 @@ module Flowbite
88
94
 
89
95
  protected
90
96
 
97
+ def classes
98
+ if @options[:class]
99
+ Array.wrap(@options[:class])
100
+ else
101
+ [default_container_classes, @class].flatten.compact
102
+ end
103
+ end
104
+
105
+ def default_container_classes
106
+ []
107
+ end
108
+
91
109
  # Returns the HTML to use for the hint element if any
92
110
  def default_hint
93
111
  return unless hint?
@@ -171,7 +189,13 @@ module Flowbite
171
189
  end
172
190
 
173
191
  def id_for_hint_element
174
- "#{@form.object_name}_#{@attribute}_hint"
192
+ [
193
+ @form.object_name,
194
+ @attribute,
195
+ "hint"
196
+ ]
197
+ .compact_blank
198
+ .join("_")
175
199
  end
176
200
 
177
201
  # @return [Hash] The keyword arguments for the input component.
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flowbite
4
+ # The link component can be used to set hyperlinks from one page to another or
5
+ # to an external website when clicking on an inline text item, button, or card
6
+ #
7
+ # Use this component to add default styles to an inline link element.
8
+ class Link < ViewComponent::Base
9
+ attr_reader :href, :options
10
+
11
+ class << self
12
+ def classes
13
+ ["font-medium", "text-fg-brand", "hover:underline"]
14
+ end
15
+ end
16
+
17
+ # Initialize the Link component.
18
+ #
19
+ # @param href What to link to. This can be a String or anything else that
20
+ # `link_to` supports. See `ActionView::Helpers::UrlHelper#link_to` for more
21
+ # details.
22
+ #
23
+ # @param options [Hash] Additional HTML options for the link element
24
+ def initialize(href:, class: nil, **options)
25
+ super()
26
+ @class = Array.wrap(binding.local_variable_get(:class))
27
+ @href = href
28
+ @options = options
29
+ end
30
+
31
+ def call
32
+ link_to(content, href, {class: classes}.merge(options))
33
+ end
34
+
35
+ private
36
+
37
+ def classes
38
+ self.class.classes + @class
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flowbite
4
+ class Styles
5
+ class StyleNotFoundError < ::KeyError; end
6
+
7
+ class << self
8
+ def from_hash(styles_hash)
9
+ styles = Styles.new
10
+ styles_hash.each do |style_name, states_hash|
11
+ styles.add_style(style_name, states_hash)
12
+ end
13
+ styles
14
+ end
15
+ end
16
+
17
+ def add_style(style_name, states_hash)
18
+ @styles[style_name] = Flowbite::Style.new(states_hash)
19
+ end
20
+
21
+ def fetch(style_name)
22
+ return @styles[style_name] if @styles.key?(style_name)
23
+
24
+ raise \
25
+ StyleNotFoundError,
26
+ "Style not found: #{style_name}. Available styles: " \
27
+ "#{@styles.keys.sort.join(", ")}"
28
+ end
29
+
30
+ def initialize
31
+ @styles = {}
32
+ end
33
+ end
34
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Flowbite
4
4
  module Components
5
- VERSION = "0.1.3"
5
+ VERSION = "0.1.4"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flowbite-components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakob Skjerning
@@ -39,6 +39,8 @@ files:
39
39
  - app/components/flowbite/button/outline.rb
40
40
  - app/components/flowbite/button/pill.rb
41
41
  - app/components/flowbite/card.rb
42
+ - app/components/flowbite/card/card.html.erb
43
+ - app/components/flowbite/card/title.rb
42
44
  - app/components/flowbite/input/checkbox.rb
43
45
  - app/components/flowbite/input/date.rb
44
46
  - app/components/flowbite/input/email.rb
@@ -70,7 +72,9 @@ files:
70
72
  - app/components/flowbite/input_field/text.rb
71
73
  - app/components/flowbite/input_field/textarea.rb
72
74
  - app/components/flowbite/input_field/url.rb
75
+ - app/components/flowbite/link.rb
73
76
  - app/components/flowbite/style.rb
77
+ - app/components/flowbite/styles.rb
74
78
  - lib/flowbite/components.rb
75
79
  - lib/flowbite/components/engine.rb
76
80
  - lib/flowbite/components/version.rb