formstrap 0.1.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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +33 -0
  4. data/CHANGELOG.md +1 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +28 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +118 -0
  9. data/Rakefile +10 -0
  10. data/app/assets/config/headmin_manifest.js +2 -0
  11. data/app/assets/images/avatar.jpg +0 -0
  12. data/app/assets/images/document.docx +0 -0
  13. data/app/assets/images/document.pdf +0 -0
  14. data/app/assets/images/image.jpg +0 -0
  15. data/app/assets/images/spreadsheet.xls +0 -0
  16. data/app/assets/images/video.mp4 +0 -0
  17. data/app/assets/javascripts/formstrap/config/i18n.js +11 -0
  18. data/app/assets/javascripts/formstrap/controllers/autocomplete_controller.js +318 -0
  19. data/app/assets/javascripts/formstrap/controllers/date_range_controller.js +38 -0
  20. data/app/assets/javascripts/formstrap/controllers/dropzone_controller.js +31 -0
  21. data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +244 -0
  22. data/app/assets/javascripts/formstrap/controllers/flatpickr_controller.js +35 -0
  23. data/app/assets/javascripts/formstrap/controllers/infinite_scroller_controller.js +28 -0
  24. data/app/assets/javascripts/formstrap/controllers/media_controller.js +252 -0
  25. data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +147 -0
  26. data/app/assets/javascripts/formstrap/controllers/redactorx_controller.js +40 -0
  27. data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +148 -0
  28. data/app/assets/javascripts/formstrap/controllers/select_controller.js +49 -0
  29. data/app/assets/javascripts/formstrap/controllers/textarea_controller.js +48 -0
  30. data/app/assets/javascripts/formstrap/index.js +32 -0
  31. data/app/assets/javascripts/formstrap.js +11515 -0
  32. data/app/assets/stylesheets/formstrap/forms/autocomplete.scss +27 -0
  33. data/app/assets/stylesheets/formstrap/forms/file.scss +83 -0
  34. data/app/assets/stylesheets/formstrap/forms/media.scss +10 -0
  35. data/app/assets/stylesheets/formstrap/forms/repeater.scss +62 -0
  36. data/app/assets/stylesheets/formstrap/forms/search.scss +12 -0
  37. data/app/assets/stylesheets/formstrap/forms.scss +12 -0
  38. data/app/assets/stylesheets/formstrap/general.scss +18 -0
  39. data/app/assets/stylesheets/formstrap/media/index.scss +9 -0
  40. data/app/assets/stylesheets/formstrap/media.scss +1 -0
  41. data/app/assets/stylesheets/formstrap/utilities/buttons.scss +27 -0
  42. data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +72 -0
  43. data/app/assets/stylesheets/formstrap/utilities.scss +2 -0
  44. data/app/assets/stylesheets/formstrap/vendor/flatpickr.css +903 -0
  45. data/app/assets/stylesheets/formstrap/vendor/tom-select-bootstrap.scss +535 -0
  46. data/app/assets/stylesheets/formstrap.css +1559 -0
  47. data/app/assets/stylesheets/formstrap.scss +11 -0
  48. data/app/controllers/concerns/formstrap/pagination.rb +27 -0
  49. data/app/controllers/formstrap/media_controller.rb +68 -0
  50. data/app/controllers/formstrap_controller.rb +2 -0
  51. data/app/models/concerns/formstrap/autocompletable.rb +36 -0
  52. data/app/models/concerns/formstrap/hintable.rb +22 -0
  53. data/app/models/concerns/formstrap/input_groupable.rb +21 -0
  54. data/app/models/concerns/formstrap/labelable.rb +31 -0
  55. data/app/models/concerns/formstrap/listable.rb +26 -0
  56. data/app/models/concerns/formstrap/placeholderable.rb +11 -0
  57. data/app/models/concerns/formstrap/validatable.rb +38 -0
  58. data/app/models/concerns/formstrap/wrappable.rb +19 -0
  59. data/app/models/formstrap/.DS_Store +0 -0
  60. data/app/models/formstrap/association_view.rb +100 -0
  61. data/app/models/formstrap/blocks_view.rb +43 -0
  62. data/app/models/formstrap/checkbox_view.rb +50 -0
  63. data/app/models/formstrap/color_view.rb +45 -0
  64. data/app/models/formstrap/date_range_view.rb +23 -0
  65. data/app/models/formstrap/date_view.rb +43 -0
  66. data/app/models/formstrap/datetime_range_view.rb +23 -0
  67. data/app/models/formstrap/datetime_view.rb +43 -0
  68. data/app/models/formstrap/email_view.rb +46 -0
  69. data/app/models/formstrap/file_view.rb +106 -0
  70. data/app/models/formstrap/flatpickr_range_view.rb +89 -0
  71. data/app/models/formstrap/flatpickr_view.rb +27 -0
  72. data/app/models/formstrap/hidden_view.rb +8 -0
  73. data/app/models/formstrap/hint_view.rb +4 -0
  74. data/app/models/formstrap/input_group_view.rb +17 -0
  75. data/app/models/formstrap/label_view.rb +22 -0
  76. data/app/models/formstrap/media_item_view.rb +41 -0
  77. data/app/models/formstrap/media_view.rb +143 -0
  78. data/app/models/formstrap/number_view.rb +47 -0
  79. data/app/models/formstrap/password_view.rb +42 -0
  80. data/app/models/formstrap/redactorx_view.rb +57 -0
  81. data/app/models/formstrap/search_view.rb +46 -0
  82. data/app/models/formstrap/select_view.rb +61 -0
  83. data/app/models/formstrap/switch_view.rb +21 -0
  84. data/app/models/formstrap/text_view.rb +46 -0
  85. data/app/models/formstrap/textarea_view.rb +47 -0
  86. data/app/models/formstrap/url_view.rb +46 -0
  87. data/app/models/formstrap/wrapper_view.rb +17 -0
  88. data/app/models/formstrap/wysiwyg_view.rb +15 -0
  89. data/app/models/view_model.rb +62 -0
  90. data/app/views/formstrap/_association.html.erb +30 -0
  91. data/app/views/formstrap/_autocomplete.html.erb +11 -0
  92. data/app/views/formstrap/_blocks.html.erb +45 -0
  93. data/app/views/formstrap/_checkbox.html.erb +34 -0
  94. data/app/views/formstrap/_color.html.erb +32 -0
  95. data/app/views/formstrap/_datalist.html.erb +3 -0
  96. data/app/views/formstrap/_date.html.erb +41 -0
  97. data/app/views/formstrap/_date_range.html.erb +40 -0
  98. data/app/views/formstrap/_datetime.html.erb +41 -0
  99. data/app/views/formstrap/_datetime_range.html.erb +40 -0
  100. data/app/views/formstrap/_email.html.erb +43 -0
  101. data/app/views/formstrap/_errors.html.erb +19 -0
  102. data/app/views/formstrap/_file.html.erb +94 -0
  103. data/app/views/formstrap/_flatpickr.html.erb +33 -0
  104. data/app/views/formstrap/_flatpickr_range.html.erb +40 -0
  105. data/app/views/formstrap/_hidden.html.erb +23 -0
  106. data/app/views/formstrap/_hint.html.erb +21 -0
  107. data/app/views/formstrap/_input_group.html.erb +21 -0
  108. data/app/views/formstrap/_label.html.erb +22 -0
  109. data/app/views/formstrap/_media.html.erb +60 -0
  110. data/app/views/formstrap/_number.html.erb +41 -0
  111. data/app/views/formstrap/_password.html.erb +39 -0
  112. data/app/views/formstrap/_redactorx.html.erb +31 -0
  113. data/app/views/formstrap/_repeater.html.erb +128 -0
  114. data/app/views/formstrap/_search.html.erb +43 -0
  115. data/app/views/formstrap/_select.html.erb +43 -0
  116. data/app/views/formstrap/_switch.html.erb +29 -0
  117. data/app/views/formstrap/_text.html.erb +42 -0
  118. data/app/views/formstrap/_textarea.html.erb +39 -0
  119. data/app/views/formstrap/_to_ary.html.erb +0 -0
  120. data/app/views/formstrap/_url.html.erb +43 -0
  121. data/app/views/formstrap/_validation.html.erb +18 -0
  122. data/app/views/formstrap/_wrapper.html.erb +8 -0
  123. data/app/views/formstrap/_wysiwyg.html.erb +28 -0
  124. data/app/views/formstrap/autocomplete/_item.html.erb +3 -0
  125. data/app/views/formstrap/autocomplete/_list.html.erb +3 -0
  126. data/app/views/formstrap/blocks/_modal.html.erb +20 -0
  127. data/app/views/formstrap/fields/_base.html.erb +25 -0
  128. data/app/views/formstrap/fields/_file.html.erb +17 -0
  129. data/app/views/formstrap/fields/_files.html.erb +17 -0
  130. data/app/views/formstrap/fields/_group.html.erb +52 -0
  131. data/app/views/formstrap/fields/_list.html.erb +31 -0
  132. data/app/views/formstrap/fields/_text.html.erb +17 -0
  133. data/app/views/formstrap/media/_item.html.erb +38 -0
  134. data/app/views/formstrap/media/_media_item_modal.html.erb +77 -0
  135. data/app/views/formstrap/media/_modal.html.erb +40 -0
  136. data/app/views/formstrap/media/_thumbnail.html.erb +20 -0
  137. data/app/views/formstrap/media/_validation.html.erb +10 -0
  138. data/app/views/formstrap/media/create.turbo_stream.erb +5 -0
  139. data/app/views/formstrap/media/index.html.erb +3 -0
  140. data/app/views/formstrap/media/index.turbo_stream.erb +11 -0
  141. data/app/views/formstrap/media/show.html.erb +9 -0
  142. data/app/views/formstrap/media/thumbnail.html.erb +3 -0
  143. data/app/views/formstrap/media/update.turbo_stream.erb +3 -0
  144. data/app/views/formstrap/pagination/_infinite.html.erb +7 -0
  145. data/app/views/formstrap/repeater/_row.html.erb +53 -0
  146. data/app/views/formstrap/shared/_notifications.html.erb +20 -0
  147. data/app/views/formstrap/shared/_popup.html.erb +32 -0
  148. data/app/views/formstrap/shared/_thumbnail.html.erb +35 -0
  149. data/bin/console +14 -0
  150. data/bin/setup +8 -0
  151. data/config/importmap.rb +2 -0
  152. data/config/locales/activerecord/en.yml +12 -0
  153. data/config/locales/activerecord/nl.yml +13 -0
  154. data/config/locales/defaults/en.yml +215 -0
  155. data/config/locales/defaults/nl.yml +213 -0
  156. data/config/locales/devise/en.yml +65 -0
  157. data/config/locales/devise/nl.yml +85 -0
  158. data/config/locales/en.yml +6 -0
  159. data/config/locales/formstrap/forms/en.yml +39 -0
  160. data/config/locales/formstrap/forms/nl.yml +39 -0
  161. data/config/locales/formstrap/media/en.yml +24 -0
  162. data/config/locales/formstrap/media/nl.yml +24 -0
  163. data/config/locales/formstrap/thumbnail/en.yml +4 -0
  164. data/config/locales/formstrap/thumbnail/nl.yml +4 -0
  165. data/config/locales/nl.yml +6 -0
  166. data/config/routes.rb +11 -0
  167. data/esbuild-css.js +25 -0
  168. data/esbuild-js.js +11 -0
  169. data/formstrap.gemspec +37 -0
  170. data/formstrap.iml +34 -0
  171. data/lib/formstrap/engine.rb +27 -0
  172. data/lib/formstrap/form_builder.rb +177 -0
  173. data/lib/formstrap/form_helper.rb +19 -0
  174. data/lib/formstrap/version.rb +3 -0
  175. data/lib/formstrap.rb +6 -0
  176. data/package.json +54 -0
  177. data/src/js/formstrap.js +1 -0
  178. data/src/scss/formstrap.scss +1 -0
  179. data/yarn.lock +1998 -0
  180. metadata +224 -0
