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.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +33 -0
- data/CHANGELOG.md +1 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +28 -0
- data/LICENSE.txt +21 -0
- data/README.md +118 -0
- data/Rakefile +10 -0
- data/app/assets/config/headmin_manifest.js +2 -0
- data/app/assets/images/avatar.jpg +0 -0
- data/app/assets/images/document.docx +0 -0
- data/app/assets/images/document.pdf +0 -0
- data/app/assets/images/image.jpg +0 -0
- data/app/assets/images/spreadsheet.xls +0 -0
- data/app/assets/images/video.mp4 +0 -0
- data/app/assets/javascripts/formstrap/config/i18n.js +11 -0
- data/app/assets/javascripts/formstrap/controllers/autocomplete_controller.js +318 -0
- data/app/assets/javascripts/formstrap/controllers/date_range_controller.js +38 -0
- data/app/assets/javascripts/formstrap/controllers/dropzone_controller.js +31 -0
- data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +244 -0
- data/app/assets/javascripts/formstrap/controllers/flatpickr_controller.js +35 -0
- data/app/assets/javascripts/formstrap/controllers/infinite_scroller_controller.js +28 -0
- data/app/assets/javascripts/formstrap/controllers/media_controller.js +252 -0
- data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +147 -0
- data/app/assets/javascripts/formstrap/controllers/redactorx_controller.js +40 -0
- data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +148 -0
- data/app/assets/javascripts/formstrap/controllers/select_controller.js +49 -0
- data/app/assets/javascripts/formstrap/controllers/textarea_controller.js +48 -0
- data/app/assets/javascripts/formstrap/index.js +32 -0
- data/app/assets/javascripts/formstrap.js +11515 -0
- data/app/assets/stylesheets/formstrap/forms/autocomplete.scss +27 -0
- data/app/assets/stylesheets/formstrap/forms/file.scss +83 -0
- data/app/assets/stylesheets/formstrap/forms/media.scss +10 -0
- data/app/assets/stylesheets/formstrap/forms/repeater.scss +62 -0
- data/app/assets/stylesheets/formstrap/forms/search.scss +12 -0
- data/app/assets/stylesheets/formstrap/forms.scss +12 -0
- data/app/assets/stylesheets/formstrap/general.scss +18 -0
- data/app/assets/stylesheets/formstrap/media/index.scss +9 -0
- data/app/assets/stylesheets/formstrap/media.scss +1 -0
- data/app/assets/stylesheets/formstrap/utilities/buttons.scss +27 -0
- data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +72 -0
- data/app/assets/stylesheets/formstrap/utilities.scss +2 -0
- data/app/assets/stylesheets/formstrap/vendor/flatpickr.css +903 -0
- data/app/assets/stylesheets/formstrap/vendor/tom-select-bootstrap.scss +535 -0
- data/app/assets/stylesheets/formstrap.css +1559 -0
- data/app/assets/stylesheets/formstrap.scss +11 -0
- data/app/controllers/concerns/formstrap/pagination.rb +27 -0
- data/app/controllers/formstrap/media_controller.rb +68 -0
- data/app/controllers/formstrap_controller.rb +2 -0
- data/app/models/concerns/formstrap/autocompletable.rb +36 -0
- data/app/models/concerns/formstrap/hintable.rb +22 -0
- data/app/models/concerns/formstrap/input_groupable.rb +21 -0
- data/app/models/concerns/formstrap/labelable.rb +31 -0
- data/app/models/concerns/formstrap/listable.rb +26 -0
- data/app/models/concerns/formstrap/placeholderable.rb +11 -0
- data/app/models/concerns/formstrap/validatable.rb +38 -0
- data/app/models/concerns/formstrap/wrappable.rb +19 -0
- data/app/models/formstrap/.DS_Store +0 -0
- data/app/models/formstrap/association_view.rb +100 -0
- data/app/models/formstrap/blocks_view.rb +43 -0
- data/app/models/formstrap/checkbox_view.rb +50 -0
- data/app/models/formstrap/color_view.rb +45 -0
- data/app/models/formstrap/date_range_view.rb +23 -0
- data/app/models/formstrap/date_view.rb +43 -0
- data/app/models/formstrap/datetime_range_view.rb +23 -0
- data/app/models/formstrap/datetime_view.rb +43 -0
- data/app/models/formstrap/email_view.rb +46 -0
- data/app/models/formstrap/file_view.rb +106 -0
- data/app/models/formstrap/flatpickr_range_view.rb +89 -0
- data/app/models/formstrap/flatpickr_view.rb +27 -0
- data/app/models/formstrap/hidden_view.rb +8 -0
- data/app/models/formstrap/hint_view.rb +4 -0
- data/app/models/formstrap/input_group_view.rb +17 -0
- data/app/models/formstrap/label_view.rb +22 -0
- data/app/models/formstrap/media_item_view.rb +41 -0
- data/app/models/formstrap/media_view.rb +143 -0
- data/app/models/formstrap/number_view.rb +47 -0
- data/app/models/formstrap/password_view.rb +42 -0
- data/app/models/formstrap/redactorx_view.rb +57 -0
- data/app/models/formstrap/search_view.rb +46 -0
- data/app/models/formstrap/select_view.rb +61 -0
- data/app/models/formstrap/switch_view.rb +21 -0
- data/app/models/formstrap/text_view.rb +46 -0
- data/app/models/formstrap/textarea_view.rb +47 -0
- data/app/models/formstrap/url_view.rb +46 -0
- data/app/models/formstrap/wrapper_view.rb +17 -0
- data/app/models/formstrap/wysiwyg_view.rb +15 -0
- data/app/models/view_model.rb +62 -0
- data/app/views/formstrap/_association.html.erb +30 -0
- data/app/views/formstrap/_autocomplete.html.erb +11 -0
- data/app/views/formstrap/_blocks.html.erb +45 -0
- data/app/views/formstrap/_checkbox.html.erb +34 -0
- data/app/views/formstrap/_color.html.erb +32 -0
- data/app/views/formstrap/_datalist.html.erb +3 -0
- data/app/views/formstrap/_date.html.erb +41 -0
- data/app/views/formstrap/_date_range.html.erb +40 -0
- data/app/views/formstrap/_datetime.html.erb +41 -0
- data/app/views/formstrap/_datetime_range.html.erb +40 -0
- data/app/views/formstrap/_email.html.erb +43 -0
- data/app/views/formstrap/_errors.html.erb +19 -0
- data/app/views/formstrap/_file.html.erb +94 -0
- data/app/views/formstrap/_flatpickr.html.erb +33 -0
- data/app/views/formstrap/_flatpickr_range.html.erb +40 -0
- data/app/views/formstrap/_hidden.html.erb +23 -0
- data/app/views/formstrap/_hint.html.erb +21 -0
- data/app/views/formstrap/_input_group.html.erb +21 -0
- data/app/views/formstrap/_label.html.erb +22 -0
- data/app/views/formstrap/_media.html.erb +60 -0
- data/app/views/formstrap/_number.html.erb +41 -0
- data/app/views/formstrap/_password.html.erb +39 -0
- data/app/views/formstrap/_redactorx.html.erb +31 -0
- data/app/views/formstrap/_repeater.html.erb +128 -0
- data/app/views/formstrap/_search.html.erb +43 -0
- data/app/views/formstrap/_select.html.erb +43 -0
- data/app/views/formstrap/_switch.html.erb +29 -0
- data/app/views/formstrap/_text.html.erb +42 -0
- data/app/views/formstrap/_textarea.html.erb +39 -0
- data/app/views/formstrap/_to_ary.html.erb +0 -0
- data/app/views/formstrap/_url.html.erb +43 -0
- data/app/views/formstrap/_validation.html.erb +18 -0
- data/app/views/formstrap/_wrapper.html.erb +8 -0
- data/app/views/formstrap/_wysiwyg.html.erb +28 -0
- data/app/views/formstrap/autocomplete/_item.html.erb +3 -0
- data/app/views/formstrap/autocomplete/_list.html.erb +3 -0
- data/app/views/formstrap/blocks/_modal.html.erb +20 -0
- data/app/views/formstrap/fields/_base.html.erb +25 -0
- data/app/views/formstrap/fields/_file.html.erb +17 -0
- data/app/views/formstrap/fields/_files.html.erb +17 -0
- data/app/views/formstrap/fields/_group.html.erb +52 -0
- data/app/views/formstrap/fields/_list.html.erb +31 -0
- data/app/views/formstrap/fields/_text.html.erb +17 -0
- data/app/views/formstrap/media/_item.html.erb +38 -0
- data/app/views/formstrap/media/_media_item_modal.html.erb +77 -0
- data/app/views/formstrap/media/_modal.html.erb +40 -0
- data/app/views/formstrap/media/_thumbnail.html.erb +20 -0
- data/app/views/formstrap/media/_validation.html.erb +10 -0
- data/app/views/formstrap/media/create.turbo_stream.erb +5 -0
- data/app/views/formstrap/media/index.html.erb +3 -0
- data/app/views/formstrap/media/index.turbo_stream.erb +11 -0
- data/app/views/formstrap/media/show.html.erb +9 -0
- data/app/views/formstrap/media/thumbnail.html.erb +3 -0
- data/app/views/formstrap/media/update.turbo_stream.erb +3 -0
- data/app/views/formstrap/pagination/_infinite.html.erb +7 -0
- data/app/views/formstrap/repeater/_row.html.erb +53 -0
- data/app/views/formstrap/shared/_notifications.html.erb +20 -0
- data/app/views/formstrap/shared/_popup.html.erb +32 -0
- data/app/views/formstrap/shared/_thumbnail.html.erb +35 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/importmap.rb +2 -0
- data/config/locales/activerecord/en.yml +12 -0
- data/config/locales/activerecord/nl.yml +13 -0
- data/config/locales/defaults/en.yml +215 -0
- data/config/locales/defaults/nl.yml +213 -0
- data/config/locales/devise/en.yml +65 -0
- data/config/locales/devise/nl.yml +85 -0
- data/config/locales/en.yml +6 -0
- data/config/locales/formstrap/forms/en.yml +39 -0
- data/config/locales/formstrap/forms/nl.yml +39 -0
- data/config/locales/formstrap/media/en.yml +24 -0
- data/config/locales/formstrap/media/nl.yml +24 -0
- data/config/locales/formstrap/thumbnail/en.yml +4 -0
- data/config/locales/formstrap/thumbnail/nl.yml +4 -0
- data/config/locales/nl.yml +6 -0
- data/config/routes.rb +11 -0
- data/esbuild-css.js +25 -0
- data/esbuild-js.js +11 -0
- data/formstrap.gemspec +37 -0
- data/formstrap.iml +34 -0
- data/lib/formstrap/engine.rb +27 -0
- data/lib/formstrap/form_builder.rb +177 -0
- data/lib/formstrap/form_helper.rb +19 -0
- data/lib/formstrap/version.rb +3 -0
- data/lib/formstrap.rb +6 -0
- data/package.json +54 -0
- data/src/js/formstrap.js +1 -0
- data/src/scss/formstrap.scss +1 -0
- data/yarn.lock +1998 -0
- 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,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,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
data/config/importmap.rb
ADDED
@@ -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
|