satis 2.1.49 → 2.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
- }