plutonium 0.14.0 → 0.15.0.pre.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Form
6
+ class Resource < Base
7
+ attr_reader :resource_fields, :resource_definition
8
+
9
+ def initialize(*, resource_fields:, resource_definition:, **, &)
10
+ super(*, **, &)
11
+ @resource_fields = resource_fields
12
+ @resource_definition = resource_definition
13
+ end
14
+
15
+ def form_template
16
+ render_fields
17
+ render_actions
18
+ end
19
+
20
+ private
21
+
22
+ def render_fields
23
+ fields_wrapper {
24
+ resource_fields.each { |name|
25
+ render_resource_field name
26
+ }
27
+ }
28
+ end
29
+
30
+ def form_action
31
+ return @form_action unless object.present? && @form_action != false && helpers.present?
32
+
33
+ @form_action ||= resource_url_for(object, action: object.new_record? ? :create : :update)
34
+ end
35
+
36
+ def render_resource_field(name)
37
+ # input :name, as: :string
38
+ # input :description, class: "col-span-full"
39
+ # input :age, field: {class: "max-h-fit"}
40
+ # input :dob do |f|
41
+ # f.date_tag
42
+ # end
43
+
44
+ when_permitted(name) do
45
+ input_definition = resource_definition.defined_inputs[name] || {}
46
+ input_options = input_definition[:options] || {}
47
+ input_field_as = input_options.delete(:as)
48
+
49
+ input_field_options = input_options.delete(:field) || {}
50
+ input_block = input_definition[:block] || ->(f) {
51
+ input_field_as ||= f.inferred_field_component
52
+ f.send(:"#{input_field_as}_tag", **input_field_options)
53
+ }
54
+
55
+ field_options = resource_definition.defined_fields[name] ? resource_definition.defined_fields[name][:options] : {}
56
+ if !input_options[:class] || !input_options[:class].include?("col-span")
57
+ # temp hack to allow col span overrides
58
+ # TODO: remove once we complete theming, which will support merges
59
+ input_options[:class] = tokens("col-span-full", input_options[:class])
60
+ end
61
+ render field(name, **field_options).wrapped(**input_options) do |f|
62
+ render input_block.call(f)
63
+ end
64
+ end
65
+ end
66
+
67
+ def when_permitted(name, &)
68
+ return unless @resource_fields.include? name
69
+
70
+ yield
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Form
6
+ class Theme < Phlexi::Form::Theme
7
+ def self.theme
8
+ super.merge({
9
+ base: "relative bg-white dark:bg-gray-800 shadow-md sm:rounded-lg my-3 p-6 space-y-6",
10
+ fields_wrapper: "grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-6 grid-flow-row-dense",
11
+ actions_wrapper: "flex justify-end space-x-2",
12
+ wrapper: nil,
13
+ inner_wrapper: "w-full",
14
+ # label themes
15
+ label: "mt-2 block mb-2 text-base font-bold",
16
+ invalid_label: "text-red-700 dark:text-red-500",
17
+ valid_label: "text-green-700 dark:text-green-500",
18
+ neutral_label: "text-gray-500 dark:text-gray-400",
19
+ # input themes
20
+ input: "w-full p-2 border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700",
21
+ invalid_input: "bg-red-50 border-red-500 dark:border-red-500 text-red-900 dark:text-red-500 placeholder-red-700 dark:placeholder-red-500 focus:ring-red-500 focus:border-red-500",
22
+ valid_input: "bg-green-50 border-green-500 dark:border-green-500 text-green-900 dark:text-green-400 placeholder-green-700 dark:placeholder-green-500 focus:ring-green-500 focus:border-green-500",
23
+ neutral_input: "border-gray-300 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-primary-500 focus:border-primary-500",
24
+ # color
25
+ color: "pu-color-input appearance-none bg-transparent border-none cursor-pointer w-10 h-10",
26
+ invalid_color: nil,
27
+ valid_color: nil,
28
+ neutral_color: nil,
29
+ # file
30
+ file: "w-full border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700 focus:outline-none",
31
+ # hint themes
32
+ hint: "mt-2 text-sm text-gray-500 dark:text-gray-200",
33
+ # error themes
34
+ error: "mt-2 text-sm text-red-600 dark:text-red-500",
35
+ # button themes
36
+ button: "px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500"
37
+ })
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class Base < Plutonium::UI::Component::Base
7
+ def initialize(page_title: nil, page_description: nil, page_actions: nil)
8
+ @page_title = page_title
9
+ @page_description = page_description
10
+ @page_actions = page_actions
11
+ end
12
+
13
+ def view_template(&)
14
+ render_before_header
15
+ render_header
16
+ render_after_header
17
+
18
+ render_before_content
19
+ render_content(&)
20
+ render_after_content
21
+
22
+ render_before_footer
23
+ render_footer
24
+ render_after_footer
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :page_title, :page_description, :page_actions
30
+
31
+ def render_header
32
+ render_before_breadcrumbs
33
+ render_breadcrumbs
34
+ render_after_breadcrumbs
35
+
36
+ render_before_page_header
37
+ render_page_header
38
+ render_after_page_header
39
+
40
+ render_before_toolbar
41
+ render_toolbar
42
+ render_after_toolbar
43
+ end
44
+
45
+ def render_breadcrumbs
46
+ Breadcrumbs()
47
+ end
48
+
49
+ def render_page_header
50
+ return unless page_title
51
+
52
+ PageHeader(title: page_title, description: page_description, actions: page_actions)
53
+ end
54
+
55
+ def render_toolbar
56
+ # Implement toolbar content
57
+ end
58
+
59
+ def render_content(&block)
60
+ block ||= proc { render_default_content }
61
+
62
+ DynaFrameContent(&block)
63
+ end
64
+
65
+ def render_default_content
66
+ raise NotImplementedError, "#{self.class}#render_default_content"
67
+ end
68
+
69
+ def render_footer
70
+ # Implement footer content
71
+ end
72
+
73
+ # Customization hooks
74
+ def render_before_header
75
+ end
76
+
77
+ def render_after_header
78
+ end
79
+
80
+ def render_before_breadcrumbs
81
+ end
82
+
83
+ def render_after_breadcrumbs
84
+ end
85
+
86
+ def render_before_page_header
87
+ end
88
+
89
+ def render_after_page_header
90
+ end
91
+
92
+ def render_before_toolbar
93
+ end
94
+
95
+ def render_after_toolbar
96
+ end
97
+
98
+ def render_before_content
99
+ end
100
+
101
+ def render_after_content
102
+ end
103
+
104
+ def render_before_footer
105
+ end
106
+
107
+ def render_after_footer
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class Edit < Base
7
+ private
8
+
9
+ def page_title
10
+ current_definition.edit_page_title || super || "Edit"
11
+ end
12
+
13
+ def page_description
14
+ current_definition.edit_page_description || super
15
+ end
16
+
17
+ def render_default_content
18
+ render "resource_form"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class Index < Base
7
+ private
8
+
9
+ def page_title
10
+ super || current_definition.index_page_title || resource_name_plural(resource_class)
11
+ end
12
+
13
+ def page_description
14
+ super || current_definition.index_page_description
15
+ end
16
+
17
+ def page_actions
18
+ super || current_definition.defined_actions.values.select { |a| a.resource_action? && a.permitted_by?(current_policy) }
19
+ end
20
+
21
+ def render_default_content
22
+ render "resource_table"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class New < Base
7
+ private
8
+
9
+ def page_title
10
+ current_definition.new_page_title || super || "New"
11
+ end
12
+
13
+ def page_description
14
+ current_definition.new_page_description || super
15
+ end
16
+
17
+ def render_default_content
18
+ render "resource_form"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class Show < Base
7
+ private
8
+
9
+ def page_title
10
+ current_definition.show_page_title || super || display_name_of(resource_record)
11
+ end
12
+
13
+ def page_description
14
+ current_definition.show_page_description || super
15
+ end
16
+
17
+ def page_actions
18
+ super || current_definition.defined_actions.values.select { |a| a.record_action? && a.permitted_by?(current_policy) }
19
+ end
20
+
21
+ def render_default_content
22
+ render "resource_details"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ module Plutonium
2
+ module UI
3
+ class PageHeader < Plutonium::UI::Component::Base
4
+ def initialize(title:, description:, actions:)
5
+ @title = title
6
+ @description = description
7
+ @actions = actions || []
8
+ end
9
+
10
+ def view_template
11
+ div(class: "sm:flex sm:space-y-0 sm:space-x-4 sm:flex-row items-center justify-between space-y-3 mb-6") {
12
+ div {
13
+ phlexi_render(@title) {
14
+ render_title @title
15
+ }
16
+
17
+ phlexi_render(@description) {
18
+ render_description @description
19
+ }
20
+ }
21
+ div(class: "flex flex-row space-x-2") {
22
+ render_actions
23
+ }
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ def render_title(title)
30
+ h2(class: "mb-2 text-3xl font-extrabold leading-none tracking-tight text-gray-900 md:text-4xl dark:text-white") {
31
+ title
32
+ }
33
+ end
34
+
35
+ def render_description(description)
36
+ p(class: "text-gray-500 dark:text-gray-400") {
37
+ description
38
+ }
39
+ end
40
+
41
+ def render_actions
42
+ @actions.each do |action|
43
+ url = resource_url_for(resource_record || resource_class, *action.route_options.url_args, **action.route_options.url_options)
44
+ ActionButton(action, url:)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ class Base < Phlexi::Table::Base
7
+ include Plutonium::UI::Component::Behaviour
8
+
9
+ class Display < Plutonium::UI::Display::Base; end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ module Components
7
+ class PagyInfo < Plutonium::UI::Component::Base
8
+ include Pagy::Frontend
9
+
10
+ def initialize(pagy, per_page_options: [5, 10, 20, 50, 100])
11
+ @pagy = pagy
12
+ @per_page_options = (per_page_options + [@pagy.limit]).uniq.sort
13
+ end
14
+
15
+ def view_template
16
+ div(class: "flex flex-col md:flex-row justify-between items-center text-sm text-gray-500 dark:text-gray-400") do
17
+ results_info
18
+ per_page_selector
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def results_info
25
+ div do
26
+ plain "Showing "
27
+ b { @pagy.from.to_s }
28
+ plain " to "
29
+ b { @pagy.to.to_s }
30
+ plain " of "
31
+ b { @pagy.count }
32
+ plain " results"
33
+ end
34
+ end
35
+
36
+ def per_page_selector
37
+ original_attributes = Phlex::HTML::EVENT_ATTRIBUTES
38
+ temp_attributes = Phlex::HTML::EVENT_ATTRIBUTES.dup
39
+ temp_attributes.delete("onchange")
40
+ Phlex::HTML.const_set(:EVENT_ATTRIBUTES, temp_attributes)
41
+
42
+ div(class: "flex items-center space-x-2 mt-2 md:mt-0") do
43
+ label(for: "perPage", class: "mr-2") { "Per page" }
44
+ select(id: "perPage", name: "items", class: select_classes, onchange: "window.location.href=this.value") do
45
+ @per_page_options.each do |option|
46
+ option(value: page_url(option), selected: option == @pagy.limit) { option.to_s }
47
+ end
48
+ end
49
+ end
50
+ ensure
51
+ # TODO: remove this once Phlex adds support for SafeValues
52
+ Phlex::HTML.const_set(:EVENT_ATTRIBUTES, original_attributes)
53
+ end
54
+
55
+ def select_classes
56
+ "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
57
+ end
58
+
59
+ def page_url(limit)
60
+ original_limit = @pagy.vars[:limit]
61
+ @pagy.vars[:limit] = limit
62
+ pagy_url_for(@pagy, @pagy.page)
63
+ ensure
64
+ @pagy.vars[:limit] = original_limit
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ module Components
7
+ class PagyPageInfo < Plutonium::UI::Component::Base
8
+ include Pagy::Frontend
9
+
10
+ def initialize(pagy, per_page_options: [5, 10, 20, 50, 100])
11
+ @pagy = pagy
12
+ @per_page_options = (per_page_options + [@pagy.limit]).uniq.sort
13
+ end
14
+
15
+ def view_template
16
+ div(class: "flex flex-col md:flex-row justify-between items-center text-sm text-gray-500 dark:text-gray-400") do
17
+ results_info
18
+ per_page_selector
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def results_info
25
+ div do
26
+ plain "Showing "
27
+ b { @pagy.from.to_s }
28
+ plain " to "
29
+ b { @pagy.to.to_s }
30
+ plain " of "
31
+ b { @pagy.count }
32
+ plain " results"
33
+ end
34
+ end
35
+
36
+ def per_page_selector
37
+ original_attributes = Phlex::HTML::EVENT_ATTRIBUTES
38
+ temp_attributes = Phlex::HTML::EVENT_ATTRIBUTES.dup
39
+ temp_attributes.delete("onchange")
40
+ Phlex::HTML.const_set(:EVENT_ATTRIBUTES, temp_attributes)
41
+
42
+ div(class: "flex items-center space-x-2 mt-2 md:mt-0") do
43
+ label(for: "perPage", class: "mr-2") { "Per page" }
44
+ select(id: "perPage", name: "items", class: select_classes, onchange: "window.location.href=this.value") do
45
+ @per_page_options.each do |option|
46
+ option(value: page_url(option), selected: option == @pagy.limit) { option.to_s }
47
+ end
48
+ end
49
+ end
50
+ ensure
51
+ # TODO: remove this once Phlex adds support for SafeValues
52
+ Phlex::HTML.const_set(:EVENT_ATTRIBUTES, original_attributes)
53
+ end
54
+
55
+ def select_classes
56
+ "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
57
+ end
58
+
59
+ def page_url(limit)
60
+ original_limit = @pagy.vars[:limit]
61
+ @pagy.vars[:limit] = limit
62
+ pagy_url_for(@pagy, @pagy.page)
63
+ ensure
64
+ @pagy.vars[:limit] = original_limit
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Table
6
+ module Components
7
+ class PagyPagination < Plutonium::UI::Component::Base
8
+ include Pagy::Frontend
9
+
10
+ def initialize(pagy)
11
+ @pagy = pagy
12
+ end
13
+
14
+ def view_template
15
+ nav(aria_label: "Page navigation", class: "flex justify-center mt-4") do
16
+ ul(class: "inline-flex -space-x-px text-sm") do
17
+ prev_link
18
+ page_links
19
+ next_link
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def prev_link
27
+ li do
28
+ if @pagy.prev
29
+ a(href: page_url(@pagy.prev), class: link_classes(true)) {
30
+ render Phlex::TablerIcons::ChevronLeft.new
31
+ }
32
+ else
33
+ a(href: "#", class: disabled_link_classes(true), aria_disabled: "true") {
34
+ render Phlex::TablerIcons::ChevronLeft.new
35
+ }
36
+ end
37
+ end
38
+ end
39
+
40
+ def next_link
41
+ li do
42
+ if @pagy.next
43
+ a(href: page_url(@pagy.next), class: link_classes(false, true)) {
44
+ render Phlex::TablerIcons::ChevronRight.new
45
+ }
46
+ else
47
+ a(href: "#", class: disabled_link_classes(false, true), aria_disabled: "true") {
48
+ render Phlex::TablerIcons::ChevronRight.new
49
+ }
50
+ end
51
+ end
52
+ end
53
+
54
+ def page_links
55
+ @pagy.series.each do |item|
56
+ li do
57
+ case item
58
+ when Integer
59
+ page_link(item)
60
+ when String
61
+ current_page_link(item)
62
+ when :gap
63
+ gap_link
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def page_link(page)
70
+ a(href: page_url(page), class: link_classes) { page.to_s }
71
+ end
72
+
73
+ def current_page_link(page)
74
+ a(href: "#", class: current_link_classes, aria_current: "page") { page.to_s }
75
+ end
76
+
77
+ def gap_link
78
+ a(href: "#", class: link_classes, aria_disabled: "true") { "..." }
79
+ end
80
+
81
+ def link_classes(first = false, last = false)
82
+ classes = ["flex", "items-center", "justify-center", "px-3", "h-8", "leading-tight", "text-gray-500", "bg-white", "border", "border-gray-300", "hover:bg-gray-100", "hover:text-gray-700", "dark:bg-gray-800", "dark:border-gray-700", "dark:text-gray-400", "dark:hover:bg-gray-700", "dark:hover:text-white"]
83
+ classes << "rounded-s-lg" if first
84
+ classes << "rounded-e-lg" if last
85
+ classes.join(" ")
86
+ end
87
+
88
+ def current_link_classes
89
+ "flex items-center justify-center px-3 h-8 text-blue-600 border border-gray-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white"
90
+ end
91
+
92
+ def disabled_link_classes(first = false, last = false)
93
+ classes = link_classes(first, last).split
94
+ classes << "opacity-50" << "cursor-not-allowed"
95
+ classes.join(" ")
96
+ end
97
+
98
+ def page_url(page)
99
+ pagy_url_for(@pagy, page)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end