pg_rails 7.0.7 → 7.0.8.pre.alpha.6

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/pg_associable/app/assets/{css → stylesheets}/pg_associable.scss +40 -41
  4. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +36 -9
  5. data/pg_associable/app/helpers/pg_associable/helpers.rb +6 -3
  6. data/pg_associable/app/inputs/pg_associable_input.rb +53 -0
  7. data/pg_associable/app/javascript/asociable_controller.tsx +247 -0
  8. data/pg_associable/app/javascript/modal_controller.js +29 -0
  9. data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +9 -8
  10. data/pg_associable/app/views/pg_engine/base/_pg_associable_modal.html.slim +5 -32
  11. data/pg_associable/index.js +2 -4
  12. data/pg_associable/lib/pg_associable/engine.rb +0 -4
  13. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +49 -41
  14. data/pg_engine/app/controllers/pg_engine/resource_helper.rb +6 -2
  15. data/pg_engine/app/decorators/pg_engine/base_decorator.rb +2 -2
  16. data/pg_engine/app/helpers/pg_engine/flash_helper.rb +2 -2
  17. data/pg_engine/app/helpers/pg_engine/form_helper.rb +70 -0
  18. data/pg_engine/app/helpers/pg_engine/route_helper.rb +14 -6
  19. data/pg_engine/app/views/admin/accounts/_form.html.slim +1 -1
  20. data/pg_engine/app/views/admin/user_accounts/_form.html.slim +3 -3
  21. data/pg_engine/app/views/admin/users/_form.html.slim +1 -1
  22. data/pg_engine/app/views/pg_engine/base/index.html.slim +3 -3
  23. data/pg_engine/config/initializers/active_admin.rb +1 -4
  24. data/pg_engine/config/simple_form/simple_form_bootstrap.rb +17 -9
  25. data/pg_engine/db/migrate/20240222115722_create_active_storage_tables.active_storage.rb +57 -0
  26. data/pg_engine/db/seeds.rb +7 -4
  27. data/pg_engine/lib/pg_engine/engine.rb +1 -1
  28. data/pg_engine/lib/pg_engine/route_helpers.rb +1 -0
  29. data/pg_engine/lib/pg_engine/utils/pdf_preview_generator.rb +50 -0
  30. data/pg_engine/lib/pg_engine.rb +3 -0
  31. data/pg_engine/lib/tasks/auto_anotar_modelos.rake +1 -1
  32. data/pg_engine/spec/fixtures/test.pdf +0 -0
  33. data/pg_engine/spec/pg_engine/pdf_preview_generator_spec.rb +12 -0
  34. data/pg_layout/app/assets/stylesheets/sidebar.scss +10 -2
  35. data/pg_layout/app/javascript/nested_controller.js +48 -0
  36. data/pg_layout/app/javascript/pg_form_controller.js +13 -0
  37. data/pg_layout/app/javascript/utils.ts +34 -0
  38. data/pg_layout/app/views/layouts/pg_layout/devise.html.slim +1 -1
  39. data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +14 -10
  40. data/pg_layout/app/views/pg_layout/_flash.html.slim +2 -2
  41. data/pg_layout/app/views/pg_layout/_navbar.html.erb +10 -0
  42. data/pg_layout/app/views/pg_layout/_sidebar.html.erb +3 -3
  43. data/pg_layout/index.js +4 -0
  44. data/pg_rails/lib/version.rb +1 -1
  45. data/pg_rails/scss/pg_rails.scss +1 -1
  46. data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +3 -3
  47. metadata +15 -13
  48. data/pg_associable/app/assets/js/asociable_controller.js +0 -58
  49. data/pg_associable/app/assets/js/asociable_inline_controller.js +0 -142
  50. data/pg_associable/app/assets/js/modal_controller.js +0 -117
  51. data/pg_associable/app/inputs/pg_associable/pg_associable_inline_input.rb +0 -39
  52. data/pg_associable/app/inputs/pg_associable/pg_associable_input.rb +0 -41
  53. data/pg_associable/app/views/pg_associable/_resultados.html.slim +0 -9
  54. data/pg_associable/lib/pg_associable/simple_form_initializer.rb +0 -34
  55. data/pg_associable/lib/tasks/pg_associable_tasks.rake +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c615490752a2afcaa4959f18c3b58bc214849a0912c308aea4b75a48122aa859
