forest_admin_datasource_customizer 1.0.0.pre.beta.65 → 1.0.0.pre.beta.67

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ff06d84529de19b6e3d26bb2c13bf90ec9777198f3fe817ee5232bf395e1b31
4
- data.tar.gz: b84c75a15e00e7865e9ed37b14cc8a8b60fd1120d9c8d712f514656b46f7abe8
3
+ metadata.gz: dc7be9cd681f1268f79a3196e2df52f823bf27435addf2bafd6149d54c2cccae
4
+ data.tar.gz: 4ec5d39db429e15805cd46c0ae414a1486c24c42df2f2bd89c00b171e01bb7ab
5
5
  SHA512:
6
- metadata.gz: b5ec8e8088d0d82a6089795dba63674e589c3ca90e226808c5c73d6d3f78dffd5ce6f3862a5699f9cbe9505eeb3d2f7ca5a744a84ed495969954e13c0743aa0d
7
- data.tar.gz: 9f124ec9a5a2e75741ce5d405fb6968bda58f5c231727bb6ea99d5b6c82603426f278ad93851720cd25f98aa0a406ce575f1d2294d46e4cc0d274c06c0593b15
6
+ metadata.gz: d7f31dd4e49979ecd1b488b505eaf7c87ed8a41f2301578a2b4b26458075e64fd491c15b58638663d166da004d2c1f972412c5c4418809e77533b4bdc736de9c
7
+ data.tar.gz: 928ae535652f6eba9f2ccf93686d09e71b0a51d0d8a98e24fd70a702e37f8734f5f0358b2b2c30bfc30d5972135b31ff02b37fadda364d9116f12304986e98f9
@@ -10,7 +10,7 @@ module ForestAdminDatasourceCustomizer
10
10
  end
11
11
 
12
12
  def add_action(name, action)
13
- action.build_fields
13
+ action.build_elements
14
14
  @actions[name] = action
15
15
 
16
16
  mark_schema_as_dirty
@@ -51,6 +51,8 @@ module ForestAdminDatasourceCustomizer
51
51
  fields = drop_deferred(context, metas[:search_values], dynamic_fields)
52
52
 
53
53
  fields.each do |field|
54
+ next if field.type == 'Layout'
55
+
54
56
  if field.value.nil?
55
57
  # customer did not define a handler to rewrite the previous value => reuse current one.
56
58
  field.value = form_values[field.label]
@@ -72,13 +74,20 @@ module ForestAdminDatasourceCustomizer
72
74
  private
73
75
 
74
76
  def drop_defaults(context, fields, data)
75
- unvalued_fields = fields.reject { |field| data.key?(field.label) }
76
- defaults = unvalued_fields.map { |field| evaluate(context, field.default_value) }
77
- unvalued_fields.each_with_index { |field, index| data[field.label] = defaults[index] }
77
+ fields.map do |field|
78
+ if field.type == 'Layout'
79
+ field
80
+ else
81
+ drop_default(context, field, data)
82
+ end
83
+ end
84
+ end
78
85
 
79
- fields.each { |field| field.default_value = nil }
86
+ def drop_default(context, field, data)
87
+ data[field.label] = evaluate(context, field.default_value) unless data.key?(field.label)
88
+ field.default_value = nil
80
89
 
81
- fields
90
+ field
82
91
  end
83
92
 
84
93
  def drop_ifs(context, fields)
@@ -105,7 +114,8 @@ module ForestAdminDatasourceCustomizer
105
114
  value = field.send(key)
106
115
  key = key.to_s.concat('=').to_sym
107
116
 
108
- field.send(key, evaluate(context, value, search_values&.dig(field.label)))
117
+ search_value = field.type == 'Layout' ? nil : search_values&.dig(field.label)
118
+ field.send(key, evaluate(context, value, search_value))
109
119
  end
110
120
 
111
121
  new_fields << Actions::ActionFieldFactory.build(field.to_h)
@@ -11,51 +11,71 @@ module ForestAdminDatasourceCustomizer
11
11
  @execute = execute
12
12
  end
13
13
 
