headmin 0.5.0 → 0.5.3

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 (113) 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 +10 -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/repeater.scss +4 -0
  14. data/app/assets/stylesheets/headmin/forms.scss +7 -0
  15. data/app/assets/stylesheets/headmin/layout/sidebar.scss +0 -1
  16. data/app/assets/stylesheets/headmin/media/index.scss +9 -0
  17. data/app/assets/stylesheets/headmin/media.scss +1 -0
  18. data/app/assets/stylesheets/headmin/table.scss +15 -0
  19. data/app/assets/stylesheets/headmin.css +70 -7
  20. data/app/assets/stylesheets/headmin.scss +1 -0
  21. data/app/controllers/headmin/media_controller.rb +52 -0
  22. data/app/controllers/headmin_controller.rb +2 -0
  23. data/app/helpers/headmin/form_helper.rb +2 -2
  24. data/app/models/concerns/headmin/field.rb +1 -1
  25. data/app/models/concerns/headmin/fieldable.rb +19 -10
  26. data/app/models/concerns/headmin/form/hintable.rb +6 -1
  27. data/app/models/headmin/blocks_view.rb +1 -1
  28. data/app/models/headmin/filter/date.rb +49 -1
  29. data/app/models/headmin/form/color_view.rb +48 -0
  30. data/app/models/headmin/form/media_view.rb +113 -0
  31. data/app/models/headmin/form/textarea_view.rb +6 -1
  32. data/app/views/examples/admin.html.erb +8 -8
  33. data/app/views/examples/auth.html.erb +2 -2
  34. data/app/views/headmin/_blocks.html.erb +1 -1
  35. data/app/views/headmin/_breadcrumbs.html.erb +2 -2
  36. data/app/views/headmin/_dropdown.html.erb +1 -1
  37. data/app/views/headmin/_filters.html.erb +5 -5
  38. data/app/views/headmin/_pagination.html.erb +2 -2
  39. data/app/views/headmin/_popup.html.erb +4 -4
  40. data/app/views/headmin/_table.html.erb +1 -1
  41. data/app/views/headmin/_thumbnail.html.erb +1 -1
  42. data/app/views/headmin/dropdown/_button.html.erb +2 -2
  43. data/app/views/headmin/dropdown/_item.html.erb +2 -2
  44. data/app/views/headmin/dropdown/_list.html.erb +3 -3
  45. data/app/views/headmin/dropdown/_locale.html.erb +5 -5
  46. data/app/views/headmin/filters/filter/_button.html.erb +2 -2
  47. data/app/views/headmin/filters/filter/_null_select.html.erb +2 -2
  48. data/app/views/headmin/forms/_color.html.erb +32 -0
  49. data/app/views/headmin/forms/_errors.html.erb +1 -1
  50. data/app/views/headmin/forms/_file.html.erb +7 -7
  51. data/app/views/headmin/forms/_hint.html.erb +6 -1
  52. data/app/views/headmin/forms/_media.html.erb +58 -0
  53. data/app/views/headmin/forms/_repeater.html.erb +9 -9
  54. data/app/views/headmin/forms/_textarea.html.erb +1 -1
  55. data/app/views/headmin/forms/media/_item.html.erb +32 -0
  56. data/app/views/headmin/forms/media/_validation.html.erb +10 -0
  57. data/app/views/headmin/forms/repeater/_row.html.erb +15 -14
  58. data/app/views/headmin/heading/_title.html.erb +2 -2
  59. data/app/views/headmin/layout/_main.html.erb +2 -0
  60. data/app/views/headmin/layout/_remote_modal.html.erb +1 -0
  61. data/app/views/headmin/layout/_sidebar.html.erb +1 -1
  62. data/app/views/headmin/media/_item.html.erb +17 -0
  63. data/app/views/headmin/media/_media_item_modal.html.erb +51 -0
  64. data/app/views/headmin/media/_modal.html.erb +35 -0
  65. data/app/views/headmin/media/create.turbo_stream.erb +5 -0
  66. data/app/views/headmin/media/index.html.erb +3 -0
  67. data/app/views/headmin/media/show.html.erb +9 -0
  68. data/app/views/headmin/media/update.turbo_stream.erb +3 -0
  69. data/app/views/headmin/nav/_dropdown.html.erb +7 -7
  70. data/app/views/headmin/nav/_item.html.erb +5 -5
  71. data/app/views/headmin/nav/item/_locale.html.erb +6 -6
  72. data/app/views/headmin/pagination/_per_page.html.erb +7 -7
  73. data/app/views/headmin/pagination/kaminari/_first_page.html.erb +2 -2
  74. data/app/views/headmin/pagination/kaminari/_gap.html.erb +1 -1
  75. data/app/views/headmin/pagination/kaminari/_last_page.html.erb +2 -2
  76. data/app/views/headmin/pagination/kaminari/_next_page.html.erb +3 -3
  77. data/app/views/headmin/pagination/kaminari/_page.html.erb +2 -2
  78. data/app/views/headmin/pagination/kaminari/_paginator.html.erb +1 -1
  79. data/app/views/headmin/pagination/kaminari/_prev_page.html.erb +2 -2
  80. data/app/views/headmin/table/_actions.html.erb +9 -9
  81. data/app/views/headmin/table/_body.html.erb +1 -1
  82. data/app/views/headmin/table/actions/_action.html.erb +4 -4
  83. data/app/views/headmin/table/actions/_export.html.erb +1 -1
  84. data/app/views/headmin/table/body/_association.html.erb +1 -1
  85. data/app/views/headmin/table/body/_boolean.erb +4 -4
  86. data/app/views/headmin/table/body/_color.html.erb +10 -0
  87. data/app/views/headmin/table/body/_date.html.erb +2 -2
  88. data/app/views/headmin/table/body/_image.html.erb +18 -0
  89. data/app/views/headmin/table/body/_string.html.erb +1 -1
  90. data/app/views/headmin/table/head/_cell.html.erb +1 -1
  91. data/app/views/headmin/table/head/cell/_asc.html.erb +2 -2
  92. data/app/views/headmin/table/head/cell/_default.html.erb +1 -1
  93. data/app/views/headmin/table/head/cell/_desc.html.erb +1 -1
  94. data/app/views/headmin/views/devise/confirmations/_new.html.erb +1 -1
  95. data/app/views/headmin/views/devise/passwords/_edit.html.erb +1 -1
  96. data/app/views/headmin/views/devise/passwords/_new.html.erb +1 -1
  97. data/app/views/headmin/views/devise/registrations/_edit.html.erb +5 -5
  98. data/app/views/headmin/views/devise/registrations/_new.html.erb +1 -1
  99. data/app/views/headmin/views/devise/sessions/_new.html.erb +1 -1
  100. data/app/views/headmin/views/devise/shared/_links.html.erb +11 -11
  101. data/app/views/headmin/views/devise/unlocks/_new.html.erb +1 -1
  102. data/config/locales/devise/nl.yml +1 -1
  103. data/config/locales/headmin/forms/en.yml +8 -0
  104. data/config/locales/headmin/forms/nl.yml +8 -0
  105. data/config/locales/headmin/media/en.yml +23 -0
  106. data/config/locales/headmin/media/nl.yml +22 -0
  107. data/config/locales/headmin/table/en.yml +2 -0
  108. data/config/locales/headmin/table/nl.yml +2 -0
  109. data/config/routes.rb +10 -0
  110. data/lib/generators/templates/views/layouts/auth.html.erb +2 -2
  111. data/lib/headmin/version.rb +1 -1
  112. data/package.json +1 -1
  113. metadata +30 -2
