plutonium 0.10.3 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -2
  3. data/app/assets/application.js.bk +31419 -0
  4. data/app/assets/plutonium-original.png +0 -0
  5. data/app/assets/plutonium-white.png +0 -0
  6. data/app/assets/plutonium.css +1 -0
  7. data/app/assets/plutonium.ico +0 -0
  8. data/app/assets/plutonium.js +12416 -0
  9. data/app/assets/plutonium.js.map +7 -0
  10. data/app/assets/plutonium.min.js +39 -0
  11. data/app/assets/plutonium.min.js.map +7 -0
  12. data/app/assets/plutonium.png +0 -0
  13. data/app/views/application/_flash_alerts.html.erb +2 -2
  14. data/app/views/application/_resource_header.html.erb +261 -697
  15. data/app/views/application/_resource_sidebar.html.erb +14 -12
  16. data/app/views/components/action_button/action_button_component.rb +3 -3
  17. data/app/views/components/attributes.rb +184 -0
  18. data/app/views/components/base.rb +19 -40
  19. data/app/views/components/block/block_component.html.erb +1 -1
  20. data/app/views/components/block/block_component.rb +11 -7
  21. data/app/views/components/breadcrumbs/breadcrumbs_component.rb +3 -3
  22. data/app/views/components/button/button_component.html.erb +2 -2
  23. data/app/views/components/button/button_component.rb +10 -5
  24. data/app/views/components/dyna_frame_content/dyna_frame_content_component.html.erb +1 -0
  25. data/app/views/components/dyna_frame_content/dyna_frame_content_component.rb +3 -3
  26. data/app/views/components/dyna_frame_host/dyna_frame_host_component.html.erb +1 -2
  27. data/app/views/components/dyna_frame_host/dyna_frame_host_component.rb +12 -5
  28. data/app/views/components/empty_card/empty_card_component.rb +3 -3
  29. data/app/views/components/form/form_builder.rb +1 -1
  30. data/app/views/components/form/form_component.rb +3 -3
  31. data/app/views/components/has_many_panel/has_many_panel_component.html.erb +25 -0
  32. data/app/views/components/has_many_panel/has_many_panel_component.rb +16 -0
  33. data/app/views/components/header/header_component.rb +3 -3
  34. data/app/views/components/interactive_action_form/interactive_action_form_component.rb +3 -3
  35. data/app/views/components/nav_grid_menu/nav_grid_menu_component.html.erb +24 -0
  36. data/app/views/components/nav_grid_menu/nav_grid_menu_component.rb +23 -0
  37. data/app/views/components/nav_grid_menu_item/nav_grid_menu_item_component.html.erb +4 -0
  38. data/app/views/components/nav_grid_menu_item/nav_grid_menu_item_component.rb +20 -0
  39. data/app/views/components/nav_user/nav_user_component.html.erb +50 -0
  40. data/app/views/components/nav_user/nav_user_component.rb +32 -0
  41. data/app/views/components/nav_user_link/nav_user_link_component.html.erb +7 -0
  42. data/app/views/components/nav_user_link/nav_user_link_component.rb +23 -0
  43. data/app/views/components/nav_user_section/nav_user_section_component.html.erb +7 -0
  44. data/app/views/components/nav_user_section/nav_user_section_component.rb +18 -0
  45. data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.html.erb +1 -3
  46. data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.rb +11 -5
  47. data/app/views/components/pagination/pagination_component.html.erb +1 -1
  48. data/app/views/components/pagination/pagination_component.rb +10 -7
  49. data/app/views/components/panel/panel_component.html.erb +13 -6
  50. data/app/views/components/panel/panel_component.rb +11 -5
  51. data/app/views/components/resource_header/resource_header_component.html.erb +83 -0
  52. data/app/views/components/resource_header/resource_header_component.rb +30 -0
  53. data/app/views/components/resource_layout/resource_layout_component.html.erb +49 -0
  54. data/app/views/components/resource_layout/resource_layout_component.rb +41 -0
  55. data/app/views/components/sidebar/sidebar_component.html.erb +3 -33
  56. data/app/views/components/sidebar/sidebar_component.rb +3 -3
  57. data/app/views/components/sidebar_menu/sidebar_menu_component.html.erb +4 -2
  58. data/app/views/components/sidebar_menu/sidebar_menu_component.rb +10 -6
  59. data/app/views/components/sidebar_menu_item/sidebar_menu_item_component.html.erb +63 -71
  60. data/app/views/components/sidebar_menu_item/sidebar_menu_item_component.rb +27 -8
  61. data/app/views/components/skeleton/table/table_component.html.erb +1 -1
  62. data/app/views/components/skeleton/table/table_component.rb +3 -3
  63. data/app/views/components/table/table_component.html.erb +40 -89
  64. data/app/views/components/table/table_component.rb +124 -28
  65. data/app/views/components/table_search_input/table_search_input_component.html.erb +1 -1
  66. data/app/views/components/table_search_input/table_search_input_component.rb +11 -6
  67. data/app/views/components/table_toolbar/table_toolbar_component.html.erb +1 -1
  68. data/app/views/components/table_toolbar/table_toolbar_component.rb +11 -3
  69. data/app/views/components/toolbar/toolbar_component.html.erb +2 -2
  70. data/app/views/components/toolbar/toolbar_component.rb +16 -8
  71. data/app/views/layouts/resource.html.erb +12 -45
  72. data/app/views/layouts/rodauth.html.erb +20 -36
  73. data/app/views/resource/_interactive_resource_action_form.html.erb +1 -1
  74. data/app/views/resource/_resource_details.html.erb +8 -5
  75. data/app/views/resource/_resource_table.html.erb +70 -1
  76. data/app/views/resource/interactive_resource_collection_action.html.erb +1 -0
  77. data/app/views/resource/interactive_resource_record_action.html.erb +1 -0
  78. data/app/views/resource/interactive_resource_recordless_action.html.erb +1 -0
  79. data/app/views/resource/new.html.erb +1 -0
  80. data/config/initializers/simple_form.rb +22 -2
  81. data/esbuild.config.js +35 -31
  82. data/lib/generators/pu/core/assets/assets_generator.rb +44 -0
  83. data/lib/generators/pu/core/assets/templates/tailwind.config.js +18 -0
  84. data/lib/generators/pu/core/install/install_generator.rb +4 -1
  85. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +6 -0
  86. data/lib/generators/pu/gen/component/component_generator.rb +13 -10
  87. data/lib/generators/pu/gen/component/templates/component.html.erb.tt +1 -1
  88. data/lib/generators/pu/gen/component/templates/component.rb.tt +10 -4
  89. data/lib/generators/pu/pkg/app/app_generator.rb +4 -4
  90. data/lib/generators/pu/pkg/app/templates/app/controllers/concerns/controller.rb.tt +28 -0
  91. data/lib/generators/pu/pkg/app/templates/app/controllers/controller.rb.tt +5 -0
  92. data/lib/generators/pu/pkg/app/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  93. data/lib/generators/pu/res/conn/conn_generator.rb +4 -4
  94. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  95. data/lib/generators/pu/res/model/model_generator.rb +3 -3
  96. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +6 -0
  97. data/lib/generators/pu/service/sidekiq/sidekiq_generator.rb +0 -5
  98. data/lib/generators/pu/service/sidekiq/templates/app/sidekiq/sidekiq_job.rb +0 -2
  99. data/lib/plutonium/config.rb +2 -14
  100. data/lib/plutonium/core/associations/renderers/basic_renderer.rb +28 -0
  101. data/lib/plutonium/core/associations/renderers/factory.rb +36 -0
  102. data/lib/plutonium/core/associations/renderers/has_many_renderer.rb +16 -0
  103. data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +31 -0
  104. data/lib/plutonium/core/controllers/authorizable.rb +13 -17
  105. data/lib/plutonium/core/controllers/base.rb +3 -7
  106. data/lib/plutonium/core/controllers/presentable.rb +6 -1
  107. data/lib/plutonium/core/definers/association_renderer_definer.rb +33 -0
  108. data/lib/plutonium/core/fields/inputs/checkbox_input.rb +13 -0
  109. data/lib/plutonium/core/fields/inputs/factory.rb +1 -0
  110. data/lib/plutonium/core/fields/inputs/polymorphic_belongs_to_association_input.rb +1 -1
  111. data/lib/plutonium/core/ui/detail.rb +1 -0
  112. data/lib/plutonium/helpers/application_helper.rb +8 -9
  113. data/lib/plutonium/helpers/assets_helper.rb +41 -0
  114. data/lib/plutonium/helpers/display_helper.rb +13 -0
  115. data/lib/plutonium/helpers/form_helper.rb +1 -1
  116. data/lib/plutonium/helpers.rb +1 -0
  117. data/lib/plutonium/icons.rb +12 -5
  118. data/lib/plutonium/pkg/app.rb +10 -0
  119. data/lib/plutonium/pundit/context.rb +18 -0
  120. data/lib/plutonium/pundit/policy_finder.rb +25 -0
  121. data/lib/plutonium/railtie.rb +26 -8
  122. data/lib/plutonium/reloader.rb +18 -7
  123. data/lib/plutonium/resource/controller.rb +4 -0
  124. data/lib/plutonium/resource/policy.rb +69 -47
  125. data/lib/plutonium/resource/presenter.rb +1 -0
  126. data/lib/plutonium/resource/query_object.rb +139 -130
  127. data/lib/plutonium/rodauth/controller_methods.rb +7 -3
  128. data/lib/plutonium/version.rb +1 -1
  129. data/lib/plutonium.rb +9 -57
  130. data/package-lock.json +782 -17
  131. data/package.json +31 -8
  132. data/postcss.config.js +17 -7
  133. data/src/.npmignore +2 -0
  134. data/src/js/controllers/color_mode_controller.js +41 -0
  135. data/src/js/controllers/frame_navigator_controller.js +99 -0
  136. data/src/js/controllers/has_many_panel_controller.js +8 -0
  137. data/src/js/controllers/nav_grid_menu_controller.js +8 -0
  138. data/src/js/controllers/nav_grid_menu_item_controller.js +8 -0
  139. data/{app/views/components/tab_bar/tab_bar_controller.js → src/js/controllers/nav_user_controller.js} +2 -2
  140. data/src/js/controllers/nav_user_link_controller.js +8 -0
  141. data/src/js/controllers/nav_user_section_controller.js +8 -0
  142. data/src/js/controllers/register_controllers.js +45 -0
  143. data/{app/assets/javascripts → src/js}/controllers/resource_dismiss_controller.js +2 -0
  144. data/{app/assets/javascripts → src/js}/controllers/resource_drop_down_controller.js +2 -0
  145. data/src/js/controllers/resource_header_controller.js +8 -0
  146. data/src/js/controllers/resource_layout_controller.js +8 -0
  147. data/src/js/controllers/sidebar_menu_controller.js +8 -0
  148. data/src/js/controllers/sidebar_menu_item_controller.js +8 -0
  149. data/src/js/core.js +4 -0
  150. data/{app/assets/javascripts/plutonium-app.js → src/js/plutonium.js} +1 -1
  151. data/{app/assets/javascripts → src/js}/turbo/turbo_debug.js +2 -4
  152. data/tailwind.config.js +85 -84
  153. metadata +73 -39
  154. data/app/assets/build/plutonium.js +0 -5122
  155. data/app/assets/javascripts/controllers/index.js +0 -34
  156. data/app/assets/javascripts/plutonium.js +0 -1
  157. data/app/views/application/_color_modes.html.erb +0 -57
  158. data/app/views/components/tab_bar/tab_bar_component.html.erb +0 -11
  159. data/app/views/components/tab_bar/tab_bar_component.rb +0 -9
  160. data/app/views/resource/_nav_user.html.erb +0 -4
  161. data/app/views/resource/_tab_menu.html.erb +0 -13
  162. data/css.manifest +0 -3
  163. data/js.manifest +0 -4
  164. data/lib/generators/pu/pkg/app/templates/app/controllers/app_controller.rb.tt +0 -5
  165. data/lib/generators/pu/pkg/app/templates/app/controllers/package_controller.rb.tt +0 -26
  166. data/public/plutonium-assets/application.css +0 -25086
  167. data/public/plutonium-assets/plutonium-app-36KN5FVJ.js +0 -6
  168. data/public/plutonium-assets/plutonium-app-36KN5FVJ.js.map +0 -7
  169. data/public/plutonium-assets/plutonium-app-6WILQCTT.js +0 -39
  170. data/public/plutonium-assets/plutonium-app-6WILQCTT.js.map +0 -7
  171. data/public/plutonium-assets/plutonium.2d4f0c333cd000051d3b.css +0 -3424
  172. data/public/plutonium-assets/plutonium.50232e35b5495f5ad90d.css +0 -3415
  173. data/public/plutonium-assets/plutonium.8bee7a8482988b0360e3.css +0 -3420
  174. /data/{app/assets/build → lib/generators/pu/core/assets/templates}/.keep +0 -0
  175. /data/{app/assets/stylesheets → src/css}/plutonium.css +0 -0
  176. /data/{app/views/components/form → src/js/controllers}/form_controller.js +0 -0
  177. /data/{app/views/components/interactive_action_form → src/js/controllers}/interactive_action_form_controller.js +0 -0
  178. /data/{app/views/components/nested_resource_form_fields → src/js/controllers}/nested_resource_form_fields_controller.js +0 -0
  179. /data/{app/views/components/table → src/js/controllers}/table_controller.js +0 -0
  180. /data/{app/views/components/table_search_input → src/js/controllers}/table_search_input_controller.js +0 -0
  181. /data/{app/views/components/table_toolbar → src/js/controllers}/table_toolbar_controller.js +0 -0
  182. /data/{app/views/components/toolbar → src/js/controllers}/toolbar_controller.js +0 -0
  183. /data/{app/assets/javascripts → src/js}/turbo/index.js +0 -0
  184. /data/{app/assets/javascripts → src/js}/turbo/turbo_actions.js +0 -0
  185. /data/{app/assets/javascripts → src/js}/turbo/turbo_frame_monkey_patch.js +0 -0
