flowbite-components 0.2.0 → 0.4.0
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 +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +3 -5
- data/app/components/flowbite/alert/alert.html.erb +36 -0
- data/app/components/flowbite/alert/icon.rb +53 -0
- data/app/components/flowbite/alert.rb +156 -0
- data/app/components/flowbite/avatar/avatar.html.erb +23 -0
- data/app/components/flowbite/avatar/dot.rb +85 -0
- data/app/components/flowbite/avatar/placeholder_icon.html.erb +13 -0
- data/app/components/flowbite/avatar/placeholder_icon.rb +44 -0
- data/app/components/flowbite/avatar.rb +132 -0
- data/app/components/flowbite/badge/badge.html.erb +7 -0
- data/app/components/flowbite/badge/dot.rb +45 -0
- data/app/components/flowbite/badge/pill.rb +40 -0
- data/app/components/flowbite/badge.rb +116 -0
- data/app/components/flowbite/card.rb +4 -8
- data/app/components/flowbite/input_field/checkbox.rb +7 -7
- data/app/components/flowbite/input_field/input_field.html.erb +1 -0
- data/app/components/flowbite/input_field/select.rb +1 -1
- data/app/components/flowbite/input_field.rb +29 -22
- data/app/components/flowbite/pagination/chevron_left_icon.rb +34 -0
- data/app/components/flowbite/pagination/chevron_right_icon.rb +34 -0
- data/app/components/flowbite/pagination/gap.rb +24 -0
- data/app/components/flowbite/pagination/link.rb +52 -0
- data/app/components/flowbite/pagination/next_link.rb +67 -0
- data/app/components/flowbite/pagination/pagination.html.erb +11 -0
- data/app/components/flowbite/pagination/previous_link.rb +67 -0
- data/app/components/flowbite/pagination.rb +63 -0
- data/app/components/flowbite/progress/progress.html.erb +14 -0
- data/app/components/flowbite/progress.rb +163 -0
- data/app/components/flowbite/rating/rating.html.erb +13 -0
- data/app/components/flowbite/rating/star.html.erb +13 -0
- data/app/components/flowbite/rating/star.rb +59 -0
- data/app/components/flowbite/rating.rb +64 -0
- data/app/components/flowbite/sidebar/item.rb +68 -0
- data/app/components/flowbite/sidebar/navigation.rb +62 -0
- data/app/components/flowbite/sidebar.rb +62 -0
- data/app/components/flowbite/spinner/spinner.html.erb +20 -0
- data/app/components/flowbite/spinner.rb +66 -0
- data/app/components/flowbite/toast/close_button.html.erb +17 -0
- data/app/components/flowbite/toast/close_button.rb +32 -0
- data/app/components/flowbite/toast/icon.html.erb +8 -2
- data/app/components/flowbite/toast/toast.html.erb +7 -34
- data/app/components/flowbite/toast.rb +39 -1
- data/lib/flowbite/components/version.rb +1 -1
- metadata +39 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a29d44c915146870abbd45cd6a839a0948877dfdf8590fed7eef8e528a382964
|
|
4
|
+
data.tar.gz: fbe0bc1142849ec153628cb781776ddea678f5391bd42ab7f71ba891174dbd4f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd37a866269767b5607d75453cacf38d4832dbdd82b5097a18531d7f576b8849b335f48a6e7ce743aa1b3251c40baa316379780299564213b2d02ddb7461705f
|
|
7
|
+
data.tar.gz: 83f821ca6081890355483066c64308c3388c783785da725a1546390404528d5a5478ea642facb7fca627eb61a13c4fbd85939343f9f94ab043d32fb003d5fb4a
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.4.0]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Avatar component, with image, initials and placeholder icon fallbacks, sizes, shapes, border and status dot
|
|
11
|
+
- Spinner component
|
|
12
|
+
- Progress component
|
|
13
|
+
- Pagination component
|
|
14
|
+
- Rating component, including `Flowbite::Rating::Star` for the individual stars
|
|
15
|
+
- `Flowbite::Toast::CloseButton` component for the close button of toasts
|
|
16
|
+
- `Flowbite::Toast` accepts `button: {options: {...}}` for passing HTML options, e.g. data attributes, to the default close button, or a `button` slot for rendering your own
|
|
17
|
+
- Alert component
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `Flowbite::Card` no longer renders an empty `h5` element when card title is blank.
|
|
22
|
+
|
|
23
|
+
## [0.3.0]
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
* Badge component.
|
|
28
|
+
* Sidebar component.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
* `:class` arguments to subcomponents (ie `hint` and `input`) of Flowbite::InputField are now forwarded correctly. If passed as `:class` the given value is added to the existing class names, if passed in `options[:class]` the existing class attribute is replaced.
|
|
33
|
+
|
|
34
|
+
|
|
6
35
|
## [0.2.0]
|
|
7
36
|
|
|
8
37
|
### Added
|
data/README.md
CHANGED
|
@@ -272,7 +272,7 @@ bundle exec rake test
|
|
|
272
272
|
To check code style:
|
|
273
273
|
|
|
274
274
|
```bash
|
|
275
|
-
bundle exec rake
|
|
275
|
+
bundle exec rake lint
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
To install this gem onto your local machine:
|
|
@@ -285,10 +285,8 @@ bundle exec rake install
|
|
|
285
285
|
|
|
286
286
|
This library includes a demo application with previews for all components. To view them:
|
|
287
287
|
|
|
288
|
-
1.
|
|
289
|
-
2.
|
|
290
|
-
3. Run `rails server`
|
|
291
|
-
4. Visit `http://localhost:3000/lookbook`
|
|
288
|
+
1. Run `./demo/run` from the project root.
|
|
289
|
+
2. Visit `http://localhost:3000/lookbook`
|
|
292
290
|
|
|
293
291
|
## Contributing
|
|
294
292
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<%= tag.div(
|
|
2
|
+
class: container_classes.join(" "),
|
|
3
|
+
role: "alert",
|
|
4
|
+
**options,
|
|
5
|
+
) do %>
|
|
6
|
+
<% if additional_content? %>
|
|
7
|
+
<% if icon? || title? %>
|
|
8
|
+
<div class="flex items-center">
|
|
9
|
+
<% if icon? %>
|
|
10
|
+
<%= render Flowbite::Alert::Icon.new(class: icon_classes) %>
|
|
11
|
+
<span class="sr-only"><%= style_label %></span>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<% if title? %>
|
|
15
|
+
<h3 class="font-medium"><%= title %></h3>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% if content.present? %>
|
|
20
|
+
<div class="mt-2 mb-4"><%= content %></div>
|
|
21
|
+
<% end %>
|
|
22
|
+
<%= additional_content %>
|
|
23
|
+
<% elsif icon? %>
|
|
24
|
+
<%= render Flowbite::Alert::Icon.new(class: icon_classes) %>
|
|
25
|
+
<span class="sr-only"><%= style_label %></span>
|
|
26
|
+
<div>
|
|
27
|
+
<% if title? %>
|
|
28
|
+
<span class="font-medium me-1"><%= title %></span>
|
|
29
|
+
<% end %><%= content %>
|
|
30
|
+
</div>
|
|
31
|
+
<% else %>
|
|
32
|
+
<% if title? %>
|
|
33
|
+
<span class="font-medium"><%= title %></span>
|
|
34
|
+
<% end %><%= content %>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% end %>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Alert
|
|
5
|
+
# Renders the info icon shown inside an alert.
|
|
6
|
+
#
|
|
7
|
+
# Flowbite uses the same outlined info icon for all alert styles; the
|
|
8
|
+
# icon inherits its color from the alert via +currentColor+.
|
|
9
|
+
#
|
|
10
|
+
# @param class [String, Array<String>] Additional CSS classes for the svg
|
|
11
|
+
# element.
|
|
12
|
+
#
|
|
13
|
+
# @see https://flowbite.com/docs/components/alerts/
|
|
14
|
+
class Icon < ViewComponent::Base
|
|
15
|
+
SVG_PATH = "M10 11h2v5m-2 0h4m-2.592-8.5h.01M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def classes
|
|
19
|
+
["w-4", "h-4", "shrink-0"]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(class: nil)
|
|
24
|
+
@class = Array.wrap(binding.local_variable_get(:class))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call
|
|
28
|
+
tag.svg(
|
|
29
|
+
tag.path(
|
|
30
|
+
d: svg_path,
|
|
31
|
+
stroke: "currentColor",
|
|
32
|
+
"stroke-linecap": "round",
|
|
33
|
+
"stroke-linejoin": "round",
|
|
34
|
+
"stroke-width": "2"
|
|
35
|
+
),
|
|
36
|
+
"aria-hidden": "true",
|
|
37
|
+
class: svg_classes,
|
|
38
|
+
fill: "none",
|
|
39
|
+
viewBox: "0 0 24 24",
|
|
40
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def svg_classes
|
|
45
|
+
self.class.classes + @class
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def svg_path
|
|
49
|
+
SVG_PATH
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders an alert element for showing feedback messages to the user.
|
|
5
|
+
#
|
|
6
|
+
# The block given to the alert is used as its message.
|
|
7
|
+
#
|
|
8
|
+
# @example Usage
|
|
9
|
+
# <%= render(Flowbite::Alert.new) { "Something has happened!" } %>
|
|
10
|
+
#
|
|
11
|
+
# @example With title and style
|
|
12
|
+
# <%= render(Flowbite::Alert.new(style: :danger, title: "Danger alert!")) do %>
|
|
13
|
+
# Change a few things up and try submitting again.
|
|
14
|
+
# <% end %>
|
|
15
|
+
#
|
|
16
|
+
# @example With a list
|
|
17
|
+
# <%= render(Flowbite::Alert.new(title: "Ensure that these requirements are met:")) do %>
|
|
18
|
+
# <ul class="mt-2 list-disc list-outside space-y-1 ps-2.5">
|
|
19
|
+
# <li>At least 10 characters</li>
|
|
20
|
+
# </ul>
|
|
21
|
+
# <% end %>
|
|
22
|
+
#
|
|
23
|
+
# @example With additional content
|
|
24
|
+
# <%= render(Flowbite::Alert.new(bordered: true, title: "This is an info alert")) do |alert| %>
|
|
25
|
+
# <% alert.with_additional_content do %>
|
|
26
|
+
# <%= render(Flowbite::Button.new(size: :xs)) { "View more" } %>
|
|
27
|
+
# <% end %>
|
|
28
|
+
# More info about this alert.
|
|
29
|
+
# <% end %>
|
|
30
|
+
#
|
|
31
|
+
# @viewcomponent_slot additional_content Content rendered below the
|
|
32
|
+
# message, for example action buttons. When given, the title is rendered
|
|
33
|
+
# as a heading next to the icon and the message below it.
|
|
34
|
+
#
|
|
35
|
+
# @see https://flowbite.com/docs/components/alerts/
|
|
36
|
+
# @lookbook_embed AlertPreview
|
|
37
|
+
class Alert < ViewComponent::Base
|
|
38
|
+
renders_one :additional_content
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
# @return [Array<String>] The color classes for the given style and state.
|
|
42
|
+
def classes(state: :default, style: :info)
|
|
43
|
+
styles.fetch(style).fetch(state)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# rubocop:disable Layout/LineLength
|
|
47
|
+
def styles
|
|
48
|
+
Flowbite::Styles.from_hash({
|
|
49
|
+
danger: {
|
|
50
|
+
border: ["border-danger-subtle"],
|
|
51
|
+
default: ["bg-danger-soft", "text-fg-danger-strong"]
|
|
52
|
+
},
|
|
53
|
+
dark: {
|
|
54
|
+
border: ["border-default-medium"],
|
|
55
|
+
default: ["bg-neutral-secondary-medium", "text-heading"]
|
|
56
|
+
},
|
|
57
|
+
info: {
|
|
58
|
+
border: ["border-brand-subtle"],
|
|
59
|
+
default: ["bg-brand-softer", "text-fg-brand-strong"]
|
|
60
|
+
},
|
|
61
|
+
success: {
|
|
62
|
+
border: ["border-success-subtle"],
|
|
63
|
+
default: ["bg-success-soft", "text-fg-success-strong"]
|
|
64
|
+
},
|
|
65
|
+
warning: {
|
|
66
|
+
border: ["border-warning-subtle"],
|
|
67
|
+
default: ["bg-warning-soft", "text-fg-warning"]
|
|
68
|
+
}
|
|
69
|
+
}.freeze)
|
|
70
|
+
end
|
|
71
|
+
# rubocop:enable Layout/LineLength
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
attr_reader :options, :style, :title
|
|
75
|
+
|
|
76
|
+
# @param border_accent [Boolean] Whether to show a thick top border
|
|
77
|
+
# accent instead of rounded corners (default: false).
|
|
78
|
+
# @param bordered [Boolean] Whether to add a border in the color of the
|
|
79
|
+
# style (default: false).
|
|
80
|
+
# @param class [String, Array<String>] Additional CSS classes for the
|
|
81
|
+
# alert container.
|
|
82
|
+
# @param icon [Boolean] Whether to show an info icon (default: true).
|
|
83
|
+
# @param options [Hash] Additional HTML options for the alert container.
|
|
84
|
+
# @param style [Symbol] The color style of the alert (:danger, :dark,
|
|
85
|
+
# :info, :success, :warning).
|
|
86
|
+
# @param title [String] An optional bold title shown before the message.
|
|
87
|
+
def initialize(border_accent: false, bordered: false, class: nil,
|
|
88
|
+
icon: true, style: :info, title: nil, **options)
|
|
89
|
+
@border_accent = border_accent
|
|
90
|
+
@bordered = bordered
|
|
91
|
+
@class = Array.wrap(binding.local_variable_get(:class))
|
|
92
|
+
@icon = icon
|
|
93
|
+
@options = options
|
|
94
|
+
@style = style
|
|
95
|
+
@title = title
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def border_accent?
|
|
99
|
+
!!@border_accent
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def bordered?
|
|
103
|
+
!!@bordered
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def container_classes
|
|
107
|
+
result = ["p-4", "text-sm"]
|
|
108
|
+
result += layout_classes
|
|
109
|
+
result += self.class.classes(style: style)
|
|
110
|
+
result += border_classes
|
|
111
|
+
result += ["rounded-base"] unless border_accent?
|
|
112
|
+
result + @class
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @return [Array<String>] The CSS classes for the icon in the current
|
|
116
|
+
# layout.
|
|
117
|
+
def icon_classes
|
|
118
|
+
if additional_content?
|
|
119
|
+
["me-2"]
|
|
120
|
+
else
|
|
121
|
+
["me-2", "mt-0.5"]
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def icon?
|
|
126
|
+
!!@icon
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @return [String] The screen reader label describing the alert style.
|
|
130
|
+
def style_label
|
|
131
|
+
style.to_s.capitalize
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def title?
|
|
135
|
+
title.present?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def border_classes
|
|
141
|
+
if border_accent?
|
|
142
|
+
["border-t-4"] + self.class.classes(state: :border, style: style)
|
|
143
|
+
elsif bordered?
|
|
144
|
+
["border"] + self.class.classes(state: :border, style: style)
|
|
145
|
+
else
|
|
146
|
+
[]
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def layout_classes
|
|
151
|
+
return [] if additional_content? || !icon?
|
|
152
|
+
|
|
153
|
+
["flex", "items-start"]
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<% avatar = capture do %>
|
|
2
|
+
<% if image? %>
|
|
3
|
+
<%= tag.img(alt: alt, class: image_classes, src: src, **options) %>
|
|
4
|
+
<% elsif initials? %>
|
|
5
|
+
<%= tag.div(class: initials_classes, **options) do %>
|
|
6
|
+
<span class="font-medium text-body"><%= initials %></span>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% else %>
|
|
9
|
+
<%= tag.div(class: placeholder_classes, **options) do %>
|
|
10
|
+
<%= render(Flowbite::Avatar::PlaceholderIcon.new(size: size)) %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<% if dot? %>
|
|
16
|
+
<div class="relative flex h-fit w-fit">
|
|
17
|
+
<%= avatar %>
|
|
18
|
+
<%= top_dot %>
|
|
19
|
+
<%= bottom_dot %>
|
|
20
|
+
</div>
|
|
21
|
+
<% else %>
|
|
22
|
+
<%= avatar %>
|
|
23
|
+
<% end %>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Avatar
|
|
5
|
+
# Renders a status dot indicator positioned on top of an avatar.
|
|
6
|
+
#
|
|
7
|
+
# Usually added through the +top_dot+ or +bottom_dot+ slots of
|
|
8
|
+
# Flowbite::Avatar, which set the position and size. When rendered on its
|
|
9
|
+
# own, the dot must be placed inside a relatively positioned container
|
|
10
|
+
# together with the avatar.
|
|
11
|
+
#
|
|
12
|
+
# @example Through an Avatar slot
|
|
13
|
+
# <%= render(Flowbite::Avatar.new(src: user.avatar_url)) do |avatar| %>
|
|
14
|
+
# <% avatar.with_top_dot(label: "Online", style: :success) %>
|
|
15
|
+
# <% end %>
|
|
16
|
+
#
|
|
17
|
+
# @example Standalone
|
|
18
|
+
# <div class="relative">
|
|
19
|
+
# <%= render(Flowbite::Avatar.new(src: user.avatar_url)) %>
|
|
20
|
+
# <%= render(Flowbite::Avatar::Dot.new(label: "Online", position: :bottom, style: :success)) %>
|
|
21
|
+
# </div>
|
|
22
|
+
#
|
|
23
|
+
# @see https://flowbite.com/docs/components/avatar/
|
|
24
|
+
class Dot < ViewComponent::Base
|
|
25
|
+
POSITIONS = {
|
|
26
|
+
bottom: ["bottom-0"],
|
|
27
|
+
top: ["top-0"]
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
# Horizontal offsets placing the dot at the right edge of an avatar of
|
|
31
|
+
# the given size.
|
|
32
|
+
SIZES = {
|
|
33
|
+
xs: ["left-1.5"],
|
|
34
|
+
sm: ["left-3"],
|
|
35
|
+
base: ["left-5"],
|
|
36
|
+
default: ["left-7"],
|
|
37
|
+
lg: ["left-8"],
|
|
38
|
+
xl: ["left-11"],
|
|
39
|
+
"2xl": ["left-13"]
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
def classes(position: :top, size: :default, style: :success)
|
|
44
|
+
POSITIONS.fetch(position) + sizes.fetch(size) +
|
|
45
|
+
["absolute", "w-3.5", "h-3.5"] +
|
|
46
|
+
styles.fetch(style).fetch(:default) +
|
|
47
|
+
["border-2", "border-buffer", "rounded-full"]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def sizes
|
|
51
|
+
SIZES
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def styles
|
|
55
|
+
Flowbite::Styles.from_hash(
|
|
56
|
+
{
|
|
57
|
+
danger: {default: ["bg-danger"]},
|
|
58
|
+
success: {default: ["bg-success"]}
|
|
59
|
+
}.freeze
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attr_reader :label, :position, :size, :style
|
|
65
|
+
|
|
66
|
+
# @param label [String] Text describing the status for screen readers,
|
|
67
|
+
# since the dot otherwise only conveys it through color.
|
|
68
|
+
# @param position [Symbol] Where to place the dot (:bottom, :top).
|
|
69
|
+
# @param size [Symbol] The size of the avatar the dot is placed on.
|
|
70
|
+
# @param style [Symbol] The color of the dot (:danger, :success).
|
|
71
|
+
def initialize(label: nil, position: :top, size: :default, style: :success)
|
|
72
|
+
@label = label
|
|
73
|
+
@position = position
|
|
74
|
+
@size = size
|
|
75
|
+
@style = style
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def call
|
|
79
|
+
content_tag(:span, class: self.class.classes(position: position, size: size, style: style)) do
|
|
80
|
+
content_tag(:span, label, class: "sr-only") if label.present?
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
aria-hidden="true"
|
|
3
|
+
class="<%= classes.join(" ") %>"
|
|
4
|
+
fill="currentColor"
|
|
5
|
+
viewBox="0 0 20 20"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
>
|
|
8
|
+
<path
|
|
9
|
+
clip-rule="evenodd"
|
|
10
|
+
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
|
11
|
+
fill-rule="evenodd"
|
|
12
|
+
/>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Avatar
|
|
5
|
+
# Renders the person icon shown in an avatar without an image or
|
|
6
|
+
# initials.
|
|
7
|
+
#
|
|
8
|
+
# @see https://flowbite.com/docs/components/avatar/
|
|
9
|
+
class PlaceholderIcon < ViewComponent::Base
|
|
10
|
+
# The icon is slightly larger than the avatar and offset to the left,
|
|
11
|
+
# so the person is centered and cropped by the container.
|
|
12
|
+
SIZES = {
|
|
13
|
+
xs: ["w-5.5", "h-5.5", "-left-0.5"],
|
|
14
|
+
sm: ["w-7", "h-7", "-left-0.5"],
|
|
15
|
+
base: ["w-10", "h-10", "-left-1"],
|
|
16
|
+
default: ["w-12", "h-12", "-left-1"],
|
|
17
|
+
lg: ["w-13", "h-13", "-left-1"],
|
|
18
|
+
xl: ["w-17", "h-17", "-left-1.5"],
|
|
19
|
+
"2xl": ["w-19", "h-19", "-left-1.5"]
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def classes(size: :default)
|
|
24
|
+
["absolute"] + sizes.fetch(size) + ["text-body-subtle"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def sizes
|
|
28
|
+
SIZES
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attr_reader :size
|
|
33
|
+
|
|
34
|
+
# @param size [Symbol] The size of the avatar the icon is placed in.
|
|
35
|
+
def initialize(size: :default)
|
|
36
|
+
@size = size
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def classes
|
|
40
|
+
self.class.classes(size: size)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders an avatar representing a user.
|
|
5
|
+
#
|
|
6
|
+
# The avatar shows an image when +src+ is given. Without an image it falls
|
|
7
|
+
# back to the given +initials+, and without initials it renders a
|
|
8
|
+
# placeholder person icon.
|
|
9
|
+
#
|
|
10
|
+
# @example Image
|
|
11
|
+
# <%= render(Flowbite::Avatar.new(alt: "Jese Leos", src: user.avatar_url)) %>
|
|
12
|
+
#
|
|
13
|
+
# @example Initials with a rounded square shape
|
|
14
|
+
# <%= render(Flowbite::Avatar.new(initials: "JL", shape: :rounded)) %>
|
|
15
|
+
#
|
|
16
|
+
# @example Placeholder icon with a status dot
|
|
17
|
+
# <%= render(Flowbite::Avatar.new) do |avatar| %>
|
|
18
|
+
# <% avatar.with_bottom_dot(label: "Online", style: :success) %>
|
|
19
|
+
# <% end %>
|
|
20
|
+
#
|
|
21
|
+
# @viewcomponent_slot [Flowbite::Avatar::Dot] bottom_dot A status dot placed
|
|
22
|
+
# at the bottom of the avatar. Use +with_bottom_dot+ to add one.
|
|
23
|
+
# @viewcomponent_slot [Flowbite::Avatar::Dot] top_dot A status dot placed at
|
|
24
|
+
# the top of the avatar. Use +with_top_dot+ to add one.
|
|
25
|
+
#
|
|
26
|
+
# @see https://flowbite.com/docs/components/avatar/
|
|
27
|
+
# @lookbook_embed AvatarPreview
|
|
28
|
+
class Avatar < ViewComponent::Base
|
|
29
|
+
renders_one :bottom_dot, ->(**options) { Flowbite::Avatar::Dot.new(position: :bottom, size: size, **options) }
|
|
30
|
+
renders_one :top_dot, ->(**options) { Flowbite::Avatar::Dot.new(position: :top, size: size, **options) }
|
|
31
|
+
|
|
32
|
+
BORDERED_CLASSES = ["p-1", "ring-2", "ring-default"].freeze
|
|
33
|
+
|
|
34
|
+
INITIALS_CLASSES = ["relative", "inline-flex", "items-center", "justify-center", "overflow-hidden", "bg-neutral-tertiary"].freeze
|
|
35
|
+
|
|
36
|
+
PLACEHOLDER_CLASSES = ["relative", "overflow-hidden", "bg-neutral-secondary-medium"].freeze
|
|
37
|
+
|
|
38
|
+
SIZES = {
|
|
39
|
+
xs: ["w-4.5", "h-4.5"],
|
|
40
|
+
sm: ["w-6", "h-6"],
|
|
41
|
+
base: ["w-8", "h-8"],
|
|
42
|
+
default: ["w-10", "h-10"],
|
|
43
|
+
lg: ["w-11", "h-11"],
|
|
44
|
+
xl: ["w-14", "h-14"],
|
|
45
|
+
"2xl": ["w-16", "h-16"]
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
def classes(bordered: false, shape: :circle, size: :default)
|
|
50
|
+
result = sizes.fetch(size) + shape_classes(shape: shape, size: size)
|
|
51
|
+
result += BORDERED_CLASSES if bordered
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @return [Array<String>] The classes defining the corners of the
|
|
56
|
+
# avatar. Small rounded avatars get smaller corners.
|
|
57
|
+
def shape_classes(shape: :circle, size: :default)
|
|
58
|
+
case shape
|
|
59
|
+
when :circle then ["rounded-full"]
|
|
60
|
+
when :rounded then [:xs, :sm].include?(size) ? ["rounded-sm"] : ["rounded-base"]
|
|
61
|
+
else raise ArgumentError, "Unknown shape: #{shape}. Available shapes: circle, rounded"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def sizes
|
|
66
|
+
SIZES
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
attr_reader :alt, :initials, :options, :size, :src
|
|
71
|
+
|
|
72
|
+
# @param alt [String] Alternative text for the image. Defaults to an
|
|
73
|
+
# empty string, marking the image as decorative.
|
|
74
|
+
# @param bordered [Boolean] Whether to add a ring around the avatar.
|
|
75
|
+
# @param class [String, Array<String>] Additional CSS classes for the
|
|
76
|
+
# avatar element.
|
|
77
|
+
# @param initials [String] Initials to show when there is no image.
|
|
78
|
+
# @param options [Hash] Additional HTML options for the avatar element.
|
|
79
|
+
# @param shape [Symbol] The shape of the avatar (:circle, :rounded).
|
|
80
|
+
# @param size [Symbol] The size of the avatar (:xs, :sm, :base, :default,
|
|
81
|
+
# :lg, :xl, :"2xl").
|
|
82
|
+
# @param src [String] The URL of the avatar image. When blank, the avatar
|
|
83
|
+
# falls back to initials or a placeholder icon.
|
|
84
|
+
def initialize(alt: nil, bordered: false, class: nil, initials: nil, shape: :circle, size: :default, src: nil, **options)
|
|
85
|
+
@alt = alt.to_s
|
|
86
|
+
@bordered = bordered
|
|
87
|
+
@class = Array.wrap(binding.local_variable_get(:class))
|
|
88
|
+
@initials = initials
|
|
89
|
+
@shape = shape
|
|
90
|
+
@size = size
|
|
91
|
+
@src = src
|
|
92
|
+
@options = options
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def bordered?
|
|
96
|
+
!!@bordered
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def dot?
|
|
100
|
+
top_dot? || bottom_dot?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def image?
|
|
104
|
+
@src.present?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @return [Array<String>] The classes for the image element.
|
|
108
|
+
def image_classes
|
|
109
|
+
classes + @class
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def initials?
|
|
113
|
+
@initials.present?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @return [Array<String>] The classes for the initials container.
|
|
117
|
+
def initials_classes
|
|
118
|
+
INITIALS_CLASSES + classes + @class
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @return [Array<String>] The classes for the placeholder icon container.
|
|
122
|
+
def placeholder_classes
|
|
123
|
+
PLACEHOLDER_CLASSES + classes + @class
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def classes
|
|
129
|
+
self.class.classes(bordered: bordered?, shape: @shape, size: @size)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Badge
|
|
5
|
+
# Renders a colored dot indicator for use inside a badge.
|
|
6
|
+
#
|
|
7
|
+
# @param style [Symbol] The color style of the dot (:alternative, :brand,
|
|
8
|
+
# :danger, :gray, :success, :warning).
|
|
9
|
+
class Dot < ViewComponent::Base
|
|
10
|
+
CLASSES = {
|
|
11
|
+
alternative: ["bg-heading", "me-1", "rounded-full"],
|
|
12
|
+
brand: ["bg-fg-brand-strong", "me-1", "rounded-full"],
|
|
13
|
+
danger: ["bg-fg-danger-strong", "me-1", "rounded-full"],
|
|
14
|
+
gray: ["bg-heading", "me-1", "rounded-full"],
|
|
15
|
+
success: ["bg-fg-success-strong", "me-1", "rounded-full"],
|
|
16
|
+
warning: ["bg-fg-warning", "me-1", "rounded-full"]
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
SIZES = {
|
|
20
|
+
default: ["h-1.5", "w-1.5"]
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
def classes(size: :default, style: :brand)
|
|
25
|
+
CLASSES.fetch(style) + sizes.fetch(size)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def sizes
|
|
29
|
+
SIZES
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_reader :size, :style
|
|
34
|
+
|
|
35
|
+
def initialize(size: :default, style: :brand)
|
|
36
|
+
@size = size
|
|
37
|
+
@style = style
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def call
|
|
41
|
+
content_tag(:span, nil, class: self.class.classes(size: size, style: style))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|