satis 2.1.49 → 2.1.50

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ad2f7b4c5a019c16cbd6d7a87a119d3db6db6582b514f3d79bc1bb9c6b217ac
4
- data.tar.gz: 7cc90112489b1b41672e7d3319de43a9c8525b9d1a5c1c7a3255d3eafda2f97c
3
+ metadata.gz: e8a393c04d2d3c296a40cbeff2d67c11d77035cacafd6861641422595a051ef1
4
+ data.tar.gz: eb3403bba306eb4e4953b31abda212636e0dc0f304917fc0ab1c6c533db50d98
5
5
  SHA512:
6
- metadata.gz: bbaf6180578a757ee3878c711055b51f91af0083fade4eece870e8ab4f8b14a289f91718572e260a8a1b01fb14bc1e2ec4b8d3e745e248d3c36f35196994e70b
7
- data.tar.gz: 472ebef37cbb6d5beceffd4ab8aaedbaf40aa3d1192d61c347a3ed4d5c1231e169d0f39d5d3e5c534d2860f0885c17cc3d9044f1d3e672078e63e821d04155a6
6
+ metadata.gz: 69d33e8297502ab3dbe754f7cbca3d886c7cacd529d1356fda0acd6725bd77fe883db23e2cd5c51b2b4cc4deaf7d8ded771e086ddd60f5bdebca6fb5e2932369
7
+ data.tar.gz: d94edace53c39bb158ff07d39728901fdb849b14e2472c5a4983405ec4ad3d8501670d3a1798902e8056c7c37e46f228e0b26453579d8c626b1ec7e9dcf38767
@@ -1,5 +1,5 @@
1
1
  .attachments__group {
2
- @apply grid gap-6 mt-10 justify-start;
2
+ @apply grid gap-6 mt-0 justify-start;
3
3
  grid-template-columns: repeat(auto-fill, 200px);
4
4
  }
5
5
 
@@ -13,8 +13,7 @@
13
13
  }
14
14
 
15
15
  .attachments__attachment .attachments__button {
16
- @apply hidden absolute top-2.5 bg-white bg-opacity-80 p-1.5 rounded-full;
17
- @apply dark:bg-gray-700 dark:text-white;
16
+ @apply hidden;
18
17
  }
19
18
 
20
19
  .attachments__attachment .attachments__button:first-of-type {
@@ -26,7 +25,7 @@
26
25
  }
27
26
 
28
27
  .attachments__attachment:hover .attachments__button {
29
- @apply block w-8 h-8 text-center;
28
+ @apply block w-7 h-7 justify-center items-center text-center bg-gray-100 dark:bg-gray-800 dark:text-white;
30
29
  }
31
30
 
32
31
  .attachments__attachment .attachments__filename {
@@ -85,8 +84,7 @@
85
84
  }
86
85
 
87
86
  .upload-btn:hover {
88
- @apply dark:bg-gray-900 dark:text-white dark:border-gray-700 bg-white;
89
- border-color: #999;
87
+ @apply dark:bg-gray-900 dark:text-white dark:border-gray-700 border-gray-500 bg-white;
90
88
  }
91
89
 
92
90
  .upload-btn:focus {
@@ -126,8 +124,14 @@
126
124
  }
127
125
 
128
126
  .uploading .upload-btn {
129
- @apply dark:bg-gray-800 dark:border-gray-600 dark:text-white;
130
- background-color: #fcf8e3;
127
+ @apply dark:bg-gray-800 dark:border-gray-600 dark:text-white bg-white;
131
128
  border-color: #f39c12;
132
129
  cursor: not-allowed;
133
130
  }