@@ -1,72 +1,64 @@
1
- <% if value.is_a?(String) %>
2
- <li>
3
- <a
4
- href="<%= value %>"
5
- class="<%= link_button_class %>"
6
- >
7
- <svg
8
- aria-hidden="true"
9
- class="w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-200 group-hover:text-gray-900 dark:group-hover:text-white"
10
- fill="currentColor"
11
- viewBox="0 0 20 20"
12
- xmlns="http://www.w3.org/2000/svg"
13
- >
14
- <path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path>
15
- <path d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"></path>
16
- </svg>
17
- <span class="<%= link_label_class %>"><%= name %></span>
18
- <% if indicator.present? %>
19
- <span class="inline-flex justify-center items-center w-5 h-5 text-xs font-semibold rounded-full text-primary-800 bg-primary-100 dark:bg-primary-200 dark:text-primary-800">
20
- <%= indicator %>
21
- </span>
22
- <% end %>
23
- </a>
1
+ <li <%= attributes_html %>>
2
+ <% if sub_items.any? %>
3
+ <button
4
+ type="button"
5
+ class="<%= link_button_class %> w-full transition duration-75"
6
+ data-resource-drop-down-target="trigger"
7
+ aria-controls="dropdown-<%= name.parameterize %>">
8
+ <% if icon.present? %>
9
+ <%= render_icon icon, size: :lg %>
10
+ <% else %>
11
+ <div class="shrink-0 w-6 h-6">
12
+ &nbsp;
13
+ </div>
14
+ <% end %>
15
+ <span class="<%= link_label_class %>"><%= name %></span>
16
+ <svg
17
+ aria-hidden="true"
18
+ class="w-6 h-6"
19
+ fill="currentColor"
20
+ viewBox="0 0 20 20"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ >
23
+ <path
24
+ fill-rule="evenodd"
25
+ d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
26
+ clip-rule="evenodd"
27
+ ></path>
28
+ </svg>
29
+ </button>
30
+
31
+ <ul id="dropdown-<%= name.parameterize %>" data-resource-drop-down-target="menu" class="hidden py-2 space-y-2">
32
+ <% sub_items.each do |sub_item| %>
33
+ <%#= sub_item %>
34
+ <li>
35
+ <a
36
+ href="<%= sub_item.url %>"
37
+ class="<%= sub_link_button_class %>">
38
+ <%= sub_item.name %>
39
+ </a>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+ <% else %>
44
+ <a
45
+ href="<%= url %>"
46
+ class="<%= link_button_class %>">
47
+ <% if icon.present? %>
48
+ <%= render_icon icon, size: :lg %>
49
+ <% else %>
50
+ <div class="shrink-0 w-6 h-6">
51
+ &nbsp;
52
+ </div>
53
+ <% end %>
54
+ <span class="<%= link_label_class %>"><%= name %></span>
55
+ <%#
56
+ <% if indicator.present? % >
57
+ <span class="inline-flex justify-center items-center w-5 h-5 text-xs font-semibold rounded-full text-primary-800 bg-primary-100 dark:bg-primary-200 dark:text-primary-800">
58
+ <%= indicator % >
59
+ </span>
60
+ <% end % >
61
+ %>
62
+ </a>
63
+ <% end %>
24
64
  </li>