14
- def build_fields
14
+ def build_elements
15
15
  @form = @form&.map do |field|
16
- case field[:widget]
17
- when 'AddressAutocomplete'
18
- WidgetField::AddressAutocompleteField.new(**field)
19
- when 'Checkbox'
20
- WidgetField::CheckboxField.new(**field)
21
- when 'CheckboxGroup'
22
- WidgetField::CheckboxGroupField.new(**field)
23
- when 'ColorPicker'
24
- WidgetField::ColorPickerField.new(**field)
25
- when 'CurrencyInput'
26
- WidgetField::CurrencyInputField.new(**field)
27
- when 'DatePicker'
28
- WidgetField::DatePickerField.new(**field)
29
- when 'Dropdown'
30
- WidgetField::DropdownField.new(**field)
31
- when 'FilePicker'
32
- WidgetField::FilePickerField.new(**field)
33
- when 'JsonEditor'
34
- WidgetField::JsonEditorField.new(**field)
35
- when 'NumberInput'
36
- WidgetField::NumberInputField.new(**field)
37
- when 'NumberInputList'
38
- WidgetField::NumberInputListField.new(**field)
39
- when 'RadioGroup'
40
- WidgetField::RadioGroupField.new(**field)
41
- when 'RichText'
42
- WidgetField::RichTextField.new(**field)
43
- when 'TextArea'
44
- WidgetField::TextAreaField.new(**field)
45
- when 'TextInput'
46
- WidgetField::TextInputField.new(**field)
47
- when 'TextInputList'
48
- WidgetField::TextInputListField.new(**field)
49
- when 'TimePicker'
50
- WidgetField::TimePickerField.new(**field)
51
- when 'UserDropdown'
52
- WidgetField::UserDropdownField.new(**field)
16
+ if field.key? :widget
17
+ build_widget(field)
18
+ elsif field[:type] == 'Layout'
19
+ build_layout_element(field)
53
20
  else
54
21
  DynamicField.new(**field)
55
22
  end
56
23
  end
57
24
  end
58
25
 
26
+ def build_widget(field)
27
+ case field[:widget]
28
+ when 'AddressAutocomplete'
29
+ WidgetField::AddressAutocompleteField.new(**field)
30
+ when 'Checkbox'
31
+ WidgetField::CheckboxField.new(**field)
32
+ when 'CheckboxGroup'
33
+ WidgetField::CheckboxGroupField.new(**field)
34
+ when 'ColorPicker'
35
+ WidgetField::ColorPickerField.new(**field)
36
+ when 'CurrencyInput'
37
+ WidgetField::CurrencyInputField.new(**field)
38
+ when 'DatePicker'
39
+ WidgetField::DatePickerField.new(**field)
40
+ when 'Dropdown'
41
+ WidgetField::DropdownField.new(**field)
42
+ when 'FilePicker'
43
+ WidgetField::FilePickerField.new(**field)
44
+ when 'JsonEditor'
45
+ WidgetField::JsonEditorField.new(**field)
46
+ when 'NumberInput'
47
+ WidgetField::NumberInputField.new(**field)
48
+ when 'NumberInputList'
49
+ WidgetField::NumberInputListField.new(**field)
50
+ when 'RadioGroup'
51
+ WidgetField::RadioGroupField.new(**field)
52
+ when 'RichText'
53
+ WidgetField::RichTextField.new(**field)
54
+ when 'TextArea'
55
+ WidgetField::TextAreaField.new(**field)
56
+ when 'TextInput'
57
+ WidgetField::TextInputField.new(**field)
58
+ when 'TextInputList'
59
+ WidgetField::TextInputListField.new(**field)
60
+ when 'TimePicker'
61
+ WidgetField::TimePickerField.new(**field)
62
+ when 'UserDropdown'
63
+ WidgetField::UserDropdownField.new(**field)
64
+ else
65
+ raise ForestAdminDatasourceToolkit::Exceptions::ForestException, "Unknow widget type: #{field[:widget]}"
66
+ end
67
+ end
68
+
69
+ def build_layout_element(field)
70
+ case field[:component]
71
+ when 'Separator'
72
+ FormLayoutElement::SeparatorElement.new(**field)
73
+ else
74
+ raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
75
+ "Unknow component type: #{field[:component]}"
76
+ end
77
+ end
78
+
59
79
  def static_form?
