headmin 0.6.0 → 0.6.2

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/Gemfile.lock +92 -90
  4. data/README.md +2 -2
  5. data/app/assets/javascripts/headmin/controllers/filter_controller.js +15 -3
  6. data/app/assets/javascripts/headmin/controllers/filter_row_controller.js +75 -47
  7. data/app/assets/javascripts/headmin/controllers/infinite_scroller_controller.js +30 -0
  8. data/app/assets/javascripts/headmin/controllers/media_controller.js +24 -8
  9. data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +142 -105
  10. data/app/assets/javascripts/headmin/index.js +2 -0
  11. data/app/assets/javascripts/headmin.js +122 -19
  12. data/app/assets/stylesheets/headmin.css +1 -1
  13. data/app/controllers/headmin/media_controller.rb +11 -0
  14. data/app/helpers/headmin/form_helper.rb +0 -11
  15. data/app/models/concerns/headmin/attachment.rb +34 -0
  16. data/app/models/headmin/filter/association.rb +0 -12
  17. data/app/models/headmin/filter/association_count.rb +50 -0
  18. data/app/models/headmin/filter/association_count_view.rb +78 -0
  19. data/app/models/headmin/filter/base.rb +10 -0
  20. data/app/models/headmin/filter/date.rb +8 -1
  21. data/app/models/headmin/filter/date_view.rb +1 -1
  22. data/app/models/headmin/filter/number.rb +0 -12
  23. data/app/models/headmin/filter/operator_view.rb +3 -1
  24. data/app/models/headmin/form/media_view.rb +6 -2
  25. data/app/views/headmin/_filters.html.erb +3 -8
  26. data/app/views/headmin/_form.html.erb +1 -1
  27. data/app/views/headmin/_index.html.erb +1 -1
  28. data/app/views/headmin/filters/_association_count.html.erb +28 -0
  29. data/app/views/headmin/filters/_date.html.erb +1 -0
  30. data/app/views/headmin/forms/fields/_base.html.erb +1 -1
  31. data/app/views/headmin/layout/_content.html.erb +1 -1
  32. data/app/views/headmin/media/_media_item_modal.html.erb +26 -0
  33. data/app/views/headmin/media/_modal.html.erb +8 -3
  34. data/app/views/headmin/media/_thumbnail.html.erb +20 -0
  35. data/app/views/headmin/media/create.turbo_stream.erb +1 -1
  36. data/app/views/headmin/media/index.turbo_stream.erb +11 -0
  37. data/app/views/headmin/media/thumbnail.html.erb +3 -0
  38. data/app/views/headmin/nav/_item.html.erb +6 -1
  39. data/app/views/headmin/pagination/_infinite.html.erb +7 -0
  40. data/config/initializers/extend_active_storage_attachment.rb +3 -0
  41. data/config/locales/headmin/filters/en.yml +2 -0
  42. data/config/locales/headmin/filters/nl.yml +2 -0
  43. data/config/locales/headmin/media/en.yml +1 -0
  44. data/config/locales/headmin/media/nl.yml +1 -0
  45. data/config/locales/headmin/pagination/en.yml +2 -0
  46. data/config/locales/headmin/pagination/nl.yml +2 -0
  47. data/config/routes.rb +2 -1
  48. data/lib/headmin/version.rb +1 -1
  49. data/package.json +1 -1
  50. metadata +12 -3
  51. data/app/views/headmin/media/_item.html.erb +0 -16