25
- <% else %>
26
- <li>
27
- <button
28
- type="button"
29
- class="<%= link_button_class %> w-full transition duration-75"
30
- aria-controls="dropdown-<%= name.parameterize %>"
31
- data-collapse-toggle="dropdown-<%= name.parameterize %>"
32
- >
33
- <svg
34
- aria-hidden="true"
35
- class="shrink-0 w-6 h-6 text-gray-500 transition duration-75 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white"
36
- fill="currentColor"
37
- viewBox="0 0 20 20"
38
- xmlns="http://www.w3.org/2000/svg"
39
- >
40
- <path
41
- fill-rule="evenodd"
42
- d="M10 2a4 4 0 00-4 4v1H5a1 1 0 00-.994.89l-1 9A1 1 0 004 18h12a1 1 0 00.994-1.11l-1-9A1 1 0 0015 7h-1V6a4 4 0 00-4-4zm2 5V6a2 2 0 10-4 0v1h4zm-6 3a1 1 0 112 0 1 1 0 01-2 0zm7-1a1 1 0 100 2 1 1 0 000-2z"
43
- clip-rule="evenodd"
44
- ></path>
45
- </svg>
46
- <span class="<%= link_label_class %>"><%= name %></span>
47
- <svg
48
- aria-hidden="true"
49
- class="w-6 h-6"
50
- fill="currentColor"
51
- viewBox="0 0 20 20"
52
- xmlns="http://www.w3.org/2000/svg"
53
- >
54
- <path
55
- fill-rule="evenodd"
56
- d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
57
- clip-rule="evenodd"
58
- ></path>
59
- </svg>
60
- </button>
61
- <ul id="dropdown-<%= name.parameterize %>" class="hidden py-2 space-y-2">
62
- <% value.each do |name, value| %>
63
- <li>
64
- <a
65
- href="<%= value %>"
66
- class="<%= sub_link_button_class %>"
67
- ><%= name %></a>
68
- </li>
69
- <% end %>
70
- </ul>
71
- </li>
72
- <% end %>
@@ -1,23 +1,42 @@
1
- module Plutonium::Ui
2
- class SidebarMenuItemComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class SidebarMenuItemComponent < PlutoniumUi::Base
3
+ renders_many :sub_items, "::PlutoniumUi::SidebarMenuItemComponent"
4
+
3
5
  option :name
