super 0.0.12 → 0.17.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +0 -1
  3. data/README.md +38 -46
  4. data/app/assets/javascripts/super/application.js +5747 -3803
  5. data/app/assets/stylesheets/super/application.css +114686 -71486
  6. data/app/controllers/super/application_controller.rb +44 -37
  7. data/app/controllers/super/substructure_controller.rb +276 -0
  8. data/app/helpers/super/form_builder_helper.rb +7 -0
  9. data/app/views/layouts/super/application.html.erb +4 -19
  10. data/app/views/super/application/_collection_header.html.erb +2 -2
  11. data/app/views/super/application/_display_actions.html.erb +1 -1
  12. data/app/views/super/application/_display_index.html.erb +2 -2
  13. data/app/views/super/application/_display_show.html.erb +1 -1
  14. data/app/views/super/application/_filter.html.erb +62 -2
  15. data/app/views/super/application/_form_field.html.erb +5 -0
  16. data/app/views/super/application/_layout.html.erb +1 -1
  17. data/app/views/super/application/_member_header.html.erb +2 -2
  18. data/app/views/super/application/_pagination.html.erb +1 -1
  19. data/app/views/super/application/_site_footer.html.erb +3 -0
  20. data/app/views/super/application/_site_header.html.erb +17 -0
  21. data/app/views/super/application/_sort_expression.html.erb +2 -2
  22. data/app/views/super/feather/README.md +0 -1
  23. data/frontend/super-frontend/dist/application.css +114686 -71486
  24. data/frontend/super-frontend/dist/application.js +5747 -3803
  25. data/lib/generators/super/install/install_generator.rb +0 -16
  26. data/lib/generators/super/install/templates/base_controller.rb.tt +0 -8
  27. data/lib/generators/super/resource/templates/resources_controller.rb.tt +4 -4
  28. data/lib/generators/super/webpacker/webpacker_generator.rb +9 -5
  29. data/lib/super.rb +5 -2
  30. data/lib/super/action_inquirer.rb +18 -3
  31. data/lib/super/assets.rb +44 -23
  32. data/lib/super/badge.rb +60 -0
  33. data/lib/super/cheat.rb +17 -0
  34. data/lib/super/compatibility.rb +19 -0
  35. data/lib/super/display/guesser.rb +3 -1
  36. data/lib/super/display/schema_types.rb +51 -2
  37. data/lib/super/error.rb +2 -0
  38. data/lib/super/filter.rb +1 -1
  39. data/lib/super/filter/form_object.rb +74 -48
  40. data/lib/super/filter/guesser.rb +2 -0
  41. data/lib/super/filter/operator.rb +90 -64
  42. data/lib/super/filter/schema_types.rb +63 -80
  43. data/lib/super/form/builder.rb +110 -27
  44. data/lib/super/form/field_transcript.rb +43 -0
  45. data/lib/super/form/guesser.rb +10 -1
  46. data/lib/super/form/schema_types.rb +73 -16
  47. data/lib/super/link.rb +38 -32
  48. data/lib/super/link_builder.rb +58 -0
  49. data/lib/super/navigation.rb +164 -0
  50. data/lib/super/pagination.rb +2 -44
  51. data/lib/super/reset.rb +22 -0
  52. data/lib/super/schema.rb +4 -0
  53. data/lib/super/useful/builder.rb +4 -4
  54. data/lib/super/version.rb +1 -1
  55. data/lib/tasks/super/cheat.rake +9 -0
  56. metadata +14 -19
  57. data/CONTRIBUTING.md +0 -56
  58. data/Rakefile +0 -36
  59. data/app/views/super/application/_filter_type_select.html.erb +0 -21
  60. data/app/views/super/application/_filter_type_text.html.erb +0 -18
  61. data/app/views/super/application/_filter_type_timestamp.html.erb +0 -24
  62. data/app/views/super/application/_form_field_checkbox.html.erb +0 -1
  63. data/app/views/super/application/_form_field_rich_text_area.html.erb +0 -1
  64. data/app/views/super/application/_form_field_select.html.erb +0 -1
  65. data/app/views/super/application/_form_field_text.html.erb +0 -1
  66. data/app/views/super/feather/_chevron_down.html +0 -1
  67. data/docs/cheat.md +0 -41
  68. data/lib/super/controls.rb +0 -22
  69. data/lib/super/controls/optional.rb +0 -113
  70. data/lib/super/controls/steps.rb +0 -106
  71. data/lib/super/controls/view.rb +0 -55
  72. data/lib/super/navigation/automatic.rb +0 -73
