pg_rails 7.6.8 → 7.6.10
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/assets/stylesheets/pg_associable.scss +10 -1
- 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 -12
- 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 +6 -2
- 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/components/inline_edit/inline_show_component.html.slim +1 -1
- data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +7 -0
- data/pg_engine/config/simple_form/simple_form_bootstrap.rb +16 -1
- data/pg_layout/app/javascript/application.js +23 -21
- 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: 893c71adcb5816e5cbd09b2860163a03d84c56a4980388578f22ce31dd75e2c6
|
4
|
+
data.tar.gz: 8debfcd4a503776a404c284add03df833a2e95efea61ba51e67068203bd72492
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee18015d09a9ab2940295018f8cb5e8feb2bebb015e90b18984971f6194360cb205b614d5649155df68cbc619f173f3dcb46c9df6aa4f3f3166a00841cb244ef
|
7
|
+
data.tar.gz: 77223fb87d0035b0165b4c0edf47830dfc5493a07ec567bc6628a8c3d3407dc2d16030b03d5502d310f8fbf0d2602293b1b63c82d0912166186449798db3beed
|
@@ -82,17 +82,26 @@
|
|
82
82
|
.sub-wrapper {
|
83
83
|
overflow: auto;
|
84
84
|
box-shadow: 0px 9px 13px -3px rgba(0, 0, 0, 0.5);
|
85
|
+
z-index: 50;
|
85
86
|
// position: absolute;
|
86
87
|
// z-index: 1;
|
87
88
|
background-color: #{$body-bg};
|
88
89
|
border: 1px solid #a7b7bb;
|
89
90
|
border-top: none;
|
90
91
|
border-radius: 4px;
|
91
|
-
padding: 5px 0;
|
92
|
+
// padding: 5px 0;
|
92
93
|
width: 100%;
|
93
94
|
|
94
95
|
border-top-left-radius: 0;
|
95
96
|
border-top-right-radius: 0;
|
97
|
+
|
98
|
+
a[data-action="asociable#crearItem"] {
|
99
|
+
position: sticky;
|
100
|
+
bottom: 0;
|
101
|
+
background-color: #faf5ff;
|
102
|
+
outline: 1px solid #ababcc;
|
103
|
+
box-shadow: 20px 9px 13px 7px rgba(0, 0, 0, 0.5);
|
104
|
+
}
|
96
105
|
}
|
97
106
|
.modal-asociable .modal-footer {
|
98
107
|
justify-content: space-between;
|
@@ -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)
|
@@ -27,7 +27,6 @@ export default class extends Controller {
|
|
27
27
|
this.subWrapper.setAttribute('id', `resultados-inline-${this.elemId}`)
|
28
28
|
this.subWrapper.classList.add('sub-wrapper')
|
29
29
|
this.subWrapper.classList.add('position-absolute')
|
30
|
-
this.subWrapper.classList.add('z-1')
|
31
30
|
result.appendChild(this.subWrapper)
|
32
31
|
this.input.parentNode.appendChild(result)
|
33
32
|
|
@@ -138,16 +137,6 @@ export default class extends Controller {
|
|
138
137
|
resetResultados () {
|
139
138
|
this.lastValue = null
|
140
139
|
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
140
|
if (this.element.dataset.preload) {
|
152
141
|
JSON.parse(this.element.dataset.preload).forEach((object) => {
|
153
142
|
rows.push(
|
@@ -162,6 +151,18 @@ export default class extends Controller {
|
|
162
151
|
)
|
163
152
|
})
|
164
153
|
}
|
154
|
+
if (this.element.dataset.puedeCrear) {
|
155
|
+
rows.push(
|
156
|
+
<a key="new" href="javascript:void(0)"
|
157
|
+
className="list-group-item mt-3 text-center"
|
158
|
+
data-action="asociable#crearItem"
|
159
|
+
>
|
160
|
+
<i className="bi bi-stars"/>
|
161
|
+
|
162
|
+
{this.element.dataset.puedeCrear}
|
163
|
+
</a>
|
164
|
+
)
|
165
|
+
}
|
165
166
|
this.subWrapper.innerHTML = renderToStaticMarkup(
|
166
167
|
<div className="resultados" tabIndex={-1}>
|
167
168
|
<ul className="list-group list-group-flush">
|
@@ -279,7 +280,6 @@ export default class extends Controller {
|
|
279
280
|
<input type="hidden" name="id" value={this.elemId} />
|
280
281
|
<input type="hidden" name="query" value={this.input.value} />
|
281
282
|
<input type="hidden" name="timeout_id" value={timeouts} />
|
282
|
-
<input type="hidden" name="puede_crear" value={this.element.dataset.puedeCrear} />
|
283
283
|
</form>
|
284
284
|
)
|
285
285
|
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.text-secondary.pt-1 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'
|
@@ -211,6 +211,10 @@ input[type=datetime-local], input[type=datetime] {
|
|
211
211
|
border-bottom: 1px dotted #333;
|
212
212
|
padding-bottom: 1px;
|
213
213
|
|
214
|
+
.edit-link {
|
215
|
+
color: rgba(33, 37, 41, 0.21);
|
216
|
+
}
|
217
|
+
|
214
218
|
&:not(:has(.trix-content)) .edit-link {
|
215
219
|
margin-right: 0.25em;
|
216
220
|
}
|
@@ -221,8 +225,8 @@ input[type=datetime-local], input[type=datetime] {
|
|
221
225
|
border-bottom: 1px dotted #333;
|
222
226
|
}
|
223
227
|
|
224
|
-
|
225
|
-
i {
|
228
|
+
&:hover {
|
229
|
+
.edit-link i {
|
226
230
|
color: black;
|
227
231
|
}
|
228
232
|
}
|
@@ -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
|
@@ -4,7 +4,7 @@
|
|
4
4
|
data: { \
|
5
5
|
controller: :tooltip,
|
6
6
|
'bs-title': "Modificar #{@model.class.human_attribute_name(@attribute).downcase}" },
|
7
|
-
class: '
|
7
|
+
class: 'edit-link', style: 'font-size: 0.8em' do
|
8
8
|
i.bi.bi-pencil
|
9
9
|
span = @model.decorate.send(@attribute)
|
10
10
|
- else
|
@@ -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]
|
@@ -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,28 +5,30 @@ import './elements'
|
|
5
5
|
|
6
6
|
import { Turbo } from '@hotwired/turbo-rails'
|
7
7
|
|
8
|
+
import Trix from 'trix'
|
9
|
+
|
8
10
|
document.addEventListener('trix-before-initialize', (ev) => {
|
9
|
-
Trix.config.lang.attachFiles =
|
10
|
-
Trix.config.lang.bold =
|
11
|
-
Trix.config.lang.bullets =
|
12
|
-
Trix.config.lang.byte =
|
13
|
-
Trix.config.lang.bytes =
|
14
|
-
Trix.config.lang.captionPlaceholder =
|
15
|
-
Trix.config.lang.code =
|
16
|
-
Trix.config.lang.heading1 =
|
17
|
-
Trix.config.lang.indent =
|
18
|
-
Trix.config.lang.italic =
|
19
|
-
Trix.config.lang.link =
|
20
|
-
Trix.config.lang.numbers =
|
21
|
-
Trix.config.lang.outdent =
|
22
|
-
Trix.config.lang.quote =
|
23
|
-
Trix.config.lang.redo =
|
24
|
-
Trix.config.lang.remove =
|
25
|
-
Trix.config.lang.strike =
|
26
|
-
Trix.config.lang.undo =
|
27
|
-
Trix.config.lang.unlink =
|
28
|
-
Trix.config.lang.url =
|
29
|
-
Trix.config.lang.urlPlaceholder =
|
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'
|
30
32
|
})
|
31
33
|
|
32
34
|
document.addEventListener('pg:record-created', (ev) => {
|
@@ -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