plutonium 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/Rakefile +1 -2
  4. data/app/assets/application.js.bk +31419 -0
  5. data/app/assets/plutonium-logo-original.png +0 -0
  6. data/app/assets/plutonium-logo-white.png +0 -0
  7. data/app/assets/plutonium-logo.png +0 -0
  8. data/app/assets/plutonium.css +1 -0
  9. data/app/assets/plutonium.ico +0 -0
  10. data/app/assets/plutonium.js +12416 -0
  11. data/app/assets/plutonium.js.map +7 -0
  12. data/app/assets/plutonium.min.js +39 -0
  13. data/app/assets/plutonium.min.js.map +7 -0
  14. data/app/views/application/_flash_alerts.html.erb +5 -2
  15. data/app/views/application/_flash_toasts.html.erb +2 -0
  16. data/app/views/application/_resource_header.html.erb +263 -697
  17. data/app/views/application/_resource_sidebar.html.erb +14 -12
  18. data/app/views/components/action_button/action_button_component.rb +3 -3
  19. data/app/views/components/attributes.rb +184 -0
  20. data/app/views/components/base.rb +19 -40
  21. data/app/views/components/block/block_component.html.erb +1 -1
  22. data/app/views/components/block/block_component.rb +11 -7
  23. data/app/views/components/breadcrumbs/breadcrumbs_component.rb +3 -3
  24. data/app/views/components/button/button_component.html.erb +2 -2
  25. data/app/views/components/button/button_component.rb +10 -5
  26. data/app/views/components/dyna_frame_content/dyna_frame_content_component.html.erb +1 -0
  27. data/app/views/components/dyna_frame_content/dyna_frame_content_component.rb +3 -3
  28. data/app/views/components/dyna_frame_host/dyna_frame_host_component.html.erb +1 -2
  29. data/app/views/components/dyna_frame_host/dyna_frame_host_component.rb +12 -5
  30. data/app/views/components/empty_card/empty_card_component.rb +3 -3
  31. data/app/views/components/form/form_builder.rb +1 -1
  32. data/app/views/components/form/form_component.rb +3 -3
  33. data/app/views/components/has_many_panel/has_many_panel_component.html.erb +25 -0
  34. data/app/views/components/has_many_panel/has_many_panel_component.rb +16 -0
  35. data/app/views/components/header/header_component.rb +3 -3
  36. data/app/views/components/interactive_action_form/interactive_action_form_component.rb +3 -3
  37. data/app/views/components/nav_grid_menu/nav_grid_menu_component.html.erb +24 -0
  38. data/app/views/components/nav_grid_menu/nav_grid_menu_component.rb +23 -0
  39. data/app/views/components/nav_grid_menu_item/nav_grid_menu_item_component.html.erb +4 -0
  40. data/app/views/components/nav_grid_menu_item/nav_grid_menu_item_component.rb +20 -0
  41. data/app/views/components/nav_user/nav_user_component.html.erb +50 -0
  42. data/app/views/components/nav_user/nav_user_component.rb +32 -0
  43. data/app/views/components/nav_user_link/nav_user_link_component.html.erb +7 -0
  44. data/app/views/components/nav_user_link/nav_user_link_component.rb +23 -0
  45. data/app/views/components/nav_user_section/nav_user_section_component.html.erb +7 -0
  46. data/app/views/components/nav_user_section/nav_user_section_component.rb +18 -0
  47. data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.html.erb +1 -3
  48. data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.rb +11 -5
  49. data/app/views/components/pagination/pagination_component.html.erb +1 -1
  50. data/app/views/components/pagination/pagination_component.rb +10 -7
  51. data/app/views/components/panel/panel_component.html.erb +13 -6
  52. data/app/views/components/panel/panel_component.rb +11 -5
  53. data/app/views/components/resource_header/resource_header_component.html.erb +81 -0
  54. data/app/views/components/resource_header/resource_header_component.rb +20 -0
  55. data/app/views/components/resource_layout/resource_layout_component.html.erb +32 -0
  56. data/app/views/components/resource_layout/resource_layout_component.rb +39 -0
  57. data/app/views/components/sidebar/sidebar_component.html.erb +3 -33
  58. data/app/views/components/sidebar/sidebar_component.rb +3 -3
  59. data/app/views/components/sidebar_menu/sidebar_menu_component.html.erb +4 -2
  60. data/app/views/components/sidebar_menu/sidebar_menu_component.rb +10 -6
  61. data/app/views/components/sidebar_menu_item/sidebar_menu_item_component.html.erb +63 -71
  62. data/app/views/components/sidebar_menu_item/sidebar_menu_item_component.rb +27 -8
  63. data/app/views/components/skeleton/table/table_component.html.erb +1 -1
  64. data/app/views/components/skeleton/table/table_component.rb +3 -3
  65. data/app/views/components/table/table_component.html.erb +40 -89
  66. data/app/views/components/table/table_component.rb +124 -28
  67. data/app/views/components/table_search_input/table_search_input_component.html.erb +1 -1
  68. data/app/views/components/table_search_input/table_search_input_component.rb +11 -6
  69. data/app/views/components/table_toolbar/table_toolbar_component.html.erb +1 -1
  70. data/app/views/components/table_toolbar/table_toolbar_component.rb +11 -3
  71. data/app/views/components/toolbar/toolbar_component.html.erb +2 -2
  72. data/app/views/components/toolbar/toolbar_component.rb +16 -8
  73. data/app/views/layouts/resource.html copy.erb +2 -2
  74. data/app/views/layouts/resource.html.erb +21 -37
  75. data/app/views/layouts/rodauth.html.erb +32 -25
  76. data/app/views/resource/_interactive_resource_action_form.html.erb +1 -1
  77. data/app/views/resource/_resource_details.html.erb +8 -5
  78. data/app/views/resource/_resource_table.html.erb +70 -1
  79. data/app/views/resource/interactive_resource_collection_action.html.erb +1 -0
  80. data/app/views/resource/interactive_resource_record_action.html.erb +1 -0
  81. data/app/views/resource/interactive_resource_recordless_action.html.erb +1 -0
  82. data/app/views/resource/new.html.erb +1 -0
  83. data/app/views/rodauth/add_recovery_codes.html.erb +8 -7
  84. data/app/views/rodauth/otp_auth.html.erb +1 -1
  85. data/app/views/rodauth/otp_setup.html.erb +10 -8
  86. data/config/initializers/simple_form.rb +22 -2
  87. data/esbuild.config.js +35 -31
  88. data/lib/generators/pu/core/assets/assets_generator.rb +41 -0
  89. data/lib/generators/pu/core/assets/templates/tailwind.config.js +18 -0
  90. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +3 -0
  91. data/lib/generators/pu/core/ruby/ruby_generator.rb +30 -0
  92. data/lib/generators/pu/core/ruby/templates/.keep +0 -0
  93. data/lib/generators/pu/docker/install/install_generator.rb +35 -0
  94. data/lib/generators/pu/docker/install/templates/.keep +0 -0
  95. data/lib/generators/pu/docker/install/templates/Dockerfile.dev.tt +30 -0
  96. data/lib/generators/pu/docker/install/templates/Dockerfile.tt +75 -0
  97. data/lib/generators/pu/docker/install/templates/bin/console +3 -0
  98. data/lib/generators/pu/docker/install/templates/bin/restart +3 -0
  99. data/lib/generators/pu/docker/install/templates/bin/shell +3 -0
  100. data/lib/generators/pu/docker/install/templates/docker-compose.yml +29 -0
  101. data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +32 -0
  102. data/lib/generators/pu/gem/dotenv/templates/.env +6 -0
  103. data/lib/generators/pu/gem/dotenv/templates/.env.local +5 -0
  104. data/lib/generators/pu/gem/dotenv/templates/.env.local.template +5 -0
  105. data/lib/generators/pu/gem/dotenv/templates/.env.template +6 -0
  106. data/lib/generators/pu/gem/dotenv/templates/.keep +0 -0
  107. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +21 -0
  108. data/lib/generators/pu/gem/redis/redis_generator.rb +22 -0
  109. data/lib/generators/pu/gem/redis/templates/.keep +0 -0
  110. data/lib/generators/pu/gen/component/component_generator.rb +13 -10
  111. data/lib/generators/pu/gen/component/templates/component.html.erb.tt +1 -1
  112. data/lib/generators/pu/gen/component/templates/component.rb.tt +10 -4
  113. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +154 -32
  114. data/lib/generators/pu/lib/plutonium_generators/generator.rb +6 -6
  115. data/lib/generators/pu/lib/plutonium_generators/installer.rb +1 -1
  116. data/lib/generators/pu/pkg/app/app_generator.rb +4 -4
  117. data/lib/generators/pu/pkg/app/templates/app/controllers/concerns/controller.rb.tt +28 -0
  118. data/lib/generators/pu/pkg/app/templates/app/controllers/controller.rb.tt +5 -0
  119. data/lib/generators/pu/pkg/app/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  120. data/lib/generators/pu/res/conn/conn_generator.rb +4 -4
  121. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  122. data/lib/generators/pu/res/model/model_generator.rb +3 -3
  123. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +6 -0
  124. data/lib/generators/pu/rodauth/account_generator.rb +10 -10
  125. data/lib/generators/pu/rodauth/install_generator.rb +9 -2
  126. data/lib/generators/pu/rodauth/migration/sequel/audit_logging.erb +2 -2
  127. data/lib/generators/pu/rodauth/migration_generator.rb +1 -1
  128. data/lib/generators/pu/rodauth/templates/app/{misc → rodauth}/account_rodauth_plugin.rb.tt +2 -2
  129. data/lib/generators/pu/rodauth/templates/app/{misc → rodauth}/rodauth_plugin.rb.tt +0 -3
  130. data/lib/generators/pu/rodauth/templates/db/migrate/install_rodauth.rb.tt +5 -0
  131. data/lib/generators/pu/service/postgres/postgres_generator.rb +61 -0
  132. data/lib/generators/pu/service/postgres/templates/.keep +0 -0
  133. data/lib/generators/pu/service/postgres/templates/bin/initdb.d/create-multiple-postgresql-databases.sh +22 -0
  134. data/lib/generators/pu/service/postgres/templates/database.yml.tt +93 -0
  135. data/lib/generators/pu/service/sidekiq/sidekiq_generator.rb +57 -0
  136. data/lib/generators/pu/service/sidekiq/templates/.keep +0 -0
  137. data/lib/generators/pu/service/sidekiq/templates/app/sidekiq/sidekiq_job.rb +3 -0
  138. data/lib/generators/pu/service/sidekiq/templates/config/initializers/sidekiq.rb +53 -0
  139. data/lib/generators/pu/service/sidekiq/templates/config/sidekiq.yml +6 -0
  140. data/lib/plutonium/config.rb +2 -9
  141. data/lib/plutonium/core/associations/renderers/basic_renderer.rb +28 -0
  142. data/lib/plutonium/core/associations/renderers/factory.rb +36 -0
  143. data/lib/plutonium/core/associations/renderers/has_many_renderer.rb +16 -0
  144. data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +31 -0
  145. data/lib/plutonium/core/controllers/authorizable.rb +13 -17
  146. data/lib/plutonium/core/controllers/base.rb +3 -7
  147. data/lib/plutonium/core/controllers/entity_scoping.rb +3 -3
  148. data/lib/plutonium/core/controllers/presentable.rb +6 -1
  149. data/lib/plutonium/core/definers/association_renderer_definer.rb +33 -0
  150. data/lib/plutonium/core/fields/inputs/checkbox_input.rb +13 -0
  151. data/lib/plutonium/core/fields/inputs/factory.rb +1 -0
  152. data/lib/plutonium/core/fields/inputs/polymorphic_belongs_to_association_input.rb +1 -1
  153. data/lib/plutonium/core/ui/detail.rb +1 -0
  154. data/lib/plutonium/helpers/application_helper.rb +8 -9
  155. data/lib/plutonium/helpers/assets_helper.rb +33 -0
  156. data/lib/plutonium/helpers/display_helper.rb +13 -0
  157. data/lib/plutonium/helpers/form_helper.rb +1 -1
  158. data/lib/plutonium/helpers.rb +1 -0
  159. data/lib/plutonium/icons.rb +12 -5
  160. data/lib/plutonium/pkg/app.rb +10 -0
  161. data/lib/plutonium/pundit/context.rb +18 -0
  162. data/lib/plutonium/pundit/policy_finder.rb +25 -0
  163. data/lib/plutonium/railtie.rb +24 -8
  164. data/lib/plutonium/reloader.rb +18 -7
  165. data/lib/plutonium/resource/controller.rb +5 -0
  166. data/lib/plutonium/resource/policy.rb +69 -47
  167. data/lib/plutonium/resource/presenter.rb +1 -0
  168. data/lib/plutonium/resource/query_object.rb +139 -130
  169. data/lib/plutonium/rodauth/controller_methods.rb +7 -3
  170. data/lib/plutonium/version.rb +1 -1
  171. data/lib/plutonium.rb +10 -54
  172. data/lib/tasks/create_rodauth_admin.rake +16 -0
  173. data/package-lock.json +782 -17
  174. data/package.json +31 -8
  175. data/postcss.config.js +17 -7
  176. data/src/.npmignore +2 -0
  177. data/src/js/controllers/color_mode_controller.js +41 -0
  178. data/src/js/controllers/frame_navigator_controller.js +99 -0
  179. data/src/js/controllers/has_many_panel_controller.js +8 -0
  180. data/src/js/controllers/nav_grid_menu_controller.js +8 -0
  181. data/src/js/controllers/nav_grid_menu_item_controller.js +8 -0
  182. data/{app/views/components/tab_bar/tab_bar_controller.js → src/js/controllers/nav_user_controller.js} +2 -2
  183. data/src/js/controllers/nav_user_link_controller.js +8 -0
  184. data/src/js/controllers/nav_user_section_controller.js +8 -0
  185. data/src/js/controllers/register_controllers.js +45 -0
  186. data/{app/assets/javascripts → src/js}/controllers/resource_dismiss_controller.js +2 -0
  187. data/{app/assets/javascripts → src/js}/controllers/resource_drop_down_controller.js +2 -0
  188. data/src/js/controllers/resource_header_controller.js +8 -0
  189. data/src/js/controllers/resource_layout_controller.js +8 -0
  190. data/src/js/controllers/sidebar_menu_controller.js +8 -0
  191. data/src/js/controllers/sidebar_menu_item_controller.js +8 -0
  192. data/src/js/core.js +4 -0
  193. data/{app/assets/javascripts/plutonium-app.js → src/js/plutonium.js} +1 -1
  194. data/{app/assets/javascripts → src/js}/turbo/turbo_debug.js +2 -4
  195. data/tailwind.config.js +85 -84
  196. metadata +106 -41
  197. data/app/assets/build/plutonium.js +0 -5122
  198. data/app/assets/javascripts/controllers/index.js +0 -34
  199. data/app/assets/javascripts/plutonium.js +0 -1
  200. data/app/views/application/_color_modes.html.erb +0 -57
  201. data/app/views/components/tab_bar/tab_bar_component.html.erb +0 -11
  202. data/app/views/components/tab_bar/tab_bar_component.rb +0 -9
  203. data/app/views/resource/_nav_user.html.erb +0 -4
  204. data/app/views/resource/_tab_menu.html.erb +0 -13
  205. data/css.manifest +0 -3
  206. data/js.manifest +0 -4
  207. data/lib/generators/pu/pkg/app/templates/app/controllers/app_controller.rb.tt +0 -5
  208. data/lib/generators/pu/pkg/app/templates/app/controllers/package_controller.rb.tt +0 -26
  209. data/public/plutonium-assets/application.css +0 -25086
  210. data/public/plutonium-assets/plutonium-app-36KN5FVJ.js +0 -6
  211. data/public/plutonium-assets/plutonium-app-36KN5FVJ.js.map +0 -7
  212. data/public/plutonium-assets/plutonium-app-6WILQCTT.js +0 -39
  213. data/public/plutonium-assets/plutonium-app-6WILQCTT.js.map +0 -7
  214. data/public/plutonium-assets/plutonium.2d4f0c333cd000051d3b.css +0 -3424
  215. data/public/plutonium-assets/plutonium.50232e35b5495f5ad90d.css +0 -3415
  216. /data/{app/assets/build → lib/generators/pu/core/assets/templates}/.keep +0 -0
  217. /data/lib/generators/pu/rodauth/templates/app/{misc → rodauth}/rodauth_app.rb.tt +0 -0
  218. /data/{app/assets/stylesheets → src/css}/plutonium.css +0 -0
  219. /data/{app/views/components/form → src/js/controllers}/form_controller.js +0 -0
  220. /data/{app/views/components/interactive_action_form → src/js/controllers}/interactive_action_form_controller.js +0 -0
  221. /data/{app/views/components/nested_resource_form_fields → src/js/controllers}/nested_resource_form_fields_controller.js +0 -0
  222. /data/{app/views/components/table → src/js/controllers}/table_controller.js +0 -0
  223. /data/{app/views/components/table_search_input → src/js/controllers}/table_search_input_controller.js +0 -0
  224. /data/{app/views/components/table_toolbar → src/js/controllers}/table_toolbar_controller.js +0 -0
  225. /data/{app/views/components/toolbar → src/js/controllers}/toolbar_controller.js +0 -0
  226. /data/{app/assets/javascripts → src/js}/turbo/index.js +0 -0
  227. /data/{app/assets/javascripts → src/js}/turbo/turbo_actions.js +0 -0
  228. /data/{app/assets/javascripts → src/js}/turbo/turbo_frame_monkey_patch.js +0 -0