4
- option :value
5
- option :indicator, optional: true
6
+ option :url, default: -> { "" }
7
+ option :options, default: -> { {} }
8
+ option :icon, optional: true
6
9
 
7
10
  private
8
11
 
12
+ def base_attributes
13
+ # base attributes go here
14
+ {
15
+ id: "sidebar-menu-item-#{name.parameterize}",
16
+ classname: "sidebar-menu-item",
17
+ controller: ["sidebar-menu-item", sub_items.any? ? "resource-drop-down" : nil],
18
+ link_button_class: "group flex items-center rounded-lg p-2 text-base font-medium text-gray-900 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700",
19
+ link_label_class: "flex-1 ml-3 text-left whitespace-nowrap",
20
+ sub_link_button_class: "flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
21
+ }
22
+ end
23
+
24
+ def filtered_attributes
25
+ %i[link_button_class link_label_class sub_link_button_class]
26
+ end
27
+
9
28
  def link_button_class
10
- "group flex items-center rounded-lg p-2 text-base font-medium text-gray-900 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
29
+ raw_attributes_hash[:link_button_class]
11
30
  end
12
31
 
13
32
  def link_label_class
14
- "flex-1 ml-3 text-left whitespace-nowrap"
33
+ raw_attributes_hash[:link_label_class]
15
34
  end
