headmin 0.5.1 → 0.5.4

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.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -2
  3. data/Gemfile +14 -0
  4. data/Gemfile.lock +79 -2
  5. data/app/assets/javascripts/headmin/controllers/media_controller.js +237 -0
  6. data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +110 -0
  7. data/app/assets/javascripts/headmin/controllers/remote_modal_controller.js +9 -0
  8. data/app/assets/javascripts/headmin/controllers/textarea_controller.js +34 -0
  9. data/app/assets/javascripts/headmin/index.js +8 -0
  10. data/app/assets/javascripts/headmin.js +294 -0
  11. data/app/assets/stylesheets/headmin/forms/file.scss +40 -5
  12. data/app/assets/stylesheets/headmin/forms/media.scss +10 -0
  13. data/app/assets/stylesheets/headmin/forms.scss +1 -0
  14. data/app/assets/stylesheets/headmin/layout/sidebar.scss +0 -1
  15. data/app/assets/stylesheets/headmin/media/index.scss +9 -0
  16. data/app/assets/stylesheets/headmin/media.scss +1 -0
  17. data/app/assets/stylesheets/headmin/overrides/redactorx.scss +1 -1
  18. data/app/assets/stylesheets/headmin/table.scss +8 -0
  19. data/app/assets/stylesheets/headmin/vendor/{tom-select-bootstrap.css → tom-select-bootstrap.scss} +0 -1
  20. data/app/assets/stylesheets/headmin.css +61 -8
  21. data/app/assets/stylesheets/headmin.scss +1 -0
  22. data/app/controllers/headmin/media_controller.rb +52 -0
  23. data/app/controllers/headmin_controller.rb +2 -0
  24. data/app/helpers/headmin/form_helper.rb +2 -2
  25. data/app/models/concerns/headmin/field.rb +2 -2
  26. data/app/models/concerns/headmin/fieldable.rb +19 -10
  27. data/app/models/concerns/headmin/form/hintable.rb +6 -1
  28. data/app/models/headmin/filter/association.rb +86 -0
  29. data/app/models/headmin/filter/association_view.rb +74 -0
  30. data/app/models/headmin/filter/base.rb +5 -2
  31. data/app/models/headmin/filter/boolean_view.rb +1 -0
  32. data/app/models/headmin/filter/date.rb +49 -1
  33. data/app/models/headmin/filter/date_view.rb +1 -0
  34. data/app/models/headmin/filter/flatpickr_view.rb +1 -0
  35. data/app/models/headmin/filter/number_view.rb +1 -0
  36. data/app/models/headmin/filter/operator_view.rb +3 -1
  37. data/app/models/headmin/filter/options_view.rb +1 -0
  38. data/app/models/headmin/filter/text_view.rb +1 -0
  39. data/app/models/headmin/form/association_view.rb +102 -0
  40. data/app/models/headmin/form/blocks_view.rb +4 -1
  41. data/app/models/headmin/form/file_view.rb +0 -8
  42. data/app/models/headmin/form/flatpickr_view.rb +2 -1
  43. data/app/models/headmin/form/media_item_view.rb +39 -0
  44. data/app/models/headmin/form/media_view.rb +137 -0
  45. data/app/models/headmin/form/select_view.rb +2 -1
  46. data/app/models/headmin/form/textarea_view.rb +6 -1
  47. data/app/models/headmin/thumbnail_view.rb +40 -19
  48. data/app/models/view_model.rb +4 -0
  49. data/app/views/examples/admin.html.erb +8 -8
  50. data/app/views/examples/auth.html.erb +2 -2
  51. data/app/views/headmin/_breadcrumbs.html.erb +2 -2
  52. data/app/views/headmin/_dropdown.html.erb +1 -1
  53. data/app/views/headmin/_filters.html.erb +12 -7
  54. data/app/views/headmin/_pagination.html.erb +2 -2
  55. data/app/views/headmin/_popup.html.erb +4 -4
  56. data/app/views/headmin/_table.html.erb +1 -1
  57. data/app/views/headmin/_thumbnail.html.erb +33 -9
  58. data/app/views/headmin/dropdown/_button.html.erb +2 -2
  59. data/app/views/headmin/dropdown/_item.html.erb +2 -2
  60. data/app/views/headmin/dropdown/_list.html.erb +3 -3
  61. data/app/views/headmin/dropdown/_locale.html.erb +5 -5
  62. data/app/views/headmin/filters/_association.html.erb +24 -0
  63. data/app/views/headmin/filters/_options.html.erb +1 -1
  64. data/app/views/headmin/filters/filter/_button.html.erb +2 -2
  65. data/app/views/headmin/filters/filter/_null_select.html.erb +2 -2
  66. data/app/views/headmin/forms/_association.html.erb +30 -0
  67. data/app/views/headmin/forms/_errors.html.erb +1 -1
  68. data/app/views/headmin/forms/_file.html.erb +10 -11
  69. data/app/views/headmin/forms/_hint.html.erb +6 -1
  70. data/app/views/headmin/forms/_media.html.erb +60 -0
  71. data/app/views/headmin/forms/_repeater.html.erb +18 -16
  72. data/app/views/headmin/forms/_textarea.html.erb +1 -1
  73. data/app/views/headmin/forms/_wrapper.html.erb +0 -1
  74. data/app/views/headmin/forms/fields/_list.html.erb +6 -4
  75. data/app/views/headmin/forms/media/_item.html.erb +38 -0
  76. data/app/views/headmin/forms/media/_validation.html.erb +10 -0
  77. data/app/views/headmin/forms/repeater/_row.html.erb +17 -15
  78. data/app/views/headmin/heading/_title.html.erb +2 -2
  79. data/app/views/headmin/layout/_main.html.erb +2 -0
  80. data/app/views/headmin/layout/_remote_modal.html.erb +1 -0
  81. data/app/views/headmin/layout/_sidebar.html.erb +1 -1
  82. data/app/views/headmin/media/_item.html.erb +16 -0
  83. data/app/views/headmin/media/_media_item_modal.html.erb +51 -0
  84. data/app/views/headmin/media/_modal.html.erb +35 -0
  85. data/app/views/headmin/media/create.turbo_stream.erb +5 -0
  86. data/app/views/headmin/media/index.html.erb +3 -0
  87. data/app/views/headmin/media/show.html.erb +9 -0
  88. data/app/views/headmin/media/update.turbo_stream.erb +3 -0
  89. data/app/views/headmin/nav/_dropdown.html.erb +7 -7
  90. data/app/views/headmin/nav/_item.html.erb +5 -5
  91. data/app/views/headmin/nav/item/_locale.html.erb +6 -6
  92. data/app/views/headmin/pagination/_per_page.html.erb +7 -7
  93. data/app/views/headmin/pagination/kaminari/_first_page.html.erb +2 -2
  94. data/app/views/headmin/pagination/kaminari/_gap.html.erb +1 -1
  95. data/app/views/headmin/pagination/kaminari/_last_page.html.erb +2 -2
  96. data/app/views/headmin/pagination/kaminari/_next_page.html.erb +3 -3
  97. data/app/views/headmin/pagination/kaminari/_page.html.erb +2 -2
  98. data/app/views/headmin/pagination/kaminari/_paginator.html.erb +1 -1
  99. data/app/views/headmin/pagination/kaminari/_prev_page.html.erb +2 -2
  100. data/app/views/headmin/table/_actions.html.erb +9 -9
  101. data/app/views/headmin/table/_body.html.erb +1 -1
  102. data/app/views/headmin/table/actions/_action.html.erb +4 -4
  103. data/app/views/headmin/table/actions/_export.html.erb +1 -1
  104. data/app/views/headmin/table/body/_association.html.erb +17 -3
  105. data/app/views/headmin/table/body/_boolean.erb +4 -4
  106. data/app/views/headmin/table/body/_date.html.erb +2 -2
  107. data/app/views/headmin/table/body/_image.html.erb +18 -0
  108. data/app/views/headmin/table/body/_string.html.erb +1 -1
  109. data/app/views/headmin/table/head/_cell.html.erb +1 -1
  110. data/app/views/headmin/table/head/cell/_asc.html.erb +2 -2
  111. data/app/views/headmin/table/head/cell/_default.html.erb +1 -1
  112. data/app/views/headmin/table/head/cell/_desc.html.erb +1 -1
  113. data/app/views/headmin/views/devise/confirmations/_new.html.erb +1 -1
  114. data/app/views/headmin/views/devise/passwords/_edit.html.erb +1 -1
  115. data/app/views/headmin/views/devise/passwords/_new.html.erb +1 -1
  116. data/app/views/headmin/views/devise/registrations/_edit.html.erb +5 -5
  117. data/app/views/headmin/views/devise/registrations/_new.html.erb +1 -1
  118. data/app/views/headmin/views/devise/sessions/_new.html.erb +1 -1
  119. data/app/views/headmin/views/devise/shared/_links.html.erb +14 -20
  120. data/app/views/headmin/views/devise/unlocks/_new.html.erb +1 -1
  121. data/config/locales/activerecord/en.yml +1 -0
  122. data/config/locales/activerecord/nl.yml +1 -0
  123. data/config/locales/devise/nl.yml +1 -1
  124. data/config/locales/headmin/filters/en.yml +3 -1
  125. data/config/locales/headmin/filters/nl.yml +2 -0
  126. data/config/locales/headmin/forms/en.yml +8 -0
  127. data/config/locales/headmin/forms/nl.yml +8 -0
  128. data/config/locales/headmin/media/en.yml +23 -0
  129. data/config/locales/headmin/media/nl.yml +23 -0
  130. data/config/locales/headmin/table/en.yml +2 -0
  131. data/config/locales/headmin/table/nl.yml +2 -0
  132. data/config/routes.rb +10 -0
  133. data/lib/generators/templates/views/layouts/auth.html.erb +2 -2
  134. data/lib/headmin/version.rb +1 -1
  135. data/package.json +1 -1
  136. metadata +34 -3