@@ -1,12 +1,14 @@
1
- <%= render_component :sidebar do %>
2
- <%= render_component :sidebar_menu do %>
3
- <%= render_component :sidebar_menu_item, name: "Dashboard", value: root_path %>
4
-
5
- <% resource_items = registered_resources.map { |resource| [resource.model_name.human.pluralize, resource_url_for(resource, parent: nil)] } %>
6
- <%= render_component :sidebar_menu_item, name: "Resources", value: resource_items %>
7
- <% end %>
8
-
9
- <%#= render_component :sidebar_menu, separated: true do %>
10
- <%#= render_component :sidebar_menu_item, name: "Docs", value: "/" %>
11
- <%# end %>
12
- <% end %>
1
+ <%= render_component(:sidebar) do %>
2
+ <%=
3
+ render_component(:sidebar_menu) do |menu|
4
+ menu.with_item(name: "Dashboard", url: root_path, icon: "outline/home")
5
+ if registered_resources.any?
6
+ menu.with_item(name: "Resources", icon: "outline/grid") do |sub_menu|
7
+ registered_resources.each do |resource|
8
+ sub_menu.with_sub_item(name: resource.model_name.human.pluralize, url: resource_url_for(resource, parent: nil))
9
+ end
10
+ end
11
+ end
12
+ end
13
+ %>
14
+ <% end %>
@@ -1,5 +1,5 @@
1
- module Plutonium::Ui
2
- class ActionButtonComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class ActionButtonComponent < PlutoniumUi::Base
3
3
  option :label