4
- data.tar.gz: d4e8cc1fc2418a2691ef25607776de7ab1b6936367379cb4571c12925836933c
3
+ metadata.gz: 54b808ee15e453ddfc9031c04c599eea58099615a03b4d23abadec9ee3951d8c
4
+ data.tar.gz: 552aabdd132314da31928c4fcb2b59969ad7afe21c7a239f45fbc7738a1356ca
5
5
  SHA512:
6
- metadata.gz: 3c7cad41dc0953598c49c7a48ca188e24a62d8dea1d1aed95f530e5eb92faa18bbf1033aac19c8adf963f759e7d0de52e905b4001354afb282373a257f8042be
7
- data.tar.gz: 8ecd2f4a9617b57b3938f307502883681d70dab249a6f0fbb0789855b411465dac411ffe7dc3056ee0b49a40f84db8a77ce71739bd7dbc9ad81691b42c4de591
6
+ metadata.gz: 5dddf3ab456741e7f671be016484caf643c1ad09a76ba96900707ab4e992049c5eb7da1273ab82b3fd83975278b72797cf4f9c0a178da5097f6343103e7968e7
7
+ data.tar.gz: 87c679f069138a579790599626636fcf31c44ea9a1393bd13314cb2c5268c6add6c46dc89eabcb742739a2d19179d0cf475860bdf108cd981e743183f0ae1a30
data/README.md CHANGED
@@ -86,3 +86,6 @@ be rails pg_engine_engine:install:migrations
86
86
  "build:css:prefix": "postcss ./pg_rails/builds/application.css --use=autoprefixer --output=./pg_rails/builds/application.css",
87
87
  "build:css": "yarn build:css:compile && yarn build:css:prefix",
88
88
 
89
+
90
+ ruby-vips
91
+ pdftoppm
@@ -1,44 +1,47 @@
1
1
  @use 'sass:color';
2
2
 
