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 +4 -4
- data/app/components/satis/attachments/component.css +12 -8
- data/app/components/satis/attachments/component.html.slim +8 -25
- data/app/components/satis/attachments/component.rb +3 -11
- data/app/controllers/satis/attachments_controller.rb +15 -16
- data/app/javascript/satis/controllers/attachment_upload_controller.js +41 -39
- data/app/javascript/satis/controllers/index.js +0 -3
- data/app/views/satis/attachments/_attachment.html.slim +13 -0
- data/app/views/satis/attachments/create.turbo_stream.slim +1 -0
- data/app/views/satis/attachments/destroy.turbo_stream.slim +1 -0
- data/lib/satis/forms/builder.rb +10 -2
- data/lib/satis/version.rb +1 -1
- metadata +4 -2
- data/app/javascript/satis/controllers/attachment_delete_controller.js +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8a393c04d2d3c296a40cbeff2d67c11d77035cacafd6861641422595a051ef1
|
4
|
+
data.tar.gz: eb3403bba306eb4e4953b31abda212636e0dc0f304917fc0ab1c6c533db50d98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69d33e8297502ab3dbe754f7cbca3d886c7cacd529d1356fda0acd6725bd77fe883db23e2cd5c51b2b4cc4deaf7d8ded771e086ddd60f5bdebca6fb5e2932369
|
7
|
+
data.tar.gz: d94edace53c39bb158ff07d39728901fdb849b14e2472c5a4983405ec4ad3d8501670d3a1798902e8056c7c37e46f228e0b26453579d8c626b1ec7e9dcf38767
|
@@ -1,5 +1,5 @@
|
|
1
1
|
.attachments__group {
|
2
|
-
@apply grid gap-6 mt-
|
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
|
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-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
@
|
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]
|
15
|
-
|
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(@
|
23
|
-
attachment&.purge
|
18
|
+
@attachment = @model.public_send(@attribute).find_by(id: params[:id])
|
19
|
+
@attachment&.purge
|
24
20
|
|
25
|
-
|
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
|
-
@
|
32
|
-
@model = GlobalID::Locator.locate_signed(params[:sgid], for:
|
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 = ["
|
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
|
-
|
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
|
-
|
21
|
-
|
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.
|
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.
|
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(
|
99
|
+
formData.append("attachments", files[i])
|
79
100
|
}
|
80
101
|
|
81
102
|
this.element.classList.add("uploading")
|
82
103
|
|
83
|
-
|
84
|
-
method: 'POST',
|
104
|
+
post(this.urlValue, {
|
85
105
|
body: formData,
|
86
|
-
|
87
|
-
|
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}"
|
data/lib/satis/forms/builder.rb
CHANGED
@@ -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
|
-
|
192
|
+
yield,
|
185
193
|
hint_text(options[:hint]),
|
186
194
|
error_text(method)
|
187
195
|
].compact
|
data/lib/satis/version.rb
CHANGED
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.
|
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
|
-
}
|