@@ -48,13 +48,13 @@
48
48
  src = attachment.image? ? url_for(attachment.variant(resize_to_fill: [file.thumbnail_width, file.thumbnail_height])) : url_for(attachment)
49
49
  %>
50
50
  <div class="h-form-file-thumbnail" title="<%= "#{filename} (#{size})" %>" data-file-preview-target="thumbnail">
51
- <%= ff.hidden_field(:id, disabled: file.destroy) %>
52
- <%= ff.hidden_field(:_destroy, data: {'file-preview-target': 'thumbnailDestroy'}, disabled: file.destroy) %>
51
+ <%= ff.hidden_field(:id, disabled: !file.destroy) %>
52
+ <%= ff.hidden_field(:_destroy, data: {"file-preview-target": "thumbnailDestroy"}, disabled: !file.destroy) %>
53
53
  <%= render "headmin/thumbnail", src: src, width: file.thumbnail_width, height: file.thumbnail_height %>
54
54
 
55
55
  <% if file.destroy %>
56
56
  <div class="h-form-file-thumbnail-remove" data-action="click->file-preview#remove" data-file-preview-name-param="<%= filename %>">
57
- <%= bootstrap_icon('x') %>
57
+ <%= bootstrap_icon("x") %>
58
58
  </div>
59
59
  <% end %>
60
60
  </div>
@@ -62,11 +62,11 @@
62
62
 
63
63
  <!-- Placeholder -->
64
64
  <% if file.dropzone %>
65
- <div class="h-dropzone-placeholder <%= 'd-none' if file.attachments.any? %>" data-file-preview-target="placeholder" style="height: <%= file.thumbnail_height %>px;">
66
- <%= t('headmin.forms.file.placeholder', count: file.number_of_files) %>
65
+ <div class="h-dropzone-placeholder <%= "d-none" if file.attachments.any? %>" data-file-preview-target="placeholder" style="height: <%= file.thumbnail_height %>px;">
66
+ <%= t("headmin.forms.file.placeholder", count: file.number_of_files) %>
67
67
  </div>
68
68
  <% else %>
69
- <div class="h-form-file-thumbnail <%= 'd-none' if file.attachments.any? %>" title="<%= t('headmin.forms.file.not_found') %>" data-file-preview-target="placeholder">
69
+ <div class="h-form-file-thumbnail <%= "d-none" if file.attachments.any? %>" title="<%= t("headmin.forms.file.not_found") %>" data-file-preview-target="placeholder">
70
70
  <%= render "headmin/thumbnail", width: file.thumbnail_width, height: file.thumbnail_height, icon: "plus" %>
