super 0.19.0.rc1 → 0.21.0
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 +4 -4
- data/README.md +5 -5
- data/app/controllers/super/application_controller.rb +10 -19
- data/app/controllers/super/sitewide_controller.rb +33 -0
- data/app/controllers/super/substructure_controller.rb +63 -85
- data/app/controllers/super/view_controller.rb +55 -0
- data/app/views/super/application/_batch_checkbox.html.erb +4 -0
- data/app/views/super/application/_display_index.html.erb +8 -2
- data/app/views/super/application/_display_show.html.erb +10 -2
- data/app/views/super/application/_filter.html.erb +63 -59
- data/app/views/super/application/_form.html.erb +1 -1
- data/app/views/super/application/_layout.html.erb +0 -2
- data/app/views/super/application/_pagination.html.erb +1 -4
- data/app/views/super/application/_query.html.erb +13 -12
- data/app/views/super/application/_site_footer.html.erb +1 -1
- data/app/views/super/application/_sort.html.erb +18 -14
- data/app/views/super/application/_view_chain.html.erb +23 -4
- data/config/locales/en.yml +10 -1
- data/frontend/super-frontend/dist/application.js +6276 -6286
- data/frontend/super-frontend/dist/package.json +2 -2
- data/lib/super/action_inquirer.rb +3 -0
- data/lib/super/badge.rb +11 -2
- data/lib/super/cheat.rb +6 -1
- data/lib/super/display/schema_types.rb +37 -14
- data/lib/super/display.rb +5 -4
- data/lib/super/error.rb +3 -2
- data/lib/super/form/schema_types.rb +8 -36
- data/lib/super/form_builder/action_text_methods.rb +16 -0
- data/lib/super/form_builder/base_methods.rb +73 -0
- data/lib/super/form_builder/flatpickr_methods.rb +75 -0
- data/lib/super/form_builder/option_methods.rb +73 -0
- data/lib/super/form_builder.rb +143 -0
- data/{app/helpers → lib}/super/form_builder_helper.rb +15 -4
- data/lib/super/link.rb +47 -63
- data/lib/super/link_builder.rb +24 -43
- data/lib/super/navigation.rb +40 -40
- data/lib/super/query.rb +61 -0
- data/lib/super/{engine.rb → railtie.rb} +7 -1
- data/lib/super/reset.rb +7 -0
- data/lib/super/schema.rb +8 -2
- data/lib/super/sort.rb +1 -3
- data/lib/super/useful/deprecations.rb +18 -0
- data/lib/super/useful/i19.rb +35 -0
- data/lib/super/version.rb +1 -1
- data/lib/super/view_chain.rb +14 -4
- data/lib/super.rb +14 -3
- metadata +21 -56
- data/config/routes.rb +0 -4
- data/lib/super/form/builder.rb +0 -289
- data/lib/super/query/form_object.rb +0 -48
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@superadministration/super",
|
3
3
|
"description": "The frontend code for Super, a Rails admin framework",
|
4
|
-
"homepage": "https://github.com/
|
4
|
+
"homepage": "https://github.com/superadministration/super",
|
5
5
|
"license": "LGPL-3.0-only",
|
6
6
|
"main": "application.js",
|
7
7
|
"files": [
|
@@ -9,5 +9,5 @@
|
|
9
9
|
"application.css",
|
10
10
|
"package.json"
|
11
11
|
],
|
12
|
-
"version": "0.
|
12
|
+
"version": "0.20.0"
|
13
13
|
}
|
data/lib/super/badge.rb
CHANGED
@@ -12,9 +12,18 @@ module Super
|
|
12
12
|
purple: "bg-purple-800 text-white",
|
13
13
|
}
|
14
14
|
|
15
|
-
def initialize(text, styles: nil)
|
15
|
+
def initialize(text, style: nil, styles: nil)
|
16
16
|
@text = text
|
17
|
-
|
17
|
+
if styles.present?
|
18
|
+
Super::Useful::Deprecation["0.22"].deprecation_warning("styles:", "use `style:` with a single style")
|
19
|
+
end
|
20
|
+
@requested_styles = Array(styles.presence).flatten + Array(style.presence).flatten
|
21
|
+
if @requested_styles.any? { |s| s.is_a?(String) }
|
22
|
+
Super::Useful::Deprecation["0.22"].warn("Super::Badge.new(text, style:) accepts exactly one Symbol style from this list #{STYLES.keys.inspect}")
|
23
|
+
end
|
24
|
+
if @requested_styles.size != 1
|
25
|
+
Super::Useful::Deprecation["0.22"].warn("Super::Badge.new(text, style:) accepts exactly one style, but it received #{@requested_styles.size}")
|
26
|
+
end
|
18
27
|
end
|
19
28
|
|
20
29
|
def styles
|
data/lib/super/cheat.rb
CHANGED
@@ -3,7 +3,12 @@
|
|
3
3
|
module Super
|
4
4
|
class Cheat
|
5
5
|
def controller
|
6
|
-
paths = %w[
|
6
|
+
paths = %w[
|
7
|
+
../../app/controllers/super/application_controller.rb
|
8
|
+
../../app/controllers/super/substructure_controller.rb
|
9
|
+
../../app/controllers/super/view_controller.rb
|
10
|
+
../../app/controllers/super/sitewide_controller.rb
|
11
|
+
]
|
7
12
|
methods =
|
8
13
|
paths
|
9
14
|
.map { |f| File.read(File.expand_path(f, __dir__)) }
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Super
|
4
4
|
class Display
|
5
5
|
class SchemaTypes
|
6
|
-
TYPES = Useful::Enum.new(:
|
6
|
+
TYPES = Useful::Enum.new(:attribute, :record, :none)
|
7
7
|
|
8
8
|
class Builder
|
9
9
|
extend Useful::Builder
|
@@ -15,37 +15,47 @@ module Super
|
|
15
15
|
builder def real; @real = true; end
|
16
16
|
builder def computed; @real = false; end
|
17
17
|
|
18
|
-
|
18
|
+
# @deprecated Prefer {#attribute}
|
19
|
+
builder def column
|
20
|
+
Useful::Deprecation["0.22"].deprecation_warning(":column", "use :attribute instead")
|
21
|
+
@type = :attribute
|
22
|
+
end
|
23
|
+
builder def attribute; @type = :attribute; end
|
19
24
|
builder def record; @type = :record; end
|
20
25
|
builder def none; @type = :none; end
|
21
26
|
|
22
27
|
builder def ignore_nil; @ignore_nil = true; end
|
23
28
|
|
29
|
+
builder def attribute_name(name); @attribute_name = name; end
|
30
|
+
|
24
31
|
def build
|
25
32
|
Built.new(
|
26
33
|
real: @real,
|
27
34
|
type: @type,
|
28
35
|
ignore_nil: !!@ignore_nil,
|
36
|
+
attribute_name: @attribute_name,
|
29
37
|
&@transform_block
|
30
38
|
)
|
31
39
|
end
|
32
40
|
end
|
33
41
|
|
34
42
|
class Built
|
35
|
-
def initialize(real:, type:, ignore_nil:, &transform_block)
|
43
|
+
def initialize(real:, type:, ignore_nil:, attribute_name:, &transform_block)
|
36
44
|
@real = real
|
37
45
|
@type = type
|
38
46
|
@ignore_nil = ignore_nil
|
47
|
+
@attribute_name = attribute_name
|
39
48
|
@transform_block = transform_block
|
40
49
|
end
|
41
50
|
|
42
51
|
def real?; @real; end
|
43
52
|
attr_reader :type
|
53
|
+
attr_reader :attribute_name
|
44
54
|
|
45
55
|
def present(attribute_name, value = nil)
|
46
56
|
if @transform_block.nil?
|
47
57
|
if attribute_name
|
48
|
-
raise Error::ArgumentError, "Transformation block is not set for
|
58
|
+
raise Error::ArgumentError, "Transformation block is not set for attribute: #{attribute_name}"
|
49
59
|
else
|
50
60
|
raise Error::ArgumentError, "Transformation block is not set!"
|
51
61
|
end
|
@@ -61,6 +71,7 @@ module Super
|
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
74
|
+
# @deprecated
|
64
75
|
class Badge
|
65
76
|
extend Useful::Builder
|
66
77
|
|
@@ -107,26 +118,36 @@ module Super
|
|
107
118
|
@fields = fields
|
108
119
|
end
|
109
120
|
|
110
|
-
def real(type = :
|
121
|
+
def real(type = :attribute, &transform_block)
|
122
|
+
if type == :column
|
123
|
+
Useful::Deprecation["0.22"].deprecation_warning(":column", "use :attribute instead")
|
124
|
+
type = :attribute
|
125
|
+
end
|
126
|
+
|
111
127
|
TYPES
|
112
128
|
.case(type)
|
113
|
-
.when(:
|
114
|
-
.when(:record)
|
115
|
-
.when(:none)
|
129
|
+
.when(:attribute) { Builder.new.real.ignore_nil.attribute.transform(&transform_block) }
|
130
|
+
.when(:record) { Builder.new.real.ignore_nil.record.transform(&transform_block) }
|
131
|
+
.when(:none) { Builder.new.real.ignore_nil.none.transform(&transform_block) }
|
116
132
|
.result
|
117
133
|
end
|
118
134
|
|
119
|
-
def computed(type = :
|
135
|
+
def computed(type = :attribute, &transform_block)
|
136
|
+
if type == :column
|
137
|
+
Useful::Deprecation["0.22"].deprecation_warning(":column", "use :attribute instead")
|
138
|
+
type = :attribute
|
139
|
+
end
|
140
|
+
|
120
141
|
TYPES
|
121
142
|
.case(type)
|
122
|
-
.when(:
|
123
|
-
.when(:record)
|
124
|
-
.when(:none)
|
143
|
+
.when(:attribute) { Builder.new.computed.ignore_nil.attribute.transform(&transform_block) }
|
144
|
+
.when(:record) { Builder.new.computed.ignore_nil.record.transform(&transform_block) }
|
145
|
+
.when(:none) { Builder.new.computed.ignore_nil.none.transform(&transform_block) }
|
125
146
|
.result
|
126
147
|
end
|
127
148
|
|
128
149
|
def manual(&transform_block)
|
129
|
-
real(:
|
150
|
+
real(:attribute, &transform_block)
|
130
151
|
end
|
131
152
|
|
132
153
|
def batch
|
@@ -146,8 +167,10 @@ module Super
|
|
146
167
|
end
|
147
168
|
end
|
148
169
|
|
170
|
+
# @deprecated Use {#real} or {#computed} instead, and return an instance of {Super::Badge}
|
149
171
|
def badge(*builder_methods)
|
150
|
-
|
172
|
+
Useful::Deprecation["0.22"].deprecation_warning("#badge", "use #real or #computed instead, and return an instance of Super::Badge")
|
173
|
+
builder_methods = %i[real ignore_nil attribute] if builder_methods.empty?
|
151
174
|
builder = builder_methods.each_with_object(Builder.new) do |builder_method, builder|
|
152
175
|
builder.public_send(builder_method)
|
153
176
|
end
|
data/lib/super/display.rb
CHANGED
@@ -26,7 +26,9 @@ module Super
|
|
26
26
|
include Schema::Common
|
27
27
|
|
28
28
|
def initialize
|
29
|
-
@fields = Super::Schema::Fields.new
|
29
|
+
@fields = Super::Schema::Fields.new(
|
30
|
+
transform_value_on_set: -> (val) { if val.respond_to?(:build) then val.build else val end }
|
31
|
+
)
|
30
32
|
@schema_types = SchemaTypes.new(fields: @fields)
|
31
33
|
|
32
34
|
yield(@fields, @schema_types)
|
@@ -43,7 +45,7 @@ module Super
|
|
43
45
|
|
44
46
|
def to_partial_path
|
45
47
|
if @action_inquirer.nil?
|
46
|
-
raise Super::Error::
|
48
|
+
raise Super::Error::Initialization,
|
47
49
|
"You must call the `#apply` method after instantiating Super::Display"
|
48
50
|
elsif @action_inquirer.index?
|
49
51
|
"display_index"
|
@@ -57,7 +59,6 @@ module Super
|
|
57
59
|
# @private
|
58
60
|
def render_attribute(template:, record:, column:)
|
59
61
|
formatter = @fields[column]
|
60
|
-
formatter = formatter.build if formatter.respond_to?(:build)
|
61
62
|
|
62
63
|
formatted =
|
63
64
|
SchemaTypes::TYPES
|
@@ -65,7 +66,7 @@ module Super
|
|
65
66
|
.when(:record) do
|
66
67
|
formatter.present(column, record)
|
67
68
|
end
|
68
|
-
.when(:
|
69
|
+
.when(:attribute) do
|
69
70
|
value = record.public_send(column)
|
70
71
|
formatter.present(column, value)
|
71
72
|
end
|
data/lib/super/error.rb
CHANGED
@@ -24,13 +24,14 @@ module Super
|
|
24
24
|
)
|
25
25
|
end
|
26
26
|
end
|
27
|
-
# Error raised when something wasn't
|
27
|
+
# Error raised when something wasn't initialized correctly, and if there isn't
|
28
28
|
# a more specific error
|
29
|
-
class
|
29
|
+
class Initialization < Error; end
|
30
30
|
class ArgumentError < Error; end
|
31
31
|
class AlreadyRegistered < Error; end
|
32
32
|
class AlreadyTranscribed < Error; end
|
33
33
|
class NotImplementedError < Error; end
|
34
|
+
class IncompleteBuilder < Error; end
|
34
35
|
|
35
36
|
class Enum < Error
|
36
37
|
class ImpossibleValue < Enum; end
|
@@ -105,42 +105,6 @@ module Super
|
|
105
105
|
Direct.new(super_builder: super_builder, method_name: method_name, args: args, kwargs: kwargs)
|
106
106
|
end
|
107
107
|
|
108
|
-
def select(*args, **kwargs)
|
109
|
-
Direct.new(super_builder: true, method_name: :select!, args: args, kwargs: kwargs)
|
110
|
-
end
|
111
|
-
|
112
|
-
def text_field(*args, **kwargs)
|
113
|
-
Direct.new(super_builder: true, method_name: :text_field!, args: args, kwargs: kwargs)
|
114
|
-
end
|
115
|
-
|
116
|
-
def rich_text_area(*args, **kwargs)
|
117
|
-
Direct.new(super_builder: true, method_name: :rich_text_area!, args: args, kwargs: kwargs)
|
118
|
-
end
|
119
|
-
|
120
|
-
def check_box(*args, **kwargs)
|
121
|
-
Direct.new(super_builder: true, method_name: :check_box!, args: args, kwargs: kwargs)
|
122
|
-
end
|
123
|
-
|
124
|
-
def date_flatpickr(*args, **kwargs)
|
125
|
-
Direct.new(super_builder: true, method_name: :date_flatpickr!, args: args, kwargs: kwargs)
|
126
|
-
end
|
127
|
-
|
128
|
-
def datetime_flatpickr(*args, **kwargs)
|
129
|
-
Direct.new(super_builder: true, method_name: :datetime_flatpickr!, args: args, kwargs: kwargs)
|
130
|
-
end
|
131
|
-
|
132
|
-
def hidden_field(*args, **kwargs)
|
133
|
-
Direct.new(super_builder: false, method_name: :hidden_field, args: args, kwargs: kwargs)
|
134
|
-
end
|
135
|
-
|
136
|
-
def password_field(*args, **kwargs)
|
137
|
-
Direct.new(super_builder: true, method_name: :password_field!, args: args, kwargs: kwargs)
|
138
|
-
end
|
139
|
-
|
140
|
-
def time_flatpickr(*args, **kwargs)
|
141
|
-
Direct.new(super_builder: true, method_name: :time_flatpickr!, args: args, kwargs: kwargs)
|
142
|
-
end
|
143
|
-
|
144
108
|
def has_many(reader, **extras)
|
145
109
|
subfields = Schema::Fields.new
|
146
110
|
@fields.nested do
|
@@ -176,6 +140,14 @@ module Super
|
|
176
140
|
nested: {}
|
177
141
|
)
|
178
142
|
end
|
143
|
+
|
144
|
+
def self.define_schema_type_for(method_name)
|
145
|
+
class_eval(<<~RUBY)
|
146
|
+
def #{method_name}(*args, **kwargs)
|
147
|
+
Direct.new(super_builder: true, method_name: :#{method_name}!, args: args, kwargs: kwargs)
|
148
|
+
end
|
149
|
+
RUBY
|
150
|
+
end
|
179
151
|
end
|
180
152
|
end
|
181
153
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Super
|
4
|
+
class FormBuilder
|
5
|
+
class Wrappers
|
6
|
+
def rich_text_area(attribute, options = {})
|
7
|
+
options, defaults = split_defaults(options, class: "trix-content super-input w-full")
|
8
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
9
|
+
|
10
|
+
@builder.rich_text_area(attribute, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
define_convenience :rich_text_area
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Super
|
4
|
+
class FormBuilder
|
5
|
+
class Wrappers
|
6
|
+
skipped_field_helpers = [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]
|
7
|
+
(ActionView::Helpers::FormBuilder.field_helpers - skipped_field_helpers).each do |builder_method_name|
|
8
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
9
|
+
def #{builder_method_name}(attribute, options = {})
|
10
|
+
options, defaults = split_defaults(options, class: "super-input w-full")
|
11
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
12
|
+
|
13
|
+
@builder.#{builder_method_name}(attribute, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
define_convenience :#{builder_method_name}
|
17
|
+
RUBY
|
18
|
+
end
|
19
|
+
|
20
|
+
def label(attribute, text = nil, options = {}, &block)
|
21
|
+
options, defaults = split_defaults(options, class: "block")
|
22
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
23
|
+
|
24
|
+
@builder.label(attribute, text, options, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_box(attribute, options = {}, checked_value = "1", unchecked_value = "0")
|
28
|
+
@builder.check_box(attribute, options, checked_value, unchecked_value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_box!(attribute, checked_value: "1", unchecked_value: "0", label_text: nil, label: {}, field: {}, show_errors: true)
|
32
|
+
label[:super] ||= {}
|
33
|
+
label[:super] = { class: "select-none ml-1" }.merge(label[:super])
|
34
|
+
container do
|
35
|
+
compact_join([
|
36
|
+
"<div>".html_safe,
|
37
|
+
public_send(:check_box, attribute, field, checked_value, unchecked_value),
|
38
|
+
public_send(:label, attribute, label_text, label),
|
39
|
+
"</div>".html_safe,
|
40
|
+
show_errors && inline_errors(attribute),
|
41
|
+
])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
::Super::Form::SchemaTypes.define_schema_type_for(:check_box)
|
46
|
+
|
47
|
+
# def file_field(attribute, options = {})
|
48
|
+
# end
|
49
|
+
|
50
|
+
# def file_field!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
|
51
|
+
# end
|
52
|
+
|
53
|
+
def hidden_field(attribute, options = {})
|
54
|
+
@builder.hidden_field(attribute, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
# def radio_button(attribute, tag_value, options = {})
|
58
|
+
# @builder.radio_button(attribute, tag_value, options)
|
59
|
+
# end
|
60
|
+
|
61
|
+
# def radio_button(attribute, tag_value, label_text: nil, label: {}, field: {}, show_errors: true)
|
62
|
+
# end
|
63
|
+
|
64
|
+
def submit(value = nil, options = {})
|
65
|
+
value, options = nil, value if value.is_a?(Hash)
|
66
|
+
options, defaults = split_defaults(options, class: "super-button")
|
67
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
68
|
+
|
69
|
+
@builder.submit(value, options)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Super
|
4
|
+
class FormBuilder
|
5
|
+
class Wrappers
|
6
|
+
def date_flatpickr(attribute, options = {})
|
7
|
+
options, defaults = split_defaults(
|
8
|
+
options,
|
9
|
+
class: "super-input w-full",
|
10
|
+
data: {
|
11
|
+
controller: "flatpickr",
|
12
|
+
flatpickr_options_value: {
|
13
|
+
dateFormat: "Y-m-d",
|
14
|
+
}
|
15
|
+
}
|
16
|
+
)
|
17
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
18
|
+
options[:data] = defaults[:data].deep_merge(options[:data] || {})
|
19
|
+
options[:value] = @builder.object.public_send(attribute).presence
|
20
|
+
options[:value] = options[:value].iso8601 if options[:value].respond_to?(:iso8601)
|
21
|
+
|
22
|
+
@builder.text_field(attribute, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
define_convenience :date_flatpickr
|
26
|
+
|
27
|
+
def datetime_flatpickr(attribute, options = {})
|
28
|
+
options, defaults = split_defaults(
|
29
|
+
options,
|
30
|
+
class: "super-input w-full",
|
31
|
+
data: {
|
32
|
+
controller: "flatpickr",
|
33
|
+
flatpickr_options_value: {
|
34
|
+
enableSeconds: true,
|
35
|
+
enableTime: true,
|
36
|
+
dateFormat: "Z",
|
37
|
+
}
|
38
|
+
}
|
39
|
+
)
|
40
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
41
|
+
options[:data] = defaults[:data].deep_merge(options[:data] || {})
|
42
|
+
options[:value] = @builder.object.public_send(attribute).presence
|
43
|
+
options[:value] = options[:value].iso8601 if options[:value].respond_to?(:iso8601)
|
44
|
+
|
45
|
+
@builder.text_field(attribute, options)
|
46
|
+
end
|
47
|
+
|
48
|
+
define_convenience :datetime_flatpickr
|
49
|
+
|
50
|
+
def time_flatpickr(attribute, options = {})
|
51
|
+
options, defaults = split_defaults(
|
52
|
+
options,
|
53
|
+
class: "super-input w-full",
|
54
|
+
data: {
|
55
|
+
controller: "flatpickr",
|
56
|
+
flatpickr_options_value: {
|
57
|
+
enableSeconds: true,
|
58
|
+
enableTime: true,
|
59
|
+
noCalendar: true,
|
60
|
+
dateFormat: "H:i:S",
|
61
|
+
}
|
62
|
+
}
|
63
|
+
)
|
64
|
+
options[:class] = join_classes(defaults[:class], options[:class])
|
65
|
+
options[:data] = defaults[:data].deep_merge(options[:data] || {})
|
66
|
+
options[:value] = @builder.object.public_send(attribute).presence
|
67
|
+
options[:value] = options[:value].strftime("%H:%M:%S") if options[:value].respond_to?(:strftime)
|
68
|
+
|
69
|
+
@builder.text_field(attribute, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
define_convenience :time_flatpickr
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Super
|
4
|
+
class FormBuilder
|
5
|
+
class Wrappers
|
6
|
+
def select(attribute, choices, options = {}, html_options = {}, &block)
|
7
|
+
options, defaults = split_defaults(options, include_blank: true)
|
8
|
+
options = defaults.merge(options)
|
9
|
+
html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
|
10
|
+
html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
|
11
|
+
|
12
|
+
@builder.select(attribute, choices, options, html_options, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
define_convenience :select
|
16
|
+
|
17
|
+
def collection_select(attribute, collection, value_method, text_method, options = {}, html_options = {})
|
18
|
+
options, defaults = split_defaults(options, include_blank: true)
|
19
|
+
options = defaults.merge(options)
|
20
|
+
html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
|
21
|
+
html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
|
22
|
+
|
23
|
+
@builder.collection_select(attribute, collection, value_method, text_method, options, html_options)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_convenience :collection_select
|
27
|
+
|
28
|
+
def grouped_collection_select(attribute, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
29
|
+
options, defaults = split_defaults(options, include_blank: true)
|
30
|
+
options = defaults.merge(options)
|
31
|
+
html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
|
32
|
+
html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
|
33
|
+
|
34
|
+
@builder.grouped_collection_select(attribute, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
35
|
+
end
|
36
|
+
|
37
|
+
define_convenience :grouped_collection_select
|
38
|
+
|
39
|
+
def time_zone_select(attribute, priority_zones = nil, options = {}, html_options = {})
|
40
|
+
options, defaults = split_defaults(options, include_blank: true)
|
41
|
+
options = defaults.merge(options)
|
42
|
+
html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
|
43
|
+
html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
|
44
|
+
|
45
|
+
@builder.time_zone_select(attribute, priority_zones, options, html_options)
|
46
|
+
end
|
47
|
+
|
48
|
+
define_convenience :time_zone_select, priority_zones: "nil"
|
49
|
+
|
50
|
+
def collection_check_boxes(attribute, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
51
|
+
options, defaults = split_defaults(options, include_blank: true)
|
52
|
+
options = defaults.merge(options)
|
53
|
+
html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
|
54
|
+
html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
|
55
|
+
|
56
|
+
@builder.collection_check_boxes(attribute, collection, value_method, text_method, options, html_options, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
define_convenience :collection_check_boxes
|
60
|
+
|
61
|
+
def collection_radio_buttons(attribute, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
62
|
+
options, defaults = split_defaults(options, include_blank: true)
|
63
|
+
options = defaults.merge(options)
|
64
|
+
html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
|
65
|
+
html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
|
66
|
+
|
67
|
+
@builder.collection_radio_buttons(attribute, collection, value_method, text_method, options, html_options, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
define_convenience :collection_radio_buttons
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Super
|
4
|
+
# Example
|
5
|
+
#
|
6
|
+
# ```ruby
|
7
|
+
# super_form_for([:admin, @member]) do |f|
|
8
|
+
# # the long way
|
9
|
+
# f.super.label :name
|
10
|
+
# f.super.text_field :name
|
11
|
+
# f.super.inline_errors :name
|
12
|
+
#
|
13
|
+
# # the short way (slightly different from the long way, for alignment)
|
14
|
+
# f.super.text_field! :position
|
15
|
+
# end
|
16
|
+
# ```
|
17
|
+
#
|
18
|
+
# Refer to the Rails docs:
|
19
|
+
# https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html
|
20
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
21
|
+
FIELD_ERROR_PROC = proc { |html_tag, instance| html_tag }
|
22
|
+
FORM_BUILDER_DEFAULTS = { builder: self }.freeze
|
23
|
+
|
24
|
+
def super(**options)
|
25
|
+
@super_wrappers ||= Wrappers.new(self, @template)
|
26
|
+
end
|
27
|
+
|
28
|
+
class Wrappers
|
29
|
+
def initialize(builder, template)
|
30
|
+
@builder = builder
|
31
|
+
@template = template
|
32
|
+
end
|
33
|
+
|
34
|
+
def inline_errors(attribute)
|
35
|
+
if @builder.object
|
36
|
+
messages = Form::InlineErrors.error_messages(@builder.object, attribute).map do |msg|
|
37
|
+
error_content_tag(msg)
|
38
|
+
end
|
39
|
+
|
40
|
+
@template.safe_join(messages)
|
41
|
+
else
|
42
|
+
error_content_tag(<<~MSG.html_safe)
|
43
|
+
This form doesn't have an object, so something is probably wrong.
|
44
|
+
Maybe <code>accepts_nested_attributes_for</code> isn't set up?
|
45
|
+
MSG
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def container(&block)
|
50
|
+
@template.content_tag(:div, class: "super-field-group", &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
private_class_method def self.define_with_label_tag(method_name, **optionals)
|
56
|
+
parameters = instance_method(method_name).parameters
|
57
|
+
definition = []
|
58
|
+
call = []
|
59
|
+
definition_last = ["label_text: nil", "label: {}", "show_errors: true"]
|
60
|
+
call_last = []
|
61
|
+
parameters.each do |type, name|
|
62
|
+
if name == :options && type == :opt
|
63
|
+
definition.push("field: {}")
|
64
|
+
call.push("field")
|
65
|
+
elsif name == :html_options && type == :opt
|
66
|
+
definition.push("field_html: {}")
|
67
|
+
call.push("field_html")
|
68
|
+
elsif type == :block
|
69
|
+
definition_last.push("&#{name}")
|
70
|
+
call_last.push("&#{name}")
|
71
|
+
else
|
72
|
+
if type == :req
|
73
|
+
definition.push(name.to_s)
|
74
|
+
call.push(name.to_s)
|
75
|
+
elsif type == :opt || type == :key
|
76
|
+
if !optionals.key?(name)
|
77
|
+
raise Super::Error::ArgumentError, "Form bang method has optional argument, but doesn't know the default value: #{name}"
|
78
|
+
end
|
79
|
+
|
80
|
+
default_value = optionals[name]
|
81
|
+
|
82
|
+
if type == :opt
|
83
|
+
definition.push("#{name} = #{default_value}")
|
84
|
+
call.push(name.to_s)
|
85
|
+
elsif type == :key
|
86
|
+
definition.push("#{name}: #{default_value}")
|
87
|
+
call.push("#{name}: #{name}")
|
88
|
+
else
|
89
|
+
raise Super::Error::ArgumentError, "Form bang method has a unprocessable argument with name #{name}"
|
90
|
+
end
|
91
|
+
else
|
92
|
+
raise Super::Error::ArgumentError, "Form bang method has keyword argument type #{type} and name #{name}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
definition += definition_last
|
98
|
+
call += call_last
|
99
|
+
|
100
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
101
|
+
def #{method_name}!(#{definition.join(", ")})
|
102
|
+
container do
|
103
|
+
compact_join([
|
104
|
+
public_send(:label, attribute, label_text, label),
|
105
|
+
%(<div class="mt-1">).html_safe,
|
106
|
+
#{method_name}(#{call.join(", ")}),
|
107
|
+
show_errors && inline_errors(attribute),
|
108
|
+
%(</div>).html_safe,
|
109
|
+
])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
RUBY
|
113
|
+
end
|
114
|
+
|
115
|
+
private_class_method def self.define_convenience(method_name, *args, **kwargs)
|
116
|
+
define_with_label_tag(method_name, *args, **kwargs)
|
117
|
+
::Super::Form::SchemaTypes.define_schema_type_for(method_name)
|
118
|
+
end
|
119
|
+
|
120
|
+
def split_defaults(options, **internal_defaults)
|
121
|
+
defaults = options.delete(:super) || {}
|
122
|
+
# prefer options set in `defaults`, since they are user overrides
|
123
|
+
defaults = internal_defaults.merge(defaults)
|
124
|
+
|
125
|
+
[options, defaults]
|
126
|
+
end
|
127
|
+
|
128
|
+
def join_classes(*class_lists)
|
129
|
+
class_lists.flatten.map(&:presence).compact
|
130
|
+
end
|
131
|
+
|
132
|
+
def error_content_tag(content)
|
133
|
+
@template.content_tag(:p, content, class: "text-red-400 text-xs italic pt-1")
|
134
|
+
end
|
135
|
+
|
136
|
+
def compact_join(*parts)
|
137
|
+
@template.safe_join(
|
138
|
+
parts.flatten.map(&:presence).compact
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|