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
|
@@ -24,16 +24,17 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
24
24
|
# @param helper_popover_placement [String] Placement of the popover (default: "right")
|
|
25
25
|
# @param name [String] Name of the field (required, unless using form builder)
|
|
26
26
|
# @param value [String] Value for the field (optional)
|
|
27
|
+
# @param placeholder [String] Placeholder text for the input field (optional, defaults to I18n if object/attribute present, pass false to disable)
|
|
27
28
|
# @param type [Symbol] Input type (`:text`, `:email`, etc)
|
|
28
29
|
# @param icon [Symbol] Left icon (optional)
|
|
29
30
|
# @param right_icon [Symbol] Right icon (optional)
|
|
30
31
|
# @param addon [String] Add-on text or icon before the input (optional)
|
|
31
|
-
# @param
|
|
32
|
-
# @param
|
|
33
|
-
# @param
|
|
34
|
-
# @param
|
|
32
|
+
# @param addon_html [Hash] Props for the Add-on (optional)
|
|
33
|
+
# @param icon_html [Hash] Props for the left icon (optional)
|
|
34
|
+
# @param right_icon_html [Hash] Props for the right icon (optional)
|
|
35
|
+
# @param div_html [Hash] Props for the whole div (optional)
|
|
35
36
|
# @param multiline [Boolean] Renders a textarea if true
|
|
36
|
-
# @param color [Symbol] Field color (`:default`, `:success`, `:
|
|
37
|
+
# @param color [Symbol] Field color (`:default`, `:success`, `:danger`, etc)
|
|
37
38
|
# @param sizing [Integer] Input size
|
|
38
39
|
# @param shadow [Boolean] Adds drop shadow if true
|
|
39
40
|
# @param disabled [Boolean] Disables the input if true
|
|
@@ -48,13 +49,14 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
48
49
|
@shadow = @props.delete(:shadow)
|
|
49
50
|
@addon = @props.delete(:addon)
|
|
50
51
|
@right_icon = @props.delete(:right_icon)
|
|
51
|
-
@
|
|
52
|
-
@
|
|
53
|
-
@
|
|
54
|
-
@
|
|
52
|
+
@addon_html = @props.delete(:addon_html) || {}
|
|
53
|
+
@div_html = @props.delete(:div_html) || {}
|
|
54
|
+
@icon_html = @props.delete(:icon_html) || {}
|
|
55
|
+
@right_icon_html = @props.delete(:right_icon_html) || {}
|
|
55
56
|
@sizing = sizing_with_addon @props.delete(:sizing)
|
|
56
57
|
@props[:type] = @type
|
|
57
58
|
|
|
59
|
+
define_placeholder(@props.delete(:placeholder))
|
|
58
60
|
declare_classes
|
|
59
61
|
@props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
|
|
60
62
|
end
|
|
@@ -67,9 +69,23 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
67
69
|
|
|
68
70
|
private
|
|
69
71
|
|
|
72
|
+
def define_placeholder(placeholder)
|
|
73
|
+
return if placeholder.is_a?(FalseClass)
|
|
74
|
+
|
|
75
|
+
if placeholder.nil? && @object.present? && @attribute.present?
|
|
76
|
+
placeholder = I18n.t(
|
|
77
|
+
@attribute,
|
|
78
|
+
scope: [ @object.class.name.pluralize.underscore.to_sym, :placeholders ],
|
|
79
|
+
default: nil
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
@props[:placeholder] = placeholder if placeholder.present?
|
|
84
|
+
end
|
|
85
|
+
|
|
70
86
|
def valid_color(color)
|
|
71
87
|
return color if styles[:bg].key?(color)
|
|
72
|
-
return :
|
|
88
|
+
return :danger if errors.present?
|
|
73
89
|
|
|
74
90
|
@@color
|
|
75
91
|
end
|
|
@@ -101,7 +117,7 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
101
117
|
content_tag(
|
|
102
118
|
tag,
|
|
103
119
|
anyicon(
|
|
104
|
-
|
|
120
|
+
icon_v,
|
|
105
121
|
class: styles[:additional_icons][:class][@color]
|
|
106
122
|
),
|
|
107
123
|
**props
|
|
@@ -109,20 +125,20 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
109
125
|
end
|
|
110
126
|
|
|
111
127
|
def create_icon
|
|
112
|
-
add class: styles[:additional_icons][:icon], to: @
|
|
113
|
-
add(class: "pointer-events-none", to: @
|
|
114
|
-
icon(@icon, props: @
|
|
128
|
+
add class: styles[:additional_icons][:icon], to: @icon_html
|
|
129
|
+
add(class: "pointer-events-none", to: @icon_html) unless events?(@icon_html)
|
|
130
|
+
icon(@icon, props: @icon_html)
|
|
115
131
|
end
|
|
116
132
|
|
|
117
133
|
def create_addon
|
|
118
|
-
add class: styles[:additional_icons][:addon][@color], to: @
|
|
119
|
-
icon(@addon, tag: :span, props: @
|
|
134
|
+
add class: styles[:additional_icons][:addon][@color], to: @addon_html
|
|
135
|
+
icon(@addon, tag: :span, props: @addon_html)
|
|
120
136
|
end
|
|
121
137
|
|
|
122
138
|
def create_right_icon
|
|
123
|
-
add class: styles[:additional_icons][:right_icon], to: @
|
|
124
|
-
add(class: "pointer-events-none", to: @
|
|
125
|
-
icon(@right_icon, props: @
|
|
139
|
+
add class: styles[:additional_icons][:right_icon], to: @right_icon_html
|
|
140
|
+
add(class: "pointer-events-none", to: @right_icon_html) unless events?(@right_icon_html)
|
|
141
|
+
icon(@right_icon, props: @right_icon_html)
|
|
126
142
|
end
|
|
127
143
|
|
|
128
144
|
def events?(props)
|
|
@@ -143,6 +159,7 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
143
159
|
end
|
|
144
160
|
|
|
145
161
|
if @form.present? && @attribute.present?
|
|
162
|
+
@props[:value] = @value if @value.present?
|
|
146
163
|
@form.public_send(input_type, @attribute, @props)
|
|
147
164
|
else
|
|
148
165
|
public_send("#{input_type}_tag", @name, @value, @props)
|
|
@@ -150,13 +167,13 @@ class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
|
|
150
167
|
end
|
|
151
168
|
|
|
152
169
|
def icon_container_with_addon
|
|
153
|
-
add class: "flex", to: @
|
|
154
|
-
content_tag :div, safe_join([ create_addon, create_right_icon, input ]), @
|
|
170
|
+
add class: "flex", to: @div_html
|
|
171
|
+
content_tag :div, safe_join([ create_addon, create_right_icon, input ]), @div_html
|
|
155
172
|
end
|
|
156
173
|
|
|
157
174
|
def icon_container_without_addon
|
|
158
|
-
add class: "relative w-full", to: @
|
|
159
|
-
content_tag :div, safe_join([ create_icon, create_right_icon, input ]), @
|
|
175
|
+
add class: "relative w-full", to: @div_html
|
|
176
|
+
content_tag :div, safe_join([ create_icon, create_right_icon, input ]), @div_html
|
|
160
177
|
end
|
|
161
178
|
|
|
162
179
|
def icon_container
|
|
@@ -27,7 +27,7 @@ class Fluxbit::Form::ToggleComponent < Fluxbit::Form::FieldComponent
|
|
|
27
27
|
# @param name [String] Name of the field (required unless using form builder)
|
|
28
28
|
# @param other_label [String] Additional label, rendered via slot (optional)
|
|
29
29
|
# @param sizing [Integer] Size index for the toggle (default: config)
|
|
30
|
-
# @param color [Symbol] Checked toggle color (:default, :success, :
|
|
30
|
+
# @param color [Symbol] Checked toggle color (:default, :success, :danger, :info, :warning, etc)
|
|
31
31
|
# @param unchecked_color [Symbol] Unchecked toggle color (see config)
|
|
32
32
|
# @param button_color [Symbol] Color for the toggle button
|
|
33
33
|
# @param invert_label [Boolean] If true, inverts label/toggle order (default: config)
|
|
@@ -54,7 +54,7 @@ class Fluxbit::Form::ToggleComponent < Fluxbit::Form::FieldComponent
|
|
|
54
54
|
|
|
55
55
|
def valid_color(color)
|
|
56
56
|
return color if styles[:toggle][:checked].key?(color)
|
|
57
|
-
return :
|
|
57
|
+
return :danger if errors.present?
|
|
58
58
|
|
|
59
59
|
@@color
|
|
60
60
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<%= label %>
|
|
4
4
|
<div class="mt-1 lg:hidden">
|
|
5
5
|
<div class="flex items-center">
|
|
6
|
-
<div class="inline-block h-12 w-12 shrink-0 overflow-hidden
|
|
6
|
+
<div class="inline-block h-12 w-12 shrink-0 overflow-hidden <%= container_rounded_class %> relative" aria-hidden="true">
|
|
7
7
|
<%= image_element %>
|
|
8
8
|
</div>
|
|
9
9
|
<div class="ml-5 rounded-md shadow-xs">
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
|
-
<div class="relative hidden overflow-hidden
|
|
22
|
-
<div class="inline-block h-40 w-40 shrink-0 overflow-hidden
|
|
21
|
+
<div class="relative hidden overflow-hidden <%= container_rounded_class %> lg:block w-40">
|
|
22
|
+
<div class="inline-block h-40 w-40 shrink-0 overflow-hidden <%= container_rounded_class %> relative" aria-hidden="true">
|
|
23
23
|
<%= image_element %>
|
|
24
24
|
</div>
|
|
25
25
|
<label for="desktop-<%= id %>" class="absolute inset-0 flex flex-col h-full w-full items-center justify-center bg-blue-800/75 text-sm font-medium text-white opacity-0 hover:opacity-100">
|
|
@@ -22,11 +22,14 @@ class Fluxbit::Form::UploadImageComponent < Fluxbit::Form::FieldComponent
|
|
|
22
22
|
# @param image_path [String] Path to the image to be displayed (optional)
|
|
23
23
|
# @param image_placeholder [String] Placeholder image path if no image is attached (optional)
|
|
24
24
|
# @param title [Boolean, String] Whether to show a title (true for default, false to hide, or custom string)
|
|
25
|
+
# @param rounded [Boolean] Whether to show image as circle (true, default) or square with rounded edges (false)
|
|
25
26
|
# @param class [String] Additional CSS classes for the input element
|
|
26
27
|
# @param ... any other HTML attribute supported by file_field_tag
|
|
27
28
|
def initialize(**props)
|
|
28
29
|
super(**props)
|
|
29
30
|
@title = @props.delete(:title) || "Change"
|
|
31
|
+
@rounded = @props.delete(:rounded)
|
|
32
|
+
@rounded = true if @rounded.nil?
|
|
30
33
|
@image_path = @props.delete(:image_path) ||
|
|
31
34
|
(if @object&.send(@attribute).respond_to?(:attached?) && @object&.send(@attribute)&.send("attached?")
|
|
32
35
|
@object&.send(@attribute)&.variant(resize_to_fit: [ 160, 160 ])
|
|
@@ -44,7 +47,15 @@ class Fluxbit::Form::UploadImageComponent < Fluxbit::Form::FieldComponent
|
|
|
44
47
|
|
|
45
48
|
def image_element
|
|
46
49
|
image_tag @image_path,
|
|
47
|
-
class: "img_photo_#{id} img_photo absolute inset-0 w-full h-full object-cover
|
|
50
|
+
class: "img_photo_#{id} img_photo absolute inset-0 w-full h-full object-cover #{image_rounded_class}",
|
|
48
51
|
alt: @attribute&.to_s&.humanize
|
|
49
52
|
end
|
|
53
|
+
|
|
54
|
+
def container_rounded_class
|
|
55
|
+
@rounded ? "rounded-full" : "rounded-lg"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def image_rounded_class
|
|
59
|
+
@rounded ? "rounded-full" : "rounded-lg"
|
|
60
|
+
end
|
|
50
61
|
end
|
|
@@ -24,6 +24,7 @@ class Fluxbit::GravatarComponent < Fluxbit::AvatarComponent
|
|
|
24
24
|
# @option props [Symbol] :filetype (:png) The filetype of the Gravatar (:png, :jpg, :gif).
|
|
25
25
|
# @option props [Symbol] :default (:identicon) The default image to use if no Gravatar is found.
|
|
26
26
|
# @option props [Integer] :size (:md) The size of the Gravatar base on the size provided by AvatarComponent.
|
|
27
|
+
# @option props [Boolean] :url_only (false) If true, returns only the Gravatar URL instead of rendering the avatar component.
|
|
27
28
|
# @option props [String] :remove_class ('') Classes to be removed from the default Gravatar class list.
|
|
28
29
|
# @option props [Hash] **props Remaining options declared as HTML attributes, applied to the Gravatar container.
|
|
29
30
|
def initialize(**props)
|
|
@@ -37,10 +38,16 @@ class Fluxbit::GravatarComponent < Fluxbit::AvatarComponent
|
|
|
37
38
|
}
|
|
38
39
|
add class: gravatar_styles[:base], to: @props
|
|
39
40
|
@email = @props.delete(:email)
|
|
41
|
+
@url_only = @props.delete(:url_only)
|
|
40
42
|
src = gravatar_url
|
|
41
43
|
super(src: src, **@props)
|
|
42
44
|
end
|
|
43
45
|
|
|
46
|
+
def call
|
|
47
|
+
return gravatar_url.html_safe if @url_only
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
|
|
44
51
|
# The raw MD5 hash of the users' email. Gravatar is particularly tricky as
|
|
45
52
|
# it downcases all emails. This is really the guts of the module,
|
|
46
53
|
# everything else is just convenience.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fluxbit
|
|
4
|
+
module IconHelpers
|
|
5
|
+
def chevron_right(**props)
|
|
6
|
+
add to: props, class: "w-2.5 h-2.5", first_element: true
|
|
7
|
+
remove_class_from_props(props)
|
|
8
|
+
props["aria-hidden"] = "true"
|
|
9
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
10
|
+
props[:fill] = "none"
|
|
11
|
+
props[:viewBox] = "0 0 6 10"
|
|
12
|
+
stroke_width = props.delete(:stroke_width) || 2
|
|
13
|
+
|
|
14
|
+
tag.svg(**props) do
|
|
15
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "m1 9 4-4-4-4")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def chevron_left(**props)
|
|
20
|
+
add to: props, class: "w-2.5 h-2.5", first_element: true
|
|
21
|
+
remove_class_from_props(props)
|
|
22
|
+
props["aria-hidden"] = "true"
|
|
23
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
24
|
+
props[:fill] = "none"
|
|
25
|
+
props[:viewBox] = "0 0 6 10"
|
|
26
|
+
stroke_width = props.delete(:stroke_width) || 2
|
|
27
|
+
|
|
28
|
+
tag.svg(**props) do
|
|
29
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "m5 1-4 4 4 4")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def chevron_up(**props)
|
|
34
|
+
add to: props, class: "w-2.5 h-2.5", first_element: true
|
|
35
|
+
remove_class_from_props(props)
|
|
36
|
+
props["aria-hidden"] = "true"
|
|
37
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
38
|
+
props[:fill] = "none"
|
|
39
|
+
props[:viewBox] = "0 0 10 6"
|
|
40
|
+
stroke_width = props.delete(:stroke_width) || 2
|
|
41
|
+
|
|
42
|
+
tag.svg(**props) do
|
|
43
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "m1 5 4-4 4 4")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def chevron_down(**props)
|
|
48
|
+
add to: props, class: "w-2.5 h-2.5", first_element: true
|
|
49
|
+
remove_class_from_props(props)
|
|
50
|
+
props["aria-hidden"] = "true"
|
|
51
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
52
|
+
props[:fill] = "none"
|
|
53
|
+
props[:viewBox] = "0 0 10 6"
|
|
54
|
+
stroke_width = props.delete(:stroke_width) || 2
|
|
55
|
+
|
|
56
|
+
tag.svg(**props) do
|
|
57
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "m1 1 4 4 4-4")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def close_icon(**props)
|
|
62
|
+
props["aria-hidden"] = "true"
|
|
63
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
64
|
+
props[:fill] = "none"
|
|
65
|
+
props[:viewBox] = "0 0 14 14"
|
|
66
|
+
|
|
67
|
+
tag.svg(**props) do
|
|
68
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => 2, d: "m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def plus_icon(**props)
|
|
73
|
+
props["aria-hidden"] = "true"
|
|
74
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
75
|
+
props[:fill] = "currentColor"
|
|
76
|
+
props[:viewBox] = "0 0 24 24"
|
|
77
|
+
|
|
78
|
+
tag.svg(**props) do
|
|
79
|
+
tag.path("fill-rule" => "evenodd", d: "M12 3.75a.75.75 0 0 1 .75.75v6.75h6.75a.75.75 0 0 1 0 1.5h-6.75v6.75a.75.75 0 0 1-1.5 0v-6.75H4.5a.75.75 0 0 1 0-1.5h6.75V4.5a.75.75 0 0 1 .75-.75Z", "clip-rule" => "evenodd")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def chevron_double_left(**props)
|
|
84
|
+
add to: props, class: "w-3 h-2.5", first_element: true
|
|
85
|
+
remove_class_from_props(props)
|
|
86
|
+
props["aria-hidden"] = "true"
|
|
87
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
88
|
+
props[:fill] = "none"
|
|
89
|
+
props[:viewBox] = "0 0 10 10"
|
|
90
|
+
stroke_width = props.delete(:stroke_width) || 2
|
|
91
|
+
|
|
92
|
+
tag.svg(**props) do
|
|
93
|
+
safe_join [
|
|
94
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "M4 1l-4 4 4 4"),
|
|
95
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "M10 1l-4 4 4 4")
|
|
96
|
+
]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def chevron_double_right(**props)
|
|
101
|
+
add to: props, class: "w-3 h-2.5", first_element: true
|
|
102
|
+
remove_class_from_props(props)
|
|
103
|
+
props["aria-hidden"] = "true"
|
|
104
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
105
|
+
props[:fill] = "none"
|
|
106
|
+
props[:viewBox] = "0 0 12 10"
|
|
107
|
+
stroke_width = props.delete(:stroke_width) || 2
|
|
108
|
+
|
|
109
|
+
tag.svg(**props) do
|
|
110
|
+
safe_join [
|
|
111
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "M2 1l4 4-4 4"),
|
|
112
|
+
tag.path(stroke: "currentColor", "stroke-linecap" => "round", "stroke-linejoin" => "round", "stroke-width" => stroke_width, d: "M8 1l4 4-4 4")
|
|
113
|
+
]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def ellipsis_horizontal(**props)
|
|
118
|
+
add to: props, class: "w-2.5 h-2.5", first_element: true
|
|
119
|
+
remove_class_from_props(props)
|
|
120
|
+
props["aria-hidden"] = "true"
|
|
121
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
122
|
+
props[:fill] = "currentColor"
|
|
123
|
+
props[:viewBox] = "0 0 10 2"
|
|
124
|
+
|
|
125
|
+
tag.svg(**props) do
|
|
126
|
+
safe_join [
|
|
127
|
+
tag.circle(cx: 1, cy: 1, r: 1),
|
|
128
|
+
tag.circle(cx: 5, cy: 1, r: 1),
|
|
129
|
+
tag.circle(cx: 9, cy: 1, r: 1)
|
|
130
|
+
]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def eye_icon(**props)
|
|
135
|
+
add to: props, class: "size-4", first_element: true
|
|
136
|
+
remove_class_from_props(props)
|
|
137
|
+
props["aria-hidden"] = "true"
|
|
138
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
139
|
+
props[:fill] = "none"
|
|
140
|
+
props[:viewBox] = "0 0 24 24"
|
|
141
|
+
props[:"stroke-width"] = "1.5"
|
|
142
|
+
props[:stroke] = "currentColor"
|
|
143
|
+
|
|
144
|
+
tag.svg(**props) do
|
|
145
|
+
safe_join [
|
|
146
|
+
tag.path("stroke-linecap" => "round", "stroke-linejoin" => "round", d: "M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"),
|
|
147
|
+
tag.path("stroke-linecap" => "round", "stroke-linejoin" => "round", d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z")
|
|
148
|
+
]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def eye_slash_icon(**props)
|
|
153
|
+
add to: props, class: "size-4", first_element: true
|
|
154
|
+
remove_class_from_props(props)
|
|
155
|
+
props["aria-hidden"] = "true"
|
|
156
|
+
props[:xmlns] = "http://www.w3.org/2000/svg"
|
|
157
|
+
props[:fill] = "none"
|
|
158
|
+
props[:viewBox] = "0 0 24 24"
|
|
159
|
+
props[:"stroke-width"] = "1.5"
|
|
160
|
+
props[:stroke] = "currentColor"
|
|
161
|
+
|
|
162
|
+
tag.svg(**props) do
|
|
163
|
+
tag.path("stroke-linecap" => "round", "stroke-linejoin" => "round", d: "M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
##
|
|
5
|
+
# The `Fluxbit::LinkComponent` is a customizable link component that extends {Fluxbit::Component}.
|
|
6
|
+
# It provides a straightforward way to generate anchor elements (`<a>`) with configurable colors
|
|
7
|
+
# and styling options. Additional HTML attributes can be passed to further control the component's
|
|
8
|
+
# behavior and appearance.
|
|
9
|
+
#
|
|
10
|
+
# Example usage:
|
|
11
|
+
# = render Fluxbit::LinkComponent.new(size: 2, spacing: :wider, line_height: :relaxed) do
|
|
12
|
+
# "My Heading"
|
|
13
|
+
#
|
|
14
|
+
class Fluxbit::LinkComponent < Fluxbit::Component
|
|
15
|
+
include Fluxbit::Config::LinkComponent
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Initializes the link component with the provided options.
|
|
19
|
+
#
|
|
20
|
+
# @param [Symbol] color (:default) The color style for the heading. Must be one of the keys defined in +styles[:colors]+.
|
|
21
|
+
# @param [Hash] props Additional HTML attributes to be applied to the heading element, such as +class+, +id+, +data-*, etc.
|
|
22
|
+
#
|
|
23
|
+
# @return [Fluxbit::LinkComponent]
|
|
24
|
+
#
|
|
25
|
+
# @example
|
|
26
|
+
# = render Fluxbit::LinkComponent.new(color: :primary) do
|
|
27
|
+
# "My Heading"
|
|
28
|
+
#
|
|
29
|
+
def initialize(**props)
|
|
30
|
+
super
|
|
31
|
+
@props = props
|
|
32
|
+
@color = @props.delete(:color) || @@color
|
|
33
|
+
|
|
34
|
+
add to: @props, class: [ styles[:colors][@color], styles[:base] ]
|
|
35
|
+
@props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
|
|
36
|
+
@href = @props.delete(:href) || "#"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def call
|
|
40
|
+
link_to content, @href, **@props
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -23,10 +23,10 @@ class Fluxbit::ModalComponent < Fluxbit::Component
|
|
|
23
23
|
# @option props [Boolean] :only_css (false) Determines if the modal can be closed by clicking the backdrop, using a CSS-based approach.
|
|
24
24
|
# @option props [Boolean] :static (false) If true, the modal will not close when clicking the backdrop or pressing the ESC key.
|
|
25
25
|
# @option props [String] :remove_class ('') Classes to be removed from the default modal class list.
|
|
26
|
-
# @option props [Hash] :
|
|
27
|
-
# @option props [Hash] :
|
|
28
|
-
# @option props [Hash] :
|
|
29
|
-
# @option props [Hash] :
|
|
26
|
+
# @option props [Hash] :content_html ({}) Additional HTML attributes and classes for the content wrapper inside the modal.
|
|
27
|
+
# @option props [Hash] :header_html ({}) Additional HTML attributes and classes for the header section.
|
|
28
|
+
# @option props [Hash] :footer_html ({}) Additional HTML attributes and classes for the footer section.
|
|
29
|
+
# @option props [Hash] :close_button_html ({}) Additional HTML attributes and classes for the close button element.
|
|
30
30
|
# @option props [Hash] **props Remaining options declared as HTML attributes, applied to the modal container.
|
|
31
31
|
def initialize(**props)
|
|
32
32
|
super
|
|
@@ -50,39 +50,35 @@ class Fluxbit::ModalComponent < Fluxbit::Component
|
|
|
50
50
|
@props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
|
|
51
51
|
|
|
52
52
|
# Content properties
|
|
53
|
-
@
|
|
54
|
-
add(class: content_classes, to: @
|
|
55
|
-
@
|
|
53
|
+
@content_html = @props.delete(:content_html) || {}
|
|
54
|
+
add(class: content_classes, to: @content_html, first_element: true)
|
|
55
|
+
@content_html[:class] = remove_class(@content_html.delete(:remove_class) || "", @content_html[:class])
|
|
56
56
|
|
|
57
57
|
# Header properties
|
|
58
|
-
@
|
|
59
|
-
add(class: header_classes, to: @
|
|
60
|
-
@
|
|
58
|
+
@header_html = @props.delete(:header_html) || {}
|
|
59
|
+
add(class: header_classes, to: @header_html, first_element: true)
|
|
60
|
+
@header_html[:class] = remove_class(@header_html.delete(:remove_class) || "", @header_html[:class])
|
|
61
61
|
|
|
62
62
|
# Footer properties
|
|
63
|
-
@
|
|
64
|
-
add(class: footer_classes, to: @
|
|
65
|
-
@
|
|
63
|
+
@footer_html = @props.delete(:footer_html) || {}
|
|
64
|
+
add(class: footer_classes, to: @footer_html, first_element: true)
|
|
65
|
+
@footer_html[:class] = remove_class(@footer_html.delete(:remove_class) || "", @footer_html[:class])
|
|
66
66
|
|
|
67
67
|
# Close button properties
|
|
68
|
-
@
|
|
69
|
-
add(class: styles[:header][:close][:base], to: @
|
|
70
|
-
@
|
|
71
|
-
@
|
|
72
|
-
@
|
|
73
|
-
@close_button_props["aria-label"] = "Close"
|
|
68
|
+
@close_button_html = @props.delete(:close_button_html) || {}
|
|
69
|
+
add(class: styles[:header][:close][:base], to: @close_button_html, first_element: true)
|
|
70
|
+
@close_button_html[:class] = remove_class(@close_button_html.delete(:remove_class) || "", @close_button_html[:class])
|
|
71
|
+
@close_button_html[:type] = "button"
|
|
72
|
+
@close_button_html["data-modal-hide"] = @props[:id]
|
|
74
73
|
end
|
|
75
74
|
|
|
76
75
|
def call
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
) do
|
|
81
|
-
content_tag(:div, **@content_props) do
|
|
82
|
-
content_tag(:div, class: styles[:content][:inner]) do
|
|
76
|
+
tag.div(**@props) do
|
|
77
|
+
tag.div(**@content_html) do
|
|
78
|
+
tag.div(class: styles[:content][:inner]) do
|
|
83
79
|
concat(header) if title? || @title.present? || @close_button
|
|
84
|
-
concat(
|
|
85
|
-
concat(
|
|
80
|
+
concat(tag.div(content, class: body_classes))
|
|
81
|
+
concat(tag.div(footer, **@footer_html)) if footer?
|
|
86
82
|
end
|
|
87
83
|
end
|
|
88
84
|
end
|
|
@@ -114,7 +110,7 @@ class Fluxbit::ModalComponent < Fluxbit::Component
|
|
|
114
110
|
def header
|
|
115
111
|
return close_button if @close_button && !title? && !@title.present?
|
|
116
112
|
|
|
117
|
-
content_tag(:div, **@
|
|
113
|
+
content_tag(:div, **@header_html) do
|
|
118
114
|
concat(title) if title?
|
|
119
115
|
concat(content_tag(:h3, @title, class: styles[:header][:title])) if @title.present?
|
|
120
116
|
concat(close_button) if @close_button
|
|
@@ -126,12 +122,13 @@ class Fluxbit::ModalComponent < Fluxbit::Component
|
|
|
126
122
|
end
|
|
127
123
|
|
|
128
124
|
def close_button
|
|
125
|
+
@close_button_html["aria-label"] = t("fluxbit.modal.aria_close")
|
|
129
126
|
content_tag(
|
|
130
127
|
:button,
|
|
131
|
-
**@
|
|
128
|
+
**@close_button_html
|
|
132
129
|
) do
|
|
133
|
-
concat content_tag(:span, "
|
|
134
|
-
concat
|
|
130
|
+
concat content_tag(:span, t("fluxbit.modal.dismiss"), class: "sr-only")
|
|
131
|
+
concat close_icon(class: "size-3")
|
|
135
132
|
end
|
|
136
133
|
end
|
|
137
134
|
|