formstrap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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