131
+
132
+ .attachments__group .attachment-upload.upload-btn.attachments__attachment {
133
+ @apply bg-white dark:bg-gray-800 border-2 border-dashed hover:border-gray-500 border-gray-300 p-4 flex flex-col justify-center items-center text-center w-full min-h-[150px] box-border;
134
+ }
135
+ .attachments__group .attachment-upload.upload-btn.attachments__attachment.dark {
136
+ @apply hover:border-white;
137
+ }
@@ -1,25 +1,8 @@
1
- div.attachment-upload.upload-btn data-controller="attachment-upload" data-attachment-upload-url="#{Satis::Engine.routes.url_helpers.attachments_path(sgid: model_sgid, attribute: attribute)}" data-attachment-upload-param-name="attachments[]"data-attachment-upload-extra-data='{}' data-turbo="true" turbo-method="post"
2
- span.icon.upload
3
- i.fas.fa-upload
4
- span.icon.uploading
5
- i.fal.fa-circle-notch.fa-spin
6
- span.button-text Drag or click to attach files
7
-
8
-
9
-
10
- div.attachments__group
11
- - model.send(attribute).each do |attachment|
12
- div.attachments__attachment(style= attachment_style(attachment))
13
- - unless attachment.representable?
14
- div.preview-text
15
- i.fas.fa-file(aria-hidden="true")
16
-
17
- = link_to Satis::Engine.routes.url_helpers.attachment_path(attachment, sgid: model_sgid, attribute: attribute), data: {turbo_method: :delete, turbo_confirm: "Are you sure you want to delete this attachment?"}, class: 'attachments__button' do
18
- i.fas.fa-xmark
19
-
20
-
21
- = link_to Rails.application.routes.url_helpers.rails_blob_url(attachment, host: request.host + ":#{request.port}"), data: { turbo: true, turbolinks: false }, class: 'attachments__button', download: true do
22
- i.fas.fa-download
23
-
24
- span.attachments__filename
25
- = attachment.filename
1
+ turbo-frame id="#{model.id}_#{attribute}_attachments" target="_top" class="attachments__group"
2
+ div.attachment-upload.upload-btn.attachments__attachment data-controller="satis-attachment-upload" data-satis-attachment-upload-url-value=(model.persisted? ? Satis::Engine.routes.url_helpers.attachments_path(sgid: model_sgid, attribute: attribute) : false) data-satis-attachment-upload-parameter-name-value=(form&.field_name(attribute)||attribute) data-action="click->satis-attachment-upload#handleClick"
3
+ span.icon.upload
4
+ i.fas.fa-upload
5
+ span.icon.uploading
6
+ i.fal.fa-circle-notch.fa-spin
7
+ span.button-text data-satis-attachment-upload-target="button" Drag or click to attach files
8
+ = render partial: "satis/attachments/attachment", collection: model.send(attribute)
@@ -1,24 +1,16 @@
1
1
  module Satis
2
2
  module Attachments
3
3
  class Component < Satis::ApplicationComponent
4
- attr_reader :model, :attribute, :attachments_options
4
+ attr_reader :model, :attribute, :attachments_options, :form
5
5
 
6
6
  def initialize(model, attribute, **options)
7
7
  super()
8
- @model = model
8
+ @form = options[:form]
9
+ @model = model || form.object
9
10
  @attribute = attribute
10
11
  @attachments_options = options
11
12
  end
12
13
 
13
- def attachment_style(attachment)
14
- if attachment.representable?
15
- url = attachment.representation(resize_to_limit: [200, 200]).processed.url
16
- "background-image: url(#{url})"
17
- else
18
- "background-color: f0f0f0"
19
- end
20
- end
21
-
22
14
  def model_sgid
23
15
  @model.to_sgid(expires_in: nil, for: "satis_attachments")
24
16
  end
@@ -4,32 +4,31 @@ module Satis
4
4
  class AttachmentsController < ApplicationController
5
5
  before_action :set_objects
6
6
 
7
-
8
- def index
9
- @attachments = @model.public_send(@attachment_type)
10
- render json: @attachments
11
- end
12
-
13
7
  def create
14
- params[:attachments].each do |file|
15
- @model.public_send(@attachment_type).attach(file)
16
- end
17
- redirect_to request.referer || root_path, notice: "Attachment created successfully."
8
+ @model.public_send(@attribute).attach(params[:attachments])
9
+ @attachments = @model.public_send(@attribute).last(params[:attachments].size)
18
10
 
11
+ respond_to do |format|
12
+ format.html { redirect_to request.referer || root_path, notice: "Attachment created successfully." }
13
+ format.turbo_stream
14
+ end
19
15
  end
