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.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -0
  3. data/app/assets/javascripts/fluxbit_view_components/assigner_controller.js +49 -0
  4. data/app/assets/javascripts/fluxbit_view_components/auto_submit_controller.js +39 -0
  5. data/app/assets/javascripts/fluxbit_view_components/drawer_controller.js +135 -0
  6. data/app/assets/javascripts/fluxbit_view_components/index.js +56 -0
  7. data/app/assets/javascripts/fluxbit_view_components/method_link_controller.js +143 -0
  8. data/app/assets/javascripts/fluxbit_view_components/modal_controller.js +118 -0
  9. data/app/assets/javascripts/fluxbit_view_components/password_controller.js +170 -0
  10. data/app/assets/javascripts/fluxbit_view_components/progress_controller.js +374 -0
  11. data/app/assets/javascripts/fluxbit_view_components/row_click_controller.js +32 -0
  12. data/app/assets/javascripts/fluxbit_view_components/select_all_controller.js +122 -0
  13. data/app/assets/javascripts/fluxbit_view_components/spinner_percent_controller.js +174 -0
  14. data/app/assets/javascripts/fluxbit_view_components/theme_button_controller.js +90 -0
  15. data/app/assets/javascripts/fluxbit_view_components.js +1175 -0
  16. data/app/components/fluxbit/accordion_component.rb +125 -0
  17. data/app/components/fluxbit/alert_component.rb +8 -8
  18. data/app/components/fluxbit/avatar_component.rb +11 -12
  19. data/app/components/fluxbit/avatar_group_component.rb +1 -1
  20. data/app/components/fluxbit/badge_component.rb +8 -7
  21. data/app/components/fluxbit/banner_component.rb +139 -0
  22. data/app/components/fluxbit/bottom_navigation_component.rb +437 -0
  23. data/app/components/fluxbit/breadcrumb_component.rb +66 -0
  24. data/app/components/fluxbit/button_component.rb +39 -11
  25. data/app/components/fluxbit/button_group_component.rb +1 -1
  26. data/app/components/fluxbit/card_component.rb +26 -23
  27. data/app/components/fluxbit/carousel_component.rb +154 -0
  28. data/app/components/fluxbit/component.rb +24 -3
  29. data/app/components/fluxbit/drawer_component.html.erb +30 -0
  30. data/app/components/fluxbit/drawer_component.rb +125 -0
  31. data/app/components/fluxbit/dropdown_component.rb +41 -0
  32. data/app/components/fluxbit/dropdown_item_component.rb +68 -0
  33. data/app/components/fluxbit/flex_component.rb +1 -1
  34. data/app/components/fluxbit/form/component.rb +15 -8
  35. data/app/components/fluxbit/form/dropzone_component.rb +3 -3
  36. data/app/components/fluxbit/form/field_component.rb +4 -2
  37. data/app/components/fluxbit/form/help_text_component.rb +1 -1
  38. data/app/components/fluxbit/form/label_component.rb +10 -3
  39. data/app/components/fluxbit/form/password_component.rb +247 -0
  40. data/app/components/fluxbit/form/radio_group_button_component.rb +126 -0
  41. data/app/components/fluxbit/form/select_component.rb +108 -11
  42. data/app/components/fluxbit/form/text_field_component.rb +40 -23
  43. data/app/components/fluxbit/form/toggle_component.rb +2 -2
  44. data/app/components/fluxbit/form/upload_image_component.html.erb +3 -3
  45. data/app/components/fluxbit/form/upload_image_component.rb +12 -1
  46. data/app/components/fluxbit/gravatar_component.rb +7 -0
  47. data/app/components/fluxbit/icon_helpers.rb +167 -0
  48. data/app/components/fluxbit/link_component.rb +42 -0
  49. data/app/components/fluxbit/modal_component.rb +28 -31
  50. data/app/components/fluxbit/pagination_component.rb +206 -0
  51. data/app/components/fluxbit/popover_component.rb +14 -14
  52. data/app/components/fluxbit/progress_component.rb +196 -0
  53. data/app/components/fluxbit/skeleton_component.rb +237 -0
  54. data/app/components/fluxbit/speed_dial_action_component.html.erb +30 -0
  55. data/app/components/fluxbit/speed_dial_action_component.rb +59 -0
  56. data/app/components/fluxbit/speed_dial_component.html.erb +33 -0
  57. data/app/components/fluxbit/speed_dial_component.rb +73 -0
  58. data/app/components/fluxbit/spinner_component.rb +71 -0
  59. data/app/components/fluxbit/spinner_percent_component.rb +174 -0
  60. data/app/components/fluxbit/stepper_component.rb +223 -0
  61. data/app/components/fluxbit/tab_component.rb +44 -25
  62. data/app/components/fluxbit/table_component.rb +186 -0
  63. data/app/components/fluxbit/table_group_component.rb +28 -0
  64. data/app/components/fluxbit/theme_button_component.rb +64 -0
  65. data/app/components/fluxbit/timeline_component.rb +63 -0
  66. data/app/components/fluxbit/timeline_item_component.html.erb +64 -0
  67. data/app/components/fluxbit/timeline_item_component.rb +78 -0
  68. data/app/components/fluxbit/tooltip_component.rb +2 -2
  69. data/app/helpers/fluxbit/components_helper.rb +74 -4
  70. data/app/helpers/fluxbit/form_builder.rb +64 -15
  71. data/app/helpers/fluxbit/view_helper.rb +71 -0
  72. data/config/locales/en.yml +37 -4
  73. data/config/locales/pt-BR.yml +36 -0
  74. data/lib/fluxbit/config/accordion_component.rb +73 -0
  75. data/lib/fluxbit/config/avatar_component.rb +11 -11
  76. data/lib/fluxbit/config/badge_component.rb +14 -11
  77. data/lib/fluxbit/config/banner_component.rb +60 -0
  78. data/lib/fluxbit/config/bottom_navigation_component.rb +74 -0
  79. data/lib/fluxbit/config/breadcrumb_component.rb +24 -0
  80. data/lib/fluxbit/config/button_component.rb +6 -4
  81. data/lib/fluxbit/config/card_component.rb +23 -12
  82. data/lib/fluxbit/config/carousel_component.rb +33 -0
  83. data/lib/fluxbit/config/drawer_component.rb +48 -0
  84. data/lib/fluxbit/config/dropdown_component.rb +29 -0
  85. data/lib/fluxbit/config/form/check_box_component.rb +1 -1
  86. data/lib/fluxbit/config/form/dropzone_component.rb +1 -1
  87. data/lib/fluxbit/config/form/help_text_component.rb +1 -1
  88. data/lib/fluxbit/config/form/label_component.rb +3 -2
  89. data/lib/fluxbit/config/form/password_component.rb +19 -0
  90. data/lib/fluxbit/config/form/radio_group_button_component.rb +24 -0
  91. data/lib/fluxbit/config/form/text_field_component.rb +11 -11
  92. data/lib/fluxbit/config/form/toggle_component.rb +5 -5
  93. data/lib/fluxbit/config/link_component.rb +24 -0
  94. data/lib/fluxbit/config/modal_component.rb +1 -1
  95. data/lib/fluxbit/config/pagination_component.rb +31 -0
  96. data/lib/fluxbit/config/popover_component.rb +1 -1
  97. data/lib/fluxbit/config/progress_component.rb +63 -0
  98. data/lib/fluxbit/config/skeleton_component.rb +82 -0
  99. data/lib/fluxbit/config/speed_dial_component.rb +50 -0
  100. data/lib/fluxbit/config/spinner_component.rb +30 -0
  101. data/lib/fluxbit/config/spinner_percent_component.rb +61 -0
  102. data/lib/fluxbit/config/stepper_component.rb +299 -0
  103. data/lib/fluxbit/config/tab_component.rb +6 -0
  104. data/lib/fluxbit/config/table_component.rb +75 -0
  105. data/lib/fluxbit/config/theme_button_component.rb +19 -0
  106. data/lib/fluxbit/config/timeline_component.rb +77 -0
  107. data/lib/fluxbit/view_components/engine.rb +11 -3
  108. data/lib/fluxbit/view_components/version.rb +1 -1
  109. data/lib/fluxbit/view_components.rb +20 -0
  110. data/lib/generators/fluxbit/devise_views_generator.rb +116 -0
  111. data/lib/generators/fluxbit/pagy_generator.rb +39 -0
  112. data/lib/generators/fluxbit/scaffold_generator.rb +165 -0
  113. data/lib/generators/fluxbit/templates/_alert.html.erb.tt +1 -0
  114. data/lib/generators/fluxbit/templates/_flash.html.erb.tt +15 -0
  115. data/lib/generators/fluxbit/templates/_form.html.erb.tt +38 -0
  116. data/lib/generators/fluxbit/templates/_metadata.html.erb.tt +44 -0
  117. data/lib/generators/fluxbit/templates/controller.rb.tt +406 -0
  118. data/lib/generators/fluxbit/templates/create.turbo_stream.erb.tt +7 -0
  119. data/lib/generators/fluxbit/templates/destroy.turbo_stream.erb.tt +3 -0
  120. data/lib/generators/fluxbit/templates/destroy_all.turbo_stream.erb.tt +9 -0
  121. data/lib/generators/fluxbit/templates/devise_views/confirmations/new.html.erb +11 -0
  122. data/lib/generators/fluxbit/templates/devise_views/layouts/devise.html.erb +64 -0
  123. data/lib/generators/fluxbit/templates/devise_views/mailer/confirmation_instructions.html.erb +5 -0
  124. data/lib/generators/fluxbit/templates/devise_views/mailer/email_changed.html.erb +7 -0
  125. data/lib/generators/fluxbit/templates/devise_views/mailer/password_changed.html.erb +3 -0
  126. data/lib/generators/fluxbit/templates/devise_views/mailer/reset_password_instructions.html.erb +8 -0
  127. data/lib/generators/fluxbit/templates/devise_views/mailer/unlock_instructions.html.erb +7 -0
  128. data/lib/generators/fluxbit/templates/devise_views/passwords/edit.html.erb +29 -0
  129. data/lib/generators/fluxbit/templates/devise_views/passwords/new.html.erb +11 -0
  130. data/lib/generators/fluxbit/templates/devise_views/registrations/edit.html.erb +43 -0
  131. data/lib/generators/fluxbit/templates/devise_views/registrations/new.html.erb +34 -0
  132. data/lib/generators/fluxbit/templates/devise_views/sessions/new.html.erb +15 -0
  133. data/lib/generators/fluxbit/templates/devise_views/shared/_error_messages.html.erb +14 -0
  134. data/lib/generators/fluxbit/templates/devise_views/shared/_links.html.erb +25 -0
  135. data/lib/generators/fluxbit/templates/devise_views/unlocks/new.html.erb +11 -0
  136. data/lib/generators/fluxbit/templates/edit.html.erb.tt +47 -0
  137. data/lib/generators/fluxbit/templates/fluxbit_pagy.css +27 -0
  138. data/lib/generators/fluxbit/templates/i18n.en.yml.tt +121 -0
  139. data/lib/generators/fluxbit/templates/i18n.pt-BR.yml.tt +121 -0
  140. data/lib/generators/fluxbit/templates/index.html.erb.tt +254 -0
  141. data/lib/generators/fluxbit/templates/index.json.jbuilder.tt +33 -0
  142. data/lib/generators/fluxbit/templates/new.html.erb.tt +47 -0
  143. data/lib/generators/fluxbit/templates/partial.html.erb.tt +61 -0
  144. data/lib/generators/fluxbit/templates/policy.rb.tt +36 -0
  145. data/lib/generators/fluxbit/templates/send_alert_via_drawer.erb.tt +10 -0
  146. data/lib/generators/fluxbit/templates/show.html.erb.tt +44 -0
  147. data/lib/generators/fluxbit/templates/show.json.jbuilder.tt +6 -0
  148. data/lib/generators/fluxbit/templates/update.turbo_stream.erb.tt +10 -0
  149. data/lib/generators/fluxbit/templates/update_all.turbo_stream.erb.tt +20 -0
  150. data/lib/install/install.rb +58 -0
  151. metadata +107 -18
  152. data/app/helpers/fluxbit/classes_helper.rb +0 -9