71
71
  </div>
72
72
  <% end %>
@@ -80,7 +80,7 @@
80
80
 
81
81
  <% if file.destroy %>
82
82
  <div class="h-form-file-thumbnail-remove" data-action="click->file-preview#remove">
83
- <%= bootstrap_icon('x') %>
83
+ <%= bootstrap_icon("x") %>
84
84
  </div>
85
85
  <% end %>
86
86
  </div>
@@ -13,4 +13,9 @@
13
13
  hint = Headmin::Form::HintView.new(local_assigns)
14
14
  %>
15
15
 
16
- <div class="form-text"><%= raw(hint.content) %></div>
16
+ <div class="form-text d-flex justify-content-between">
17
+ <div><%= raw(hint.content) %></div>
18
+ <% if hint.maxlength %>
19
+ <div data-textarea-target="count"></div>
20
+ <% end %>
21
+ </div>
@@ -0,0 +1,58 @@
1
+ <%
2
+ # headmin/forms/file
3
+ #
4
+ # ==== Required parameters
5
+ # * +attribute+ - Name of the attribute of the form model
6
+ # * +form+ - Form object
7
+ #
8
+ # ==== Optional parameters
9
+ # * +destroy+ - Adds delete buttons to the preview thumbnails
10
+ # * +hint+ - Informative text to assist with data input. HTML markup is allowed.
11
+ # * +label+ - Text to display inside label tag. Defaults to the attribute name. Set to false if you don"t want to show a label.
12
+ # * +min+ - Limit the selection to a minimum amount of items.
13
+ # * +max+ - Limit the selection to a maximum amount of items.
14
+ # * +sort+ - Allow sorting by dragging items. `active_storage_attachments` must have a position column.
15
+ # * +wrapper+ - Hash with all options for the surrounding html tag
16
+ #
17
+ # ==== References
18
+ # https://headmin.dev/docs/forms/media
19
+ #
20
+ # ==== Examples
21
+ # Basic version
22
+ # <%= form_with do |form| %#>
23
+ # <%= render "headmin/forms/media", form: form, attribute: :file %#>
24
+ # <% end %#>
25
+
26
+ media = Headmin::Form::MediaView.new(local_assigns)
27
+ %>
28
+
29
+ <%= render "headmin/forms/wrapper", media.wrapper_options do %>
30
+ <%= render "headmin/forms/label", media.label_options if media.prepend_label? %>
31
+ <div class="h-form-file-thumbnails" data-media-target="thumbnails">
32
+ <%= render "headmin/forms/media/validation", media.custom_validation_options %>
33
+
34
+ <!-- Render previews for attachments -->
35
+ <%= form.fields_for(media.nested_attribute, media.association_object) do |ff| %>
36
+ <%= render "headmin/forms/media/item", form: ff, url: media.media_modal_url, sort: media.sort %>
37
+ <% end %>
38
+
39
+ <!-- Placeholder -->
40
+ <div class="<%= "d-none" if media.attachments.any? %>" data-media-target="placeholder">
41
+ <a href="<%= media.media_modal_url %>" data-turbo-frame="remote_modal" data-media-target="modalButton">
42
+ <%= render "headmin/thumbnail", src: nil, width: 100, height: 100, icon: "plus" %>
43
+ </a>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- Template -->
48
+ <% association_object = ActiveStorage::Attachment.new %>
49
+ <template data-media-target="template" data-template-id-regex="<%= association_object.object_id %>">
50
+ <%= form.fields_for(media.nested_attribute, ActiveStorage::Attachment.new, child_index: association_object.object_id) do |ff| %>
51
+ <%= render "headmin/forms/media/item", form: ff, url: media.media_modal_url, sort: media.sort %>
52
+ <% end %>
53
+ </template>
54
+
55
+ <%= render "headmin/forms/validation", media.validation_options if media.validate? %>
56
+ <%= render "headmin/forms/hint", media.hint_options if media.hint? %>
57
+ <%= render "headmin/forms/label", media.label_options if media.append_label? %>
58
+ <% end %>
@@ -21,12 +21,12 @@
21
21
  # <% end %#>
22
22
  #
23
23
  # # With fixed header row. A header row can be used to show the labels, so you can omit them in the repeated fields
24
- # <% render "headmin/forms/repeater", form: form, attribute: :questions, header: 'admin/questions/header' do |question| %#>
24
+ # <% render "headmin/forms/repeater", form: form, attribute: :questions, header: "admin/questions/header" do |question| %#>
25
25
  # <% render "admin/questions/fields, form: :question" %#>
26
26
  # <% end %#>
27
27
  #
28
28
  # # Allow more than one type of fields to be inserted. You must specify the templates as an array of view paths
