lato_cms 3.0.8 → 3.0.10
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/assets/javascripts/lato_cms/controllers/lato_cms_advanced_editor_controller.js +4 -6
- data/app/assets/javascripts/lato_cms/controllers/lato_cms_repeater_controller.js +29 -1
- data/app/assets/stylesheets/lato_cms/application.scss +2 -1
- data/app/controllers/lato_cms/pages_controller.rb +13 -0
- data/app/views/lato_cms/pages/_repeater.html.erb +8 -3
- data/app/views/lato_cms/pages/show.html.erb +1 -1
- data/config/locales/en.yml +2 -0
- data/config/locales/it.yml +2 -0
- data/lib/lato_cms/version.rb +1 -1
- 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: 67cc56a05385cb8ed79d06de5407bae73f23444f0872e02021696ebc1dd4000b
|
|
4
|
+
data.tar.gz: 794483334a4c7906ad00a370448ce488650bc6a94b6755a5f84bd2c909fd3676
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d020a8b135ea587b61396ed1d8bc8314a9066a165d576e3a31aeb4c127894d0e0652729adf4c3c634a70ef07b075f54de1010d70dcff0ffbdb3f6218e3641fc7
|
|
7
|
+
data.tar.gz: 20a54bfd7811d1de1acfe2b44af5012fb5d1f810ab9fc2c84b037431e98b9f28541ae9c2a0dc17061db40670c72ce213670404e158ad40b991a35e2f6a534aec
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
|
2
2
|
|
|
3
3
|
export default class extends Controller {
|
|
4
|
-
static targets = ["
|
|
4
|
+
static targets = ["editorCol", "reopenBtn"]
|
|
5
5
|
static values = { listPath: String }
|
|
6
6
|
|
|
7
7
|
isSidebarOpen = true
|
|
8
8
|
|
|
9
9
|
// ─── Advanced full-screen editor is the only view ────────────────────────────
|
|
10
|
+
// The full-screen layout is rendered server-side (--active class + body CSS) so
|
|
11
|
+
// it survives re-renders (e.g. component clone) with no flash. JS only wires up
|
|
12
|
+
// Escape-to-back and the sidebar toggle.
|
|
10
13
|
|
|
11
|
-
// Activate on load: no base view exists, only back to the pages list.
|
|
12
14
|
connect() {
|
|
13
|
-
this.rowTarget.classList.add('lato-cms-advanced-editor__row--active')
|
|
14
|
-
document.body.classList.add('lato-cms-advanced-editor--open')
|
|
15
|
-
|
|
16
15
|
// Escape returns to the pages list instead of exiting to a base view.
|
|
17
16
|
this._escHandler = (e) => { if (e.key === 'Escape') this.back() }
|
|
18
17
|
document.addEventListener('keydown', this._escHandler)
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
disconnect() {
|
|
22
|
-
document.body.classList.remove('lato-cms-advanced-editor--open')
|
|
23
21
|
document.removeEventListener('keydown', this._escHandler)
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
|
2
2
|
|
|
3
3
|
export default class extends Controller {
|
|
4
|
-
static targets = ['items', 'template', 'item', 'position', 'orderInput']
|
|
4
|
+
static targets = ['items', 'template', 'item', 'position', 'orderInput', 'addButton']
|
|
5
|
+
static values = { min: { type: Number, default: 0 }, max: { type: Number, default: 0 } }
|
|
6
|
+
|
|
7
|
+
connect () {
|
|
8
|
+
this.refresh()
|
|
9
|
+
}
|
|
5
10
|
|
|
6
11
|
add () {
|
|
12
|
+
// Guard against exceeding max even if the button state is stale.
|
|
13
|
+
if (this.maxValue > 0 && this.itemTargets.length >= this.maxValue) return
|
|
14
|
+
|
|
7
15
|
const itemId = window.crypto.randomUUID()
|
|
8
16
|
const html = this.templateTarget.innerHTML
|
|
9
17
|
.replaceAll('NEW_RECORD', itemId)
|
|
@@ -14,6 +22,9 @@ export default class extends Controller {
|
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
remove (event) {
|
|
25
|
+
// Block removal below the required minimum.
|
|
26
|
+
if (this.minValue > 0 && this.itemTargets.length <= this.minValue) return
|
|
27
|
+
|
|
17
28
|
const item = event.currentTarget.closest('[data-lato-cms-repeater-target="item"]')
|
|
18
29
|
if (!item) return
|
|
19
30
|
|
|
@@ -29,5 +40,22 @@ export default class extends Controller {
|
|
|
29
40
|
const orderInput = item.querySelector('[data-lato-cms-repeater-target="orderInput"]')
|
|
30
41
|
if (orderInput) orderInput.value = item.dataset.repeaterItemId
|
|
31
42
|
})
|
|
43
|
+
|
|
44
|
+
this.updateControls()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Toggle add/remove availability against the min/max constraints.
|
|
48
|
+
updateControls () {
|
|
49
|
+
const count = this.itemTargets.length
|
|
50
|
+
|
|
51
|
+
if (this.hasAddButtonTarget) {
|
|
52
|
+
this.addButtonTarget.disabled = this.maxValue > 0 && count >= this.maxValue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const disableRemove = this.minValue > 0 && count <= this.minValue
|
|
56
|
+
this.itemTargets.forEach((item) => {
|
|
57
|
+
const removeButton = item.querySelector('[data-action~="lato-cms-repeater#remove"]')
|
|
58
|
+
if (removeButton) removeButton.disabled = disableRemove
|
|
59
|
+
})
|
|
32
60
|
}
|
|
33
61
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// ── Advanced editor mode ───────────────────────────────────────────────────────
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Full-screen editor is the only view on the page show: lock background scroll.
|
|
4
|
+
body.controller-pages.action-show {
|
|
4
5
|
overflow: hidden;
|
|
5
6
|
}
|
|
6
7
|
|
|
@@ -233,6 +233,19 @@ module LatoCms
|
|
|
233
233
|
component_id = template_component[:component_id]
|
|
234
234
|
item_ids = Array(repeater_order).reject(&:blank?).map(&:to_s)
|
|
235
235
|
|
|
236
|
+
# Enforce min/max item count (authoritative check). Missing settings = no constraint.
|
|
237
|
+
settings = template_component[:settings] || {}
|
|
238
|
+
min = settings['min'].presence && settings['min'].to_i
|
|
239
|
+
max = settings['max'].presence && settings['max'].to_i
|
|
240
|
+
if min && item_ids.size < min
|
|
241
|
+
errors << { field_id: template_component_id, errors: [t('lato_cms.repeater_min_error', count: min)] }
|
|
242
|
+
return
|
|
243
|
+
end
|
|
244
|
+
if max && item_ids.size > max
|
|
245
|
+
errors << { field_id: template_component_id, errors: [t('lato_cms.repeater_max_error', count: max)] }
|
|
246
|
+
return
|
|
247
|
+
end
|
|
248
|
+
|
|
236
249
|
order_field = @page.fields.find_or_initialize_by(
|
|
237
250
|
template_id: @page.template_id,
|
|
238
251
|
template_component_id: template_component_id,
|
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
<% saved_order = begin; order_field&.value.present? ? JSON.parse(order_field.value) : []; rescue JSON::ParserError; []; end %>
|
|
3
3
|
<% fields_by_item = component_fields.reject(&:repeater_order?).group_by(&:repeater_item_id) %>
|
|
4
4
|
<% item_ids = (saved_order.presence || fields_by_item.keys.compact) %>
|
|
5
|
-
<%
|
|
5
|
+
<% min = tc[:settings]['min'].to_i %>
|
|
6
|
+
<% max = tc[:settings]['max'].to_i %>
|
|
7
|
+
<%# Seed `min` empty items when the list is empty, so the minimum is met up front. %>
|
|
8
|
+
<% item_ids = Array.new(min) { SecureRandom.uuid } if item_ids.blank? && min.positive? %>
|
|
6
9
|
|
|
7
|
-
<div data-controller="lato-cms-repeater"
|
|
10
|
+
<div data-controller="lato-cms-repeater"
|
|
11
|
+
data-lato-cms-repeater-min-value="<%= min %>"
|
|
12
|
+
data-lato-cms-repeater-max-value="<%= max %>">
|
|
8
13
|
<div data-lato-cms-repeater-target="items">
|
|
9
14
|
<% item_ids.each_with_index do |item_id, item_index| %>
|
|
10
15
|
<% item_fields = (fields_by_item[item_id] || []).index_by(&:base_field_id) %>
|
|
@@ -16,7 +21,7 @@
|
|
|
16
21
|
<%= render 'lato_cms/pages/repeater_item', tc: tc, item_id: 'NEW_RECORD', item_index: 'NEW_INDEX', item_fields: {} %>
|
|
17
22
|
</template>
|
|
18
23
|
|
|
19
|
-
<button type="button" class="btn btn-sm btn-outline-primary" data-action="lato-cms-repeater#add">
|
|
24
|
+
<button type="button" class="btn btn-sm btn-outline-primary" data-action="lato-cms-repeater#add" data-lato-cms-repeater-target="addButton">
|
|
20
25
|
<i class="bi bi-plus-lg me-1"></i><%= t('lato_cms.repeater_add_item') %>
|
|
21
26
|
</button>
|
|
22
27
|
</div>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
] %>
|
|
5
5
|
|
|
6
6
|
<div data-controller="lato-cms-advanced-editor" data-lato-cms-advanced-editor-list-path-value="<%= lato_cms.pages_path %>">
|
|
7
|
-
<div class="row align-items-stretch lato-cms-advanced-editor__row
|
|
7
|
+
<div class="row align-items-stretch lato-cms-advanced-editor__row lato-cms-advanced-editor__row--active">
|
|
8
8
|
|
|
9
9
|
<%# ── Preview column ─────────────────────────────────────────────────── %>
|
|
10
10
|
<div class="col-12 col-lg-6 mb-4 lato-cms-advanced-editor__preview-col">
|
data/config/locales/en.yml
CHANGED
|
@@ -80,6 +80,8 @@ en:
|
|
|
80
80
|
fields_save_component: Save
|
|
81
81
|
repeater_add_item: Add item
|
|
82
82
|
repeater_remove_item: Remove
|
|
83
|
+
repeater_min_error: "At least %{count} items are required"
|
|
84
|
+
repeater_max_error: "At most %{count} items are allowed"
|
|
83
85
|
fields_component_missing_alert_html: "Component <strong>%{component_id}</strong> not found. The component configuration may have been removed."
|
|
84
86
|
fields_previously_saved_data: "Previously saved data:"
|
|
85
87
|
field_file_remove: Remove
|
data/config/locales/it.yml
CHANGED
|
@@ -80,6 +80,8 @@ it:
|
|
|
80
80
|
fields_save_component: Salva
|
|
81
81
|
repeater_add_item: Aggiungi elemento
|
|
82
82
|
repeater_remove_item: Rimuovi
|
|
83
|
+
repeater_min_error: "Sono richiesti almeno %{count} elementi"
|
|
84
|
+
repeater_max_error: "Sono ammessi al massimo %{count} elementi"
|
|
83
85
|
fields_component_missing_alert_html: "Componente <strong>%{component_id}</strong> non trovato. La configurazione del componente potrebbe essere stata rimossa."
|
|
84
86
|
fields_previously_saved_data: "Dati salvati in precedenza:"
|
|
85
87
|
field_file_remove: Rimuovi
|
data/lib/lato_cms/version.rb
CHANGED