plutonium 0.15.0 → 0.15.2

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/views/resource/_interactive_action_form.html.erb +1 -0
  3. data/app/views/resource/{interactive_resource_collection_action.html.erb → interactive_bulk_action.html.erb} +1 -1
  4. data/app/views/resource/interactive_record_action.html.erb +1 -0
  5. data/app/views/resource/{interactive_resource_record_action.html.erb → interactive_resource_action.html.erb} +1 -1
  6. data/lib/plutonium/action/base.rb +6 -3
  7. data/lib/plutonium/action/interactive.rb +16 -9
  8. data/lib/plutonium/definition/base.rb +8 -1
  9. data/lib/plutonium/definition/sorting.rb +15 -0
  10. data/lib/plutonium/interaction/README.md +34 -1
  11. data/lib/plutonium/interaction/base.rb +38 -7
  12. data/lib/plutonium/interaction/concerns/presentable.rb +24 -12
  13. data/lib/plutonium/interaction/outcome.rb +77 -55
  14. data/lib/plutonium/interaction/response/base.rb +3 -1
  15. data/lib/plutonium/interaction/response/failure.rb +18 -0
  16. data/lib/plutonium/interaction/response/file.rb +20 -0
  17. data/lib/plutonium/interaction/response/redirect.rb +1 -11
  18. data/lib/plutonium/interaction/response/render.rb +1 -9
  19. data/lib/plutonium/resource/controllers/authorizable.rb +29 -5
  20. data/lib/plutonium/resource/controllers/interactive_actions.rb +171 -130
  21. data/lib/plutonium/resource/controllers/queryable.rb +2 -2
  22. data/lib/plutonium/resource/interaction.rb +1 -3
  23. data/lib/plutonium/resource/query_object.rb +2 -2
  24. data/lib/plutonium/routing/mapper_extensions.rb +9 -9
  25. data/lib/plutonium/ui/action_button.rb +4 -3
  26. data/lib/plutonium/ui/component/methods.rb +1 -0
  27. data/lib/plutonium/ui/display/theme.rb +4 -3
  28. data/lib/plutonium/ui/form/interaction.rb +35 -0
  29. data/lib/plutonium/ui/form/resource.rb +1 -1
  30. data/lib/plutonium/ui/page/interactive_action.rb +23 -0
  31. data/lib/plutonium/ui/table/components/search_bar.rb +1 -1
  32. data/lib/plutonium/ui/table/display_theme.rb +2 -2
  33. data/lib/plutonium/ui/table/resource.rb +11 -5
  34. data/lib/plutonium/version.rb +1 -1
  35. data/lib/plutonium.rb +1 -0
  36. metadata +11 -21
  37. data/app/views/resource/_interactive_resource_action_form.html.erb +0 -45
  38. data/app/views/resource/interactive_resource_recordless_action.html.erb +0 -5
  39. data/gemfiles/rails_7.gemfile.lock +0 -339
@@ -26,7 +26,6 @@ module Plutonium
26
26
  class ActionMissingCurrentAuthorizedScope < ActionPolicy::UnauthorizedAction; end
27
27
 
28
28
  included do
29
- verify_authorized
30
29
  after_action :verify_authorize_current
31
30
  after_action :verify_current_authorized_scope, except: %i[new create]
32
31
 
@@ -35,28 +34,53 @@ module Plutonium
35
34
  attr_writer :authorize_current_count
36
35
  attr_writer :current_authorized_scope_count
37
36
 
37
+ attr_reader :verify_authorize_current_skipped
38
+ attr_reader :verify_current_authorized_scope_skipped
39
+
38
40
  protected :authorize_current_count=, :authorize_current_count
39
41
  protected :current_authorized_scope_count=, :current_authorized_scope_count
40
42
  end
41
43
 
44
+ class_methods do
45
+ # Skips verify_authorize_current after_action callback.
46
+ def skip_verify_authorize_current(**options)
47
+ skip_after_action :verify_authorize_current, options
48
+ end
49
+
50
+ # Skips verify_current_authorized_scope after_action callback.
51
+ def skip_verify_current_authorized_scope(**options)
52
+ skip_after_action :verify_current_authorized_scope, options
53
+ end
54
+ end
55
+
42
56
  private