@@ -0,0 +1,43 @@
1
+ module Formstrap
2
+ class DatetimeView < ViewModel
3
+ include Formstrap::Hintable
4
+ include Formstrap::InputGroupable
5
+ include Formstrap::Labelable
6
+ include Formstrap::Listable
7
+ include Formstrap::Placeholderable
8
+ include Formstrap::Validatable
9
+ include Formstrap::Wrappable
10
+
11
+ def input_options
12
+ keys = attributes - %i[append attribute float form input_group input_group label prepend validate wrapper]
13
+ options = to_h.slice(*keys)
14
+ default_input_options.deep_merge(options)
15
+ end
16
+
17
+ def input_group_options
18
+ default_input_group_options
19
+ .deep_merge(label_input_group_options)
20
+ .deep_merge(@input_group || {})
21
+ end
22
+
23
+ def wrapper_options
24
+ default_wrapper_options.deep_merge({
25
+ class: ["mb-3", ("form-floating" if float)]
26
+ }).deep_merge(@wrapper || {})
27
+ end
28
+
29
+ private
30
+
31
+ def default_input_options
32
+ {
33
+ aria: {describedby: validation_id},
34
+ class: [form_control_class, validation_class],
35
+ placeholder: placeholder
36
+ }
37
+ end
38
+
39
+ def form_control_class
40
+ plaintext ? "form-control-plaintext" : "form-control"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,46 @@
1
+ module Formstrap
2
+ class EmailView < ViewModel
3
+ include Formstrap::Autocompletable
4
+ include Formstrap::Hintable
5
+ include Formstrap::InputGroupable
6
+ include Formstrap::Labelable
7
+ include Formstrap::Listable
8
+ include Formstrap::Placeholderable
9
+ include Formstrap::Validatable
10
+ include Formstrap::Wrappable
11
+
12
+ def input_options
13
+ keys = attributes - %i[append attribute collection float form input_group label prepend validate wrapper]
14
+ options = to_h.slice(*keys)
15
+ options = default_input_options.deep_merge(options)
16
+ options.deep_merge(autocomplete_input_options)
17
+ end
18
+
19
+ def input_group_options
20
+ default_input_group_options
21
+ .deep_merge(autocomplete_input_group_options)
22
+ .deep_merge(label_input_group_options)
23
+ .deep_merge(@input_group || {})
24
+ end
25
+
26
+ def wrapper_options
27
+ default_wrapper_options.deep_merge({
28
+ class: ["mb-3", ("form-floating" if float)]
29
+ }).deep_merge(@wrapper || {})
30
+ end
31
+
32
+ private
33
+
34
+ def default_input_options
35
+ {
36
+ aria: {describedby: validation_id},
37
+ class: [form_control_class, validation_class],
38
+ placeholder: placeholder
39
+ }
40
+ end
41
+
42
+ def form_control_class
43
+ plaintext ? "form-control-plaintext" : "form-control"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,106 @@
1
+ module Formstrap
2
+ class FileView < ViewModel
3
+ include Formstrap::Hintable
4
+ include Formstrap::InputGroupable
5
+ include Formstrap::Labelable
6
+ include Formstrap::Placeholderable
7
+ include Formstrap::Validatable
8
+ include Formstrap::Wrappable
9
+
10
+ def input_options
11
+ keys = attributes - %i[append attribute dropzone destroy form input_group label prepend preview validate wrapper]
12
+ options = to_h.slice(*keys)
13
+ options = default_input_options.deep_merge(options)
14
+ options = options.deep_merge(required: false) if attachments.any?
15
+ options
16
+ end
17
+
18
+ def input_group_options
19
+ default_input_group_options
20
+ .deep_merge(label_input_group_options)
21
+ .deep_merge(@input_group || {})
22
+ end
23
+
24
+ def wrapper_options
25
+ default_wrapper_options.deep_merge({
26
+ class: ["mb-3 h-form-file", ("form-floating" if float)],
27
+ data: {
28
+ controller: ("file-preview" if preview)
29
+ }
30
+ }).deep_merge(@wrapper || {})
31
+ end
32
+
33
+ def preview
34
+ @preview || dropzone
35
+ end
36
+
37
+ def destroy
38
+ @destroy || false
39
+ end
40
+
41
+ def attached
42
+ form.object&.send(attribute)
43
+ end
44
+
45
+ def attachments
46
+ attached&.attachments || []
47
+ end
48
+
49
+ def number_of_files
50
+ multiple ? 2 : 1
51
+ end
52
+
53
+ def nested_attribute
54
+ attached.is_a?(ActiveStorage::Attached::Many) ? :"#{attribute}_attachments" : :"#{attribute}_attachment"
55
+ end
56
+
57
+ def dropzone_options
58
+ if dropzone
59
+ {
60
+ class: ["h-dropzone", validation_class],
61
+ data: {
62
+ controller: "dropzone"
63
+ }
64
+ }
65
+ else
66
+ {
67
+ class: validation_class
68
+ }
69
+ end
70
+ end
71
+
72
+ # def input_group_options
73
+ # {
74
+ # prepend: prepend,
75
+ # append: append,
76
+ # class: "h-form-file",
77
+ # data: {
78
+ # controller: "#{"file-preview" if preview} #{"dropzone" if dropzone}"
79
+ # }
80
+ # }
81
+ # end
82
+
83
+ private
84
+
85
+ def default_input_options
86
+ {
87
+ aria: {describedby: validation_id},
88
+ class: ["form-control", validation_class],
89
+ placeholder: placeholder,
90
+ data: preview_data
91
+ }
92
+ end
93
+
94
+ def preview_data
95
+ if preview
96
+ {
97
+ "file-preview-target": "input",
98
+ "dropzone-target": "input",
99
+ action: "change->file-preview#preview dropEnd->file-preview#preview"
100
+ }
101
+ else
102
+ {}
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,89 @@
1
+ module Formstrap
2
+ class FlatpickrRangeView < ViewModel
3
+ include Formstrap::Hintable
4
+ include Formstrap::InputGroupable
5
+ include Formstrap::Labelable
6
+ include Formstrap::Listable
7
+ include Formstrap::Placeholderable
8
+ include Formstrap::Validatable
9
+ include Formstrap::Wrappable
10
+
11
+ def attribute
12
+ @attribute || :period
13
+ end
14
+
15
+ def start_attribute
16
+ @start.dig(:attribute) || :start_date
17
+ end
18
+
19
+ def end_attribute
20
+ @end.dig(:attribute) || :end_date
21
+ end
22
+
23
+ def start_options
24
+ default_start_options.deep_merge(@start || {})
25
+ end
26
+
27
+ def end_options
28
+ default_end_options.deep_merge(@end || {})
29
+ end
30
+
31
+ def input_options
32
+ keys = attributes - %i[append attribute end float form input_group label prepend start validate wrapper]
33
+ options = to_h.slice(*keys)
34
+ default_input_options.deep_merge(options)
35
+ end
36
+
37
+ def input_group_options
38
+ default_input_group_options
39
+ .deep_merge(label_input_group_options)
40
+ .deep_merge(@input_group || {})
41
+ end
42
+
43
+ def wrapper_options
44
+ default_wrapper_options.deep_merge({
45
+ class: ["mb-3", ("form-floating" if float)]
46
+ }).deep_merge(@wrapper || {})
47
+ end
48
+
49
+ private
50
+
51
+ def default_options
52
+ {
53
+ name: nil
54
+ }
55
+ end
56
+
57
+ def default_start_options
58
+ {}
59
+ end
60
+
61
+ def default_end_options
62
+ {}
63
+ end
64
+
65
+ def default_input_options
66
+ {
67
+ aria: {describedby: validation_id},
68
+ class: [form_control_class, validation_class],
69
+ placeholder: placeholder,
70
+ name: nil,
71
+ data: {
72
+ controller: "flatpickr date-range",
73
+ action: "change->date-range#update",
74
+ flatpickr: {
75
+ mode: "range",
76
+ defaultDate: [
77
+ form.object&.send(start_attribute)&.strftime("%d/%m/%Y"),
78
+ form.object&.send(end_attribute)&.strftime("%d/%m/%Y")
79
+ ]
80
+ }
81
+ }
82
+ }
83
+ end
84
+
85
+ def form_control_class
86
+ plaintext ? "form-control-plaintext" : "form-control"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,27 @@
1
+ module Formstrap
2
+ class FlatpickrView < ViewModel
3
+ def options
4
+ keys = attributes - %i[data]
5
+ options = to_h.slice(*keys)
6
+ default_options.deep_merge(options)
7
+ end
8
+
9
+ private
10
+
11
+ def default_options
12
+ {
13
+ data: default_data.deep_merge(data || {})
14
+ }
15
+ end
16
+
17
+ def default_data
18
+ {
19
+ controller: "flatpickr",
20
+
21
+ flatpickr: {
22
+ defaultDate: attribute.nil? ? Date.today : form.object&.send(attribute)&.strftime("%d/%m/%Y")
23
+ }
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ module Formstrap
2
+ class HiddenView < ViewModel
3
+ def input_options
4
+ keys = attributes - %i[attribute form]
5
+ to_h.slice(*keys)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ module Formstrap
2
+ class HintView < ViewModel
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module Formstrap
2
+ class InputGroupView < ViewModel
3
+ def options
4
+ keys = attributes - %i[bypass append prepend]
5
+ options = to_h.slice(*keys)
6
+ default_options.deep_merge(options)
7
+ end
8
+
9
+ private
10
+
11
+ def default_options
12
+ {
13
+ class: ["input-group"]
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Formstrap
2
+ class LabelView < ViewModel
3
+ def form
4
+ @form.object_id
5
+ end
6
+
7
+ def options
8
+ keys = attributes - %i[attribute form text]
9
+ options = to_h.slice(*keys)
10
+ default_options.deep_merge(options)
11
+ end
12
+
13
+ private
14
+
15
+ def default_options
16
+ {
17
+ class: "form-label",
18
+ required: required
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ module Formstrap
2
+ class MediaItemView < ViewModel
3
+ include Rails.application.routes.url_helpers
4
+
5
+ def thumbnail_options
6
+ options = {
7
+ file: attachment
8
+ }
9
+
10
+ # Don't pass width or height if it was not defined
11
+ options = options.merge(width: width) if is_defined?(:width)
12
+ options = options.merge(height: height) if is_defined?(:height)
13
+
14
+ options
15
+ end
16
+
17
+ def attachment
18
+ form.object
19
+ end
20
+
21
+ def position_value
22
+ attachment.new_record? ? nil : attachment.position
23
+ end
24
+
25
+ def id
26
+ attachment.blob ? attachment.blob.id : "$1"
27
+ end
28
+
29
+ def filename
30
+ attachment.blob&.filename&.to_s
31
+ end
32
+
33
+ def content_type
34
+ attachment.blob&.content_type
35
+ end
36
+
37
+ def size
38
+ number_to_human_size(attachment.blob&.byte_size || 0)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,143 @@
1
+ module Formstrap
2
+ class MediaView < ViewModel
3
+ include Rails.application.routes.url_helpers
4
+ include Formstrap::Hintable
5
+ include Formstrap::Labelable
6
+ include Formstrap::Placeholderable
7
+ include Formstrap::Validatable
8
+ include Formstrap::Wrappable
9
+
10
+ def input_group_options
11
+ default_input_group_options
12
+ .deep_merge(label_input_group_options)
13
+ .deep_merge(@input_group || {})
14
+ end
15
+
16
+ def wrapper_options
17
+ default_wrapper_options.deep_merge({
18
+ class: ["mb-3", ("form-floating" if float)],
19
+ data: {
20
+ controller: "media",
21
+ name: name,
22
+ min: min,
23
+ max: max,
24
+ sort: sort,
25
+ accept: accept,
26
+ required: required.nil? ? 0 : required
27
+ }
28
+ }).deep_merge(@wrapper || {})
29
+ end
30
+
31
+ def item_options
32
+ options = {
33
+ sort: sort
34
+ }
35
+
36
+ # Don't pass width or height if it was not defined
37
+ options = options.merge(width: width) if is_defined?(:width)
38
+ options = options.merge(height: height) if is_defined?(:height)
39
+
40
+ options
41
+ end
42
+
43
+ def custom_validation_options
44
+ {
45
+ form: form,
46
+ attribute: attribute,
47
+ min: min,
48
+ max: max,
49
+ accept: accept
50
+ }
51
+ end
52
+
53
+ def thumbnail_options
54
+ options = {
55
+ icon: "plus"
56
+ }
57
+
58
+ # Don't pass width or height if it was not defined
59
+ options = options.merge(width: width) unless is_defined?(:width)
60
+ options = options.merge(height: height) unless is_defined?(:height)
61
+
62
+ options
63
+ end
64
+
65
+ def association_object
66
+ if attached.is_a?(ActiveStorage::Attached::Many)
67
+ result = form.object.send(nested_attribute)
68
+
69
+ # We sort the collection with ruby to prevent a new query to be made that would dispose of the objects in memory
70
+ result = result.sort_by { |item| item.position } if sort
71
+ result
72
+ else
73
+ form.object.send(nested_attribute)
74
+ end
75
+ end
76
+
77
+ def attachments
78
+ if attached.is_a?(ActiveStorage::Attached::Many)
79
+ result = form.object.send(nested_attribute)
80
+
81
+ # We sort the collection with ruby to prevent a new query to be made that would dispose of the objects in memory
82
+ result = result.sort_by { |item| item.position } if sort
83
+ result.to_a.compact
84
+ else
85
+ [form.object.send(nested_attribute)].compact
86
+ end
87
+ end
88
+
89
+ def attached
90
+ form.object.send(attribute)
91
+ end
92
+
93
+ def build_nested_attribute
94
+ if attached.is_a?(ActiveStorage::Attached::Many)
95
+ form.object.send(nested_attribute).build
96
+ else
97
+ form.object.send("build_#{nested_attribute}")
98
+ end
99
+ end
100
+
101
+ def nested_attribute
102
+ if attached.is_a?(ActiveStorage::Attached::Many)
103
+ :"#{attribute}_attachments"
104
+ else
105
+ :"#{attribute}_attachment"
106
+ end
107
+ end
108
+
109
+ def min
110
+ if @required
111
+ (@min.to_i < 1) ? 1 : @min.to_i
112
+ else
113
+ (@min.to_i < 1) ? 0 : @min.to_i
114
+ end
115
+ end
116
+
117
+ def max
118
+ if attached.is_a?(ActiveStorage::Attached::Many)
119
+ @max
120
+ else
121
+ 1
122
+ end
123
+ end
124
+
125
+ attr_reader :accept
126
+
127
+ def required
128
+ @required ? 1 : nil
129
+ end
130
+
131
+ def blob_ids
132
+ attachments.map { |attachment| attachment.blob_id }
133
+ end
134
+
135
+ def name
136
+ "#{attribute}_#{object_id}"
137
+ end
138
+
139
+ def sort
140
+ @sort == true
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,47 @@
1
+ module Formstrap
2
+ class NumberView < ViewModel
3
+ include Formstrap::Hintable
4
+ include Formstrap::InputGroupable
5
+ include Formstrap::Labelable
6
+ include Formstrap::Listable
7
+ include Formstrap::Placeholderable
8
+ include Formstrap::Validatable
9
+ include Formstrap::Wrappable
10
+
11
+ def input_options
12
+ keys = attributes - %i[append attribute float form input_group label prepend validate wrapper]
13
+ options = to_h.slice(*keys)
14
+ default_input_options.deep_merge(options)
15
+ end
16
+
17
+ def input_group_options
18
+ default_input_group_options
19
+ .deep_merge(label_input_group_options)
20
+ .deep_merge(@input_group || {})
21
+ end
22
+
23
+ def wrapper_options
24
+ default_wrapper_options.deep_merge({
25
+ class: ["mb-3", float_class]
26
+ }).deep_merge(@wrapper || {})
27
+ end
28
+
29
+ private
30
+
31
+ def float_class
32
+ "form-floating" if float
33
+ end
34
+
35
+ def default_input_options
36
+ {
37
+ aria: {describedby: validation_id},
38
+ class: [form_control_class, validation_class],
39
+ placeholder: placeholder
40
+ }
41
+ end
42
+
43
+ def form_control_class
44
+ plaintext ? "form-control-plaintext" : "form-control"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,42 @@
1
+ module Formstrap
2
+ class PasswordView < ViewModel
3
+ include Formstrap::Hintable
4
+ include Formstrap::InputGroupable
5
+ include Formstrap::Labelable
6
+ include Formstrap::Placeholderable
7
+ include Formstrap::Validatable
8
+ include Formstrap::Wrappable
9
+
10
+ def input_options
11
+ keys = attributes - %i[append attribute float form input_group label prepend validate wrapper]
12
+ options = to_h.slice(*keys)
13
+ default_input_options.deep_merge(options)
14
+ end
15
+
16
+ def input_group_options
17
+ default_input_group_options
18
+ .deep_merge(label_input_group_options)
19
+ .deep_merge(@input_group || {})
20
+ end
21
+
22
+ def wrapper_options
23
+ default_wrapper_options.deep_merge({
24
+ class: ["mb-3", ("form-floating" if float)]
25
+ }).deep_merge(@wrapper || {})
26
+ end
27
+
28
+ private
29
+
30
+ def default_input_options
31
+ {
32
+ aria: {describedby: validation_id},
33
+ class: [form_control_class, validation_class],
34
+ placeholder: placeholder
35
+ }
36
+ end
37
+
38
+ def form_control_class
39
+ plaintext ? "form-control-plaintext" : "form-control"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ module Formstrap
2
+ class RedactorxView < ViewModel
3
+ def options
4
+ default_options.deep_merge(to_h)
5
+ end
6
+
7
+ private
8
+
9
+ def default_options
10
+ {
11
+ data: {
12
+ controller: "redactorx",
13
+ "redactor-options": redactor_options
14
+ }
15
+ }
16
+ end
17
+
18
+ def control
19
+ @control || (hybrid ? true : nil)
20
+ end
21
+
22
+ def context
23
+ @context || (hybrid ? true : nil)
24
+ end
25
+
26
+ def toolbar
27
+ @toolbar || (hybrid ? false : nil)
28
+ end
29
+
30
+ def addbar
31
+ @addbar || %w[paragraph image embed table quote pre line]
32
+ end
33
+
34
+ def plugins
35
+ @plugins || %w[shortcut]
36
+ end
37
+
38
+ attr_reader :topbar
39
+
40
+ def redactor_options
41
+ default_redactor_options.deep_merge(redactor || {})
42
+ end
43
+
44
+ def default_redactor_options
45
+ {
46
+ control: control,
47
+ context: context,
48
+ toolbar: toolbar,
49
+ buttons: {
50
+ addbar: addbar,
51
+ plugins: plugins,
52
+ topbar: topbar
53
+ }
54
+ }.delete_if { |k, v| v.nil? }
55
+ end
56
+ end
57
+ end