29
- # <% templates = ['admin/questions/fields/type_1', 'admin/questions/fields/type_2'] %#>
29
+ # <% templates = ["admin/questions/fields/type_1", "admin/questions/fields/type_2"] %#>
30
30
  # <% render "headmin/forms/repeater", form: form, attribute: :questions, templates: templates do |question| %#>
31
31
  # <% render "admin/questions/fields, form: :question" %#>
32
32
  # <% end %#>
@@ -37,12 +37,12 @@
37
37
  templates = local_assigns.has_key?(:templates) ? templates : []
38
38
  flush = local_assigns.has_key?(:flush) ? flush : true
39
39
 
40
- template_names = templates.map { |template| File.basename(template, '.html.erb') }
41
- template_names = template_names.any? ? template_names : ['new']
40
+ template_names = templates.map { |template| File.basename(template, ".html.erb") }
41
+ template_names = template_names.any? ? template_names : ["new"]
42
42
  object_model = form.object.class
43
43
  association_model = object_model.reflect_on_association(attribute).class_name.constantize
44
44
  association_object = association_model.new
45
- with_positions = association_object.attributes.keys.include?('position')
45
+ with_positions = association_object.attributes.keys.include?("position")
46
46
  associations = form.object.send(attribute)
47
47
  associations = with_positions ? associations.order(:position) : associations
48
48
  repeater_id = form.object_id
@@ -55,7 +55,7 @@
55
55
  <%= render "headmin/forms/label", form: form, attribute: attribute, text: label, required: required %>
56
56
  <% end %>
57
57
 
58
- <ul class="repeater list-group <%= 'list-group-flush' if flush %>" data-controller="repeater" data-repeater-target="list" data-repeater-id-value="<%= repeater_id %>">
58
+ <ul class="repeater list-group <%= "list-group-flush" if flush %>" data-controller="repeater" data-repeater-target="list" data-repeater-id-value="<%= repeater_id %>">
59
59
 
60
60
  <!-- Header -->
61
61
  <% if header %>
@@ -76,7 +76,7 @@
76
76
 
77
77
  <!-- Empty notice -->
78
78
  <div class="text-secondary invisible" data-repeater-target="empty">
79
- <%= t('.empty') %>
79
+ <%= t(".empty") %>
80
80
  </div>
81
81
 
82
82
  <!-- Button -->
@@ -88,8 +88,8 @@
88
88
  data-popup-pass-thru="<%= pass_thru %>"
89
89
  data-action="click->repeater#resetButtonIndices click->popup#open"
90
90
  >
91
- <%= bootstrap_icon('plus') %>
92
- <%= t('.add', name: association_model.model_name.human) %>
91
+ <%= bootstrap_icon("plus") %>
92
+ <%= t(".add", name: association_model.model_name.human) %>
93
93
  </div>
94
94
 
95
95
  <!-- Popup -->
@@ -34,6 +34,6 @@
34
34
  <%= form.text_area(textarea.attribute, textarea.input_options) %>
35
35
  <% end %>
36
36
  <%= render "headmin/forms/validation", textarea.validation_options if textarea.validate? %>
37
- <%= render "headmin/forms/hint", textarea.hint_options if textarea.hint? %>
37
+ <%= render "headmin/forms/hint", textarea.hint_options if textarea.hint? || textarea.maxlength? %>
38
38
  <%= render "headmin/forms/label", textarea.label_options if textarea.append_label? %>
39
39
  <% end %>
@@ -0,0 +1,32 @@
1
+ <%
2
+ attachment = form.object
3
+ filename = attachment.blob&.filename&.to_s
4
+ size = number_to_human_size(attachment.blob&.byte_size || 0)
5
+ src = nil
6
+ if attachment.blob
7
+ src = attachment.image? ? url_for(attachment.variant(resize_to_fill: [100, 100])) : url_for(attachment)
8
+ end
9
+ %>
10
+
11
+ <div class="h-form-file-thumbnail media-drag-sort-handle" title="<%= "#{filename} (#{size})" %>" data-media-target="item">
12
+ <%= form.hidden_field(:id) %>
13
+ <%= form.hidden_field(:blob_id) %>
14
+ <%= form.hidden_field(:position, value: attachment.new_record? ? nil : attachment.position) if sort %>
15
+ <%= form.hidden_field(:_destroy) %>
16
+
17
+ <a href="<%= url %>" data-turbo-frame="remote_modal" data-media-target="modalButton">
18
+ <%= render "headmin/thumbnail", src: src, width: 100, height: 100 %>
19
+ </a>
20
+
21
+ <div class="h-form-file-thumbnail-actions">
22
+ <!-- Edit -->
23
+ <a href="<%= headmin_media_item_url(id: attachment.blob ? attachment.blob.id : "$1") %>" class="h-form-file-thumbnail-edit" data-turbo-frame="remote_modal" data-media-target="editButton">
24
+ <%= bootstrap_icon("pencil") %>
25
+ </a>
26
+
27
+ <!-- Remove -->
28
+ <div class="h-form-file-thumbnail-remove" data-action="click->media#destroy">
29
+ <%= bootstrap_icon("x") %>
30
+ </div>
31
+ </div>
32
+ </div>
@@ -0,0 +1,10 @@
1
+ <!-- Custom validation field -->
2
+ <%= form.text_field :"validation_#{attribute}",
3
+ name: nil,
4
+ value: nil,
5
+ class: "h-form-media-validation",
6
+ data: {
7
+ "media-target": "validationInput",
8
+ "min-message": t(".min", count: min),
9
+ "max-message": t(".max", count: max),
10
+ } %>
@@ -18,34 +18,35 @@
18
18
  <%= form.hidden_field :id %>
