lato_cms 3.0.0 → 3.0.2
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_component_form_controller.js +4 -0
- data/app/assets/javascripts/lato_cms/controllers/lato_cms_file_field_controller.js +203 -0
- data/app/assets/javascripts/lato_cms/controllers/lato_cms_gallery_field_controller.js +36 -4
- data/app/assets/javascripts/lato_cms/controllers/lato_cms_image_field_controller.js +135 -6
- data/app/assets/javascripts/lato_cms/controllers/lato_cms_repeater_controller.js +33 -0
- data/app/assets/stylesheets/lato_cms/application.scss +107 -3
- data/app/controllers/lato_cms/pages_controller.rb +120 -49
- data/app/helpers/lato_cms/pages_helper.rb +15 -2
- data/app/models/lato_cms/page.rb +35 -4
- data/app/models/lato_cms/page_field.rb +27 -3
- data/app/models/lato_cms/template_manager.rb +2 -0
- data/app/views/lato_cms/pages/_component_accordion.html.erb +13 -9
- data/app/views/lato_cms/pages/_repeater.html.erb +22 -0
- data/app/views/lato_cms/pages/_repeater_item.html.erb +28 -0
- data/app/views/lato_cms/pages/fields/_boolean.html.erb +6 -4
- data/app/views/lato_cms/pages/fields/_color.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_date.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_datetime.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_file.html.erb +40 -15
- data/app/views/lato_cms/pages/fields/_gallery.html.erb +3 -2
- data/app/views/lato_cms/pages/fields/_image.html.erb +42 -14
- data/app/views/lato_cms/pages/fields/_json.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_multiselect.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_number.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_select.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_string.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_text.html.erb +5 -3
- data/app/views/lato_cms/pages/fields/_textarea.html.erb +5 -3
- data/config/locales/en.yml +6 -0
- data/config/locales/it.yml +6 -0
- data/lib/lato_cms/version.rb +1 -1
- metadata +5 -1
|
@@ -77,62 +77,20 @@ module LatoCms
|
|
|
77
77
|
return
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
field = @page.fields.find_or_initialize_by(
|
|
82
|
-
template_id: @page.template_id,
|
|
83
|
-
template_component_id: template_component_id,
|
|
84
|
-
component_id: component_id,
|
|
85
|
-
field_id: field_id
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
field_config = component&.dig('fields', field_id)
|
|
89
|
-
field_type = field_config&.dig('type') || 'string'
|
|
90
|
-
|
|
91
|
-
case field_type
|
|
92
|
-
when 'file', 'image'
|
|
93
|
-
field.save if field.new_record?
|
|
94
|
-
if field_data[:files].present?
|
|
95
|
-
Array(field_data[:files]).compact.each { |f| field.files.attach(f) }
|
|
96
|
-
end
|
|
97
|
-
if field_data[:remove_file_ids].present?
|
|
98
|
-
Array(field_data[:remove_file_ids]).reject(&:blank?).each do |file_id_to_remove|
|
|
99
|
-
field.files.find { |f| f.id == file_id_to_remove.to_i }&.purge
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
when 'gallery'
|
|
103
|
-
field.save if field.new_record?
|
|
104
|
-
if field_data[:files].present?
|
|
105
|
-
Array(field_data[:files]).compact.each { |f| field.files.attach(f) }
|
|
106
|
-
end
|
|
107
|
-
if field_data[:remove_file_ids].present?
|
|
108
|
-
Array(field_data[:remove_file_ids]).reject(&:blank?).each do |file_id_to_remove|
|
|
109
|
-
field.files.find { |f| f.id == file_id_to_remove.to_i }&.purge
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
# Persist order: existing IDs in dragged order + any new file IDs appended at end
|
|
113
|
-
order = Array(field_data[:order]).reject(&:blank?).map(&:to_s)
|
|
114
|
-
all_ids = field.files.reload.map { |f| f.id.to_s }
|
|
115
|
-
sorted = order.select { |id| all_ids.include?(id) }
|
|
116
|
-
new_ids = all_ids - sorted
|
|
117
|
-
field.value = (sorted + new_ids).to_json
|
|
118
|
-
when 'multiselect'
|
|
119
|
-
value = field_data[:value]
|
|
120
|
-
value = Array(value).reject(&:blank?)
|
|
121
|
-
field.value = value.to_json
|
|
122
|
-
else
|
|
123
|
-
raw_value = field_data.is_a?(ActionController::Parameters) ? field_data[:value] : field_data
|
|
124
|
-
field.value = raw_value.to_s.presence
|
|
125
|
-
end
|
|
80
|
+
template_component = @page.template_components.find { |tc| tc[:template_component_id] == template_component_id.to_s }
|
|
126
81
|
|
|
127
|
-
|
|
128
|
-
|
|
82
|
+
if template_component&.dig(:repeater)
|
|
83
|
+
save_repeater_fields(template_component, component, params[:repeater_items] || {}, params[:repeater_order] || [], errors)
|
|
84
|
+
else
|
|
85
|
+
fields_data.each do |field_id, field_data|
|
|
86
|
+
save_field(component, template_component_id, component_id, field_id, field_id, field_data, errors)
|
|
129
87
|
end
|
|
130
88
|
end
|
|
131
89
|
|
|
132
90
|
respond_to do |format|
|
|
133
91
|
if errors.empty?
|
|
134
92
|
format.html { redirect_to lato_cms.pages_show_path(@page), notice: t('lato_cms.fields_saved') }
|
|
135
|
-
format.json { render json: { message: t('lato_cms.fields_saved') } }
|
|
93
|
+
format.json { render json: { message: t('lato_cms.fields_saved'), fields: @page.fields.reload.map(&:as_json) } }
|
|
136
94
|
else
|
|
137
95
|
error_messages = errors.map { |e| "#{e[:field_id]}: #{e[:errors].join(', ')}" }.join('; ')
|
|
138
96
|
format.html { redirect_to lato_cms.pages_show_path(@page), alert: error_messages }
|
|
@@ -183,6 +141,119 @@ module LatoCms
|
|
|
183
141
|
|
|
184
142
|
private
|
|
185
143
|
|
|
144
|
+
def save_repeater_fields(template_component, component, repeater_items, repeater_order, errors)
|
|
145
|
+
template_component_id = template_component[:template_component_id]
|
|
146
|
+
component_id = template_component[:component_id]
|
|
147
|
+
item_ids = Array(repeater_order).reject(&:blank?).map(&:to_s)
|
|
148
|
+
|
|
149
|
+
order_field = @page.fields.find_or_initialize_by(
|
|
150
|
+
template_id: @page.template_id,
|
|
151
|
+
template_component_id: template_component_id,
|
|
152
|
+
component_id: component_id,
|
|
153
|
+
field_id: LatoCms::PageField::REPEATER_ORDER_FIELD_ID
|
|
154
|
+
)
|
|
155
|
+
order_field.value = item_ids.to_json
|
|
156
|
+
errors << { field_id: order_field.field_id, errors: order_field.errors.full_messages } unless order_field.save
|
|
157
|
+
|
|
158
|
+
existing_item_ids = @page.fields.where(template_component_id: template_component_id).reject(&:repeater_order?).filter_map(&:repeater_item_id).uniq
|
|
159
|
+
removed_item_ids = existing_item_ids - item_ids
|
|
160
|
+
removed_item_ids.each do |item_id|
|
|
161
|
+
@page.fields
|
|
162
|
+
.where(template_component_id: template_component_id)
|
|
163
|
+
.where("field_id LIKE ?", "#{ActiveRecord::Base.sanitize_sql_like(item_id)}.%")
|
|
164
|
+
.destroy_all
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
item_ids.each do |item_id|
|
|
168
|
+
item_fields = repeater_items[item_id] || repeater_items[item_id.to_sym] || {}
|
|
169
|
+
(template_component[:fields] || {}).each_key do |field_id|
|
|
170
|
+
field_data = item_fields[field_id] || item_fields[field_id.to_sym] || {}
|
|
171
|
+
save_field(component, template_component_id, component_id, field_id, "#{item_id}.#{field_id}", field_data, errors)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def save_field(component, template_component_id, component_id, config_field_id, persisted_field_id, field_data, errors)
|
|
177
|
+
field = @page.fields.find_or_initialize_by(
|
|
178
|
+
template_id: @page.template_id,
|
|
179
|
+
template_component_id: template_component_id,
|
|
180
|
+
component_id: component_id,
|
|
181
|
+
field_id: persisted_field_id
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
field_config = component&.dig('fields', config_field_id)
|
|
185
|
+
field_type = field_config&.dig('type') || 'string'
|
|
186
|
+
|
|
187
|
+
assign_field_value(field, field_type, field_data)
|
|
188
|
+
|
|
189
|
+
unless field.save
|
|
190
|
+
errors << { field_id: persisted_field_id, errors: field.errors.full_messages }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def assign_field_value(field, field_type, field_data)
|
|
195
|
+
case field_type
|
|
196
|
+
when 'file'
|
|
197
|
+
field.save if field.new_record?
|
|
198
|
+
assign_file_field_files(field, field_data)
|
|
199
|
+
when 'image'
|
|
200
|
+
field.save if field.new_record?
|
|
201
|
+
replace_field_image(field, field_data)
|
|
202
|
+
when 'gallery'
|
|
203
|
+
field.save if field.new_record?
|
|
204
|
+
attach_field_files(field, field_data)
|
|
205
|
+
remove_field_files(field, field_data)
|
|
206
|
+
order = Array(field_data[:order]).reject(&:blank?).map(&:to_s)
|
|
207
|
+
all_ids = field.files.reload.map { |f| f.id.to_s }
|
|
208
|
+
sorted = order.select { |id| all_ids.include?(id) }
|
|
209
|
+
field.value = (sorted + (all_ids - sorted)).to_json
|
|
210
|
+
when 'multiselect'
|
|
211
|
+
field.value = Array(field_data[:value]).reject(&:blank?).to_json
|
|
212
|
+
else
|
|
213
|
+
raw_value = field_data.is_a?(ActionController::Parameters) ? field_data[:value] : field_data
|
|
214
|
+
field.value = raw_value.to_s.presence
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def attach_field_files(field, field_data)
|
|
219
|
+
return unless field_data[:files].present?
|
|
220
|
+
|
|
221
|
+
Array(field_data[:files]).compact.each { |file| field.files.attach(file) }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def assign_file_field_files(field, field_data)
|
|
225
|
+
if field.field_settings['multiple'] != true && field_data[:files].present?
|
|
226
|
+
new_file = Array(field_data[:files]).compact.first
|
|
227
|
+
return unless new_file
|
|
228
|
+
|
|
229
|
+
field.files.each(&:purge)
|
|
230
|
+
field.files.attach(new_file)
|
|
231
|
+
else
|
|
232
|
+
attach_field_files(field, field_data)
|
|
233
|
+
remove_field_files(field, field_data)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def remove_field_files(field, field_data)
|
|
238
|
+
return unless field_data[:remove_file_ids].present?
|
|
239
|
+
|
|
240
|
+
Array(field_data[:remove_file_ids]).reject(&:blank?).each do |file_id_to_remove|
|
|
241
|
+
field.files.find { |file| file.id == file_id_to_remove.to_i }&.purge
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def replace_field_image(field, field_data)
|
|
246
|
+
if field_data[:files].present?
|
|
247
|
+
new_file = Array(field_data[:files]).compact.first
|
|
248
|
+
return unless new_file
|
|
249
|
+
|
|
250
|
+
field.files.each(&:purge)
|
|
251
|
+
field.files.attach(new_file)
|
|
252
|
+
else
|
|
253
|
+
remove_field_files(field, field_data)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
186
257
|
def create_params
|
|
187
258
|
params.require(:page).permit(:title, :locale)
|
|
188
259
|
end
|
|
@@ -42,17 +42,30 @@ module LatoCms
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def lato_cms_render_field(field_id:, field_config:, page_field:)
|
|
45
|
+
def lato_cms_render_field(field_id:, field_config:, page_field:, input_name_prefix: nil, dom_id_prefix: nil)
|
|
46
46
|
render resolve_lato_cms_field_partial(field_config),
|
|
47
47
|
field_id: field_id,
|
|
48
48
|
field_config: field_config,
|
|
49
|
-
page_field: page_field
|
|
49
|
+
page_field: page_field,
|
|
50
|
+
input_name_prefix: input_name_prefix,
|
|
51
|
+
dom_id_prefix: dom_id_prefix
|
|
50
52
|
rescue ActionView::MissingTemplate => e
|
|
51
53
|
content_tag(:div, class: 'alert alert-danger mb-0') do
|
|
52
54
|
"Field '#{field_id}' render error: #{e.message}"
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
|
|
58
|
+
def lato_cms_field_input_name(field_id, suffix = 'value', input_name_prefix: nil, multiple: false)
|
|
59
|
+
base = input_name_prefix.presence || "fields[#{field_id}]"
|
|
60
|
+
name = "#{base}[#{suffix}]"
|
|
61
|
+
multiple ? "#{name}[]" : name
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def lato_cms_field_dom_id(field_id, suffix = 'value', dom_id_prefix: nil)
|
|
65
|
+
base = dom_id_prefix.presence || "fields_#{field_id}"
|
|
66
|
+
"#{base}_#{suffix}".parameterize(separator: '_')
|
|
67
|
+
end
|
|
68
|
+
|
|
56
69
|
private
|
|
57
70
|
|
|
58
71
|
def resolve_lato_cms_field_partial(field_config)
|
data/app/models/lato_cms/page.rb
CHANGED
|
@@ -87,14 +87,45 @@ module LatoCms
|
|
|
87
87
|
hash[tc[:template_component_id]] = {
|
|
88
88
|
component_id: tc[:component_id],
|
|
89
89
|
name: tc[:name],
|
|
90
|
-
fields:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
90
|
+
fields: tc[:repeater] ?
|
|
91
|
+
build_repeater_fields_json(tc, fields_by_component[tc[:template_component_id]] || []) :
|
|
92
|
+
build_component_fields_json(tc, component_fields)
|
|
94
93
|
}
|
|
95
94
|
end
|
|
96
95
|
end
|
|
97
96
|
|
|
97
|
+
def build_component_fields_json(tc, component_fields)
|
|
98
|
+
(tc[:fields] || {}).each_with_object({}) do |(fid, fconfig), fhash|
|
|
99
|
+
field = component_fields[fid.to_s]
|
|
100
|
+
fhash[fid] = field ? field.as_json : empty_field_json(fid.to_s, fconfig)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def build_repeater_fields_json(tc, component_fields)
|
|
105
|
+
order = repeater_order(component_fields)
|
|
106
|
+
fields_by_item = component_fields.reject(&:repeater_order?).group_by(&:repeater_item_id)
|
|
107
|
+
item_ids = order.presence || fields_by_item.keys.compact
|
|
108
|
+
|
|
109
|
+
item_ids.filter_map do |item_id|
|
|
110
|
+
item_fields = (fields_by_item[item_id] || []).index_by(&:base_field_id)
|
|
111
|
+
next if item_fields.blank?
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
id: item_id,
|
|
115
|
+
fields: build_component_fields_json(tc, item_fields)
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def repeater_order(component_fields)
|
|
121
|
+
order_field = component_fields.find(&:repeater_order?)
|
|
122
|
+
return [] unless order_field&.value.present?
|
|
123
|
+
|
|
124
|
+
JSON.parse(order_field.value)
|
|
125
|
+
rescue JSON::ParserError
|
|
126
|
+
[]
|
|
127
|
+
end
|
|
128
|
+
|
|
98
129
|
def empty_field_json(fid, fconfig)
|
|
99
130
|
{
|
|
100
131
|
id: nil,
|
|
@@ -10,6 +10,8 @@ module LatoCms
|
|
|
10
10
|
validates :field_id, presence: true
|
|
11
11
|
validates :field_id, uniqueness: { scope: [:page_id, :template_component_id] }
|
|
12
12
|
|
|
13
|
+
REPEATER_ORDER_FIELD_ID = "__repeater_order".freeze
|
|
14
|
+
|
|
13
15
|
before_save :parse_value
|
|
14
16
|
|
|
15
17
|
def parsed_value
|
|
@@ -34,9 +36,27 @@ module LatoCms
|
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def field_config
|
|
39
|
+
return nil if repeater_order?
|
|
40
|
+
|
|
37
41
|
component = LatoCms::TemplateManager.find_component(component_id)
|
|
38
42
|
return nil unless component
|
|
39
|
-
component.dig('fields',
|
|
43
|
+
component.dig('fields', base_field_id)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def repeater_order?
|
|
47
|
+
field_id == REPEATER_ORDER_FIELD_ID
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def repeater_item_id
|
|
51
|
+
return nil unless field_id.to_s.include?('.')
|
|
52
|
+
|
|
53
|
+
field_id.to_s.split('.', 2).first
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def base_field_id
|
|
57
|
+
return field_id unless repeater_item_id
|
|
58
|
+
|
|
59
|
+
field_id.to_s.split('.', 2).last
|
|
40
60
|
end
|
|
41
61
|
|
|
42
62
|
def field_type
|
|
@@ -58,7 +78,8 @@ module LatoCms
|
|
|
58
78
|
def as_json(_options = {})
|
|
59
79
|
result = {
|
|
60
80
|
id: id,
|
|
61
|
-
|
|
81
|
+
persisted_field_id: field_id,
|
|
82
|
+
field_id: base_field_id,
|
|
62
83
|
field_type: field_type,
|
|
63
84
|
field_name: field_name,
|
|
64
85
|
required: field_required?,
|
|
@@ -67,7 +88,9 @@ module LatoCms
|
|
|
67
88
|
}
|
|
68
89
|
|
|
69
90
|
case field_type
|
|
70
|
-
when 'file'
|
|
91
|
+
when 'file'
|
|
92
|
+
result[:attachments] = files.map { |f| attachment_as_json(f) }
|
|
93
|
+
when 'image'
|
|
71
94
|
attached = files.first
|
|
72
95
|
result[:attachments] = attached ? [attachment_as_json(attached)] : []
|
|
73
96
|
when 'gallery'
|
|
@@ -89,6 +112,7 @@ module LatoCms
|
|
|
89
112
|
id: attachment.id,
|
|
90
113
|
filename: attachment.filename.to_s,
|
|
91
114
|
content_type: attachment.content_type,
|
|
115
|
+
byte_size: attachment.byte_size,
|
|
92
116
|
url: Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)
|
|
93
117
|
}
|
|
94
118
|
end
|
|
@@ -41,6 +41,8 @@ module LatoCms
|
|
|
41
41
|
component_id: tc_config['component_id'].to_s,
|
|
42
42
|
name: tc_config['name'].presence || component&.dig('name') || tc_config['component_id'].to_s.humanize,
|
|
43
43
|
required: tc_config['required'] == true,
|
|
44
|
+
repeater: tc_config['repeater'] == true || tc_config['repeatable'] == true,
|
|
45
|
+
settings: tc_config['settings'] || {},
|
|
44
46
|
component: component,
|
|
45
47
|
fields: component ? (component['fields'] || {}) : {}
|
|
46
48
|
}
|
|
@@ -54,16 +54,20 @@
|
|
|
54
54
|
<%= hidden_field_tag :template_component_id, tc[:template_component_id] %>
|
|
55
55
|
<%= hidden_field_tag :component_id, tc[:component_id] %>
|
|
56
56
|
|
|
57
|
-
<% tc[:
|
|
58
|
-
|
|
57
|
+
<% if tc[:repeater] %>
|
|
58
|
+
<%= render 'lato_cms/pages/repeater', tc: tc, component_fields: component_fields %>
|
|
59
|
+
<% else %>
|
|
60
|
+
<% tc[:fields].each do |field_id, field_config| %>
|
|
61
|
+
<% page_field = component_fields.find { |f| f.field_id == field_id } %>
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
<div class="mb-3">
|
|
64
|
+
<%= lato_cms_render_field(
|
|
65
|
+
field_id: field_id,
|
|
66
|
+
field_config: field_config,
|
|
67
|
+
page_field: page_field
|
|
68
|
+
) %>
|
|
69
|
+
</div>
|
|
70
|
+
<% end %>
|
|
67
71
|
<% end %>
|
|
68
72
|
|
|
69
73
|
<div class="d-flex justify-content-end align-items-center gap-3 mt-1">
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% order_field = component_fields.find(&:repeater_order?) %>
|
|
2
|
+
<% saved_order = begin; order_field&.value.present? ? JSON.parse(order_field.value) : []; rescue JSON::ParserError; []; end %>
|
|
3
|
+
<% fields_by_item = component_fields.reject(&:repeater_order?).group_by(&:repeater_item_id) %>
|
|
4
|
+
<% item_ids = (saved_order.presence || fields_by_item.keys.compact) %>
|
|
5
|
+
<% item_ids = [SecureRandom.uuid] if item_ids.blank? && tc[:settings]['min'].to_i.positive? %>
|
|
6
|
+
|
|
7
|
+
<div data-controller="lato-cms-repeater">
|
|
8
|
+
<div data-lato-cms-repeater-target="items">
|
|
9
|
+
<% item_ids.each_with_index do |item_id, item_index| %>
|
|
10
|
+
<% item_fields = (fields_by_item[item_id] || []).index_by(&:base_field_id) %>
|
|
11
|
+
<%= render 'lato_cms/pages/repeater_item', tc: tc, item_id: item_id, item_index: item_index, item_fields: item_fields %>
|
|
12
|
+
<% end %>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<template data-lato-cms-repeater-target="template">
|
|
16
|
+
<%= render 'lato_cms/pages/repeater_item', tc: tc, item_id: 'NEW_RECORD', item_index: 'NEW_INDEX', item_fields: {} %>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<button type="button" class="btn btn-sm btn-outline-primary" data-action="lato-cms-repeater#add">
|
|
20
|
+
<i class="bi bi-plus-lg me-1"></i><%= t('lato_cms.repeater_add_item') %>
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<div class="card mb-3" data-lato-cms-repeater-target="item" data-repeater-item-id="<%= item_id %>">
|
|
2
|
+
<input type="hidden" name="repeater_order[]" value="<%= item_id %>" data-lato-cms-repeater-target="orderInput">
|
|
3
|
+
|
|
4
|
+
<div class="card-header d-flex justify-content-between align-items-center py-2">
|
|
5
|
+
<strong class="small"><%= tc[:name] %> #<span data-lato-cms-repeater-target="position"><%= item_index.to_i + 1 %></span></strong>
|
|
6
|
+
<button type="button" class="btn btn-sm btn-outline-danger" data-action="lato-cms-repeater#remove">
|
|
7
|
+
<i class="bi bi-trash me-1"></i><%= t('lato_cms.repeater_remove_item') %>
|
|
8
|
+
</button>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="card-body pb-2">
|
|
12
|
+
<% tc[:fields].each do |field_id, field_config| %>
|
|
13
|
+
<% page_field = item_fields[field_id.to_s] %>
|
|
14
|
+
<% input_name_prefix = "repeater_items[#{item_id}][#{field_id}]" %>
|
|
15
|
+
<% dom_id_prefix = "repeater_items_#{item_id}_#{field_id}" %>
|
|
16
|
+
|
|
17
|
+
<div class="mb-3">
|
|
18
|
+
<%= lato_cms_render_field(
|
|
19
|
+
field_id: field_id,
|
|
20
|
+
field_config: field_config,
|
|
21
|
+
page_field: page_field,
|
|
22
|
+
input_name_prefix: input_name_prefix,
|
|
23
|
+
dom_id_prefix: dom_id_prefix
|
|
24
|
+
) %>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
<% label = field_config['name'] || field_id.humanize %>
|
|
2
2
|
<% current_value = page_field&.value %>
|
|
3
3
|
<% checked = current_value == 'true' %>
|
|
4
|
+
<% input_name = lato_cms_field_input_name(field_id, input_name_prefix: local_assigns[:input_name_prefix]) %>
|
|
5
|
+
<% input_id = lato_cms_field_dom_id(field_id, dom_id_prefix: local_assigns[:dom_id_prefix]) %>
|
|
4
6
|
|
|
5
7
|
<div class="form-check form-switch">
|
|
6
|
-
<input type="hidden" name="
|
|
8
|
+
<input type="hidden" name="<%= input_name %>" value="false">
|
|
7
9
|
<input type="checkbox"
|
|
8
10
|
class="form-check-input"
|
|
9
|
-
name="
|
|
10
|
-
id="
|
|
11
|
+
name="<%= input_name %>"
|
|
12
|
+
id="<%= input_id %>"
|
|
11
13
|
value="true"
|
|
12
14
|
<%= 'checked' if checked %>>
|
|
13
|
-
<label class="form-check-label" for="
|
|
15
|
+
<label class="form-check-label" for="<%= input_id %>">
|
|
14
16
|
<%= label %>
|
|
15
17
|
</label>
|
|
16
18
|
</div>
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<% label = field_config['name'] || field_id.humanize %>
|
|
2
2
|
<% required = field_config['required'] == true %>
|
|
3
3
|
<% current_value = page_field&.value.presence || '#000000' %>
|
|
4
|
+
<% input_name = lato_cms_field_input_name(field_id, input_name_prefix: local_assigns[:input_name_prefix]) %>
|
|
5
|
+
<% input_id = lato_cms_field_dom_id(field_id, dom_id_prefix: local_assigns[:dom_id_prefix]) %>
|
|
4
6
|
|
|
5
|
-
<label class="form-label" for="
|
|
7
|
+
<label class="form-label" for="<%= input_id %>">
|
|
6
8
|
<%= label %><%= ' *' if required %>
|
|
7
9
|
</label>
|
|
8
10
|
<div class="d-flex align-items-center gap-2" data-controller="lato-cms-color-field">
|
|
9
11
|
<input type="color"
|
|
10
12
|
class="form-control form-control-color"
|
|
11
|
-
name="
|
|
12
|
-
id="
|
|
13
|
+
name="<%= input_name %>"
|
|
14
|
+
id="<%= input_id %>"
|
|
13
15
|
value="<%= current_value %>"
|
|
14
16
|
data-lato-cms-color-field-target="input"
|
|
15
17
|
data-action="input->lato-cms-color-field#update"
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
<% required = field_config['required'] == true %>
|
|
3
3
|
<% settings = field_config['settings'] || {} %>
|
|
4
4
|
<% current_value = page_field&.value || '' %>
|
|
5
|
+
<% input_name = lato_cms_field_input_name(field_id, input_name_prefix: local_assigns[:input_name_prefix]) %>
|
|
6
|
+
<% input_id = lato_cms_field_dom_id(field_id, dom_id_prefix: local_assigns[:dom_id_prefix]) %>
|
|
5
7
|
|
|
6
|
-
<label class="form-label" for="
|
|
8
|
+
<label class="form-label" for="<%= input_id %>">
|
|
7
9
|
<%= label %><%= ' *' if required %>
|
|
8
10
|
</label>
|
|
9
11
|
<input type="date"
|
|
10
12
|
class="form-control"
|
|
11
|
-
name="
|
|
12
|
-
id="
|
|
13
|
+
name="<%= input_name %>"
|
|
14
|
+
id="<%= input_id %>"
|
|
13
15
|
value="<%= current_value %>"
|
|
14
16
|
<%= 'required' if required %>
|
|
15
17
|
<% if settings['min'] %>min="<%= settings['min'] %>"<% end %>
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
<% required = field_config['required'] == true %>
|
|
3
3
|
<% settings = field_config['settings'] || {} %>
|
|
4
4
|
<% current_value = page_field&.value || '' %>
|
|
5
|
+
<% input_name = lato_cms_field_input_name(field_id, input_name_prefix: local_assigns[:input_name_prefix]) %>
|
|
6
|
+
<% input_id = lato_cms_field_dom_id(field_id, dom_id_prefix: local_assigns[:dom_id_prefix]) %>
|
|
5
7
|
|
|
6
|
-
<label class="form-label" for="
|
|
8
|
+
<label class="form-label" for="<%= input_id %>">
|
|
7
9
|
<%= label %><%= ' *' if required %>
|
|
8
10
|
</label>
|
|
9
11
|
<input type="datetime-local"
|
|
10
12
|
class="form-control"
|
|
11
|
-
name="
|
|
12
|
-
id="
|
|
13
|
+
name="<%= input_name %>"
|
|
14
|
+
id="<%= input_id %>"
|
|
13
15
|
value="<%= current_value %>"
|
|
14
16
|
<%= 'required' if required %>
|
|
15
17
|
<% if settings['min'] %>min="<%= settings['min'] %>"<% end %>
|
|
@@ -1,29 +1,54 @@
|
|
|
1
1
|
<% label = field_config['name'] || field_id.humanize %>
|
|
2
2
|
<% required = field_config['required'] == true %>
|
|
3
3
|
<% settings = field_config['settings'] || {} %>
|
|
4
|
-
<% multiple = settings['multiple']
|
|
4
|
+
<% multiple = settings['multiple'] == true %>
|
|
5
5
|
<% accept = settings['accept'] %>
|
|
6
|
+
<% files_input_name = lato_cms_field_input_name(field_id, 'files', input_name_prefix: local_assigns[:input_name_prefix], multiple: true) %>
|
|
7
|
+
<% remove_input_name = lato_cms_field_input_name(field_id, 'remove_file_ids', input_name_prefix: local_assigns[:input_name_prefix], multiple: true) %>
|
|
8
|
+
<% input_id = lato_cms_field_dom_id(field_id, 'files', dom_id_prefix: local_assigns[:dom_id_prefix]) %>
|
|
6
9
|
|
|
7
10
|
<label class="form-label">
|
|
8
11
|
<%= label %><%= ' *' if required %>
|
|
9
12
|
</label>
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
<div data-controller="lato-cms-file-field"
|
|
15
|
+
data-field-id="<%= field_id %>"
|
|
16
|
+
data-lato-cms-file-field-multiple-value="<%= multiple %>"
|
|
17
|
+
data-new-badge-label="<%= t('lato_cms.field_file_new_badge') %>"
|
|
18
|
+
data-remove-label="<%= t('lato_cms.field_file_remove') %>"
|
|
19
|
+
data-remove-pending-label="<%= t('lato_cms.field_file_remove_pending') %>"
|
|
20
|
+
data-undo-label="<%= t('lato_cms.field_file_remove_undo') %>">
|
|
21
|
+
<div class="mb-2" data-lato-cms-file-field-target="list">
|
|
22
|
+
<% if page_field&.files&.attached? %>
|
|
13
23
|
<% page_field.files.each do |file| %>
|
|
14
|
-
<div class="
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
24
|
+
<div class="lato-cms-file-field__item" data-lato-cms-file-field-target="item" data-attachment-id="<%= file.id %>">
|
|
25
|
+
<div class="lato-cms-file-field__info">
|
|
26
|
+
<i class="bi bi-paperclip"></i>
|
|
27
|
+
<span><%= file.blob.filename %></span>
|
|
28
|
+
<span class="text-muted small">(<%= number_to_human_size(file.blob.byte_size) %>)</span>
|
|
29
|
+
</div>
|
|
30
|
+
<button type="button" class="btn btn-sm btn-outline-danger" data-action="lato-cms-file-field#remove">
|
|
31
|
+
<i class="bi bi-trash me-1"></i><%= t('lato_cms.field_file_remove') %>
|
|
32
|
+
</button>
|
|
33
|
+
<div class="lato-cms-file-field__removed d-none" data-lato-cms-file-field-target="removedNotice">
|
|
34
|
+
<span class="text-danger small">
|
|
35
|
+
<i class="bi bi-trash me-1"></i><%= t('lato_cms.field_file_remove_pending') %>
|
|
36
|
+
</span>
|
|
37
|
+
<button type="button" class="btn btn-link btn-sm p-0" data-action="lato-cms-file-field#undo">
|
|
38
|
+
<%= t('lato_cms.field_file_remove_undo') %>
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
19
41
|
</div>
|
|
20
42
|
<% end %>
|
|
43
|
+
<% end %>
|
|
21
44
|
</div>
|
|
22
|
-
<% end %>
|
|
23
45
|
|
|
24
|
-
<input type="file"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
46
|
+
<input type="file"
|
|
47
|
+
class="form-control"
|
|
48
|
+
name="<%= files_input_name %>"
|
|
49
|
+
id="<%= input_id %>"
|
|
50
|
+
data-lato-cms-file-field-target="input"
|
|
51
|
+
data-action="change->lato-cms-file-field#addFiles"
|
|
52
|
+
<%= 'multiple' if multiple %>
|
|
53
|
+
<% if accept %>accept="<%= accept %>"<% end %>>
|
|
54
|
+
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<% required = field_config['required'] == true %>
|
|
3
3
|
<% settings = field_config['settings'] || {} %>
|
|
4
4
|
<% accept = settings['accept'] || 'image/*' %>
|
|
5
|
+
<% files_input_name = lato_cms_field_input_name(field_id, 'files', input_name_prefix: local_assigns[:input_name_prefix], multiple: true) %>
|
|
5
6
|
|
|
6
7
|
<%# Build ordered file list from value JSON or default order %>
|
|
7
8
|
<% ordered_files = begin
|
|
@@ -19,7 +20,7 @@ end %>
|
|
|
19
20
|
<%= label %><%= ' *' if required %>
|
|
20
21
|
</label>
|
|
21
22
|
|
|
22
|
-
<div data-controller="lato-cms-gallery-field" data-lato-cms-gallery-field-field-id-value="<%= field_id %>">
|
|
23
|
+
<div data-controller="lato-cms-gallery-field" data-lato-cms-gallery-field-field-id-value="<%= field_id %>" data-lato-cms-gallery-field-input-name-prefix-value="<%= local_assigns[:input_name_prefix].presence || "fields[#{field_id}]" %>">
|
|
23
24
|
<div class="lato-cms-gallery-field__grid mb-2"
|
|
24
25
|
data-lato-cms-gallery-field-target="grid"
|
|
25
26
|
data-action="dragover->lato-cms-gallery-field#onGridDragOver drop->lato-cms-gallery-field#onGridDrop">
|
|
@@ -45,7 +46,7 @@ end %>
|
|
|
45
46
|
<i class="bi bi-plus-lg me-1"></i><%= t('lato_cms.field_gallery_add_images') %>
|
|
46
47
|
<input type="file"
|
|
47
48
|
class="d-none"
|
|
48
|
-
name="
|
|
49
|
+
name="<%= files_input_name %>"
|
|
49
50
|
accept="<%= accept %>"
|
|
50
51
|
multiple
|
|
51
52
|
data-lato-cms-gallery-field-target="fileInput"
|