16
35
 
17
36
  def sub_link_button_class
18
- "flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
37
+ raw_attributes_hash[:sub_link_button_class]
19
38
  end
20
39
  end
21
40
  end
22
41
 
23
- Plutonium::ComponentRegistry.register :sidebar_menu_item, to: Plutonium::Ui::SidebarMenuItemComponent
42
+ Plutonium::ComponentRegistry.register :sidebar_menu_item, to: PlutoniumUi::SidebarMenuItemComponent
@@ -1,5 +1,5 @@
1
1
 
2
- <div role="status" class="p-4 space-y-4 border border-gray-200 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
2
+ <div role="status" class="p-4 space-y-4 border border-gray-200 divide-y divide-gray-200 rounded shadow motion-safe:animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
3
3
  <div class="flex items-center justify-between">
4
4
  <div>
5
5
  <div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
@@ -1,6 +1,6 @@
1
- module Plutonium::Ui::Skeleton
2
- class TableComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi::Skeleton
2
+ class TableComponent < PlutoniumUi::Base
3
3
  end
4
4
  end
5
5
 
6
- Plutonium::ComponentRegistry.register :skeleton__table, to: Plutonium::Ui::Skeleton::TableComponent
6
+ Plutonium::ComponentRegistry.register :skeleton__table, to: PlutoniumUi::Skeleton::TableComponent
@@ -1,92 +1,43 @@
1
- <% if search_object.scope_definitions.present? %>
2
- <%
3
- name = name.to_s
4
- current_scope = resource_query_params[:scope]
5
- %>
6
- <%= render_component :block, rounded: :top do %>
7
- <ul class="text-sm font-medium flex flex-wrap -mb-px">
8
- <li class="me-2">
9
- <% if current_scope.blank? %>
10
- <a class="inline-block p-4 text-primary-600 border-b-2 border-primary-600 rounded-t-lg active dark:text-primary-500 dark:border-primary-500" aria-current="page" href="?<%= search_object.build_url(scope: nil) %>">All</a>
11
- <% else %>
12
- <a class="inline-block p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 dark:text-gray-500" href="<%= search_object.build_url(scope: nil) %>">All</a>
13
- <% end %>
14
- </li>
15
-
16
- <% search_object.scope_definitions.each do |name, definition| %>
17
- <li class="me-2">
18
- <% if name == current_scope %>
19
- <a class="inline-block p-4 text-primary-600 border-b-2 border-primary-600 rounded-t-lg active dark:text-primary-500 dark:border-primary-500" aria-current="page" href="?<%= search_object.build_url(scope: name) %>"><%= name.humanize %></a>
20
- <% else %>
21
- <a class="inline-block p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 dark:text-gray-500" href="<%= search_object.build_url(scope: name) %>"><%= name.humanize %></a>
22
- <% end %>
23
- </li>
1
+ <table <%= attributes_html %>>
2
+ <thead class="<%= table_head_classname %>">
3
+ <tr>
4
+ <th scope="col" class="p-4">
5
+ <div class="flex items-center">
6
+ <input id="checkbox-all-search" type="checkbox" class="w-4 h-4 text-primary-600 bg-gray-100 border-gray-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
7
+ <label for="checkbox-all-search" class="sr-only">checkbox</label>
8
+ </div>
9
+ </th>
10
+ <% @columns.each do |column| %>
11
+ <th scope="col" class="<%= table_head_cell_classname %>">
12
+ <%= table_head_cell column %>
13
+ </th>
14
+ <% end %>
15
+ <% if actions_block.present? %>
16
+ <th scope="col" class="<%= table_actions_head_classname %>">&nbsp;</th>
17
+ <% end %>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <% @rows.each do |row| %>
22
+ <tr class="<%= table_row_classname %>">
23
+ <th scope="row" class="w-4 p-4">
24
+ <div class="flex items-center">
25
+ <input id="checkbox-table-search-1" type="checkbox" class="w-4 h-4 text-primary-600 bg-gray-100 border-gray-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
26
+ <label for="checkbox-table-search-1" class="sr-only">checkbox</label>
27
+ </div>
28
+ </th>
29
+ <% @columns.each do |column| %>
30
+ <td class="<%= table_row_cell_classname %>">
31
+ <%# the capture ensures, that we do not only return the return of the block, but all the html from the block %>
32
+ <%= view_context.capture(row, &column.td_block) %>
33
+ </td>
24
34
  <% end %>