19
19
  <%= form.hidden_field :_destroy if destroyable %>
20
20
  <%= form.hidden_field :position if draggable %>
21
- <%= yield %>
22
21
 
23
22
  <!-- Drag handle -->
24
23
  <% if draggable %>
25
24
  <div class="repeater-row-handle">
26
- <%= bootstrap_icon('grip-vertical') %>
25
+ <%= bootstrap_icon("grip-vertical") %>
27
26
  </div>
28
27
  <% end %>
29
28
 
30
29
  <!-- Add button-->
31
30
  <div
32
- class="repeater-row-add btn btn-link"
33
- title="<%= t('.add') %>"
34
- data-repeater-target="addButton"
35
- data-popup-target="button"
36
- data-popup-id="<%= "repeater-buttons-#{repeater_id}" %>"
37
- data-popup-pass-thru="<%= pass_thru %>"
38
- data-action="click->repeater#resetButtonIndices click->popup#open"
31
+ class="repeater-row-add btn btn-link"
32
+ title="<%= t(".add") %>"
33
+ data-repeater-target="addButton"
34
+ data-popup-target="button"
35
+ data-popup-id="<%= "repeater-buttons-#{repeater_id}" %>"
36
+ data-popup-pass-thru="<%= pass_thru %>"
37
+ data-action="click->repeater#resetButtonIndices click->popup#open"
39
38
  >
40
- <%= bootstrap_icon('plus-circle') %>
39
+ <%= bootstrap_icon("plus-circle") %>
41
40
  </div>
42
41
 
43
42
  <!-- Remove button-->
44
43
  <div
45
- class="repeater-row-remove btn btn-link"
46
- title="<%= t('.remove') %>"
47
- data-action="click->repeater#removeRow"
44
+ class="repeater-row-remove btn btn-link"
45
+ title="<%= t(".remove") %>"
46
+ data-action="click->repeater#removeRow"
48
47
  >
49
- <%= bootstrap_icon('dash-circle') %>
48
+ <%= bootstrap_icon("dash-circle") %>
50
49
  </div>
50
+
51
+ <%= yield %>
51
52
  </div>
@@ -13,11 +13,11 @@
13
13
  <div class="h-heading-title">
14
14
  <div class="d-flex align-items-center">
15
15
  <h1 class="me-2">
16
- <%= title ? title : t('.new') %>
16
+ <%= title ? title : t(".new") %>
17
17
  </h1>
18
18
  <% if new_link %>
19
19
  <a href="<%= new_link %>" class="btn btn-outline-primary btn-sm">
20
- <%= t('.new') %>
20
+ <%= t(".new") %>
21
21
  </a>
22
22
  <% end %>
23
23
  </div>
@@ -10,6 +10,8 @@
10
10
  class_names = local_assigns.has_key?(:class) ? local_assigns[:class] : false
11
11
  %>
12
12
 
13
+ <%= render "headmin/layout/remote_modal" %>
14
+
13
15
  <div class="main <%= class_names %>" data-controller="popup">
14
16
  <div class="container-fluid">
15
17
  <div class="row">
@@ -0,0 +1 @@
1
+ <%= turbo_frame_tag "remote_modal", target: "_top" %>
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div class="sidebar col-sm-12 col-md-1 col-lg-2 bg-dark overflow-y d-print-none">
9
9
  <nav class="navbar navbar-expand-md navbar-dark bg-dark w-100 h-100 flex-md-column">
10
- <a href="<%= local_assigns[:url] %>" class="nav-brand mb-lg-4 ms-lg-3 me-lg-auto">
10
+ <a href="<%= local_assigns[:url] %>" class="nav-brand mb-4 mt-3 me-lg-auto">
11
11
  <%= local_assigns[:logo] %>
12
12
  </a>
13
13
  <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar-nav" aria-controls="sidebar-nav" aria-expanded="false" aria-label="Toggle navigation">
