avo 1.3.2 → 1.4.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/app/components/avo/common/key_value_component.html.erb +53 -0
- data/app/components/avo/common/key_value_component.rb +11 -0
- data/app/components/avo/edit/fields/key_value_field_component.html.erb +3 -0
- data/app/components/avo/edit/fields/key_value_field_component.rb +4 -0
- data/app/components/avo/edit/fields/trix_field_component.html.erb +19 -10
- data/app/components/avo/show/fields/key_value_field_component.html.erb +3 -0
- data/app/components/avo/show/fields/key_value_field_component.rb +4 -0
- data/app/controllers/avo/application_controller.rb +5 -1
- data/app/controllers/avo/attachments_controller.rb +14 -3
- data/app/helpers/avo/application_helper.rb +1 -1
- data/app/packs/entrypoints/application.js +14 -11
- data/app/packs/js/controllers/fields/code_field_controller.js +1 -0
- data/app/packs/js/controllers/fields/date_field_controller.js +1 -1
- data/app/packs/js/controllers/fields/key_value_controller.js +137 -0
- data/app/packs/js/controllers/fields/trix_field_controller.js +121 -0
- data/app/views/avo/partials/_javascript.html.erb +3 -1
- data/avo.gemspec +1 -1
- data/config/routes.rb +4 -0
- data/config/webpacker.yml +2 -2
- data/lib/avo.rb +1 -1
- data/lib/avo/fields/key_value_field.rb +24 -1
- data/lib/avo/fields/trix_field.rb +4 -0
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/action_generator.rb +1 -1
- data/lib/generators/avo/locales_generator.rb +2 -2
- data/lib/generators/avo/templates/action.tt +5 -9
- data/lib/generators/avo/templates/locales/avo.en.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.nb-NO.yml +81 -0
- data/lib/generators/avo/templates/locales/avo.pt-BR.yml +82 -0
- data/lib/generators/avo/templates/locales/avo.ro.yml +70 -0
- data/public/avo-packs/css/{application-af3e670d.css → application-5bdca030.css} +92 -18
- data/public/avo-packs/css/application-5bdca030.css.br +0 -0
- data/public/avo-packs/css/application-5bdca030.css.gz +0 -0
- data/public/avo-packs/css/application-5bdca030.css.map +1 -0
- data/public/avo-packs/css/application-5bdca030.css.map.br +0 -0
- data/public/avo-packs/css/application-5bdca030.css.map.gz +0 -0
- data/public/avo-packs/js/application-16a456a2b7cb56b01153.js +26 -0
- data/public/avo-packs/js/{application-801f1297670a4c6df1b6.js.LICENSE.txt → application-16a456a2b7cb56b01153.js.LICENSE.txt} +0 -0
- data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.br +0 -0
- data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.gz +0 -0
- data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.map +1 -0
- data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.map.br +0 -0
- data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.map.gz +0 -0
- data/public/avo-packs/manifest.json +15 -15
- metadata +36 -19
- data/public/avo-packs/css/application-af3e670d.css.br +0 -0
- data/public/avo-packs/css/application-af3e670d.css.gz +0 -0
- data/public/avo-packs/css/application-af3e670d.css.map +0 -1
- data/public/avo-packs/css/application-af3e670d.css.map.br +0 -0
- data/public/avo-packs/css/application-af3e670d.css.map.gz +0 -0
- data/public/avo-packs/js/application-801f1297670a4c6df1b6.js +0 -26
- data/public/avo-packs/js/application-801f1297670a4c6df1b6.js.br +0 -0
- data/public/avo-packs/js/application-801f1297670a4c6df1b6.js.gz +0 -0
- data/public/avo-packs/js/application-801f1297670a4c6df1b6.js.map +0 -1
- data/public/avo-packs/js/application-801f1297670a4c6df1b6.js.map.br +0 -0
- data/public/avo-packs/js/application-801f1297670a4c6df1b6.js.map.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc2704b71b6989b5f292a21a2f906d05df0240764a9caffb39e6a89e8a52e1bc
|
4
|
+
data.tar.gz: 2cf8653fc50133c1618f891cb618c934bf195c510098f95ed3ca19488585518f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6206068ce49294ee565b56a08ebf1fb3c0595ebf1fa3379ed19cb2672db28d59b02ad6a1d3e11f19303dbd643d4a98ce3d82c2539f1e7d4cea4fa981c98c17c8
|
7
|
+
data.tar.gz: 71a144f0942247c4e9ca8f4d48adb48755d25d1f93642afc793ef826a49e7810bed17fb4d3e0b21905e5bf5aff3c564b47f9095ab3bb6f95b0f6d3a0fcdaf67f
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (1.
|
4
|
+
avo (1.4.0.pre.1)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -11,7 +11,7 @@ PATH
|
|
11
11
|
image_processing
|
12
12
|
manifester
|
13
13
|
meta-tags
|
14
|
-
pagy
|
14
|
+
pagy (>= 3.1.3, < 4.0)
|
15
15
|
pundit
|
16
16
|
rails (>= 6.0)
|
17
17
|
view_component
|
@@ -187,7 +187,7 @@ GEM
|
|
187
187
|
nokogiri (1.11.3-x86_64-linux)
|
188
188
|
racc (~> 1.4)
|
189
189
|
orm_adapter (0.5.0)
|
190
|
-
pagy (3.
|
190
|
+
pagy (3.13.0)
|
191
191
|
parallel (1.20.1)
|
192
192
|
parser (3.0.0.0)
|
193
193
|
ast (~> 2.4.1)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<div class="w-full shadow-lg rounded-lg overflow-hidden"
|
2
|
+
data-controller="key-value"
|
3
|
+
data-key-value-target="controller"
|
4
|
+
data-options="<%= @field.options.to_json %>"
|
5
|
+
data-input-classes="<%= input_classes %>"
|
6
|
+
data-editable="<%= @view.in?([:edit, :create]) %>"
|
7
|
+
>
|
8
|
+
<div class="w-full flex flex-col">
|
9
|
+
<div class="flex w-full">
|
10
|
+
<div class="flex w-full bg-gray-800 shadow overflow-hidden">
|
11
|
+
<div class="w-1/2 py-3 px-3 uppercase font-semibold text-xs text-white border-gray-600 border-r">
|
12
|
+
<%= @field.key_label %>
|
13
|
+
</div>
|
14
|
+
<div class="w-1/2 py-3 px-3 uppercase font-semibold text-xs text-white">
|
15
|
+
<%= @field.value_label %>
|
16
|
+
</div>
|
17
|
+
<% if @view.in?([:edit, :create]) %>
|
18
|
+
<div class="flex items-center justify-center p-2 px-3 border-l border-gray-600">
|
19
|
+
<a href="javascript:void(0);"
|
20
|
+
title="<%= @field.action_text %>"
|
21
|
+
data-tippy="tooltip"
|
22
|
+
data-button="add-row"
|
23
|
+
data-action="click->key-value#addRow"
|
24
|
+
<% if @field.disable_adding_rows %>
|
25
|
+
class="cursor-not-allowed"
|
26
|
+
<% end %>
|
27
|
+
>
|
28
|
+
<%= svg 'plus-circle', class: 'text-gray-400 h-5 hover:text-gray-500' %>
|
29
|
+
</a>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
<div data-key-value-target="rows"></div>
|
35
|
+
</div>
|
36
|
+
<% if @form.present? %>
|
37
|
+
<%= @form.text_area @field.id,
|
38
|
+
value: @field.parsed_value,
|
39
|
+
class: 'hidden',
|
40
|
+
placeholder: @field.placeholder,
|
41
|
+
'data-key-value-target': 'input',
|
42
|
+
'data-view': :edit
|
43
|
+
%>
|
44
|
+
<% else %>
|
45
|
+
<%= text_area_tag @field.id,
|
46
|
+
@field.parsed_value,
|
47
|
+
class: 'hidden',
|
48
|
+
placeholder: @field.placeholder,
|
49
|
+
'data-key-value-target': 'input',
|
50
|
+
'data-view': :edit
|
51
|
+
%>
|
52
|
+
<% end %>
|
53
|
+
</div>
|
@@ -1,11 +1,20 @@
|
|
1
|
-
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, full_width: true do %>
|
2
|
+
<div
|
3
|
+
data-controller="trix-field"
|
4
|
+
data-trix-field-target="controller"
|
5
|
+
data-resource-name="<%= @resource.model_class.model_name.route_key %>"
|
6
|
+
data-resource-id="<%= @resource.model.id %>"
|
7
|
+
data-attachments-disabled="<%= @field.attachments_disabled %>"
|
8
|
+
data-attachment-key="<%= @field.attachment_key %>"
|
9
|
+
>
|
10
|
+
<% trix_id = "trix_#{@resource.name.underscore}_#{@field.id}" %>
|
11
|
+
<trix-editor data-trix-field-target="editor" input="<%= trix_id %>" placeholder="<%= @field.placeholder %>"><%== @field.value %></trix-editor>
|
12
|
+
<%= @form.text_area @field.id,
|
13
|
+
id: trix_id,
|
14
|
+
class: helpers.input_classes('w-full hidden', has_error: (@resource.model.present? and @resource.model.errors.include?(@field.id))),
|
15
|
+
placeholder: @field.placeholder,
|
16
|
+
disabled: @field.readonly,
|
17
|
+
rows: @field.meta[:rows]
|
18
|
+
%>
|
19
|
+
</div>
|
11
20
|
<% end %>
|
@@ -42,7 +42,7 @@ module Avo
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def check_avo_license
|
45
|
-
unless on_root_path || on_resources_path
|
45
|
+
unless on_root_path || on_resources_path || on_api_path
|
46
46
|
if @license.invalid? || @license.lacks(:custom_tools)
|
47
47
|
if Rails.env.development?
|
48
48
|
@custom_tools_alert_visible = true
|
@@ -272,5 +272,9 @@ module Avo
|
|
272
272
|
def on_resources_path
|
273
273
|
request.original_url.match?(/.*\/#{Avo.configuration.namespace}\/resources\/.*/)
|
274
274
|
end
|
275
|
+
|
276
|
+
def on_api_path
|
277
|
+
request.original_url.match?(/.*\/#{Avo.configuration.namespace}\/avo_api\/.*/)
|
278
|
+
end
|
275
279
|
end
|
276
280
|
end
|
@@ -2,9 +2,20 @@ require_dependency "avo/application_controller"
|
|
2
2
|
|
3
3
|
module Avo
|
4
4
|
class AttachmentsController < ApplicationController
|
5
|
-
before_action :set_resource_name, only: :destroy
|
6
|
-
before_action :set_resource, only: :destroy
|
7
|
-
before_action :set_model, only: :destroy
|
5
|
+
before_action :set_resource_name, only: [:destroy, :create]
|
6
|
+
before_action :set_resource, only: [:destroy, :create]
|
7
|
+
before_action :set_model, only: [:destroy, :create]
|
8
|
+
|
9
|
+
def create
|
10
|
+
blob = ActiveStorage::Blob.create_and_upload! io: params[:file], filename: params[:filename]
|
11
|
+
|
12
|
+
@model.send(params[:attachment_key]).attach blob
|
13
|
+
|
14
|
+
render json: {
|
15
|
+
url: main_app.url_for(blob),
|
16
|
+
href: main_app.url_for(blob)
|
17
|
+
}
|
18
|
+
end
|
8
19
|
|
9
20
|
def show
|
10
21
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
import 'core-js/stable'
|
3
3
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
4
4
|
import 'regenerator-runtime/runtime'
|
5
|
-
import 'trix'
|
6
5
|
import * as Mousetrap from 'mousetrap'
|
7
6
|
import { Application } from 'stimulus'
|
8
7
|
import { Turbo } from '@hotwired/turbo-rails'
|
@@ -19,6 +18,19 @@ window.Turbolinks = Turbo
|
|
19
18
|
|
20
19
|
Mousetrap.bind('r r r', () => Turbo.visit(window.location.href, { action: 'replace' }))
|
21
20
|
|
21
|
+
function initTippy() {
|
22
|
+
tippy('[data-tippy="tooltip"]', {
|
23
|
+
theme: 'light',
|
24
|
+
content(reference) {
|
25
|
+
const title = reference.getAttribute('title')
|
26
|
+
reference.removeAttribute('title')
|
27
|
+
|
28
|
+
return title
|
29
|
+
},
|
30
|
+
})
|
31
|
+
}
|
32
|
+
window.initTippy = initTippy
|
33
|
+
|
22
34
|
const application = Application.start()
|
23
35
|
|
24
36
|
const context = require.context('./../js/controllers', true, /\.js$/)
|
@@ -29,16 +41,7 @@ application.load(definitionsFromContext(fieldsContext))
|
|
29
41
|
|
30
42
|
document.addEventListener('turbo:load', () => {
|
31
43
|
document.body.classList.remove('turbo-loading')
|
32
|
-
|
33
|
-
tippy('[data-tippy="tooltip"]', {
|
34
|
-
theme: 'light',
|
35
|
-
content(reference) {
|
36
|
-
const title = reference.getAttribute('title')
|
37
|
-
reference.removeAttribute('title')
|
38
|
-
|
39
|
-
return title
|
40
|
-
},
|
41
|
-
})
|
44
|
+
initTippy()
|
42
45
|
})
|
43
46
|
document.addEventListener('turbo:visit', () => document.body.classList.add('turbo-loading'))
|
44
47
|
document.addEventListener('turbo:submit-start', () => document.body.classList.add('turbo-loading'))
|
@@ -12,6 +12,7 @@ import 'codemirror/mode/shell/shell'
|
|
12
12
|
import 'codemirror/mode/sql/sql'
|
13
13
|
import 'codemirror/mode/vue/vue'
|
14
14
|
import 'codemirror/mode/xml/xml'
|
15
|
+
import 'codemirror/mode/yaml/yaml'
|
15
16
|
|
16
17
|
import { Controller } from 'stimulus'
|
17
18
|
import { castBoolean } from '@/js/helpers/cast_boolean'
|
@@ -33,7 +33,7 @@ export default class extends Controller {
|
|
33
33
|
|
34
34
|
// enable timezone display
|
35
35
|
if (enableTime) {
|
36
|
-
currentValue = DateTime.fromISO(this.inputTarget.value, { zone: window.timezone })
|
36
|
+
currentValue = DateTime.fromISO(this.inputTarget.value, { zone: window.Avo.configuration.timezone })
|
37
37
|
currentValue = currentValue.setZone(this.inputTarget.dataset.timezone)
|
38
38
|
currentValue = currentValue.toISO()
|
39
39
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
/* eslint-disable max-len */
|
2
|
+
import { Controller } from 'stimulus'
|
3
|
+
import { castBoolean } from '@/js/helpers/cast_boolean'
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static targets = ['input', 'controller', 'rows']
|
7
|
+
|
8
|
+
fieldValue = []
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
|
12
|
+
get keyInputDisabled() {
|
13
|
+
return !this.options.editable || this.options.disable_editing_keys
|
14
|
+
}
|
15
|
+
|
16
|
+
get valueInputDisabled() {
|
17
|
+
return !this.options.editable
|
18
|
+
}
|
19
|
+
|
20
|
+
connect() {
|
21
|
+
this.setOptions()
|
22
|
+
|
23
|
+
try {
|
24
|
+
const objectValue = JSON.parse(this.inputTarget.value)
|
25
|
+
Object.keys(objectValue).forEach((key) => this.fieldValue.push([key, objectValue[key]]))
|
26
|
+
} catch (error) {
|
27
|
+
this.fieldValue = []
|
28
|
+
}
|
29
|
+
|
30
|
+
this.updateKeyValueComponent()
|
31
|
+
}
|
32
|
+
|
33
|
+
addRow() {
|
34
|
+
if (this.options.disable_adding_rows || !this.options.editable) return
|
35
|
+
this.fieldValue.push(['', ''])
|
36
|
+
this.updateKeyValueComponent()
|
37
|
+
this.focusLastRow()
|
38
|
+
}
|
39
|
+
|
40
|
+
deleteRow(event) {
|
41
|
+
if (this.options.disable_deleting_rows || !this.options.editable) return
|
42
|
+
const { index } = event.target.dataset
|
43
|
+
this.fieldValue.splice(index, 1)
|
44
|
+
this.updateTextareaInput()
|
45
|
+
this.updateKeyValueComponent()
|
46
|
+
}
|
47
|
+
|
48
|
+
focusLastRow() {
|
49
|
+
return this.rowsTarget.querySelector('.flex.key-value-row:last-child .key-value-input-key').focus()
|
50
|
+
}
|
51
|
+
|
52
|
+
valueFieldUpdated(event) {
|
53
|
+
const { value } = event.target
|
54
|
+
const { index } = event.target.dataset
|
55
|
+
this.fieldValue[index][1] = value
|
56
|
+
|
57
|
+
this.updateTextareaInput()
|
58
|
+
}
|
59
|
+
|
60
|
+
keyFieldUpdated(event) {
|
61
|
+
const { value } = event.target
|
62
|
+
const { index } = event.target.dataset
|
63
|
+
this.fieldValue[index][0] = value
|
64
|
+
|
65
|
+
this.updateTextareaInput()
|
66
|
+
}
|
67
|
+
|
68
|
+
updateTextareaInput() {
|
69
|
+
if (!this.hasInputTarget) return
|
70
|
+
let result = {}
|
71
|
+
if (this.fieldValue && this.fieldValue.length > 0) {
|
72
|
+
result = Object.assign(...this.fieldValue.map(([key, val]) => ({ [key]: val })))
|
73
|
+
}
|
74
|
+
this.inputTarget.innerText = JSON.stringify(result)
|
75
|
+
}
|
76
|
+
|
77
|
+
updateKeyValueComponent() {
|
78
|
+
let result = ''
|
79
|
+
let index = 0
|
80
|
+
this.fieldValue.forEach((row) => {
|
81
|
+
const [key, value] = row
|
82
|
+
result += this.interpolatedRow(key, value, index)
|
83
|
+
index++
|
84
|
+
})
|
85
|
+
this.rowsTarget.innerHTML = result
|
86
|
+
window.initTippy()
|
87
|
+
}
|
88
|
+
|
89
|
+
interpolatedRow(key, value, index) {
|
90
|
+
let result = `<div class="flex key-value-row">
|
91
|
+
${this.inputField('key', index, key, value)}
|
92
|
+
${this.inputField('value', index, key, value)}`
|
93
|
+
if (this.options.editable) {
|
94
|
+
result += `<a
|
95
|
+
href="javascript:void(0);"
|
96
|
+
data-index="${index}"
|
97
|
+
data-action="click->key-value#deleteRow"
|
98
|
+
title="${this.options.delete_text}"
|
99
|
+
data-tippy="tooltip"
|
100
|
+
data-button="delete-row"
|
101
|
+
tabindex="-1"
|
102
|
+
${this.options.disable_deleting_rows ? "disabled='disabled'" : ''}
|
103
|
+
class="flex items-center justify-center p-2 px-3 border-none ${this.options.disable_deleting_rows ? 'cursor-not-allowed' : ''}"
|
104
|
+
><svg class="pointer-events-none text-gray-500 h-5 hover:text-gray-500" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg></a>`
|
105
|
+
}
|
106
|
+
result += '</div>'
|
107
|
+
|
108
|
+
return result
|
109
|
+
}
|
110
|
+
|
111
|
+
inputField(id = 'key', index, key, value) {
|
112
|
+
return `<input
|
113
|
+
class="${this.options.inputClasses} !rounded-none border-gray-600 border-r border-l-0 border-b-0 border-t-0 focus:border-gray-300 w-1/2 focus:outline-none outline-none key-value-input-${id}"
|
114
|
+
data-action="input->key-value#${id}FieldUpdated"
|
115
|
+
placeholder="${this.options[`${id}_label`]}"
|
116
|
+
data-index="${index}"
|
117
|
+
${this[`${id}InputDisabled`] ? "disabled='disabled'" : ''}
|
118
|
+
value="${id === 'key' ? key : value}"
|
119
|
+
autofocus
|
120
|
+
/>`
|
121
|
+
}
|
122
|
+
|
123
|
+
setOptions() {
|
124
|
+
let fieldOptions
|
125
|
+
|
126
|
+
try {
|
127
|
+
fieldOptions = JSON.parse(this.controllerTarget.dataset.options)
|
128
|
+
} catch (error) {
|
129
|
+
fieldOptions = {}
|
130
|
+
}
|
131
|
+
this.options = {
|
132
|
+
...fieldOptions,
|
133
|
+
inputClasses: this.controllerTarget.dataset.inputClasses,
|
134
|
+
editable: castBoolean(this.controllerTarget.dataset.editable),
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
import 'trix'
|
2
|
+
import { Controller } from 'stimulus'
|
3
|
+
import { castBoolean } from '@/js/helpers/cast_boolean'
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static targets = ['editor', 'controller']
|
7
|
+
|
8
|
+
get resourceId() {
|
9
|
+
return this.controllerTarget.dataset.resourceId
|
10
|
+
}
|
11
|
+
|
12
|
+
get resourceName() {
|
13
|
+
return this.controllerTarget.dataset.resourceName
|
14
|
+
}
|
15
|
+
|
16
|
+
get attachmentKey() {
|
17
|
+
return this.controllerTarget.dataset.attachmentKey
|
18
|
+
}
|
19
|
+
|
20
|
+
get attachmentsDisabled() {
|
21
|
+
return castBoolean(this.controllerTarget.dataset.attachmentsDisabled)
|
22
|
+
}
|
23
|
+
|
24
|
+
get uploadUrl() {
|
25
|
+
return `${window.location.origin}${window.Avo.configuration.root_path}/avo_api/resources/${this.resourceName}/${this.resourceId}/attachments`
|
26
|
+
}
|
27
|
+
|
28
|
+
connect() {
|
29
|
+
if (this.attachmentsDisabled) {
|
30
|
+
// Remove the attachments button
|
31
|
+
this.controllerTarget.querySelector('.trix-button-group--file-tools').remove()
|
32
|
+
}
|
33
|
+
|
34
|
+
window.addEventListener('trix-file-accept', (event) => {
|
35
|
+
if (event.target === this.editorTarget) {
|
36
|
+
// Prevent file uploads for fields that have attachments disabled.
|
37
|
+
if (this.attachmentsDisabled) {
|
38
|
+
event.preventDefault()
|
39
|
+
window.toastr.warning('This field has attachments disabled.')
|
40
|
+
|
41
|
+
return
|
42
|
+
}
|
43
|
+
|
44
|
+
// Prevent file uploads for resources that haven't been saved yet.
|
45
|
+
if (this.resourceId === '') {
|
46
|
+
event.preventDefault()
|
47
|
+
window.toastr.warning("You can't upload files into the Trix editor until you save the resource.")
|
48
|
+
|
49
|
+
return
|
50
|
+
}
|
51
|
+
|
52
|
+
// Prevent file uploads for fields without an attachment key.
|
53
|
+
if (this.attachmentKey === '') {
|
54
|
+
event.preventDefault()
|
55
|
+
window.toastr.warning("You haven't set an <a href='https://google.com' class='!text-blue-700 underline'>attachment_key</a> to this Trix field.")
|
56
|
+
}
|
57
|
+
}
|
58
|
+
})
|
59
|
+
|
60
|
+
window.addEventListener('trix-attachment-add', (event) => {
|
61
|
+
if (event.target === this.editorTarget) {
|
62
|
+
if (event.attachment.file) {
|
63
|
+
this.uploadFileAttachment(event.attachment)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
})
|
67
|
+
}
|
68
|
+
|
69
|
+
uploadFileAttachment(attachment) {
|
70
|
+
this.uploadFile(
|
71
|
+
attachment.file,
|
72
|
+
(progress) => attachment.setUploadProgress(progress),
|
73
|
+
(attributes) => attachment.setAttributes(attributes),
|
74
|
+
)
|
75
|
+
}
|
76
|
+
|
77
|
+
uploadFile(file, progressCallback, successCallback) {
|
78
|
+
const formData = this.createFormData(file)
|
79
|
+
const xhr = new XMLHttpRequest()
|
80
|
+
|
81
|
+
xhr.open('POST', this.uploadUrl, true)
|
82
|
+
|
83
|
+
xhr.setRequestHeader('X-CSRF-Token', document.querySelector('meta[name="csrf-token"]').content)
|
84
|
+
|
85
|
+
xhr.upload.addEventListener('progress', (event) => {
|
86
|
+
// eslint-disable-next-line no-mixed-operators
|
87
|
+
const progress = event.loaded / event.total * 100
|
88
|
+
progressCallback(progress)
|
89
|
+
})
|
90
|
+
|
91
|
+
xhr.addEventListener('load', () => {
|
92
|
+
if (xhr.status === 200) {
|
93
|
+
let response
|
94
|
+
try {
|
95
|
+
response = JSON.parse(xhr.response)
|
96
|
+
} catch (error) {
|
97
|
+
response = {}
|
98
|
+
}
|
99
|
+
|
100
|
+
const attributes = {
|
101
|
+
url: response.url,
|
102
|
+
href: response.href,
|
103
|
+
}
|
104
|
+
|
105
|
+
successCallback(attributes)
|
106
|
+
}
|
107
|
+
})
|
108
|
+
|
109
|
+
xhr.send(formData)
|
110
|
+
}
|
111
|
+
|
112
|
+
createFormData(file) {
|
113
|
+
const data = new FormData()
|
114
|
+
data.append('Content-Type', file.type)
|
115
|
+
data.append('file', file)
|
116
|
+
data.append('filename', file.name)
|
117
|
+
data.append('attachment_key', this.attachmentKey)
|
118
|
+
|
119
|
+
return data
|
120
|
+
}
|
121
|
+
}
|