@@ -0,0 +1,137 @@
1
+ module Headmin
2
+ module Form
3
+ class MediaView < ViewModel
4
+ include Rails.application.routes.url_helpers
5
+ include Headmin::Form::Hintable
6
+ include Headmin::Form::Labelable
7
+ include Headmin::Form::Placeholderable
8
+ include Headmin::Form::Validatable
9
+ include Headmin::Form::Wrappable
10
+
11
+ def input_group_options
12
+ default_input_group_options
13
+ .deep_merge(label_input_group_options)
14
+ .deep_merge(@input_group || {})
15
+ end
16
+
17
+ def wrapper_options
18
+ default_wrapper_options.deep_merge({
19
+ class: ["mb-3", ("form-floating" if float)],
20
+ data: {
21
+ controller: "media",
22
+ name: name,
23
+ min: min,
24
+ max: max,
25
+ sort: sort,
26
+ required: required.nil? ? 0 : required
27
+ }
28
+ }).deep_merge(@wrapper || {})
29
+ end
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
+
43
+ def custom_validation_options
44
+ {
45
+ form: form,
46
+ attribute: attribute,
47
+ min: min,
48
+ max: max
49
+ }
50
+ end
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
+
64
+ def association_object
65
+ if attached.is_a?(ActiveStorage::Attached::Many)
66
+ result = form.object.send(nested_attribute)
67
+ result = result.order(position: :asc) if sort
68
+ result
69
+ else
70
+ form.object.send(nested_attribute)
71
+ end
72
+ end
73
+
74
+ def attachments
75
+ if attached.is_a?(ActiveStorage::Attached::Many)
76
+ result = form.object.send(nested_attribute)
77
+ result = result.order(position: :asc) if sort
78
+ result.to_a.compact
79
+ else
80
+ [form.object.send(nested_attribute)].compact
81
+ end
82
+ end
83
+
84
+ def attached
85
+ form.object.send(attribute)
86
+ end
87
+
88
+ def build_nested_attribute
89
+ if attached.is_a?(ActiveStorage::Attached::Many)
90
+ form.object.send(nested_attribute).build
91
+ else
92
+ form.object.send("build_#{nested_attribute}")
93
+ end
94
+ end
95
+
96
+ def nested_attribute
97
+ if attached.is_a?(ActiveStorage::Attached::Many)
98
+ :"#{attribute}_attachments"
99
+ else
100
+ :"#{attribute}_attachment"
101
+ end
102
+ end
103
+
104
+ def min
105
+ if @required
106
+ @min.to_i < 1 ? 1 : @min.to_i
107
+ else
108
+ @min.to_i < 1 ? 0 : @min.to_i
109
+ end
110
+ end
111
+
112
+ def max
113
+ if attached.is_a?(ActiveStorage::Attached::Many)
114
+ @max
115
+ else
116
+ 1
117
+ end
118
+ end
119
+
120
+ def required
121
+ @required ? 1 : nil
122
+ end
123
+
124
+ def blob_ids
125
+ attachments.map { |attachment| attachment.blob_id }
126
+ end
127
+
128
+ def name
129
+ "#{attribute}_#{object_id}"
130
+ end
131
+
132
+ def sort
133
+ @sort == true
134
+ end
135
+ end
136
+ end
137
+ end
@@ -36,8 +36,9 @@ module Headmin
36
36
  private