@@ -5,6 +5,7 @@ module Fluxbit::Config::TabComponent
5
5
  mattr_accessor :color, default: :blue
6
6
  mattr_accessor :vertical, default: false
7
7
  mattr_accessor :tab_panel, default: :default
8
+ mattr_accessor :align, default: :left
8
9
 
9
10
  # rubocop: disable Layout/LineLength, Metrics/BlockLength
10
11
  mattr_accessor :styles do
@@ -18,6 +19,11 @@ module Fluxbit::Config::TabComponent
18
19
  horizontal: "flex text-center",
19
20
  vertical: "flex-column space-y space-y-4 text-sm font-medium text-gray-500 dark:text-gray-400 md:me-4 mb-4 md:mb-0"
20
21
  },
22
+ align: {
23
+ left: "justify-start",
24
+ center: "justify-center",
25
+ right: "justify-end"
26
+ },
21
27
  li: "",
22
28
  variant: {
23
29
  default: "flex-wrap text-sm font-medium text-gray-500 border-b border-gray-200 dark:border-gray-700 dark:text-gray-400",
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fluxbit::Config::TableComponent
4
+ mattr_accessor :striped, default: false
5
+ mattr_accessor :bordered, default: false
6
+ mattr_accessor :hover, default: false
7
+ mattr_accessor :shadow, default: false
8
+
9
+ # rubocop: disable Layout/LineLength, Metrics/BlockLength
10
+ mattr_accessor :styles do
11
+ {
12
+ root: {
13
+ base: "w-full text-left text-sm rtl:text-right text-gray-500 dark:text-gray-400",
14
+ shadow: "absolute left-0 top-0 -z-10 h-full w-full rounded-lg bg-white drop-shadow-md dark:bg-black"
15
+ },
16
+ wrapper: {
17
+ base: "overflow-x-auto relative",
18
+ shadow: "shadow-md sm:rounded-lg"
19
+ },
20
+ head: {
21
+ base: "text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400",
22
+ cell: "px-6 py-3"
23
+ },
24
+ footer: {
25
+ base: "text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400",
26
+ cell: "px-6 py-3"
27
+ },
28
+ body: {
29
+ base: "bg-white border-b dark:bg-gray-800 dark:border-gray-700 border-gray-200"
30
+ },
31
+ row: {
32
+ base: "",
33
+ bordered: "border-b dark:border-gray-700 border-gray-200",
34
+ hovered: {
35
+ default: "hover:bg-gray-200 dark:hover:bg-gray-600",
36
+ primary: "hover:bg-blue-200 dark:hover:bg-blue-700",
37
+ secondary: "hover:bg-gray-200 dark:hover:bg-gray-700",
38
+ success: "hover:bg-green-200 dark:hover:bg-green-700",
39
+ danger: "hover:bg-red-200 dark:hover:bg-red-700",
40
+ warning: "hover:bg-yellow-200 dark:hover:bg-yellow-700",
41
+ info: "hover:bg-cyan-200 dark:hover:bg-cyan-700",
42
+ light: "hover:bg-gray-300 dark:hover:bg-gray-700",
43
+ dark: "hover:bg-gray-700 dark:hover:bg-gray-200"
44
+ },
45
+ striped: {
46
+ default: "odd:bg-white even:bg-gray-50 odd:dark:bg-gray-900 even:dark:bg-gray-800",
47
+ primary: "odd:bg-blue-50 even:bg-blue-100 odd:dark:bg-blue-900 even:dark:bg-blue-800",
48
+ secondary: "odd:bg-gray-50 even:bg-gray-100 odd:dark:bg-gray-900 even:dark:bg-gray-800",
49
+ success: "odd:bg-green-50 even:bg-green-100 odd:dark:bg-green-900 even:dark:bg-green-800",
50
+ danger: "odd:bg-red-50 even:bg-red-100 odd:dark:bg-red-900 even:dark:bg-red-800",
51
+ warning: "odd:bg-yellow-50 even:bg-yellow-100 odd:dark:bg-yellow-900 even:dark:bg-yellow-800",
52
+ info: "odd:bg-cyan-50 even:bg-cyan-100 odd:dark:bg-cyan-900 even:dark:bg-cyan-800",
53
+ light: "odd:bg-gray-100 even:bg-gray-200 odd:dark:bg-gray-700 even:dark:bg-gray-600",
54
+ dark: "odd:bg-gray-800 even:bg-gray-900 odd:dark:bg-gray-200 even:dark:bg-gray-100"
55
+ },
56
+ colors: {
57
+ default: "",
58
+ primary: "bg-blue-50 dark:bg-blue-900",
59
+ secondary: "bg-gray-50 dark:bg-gray-800",
60
+ success: "bg-green-50 dark:bg-green-900",
61
+ danger: "bg-red-50 dark:bg-red-900",
62
+ warning: "bg-yellow-50 dark:bg-yellow-900",
63
+ info: "bg-cyan-50 dark:bg-cyan-900",
64
+ light: "bg-gray-100 dark:bg-gray-700",
65
+ dark: "bg-gray-800 dark:bg-gray-200"
66
+ },
67
+ cell: {
68
+ base: "px-6 py-2",
69
+ selected: "font-medium text-gray-900 whitespace-nowrap dark:text-white"
70
+ }
71
+ }
72
+ }
73
+ end
74
+ # rubocop: enable Layout/LineLength, Metrics/BlockLength
75
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fluxbit::Config::ThemeButtonComponent
4
+ # Theme button specific defaults
5
+ mattr_accessor :color, default: :transparent
6
+ mattr_accessor :pill, default: true
7
+ mattr_accessor :size, default: 2
8
+ mattr_accessor :as, default: :button
9
+
10
+ # Delegate styles to ButtonComponent (class method)
11
+ def self.styles
12
+ Fluxbit::Config::ButtonComponent.styles
13
+ end
14
+
15
+ # Delegate styles to ButtonComponent (instance method for tests)
16
+ def styles
17
+ Fluxbit::Config::ButtonComponent.styles
18
+ end
19
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fluxbit::Config::TimelineComponent
4
+ mattr_accessor :variant, default: :default
5
+ mattr_accessor :position, default: :left
6
+
7
+ mattr_accessor :styles do
8
+ {
9
+ base: "relative",
10
+ variants: {
11
+ default: "space-y-8 border-l border-gray-200 dark:border-gray-700",
12
+ vertical: "space-y-6 border-l border-gray-200 dark:border-gray-700",
13
+ stepper: "items-center sm:flex",
14
+ activity: "space-y-4 border-l border-gray-200 dark:border-gray-700"
15
+ },
16
+ positions: {
17
+ left: "ml-3",
18
+ center: "",
19
+ right: "mr-3"
20
+ },
21
+ item: {
22
+ base: "relative flex items-start",
23
+ indicator: {
24
+ base: "absolute flex items-center justify-center w-6 h-6 -left-3 rounded-full bg-white dark:bg-gray-900",
25
+ status: {
26
+ default: "bg-blue-100 dark:bg-blue-900",
27
+ completed: "bg-green-100 dark:bg-green-900",
28
+ current: "bg-blue-600",
29
+ pending: "bg-gray-100 dark:bg-gray-700"
30
+ },
31
+ colors: {
32
+ blue: "text-blue-800 dark:text-blue-300",
33
+ green: "text-green-800 dark:text-green-300",
34
+ red: "text-red-800 dark:text-red-300",
35
+ yellow: "text-yellow-800 dark:text-yellow-300",
36
+ purple: "text-purple-800 dark:text-purple-300",
37
+ indigo: "text-indigo-800 dark:text-indigo-300"
38
+ },
39
+ rings: {
40
+ none: "",
41
+ small: "ring-1 ring-white dark:ring-gray-900",
42
+ default: "ring-2 ring-white dark:ring-gray-900",
43
+ large: "ring-4 ring-white dark:ring-gray-900"
44
+ }
45
+ },
46
+ content: {
47
+ base: "ml-6 mb-8",
48
+ title: "mb-1 text-lg font-semibold text-gray-900 dark:text-white",
49
+ description: "mb-4 text-base font-normal text-gray-500 dark:text-gray-400",
50
+ date: "inline-flex items-center px-2.5 py-0.5 text-xs font-medium text-blue-800 bg-blue-100 rounded dark:bg-blue-900 dark:text-blue-300"
51
+ },
52
+ icon: "w-3.5 h-3.5",
53
+ dot: "w-2.5 h-2.5 bg-current rounded-full"
54
+ },
55
+ stepper: {
56
+ item: "relative mb-6 sm:mb-0",
57
+ indicator_container: "flex items-center",
58
+ indicator: "z-10 flex items-center justify-center w-6 h-6 bg-blue-100 rounded-full dark:bg-blue-900 shrink-0",
59
+ indicator_completed: "z-10 flex items-center justify-center w-6 h-6 bg-blue-600 rounded-full dark:bg-blue-900 shrink-0",
60
+ connector: "hidden sm:flex w-full bg-gray-200 h-0.5 dark:bg-gray-700",
61
+ content: "mt-3 sm:pe-8",
62
+ title: "text-lg font-semibold text-gray-900 dark:text-white",
63
+ description: "block mb-2 text-sm font-normal leading-none text-gray-400 dark:text-gray-500",
64
+ description_paragraph: "text-base font-normal text-gray-500 dark:text-gray-400",
65
+ icon: "w-2.5 h-2.5 text-blue-800 dark:text-blue-300",
66
+ icon_completed: "w-2.5 h-2.5 text-white dark:text-blue-300"
67
+ },
68
+ activity: {
69
+ base: "ml-6",
70
+ time: "mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500",
71
+ title: "text-lg font-semibold text-gray-900 dark:text-white",
72
+ description: "mb-4 text-base font-normal text-gray-500 dark:text-gray-400",
73
+ indicator: "absolute w-3 h-3 bg-gray-200 rounded-full mt-1.5 -left-1.5 border border-white dark:border-gray-900 dark:bg-gray-700"
74
+ }
75
+ }
76
+ end
77
+ end
@@ -15,8 +15,16 @@ module Fluxbit
15
15
  ]