25
- </ul>
26
- <% end %>
27
- <% end %>
28
-
29
- <%= render_component :block, id: "resource-table-container", scroll: :x, rounded: table_rounding, data: {controller:"scroll-preserver", action: "scroll->scroll-preserver#scrolled"} do %>
30
- <% unless resources.any? %>
31
- <div class="col-12">
32
- <%=
33
- render_component :empty_card, message: "No #{resource_name_plural(resource_class).downcase} match your query" do
34
- if current_policy.create?
35
- render_component :button, label: "Create #{resource_name(resource_class)}",
36
- to: resource_url_for(resource_class, action: :new),
37
- color: :primary
38
- end
39
- end
40
- %>
41
- </div>
42
- <% else %>
43
- <table data-controller="table" <%= render_component_attributes %>>
44
- <thead class="<%= table_head_classes %>">
45
- <tr>
46
- <th scope="col" class="p-4">
47
- <div class="flex items-center">
48
- <input id="checkbox-all-search" type="checkbox" class="w-4 h-4 text-primary-600 bg-gray-100 border-gray-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
49
- <label for="checkbox-all-search" class="sr-only">checkbox</label>
50
- </div>
51
- </th>
52
- <% fields.each do |name, field| %>
53
- <th scope="col" class="px-6 py-3">
54
- <%= table_header name, field.label, search_object %>
55
- </th>
56
- <% end %>
57
- <th scope="col" class="px-6 py-3 text-end">&nbsp;</th>
58
- </tr>
59
- </thead>
60
- <tbody>
61
- <% resources.each do |resource| %>
62
- <tr class="<%= table_row_classes %>">
63
- <th scope="row" class="w-4 p-4">
64
- <div class="flex items-center">
65
- <input id="checkbox-table-search-1" type="checkbox" class="w-4 h-4 text-primary-600 bg-gray-100 border-gray-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
66
- <label for="checkbox-table-search-1" class="sr-only">checkbox</label>
67
- </div>
68
- </th>
69
- <% fields.each do |name, field| %>
70
- <%
71
- options = field.options
72
- max_width = options.delete :pu_max_width
73
- %>
74
- <td class="<%= table_row_cell_classes %>">
75
- <%= field.render self, resource %>
76
- </td>
77
- <% end %>
78
- <td class="<%= table_actions_cell_classes %>">
79
- <% record_actions.permitted_for(policy(resource)).values.each do |action| %>
80
- <%= table_action_button resource, action %>
81
- <% end %>
82
- </td>
83
- </tr>
35
+ <% if actions_block.present? %>
36
+ <td class="<%= table_actions_row_classname %>">
37
+ <%= view_context.capture(row, &actions_block) %>
38
+ </td>
84
39
  <% end %>
85
- </tbody>
86
- </table>
87
-
88
- <% if pager.present? %>
89
- <%= render_component :pagination, pager: pager %>
40
+ </tr>
90
41
  <% end %>
91
- <% end %>
92
- <% end %>
42
+ </tbody>
43
+ </table>
@@ -1,63 +1,159 @@
1
- module Plutonium::Ui
2
- class TableComponent < Plutonium::Ui::Base
3
- option :resource_class
4
- option :resources
5
- option :record_actions
6
- option :search_object
7
- option :fields
8
- option :pager
1
+ module PlutoniumUi
2
+ # TableComponent is a UI component that represents a table with sortable columns and optional row actions.
3
+ class TableComponent < PlutoniumUi::Base
4
+ option :rows
9
5
 
10
- def classname
11
- "table-auto w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-200 #{super}"
6
+ attr_reader :columns, :actions_block
7
+
8
+ # Initializes the TableComponent with the given options.
9
+ #
10
+ # @param options [Hash] The options for the component.
11
+ def initialize(...)
12
+ super(...)
13
+ @columns = []
14
+ end
15
+
16
+ # Defines a column in the table.
17
+ #
18
+ # @param name [Symbol] The name of the column.
19
+ # @param label [String] The label of the column.
20
+ # @param search_object [Object] The search object associated with the column.
21
+ # @param block [Proc] An optional block for additional column customization.
22
+ def column(name:, label:, search_object:, &block)
23
+ @columns ||= []
24
+ @columns << Column.new(name:, label:, search_object:, &block)
25
+ end
26
+
27
+ # Returns the base attributes for the table.
28
+ #
29
+ # @return [Hash] The base attributes hash.
30
+ def base_attributes
31
+ {
32
+ classname: "table-auto w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-200",
33
+ table_head_classname: "text-xs text-gray-700 uppercase bg-gray-200 dark:bg-gray-700 dark:text-gray-200",
34
+ table_head_cell_classname: "px-6 py-3",
35
+ table_row_classname: "bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600",
36
+ table_row_cell_classname: "px-6 py-4 whitespace-nowrap max-w-[250px] hover:max-w-fit overflow-hidden text-ellipsis",
37
+ table_actions_head_classname: "px-6 py-3 text-end",
38
+ table_actions_row_classname: "flex justify-end px-6 py-4"
39
+ }
40
+ end
41
+
42
+ def filtered_attributes
43
+ base_attributes.keys - possible_attributes
44
+ end
45
+
46
+ # Defines a block of actions to be included in each row.
47
+ #
48
+ # @param block [Proc] The block defining the actions.
49
+ def with_actions(&block)
50
+ @actions_block = block
12
51
  end