@@ -58,6 +58,68 @@ module Super
58
58
  @builder.check_box(attribute, options, checked_value, unchecked_value)
59
59
  end
60
60
 
61
+ def date_flatpickr(attribute, options = {})
62
+ options, defaults = split_defaults(
63
+ options,
64
+ class: "super-input w-full",
65
+ data: {
66
+ controller: "flatpickr",
67
+ flatpickr_options_value: {
68
+ dateFormat: "Y-m-d",
69
+ }
70
+ }
71
+ )
72
+ options[:class] = join_classes(defaults[:class], options[:class])
73
+ options[:data] = defaults[:data].deep_merge(options[:data] || {})
74
+ options[:value] = @builder.object.public_send(attribute).presence
75
+ options[:value] = options[:value].iso8601 if options[:value].respond_to?(:iso8601)
76
+
77
+ @builder.text_field(attribute, options)
78
+ end
79
+
80
+ def datetime_flatpickr(attribute, options = {})
81
+ options, defaults = split_defaults(
82
+ options,
83
+ class: "super-input w-full",
84
+ data: {
85
+ controller: "flatpickr",
86
+ flatpickr_options_value: {
87
+ enableSeconds: true,
88
+ enableTime: true,
89
+ dateFormat: "Z",
90
+ }
91
+ }
92
+ )
93
+ options[:class] = join_classes(defaults[:class], options[:class])
94
+ options[:data] = defaults[:data].deep_merge(options[:data] || {})
95
+ options[:value] = @builder.object.public_send(attribute).presence
96
+ options[:value] = options[:value].iso8601 if options[:value].respond_to?(:iso8601)
97
+
98
+ @builder.text_field(attribute, options)
99
+ end
100
+
101
+ def time_flatpickr(attribute, options = {})
102
+ options, defaults = split_defaults(
103
+ options,
104
+ class: "super-input w-full",
105
+ data: {
106
+ controller: "flatpickr",
107
+ flatpickr_options_value: {
108
+ enableSeconds: true,
109
+ enableTime: true,
110
+ noCalendar: true,
111
+ dateFormat: "H:i:S",
112
+ }
113
+ }
114
+ )
115
+ options[:class] = join_classes(defaults[:class], options[:class])
116
+ options[:data] = defaults[:data].deep_merge(options[:data] || {})
117
+ options[:value] = @builder.object.public_send(attribute).presence
118
+ options[:value] = options[:value].strftime("%H:%M:%S") if options[:value].respond_to?(:strftime)
119
+
120
+ @builder.text_field(attribute, options)
121
+ end
122
+
61
123
  def password_field(attribute, options = {})
62
124
  options, defaults = split_defaults(options, class: "super-input w-full")
63
125
  options[:class] = join_classes(defaults[:class], options[:class])
@@ -75,25 +137,10 @@ module Super
75
137
  def select(attribute, choices, options = {}, html_options = {}, &block)
76
138
  options, defaults = split_defaults(options, include_blank: true)
77
139
  options = defaults.merge(options)
78
- html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select-field")
140
+ html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
79
141
  html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
80
142
 
81
- parts = [
82
- %(<div class="super-input-select">).html_safe,
83
- @builder.select(attribute, choices, options, html_options, &block),
84
- <<~HTML.html_safe,
85
- <div class="super-input-select-icon text-blue-700">
86
- <span class="h-4 w-4">
87
- HTML
88
- @template.render("super/feather/chevron_down"),
89
- <<~HTML.html_safe,
90
- </span>
91
- </div>
92
- HTML
93
- %(</div>).html_safe,
94
- ]
95
-
96
- @template.safe_join(parts)
143
+ @builder.select(attribute, choices, options, html_options, &block)
97
144
  end
98
145
 
99
146
  def submit(value = nil, options = {})
@@ -115,24 +162,60 @@ module Super
115
162
  @template.content_tag(:div, class: "super-field-group", &block)
116
163
  end
117
164
 
118
- def check_box!(attribute, checked_value = "1", unchecked_value = "0", label: {}, field: {}, show_errors: true)
165
+ def check_box!(attribute, checked_value: "1", unchecked_value: "0", label_text: nil, label: {}, field: {}, show_errors: true)
119
166
  label[:super] ||= {}
120
167
  label[:super] = { class: "select-none ml-1" }.merge(label[:super])
