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