37
37
 
38
38
  def default_options
39
+ selected = attribute.nil? ? nil : form.object&.send(attribute)
39
40
  {
40
- selected: form.object&.send(attribute)
41
+ selected: selected
41
42
  }
42
43
  end
43
44
 
@@ -22,7 +22,8 @@ module Headmin
22
22
 
23
23
  def wrapper_options
24
24
  default_wrapper_options.deep_merge({
25
- class: ["mb-3", ("form-floating" if float)]
25
+ class: ["mb-3", ("form-floating" if float)],
26
+ data: {controller: :textarea}
26
27
  }).deep_merge(@wrapper || {})
27
28
  end
28
29
 
@@ -32,6 +33,10 @@ module Headmin
32
33
  {
33
34
  aria: {describedby: validation_id},
34
35
  class: [form_control_class, validation_class],
36
+ data: {
37
+ textarea_target: :textarea,
38
+ action: "input->textarea#update"
39
+ },
35
40
  placeholder: placeholder
36
41
  }
37
42
  end
@@ -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 = [@local_assigns[:class]]
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
- @local_assigns[:width] || 150
10
+ if is_defined?(:width)
11
+ @width
12
+ else
13
+ @width || 100
14
+ end
15
15
  end
16
16
 
17
17
  def height
18
- @local_assigns[:height] || 150
18
+ if is_defined?(:height)
19
+ @height
20
+ else
21
+ @height || 100
22
+ end
19
23
  end