121
168
  container do
122
169
  compact_join([
123
170
  "<div>".html_safe,
124
171
  public_send(:check_box, attribute, field, checked_value, unchecked_value),
125
- public_send(:label, attribute, nil, label),
172
+ public_send(:label, attribute, label_text, label),
126
173
  "</div>".html_safe,
127
174
  show_errors && inline_errors(attribute),
128
175
  ])
129
176
  end
130
177
  end
131
178
 
132
- def password_field!(attribute, label: {}, field: {}, show_errors: true)
179
+ def date_flatpickr!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
180
+ container do
181
+ compact_join([
182
+ public_send(:label, attribute, label_text, label),
183
+ %(<div class="mt-1">).html_safe,
184
+ public_send(:date_flatpickr, attribute, field),
185
+ show_errors && inline_errors(attribute),
186
+ %(</div>).html_safe,
187
+ ])
188
+ end
189
+ end
190
+
191
+ def datetime_flatpickr!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
192
+ container do
193
+ compact_join([
194
+ public_send(:label, attribute, label_text, label),
195
+ %(<div class="mt-1">).html_safe,
196
+ public_send(:datetime_flatpickr, attribute, field),
197
+ show_errors && inline_errors(attribute),
198
+ %(</div>).html_safe,
199
+ ])
200
+ end
201
+ end
202
+
203
+ def time_flatpickr!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
204
+ container do
205
+ compact_join([
206
+ public_send(:label, attribute, label_text, label),
207
+ %(<div class="mt-1">).html_safe,
208
+ public_send(:time_flatpickr, attribute, field),
209
+ show_errors && inline_errors(attribute),
210
+ %(</div>).html_safe,
211
+ ])
212
+ end
213
+ end
214
+
215
+ def password_field!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
133
216
  container do