4
4
  option :to
5
5
  option :method
@@ -12,4 +12,4 @@ module Plutonium::Ui
12
12
  end
13
13
  end
14
14
 
15
- Plutonium::ComponentRegistry.register :action_button, to: Plutonium::Ui::ActionButtonComponent
15
+ Plutonium::ComponentRegistry.register :action_button, to: PlutoniumUi::ActionButtonComponent
@@ -0,0 +1,184 @@
1
+ module PlutoniumUi
2
+ module Attributes
3
+ extend ActiveSupport::Concern
4
+
5
+ # Provides methods for handling a base set of UI attributes.
6
+ # The attributes include `id`, `data`, `classname`, `controllers`, and `tooltip`.
7
+ #
8
+ # @example Usage
9
+ # class MyComponent < PlutoniumUi::Base
10
+ # include PlutoniumUi::Attributes
11
+ # private
12
+ # # default set of attributes
13
+ # def base_attributes
14
+ # {
15
+ # id: "my-id",
16
+ # classname: "my-class", # classname can be an array or string
17
+ # controller: "my-controller",
18
+ # data: {
19
+ # key1: :value1,
20
+ # key2: :value2
21
+ # },
22
+ # custom_key1: :custom_value1,
23
+ # custom_key2: :custom_value2
24
+ # }
25
+ # end
26
+ # end
27
+ #
28
+ # object = MyComponent.new(
29
+ # id: "my-preferred-id", # takes highest precedence, even over attributes hash
30
+ # classname: ["appended-class1", "appended-class2"], # appends to the class
31
+ # controllers: ["my-custom-controller"],
32
+ # # attributes hash allows us to override values set in base_attributes
33
+ # # hash is merged with attributes taking precedence
34
+ # attributes: {
35
+ # data: {
36
+ # key1: :overridden_value,
37
+ # key3: :attributes_value
38
+ # },
39
+ # custom_key1: :overridden_custom_value,
40
+ # attributes_custom_key1: :attributes_custom_value
41
+ # }
42
+ # )
43
+ # object.attributes_html
44
+ # # => id="my-preferred-id" class="my-class appended-class1 appended-class2" data-key1="overridden_value" data-key2="value2" data-key3="attributes_value" data-controller="my-controller my-custom-controller" custom_key1="overridden_custom_value" custom_key2="custom_value2" attributes_custom_key1="attributes_custom_value"
45
+
46
+ included do
47
+ option :id, optional: true
48
+ # These will be merged with base_attributes
49
+ option :data, default: proc { {} }
50
+ option :classname, optional: true
51
+ option :controllers, optional: true
52
+ option :tooltip, optional: true
53
+ # This will override values set in base_attributes
54
+ option :attributes, default: proc { {} }
55
+ end
56
+
57
+ # Returns a memoized hash of attributes for the current object.
58
+ #
59
+ # The hash includes merged attributes from `base_attributes` and `attributes`,
60
+ # and further processing is done to include specific fields like `id`,
61
+ # `classname`, `controllers`, and `data`. The `data` field is enriched
62
+ # with controller information.
63
+ #
64
+ # @return [Hash] The processed and merged attributes hash.
65
+ #
66
+ # @example Usage
67
+ # object = MyComponent.new
68
+ # attributes = object.attributes_hash
69
+ # # => { id: "some_id", title: "some_tooltip", class: "some_class", data: { controller: "some_controller", key: "value" } }
70
+ def attributes_hash
71
+ @attributes_hash ||= raw_attributes_hash.except(*filtered_attributes)
72
+ end
73
+
74
+ def raw_attributes_hash
75
+ @raw_attributes_hash ||= build_attributes_hash
76
+ end
77
+
78
+ # Generates an HTML-safe string of attributes for the current object.
79
+ #
80
+ # This method uses the `attributes_hash` method to get a hash of attributes
81
+ # and then converts it into an HTML-safe string using the `tag` helper.
82
+ #
83
+ # @return [String] An HTML-safe string of attributes.
84
+ #
85
+ # @example Usage
86
+ # object = MyComponent.new
87
+ # html_attributes = object.attributes_html
88
+ # # => 'id="some_id" title="some_tooltip" class="some_class" data-controller="some_controller" data-key="value"'
89
+ def attributes_html
90
+ convert_attributes_to_html(attributes_hash)
91
+ end
92
+
93
+ private
94
+
95
+ # Returns the base attributes for the object.
96
+ # This can be overridden in subclasses to provide additional attributes.
97
+ #
98
+ # @return [Hash] The base attributes hash.
99
+ def base_attributes
100
+ {}
101
+ end
102
+
103
+ # Returns the list of attributes that should be filtered from #attributes_hash
104
+ # Useful for allowing attributes to be overriden while not rendering them on the dom element.
105
+ #
106
+ # @return [Array] The list of attributes to filter.
107
+ def filtered_attributes
108
+ []
109
+ end
110
+
111
+ # Returns the list of attributes that are we include in our attributes_hash.
112
+ # Can be used in #filtered_attributes to simplify filtering.
113
+ #
114
+ # @return [Array] The list of possible attributes.
115
+ def possible_attributes
116
+ %i[id class data title]
117
+ end
118
+
119
+ # Builds the attributes hash by merging base attributes and custom attributes,
120
+ # and processing specific fields like id, classname, controllers, and data.
121
+ #
122
+ # @return [Hash] The processed and merged attributes hash.
123
+ def build_attributes_hash
124
+ merged_attributes = merge_base_and_custom_attributes
125
+ {
126
+ id: extract_id(merged_attributes),
127
+ title: tooltip,
128
+ class: extract_classname(merged_attributes),
129
+ data: build_data(merged_attributes)
130
+ }.deep_merge(merged_attributes).compact
131
+ end
132
+
133
+ # Merges base attributes with custom attributes.
134
+ #
135
+ # @return [Hash] The merged attributes.
136
+ def merge_base_and_custom_attributes
137
+ base_attributes.deep_merge(attributes)
138
+ end
139
+
140
+ # Extracts the id from the merged attributes or the current object.
141
+ #
142
+ # @param merged_attributes [Hash] The merged attributes hash.
143
+ # @return [Object] The extracted id.
144
+ def extract_id(merged_attributes)
145
+ [id, merged_attributes.delete(:id)].compact.first
146
+ end
147
+
148
+ # Extracts and combines class names from merged attributes and the current object.
149
+ #
150
+ # @param merged_attributes [Hash] The merged attributes hash.
151
+ # @return [String, nil] The combined class names.
152
+ def extract_classname(merged_attributes)
153
+ (Array(merged_attributes.delete(:classname)) + Array(classname)).compact.join(" ").presence
154
+ end
155
+
156
+ # Extracts and combines controller names from merged attributes and the current object.
157
+ #
158
+ # @param merged_attributes [Hash] The merged attributes hash.
159
+ # @return [String, nil] The combined controller names.
160
+ def extract_controllers(merged_attributes)
161
+ (Array(merged_attributes.delete(:controller)) + Array(controllers)).compact.join(" ").presence
162
+ end
163
+
164
+ # Builds the data hash by merging data from merged attributes and the current object,
165
+ # and adding controller information.
166
+ #
167
+ # @param merged_attributes [Hash] The merged attributes hash.
168
+ # @return [Hash] The built data hash.
169
+ def build_data(merged_attributes)
170
+ data = (merged_attributes.delete(:data) || {}).merge(self.data)
171
+ data[:controller] = extract_controllers(merged_attributes)
172
+ data.compact!
173
+ data
174
+ end
175
+
176
+ # Converts a hash of attributes to an HTML-safe string.
177
+ #
178
+ # @param attributes [Hash] The hash of attributes to convert.
179
+ # @return [String] An HTML-safe string of attributes.
180
+ def convert_attributes_to_html(attributes)
181
+ tag.attributes(attributes)
182
+ end
183
+ end
184
+ end
@@ -1,50 +1,29 @@
1
1
  require "view_component"
