plutonium 0.14.0 → 0.15.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/README copy.md +1 -1
  3. data/README.md +1 -1
  4. data/app/assets/plutonium.css +1 -1
  5. data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
  6. data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
  7. data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
  8. data/app/views/resource/_resource_details.html.erb +1 -36
  9. data/app/views/resource/_resource_form.html.erb +1 -5
  10. data/app/views/resource/_resource_table.html.erb +315 -85
  11. data/app/views/resource/edit.html.erb +1 -5
  12. data/app/views/resource/index.html.erb +1 -5
  13. data/app/views/resource/new.html.erb +1 -5
  14. data/app/views/resource/show.html.erb +1 -5
  15. data/config/initializers/pagy.rb +1 -0
  16. data/config/initializers/rabl.rb +27 -20
  17. data/gemfiles/rails_7.gemfile.lock +5 -1
  18. data/lib/generators/pu/core/assets/assets_generator.rb +2 -2
  19. data/lib/generators/pu/core/install/install_generator.rb +0 -3
  20. data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
  21. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
  22. data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
  23. data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
  24. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +5 -2
  25. data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
  26. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +19 -0
  27. data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
  28. data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
  29. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
  30. data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
  31. data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
  32. data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
  33. data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
  34. data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
  35. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
  36. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  37. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
  38. data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
  39. data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
  40. data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
  41. data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
  42. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  43. data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
  44. data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
  45. data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
  46. data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
  47. data/lib/generators/pu/res/model/model_generator.rb +0 -7
  48. data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
  49. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
  50. data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
  51. data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
  52. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
  53. data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
  54. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
  55. data/lib/plutonium/action/README.md +0 -0
  56. data/lib/plutonium/action/base.rb +103 -0
  57. data/lib/plutonium/action/interactive.rb +117 -0
  58. data/lib/plutonium/action/route_options.rb +65 -0
  59. data/lib/plutonium/action/simple.rb +8 -0
  60. data/lib/plutonium/auth.rb +1 -1
  61. data/lib/plutonium/configuration.rb +130 -0
  62. data/lib/plutonium/core/actions/collection.rb +1 -1
  63. data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
  64. data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +1 -1
  65. data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
  66. data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
  67. data/lib/plutonium/core/controller.rb +110 -0
  68. data/lib/plutonium/core/controllers/authorizable.rb +12 -35
  69. data/lib/plutonium/core/controllers/bootable.rb +38 -7
  70. data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
  71. data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
  72. data/lib/plutonium/core/ui/collection.rb +1 -1
  73. data/lib/plutonium/core/ui/detail.rb +1 -1
  74. data/lib/plutonium/core/ui/form.rb +1 -1
  75. data/lib/plutonium/definition/actions.rb +50 -0
  76. data/lib/plutonium/definition/base.rb +92 -0
  77. data/lib/plutonium/definition/config_attr.rb +30 -0
  78. data/lib/plutonium/definition/defineable_props.rb +96 -0
  79. data/lib/plutonium/definition/search.rb +21 -0
  80. data/lib/plutonium/engine/validator.rb +30 -0
  81. data/lib/plutonium/engine.rb +25 -0
  82. data/lib/plutonium/helpers/assets_helper.rb +73 -20
  83. data/lib/plutonium/helpers/form_helper.rb +1 -3
  84. data/lib/plutonium/interaction/README.md +369 -0
  85. data/lib/plutonium/interaction/base.rb +75 -0
  86. data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
  87. data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
  88. data/lib/plutonium/interaction/outcome.rb +129 -0
  89. data/lib/plutonium/interaction/response/base.rb +63 -0
  90. data/lib/plutonium/interaction/response/null.rb +33 -0
  91. data/lib/plutonium/interaction/response/redirect.rb +30 -0
  92. data/lib/plutonium/interaction/response/render.rb +28 -0
  93. data/lib/plutonium/lib/bit_flags.rb +70 -9
  94. data/lib/plutonium/lib/overlayed_hash.rb +86 -0
  95. data/lib/plutonium/lib/smart_cache.rb +171 -0
  96. data/lib/plutonium/models/has_cents.rb +170 -0
  97. data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
  98. data/lib/plutonium/{application → portal}/controller.rb +3 -11
  99. data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
  100. data/lib/plutonium/portal/engine.rb +15 -0
  101. data/lib/plutonium/railtie.rb +35 -15
  102. data/lib/plutonium/reloader.rb +71 -29
  103. data/lib/plutonium/resource/controller.rb +51 -34
  104. data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
  105. data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
  106. data/lib/plutonium/resource/controllers/defineable.rb +26 -0
  107. data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
  108. data/lib/plutonium/resource/controllers/presentable.rb +41 -0
  109. data/lib/plutonium/resource/controllers/queryable.rb +44 -0
  110. data/lib/plutonium/resource/definition.rb +6 -0
  111. data/lib/plutonium/resource/policy.rb +25 -13
  112. data/lib/plutonium/resource/query_object.rb +50 -51
  113. data/lib/plutonium/resource/record.rb +6 -89
  114. data/lib/plutonium/resource/register.rb +82 -0
  115. data/lib/plutonium/routing/mapper_extensions.rb +1 -1
  116. data/lib/plutonium/routing/resource_registration.rb +1 -1
  117. data/lib/plutonium/routing/route_set_extensions.rb +6 -18
  118. data/lib/plutonium/ui/action_button.rb +125 -0
  119. data/lib/plutonium/ui/breadcrumbs.rb +163 -0
  120. data/lib/plutonium/ui/component/base.rb +13 -0
  121. data/lib/plutonium/ui/component/behaviour.rb +38 -0
  122. data/lib/plutonium/ui/component/kit.rb +31 -0
  123. data/lib/plutonium/ui/component/methods.rb +54 -0
  124. data/lib/plutonium/ui/display/base.rb +25 -0
  125. data/lib/plutonium/ui/display/component/association.rb +26 -0
  126. data/lib/plutonium/ui/display/resource.rb +77 -0
  127. data/lib/plutonium/ui/display/theme.rb +27 -0
  128. data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
  129. data/lib/plutonium/ui/empty_card.rb +20 -0
  130. data/lib/plutonium/ui/form/base.rb +37 -0
  131. data/lib/plutonium/ui/form/resource.rb +75 -0
  132. data/lib/plutonium/ui/form/theme.rb +42 -0
  133. data/lib/plutonium/ui/page/base.rb +112 -0
  134. data/lib/plutonium/ui/page/edit.rb +23 -0
  135. data/lib/plutonium/ui/page/index.rb +27 -0
  136. data/lib/plutonium/ui/page/new.rb +23 -0
  137. data/lib/plutonium/ui/page/show.rb +27 -0
  138. data/lib/plutonium/ui/page_header.rb +49 -0
  139. data/lib/plutonium/ui/table/base.rb +13 -0
  140. data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
  141. data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
  142. data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
  143. data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
  144. data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
  145. data/lib/plutonium/ui/table/display_theme.rb +21 -0
  146. data/lib/plutonium/ui/table/resource.rb +98 -0
  147. data/lib/plutonium/ui/table/theme.rb +35 -0
  148. data/lib/plutonium/ui.rb +9 -0
  149. data/lib/plutonium/version.rb +5 -1
  150. data/lib/plutonium.rb +53 -26
  151. data/package-lock.json +19 -22
  152. data/package.json +4 -4
  153. data/sig/.keep +0 -0
  154. data/src/css/plutonium.css +15 -0
  155. data/tailwind.options.js +11 -3
  156. metadata +220 -81
  157. data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
  158. data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
  159. data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
  160. data/lib/plutonium/concerns/resource_validatable.rb +0 -34
  161. data/lib/plutonium/config.rb +0 -9
  162. data/lib/plutonium/core/controllers/base.rb +0 -101
  163. data/lib/plutonium/core/controllers/presentable.rb +0 -65
  164. data/lib/plutonium/core/controllers/queryable.rb +0 -28
  165. data/lib/plutonium/pkg/app.rb +0 -35
  166. data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
  167. data/lib/plutonium/pkg/feature.rb +0 -18
  168. data/lib/plutonium/policy/initializer.rb +0 -22
  169. data/lib/plutonium/policy/scope.rb +0 -19
  170. data/lib/plutonium/pundit/context.rb +0 -18
  171. data/lib/plutonium/pundit/policy_finder.rb +0 -25
  172. data/lib/plutonium/resource/policy_context.rb +0 -5
  173. data/lib/plutonium/resource_register.rb +0 -83
  174. data/lib/plutonium/smart_cache.rb +0 -151
  175. data/sig/plutonium.rbs +0 -12
  176. /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
  177. /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
  178. /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
  179. /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
  180. /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
  181. /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
  182. /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
  183. /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  184. /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
  185. /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
  186. /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  187. /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
  188. /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
  189. /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
  190. /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ module Components
