lato 3.15.0 → 3.16.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/app/assets/javascripts/lato/controllers/lato_input_list_controller.js +74 -0
- data/app/controllers/concerns/lato/componentable.rb +6 -1
- data/app/helpers/lato/components_helper.rb +4 -0
- data/app/views/lato/components/_form_item_input_list.html.erb +66 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/ro.yml +2 -0
- data/lib/lato/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '05116771901e6364c0b094f61367d9869ca4d7e3d1a3de2791d03ef89ad2ff4d'
|
|
4
|
+
data.tar.gz: f187a060e0ac99079fc2c652301ce1d489d996163bf6e636d0c7cba66f8e72ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 535712544161d9d74577c4f5d1032dfc1767ccd562c10befd16b81f46d91c7c269eba62480922549b39d77f47025021faa408a2f582d7d4c559db78383ee4b76
|
|
7
|
+
data.tar.gz: ad25e774ed14a29f14c7ebaf401ba6159bd94b7df9a578bdaf2634f6292a7753f0703b06f3bf74402f99299d4a21d11182dbbedb45ae85695b35bc8926ce838f
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["list", "listItem", "template"]
|
|
5
|
+
|
|
6
|
+
addItem() {
|
|
7
|
+
const template = this.templateTarget
|
|
8
|
+
const nextIndex = this.getNextIndex()
|
|
9
|
+
const newItem = template.content.cloneNode(true)
|
|
10
|
+
newItem.querySelector('.template').setAttribute('data-lato-input-list-target', 'listItem')
|
|
11
|
+
newItem.querySelector('.template').setAttribute('data-index', nextIndex)
|
|
12
|
+
newItem.querySelector('.template').classList.remove('template')
|
|
13
|
+
|
|
14
|
+
this.updateFieldNames(newItem, nextIndex)
|
|
15
|
+
this.listTarget.appendChild(newItem)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
removeItem(event) {
|
|
19
|
+
const item = event.target.closest('[data-lato-input-list-target="listItem"]')
|
|
20
|
+
if (!item) return
|
|
21
|
+
|
|
22
|
+
const existingDestroyField = item.querySelector('input[type="hidden"][name*="[_destroy]"]')
|
|
23
|
+
if (existingDestroyField) return
|
|
24
|
+
|
|
25
|
+
const hiddenIdField = item.querySelector('input[type="hidden"][name*="[id]"]')
|
|
26
|
+
if (hiddenIdField && hiddenIdField.value) {
|
|
27
|
+
const destroyField = document.createElement('input')
|
|
28
|
+
destroyField.type = 'hidden'
|
|
29
|
+
destroyField.name = hiddenIdField.name.replace('[id]', '[_destroy]')
|
|
30
|
+
destroyField.value = '1'
|
|
31
|
+
item.appendChild(destroyField)
|
|
32
|
+
|
|
33
|
+
item.style.display = 'none'
|
|
34
|
+
item.classList.add('d-none')
|
|
35
|
+
|
|
36
|
+
const inputs = item.querySelectorAll('input, select, textarea')
|
|
37
|
+
inputs.forEach(input => {
|
|
38
|
+
if (input.type !== 'hidden') input.disabled = true
|
|
39
|
+
})
|
|
40
|
+
} else {
|
|
41
|
+
item.remove()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
updateFieldNames(element, index) {
|
|
46
|
+
const inputs = element.querySelectorAll('input, select, textarea')
|
|
47
|
+
const labels = element.querySelectorAll('label')
|
|
48
|
+
|
|
49
|
+
inputs.forEach(input => {
|
|
50
|
+
if (input.name) {
|
|
51
|
+
input.name = input.name.replace('0', index)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (input.id) {
|
|
55
|
+
input.id = input.id.replace('0', index)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
labels.forEach(label => {
|
|
60
|
+
if (label.getAttribute('for')) {
|
|
61
|
+
label.setAttribute('for', label.getAttribute('for').replace('0', index))
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getNextIndex() {
|
|
67
|
+
const itemsIndicies = Array.from(this.listItemTargets).map(item => {
|
|
68
|
+
const indexAttr = item.getAttribute('data-index')
|
|
69
|
+
return indexAttr ? parseInt(indexAttr, 10) : -1
|
|
70
|
+
})
|
|
71
|
+
const maxIndex = itemsIndicies.length > 0 ? Math.max(...itemsIndicies) : -1
|
|
72
|
+
return maxIndex + 1
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -50,9 +50,14 @@ module Lato
|
|
|
50
50
|
|
|
51
51
|
# manage pagination
|
|
52
52
|
if pagination || params["#{key}_page"] || params["#{key}_per_page"]
|
|
53
|
+
# Read from params or fallback to cookies, then to defaults
|
|
53
54
|
page = params["#{key}_page"]&.to_i || 1
|
|
54
|
-
per_page = params["#{key}_per_page"]&.to_i || (pagination.is_a?(Integer) ? pagination : 25)
|
|
55
|
+
per_page = params["#{key}_per_page"]&.to_i || cookies["lato_index_#{key}_per_page"]&.to_i || (pagination.is_a?(Integer) ? pagination : 25)
|
|
55
56
|
per_page = 100 if per_page > 100
|
|
57
|
+
|
|
58
|
+
# Save to cookies for persistence
|
|
59
|
+
cookies["lato_index_#{key}_per_page"] = { value: per_page.to_s, expires: 1.year.from_now }
|
|
60
|
+
|
|
56
61
|
collection = collection.page(page).per(per_page)
|
|
57
62
|
end
|
|
58
63
|
|
|
@@ -240,6 +240,10 @@ module Lato
|
|
|
240
240
|
form.color_field key, options
|
|
241
241
|
end
|
|
242
242
|
|
|
243
|
+
def lato_form_item_input_list(form, key, partial, partial_params = {})
|
|
244
|
+
render 'lato/components/form_item_input_list', form: form, key: key, partial: partial, partial_params: partial_params
|
|
245
|
+
end
|
|
246
|
+
|
|
243
247
|
def lato_form_submit(form, label, options = {})
|
|
244
248
|
options[:class] ||= []
|
|
245
249
|
options[:class].push('btn')
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<%
|
|
2
|
+
|
|
3
|
+
form ||= nil
|
|
4
|
+
key ||= nil
|
|
5
|
+
partial ||= nil
|
|
6
|
+
partial_params ||= {}
|
|
7
|
+
|
|
8
|
+
existing_records = form.object.send(key) || []
|
|
9
|
+
|
|
10
|
+
%>
|
|
11
|
+
|
|
12
|
+
<% unless partial %>
|
|
13
|
+
<div class="alert alert-warning">
|
|
14
|
+
Missing partial parameter for form_item_input_list partial.
|
|
15
|
+
</div>
|
|
16
|
+
<% else %>
|
|
17
|
+
<div data-controller="lato-input-list">
|
|
18
|
+
<div data-lato-input-list-target="list" id="<%= "#{form.object_name}_#{key}" %>">
|
|
19
|
+
<% existing_records.each_with_index do |record, index| %>
|
|
20
|
+
<%
|
|
21
|
+
item_nested_form = form.fields_for(key, record) do |f|
|
|
22
|
+
partial_params[:form] = f
|
|
23
|
+
end
|
|
24
|
+
%>
|
|
25
|
+
|
|
26
|
+
<div class="border p-2 mb-2 rounded" data-lato-input-list-target="listItem" data-index="<%= index %>">
|
|
27
|
+
<input type="hidden" name="<%= "#{form.object_name}[#{key}_attributes][#{index}][id]" %>" value="<%= record.id %>">
|
|
28
|
+
|
|
29
|
+
<%= render partial: partial, locals: {
|
|
30
|
+
form: item_nested_form,
|
|
31
|
+
**partial_params
|
|
32
|
+
} %>
|
|
33
|
+
|
|
34
|
+
<div class="d-flex justify-content-end mt-2">
|
|
35
|
+
<button type="button" class="btn btn-danger btn-sm" data-action="click->lato-input-list#removeItem" title="<%= I18n.t('lato.remove_item') %>" data-controller="lato-tooltip">
|
|
36
|
+
<i class="bi bi-trash"></i>
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="d-flex justify-content-end mt-2 pt-2 border-top">
|
|
44
|
+
<button type="button" class="btn btn-outline-primary btn-sm" data-action="click->lato-input-list#addItem">
|
|
45
|
+
<i class="bi bi-plus-circle"></i> <%= I18n.t('lato.add_item') %>
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<template data-lato-input-list-target="template">
|
|
50
|
+
<div class="template border p-2 mb-2 rounded">
|
|
51
|
+
<%= render partial: partial, locals: {
|
|
52
|
+
form: form.fields_for(key, form.object.send(key).new) do |f|
|
|
53
|
+
partial_params[:form] = f
|
|
54
|
+
end,
|
|
55
|
+
**partial_params
|
|
56
|
+
} %>
|
|
57
|
+
|
|
58
|
+
<div class="d-flex justify-content-end mt-2">
|
|
59
|
+
<button type="button" class="btn btn-danger btn-sm" data-action="click->lato-input-list#removeItem" title="<%= I18n.t('lato.remove_item') %>" data-controller="lato-tooltip">
|
|
60
|
+
<i class="bi bi-trash"></i>
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
</div>
|
|
66
|
+
<% end %>
|
data/config/locales/en.yml
CHANGED
|
@@ -71,6 +71,8 @@ en:
|
|
|
71
71
|
operation_failed_title: Operation failed
|
|
72
72
|
operation_failed_subtitle: The operation has failed because of an error
|
|
73
73
|
dropzone_drag_and_drop_or_click: Drag and drop files here or click to upload
|
|
74
|
+
add_item: Add item
|
|
75
|
+
remove_item: Remove item
|
|
74
76
|
|
|
75
77
|
invitation_mailer:
|
|
76
78
|
invite_mail_subject: You received an invitation
|
data/config/locales/fr.yml
CHANGED
|
@@ -73,6 +73,8 @@ fr:
|
|
|
73
73
|
operation_failed_title: Échec de l'opération
|
|
74
74
|
operation_failed_subtitle: Une erreur s'est produite lors de l'opération
|
|
75
75
|
dropzone_drag_and_drop_or_click: Faites glisser et déposez les fichiers ici ou cliquez pour télécharger
|
|
76
|
+
add_item: Ajouter un élément
|
|
77
|
+
remove_item: Supprimer un élément
|
|
76
78
|
|
|
77
79
|
invitation_mailer:
|
|
78
80
|
invite_mail_subject: Vous avez reçu une invitation
|
data/config/locales/it.yml
CHANGED
|
@@ -73,6 +73,8 @@ it:
|
|
|
73
73
|
operation_failed_title: Operazione fallita
|
|
74
74
|
operation_failed_subtitle: Si è verificato un errore durante l'operazione
|
|
75
75
|
dropzone_drag_and_drop_or_click: Trascina qui i file o clicca per caricare
|
|
76
|
+
add_item: Aggiungi elemento
|
|
77
|
+
remove_item: Rimuovi elemento
|
|
76
78
|
|
|
77
79
|
invitation_mailer:
|
|
78
80
|
invite_mail_subject: Hai ricevuto un invito
|
data/config/locales/ro.yml
CHANGED
|
@@ -73,6 +73,8 @@ ro:
|
|
|
73
73
|
operation_failed_title: Operație eșuată
|
|
74
74
|
operation_failed_subtitle: A apărut o eroare în timpul operației
|
|
75
75
|
dropzone_drag_and_drop_or_click: Trage și plasează fișiere aici sau fă clic pentru a le încărca
|
|
76
|
+
add_item: Adaugă un element
|
|
77
|
+
remove_item: Elimină un element
|
|
76
78
|
|
|
77
79
|
invitation_mailer:
|
|
78
80
|
invite_mail_subject: Ai primit o invitație
|
data/lib/lato/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lato
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gregorio Galante
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-11-
|
|
11
|
+
date: 2025-11-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -168,6 +168,7 @@ files:
|
|
|
168
168
|
- app/assets/javascripts/lato/controllers/lato_input_autocomplete2_controller.js
|
|
169
169
|
- app/assets/javascripts/lato/controllers/lato_input_autocomplete_controller.js
|
|
170
170
|
- app/assets/javascripts/lato/controllers/lato_input_dropzone_controller.js
|
|
171
|
+
- app/assets/javascripts/lato/controllers/lato_input_list_controller.js
|
|
171
172
|
- app/assets/javascripts/lato/controllers/lato_network_controller.js
|
|
172
173
|
- app/assets/javascripts/lato/controllers/lato_operation_controller.js
|
|
173
174
|
- app/assets/javascripts/lato/controllers/lato_tooltip_controller.js
|
|
@@ -224,6 +225,7 @@ files:
|
|
|
224
225
|
- app/views/lato/authentication/verify_email.html.erb
|
|
225
226
|
- app/views/lato/authentication/web3_signin.html.erb
|
|
226
227
|
- app/views/lato/components/_form_item_input_file_dropzone.html.erb
|
|
228
|
+
- app/views/lato/components/_form_item_input_list.html.erb
|
|
227
229
|
- app/views/lato/components/_index.html.erb
|
|
228
230
|
- app/views/lato/components/_navbar_nav_item.html.erb
|
|
229
231
|
- app/views/lato/components/_navbar_nav_locales_item.html.erb
|