60
80
  return form&.all?(&:static?) if form
61
81
 
@@ -0,0 +1,29 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ module Decorators
3
+ module Action
4
+ class BaseFormElement
5
+ attr_accessor :type
6
+
7
+ def initialize(
8
+ type:,
9
+ **_kwargs
10
+ )
11
+ @type = type
12
+ end
13
+
14
+ def static?
15
+ instance_variables.all? { |attribute| !instance_variable_get(attribute).respond_to?(:call) }
16
+ end
17
+
18
+ def to_h
19
+ result = {}
20
+ instance_variables.each do |attribute|
21
+ result[attribute.to_s.delete('@').to_sym] = instance_variable_get(attribute)
22
+ end
23
+
24
+ result
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,7 +1,7 @@
1
1
  module ForestAdminDatasourceCustomizer
2
2
  module Decorators
3
3
  module Action
4
- class DynamicField
4
+ class DynamicField < BaseFormElement
5
5
  attr_accessor :type, :label, :description, :is_required, :is_read_only, :if_condition, :value, :default_value,
6
6
  :collection_name, :enum_values, :placeholder
7
7
 
@@ -19,7 +19,8 @@ module ForestAdminDatasourceCustomizer
19
19
  placeholder: nil,
20
20
  **_kwargs
21
21
  )
22
- @type = type
22
+ super(type: type)
23
+
23
24
  @label = label
24
25
  @description = description
25
26
  @is_required = is_required
@@ -31,19 +32,6 @@ module ForestAdminDatasourceCustomizer
31
32
  @enum_values = enum_values
32
33
  @placeholder = placeholder
33
34
  end
34
-
35
- def static?
36
- instance_variables.all? { |attribute| !instance_variable_get(attribute).respond_to?(:call) }
37
- end
38
-
39
- def to_h
40
- result = {}
41
- instance_variables.each do |attribute|
42
- result[attribute.to_s.delete('@').to_sym] = instance_variable_get(attribute)
43
- end
44
-
45
- result
46
- end
47
35
  end
48
36
  end
49
37
  end