@@ -0,0 +1,50 @@
1
+ module Headmin
2
+ module Filter
3
+ class AssociationCount < Headmin::Filter::Base
4
+ OPERATORS = %w[eq not_eq gt gteq lt lteq]
5
+
6
+ def cast_value(value)
7
+ is_i?(value) ? value.to_i : 0
8
+ end
9
+
10
+ def query(collection)
11
+ return collection unless @instructions.any?
12
+
13
+ # Store the collections' class for later use
14
+ @parent_class = collection.is_a?(Class) ? collection : collection.klass
15
+
16
+ # Join table and group on primary key if necessary
17
+ collection = collection.left_joins(reflection.name).group(primary_key)
18
+
19
+ # Build query and execute
20
+ query = nil
21
+ @instructions.each do |instruction|
22
+ query = build_query(query, collection, instruction)
23
+ end
24
+ collection.having(query)
25
+ end
26
+
27
+ private
28
+
29
+ def build_query(query, collection, instruction)
30
+ query_operator = convert_to_query_operator(instruction[:operator])
31
+ query_value = convert_to_query_value(instruction[:value], instruction[:operator])
32
+
33
+ query_operator, query_value = process_null_operators(query_operator, query_value)
34
+ new_query = reflection.klass.arel_table[reflection.foreign_key.to_sym].count.send(query_operator, query_value)
35
+ query ? query.send(instruction[:conditional], new_query) : new_query
36
+ end
37
+
38
+ def reflection
39
+ reflection = @parent_class.reflect_on_association(attribute.to_s.split("_")[0].to_sym)
40
+ raise UnknownAssociation if reflection.nil?
41
+
42
+ reflection
43
+ end
44
+
45
+ def primary_key
46
+ "#{@parent_class.table_name}.#{@parent_class.primary_key}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,78 @@
1
+ module Headmin
2
+ module Filter
3
+ class AssociationCountView < 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
+ def collection
17
+ @collection || association_model.all.map { |record| [record.to_s, record.id] }
18
+ end
19
+
20
+ def association_model
21
+ reflection.klass
22
+ end
23
+
24
+ private
25
+
26
+ def id
27
+ "#{name}_value"
28
+ end
29
+
30
+ def name
31
+ @name || attribute
32
+ end
33
+
34
+ def attribute
35
+ "#{@association}_count"
36
+ end
37
+
38
+ def label
39
+ @label || I18n.t("attributes.#{attribute}", default: "#{I18n.t("attributes.count")} #{association_model.model_name.human(count: collection? ? 2 : 1)}")
40
+ end
41
+
42
+ def reflection
43
+ form.object.class.reflect_on_association(@association)
44
+ end
45
+
46
+ def collection?
47
+ reflection.collection?
48
+ end
49
+
50
+ def default_base_options
51
+ {
52
+ label: label,
53
+ name: attribute,
54
+ display_values: collection,
55
+ filter: Headmin::Filter::AssociationCount.new(name, @params),
56
+ allowed_operators: Headmin::Filter::AssociationCount::OPERATORS
57
+ }
58
+ end
59
+
60
+ def default_input_options
61
+ {
62
+ label: false,
63
+ wrapper: false,
64
+ name: nil,
65
+ id: id,
66
+ data: {
67
+ action: "change->filter#updateHiddenValue",
68
+ filter_target: "value",
69
+ filter_row_target: "original"
70
+ },
71
+ collection: collection,
72
+ selected: selected,
73
+ class: "form-control"
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end
@@ -100,6 +100,16 @@ module Headmin
100
100
  query ? query.send(instruction[:conditional], new_query) : new_query
101
101
  end
102
102
 
103
+ def is_i?(value)
104
+ # Regex: this selects signed digits (\d) only, it is then checked to the value, e.g.:
105
+ # is_i?("3") = true
106
+ # is_i?("-3") = true
107
+ # is_i?("3a") = false
108
+ # is_i?("3.2") = false
109
+
110
+ /\A[-+]?\d+\z/.match(value)
111
+ end
112
+
103
113
  private
104
114
 
105
115
  def parse(string)
@@ -56,8 +56,15 @@ module Headmin
56
56
  def display_value(value)
57
57
  # This uses the default date format of headmin.
58
58
  # Can be overwritten by setting default date format of the application.
59
- if value.class.to_s == "Range"
59
+
60
+ current_operator = instructions.find { |instruction| instruction[:value] == value }[:operator]
61
+
62
+ # To make the operators eq and not_eq work, we pass a range.
63
+ # However, display value should return this as a date and not a range.
64
+ if value.class.to_s == "Range" && (current_operator == "eq" || current_operator == "not_eq")
60
65
  I18n.l(value.last.to_date)
66
+ elsif values.class.to_s
67
+ "#{I18n.l(value.first.to_date)} - #{I18n.l(value.last.to_date)}"
61
68
  else
62
69
  I18n.l(value.to_date)
63
70
  end
@@ -28,7 +28,7 @@ module Headmin
28
28
  label: label,
29
29
  name: attribute,
30
30
  filter: Headmin::Filter::Date.new(name, @params),
31
- allowed_operators: Headmin::Filter::Date::OPERATORS - %w[in not_in between not_between]
31
+ allowed_operators: Headmin::Filter::Date::OPERATORS - %w[in not_in]
32
32
  }
