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,64 @@
|
|
|
1
|
+
<li <%= tag.attributes(**@props) %>>
|
|
2
|
+
<% if @variant == :activity %>
|
|
3
|
+
<div class="<%= styles[:activity][:indicator] %>"></div>
|
|
4
|
+
<div class="<%= styles[:activity][:base] %>">
|
|
5
|
+
<% if @date %>
|
|
6
|
+
<time class="<%= styles[:activity][:time] %>"><%= @date %></time>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% if render_as_link? %>
|
|
9
|
+
<a href="<%= @href %>" class="<%= styles[:activity][:title] %>"><%= @title %></a>
|
|
10
|
+
<% else %>
|
|
11
|
+
<h3 class="<%= styles[:activity][:title] %>"><%= @title %></h3>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% if @description %>
|
|
14
|
+
<p class="<%= styles[:activity][:description] %>"><%= @description %></p>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
17
|
+
<% elsif @variant == :stepper %>
|
|
18
|
+
<div class="<%= styles[:stepper][:indicator_container] %>">
|
|
19
|
+
<div class="<%= stepper_indicator_classes %>">
|
|
20
|
+
<% if @icon %>
|
|
21
|
+
<%= icon(@icon, class: (@status == :completed || @status == :current) ? styles[:stepper][:icon_completed] : styles[:stepper][:icon]) %>
|
|
22
|
+
<% end %>
|
|
23
|
+
</div>
|
|
24
|
+
<% unless is_last? %>
|
|
25
|
+
<div class="<%= styles[:stepper][:connector] %>"></div>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="<%= styles[:stepper][:content] %>">
|
|
29
|
+
<% if @title %>
|
|
30
|
+
<h3 class="<%= styles[:stepper][:title] %>"><%= @title %></h3>
|
|
31
|
+
<% end %>
|
|
32
|
+
<% if @date %>
|
|
33
|
+
<time class="<%= styles[:stepper][:description] %>"><%= @date %></time>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% if @description %>
|
|
36
|
+
<p class="<%= styles[:stepper][:description_paragraph] %>"><%= @description %></p>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
<% else %>
|
|
40
|
+
<!-- Default and vertical variants -->
|
|
41
|
+
<div class="<%= indicator_classes %>">
|
|
42
|
+
<% if @icon %>
|
|
43
|
+
<%= icon(@icon, class: styles[:item][:icon]) %>
|
|
44
|
+
<% else %>
|
|
45
|
+
<div class="<%= styles[:item][:dot] %>"></div>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="<%= styles[:item][:content][:base] %>">
|
|
49
|
+
<% if @date %>
|
|
50
|
+
<time class="<%= styles[:item][:content][:date] %>"><%= @date %></time>
|
|
51
|
+
<% end %>
|
|
52
|
+
<% if @title %>
|
|
53
|
+
<% if render_as_link? %>
|
|
54
|
+
<a href="<%= @href %>" class="<%= styles[:item][:content][:title] %>"><%= @title %></a>
|
|
55
|
+
<% else %>
|
|
56
|
+
<h3 class="<%= styles[:item][:content][:title] %>"><%= @title %></h3>
|
|
57
|
+
<% end %>
|
|
58
|
+
<% end %>
|
|
59
|
+
<% if @description %>
|
|
60
|
+
<p class="<%= styles[:item][:content][:description] %>"><%= @description %></p>
|
|
61
|
+
<% end %>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
64
|
+
</li>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The `Fluxbit::TimelineItemComponent` is a component for rendering individual timeline items.
|
|
4
|
+
class Fluxbit::TimelineItemComponent < Fluxbit::Component
|
|
5
|
+
include Fluxbit::Config::TimelineComponent
|
|
6
|
+
|
|
7
|
+
# Initializes the TimelineItem component.
|
|
8
|
+
#
|
|
9
|
+
# @param [Hash] **props The properties to customize the timeline item.
|
|
10
|
+
# @option props [Symbol] :variant (nil) The timeline variant from parent component.
|
|
11
|
+
# @option props [String] :title (nil) The title of the timeline item.
|
|
12
|
+
# @option props [String] :description (nil) The description text.
|
|
13
|
+
# @option props [String] :date (nil) The date or time for the item.
|
|
14
|
+
# @option props [String, Symbol] :icon (nil) The icon to display in the timeline indicator.
|
|
15
|
+
# @option props [Symbol] :status (:default) The status of the item (:default, :completed, :current, :pending).
|
|
16
|
+
# @option props [Symbol] :color (:blue) The color theme (:blue, :green, :red, :yellow, :purple, :indigo).
|
|
17
|
+
# @option props [String] :href (nil) URL to make the item clickable.
|
|
18
|
+
# @option props [Symbol] :ring (:default) The ring size around indicator (:none, :small, :default, :large).
|
|
19
|
+
# @option props [String] :remove_class ('') CSS classes to remove from the default class list.
|
|
20
|
+
# @option props [Hash] **props Remaining options declared as HTML attributes.
|
|
21
|
+
#
|
|
22
|
+
# @return [Fluxbit::TimelineItemComponent]
|
|
23
|
+
def initialize(**props)
|
|
24
|
+
super
|
|
25
|
+
@props = props
|
|
26
|
+
|
|
27
|
+
@variant = @props.delete(:variant) || :default
|
|
28
|
+
@title = @props.delete(:title)
|
|
29
|
+
@description = @props.delete(:description)
|
|
30
|
+
@date = @props.delete(:date)
|
|
31
|
+
@icon = @props.delete(:icon)
|
|
32
|
+
@status = options @props.delete(:status), collection: styles[:item][:indicator][:status].keys, default: :default
|
|
33
|
+
@color = options @props.delete(:color), collection: styles[:item][:indicator][:colors].keys, default: :blue
|
|
34
|
+
@ring = options @props.delete(:ring), collection: styles[:item][:indicator][:rings].keys, default: :default
|
|
35
|
+
@href = @props.delete(:href)
|
|
36
|
+
|
|
37
|
+
if @variant != :stepper
|
|
38
|
+
add class: styles[:item][:base], to: @props
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
remove_class_from_props(@props)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def before_render
|
|
45
|
+
super
|
|
46
|
+
if @variant == :stepper
|
|
47
|
+
# Add stepper classes after @is_last is set
|
|
48
|
+
add class: styles[:stepper][:item], to: @props
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def indicator_classes
|
|
55
|
+
[
|
|
56
|
+
styles[:item][:indicator][:base],
|
|
57
|
+
styles[:item][:indicator][:status][@status],
|
|
58
|
+
styles[:item][:indicator][:colors][@color],
|
|
59
|
+
styles[:item][:indicator][:rings][@ring]
|
|
60
|
+
].compact.join(" ")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_as_link?
|
|
64
|
+
@href.present?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def is_last?
|
|
68
|
+
@is_last || false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def stepper_indicator_classes
|
|
72
|
+
if @status == :completed || @status == :current
|
|
73
|
+
styles[:stepper][:indicator_completed]
|
|
74
|
+
else
|
|
75
|
+
styles[:stepper][:indicator]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -24,7 +24,7 @@ class Fluxbit::TooltipComponent < Fluxbit::Component
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def call
|
|
27
|
-
|
|
27
|
+
tag.div(**@props) do
|
|
28
28
|
concat content
|
|
29
29
|
concat arrow
|
|
30
30
|
end
|
|
@@ -33,6 +33,6 @@ class Fluxbit::TooltipComponent < Fluxbit::Component
|
|
|
33
33
|
def arrow
|
|
34
34
|
return "" unless @has_arrow
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
tag.div("", "data-popper-arrow" => true, class: "tooltip-arrow")
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -3,20 +3,59 @@
|
|
|
3
3
|
module Fluxbit
|
|
4
4
|
module ComponentsHelper
|
|
5
5
|
# Components
|
|
6
|
-
[ :avatar, :avatar_group, :gravatar, :alert, :button, :button_group,
|
|
7
|
-
:badge, :card, :modal, :popover, :tooltip, :flex, :tab ].each do |component|
|
|
6
|
+
[ :accordion, :avatar, :avatar_group, :banner, :bottom_navigation, :breadcrumb, :gravatar, :alert, :button, :button_group, :link,
|
|
7
|
+
:badge, :carousel, :drawer, :dropdown, :card, :modal, :pagination, :popover, :tooltip, :flex, :tab, :table, :skeleton, :stepper, :speed_dial, :theme_button, :timeline, :spinner_percent, :progress ].each do |component|
|
|
8
8
|
define_method("fx_#{component}") do |*args, **kwargs, &block|
|
|
9
9
|
fluxbit_method(component.to_s.camelize, *args, **kwargs, &block)
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# Forms
|
|
14
|
-
[ :help_text, :
|
|
15
|
-
:
|
|
14
|
+
[ :help_text, :form_builder, :label, :range,
|
|
15
|
+
:toggle, :upload_image, :dropzone, :radio_group_button ].each do |component|
|
|
16
16
|
define_method("fx_#{component}") do |*args, **kwargs, &block|
|
|
17
17
|
fluxbit_method("Form::#{component.to_s.camelize}", *args, **kwargs, &block)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# Mimics Rails' select_tag signature:
|
|
22
|
+
# select_tag(name, option_tags = nil, options = {})
|
|
23
|
+
#
|
|
24
|
+
# @param name [String, Symbol] Name attribute for the select tag
|
|
25
|
+
# @param option_tags [String, Array, Hash, nil] Pre-formatted HTML or raw options data
|
|
26
|
+
# @param options [Hash] HTML attributes and select options (prompt, class, etc.)
|
|
27
|
+
# @param block [Proc] Optional block for custom options
|
|
28
|
+
#
|
|
29
|
+
# @example Basic usage
|
|
30
|
+
# fx_select "role", ["Admin", "User", "Guest"]
|
|
31
|
+
#
|
|
32
|
+
# @example With options
|
|
33
|
+
# fx_select "country", countries, prompt: "Select a country", class: "custom-select"
|
|
34
|
+
#
|
|
35
|
+
# @example With pre-formatted options
|
|
36
|
+
# fx_select "status", options_for_select(statuses, selected: "active")
|
|
37
|
+
#
|
|
38
|
+
# @example Named parameters (backwards compatible)
|
|
39
|
+
# fx_select name: "role", options: ["Admin", "User"], prompt: "Choose..."
|
|
40
|
+
def fx_select(name = nil, option_tags = nil, options = {}, &block)
|
|
41
|
+
# Handle both positional and named parameters
|
|
42
|
+
if name.is_a?(Hash)
|
|
43
|
+
# Named parameters: fx_select(name: "role", options: [...], prompt: "...")
|
|
44
|
+
options = name
|
|
45
|
+
name = options.delete(:name)
|
|
46
|
+
option_tags = options.delete(:options) || options.delete(:choices)
|
|
47
|
+
elsif option_tags.is_a?(Hash) && options.empty?
|
|
48
|
+
# Two args with second being options: fx_select("role", prompt: "...")
|
|
49
|
+
options = option_tags
|
|
50
|
+
option_tags = options.delete(:options) || options.delete(:choices)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Set the options/choices
|
|
54
|
+
options[:options] = option_tags if option_tags.present?
|
|
55
|
+
options[:name] = name if name.present?
|
|
56
|
+
|
|
57
|
+
fluxbit_method("Form::Select", **options, &block)
|
|
58
|
+
end
|
|
20
59
|
def form_builder(...) = fluxbit_method("Form::FormBuilder", ...)
|
|
21
60
|
|
|
22
61
|
Fluxbit::Form::TextFieldComponent::TYPE_OPTIONS.each do |type|
|
|
@@ -30,10 +69,41 @@ module Fluxbit
|
|
|
30
69
|
fluxbit_method("Form::CheckBox", *args, type: type, **kwargs, &block)
|
|
31
70
|
end
|
|
32
71
|
end
|
|
72
|
+
|
|
33
73
|
# Typography
|
|
34
74
|
def fx_heading(...) = fluxbit_method("Heading", ...)
|
|
35
75
|
def fx_txt(...) = fluxbit_method("Text", ...)
|
|
36
76
|
|
|
77
|
+
|
|
78
|
+
def fx_link_to_old(body, url, **kwargs, &block)
|
|
79
|
+
kwargs[:body] = body if body.is_a?(String)
|
|
80
|
+
kwargs[:url] = url if url.is_a?(String)
|
|
81
|
+
kwargs[:with_content] = block if block_given?
|
|
82
|
+
Fluxbit::TextComponent.new(body, url, **kwargs).render(&block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def fx_link_to(body = nil, url = nil, **kwargs, &block)
|
|
86
|
+
kwargs[:as] = :a
|
|
87
|
+
if block_given? && body.is_a?(String) && (url.is_a?(String) || url.is_a?(Symbol))
|
|
88
|
+
# Handle case: fx_link_to("Link text", "/path") { ... }
|
|
89
|
+
kwargs[:href] = url
|
|
90
|
+
fluxbit_method("Link", as: :a, content: body, **kwargs, &block)
|
|
91
|
+
elsif block_given? && body.is_a?(String) && url.nil?
|
|
92
|
+
# Handle case: fx_link_to("/path") { ... content ... }
|
|
93
|
+
kwargs[:href] = body # first arg is URL
|
|
94
|
+
fluxbit_method("Link", as: :a, **kwargs, &block)
|
|
95
|
+
elsif !block_given? && body.is_a?(String) && (url.is_a?(String) || url.is_a?(Symbol))
|
|
96
|
+
# Handle case: fx_link_to("Link text", "/path", class: "...")
|
|
97
|
+
component_klass = "Fluxbit::LinkComponent".constantize
|
|
98
|
+
kwargs[:href] = url
|
|
99
|
+
render(component_klass.new(**kwargs).with_content(body))
|
|
100
|
+
else
|
|
101
|
+
# Handle other cases or provide feedback
|
|
102
|
+
raise ArgumentError, "Invalid arguments for fx_link_to"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
37
107
|
def fluxbit_method(method_name, *args, **kwargs, &c)
|
|
38
108
|
component_klass = "Fluxbit::#{method_name}Component".constantize
|
|
39
109
|
if kwargs[:with_content]
|
|
@@ -22,14 +22,17 @@ module Fluxbit
|
|
|
22
22
|
within: within,
|
|
23
23
|
data: { errors_summary: true }
|
|
24
24
|
) do |banner|
|
|
25
|
-
[
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
parts = []
|
|
26
|
+
|
|
27
|
+
parts << render(Fluxbit::ListComponent.new) do |list|
|
|
28
|
+
object.errors.full_messages.each do |error|
|
|
29
|
+
list.with_item { template.sanitize(error.to_s, tags: [], attributes: []) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
parts << template.capture { yield(banner) } if block_given?
|
|
34
|
+
|
|
35
|
+
template.safe_join(parts)
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
@@ -65,7 +68,7 @@ module Fluxbit
|
|
|
65
68
|
end
|
|
66
69
|
end
|
|
67
70
|
|
|
68
|
-
[ :range, :toggle, :upload_image, :dropzone ].each do |component|
|
|
71
|
+
[ :range, :toggle, :upload_image, :dropzone, :password ].each do |component|
|
|
69
72
|
define_method("fx_#{component}") do |method, **options, &block|
|
|
70
73
|
options[:error] ||= error_for(method)
|
|
71
74
|
options[:error] = !!options[:error] if options[:error_hidden] && options[:error]
|
|
@@ -74,14 +77,60 @@ module Fluxbit
|
|
|
74
77
|
end
|
|
75
78
|
end
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
def fx_radio_group_button(method, **options, &block)
|
|
81
|
+
options[:name] ||= "#{@object_name}[#{method}]"
|
|
82
|
+
render Fluxbit::Form::RadioGroupButtonComponent.new(**options), &block
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Mimics Rails' form.select signature:
|
|
86
|
+
# select(method, choices = nil, options = {}, html_options = {}, &block)
|
|
87
|
+
#
|
|
88
|
+
# @param method [Symbol] The attribute name
|
|
89
|
+
# @param choices [Array, Hash, String, nil] Options for the select (raw data or pre-formatted HTML)
|
|
90
|
+
# @param options [Hash] Options like prompt, include_blank, selected, disabled
|
|
91
|
+
# @param html_options [Hash] HTML attributes for the select tag
|
|
92
|
+
# @param block [Proc] Optional block for custom options
|
|
93
|
+
#
|
|
94
|
+
# @example Basic usage
|
|
95
|
+
# form.fx_select :role, ["Admin", "User", "Guest"]
|
|
96
|
+
#
|
|
97
|
+
# @example With options
|
|
98
|
+
# form.fx_select :country, countries, { prompt: "Select a country" }, { class: "custom-class" }
|
|
99
|
+
#
|
|
100
|
+
# @example With pre-formatted options
|
|
101
|
+
# form.fx_select :status, options_for_select(statuses, selected: "active")
|
|
102
|
+
def fx_select(method, choices = nil, options = {}, html_options = {}, &block)
|
|
103
|
+
# Handle Rails-style signature
|
|
104
|
+
all_options = html_options.merge(options)
|
|
105
|
+
|
|
106
|
+
# Set the choices/options
|
|
107
|
+
all_options[:options] = choices if choices.present?
|
|
108
|
+
|
|
109
|
+
# Add error handling
|
|
110
|
+
all_options[:error] ||= error_for(method)
|
|
111
|
+
all_options[:error] = !!all_options[:error] if all_options[:error_hidden] && all_options[:error]
|
|
112
|
+
|
|
113
|
+
# Set selected value from object if not explicitly provided
|
|
81
114
|
value = object&.public_send(method)
|
|
82
|
-
|
|
115
|
+
all_options[:selected] ||= value if value.present?
|
|
116
|
+
|
|
117
|
+
render Fluxbit::Form::SelectComponent.new(form: self, attribute: method, **all_options, &block)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def fx_submit(content = nil, **options, &block)
|
|
121
|
+
options[:form] = self
|
|
122
|
+
options[:content] = content if content.present?
|
|
123
|
+
options[:color] ||= :primary
|
|
124
|
+
options[:size] ||= 2
|
|
125
|
+
options[:disabled] = true if object&.persisted? && !object.valid?
|
|
83
126
|
|
|
84
|
-
|
|
127
|
+
if block_given?
|
|
128
|
+
render Fluxbit::ButtonComponent.new(**options) do |*args|
|
|
129
|
+
yield(*args)
|
|
130
|
+
end
|
|
131
|
+
else
|
|
132
|
+
render Fluxbit::ButtonComponent.new(**options)
|
|
133
|
+
end
|
|
85
134
|
end
|
|
86
135
|
end
|
|
87
136
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fluxbit
|
|
4
|
+
module ViewHelper
|
|
5
|
+
def fx_body_class
|
|
6
|
+
"h-full bg-slate-100 dark:bg-slate-900 dark:text-white"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def fx_sort_field(field, url, label = nil)
|
|
10
|
+
order = (request.query_parameters[:order] || "").rpartition("_")
|
|
11
|
+
|
|
12
|
+
order_direction = "asc"
|
|
13
|
+
if order.first == field.to_s
|
|
14
|
+
order_direction = order.last == "asc" ? "desc" : "asc"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
link_to public_send(url, request.query_parameters.merge(order: "#{field}_#{order_direction}")), class: "order-#{order_direction} flex" do
|
|
18
|
+
label_text = label || field.to_s.titlecase
|
|
19
|
+
icon = if order.first == field.to_s
|
|
20
|
+
order_direction == "asc" ? fx_sort_up : fx_sort_down
|
|
21
|
+
else
|
|
22
|
+
fx_sort_up_down
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
safe_join([ h(label_text), icon ], " ")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def fx_sort_up_down
|
|
30
|
+
content_tag :svg, class: "size-4", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor" do
|
|
31
|
+
concat(
|
|
32
|
+
content_tag(
|
|
33
|
+
:path,
|
|
34
|
+
"",
|
|
35
|
+
fill_rule: "evenodd",
|
|
36
|
+
d: "M11.47 4.72a.75.75 0 0 1 1.06 0l3.75 3.75a.75.75 0 0 1-1.06 1.06L12 6.31 8.78 9.53a.75.75 0 0 1-1.06-1.06l3.75-3.75Zm-3.75 9.75a.75.75 0 0 1 1.06 0L12 17.69l3.22-3.22a.75.75 0 1 1 1.06 1.06l-3.75 3.75a.75.75 0 0 1-1.06 0l-3.75-3.75a.75.75 0 0 1 0-1.06Z",
|
|
37
|
+
clip_rule: "evenodd"
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def fx_sort_up
|
|
44
|
+
content_tag :svg, class: "size-4", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor" do
|
|
45
|
+
concat(
|
|
46
|
+
content_tag(
|
|
47
|
+
:path,
|
|
48
|
+
"",
|
|
49
|
+
fill_rule: "evenodd",
|
|
50
|
+
d: "M11.47 4.72a.75.75 0 0 1 1.06 0l3.75 3.75a.75.75 0 0 1-1.06 1.06L12 6.31 8.78 9.53a.75.75 0 0 1-1.06-1.06l3.75-3.75Z",
|
|
51
|
+
clip_rule: "evenodd"
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def fx_sort_down
|
|
58
|
+
content_tag :svg, class: "size-4", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor" do
|
|
59
|
+
concat(
|
|
60
|
+
content_tag(
|
|
61
|
+
:path,
|
|
62
|
+
"",
|
|
63
|
+
fill_rule: "evenodd",
|
|
64
|
+
d: "M11.47 19.28a.75.75 0 0 1 1.06 0l3.75-3.75a.75.75 0 0 1-1.06-1.06L12 17.69l-3.22-3.22a.75.75 0 1 1-1.06 1.06l3.75 3.75Z",
|
|
65
|
+
clip_rule: "evenodd"
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/config/locales/en.yml
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
en:
|
|
2
2
|
fluxbit:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
alert:
|
|
4
|
+
aria_close: Close
|
|
5
|
+
dismiss: Dismiss
|
|
6
|
+
banner:
|
|
7
|
+
aria_close: Close
|
|
8
|
+
dismiss: Dismiss
|
|
9
|
+
drawer:
|
|
10
|
+
dismiss: Dismiss
|
|
11
|
+
modal:
|
|
12
|
+
aria_close: Close
|
|
13
|
+
dismiss: Dismiss
|
|
14
|
+
pagination:
|
|
15
|
+
aria_label:
|
|
16
|
+
nav:
|
|
17
|
+
one: Page
|
|
18
|
+
other: Pages
|
|
19
|
+
prev: Previous
|
|
20
|
+
next: Next
|
|
21
|
+
first: First
|
|
22
|
+
last: Last
|
|
23
|
+
first: First
|
|
24
|
+
last: Last
|
|
25
|
+
prev: Previous
|
|
26
|
+
next: Next
|
|
27
|
+
skeleton:
|
|
28
|
+
loading: Loading...
|
|
29
|
+
speed_dial:
|
|
30
|
+
open_actions_menu: Open actions menu
|
|
31
|
+
form:
|
|
32
|
+
password:
|
|
33
|
+
checks:
|
|
34
|
+
length: At least %{count} characters
|
|
35
|
+
uppercase: Contains uppercase letter
|
|
36
|
+
lowercase: Contains lowercase letter
|
|
37
|
+
numbers: Contains number
|
|
38
|
+
special: Contains special character
|
|
39
|
+
strength: Password strength
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
pt-BR:
|
|
2
|
+
fluxbit:
|
|
3
|
+
alert:
|
|
4
|
+
aria_close: Close
|
|
5
|
+
dismiss: Dismiss
|
|
6
|
+
drawer:
|
|
7
|
+
dismiss: Dismiss
|
|
8
|
+
modal:
|
|
9
|
+
aria_close: Close
|
|
10
|
+
dismiss: Dismiss
|
|
11
|
+
pagination:
|
|
12
|
+
aria_label:
|
|
13
|
+
nav:
|
|
14
|
+
one: Página
|
|
15
|
+
other: Páginas
|
|
16
|
+
prev: Anterior
|
|
17
|
+
next: Próximo
|
|
18
|
+
first: Primeiro
|
|
19
|
+
last: Último
|
|
20
|
+
first: Primeiro
|
|
21
|
+
last: Último
|
|
22
|
+
prev: Anterior
|
|
23
|
+
next: Próximo
|
|
24
|
+
skeleton:
|
|
25
|
+
loading: Carregando...
|
|
26
|
+
speed_dial:
|
|
27
|
+
open_actions_menu: Abrir menu de ações
|
|
28
|
+
form:
|
|
29
|
+
password:
|
|
30
|
+
checks:
|
|
31
|
+
length: Pelo menos %{count} caracteres
|
|
32
|
+
uppercase: Contém letra maiúscula
|
|
33
|
+
lowercase: Contém letra minúscula
|
|
34
|
+
numbers: Contém número
|
|
35
|
+
special: Contém caractere especial
|
|
36
|
+
strength: Força da senha
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fluxbit::Config::AccordionComponent
|
|
4
|
+
mattr_accessor :flush, default: false
|
|
5
|
+
mattr_accessor :color, default: :default
|
|
6
|
+
mattr_accessor :collapse_all, default: false
|
|
7
|
+
|
|
8
|
+
# rubocop: disable Layout/LineLength, Metrics/BlockLength
|
|
9
|
+
mattr_accessor :styles do
|
|
10
|
+
{
|
|
11
|
+
base: "space-y-2",
|
|
12
|
+
item: {
|
|
13
|
+
base: "",
|
|
14
|
+
header: {
|
|
15
|
+
base: "flex items-center justify-between w-full p-5 font-medium rtl:text-right border border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3",
|
|
16
|
+
first: "rounded-t-xl",
|
|
17
|
+
last: "rounded-b-xl",
|
|
18
|
+
middle: "border-b-0"
|
|
19
|
+
},
|
|
20
|
+
content: {
|
|
21
|
+
base: "p-5 border border-gray-200 dark:border-gray-700",
|
|
22
|
+
first: "",
|
|
23
|
+
last: "rounded-b-xl border-t-0",
|
|
24
|
+
middle: "border-t-0 border-b-0"
|
|
25
|
+
},
|
|
26
|
+
icon: {
|
|
27
|
+
base: "w-3 h-3 rotate-180 shrink-0",
|
|
28
|
+
open: "rotate-180",
|
|
29
|
+
closed: ""
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
colors: {
|
|
33
|
+
default: {
|
|
34
|
+
header: "bg-white dark:bg-gray-900 text-gray-500 dark:text-gray-400",
|
|
35
|
+
content: "bg-white dark:bg-gray-900 text-gray-500 dark:text-gray-400"
|
|
36
|
+
},
|
|
37
|
+
light: {
|
|
38
|
+
header: "bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white",
|
|
39
|
+
content: "bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white"
|
|
40
|
+
},
|
|
41
|
+
primary: {
|
|
42
|
+
header: "bg-blue-50 dark:bg-blue-900 text-blue-900 dark:text-blue-100 border-blue-200 dark:border-blue-800",
|
|
43
|
+
content: "bg-blue-50 dark:bg-blue-900 text-blue-900 dark:text-blue-100 border-blue-200 dark:border-blue-800"
|
|
44
|
+
},
|
|
45
|
+
secondary: {
|
|
46
|
+
header: "bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 border-gray-300 dark:border-gray-700",
|
|
47
|
+
content: "bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 border-gray-300 dark:border-gray-700"
|
|
48
|
+
},
|
|
49
|
+
success: {
|
|
50
|
+
header: "bg-green-50 dark:bg-green-900 text-green-900 dark:text-green-100 border-green-200 dark:border-green-800",
|
|
51
|
+
content: "bg-green-50 dark:bg-green-900 text-green-900 dark:text-green-100 border-green-200 dark:border-green-800"
|
|
52
|
+
},
|
|
53
|
+
danger: {
|
|
54
|
+
header: "bg-red-50 dark:bg-red-900 text-red-900 dark:text-red-100 border-red-200 dark:border-red-800",
|
|
55
|
+
content: "bg-red-50 dark:bg-red-900 text-red-900 dark:text-red-100 border-red-200 dark:border-red-800"
|
|
56
|
+
},
|
|
57
|
+
warning: {
|
|
58
|
+
header: "bg-yellow-50 dark:bg-yellow-900 text-yellow-900 dark:text-yellow-100 border-yellow-200 dark:border-yellow-800",
|
|
59
|
+
content: "bg-yellow-50 dark:bg-yellow-900 text-yellow-900 dark:text-yellow-100 border-yellow-200 dark:border-yellow-800"
|
|
60
|
+
},
|
|
61
|
+
info: {
|
|
62
|
+
header: "bg-cyan-50 dark:bg-cyan-900 text-cyan-900 dark:text-cyan-100 border-cyan-200 dark:border-cyan-800",
|
|
63
|
+
content: "bg-cyan-50 dark:bg-cyan-900 text-cyan-900 dark:text-cyan-100 border-cyan-200 dark:border-cyan-800"
|
|
64
|
+
},
|
|
65
|
+
dark: {
|
|
66
|
+
header: "bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 border-gray-700 dark:border-gray-600",
|
|
67
|
+
content: "bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 border-gray-700 dark:border-gray-600"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
# rubocop: enable Layout/LineLength, Metrics/BlockLength
|
|
73
|
+
end
|
|
@@ -32,7 +32,7 @@ module Fluxbit::Config::AvatarComponent
|
|
|
32
32
|
},
|
|
33
33
|
color: {
|
|
34
34
|
dark: "ring-gray-800 dark:ring-gray-800",
|
|
35
|
-
|
|
35
|
+
danger: "ring-red-500 dark:ring-red-700",
|
|
36
36
|
gray: "ring-gray-500 dark:ring-gray-400",
|
|
37
37
|
info: "ring-cyan-400 dark:ring-cyan-800",
|
|
38
38
|
light: "ring-gray-300 dark:ring-gray-500",
|
|
@@ -42,20 +42,20 @@ module Fluxbit::Config::AvatarComponent
|
|
|
42
42
|
pink: "ring-pink-500 dark:ring-pink-500"
|
|
43
43
|
},
|
|
44
44
|
size: {
|
|
45
|
-
xs: "
|
|
46
|
-
sm: "
|
|
47
|
-
md: "
|
|
48
|
-
lg: "
|
|
49
|
-
xl: "
|
|
45
|
+
xs: "size-6",
|
|
46
|
+
sm: "size-8",
|
|
47
|
+
md: "size-10",
|
|
48
|
+
lg: "size-20",
|
|
49
|
+
xl: "size-36"
|
|
50
50
|
},
|
|
51
51
|
placeholder_icon: {
|
|
52
52
|
base: "relative overflow-hidden bg-gray-200 dark:bg-gray-600",
|
|
53
53
|
size: {
|
|
54
|
-
xs: "
|
|
55
|
-
sm: "
|
|
56
|
-
md: "
|
|
57
|
-
lg: "
|
|
58
|
-
xl: "
|
|
54
|
+
xs: "size-8",
|
|
55
|
+
sm: "size-10",
|
|
56
|
+
md: "size-12",
|
|
57
|
+
lg: "size-22",
|
|
58
|
+
xl: "size-38"
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
stacked: "ring-2 ring-gray-300 dark:ring-gray-500",
|