@@ -0,0 +1,17 @@
1
+ <div data-media-modal-target="item" title="<%= "#{blob.filename} (#{l(blob.created_at, format: :long)})" %>">
2
+ <!-- Input -->
3
+ <input
4
+ id="media-item-<%= blob.id %>"
5
+ type="checkbox"
6
+ value="<%= blob.id %>"
7
+ <%= "checked" if params[:ids]&.include?(blob.id.to_s) %>
8
+ data-action="change->media-modal#inputChange"
9
+ data-media-modal-target="idCheckbox"
10
+ hidden>
11
+
12
+ <!-- Label -->
13
+ <label for="media-item-<%= blob.id %>">
14
+ <% src = blob.image? ? url_for(blob.variant(resize_to_fill: [100, 100])) : url_for(blob) %>
15
+ <%= render "headmin/thumbnail", src: src, width: 100, height: 100 %>
16
+ </label>
17
+ </div>
@@ -0,0 +1,51 @@
1
+ <%= turbo_frame_tag "modal_content" do %>
2
+ <%= form_with url: headmin_media_item_url(id: @blob.id), model: @blob do |form| %>
3
+ <div class="modal-header">
4
+ <h5 class="modal-title"><%= t(".edit") %></h5>
5
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<%= t(".close") %>"></button>
6
+ </div>
7
+ <div class="modal-body">
8
+ <%= render "headmin/notifications" %>
9
+ <div class="row">
10
+ <div class="col-3">
11
+ <%= render "headmin/thumbnail", src: url_for(@blob), width: 100, height: 100 %>
12
+ </div>
13
+ <div class="col-8 d-flex flex-column justify-content-center">
14
+ <p class="small text-secondary m-0">
15
+ <strong><%= t(".uploaded_at") %>:</strong>
16
+ <%= l(@blob.created_at, format: :long) %>
17
+ </p>
18
+ <p class="small text-secondary m-0">
19
+ <strong><%= t(".type") %>:</strong>
20
+ <%= @blob.content_type %>
21
+ </p>
22
+ <p class="small text-secondary m-0">
23
+ <strong><%= t(".dimensions") %>:</strong>
24
+ <% if @blob.metadata[:width] && @blob.metadata[:height] %>
25
+ <%= "#{@blob.metadata[:width]}px x #{@blob.metadata[:height]}px" %>
26
+ <% else %>
27
+ <%= t(".not_analysed") %>
28
+ <% end %>
29
+ </p>
30
+ <p class="small text-secondary m-0">
31
+ <strong><%= t(".size") %>:</strong>
32
+ <% if @blob.byte_size > 0 %>
33
+ <%= number_to_human_size(@blob.byte_size) %>
34
+ <% else %>
35
+ <%= t(".not_analysed") %>
36
+ <% end %>
37
+ </p>
38
+ </div>
39
+ </div>
40
+ <div class="row mt-3">
41
+ <div class="col-12">
42
+ <%= 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
+ </div>
44
+ </div>
45
+ </div>
46
+ <div class="modal-footer">
47
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t(".close") %></button>
48
+ <%= form.submit t(".update"), class: "btn btn-primary" %>
49
+ </div>
50
+ <% end %>
51
+ <% end %>
@@ -0,0 +1,35 @@
1
+ <div class="media-modal modal fade" tabindex="-1" data-controller="remote-modal media-modal" data-name="<%= name %>" data-min="<%= min %>" data-max="<%= max %>">
2
+ <div class="modal-dialog modal-lg modal-dialog-scrollable">
3
+ <div class="modal-content">
4
+ <div class="modal-header">
5
+ <h5 class="modal-title">
6
+ <%= t(".title", count: min.to_i < 1 ? 1 : min.to_i) %>
7
+ </h5>
8
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<%= t(".close") %>"></button>
9
+ </div>
10
+ <div class="modal-body">
11
+ <%= turbo_frame_tag "thumbnails", class: "d-flex flex-wrap gap-2" do %>
12
+ <% @blobs.each do |blob| %>
13
+ <%= render "headmin/media/item", blob: blob %>
14
+ <% end %>
15
+ <div data-media-modal-target="placeholder" class="<%= "d-none" if !@blobs.empty? %>">
16
+ <p><%= t(".placeholder") %></p>
17
+ </div>
18
+ <% end %>
19
+ </div>
20
+ <div class="modal-footer">
21
+ <%= form_with url: headmin_media_path, multipart: true, data: {"media-modal-target": "form"}, class: "me-auto" do |form| %>
22
+ <%= form.label :files, class: "btn h-btn-outline-light" do %>
23
+ <%= bootstrap_icon("upload") %>
24
+ <%= t(".upload") %>
25
+ <%= form.file_field :files, class: "d-none", multiple: true, data: {action: "change->media-modal#submitForm"} %>
26
+ <% end %>
27
+ <% end %>
28
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t(".close") %></button>
29
+ <button type="button" class="btn btn-primary" data-bs-dismiss="modal" data-action="click->media-modal#select" data-media-modal-target="selectButton">
30
+ <%= t(".select") %> (<span data-media-modal-target="count">0</span><%= t(".maximum", count: max.to_i) if max.present? %>)
31
+ </button>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
@@ -0,0 +1,5 @@
1
+ <%= turbo_stream.prepend "thumbnails" do %>
2
+ <% @blobs.each do |blob| %>
3
+ <%= render "headmin/media/item", blob: blob %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_frame_tag "remote_modal" do %>
2
+ <%= render "headmin/media/modal", blobs: @blobs, name: params[:name], min: params[:min], max: params[:max] %>
3
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= turbo_frame_tag "remote_modal" do %>
2
+ <div class="media-item-modal modal fade" tabindex="-1" data-controller="remote-modal">
3
+ <div class="modal-dialog modal-md modal-dialog-scrollable">
4
+ <div class="modal-content">
5
+ <%= render "headmin/media/media_item_modal" %>
6
+ </div>
7
+ </div>
8
+ </div>
9
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_stream.update "modal_content" do %>
2
+ <%= render "headmin/media/media_item_modal" %>
3
+ <% end %>
@@ -9,12 +9,12 @@
9
9
  #