20
24
 
21
- def src
22
- @local_assigns[:src]
25
+ def blob
26
+ file.is_a?(ActiveStorage::Blob) ? file : file&.blob
23
27
  end
24
28
 
25
- def icon
26
- @local_assigns[:icon]
29
+ def mime_type
30
+ blob&.content_type
27
31
  end
28
32
 
29
- def mime_type
30
- if src
31
- stripped_path = URI.parse(src).path
32
- extension = File.extname(stripped_path)
33
- Rack::Mime.mime_type(extension)
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 image?
38
- mime_type&.match?(/^image/)
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
@@ -40,6 +40,10 @@ class ViewModel
40
40
 
41
41
  private
42
42
 
43
+ def is_defined?(attribute)
44
+ attributes.include?(attribute.to_sym)
45
+ end
46
+
43
47
  def value_for(attribute)
44
48
  reserved_methods.include?(attribute) ? instance_variable_get("@#{attribute}") : send(attribute)
45
49
  end
@@ -6,15 +6,15 @@
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
8
8
 
9
- <%= stylesheet_pack_tag 'admin', media: 'all', 'data-turbo-track': 'reload' %>
10
- <%= javascript_pack_tag 'admin', 'data-turbo-track': 'reload' %>
9
+ <%= stylesheet_pack_tag "admin", media: "all", "data-turbo-track": "reload" %>
10
+ <%= javascript_pack_tag "admin", "data-turbo-track": "reload" %>
11
11
  </head>