16
16
 
17
17
  # Remove default wrapping .field_with_errors for proper Shopify form validations
18
- config.to_prepare do
19
- ActionView::Base.field_error_proc = ->(html_tag, _instance) { html_tag.html_safe }
18
+ # config.to_prepare do
19
+ # ActionView::Base.field_error_proc = ->(html_tag, _instance) { html_tag.html_safe }
20
+ # end
21
+
22
+ initializer "fluxbitview_components.assets" do |app|
23
+ if app.config.respond_to?(:assets)
24
+ app.config.assets.precompile += %w[
25
+ fluxbit_view_components.js
26
+ ]
27
+ end
20
28
  end
21
29
 
22
30
  initializer "fluxbit_view_components.importmap", before: "importmap" do |app|
@@ -27,8 +35,8 @@ module Fluxbit
27
35
 
28
36
  initializer "fluxbit_view_components.helpers" do
29
37
  ActiveSupport.on_load(:action_controller_base) do
30
- helper Fluxbit::ClassesHelper
31
38
  helper Fluxbit::ComponentsHelper
39
+ helper Fluxbit::ViewHelper
32
40
  end
33
41
  end
34
42
  end
@@ -1,5 +1,5 @@
1
1
  module Fluxbit
2
2
  module ViewComponents
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -13,24 +13,44 @@ module Fluxbit
13
13
  require "fluxbit/config/form/check_box_component"