33
33
  end
34
34
 
@@ -10,18 +10,6 @@ module Headmin
10
10
  def to_s
11
11
  string
12
12
  end
13
-
14
- private
15
-
16
- def is_i?(value)
17
- # Regex: this selects signed digits (\d) only, it is then checked to the value, e.g.:
18
- # is_i?("3") = true
19
- # is_i?("-3") = true
20
- # is_i?("3a") = false
21
- # is_i?("3.2") = false
22
-
23
- /\A[-+]?\d+\z/.match(value)
24
- end
25
13
  end
26
14
  end
27
15
  end
@@ -24,7 +24,9 @@ module Headmin
24
24
  is_null: "&#9675; #{I18n.t("headmin.filters.operators.is_null")}",
25
25
  is_not_null: "&#9679; #{I18n.t("headmin.filters.operators.is_not_null")}",
26
26
  in: "&ni; #{I18n.t("headmin.filters.operators.in")}",
27
- not_in: "&notni; #{I18n.t("headmin.filters.operators.not_in")}"
27
+ not_in: "&notni; #{I18n.t("headmin.filters.operators.not_in")}",
28
+ between: "&harr; #{I18n.t("headmin.filters.operators.between")}",
29
+ not_between: "&harrcir; #{I18n.t("headmin.filters.operators.not_between")}"
28
30
  }
29
31
  end
30
32
  end
@@ -66,7 +66,9 @@ module Headmin
66
66
  def association_object
67
67
  if attached.is_a?(ActiveStorage::Attached::Many)
68
68
  result = form.object.send(nested_attribute)
69
- result = result.order(position: :asc) if sort
69
+
70
+ # We sort the collection with ruby to prevent a new query to be made that would dispose of the objects in memory
71
+ result = result.sort_by { |item| item.position } if sort
70
72
  result
71
73
  else
72
74
  form.object.send(nested_attribute)
@@ -76,7 +78,9 @@ module Headmin
76
78
  def attachments
77
79
  if attached.is_a?(ActiveStorage::Attached::Many)
78
80
  result = form.object.send(nested_attribute)
79
- result = result.order(position: :asc) if sort
81
+
82
+ # We sort the collection with ruby to prevent a new query to be made that would dispose of the objects in memory
83
+ result = result.sort_by { |item| item.position } if sort
80
84
  result.to_a.compact
81
85
  else
82
86
  [form.object.send(nested_attribute)].compact
@@ -14,21 +14,16 @@
14
14
  # <%= render "headmin/filters", url: admin_polls_path %#>
15
15
 
16
16
  action = local_assigns.has_key?(:url) ? url : request.path
17
-
18
- begin
19
- model = controller_name.classify.constantize
20
- rescue
21
- raise "Cannot find class!"
22
- end
17
+ model = controller_name.classify.constantize rescue nil
23
18
  %>
24
19
 