12
12
 
13
13
  <body>
14
14
  <%= render "headmin/layout/main" do %>
15
- <%= render "headmin/layout/sidebar", logo: inline_svg_tag('logo.svg') do %>
15
+ <%= render "headmin/layout/sidebar", logo: inline_svg_tag("logo.svg") do %>
16
16
  <%= render "headmin/layout/sidebar/menu" do %>
17
- <%= render "headmin/layout/sidebar/menu/item", name: t('.dashboard'), link: admin_root_path, icon: 'speedometer' %>
17
+ <%= render "headmin/layout/sidebar/menu/item", name: t(".dashboard"), link: admin_root_path, icon: "speedometer" %>
18
18
  <div class="d-block d-md-none">
19
19
  <%= render "headmin/layout/sidebar/menu/locale" do end %>
20
20
  <%= render "headmin/layout/sidebar/menu/account" do end %>
@@ -30,10 +30,10 @@
30
30
  <%= render "headmin/notifications" %>
31
31
  <%= yield %>
32
32
  <% end %>
33
- <%= render "headmin/layout/footer", class: 'text-secondary' do %>
34
- <%= bootstrap_icon('question-circle') %>
35
- <a class="text-secondary" href="mailto:info@insiting.com" title="<%= t('.help') %>">
36
- <%= t('.help') %>
33
+ <%= render "headmin/layout/footer", class: "text-secondary" do %>
34
+ <%= bootstrap_icon("question-circle") %>
35
+ <a class="text-secondary" href="mailto:info@insiting.com" title="<%= t(".help") %>">
36
+ <%= t(".help") %>
37
37
  </a>
38
38
  <% end %>
39
39
  <% end %>
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
8
- <%= stylesheet_pack_tag 'admin', media: 'all', 'data-turbolinks-track': 'reload' %>
9
- <%= javascript_pack_tag 'admin', 'data-turbolinks-track': 'reload' %>
8
+ <%= stylesheet_pack_tag "admin", media: "all", "data-turbolinks-track": "reload" %>
9
+ <%= javascript_pack_tag "admin", "data-turbolinks-track": "reload" %>
10
10
  </head>
11
11
  <body class="bg-light w-100 vh-100">
12
12
 
@@ -14,11 +14,11 @@
14
14
  <% breadcrumb_count = breadcrumbs.count %>
15
15
  <% breadcrumbs.each_with_index do |breadcrumb, index| %>
16
16
  <% last = (index == breadcrumb_count - 1) %>
17
- <li class="breadcrumb-item text-muted" aria-current="<%= last ? 'page' : nil %>">
17
+ <li class="breadcrumb-item text-muted" aria-current="<%= last ? "page" : nil %>">
18
18
  <% if last %>
19
19
  <%= breadcrumb.name %>
20
20
  <% else %>
21
- <%= link_to_unless_current(breadcrumb.name, breadcrumb.path, class: 'text-muted text-decoration-none') %>
21
+ <%= link_to_unless_current(breadcrumb.name, breadcrumb.path, class: "text-muted text-decoration-none") %>
22
22
  <% end %>
23
23
  </li>
24
24
  <% end %>
@@ -10,7 +10,7 @@
10
10
  # Your content
11
11
  # <% end %#>
12
12
 
13
- class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : 'btn-group'
13
+ class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : "btn-group"
14
14
  %>
15
15
 
16
16
  <div class="dropdown <%= class_names %>">
@@ -7,22 +7,27 @@
7
7
  # ==== Examples
8
8
  # Basic version
9
9
  # <%= render "headmin/filters" do %#>
10
- # <%= render "headmin/filters/select", name: 'color', options: ['red', 'green', 'blue'] %#>
10
+ # <%= render "headmin/filters/select", name: "color", options: ["red", "green", "blue"] %#>
11
11
  # <% end %#>
12
12
  #
13
13
  # With custom URL
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
- <%= form_with url: action, method: :get, data: {controller: "filters", filters_target: "form"} do |form| %>
18
+ begin
19
+ model = controller_name.singularize.capitalize.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
 
