avo 3.29.1 → 3.30.0
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/Gemfile.lock +8 -8
- data/app/components/avo/filters_component.html.erb +1 -1
- data/app/components/avo/items/panel_component.html.erb +1 -1
- data/app/components/avo/items/switcher_component.html.erb +1 -1
- data/app/components/avo/items/switcher_component.rb +1 -1
- data/app/components/avo/items/visible_items_component.html.erb +1 -0
- data/app/components/avo/items/visible_items_component.rb +1 -0
- data/app/controllers/avo/base_controller.rb +1 -2
- data/app/javascript/js/controllers/fields/date_field_controller.js +12 -0
- data/app/javascript/js/controllers/fields/easy_mde_controller.js +9 -2
- data/app/javascript/js/controllers/fields/key_value_controller.js +10 -1
- data/app/javascript/js/controllers/fields/tags_field_controller.js +7 -0
- data/app/javascript/js/controllers/media_library_attach_controller.js +7 -0
- data/app/javascript/js/controllers/preview_controller.js +8 -1
- data/app/javascript/js/controllers/record_selector_controller.js +11 -7
- data/app/javascript/js/controllers/search_controller.js +4 -0
- data/app/javascript/js/controllers/table_row_controller.js +9 -6
- data/app/javascript/js/controllers/tippy_controller.js +8 -1
- data/app/javascript/js/controllers/toggle_controller.js +1 -1
- data/lib/avo/fields/base_field.rb +3 -3
- data/lib/avo/fields/belongs_to_field.rb +15 -0
- data/lib/avo/fields/frame_base_field.rb +1 -1
- data/lib/avo/test_helpers.rb +6 -0
- data/lib/avo/version.rb +1 -1
- data/public/avo-assets/avo.base.css +1 -1
- data/public/avo-assets/avo.base.js +95 -95
- data/public/avo-assets/avo.base.js.map +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 381ab579d675357bd7e3ec288df2f56d8c80db211b55e3ca9bffeda3a74c82ce
|
|
4
|
+
data.tar.gz: d485e192ea98b5e5f84a5bc6f906c175dbe5eb755cd4aa7b94a275f24d9c5937
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b76ac1bb168ec995a0e88e9cee6b87fa624cce346c0cd07339d895c777181862ce890b375ba679e67436947a055d918f345656e48bf6fd766433c586d1f55e2
|
|
7
|
+
data.tar.gz: 576a88f74323ac640e183136d9865ff4f5e7ce675373e697f2ceb32036cd43e34c36bf6f39f5bd4911ea7477047620edd52bfd0235a0e188d849d0f4f783a3a9
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
avo (3.
|
|
4
|
+
avo (3.30.0)
|
|
5
5
|
actionview (>= 6.1)
|
|
6
6
|
active_link_to
|
|
7
7
|
activerecord (>= 6.1)
|
|
@@ -284,7 +284,7 @@ GEM
|
|
|
284
284
|
railties (>= 6.1.0)
|
|
285
285
|
faker (3.5.3)
|
|
286
286
|
i18n (>= 1.8.11, < 2)
|
|
287
|
-
faraday (2.14.
|
|
287
|
+
faraday (2.14.1)
|
|
288
288
|
faraday-net_http (>= 2.0, < 3.5)
|
|
289
289
|
json
|
|
290
290
|
logger
|
|
@@ -369,7 +369,7 @@ GEM
|
|
|
369
369
|
jmespath (1.6.2)
|
|
370
370
|
jsbundling-rails (1.3.1)
|
|
371
371
|
railties (>= 6.0.0)
|
|
372
|
-
json (2.
|
|
372
|
+
json (2.18.1)
|
|
373
373
|
kramdown (2.5.1)
|
|
374
374
|
rexml (>= 3.3.9)
|
|
375
375
|
kramdown-parser-gfm (1.1.0)
|
|
@@ -416,7 +416,7 @@ GEM
|
|
|
416
416
|
railties (>= 3.0)
|
|
417
417
|
msgpack (1.8.0)
|
|
418
418
|
multipart-post (2.4.1)
|
|
419
|
-
net-http (0.
|
|
419
|
+
net-http (0.9.1)
|
|
420
420
|
uri (>= 0.11.1)
|
|
421
421
|
net-imap (0.5.12)
|
|
422
422
|
date
|
|
@@ -428,11 +428,11 @@ GEM
|
|
|
428
428
|
net-smtp (0.5.1)
|
|
429
429
|
net-protocol
|
|
430
430
|
nio4r (2.7.5)
|
|
431
|
-
nokogiri (1.
|
|
431
|
+
nokogiri (1.19.1-aarch64-linux-gnu)
|
|
432
432
|
racc (~> 1.4)
|
|
433
|
-
nokogiri (1.
|
|
433
|
+
nokogiri (1.19.1-arm64-darwin)
|
|
434
434
|
racc (~> 1.4)
|
|
435
|
-
nokogiri (1.
|
|
435
|
+
nokogiri (1.19.1-x86_64-linux-gnu)
|
|
436
436
|
racc (~> 1.4)
|
|
437
437
|
observer (0.1.2)
|
|
438
438
|
orm_adapter (0.5.0)
|
|
@@ -463,7 +463,7 @@ GEM
|
|
|
463
463
|
puma (6.6.1)
|
|
464
464
|
nio4r (~> 2.0)
|
|
465
465
|
racc (1.8.1)
|
|
466
|
-
rack (3.2.
|
|
466
|
+
rack (3.2.5)
|
|
467
467
|
rack-session (2.1.1)
|
|
468
468
|
base64 (>= 0.1.0)
|
|
469
469
|
rack (>= 3.0.0)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<div data-controller="toggle" data-component-name="<%= self.class.to_s.underscore %>">
|
|
1
|
+
<div data-controller="toggle" data-toggle-exemption-containers-value='[".flatpickr-calendar"]' data-component-name="<%= self.class.to_s.underscore %>">
|
|
2
2
|
<div class="relative w-full flex justify-between">
|
|
3
3
|
<%= a_button class: 'focus:outline-none',
|
|
4
4
|
color: :primary,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<% end %>
|
|
9
9
|
<% c.with_body do %>
|
|
10
10
|
<% content_tag :div, class: "divide-y overflow-auto" do %>
|
|
11
|
-
<%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form %>
|
|
11
|
+
<%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form, reflection: @reflection %>
|
|
12
12
|
<% end %>
|
|
13
13
|
<% end %>
|
|
14
14
|
<% if sidebars.any? { |sidebar| sidebar.visible_items.any? } %>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<% elsif item.is_row? %>
|
|
12
12
|
<%= render Avo::RowComponent.new(divider: item.divider) do |c| %>
|
|
13
13
|
<% c.with_body do %>
|
|
14
|
-
<%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form %>
|
|
14
|
+
<%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form, reflection: @reflection %>
|
|
15
15
|
<% end %>
|
|
16
16
|
<% end %>
|
|
17
17
|
<% elsif item.is_collaboration? %>
|
|
@@ -42,7 +42,7 @@ class Avo::Items::SwitcherComponent < Avo::BaseComponent
|
|
|
42
42
|
def render?
|
|
43
43
|
# Stops rendering if the field should be hidden in reflections
|
|
44
44
|
if item.is_field?
|
|
45
|
-
return false if in_reflection? && item.hidden_in_reflection?
|
|
45
|
+
return false if in_reflection? && item.hidden_in_reflection?(@reflection)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
true
|
|
@@ -7,7 +7,6 @@ module Avo
|
|
|
7
7
|
|
|
8
8
|
before_action :set_resource_name
|
|
9
9
|
before_action :set_resource
|
|
10
|
-
before_action :set_applied_filters, only: :index
|
|
11
10
|
before_action :set_record, only: [:show, :edit, :destroy, :update, :preview]
|
|
12
11
|
before_action :set_record_to_fill, only: [:new, :edit, :create, :update]
|
|
13
12
|
before_action :detect_fields
|
|
@@ -26,6 +25,7 @@ module Avo
|
|
|
26
25
|
end
|
|
27
26
|
add_breadcrumb @resource.plural_name.humanize
|
|
28
27
|
|
|
28
|
+
set_applied_filters
|
|
29
29
|
set_index_params
|
|
30
30
|
set_filters
|
|
31
31
|
set_actions
|
|
@@ -92,7 +92,6 @@ module Avo
|
|
|
92
92
|
add_breadcrumb @resource.plural_name.humanize, resources_path(resource: @resource)
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
|
|
96
95
|
add_breadcrumb @resource.record_title
|
|
97
96
|
add_breadcrumb I18n.t("avo.details").upcase_first
|
|
98
97
|
|
|
@@ -138,6 +138,7 @@ export default class extends Controller {
|
|
|
138
138
|
},
|
|
139
139
|
altInput: true,
|
|
140
140
|
onChange: this.onChange.bind(this),
|
|
141
|
+
onClose: this.onClose.bind(this),
|
|
141
142
|
noCalendar: false,
|
|
142
143
|
...this.pickerOptionsValue,
|
|
143
144
|
}
|
|
@@ -223,6 +224,17 @@ export default class extends Controller {
|
|
|
223
224
|
this.updateRealInput(value)
|
|
224
225
|
}
|
|
225
226
|
|
|
227
|
+
onClose(selectedDates, dateStr, instance) {
|
|
228
|
+
if (instance.config.allowInput && instance.altInput.value) {
|
|
229
|
+
const value = instance.altInput.value
|
|
230
|
+
if (value) {
|
|
231
|
+
instance.setDate(value, true, instance.config.altFormat)
|
|
232
|
+
} else {
|
|
233
|
+
this.updateRealInput('')
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
226
238
|
// Value should be a string
|
|
227
239
|
updateRealInput(value) {
|
|
228
240
|
this.inputTarget.value = value
|
|
@@ -28,9 +28,16 @@ export default class extends Controller {
|
|
|
28
28
|
options.status = false
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
this.easyMde = new EasyMDE(options)
|
|
32
32
|
if (this.view === 'show') {
|
|
33
|
-
easyMde.codemirror.options.readOnly = true
|
|
33
|
+
this.easyMde.codemirror.options.readOnly = true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
disconnect() {
|
|
38
|
+
if (this.easyMde) {
|
|
39
|
+
this.easyMde.toTextArea()
|
|
40
|
+
this.easyMde = null
|
|
34
41
|
}
|
|
35
42
|
}
|
|
36
43
|
}
|
|
@@ -151,6 +151,15 @@ export default class extends Controller {
|
|
|
151
151
|
return result
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
escapeAttribute(str) {
|
|
155
|
+
if (str === null || str === undefined) return ''
|
|
156
|
+
return String(str)
|
|
157
|
+
.replace(/&/g, '&')
|
|
158
|
+
.replace(/"/g, '"')
|
|
159
|
+
.replace(/</g, '<')
|
|
160
|
+
.replace(/>/g, '>')
|
|
161
|
+
}
|
|
162
|
+
|
|
154
163
|
inputField(id = 'key', index, key, value) {
|
|
155
164
|
const inputValue = id === 'key' ? key : value
|
|
156
165
|
|
|
@@ -160,7 +169,7 @@ export default class extends Controller {
|
|
|
160
169
|
placeholder="${this.options[`${id}_label`]}"
|
|
161
170
|
data-index="${index}"
|
|
162
171
|
${this[`${id}InputDisabled`] ? "disabled='disabled'" : ''}
|
|
163
|
-
value="${
|
|
172
|
+
value="${this.escapeAttribute(inputValue)}"
|
|
164
173
|
/>`
|
|
165
174
|
}
|
|
166
175
|
|
|
@@ -79,6 +79,13 @@ export default class extends Controller {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
disconnect() {
|
|
83
|
+
if (this.tagify) {
|
|
84
|
+
this.tagify.destroy()
|
|
85
|
+
this.tagify = null
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
initTagify() {
|
|
83
90
|
this.tagify = new Tagify(this.inputTarget, this.tagifyOptions)
|
|
84
91
|
const that = this
|
|
@@ -77,6 +77,13 @@ export default class extends Controller {
|
|
|
77
77
|
this.setupFileInput()
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
disconnect() {
|
|
81
|
+
if (this.fileInput) {
|
|
82
|
+
this.fileInput.remove()
|
|
83
|
+
this.fileInput = null
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
setupFileInput() {
|
|
81
88
|
// Create a hidden file input element
|
|
82
89
|
this.fileInput = document.createElement('input')
|
|
@@ -9,7 +9,7 @@ export default class extends Controller {
|
|
|
9
9
|
connect() {
|
|
10
10
|
const vm = this;
|
|
11
11
|
|
|
12
|
-
tippy(vm.context.element, {
|
|
12
|
+
this.tippyInstance = tippy(vm.context.element, {
|
|
13
13
|
content: "loading...",
|
|
14
14
|
allowHTML: true,
|
|
15
15
|
theme: 'light',
|
|
@@ -21,4 +21,11 @@ export default class extends Controller {
|
|
|
21
21
|
},
|
|
22
22
|
})
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
disconnect() {
|
|
26
|
+
if (this.tippyInstance) {
|
|
27
|
+
this.tippyInstance.destroy()
|
|
28
|
+
this.tippyInstance = null
|
|
29
|
+
}
|
|
30
|
+
}
|
|
24
31
|
}
|
|
@@ -82,10 +82,14 @@ export default class extends Controller {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
#addEventListeners() {
|
|
85
|
+
// Create bound handler references so the same functions are used for add and remove
|
|
86
|
+
this.boundMouseenterHandler = this.#selectorMouseenterHandler.bind(this)
|
|
87
|
+
this.boundMouseleaveHandler = this.#selectorMouseleaveHandler.bind(this)
|
|
88
|
+
|
|
85
89
|
// Attach event listeners to item selector cells
|
|
86
90
|
Array.from(this.itemSelectorCells).forEach((itemSelectorCell) => {
|
|
87
|
-
itemSelectorCell.addEventListener('mouseenter', this
|
|
88
|
-
itemSelectorCell.addEventListener('mouseleave', this
|
|
91
|
+
itemSelectorCell.addEventListener('mouseenter', this.boundMouseenterHandler)
|
|
92
|
+
itemSelectorCell.addEventListener('mouseleave', this.boundMouseleaveHandler)
|
|
89
93
|
})
|
|
90
94
|
|
|
91
95
|
// Attach event listeners to keyboard events
|
|
@@ -94,13 +98,13 @@ export default class extends Controller {
|
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
#removeEventListeners() {
|
|
97
|
-
// Remove event listeners
|
|
101
|
+
// Remove event listeners using the same bound references from #addEventListeners
|
|
98
102
|
Array.from(this.itemSelectorCells).forEach((itemSelectorCell) => {
|
|
99
|
-
itemSelectorCell.removeEventListener('mouseenter', this
|
|
100
|
-
itemSelectorCell.removeEventListener('mouseleave', this
|
|
103
|
+
itemSelectorCell.removeEventListener('mouseenter', this.boundMouseenterHandler)
|
|
104
|
+
itemSelectorCell.removeEventListener('mouseleave', this.boundMouseleaveHandler)
|
|
101
105
|
})
|
|
102
|
-
document.removeEventListener('keydown', this.#keydownHandler
|
|
103
|
-
document.removeEventListener('keyup', this.#keyupHandler
|
|
106
|
+
document.removeEventListener('keydown', this.#keydownHandler)
|
|
107
|
+
document.removeEventListener('keyup', this.#keyupHandler)
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
#selectorMouseenterHandler(event) {
|
|
@@ -127,6 +127,10 @@ export default class extends Controller {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
disconnect() {
|
|
130
|
+
if (this.isGlobalSearch) {
|
|
131
|
+
Mousetrap.unbind(['command+k', 'ctrl+k'])
|
|
132
|
+
}
|
|
133
|
+
|
|
130
134
|
// Don't leave open autocompletes around when disconnected. Otherwise it will still
|
|
131
135
|
// be visible when navigating back to this page.
|
|
132
136
|
if (this.destroyMethod) {
|
|
@@ -3,7 +3,15 @@ import { Controller } from '@hotwired/stimulus'
|
|
|
3
3
|
export default class extends Controller {
|
|
4
4
|
connect() {
|
|
5
5
|
this.isSelecting = false
|
|
6
|
-
this.#
|
|
6
|
+
this.boundHandleMouseDown = this.#handleMouseDown.bind(this)
|
|
7
|
+
this.boundHandleMouseMove = this.#handleMouseMove.bind(this)
|
|
8
|
+
this.element.addEventListener('mousedown', this.boundHandleMouseDown)
|
|
9
|
+
this.element.addEventListener('mousemove', this.boundHandleMouseMove)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
disconnect() {
|
|
13
|
+
this.element.removeEventListener('mousedown', this.boundHandleMouseDown)
|
|
14
|
+
this.element.removeEventListener('mousemove', this.boundHandleMouseMove)
|
|
7
15
|
}
|
|
8
16
|
|
|
9
17
|
visitRecord(event) {
|
|
@@ -42,11 +50,6 @@ export default class extends Controller {
|
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
#bindSelectionEvents() {
|
|
46
|
-
this.element.addEventListener('mousedown', this.#handleMouseDown.bind(this))
|
|
47
|
-
this.element.addEventListener('mousemove', this.#handleMouseMove.bind(this))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
53
|
#handleMouseDown() {
|
|
51
54
|
this.isSelecting = false
|
|
52
55
|
}
|
|
@@ -5,10 +5,17 @@ export default class extends Controller {
|
|
|
5
5
|
static targets = ['source', 'content']
|
|
6
6
|
|
|
7
7
|
connect() {
|
|
8
|
-
tippy(this.sourceTarget, {
|
|
8
|
+
this.tippyInstance = tippy(this.sourceTarget, {
|
|
9
9
|
content: this.contentTarget.innerHTML,
|
|
10
10
|
allowHTML: true,
|
|
11
11
|
theme: 'light',
|
|
12
12
|
})
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
disconnect() {
|
|
16
|
+
if (this.tippyInstance) {
|
|
17
|
+
this.tippyInstance.destroy()
|
|
18
|
+
this.tippyInstance = null
|
|
19
|
+
}
|
|
20
|
+
}
|
|
14
21
|
}
|
|
@@ -11,7 +11,7 @@ export default class extends Controller {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
get exemptionContainerTargets() {
|
|
14
|
-
return this.exemptionContainersValue.
|
|
14
|
+
return this.exemptionContainersValue.flatMap((selector) => [...document.querySelectorAll(selector)])
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
connect() {
|
|
@@ -272,12 +272,12 @@ module Avo
|
|
|
272
272
|
true
|
|
273
273
|
end
|
|
274
274
|
|
|
275
|
-
def visible_in_reflection?
|
|
275
|
+
def visible_in_reflection?(reflection = nil)
|
|
276
276
|
true
|
|
277
277
|
end
|
|
278
278
|
|
|
279
|
-
def hidden_in_reflection?
|
|
280
|
-
!visible_in_reflection?
|
|
279
|
+
def hidden_in_reflection?(reflection = nil)
|
|
280
|
+
!visible_in_reflection?(reflection)
|
|
281
281
|
end
|
|
282
282
|
|
|
283
283
|
def options_for_filter
|
|
@@ -308,6 +308,21 @@ module Avo
|
|
|
308
308
|
"#{id}_type"
|
|
309
309
|
end
|
|
310
310
|
|
|
311
|
+
# When displayed inside a has_one/has_many reflection, hide this field if it
|
|
312
|
+
# points back to the parent record (i.e., it is the inverse of the reflection).
|
|
313
|
+
# This mirrors the filtering logic in Avo::Concerns::HasItems#get_fields.
|
|
314
|
+
def visible_in_reflection?(reflection = nil)
|
|
315
|
+
return true if reflection.nil?
|
|
316
|
+
return true unless respond_to?(:foreign_key)
|
|
317
|
+
return true unless reflection.inverse_of.present?
|
|
318
|
+
return true unless reflection.inverse_of.respond_to?(:foreign_key)
|
|
319
|
+
|
|
320
|
+
inverse_fk = reflection.inverse_of.foreign_key
|
|
321
|
+
self_fk = is_polymorphic? ? self.reflection&.foreign_key : foreign_key
|
|
322
|
+
|
|
323
|
+
inverse_fk != self_fk
|
|
324
|
+
end
|
|
325
|
+
|
|
311
326
|
private
|
|
312
327
|
|
|
313
328
|
def get_model_class(record)
|
data/lib/avo/test_helpers.rb
CHANGED
|
@@ -204,6 +204,12 @@ module Avo
|
|
|
204
204
|
find(".flatpickr-second").set(value)
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
+
def set_picker_text_input(value)
|
|
208
|
+
element = find("input.form-control[type='text']")
|
|
209
|
+
# Set value without firing input/change events
|
|
210
|
+
page.execute_script("arguments[0].value = arguments[1]", element.native, value)
|
|
211
|
+
end
|
|
212
|
+
|
|
207
213
|
def open_picker
|
|
208
214
|
text_input.click
|
|
209
215
|
end
|
data/lib/avo/version.rb
CHANGED
|
@@ -1779,7 +1779,7 @@ span.flatpickr-weekday {
|
|
|
1779
1779
|
}
|
|
1780
1780
|
}
|
|
1781
1781
|
|
|
1782
|
-
/*! @algolia/autocomplete-theme-classic 1.19.
|
|
1782
|
+
/*! @algolia/autocomplete-theme-classic 1.19.6 | MIT License | © Algolia, Inc. and contributors | https://github.com/algolia/autocomplete */
|
|
1783
1783
|
|
|
1784
1784
|
/* ----------------*/
|
|
1785
1785
|
|