13
52
 
14
53
  private
15
54
 
16
- def table_rounding
17
- search_object.scope_definitions.present? ? :bottom : :all
55
+ # Ensures that the view component calls the block, and @columns get populated.
56
+ def before_render
57
+ content
18
58
  end
19
59
 
20
- def table_head_classes
21
- "text-xs text-gray-700 uppercase bg-gray-200 dark:bg-gray-700 dark:text-gray-200"
60
+ # Retrieves the CSS class for the table head.
61
+ #
62
+ # @return [String] The CSS class for the table head.
63
+ def table_head_classname
64
+ raw_attributes_hash[:table_head_classname]
22
65
  end
23
66
 
24
- def table_row_classes
25
- "bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600"
67
+ # Retrieves the CSS class for a table head cell.
68
+ #
69
+ # @return [String] The CSS class for a table head cell.
70
+ def table_head_cell_classname
71
+ raw_attributes_hash[:table_head_cell_classname]
26
72
  end
27
73
 
28
- def table_row_cell_classes
29
- "px-6 py-4 whitespace-nowrap max-w-[250px] hover:max-w-fit overflow-hidden text-ellipsis"
74
+ # Retrieves the CSS class for a table row.
75
+ #
76
+ # @return [String] The CSS class for a table row.
77
+ def table_row_classname
78
+ raw_attributes_hash[:table_row_classname]
30
79
  end
31
80
 
32
- def table_actions_cell_classes
33
- "flex justify-end px-6 py-4"
81
+ # Retrieves the CSS class for a table row cell.
82
+ #
83
+ # @return [String] The CSS class for a table row cell.
84
+ def table_row_cell_classname
85
+ raw_attributes_hash[:table_row_cell_classname]
34
86
  end
35
87
 
36
- def table_header(name, label, search_object)
88
+ # Retrieves the CSS class for the table actions head.
89
+ #
90
+ # @return [String] The CSS class for the table actions head.
91
+ def table_actions_head_classname
92
+ raw_attributes_hash[:table_actions_head_classname]
93
+ end
94
+
95
+ # Retrieves the CSS class for the table actions row.
96
+ #
97
+ # @return [String] The CSS class for the table actions row.
98
+ def table_actions_row_classname
99
+ raw_attributes_hash[:table_actions_row_classname]
100
+ end
101
+
102
+ # Generates the content for a table head cell, including sorting links if applicable.
103
+ #
104
+ # @param column [Column] The column object.
105
+ # @return [String] The HTML content for the table head cell.
106
+ def table_head_cell(column)
107
+ name = column.name
108
+ label = column.label
109
+ search_object = column.search_object
110
+
37
111
  if (sort_params = search_object.sort_params_for(name))
38
- tag.span do
112
+ tag.div class: "inline-flex" do
39
113
  concat begin
40
- link_to(sort_params[:url], class: "text-decoration-none") do
114
+ link_to(sort_params[:url], class: "flex", title: sort_params[:direction] || "Sort") do
41
115
  concat label
42
116
  if sort_params[:direction].present?
43
117
  icon = (sort_params[:direction] == "ASC") ? "up" : "down"
44
118
  concat " "
45
- concat tag.i(icon, class: "bi bi-sort-#{icon} text-muted", title: sort_params[:direction])
119
+ concat render_icon("outline/arrow-#{icon}")
46
120
  end
47
121
  end
48
122
  end
123
+
49
124
  if sort_params[:position].present?
50
125
  concat " "
51
- concat link_to(sort_params[:position] + 1, sort_params[:reset_url],
52
- class: "badge rounded-pill text-bg-secondary text-decoration-none", title: "remove sorting",
53
- style: "font-size: 0.6em;")
126
+ concat link_to(
127
+ sort_params[:position] + 1,
128
+ sort_params[:reset_url],
129
+ class: "inline-flex items-center justify-center w-4 h-4 text-xs font-bold text-white bg-yellow-500 border-1 border-white rounded-full dark:border-gray-90",
130
+ title: "Clear sorting"
131
+ )
54
132
  end
55
133
  end
56
134
  else
57
135
  label
58
136
  end
59
137
  end
