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,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