fluxbit_view_components 0.3.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/README.md +10 -0
- data/app/assets/javascripts/fluxbit_view_components/assigner_controller.js +49 -0
- data/app/assets/javascripts/fluxbit_view_components/auto_submit_controller.js +39 -0
- data/app/assets/javascripts/fluxbit_view_components/drawer_controller.js +135 -0
- data/app/assets/javascripts/fluxbit_view_components/index.js +56 -0
- data/app/assets/javascripts/fluxbit_view_components/method_link_controller.js +143 -0
- data/app/assets/javascripts/fluxbit_view_components/modal_controller.js +118 -0
- data/app/assets/javascripts/fluxbit_view_components/password_controller.js +170 -0
- data/app/assets/javascripts/fluxbit_view_components/progress_controller.js +374 -0
- data/app/assets/javascripts/fluxbit_view_components/row_click_controller.js +32 -0
- data/app/assets/javascripts/fluxbit_view_components/select_all_controller.js +122 -0
- data/app/assets/javascripts/fluxbit_view_components/spinner_percent_controller.js +174 -0
- data/app/assets/javascripts/fluxbit_view_components/theme_button_controller.js +90 -0
- data/app/assets/javascripts/fluxbit_view_components.js +1175 -0
- data/app/components/fluxbit/accordion_component.rb +125 -0
- data/app/components/fluxbit/alert_component.rb +8 -8
- data/app/components/fluxbit/avatar_component.rb +11 -12
- data/app/components/fluxbit/avatar_group_component.rb +1 -1
- data/app/components/fluxbit/badge_component.rb +8 -7
- data/app/components/fluxbit/banner_component.rb +139 -0
- data/app/components/fluxbit/bottom_navigation_component.rb +437 -0
- data/app/components/fluxbit/breadcrumb_component.rb +66 -0
- data/app/components/fluxbit/button_component.rb +39 -11
- data/app/components/fluxbit/button_group_component.rb +1 -1
- data/app/components/fluxbit/card_component.rb +26 -23
- data/app/components/fluxbit/carousel_component.rb +154 -0
- data/app/components/fluxbit/component.rb +24 -3
- data/app/components/fluxbit/drawer_component.html.erb +30 -0
- data/app/components/fluxbit/drawer_component.rb +125 -0
- data/app/components/fluxbit/dropdown_component.rb +41 -0
- data/app/components/fluxbit/dropdown_item_component.rb +68 -0
- data/app/components/fluxbit/flex_component.rb +1 -1
- data/app/components/fluxbit/form/component.rb +15 -8
- data/app/components/fluxbit/form/dropzone_component.rb +3 -3
- data/app/components/fluxbit/form/field_component.rb +4 -2
- data/app/components/fluxbit/form/help_text_component.rb +1 -1
- data/app/components/fluxbit/form/label_component.rb +10 -3
- data/app/components/fluxbit/form/password_component.rb +247 -0
- data/app/components/fluxbit/form/radio_group_button_component.rb +126 -0
- data/app/components/fluxbit/form/select_component.rb +108 -11
- data/app/components/fluxbit/form/text_field_component.rb +40 -23
- data/app/components/fluxbit/form/toggle_component.rb +2 -2
- data/app/components/fluxbit/form/upload_image_component.html.erb +3 -3
- data/app/components/fluxbit/form/upload_image_component.rb +12 -1
- data/app/components/fluxbit/gravatar_component.rb +7 -0
- data/app/components/fluxbit/icon_helpers.rb +167 -0
- data/app/components/fluxbit/link_component.rb +42 -0
- data/app/components/fluxbit/modal_component.rb +28 -31
- data/app/components/fluxbit/pagination_component.rb +206 -0
- data/app/components/fluxbit/popover_component.rb +14 -14
- data/app/components/fluxbit/progress_component.rb +196 -0
- data/app/components/fluxbit/skeleton_component.rb +237 -0
- data/app/components/fluxbit/speed_dial_action_component.html.erb +30 -0
- data/app/components/fluxbit/speed_dial_action_component.rb +59 -0
- data/app/components/fluxbit/speed_dial_component.html.erb +33 -0
- data/app/components/fluxbit/speed_dial_component.rb +73 -0
- data/app/components/fluxbit/spinner_component.rb +71 -0
- data/app/components/fluxbit/spinner_percent_component.rb +174 -0
- data/app/components/fluxbit/stepper_component.rb +223 -0
- data/app/components/fluxbit/tab_component.rb +44 -25
- data/app/components/fluxbit/table_component.rb +186 -0
- data/app/components/fluxbit/table_group_component.rb +28 -0
- data/app/components/fluxbit/theme_button_component.rb +64 -0
- data/app/components/fluxbit/timeline_component.rb +63 -0
- data/app/components/fluxbit/timeline_item_component.html.erb +64 -0
- data/app/components/fluxbit/timeline_item_component.rb +78 -0
- data/app/components/fluxbit/tooltip_component.rb +2 -2
- data/app/helpers/fluxbit/components_helper.rb +74 -4
- data/app/helpers/fluxbit/form_builder.rb +64 -15
- data/app/helpers/fluxbit/view_helper.rb +71 -0
- data/config/locales/en.yml +37 -4
- data/config/locales/pt-BR.yml +36 -0
- data/lib/fluxbit/config/accordion_component.rb +73 -0
- data/lib/fluxbit/config/avatar_component.rb +11 -11
- data/lib/fluxbit/config/badge_component.rb +14 -11
- data/lib/fluxbit/config/banner_component.rb +60 -0
- data/lib/fluxbit/config/bottom_navigation_component.rb +74 -0
- data/lib/fluxbit/config/breadcrumb_component.rb +24 -0
- data/lib/fluxbit/config/button_component.rb +6 -4
- data/lib/fluxbit/config/card_component.rb +23 -12
- data/lib/fluxbit/config/carousel_component.rb +33 -0
- data/lib/fluxbit/config/drawer_component.rb +48 -0
- data/lib/fluxbit/config/dropdown_component.rb +29 -0
- data/lib/fluxbit/config/form/check_box_component.rb +1 -1
- data/lib/fluxbit/config/form/dropzone_component.rb +1 -1
- data/lib/fluxbit/config/form/help_text_component.rb +1 -1
- data/lib/fluxbit/config/form/label_component.rb +3 -2
- data/lib/fluxbit/config/form/password_component.rb +19 -0
- data/lib/fluxbit/config/form/radio_group_button_component.rb +24 -0
- data/lib/fluxbit/config/form/text_field_component.rb +11 -11
- data/lib/fluxbit/config/form/toggle_component.rb +5 -5
- data/lib/fluxbit/config/link_component.rb +24 -0
- data/lib/fluxbit/config/modal_component.rb +1 -1
- data/lib/fluxbit/config/pagination_component.rb +31 -0
- data/lib/fluxbit/config/popover_component.rb +1 -1
- data/lib/fluxbit/config/progress_component.rb +63 -0
- data/lib/fluxbit/config/skeleton_component.rb +82 -0
- data/lib/fluxbit/config/speed_dial_component.rb +50 -0
- data/lib/fluxbit/config/spinner_component.rb +30 -0
- data/lib/fluxbit/config/spinner_percent_component.rb +61 -0
- data/lib/fluxbit/config/stepper_component.rb +299 -0
- data/lib/fluxbit/config/tab_component.rb +6 -0
- data/lib/fluxbit/config/table_component.rb +75 -0
- data/lib/fluxbit/config/theme_button_component.rb +19 -0
- data/lib/fluxbit/config/timeline_component.rb +77 -0
- data/lib/fluxbit/view_components/engine.rb +11 -3
- data/lib/fluxbit/view_components/version.rb +1 -1
- data/lib/fluxbit/view_components.rb +20 -0
- data/lib/generators/fluxbit/devise_views_generator.rb +116 -0
- data/lib/generators/fluxbit/pagy_generator.rb +39 -0
- data/lib/generators/fluxbit/scaffold_generator.rb +165 -0
- data/lib/generators/fluxbit/templates/_alert.html.erb.tt +1 -0
- data/lib/generators/fluxbit/templates/_flash.html.erb.tt +15 -0
- data/lib/generators/fluxbit/templates/_form.html.erb.tt +38 -0
- data/lib/generators/fluxbit/templates/_metadata.html.erb.tt +44 -0
- data/lib/generators/fluxbit/templates/controller.rb.tt +406 -0
- data/lib/generators/fluxbit/templates/create.turbo_stream.erb.tt +7 -0
- data/lib/generators/fluxbit/templates/destroy.turbo_stream.erb.tt +3 -0
- data/lib/generators/fluxbit/templates/destroy_all.turbo_stream.erb.tt +9 -0
- data/lib/generators/fluxbit/templates/devise_views/confirmations/new.html.erb +11 -0
- data/lib/generators/fluxbit/templates/devise_views/layouts/devise.html.erb +64 -0
- data/lib/generators/fluxbit/templates/devise_views/mailer/confirmation_instructions.html.erb +5 -0
- data/lib/generators/fluxbit/templates/devise_views/mailer/email_changed.html.erb +7 -0
- data/lib/generators/fluxbit/templates/devise_views/mailer/password_changed.html.erb +3 -0
- data/lib/generators/fluxbit/templates/devise_views/mailer/reset_password_instructions.html.erb +8 -0
- data/lib/generators/fluxbit/templates/devise_views/mailer/unlock_instructions.html.erb +7 -0
- data/lib/generators/fluxbit/templates/devise_views/passwords/edit.html.erb +29 -0
- data/lib/generators/fluxbit/templates/devise_views/passwords/new.html.erb +11 -0
- data/lib/generators/fluxbit/templates/devise_views/registrations/edit.html.erb +43 -0
- data/lib/generators/fluxbit/templates/devise_views/registrations/new.html.erb +34 -0
- data/lib/generators/fluxbit/templates/devise_views/sessions/new.html.erb +15 -0
- data/lib/generators/fluxbit/templates/devise_views/shared/_error_messages.html.erb +14 -0
- data/lib/generators/fluxbit/templates/devise_views/shared/_links.html.erb +25 -0
- data/lib/generators/fluxbit/templates/devise_views/unlocks/new.html.erb +11 -0
- data/lib/generators/fluxbit/templates/edit.html.erb.tt +47 -0
- data/lib/generators/fluxbit/templates/fluxbit_pagy.css +27 -0
- data/lib/generators/fluxbit/templates/i18n.en.yml.tt +121 -0
- data/lib/generators/fluxbit/templates/i18n.pt-BR.yml.tt +121 -0
- data/lib/generators/fluxbit/templates/index.html.erb.tt +254 -0
- data/lib/generators/fluxbit/templates/index.json.jbuilder.tt +33 -0
- data/lib/generators/fluxbit/templates/new.html.erb.tt +47 -0
- data/lib/generators/fluxbit/templates/partial.html.erb.tt +61 -0
- data/lib/generators/fluxbit/templates/policy.rb.tt +36 -0
- data/lib/generators/fluxbit/templates/send_alert_via_drawer.erb.tt +10 -0
- data/lib/generators/fluxbit/templates/show.html.erb.tt +44 -0
- data/lib/generators/fluxbit/templates/show.json.jbuilder.tt +6 -0
- data/lib/generators/fluxbit/templates/update.turbo_stream.erb.tt +10 -0
- data/lib/generators/fluxbit/templates/update_all.turbo_stream.erb.tt +20 -0
- data/lib/install/install.rb +58 -0
- metadata +107 -18
- data/app/helpers/fluxbit/classes_helper.rb +0 -9
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# The `Fluxbit::PaginationComponent` is a component for rendering customizable pagination controls.
|
|
2
|
+
# It extends `Fluxbit::Component` and provides options for configuring the pagination's
|
|
3
|
+
# appearance, behavior, and content areas. You can control the pagination's layout, item count,
|
|
4
|
+
# and other interactive elements. The pagination is divided into different sections (previous, next, etc.),
|
|
5
|
+
# each of which can be styled or customized through various properties.
|
|
6
|
+
class Fluxbit::PaginationComponent < Fluxbit::Component
|
|
7
|
+
include Fluxbit::Config::PaginationComponent
|
|
8
|
+
|
|
9
|
+
def initialize(pagy = nil, **props)
|
|
10
|
+
@pagy = pagy
|
|
11
|
+
|
|
12
|
+
@props = props
|
|
13
|
+
@count = @props.delete(:count) || 0
|
|
14
|
+
@last = @props.delete(:last) || 1
|
|
15
|
+
@next = @props.delete(:next)
|
|
16
|
+
@page = @props.delete(:page) || 1
|
|
17
|
+
@prev = @props.delete(:prev)
|
|
18
|
+
@size = @props.delete(:size) || :default
|
|
19
|
+
@ends = @props.delete(:ends) || true
|
|
20
|
+
@request_path = @props.delete(:request_path) || nil
|
|
21
|
+
|
|
22
|
+
if @pagy
|
|
23
|
+
@count = @pagy.count
|
|
24
|
+
@last = @pagy.last
|
|
25
|
+
@next = @pagy.next
|
|
26
|
+
@page = @pagy.page
|
|
27
|
+
@prev = @pagy.prev
|
|
28
|
+
@size = @pagy.vars[:size]
|
|
29
|
+
@ends = @pagy.vars[:ends]
|
|
30
|
+
@request_path = @pagy.vars[:request_path]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
unless @size.is_a?(Integer) && @size >= 0
|
|
34
|
+
raise ArgumentError, "expected :size to be an Integer >= 0, got #{@size.inspect} (#{@size.class})"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@show_first_last = options @props.delete(:show_first_last), default: @@show_first_last
|
|
38
|
+
@show_prev_next = options @props.delete(:show_prev_next), default: @@show_prev_next
|
|
39
|
+
@show_pages = options @props.delete(:show_pages), default: @@show_pages
|
|
40
|
+
@show_icons = options @props.delete(:show_icons), default: @@show_icons
|
|
41
|
+
@show_texts = options @props.delete(:show_texts), default: @@show_texts
|
|
42
|
+
@sizing = options @props.delete(:sizing), default: @@sizing
|
|
43
|
+
@aria_label = @props.delete(:aria_label) || translate("aria_label.nav", count: @last)
|
|
44
|
+
@show_texts = true if !@show_icons && !@show_texts
|
|
45
|
+
|
|
46
|
+
add(class: [ styles[:root], styles[:sizes][@sizing][:root] ], to: @props)
|
|
47
|
+
@page_link_style = [ styles[:page_link], styles[:sizes][@sizing][:page_link] ].join(" ")
|
|
48
|
+
@current_style = [ styles[:current], styles[:sizes][@sizing][:page_link] ].join(" ")
|
|
49
|
+
@props[:aria] ||= {}
|
|
50
|
+
@props[:aria][:label] = @aria_label unless @props[:aria][:label]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def call
|
|
54
|
+
tag.nav(**@props) do
|
|
55
|
+
concat first_button if @show_first_last
|
|
56
|
+
concat prev_button if @show_prev_next
|
|
57
|
+
|
|
58
|
+
if @show_pages
|
|
59
|
+
series.each do |item|
|
|
60
|
+
case item
|
|
61
|
+
when Integer
|
|
62
|
+
concat(tag.a(item.to_s, href: url_for(item), role: "link", class: @page_link_style, aria: { label: item.to_s }))
|
|
63
|
+
when String
|
|
64
|
+
concat(tag.a(item.to_s, role: "link", class: @current_style, aria: { disabled: true, current: "page" }))
|
|
65
|
+
when :gap
|
|
66
|
+
concat(tag.a(ellipsis_horizontal, role: "link", class: @page_link_style, aria: { disabled: true }))
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
concat next_button if @show_prev_next
|
|
72
|
+
concat last_button if @show_first_last
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def translate(key, options = {})
|
|
79
|
+
I18n.t(key, **options.merge(scope: "fluxbit.pagination")) # , default: Fluxbit::DEFAULT_TRANSLATIONS["fluxbit.pagination.#{key}"]))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def first_button
|
|
83
|
+
props = { role: "link", class: @page_link_style, aria: { label: translate("aria_label.first") } }
|
|
84
|
+
if @page != 1
|
|
85
|
+
props[:href] = url_for(1)
|
|
86
|
+
else
|
|
87
|
+
props[:aria][:disabled] = true
|
|
88
|
+
add class: styles[:disabled], to: props, first_element: true
|
|
89
|
+
end
|
|
90
|
+
add class: styles[:previous], to: props
|
|
91
|
+
|
|
92
|
+
tag.a(**props) do
|
|
93
|
+
concat(chevron_double_left) if @show_icons
|
|
94
|
+
concat(tag.span(
|
|
95
|
+
translate("first"),
|
|
96
|
+
class: @show_texts ? (@show_icons ? styles[:text_with_icon_prev] : styles[:only_text]) : styles[:only_icon]
|
|
97
|
+
))
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def last_button
|
|
102
|
+
props = { role: "link", class: @page_link_style, aria: { label: translate("aria_label.last") } }
|
|
103
|
+
if @page != @last
|
|
104
|
+
props[:href] = url_for(@last)
|
|
105
|
+
else
|
|
106
|
+
props[:aria][:disabled] = true
|
|
107
|
+
add class: styles[:disabled], to: props, first_element: true
|
|
108
|
+
end
|
|
109
|
+
add class: styles[:next], to: props
|
|
110
|
+
|
|
111
|
+
tag.a(**props) do
|
|
112
|
+
concat(tag.span(
|
|
113
|
+
translate("last"),
|
|
114
|
+
class: @show_texts ? (@show_icons ? styles[:text_with_icon_next] : styles[:only_text]) : styles[:only_icon]
|
|
115
|
+
))
|
|
116
|
+
concat(chevron_double_right) if @show_icons
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def prev_button
|
|
121
|
+
props = { role: "link", class: @page_link_style, aria: { label: translate("aria_label.prev") } }
|
|
122
|
+
if prev_page = @prev
|
|
123
|
+
props[:href] = url_for(prev_page)
|
|
124
|
+
else
|
|
125
|
+
props[:aria][:disabled] = true
|
|
126
|
+
add class: styles[:disabled], to: props, first_element: true
|
|
127
|
+
end
|
|
128
|
+
add(class: styles[:previous], to: props) unless @show_first_last
|
|
129
|
+
|
|
130
|
+
tag.a(**props) do
|
|
131
|
+
concat(chevron_left) if @show_icons
|
|
132
|
+
concat(tag.span(
|
|
133
|
+
translate("prev"),
|
|
134
|
+
class: @show_texts ? (@show_icons ? styles[:text_with_icon_prev] : styles[:only_text]) : styles[:only_icon]
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def next_button
|
|
141
|
+
props = { role: "link", class: @page_link_style, aria: { label: translate("aria_label.next") } }
|
|
142
|
+
if next_page = @next
|
|
143
|
+
props[:href] = url_for(next_page)
|
|
144
|
+
else
|
|
145
|
+
props[:aria][:disabled] = true
|
|
146
|
+
add class: styles[:disabled], to: props, first_element: true
|
|
147
|
+
end
|
|
148
|
+
add(class: styles[:next], to: props) unless @show_first_last
|
|
149
|
+
|
|
150
|
+
tag.a(**props) do
|
|
151
|
+
concat(tag.span(
|
|
152
|
+
translate("next"),
|
|
153
|
+
class: @show_texts ? (@show_icons ? styles[:text_with_icon_next] : styles[:only_text]) : styles[:only_icon]
|
|
154
|
+
))
|
|
155
|
+
concat(chevron_right) if @show_icons
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def series
|
|
160
|
+
return @pagy.series(size: @size) if @pagy && @pagy.respond_to?(:series, true)
|
|
161
|
+
return [] if @size.zero?
|
|
162
|
+
|
|
163
|
+
[].tap do |series|
|
|
164
|
+
if @size >= @last
|
|
165
|
+
series.push(*1..@last)
|
|
166
|
+
else
|
|
167
|
+
left = ((@size - 1) / 2.0).floor # left half might be 1 page shorter for even size
|
|
168
|
+
start = if @page <= left # beginning pages
|
|
169
|
+
1
|
|
170
|
+
elsif @page > (@last - @size + left) # end pages
|
|
171
|
+
@last - @size + 1
|
|
172
|
+
else # intermediate pages
|
|
173
|
+
@page - left
|
|
174
|
+
end
|
|
175
|
+
series.push(*start...start + @size)
|
|
176
|
+
# Set first and last pages plus gaps when needed, respecting the size
|
|
177
|
+
if @ends && @size >= 7
|
|
178
|
+
series[0] = 1
|
|
179
|
+
series[1] = :gap unless series[1] == 2
|
|
180
|
+
series[-2] = :gap unless series[-2] == @last - 1
|
|
181
|
+
series[-1] = @last
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
series[series.index(@page)] = @page.to_s
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def url_for(page)
|
|
189
|
+
vars = @pagy&.vars || {}
|
|
190
|
+
# Use current request parameters as base
|
|
191
|
+
params = (respond_to?(:request) ? request.GET : controller.request.GET).dup
|
|
192
|
+
params.merge!(vars[:params].transform_keys(&:to_s)) if vars[:params].is_a?(Hash)
|
|
193
|
+
# Set page and possibly limit
|
|
194
|
+
params[vars[:page_param].to_s] = page
|
|
195
|
+
params[vars[:limit_param].to_s] = vars[:limit] if vars[:limit_extra]
|
|
196
|
+
# Apply params proc if given
|
|
197
|
+
params = vars[:params].call(params) if vars[:params].is_a?(Proc)
|
|
198
|
+
|
|
199
|
+
# Build query string
|
|
200
|
+
query_str = params.any? ? "?#{Rack::Utils.build_nested_query(params)}" : ""
|
|
201
|
+
# Base path (use stored request_path or current path)
|
|
202
|
+
base_path = @request_path || (respond_to?(:request) ? request.path : controller.request.path)
|
|
203
|
+
base_path = "#{request.base_url}#{base_path}" if vars[:absolute]
|
|
204
|
+
"#{base_path}#{query_str}#{vars[:fragment] || ''}"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -16,7 +16,7 @@ class Fluxbit::PopoverComponent < Fluxbit::Component
|
|
|
16
16
|
# @option props [Boolean] :has_arrow (true) Determines if an arrow should be displayed on the popover.
|
|
17
17
|
# @option props [String] :image (nil) The URL of an image to be displayed in the popover.
|
|
18
18
|
# @option props [Symbol] :image_position (:right) The position of the image relative to the content (:left or :right).
|
|
19
|
-
# @option props [Hash] :
|
|
19
|
+
# @option props [Hash] :image_html ({}) Additional HTML attributes for the image element.
|
|
20
20
|
# @option props [Symbol, String] :size (2) The size of the popover (0 to 4).
|
|
21
21
|
# @option props [String] :remove_class ('') Classes to be removed from the default popover class list.
|
|
22
22
|
# @option props [Hash] **props Remaining options declared as HTML attributes, applied to the popover container.
|
|
@@ -27,31 +27,31 @@ class Fluxbit::PopoverComponent < Fluxbit::Component
|
|
|
27
27
|
@has_arrow = options @props.delete(:has_arrow), default: @@has_arrow
|
|
28
28
|
@image = @props.delete(:image)
|
|
29
29
|
@image_position = options @props.delete(:image_position), default: @@image_position
|
|
30
|
-
@
|
|
30
|
+
@image_html = options @props.delete(:image_html), default: @@image_html
|
|
31
31
|
@props["data-popover"] = "data-popover"
|
|
32
32
|
@props["role"] = "tooltip"
|
|
33
33
|
|
|
34
34
|
add(class: [ styles[:base], styles[:size][@props.delete(:size) || @@size] ], to: @props)
|
|
35
|
-
add(class: styles[:image_content][:image], to: @
|
|
36
|
-
@
|
|
35
|
+
add(class: styles[:image_content][:image], to: @image_html)
|
|
36
|
+
@image_html[:src] = @image
|
|
37
37
|
|
|
38
38
|
@props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
|
|
39
|
-
@
|
|
39
|
+
@image_html[:class] = remove_class(@props.delete(:remove_class) || "", @image_html[:class])
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def call
|
|
43
|
-
|
|
43
|
+
tag.div(**@props) do
|
|
44
44
|
concat div_title unless @title.blank?
|
|
45
|
-
concat (
|
|
45
|
+
concat (tag.div(class: styles[@image.blank? ? :content : :image_base]) do
|
|
46
46
|
if @image.blank?
|
|
47
47
|
content
|
|
48
48
|
else
|
|
49
49
|
if @image_position == :left
|
|
50
|
-
concat
|
|
51
|
-
concat
|
|
50
|
+
concat tag.img(**@image_html)
|
|
51
|
+
concat tag.div(content, class: styles[:image_content][:text])
|
|
52
52
|
else
|
|
53
|
-
concat
|
|
54
|
-
concat
|
|
53
|
+
concat tag.div(content, class: styles[:image_content][:text])
|
|
54
|
+
concat tag.img(**@image_html)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
end)
|
|
@@ -60,12 +60,12 @@ class Fluxbit::PopoverComponent < Fluxbit::Component
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def popper_arrow
|
|
63
|
-
|
|
63
|
+
tag.div("data-popper-arrow" => true)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def div_title
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
tag.div(class: styles[:title][:div]) do
|
|
68
|
+
tag.h3(@title, class: styles[:title][:h3])
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# The `Fluxbit::ProgressComponent` is a customizable progress bar component that extends `Fluxbit::Component`.
|
|
5
|
+
# It allows you to create progress indicators with various styles, sizes, colors, and label positioning options
|
|
6
|
+
# to display completion status or loading progress.
|
|
7
|
+
#
|
|
8
|
+
# @example Basic usage
|
|
9
|
+
# = fx_progress(progress: 45)
|
|
10
|
+
#
|
|
11
|
+
# @example With labels
|
|
12
|
+
# = fx_progress(progress: 75, text_label: "Loading", label_progress: true)
|
|
13
|
+
#
|
|
14
|
+
# @see docs/02_Components/Progress.md For detailed documentation.
|
|
15
|
+
class Fluxbit::ProgressComponent < Fluxbit::Component
|
|
16
|
+
include Fluxbit::Config::ProgressComponent
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Initializes the progress component with the given properties.
|
|
20
|
+
#
|
|
21
|
+
# @param [Hash] **props The properties to customize the component.
|
|
22
|
+
# @option props [Integer] :progress (0) The progress percentage (0-100).
|
|
23
|
+
# @option props [Symbol, String] :color (:default) The color theme of the progress bar.
|
|
24
|
+
# @option props [Integer] :size (1) The size of the progress bar (0-3).
|
|
25
|
+
# @option props [String] :text_label (nil) Label text to display with the progress bar.
|
|
26
|
+
# @option props [Boolean] :label_progress (false) Whether to show the progress percentage.
|
|
27
|
+
# @option props [Boolean] :label_text (false) Whether to show the text label.
|
|
28
|
+
# @option props [Symbol] :progress_label_position (:inside) Position of progress label (:inside or :outside).
|
|
29
|
+
# @option props [Symbol] :text_label_position (:outside) Position of text label (:inside or :outside).
|
|
30
|
+
# @option props [Hash] :label_html ({}) HTML attributes for label elements. Supports :remove_class.
|
|
31
|
+
# @option props [Boolean] :stimulus (false) Whether to add Stimulus controller data attributes for JavaScript interactions.
|
|
32
|
+
# @option props [String] :remove_class ('') CSS classes to remove from the default class list.
|
|
33
|
+
# @option props [Hash] **props Remaining options as HTML attributes.
|
|
34
|
+
#
|
|
35
|
+
# @return [Fluxbit::ProgressComponent]
|
|
36
|
+
def initialize(**props)
|
|
37
|
+
super
|
|
38
|
+
@props = props
|
|
39
|
+
|
|
40
|
+
# Use options() function with config defaults
|
|
41
|
+
@progress = options(@props.delete(:progress), default: @@progress)
|
|
42
|
+
@color = options(@props.delete(:color), collection: styles[:bar][:colors].keys, default: @@color)
|
|
43
|
+
@size = options(@props.delete(:size), default: @@size)
|
|
44
|
+
@text_label = options(@props.delete(:text_label), default: @@text_label)
|
|
45
|
+
@label_progress = options(@props.delete(:label_progress), default: @@label_progress)
|
|
46
|
+
@label_text = options(@props.delete(:label_text), default: @@label_text)
|
|
47
|
+
@progress_label_position = options(@props.delete(:progress_label_position),
|
|
48
|
+
collection: [ :inside, :outside ],
|
|
49
|
+
default: @@progress_label_position)
|
|
50
|
+
@text_label_position = options(@props.delete(:text_label_position),
|
|
51
|
+
collection: [ :inside, :outside ],
|
|
52
|
+
default: @@text_label_position)
|
|
53
|
+
@label_html = options(@props.delete(:label_html), default: @@label_html)
|
|
54
|
+
@stimulus = options(@props.delete(:stimulus), default: @@stimulus)
|
|
55
|
+
|
|
56
|
+
# Sanitize progress value
|
|
57
|
+
@progress = [ @progress.to_i, 0 ].max
|
|
58
|
+
@progress = [ @progress, 100 ].min
|
|
59
|
+
|
|
60
|
+
# Apply styling
|
|
61
|
+
declare_classes
|
|
62
|
+
|
|
63
|
+
# Handle class removal
|
|
64
|
+
@props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def call
|
|
68
|
+
content_tag(:div) do
|
|
69
|
+
safe_join([
|
|
70
|
+
render_outside_labels,
|
|
71
|
+
render_progress_container
|
|
72
|
+
].compact)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def declare_classes
|
|
79
|
+
add(class: styles[:base], to: @props, first_element: true)
|
|
80
|
+
add(class: styles[:sizes][@size], to: @props, first_element: true)
|
|
81
|
+
|
|
82
|
+
# Add Stimulus controller attributes if enabled
|
|
83
|
+
if @stimulus
|
|
84
|
+
@props["data-controller"] = [ @props["data-controller"], "fx-progress" ].compact.join(" ")
|
|
85
|
+
@props["data-fx-progress-progress-value"] = @progress
|
|
86
|
+
@props["data-fx-progress-animate-value"] = true
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def render_outside_labels
|
|
91
|
+
return unless has_outside_labels?
|
|
92
|
+
|
|
93
|
+
content_tag(:div, class: styles[:labels][:outside][:base]) do
|
|
94
|
+
safe_join([
|
|
95
|
+
render_outside_text_label,
|
|
96
|
+
render_outside_progress_label
|
|
97
|
+
].compact)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def render_outside_text_label
|
|
102
|
+
return unless @label_text && @text_label.present? && @text_label_position == :outside
|
|
103
|
+
|
|
104
|
+
label_props = build_label_props(styles[:labels][:outside][:text], is_progress_label: false)
|
|
105
|
+
content_tag(:span, @text_label, **label_props)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def render_outside_progress_label
|
|
109
|
+
return unless @label_progress && @progress_label_position == :outside
|
|
110
|
+
|
|
111
|
+
label_props = build_label_props(styles[:labels][:outside][:progress], is_progress_label: true)
|
|
112
|
+
content_tag(:span, "#{@progress}%", **label_props)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def render_progress_container
|
|
116
|
+
content_tag(:div, **@props) do
|
|
117
|
+
content_tag(:div, progress_bar_content, **progress_bar_props)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def progress_bar_props
|
|
122
|
+
bar_props = {
|
|
123
|
+
class: progress_bar_classes,
|
|
124
|
+
style: "width: #{@progress}%"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Add Stimulus target if enabled
|
|
128
|
+
if @stimulus
|
|
129
|
+
bar_props["data-fx-progress-target"] = "bar"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
bar_props
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def progress_bar_classes
|
|
136
|
+
classes = [ styles[:bar][:base] ]
|
|
137
|
+
classes << styles[:bar][:colors][@color]
|
|
138
|
+
classes << styles[:bar][:text_sizes][@size] if has_inside_labels?
|
|
139
|
+
classes.join(" ")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def progress_bar_content
|
|
143
|
+
return "" unless has_inside_labels?
|
|
144
|
+
|
|
145
|
+
safe_join([
|
|
146
|
+
render_inside_text_label,
|
|
147
|
+
render_inside_progress_label
|
|
148
|
+
].compact, " ")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def render_inside_text_label
|
|
152
|
+
return unless @label_text && @text_label.present? && @text_label_position == :inside
|
|
153
|
+
|
|
154
|
+
label_props = build_label_props(styles[:labels][:inside][:text], is_progress_label: false)
|
|
155
|
+
content_tag(:span, @text_label, **label_props)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def render_inside_progress_label
|
|
159
|
+
return unless @label_progress && @progress_label_position == :inside
|
|
160
|
+
|
|
161
|
+
label_props = build_label_props(styles[:labels][:inside][:progress], is_progress_label: true)
|
|
162
|
+
content_tag(:span, "#{@progress}%", **label_props)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def has_outside_labels?
|
|
166
|
+
(@label_text && @text_label.present? && @text_label_position == :outside) ||
|
|
167
|
+
(@label_progress && @progress_label_position == :outside)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def has_inside_labels?
|
|
171
|
+
(@label_text && @text_label.present? && @text_label_position == :inside) ||
|
|
172
|
+
(@label_progress && @progress_label_position == :inside)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def build_label_props(base_class, is_progress_label: false)
|
|
176
|
+
label_props = @label_html.dup
|
|
177
|
+
|
|
178
|
+
# Start with base class and merge with any custom classes
|
|
179
|
+
label_props[:class] = base_class
|
|
180
|
+
add(class: @label_html[:class], to: label_props) if @label_html[:class].present?
|
|
181
|
+
|
|
182
|
+
# Add Stimulus target if enabled and this is a progress label
|
|
183
|
+
if @stimulus && is_progress_label
|
|
184
|
+
label_props["data-fx-progress-target"] = "progressLabel"
|
|
185
|
+
elsif @stimulus && !is_progress_label
|
|
186
|
+
label_props["data-fx-progress-target"] = "textLabel"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Handle remove_class for labels
|
|
190
|
+
if label_props[:remove_class].present?
|
|
191
|
+
label_props[:class] = remove_class(label_props.delete(:remove_class), label_props[:class])
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
label_props
|
|
195
|
+
end
|
|
196
|
+
end
|