134
217
  compact_join([
135
- public_send(:label, attribute, label),
218
+ public_send(:label, attribute, label_text, label),
136
219
  %(<div class="mt-1">).html_safe,
137
220
  public_send(:password_field, attribute, field),
138
221
  show_errors && inline_errors(attribute),
@@ -141,10 +224,10 @@ module Super
141
224
  end
142
225
  end
143
226
 
144
- def rich_text_area!(attribute, label: {}, field: {}, show_errors: true)
227
+ def rich_text_area!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
145
228
  container do
146
229
  compact_join([
147
- public_send(:label, attribute, label),
230
+ public_send(:label, attribute, label_text, label),
148
231
  %(<div class="mt-1">).html_safe,
149
232
  public_send(:rich_text_area, attribute, field),
150
233
  show_errors && inline_errors(attribute),
@@ -153,10 +236,10 @@ module Super
153
236
  end
154
237
  end
155
238
 
156
- def select!(attribute, collection, label: {}, field: {}, show_errors: true)
239
+ def select!(attribute, collection, label_text: nil, label: {}, field: {}, show_errors: true)
157
240
  container do
158
241
  compact_join([
159
- public_send(:label, attribute, label),
242
+ public_send(:label, attribute, label_text, label),
160
243
  %(<div class="mt-1">).html_safe,
161
244
  public_send(:select, attribute, collection, field),
162
245
  show_errors && inline_errors(attribute),
@@ -165,10 +248,10 @@ module Super
165
248
  end
166
249
  end
167
250
 
168
- def text_field!(attribute, label: {}, field: {}, show_errors: true)
251
+ def text_field!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
169
252
  container do
170
253
  compact_join([
171
- public_send(:label, attribute, label),
254
+ public_send(:label, attribute, label_text, label),
172
255
  %(<div class="mt-1">).html_safe,
173
256
  public_send(:text_field, attribute, field),
174
257
  show_errors && inline_errors(attribute),
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Form
5
+ # Holds a recording of a form field definition
6
+ class FieldTranscript
7
+ def initialize
8
+ @super = false
9
+ end
10
+
11
+ attr_reader :method_name
12
+ attr_reader :args
13
+ attr_reader :kwargs
14
+
15
+ def super
16
+ @super = true
17
+ self
18
+ end
19
+
20
+ def super?
21
+ @super
22
+ end
23
+
24
+ def method_missing(new_method_name, *args, **kwargs)
25
+ if @method_name.present?
26
+ method_chain =
27
+ if super?
28
+ "super.#{@method_name}"
29
+ else
30
+ @method_name.to_s
31
+ end
32
+
33
+ raise Error::AlreadyTranscribed, "This instance already holds a transcription for: #{method_chain}"
34
+ end
35
+
36
+ @method_name = new_method_name
37
+ @args = args
38
+ @kwargs = kwargs
39
+ @method_name
40
+ end
41
+ end
42
+ end
43
+ end
@@ -22,7 +22,16 @@ module Super
22
22
  private
23
23
 
24
24
  def attribute_type_for(attribute_name)
25
- @type.string
25
+ case @model.type_for_attribute(attribute_name).type
26
+ when :datetime
27
+ @type.datetime_flatpickr
28
+ when :time
29
+ @type.time_flatpickr
30
+ when :date
31
+ @type.date_flatpickr
32
+ else
33
+ @type.text_field
34
+ end
26
35
  end
27
36
  end
28
37
  end
@@ -3,6 +3,38 @@
3
3
  module Super
4
4
  class Form
5
5
  class SchemaTypes
6
+ class Direct
7
+ def initialize(super_builder:, method_name:, args:, kwargs:)
8
+ @super_builder = super_builder
9
+ @method_name = method_name
10
+ @args = args
11
+ @kwargs = kwargs
12
+ end
13
+
14
+ attr_reader :super_builder
15
+ attr_reader :method_name
16
+ attr_reader :args
17
+ attr_reader :kwargs
18
+
19
+ def nested_fields
20
+ {}
21
+ end
22
+
23
+ def to_partial_path
24
+ "form_field"
25
+ end
26
+
27
+ def ==(other)
28
+ return false if other.class != self.class
29
+ return false if other.super_builder != super_builder
30
+ return false if other.method_name != method_name
31
+ return false if other.args != args
32
+ return false if other.kwargs != kwargs
33
+
34
+ true
35
+ end
36
+ end
37
+
6
38
  class Generic
7
39
  def initialize(partial_path:, extras:, nested:)
8
40
  @partial_path = partial_path
@@ -11,6 +43,7 @@ module Super
11
43
  end
12
44
 
13
45
  attr_reader :nested_fields
46
+ attr_reader :extras
14
47
 
15
48
  def each_attribute
16
49
  if block_given?
@@ -64,49 +97,73 @@ module Super
64
97
  @fields = fields
65
98
  end
66
99
 
67
- def generic(partial_path, **extras)
100
+ def partial(partial_path, **extras)
68
101
  Generic.new(partial_path: partial_path, extras: extras, nested: {})
69
102
  end
70
103
 
71
- def select(**extras)
72
- Generic.new(partial_path: "form_field_select", extras: extras, nested: {})
104
+ def direct(method_name, *args, super_builder: true, **kwargs)
105
+ Direct.new(super_builder: super_builder, method_name: method_name, args: args, kwargs: kwargs)
106
+ end
107
+
108
+ def select(*args, **kwargs)
109
+ Direct.new(super_builder: true, method_name: :select!, args: args, kwargs: kwargs)
73
110
  end
74
111
 
75
- def string(**extras)
76
- Generic.new(partial_path: "form_field_text", extras: extras, nested: {})
112
+ def text_field(*args, **kwargs)
113
+ Direct.new(super_builder: true, method_name: :text_field!, args: args, kwargs: kwargs)
77
114
  end
78
115
 
79
- alias text string
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
80
135
 
81
- def rich_text_area(**extras)
82
- Generic.new(partial_path: "form_field_rich_text_area", extras: extras, nested: {})
136
+ def password_field(*args, **kwargs)
137
+ Direct.new(super_builder: true, method_name: :password_field!, args: args, kwargs: kwargs)
83
138
  end
84
139
 
85
- def checkbox(**extras)
86
- Generic.new(partial_path: "form_field_checkbox", extras: extras, nested: {})
140
+ def time_flatpickr(*args, **kwargs)
141
+ Direct.new(super_builder: true, method_name: :time_flatpickr!, args: args, kwargs: kwargs)
87
142
  end
88
143
 
89
144
  def has_many(reader, **extras)
90
- nested = @fields.nested do
91
- yield
145
+ subfields = Schema::Fields.new
146
+ @fields.nested do
147
+ yield subfields
92
148
  end
93
149
 
94
150
  Generic.new(
95
151
  partial_path: "form_has_many",
96
152
  extras: extras.merge(reader: reader),
97
- nested: nested
153
+ nested: subfields.to_h
98
154
  )
99
155
  end
100
156
 
101
157
  def has_one(reader, **extras)
102
- nested = @fields.nested do
103
- yield
158
+ subfields = Schema::Fields.new
159
+ @fields.nested do
160
+ yield subfields
104
161
  end
105
162
 
106
163
  Generic.new(
107
164
  partial_path: "form_has_one",
108
165
  extras: extras.merge(reader: reader),
109
- nested: nested
166
+ nested: subfields.to_h
110
167
  )
111
168
  end
112
169
 
data/lib/super/link.rb CHANGED
@@ -22,57 +22,57 @@ module Super
22
22
 
23
23
  def self.registry
24
24
  @registry ||= {
25
- new: new(
25
+ new: LinkBuilder.new(
26
26
  "New",
27
27
  -> (params:) {
28
- Rails.application.routes.url_for(
28
+ {
29
29
  controller: params[:controller],
30
30
  action: :new,
31
31
  only_path: true
32
- )
32
+ }
33
33
  }
34
34
  ),
35
- index: new(
35
+ index: LinkBuilder.new(
36
36
  "Index",
37
37
  -> (params:) {
38
- Rails.application.routes.url_for(
38
+ {
39
39
  controller: params[:controller],
40
40
  action: :index,
41
41
  only_path: true
42
- )
42
+ }
43
43
  }
44
44
  ),
45
- show: new(
45
+ show: LinkBuilder.new(
46
46
  "View",
47
47
  -> (record:, params:) {
48
- Rails.application.routes.url_for(
48
+ {
49
49
  controller: params[:controller],
50
50
  action: :show,
51
51
  id: record,
52
52
  only_path: true
53
- )
53
+ }
54
54
  }
55
55
  ),
56
- edit: new(
56
+ edit: LinkBuilder.new(
57
57
  "Edit",
58
58
  -> (record:, params:) {
59
- Rails.application.routes.url_for(
59
+ {
60
60
  controller: params[:controller],
61
61
  action: :edit,
62
62
  id: record,
63
63
  only_path: true
64
- )
64
+ }
65
65
  }
66
66
  ),
67
- destroy: new(
67
+ destroy: LinkBuilder.new(
68
68
  "Delete",
69
69
  -> (record:, params:) {
70
- Rails.application.routes.url_for(
70
+ {
71
71
  controller: params[:controller],
72
72
  action: :destroy,
73
73
  id: record,
74
74
  only_path: true
75
- )
75
+ }
76
76
  },
77
77
  method: :delete,
78
78
  data: { confirm: "Really delete?" }
@@ -82,7 +82,7 @@ module Super
82
82
 
83
83
  def self.polymorphic_parts(*parts_tail)
84
84
  parts_head = Super.configuration.path.strip.gsub(%r{\A/+}, "").gsub(%r{/+\z}, "").strip.split("/")
85
- parts_head + parts_tail
85
+ parts_head.map { |part| part.is_a?(String) ? part.to_sym : part } + parts_tail
86
86
  end
87
87
 
88
88
  def initialize(text, href, **options)
@@ -91,27 +91,33 @@ module Super
91
91
  @options = options
92
92
  end
93
93
 
94
- def to_s(default_options: nil, **proc_arguments)
94
+ attr_reader :text
95
+ attr_reader :options
96
+
97
+ def href
98
+ if @href.is_a?(String)
99
+ return @href
100
+ end
101
+
102
+ if @href.is_a?(Hash)
103
+ @href = Rails.application.routes.url_for(**@href)
104
+ return @href
105
+ end
106
+
107
+ @href = Super::Compatability.polymorphic_path_container.polymorphic_path(@href)
108
+ end
109
+
110
+ def to_s(default_options: nil, **)
95
111
  default_options ||= {}
96
112
  ActionController::Base.helpers.link_to(
97
- value(text, proc_arguments),
98
- value(href, proc_arguments),
99
- default_options.deep_merge(value(options, proc_arguments))
113
+ text,
114
+ href,
115
+ default_options.deep_merge(options)
100
116
  )
101
117
  end
102
118
 
103
- private
104
-
105
- attr_reader :text
106
- attr_reader :href
107
- attr_reader :options
108
-
109
- def value(proc_or_value, proc_arguments)
110
- if proc_or_value.kind_of?(Proc)
111
- proc_or_value.call(**proc_arguments)
112
- else
113
- proc_or_value
114
- end
119
+ def ==(other)
120
+ self.class == other.class && text == other.text && href == other.href && options == other.options
115
121
  end
116
122
  end
117
123
  end