7
+ class ScopesBar < Plutonium::UI::Component::Base
8
+ include Plutonium::UI::Component::Behaviour
9
+
10
+ def view_template
11
+ div(
12
+ class:
13
+ # "flex flex-wrap justify-between items-center gap-4 p-4 bg-white border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700 mb-4"
14
+ "flex flex-wrap justify-between items-center gap-4 mb-4"
15
+ ) do
16
+ div(class: "flex flex-wrap items-center gap-2") do
17
+ name = "all"
18
+ if current_scope.blank?
19
+ a(
20
+ id: "#{name}-scope",
21
+ href: current_query_object.build_url(scope: nil),
22
+ class:
23
+ "px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
24
+ ) { name.humanize }
25
+ else
26
+ a(
27
+ id: "#{name}-scope",
28
+ href: current_query_object.build_url(scope: nil),
29
+ class:
30
+ "px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
31
+ ) { name.humanize }
32
+ end
33
+
34
+ current_query_object.scope_definitions.each do |name, definition|
35
+ if name == current_scope
36
+ a(
37
+ id: "#{name}-scope",
38
+ href: current_query_object.build_url(scope: name),
39
+ class:
40
+ "px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
41
+ ) { name.humanize }
42
+ else
43
+ a(
44
+ id: "#{name}-scope",
45
+ href: current_query_object.build_url(scope: name),
46
+ class:
47
+ "px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
48
+ ) { name.humanize }
49
+ end
50
+ end
51
+ end
52
+
53
+ # div(class: "flex flex-wrap items-center gap-2") do
54
+ # button(
55
+ # class:
56
+ # "inline-flex items-center px-3 py-2 text-sm font-medium text-white bg-red-700 rounded-lg hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800"
57
+ # ) do
58
+ # svg(
59
+ # class: "w-4 h-4 mr-2",
60
+ # fill: "none",
61
+ # stroke: "currentColor",
62
+ # viewbox: "0 0 24 24",
63
+ # xmlns: "http://www.w3.org/2000/svg"
64
+ # ) do |s|
65
+ # s.path(
66
+ # stroke_linecap: "round",
67
+ # stroke_linejoin: "round",
68
+ # stroke_width: "2",
69
+ # d:
70
+ # "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
71
+ # )
72
+ # end
73
+ # plain " Delete Selected "
74
+ # end
75
+ # button(
76
+ # class:
77
+ # "inline-flex items-center px-3 py-2 text-sm font-medium text-white bg-yellow-700 rounded-lg hover:bg-yellow-800 focus:ring-4 focus:outline-none focus:ring-yellow-300 dark:bg-yellow-600 dark:hover:bg-yellow-700 dark:focus:ring-yellow-800"
78
+ # ) do
79
+ # svg(
80
+ # class: "w-4 h-4 mr-2",
81
+ # fill: "none",
82
+ # stroke: "currentColor",
83
+ # viewbox: "0 0 24 24",
84
+ # xmlns: "http://www.w3.org/2000/svg"
85
+ # ) do |s|
86
+ # s.path(
87
+ # stroke_linecap: "round",
88
+ # stroke_linejoin: "round",
89
+ # stroke_width: "2",
90
+ # d:
91
+ # "M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
92
+ # )
93
+ # end
94
+ # plain " Archive Selected "
95
+ # end
96
+ # button(
97
+ # id: "dropdownActionButton",
98
+ # data_dropdown_toggle: "dropdownAction",
99
+ # class:
100
+ # "inline-flex items-center text-gray-500 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-3 py-2 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700",
101
+ # type: "button"
102
+ # ) do
103
+ # span(class: "sr-only") { "Action button" }
104
+ # plain " More Actions "
105
+ # svg(
106
+ # class: "w-2.5 h-2.5 ml-2.5",
107
+ # aria_hidden: "true",
108
+ # xmlns: "http://www.w3.org/2000/svg",
109
+ # fill: "none",
110
+ # viewbox: "0 0 10 6"
111
+ # ) do |s|
112
+ # s.path(
113
+ # stroke: "currentColor",
114
+ # stroke_linecap: "round",
115
+ # stroke_linejoin: "round",
116
+ # stroke_width: "2",
117
+ # d: "m1 1 4 4 4-4"
118
+ # )
119
+ # end
120
+ # end
121
+ # end
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def current_scope = resource_query_params[:scope]
128
+
129
+ def render?
130
+ current_query_object.scope_definitions.present?
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ module Components
7
+ class SearchBar < Plutonium::UI::Component::Base
8
+ def view_template
9
+ original_attributes = Phlex::HTML::EVENT_ATTRIBUTES
10
+ temp_attributes = Phlex::HTML::EVENT_ATTRIBUTES.dup
11
+ temp_attributes.delete("oninput")
12
+ temp_attributes.delete("onclick")
13
+ Phlex::HTML.const_set(:EVENT_ATTRIBUTES, temp_attributes)
14
+
15
+ div(
16
+ class:
17
+ # "p-4 bg-white border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700 space-y-2 mb-4"
18
+ "space-y-2 mb-4"
19
+ ) do
20
+ search_query = current_query_object.search_query
21
+ query_params = resource_query_params
22
+ render Phlexi::Form(:q, attributes: {data: {controller: "form", turbo_frame: nil}}) {
23
+ div(class: "relative") do
24
+ div(class: "absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none") do
25
+ svg(
26
+ class: "w-5 h-5 text-gray-500 dark:text-gray-400",
27
+ aria_hidden: "true",
28
+ fill: "currentColor",
29
+ viewbox: "0 0 20 20",
30
+ xmlns: "http://www.w3.org/2000/svg"
31
+ ) do |s|
32
+ s.path(
33
+ fill_rule: "evenodd",
34
+ d:
35
+ "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
36
+ clip_rule: "evenodd"
37
+ )
38
+ end
39
+ end
40
+ render field(:search, value: search_query)
41
+ .placeholder("Search...")
42
+ .input_tag(
43
+ id: "search",
44
+ value: search_query,
45
+ class: "block w-full p-2 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500",
46
+ data: {
47
+ action: "form#submit",
48
+ turbo_permanent: true
49
+ }
50
+ )
51
+
52
+ render field(:scope, value: query_params[:scope]).input_tag(type: :hidden)
53
+ render field(:sort_fields, value: query_params[:sort_fields]).input_array_tag do |f|
54
+ render f.input_tag(type: :hidden)
55
+ end
56
+ nest_one(:sort_directions) do |directions|
57
+ query_params[:sort_directions]&.each do |name, value|
58
+ render directions.field(name, value:).input_tag(hidden: true)
59
+ end
60
+ end
61
+ end
62
+ }
63
+
64
+ # div(class: "flex flex-wrap items-center gap-4") do
65
+ # span(class: "text-sm font-medium text-gray-900 dark:text-white") do
66
+ # "Filters:"
67
+ # end
68
+ # select(
69
+ # id: "category-filter",
70
+ # class:
71
+ # "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
72
+ # ) do
73
+ # option(selected: "selected", value: "") { "All Categories" }
74
+ # option(value: "technology") { "Technology" }
75
+ # option(value: "science") { "Science" }
76
+ # option(value: "health") { "Health" }
77
+ # end
78
+ # div(class: "flex items-center space-x-2") do
79
+ # input(
80
+ # type: "date",
81
+ # id: "start-date",
82
+ # class:
83
+ # "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
84
+ # )
85
+ # span(class: "text-gray-500 dark:text-gray-400") { "to" }
86
+ # input(
87
+ # type: "date",
88
+ # id: "end-date",
89
+ # class:
90
+ # "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
91
+ # )
92
+ # end
93
+ # select(
94
+ # id: "author-filter",
95
+ # class:
96
+ # "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
97
+ # ) do
98
+ # option(selected: "selected", value: "") { "All Authors" }
99
+ # option(value: "john-doe") { "John Doe" }
100
+ # option(value: "jane-smith") { "Jane Smith" }
101
+ # end
102
+ # button(
103
+ # onclick: "applyFilters()",
104
+ # class:
105
+ # "inline-flex items-center text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800"
106
+ # ) do
107
+ # svg(
108
+ # class: "w-4 h-4 mr-2",
109
+ # fill: "currentColor",
110
+ # viewbox: "0 0 20 20",
111
+ # xmlns: "http://www.w3.org/2000/svg"
112
+ # ) do |s|
113
+ # s.path(
114
+ # fill_rule: "evenodd",
115
+ # d:
116
+ # "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z",
117
+ # clip_rule: "evenodd"
118
+ # )
119
+ # end
120
+ # plain " Apply Filters "
121
+ # end
122
+ # button(
123
+ # onclick: "clearFilters()",
124
+ # class:
125
+ # "inline-flex items-center text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-4 py-2 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700"
126
+ # ) do
127
+ # svg(
128
+ # class: "w-4 h-4 mr-2",
129
+ # fill: "currentColor",
130
+ # viewbox: "0 0 20 20",
131
+ # xmlns: "http://www.w3.org/2000/svg"
132
+ # ) do |s|
133
+ # s.path(
134
+ # fill_rule: "evenodd",
135
+ # d:
136
+ # "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
137
+ # clip_rule: "evenodd"
138
+ # )
139
+ # end
140
+ # plain " Clear Filters "
141
+ # end
142
+ # end
143
+ end
144
+ ensure
145
+ # TODO: remove this once Phlex adds support for SafeValues
146
+ Phlex::HTML.const_set(:EVENT_ATTRIBUTES, original_attributes)
147
+ end
148
+
149
+ private
150
+
151
+ def render?
152
+ current_query_object.search_filter.present? && current_policy.allowed_to?(:search?)
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ class DisplayTheme < Phlexi::Table::DisplayTheme
7
+ def self.theme
8
+ super.merge({
9
+ string: "max-h-[150px] overflow-y-auto",
10
+ prefixed_icon: "w-4 h-4 mr-1",
11
+ link: "text-primary-600 dark:text-primary-500",
12
+ color: "flex items-center",
13
+ color_indicator: "w-10 h-10 rounded-full mr-2",
14
+ email: "flex items-center text-primary-600 dark:text-primary-500 whitespace-nowrap",
15
+ json: "max-h-[150px] overflow-y-auto whitespace-pre font-mono shadow-inner p-4"
16
+ })
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ class Resource < Plutonium::UI::Component::Base
7
+ attr_reader :collection, :resource_fields, :resource_definition
8
+
9
+ def initialize(collection, resource_fields:, resource_definition:)
10
+ @collection = collection
11
+ @resource_fields = resource_fields
12
+ @resource_definition = resource_definition
13
+ end
14
+
15
+ def view_template
16
+ render_search_bar
17
+ render_scopes_bar
18
+
19
+ collection.empty? ? render_empty_card : render_table
20
+
21
+ render_footer
22
+ end
23
+
24
+ private
25
+
26
+ def render_search_bar
27
+ TableSearchBar()
28
+ end
29
+
30
+ def render_scopes_bar
31
+ TableScopesBar()
32
+ end
33
+
34
+ def render_empty_card
35
+ EmptyCard("No #{resource_name_plural(resource_class)} match your query") {
36
+ action = resource_definition.defined_actions[:new]
37
+ if action&.permitted_by?(current_policy)
38
+ url = resource_url_for(resource_class, *action.route_options.url_args, **action.route_options.url_options)
39
+ ActionButton(action, url:)
40
+ end
41
+ }
42
+ end
43
+
44
+ def render_table
45
+ render Plutonium::UI::Table::Base.new(collection) do |table|
46
+ @resource_fields.each do |name|
47
+ # column :name, as: :string
48
+ # column :description, class: "text-red-700"
49
+ # column :age, align: :end
50
+ # column :dob do |proxy|
51
+ # proxy.field(:dob).date_tag
52
+ # end
53
+
54
+ column_definition = resource_definition.defined_columns[name] || {}
55
+ column_display_options = column_definition[:options] || {}
56
+ display_field_as = column_display_options.delete(:as)
57
+ align_field_to = column_display_options.delete(:align)
58
+
59
+ display_block = column_definition[:block] || ->(wrapped_object, key) {
60
+ f = wrapped_object.field(key)
61
+ display_field_as ||= f.inferred_field_component
62
+ f.send(:"#{display_field_as}_tag", **column_display_options)
63
+ }
64
+
65
+ field_options = resource_definition.defined_fields[name] ? resource_definition.defined_fields[name][:options] : {}
66
+ field_options[:as] = display_field_as
67
+ field_options[:align] = align_field_to if align_field_to
68
+ table.column name, **field_options, sort_params: current_query_object.sort_params_for(name), &display_block
69
+ end
70
+
71
+ table.actions do |wrapped_object|
72
+ record = wrapped_object.unwrapped
73
+ policy = policy_for(record:)
74
+
75
+ div(class: "flex space-x-2") {
76
+ resource_definition.defined_actions
77
+ .select { |k, a| a.collection_record_action? && policy.allowed_to?(:"#{k}?") }
78
+ .values
79
+ .each { |action|
80
+ url = resource_url_for(record, *action.route_options.url_args, **action.route_options.url_options)
81
+
82
+ ActionButton(action, url:, variant: :table)
83
+ }
84
+ }
85
+ end
86
+ end
87
+ end
88
+
89
+ def render_footer
90
+ div(class: "sticky bottom-[-2px] p-4 pb-6 w-full z-50 bg-gray-50 dark:bg-gray-900") {
91
+ TableInfo(pagy_instance)
92
+ TablePagination(pagy_instance)
93
+ }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ class Theme < Phlexi::Table::Theme
7
+ def self.theme
8
+ super.merge({
9
+ selection_checkbox: "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600",
10
+ name_column: "font-medium text-gray-900 whitespace-nowrap dark:text-white",
11
+ align_start: "text-start",
12
+ align_end: "text-end",
13
+ wrapper: "relative overflow-x-auto shadow-md sm:rounded-lg",
14
+ base: "w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400",
15
+ caption: "p-5 text-lg font-semibold text-left rtl:text-right text-gray-900 bg-white dark:text-white dark:bg-gray-800",
16
+ description: "mt-1 text-sm font-normal text-gray-500 dark:text-gray-400",
17
+ header: "text-xs text-gray-700 uppercase bg-gray-200 dark:bg-gray-700 dark:text-gray-400",
18
+ header_grouping_cell: "px-6 py-3 text-center text-sm border-b border-t border-r last:border-r-0 dark:border-gray-800",
19
+ header_cell: "px-6 py-3",
20
+ header_cell_content_wrapper: "inline-flex items-center",
21
+ header_cell_sort_wrapper: "flex items-center",
22
+ header_cell_sort_indicator: "ml-1.5",
23
+ body_row: "bg-white border-b last:border-none dark:bg-gray-800 dark:border-gray-700",
24
+ body_cell: "px-6 py-4 whitespace-pre max-w-[310px] hover:max-w-[600px] overflow-hidden text-ellipsis transition-all duration-300 ease-in-out",
25
+ sort_icon: "w-3 h-3",
26
+ sort_icon_active: "text-primary-600",
27
+ sort_index_clear_link: "ml-2",
28
+ sort_index_clear_link_text: "text-xs font-bold",
29
+ sort_index_clear_link_icon: "ml-1 text-red-600"
30
+ })
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tailwind_merge"
4
+
5
+ module Plutonium
6
+ module UI
7
+ TAILWIND_MERGER = TailwindMerge::Merger.new
8
+ end
9
+ end
@@ -1,3 +1,7 @@
1
1
  module Plutonium