24
29
  <!-- Default parameters (e.g. sorting, pagination) -->
25
- <% default_params.except('page').each do |name, value| %>
30
+ <% default_params.except("page").each do |name, value| %>
26
31
  <%= form.hidden_field name.to_sym, value: value %>
27
32
  <% end %>
28
33
 
@@ -44,8 +49,8 @@
44
49
  <% if content_for?(:filters_menu) %>
45
50
  <div class="btn-group">
46
51
  <button type="button" class="btn h-btn-outline-light dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
47
- <%= bootstrap_icon('funnel') %>
48
- <%= t('.button') %>
52
+ <%= bootstrap_icon("funnel") %>
53
+ <%= t(".button") %>
49
54
  </button>
50
55
  <ul class="dropdown-menu">
51
56
  <%= content_for :filters_menu %>
@@ -53,7 +58,7 @@
53
58
  <hr class="dropdown-divider">
54
59
  </li>
55
60
  <li>
56
- <a class="dropdown-item text-danger" href="#" data-action="click->filters#removeAll"><%= t('.remove_all') %></a>
61
+ <a class="dropdown-item text-danger" href="#" data-action="click->filters#removeAll"><%= t(".remove_all") %></a>
57
62
  </li>
58
63
  </ul>
59
64
  </div>
@@ -12,7 +12,7 @@
12
12
  <div class="d-flex flex-row-reverse flex-md-row align-items-center justify-content-between justify-content-md-end my-1">
13
13
  <% content_for :collection_total_count, collection.total_count.to_s unless content_for?(:collection_total_count) %>
14
14
  <div class="ms-2 me-md-2 text-secondary flex-grow-1 flex-shrink-0">
15
- <%= t('.items', count: content_for(:collection_total_count).to_i) %>
15
+ <%= t(".items", count: content_for(:collection_total_count).to_i) %>
16
16
  </div>
17
- <%= paginate collection, views_prefix: 'headmin/pagination' %>
17
+ <%= paginate collection, views_prefix: "headmin/pagination" %>
18
18
  </div>
@@ -8,7 +8,7 @@
8
8
  # To trigger this popup create an element with a data attribute like this
9
9
  #
10
10
  # <button data-popup-id="popupId">open Popup</button>
11
- # <%= render "headmin/popup", id: 'popupId' do %#>
11
+ # <%= render "headmin/popup", id: "popupId" do %#>
12
12
  # popup Content
13
13
  # <% end %#>
14
14
  #
@@ -16,14 +16,14 @@
16
16
  # "data-popup-pass-thru" attribute.
17
17
  #
18
18
  # <button data-popup-id="popupId" data-popup-pass-thru="#link">open Popup</button>
19
- # <%= render "headmin/popup", id: 'popupId' do %#>
19
+ # <%= render "headmin/popup", id: "popupId" do %#>
20
20
  # <a href="https://example.com" id="link">
21
21
  # <% end %#>
22
22
  data = local_assigns.has_key?(:data) ? data : {}
23
23
 
24
24
  options = {
25
- class: ['h-popup', 'closed'],
26
- data: data.merge('popup-target': 'popup', 'popup-id': id)
25
+ class: ["h-popup", "closed"],
26
+ data: data.merge("popup-target": "popup", "popup-id": id)
27
27
  }
28
28
  %>
29
29
 
@@ -13,7 +13,7 @@
13
13
  total_count = content_for(:collection_total_count).to_i || nil
14
14
  %>
15
15
 
16
- <div class="h-table card my-2 shadow-sm <%= 'table-drag-sort' if sort_url %>" data-sort-url="<%= sort_url %>">
16
+ <div class="h-table card my-2 shadow-sm <%= "table-drag-sort" if sort_url %>" data-sort-url="<%= sort_url %>">
17
17
  <div class="table-responsive">
18
18
  <table class="table table-hover m-0"
19
19
  data-controller="table"