10
10
  # ==== Examples
11
11
  # Basic version.
12
- # <%= render "headmin/nav/dropdown", name: 'My Account', url: admin_user_path, icon: 'person-circle' do %#>
13
- # <%= render "headmin/nav/item", name: 'Edit info', url: edit_admin_user_path(current_user) %#>
14
- # <%= render "headmin/nav/item", name: 'Log out', url: destroy_user_session_path(current_user) %#>
12
+ # <%= render "headmin/nav/dropdown", name: "My Account", url: admin_user_path, icon: "person-circle" do %#>
13
+ # <%= render "headmin/nav/item", name: "Edit info", url: edit_admin_user_path(current_user) %#>
14
+ # <%= render "headmin/nav/item", name: "Log out", url: destroy_user_session_path(current_user) %#>
15
15
  # <% end %#>
16
16
 
17
- name = local_assigns.has_key?(:name) ? name : ''
17
+ name = local_assigns.has_key?(:name) ? name : ""
18
18
  icon = local_assigns.has_key?(:icon) ? icon : nil
19
19
  collapse_id = "nav-dropdown-#{name.parameterize}"
20
20
  url = local_assigns.has_key?(:url) ? url : request.url
@@ -22,13 +22,13 @@
22
22
  %>
23
23
 
24
24
  <li class="nav-item">
25
- <a class="nav-link d-flex align-items-center dropdown-toggle <%= 'active' if active %>" href="#<%= collapse_id %>" role="button" data-bs-toggle="collapse" aria-expanded="<%= active.to_s %>" aria-controls="<%= collapse_id %>">
26
- <%= bootstrap_icon(icon, class: 'me-2') if icon %>
25
+ <a class="nav-link d-flex align-items-center dropdown-toggle <%= "active" if active %>" href="#<%= collapse_id %>" role="button" data-bs-toggle="collapse" aria-expanded="<%= active.to_s %>" aria-controls="<%= collapse_id %>">
26
+ <%= bootstrap_icon(icon, class: "me-2") if icon %>
27
27
  <span class="d-block d-md-none d-lg-block">
28
28
  <%= name %>
29
29
  </span>
30
30
  </a>
31
- <ul class="collapse <%= 'show' if active %>" id="<%= collapse_id %>">
31
+ <ul class="collapse <%= "show" if active %>" id="<%= collapse_id %>">
32
32
  <%= yield %>
33
33
  </ul>
34
34
  </li>
@@ -9,20 +9,20 @@
9
9
  #
10
10
  # ==== Examples
11
11
  # Basic version.
12
- # <%= render "headmin/nav/item", name: 'Dashboard', url: admin_root_path %#>
12
+ # <%= render "headmin/nav/item", name: "Dashboard", url: admin_root_path %#>
13
13
  #
14
14
  # With icon
15
- # <%= render "headmin/nav/item", name: 'Dashboard', url: admin_root_path, icon: 'speedometer' %#>
15
+ # <%= render "headmin/nav/item", name: "Dashboard", url: admin_root_path, icon: "speedometer" %#>
16
16
 
17
- name = local_assigns.has_key?(:name) ? name : ''
17
+ name = local_assigns.has_key?(:name) ? name : ""
18
18
  icon = local_assigns.has_key?(:icon) ? icon : nil
19
19
  url = local_assigns.has_key?(:url) ? url : request.url
20
20
  active = local_assigns.has_key?(:active) ? active : current_url?(url)
21
21
  %>
22
22
 
23
23
  <li class="nav-item">
24
- <a class="nav-link d-flex align-items-center <%= 'active' if active %>" aria-current="page" href="<%= url %>">
25
- <%= bootstrap_icon(icon, class: 'me-2') if icon %>
24
+ <a class="nav-link d-flex align-items-center <%= "active" if active %>" aria-current="page" href="<%= url %>">
25
+ <%= bootstrap_icon(icon, class: "me-2") if icon %>
26
26
  <span class="d-block d-md-none d-lg-block">
27
27
  <%= name %>
28
28
  </span>
@@ -4,14 +4,14 @@
4
4
  parameters: none
5
5
  %>
6
6
 
