pg_rails 7.6.7 → 7.6.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +16 -5
- data/pg_associable/app/helpers/pg_associable/helpers.rb +2 -1
- data/pg_associable/app/javascript/asociable_controller.tsx +12 -11
- data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +14 -3
- data/pg_associable/spec/system/associable_spec.rb +1 -1
- data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +4 -0
- data/pg_engine/app/components/inline_edit/inline_component.rb +0 -6
- data/pg_engine/app/components/inline_edit/inline_edit_component.html.slim +17 -16
- data/pg_engine/app/components/inline_edit/inline_edit_component.rb +1 -1
- data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +7 -0
- data/pg_engine/app/views/active_storage/blobs/_blob.html.erb +16 -1
- data/pg_engine/config/simple_form/simple_form_bootstrap.rb +16 -1
- data/pg_layout/app/javascript/application.js +26 -0
- data/pg_layout/app/javascript/controllers/pg_form_controller.js +15 -0
- data/pg_rails/lib/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: 91cd30e1ebe3fd2ec058f38ac2ad843421e8bb65c989aa5c8b5b7597703719f5
|
4
|
+
data.tar.gz: ceeaa8d7d5a2e3cfb32b39ff6c5da0b4c497a826685ef35f92be2cade7ad2d28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e4444db810e6836a963c75fa5727c39f434c6032c269671bd9286fca0eb788a3013c45f494cebf2c583df4bc0c37803922d86090bc10685e46fee4b158919aa
|
7
|
+
data.tar.gz: a44d48f9d63295bdf519ac6ba424ebc0d1ed1c69f28b5c8be898b1c58dc72165a8ae3b5bb75f4ac47efc13987fba2e134cf275f03a7ac00f9251774f1f387c78
|
@@ -6,7 +6,7 @@ module PgAssociable
|
|
6
6
|
mod.include PgEngine::RouteHelper
|
7
7
|
end
|
8
8
|
|
9
|
-
|
9
|
+
LIMIT_TO_AUTOPRELOAD = 10
|
10
10
|
# TODO: si está entre 10 y 50, habilitar un buscador por js
|
11
11
|
|
12
12
|
def pg_associable_pro(atributo, options = {})
|
@@ -27,15 +27,21 @@ module PgAssociable
|
|
27
27
|
return input(atributo, options) if options[:disabled]
|
28
28
|
|
29
29
|
collection, puede_crear = collection_pc(atributo, options)
|
30
|
-
|
30
|
+
collection_count = collection.count
|
31
|
+
options.deep_merge!({ wrapper_html: { 'data-puede-crear': text_for_new(atributo) } }) if puede_crear
|
31
32
|
|
32
|
-
if !puede_crear &&
|
33
|
+
if !puede_crear && collection_count <= LIMIT_TO_AUTOPRELOAD
|
33
34
|
select_comun(atributo, options, collection)
|
34
35
|
else
|
35
|
-
select_pro(atributo, options, collection)
|
36
|
+
select_pro(atributo, options, collection, collection_count)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
40
|
+
def text_for_new(atributo)
|
41
|
+
klass = clase_asociacion(atributo)
|
42
|
+
klass.new.decorate.text_for_new
|
43
|
+
end
|
44
|
+
|
39
45
|
def collection_pc(atributo, _options)
|
40
46
|
klass = clase_asociacion(atributo)
|
41
47
|
user = Current.user
|
@@ -45,7 +51,12 @@ module PgAssociable
|
|
45
51
|
[collection, puede_crear]
|
46
52
|
end
|
47
53
|
|
48
|
-
def select_pro(atributo, options, collection)
|
54
|
+
def select_pro(atributo, options, collection, collection_count = nil)
|
55
|
+
if collection_count.present? && collection_count.positive? &&
|
56
|
+
collection_count < LIMIT_TO_AUTOPRELOAD && options[:preload].blank?
|
57
|
+
options[:preload] = collection
|
58
|
+
end
|
59
|
+
|
49
60
|
if (preload = options.delete(:preload))
|
50
61
|
collection = preload.is_a?(Integer) ? collection.limit(preload) : preload
|
51
62
|
options.deep_merge!({ wrapper_html: { 'data-preload': collection.decorate.to_json } })
|
@@ -16,9 +16,10 @@ module PgAssociable
|
|
16
16
|
query = params[:query]
|
17
17
|
timeout_id = params[:timeout_id]
|
18
18
|
@collection = search_in_scope(query)
|
19
|
+
@count = @collection.count
|
19
20
|
render turbo_stream:
|
20
21
|
turbo_stream.update("#{resultados_prefix}-#{params[:id]}",
|
21
|
-
partial:, locals: { collection: @collection, query:, timeout_id: })
|
22
|
+
partial:, locals: { collection: @collection, query:, timeout_id:, count: @count })
|
22
23
|
end
|
23
24
|
|
24
25
|
def search_in_scope(query)
|
@@ -138,16 +138,6 @@ export default class extends Controller {
|
|
138
138
|
resetResultados () {
|
139
139
|
this.lastValue = null
|
140
140
|
const rows = []
|
141
|
-
if (this.element.dataset.puedeCrear) {
|
142
|
-
rows.push(
|
143
|
-
<a key="new" href="javascript:void(0)"
|
144
|
-
className="list-group-item"
|
145
|
-
data-action="asociable#crearItem"
|
146
|
-
>
|
147
|
-
Nuevo
|
148
|
-
</a>
|
149
|
-
)
|
150
|
-
}
|
151
141
|
if (this.element.dataset.preload) {
|
152
142
|
JSON.parse(this.element.dataset.preload).forEach((object) => {
|
153
143
|
rows.push(
|
@@ -162,6 +152,18 @@ export default class extends Controller {
|
|
162
152
|
)
|
163
153
|
})
|
164
154
|
}
|
155
|
+
if (this.element.dataset.puedeCrear) {
|
156
|
+
rows.push(
|
157
|
+
<a key="new" href="javascript:void(0)"
|
158
|
+
className="list-group-item mt-3 text-center"
|
159
|
+
data-action="asociable#crearItem"
|
160
|
+
>
|
161
|
+
<i className="bi bi-stars"/>
|
162
|
+
|
163
|
+
{this.element.dataset.puedeCrear}
|
164
|
+
</a>
|
165
|
+
)
|
166
|
+
}
|
165
167
|
this.subWrapper.innerHTML = renderToStaticMarkup(
|
166
168
|
<div className="resultados" tabIndex={-1}>
|
167
169
|
<ul className="list-group list-group-flush">
|
@@ -279,7 +281,6 @@ export default class extends Controller {
|
|
279
281
|
<input type="hidden" name="id" value={this.elemId} />
|
280
282
|
<input type="hidden" name="query" value={this.input.value} />
|
281
283
|
<input type="hidden" name="timeout_id" value={timeouts} />
|
282
|
-
<input type="hidden" name="puede_crear" value={this.element.dataset.puedeCrear} />
|
283
284
|
</form>
|
284
285
|
)
|
285
286
|
const form = document.createElement('div')
|
@@ -1,12 +1,23 @@
|
|
1
|
-
/ # locals: (collection:, query:, timeout_id:, field_name: nil)
|
1
|
+
/ # locals: (collection:, query:, timeout_id:, count:, field_name: nil)
|
2
2
|
.resultados.inline tabindex="-1"
|
3
3
|
div data-controller="clear-timeout" data-timeout-id="#{timeout_id}"
|
4
4
|
ul.list-group.list-group-flush
|
5
|
-
- if
|
5
|
+
- if count.positive?
|
6
|
+
.text-center.fst-italic style="font-size:0.7em"
|
7
|
+
| #{count} resultados para "#{query}"
|
6
8
|
- collection.each do |object|
|
7
9
|
= link_to object.to_s, 'javascript:void(0)',
|
8
10
|
class: 'list-group-item',
|
9
11
|
data: { action: 'asociable#selectItem',
|
10
12
|
id: object.id, object: object.decorate.to_json, field_name: }
|
11
13
|
- else
|
12
|
-
li
|
14
|
+
li [class="list-group-item text-center text-warning-emphasis py-2" style="font-size: 0.85em"]
|
15
|
+
| No hay resultados para "#{query}"
|
16
|
+
|
17
|
+
- if policy(clase_modelo).new_from_associable?
|
18
|
+
/ TODO: unificar código repetido en asociable_controller.js
|
19
|
+
a [key="new" href="javascript:void(0)" class="list-group-item mt-3 text-center"
|
20
|
+
data-action="asociable#crearItem"]
|
21
|
+
i class="bi bi-stars"
|
22
|
+
|
|
23
|
+
= clase_modelo.new.decorate.text_for_new
|
@@ -14,7 +14,7 @@ describe 'Associable' do
|
|
14
14
|
fill_in 'cosa_nombre', with: 'La cosa'
|
15
15
|
select 'Los', from: 'cosa_tipo'
|
16
16
|
find('.cosa_categoria_de_cosa input[type=text]').click
|
17
|
-
expect(page).to have_text :all, '
|
17
|
+
expect(page).to have_text :all, 'Nueva categoría de cosa'
|
18
18
|
find('.cosa_categoria_de_cosa .list-group-item').click
|
19
19
|
select Account.first.to_s
|
20
20
|
fill_in 'categoria_de_cosa_nombre', with: 'la categoría'
|
@@ -8,12 +8,6 @@ class InlineComponent < ViewComponent::Base
|
|
8
8
|
super
|
9
9
|
end
|
10
10
|
|
11
|
-
def before_render
|
12
|
-
return unless controller.in_modal?
|
13
|
-
|
14
|
-
controller.instance_variable_set(:@using_modal, true)
|
15
|
-
end
|
16
|
-
|
17
11
|
SUFIJOS = %i[f text].freeze
|
18
12
|
def unsuffixed(attribute)
|
19
13
|
ret = attribute.to_s.dup
|
@@ -1,18 +1,19 @@
|
|
1
1
|
= helpers.turbo_frame_tag(@frame_id, class: 'inline-edit')
|
2
|
-
=
|
3
|
-
=
|
2
|
+
div data-controller="pg_form"
|
3
|
+
= helpers.pg_form_for(@model, render_errors: false, wrapper_mappings: @wrapper_mappings) do |f|
|
4
|
+
= hidden_field_tag :inline_attribute, @attribute
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
/ html5 solo aplica a datetime
|
7
|
+
/ date_selector solo aplica a date
|
8
|
+
/ ignore_date y prompt solo aplica a time
|
9
|
+
/ cada caso es inocuo para el resto de los types
|
10
|
+
= f.field @unsuffixed_attribute, label: false, date_selector: true, minute_step: 15,
|
11
|
+
ignore_date: true, prompt: { hour: 'hh', minute: 'mm' }
|
12
|
+
.actions.d-flex.gap-1
|
13
|
+
= button_tag class: 'btn btn-sm btn-primary',
|
14
|
+
data: { controller: 'tooltip', 'bs-title': 'Guardar' } do
|
15
|
+
i.bi.bi-check-lg
|
16
|
+
= link_to users_inline_show_path(model: @model.to_gid, attribute: @attribute),
|
17
|
+
class: 'btn btn-sm btn-secondary',
|
18
|
+
data: { controller: 'tooltip', 'bs-title': 'Cancelar' } do
|
19
|
+
i.bi.bi-x-lg
|
@@ -3,7 +3,7 @@ class InlineEditComponent < InlineComponent
|
|
3
3
|
@wrapper_mappings = {
|
4
4
|
string: :inline_form_grow,
|
5
5
|
pg_associable: :inline_form_control,
|
6
|
-
date: :
|
6
|
+
date: :inline_form_control_autosubmit,
|
7
7
|
datetime: :inline_form_control,
|
8
8
|
time: :inline_multi_select,
|
9
9
|
select: :inline_form_select
|
@@ -26,6 +26,13 @@ module PgEngine
|
|
26
26
|
truncate(object.to_s)
|
27
27
|
end
|
28
28
|
|
29
|
+
def text_for_new
|
30
|
+
klass = object.class
|
31
|
+
# TODO: no está bueno tener que hacer el new, gender debería estar en la clase
|
32
|
+
verb = klass.new.gender == 'f' ? 'Nueva' : 'Nuevo'
|
33
|
+
"#{verb} #{klass.model_name.human.downcase}"
|
34
|
+
end
|
35
|
+
|
29
36
|
# rubocop:disable Style/MissingRespondToMissing
|
30
37
|
def method_missing(method_name, *args, &)
|
31
38
|
valor = object.attributes[method_name.to_s]
|
@@ -1 +1,16 @@
|
|
1
|
-
|
1
|
+
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
|
2
|
+
<% if blob.representable? %>
|
3
|
+
<%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<%= link_to rails_blob_path(blob) do %>
|
7
|
+
<figcaption class="attachment__caption">
|
8
|
+
<% if caption = blob.try(:caption) %>
|
9
|
+
<%= caption %>
|
10
|
+
<% else %>
|
11
|
+
<span class="attachment__name"><%= blob.filename %></span>
|
12
|
+
<span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
|
13
|
+
<% end %>
|
14
|
+
</figcaption>
|
15
|
+
<% end %>
|
16
|
+
</figure>
|
@@ -310,6 +310,21 @@ SimpleForm.setup do |config|
|
|
310
310
|
b.optional :hint, wrap_with: { class: 'form-text' }
|
311
311
|
end
|
312
312
|
|
313
|
+
config.wrappers :inline_form_control_autosubmit, class: '' do |b|
|
314
|
+
b.use :html5
|
315
|
+
b.use :placeholder
|
316
|
+
b.optional :maxlength
|
317
|
+
b.optional :minlength
|
318
|
+
b.optional :pattern
|
319
|
+
b.optional :min_max
|
320
|
+
b.optional :readonly
|
321
|
+
b.use :label, class: 'visually-hidden'
|
322
|
+
|
323
|
+
b.use :input, class: 'form-control form-control-sm', error_class: 'is-invalid', 'data-action': 'pg_form#submit'
|
324
|
+
b.use :error, wrap_with: { class: 'invalid-feedback' }
|
325
|
+
b.optional :hint, wrap_with: { class: 'form-text' }
|
326
|
+
end
|
327
|
+
|
313
328
|
config.wrappers :inline_form_select, class: '' do |b|
|
314
329
|
b.use :html5
|
315
330
|
b.use :placeholder
|
@@ -320,7 +335,7 @@ SimpleForm.setup do |config|
|
|
320
335
|
b.optional :readonly
|
321
336
|
b.use :label, class: 'visually-hidden'
|
322
337
|
|
323
|
-
b.use :input, class: 'form-select form-select-sm', error_class: 'is-invalid'
|
338
|
+
b.use :input, class: 'form-select form-select-sm', error_class: 'is-invalid', 'data-action': 'pg_form#submit'
|
324
339
|
b.use :error, wrap_with: { class: 'invalid-feedback' }
|
325
340
|
b.optional :hint, wrap_with: { class: 'form-text' }
|
326
341
|
end
|
@@ -5,6 +5,32 @@ import './elements'
|
|
5
5
|
|
6
6
|
import { Turbo } from '@hotwired/turbo-rails'
|
7
7
|
|
8
|
+
import Trix from 'trix'
|
9
|
+
|
10
|
+
document.addEventListener('trix-before-initialize', (ev) => {
|
11
|
+
Trix.config.lang.attachFiles = 'Adjuntar archivos'
|
12
|
+
Trix.config.lang.bold = 'Negrita'
|
13
|
+
Trix.config.lang.bullets = 'Lista sin números'
|
14
|
+
Trix.config.lang.byte = 'Byte'
|
15
|
+
Trix.config.lang.bytes = 'Bytes'
|
16
|
+
Trix.config.lang.captionPlaceholder = 'Agregá un subtítulo'
|
17
|
+
Trix.config.lang.code = 'Monospace'
|
18
|
+
Trix.config.lang.heading1 = 'Título'
|
19
|
+
Trix.config.lang.indent = 'Incrementar nivel'
|
20
|
+
Trix.config.lang.italic = 'Cursiva'
|
21
|
+
Trix.config.lang.link = 'Linkear'
|
22
|
+
Trix.config.lang.numbers = 'Lista numerada'
|
23
|
+
Trix.config.lang.outdent = 'Disminuir nivel'
|
24
|
+
Trix.config.lang.quote = 'Cita'
|
25
|
+
Trix.config.lang.redo = 'Rehacer'
|
26
|
+
Trix.config.lang.remove = 'Quitar'
|
27
|
+
Trix.config.lang.strike = 'Tachar'
|
28
|
+
Trix.config.lang.undo = 'Deshacer'
|
29
|
+
Trix.config.lang.unlink = 'Deslinkear'
|
30
|
+
Trix.config.lang.url = 'URL'
|
31
|
+
Trix.config.lang.urlPlaceholder = 'Ingresá una URL'
|
32
|
+
})
|
33
|
+
|
8
34
|
document.addEventListener('pg:record-created', (ev) => {
|
9
35
|
Turbo.visit(window.location)
|
10
36
|
setTimeout(() => {
|
@@ -34,4 +34,19 @@ export default class extends Controller {
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
}
|
37
|
+
|
38
|
+
submit () {
|
39
|
+
let form = null
|
40
|
+
if (this.element instanceof HTMLFormElement) {
|
41
|
+
form = this.element
|
42
|
+
} else {
|
43
|
+
form = this.element.querySelector('form')
|
44
|
+
}
|
45
|
+
if (form) {
|
46
|
+
form.requestSubmit()
|
47
|
+
} else {
|
48
|
+
Rollbar.error('No form found')
|
49
|
+
console.error('No form found')
|
50
|
+
}
|
51
|
+
}
|
37
52
|
}
|
data/pg_rails/lib/version.rb
CHANGED