pg_rails 7.0.7 → 7.0.8.pre.alpha.6

Sign up to get free protection for your applications and to get access to all the features.
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