@@ -1,11 +1,35 @@
1
- <% thumbnail = Headmin::ThumbnailView.new(local_assigns) %>
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 %>" style="width: <%= thumbnail.width %>px; height: <%= thumbnail.height %>px;">
4
- <% if thumbnail.image? %>
5
- <div class="h-thumbnail-bg" style="background-image: url('<%= thumbnail.src %>');"></div>
6
- <% else %>
7
- <div class="h-thumbnail-bg">
8
- <%= bootstrap_icon(thumbnail.icon_name, class: 'h-thumbnail-icon') %>
9
- </div>
10
- <% end %>
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>
11
35
  </div>
@@ -5,8 +5,8 @@
5
5
  id: unique identifier for the dropdown
6
6
  class: css class names as string
7
7
  %>
8
- <% id = local_assigns.has_key?(:id) ? id : 'dropdown-1' %>
9
- <% class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : 'btn' %>
8
+ <% id = local_assigns.has_key?(:id) ? id : "dropdown-1" %>
9
+ <% class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : "btn" %>
10
10
 
11
11
  <a class="dropdown-toggle d-flex align-items-center <%= class_names %>" type="button" id="<%= id %>" data-bs-toggle="dropdown" aria-expanded="false">
12
12
  <%= yield %>
@@ -8,8 +8,8 @@
8
8
  %>
9
9
 
10
10
  <% method = local_assigns.has_key?(:method) ? method : nil %>
11
- <% url = local_assigns.has_key?(:url) ? url : '' %>
12
- <% name = local_assigns.has_key?(:name) ? name : '' %>
11
+ <% url = local_assigns.has_key?(:url) ? url : "" %>
12
+ <% name = local_assigns.has_key?(:name) ? name : "" %>
13
13
 
14
14
  <li>
15
15
  <a class="dropdown-item" href="<%= url %>" <%= "data-turbo-method=#{method}" if method %>>
@@ -7,10 +7,10 @@
7
7
  #
8
8
  # ==== Examples
9
9
  # Basic version
10
- # <%= render "headmin/dropdown/list", id: 'dropdown-1' %#>
10
+ # <%= render "headmin/dropdown/list", id: "dropdown-1" %#>
11
11
 
12
- class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : ''
13
- id = local_assigns.has_key?(:id) ? id : 'dropdown-1'
12
+ class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : ""
13
+ id = local_assigns.has_key?(:id) ? id : "dropdown-1"
14
14
  %>
15
15
 
16
16
  <ul class="dropdown-menu <%= class_names %>" aria-labelledby="<%= id %>">
@@ -5,13 +5,13 @@
5
5
  %>
6
6
 
7
7
  <%= render "headmin/dropdown" do %>
8
- <%= render "headmin/dropdown/button", id: 'locale-dropdown' do %>
9
- <%= bootstrap_icon('globe', class: 'me-2') %>
10
- <%= t('language_name', locale: ::I18n.locale) %>
8
+ <%= render "headmin/dropdown/button", id: "locale-dropdown" do %>
9
+ <%= bootstrap_icon("globe", class: "me-2") %>
10
+ <%= t("language_name", locale: ::I18n.locale) %>
11
11
  <% end %>
12
- <%= render "headmin/dropdown/list", id: 'locale-dropdown' do %>
12
+ <%= render "headmin/dropdown/list", id: "locale-dropdown" do %>
13
13
  <% I18n.available_locales.each do |locale| %>
14
- <%= render "headmin/dropdown/item", name: t('language_name', locale: locale), url: url_for({locale: locale.to_s}) %>
14
+ <%= render "headmin/dropdown/item", name: t("language_name", locale: locale), url: url_for({locale: locale.to_s}) %>
15
15
  <% end %>
16
16
  <% end %>
17
17
  <% end %>