25
- <%= form_with model: model.new, url: action, method: :get, data: {controller: "filters", filters_target: "form"} do |form| %>
20
+ <%= form_with model: model&.new, url: action, method: :get, data: {controller: "filters", filters_target: "form"} do |form| %>
26
21
  <%# Perform yield in order to capture content blocks, pass form so we can use headmin form inputs %>
27
22
  <%= yield(form) %>
28
23
 
29
24
  <!-- Default parameters (e.g. sorting, pagination) -->
30
25
  <% default_params.except("page").each do |name, value| %>
31
- <%= form.hidden_field name.to_sym, value: value %>
26
+ <%= hidden_field_tag(name, value) %>
32
27
  <% end %>
33
28
 
34
29
  <div class="d-flex flex-column flex-md-row align-content-start align-items-md-start">
@@ -58,7 +58,7 @@
58
58
  }
59
59
  %>
60
60
 
61
- <div class="container">
61
+ <div class="container py-4">
62
62
  <%= form_with model: model, scope: scope, url: url, format: format, **options do |form| %>
63
63
  <%= yield(form) %>
64
64
  <% end %>
@@ -8,6 +8,6 @@
8
8
  # <% end %#>
9
9
  %>
10
10
 
11
- <div class="container-fluid">
11
+ <div class="container-fluid py-4">
12
12
  <%= yield %>
13
13
  </div>
@@ -0,0 +1,28 @@
1
+ <%
2
+ # headmin/filters/association_count
3
+ #
4
+ # ==== Required parameters
5
+ # * +association+ - Name of the association that has to be counted
6
+ # * +form+ - Form object
7
+ #
8
+ # ==== Optional parameters
9
+ # * +label+ - Display label
10
+ # * +name+ - Name of the filter parameter
11
+ #
12
+ # ==== Examples
13
+ # Basic version (one-to-many)
14
+ # <%= render "headmin/filters", url: admin_orders_path do |form| %#>
15
+ # <%= render "headmin/filters/association_count", form: form, association: :beverages %#>
16
+ # <% end %#>
17
+ #
18
+ # Basic version (one-to-one)
19
+ # <%= render "headmin/filters", url: admin_orders_path do |form| %#>
20
+ # <%= render "headmin/filters/association_count", form: form, association: :beverage %#>
21
+ # <% end %#>
22
+
23
+ number = Headmin::Filter::AssociationCountView.new(local_assigns.merge(params: params))
24
+ %>
25
+
26
+ <%= render "headmin/filters/base", number.base_options do |value| %>
27
+ <%= render "headmin/forms/number", number.input_options.merge({value: value}) %>
28
+ <% end %>
@@ -20,4 +20,5 @@
20
20
 
21
21
  <%= render "headmin/filters/base", date.base_options do |value| %>
22
22
  <%= render "headmin/forms/date", date.input_options.merge({value: value}) %>
23
+ <%= render "headmin/forms/date_range", date.input_options.merge({start: {value: value.class.to_s == "Range" ? value.first : value}, end: {value: value.class.to_s == "Range" ? value.last : value}}) %>
23
24
  <% end %>
@@ -11,7 +11,6 @@
11
11
  # <%= render "headmin/forms/fields/base", form: form, field_type: :text, name: name do |field, label| %#>
12
12
  # <%= render "headmin/forms/text", form: field, attribute: :value, label: label %#>
13
13
  # <% end %#>
14
-
15
14
  field = form.object.fields.detect { |field| field.name.to_s == name.to_s }
16
15
  field = field ? field : form.object.fields.new(field_type: field_type, name: name)
17
16
  label = t("attributes.#{name.to_s.parameterize}", default: field.name.to_s)
@@ -21,5 +20,6 @@
21
20
  <%= ff.hidden_field :id %>
22
21
  <%= ff.hidden_field :name %>
23
22
  <%= ff.hidden_field :field_type %>
23
+
24
24
  <%= yield ff, label %>
25
25
  <% end %>
@@ -4,6 +4,6 @@
4
4
  parameters: none
5
5
  %>
6
6
 