2
- VERSION = "0.14.0"
2
+ VERSION = "0.15.0-rc1"
3
+ NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
+ v[1] = v[1].to_i + 1
5
+ v[2] = 0
6
+ }.join(".")
3
7
  end
data/lib/plutonium.rb CHANGED
@@ -1,57 +1,84 @@
1
- require "zeitwerk"
1
+ # frozen_string_literal: true
2
2
 
3
- # Zeitwerk loader setup for the Plutonium gem
4
- loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
5
- loader.ignore("#{__dir__}/generators")
6
- loader.ignore("#{__dir__}/plutonium/railtie.rb")
7
- loader.enable_reloading if defined?(Rails.env) && Rails.env.development?
8
- loader.setup
3
+ require "zeitwerk"
4
+ require "action_policy"
5
+ require "phlex"
6
+ require "phlex-rails"
7
+ require "phlex/tabler_icons"
8
+ require "phlexi-display"
9
+ require "phlexi-form"
10
+ require "phlexi-table"
9
11
 
10
- require_relative "plutonium/railtie" if defined?(Rails::Railtie)
12
+ require_relative "plutonium/configuration"
11
13
 
14
+ # Plutonium module
15
+ #
16
+ # This module provides the main functionality for the Plutonium gem.
17
+ # It sets up autoloading using Zeitwerk and provides utility methods
18
+ # for accessing gem-related information.
12
19
  module Plutonium