43
57
 
58
+ def skip_verify_authorize_current!
59
+ @verify_authorize_current_skipped = true
60
+ end
61
+
62
+ def skip_verify_current_authorized_scope!
63
+ @verify_current_authorized_scope_skipped = true
64
+ end
65
+
44
66
  # Verifies that authorize_current has been called
45
67
  #
46
68
  # @raise [ActionMissingAuthorizeCurrent] if authorize_current hasn't been called
47
69
  def verify_authorize_current
48
- return if verify_authorized_skipped
70
+ return if verify_authorize_current_skipped
71
+ return if authorize_current_count > 0
49
72
 
50
- raise ActionMissingAuthorizeCurrent.new(controller_path, action_name) if authorize_current_count.zero?
73
+ raise ActionMissingAuthorizeCurrent.new(controller_path, action_name)
51
74
  end
52
75
 
53
76
  # Verifies that current_authorized_scope has been called
54
77
  #
55
78
  # @raise [ActionMissingCurrentAuthorizedScope] if current_authorized_scope hasn't been called
56
79
  def verify_current_authorized_scope
57
- return if verify_authorized_skipped
80
+ return if current_authorized_scope_count
81
+ return if current_authorized_scope_count > 0
58
82
 
59
- raise ActionMissingCurrentAuthorizedScope.new(controller_path, action_name) if current_authorized_scope_count.zero?
83
+ raise ActionMissingCurrentAuthorizedScope.new(controller_path, action_name)
60
84
  end
61
85
 
62
86
  # @return [Integer] the number of times authorize_current has been called
@@ -7,169 +7,178 @@ module Plutonium
7
7
  included do
8
8
  helper_method :current_interactive_action
9
9
 
