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,38 @@
1
+ <%
2
+ # formstrap/media/item
3
+ #
4
+ # ==== Required parameters
5
+ # * +form+ - Form object
6
+ # * +url+ - URL for the media modal
7
+ #
8
+ # ==== Optional parameters
9
+ # * +sort+ - Allow sorting by dragging items. `active_storage_attachments` must have a position column.
10
+ # * +width+ - Width of the thumbnail
11
+ # * +height+ - Height of the thumbnail
12
+ #
13
+
14
+ media_item = Formstrap::MediaItemView.new(local_assigns)
15
+ %>
16
+
17
+ <div class="h-form-file-thumbnail media-drag-sort-handle" title="<%= "#{media_item.filename} (#{media_item.size})" %>" data-media-target="item">
18
+ <%= form.hidden_field(:id) %>
19
+ <%= form.hidden_field(:blob_id) %>
20
+ <%= form.hidden_field(:position, value: media_item.position_value) if media_item.sort %>
21
+ <%= form.hidden_field(:_destroy) %>
22
+
23
+ <a href="<%= media_item.url %>" data-turbo-frame="remote_modal" data-media-target="modalButton">
24
+ <%= render "formstrap/shared/thumbnail", media_item.thumbnail_options %>
25
+ </a>
26
+
27
+ <div class="h-form-file-thumbnail-actions">
28
+ <!-- Edit -->
29
+ <a href="<%= formstrap_media_item_path(id: media_item.id) %>" class="h-form-file-thumbnail-edit" data-turbo-frame="remote_modal" data-media-target="editButton">
30
+ <%= bootstrap_icon("pencil") %>
31
+ </a>
32
+
33
+ <!-- Remove -->
34
+ <div class="h-form-file-thumbnail-remove" data-action="click->media#destroy">
35
+ <%= bootstrap_icon("x") %>
36
+ </div>
37
+ </div>
38
+ </div>
@@ -0,0 +1,77 @@
1
+ <%= turbo_frame_tag "modal_content" do %>
2
+ <%= formstrap_form_with url: formstrap_media_item_url(id: @blob.id), model: @blob do |form| %>
3
+ <div class="modal-header">
4
+ <h5 class="modal-title"><%= t(".edit") %></h5>
5
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<%= t(".close") %>"></button>
6
+ </div>
7
+ <div class="modal-body">
8
+ <%= render "formstrap/shared/notifications" %>
9
+ <div class="row">
10
+ <div class="col-4">
11
+ <%= render "formstrap/shared/thumbnail", file: @blob %>
12
+ </div>
13
+ <div class="col-8 d-flex flex-column justify-content-center">
14
+ <p class="small text-secondary m-0">
15
+ <strong><%= t(".uploaded_at") %>:</strong>
16
+ <%= l(@blob.created_at, format: :long) %>
17
+ </p>
18
+ <p class="small text-secondary m-0">
19
+ <strong><%= t(".type") %>:</strong>
20
+ <%= @blob.content_type %>
21
+ </p>
22
+ <p class="small text-secondary m-0">
23
+ <strong><%= t(".dimensions") %>:</strong>
24
+ <% if @blob.metadata[:width] && @blob.metadata[:height] %>
25
+ <%= "#{@blob.metadata[:width]}px x #{@blob.metadata[:height]}px" %>
26
+ <% else %>
27
+ <%= t(".not_analysed") %>
28
+ <% end %>
29
+ </p>
30
+ <p class="small text-secondary m-0">
31
+ <strong><%= t(".size") %>:</strong>
32
+ <% if @blob.byte_size > 0 %>
33
+ <%= number_to_human_size(@blob.byte_size) %>
34
+ <% else %>
35
+ <%= t(".not_analysed") %>
36
+ <% end %>
37
+ </p>
38
+ </div>
39
+ </div>
40
+ <div class="row mt-3">
41
+ <div class="col-12">
42
+ <%= form.text :filename, append: "." + form.object.filename.to_s.rpartition(".").last, value: form.object.filename.to_s.rpartition(".").first %>
43
+ </div>
44
+ </div>
45
+ <% attachments = @blob.attachments.not_a_variant.all %>
46
+ <% if attachments.any? %>
47
+ <hr>
48
+ <h6><%= t(".attachment_used_by") %>:</h6>
49
+ <table class="table table-sm">
50
+ <tbody>
51
+ <% attachments.each do |attachment| %>
52
+ <% context_resource = attachment.record_hierarchy.last %>
53
+ <% context_resource_path = polymorphic_path([:admin, context_resource]) rescue nil %>
54
+ <tr>
55
+ <td width="40%">
56
+ <% if context_resource_path.present? %>
57
+ <%= link_to truncate(context_resource.to_s), context_resource_path, title: context_resource %>
58
+ <% else %>
59
+ <%= context_resource %>
60
+ <% end %>
61
+ </td>
62
+ <td width="25%"><%= "#{context_resource.model_name.human(count: 1)} (#{attachment.class.human_attribute_name(attachment.name.to_sym)})" %></td>
63
+ <td width="35%">
64
+ <small class="text-secondary"><%= l(attachment.created_at, format: :long) %></small>
65
+ </td>
66
+ </tr>
67
+ <% end %>
68
+ </tbody>
69
+ </table>
70
+ <% end %>
71
+ </div>
72
+ <div class="modal-footer">
73
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t(".close") %></button>
74
+ <%= form.submit t(".update"), class: "btn btn-primary" %>
75
+ </div>
76
+ <% end %>
77
+ <% end %>
@@ -0,0 +1,40 @@
1
+ <div class="media-modal modal fade" tabindex="-1" data-controller="remote-modal media-modal" data-media-modal-ids-value="<%= params[:ids] ? params[:ids] : [] %>" data-name="<%= name %>" data-min="<%= min %>" data-max="<%= max %>">
2
+ <div class="modal-dialog modal-lg modal-dialog-scrollable">
3
+ <div class="modal-content">
4
+ <div class="modal-header">
5
+ <h5 class="modal-title">
6
+ <%= t(".title", count: min.to_i < 1 ? 1 : min.to_i) %>
7
+ </h5>
8
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<%= t(".close") %>"></button>
9
+ </div>
10
+ <div class="modal-body">
11
+ <%= turbo_frame_tag "thumbnails", class: "d-flex flex-wrap gap-2" do %>
12
+ <% @blobs.each do |blob| %>
13
+ <%= turbo_frame_tag blob, src: formstrap_media_item_thumbnail_path(blob), loading: "lazy" do %>
14
+ <%= render "thumbnail" %>
15
+ <% end %>
16
+ <% end %>
17
+ <div data-media-modal-target="placeholder" class="<%= "d-none" if !@blobs.empty? %>">
18
+ <p><%= t(".placeholder") %></p>
19
+ </div>
20
+ <% end %>
21
+ <div class="mt-3">
22
+ <%= render "formstrap/pagination/infinite", items: @blobs %>
23
+ </div>
24
+ </div>
25
+ <div class="modal-footer">
26
+ <%= form_with url: formstrap_new_media_path, multipart: true, data: {"media-modal-target": "form"}, class: "me-auto" do |form| %>
27
+ <%= form.label :files, class: "btn h-btn-outline-light" do %>
28
+ <%= bootstrap_icon("upload") %>
29
+ <%= t(".upload") %>
30
+ <%= form.file_field :files, accept: mimetypes, class: "d-none", multiple: true, data: {action: "change->media-modal#submitForm"} %>
31
+ <% end %>
32
+ <% end %>
33
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t(".close") %></button>
34
+ <button type="button" class="btn btn-primary" data-bs-dismiss="modal" data-action="click->media-modal#select" data-media-modal-target="selectButton">
35
+ <%= t(".select") %> (<span data-media-modal-target="count">0</span><%= t(".maximum", count: max.to_i) if max.present? %>)
36
+ </button>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,20 @@
1
+ <% blob = local_assigns.has_key?(:blob) ? local_assigns[:blob] : nil %>
2
+ <% if blob.present? %>
3
+ <div data-media-modal-target="item" title="<%= "#{blob.filename} (#{l(blob.created_at, format: :long)})" %>">
4
+ <!-- Input -->
5
+ <input
6
+ id="media-item-<%= blob.id %>"
7
+ type="checkbox"
8
+ value="<%= blob.id %>"
9
+ data-action="change->media-modal#inputChange"
10
+ data-media-modal-target="idCheckbox"
11
+ hidden>
12
+
13
+ <!-- Label -->
14
+ <label for="media-item-<%= blob.id %>">
15
+ <%= render "formstrap/shared/thumbnail", file: blob %>
16
+ </label>
17
+ </div>
18
+ <% else %>
19
+ <%= render "formstrap/shared/thumbnail", file: nil %>
20
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <!-- Custom validation field -->
2
+ <%= form.text_field :"validation_#{attribute}",
3
+ name: nil,
4
+ value: nil,
5
+ class: "h-form-media-validation",
6
+ data: {
7
+ "media-target": "validationInput",
8
+ "min-message": t(".min", count: min),
9
+ "max-message": t(".max", count: max),
10
+ } %>
@@ -0,0 +1,5 @@
1
+ <%= turbo_stream.prepend "thumbnails" do %>
2
+ <% @blobs.each do |blob| %>
3
+ <%= render "formstrap/media/thumbnail", blob: blob %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_frame_tag "remote_modal" do %>
2
+ <%= render "formstrap/media/modal", blobs: @blobs, mimetypes: @mimetypes, name: params[:name], min: params[:min], max: params[:max] %>
3
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= turbo_stream.append "thumbnails" do %>
2
+ <% @blobs.each do |blob| %>
3
+ <%= turbo_frame_tag blob, src: formstrap_media_item_thumbnail_path(blob), loading: "lazy" do %>
4
+ <%= render 'thumbnail' %>
5
+ <% end %>
6
+ <% end %>
7
+ <% end %>
8
+
9
+ <%= turbo_stream.replace "infinite" do %>
10
+ <%= render "formstrap/pagination/infinite", items: @blobs %>
11
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= turbo_frame_tag "remote_modal" do %>
2
+ <div class="media-item-modal modal fade" tabindex="-1" data-controller="remote-modal">
3
+ <div class="modal-dialog modal-md modal-dialog-scrollable">
4
+ <div class="modal-content">
5
+ <%= render "formstrap/media/media_item_modal" %>
6
+ </div>
7
+ </div>
8
+ </div>
9
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_frame_tag @blob do %>
2
+ <%= render "thumbnail", blob: @blob %>
3
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_stream.update "modal_content" do %>
2
+ <%= render "formstrap/media/media_item_modal" %>
3
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% auto_load = local_assigns.has_key?(:auto_load) ? local_assigns[:auto_load] : true %>
2
+
3
+ <% unless items.page(params[:page]).last_page? %>
4
+ <div id="infinite">
5
+ <%= link_to t(".load_more"), path_to_next_page(items), class: "btn btn-primary", data: {turbo_stream: true, controller: auto_load ? "infinite-scroller" : ""} %>
6
+ </div>
7
+ <% end %>
@@ -0,0 +1,53 @@
1
+ <%
2
+ # admin/fields/repeater/row
3
+ # accepts block: yes
4
+ # parameters:
5
+ # form: Form object
6
+ # pass_thru: (string) Pass thru for add button if needed
7
+
8
+ draggable = form.object.respond_to?(:position)
9
+ destroyable = form.object.respond_to?(:destroy)
10
+ error_class = form.object.errors.present? ? "border border-danger" : ""
11
+ class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : "repeater-row list-group-item"
12
+ %>
13
+ <div class="<%= class_names %> <%= error_class %>"
14
+ data-repeater-target="row"
15
+ data-row-index="<%= form.options[:child_index] %>"
16
+ data-new-record="<%= form.object.new_record? %>"
17
+ >
18
+
19
+ <%= form.hidden_field :id %>
20
+ <%= form.hidden_field :_destroy if destroyable %>
21
+ <%= form.hidden_field :position if draggable %>
22
+
23
+ <!-- Drag handle -->
24
+ <% if draggable %>
25
+ <div class="repeater-row-handle">
26
+ <%= bootstrap_icon("grip-vertical") %>
27
+ </div>
28
+ <% end %>
29
+
30
+ <!-- Add button-->
31
+ <div
32
+ class="repeater-row-add btn btn-link"
33
+ title="<%= t(".add") %>"
34
+ data-repeater-target="addButton"
35
+ data-popup-target="button"
36
+ data-popup-id="<%= "repeater-buttons-#{repeater_id}" %>"
37
+ data-popup-pass-thru="<%= pass_thru %>"
38
+ data-action="click->repeater#resetButtonIndices click->popup#open"
39
+ >
40
+ <%= bootstrap_icon("plus-circle") %>
41
+ </div>
42
+
43
+ <!-- Remove button-->
44
+ <div
45
+ class="repeater-row-remove btn btn-link"
46
+ title="<%= t(".remove") %>"
47
+ data-action="click->repeater#removeRow"
48
+ >
49
+ <%= bootstrap_icon("dash-circle") %>
50
+ </div>
51
+
52
+ <%= yield %>
53
+ </div>
@@ -0,0 +1,20 @@
1
+ <%
2
+ # formstrap/shared/notifications
3
+ #
4
+ # ==== Examples
5
+ # Basic version
6
+ # <%= render "formstrap/shared/notifications" %#>
7
+ %>
8
+
9
+ <div class="toast-container position-absolute top-0 end-0 p-3">
10
+ <% flash.each do |name, message| %>
11
+ <div class="toast align-items-center text-white show <%= notification_color(name.to_sym)[:background] %>" role="alert" aria-live="assertive" aria-atomic="true" data-controller="toast">
12
+ <div class="d-flex">
13
+ <div class="toast-body <%= notification_color(name.to_sym)[:text] %>">
14
+ <%= message %>
15
+ </div>
16
+ <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
17
+ </div>
18
+ </div>
19
+ <% end %>
20
+ </div>
@@ -0,0 +1,32 @@
1
+ <%
2
+ # formstrap/shared/popup
3
+ #
4
+ # ==== Options
5
+ # * +id</tt> - Identifier for this popup
6
+ #
7
+ # ==== Examples
8
+ # To trigger this popup create an element with a data attribute like this
9
+ #
10
+ # <button data-popup-id="popupId">open Popup</button>
11
+ # <%= render "formstrap/shared/popup", id: "popupId" do %#>
12
+ # popup Content
13
+ # <% end %#>
14
+ #
15
+ # If you want to pass a click event to an element inside the popup without opening the popup you can use the
16
+ # "data-popup-pass-thru" attribute.
17
+ #
18
+ # <button data-popup-id="popupId" data-popup-pass-thru="#link">open Popup</button>
19
+ # <%= render "formstrap/shared/popup", id: "popupId" do %#>
20
+ # <a href="https://example.com" id="link">
21
+ # <% end %#>
22
+ data = local_assigns.has_key?(:data) ? data : {}
23
+
24
+ options = {
25
+ class: ["h-popup", "closed"],
26
+ data: data.merge("popup-target": "popup", "popup-id": id)
27
+ }
28
+ %>
29
+
30
+ <%= content_tag(:div, options) do %>
31
+ <%= yield %>
32
+ <% end %>
@@ -0,0 +1,35 @@
1
+ <%
2
+ # formstrap/shared/thumbnail
3
+ #
4
+ # ==== Required parameters
5
+ # * +file+ - Name of the attribute of the form model
6
+ #
7
+ # ==== Optional parameters
8
+ # * +width+ - Width of the thumbnail. Set to nil to fill according to the original aspect ratio
9
+ # * +height+ - Height of the thumbnail. Set to nil to fill according to the original aspect ratio
10
+ # * +icon+ - Force icon to be shown instead of a thumbnail
11
+ #
12
+ # ==== References
13
+ # https://headmin.dev/docs/thumbnail
14
+ #
15
+ # ==== Examples
16
+ # Basic version
17
+ # <%= render "formstrap/shared/thumbnail", file: file %#>
18
+ #
19
+ # Custom size (200px x 100px)
20
+ # <%= render "formstrap/shared/thumbnail", file: file, width: 200, height: 100 %#>
21
+ #
22
+ # Custom (bootstrap) icon
23
+ # <%= render "formstrap/shared/thumbnail", file: file, icon: "file-earmark-excel" %#>
24
+ thumbnail = Admin::ThumbnailView.new(local_assigns)
25
+ %>
26
+
27
+ <div class="<%= thumbnail.class_names %>">
28
+ <div class="h-thumbnail-bg" style="min-width: <%= thumbnail.width || 100 %>px; min-height: <%= thumbnail.height || 100 %>px;">
29
+ <% if thumbnail.variable? && !thumbnail.icon? %>
30
+ <%= image_tag(thumbnail.file.variant(thumbnail.variant_options)) %>
31
+ <% else %>
32
+ <%= bootstrap_icon(thumbnail.icon_name, class: "h-thumbnail-icon") %>
33
+ <% end %>
34
+ </div>
35
+ </div>
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "formstrap"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,2 @@
1
+ # Pin npm packages by running ./bin/importmap
2
+ pin "formstrap"
@@ -0,0 +1,12 @@
1
+ en:
2
+ attributes:
3
+ created_at: Created at
4
+ email: Email
5
+ filename: Filename
6
+ handle: Handle
7
+ password: Password
8
+ password_confirmation: Password confirmation
9
+ remember_me: Stay logged in
10
+ updated_at: Updated at
11
+ value: Value
12
+ visible: Visible?
@@ -0,0 +1,13 @@
1
+ nl:
2
+ attributes:
3
+ created_at: Aangemaakt
4
+ email: E-mailadres
5
+ filename: Bestandsnaam
6
+ handle: Naam
7
+ password: Wachtwoord
8
+ password_confirmation: Wachtwoord bevestiging
9
+ remember_me: Aangemeld blijven
10
+ updated_at: Aangepast
11
+ value: Waarde
12
+ visible: Zichtbaar?
13
+
@@ -0,0 +1,215 @@
1
+ ---
2
+ en:
3
+ activerecord:
4
+ errors:
5
+ messages:
6
+ record_invalid: 'Validation failed: %{errors}'
7
+ restrict_dependent_destroy:
8
+ has_one: Cannot delete record because a dependent %{record} exists
9
+ has_many: Cannot delete record because dependent %{record} exist
10
+ date:
11
+ abbr_day_names:
12
+ - Sun
13
+ - Mon
14
+ - Tue
15
+ - Wed
16
+ - Thu
17
+ - Fri
18
+ - Sat
19
+ abbr_month_names:
20
+ -
21
+ - Jan
22
+ - Feb
23
+ - Mar
24
+ - Apr
25
+ - May
26
+ - Jun
27
+ - Jul
28
+ - Aug
29
+ - Sep
30
+ - Oct
31
+ - Nov
32
+ - Dec
33
+ day_names:
34
+ - Sunday
35
+ - Monday
36
+ - Tuesday
37
+ - Wednesday
38
+ - Thursday
39
+ - Friday
40
+ - Saturday
41
+ formats:
42
+ default: "%Y-%m-%d"
43
+ long: "%B %d, %Y"
44
+ short: "%b %d"
45
+ month_names:
46
+ -
47
+ - January
48
+ - February
49
+ - March
50
+ - April
51
+ - May
52
+ - June
53
+ - July
54
+ - August
55
+ - September
56
+ - October
57
+ - November
58
+ - December
59
+ order:
60
+ - :year
61
+ - :month
62
+ - :day
63
+ datetime:
64
+ distance_in_words:
65
+ about_x_hours:
66
+ one: about 1 hour
67
+ other: about %{count} hours
68
+ about_x_months:
69
+ one: about 1 month
70
+ other: about %{count} months
71
+ about_x_years:
72
+ one: about 1 year
73
+ other: about %{count} years
74
+ almost_x_years:
75
+ one: almost 1 year
76
+ other: almost %{count} years
77
+ half_a_minute: half a minute
78
+ less_than_x_seconds:
79
+ one: less than 1 second
80
+ other: less than %{count} seconds
81
+ less_than_x_minutes:
82
+ one: less than a minute
83
+ other: less than %{count} minutes
84
+ over_x_years:
85
+ one: over 1 year
86
+ other: over %{count} years
87
+ x_seconds:
88
+ one: 1 second
89
+ other: "%{count} seconds"
90
+ x_minutes:
91
+ one: 1 minute
92
+ other: "%{count} minutes"
93
+ x_days:
94
+ one: 1 day
95
+ other: "%{count} days"
96
+ x_months:
97
+ one: 1 month
98
+ other: "%{count} months"
99
+ x_years:
100
+ one: 1 year
101
+ other: "%{count} years"
102
+ prompts:
103
+ second: Second
104
+ minute: Minute
105
+ hour: Hour
106
+ day: Day
107
+ month: Month
108
+ year: Year
109
+ errors:
110
+ format: "%{attribute} %{message}"
111
+ messages:
112
+ accepted: must be accepted
113
+ blank: can't be blank
114
+ confirmation: doesn't match %{attribute}
115
+ empty: can't be empty
116
+ equal_to: must be equal to %{count}
117
+ even: must be even
118
+ exclusion: is reserved
119
+ greater_than: must be greater than %{count}
120
+ greater_than_or_equal_to: must be greater than or equal to %{count}
121
+ inclusion: is not included in the list
122
+ invalid: is invalid
123
+ less_than: must be less than %{count}
124
+ less_than_or_equal_to: must be less than or equal to %{count}
125
+ model_invalid: 'Validation failed: %{errors}'
126
+ not_a_number: is not a number
127
+ not_an_integer: must be an integer
128
+ odd: must be odd
129
+ other_than: must be other than %{count}
130
+ present: must be blank
131
+ required: must exist
132
+ taken: has already been taken
133
+ too_long:
134
+ one: is too long (maximum is 1 character)
135
+ other: is too long (maximum is %{count} characters)
136
+ too_short:
137
+ one: is too short (minimum is 1 character)
138
+ other: is too short (minimum is %{count} characters)
139
+ wrong_length:
140
+ one: is the wrong length (should be 1 character)
141
+ other: is the wrong length (should be %{count} characters)
142
+ template:
143
+ body: 'There were problems with the following fields:'
144
+ header:
145
+ one: 1 error prohibited this %{model} from being saved
146
+ other: "%{count} errors prohibited this %{model} from being saved"
147
+ helpers:
148
+ select:
149
+ prompt: Please select
150
+ submit:
151
+ create: Create %{model}
152
+ submit: Save %{model}
153
+ update: Update %{model}
154
+ number:
155
+ currency:
156
+ format:
157
+ delimiter: ","
158
+ format: "%u%n"
159
+ precision: 2
160
+ separator: "."
161
+ significant: false
162
+ strip_insignificant_zeros: false
163
+ unit: "$"
164
+ format:
165
+ delimiter: ","
166
+ precision: 3
167
+ separator: "."
168
+ significant: false
169
+ strip_insignificant_zeros: false
170
+ human:
171
+ decimal_units:
172
+ format: "%n %u"
173
+ units:
174
+ billion: Billion
175
+ million: Million
176
+ quadrillion: Quadrillion
177
+ thousand: Thousand
178
+ trillion: Trillion
179
+ unit: ''
180
+ format:
181
+ delimiter: ''
182
+ precision: 3
183
+ significant: true
184
+ strip_insignificant_zeros: true
185
+ storage_units:
186
+ format: "%n %u"
187
+ units:
188
+ byte:
189
+ one: Byte
190
+ other: Bytes
191
+ eb: EB
192
+ gb: GB
193
+ kb: KB
194
+ mb: MB
195
+ pb: PB
196
+ tb: TB
197
+ percentage:
198
+ format:
199
+ delimiter: ''
200
+ format: "%n%"
201
+ precision:
202
+ format:
203
+ delimiter: ''
204
+ support:
205
+ array:
206
+ last_word_connector: ", and "
207
+ two_words_connector: " and "
208
+ words_connector: ", "
209
+ time:
210
+ am: am
211
+ formats:
212
+ default: "%a, %d %b %Y %H:%M:%S %z"
213
+ long: "%B %d, %Y %H:%M"
214
+ short: "%d %b %H:%M"
215
+ pm: pm