13
- # Custom error class for the Plutonium module
20
+ # Custom error class for Plutonium-specific exceptions
14
21
  class Error < StandardError; end
15
22
 
23
+ # Set up Zeitwerk loader for the Plutonium gem
24
+ # @return [Zeitwerk::Loader] configured Zeitwerk loader instance
25
+ Loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false).tap do |loader|
26
+ loader.ignore("#{__dir__}/generators")
27
+ loader.ignore("#{__dir__}/plutonium/railtie.rb")
28
+ loader.inflector.inflect("ui" => "UI")
29
+ loader.enable_reloading if defined?(Rails.env) && Rails.env.development?
30
+ loader.setup
31
+ end
32
+
16
33
  class << self
17
- # @return [Pathname] the root directory of the gem
34
+ def deprecator
35
+ @deprecator ||= ActiveSupport::Deprecation.new("1.0", "Plutonium")
36
+ end
37
+
38
+ # Get the root directory of the gem
39
+ #
40
+ # @return [Pathname] the root directory path
18
41
  def root
19
- Pathname.new(File.expand_path("..", __dir__))
42
+ @root ||= Pathname.new(File.expand_path("..", __dir__))
20
43
  end
21
44
 
22
- # @return [Pathname] the root directory of the lib folder of the gem
45
+ # Get the root directory of the lib folder of the gem
46
+ #
47
+ # @return [Pathname] the lib root directory path
23
48
  def lib_root