14
14
  require "fluxbit/config/form/dropzone_component"
15
15
  require "fluxbit/config/form/label_component"
16
+ require "fluxbit/config/form/password_component"
17
+ require "fluxbit/config/form/radio_group_button_component"
16
18
  require "fluxbit/config/form/range_component"
17
19
  require "fluxbit/config/form/text_field_component"
18
20
  require "fluxbit/config/form/toggle_component"
19
21
  end
20
22
 
23
+ require "fluxbit/config/accordion_component"
21
24
  require "fluxbit/config/alert_component"
22
25
  require "fluxbit/config/avatar_component"
23
26
  require "fluxbit/config/badge_component"
27
+ require "fluxbit/config/banner_component"
28
+ require "fluxbit/config/bottom_navigation_component"
29
+ require "fluxbit/config/breadcrumb_component"
24
30
  require "fluxbit/config/button_component"
25
31
  require "fluxbit/config/card_component"
32
+ require "fluxbit/config/carousel_component"
33
+ require "fluxbit/config/drawer_component"
34
+ require "fluxbit/config/dropdown_component"
26
35
  require "fluxbit/config/flex_component"
27
36
  require "fluxbit/config/gravatar_component"
28
37
  require "fluxbit/config/heading_component"
38
+ require "fluxbit/config/link_component"
29
39
  require "fluxbit/config/modal_component"
