formstrap 0.1.3 → 0.2.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 +4 -4
- data/CHANGELOG.md +38 -1
- data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +5 -5
- data/app/assets/javascripts/formstrap/controllers/media_controller.js +4 -2
- data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +38 -2
- data/app/assets/javascripts/formstrap/controllers/popup_controller.js +75 -0
- data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +4 -4
- data/app/assets/javascripts/formstrap/index.js +2 -0
- data/app/assets/javascripts/formstrap.js +1790 -129
- data/app/assets/stylesheets/formstrap/{forms/autocomplete.scss → autocomplete.scss} +1 -1
- data/app/assets/stylesheets/formstrap/{forms/file.scss → file.scss} +10 -10
- data/app/assets/stylesheets/formstrap/general.scss +8 -1
- data/app/assets/stylesheets/formstrap/media/_modal.scss +9 -0
- data/app/assets/stylesheets/formstrap/{forms/media.scss → media/_validation.scss} +2 -2
- data/app/assets/stylesheets/formstrap/media.scss +2 -1
- data/app/assets/stylesheets/formstrap/{forms/repeater.scss → repeater.scss} +8 -8
- data/app/assets/stylesheets/formstrap/shared/popup.scss +17 -0
- data/app/assets/stylesheets/formstrap/shared/thumbnail.scss +23 -0
- data/app/assets/stylesheets/formstrap/shared.scss +2 -0
- data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +2 -2
- data/app/assets/stylesheets/formstrap/utilities.scss +0 -1
- data/app/assets/stylesheets/formstrap.css +155 -143
- data/app/assets/stylesheets/formstrap.scss +5 -1
- data/app/controllers/formstrap/media_controller.rb +6 -12
- data/app/helpers/application_helper.rb +20 -0
- data/app/models/concerns/formstrap/attachment.rb +16 -0
- data/app/models/concerns/formstrap/blob.rb +56 -0
- data/app/models/formstrap/file_view.rb +3 -3
- data/app/models/formstrap/media_view.rb +12 -1
- data/app/models/formstrap/thumbnail_view.rb +108 -0
- data/app/views/formstrap/_autocomplete.html.erb +1 -1
- data/app/views/formstrap/_file.html.erb +8 -8
- data/app/views/formstrap/_media.html.erb +5 -4
- data/app/views/formstrap/_repeater.html.erb +3 -3
- data/app/views/formstrap/media/_item.html.erb +10 -7
- data/app/views/formstrap/media/_modal.html.erb +35 -8
- data/app/views/formstrap/media/_thumbnail.html.erb +12 -8
- data/app/views/formstrap/media/_validation.html.erb +1 -1
- data/app/views/formstrap/media/create.turbo_stream.erb +3 -1
- data/app/views/formstrap/media/index.html.erb +1 -1
- data/app/views/formstrap/repeater/_row.html.erb +4 -4
- data/app/views/formstrap/shared/_popup.html.erb +1 -1
- data/app/views/formstrap/shared/_thumbnail.html.erb +5 -3
- data/config/locales/formstrap/forms/en.yml +0 -14
- data/config/locales/formstrap/forms/nl.yml +1 -15
- data/config/locales/formstrap/media/en.yml +2 -10
- data/config/locales/formstrap/media/nl.yml +2 -10
- data/config/routes.rb +0 -2
- data/lib/formstrap/version.rb +1 -1
- metadata +16 -29
- data/app/assets/stylesheets/formstrap/forms.scss +0 -12
- data/app/assets/stylesheets/formstrap/media/index.scss +0 -9
- data/app/assets/stylesheets/formstrap/utilities/buttons.scss +0 -27
- data/app/models/formstrap/blocks_view.rb +0 -43
- data/app/views/formstrap/_blocks.html.erb +0 -45
- data/app/views/formstrap/_to_ary.html.erb +0 -0
- data/app/views/formstrap/blocks/_modal.html.erb +0 -20
- data/app/views/formstrap/fields/_base.html.erb +0 -25
- data/app/views/formstrap/fields/_file.html.erb +0 -17
- data/app/views/formstrap/fields/_files.html.erb +0 -17
- data/app/views/formstrap/fields/_group.html.erb +0 -52
- data/app/views/formstrap/fields/_list.html.erb +0 -31
- data/app/views/formstrap/fields/_text.html.erb +0 -17
- data/app/views/formstrap/media/_media_item_modal.html.erb +0 -77
- data/app/views/formstrap/media/show.html.erb +0 -9
- data/app/views/formstrap/media/update.turbo_stream.erb +0 -3
- data/config/locales/activerecord/en.yml +0 -12
- data/config/locales/activerecord/nl.yml +0 -13
- data/config/locales/defaults/en.yml +0 -215
- data/config/locales/defaults/nl.yml +0 -213
- data/config/locales/devise/en.yml +0 -65
- data/config/locales/devise/nl.yml +0 -85
- /data/app/assets/stylesheets/formstrap/{forms/search.scss → search.scss} +0 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
class ApplicationHelper
|
2
|
+
def show_svg(attachment, options = {})
|
3
|
+
return if !attachment.content_type.include?('svg')
|
4
|
+
|
5
|
+
attachment.open do |file|
|
6
|
+
content = file.read
|
7
|
+
doc = Nokogiri::HTML::DocumentFragment.parse content
|
8
|
+
svg = doc.at_css 'svg'
|
9
|
+
|
10
|
+
# for security
|
11
|
+
doc.search('script').each do |src|
|
12
|
+
src.remove
|
13
|
+
end
|
14
|
+
|
15
|
+
options.each { |attr, value| svg[attr.to_s] = value }
|
16
|
+
|
17
|
+
doc.to_html.html_safe
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Formstrap
|
2
|
+
module Attachment
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
scope :not_a_variant, -> { where.not(record_type: "ActiveStorage::VariantRecord") }
|
6
|
+
|
7
|
+
def root_record
|
8
|
+
record
|
9
|
+
end
|
10
|
+
|
11
|
+
def root_record_name
|
12
|
+
"#{record.class.name} #{record.id}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Formstrap
|
2
|
+
module Blob
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class << self
|
7
|
+
def search(query)
|
8
|
+
where("active_storage_blobs.filename LIKE ?", "%#{query}%")
|
9
|
+
end
|
10
|
+
|
11
|
+
def not_attached_to_variant
|
12
|
+
not_attached_to("ActiveStorage::VariantRecord")
|
13
|
+
end
|
14
|
+
|
15
|
+
def not_attached_to(arg)
|
16
|
+
case arg
|
17
|
+
when String
|
18
|
+
record_types = [arg]
|
19
|
+
when Class
|
20
|
+
record_types = [arg.to_s]
|
21
|
+
when Array
|
22
|
+
record_types = arg.map(&:to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
left_outer_joins(:attachments)
|
26
|
+
.where.not(active_storage_attachments: {record_type: record_types})
|
27
|
+
.or(is_orphan)
|
28
|
+
end
|
29
|
+
|
30
|
+
def by_mimetypes_string(mimetype_string)
|
31
|
+
return where({}) if mimetype_string.blank?
|
32
|
+
by_mimetypes(mimetype_string.split(","))
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def is_orphan
|
38
|
+
left_outer_joins(:attachments)
|
39
|
+
.where(active_storage_attachments: {id: nil})
|
40
|
+
end
|
41
|
+
|
42
|
+
def by_mimetypes(mimetypes = [])
|
43
|
+
results = self
|
44
|
+
|
45
|
+
mimetypes.map.with_index do |mimetype, index|
|
46
|
+
content_type = mimetype.tr("*", "%")
|
47
|
+
query = where(arel_table[:content_type].matches(content_type))
|
48
|
+
results = (index == 0) ? query : results.or(query)
|
49
|
+
end
|
50
|
+
|
51
|
+
results
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -23,7 +23,7 @@ module Formstrap
|
|
23
23
|
|
24
24
|
def wrapper_options
|
25
25
|
default_wrapper_options.deep_merge({
|
26
|
-
class: ["mb-3
|
26
|
+
class: ["mb-3 formstrap-file", ("form-floating" if float)],
|
27
27
|
data: {
|
28
28
|
controller: ("file-preview" if preview)
|
29
29
|
}
|
@@ -57,7 +57,7 @@ module Formstrap
|
|
57
57
|
def dropzone_options
|
58
58
|
if dropzone
|
59
59
|
{
|
60
|
-
class: ["
|
60
|
+
class: ["formstrap-dropzone", validation_class],
|
61
61
|
data: {
|
62
62
|
controller: "dropzone"
|
63
63
|
}
|
@@ -73,7 +73,7 @@ module Formstrap
|
|
73
73
|
# {
|
74
74
|
# prepend: prepend,
|
75
75
|
# append: append,
|
76
|
-
# class: "
|
76
|
+
# class: "formstrap-file",
|
77
77
|
# data: {
|
78
78
|
# controller: "#{"file-preview" if preview} #{"dropzone" if dropzone}"
|
79
79
|
# }
|
@@ -30,7 +30,8 @@ module Formstrap
|
|
30
30
|
|
31
31
|
def item_options
|
32
32
|
options = {
|
33
|
-
sort: sort
|
33
|
+
sort: sort,
|
34
|
+
url: modal_url,
|
34
35
|
}
|
35
36
|
|
36
37
|
# Don't pass width or height if it was not defined
|
@@ -122,6 +123,16 @@ module Formstrap
|
|
122
123
|
end
|
123
124
|
end
|
124
125
|
|
126
|
+
def modal_url
|
127
|
+
formstrap_media_path(name: name, ids: blob_ids, min: min, max: max, mimetype: accept, exclude_models: exclude_models)
|
128
|
+
end
|
129
|
+
|
130
|
+
def edit_modal_url(attachment)
|
131
|
+
return nil unless edit_url.present?
|
132
|
+
return edit_url unless attachment&.persisted?
|
133
|
+
edit_url.gsub(":id", attachment.id.to_s)
|
134
|
+
end
|
135
|
+
|
125
136
|
attr_reader :accept
|
126
137
|
|
127
138
|
def required
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Formstrap
|
2
|
+
class ThumbnailView < ViewModel
|
3
|
+
def class_names
|
4
|
+
class_names = [@class]
|
5
|
+
class_names << "img-thumbnail formstrap-thumbnail"
|
6
|
+
class_names.join(" ")
|
7
|
+
end
|
8
|
+
|
9
|
+
def width
|
10
|
+
if is_defined?(:width)
|
11
|
+
@width
|
12
|
+
else
|
13
|
+
@width || 100
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def height
|
18
|
+
if is_defined?(:height)
|
19
|
+
@height
|
20
|
+
else
|
21
|
+
@height || 100
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def blob
|
26
|
+
file.is_a?(ActiveStorage::Blob) ? file : file&.blob
|
27
|
+
end
|
28
|
+
|
29
|
+
def mime_type
|
30
|
+
blob&.content_type
|
31
|
+
end
|
32
|
+
|
33
|
+
def variant_options
|
34
|
+
if width.nil? || height.nil?
|
35
|
+
default_variant_options.merge({
|
36
|
+
resize_to_fit: [width, height]
|
37
|
+
})
|
38
|
+
else
|
39
|
+
default_variant_options.merge({
|
40
|
+
resize_to_fill: [width, height, crop: :attention]
|
41
|
+
})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def default_variant_options
|
46
|
+
{
|
47
|
+
saver: {
|
48
|
+
quality: 80
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
# Is it possible to create a ActiveStorage::Variant?
|
54
|
+
def variable?
|
55
|
+
blob&.variable?
|
56
|
+
end
|
57
|
+
|
58
|
+
def icon?
|
59
|
+
is_defined?(:icon)
|
60
|
+
end
|
61
|
+
|
62
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
63
|
+
def icon_name
|
64
|
+
return icon if icon
|
65
|
+
type_map = {
|
66
|
+
image: %w[image/bmp image/gif image/vnd.microsoft.icon image/jpeg image/png image/svg+xml image/tiff image/webp],
|
67
|
+
play: %w[video/mp4 video/mpeg video/ogg video/mp2t video/webm video/3gpp video/3gpp2],
|
68
|
+
music: %w[audio/aac audio/midi audio/x-midi audio/mpeg audio/ogg audio/opus audio/wav audio/webm audio/3gpp audio/3gpp2],
|
69
|
+
word: %w[application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document],
|
70
|
+
ppt: %w[application/vnd.ms-powerpoint application/vnd.openxmlformats-officedocument.presentationml.presentation],
|
71
|
+
excel: %w[application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet],
|
72
|
+
slides: %w[application/vnd.oasis.opendocument.presentation],
|
73
|
+
spreadsheet: %w[application/vnd.oasis.opendocument.spreadsheet],
|
74
|
+
richtext: %w[application/vnd.oasis.opendocument.text],
|
75
|
+
zip: %w[application/zip application/x-7z-compressed application/x-bzip application/x-bzip2 application/gzip application/vnd.rar],
|
76
|
+
pdf: %w[application/pdf]
|
77
|
+
}
|
78
|
+
|
79
|
+
type = type_map.find { |key, mime_types| mime_types.include?(mime_type) }
|
80
|
+
if type
|
81
|
+
["file", "earmark", type.first].compact.join("-")
|
82
|
+
else
|
83
|
+
"question"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def svg?
|
88
|
+
blob&.content_type == "image/svg+xml"
|
89
|
+
end
|
90
|
+
|
91
|
+
def inline_svg(options = {})
|
92
|
+
blob.open do |file|
|
93
|
+
content = file.read
|
94
|
+
doc = Nokogiri::HTML::DocumentFragment.parse content
|
95
|
+
svg = doc.at_css 'svg'
|
96
|
+
|
97
|
+
# for security
|
98
|
+
doc.search('script').each do |src|
|
99
|
+
src.remove
|
100
|
+
end
|
101
|
+
|
102
|
+
options.each { |attr, value| svg[attr.to_s] = value }
|
103
|
+
|
104
|
+
doc.to_html.html_safe
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -36,7 +36,7 @@
|
|
36
36
|
<%= render "formstrap/input_group", file.input_group_options do %>
|
37
37
|
|
38
38
|
<% if file.preview %>
|
39
|
-
<div class="
|
39
|
+
<div class="formstrap-file-thumbnails" data-file-preview-target="thumbnails">
|
40
40
|
|
41
41
|
<!-- Render previews for attachments -->
|
42
42
|
<%= form.fields_for(file.nested_attribute) do |ff| %>
|
@@ -46,13 +46,13 @@
|
|
46
46
|
filename = attachment.blob.filename.to_s
|
47
47
|
size = number_to_human_size(attachment.blob.byte_size)
|
48
48
|
%>
|
49
|
-
<div class="
|
49
|
+
<div class="formstrap-file-thumbnail" title="<%= "#{filename} (#{size})" %>" data-file-preview-target="thumbnail">
|
50
50
|
<%= ff.hidden_field(:id, disabled: !file.destroy) %>
|
51
51
|
<%= ff.hidden_field(:_destroy, data: {"file-preview-target": "thumbnailDestroy"}, disabled: !file.destroy) %>
|
52
52
|
<%= render "formstrap/shared/thumbnail", file: attachment %>
|
53
53
|
|
54
54
|
<% if file.destroy %>
|
55
|
-
<div class="
|
55
|
+
<div class="formstrap-file-thumbnail-remove" data-action="click->file-preview#remove" data-file-preview-name-param="<%= filename %>">
|
56
56
|
<%= bootstrap_icon("x") %>
|
57
57
|
</div>
|
58
58
|
<% end %>
|
@@ -61,11 +61,11 @@
|
|
61
61
|
|
62
62
|
<!-- Placeholder -->
|
63
63
|
<% if file.dropzone %>
|
64
|
-
<div class="
|
65
|
-
<%= t("
|
64
|
+
<div class="formstrap-dropzone-placeholder <%= "d-none" if file.attachments.any? %>" data-file-preview-target="placeholder" style="height: 100px;">
|
65
|
+
<%= t(".placeholder", count: file.number_of_files) %>
|
66
66
|
</div>
|
67
67
|
<% else %>
|
68
|
-
<div class="
|
68
|
+
<div class="formstrap-file-thumbnail <%= "d-none" if file.attachments.any? %>" title="<%= t(".not_found") %>" data-file-preview-target="placeholder">
|
69
69
|
<%= render "formstrap/shared/thumbnail", icon: "plus" %>
|
70
70
|
</div>
|
71
71
|
<% end %>
|
@@ -74,11 +74,11 @@
|
|
74
74
|
|
75
75
|
<!-- Template -->
|
76
76
|
<template data-file-preview-target="template">
|
77
|
-
<div class="
|
77
|
+
<div class="formstrap-file-thumbnail" title="" data-file-preview-target="thumbnail">
|
78
78
|
<%= render "formstrap/shared/thumbnail" %>
|
79
79
|
|
80
80
|
<% if file.destroy %>
|
81
|
-
<div class="
|
81
|
+
<div class="formstrap-file-thumbnail-remove" data-action="click->file-preview#remove">
|
82
82
|
<%= bootstrap_icon("x") %>
|
83
83
|
</div>
|
84
84
|
<% end %>
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# * +width+ - Width of the thumbnail
|
17
17
|
# * +height+ - Height of the thumbnail
|
18
18
|
# * +exclude_models+ - Array of model names to exclude from selection (e.g. ["User", "Company"])
|
19
|
+
# * +edit_url+ - Placeholder link for the edit page of the attachment. Needs to contain a ":id" part, e.g. "admin_media_edit_url(":id"). This url be opened in a modal
|
19
20
|
#
|
20
21
|
# ==== References
|
21
22
|
# https://headmin.dev/docs/forms/media
|
@@ -31,17 +32,17 @@
|
|
31
32
|
|
32
33
|
<%= render "formstrap/wrapper", media.wrapper_options do %>
|
33
34
|
<%= render "formstrap/label", media.label_options if media.prepend_label? %>
|
34
|
-
<div class="
|
35
|
+
<div class="formstrap-file-thumbnails" data-media-target="thumbnails">
|
35
36
|
<%= render "formstrap/media/validation", media.custom_validation_options %>
|
36
37
|
|
37
38
|
<!-- Render previews for attachments -->
|
38
39
|
<%= form.fields_for(media.nested_attribute, media.association_object) do |ff| %>
|
39
|
-
<%= render "formstrap/media/item", media.item_options.merge(form: ff,
|
40
|
+
<%= render "formstrap/media/item", media.item_options.merge(form: ff, edit_url: media.edit_modal_url(ff.object)) %>
|
40
41
|
<% end %>
|
41
42
|
|
42
43
|
<!-- Placeholder -->
|
43
44
|
<div class="<%= "d-none" if media.attachments.any? %>" data-media-target="placeholder">
|
44
|
-
<a href="<%=
|
45
|
+
<a href="<%= media.modal_url %>" data-turbo-frame="modal" data-media-target="modalButton">
|
45
46
|
<%= render "formstrap/shared/thumbnail", media.thumbnail_options %>
|
46
47
|
</a>
|
47
48
|
</div>
|
@@ -51,7 +52,7 @@
|
|
51
52
|
<% association_object = ActiveStorage::Attachment.new %>
|
52
53
|
<template data-media-target="template" data-template-id-regex="<%= association_object.object_id %>">
|
53
54
|
<%= form.fields_for(media.nested_attribute, ActiveStorage::Attachment.new, child_index: association_object.object_id) do |ff| %>
|
54
|
-
<%= render "formstrap/media/item", media.item_options.merge(form: ff,
|
55
|
+
<%= render "formstrap/media/item", media.item_options.merge(form: ff, edit_url: media.edit_modal_url(ff.object)) %>
|
55
56
|
<% end %>
|
56
57
|
</template>
|
57
58
|
|
@@ -59,7 +59,7 @@
|
|
59
59
|
<%= render "formstrap/label", form: form, attribute: attribute, text: label, required: required %>
|
60
60
|
<% end %>
|
61
61
|
|
62
|
-
<ul class="repeater list-group <%= "list-group-flush" if flush %> mb-3" data-controller="repeater" data-repeater-target="list" data-repeater-id-value="<%= repeater_id %>">
|
62
|
+
<ul class="formstrap-repeater list-group <%= "list-group-flush" if flush %> mb-3" data-controller="repeater" data-repeater-target="list" data-repeater-id-value="<%= repeater_id %>">
|
63
63
|
|
64
64
|
<!-- Header -->
|
65
65
|
<% if header %>
|
@@ -85,7 +85,7 @@
|
|
85
85
|
|
86
86
|
<!-- Button -->
|
87
87
|
<div
|
88
|
-
class="btn btn-sm
|
88
|
+
class="btn btn-sm btn-outline-secondary"
|
89
89
|
data-repeater-target="addButton"
|
90
90
|
data-popup-target="button"
|
91
91
|
data-popup-id="<%= "repeater-buttons-#{repeater_id}" %>"
|
@@ -101,7 +101,7 @@
|
|
101
101
|
<div class="d-grid gap-2">
|
102
102
|
<% template_names.each do |name| %>
|
103
103
|
<div
|
104
|
-
class="btn btn-sm
|
104
|
+
class="btn btn-sm btn-outline-secondary"
|
105
105
|
data-popup-target="button"
|
106
106
|
data-popup-id="<%= "repeater-buttons-#{repeater_id}" %>"
|
107
107
|
data-action="click->repeater#addRow click->popup#close"
|
@@ -9,29 +9,32 @@
|
|
9
9
|
# * +sort+ - Allow sorting by dragging items. `active_storage_attachments` must have a position column.
|
10
10
|
# * +width+ - Width of the thumbnail
|
11
11
|
# * +height+ - Height of the thumbnail
|
12
|
+
# * +edit_url+ - URL for the edit modal
|
12
13
|
#
|
13
14
|
|
14
15
|
media_item = Formstrap::MediaItemView.new(local_assigns)
|
15
16
|
%>
|
16
17
|
|
17
|
-
<div class="
|
18
|
+
<div class="formstrap-file-thumbnail media-drag-sort-handle" title="<%= "#{media_item.filename} (#{media_item.size})" %>" data-media-target="item">
|
18
19
|
<%= form.hidden_field(:id) %>
|
19
20
|
<%= form.hidden_field(:blob_id) %>
|
20
21
|
<%= form.hidden_field(:position, value: media_item.position_value) if media_item.sort %>
|
21
22
|
<%= form.hidden_field(:_destroy) %>
|
22
23
|
|
23
|
-
<a href="<%= media_item.url %>" data-turbo-frame="
|
24
|
+
<a href="<%= media_item.url %>" data-turbo-frame="modal" data-media-target="modalButton">
|
24
25
|
<%= render "formstrap/shared/thumbnail", media_item.thumbnail_options %>
|
25
26
|
</a>
|
26
27
|
|
27
|
-
<div class="
|
28
|
+
<div class="formstrap-file-thumbnail-actions">
|
28
29
|
<!-- Edit -->
|
29
|
-
|
30
|
-
<%=
|
31
|
-
|
30
|
+
<% if media_item.edit_url %>
|
31
|
+
<a href="<%= media_item.edit_url %>" class="formstrap-file-thumbnail-edit" data-turbo-frame="modal" data-media-target="editButton">
|
32
|
+
<%= bootstrap_icon("pencil") %>
|
33
|
+
</a>
|
34
|
+
<% end %>
|
32
35
|
|
33
36
|
<!-- Remove -->
|
34
|
-
<div class="
|
37
|
+
<div class="formstrap-file-thumbnail-remove" data-action="click->media#destroy">
|
35
38
|
<%= bootstrap_icon("x") %>
|
36
39
|
</div>
|
37
40
|
</div>
|
@@ -1,11 +1,30 @@
|
|
1
|
-
<div class="media-modal modal fade" tabindex="-1" data-controller="
|
1
|
+
<div class="formstrap-media-modal modal fade" tabindex="-1" data-controller="modal media-modal" data-media-modal-ids-value="<%= params[:ids] ? params[:ids] : [] %>" data-name="<%= name %>" data-min="<%= min %>" data-max="<%= max %>">
|
2
2
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
3
3
|
<div class="modal-content">
|
4
4
|
<div class="modal-header">
|
5
5
|
<h5 class="modal-title">
|
6
6
|
<%= t(".title", count: min.to_i < 1 ? 1 : min.to_i) %>
|
7
7
|
</h5>
|
8
|
-
|
8
|
+
|
9
|
+
<div class="modal-header-actions d-flex align-items-center gap-3">
|
10
|
+
<%= formstrap_form_with url: formstrap_media_path, method: :get, data: {turbo_frame: "modal", media_modal_target: "search"}, class: "modal-search d-flex gap-1" do |form| %>
|
11
|
+
<%= form.hidden_field :name, value: request.query_parameters["name"] %>
|
12
|
+
<%= form.hidden_field :min, value: request.query_parameters["min"] %>
|
13
|
+
<%= form.hidden_field :max, value: request.query_parameters["max"] %>
|
14
|
+
<%= form.hidden_field :mimetype, value: request.query_parameters["mimetype"] %>
|
15
|
+
<%= form.hidden_field "exclude_models[]", value: request.query_parameters["exclude_models"] %>
|
16
|
+
|
17
|
+
<% if request.query_parameters["ids"] %>
|
18
|
+
<% request.query_parameters["ids"].each do |id| %>
|
19
|
+
<%= form.hidden_field "ids[]", value: id, data: {media_modal_target: "searchId"} %>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<%= form.search :search, value: request.query_parameters["search"], class: "form-control", label: false, wrapper: {class: ""}, placeholder: t(".search") %>
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<%= t(".close") %>"></button>
|
27
|
+
</div>
|
9
28
|
</div>
|
10
29
|
<div class="modal-body">
|
11
30
|
<%= turbo_frame_tag "thumbnails", class: "d-flex flex-wrap gap-2" do %>
|
@@ -14,17 +33,25 @@
|
|
14
33
|
<%= render "thumbnail" %>
|
15
34
|
<% end %>
|
16
35
|
<% end %>
|
17
|
-
|
36
|
+
|
37
|
+
<div data-media-modal-target="placeholder" class="<%= "d-none" if !@blobs.empty? && request.request_parameters["search"].nil? %>">
|
18
38
|
<p><%= t(".placeholder") %></p>
|
19
39
|
</div>
|
40
|
+
|
41
|
+
<div data-media-modal-target="search_no_results" class="<%= "d-none" if !@blobs.empty? || request.request_parameters["search"].present? %>">
|
42
|
+
<p><%= t(".search_no_results") %></p>
|
43
|
+
</div>
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
<% unless @blobs.empty? %>
|
47
|
+
<div class="mt-3">
|
48
|
+
<%= render "formstrap/pagination/infinite", items: @blobs %>
|
49
|
+
</div>
|
20
50
|
<% end %>
|
21
|
-
<div class="mt-3">
|
22
|
-
<%= render "formstrap/pagination/infinite", items: @blobs %>
|
23
|
-
</div>
|
24
51
|
</div>
|
25
52
|
<div class="modal-footer">
|
26
|
-
<%=
|
27
|
-
<%= form.label :files, class: "btn
|
53
|
+
<%= formstrap_form_with url: formstrap_new_media_path, multipart: true, data: {"media-modal-target": "form", turbo_frame: "modal"}, class: "me-auto" do |form| %>
|
54
|
+
<%= form.label :files, class: "btn btn-outline-secondary" do %>
|
28
55
|
<%= bootstrap_icon("upload") %>
|
29
56
|
<%= t(".upload") %>
|
30
57
|
<%= form.file_field :files, accept: mimetypes, class: "d-none", multiple: true, data: {action: "change->media-modal#submitForm"} %>
|
@@ -3,18 +3,22 @@
|
|
3
3
|
<div data-media-modal-target="item" title="<%= "#{blob.filename} (#{l(blob.created_at, format: :long)})" %>">
|
4
4
|
<!-- Input -->
|
5
5
|
<input
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
12
|
|
13
13
|
<!-- Label -->
|
14
|
-
<label for="media-item-<%= blob.id %>">
|
14
|
+
<label for="media-item-<%= blob.id %>" class="d-flex">
|
15
15
|
<%= render "formstrap/shared/thumbnail", file: blob %>
|
16
16
|
</label>
|
17
17
|
</div>
|
18
18
|
<% else %>
|
19
|
-
|
19
|
+
<div>
|
20
|
+
<label class="d-flex">
|
21
|
+
<%= render "formstrap/shared/thumbnail", file: nil %>
|
22
|
+
</label>
|
23
|
+
</div>
|
20
24
|
<% end %>
|
@@ -1,5 +1,7 @@
|
|
1
1
|
<%= turbo_stream.prepend "thumbnails" do %>
|
2
2
|
<% @blobs.each do |blob| %>
|
3
|
-
|
3
|
+
<turbo-frame id="blob_<%= blob.id %>">
|
4
|
+
<%= render "formstrap/media/thumbnail", blob: blob %>
|
5
|
+
</turbo-frame>
|
4
6
|
<% end %>
|
5
7
|
<% end %>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
draggable = form.object.respond_to?(:position)
|
9
9
|
destroyable = form.object.respond_to?(:destroy)
|
10
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"
|
11
|
+
class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : "formstrap-repeater-row list-group-item"
|
12
12
|
%>
|
13
13
|
<div class="<%= class_names %> <%= error_class %>"
|
14
14
|
data-repeater-target="row"
|
@@ -22,14 +22,14 @@
|
|
22
22
|
|
23
23
|
<!-- Drag handle -->
|
24
24
|
<% if draggable %>
|
25
|
-
<div class="repeater-row-handle">
|
25
|
+
<div class="formstrap-repeater-row-handle">
|
26
26
|
<%= bootstrap_icon("grip-vertical") %>
|
27
27
|
</div>
|
28
28
|
<% end %>
|
29
29
|
|
30
30
|
<!-- Add button-->
|
31
31
|
<div
|
32
|
-
class="repeater-row-add btn btn-link"
|
32
|
+
class="formstrap-repeater-row-add btn btn-link"
|
33
33
|
title="<%= t(".add") %>"
|
34
34
|
data-repeater-target="addButton"
|
35
35
|
data-popup-target="button"
|
@@ -42,7 +42,7 @@
|
|
42
42
|
|
43
43
|
<!-- Remove button-->
|
44
44
|
<div
|
45
|
-
class="repeater-row-remove btn btn-link"
|
45
|
+
class="formstrap-repeater-row-remove btn btn-link"
|
46
46
|
title="<%= t(".remove") %>"
|
47
47
|
data-action="click->repeater#removeRow"
|
48
48
|
>
|
@@ -21,15 +21,17 @@
|
|
21
21
|
#
|
22
22
|
# Custom (bootstrap) icon
|
23
23
|
# <%= render "formstrap/shared/thumbnail", file: file, icon: "file-earmark-excel" %#>
|
24
|
-
thumbnail =
|
24
|
+
thumbnail = Formstrap::ThumbnailView.new(local_assigns)
|
25
25
|
%>
|
26
26
|
|
27
27
|
<div class="<%= thumbnail.class_names %>">
|
28
|
-
<div class="
|
28
|
+
<div class="formstrap-thumbnail-bg" style="min-width: <%= thumbnail.width || 100 %>px; min-height: <%= thumbnail.height || 100 %>px;">
|
29
29
|
<% if thumbnail.variable? && !thumbnail.icon? %>
|
30
30
|
<%= image_tag(thumbnail.file.variant(thumbnail.variant_options)) %>
|
31
|
+
<% elsif thumbnail.svg? %>
|
32
|
+
<%= thumbnail.inline_svg(width: thumbnail.width || 100, height: thumbnail.height || 100) %>
|
31
33
|
<% else %>
|
32
|
-
<%= bootstrap_icon(thumbnail.icon_name, class: "
|
34
|
+
<%= bootstrap_icon(thumbnail.icon_name, class: "formstrap-thumbnail-icon") %>
|
33
35
|
<% end %>
|
34
36
|
</div>
|
35
37
|
</div>
|
@@ -7,19 +7,6 @@ en:
|
|
7
7
|
other: Browse files or drag & drop them here
|
8
8
|
group:
|
9
9
|
save: Save
|
10
|
-
blocks:
|
11
|
-
add: "Add %{name}"
|
12
|
-
empty:
|
13
|
-
title: No blocks are added yet.
|
14
|
-
add: Add block
|
15
|
-
block:
|
16
|
-
add: Add
|
17
|
-
remove:
|
18
|
-
title: Delete
|
19
|
-
confirm: Are you sure you want to delete this?
|
20
|
-
modal:
|
21
|
-
close: Close
|
22
|
-
title: "Edit block '%{name}'"
|
23
10
|
media:
|
24
11
|
validation:
|
25
12
|
min:
|
@@ -28,7 +15,6 @@ en:
|
|
28
15
|
max:
|
29
16
|
one: Please limit your selection to maximum 1 item
|
30
17
|
other: "Please limit your selection to maximum %{count} items"
|
31
|
-
|
32
18
|
select:
|
33
19
|
blank: Make a choice
|
34
20
|
repeater:
|