24
- root.join("lib", "plutonium")
49
+ @lib_root ||= root.join("lib", "plutonium")
25
50
  end
26
51
 
27
- # @return [Logger] the Rails logger
52
+ # Get the Rails logger
53
+ #
54
+ # @return [Logger] the Rails logger instance
28
55
  def logger
29
56
  Rails.logger
30
57
  end
31
58
 
32
- # @return [String] the name of the application
59
+ # Get the name of the application
60
+ #
61
+ # @return [String] the application name
33
62
  def application_name
34
63
  @application_name || Rails.application.class.module_parent_name
35
64
  end
36
65
 
37
- # @param [String] application_name the name of the application
38
- # @return [void]
66
+ # Set the name of the application
67
+ #
68
+ # @param [String] name the name of the application
39
69
  attr_writer :application_name
40
70
 
41
- # @return [Boolean] whether the gem is in development mode
42
- def development?
43
- ActiveModel::Type::Boolean.new.cast(ENV["PLUTONIUM_DEV"]).present?
44
- end
45
-
46
- # Eager loads Rails application if not already eager loaded
71
+ # Eager load Rails application if not already eager loaded
72
+ #
47
73
  # @return [void]
48
74
  def eager_load_rails!
49
- return if Rails.env.production? && defined?(@rails_eager_loaded)
75
+ return if @rails_eager_loaded || Rails.application.config.eager_load
50
76
 
51
- Rails.application.eager_load! unless Rails.application.config.eager_load
77
+ Rails.application.eager_load!
52
78
  @rails_eager_loaded = true
53
79
  end
54
80
  end
55
81
  end
56
82
 
57
- Plutonium::ZEITWERK_LOADER = loader
83
+ # Load Railtie if Rails is defined
84
+ require_relative "plutonium/railtie" if defined?(Rails::Railtie)