@@ -0,0 +1,26 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ module Decorators
3
+ module Action
4
+ module FormLayoutElement
5
+ include Types
6
+
7
+ class LayoutElement < BaseFormElement
8
+ attr_accessor :if_condition, :component
9
+
10
+ def initialize(component:, if_condition: nil, **kwargs)
11
+ super(type: 'Layout', **kwargs)
12
+
13
+ @component = component
14
+ @if_condition = if_condition
15
+ end
16
+ end
17
+
18
+ class SeparatorElement < LayoutElement
19
+ def initialize(options)
20
+ super(component: 'Separator', **options)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,343 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ module Decorators
3
+ module Action
4
+ module LayoutElement
5
+ include Types
6
+
7
+ class TimePickerField < DynamicField
8
+ attr_accessor :widget
9
+
10
+ def initialize(options)
11
+ super(**options)
12
+ WidgetField.validate_arg(options, :type, { type: 'contains', value: ['Time'] })
13
+ @widget = 'TimePicker'
14
+ end
15
+ end
16
+
17
+ class AddressAutocompleteField < DynamicField
18
+ attr_accessor :widget
19
+
20
+ def initialize(options)
21
+ super(**options)
22
+ WidgetField.validate_arg(options, :type, { type: 'contains', value: ['String'] })
23
+
24
+ @widget = 'AddressAutocomplete'
25
+ end
26
+ end
27
+
28
+ class CheckboxField < DynamicField
29
+ attr_accessor :widget
30
+
31
+ def initialize(options)
32
+ super(**options)
33
+ WidgetField.validate_arg(
34
+ options,
35
+ :type,
36
+ { type: 'contains', value: [Types::FieldType::BOOLEAN] }
37
+ )
38
+
39
+ @widget = 'Checkbox'
40
+ end
41
+ end
42
+
43
+ class CheckboxGroupField < DynamicField
44
+ attr_accessor :widget, :options
45
+
46
+ def initialize(options)
47
+ super(**options)
48
+
49
+ WidgetField.validate_arg(options, :options, { type: 'present' })
50
+ WidgetField.validate_arg(
51
+ options,
52
+ :type,
53
+ { type: 'contains', value: [Types::FieldType::STRING_LIST, Types::FieldType::NUMBER_LIST] }
54
+ )
55
+
56
+ @widget = 'CheckboxGroup'
57
+ @options = options[:options]
58
+ end
59
+ end
60
+
61
+ class ColorPickerField < DynamicField
62
+ attr_accessor :widget, :enable_opacity, :quick_palette
63
+
64
+ def initialize(options)
65
+ super(**options)
66
+
67
+ WidgetField.validate_arg(options, :enable_opacity, { type: 'contains', value: [Types::FieldType::STRING] })
68
+
69
+ @widget = 'ColorPicker'
70
+ @enable_opacity = options[:enable_opacity] || nil
71
+ @quick_palette = options[:quick_palette] || nil
72
+ end
73
+ end
74
+
75
+ class CurrencyInputField < DynamicField
76
+ attr_accessor :widget, :currency, :base, :min, :max, :step
77
+
78
+ def initialize(options)
79
+ super(**options)
80
+
81
+ WidgetField.validate_arg(options, :type, { type: 'contains', value: [Types::FieldType::NUMBER] })
82
+ WidgetField.validate_arg(options, :currency, { type: 'present' })
83
+
84
+ @widget = 'CurrencyInput'
85
+ @currency = options[:currency]
86
+ @base = options[:base] || 'Unit'
87
+ @min = options[:min] || nil
88
+ @max = options[:max] || nil
89
+ @step = options[:step] || nil
90
+ end
91
+ end
92
+
93
+ class DatePickerField < DynamicField
94
+ attr_accessor :widget, :min, :max, :format, :step
95
+
96
+ def initialize(options)
97
+ super(**options)
98
+
99
+ WidgetField.validate_arg(
100
+ options,
101
+ 'type',
102
+ { type: 'contains',
103
+ value: [Types::FieldType::DATE, Types::FieldType::DATE_ONLY, Types::FieldType::STRING] }
104
+ )
105
+
106
+ @widget = 'DatePicker'
107
+ @format = options[:format] || nil
108
+ @min = options[:min] || nil
109
+ @max = options[:max] || nil
110
+ @step = options[:step] || nil
111
+ end
112
+ end
113
+
114
+ class DropdownField < DynamicField
115
+ attr_accessor :widget, :options, :search
116
+
117
+ def initialize(options)
118
+ super(**options)
119
+ WidgetField.validate_arg(options, :options, { type: 'present' })
120
+ WidgetField.validate_arg(
121
+ options,
122
+ 'type',
123
+ {
124
+ type: 'contains',
125
+ value: [Types::FieldType::DATE, Types::FieldType::DATE_ONLY, Types::FieldType::STRING,
126
+ Types::FieldType::STRING_LIST]
127
+ }
128
+ )
129
+
130
+ @widget = 'Dropdown'
131
+ @options = options[:options]
132
+ @search = options[:search] || nil
133
+ end
134
+ end
135
+
136
+ class FilePickerField < DynamicField
137
+ attr_accessor :widget, :extensions, :max_count, :max_size_mb
138
+
139
+ def initialize(options)
140
+ super(**options)
141
+ WidgetField.validate_arg(options, :options, { type: 'present' })
142
+ WidgetField.validate_arg(
143
+ options,
144
+ 'type',
145
+ {
146
+ type: 'contains',
147
+ value: [Types::FieldType::FILE, Types::FieldType::FILE_LIST]
148
+ }
149
+ )
150
+
151
+ @widget = 'FilePicker'
152
+ @extensions = options[:extensions] || nil
153
+ @max_size_mb = options[:max_size_mb] || nil
154
+ @max_count = options[:max_count] || nil
155
+ end
156
+ end
157
+
158
+ class NumberInputField < DynamicField
159
+ attr_accessor :widget, :step, :min, :max
160
+
161
+ def initialize(options)
162
+ super(**options)
163
+ WidgetField.validate_arg(options, :options, { type: 'present' })
164
+ WidgetField.validate_arg(
165
+ options,
166
+ 'type',
167
+ {
168
+ type: 'contains',
169
+ value: [Types::FieldType::NUMBER]
170
+ }
171
+ )
172
+
173
+ @widget = 'NumberInput'
174
+ @step = options[:step] || nil
175
+ @min = options[:min] || nil
176
+ @max = options[:max] || nil
177
+ end
178
+ end
179
+
180
+ class JsonEditorField < DynamicField
181
+ attr_accessor :widget
182
+
183
+ def initialize(options)
184
+ super(**options)
185
+ WidgetField.validate_arg(
186
+ options,
187
+ 'type',
188
+ {
189
+ type: 'contains',
190
+ value: [Types::FieldType::DATE, Types::FieldType::DATE_ONLY, Types::FieldType::STRING,
191
+ Types::FieldType::STRING_LIST]
192
+ }
193
+ )
194
+
195
+ @widget = 'JsonEditor'
196
+ end
197
+ end
198
+
199
+ class NumberInputListField < DynamicField
200
+ attr_accessor :widget, :allow_duplicates, :allow_empty_values, :enable_reorder, :min, :max, :step
201
+
202
+ def initialize(options)
203
+ super(**options)
204
+ WidgetField.validate_arg(options, :options, { type: 'present' })
205
+ WidgetField.validate_arg(
206
+ options,
207
+ 'type',
208
+ {
209
+ type: 'contains',
210
+ value: [Types::FieldType::NUMBER_LIST]
211
+ }
212
+ )
213
+
214
+ @widget = 'NumberInputList'
215
+ @allow_duplicates = options[:allow_duplicates] || nil
216
+ @allow_empty_values = options[:allow_empty_values] || nil
217
+ @enable_reorder = options[:enable_reorder] || nil
218
+ @min = options[:min] || nil
219
+ @max = options[:max] || nil
220
+ @step = options[:step] || nil
221
+ end
222
+ end
223
+
224
+ class RadioGroupField < DynamicField
225
+ attr_accessor :widget, :options
226
+
227
+ def initialize(options)
228
+ super(**options)
229
+ WidgetField.validate_arg(options, :options, { type: 'present' })
230
+ WidgetField.validate_arg(
231
+ options,
232
+ 'type',
233
+ {
234
+ type: 'contains',
235
+ value: [Types::FieldType::DATE, Types::FieldType::DATEONLY, Types::FieldType::NUMBER,
236
+ Types::FieldType::STRING]
237
+ }
238
+ )
239
+
240
+ @widget = 'RadioGroup'
241
+ @options = options[:options]
242
+ end
243
+ end
244
+
245
+ class RichTextField < DynamicField
246
+ attr_accessor :widget
247
+
248
+ def initialize(options)
249
+ super(**options)
250
+ WidgetField.validate_arg(options, :options, { type: 'present' })
251
+ WidgetField.validate_arg(
252
+ options,
253
+ 'type',
254
+ {
255
+ type: 'contains',
256
+ value: [Types::FieldType::STRING]
257
+ }
258
+ )
259
+
260
+ @widget = 'RichText'
261
+ end
262
+ end
263
+
264
+ class TextAreaField < DynamicField
265
+ attr_accessor :widget, :rows
266
+
267
+ def initialize(options)
268
+ super(**options)
269
+ WidgetField.validate_arg(
270
+ options,
271
+ 'type',
272
+ {
273
+ type: 'contains',
274
+ value: [Types::FieldType::STRING]
275
+ }
276
+ )
277
+
278
+ @widget = 'TextArea'
279
+ @rows = options[:rows] || nil
280
+ end
281
+ end
282
+
283
+ class TextInputField < DynamicField
284
+ attr_accessor :widget
285
+
286
+ def initialize(options)
287
+ super(**options)
288
+ WidgetField.validate_arg(
289
+ options,
290
+ 'type',
291
+ {
292
+ type: 'contains',
293
+ value: [Types::FieldType::STRING]
294
+ }
295
+ )
296
+
297
+ @widget = 'TextInput'
298
+ end
299
+ end
300
+
301
+ class TextInputListField < DynamicField
302
+ attr_accessor :widget, :allow_duplicates, :allow_empty_values, :enable_reorder
303
+
304
+ def initialize(options)
305
+ super(**options)
306
+ WidgetField.validate_arg(
307
+ options,
308
+ 'type',
309
+ {
310
+ type: 'contains',
311
+ value: [Types::FieldType::STRING_LIST]
312
+ }
313
+ )
314
+
315
+ @widget = 'TextInput'
316
+ @allow_duplicates = options[:allow_duplicates] || nil
317
+ @allow_empty_values = options[:allow_empty_values] || nil
318
+ @enable_reorder = options[:enable_reorder] || nil
319
+ end
320
+ end
321
+
322
+ class UserDropdownField < DynamicField
323
+ attr_accessor :widget
324
+
325
+ def initialize(options)
326
+ super(**options)
327
+ WidgetField.validate_arg(options, :options, { type: 'present' })
328
+ WidgetField.validate_arg(
329
+ options,
330
+ 'type',
331
+ {
332
+ type: 'contains',
333
+ value: [Types::FieldType::STRING, Types::FieldType::STRING_LIST]
334
+ }
335
+ )
336
+
337
+ @widget = 'UserDropdown'
338
+ end
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
@@ -246,7 +246,7 @@ module ForestAdminDatasourceCustomizer
246
246
  'type',
