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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Badge
|
|
5
|
+
# Renders a pill-shaped badge with fully rounded corners.
|
|
6
|
+
#
|
|
7
|
+
# @example Basic usage
|
|
8
|
+
# <%= render(Flowbite::Badge::Pill.new) { "Default" } %>
|
|
9
|
+
#
|
|
10
|
+
# @see https://flowbite.com/docs/components/badge/
|
|
11
|
+
class Pill < Flowbite::Badge
|
|
12
|
+
class << self
|
|
13
|
+
# rubocop:disable Layout/LineLength
|
|
14
|
+
def styles
|
|
15
|
+
Flowbite::Styles.from_hash({
|
|
16
|
+
alternative: {
|
|
17
|
+
default: ["bg-neutral-primary-soft", "hover:bg-neutral-secondary-medium", "rounded-full", "text-heading"]
|
|
18
|
+
},
|
|
19
|
+
brand: {
|
|
20
|
+
default: ["bg-brand-softer", "hover:bg-brand-soft", "rounded-full", "text-fg-brand-strong"]
|
|
21
|
+
},
|
|
22
|
+
danger: {
|
|
23
|
+
default: ["bg-danger-soft", "hover:bg-danger-medium", "rounded-full", "text-fg-danger-strong"]
|
|
24
|
+
},
|
|
25
|
+
gray: {
|
|
26
|
+
default: ["bg-neutral-secondary-medium", "hover:bg-neutral-tertiary-medium", "rounded-full", "text-heading"]
|
|
27
|
+
},
|
|
28
|
+
success: {
|
|
29
|
+
default: ["bg-success-soft", "hover:bg-success-medium", "rounded-full", "text-fg-success-strong"]
|
|
30
|
+
},
|
|
31
|
+
warning: {
|
|
32
|
+
default: ["bg-warning-soft", "hover:bg-warning-medium", "rounded-full", "text-fg-warning"]
|
|
33
|
+
}
|
|
34
|
+
}.freeze)
|
|
35
|
+
end
|
|
36
|
+
# rubocop:enable Layout/LineLength
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders a badge component for displaying labels, counts, or status
|
|
5
|
+
# indicators.
|
|
6
|
+
#
|
|
7
|
+
# @example Basic usage
|
|
8
|
+
# <%= render(Flowbite::Badge.new) { "Default" } %>
|
|
9
|
+
#
|
|
10
|
+
# @example With border
|
|
11
|
+
# <%= render(Flowbite::Badge.new(bordered: true, style: :success)) { "Success" } %>
|
|
12
|
+
#
|
|
13
|
+
# @see https://flowbite.com/docs/components/badge/
|
|
14
|
+
# @lookbook_embed BadgePreview
|
|
15
|
+
class Badge < ViewComponent::Base
|
|
16
|
+
SIZES = {
|
|
17
|
+
default: ["text-xs", "font-medium", "px-1.5", "py-0.5"],
|
|
18
|
+
lg: ["text-sm", "font-medium", "px-2", "py-1"]
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
BORDER_CLASSES = {
|
|
22
|
+
alternative: ["border", "border-default"],
|
|
23
|
+
brand: ["border", "border-brand-subtle"],
|
|
24
|
+
danger: ["border", "border-danger-subtle"],
|
|
25
|
+
gray: ["border", "border-default-medium"],
|
|
26
|
+
success: ["border", "border-success-subtle"],
|
|
27
|
+
warning: ["border", "border-warning-subtle"]
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
def classes(size: :default, state: :default, style: :brand)
|
|
32
|
+
styles.fetch(style).fetch(state) + sizes.fetch(size)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def sizes
|
|
36
|
+
SIZES
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# rubocop:disable Layout/LineLength
|
|
40
|
+
def styles
|
|
41
|
+
Flowbite::Styles.from_hash({
|
|
42
|
+
alternative: {
|
|
43
|
+
default: ["bg-neutral-primary-soft", "hover:bg-neutral-secondary-medium", "rounded", "text-heading"]
|
|
44
|
+
},
|
|
45
|
+
brand: {
|
|
46
|
+
default: ["bg-brand-softer", "hover:bg-brand-soft", "rounded", "text-fg-brand-strong"]
|
|
47
|
+
},
|
|
48
|
+
danger: {
|
|
49
|
+
default: ["bg-danger-soft", "hover:bg-danger-medium", "rounded", "text-fg-danger-strong"]
|
|
50
|
+
},
|
|
51
|
+
gray: {
|
|
52
|
+
default: ["bg-neutral-secondary-medium", "hover:bg-neutral-tertiary-medium", "rounded", "text-heading"]
|
|
53
|
+
},
|
|
54
|
+
success: {
|
|
55
|
+
default: ["bg-success-soft", "hover:bg-success-medium", "rounded", "text-fg-success-strong"]
|
|
56
|
+
},
|
|
57
|
+
warning: {
|
|
58
|
+
default: ["bg-warning-soft", "hover:bg-warning-medium", "rounded", "text-fg-warning"]
|
|
59
|
+
}
|
|
60
|
+
}.freeze)
|
|
61
|
+
end
|
|
62
|
+
# rubocop:enable Layout/LineLength
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
attr_reader :options
|
|
66
|
+
|
|
67
|
+
# @param bordered [Boolean] Whether to add a border to the badge.
|
|
68
|
+
# @param class [String, Array<String>] Additional CSS classes.
|
|
69
|
+
# @param dot [Boolean] Whether to show a dot indicator.
|
|
70
|
+
# @param href [String] If provided, renders the badge as a link.
|
|
71
|
+
# @param size [Symbol] The size of the badge (:default or :lg).
|
|
72
|
+
# @param style [Symbol] The color style (:alternative, :brand, :danger,
|
|
73
|
+
# :gray, :success, :warning).
|
|
74
|
+
def initialize(bordered: false, class: nil, dot: false, href: nil,
|
|
75
|
+
size: :default, style: :brand, **options)
|
|
76
|
+
@bordered = bordered
|
|
77
|
+
@class = Array.wrap(binding.local_variable_get(:class))
|
|
78
|
+
@dot = dot
|
|
79
|
+
@href = href
|
|
80
|
+
@size = size
|
|
81
|
+
@style = style
|
|
82
|
+
@options = options
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def bordered?
|
|
86
|
+
!!@bordered
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def dot?
|
|
90
|
+
!!@dot
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def link?
|
|
94
|
+
@href.present?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def classes
|
|
100
|
+
result = self.class.classes(size: @size, state: :default, style: @style)
|
|
101
|
+
result += BORDER_CLASSES.fetch(@style) if bordered?
|
|
102
|
+
result += ["inline-flex", "items-center"] if dot?
|
|
103
|
+
result + @class
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def tag_name
|
|
107
|
+
link? ? :a : :span
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def tag_options
|
|
111
|
+
opts = {class: classes}
|
|
112
|
+
opts[:href] = @href if link?
|
|
113
|
+
opts.merge(options)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -70,14 +70,10 @@ module Flowbite
|
|
|
70
70
|
|
|
71
71
|
# Returns the HTML to use for the title element if any
|
|
72
72
|
def default_title
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if default_title_content
|
|
76
|
-
component.with_content(default_title_content)
|
|
77
|
-
else
|
|
78
|
-
component
|
|
79
|
-
end
|
|
73
|
+
return unless title?
|
|
80
74
|
|
|
75
|
+
component = Flowbite::Card::Title.new(**default_title_options)
|
|
76
|
+
component.with_content(default_title_content)
|
|
81
77
|
render(component)
|
|
82
78
|
end
|
|
83
79
|
|
|
@@ -94,7 +90,7 @@ module Flowbite
|
|
|
94
90
|
end
|
|
95
91
|
|
|
96
92
|
def title?
|
|
97
|
-
|
|
93
|
+
default_title_content.present?
|
|
98
94
|
end
|
|
99
95
|
end
|
|
100
96
|
end
|
|
@@ -31,6 +31,13 @@ module Flowbite
|
|
|
31
31
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
|
+
def default_input_arguments
|
|
35
|
+
args = super
|
|
36
|
+
args[:unchecked_value] = @input[:unchecked_value] if @input.key?(:unchecked_value)
|
|
37
|
+
args[:value] = @input[:value] if @input.key?(:value)
|
|
38
|
+
args
|
|
39
|
+
end
|
|
40
|
+
|
|
34
41
|
def hint_classes
|
|
35
42
|
if disabled?
|
|
36
43
|
"text-xs font-normal text-fg-disabled"
|
|
@@ -39,13 +46,6 @@ module Flowbite
|
|
|
39
46
|
end
|
|
40
47
|
end
|
|
41
48
|
|
|
42
|
-
def input_arguments
|
|
43
|
-
args = super
|
|
44
|
-
args[:unchecked_value] = @input[:unchecked_value] if @input.key?(:unchecked_value)
|
|
45
|
-
args[:value] = @input[:value] if @input.key?(:value)
|
|
46
|
-
args
|
|
47
|
-
end
|
|
48
|
-
|
|
49
49
|
def label_classes
|
|
50
50
|
if disabled?
|
|
51
51
|
"font-medium text-fg-disabled"
|
|
@@ -155,11 +155,19 @@ module Flowbite
|
|
|
155
155
|
return unless hint?
|
|
156
156
|
|
|
157
157
|
component = Flowbite::Input::Hint.new(
|
|
158
|
+
**default_hint_arguments
|
|
159
|
+
).with_content(default_hint_content)
|
|
160
|
+
render(component)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @return [Hash] The keyword arguments for the hint component.
|
|
164
|
+
def default_hint_arguments
|
|
165
|
+
{
|
|
158
166
|
attribute: @attribute,
|
|
167
|
+
class: @hint[:class],
|
|
159
168
|
form: @form,
|
|
160
169
|
options: default_hint_options
|
|
161
|
-
|
|
162
|
-
render(component)
|
|
170
|
+
}
|
|
163
171
|
end
|
|
164
172
|
|
|
165
173
|
def default_hint_content
|
|
@@ -180,23 +188,37 @@ module Flowbite
|
|
|
180
188
|
}.merge(@hint[:options] || {})
|
|
181
189
|
end
|
|
182
190
|
|
|
191
|
+
# Returns the HTML to use for the default input element.
|
|
192
|
+
def default_input
|
|
193
|
+
render(input_component.new(**default_input_arguments))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @return [Hash] The keyword arguments for the default input component.
|
|
197
|
+
def default_input_arguments
|
|
198
|
+
{
|
|
199
|
+
attribute: @attribute,
|
|
200
|
+
class: @input[:class],
|
|
201
|
+
disabled: @disabled,
|
|
202
|
+
form: @form,
|
|
203
|
+
options: default_input_options,
|
|
204
|
+
size: @size
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
|
|
183
208
|
# Returns a Hash with the default attributes to apply to the input element.
|
|
184
209
|
#
|
|
185
210
|
# The default attributes can be overriden by passing the `input[options]`
|
|
186
211
|
# argument to the constructor.
|
|
187
212
|
def default_input_options
|
|
188
|
-
if hint?
|
|
213
|
+
options = if hint?
|
|
189
214
|
{
|
|
190
215
|
"aria-describedby": id_for_hint_element
|
|
191
216
|
}
|
|
192
217
|
else
|
|
193
218
|
{}
|
|
194
219
|
end
|
|
195
|
-
end
|
|
196
220
|
|
|
197
|
-
|
|
198
|
-
def default_input
|
|
199
|
-
render(input_component.new(**input_arguments))
|
|
221
|
+
options.merge(@input[:options] || {})
|
|
200
222
|
end
|
|
201
223
|
|
|
202
224
|
def default_label
|
|
@@ -241,20 +263,5 @@ module Flowbite
|
|
|
241
263
|
.compact_blank
|
|
242
264
|
.join("_")
|
|
243
265
|
end
|
|
244
|
-
|
|
245
|
-
# @return [Hash] The keyword arguments for the input component.
|
|
246
|
-
def input_arguments
|
|
247
|
-
{
|
|
248
|
-
attribute: @attribute,
|
|
249
|
-
disabled: @disabled,
|
|
250
|
-
form: @form,
|
|
251
|
-
options: input_options,
|
|
252
|
-
size: @size
|
|
253
|
-
}
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def input_options
|
|
257
|
-
default_input_options.merge(@input[:options] || {})
|
|
258
|
-
end
|
|
259
266
|
end
|
|
260
267
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Pagination
|
|
5
|
+
# Renders the chevron-left icon shown on a pagination's previous link.
|
|
6
|
+
#
|
|
7
|
+
# This is automatically used by {Flowbite::Pagination::PreviousLink}, but
|
|
8
|
+
# can be used standalone if needed.
|
|
9
|
+
#
|
|
10
|
+
# @example Standalone usage
|
|
11
|
+
# <%= render Flowbite::Pagination::ChevronLeftIcon.new %>
|
|
12
|
+
class ChevronLeftIcon < ViewComponent::Base
|
|
13
|
+
def call
|
|
14
|
+
tag.svg(
|
|
15
|
+
class: "w-4 h-4 rtl:rotate-180",
|
|
16
|
+
"aria-hidden": "true",
|
|
17
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
18
|
+
width: 24,
|
|
19
|
+
height: 24,
|
|
20
|
+
fill: "none",
|
|
21
|
+
viewBox: "0 0 24 24"
|
|
22
|
+
) do
|
|
23
|
+
tag.path(
|
|
24
|
+
stroke: "currentColor",
|
|
25
|
+
"stroke-linecap": "round",
|
|
26
|
+
"stroke-linejoin": "round",
|
|
27
|
+
"stroke-width": "2",
|
|
28
|
+
d: "m15 19-7-7 7-7"
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Pagination
|
|
5
|
+
# Renders the chevron-right icon shown on a pagination's next link.
|
|
6
|
+
#
|
|
7
|
+
# This is automatically used by {Flowbite::Pagination::NextLink}, but can
|
|
8
|
+
# be used standalone if needed.
|
|
9
|
+
#
|
|
10
|
+
# @example Standalone usage
|
|
11
|
+
# <%= render Flowbite::Pagination::ChevronRightIcon.new %>
|
|
12
|
+
class ChevronRightIcon < ViewComponent::Base
|
|
13
|
+
def call
|
|
14
|
+
tag.svg(
|
|
15
|
+
class: "w-4 h-4 rtl:rotate-180",
|
|
16
|
+
"aria-hidden": "true",
|
|
17
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
18
|
+
width: 24,
|
|
19
|
+
height: 24,
|
|
20
|
+
fill: "none",
|
|
21
|
+
viewBox: "0 0 24 24"
|
|
22
|
+
) do
|
|
23
|
+
tag.path(
|
|
24
|
+
stroke: "currentColor",
|
|
25
|
+
"stroke-linecap": "round",
|
|
26
|
+
"stroke-linejoin": "round",
|
|
27
|
+
"stroke-width": "2",
|
|
28
|
+
d: "m9 5 7 7-7 7"
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Pagination
|
|
5
|
+
# Renders a gap indicator between page numbers in a pagination component.
|
|
6
|
+
#
|
|
7
|
+
# This is automatically used by {Flowbite::Pagination}, but can be used
|
|
8
|
+
# standalone if needed.
|
|
9
|
+
#
|
|
10
|
+
# @example Standalone usage
|
|
11
|
+
# <%= render Flowbite::Pagination::Gap.new %>
|
|
12
|
+
class Gap < ViewComponent::Base
|
|
13
|
+
class << self
|
|
14
|
+
def classes
|
|
15
|
+
["flex", "items-center", "justify-center", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "hover:text-heading", "font-medium", "text-sm", "w-9", "h-9", "focus:outline-none"]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
content_tag(:li, "…", class: self.class.classes)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Pagination
|
|
5
|
+
# Renders a single page number link in a pagination component.
|
|
6
|
+
#
|
|
7
|
+
# This is automatically used by {Flowbite::Pagination}, but can be used
|
|
8
|
+
# standalone if needed.
|
|
9
|
+
#
|
|
10
|
+
# @param current [Boolean] Whether this link represents the current page.
|
|
11
|
+
# @param page [Integer] The page number to display.
|
|
12
|
+
# @param url [String] The URL for the page.
|
|
13
|
+
#
|
|
14
|
+
# @example Standalone usage
|
|
15
|
+
# <%= render Flowbite::Pagination::Link.new(page: 2, url: "/posts?page=2") %>
|
|
16
|
+
class Link < ViewComponent::Base
|
|
17
|
+
class << self
|
|
18
|
+
def classes(current: false)
|
|
19
|
+
if current
|
|
20
|
+
["flex", "items-center", "justify-center", "text-fg-brand", "bg-neutral-tertiary-medium", "box-border", "border", "border-default-medium", "hover:text-fg-brand", "font-medium", "text-sm", "w-9", "h-9", "focus:outline-none"]
|
|
21
|
+
else
|
|
22
|
+
["flex", "items-center", "justify-center", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "hover:text-heading", "font-medium", "text-sm", "w-9", "h-9", "focus:outline-none"]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_reader :page, :url
|
|
28
|
+
|
|
29
|
+
def initialize(page:, url:, current: false)
|
|
30
|
+
@current = current
|
|
31
|
+
@page = page
|
|
32
|
+
@url = url
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def call
|
|
36
|
+
content_tag(:li) { render_link }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def current?
|
|
40
|
+
!!@current
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def render_link
|
|
46
|
+
link_options = {class: self.class.classes(current: current?)}
|
|
47
|
+
link_options[:aria] = {current: "page"} if current?
|
|
48
|
+
content_tag(:a, page, href: url, **link_options)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Pagination
|
|
5
|
+
# Renders the "Next page" link in a pagination component.
|
|
6
|
+
#
|
|
7
|
+
# This is automatically used by {Flowbite::Pagination}, but can be used
|
|
8
|
+
# standalone if needed.
|
|
9
|
+
#
|
|
10
|
+
# @param disabled [Boolean] Whether to disable the link. When disabled, a
|
|
11
|
+
# non-interactive +<span>+ is rendered instead of a link, so the control
|
|
12
|
+
# can't be focused or activated.
|
|
13
|
+
# @param url [String] The URL for the next page. Ignored if +disabled+.
|
|
14
|
+
#
|
|
15
|
+
# @example Standalone usage
|
|
16
|
+
# <%= render Flowbite::Pagination::NextLink.new(url: "/posts?page=2") %>
|
|
17
|
+
class NextLink < ViewComponent::Base
|
|
18
|
+
class << self
|
|
19
|
+
def classes(disabled: false)
|
|
20
|
+
classes = ["flex", "items-center", "justify-center", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "hover:text-heading", "font-medium", "rounded-e-base", "text-sm", "w-9", "h-9", "focus:outline-none"]
|
|
21
|
+
classes += ["opacity-50", "cursor-not-allowed"] if disabled
|
|
22
|
+
classes
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :url
|
|
27
|
+
|
|
28
|
+
def initialize(url:, disabled: false)
|
|
29
|
+
@disabled = disabled
|
|
30
|
+
@url = url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
content_tag(:li) { render_link }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def disabled?
|
|
38
|
+
!!@disabled
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def render_link
|
|
44
|
+
content_tag(tag_name, tag_options) do
|
|
45
|
+
concat(content_tag(:span, "Next", class: "sr-only"))
|
|
46
|
+
concat(render(Flowbite::Pagination::ChevronRightIcon.new))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def tag_name
|
|
51
|
+
disabled? ? :span : :a
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def tag_options
|
|
55
|
+
options = {class: self.class.classes(disabled: disabled?)}
|
|
56
|
+
|
|
57
|
+
if disabled?
|
|
58
|
+
options[:"aria-disabled"] = "true"
|
|
59
|
+
else
|
|
60
|
+
options[:href] = url
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
options
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%= tag.nav("aria-label": "Pagination", **options) do %>
|
|
2
|
+
<ul class="<%= list_classes.join(" ") %>">
|
|
3
|
+
<%= render(Flowbite::Pagination::PreviousLink.new(url: previous_url, disabled: previous_url.nil?)) %>
|
|
4
|
+
|
|
5
|
+
<% pages.each do |page| %>
|
|
6
|
+
<%= page %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= render(Flowbite::Pagination::NextLink.new(url: next_url, disabled: next_url.nil?)) %>
|
|
10
|
+
</ul>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Pagination
|
|
5
|
+
# Renders the "Previous page" link in a pagination component.
|
|
6
|
+
#
|
|
7
|
+
# This is automatically used by {Flowbite::Pagination}, but can be used
|
|
8
|
+
# standalone if needed.
|
|
9
|
+
#
|
|
10
|
+
# @param disabled [Boolean] Whether to disable the link. When disabled, a
|
|
11
|
+
# non-interactive +<span>+ is rendered instead of a link, so the control
|
|
12
|
+
# can't be focused or activated.
|
|
13
|
+
# @param url [String] The URL for the previous page. Ignored if +disabled+.
|
|
14
|
+
#
|
|
15
|
+
# @example Standalone usage
|
|
16
|
+
# <%= render Flowbite::Pagination::PreviousLink.new(url: "/posts?page=1") %>
|
|
17
|
+
class PreviousLink < ViewComponent::Base
|
|
18
|
+
class << self
|
|
19
|
+
def classes(disabled: false)
|
|
20
|
+
classes = ["flex", "items-center", "justify-center", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "hover:text-heading", "font-medium", "rounded-s-base", "text-sm", "w-9", "h-9", "focus:outline-none"]
|
|
21
|
+
classes += ["opacity-50", "cursor-not-allowed"] if disabled
|
|
22
|
+
classes
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :url
|
|
27
|
+
|
|
28
|
+
def initialize(url:, disabled: false)
|
|
29
|
+
@disabled = disabled
|
|
30
|
+
@url = url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
content_tag(:li) { render_link }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def disabled?
|
|
38
|
+
!!@disabled
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def render_link
|
|
44
|
+
content_tag(tag_name, tag_options) do
|
|
45
|
+
concat(content_tag(:span, "Previous", class: "sr-only"))
|
|
46
|
+
concat(render(Flowbite::Pagination::ChevronLeftIcon.new))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def tag_name
|
|
51
|
+
disabled? ? :span : :a
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def tag_options
|
|
55
|
+
options = {class: self.class.classes(disabled: disabled?)}
|
|
56
|
+
|
|
57
|
+
if disabled?
|
|
58
|
+
options[:"aria-disabled"] = "true"
|
|
59
|
+
else
|
|
60
|
+
options[:href] = url
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
options
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders pagination controls for navigating between pages of results.
|
|
5
|
+
#
|
|
6
|
+
# This component doesn't know about pages, page numbers, or any pagination
|
|
7
|
+
# library. The caller decides which pages to show (including where to
|
|
8
|
+
# place gaps) using the +pages+ slot, and provides the URLs for the
|
|
9
|
+
# previous/next links directly.
|
|
10
|
+
#
|
|
11
|
+
# @example Usage
|
|
12
|
+
# <%= render(Flowbite::Pagination.new(
|
|
13
|
+
# previous_url: (posts_path(page: @pagy.page - 1) if @pagy.page > 1),
|
|
14
|
+
# next_url: (posts_path(page: @pagy.page + 1) if @pagy.page < @pagy.pages)
|
|
15
|
+
# )) do |pagination| %>
|
|
16
|
+
# <% (1..@pagy.pages).each do |page| %>
|
|
17
|
+
# <% pagination.with_page do %>
|
|
18
|
+
# <%= render(Flowbite::Pagination::Link.new(
|
|
19
|
+
# page: page,
|
|
20
|
+
# url: posts_path(page: page),
|
|
21
|
+
# current: page == @pagy.page
|
|
22
|
+
# )) %>
|
|
23
|
+
# <% end %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
#
|
|
27
|
+
# @viewcomponent_slot [Flowbite::Pagination::Link, Flowbite::Pagination::Gap]
|
|
28
|
+
# pages The pages to show. Use {Flowbite::Pagination::Link} for page
|
|
29
|
+
# numbers, and {Flowbite::Pagination::Gap} where numbers are skipped.
|
|
30
|
+
#
|
|
31
|
+
# @see https://flowbite.com/docs/components/pagination/
|
|
32
|
+
# @lookbook_embed PaginationPreview
|
|
33
|
+
class Pagination < ViewComponent::Base
|
|
34
|
+
renders_many :pages
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
def classes
|
|
38
|
+
["flex", "-space-x-px", "text-sm"]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
attr_reader :next_url, :options, :previous_url
|
|
43
|
+
|
|
44
|
+
# @param class [Array<String>] Additional CSS classes for the list element.
|
|
45
|
+
# @param next_url [String] The URL for the next page. Omit to disable the
|
|
46
|
+
# next link, e.g. when already on the last page.
|
|
47
|
+
# @param options [Hash] Additional HTML options for the nav element.
|
|
48
|
+
# @param previous_url [String] The URL for the previous page. Omit to
|
|
49
|
+
# disable the previous link, e.g. when already on the first page.
|
|
50
|
+
def initialize(next_url: nil, previous_url: nil, class: nil, **options)
|
|
51
|
+
@next_url = next_url
|
|
52
|
+
@previous_url = previous_url
|
|
53
|
+
@class = Array.wrap(binding.local_variable_get(:class))
|
|
54
|
+
@options = options
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def list_classes
|
|
60
|
+
self.class.classes + @class
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|