@@ -0,0 +1,24 @@
1
+ <%
2
+ # headmin/filters/association
3
+ #
4
+ # ==== Required parameters
5
+ # * +attribute+ - Name of the attribute to be filtered
6
+ # * +collection+ - Values to create option tags for
7
+ # * +form+ - Form object
8
+ #
9
+ # ==== Optional parameters
10
+ # * +label+ - Display label
11
+ # * +name+ - Name of the filter parameter
12
+ #
13
+ # ==== Examples
14
+ # Basic version
15
+ # <%= render "headmin/filters", url: admin_orders_path do |form| %#>
16
+ # <%= render "headmin/filters/association", form: form, attribute: :product %#>
17
+ # <% end %#>
18
+
19
+ association = Headmin::Filter::AssociationView.new(local_assigns.merge(params: params))
20
+ %>
21
+
22
+ <%= render "headmin/filters/base", association.base_options do |value| %>
23
+ <%= render "headmin/forms/select", association.input_options.merge(selected: value) %>
24
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <%
2
- # headmin/filters/boolean
2
+ # headmin/filters/options
3
3
  #
4
4
  # ==== Required parameters
5
5
  # * +attribute+ - Name of the attribute to be filtered
@@ -19,11 +19,11 @@
19
19
  >
20
20
  <%= raw("#{button_view.label} #{button_view.filter&.string(display_values: button_view.display_values)}") %>
21
21
  <span class="h-filter-remove" data-action="click->filters#remove">
22
- <%= bootstrap_icon('x-lg') %>
22
+ <%= bootstrap_icon("x-lg") %>
23
23
  </span>
24
24
  </button>
25
25
 
26
- <%= render "headmin/popup", id: "#{button_view.id}", data: {'filter-target': 'popup'} do %>
26
+ <%= render "headmin/popup", id: "#{button_view.id}", data: {"filter-target": "popup"} do %>
27
27
  <%= yield %>
28
28
  <% end %>
29
29
  </div>
@@ -1,8 +1,8 @@
1
1
  <select class="form-select" data-filter-row-target="null" data-action="change->filter#updateHiddenValue" data-filter-target="value">
2
2
  <option value="1" <%= value == 1 ? "selected" : "" %>>
3
- <%= t('headmin.filters.values.yes') %>
3
+ <%= t("headmin.filters.values.yes") %>
4
4
  </option>
5
5
  <option value="0" <%= value == 0 ? "selected" : "" %>>
6
- <%= t('headmin.filters.values.no') %>
6
+ <%= t("headmin.filters.values.no") %>
7
7
  </option>
8
8
  </select>
@@ -0,0 +1,30 @@
1
+ <%
2
+ # headmin/forms/association
3
+ #
4
+ # ==== Required parameters
5
+ # * +attribute+ - Name of the association
6
+ # * +form+ - Form object
7
+ #
8
+ # ==== Optional parameters
9
+ # * +collection+ - Values to create option tags for
10
+ #
11
+ #
12
+ # ==== Examples
13
+ # Basic version
14
+ # <%= form_with do |form| %#>
15
+ # <%= render "headmin/forms/association", form: form, attribute: :product %#>
16
+ # <% end %#>
17
+
18
+ association = Headmin::Form::AssociationView.new(local_assigns)
19
+
20
+ %>
21
+
22
+ <%= render "headmin/forms/wrapper", association.wrapper_options do %>
23
+ <%= render "headmin/forms/label", association.label_options if association.prepend_label? %>
24
+ <%= render "headmin/forms/input_group", association.input_group_options do %>
25
+ <%= form.select(association.attribute_with_id, association.collection, association.select_options, association.input_options) %>
26
+ <% end %>
27
+ <%= render "headmin/forms/validation", association.validation_options if association.validate? %>
28
+ <%= render "headmin/forms/hint", association.hint_options if association.hint? %>
29
+ <%= render "headmin/forms/label", association.label_options if association.append_label? %>
30
+ <% end %>
@@ -8,7 +8,7 @@
8
8
  <% if form.object.errors.any? %>
9
9
  <div class="alert alert-danger" role="alert">
10
10
  <h5 class="alert-heading d-flex align-items-center">
11
- <%= t('errors.template.header', count: form.object.errors.size, model: form.object.model_name.human) %>
11
+ <%= t("errors.template.header", count: form.object.errors.size, model: form.object.model_name.human) %>
12
12
  </h5>
13
13
  <ul class="mb-0">
14
14
  <% form.object.errors.full_messages.each do |message| %>