2
2
  require "dry-initializer"
3
-
4
- module Plutonium::Ui
3
+ require "active_support/notifications"
4
+
5
+ load File.join(__dir__, "attributes.rb")
6
+
7
+ module PlutoniumUi
8
+ # Base class for all Plutonium UI components.
9
+ # This class inherits from `ViewComponent::Base` and includes additional functionality
10
+ # provided by `Dry::Initializer` for managing component options, and the
11
+ # `PlutoniumUi::Attributes` module for handling UI attributes.
12
+ #
13
+ # It also includes `Plutonium::Helpers::ComponentHelper` for additional helper methods
14
+ # and delegates missing methods to the Rails helpers.
5
15
  class Base < ViewComponent::Base
6
16
  extend Dry::Initializer
7
17
  include Plutonium::Helpers::ComponentHelper
18
+ include PlutoniumUi::Attributes
8
19
 
9
20
  delegate_missing_to :helpers
10
-
11
- option :id, optional: true
12
- option :data, default: proc { {} }
13
- option :classname, optional: true
14
- option :tooltip, optional: true
15
- option :attributes, default: proc { {} }
16
-
17
- private
18
-
19
- def base_classname = nil
20
-
21
- def merged_classname
22
- [base_classname, classname].compact.join.presence
23
- end
24
-
25
- def component_attributes
26
- {id:, data:, class: merged_classname, title: tooltip}.merge(attributes).compact
27
- end
28
-
29
- def render_component_attributes
30
- attributes_to_string(component_attributes).html_safe
31
- end
32
-
33
- def render_icon(icon)
34
- Plutonium::Icons.render(icon).html_safe
35
- end
36
-
37
- def attributes_to_string(attributes, prefix = nil)
38
- attributes.map do |key, value|
39
- if value.is_a?(Hash)
40
- attributes_to_string(value, "#{prefix ? "#{prefix}-" : ""}#{key}")
41
- else
42
- "#{prefix ? "#{prefix}-" : ""}#{key}=\"#{value}\""
43
- end
44
- end.join(" ")
45
- end
46
21
  end
47
22
  end
48
23
 
49
- # Require components
50
- Dir.glob(File.expand_path("**/*.rb", __dir__)) { |component| load component unless component == __FILE__ }
24
+ ActiveSupport::Notifications.instrument("plutonium.components.load") do
25
+ # Require all component files within the same directory and subdirectories
26
+ Dir.glob(File.expand_path("**/*.rb", __dir__)) do |component_file_path|
27
+ load component_file_path unless component_file_path == __FILE__
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
- <div <%= render_component_attributes %>>
1
+ <div <%= attributes_html %>>
2
2
  <%= content %>
3
3
  </div>
@@ -1,14 +1,18 @@
1
- module Plutonium::Ui
2
- class BlockComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class BlockComponent < PlutoniumUi::Base
3
3
  option :rounded, optional: true
4
4
  option :scroll, optional: true
5
5
 
6
6
  private
7
7
 
8
- def classname
9
- classnames = ["relative bg-white dark:bg-gray-800 shadow-md", rounded_classes, scroll_classes]
10
- classnames << super.presence
11
- classnames.compact.join " "
8
+ def base_attributes
9
+ {
10
+ classname: classnames
11
+ }
12
+ end
13
+
14
+ def classnames
15
+ ["relative bg-white dark:bg-gray-800 shadow-md", rounded_classes, scroll_classes]
12
16
  end
13
17
 
14
18
  def scroll_classes
@@ -37,4 +41,4 @@ module Plutonium::Ui
37
41
  end
38
42
  end
39
43
 
40
- Plutonium::ComponentRegistry.register :block, to: Plutonium::Ui::BlockComponent
44
+ Plutonium::ComponentRegistry.register :block, to: PlutoniumUi::BlockComponent
@@ -1,9 +1,9 @@
1
- module Plutonium::Ui
2
- class BreadcrumbsComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class BreadcrumbsComponent < PlutoniumUi::Base
3
3
  option :resource_class
4
4
  option :parent, optional: true
5
5
  option :resource, optional: true
6
6
  end
7
7
  end
8
8
 
9
- Plutonium::ComponentRegistry.register :breadcrumbs, to: Plutonium::Ui::BreadcrumbsComponent
9
+ Plutonium::ComponentRegistry.register :breadcrumbs, to: PlutoniumUi::BreadcrumbsComponent
@@ -1,10 +1,10 @@
1
1
  <% if to %>
2
- <%= link_to to, role: "button", **component_attributes do %>
2
+ <%= link_to to, role: "button", **attributes_hash do %>
3
3
  <%= render_icon icon if icon %>
4
4
  <span><%= label %></span>
5
5
  <% end %>
6
6
  <% else %>
7
- <button <%= render_component_attributes %> type="<%= type %>" %>
7
+ <button <%= attributes_html %> type="<%= type %>" %>
8
8
  <%= render_icon icon if icon %>
9
9
  <span><%= label %></span>
10
10
  </button>
@@ -1,5 +1,5 @@
1
- module Plutonium::Ui
2
- class ButtonComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class ButtonComponent < PlutoniumUi::Base
3
3
  option :label
4
4
  option :to, optional: true
5
5
  option :icon, optional: true
@@ -10,11 +10,16 @@ module Plutonium::Ui
10
10
 
11
11
  private
12
12
 
13
- def classname
13
+ def base_attributes
14
+ {
15
+ classname: classnames
16
+ }
17
+ end
18
+
19
+ def classnames
14
20
  classnames = ["text-center py-2.5 font-medium px-5", color_classes, shape_classes, size_classes]
15
21
  classnames << "flex items-center justify-center"
16
22
  classnames << "space-x-1" if icon.present? && label.present?
17
- classnames << super.presence
18
23
  classnames.join " "
19
24
  end
20
25
 
@@ -120,4 +125,4 @@ module Plutonium::Ui
120
125
  end
121
126
  end
122
127
 
123
- Plutonium::ComponentRegistry.register :button, to: Plutonium::Ui::ButtonComponent
128
+ Plutonium::ComponentRegistry.register :button, to: PlutoniumUi::ButtonComponent
@@ -1,5 +1,6 @@
1
1
  <% if current_turbo_frame.present? %>
2
2
  <%= turbo_frame_tag current_turbo_frame do %>
3
+ <%= render "flash" %>
3
4
  <%= content %>
4
5
  <% end %>
5
6
  <% else %>
@@ -1,6 +1,6 @@
1
- module Plutonium::Ui
2
- class DynaFrameContentComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class DynaFrameContentComponent < PlutoniumUi::Base
3
3
  end
4
4
  end
5
5
 
6
- Plutonium::ComponentRegistry.register :dyna_frame_content, to: Plutonium::Ui::DynaFrameContentComponent
6
+ Plutonium::ComponentRegistry.register :dyna_frame_content, to: PlutoniumUi::DynaFrameContentComponent
@@ -1,4 +1,3 @@
1
-
2
- <%= turbo_frame_tag id, src: do %>
1
+ <%= turbo_frame_tag attributes_hash.delete(:id), **attributes_hash do %>
3
2
  <%= content %>
4
3
  <% end %>
@@ -1,11 +1,18 @@
1
- module Plutonium::Ui
2
- class DynaFrameHostComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class DynaFrameHostComponent < PlutoniumUi::Base
3
3
  option :src
4
+ option :loading
4
5
 
5
- def id
6
- super || SecureRandom.hex
6
+ private
7
+
8
+ def base_attributes
9
+ {
10
+ id: SecureRandom.hex,
11
+ src:,
12
+ loading:
13
+ }
7
14
  end
8
15
  end
9
16
  end
10
17
 
11
- Plutonium::ComponentRegistry.register :dyna_frame_host, to: Plutonium::Ui::DynaFrameHostComponent
18
+ Plutonium::ComponentRegistry.register :dyna_frame_host, to: PlutoniumUi::DynaFrameHostComponent
@@ -1,7 +1,7 @@
1
- module Plutonium::Ui
2
- class EmptyCardComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class EmptyCardComponent < PlutoniumUi::Base
3
3
  option :message
4
4
  end
5
5
  end
6
6
 
7
- Plutonium::ComponentRegistry.register :empty_card, to: Plutonium::Ui::EmptyCardComponent
7
+ Plutonium::ComponentRegistry.register :empty_card, to: PlutoniumUi::EmptyCardComponent
@@ -1,6 +1,6 @@
1
1
  require "simple_form"
2
2
 
3
- module Plutonium::Ui
3
+ module PlutoniumUi
4
4
  class FormBuilder < SimpleForm::FormBuilder
5
5
  def input(attribute_name, options = {}, &block)
6
6
  label_class = options.dig(:label_html, :class)
@@ -1,5 +1,5 @@
1
- module Plutonium::Ui
2
- class FormComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class FormComponent < PlutoniumUi::Base
3
3
  option :form
4
4
  option :preferred_action_after_submit, default: proc { "show" }
5
5
 
@@ -20,4 +20,4 @@ module Plutonium::Ui
20
20
  end
21
21
  end
22
22
 
23
- Plutonium::ComponentRegistry.register :form, to: Plutonium::Ui::FormComponent
23
+ Plutonium::ComponentRegistry.register :form, to: PlutoniumUi::FormComponent
@@ -0,0 +1,25 @@
1
+ <div <%= attributes_html %>>
2
+ <%= render_component :panel, title: do |panel| %>
3
+ <% panel.with_action do %>
4
+ <button title="Back" class="mr-2 text-gray-600 dark:text-gray-300" style="display:none" data-frame-navigator-target="backButton">
5
+ <%= render_icon "outline/chevron-left", size: :xl %>
6
+ </button>
7
+ <% end %>
8
+
9
+ <% panel.with_action do %>
10
+ <button title="Home" class="mr-2 text-gray-600 dark:text-gray-300" style="display:none" data-frame-navigator-target="homeButton">
11
+ <%= render_icon "outline/home", size: :xl %>
12
+ </button>
13
+ <% end %>
14
+
15
+ <% panel.with_action do %>
16
+ <button title="Refresh" data-frame-navigator-target="refreshButton" style="display:none" class="text-gray-600 dark:text-gray-300">
17
+ <%= render_icon "outline/refresh", size: :xl %>
18
+ </button>
19
+ <% end %>
20
+
21
+ <%= render_component :dyna_frame_host, loading: :lazy, data: { "frame-navigator-target": "frame" }, src: do %>
22
+ <%= render_component :skeleton__table %>
23
+ <% end %>
24
+ <% end %>
25
+ </div>
@@ -0,0 +1,16 @@
1
+ module PlutoniumUi
2
+ class HasManyPanelComponent < PlutoniumUi::Base
3
+ option :title
4
+ option :src
5
+
6
+ private
7
+
8
+ def base_attributes
9
+ {
10
+ controller: %w[has-many-panel frame-navigator]
11
+ }
12
+ end
13
+ end
14
+ end
15
+
16
+ Plutonium::ComponentRegistry.register :has_many_panel, to: PlutoniumUi::HasManyPanelComponent
@@ -1,7 +1,7 @@
1
- module Plutonium::Ui
2
- class HeaderComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class HeaderComponent < PlutoniumUi::Base
3
3
  option :title
4
4
  end
5
5
  end
6
6
 
7
- Plutonium::ComponentRegistry.register :header, to: Plutonium::Ui::HeaderComponent
7
+ Plutonium::ComponentRegistry.register :header, to: PlutoniumUi::HeaderComponent
@@ -1,8 +1,8 @@
1
- module Plutonium::Ui
2
- class InteractiveActionFormComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class InteractiveActionFormComponent < PlutoniumUi::Base
3
3
  option :interaction
4
4
  option :interactive_action
5
5
  end
6
6
  end
7
7
 
8
- Plutonium::ComponentRegistry.register :interactive_action_form, to: Plutonium::Ui::InteractiveActionFormComponent
8
+ Plutonium::ComponentRegistry.register :interactive_action_form, to: PlutoniumUi::InteractiveActionFormComponent
@@ -0,0 +1,24 @@
1
+ <div <%= attributes_html %>>
2
+ <button
3
+ type="button"
4
+ data-resource-drop-down-target="trigger"
5
+ class="p-2 text-gray-500 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600">
6
+ <span class="sr-only">View <%= label %></span>
7
+ <!-- Icon -->
8
+ <%= render_icon "solid/grid", size: :lg %>
9
+ </button>
10
+ <!-- Dropdown menu -->
11
+ <div
12
+ class="hidden overflow-hidden z-50 my-4 max-w-sm text-base list-none bg-white divide-y divide-gray-100 shadow-lg dark:bg-gray-700 dark:divide-gray-600 rounded-xl"
13
+ data-resource-drop-down-target="menu">
14
+ <div
15
+ class="block py-2 px-4 text-base font-medium text-center text-gray-700 bg-gray-50 dark:bg-gray-600 dark:text-gray-300">
16
+ <%= label %>
17
+ </div>
18
+ <div class="grid grid-cols-3 gap-4 p-4">
19
+ <% items.each do |item| %>
20
+ <%= item %>
21
+ <% end %>
22
+ </div>
23
+ </div>
24
+ </div>
@@ -0,0 +1,23 @@
1
+ module PlutoniumUi
2
+ class NavGridMenuComponent < PlutoniumUi::Base
3
+ renders_many :items, "PlutoniumUi::NavGridMenuItemComponent"
4
+
5
+ option :label
6
+
7
+ private
8
+
9
+ def base_attributes
10
+ # base attributes go here
11
+ {
12
+ classname: "nav-grid-menu",
13
+ controller: "nav-grid-menu resource-drop-down"
14
+ }
15
+ end
16
+
17
+ def render?
18
+ items.any?
19
+ end
20
+ end
21
+ end
22
+
23
+ Plutonium::ComponentRegistry.register :nav_grid_menu, to: PlutoniumUi::NavGridMenuComponent
@@ -0,0 +1,4 @@
1
+ <a <%= attributes_html %>>
2
+ <%= render_icon icon, size: :xl, classname: "text-gray-400 group-hover:text-gray-500 dark:text-gray-200 dark:group-hover:text-gray-400" %>
3
+ <div class="text-sm text-gray-900 dark:text-white"><%= name %></div>
4
+ </a>
@@ -0,0 +1,20 @@
1
+ module PlutoniumUi
2
+ class NavGridMenuItemComponent < PlutoniumUi::Base
3
+ option :name
4
+ option :icon
5
+ option :url, as: :href
6
+
7
+ private
8
+
9
+ def base_attributes
10
+ # base attributes go here
11
+ {
12
+ classname: "nav-grid-menu-item block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group",
13
+ controller: "nav-grid-menu-item",
14
+ href:
15
+ }
16
+ end
17
+ end
18
+ end
19
+
20
+ Plutonium::ComponentRegistry.register :nav_grid_menu_item, to: PlutoniumUi::NavGridMenuItemComponent