7
- <div class="content py-4">
7
+ <div class="content">
8
8
  <%= yield %>
9
9
  </div>
@@ -42,6 +42,32 @@
42
42
  <%= render "headmin/forms/text", form: form, attribute: :filename, append: "." + form.object.filename.to_s.rpartition(".").last, value: form.object.filename.to_s.rpartition(".").first %>
43
43
  </div>
44
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 %>
45
71
  </div>
46
72
  <div class="modal-footer">
47
73
  <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t(".close") %></button>
@@ -1,4 +1,4 @@
1
- <div class="media-modal modal fade" tabindex="-1" data-controller="remote-modal media-modal" data-name="<%= name %>" data-min="<%= min %>" data-max="<%= max %>">
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
2
  <div class="modal-dialog modal-lg modal-dialog-scrollable">
3
3
  <div class="modal-content">
4
4
  <div class="modal-header">
@@ -10,15 +10,20 @@
10
10
  <div class="modal-body">
11
11
  <%= turbo_frame_tag "thumbnails", class: "d-flex flex-wrap gap-2" do %>
12
12
  <% @blobs.each do |blob| %>
13
- <%= render "headmin/media/item", blob: blob %>
13
+ <%= turbo_frame_tag blob, src: headmin_media_item_thumbnail_path(blob), loading: "lazy" do %>
14
+ <%= render "thumbnail" %>
15
+ <% end %>
14
16
  <% end %>
15
17
  <div data-media-modal-target="placeholder" class="<%= "d-none" if !@blobs.empty? %>">
16
18
  <p><%= t(".placeholder") %></p>
17
19
  </div>
18
20
  <% end %>
21
+ <div class="mt-3">
22
+ <%= render "headmin/pagination/infinite", items: @blobs %>
23
+ </div>
19
24
  </div>
20
25
  <div class="modal-footer">
21
- <%= form_with url: headmin_media_path, multipart: true, data: {"media-modal-target": "form"}, class: "me-auto" do |form| %>
26
+ <%= form_with url: headmin_new_media_path, multipart: true, data: {"media-modal-target": "form"}, class: "me-auto" do |form| %>
22
27
  <%= form.label :files, class: "btn h-btn-outline-light" do %>
23
28
  <%= bootstrap_icon("upload") %>
24
29
  <%= t(".upload") %>
@@ -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 "headmin/thumbnail", file: blob %>
16
+ </label>
17
+ </div>
18
+ <% else %>
19
+ <%= render "headmin/thumbnail", file: nil %>
20
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <%= turbo_stream.prepend "thumbnails" do %>
2
2
  <% @blobs.each do |blob| %>
3
- <%= render "headmin/media/item", blob: blob %>
3
+ <%= render "headmin/media/thumbnail", blob: blob %>
4
4
  <% end %>
5
5
  <% end %>
@@ -0,0 +1,11 @@
1
+ <%= turbo_stream.append "thumbnails" do %>
2
+ <% @blobs.each do |blob| %>
3
+ <%= turbo_frame_tag blob, src: headmin_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 "headmin/pagination/infinite", items: @blobs %>
11
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_frame_tag @blob do %>
2
+ <%= render "thumbnail", blob: @blob %>
3
+ <% end %>
@@ -5,6 +5,7 @@
5
5
  # * +name</tt> - Name of the nav item
6
6
  # * +url</tt> - URL for this nav item
7
7
  # * +icon</tt> - Optional Bootstrap icon name
8
+ # * +locked</tt> - Item becomes unclickable and a lock icon appears at the end of the item
8
9
  # * +active</tt> - Set to true if this nav item needs to be highlighted
9
10
  #
10
11
  # ==== Examples
@@ -17,14 +18,18 @@
17
18
  name = local_assigns.has_key?(:name) ? name : ""
18
19
  icon = local_assigns.has_key?(:icon) ? icon : nil
19
20
  url = local_assigns.has_key?(:url) ? url : request.url