20
16
 
21
17
  def destroy
22
- attachment = @model.public_send(@attachment_type).find_by(id: params[:id])
23
- attachment&.purge
18
+ @attachment = @model.public_send(@attribute).find_by(id: params[:id])
19
+ @attachment&.purge
24
20
 
25
- redirect_to request.referer || root_path, notice: "Attachment deleted successfully."
21
+ respond_to do |format|
22
+ format.html { redirect_to request.referer || root_path, notice: "Attachment deleted successfully." }
23
+ format.turbo_stream
24
+ end
26
25
  end
27
26
 
28
27
  private
29
28
 
30
29
  def set_objects
31
- @attachment_type = params[:attribute] || 'attachments'
32
- @model = GlobalID::Locator.locate_signed(params[:sgid], for: 'satis_attachments')
30
+ @attribute = params[:attribute] || "attachments"
31
+ @model = GlobalID::Locator.locate_signed(params[:sgid], for: "satis_attachments")
33
32
  end
34
33
  end
35
- end
34
+ end
@@ -1,29 +1,48 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
+ import { post } from "@rails/request.js"
2
3
 
3
4
  export default class AttachmentUploadController extends Controller {
4
- static targets = ["input"]
5
+ static targets = ["button"]
6
+
7
+ static values = {
8
+ url: String,
9
+ parameterName: String
10
+ }
5
11
 
6
12
  connect() {
7
13
  this.createFileInput()
8
14
  this.addEventListeners()
9
15
  }
10
16
 
17
+ disconnect() {
18
+ this.fileInput.removeEventListener("change", this.handleChange.bind(this))
19
+ this.element.removeEventListener("dragover", this.handleDragOver.bind(this))
20
+ this.element.removeEventListener("dragleave", this.handleDragLeave.bind(this))
21
+ this.element.removeEventListener("dragenter", this.handleDragEnter.bind(this))
22
+ this.element.removeEventListener("drop", this.handleDrop.bind(this))
23
+ }
24
+
11
25
  createFileInput() {
26
+ // Mimic rails - this hiddenInput should prevent "old" files from being deleted
27
+ const hiddenInput = document.createElement("input")
28
+ hiddenInput.setAttribute("name", `${this.parameterNameValue}[]`)
29
+ hiddenInput.setAttribute("type", "hidden")
30
+ hiddenInput.setAttribute("autocomplete", "off")
31
+
32
+ this.element.closest('form').appendChild(hiddenInput)
33
+
12
34
  const input = document.createElement("input")
13
- input.setAttribute("name", this.data.get("param-name") || "file")
35
+
36
+ input.setAttribute("name", `${this.parameterNameValue}[]`)
14
37
  input.setAttribute("type", "file")
15
38
  input.setAttribute("multiple", "multiple")
16
39
  input.style.display = "none"
17
- this.element.appendChild(input)
18
- this.fileInput = input
19
40
 
20
- if (!this.data.has("param-name")) {
21
- console.warn(this.element, "has no data-upload-param attribute, uploads may not work")
22
- }
41
+ this.element.closest('form').appendChild(input)
42
+ this.fileInput = input
23
43
  }
24
44
 
25
45
  addEventListeners() {
26
- this.element.addEventListener("click", this.handleClick.bind(this))
27
46
  this.fileInput.addEventListener("change", this.handleChange.bind(this))
28
47
  this.element.addEventListener("dragover", this.handleDragOver.bind(this))
29
48
  this.element.addEventListener("dragleave", this.handleDragLeave.bind(this))
@@ -32,12 +51,15 @@ export default class AttachmentUploadController extends Controller {
32
51
  }
33
52
 
34
53
  handleClick(event) {
35
- event.preventDefault()
36
54
  this.fileInput.click()
37
55
  }
38
56
 
39
57
  handleChange(event) {
40
- this.upload(event.target.files)
58
+ if (this.hasUrlValue) {
59
+ this.upload(event.target.files)
60
+ } else {
61
+ this.buttonTarget.innerHTML = `${event.target.files.length} files selected`
62
+ }
41
63
  }
42
64
 
43
65
  handleDragOver(event) {
@@ -59,7 +81,11 @@ export default class AttachmentUploadController extends Controller {
59
81
  event.preventDefault()
60
82
  this.element.classList.remove("dragging")
61
83
  if (event.dataTransfer.files.length > 0) {
62
- this.upload(event.dataTransfer.files)
84
+ if (this.hasUrlValue) {
85
+ this.upload(event.dataTransfer.files)
86
+ } else {
87
+ this.buttonTarget.innerHTML = `${event.dataTransfer.files.length} files selected`
88
+ }
63
89
  }
64
90
  }
65
91
 
@@ -68,41 +94,17 @@ export default class AttachmentUploadController extends Controller {
68
94
  if (files.length === 0) return
69
95
 
70
96
  let formData = new FormData()
71
- if (this.data.has("extra-data")) {
72
- for (let [key, value] of Object.entries(JSON.parse(this.data.get("extra-data")))) {
73
- formData.append(key, value)
74
- }
75
- }
76
97
 
77
98
  for (let i = 0; i < files.length; i++) {
78
- formData.append(this.data.get("param-name"), files[i])
99
+ formData.append("attachments", files[i])
79
100
  }
80
101
 
81
102
  this.element.classList.add("uploading")
82
103
 
83
- fetch(this.data.get("url"), {
84
- method: 'POST',
104
+ post(this.urlValue, {
85
105
  body: formData,
86
- headers: {
87
- 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content,
88
- 'Accept': 'text/html, application/json'
89
- },
90
- redirect: 'follow' // Important: follow redirects
91
- }).then((response) => {
92
- // Check if the response is a redirect
93
- if (response.type === 'opaqueredirect' || response.redirected) {
94
- window.location.href = response.url
95
- return
96
- }
97
-
98
- if (response.ok) {
99
- this.element.classList.remove("uploading")
100
- window.location.reload(true)
101
- } else {
102
- throw new Error(response.statusText)
103
- }
104
- }).catch((error) => {
105
- console.log(error)
106
+ responseKind: 'turbo-stream'
107
+ }).then(html => {
106
108
  this.element.classList.remove("uploading")
107
109
  })
108
110
  }
@@ -59,9 +59,6 @@ application.register("satis-fields-for", FieldsForController);
59
59
  import AttachmentUploadController from "satis/controllers/attachment_upload_controller";
60
60
  application.register("satis-attachment-upload", AttachmentUploadController);
61
61
 
62
- import AttachmentDeleteController from "satis/controllers/attachment_delete_controller";
63
- application.register("satis-attachment-delete", AttachmentDeleteController);
64
-
65
62
  import FormController from "satis/controllers/form_controller";
66
63
  application.register("satis-form", FormController);
67
64
 
@@ -0,0 +1,13 @@
1
+ div.attachments__attachment style=(attachment.representable? ? "background-image: url(#{attachment.representation(resize_to_limit: [200, 200]).processed.url})" : "background-color: f0f0f0") id="attachment_#{attachment.id}"
2
+ - unless attachment.representable?
3
+ div.preview-text
4
+ i.fas.fa-file(aria-hidden="true")
5
+
6
+ = link_to Satis::Engine.routes.url_helpers.attachment_path(attachment, sgid: attachment.record.to_sgid(expires_in: nil, for: "satis_attachments"), attribute: attachment.name), data: {turbo_method: :delete, turbo_confirm: "Are you sure you want to delete this attachment?"}, class: 'attachments__button' do
7
+ i.fas.fa-trash
8
+
9
+ = link_to Rails.application.routes.url_helpers.rails_blob_url(attachment, host: request.host + ":#{request.port}"), data: { turbo: false, turbolinks: false }, class: 'attachments__button', download: attachment.filename do
10
+ i.fas.fa-download
11
+
12
+ span.attachments__filename
13
+ = attachment.filename
@@ -0,0 +1 @@
1
+ = turbo_stream.append "#{@attachments.first.record.id}_#{@attachments.first.name}_attachments", partial: "satis/attachments/attachment", collection: @attachments
@@ -0,0 +1 @@
1
+ = turbo_stream.remove "attachment_#{@attachment.id}"
@@ -81,7 +81,7 @@ module Satis
81
81
  # FIXME: Yuk - is it possible to detect when this should not be allowed?
82
82
  # Like checking for whether destroy is allowed on assocations?
83
83
  allow_actions = options.key?(:allow_actions) ? options[:allow_actions] : true
84
- show_actions = @object.respond_to?("#{name}_attributes=") && @object.send(name).respond_to?(:each) && template_object && allow_actions == true
84
+ show_actions = @object.respond_to?(:"#{name}_attributes=") && @object.send(name).respond_to?(:each) && template_object && allow_actions == true
85
85
 
86
86
  html_options = options[:html] || {}
87
87
 
@@ -156,6 +156,14 @@ module Satis
156
156
  hidden_input(method, options, &block)
157
157
  end
158
158
 
159
+ def attachments(method, options = {}, &block)
160
+ self.multipart = true
161
+ safe_join [
162
+ @template.render(Satis::Attachments::Component.new(object, method, form: self, **value_text_method_options(options),
163
+ &block))
164
+ ]
165
+ end
166
+
159
167
  # Non public
160
168
 
161
169
  def input_type_for(method, options)
@@ -181,7 +189,7 @@ module Satis
181
189
  def form_group(method, options = {}, &block)
182
190
  tag.div(class: "form-group form-group-#{method}", data: options.delete(:data)) do
183
191
  safe_join [
184
- block.call,
192
+ yield,
185
193
  hint_text(options[:hint]),
186
194
  error_text(method)
187
195
  ].compact
data/lib/satis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Satis
2
- VERSION = "2.1.49"
2
+ VERSION = "2.1.50"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: satis
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.49
4
+ version: 2.1.50
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
@@ -871,7 +871,6 @@ files:
871
871
  - app/javascript/satis/application.js
872
872
  - app/javascript/satis/controllers/application.js
873
873
  - app/javascript/satis/controllers/application_controller.js
874
- - app/javascript/satis/controllers/attachment_delete_controller.js
875
874
  - app/javascript/satis/controllers/attachment_upload_controller.js
876
875
  - app/javascript/satis/controllers/fields_for_controller.js
877
876
  - app/javascript/satis/controllers/file_controller.js
@@ -890,6 +889,9 @@ files:
890
889
  - app/mailers/satis/application_mailer.rb
891
890
  - app/models/satis/application_record.rb
892
891
  - app/models/satis/user_data.rb
892
+ - app/views/satis/attachments/_attachment.html.slim
893
+ - app/views/satis/attachments/create.turbo_stream.slim
894
+ - app/views/satis/attachments/destroy.turbo_stream.slim
893
895
  - app/views/satis/documentation/avatars/index.html.slim
894
896
  - app/views/satis/documentation/cards/index.html.slim
895
897
  - app/views/satis/documentation/editors/index.html.slim
@@ -1,48 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus"
2
- import { get } from '@rails/request.js'
3
-
4
- /***
5
- * Delete attachments controller
6
- *
7
- * Deletes an attachments
8
- */
9
- export default class AttachmentDeleteController extends Controller {
10
- connect() {}
11
-
12
- delete(event) {
13
- const self = this
14
-
15
- event.stopPropagation()
16
- event.preventDefault()
17
-
18
- const formData = new FormData()
19
- formData.append("_method", "DELETE")
20
-
21
- fetch(self.element.getAttribute("href"), {
22
- method: "POST",
23
- headers: {
24
- "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
25
- },
26
- body: formData,
27
- })
28
- .then(self.handleErrors)
29
- .then((response) => {
30
- window.location.reload(true);
31
- response.json().then(function (data) {
32
- let node = document.querySelector(data.selector)
33
- if (node) {
34
- node.innerHTML = data.html
35
- }
36
- })
37
- })
38
- .catch((error) => {
39
- console.log(error)
40
- })
41
- return false
42
- }
43
-
44
- handleErrors(response) {
45
- if (!response.ok) throw new Error(response.status)
46
- return response
47
- }
48
- }