138
+
139
+ # A value object to hold the column definition.
140
+ class Column
141
+ attr_reader :name, :label, :search_object, :td_block
142
+
143
+ # Initializes a Column object with the given attributes.
144
+ #
145
+ # @param name [Symbol] The name of the column.
146
+ # @param label [String] The label of the column.
147
+ # @param search_object [Object] The search object associated with the column.
148
+ # @param block [Proc] An optional block for additional column customization.
149
+ def initialize(name:, label:, search_object:, &block)
150
+ @name = name
151
+ @label = label
152
+ @search_object = search_object
153
+ @td_block = block
154
+ end
155
+ end
60
156
  end
61
157
  end
62
158
 
63
- Plutonium::ComponentRegistry.register :table, to: Plutonium::Ui::TableComponent
159
+ Plutonium::ComponentRegistry.register :table, to: PlutoniumUi::TableComponent
@@ -1,4 +1,4 @@
1
- <div data-controller="table-search-input" <%= render_component_attributes %>>
1
+ <div <%= attributes_html %>>
2
2
  <%= simple_form_for :q, url: "", method: :get, html: { class: "flex items-center", data: { controller: "form", turbo_frame: nil } } do |f| %>
3
3
  <label for="search" class="sr-only">Search</label>
4
4
  <div class="relative w-full">
@@ -1,10 +1,15 @@
1
- module Plutonium::Ui
2
- class TableSearchInputComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class TableSearchInputComponent < PlutoniumUi::Base
3
3
  option :search_object
4
- # def classname
5
- # "custom classnames here #{super}"
6
- # end
4
+
5
+ private
6
+
7
+ def base_attributes
8
+ {
9
+ controller: "table-search-input"
10
+ }
11
+ end
7
12
  end
8
13
  end
9
14
 
10
- Plutonium::ComponentRegistry.register :table_search_input, to: Plutonium::Ui::TableSearchInputComponent
15
+ Plutonium::ComponentRegistry.register :table_search_input, to: PlutoniumUi::TableSearchInputComponent
@@ -1,4 +1,4 @@
1
- <div data-controller="table-toolbar" <%= render_component_attributes %>>
1
+ <div <%= attributes_html %>>
2
2
  <%= render_component :toolbar, actions:, resource: resource_class do %>
3
3
  <% if search_object.search_filter.present? && current_policy.search? %>
4
4
  <%= render_component "table_search_input", search_object: search_object %>
@@ -1,9 +1,17 @@
1
- module Plutonium::Ui
2
- class TableToolbarComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class TableToolbarComponent < PlutoniumUi::Base
3
3
  option :resource_class
4
4
  option :search_object
5
5
  option :actions
6
+
7
+ private
8
+
9
+ def base_attributes
10
+ {
11
+ controller: "table-toolbar"
12
+ }
13
+ end
6
14
  end
7
15
  end
8
16
 
9
- Plutonium::ComponentRegistry.register :table_toolbar, to: Plutonium::Ui::TableToolbarComponent
17
+ Plutonium::ComponentRegistry.register :table_toolbar, to: PlutoniumUi::TableToolbarComponent
@@ -1,9 +1,9 @@
1
1
  <%#= render_component :panel do %>
2
- <div data-controller="toolbar" <%= render_component_attributes %>>
2
+ <div <%= attributes_html %>>
3
3
  <div class="w-full md:w-1/2">
4
4
  <%= content %>
5
5
  </div>
6
- <div class="<%= toolbar_actions_classes %>">
6
+ <div class="<%= toolbar_actions_classname %>">
7
7
  <% actions.values.each do |action| %>
8
8
  <%= toolbar_action_button resource, action %>
9
9
  <% end %>
@@ -1,18 +1,26 @@
1
- module Plutonium::Ui
2
- class ToolbarComponent < Plutonium::Ui::Base
1
+ module PlutoniumUi
2
+ class ToolbarComponent < PlutoniumUi::Base
3
3
  option :resource
4
4
  option :actions, default: proc { {} }
5
5
 
6
- def classname
7
- "flex flex-col md:flex-row items-center justify-between space-y-3 md:space-y-0 md:space-x-4 #{super}"
6
+ private
7
+
8
+ def base_attributes
9
+ {
10
+ classname: "flex flex-col md:flex-row items-center justify-between space-y-3 md:space-y-0 md:space-x-4",
11
+ toolbar_actions_classname: "w-full md:w-auto flex flex-col md:flex-row space-y-1 md:space-y-0 items-stretch md:items-center justify-end shrink-0",
12
+ controller: "toolbar"
13
+ }
8
14
  end
9
15
 
10
- private
16
+ def filtered_attributes
17
+ %i[toolbar_actions_classname]
18
+ end
11
19
 
12
- def toolbar_actions_classes
13
- "w-full md:w-auto flex flex-col md:flex-row space-y-1 md:space-y-0 items-stretch md:items-center justify-end shrink-0"
20
+ def toolbar_actions_classname
21
+ raw_attributes_hash[:toolbar_actions_classname]
14
22
  end
15
23
  end
16
24
  end
17
25
 
18
- Plutonium::ComponentRegistry.register :toolbar, to: Plutonium::Ui::ToolbarComponent
26
+ Plutonium::ComponentRegistry.register :toolbar, to: PlutoniumUi::ToolbarComponent