247
247
  {
248
248
  type: 'contains',
249
- value: [Types::FieldType::DATE, Types::FieldType::DATEONLY, Types::FieldType::NUMBER,
249
+ value: [Types::FieldType::DATE, Types::FieldType::DATE_ONLY, Types::FieldType::NUMBER,
250
250
  Types::FieldType::STRING]
251
251
  }
252
252
  )
@@ -326,7 +326,7 @@ module ForestAdminDatasourceCustomizer
326
326
  }
327
327
  )
328
328
 
329
- @widget = 'TextInput'
329
+ @widget = 'TextInputList'
330
330
  @allow_duplicates = options[:allow_duplicates] || nil
331
331
  @allow_empty_values = options[:allow_empty_values] || nil
332
332
  @enable_reorder = options[:enable_reorder] || nil
@@ -1,3 +1,3 @@
1
1
  module ForestAdminDatasourceCustomizer
2
- VERSION = "1.0.0-beta.65"
2
+ VERSION = "1.0.0-beta.67"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_admin_datasource_customizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.beta.65
4
+ version: 1.0.0.pre.beta.67
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-08-23 00:00:00.000000000 Z
12
+ date: 2024-09-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -81,9 +81,12 @@ files:
81
81
  - lib/forest_admin_datasource_customizer/datasource_customizer.rb
82
82
  - lib/forest_admin_datasource_customizer/decorators/action/action_collection_decorator.rb
83
83
  - lib/forest_admin_datasource_customizer/decorators/action/base_action.rb
84
+ - lib/forest_admin_datasource_customizer/decorators/action/base_form_element.rb
84
85
  - lib/forest_admin_datasource_customizer/decorators/action/context/action_context.rb
85
86
  - lib/forest_admin_datasource_customizer/decorators/action/context/action_context_single.rb
86
87
  - lib/forest_admin_datasource_customizer/decorators/action/dynamic_field.rb
88
+ - lib/forest_admin_datasource_customizer/decorators/action/form_layout_element.rb
89
+ - lib/forest_admin_datasource_customizer/decorators/action/layout_element.rb
87
90
  - lib/forest_admin_datasource_customizer/decorators/action/result_builder.rb
88
91
  - lib/forest_admin_datasource_customizer/decorators/action/types/action_scope.rb
89
92
  - lib/forest_admin_datasource_customizer/decorators/action/types/field_type.rb