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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +3 -5
  4. data/app/components/flowbite/alert/alert.html.erb +36 -0
  5. data/app/components/flowbite/alert/icon.rb +53 -0
  6. data/app/components/flowbite/alert.rb +156 -0
  7. data/app/components/flowbite/avatar/avatar.html.erb +23 -0
  8. data/app/components/flowbite/avatar/dot.rb +85 -0
  9. data/app/components/flowbite/avatar/placeholder_icon.html.erb +13 -0
  10. data/app/components/flowbite/avatar/placeholder_icon.rb +44 -0
  11. data/app/components/flowbite/avatar.rb +132 -0
  12. data/app/components/flowbite/badge/badge.html.erb +7 -0
  13. data/app/components/flowbite/badge/dot.rb +45 -0
  14. data/app/components/flowbite/badge/pill.rb +40 -0
  15. data/app/components/flowbite/badge.rb +116 -0
  16. data/app/components/flowbite/card.rb +4 -8
  17. data/app/components/flowbite/input_field/checkbox.rb +7 -7
  18. data/app/components/flowbite/input_field/input_field.html.erb +1 -0
  19. data/app/components/flowbite/input_field/select.rb +1 -1
  20. data/app/components/flowbite/input_field.rb +29 -22
  21. data/app/components/flowbite/pagination/chevron_left_icon.rb +34 -0
  22. data/app/components/flowbite/pagination/chevron_right_icon.rb +34 -0
  23. data/app/components/flowbite/pagination/gap.rb +24 -0
  24. data/app/components/flowbite/pagination/link.rb +52 -0
  25. data/app/components/flowbite/pagination/next_link.rb +67 -0
  26. data/app/components/flowbite/pagination/pagination.html.erb +11 -0
  27. data/app/components/flowbite/pagination/previous_link.rb +67 -0
  28. data/app/components/flowbite/pagination.rb +63 -0
  29. data/app/components/flowbite/progress/progress.html.erb +14 -0
  30. data/app/components/flowbite/progress.rb +163 -0
  31. data/app/components/flowbite/rating/rating.html.erb +13 -0
  32. data/app/components/flowbite/rating/star.html.erb +13 -0
  33. data/app/components/flowbite/rating/star.rb +59 -0
  34. data/app/components/flowbite/rating.rb +64 -0
  35. data/app/components/flowbite/sidebar/item.rb +68 -0
  36. data/app/components/flowbite/sidebar/navigation.rb +62 -0
  37. data/app/components/flowbite/sidebar.rb +62 -0
  38. data/app/components/flowbite/spinner/spinner.html.erb +20 -0
  39. data/app/components/flowbite/spinner.rb +66 -0
  40. data/app/components/flowbite/toast/close_button.html.erb +17 -0
  41. data/app/components/flowbite/toast/close_button.rb +32 -0
  42. data/app/components/flowbite/toast/icon.html.erb +8 -2
  43. data/app/components/flowbite/toast/toast.html.erb +7 -34
  44. data/app/components/flowbite/toast.rb +39 -1
  45. data/lib/flowbite/components/version.rb +1 -1
  46. 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
- component = Flowbite::Card::Title.new(**default_title_options)
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
- @title.present?
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"
@@ -2,6 +2,7 @@
2
2
  <%= label %>
3
3
  <%= input %>
4
4
  <%= hint %>
5
+
5
6
  <% errors.each do |error| %>
6
7
  <%= render(Flowbite::Input::ValidationError.new) { error.upcase_first } %>
7
8
  <% end %>
@@ -19,7 +19,7 @@ module Flowbite
19
19
  form: @form,
20
20
  include_blank: @include_blank,
21
21
  multiple: @multiple,
22
- options: input_options,
22
+ options: default_input_options,
23
23
  size: @size
24
24
  )
25
25
  )
@@ -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
- ).with_content(default_hint_content)
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
- # Returns the HTML to use for the default input element.
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