21
+ locked = local_assigns.has_key?(:locked) ? locked : false
20
22
  active = local_assigns.has_key?(:active) ? active : current_url?(url)
21
23
  %>
22
24
 
23
25
  <li class="nav-item">
24
- <a class="nav-link d-flex align-items-center <%= "active" if active %>" aria-current="page" href="<%= url %>">
26
+ <a class="nav-link d-flex align-items-center <%= "active" if active %> <%= "disabled" if locked %>" aria-current="page" href="<%= url %>">
25
27
  <%= bootstrap_icon(icon, class: "me-2") if icon %>
26
28
  <span class="d-block d-md-none d-lg-block">
27
29
  <%= name %>
28
30
  </span>
31
+ <% if locked %>
32
+ <%= bootstrap_icon("lock", class: "fs-6 ms-2") %>
33
+ <% end %>
29
34
  </a>
30
35
  </li>
@@ -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,3 @@
1
+ Rails.configuration.to_prepare do
2
+ ActiveStorage::Attachment.include Headmin::Attachment
3
+ end
@@ -33,6 +33,8 @@ en:
33
33
  is_null: is null
34
34
  in: includes
35
35
  not_in: does not include
36
+ between: between
37
+ not_between: not between
36
38
  values:
37
39
  "false": "False"
38
40
  "no": "No"
@@ -33,6 +33,8 @@ nl:
33
33
  is_null: is niet ingevuld
34
34
  in: bevat
35
35
  not_in: bevat niet
36
+ between: tussen
37
+ not_between: niet tussen
36
38
  values:
37
39
  "false": Onwaar
38
40
  "no": Nee
@@ -13,6 +13,7 @@ en:
13
13
  unlimited: unlimited
14
14
  upload: Upload new files
15
15
  media_item_modal:
16
+ attachment_used_by: Used by
16
17
  close: Close
17
18
  dimensions: Dimensions
18
19
  edit: Edit Media item
@@ -13,6 +13,7 @@ nl:
13
13
  unlimited: onbeperkt
14
14
  upload: Nieuwe bestanden uploaden
15
15
  media_item_modal:
16
+ attachment_used_by: Gebruikt door
16
17
  close: Sluiten
17
18
  dimensions: Afmetingen
18
19
  edit: Media item wijzigen
@@ -1,6 +1,8 @@
1
1
  en:
2
2
  headmin:
3
3
  pagination:
4
+ infinite:
5
+ load_more: Load more
4
6
  per_page:
5
7
  title: items per page
6
8
  items:
@@ -1,6 +1,8 @@
1
1
  nl:
2
2
  headmin:
3
3
  pagination:
4
+ infinite:
5
+ load_more: Meer laden
4
6
  per_page:
5
7
  title: items per pagina
6
8
  items:
data/config/routes.rb CHANGED
@@ -3,8 +3,9 @@
3
3
  Rails.application.routes.draw do
4
4
  namespace(:headmin) do
5
5
  get "media", to: "media#index", as: :media
6
- post "media", to: "media#create"
6
+ post "media", to: "media#create", as: :new_media
7
7
  get "media/:id", to: "media#show", as: :media_item
8
8
  patch "media/:id", to: "media#update"
9
+ get "media/thumbnail/:id", to: "media#thumbnail", as: :media_item_thumbnail
9
10
  end
10
11
  end
@@ -1,3 +1,3 @@
1
1
  module Headmin
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.2"
3
3
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "headmin",
3
- "version": "0.5.9",
3
+ "version": "0.6.1",
4
4
  "description": "Admin component library",
5
5
  "module": "app/assets/javascripts/headmin.js",
6
6
  "main": "app/assets/javascripts/headmin.js",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: headmin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jef Vlamings
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-09 00:00:00.000000000 Z
11
+ date: 2023-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: closure_tree
@@ -105,6 +105,7 @@ files:
105
105
  - app/assets/javascripts/headmin/controllers/filters_controller.js