40
+ require "fluxbit/config/pagination_component"
30
41
  require "fluxbit/config/paragraph_component"
31
42
  require "fluxbit/config/popover_component"
43
+ require "fluxbit/config/progress_component"
44
+ require "fluxbit/config/skeleton_component"
45
+ require "fluxbit/config/speed_dial_component"
46
+ require "fluxbit/config/spinner_component"
47
+ require "fluxbit/config/spinner_percent_component"
48
+ require "fluxbit/config/stepper_component"
32
49
  require "fluxbit/config/tab_component"
50
+ require "fluxbit/config/timeline_component"
51
+ require "fluxbit/config/table_component"
33
52
  require "fluxbit/config/text_component"
53
+ require "fluxbit/config/theme_button_component"
34
54
  require "fluxbit/config/tooltip_component"
35
55
  end
36
56
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module Fluxbit
6
+ module ViewPathTemplates
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ argument :scope, required: false, default: nil, desc: "The scope to copy views to"
11
+ class_option :views, aliases: "-v", type: :array, desc: "Select specific view directories to generate (confirmations, passwords, registrations, sessions, unlocks, mailer)"
12
+
13
+ public_task :copy_views
14
+ end
15
+
16
+ def copy_views
17
+ if options[:views]
18
+ options[:views].each do |directory|
19
+ view_directory directory.to_sym
20
+ end
21
+ else
22
+ view_directory :confirmations
23
+ view_directory :passwords
24
+ view_directory :registrations
25
+ view_directory :sessions
26
+ view_directory :unlocks
27
+ view_directory :shared
28
+ view_directory :mailer
29
+ end
30
+
31
+ copy_layout
32
+ configure_layouts
33
+ end
34
+
35
+ def copy_layout
36
+ template "layouts/devise.html.erb", "app/views/layouts/devise.html.erb"
37
+ end
38
+
39
+ def configure_layouts
40
+ layout_config = <<-RUBY
41
+ # Use the Devise layout for all Devise controllers
42
+ config.to_prepare do
43
+ Devise::SessionsController.layout "devise"
44
+ Devise::RegistrationsController.layout "devise"
45
+ Devise::ConfirmationsController.layout "devise"
46
+ Devise::UnlocksController.layout "devise"
47
+ Devise::PasswordsController.layout "devise"
48
+ end
49
+
50
+ RUBY
51
+
52
+ inject_into_file(
53
+ "config/application.rb",
54
+ layout_config,
55
+ after: "class Application < Rails::Application\n"
56
+ )
57
+ end
58
+
59
+ protected
60
+
61
+ def view_directory(name, _target_path = nil)
62
+ directory name.to_s, _target_path || "#{target_path}/#{name}" do |content|
63
+ if scope
64
+ content.gsub("devise/shared", "#{plural_scope}/shared")
65
+ else
66
+ content
67
+ end
68
+ end
69
+ end
70
+
71
+ def target_path
72
+ @target_path ||= "app/views/#{plural_scope || :devise}"
73
+ end
74
+
75
+ def plural_scope
76
+ @plural_scope ||= scope.presence && scope.underscore.pluralize
77
+ end
78
+ end
79
+
80
+ class DeviseViewsGenerator < Rails::Generators::Base
81
+ include ViewPathTemplates
82
+
83
+ source_root File.expand_path("templates/devise_views", __dir__)
84
+ desc "Copies Devise views into your app. Usage: rails g fluxbit:devise_views"
85
+ end
86
+
87
+ class SharedViewsGenerator < Rails::Generators::Base # :nodoc:
88
+ include ViewPathTemplates
89
+ source_root File.expand_path("./devise_views", __FILE__)
90
+ desc "Copies shared Devise views to your application."
91
+ hide!
92
+
93
+ # Override copy_views to just copy mailer and shared.
94
+ def copy_views
95
+ view_directory :shared
96
+ end
97
+ end
98
+
99
+ class FormForGenerator < Rails::Generators::Base # :nodoc:
100
+ include ViewPathTemplates
101
+ source_root File.expand_path("./devise_views", __FILE__)
102
+ desc "Copies default Devise views to your application."
103
+ hide!
104
+ end
105
+
106
+ class ErbGenerator < Rails::Generators::Base # :nodoc:
107
+ include ViewPathTemplates
108
+ source_root File.expand_path("./devise_views", __FILE__)
109
+ desc "Copies Devise mail erb views to your application."
110
+ hide!
111
+
112
+ def copy_views
113
+ view_directory(:mailer) if !options[:views] || options[:views].include?("mailer")
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module Fluxbit
6
+ class PagyGenerator < Rails::Generators::Base
7
+ source_root File.expand_path("templates", __dir__)
8
+ desc "Copies fluxbit_pagy.css and imports it in application.css"
9
+
10
+ def copy_css_file
11
+ copy_file "fluxbit_pagy.css", "app/assets/stylesheets/fluxbit_pagy.css"
12
+ end
13
+
14
+ def import_into_application_css
15
+ import_line = "@import \"./fluxbit_pagy\";"
16
+ tailwind_path = "app/assets/stylesheets/application.tailwind.css"
17
+ default_path = "app/assets/stylesheets/application.css"
18
+
19
+ target_file = if File.exist?(tailwind_path)
20
+ tailwind_path
21
+ elsif File.exist?(default_path)
22
+ default_path
23
+ else
24
+ say_status :error, "No application CSS file found (expected application.tailwind.css or application.css)", :red
25
+ return
26
+ end
27
+
28
+ if File.exist?(target_file)
29
+ unless File.read(target_file).include?(import_line)
30
+ append_to_file target_file, "\n#{import_line}\n"
31
+ else
32
+ say_status :skipped, "#{import_line} already present in #{target_file}"
33
+ end
34
+ else
35
+ say_status :error, "#{target_file} not found", :red
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,165 @@
1
+ require "rails/generators"
2
+ require "rails/generators/named_base"
3
+
4
+ module Fluxbit
5
+ class ScaffoldGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
8
+
9
+ # Turbo option
10
+ class_option :turbo, type: :boolean, default: true, desc: "Use Turbo Streams for dynamic updates"
11
+
12
+ # Modal/Drawer option
13
+ class_option :ui, type: :string, default: "drawer", enum: [ "modal", "drawer", "none" ], desc: "Choose UI interaction for forms"
14
+
15
+ # Paginator option
16
+ class_option :paginator, type: :boolean, default: true, desc: "Include pagination using Pagy"
17
+
18
+ # Authorization option
19
+ class_option :pundit, type: :boolean, default: true, desc: "Include Pundit for authorization"
20
+
21
+ # Namespace option
22
+ class_option :namespace, type: :string, default: nil, desc: "Namespace for the controller (e.g., 'admin', 'api/v1')"
23
+
24
+ def generate_model_and_migration
25
+ # Use Rails model generator to create model & migration (if not already present)
26
+ model_args = [ name ] + attributes
27
+ generate("model", *model_args)
28
+ end
29
+
30
+ def generate_scaffold_files
31
+ # Add resource route
32
+ if namespaced?
33
+ # Build nested namespace blocks
34
+ namespace_parts = namespace_path.split("/")
35
+ namespace_opens = namespace_parts.map.with_index do |part, idx|
36
+ " " * idx + "namespace :#{part} do"
37
+ end.join("\n")
38
+ namespace_closes = namespace_parts.size.times.map { |i| " " * (namespace_parts.size - 1 - i) + "end" }.join("\n")
39
+ indent = " " * namespace_parts.size
40
+
41
+ route %(#{namespace_opens}
42
+ #{indent}resources :#{file_name.pluralize} do
43
+ #{indent} collection do
44
+ #{indent} put "update_all"
45
+ #{indent} patch "update_all"
46
+ #{indent} delete "destroy_all"
47
+ #{indent} end
48
+ #{indent}end
49
+ #{namespace_closes})
50
+ else
51
+ route %(resources :#{file_name.pluralize} do
52
+ collection do
53
+ put "update_all"
54
+ patch "update_all"
55
+ delete "destroy_all"
56
+ end
57
+ end)
58
+ end
59
+
60
+ # Ensure controller directory exists
61
+ empty_directory controller_dir if namespaced?
62
+
63
+ # Generate controller from template
64
+ template "controller.rb.tt", File.join(controller_dir, "#{file_name.pluralize}_controller.rb")
65
+
66
+ # Ensure view directory exists
67
+ empty_directory view_dir
68
+
69
+ # Generate view templates from templates
70
+ template "index.html.erb.tt", File.join(view_dir, "index.html.erb")
71
+ template "show.html.erb.tt", File.join(view_dir, "show.html.erb")
72
+ template "new.html.erb.tt", File.join(view_dir, "new.html.erb")
73
+ template "edit.html.erb.tt", File.join(view_dir, "edit.html.erb")
74
+ template "_form.html.erb.tt", File.join(view_dir, "_form.html.erb")
75
+ template "_metadata.html.erb.tt", File.join(view_dir, "_metadata.html.erb")
76
+ template "partial.html.erb.tt", File.join(view_dir, "_#{file_name.pluralize}.html.erb")
77
+
78
+ # Generate Jbuilder templates
79
+ template "index.json.jbuilder.tt", File.join(view_dir, "index.json.jbuilder")
80
+ template "show.json.jbuilder.tt", File.join(view_dir, "show.json.jbuilder")
81
+
82
+ # Generate Turbo Stream response templates
83
+ template "create.turbo_stream.erb.tt", File.join(view_dir, "create.turbo_stream.erb")
84
+ template "update.turbo_stream.erb.tt", File.join(view_dir, "update.turbo_stream.erb")
85
+ template "update_all.turbo_stream.erb.tt", File.join(view_dir, "update_all.turbo_stream.erb")
86
+ template "destroy.turbo_stream.erb.tt", File.join(view_dir, "destroy.turbo_stream.erb")
87
+ template "destroy_all.turbo_stream.erb.tt", File.join(view_dir, "destroy_all.turbo_stream.erb")
88
+
89
+ # Generate i18n
90
+ template "i18n.en.yml.tt", File.join("config", "locales", "#{file_name.pluralize}.en.yml")
91
+ template "i18n.pt-BR.yml.tt", File.join("config", "locales", "#{file_name.pluralize}.pt-BR.yml")
92
+
93
+ # Generate Policy
94
+ template "policy.rb.tt", File.join("app/policies", "#{file_name.singularize}_policy.rb")
95
+
96
+ # Generate shared partials
97
+ template "_alert.html.erb.tt", File.join("app/views/shared", "_alert.html.erb")
98
+ template "send_alert_via_drawer.html.erb.tt", File.join("app/views/shared", "send_alert_via_drawer_alert.html.erb")
99
+ template "_flash.html.erb.tt", File.join("app/views/shared", "_flash.html.erb")
100
+ end
101
+
102
+ private
103
+
104
+ # Returns the namespace path (e.g., "admin" or "api/v1")
105
+ def namespace_path
106
+ options[:namespace]
107
+ end
108
+
109
+ # Returns true if namespace is present
110
+ def namespaced?
111
+ namespace_path.present?
112
+ end
113
+
114
+ # Returns the namespace as a module (e.g., "Admin" or "Api::V1")
115
+ def namespace_module
116
+ return nil unless namespaced?
117
+ namespace_path.split("/").map(&:camelize).join("::")
118
+ end
119
+
120
+ # Returns the controller directory path (e.g., "app/controllers/admin")
121
+ def controller_dir
122
+ if namespaced?
123
+ File.join("app/controllers", namespace_path)
124
+ else
125
+ "app/controllers"
126
+ end
127
+ end
128
+
129
+ # Returns the view directory path (e.g., "app/views/admin/products")
130
+ def view_dir
131
+ if namespaced?
132
+ File.join("app/views", namespace_path, file_name.pluralize)
133
+ else
134
+ File.join("app/views", file_name.pluralize)
135
+ end
136
+ end
137
+
138
+ # Returns the namespaced class name (e.g., "Admin::ProductsController")
139
+ def namespaced_class_name
140
+ if namespaced?
141
+ "#{namespace_module}::#{class_name}"
142
+ else
143
+ class_name
144
+ end
145
+ end
146
+
147
+ # Returns the controller file path (e.g., "admin/products_controller.rb")
148
+ def controller_file_path
149
+ if namespaced?
150
+ File.join(namespace_path, "#{file_name.pluralize}_controller.rb")
151
+ else
152
+ "#{file_name.pluralize}_controller.rb"
153
+ end
154
+ end
155
+
156
+ # Returns the path helper prefix (e.g., "admin_" or "api_v1_")
157
+ def path_prefix
158
+ if namespaced?
159
+ namespace_path.split("/").join("_") + "_"
160
+ else
161
+ ""
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1 @@
1
+ <%%= fx_alert(color: color) { message } %>
@@ -0,0 +1,15 @@
1
+ <%% mapping = {
2
+ "notice" => :success,
3
+ "success" => :success,
4
+ "error" => :danger,
5
+ "alert" => :warning,
6
+ "warning" => :warning
7
+ } %>
8
+
9
+ <%% flash.each do |type, msg| %>
10
+ <%% next if msg.blank? %>
11
+ <%% color = mapping[type.to_s] || :info %>
12
+ <%%= fx_alert(color: color) do %>
13
+ <%%= safe_join(Array(msg).map { |m| sanitize(m.to_s, tags: [], attributes: []) }, tag.br) %>
14
+ <%% end %>
15
+ <%% end %>
@@ -0,0 +1,38 @@
1
+ <% singular = file_name.singularize; plural = file_name.pluralize -%>
2
+ <%%= form_with(model: @<%= singular %>, <% if namespaced? %>url: @<%= singular %>.persisted? ? <%= path_prefix %><%= singular %>_path(@<%= singular %>) : <%= path_prefix %><%= plural %>_path, <% end %>id: "<%= singular %>_form", builder: Fluxbit::FormBuilder, format: turbo_frame_request? ? :turbo_stream : :html) do |form| %>
3
+ <%% if @<%= singular %>.errors.any? %>
4
+ <div class="mb-4">
5
+ <%%= fx_alert(color: :danger) { t("<%= plural %>.messages.errors_message", count: @<%= singular %>.errors.count) } %>
6
+ <ul class="text-sm text-red-600 pl-5 list-disc">
7
+ <%% @<%= singular %>.errors.full_messages.each do |msg| %>
8
+ <li><%%= msg %></li>
9
+ <%% end %>
10
+ </ul>
11
+ </div>
12
+ <%% end %>
13
+
14
+ <% attributes.each do |att|
15
+ name, type = att.name, att.type.to_s
16
+ next if type == 'password_digest' # handle password fields separately
17
+ -%>
18
+ <% if ['references','belongs_to'].include?(type) -%>
19
+ <%%= form.fx_select :<%= "#{name}_id" %>, <%= name.camelize %>.all.pluck(:name, :id), { prompt: t("<%= plural %>.prompts.<%= name %>") }, {wrapper_html: {class: "mb-4"} } %>
20
+ <% elsif type == 'boolean' -%>
21
+ <%%= form.fx_toggle :<%= name %>, wrapper_html: {class: "mb-4"} %>
22
+ <% elsif type == 'password' -%>
23
+ <%%= form.fx_password_field :<%= name %>, wrapper_html: {class: "mb-4"} %>
24
+ <% elsif type == 'text' -%>
25
+ <%%= form.fx_text_area :<%= name %>, rows: 5, wrapper_html: {class: "mb-4"} %>
26
+ <% elsif type == 'attachment' || type == 'attachments' -%>
27
+ <%%= form.fx_dropzone :<%= name %>, wrapper_html: {class: "mb-4"}, <%= 'multiple: true' if type == 'attachments' %>%>
28
+ <% else -%>
29
+ <%%= form.fx_text_field :<%= name %>, wrapper_html: {class: "mb-4"} %>
30
+ <% end -%>
31
+ <% end -%>
32
+ <% if attributes.any? { |a| a.type == :password_digest } -%>
33
+ <%%= form.fx_password_field :password, wrapper_html: {class: "mb-4"} %>
34
+ <%%= form.fx_password_field :password_confirmation, wrapper_html: {class: "mb-4"} %>
35
+ <% end -%>
36
+ <%%= form.fx_submit value: t(@<%= singular %>.persisted? ? "<%= plural %>.actions.update" : "<%= plural %>.actions.save"), data: { turbo_submits_with: t("<%= plural %>.helpers.submits_with") }, class: "mt-4" %>
37
+ <%%= render "metadata", <%= singular %>: @<%= singular %> %>
38
+ <%% end %>