headmin 0.5.3 → 0.5.5
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/Gemfile.lock +1 -1
- data/app/assets/javascripts/headmin/controllers/media_controller.js +14 -14
- data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +5 -5
- data/app/assets/javascripts/headmin/controllers/remote_modal_controller.js +1 -2
- data/app/assets/javascripts/headmin/controllers/textarea_controller.js +3 -3
- data/app/assets/javascripts/headmin.js +11 -11
- data/app/assets/stylesheets/headmin/forms/repeater.scss +0 -4
- data/app/assets/stylesheets/headmin/forms.scss +0 -6
- data/app/assets/stylesheets/headmin/overrides/redactorx.scss +1 -1
- data/app/assets/stylesheets/headmin/vendor/{tom-select-bootstrap.css → tom-select-bootstrap.scss} +0 -1
- data/app/assets/stylesheets/headmin.css +4 -8
- data/app/controllers/concerns/headmin/pagination.rb +5 -1
- data/app/models/concerns/headmin/field.rb +1 -1
- data/app/models/headmin/filter/association.rb +86 -0
- data/app/models/headmin/filter/association_view.rb +74 -0
- data/app/models/headmin/filter/base.rb +39 -19
- data/app/models/headmin/filter/boolean_view.rb +2 -5
- data/app/models/headmin/filter/date_view.rb +2 -5
- data/app/models/headmin/filter/field.rb +55 -0
- data/app/models/headmin/filter/field_view.rb +50 -0
- data/app/models/headmin/filter/filter_view.rb +25 -0
- data/app/models/headmin/filter/flatpickr_view.rb +1 -0
- data/app/models/headmin/filter/number_view.rb +2 -5
- data/app/models/headmin/filter/operator_view.rb +3 -1
- data/app/models/headmin/filter/options_view.rb +2 -5
- data/app/models/headmin/filter/text_view.rb +2 -5
- data/app/models/headmin/filters.rb +19 -4
- data/app/models/headmin/form/association_view.rb +102 -0
- data/app/models/headmin/form/blocks_view.rb +4 -1
- data/app/models/headmin/form/file_view.rb +0 -8
- data/app/models/headmin/form/flatpickr_view.rb +2 -1
- data/app/models/headmin/form/media_item_view.rb +39 -0
- data/app/models/headmin/form/media_view.rb +27 -3
- data/app/models/headmin/form/select_view.rb +2 -1
- data/app/models/headmin/thumbnail_view.rb +40 -19
- data/app/models/view_model.rb +4 -0
- data/app/views/headmin/_filters.html.erb +7 -2
- data/app/views/headmin/_thumbnail.html.erb +32 -8
- data/app/views/headmin/filters/_association.html.erb +24 -0
- data/app/views/headmin/filters/_field.html.erb +23 -0
- data/app/views/headmin/filters/_options.html.erb +1 -1
- data/app/views/headmin/forms/_association.html.erb +30 -0
- data/app/views/headmin/forms/_file.html.erb +4 -5
- data/app/views/headmin/forms/_media.html.erb +7 -5
- data/app/views/headmin/forms/_repeater.html.erb +10 -8
- data/app/views/headmin/forms/_wrapper.html.erb +0 -1
- data/app/views/headmin/forms/fields/_list.html.erb +6 -4
- data/app/views/headmin/forms/media/_item.html.erb +18 -12
- data/app/views/headmin/forms/repeater/_row.html.erb +2 -1
- data/app/views/headmin/media/_item.html.erb +1 -2
- data/app/views/headmin/media/_media_item_modal.html.erb +2 -2
- data/app/views/headmin/table/body/_association.html.erb +17 -3
- data/app/views/headmin/views/devise/shared/_links.html.erb +14 -20
- data/config/locales/activerecord/en.yml +1 -0
- data/config/locales/activerecord/nl.yml +1 -0
- data/config/locales/headmin/filters/en.yml +3 -1
- data/config/locales/headmin/filters/nl.yml +2 -0
- data/config/locales/headmin/media/en.yml +2 -2
- data/config/locales/headmin/media/nl.yml +2 -1
- data/lib/headmin/version.rb +1 -1
- data/package.json +1 -1
- metadata +13 -3
@@ -0,0 +1,55 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Filter
|
3
|
+
class Field < Headmin::Filter::Base
|
4
|
+
OPERATORS = %w[eq not_eq matches does_not_match]
|
5
|
+
|
6
|
+
def initialize(attribute, params, association: nil)
|
7
|
+
@attribute = association ? "#{association}_#{attribute}".to_sym : attribute
|
8
|
+
@attribute = "field_#{attribute}".to_sym
|
9
|
+
@raw_value = params[@attribute]
|
10
|
+
@association = association
|
11
|
+
@instructions = []
|
12
|
+
|
13
|
+
if params.key?(@attribute)
|
14
|
+
parse(@raw_value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def cast_value(value)
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
def display_value(value)
|
23
|
+
value.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
def query(collection)
|
27
|
+
return collection unless @instructions.any?
|
28
|
+
|
29
|
+
query = nil
|
30
|
+
|
31
|
+
@instructions.each do |instruction|
|
32
|
+
query = build_query(query, collection, instruction)
|
33
|
+
end
|
34
|
+
|
35
|
+
collection = collection.joins(:fields)
|
36
|
+
collection.where(query)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_query(query, collection, instruction)
|
40
|
+
query_operator = convert_to_query_operator(instruction[:operator])
|
41
|
+
query_value = convert_to_query_value(instruction[:value], instruction[:operator])
|
42
|
+
|
43
|
+
query_operator, query_value = process_null_operators(query_operator, query_value)
|
44
|
+
|
45
|
+
model = collection.is_a?(Class) ? collection : collection.model
|
46
|
+
|
47
|
+
new_attribute = attribute.to_s.gsub("field_", "").to_sym
|
48
|
+
fields_model = model.reflect_on_association(:fields).klass
|
49
|
+
new_query = fields_model.arel_table[:name].matches(new_attribute).and(fields_model.arel_table[:value].send(query_operator, query_value))
|
50
|
+
|
51
|
+
query ? query.send(instruction[:conditional], new_query) : new_query
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Filter
|
3
|
+
class FieldView < FilterView
|
4
|
+
def base_options
|
5
|
+
keys = %i[name label form]
|
6
|
+
options = to_h.slice(*keys)
|
7
|
+
default_base_options.merge(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def input_options
|
11
|
+
keys = %i[form]
|
12
|
+
options = to_h.slice(*keys)
|
13
|
+
default_input_options.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def id
|
19
|
+
"#{name}_value"
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
"field_#{@name}".to_sym || attribute
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_base_options
|
27
|
+
{
|
28
|
+
label: label,
|
29
|
+
name: "field_#{attribute}".to_sym,
|
30
|
+
filter: Headmin::Filter::Field.new("field_#{attribute}".to_sym, @params),
|
31
|
+
allowed_operators: Headmin::Filter::Field::OPERATORS - %w[in not_in]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_input_options
|
36
|
+
{
|
37
|
+
label: false,
|
38
|
+
wrapper: false,
|
39
|
+
id: id,
|
40
|
+
name: nil,
|
41
|
+
data: {
|
42
|
+
action: "change->filter#updateHiddenValue",
|
43
|
+
filter_target: "value",
|
44
|
+
filter_row_target: "original"
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Filter
|
3
|
+
class FilterView < ViewModel
|
4
|
+
def attribute
|
5
|
+
@association ? "#{@association}_#{@attribute}".to_sym : @attribute
|
6
|
+
end
|
7
|
+
|
8
|
+
def label
|
9
|
+
@label || I18n.t("attributes.#{attribute}", default: @association ? "#{association_model.model_name.human(count: 1)} - #{association_model.human_attribute_name(@attribute)}" : name.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def reflection
|
13
|
+
if @association
|
14
|
+
form.object.class.reflect_on_association(@association)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def association_model
|
19
|
+
if @association
|
20
|
+
reflection.klass
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Headmin
|
2
2
|
module Filter
|
3
|
-
class NumberView <
|
3
|
+
class NumberView < FilterView
|
4
4
|
def base_options
|
5
5
|
keys = %i[name label form]
|
6
6
|
options = to_h.slice(*keys)
|
@@ -23,10 +23,6 @@ module Headmin
|
|
23
23
|
@name || attribute
|
24
24
|
end
|
25
25
|
|
26
|
-
def label
|
27
|
-
@label || I18n.t("attributes.#{attribute}", default: name.to_s)
|
28
|
-
end
|
29
|
-
|
30
26
|
def default_base_options
|
31
27
|
{
|
32
28
|
label: label,
|
@@ -41,6 +37,7 @@ module Headmin
|
|
41
37
|
label: false,
|
42
38
|
wrapper: false,
|
43
39
|
id: id,
|
40
|
+
name: nil,
|
44
41
|
data: {
|
45
42
|
action: "change->filter#updateHiddenValue",
|
46
43
|
filter_target: "value",
|
@@ -22,7 +22,9 @@ module Headmin
|
|
22
22
|
matches: "≈ #{I18n.t("headmin.filters.operators.matches")}",
|
23
23
|
does_not_match: "≉ #{I18n.t("headmin.filters.operators.does_not_match")}",
|
24
24
|
is_null: "○ #{I18n.t("headmin.filters.operators.is_null")}",
|
25
|
-
is_not_null: "● #{I18n.t("headmin.filters.operators.is_not_null")}"
|
25
|
+
is_not_null: "● #{I18n.t("headmin.filters.operators.is_not_null")}",
|
26
|
+
in: "∋ #{I18n.t("headmin.filters.operators.in")}",
|
27
|
+
not_in: "∌ #{I18n.t("headmin.filters.operators.not_in")}"
|
26
28
|
}
|
27
29
|
end
|
28
30
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Headmin
|
2
2
|
module Filter
|
3
|
-
class OptionsView <
|
3
|
+
class OptionsView < FilterView
|
4
4
|
def base_options
|
5
5
|
keys = %i[name label form]
|
6
6
|
options = to_h.slice(*keys)
|
@@ -27,10 +27,6 @@ module Headmin
|
|
27
27
|
@name || attribute
|
28
28
|
end
|
29
29
|
|
30
|
-
def label
|
31
|
-
@label || I18n.t("attributes.#{attribute}", default: name.to_s)
|
32
|
-
end
|
33
|
-
|
34
30
|
def default_base_options
|
35
31
|
{
|
36
32
|
label: label,
|
@@ -46,6 +42,7 @@ module Headmin
|
|
46
42
|
label: false,
|
47
43
|
wrapper: false,
|
48
44
|
id: id,
|
45
|
+
name: nil,
|
49
46
|
data: {
|
50
47
|
action: "change->filter#updateHiddenValue",
|
51
48
|
filter_target: "value",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Headmin
|
2
2
|
module Filter
|
3
|
-
class TextView <
|
3
|
+
class TextView < FilterView
|
4
4
|
def base_options
|
5
5
|
keys = %i[name label form]
|
6
6
|
options = to_h.slice(*keys)
|
@@ -23,10 +23,6 @@ module Headmin
|
|
23
23
|
@name || attribute
|
24
24
|
end
|
25
25
|
|
26
|
-
def label
|
27
|
-
@label || I18n.t("attributes.#{attribute}", default: name.to_s)
|
28
|
-
end
|
29
|
-
|
30
26
|
def default_base_options
|
31
27
|
{
|
32
28
|
label: label,
|
@@ -41,6 +37,7 @@ module Headmin
|
|
41
37
|
label: false,
|
42
38
|
wrapper: false,
|
43
39
|
id: id,
|
40
|
+
name: nil,
|
44
41
|
data: {
|
45
42
|
action: "change->filter#updateHiddenValue",
|
46
43
|
filter_target: "value",
|
@@ -13,15 +13,30 @@ module Headmin
|
|
13
13
|
@param_types = param_types
|
14
14
|
end
|
15
15
|
|
16
|
-
def parse(attribute, type)
|
16
|
+
def parse(attribute, type, association: nil)
|
17
17
|
class_name = "Headmin::Filter::#{type.to_s.classify}".constantize
|
18
|
-
class_name.new(attribute, @params)
|
18
|
+
class_name.new(attribute, @params, association: association)
|
19
19
|
end
|
20
20
|
|
21
21
|
def query(collection)
|
22
22
|
@param_types.each do |attribute, type|
|
23
|
-
|
24
|
-
|
23
|
+
if type.is_a? Hash
|
24
|
+
# We are given attribute filters of an association
|
25
|
+
association = attribute
|
26
|
+
|
27
|
+
# By default, we offer a filter of type association
|
28
|
+
association_filter = Headmin::Filter::Association.new(attribute, @params, association: nil)
|
29
|
+
collection = association_filter.query(collection)
|
30
|
+
|
31
|
+
# Query all the passed attribute filters for this association
|
32
|
+
type.each do |new_attribute, new_type|
|
33
|
+
filter = parse(new_attribute, new_type, association: association)
|
34
|
+
collection = filter.query(collection)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
filter = parse(attribute, type)
|
38
|
+
collection = filter.query(collection)
|
39
|
+
end
|
25
40
|
end
|
26
41
|
collection
|
27
42
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Form
|
3
|
+
class AssociationView < ViewModel
|
4
|
+
include Headmin::Form::Hintable
|
5
|
+
include Headmin::Form::InputGroupable
|
6
|
+
include Headmin::Form::Labelable
|
7
|
+
include Headmin::Form::Listable
|
8
|
+
include Headmin::Form::Placeholderable
|
9
|
+
include Headmin::Form::Validatable
|
10
|
+
include Headmin::Form::Wrappable
|
11
|
+
|
12
|
+
def input_options
|
13
|
+
keys = attributes - %i[append attribute collection float form input_group include_blank label prepend validate selected tags wrapper]
|
14
|
+
options = to_h.slice(*keys)
|
15
|
+
def_input_options = default_input_options
|
16
|
+
def_input_options = def_input_options.deep_merge(options)
|
17
|
+
def_input_options = def_input_options.merge({multiple: true}) if collection?
|
18
|
+
def_input_options
|
19
|
+
end
|
20
|
+
|
21
|
+
def input_group_options
|
22
|
+
default_input_group_options
|
23
|
+
.deep_merge(label_input_group_options)
|
24
|
+
.deep_merge(@input_group || {})
|
25
|
+
end
|
26
|
+
|
27
|
+
def wrapper_options
|
28
|
+
default_wrapper_options.deep_merge({
|
29
|
+
class: ["mb-3", ("form-floating" if float)]
|
30
|
+
}).deep_merge(@wrapper || {})
|
31
|
+
end
|
32
|
+
|
33
|
+
def select_options
|
34
|
+
keys = %i[include_blank selected]
|
35
|
+
options = to_h.slice(*keys)
|
36
|
+
default_options.deep_merge(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def attribute_with_id
|
40
|
+
attribute_with_id = collection? ? "#{association_foreign_key}s" : foreign_key
|
41
|
+
|
42
|
+
if attribute_with_id.nil?
|
43
|
+
raise(AssociationDoesNotExistError, "Association attribute that was passed does not exist.")
|
44
|
+
else
|
45
|
+
attribute_with_id
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def collection
|
50
|
+
association_class.all.map { |item| [item.to_s, item.id] }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def association_foreign_key
|
56
|
+
reflection.association_foreign_key
|
57
|
+
end
|
58
|
+
|
59
|
+
def foreign_key
|
60
|
+
reflection.foreign_key
|
61
|
+
end
|
62
|
+
|
63
|
+
def reflection
|
64
|
+
form.object.class.reflect_on_association(attribute)
|
65
|
+
end
|
66
|
+
|
67
|
+
def association_class
|
68
|
+
reflection.klass
|
69
|
+
end
|
70
|
+
|
71
|
+
def collection?
|
72
|
+
reflection.collection?
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_options
|
76
|
+
{
|
77
|
+
selected: form.object&.send(attribute_with_id)
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def default_input_options
|
82
|
+
{
|
83
|
+
aria: {describedby: validation_id},
|
84
|
+
class: [form_control_class, validation_class],
|
85
|
+
data: {
|
86
|
+
tags: tags,
|
87
|
+
controller: "select"
|
88
|
+
},
|
89
|
+
multiple: tags,
|
90
|
+
placeholder: placeholder
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def form_control_class
|
95
|
+
plaintext ? "form-control-plaintext" : "form-select"
|
96
|
+
end
|
97
|
+
|
98
|
+
class AssociationDoesNotExistError < StandardError
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -55,14 +55,6 @@ module Headmin
|
|
55
55
|
attached.is_a?(ActiveStorage::Attached::Many) ? :"#{attribute}_attachments" : :"#{attribute}_attachment"
|
56
56
|
end
|
57
57
|
|
58
|
-
def thumbnail_width
|
59
|
-
100
|
60
|
-
end
|
61
|
-
|
62
|
-
def thumbnail_height
|
63
|
-
100
|
64
|
-
end
|
65
|
-
|
66
58
|
def dropzone_options
|
67
59
|
if dropzone
|
68
60
|
{
|
@@ -18,8 +18,9 @@ module Headmin
|
|
18
18
|
def default_data
|
19
19
|
{
|
20
20
|
controller: "flatpickr",
|
21
|
+
|
21
22
|
flatpickr: {
|
22
|
-
defaultDate: form.object&.send(attribute)&.strftime("%d/%m/%Y")
|
23
|
+
defaultDate: attribute.nil? ? Date.today : form.object&.send(attribute)&.strftime("%d/%m/%Y")
|
23
24
|
}
|
24
25
|
}
|
25
26
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Form
|
3
|
+
class MediaItemView < ViewModel
|
4
|
+
include Rails.application.routes.url_helpers
|
5
|
+
|
6
|
+
def thumbnail_options
|
7
|
+
options = {
|
8
|
+
file: attachment
|
9
|
+
}
|
10
|
+
|
11
|
+
# Don't pass width or height if it was not defined
|
12
|
+
options = options.merge(width: width) if is_defined?(:width)
|
13
|
+
options = options.merge(height: height) if is_defined?(:height)
|
14
|
+
|
15
|
+
options
|
16
|
+
end
|
17
|
+
|
18
|
+
def attachment
|
19
|
+
form.object
|
20
|
+
end
|
21
|
+
|
22
|
+
def position_value
|
23
|
+
attachment.new_record? ? nil : attachment.position
|
24
|
+
end
|
25
|
+
|
26
|
+
def id
|
27
|
+
attachment.blob ? attachment.blob.id : "$1"
|
28
|
+
end
|
29
|
+
|
30
|
+
def filename
|
31
|
+
attachment.blob&.filename&.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def size
|
35
|
+
number_to_human_size(attachment.blob&.byte_size || 0)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -19,7 +19,7 @@ module Headmin
|
|
19
19
|
class: ["mb-3", ("form-floating" if float)],
|
20
20
|
data: {
|
21
21
|
controller: "media",
|
22
|
-
name:
|
22
|
+
name: name,
|
23
23
|
min: min,
|
24
24
|
max: max,
|
25
25
|
sort: sort,
|
@@ -28,6 +28,18 @@ module Headmin
|
|
28
28
|
}).deep_merge(@wrapper || {})
|
29
29
|
end
|
30
30
|
|
31
|
+
def item_options
|
32
|
+
options = {
|
33
|
+
sort: sort
|
34
|
+
}
|
35
|
+
|
36
|
+
# Don't pass width or height if it was not defined
|
37
|
+
options = options.merge(width: width) if is_defined?(:width)
|
38
|
+
options = options.merge(height: height) if is_defined?(:height)
|
39
|
+
|
40
|
+
options
|
41
|
+
end
|
42
|
+
|
31
43
|
def custom_validation_options
|
32
44
|
{
|
33
45
|
form: form,
|
@@ -37,6 +49,18 @@ module Headmin
|
|
37
49
|
}
|
38
50
|
end
|
39
51
|
|
52
|
+
def thumbnail_options
|
53
|
+
options = {
|
54
|
+
icon: "plus"
|
55
|
+
}
|
56
|
+
|
57
|
+
# Don't pass width or height if it was not defined
|
58
|
+
options = options.merge(width: width) unless is_defined?(:width)
|
59
|
+
options = options.merge(height: height) unless is_defined?(:height)
|
60
|
+
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
40
64
|
def association_object
|
41
65
|
if attached.is_a?(ActiveStorage::Attached::Many)
|
42
66
|
result = form.object.send(nested_attribute)
|
@@ -101,8 +125,8 @@ module Headmin
|
|
101
125
|
attachments.map { |attachment| attachment.blob_id }
|
102
126
|
end
|
103
127
|
|
104
|
-
def
|
105
|
-
|
128
|
+
def name
|
129
|
+
"#{attribute}_#{object_id}"
|
106
130
|
end
|
107
131
|
|
108
132
|
def sort
|
@@ -1,41 +1,62 @@
|
|
1
1
|
module Headmin
|
2
|
-
class ThumbnailView
|
3
|
-
def initialize(local_assigns)
|
4
|
-
@local_assigns = local_assigns
|
5
|
-
end
|
6
|
-
|
2
|
+
class ThumbnailView < ViewModel
|
7
3
|
def class_names
|
8
|
-
class_names = [@
|
4
|
+
class_names = [@class]
|
9
5
|
class_names << "img-thumbnail h-thumbnail"
|
10
6
|
class_names.join(" ")
|
11
7
|
end
|
12
8
|
|
13
9
|
def width
|
14
|
-
|
10
|
+
if is_defined?(:width)
|
11
|
+
@width
|
12
|
+
else
|
13
|
+
@width || 100
|
14
|
+
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def height
|
18
|
-
|
18
|
+
if is_defined?(:height)
|
19
|
+
@height
|
20
|
+
else
|
21
|
+
@height || 100
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
|
-
def
|
22
|
-
|
25
|
+
def blob
|
26
|
+
file.is_a?(ActiveStorage::Blob) ? file : file&.blob
|
23
27
|
end
|
24
28
|
|
25
|
-
def
|
26
|
-
|
29
|
+
def mime_type
|
30
|
+
blob&.content_type
|
27
31
|
end
|
28
32
|
|
29
|
-
def
|
30
|
-
if
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
+
})
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
37
|
-
def
|
38
|
-
|
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)
|
39
60
|
end
|
40
61
|
|
41
62
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
data/app/models/view_model.rb
CHANGED
@@ -14,10 +14,15 @@
|
|
14
14
|
# <%= render "headmin/filters", url: admin_polls_path %#>
|
15
15
|
|
16
16
|
action = local_assigns.has_key?(:url) ? url : request.path
|
17
|
-
%>
|
18
17
|
|
19
|
-
|
18
|
+
begin
|
19
|
+
model = controller_name.classify.constantize
|
20
|
+
rescue
|
21
|
+
raise "Cannot find class!"
|
22
|
+
end
|
23
|
+
%>
|
20
24
|
|
25
|
+
<%= form_with model: model.new, url: action, method: :get, data: {controller: "filters", filters_target: "form"} do |form| %>
|
21
26
|
<%# Perform yield in order to capture content blocks, pass form so we can use headmin form inputs %>
|
22
27
|
<%= yield(form) %>
|
23
28
|
|
@@ -1,11 +1,35 @@
|
|
1
|
-
<%
|
1
|
+
<%
|
2
|
+
# headmin/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 "headmin/thumbnail", file: file %#>
|
18
|
+
#
|
19
|
+
# Custom size (200px x 100px)
|
20
|
+
# <%= render "headmin/thumbnail", file: file, width: 200, height: 100 %#>
|
21
|
+
#
|
22
|
+
# Custom (bootstrap) icon
|
23
|
+
# <%= render "headmin/thumbnail", file: file, icon: "file-earmark-excel" %#>
|
24
|
+
thumbnail = Headmin::ThumbnailView.new(local_assigns)
|
25
|
+
%>
|
2
26
|
|
3
|
-
<div class="<%= thumbnail.class_names %>"
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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 %>
|
8
32
|
<%= bootstrap_icon(thumbnail.icon_name, class: "h-thumbnail-icon") %>
|
9
|
-
|
10
|
-
|
33
|
+
<% end %>
|
34
|
+
</div>
|
11
35
|
</div>
|