10
- before_action :validate_interactive_resource_action!, only: %i[
11
- begin_interactive_resource_record_action commit_interactive_resource_record_action
12
- begin_interactive_resource_collection_action commit_interactive_resource_collection_action
13
- begin_interactive_resource_recordless_action commit_interactive_resource_recordless_action
10
+ before_action :validate_interactive_action!, only: %i[
11
+ interactive_record_action commit_interactive_record_action
12
+ interactive_bulk_action commit_interactive_bulk_action
13
+ interactive_resource_action commit_interactive_resource_action
14
14
  ]
15
15
 
16
- before_action :authorize_interactive_resource_record_action!, only: %i[
17
- begin_interactive_resource_record_action commit_interactive_resource_record_action
16
+ before_action :authorize_interactive_record_action!, only: %i[
17
+ interactive_record_action commit_interactive_record_action
18
18
  ]
19
19
 
20
20
  before_action :authorize_interactive_resource_action!, only: %i[
21
- begin_interactive_resource_collection_action commit_interactive_resource_collection_action
22
- begin_interactive_resource_recordless_action commit_interactive_resource_recordless_action
21
+ interactive_bulk_action commit_interactive_bulk_action
22
+ interactive_resource_action commit_interactive_resource_action
23
23
  ]
24
24
  end
25
25
 
26
- # GET /resources/1/actions/:interactive_action
27
- def begin_interactive_resource_record_action
28
- @interaction = current_interactive_action.interaction.new interaction_params
26
+ # GET /resources/1/record_actions/:interactive_action
27
+ def interactive_record_action
28
+ build_interactive_record_action_interaction
29
29
 
30
30
  if helpers.current_turbo_frame == "modal"
31
31
  render layout: false
32
32
  else
33
- render :interactive_resource_record_action
33
+ render :interactive_record_action
34
34
  end
35
35
  end
36
36
 
37
- # POST /resources/1/actions/:interactive_action
38
- def commit_interactive_resource_record_action
39
- respond_to do |format|
40
- inputs = interaction_params.merge(resource: resource_record)
41
- @interaction = current_interactive_action.interaction.run(inputs)
42
-
43
- if @interaction.valid?
44
- flash[:notice] = "TODO:#{current_interactive_action} was successfully updated."
45
-
46
- format.html { redirect_to resource_url_for(@interaction.result), status: :see_other }
47
- format.any { render :show, status: :ok, location: resource_url_for(@interaction.result) }
48
-
49
- if helpers.current_turbo_frame == "modal"
50
- format.turbo_stream do
51
- render turbo_stream: [
52
- turbo_stream.redirect(resource_url_for(@interaction.result))
53
- ]
37
+ # POST /resources/1/record_actions/:interactive_action
38
+ def commit_interactive_record_action
39
+ build_interactive_record_action_interaction
40
+
41
+ outcome = @interaction.call
42
+ if outcome.success?
43
+ outcome.to_response.process(self) do |value|
44
+ respond_to do |format|
45
+ return_url = redirect_url_after_action_on(resource_record)
46
+ format.any { redirect_to return_url, status: :see_other }
47
+ if helpers.current_turbo_frame == "modal"
48
+ format.turbo_stream do
49
+ render turbo_stream: [
50
+ turbo_stream.redirect(return_url)
51
+ ]
52
+ end
54
53
  end
55
54
  end
56
- else
57
- format.html do
58
- render :interactive_resource_record_action, status: :unprocessable_entity
59
- end
60
- format.any do
61
- @errors = @interaction.errors
62
- render "errors", status: :unprocessable_entity
63
- end
64
-
65
- if helpers.current_turbo_frame == "modal"
66
- format.turbo_stream do
67
- render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_resource_record_action_form")
55
+ end
56
+ else
57
+ outcome.to_response.process(self) do
58
+ respond_to do |format|
59
+ format.html do
60
+ render :interactive_record_action, status: :unprocessable_entity
61
+ end
62
+ format.any do
63
+ @errors = @interaction.errors
64
+ render "errors", status: :unprocessable_entity
65
+ end
66
+ if helpers.current_turbo_frame == "modal"
67
+ format.turbo_stream do
68
+ render turbo_stream: [
69
+ turbo_stream.replace(:modal, partial: "interactive_action_form")
70
+ ]
71
+ end
68
72
  end
69
73
  end
70
74
  end
71
75
  end
72
76
  end
73
77
 
74
- # GET /resources/actions/:interactive_action?ids[]=1&ids[]=2
75
- def begin_interactive_resource_collection_action
76
- # TODO: ensure that the selected list matches the returned value
77
- interactive_resource_collection
78
- @interaction = current_interactive_action.interaction.new((params[:interaction] || {}).except(:resources))
78
+ # GET /resources/resource_actions/:interactive_action
79
+ def interactive_resource_action
80
+ skip_verify_current_authorized_scope!
81
+ build_interactive_resource_action_interaction
79
82
 
80
83
  if helpers.current_turbo_frame == "modal"
81
84
  render layout: false
82
85
  else
83
- render :interactive_resource_collection_action
86
+ render :interactive_resource_action
84
87
  end
85
88
  end
86
89
 
87
- # POST /resources/actions/:interactive_action?ids[]=1&ids[]=2
88
- def commit_interactive_resource_collection_action
89
- respond_to do |format|
90
- inputs = interaction_params.merge(resources: interactive_resource_collection)
91
- @interaction = current_interactive_action.interaction.run(inputs)
92
-
93
- if @interaction.valid?
94
- collection_count = interactive_resource_collection.size
95
-
96
- flash[:notice] = "TODO:#{current_interactive_action} #{collection_count} #{resource_class.model_name.human.pluralize(collection_count)} successfully updated."
97
-
98
- format.html { redirect_to resource_url_for(resource_class) }
99
- if helpers.current_turbo_frame == "modal"
100
- format.turbo_stream do
101
- render turbo_stream: [
102
- turbo_stream.redirect(resource_url_for(resource_class))
103
- ]
90
+ # POST /resources/resource_actions/:interactive_action
91
+ def commit_interactive_resource_action
92
+ skip_verify_current_authorized_scope!
93
+ build_interactive_resource_action_interaction
94
+
95
+ outcome = @interaction.call
96
+ if outcome.success?
97
+ outcome.to_response.process(self) do |value|
98
+ respond_to do |format|
99
+ return_url = redirect_url_after_action_on(resource_class)
100
+ format.any { redirect_to return_url, status: :see_other }
101
+ if helpers.current_turbo_frame == "modal"
102
+ format.turbo_stream do
103
+ render turbo_stream: [
104
+ turbo_stream.redirect(return_url)
105
+ ]
106
+ end
104
107
  end
105
108
  end
106
- else
107
- format.html do
108
- render :interactive_resource_collection_action, status: :unprocessable_entity
109
- end
110
- format.any do
111
- @errors = @interaction.errors
112
- render "errors", status: :unprocessable_entity
113
- end
114
-
115
- if helpers.current_turbo_frame == "modal"
116
- format.turbo_stream do
117
- render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_resource_collection_action_form")
109
+ end
110
+ else
111
+ outcome.to_response.process(self) do
112
+ respond_to do |format|
113
+ format.html do
114
+ render :interactive_record_action, status: :unprocessable_entity
115
+ end
116
+ format.any do
117
+ @errors = @interaction.errors
118
+ render "errors", status: :unprocessable_entity
119
+ end
120
+ if helpers.current_turbo_frame == "modal"
121
+ format.turbo_stream do
122
+ render turbo_stream: [
123
+ turbo_stream.replace(:modal, partial: "interactive_action_form")
124
+ ]
125
+ end
118
126
  end
119
127
  end
120
128
  end
121
129
  end
122
130
  end
123
131
 
124
- # GET /resources/actions/:interactive_action
125
- def begin_interactive_resource_recordless_action
126
- # skip_policy_scope
127
-
128
- @interaction = current_interactive_action.interaction.new interaction_params
129
-
130
- if helpers.current_turbo_frame == "modal"
131
- render layout: false
132
- else
133
- render :interactive_resource_recordless_action
134
- end
132
+ # GET /resources/bulk_actions/:interactive_action?ids[]=1&ids[]=2
133
+ def interactive_bulk_action
134
+ raise NotImplementedError
135
+ # # TODO: ensure that the selected list matches the returned value
136
+ # interactive_bulk
137
+ # @interaction = current_interactive_action.interaction.new((params[:interaction] || {}).except(:resources))
138
+
139
+ # if helpers.current_turbo_frame == "modal"
140
+ # render layout: false
141
+ # else
142
+ # render :interactive_bulk_action
143
+ # end
135
144
  end
136
145
 
137
- # POST /resources/actions/:interactive_action
138
- def commit_interactive_resource_recordless_action
139
- # skip_policy_scope
140
-
141
- respond_to do |format|
142
- inputs = interaction_params
143
- @interaction = current_interactive_action.interaction.run(inputs)
144
-
145
- if @interaction.valid?
146
- flash[:notice] = "TODO:#{current_interactive_action} was successfully updated."
147
-
148
- format.html { redirect_to resource_url_for(resource_class) }
149
-
150
- if helpers.current_turbo_frame == "modal"
151
- format.turbo_stream do
152
- render turbo_stream: [
153
- turbo_stream.redirect(resource_url_for(resource_class))
154
- ]
155
- end
156
- end
157
- else
158
- format.html do
159
- render :interactive_resource_recordless_action, status: :unprocessable_entity
160
- end
161
- format.any do
162
- @errors = @interaction.errors
163
- render "errors", status: :unprocessable_entity
164
- end
165
-
166
- if helpers.current_turbo_frame == "modal"
167
- format.turbo_stream do
168
- render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_resource_recordless_action_form")
169
- end
170
- end
171
- end
172
- end
146
+ # POST /resources/bulk_actions/:interactive_action?ids[]=1&ids[]=2
147
+ def commit_interactive_bulk_action
148
+ raise NotImplementedError
149
+ # respond_to do |format|
150
+ # inputs = interaction_params.merge(resources: interactive_bulk)
151
+ # @interaction = current_interactive_action.interaction.run(inputs)
152
+
153
+ # if @interaction.valid?
154
+ # collection_count = interactive_bulk.size
155
+
156
+ # flash[:notice] = "TODO:#{current_interactive_action} #{collection_count} #{resource_class.model_name.human.pluralize(collection_count)} successfully updated."
157
+
158
+ # format.html { redirect_to resource_url_for(resource_class) }
159
+ # if helpers.current_turbo_frame == "modal"
160
+ # format.turbo_stream do
161
+ # render turbo_stream: [
162
+ # turbo_stream.redirect(resource_url_for(resource_class))
163
+ # ]
164
+ # end
165
+ # end
166
+ # else
167
+ # format.html do
168
+ # render :interactive_bulk_action, status: :unprocessable_entity
169
+ # end
170
+ # format.any do
171
+ # @errors = @interaction.errors
172
+ # render "errors", status: :unprocessable_entity
173
+ # end
174
+
175
+ # if helpers.current_turbo_frame == "modal"
176
+ # format.turbo_stream do
177
+ # render turbo_stream: turbo_stream.replace(:modal, partial: "interactive_bulk_action_form")
178
+ # end
179
+ # end
180
+ # end
181
+ # end
173
182
  end
174
183
 
175
184
  private
@@ -179,17 +188,19 @@ module Plutonium
179
188
  end
180
189
 
181
190
  def interactive_resource_actions
182
- @interactive_resource_actions ||= current_presenter.actions.except :new, :show, :edit, :destroy
191
+ @interactive_resource_actions ||= current_definition
192
+ .defined_actions
193
+ .select { |k, v| v.is_a?(Plutonium::Action::Interactive) }
183
194
  end
184
195
 
185
- def validate_interactive_resource_action!
196
+ def validate_interactive_action!
186
197
  interactive_resource_action = params[:interactive_action]&.to_sym
187
198
  unless interactive_resource_actions.key?(interactive_resource_action)
188
199
  raise ::AbstractController::ActionNotFound, "Unknown action '#{interactive_resource_action}'"
189
200
  end
190
201
  end
191
202
 
192
- def authorize_interactive_resource_record_action!
203
+ def authorize_interactive_record_action!
193
204
  interactive_resource_action = params[:interactive_action]&.to_sym
194
205
  authorize_current! resource_record, to: :"#{interactive_resource_action}?"
195
206
  end
@@ -199,13 +210,43 @@ module Plutonium
199
210
  authorize_current! resource_class, to: :"#{interactive_resource_action}?"
200
211
  end
201
212
 
202
- def interactive_resource_collection
203
- @interactive_resource_collection ||= current_authorized_scope.from_path_param(params.require(:ids))
213
+ def interactive_bulk
214
+ @interactive_bulk ||= current_authorized_scope.from_path_param(params.require(:ids))
215
+ end
216
+
217
+ def build_interactive_record_action_interaction
218
+ @interaction = current_interactive_action.interaction.new
219
+ @interaction.attributes = interaction_params.merge(resource: resource_record)
220
+ @interaction
221
+ end
222
+
223
+ def build_interactive_resource_action_interaction
224
+ @interaction = current_interactive_action.interaction.new
225
+ @interaction.attributes = interaction_params
226
+ @interaction
227
+ end
228
+
229
+ # Returns the submitted resource parameters
230
+ # @return [Hash] The submitted resource parameters
231
+ def submitted_interaction_params
232
+ @submitted_interaction_params ||= current_interactive_action
233
+ .interaction
234
+ .build_form(nil)
235
+ .extract_input(params)[:interaction]
236
+ end
237
+
238
+ def redirect_url_after_action_on(resource_record_or_resource_class)
239
+ if (return_to = url_from(params[:return_to]))
240
+ return return_to
241
+ end
242
+
243
+ resource_url_for(resource_record_or_resource_class)
204
244
  end
205
245
 
246
+ # Returns the resource parameters, including scoped and parent parameters
247
+ # @return [Hash] The resource parameters
206
248
  def interaction_params
207
- # active interaction handles filtering so no need for strong params
208
- (params[:interaction] || {}).except(:resource, :resources)
249
+ @interaction_params ||= submitted_interaction_params.except(:resource, :resources)
209
250
  end
210
251
  end
211
252
  end
@@ -24,11 +24,11 @@ module Plutonium
24
24
  end
25
25
 
26
26
  current_definition.defined_scopes.each do |key, value|
27
- query_object.define_scope key, value[:block]
27
+ query_object.define_scope key, value[:block], **value[:options]
28
28
  end
29
29
 
30
30
  current_definition.defined_sorts.each do |key, value|
31
- query_object.define_sorter key, value[:block]
31
+ query_object.define_sorter key, value[:block], **value[:options]
32
32
  end
33
33
 
34
34
  query_object
@@ -1,8 +1,6 @@
1
- require "active_interaction"
2
-
3
1
  module Plutonium
4
2
  module Resource
5
- class Interaction < ActiveInteraction::Base
3
+ class Interaction < Plutonium::Interaction::Base
6
4
  end
7
5
  end
8
6
  end
@@ -138,9 +138,9 @@ module Plutonium
138
138
  #
139
139
  # @param name [Symbol] The name of the sort.
140
140
  # @param body [Proc, nil] The body of the sort.
141
- def define_sorter(name, body = nil)
141
+ def define_sorter(name, body = nil, using: nil)
142
142
  if body.nil?
143
- sort_field = determine_sort_field(name)
143
+ sort_field = using || determine_sort_field(name)
144
144
  body = ->(scope, direction:) { scope.order(sort_field => direction) }
145
145
  end
146
146
 
@@ -90,9 +90,9 @@ module Plutonium
90
90
  # @return [void]
91
91
  def define_member_interactive_actions
92
92
  member do
93
- get "record_actions/:interactive_action", action: :begin_interactive_resource_record_action,
94
- as: :interactive_resource_record_action
95
- post "record_actions/:interactive_action", action: :commit_interactive_resource_record_action
93
+ get "record_actions/:interactive_action", action: :interactive_record_action,
94
+ as: :record_action
95
+ post "record_actions/:interactive_action", action: :commit_interactive_record_action
96
96
  end
97
97
  end
98
98
 
@@ -101,13 +101,13 @@ module Plutonium
101
101
  # @return [void]
102
102
  def define_collection_interactive_actions
103
103
  collection do
104
- get "collection_actions/:interactive_action", action: :begin_interactive_resource_collection_action,
105
- as: :interactive_resource_collection_action
106
- post "collection_actions/:interactive_action", action: :commit_interactive_resource_collection_action
104
+ get "bulk_actions/:interactive_action", action: :interactive_bulk_action,
105
+ as: :bulk_action
106
+ post "bulk_actions/:interactive_action", action: :commit_interactive_bulk_action
107
107
 
108
- get "recordless_actions/:interactive_action", action: :begin_interactive_resource_recordless_action,
109
- as: :interactive_resource_recordless_action
110
- post "recordless_actions/:interactive_action", action: :commit_interactive_resource_recordless_action
108
+ get "resource_actions/:interactive_action", action: :interactive_resource_action,
109
+ as: :resource_action
110
+ post "resource_actions/:interactive_action", action: :commit_interactive_resource_action
111
111
  end
112
112
  end
113
113
 
@@ -40,6 +40,7 @@ module Plutonium
40
40
  class: "inline-block",
41
41
  form: {
42
42
  data: {
43
+ turbo: @action.turbo,
43
44
  turbo_confirm: @action.confirmation,
44
45
  turbo_frame: @action.turbo_frame
45
46
  }
@@ -55,7 +56,7 @@ module Plutonium
55
56
  if @action.icon
56
57
  render @action.icon.new(class: icon_classes)
57
58
  end
58
- span { @action.label } unless @variant == :table
59
+ span { @action.label }
59
60
  end
60
61
 
61
62
  def button_classes
@@ -69,7 +70,7 @@ module Plutonium
69
70
 
70
71
  def base_classes
71
72
  if @variant == :table
72
- "inline-flex items-center justify-center p-1 rounded-lg focus:outline-none focus:ring-2"
73
+ "inline-flex items-center justify-center py-1 px-2 rounded-lg focus:outline-none focus:ring-2"
73
74
  else
74
75
  "flex items-center justify-center px-4 py-2 text-sm font-medium rounded-lg focus:outline-none focus:ring-4"
75
76
  end
@@ -77,7 +78,7 @@ module Plutonium
77
78
 
78
79
  def icon_classes
79
80
  if @variant == :table
80
- "h-4 w-4"
81
+ "h-4 w-4 mr-1"
81
82
  else
82
83
  "h-3.5 w-3.5 mr-2 -ml-1"
83
84
  end
@@ -44,6 +44,7 @@ module Plutonium
44
44
  :resource_query_params,
45
45
  :current_policy,
46
46
  :current_turbo_frame,
47
+ :current_interactive_action,
47
48
  :policy_for,
48
49
  :allowed_to?,
49
50
  :registered_resources,
@@ -7,17 +7,18 @@ module Plutonium
7
7
  def self.theme
8
8
  super.merge({
9
9
  base: "relative bg-white dark:bg-gray-800 shadow-md sm:rounded-lg my-3",
10
+ value_wrapper: "max-h-[300px] overflow-y-auto",
10
11
  fields_wrapper: "p-6 grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-6 gap-y-10 grid-flow-row-dense",
11
12
  label: "text-base font-bold text-gray-500 dark:text-gray-400 mb-1",
12
13
  description: "text-sm text-gray-400 dark:text-gray-500",
13
14
  placeholder: "text-md text-gray-500 dark:text-gray-300 mb-1 italic",
14
- string: "max-h-[300px] overflow-y-auto text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
15
- text: "max-h-[300px] overflow-y-auto text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
15
+ string: "text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
16
+ text: "text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
16
17
  link: "text-primary-600 dark:text-primary-500 whitespace-pre-line",
17
18
  color: "flex items-center text-md text-gray-900 dark:text-white mb-1 whitespace-pre-line",
18
19
  color_indicator: "w-10 h-10 rounded-full mr-2", # max-h-fit
19
20
  email: "flex items-center text-md text-primary-600 dark:text-primary-500 mb-1 whitespace-pre-line",
20
- json: "max-h-[300px] overflow-y-auto text-sm text-gray-900 dark:text-white mb-1 whitespace-pre font-mono shadow-inner p-4",
21
+ json: "text-sm text-gray-900 dark:text-white mb-1 whitespace-pre font-mono shadow-inner p-4",
21
22
  prefixed_icon: "w-8 h-8 mr-2"
22
23
  })
23
24
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Form
6
+ class Interaction < Resource
7
+ def initialize(interaction, *, **options, &)
8
+ options[:key] = :interaction
9
+ options[:resource_fields] = interaction.attribute_names.map(&:to_sym) - %i[resource resources]
10
+ options[:resource_definition] = interaction
11
+
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def form_action
18
+ # interactive action forms post to the same page
19
+ nil
20
+ end
21
+
22
+ def initialize_attributes
23
+ super
24
+ attributes.fetch(:data_turbo) { attributes[:data_turbo] = object.turbo.to_s }
25
+ end
26
+
27
+ def submit_button(*, **)
28
+ super(*, **) do
29
+ object.label
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -65,7 +65,7 @@ module Plutonium
65
65
  end
66
66
 
67
67
  def when_permitted(name, &)
68
- return unless @resource_fields.include? name
68
+ return unless resource_fields.include? name
69
69
 
70
70
  yield
71
71
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module UI
5
+ module Page
6
+ class InteractiveAction < Base
7
+ private
8
+
9
+ def page_title
10
+ current_interactive_action.label || super
11
+ end
12
+
13
+ def page_description
14
+ current_interactive_action.description || super
15
+ end
16
+
17
+ def render_default_content
18
+ render "interactive_action_form"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end