106
106
  - app/assets/javascripts/headmin/controllers/flatpickr_controller.js
107
107
  - app/assets/javascripts/headmin/controllers/hello_controller.js
108
+ - app/assets/javascripts/headmin/controllers/infinite_scroller_controller.js
108
109
  - app/assets/javascripts/headmin/controllers/media_controller.js
109
110
  - app/assets/javascripts/headmin/controllers/media_modal_controller.js
110
111
  - app/assets/javascripts/headmin/controllers/notification_controller.js
@@ -244,6 +245,7 @@ files:
244
245
  - app/helpers/headmin/form_helper.rb
245
246
  - app/helpers/headmin/notification_helper.rb
246
247
  - app/helpers/headmin/request_helper.rb
248
+ - app/models/concerns/headmin/attachment.rb
247
249
  - app/models/concerns/headmin/blob.rb
248
250
  - app/models/concerns/headmin/block.rb
249
251
  - app/models/concerns/headmin/blockable.rb
@@ -260,6 +262,8 @@ files:
260
262
  - app/models/headmin/.DS_Store
261
263
  - app/models/headmin/blocks_view.rb
262
264
  - app/models/headmin/filter/association.rb
265
+ - app/models/headmin/filter/association_count.rb
266
+ - app/models/headmin/filter/association_count_view.rb
263
267
  - app/models/headmin/filter/association_view.rb
264
268
  - app/models/headmin/filter/base.rb
265
269
  - app/models/headmin/filter/base_view.rb
@@ -337,6 +341,7 @@ files:
337
341
  - app/views/headmin/dropdown/_list.html.erb
338
342
  - app/views/headmin/dropdown/_locale.html.erb
339
343
  - app/views/headmin/filters/_association.html.erb
344
+ - app/views/headmin/filters/_association_count.html.erb
340
345
  - app/views/headmin/filters/_base.html.erb
341
346
  - app/views/headmin/filters/_boolean.html.erb
342
347
  - app/views/headmin/filters/_date.html.erb
@@ -407,17 +412,20 @@ files:
407
412
  - app/views/headmin/layout/_sidebar.html.erb
408
413
  - app/views/headmin/layout/sidebar/_bottom.html.erb
409
414
  - app/views/headmin/layout/sidebar/_nav.html.erb
410
- - app/views/headmin/media/_item.html.erb
411
415
  - app/views/headmin/media/_media_item_modal.html.erb
412
416
  - app/views/headmin/media/_modal.html.erb
417
+ - app/views/headmin/media/_thumbnail.html.erb
413
418
  - app/views/headmin/media/create.turbo_stream.erb
414
419
  - app/views/headmin/media/index.html.erb
420
+ - app/views/headmin/media/index.turbo_stream.erb
415
421
  - app/views/headmin/media/show.html.erb
422
+ - app/views/headmin/media/thumbnail.html.erb
416
423
  - app/views/headmin/media/update.turbo_stream.erb
417
424
  - app/views/headmin/nav/_dropdown.html.erb
418
425
  - app/views/headmin/nav/_item.html.erb
419
426
  - app/views/headmin/nav/item/_devise.html.erb
420
427
  - app/views/headmin/nav/item/_locale.html.erb
428
+ - app/views/headmin/pagination/_infinite.html.erb
421
429
  - app/views/headmin/pagination/_per_page.html.erb
422
430
  - app/views/headmin/pagination/kaminari/_first_page.html.erb
423
431
  - app/views/headmin/pagination/kaminari/_gap.html.erb
@@ -468,6 +476,7 @@ files:
468
476
  - bin/setup
469
477
  - config/importmap.rb
470
478
  - config/initializers/customize_input_error.rb
479
+ - config/initializers/extend_active_storage_attachment.rb
471
480
  - config/initializers/extend_active_storage_blob.rb
472
481
  - config/locales/activerecord/en.yml
473
482
  - config/locales/activerecord/nl.yml