3
- [data-state=select-item] .show-on-new-item {
4
- display: none;
5
- }
6
- [data-state=new-item] .show-on-select-item {
7
- display: none;
8
- }
9
3
  .pg-form {
10
- .pg_associable_inline, .pg_associable {
4
+ .pg_associable {
11
5
  .limpiar {
12
6
  display: none;
13
7
  position: absolute;
14
- top: 8px;
15
- right: 10px;
8
+ right: 0.6em;
9
+ top: 0;
10
+ bottom: 0;
11
+
16
12
  i {
13
+ align-self: center;
17
14
  color: #6f7071;
18
15
  }
19
16
  }
20
17
  .pencil {
18
+ display: flex;
21
19
  position: absolute;
22
- top: 6px;
23
- right: 9px;
20
+ right: 0.6em;
21
+ top: 0;
22
+ bottom: 0;
24
23
  color: #6f7071;
25
24
  cursor: pointer;
26
25
  }
26
+ .pencil::before {
27
+ align-self: center;
28
+ }
27
29
  &.focus .pencil {
28
30
  display: none;
29
31
  }
30
32
  input[type=text] {
31
33
  background-color: white;
34
+ padding-right: 2em;
32
35
  }
33
36
  &.filled {
34
37
  .pencil {
35
38
  display: none;
36
39
  }
37
40
  .limpiar {
38
- display: inline-block;
41
+ display: flex;
39
42
  }
40
- input, input:focus {
41
- background-color: color.adjust(#a7b7bb, $saturation: +10%, $lightness: +20%);
43
+ .resultados-wrapper {
44
+ display:none!important;
42
45
  }
43
46
  }
44
47
  .resultados-wrapper {
@@ -46,11 +49,6 @@
46
49
  position: relative;
47
50
  }
48
51
  &:focus-within {
49
- .search-box {
50
- // outline: 1px solid color.adjust(#a7b7bb, $saturation: +30%);
51
- // border-radius: 3px;
52
- }
53
-
54
52
  &:has(.resultados) {
55
53
  input[type=text] {
56
54
  border-bottom-left-radius: 0!important;
@@ -64,36 +62,37 @@
64
62
  display: block;
65
63
  }
66
64
  }
67
- .resultados {
68
- position: absolute;
69
- // top: 30px;
70
- background-color: white;
71
- border: 1px solid #a7b7bb;
72
- border-top: none;
73
- border-radius: 4px;
74
- padding: 5px 11px;
75
- width: 100%;
76
-
77
- border-top-left-radius: 0;
78
- border-top-right-radius: 0;
79
- }
80
- }
81
- }
82
- .modal-asociable {
83
- .buscar input[type=text] {
84
- max-width: 180px;
85
- }
86
- .resultados {
87
- margin-top: 16px;
88
- border: 1px solid #c8c8c8;
89
65
  }
90
66
  }
67
+ // .modal-asociable {
68
+ // .buscar input[type=text] {
69
+ // max-width: 180px;
70
+ // }
71
+ // .resultados {
72
+ // margin-top: 16px;
73
+ // border: 1px solid #c8c8c8;
74
+ // }
75
+ // }
91
76
  .resultados {
92
77
  .list-group-item:hover {
93
78
  background-color: color.adjust(#a7b7bb, $saturation: +10%, $lightness: +20%);
94
79
  }
95
80
  }
81
+ .sub-wrapper {
82
+ overflow: auto;
83
+ box-shadow: 0px 9px 13px -3px rgba(0, 0, 0, 0.5);
84
+ // position: absolute;
85
+ // z-index: 1;
86
+ background-color: white;
87
+ border: 1px solid #a7b7bb;
88
+ border-top: none;
89
+ border-radius: 4px;
90
+ padding: 5px 11px;
91
+ width: 100%;
96
92
 
93
+ border-top-left-radius: 0;
94
+ border-top-right-radius: 0;
95
+ }
97
96
  .modal-asociable .modal-footer {
98
97
  justify-content: space-between;
99
98
  }
@@ -1,3 +1,4 @@
1
+ # TODO: mover a form_builder
1
2
  module PgAssociable
2
3
  module FormBuilderMethods
3
4
  def self.included(mod)
@@ -5,19 +6,45 @@ module PgAssociable
5
6
  mod.include PgEngine::RouteHelper
6
7
  end
7
8
 
9
+ MAXIMO_PARA_SELECT = 10
10
+ # TODO: si está entre 10 y 50, habilitar un buscador por js
11
+
8
12
  def pg_associable(atributo, options = {})
9
- url_modal = namespaced_path(clase_asociacion(atributo), prefix: :abrir_modal)
10
- options[:as] = 'pg_associable/pg_associable'
11
- options[:wrapper] = :pg_associable
12
- options[:url_modal] = url_modal
13
+ return input(atributo, options) if options[:disabled]
14
+
15
+ collection, puede_crear = collection_pc(atributo, options)
16
+ options.deep_merge!({ wrapper_html: { 'data-puede-crear': 'true' } }) if puede_crear
17
+
18
+ if !puede_crear && collection.count <= MAXIMO_PARA_SELECT
19
+ select_comun(atributo, options, collection)
20
+ else
21
+ select_pro(atributo, options, collection)
22
+ end
23
+ end
24
+
25
+ def collection_pc(atributo, _options)
26
+ klass = clase_asociacion(atributo)
27
+ user = template.controller.current_user
28
+ in_modal = options[:asociable].present?
29
+ puede_crear = !in_modal && Pundit::PolicyFinder.new(klass).policy.new(user, klass).new?
30
+ collection = Pundit::PolicyFinder.new(klass).scope.new(user, klass).resolve
31
+ [collection, puede_crear]
32
+ end
33
+
34
+ def select_pro(atributo, options, collection)
35
+ if (preload = options.delete(:preload))
36
+ collection = preload.is_a?(Integer) ? collection.limit(preload) : preload
37
+ options.deep_merge!({ wrapper_html: { 'data-preload': collection.decorate.to_json } })
38
+ end
39
+ # TODO: usar una clase más precisa para el modal?
40
+ options.deep_merge!({ wrapper_html: { data: { controller: 'asociable',
41
+ 'asociable-modal-outlet': '.modal' } } })
42
+ options[:as] = 'pg_associable'
13
43
  association atributo, options
14
44
  end
15
45
 
16
- def pg_associable_inline(atributo, options = {})
17
- url_search = namespaced_path(clase_asociacion(atributo), prefix: :buscar)
18
- options[:as] = 'pg_associable/pg_associable_inline'
19
- options[:wrapper] = :pg_associable_inline
20
- options[:url_search] = url_search
46
+ def select_comun(atributo, options, collection)
47
+ options[:collection] = collection
21
48
  association atributo, options
22
49
  end
23
50
 
@@ -1,5 +1,7 @@
1
1
  module PgAssociable
2
2
  module Helpers
3
+ MAX_RESULTS = 8
4
+
3
5
  def pg_respond_abrir_modal
4
6
  respond_to do |format|
5
7
  format.turbo_stream do
@@ -9,10 +11,11 @@ module PgAssociable
9
11
  end
10
12
 
11
13
  def pg_respond_buscar
12
- partial = params[:partial] || 'pg_associable/resultados'
13
- @collection = policy_scope(@clase_modelo).kept.query(params[:query]).limit(6)
14
+ partial = 'pg_associable/resultados_inline'
15
+ resultados_prefix = 'resultados-inline'
16
+ @collection = policy_scope(@clase_modelo).kept.query(params[:query]).limit(MAX_RESULTS)
14
17
  render turbo_stream:
15
- turbo_stream.update("resultados-#{params[:id]}",
18
+ turbo_stream.update("#{resultados_prefix}-#{params[:id]}",
16
19
  partial:, locals: { collection: @collection })
17
20
  end
18
21
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgAssociableInput < SimpleForm::Inputs::StringInput
4
+ include ActionView::Helpers::FormTagHelper
5
+ include Rails.application.routes.url_helpers
6
+ include PgEngine::RouteHelper
7
+
8
+ def hidden_input(_wrapper_options = {})
9
+ @builder.hidden_field(attribute_name)
10
+ end
11
+
12
+ def controller
13
+ # Para que no rompa polymorphic_url en NamespaceDeductor
14
+ end
15
+
16
+ def input(wrapper_options = nil)
17
+ atributo = attribute_name.to_s.gsub('_id', '')
18
+ url_modal = namespaced_path(clase_asociacion(atributo), prefix: :abrir_modal)
19
+ url_search = namespaced_path(clase_asociacion(atributo), prefix: :buscar)
20
+
21
+ input_html_options[:data] = { url_search:, url_modal: }
22
+ input_html_options[:type] = 'text'
23
+
24
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
25
+
26
+ build_input(merged_input_options)
27
+ end
28
+
29
+ def clase_asociacion(atributo)
30
+ asociacion = object.class.reflect_on_all_associations.find { |a| a.name == atributo.to_sym }
31
+ nombre_clase = asociacion.options[:class_name]
32
+ nombre_clase = asociacion.name.to_s.camelize if nombre_clase.nil?
33
+ Object.const_get(nombre_clase)
34
+ end
35
+
36
+ def build_input(input_options)
37
+ content_tag('div', class: 'position-relative') do
38
+ hidden_input + text_field_tag(nil, object.send(reflection.name).to_s,
39
+ input_options) + limpiar + pencil
40
+ end
41
+ end
42
+
43
+ def limpiar(_wrapper_options = nil)
44
+ content_tag('a', href: 'javascript:void(0)', class: 'limpiar', title: 'Limpiar', tabindex: '0',
45
+ data: { action: 'asociable#selectItem' }) do
46
+ '<i class="bi bi-x-lg"></i>'.html_safe
47
+ end
48
+ end
49
+
50
+ def pencil(_wrapper_options = nil)
51
+ '<i tabindex="-1" class="bi bi-pencil pencil"></i>'.html_safe
52
+ end
53
+ end
@@ -0,0 +1,247 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import * as React from 'react'
3
+ import { renderToStaticMarkup } from 'react-dom/server'
4
+
5
+ export default class extends Controller {
6
+ static outlets = ['modal']
7
+
8
+ lastValue = null
9
+ subWrapper = null
10
+ elemId = null
11
+ input = null
12
+
13
+ connect () {
14
+ // ID único para identificar el campo y el modal
15
+ this.elemId = Math.trunc(Math.random() * 1000000000)
16
+
17
+ this.input = this.element.querySelector('input[type=text]')
18
+
19
+ this.element.setAttribute('data-asociable-modal-outlet', `.modal-${this.elemId}`)
20
+ this.element.classList.add(`asociable-${this.elemId}`)
21
+
22
+ const result = document.createElement('div')
23
+ result.classList.add('resultados-wrapper')
24
+ this.subWrapper = document.createElement('div')
25
+ this.subWrapper.setAttribute('id', `resultados-inline-${this.elemId}`)
26
+ this.subWrapper.classList.add('sub-wrapper')
27
+ this.subWrapper.classList.add('position-absolute')
28
+ this.subWrapper.classList.add('z-1')
29
+ result.appendChild(this.subWrapper)
30
+ this.input.parentNode.appendChild(result)
31
+
32
+ this.resetResultados()
33
+
34
+ const input = this.element.querySelector('input[type=text]')
35
+ if (input.value) {
36
+ this.element.classList.add('filled')
37
+ input.setAttribute('readonly', 'true')
38
+ }
39
+ this.element.querySelector('.pencil').onclick = () => {
40
+ input.focus()
41
+ }
42
+
43
+ const debounce = function (callback, wait) {
44
+ let timerId
45
+ return (...args) => {
46
+ clearTimeout(timerId)
47
+ timerId = setTimeout(() => {
48
+ callback(...args)
49
+ }, wait)
50
+ }
51
+ }
52
+ const doSearchBounce = debounce((force) => {
53
+ this.doSearch(force)
54
+ }, 200)
55
+
56
+ this.input.addEventListener('blur', () => {
57
+ this.input.placeholder = ''
58
+ })
59
+ this.input.onfocus = () => {
60
+ this.input.select()
61
+ if (this.input.value.length === 0) {
62
+ this.escribiAlgo()
63
+ }
64
+
65
+ if (!this.element.closest('.modal')) {
66
+ const wHeight = window.visualViewport.height
67
+ const scrollTop = document.scrollingElement.scrollTop
68
+ const viewPortBottom = scrollTop + wHeight
69
+ const swHeight = parseInt(this.subWrapper.style.maxHeight) + 20
70
+ const elementTop = this.element.getBoundingClientRect().top + scrollTop
71
+ const inputBottom = this.input.getBoundingClientRect().bottom + scrollTop
72
+ const swBottom = inputBottom + swHeight
73
+
74
+ if (swBottom > viewPortBottom) {
75
+ document.scrollingElement.scroll({ top: elementTop })
76
+ }
77
+ }
78
+ }
79
+ this.input.onkeyup = (e) => {
80
+ if (this.input.value.length === 0) {
81
+ this.escribiAlgo()
82
+ }
83
+ if (e.keyCode === 13) {
84
+ doSearchBounce(true)
85
+ } else {
86
+ doSearchBounce()
87
+ }
88
+ }
89
+ this.input.onkeydown = (e) => {
90
+ if (e.keyCode === 13) {
91
+ e.preventDefault()
92
+ return false
93
+ }
94
+ }
95
+ this.setMaxHeight()
96
+ }
97
+
98
+ resetResultados () {
99
+ const rows = []
100
+ if (this.element.dataset.puedeCrear) {
101
+ rows.push(
102
+ <a key="new" href="javascript:void(0)"
103
+ className="list-group-item"
104
+ data-action="asociable#crearItem"
105
+ >
106
+ Nuevo
107
+ </a>
108
+ )
109
+ }
110
+ if (this.element.dataset.preload) {
111
+ JSON.parse(this.element.dataset.preload).forEach((object) => {
112
+ rows.push(
113
+ <a key={object.id} href="javascript:void(0)"
114
+ className="list-group-item"
115
+ data-action="asociable#selectItem"
116
+ data-id={object.id}
117
+ data-object={JSON.stringify(object)}
118
+ >
119
+ {object.to_s}
120
+ </a>
121
+ )
122
+ })
123
+ }
124
+ this.subWrapper.innerHTML = renderToStaticMarkup(
125
+ <div className="resultados" tabIndex={-1}>
126
+ <ul className="list-group list-group-flush">
127
+ {rows}
128
+ </ul>
129
+ </div>
130
+ )
131
+ }
132
+
133
+ setMaxHeight () {
134
+ let maxHeight
135
+ if (!this.element.closest('.modal')) {
136
+ const scrollTop = document.scrollingElement.scrollTop
137
+ const inputY = this.input.getBoundingClientRect().bottom + scrollTop
138
+ const bodyHeight = document.body.getBoundingClientRect().height
139
+ maxHeight = bodyHeight - inputY
140
+ if (maxHeight < 200) {
141
+ maxHeight = 200
142
+ }
143
+ if (bodyHeight < inputY + maxHeight) {
144
+ document.body.style.height = `${inputY + maxHeight}px`
145
+ }
146
+ } else {
147
+ maxHeight = 200
148
+ }
149
+ this.subWrapper.style.maxHeight = `${maxHeight - 20}px`
150
+ }
151
+
152
+ crearItem () {
153
+ // Si ya hay un modal abierto lo abro y retorno
154
+ if (this.modalOutlets.length > 0) {
155
+ this.modalOutlet.openModal()
156
+ return
157
+ }
158
+
159
+ const elem = (
160
+ <form method="get" action={this.input.dataset.urlModal} data-turbo-stream="true">
161
+ <input type="hidden" name="id" value={this.elemId} />
162
+ </form>
163
+ )
164
+ const form = document.createElement('div')
165
+ form.innerHTML = renderToStaticMarkup(elem)
166
+ document.body.prepend(form)
167
+ form.childNodes[0].requestSubmit()
168
+ form.remove()
169
+ }
170
+
171
+ escribiAlgo () {
172
+ this.input.placeholder = 'Escribí algo para buscar'
173
+ }
174
+
175
+ buscando () {
176
+ this.subWrapper.innerHTML = renderToStaticMarkup(
177
+ <div className="resultados" tabIndex={-1}>
178
+ <div className="fst-italic text-secondary">Buscando...</div>
179
+ </div>
180
+ )
181
+ }
182
+
183
+ selectItem (e) {
184
+ if (e.target.dataset.object) {
185
+ this.completarCampo(JSON.parse(e.target.dataset.object))
186
+ } else {
187
+ this.completarCampo(null)
188
+ }
189
+ }
190
+
191
+ doSearch (force = false) {
192
+ if (this.element.classList.contains('filled')) {
193
+ return
194
+ }
195
+ if (!force && this.input.value.length < 3) {
196
+ this.resetResultados()
197
+ return
198
+ }
199
+ if (!force && this.input.value === this.lastValue) {
200
+ return
201
+ }
202
+ this.lastValue = this.input.value
203
+
204
+ const timerId = setTimeout(() => {
205
+ this.buscando()
206
+ }, 200)
207
+ document.addEventListener('turbo:before-stream-render', function () {
208
+ clearTimeout(timerId)
209
+ // TODO: testear
210
+ }, { once: true })
211
+ const elem = (
212
+ <form method="post" action={this.input.dataset.urlSearch} data-turbo-stream="true">
213
+ <input type="hidden" name="id" value={this.elemId} />
214
+ <input type="hidden" name="query" value={this.input.value} />
215
+ <input type="hidden" name="puede_crear" value={this.element.dataset.puedeCrear} />
216
+ </form>
217
+ )
218
+ const form = document.createElement('div')
219
+ form.innerHTML = renderToStaticMarkup(elem)
220
+ document.body.prepend(form)
221
+ form.childNodes[0].requestSubmit()
222
+ form.remove()
223
+ }
224
+
225
+ completarCampo (object) {
226
+ const textField = this.element.querySelector('input[type=text]')
227
+ const hiddenField = this.element.querySelector('input[type=hidden]')
228
+ if (object) {
229
+ hiddenField.value = object.id
230
+ textField.value = object.to_s
231
+ textField.setAttribute('readonly', 'true')
232
+ this.element.classList.add('filled')
233
+ this.element.dataset.object = object
234
+ const event = new CustomEvent('pg_associable:changed', { detail: object })
235
+ this.element.dispatchEvent(event)
236
+ } else {
237
+ hiddenField.value = null
238
+ textField.value = null
239
+ textField.removeAttribute('readonly')
240
+ this.element.classList.remove('filled')
241
+ this.element.dataset.object = null
242
+ const event = new CustomEvent('pg_associable:changed')
243
+ this.element.dispatchEvent(event)
244
+ }
245
+ this.resetResultados()
246
+ }
247
+ }
@@ -0,0 +1,29 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import * as bootstrap from 'bootstrap'
3
+
4
+ export default class extends Controller {
5
+ static outlets = ['asociable']
6
+ static targets = ['response']
7
+
8
+ modalPuntero = null
9
+
10
+ connect (e) {
11
+ this.modalPuntero = new bootstrap.Modal(this.element)
12
+ this.modalPuntero.show()
13
+ }
14
+
15
+ responseTargetConnected (e) {
16
+ const newObject = JSON.parse(e.dataset.response)
17
+ this.asociableOutlet.completarCampo(newObject)
18
+ this.element.remove()
19
+ }
20
+
21
+ openModal () {
22
+ this.modalPuntero.show()
23
+ }
24
+
25
+ disconnect (e) {
26
+ this.modalPuntero.dispose()
27
+ document.querySelectorAll('.modal-backdrop').forEach(e => { e.remove() })
28
+ }
29
+ }
@@ -1,12 +1,13 @@
1
- .resultados tabindex="-1"
2
- - if collection.any?
3
- .list-group.list-group-flush
4
- = link_to 'Ninguno', 'javascript:void(0)',
5
- class: 'list-group-item', data: { action: 'asociable_inline#selectItem' }
1
+ .resultados.inline tabindex="-1"
2
+ ul.list-group.list-group-flush
3
+ - if params[:puede_crear].present?
4
+ = link_to 'Nuevo', 'javascript:void(0)',
5
+ class: 'list-group-item', data: { action: 'asociable#crearItem' }
6
+ - if collection.any?
6
7
  - collection.each do |object|
7
8
  = link_to object.to_s, 'javascript:void(0)',
8
9
  class: 'list-group-item',
9
- data: { action: 'asociable_inline#selectItem', id: object.id }
10
- - else
11
- ul.list-group.list-group-flush
10
+ data: { action: 'asociable#selectItem',
11
+ id: object.id, object: object.decorate.to_json }
12
+ - else
12
13
  li.list-group-item No hay resultados para "#{params[:query]}"
@@ -2,40 +2,13 @@
2
2
  tabindex="-1" data-controller="modal"
3
3
  data-modal-asociable-outlet=".asociable-#{params[:id]}"]
4
4
  .modal-dialog
5
- .modal-content.pg_associable data-state="select-item"
5
+ / TODO: pg_associable no va
6
+ .modal-content.pg_associable
6
7
  .modal-header
7
- .modal-title.show-on-new-item
8
+ .modal-title
9
+ / TODO: género "Nuevo/a"
8
10
  h3 Nuevo #{@clase_modelo.nombre_singular.downcase}
9
- .modal-title.show-on-select-item
10
- h3 Buscar #{@clase_modelo.nombre_singular.downcase}
11
11
  a.btn-close[type="button" data-bs-dismiss="modal" aria-label="Close"]
12
12
  .modal-body
13
- .show-on-select-item
14
- - if policy_scope(@clase_modelo).kept.count.zero?
15
- p No hay #{@clase_modelo.nombre_plural.downcase} aún
16
- = link_to "Crear el primer #{@clase_modelo.nombre_singular.downcase}",
17
- 'javascript:void(0)', data: { action: 'modal#toggleCrearElegir' }
18
- - else
19
- = form_tag namespaced_path(@clase_modelo, prefix: :buscar),
20
- data: { 'modal-target': 'searchForm' },
21
- method: :post, class: 'pg-form buscar' do
22
- = hidden_field_tag :id, params[:id]
23
- = text_field_tag :query, '', class: 'form-control',
24
- placeholder: 'Escribí algo para buscar',
25
- autocomplete: 'off',
26
- data: { 'modal-target': 'searchInput' }
27
- / = button_tag 'Buscar'
28
- .resultados-wrapper id="resultados-#{params[:id]}" data-modal-target="result"
29
- #pg-associable-form.show-on-new-item
13
+ .pg-associable-form
30
14
  = render partial: 'form', locals: { object: @clase_modelo.new, asociable: true }
31
-
32
- .modal-footer
33
- = link_to "Buscar #{@clase_modelo.nombre_singular.downcase} existente",
34
- 'javascript:void(0)', class: 'btn btn-primary show-on-new-item',
35
- data: { action: 'modal#toggleCrearElegir' }
36
- = link_to "Nuevo #{@clase_modelo.nombre_singular.downcase}",
37
- 'javascript:void(0)', class: 'btn btn-primary show-on-select-item',
38
- data: { action: 'modal#toggleCrearElegir' }
39
- = link_to "Sin #{@clase_modelo.nombre_singular.downcase}",
40
- 'javascript:void(0)', class: 'btn btn-secondary',
41
- data: { action: 'modal#selectItem' }
@@ -1,7 +1,5 @@
1
- import AsociableInlineController from './app/assets/js/asociable_inline_controller'
2
- import AsociableController from './app/assets/js/asociable_controller'
3
- import ModalController from './app/assets/js/modal_controller'
1
+ import AsociableController from './app/javascript/asociable_controller'
2
+ import ModalController from './app/javascript/modal_controller'
4
3
 
5
- window.Stimulus.register('asociable_inline', AsociableInlineController)
6
4
  window.Stimulus.register('asociable', AsociableController)
7
5
  window.Stimulus.register('modal', ModalController)
@@ -1,8 +1,4 @@
1
1
  module PgAssociable
2
2
  class Engine < ::Rails::Engine
3
- # initializer 'configurar_simple_form', after: 'factory_bot.set_fixture_replacement' do
4
- initializer 'configurar_pg_scaffold' do
5
- require 'pg_associable/simple_form_initializer'
6
- end
7
3
  end
8
4
  end