7
- <%= render "headmin/dropdown", class: 'nav-item' do %>
8
- <%= render "headmin/dropdown/button", class: 'nav-link', id: 'nav-item-locale' do %>
9
- <%= bootstrap_icon('globe', class: 'me-2') %>
10
- <%= t('language_name', locale: ::I18n.locale) %>
7
+ <%= render "headmin/dropdown", class: "nav-item" do %>
8
+ <%= render "headmin/dropdown/button", class: "nav-link", id: "nav-item-locale" do %>
9
+ <%= bootstrap_icon("globe", class: "me-2") %>
10
+ <%= t("language_name", locale: ::I18n.locale) %>
11
11
  <% end %>
12
- <%= render "headmin/dropdown/list", id: 'nav-item-locale' do %>
12
+ <%= render "headmin/dropdown/list", id: "nav-item-locale" 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 %>
@@ -2,17 +2,17 @@
2
2
 
3
3
  <div class="d-flex flex-row-reverse flex-md-row align-items-center justify-content-between justify-content-md-end my-1">
4
4
  <ul class="pagination pagination-sm my-0" role="group" aria-label="...">
5
- <li class="page-item <%= 'active' if per_page == 12 %>">
6
- <%= link_to 12, url_for(params.merge(per_page: 12)), class: 'page-link' %>
5
+ <li class="page-item <%= "active" if per_page == 12 %>">
6
+ <%= link_to 12, url_for(params.merge(per_page: 12)), class: "page-link" %>
7
7
  </li>
8
- <li class="page-item <%= 'active' if per_page == 24 %>">
9
- <%= link_to 24, url_for(params.merge(per_page: nil)), class: 'page-link' %>
8
+ <li class="page-item <%= "active" if per_page == 24 %>">
9
+ <%= link_to 24, url_for(params.merge(per_page: nil)), class: "page-link" %>
10
10
  </li>
11
- <li class="page-item <%= 'active' if per_page == 48 %>">
12
- <%= link_to 48, url_for(params.merge(per_page: 48)), class: 'page-link' %>
11
+ <li class="page-item <%= "active" if per_page == 48 %>">
12
+ <%= link_to 48, url_for(params.merge(per_page: 48)), class: "page-link" %>
13
13
  </li>
14
14
  </ul>
15
15
  <div class="d-none d-md-block ms-2 me-md-2 text-secondary">
16
- <%= t('.title') %>
16
+ <%= t(".title") %>
17
17
  </div>
18
18
  </div>
@@ -6,6 +6,6 @@
6
6
  per_page: number of items to fetch per page
7
7
  remote: data-remote
8
8
  -%>
9
- <li class="page-item <%= ('disabled' if current_page.first?) %>">
10
- <%= link_to raw(t '.button'), url, class: 'page-link', remote: remote %>
9
+ <li class="page-item <%= ("disabled" if current_page.first?) %>">
10
+ <%= link_to raw(t ".button"), url, class: "page-link", remote: remote %>
11
11
  </li>
@@ -7,6 +7,6 @@
7
7
  -%>
8
8
  <li class="page-item disabled">
9
9
  <a href="#" onclick="return false;" class="page-link">
10
- <%= raw(t '.button') %>
10
+ <%= raw(t ".button") %>
11
11
  </a>
12
12
  </li>
@@ -7,6 +7,6 @@
7
7
  remote: data-remote
8
8
  -%>
9
9
  <li class="page-item
10
- <%= ('disabled' if current_page.last?) %>">
11
- <%= link_to raw(t '.button'), url, class: 'page-link', remote: remote %>
10
+ <%= ("disabled" if current_page.last?) %>">
11
+ <%= link_to raw(t ".button"), url, class: "page-link", remote: remote %>
12
12
  </li>
@@ -6,8 +6,8 @@
6
6
  per_page: number of items to fetch per page
7
7
  remote: data-remote
8
8
  -%>
9
- <li class="page-item <%= ('disabled' if current_page.last?) %>">
10
- <%= link_to url, class: 'page-link', rel: 'next', remote: remote do %>
11
- <%= raw(t '.button') %>
9
+ <li class="page-item <%= ("disabled" if current_page.last?) %>">
10
+ <%= link_to url, class: "page-link", rel: "next", remote: remote do %>
11
+ <%= raw(t ".button") %>
12
12
  <% end %>
13
13
  </li>
@@ -7,6 +7,6 @@
7
7
  per_page: number of items to fetch per page
8
8
  remote: data-remote
9
9
  -%>
10
- <li class="page-item <%= 'active' if page.current? %>">
11
- <%= link_to page, url, opts = {remote: remote, class: 'page-link', rel: page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
10
+ <li class="page-item <%= "active" if page.current? %>">
11
+ <%= link_to page, url, opts = {remote: remote, class: "page-link", rel: page.next? ? "next" : page.prev? ? "prev" : nil} %>
12
12
  </li>
@@ -7,7 +7,7 @@
7
7
  paginator: the paginator that renders the pagination tags inside
8
8
  -%>
9
9
  <%= paginator.render do %>
10
- <nav aria-label="<%= t('.title') %>">
10
+ <nav aria-label="<%= t(".title") %>">
11
11
  <ul class="pagination pagination-sm m-0">
12
12
  <%= first_page_tag %>
13
13
  <%= prev_page_tag %>