pg_rails 7.0.7 → 7.0.8.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/pg_associable/app/assets/{css → stylesheets}/pg_associable.scss +1 -1
  3. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +2 -19
  4. data/pg_associable/app/helpers/pg_associable/helpers.rb +2 -1
  5. data/pg_associable/app/inputs/pg_associable_input.rb +53 -0
  6. data/pg_associable/app/javascript/asociable_controller.tsx +171 -0
  7. data/pg_associable/app/{assets/js → javascript}/modal_controller.js +6 -3
  8. data/pg_associable/app/views/pg_associable/_resultados.html.slim +3 -1
  9. data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +5 -2
  10. data/pg_associable/index.js +2 -4
  11. data/pg_associable/lib/pg_associable/engine.rb +0 -4
  12. data/pg_engine/app/views/admin/user_accounts/_form.html.slim +2 -2
  13. data/pg_engine/config/initializers/active_admin.rb +1 -4
  14. data/pg_engine/db/seeds.rb +7 -4
  15. data/pg_engine/lib/tasks/auto_anotar_modelos.rake +1 -1
  16. data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +1 -0
  17. data/pg_layout/app/views/pg_layout/_flash.html.slim +1 -1
  18. data/pg_rails/lib/version.rb +1 -1
  19. data/pg_rails/scss/pg_rails.scss +1 -1
  20. data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +1 -1
  21. metadata +8 -12
  22. data/pg_associable/app/assets/js/asociable_controller.js +0 -58
  23. data/pg_associable/app/assets/js/asociable_inline_controller.js +0 -142
  24. data/pg_associable/app/inputs/pg_associable/pg_associable_inline_input.rb +0 -39
  25. data/pg_associable/app/inputs/pg_associable/pg_associable_input.rb +0 -41
  26. data/pg_associable/lib/pg_associable/simple_form_initializer.rb +0 -34
  27. 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: a6d74fa06142003ba7e436907908f3f7ca491e8ab6367796424de5af92d19217
4
+ data.tar.gz: a8a7545e0881a3fb79d3d2185314619341286488d7b1eeacb265c0adacb1b371
5
5
  SHA512:
6
- metadata.gz: 3c7cad41dc0953598c49c7a48ca188e24a62d8dea1d1aed95f530e5eb92faa18bbf1033aac19c8adf963f759e7d0de52e905b4001354afb282373a257f8042be
7
- data.tar.gz: 8ecd2f4a9617b57b3938f307502883681d70dab249a6f0fbb0789855b411465dac411ffe7dc3056ee0b49a40f84db8a77ce71739bd7dbc9ad81691b42c4de591
6
+ metadata.gz: 3f27603a1c050b0c5aada27652696a5b5cff62041d51b31ad99e4b09494a5b5f6b3f2376ce56bf725ae7bc7a3effcbb10d2851e5c9df7530902dec5a2d885f09
7
+ data.tar.gz: ce9641619c78d375984b774fd007c7054e8ac6fe605d0f2c95c0f71bddfe498b081f64589c8a566a4da48d82f60b332d20f1359c937aa8cb34c137fa7a497014
@@ -7,7 +7,7 @@
7
7
  display: none;
8
8
  }
9
9
  .pg-form {
10
- .pg_associable_inline, .pg_associable {
10
+ .pg_associable {
11
11
  .limpiar {
12
12
  display: none;
13
13
  position: absolute;
@@ -6,26 +6,9 @@ module PgAssociable
6
6
  end
7
7
 
8
8
  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
9
+ options[:as] = 'pg_associable'
10
+ options[:wrapper_html] = { data: { controller: 'asociable', 'asociable-modal-outlet': '.modal' } }
13
11
  association atributo, options
14
12
  end
15
-
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
21
- association atributo, options
22
- end
23
-
24
- def clase_asociacion(atributo)
25
- asociacion = object.class.reflect_on_all_associations.find { |a| a.name == atributo.to_sym }
26
- nombre_clase = asociacion.options[:class_name]
27
- nombre_clase = asociacion.name.to_s.camelize if nombre_clase.nil?
28
- Object.const_get(nombre_clase)
29
- end
30
13
  end
31
14
  end
@@ -10,9 +10,10 @@ module PgAssociable
10
10
 
11
11
  def pg_respond_buscar
12
12
  partial = params[:partial] || 'pg_associable/resultados'
13
+ resultados = params[:resultados] || 'resultados'
13
14
  @collection = policy_scope(@clase_modelo).kept.query(params[:query]).limit(6)
14
15
  render turbo_stream:
15
- turbo_stream.update("resultados-#{params[:id]}",
16
+ turbo_stream.update("#{resultados}-#{params[:id]}",
16
17
  partial:, locals: { collection: @collection })
17
18
  end
18
19
  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,171 @@
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
+ result = null
10
+ elemId = null
11
+ input = null
12
+
13
+ connect () {
14
+ console.log('connect asociable')
15
+
16
+ // ID único para identificar el campo y el modal
17
+ this.elemId = Math.trunc(Math.random() * 1000000000)
18
+
19
+ this.input = this.element.querySelector('input[type=text]')
20
+
21
+ this.element.setAttribute('data-asociable-modal-outlet', `.modal-${this.elemId}`)
22
+ this.element.classList.add(`asociable-${this.elemId}`)
23
+
24
+ this.result = document.createElement('div')
25
+ this.result.setAttribute('id', `resultados-inline-${this.elemId}`)
26
+ this.result.classList.add('resultados-wrapper')
27
+ this.input.parentNode.appendChild(this.result)
28
+
29
+ const input = this.element.querySelector('input[type=text]')
30
+ if (input.value) {
31
+ this.element.classList.add('filled')
32
+ }
33
+ this.element.querySelector('.pencil').onclick = () => {
34
+ input.focus()
35
+ }
36
+
37
+ const debounce = function (callback, wait) {
38
+ let timerId
39
+ return (...args) => {
40
+ clearTimeout(timerId)
41
+ timerId = setTimeout(() => {
42
+ callback(...args)
43
+ }, wait)
44
+ }
45
+ }
46
+ const doSearchBounce = debounce((force) => {
47
+ this.doSearch(force)
48
+ }, 200)
49
+
50
+ this.input.addEventListener('blur', () => {
51
+ this.input.placeholder = ''
52
+ })
53
+ this.input.onfocus = () => {
54
+ this.input.select()
55
+ if (this.input.value.length === 0) {
56
+ this.escribiAlgo()
57
+ }
58
+ }
59
+ this.input.onkeyup = (e) => {
60
+ if (this.input.value.length === 0) {
61
+ this.escribiAlgo()
62
+ }
63
+ if (e.keyCode === 13) {
64
+ doSearchBounce(true)
65
+ } else {
66
+ doSearchBounce()
67
+ }
68
+ }
69
+ this.input.onkeydown = (e) => {
70
+ if (e.keyCode === 13) {
71
+ e.preventDefault()
72
+ return false
73
+ }
74
+ }
75
+ }
76
+
77
+ crearItem () {
78
+ // Si ya hay un modal abierto lo abro y retorno
79
+ if (this.modalOutlets.length > 0) {
80
+ this.modalOutlet.openModal()
81
+ return
82
+ }
83
+
84
+ const elem = (
85
+ <form method="get" action={this.input.dataset.url_modal} data-turbo-stream="true">
86
+ <input type="hidden" name="id" value={this.elemId} />
87
+ </form>
88
+ )
89
+ const form = document.createElement('div')
90
+ form.innerHTML = renderToStaticMarkup(elem)
91
+ document.body.prepend(form)
92
+ form.childNodes[0].requestSubmit()
93
+ form.remove()
94
+ }
95
+
96
+ escribiAlgo () {
97
+ this.input.placeholder = 'Escribí algo para buscar'
98
+ }
99
+
100
+ buscando () {
101
+ this.result.innerHTML = renderToStaticMarkup(
102
+ <div className="resultados" tabIndex={-1}>
103
+ <div className="fst-italic text-secondary">Buscando...</div>
104
+ </div>
105
+ )
106
+ }
107
+
108
+ selectItem (e) {
109
+ if (e.target.dataset.object) {
110
+ this.completarCampo(JSON.parse(e.target.dataset.object))
111
+ } else {
112
+ this.completarCampo(null)
113
+ }
114
+ this.result.innerHTML = ''
115
+ }
116
+
117
+ doSearch (force = false) {
118
+ if (!force && this.input.value.length < 3) {
119
+ return
120
+ }
121
+ if (!force && this.input.value === this.lastValue) {
122
+ return
123
+ }
124
+ this.lastValue = this.input.value
125
+
126
+ const timerId = setTimeout(() => {
127
+ this.buscando()
128
+ }, 200)
129
+ document.addEventListener('turbo:before-stream-render', function () {
130
+ clearTimeout(timerId)
131
+ })
132
+ const elem = (
133
+ <form method="post" action={this.input.dataset.url_search} data-turbo-stream="true">
134
+ <input type="hidden" name="id" value={this.elemId} />
135
+ <input type="hidden" name="partial" value="pg_associable/resultados_inline" />
136
+ <input type="hidden" name="resultados" value="resultados-inline" />
137
+ <input type="hidden" name="query" value={this.input.value} />
138
+ </form>
139
+ )
140
+ const form = document.createElement('div')
141
+ form.innerHTML = renderToStaticMarkup(elem)
142
+ document.body.prepend(form)
143
+ form.childNodes[0].requestSubmit()
144
+ form.remove()
145
+ }
146
+
147
+ completarCampo (object) {
148
+ const textField = this.element.querySelector('input[type=text]')
149
+ const hiddenField = this.element.querySelector('input[type=hidden]')
150
+ if (object) {
151
+ hiddenField.value = object.id
152
+ textField.value = object.to_s
153
+ this.element.classList.add('filled')
154
+ this.element.dataset.object = object
155
+ const event = new CustomEvent('pg_associable:changed', { detail: object })
156
+ this.element.dispatchEvent(event)
157
+ } else {
158
+ textField.value = null
159
+ hiddenField.value = null
160
+ this.element.classList.remove('filled')
161
+ this.element.dataset.object = null
162
+ const event = new CustomEvent('pg_associable:changed')
163
+ this.element.dispatchEvent(event)
164
+ }
165
+ this.result.innerHTML = ''
166
+ }
167
+
168
+ disconnect () {
169
+ console.log('disconnect asociable')
170
+ }
171
+ }
@@ -78,15 +78,18 @@ export default class extends Controller {
78
78
 
79
79
  selectItem (e) {
80
80
  const asociable = this.asociableOutlet
81
- // TODO: text en data
82
- asociable.completarCampo(e.target.dataset.id, e.target.text)
81
+ if (e.target.dataset.object) {
82
+ asociable.completarCampo(JSON.parse(e.target.dataset.object))
83
+ } else {
84
+ asociable.completarCampo(null)
85
+ }
83
86
  this.remove()
84
87
  }
85
88
 
86
89
  responseTargetConnected (e) {
87
90
  const newObject = JSON.parse(e.dataset.response)
88
91
  const asociable = this.asociableOutlet
89
- asociable.completarCampo(newObject.id, newObject.to_s)
92
+ asociable.completarCampo(newObject)
90
93
  this.remove()
91
94
  }
92
95
 
@@ -3,7 +3,9 @@
3
3
  .list-group.list-group-flush
4
4
  - collection.each do |object|
5
5
  = link_to object.to_s, 'javascript:void(0)',
6
- class: 'list-group-item', data: { action: 'modal#selectItem', id: object.id }
6
+ class: 'list-group-item',
7
+ data: { action: 'modal#selectItem', id: object.id, \
8
+ object: object.decorate.to_json }
7
9
  - else
8
10
  ul.list-group.list-group-flush
9
11
  li.list-group-item No hay resultados para "#{params[:query]}"
@@ -1,12 +1,15 @@
1
1
  .resultados tabindex="-1"
2
2
  - if collection.any?
3
3
  .list-group.list-group-flush
4
+ = link_to 'Crear', 'javascript:void(0)',
5
+ class: 'list-group-item', data: { action: 'asociable#crearItem' }
4
6
  = link_to 'Ninguno', 'javascript:void(0)',
5
- class: 'list-group-item', data: { action: 'asociable_inline#selectItem' }
7
+ class: 'list-group-item', data: { action: 'asociable#selectItem' }
6
8
  - collection.each do |object|
7
9
  = link_to object.to_s, 'javascript:void(0)',
8
10
  class: 'list-group-item',
9
- data: { action: 'asociable_inline#selectItem', id: object.id }
11
+ data: { action: 'asociable#selectItem',
12
+ id: object.id, object: object.decorate.to_json }
10
13
  - else
11
14
  ul.list-group.list-group-flush
12
15
  li.list-group-item No hay resultados para "#{params[:query]}"
@@ -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
@@ -5,8 +5,8 @@ div style="max-width: 300px"
5
5
  = f.mensajes_de_error
6
6
 
7
7
  = hidden_field_tag :asociable, true if asociable
8
- = f.pg_associable_inline :user
9
- = f.pg_associable_inline :account
8
+ = f.pg_associable :user
9
+ = f.pg_associable :account
10
10
  = f.input :profiles
11
11
  .mt-2
12
12
  = f.button :submit
@@ -34,10 +34,7 @@ ActiveAdmin.setup do |config|
34
34
  # File.join(Rails.root, 'app', 'admin'),
35
35
  # File.join(Rails.root, 'app', 'cashier')
36
36
  # ]
37
- config.load_paths = [
38
- File.join(Rails.root, 'app', 'admin'),
39
- File.join(PgEngine::Engine.root, 'app', 'admin')
40
- ]
37
+ config.load_paths.push(File.join(PgEngine::Engine.root, 'app', 'admin'))
41
38
  # == Default Namespace
42
39
  #
43
40
  # Set the default namespace each administration resource
@@ -1,5 +1,8 @@
1
- ['mrosso10@gmail.com'].each do |mail|
2
- unless User.exists?(email: mail)
3
- User.create(email: mail, password: 'admin123', confirmed_at: Time.now, developer: true)
4
- end
1
+ DatabaseCleaner.clean_with(:truncation, except: %w(ar_internal_metadata users accounts user_accounts))
2
+
3
+ MAIL = 'mrosso10@gmail.com'
4
+
5
+ unless User.where(email: MAIL).exists?
6
+ FactoryBot.create :user, email: MAIL, password: 'admin123',
7
+ confirmed_at: Time.now, developer: true
5
8
  end
@@ -11,7 +11,7 @@ if Rails.env.development?
11
11
  'additional_file_patterns' => [],
12
12
  'routes' => 'true',
13
13
  'models' => 'true',
14
- 'position_in_routes' => 'before',
14
+ 'position_in_routes' => 'after',
15
15
  'position_in_class' => 'before',
16
16
  'position_in_test' => 'before',
17
17
  'position_in_fixture' => 'before',
@@ -3,6 +3,7 @@ html
3
3
  head
4
4
  title = Rails.application.class.module_parent_name
5
5
  meta name="viewport" content="width=device-width,initial-scale=1"
6
+ meta name="turbo-cache-control" content="no-cache"
6
7
  = csrf_meta_tags
7
8
  = csp_meta_tag
8
9
 
@@ -5,6 +5,6 @@
5
5
 
6
6
  - flash_to_show.each do |flash_type, message|
7
7
  .toast(class="bg-#{flash_type_to_class(flash_type)}-subtle" role="alert"
8
- aria-live="assertive" aria-atomic="true")
8
+ data-turbo-temporary="true" aria-live="assertive" aria-atomic="true")
9
9
  .toast-body
10
10
  = message
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.0.7'
4
+ VERSION = '7.0.8-alpha'
5
5
  end
@@ -1,3 +1,3 @@
1
1
  @import './../../pg_engine/app/assets/stylesheets/pg_rails_b5';
2
- @import './../../pg_associable/app/assets/css/pg_associable';
2
+ @import './../../pg_associable/app/assets/stylesheets/pg_associable';
3
3
  @import './../../pg_layout/app/assets/stylesheets/sidebar';
@@ -6,7 +6,7 @@ div style="max-width: 300px"
6
6
 
7
7
  = hidden_field_tag :asociable, true if asociable
8
8
  <%- attributes.each do |attribute| -%>
9
- = f.<%= attribute.reference? ? :pg_associable_inline : :input %> :<%= attribute.name %>
9
+ = f.<%= attribute.reference? ? :pg_associable : :input %> :<%= attribute.name %>
10
10
  <%- end -%>
11
11
  .mt-2
12
12
  = f.button :submit
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.7
4
+ version: 7.0.8.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martín Rosso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-11 00:00:00.000000000 Z
11
+ date: 2024-02-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Rails goodies.
14
14
  email:
@@ -19,22 +19,18 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - MIT-LICENSE
21
21
  - README.md
22
- - pg_associable/app/assets/css/pg_associable.scss
23
- - pg_associable/app/assets/js/asociable_controller.js
24
- - pg_associable/app/assets/js/asociable_inline_controller.js
25
- - pg_associable/app/assets/js/modal_controller.js
22
+ - pg_associable/app/assets/stylesheets/pg_associable.scss
26
23
  - pg_associable/app/helpers/pg_associable/form_builder_methods.rb
27
24
  - pg_associable/app/helpers/pg_associable/helpers.rb
28
- - pg_associable/app/inputs/pg_associable/pg_associable_inline_input.rb
29
- - pg_associable/app/inputs/pg_associable/pg_associable_input.rb
25
+ - pg_associable/app/inputs/pg_associable_input.rb
26
+ - pg_associable/app/javascript/asociable_controller.tsx
27
+ - pg_associable/app/javascript/modal_controller.js
30
28
  - pg_associable/app/views/pg_associable/_resultados.html.slim
31
29
  - pg_associable/app/views/pg_associable/_resultados_inline.html.slim
32
30
  - pg_associable/app/views/pg_engine/base/_pg_associable_modal.html.slim
33
31
  - pg_associable/index.js
34
32
  - pg_associable/lib/pg_associable.rb
35
33
  - pg_associable/lib/pg_associable/engine.rb
36
- - pg_associable/lib/pg_associable/simple_form_initializer.rb
37
- - pg_associable/lib/tasks/pg_associable_tasks.rake
38
34
  - pg_associable/spec/pg_associable/helpers_spec.rb
39
35
  - pg_engine/app/admin/accounts.rb
40
36
  - pg_engine/app/admin/audits.rb
@@ -220,9 +216,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
220
216
  version: '3.0'
221
217
  required_rubygems_version: !ruby/object:Gem::Requirement
222
218
  requirements:
223
- - - ">="
219
+ - - ">"
224
220
  - !ruby/object:Gem::Version
225
- version: '0'
221
+ version: 1.3.1
226
222
  requirements: []
227
223
  rubygems_version: 3.3.22
228
224
  signing_key:
@@ -1,58 +0,0 @@
1
- import { Controller } from '@hotwired/stimulus'
2
-
3
- export default class extends Controller {
4
- static outlets = ['modal']
5
-
6
- lastValue = null
7
-
8
- connect (e) {
9
- console.log('connect asociable')
10
-
11
- // ID único para identificar el campo y el modal
12
- const elemId = Math.trunc(Math.random() * 1000000000)
13
- this.element.setAttribute('data-asociable-modal-outlet', `.modal-${elemId}`)
14
- this.element.classList.add(`asociable-${elemId}`)
15
-
16
- const that = this
17
- const modalLink = this.targets.element.querySelector('.modal-link')
18
- const path = modalLink.attributes.href.value
19
- modalLink.setAttribute('href', `${path}?id=${elemId}`)
20
- modalLink.onclick = (e) => {
21
- // Si ya hay un modal abierto lo abro y evito que se haga el get
22
- // Si no, dejo que se ejecute el comportamiento por default
23
- if (that.modalOutlets.length > 0) {
24
- e.preventDefault()
25
- that.modalOutlet.openModal()
26
- }
27
- }
28
- const input = this.element.querySelector('input[type=text]')
29
- if (input.value) {
30
- this.element.classList.add('filled')
31
- }
32
- }
33
-
34
- selectItem (e) {
35
- // TODO: text en data
36
- this.completarCampo(e.target.dataset.id, e.target.text)
37
- }
38
-
39
- completarCampo (id, text) {
40
- const textField = this.element.querySelector('input[type=text]')
41
- const hiddenField = this.element.querySelector('input[type=hidden]')
42
- if (id === undefined) {
43
- id = null
44
- }
45
- hiddenField.value = id
46
- if (id) {
47
- textField.value = text
48
- this.element.classList.add('filled')
49
- } else {
50
- textField.value = null
51
- this.element.classList.remove('filled')
52
- }
53
- }
54
-
55
- disconnect (e) {
56
- console.log('disconnect asociable')
57
- }
58
- }
@@ -1,142 +0,0 @@
1
- import { Controller } from '@hotwired/stimulus'
2
-
3
- export default class extends Controller {
4
- static outlets = ['modal']
5
-
6
- result = null
7
- elemId = null
8
- input = null
9
- lastValue = ''
10
- connect (e) {
11
- console.log('connect asociable_inline')
12
- const that = this
13
- // ID único para identificar el campo y el modal
14
- this.input = this.element.querySelector('input[type=text]')
15
- this.elemId = Math.trunc(Math.random() * 1000000000)
16
- this.element.classList.add(`asociable-${this.elemId}`)
17
- this.result = document.createElement('div')
18
- this.result.setAttribute('id', `resultados-${this.elemId}`)
19
- this.result.classList.add('resultados-wrapper')
20
- this.input.parentNode.appendChild(this.result)
21
- this.input.parentNode.appendChild(this.result)
22
- this.element.querySelector('.pencil').onclick = (e) => {
23
- that.input.focus()
24
- }
25
- if (this.input.value) {
26
- this.element.classList.add('filled')
27
- }
28
-
29
- const debounce = function (callback, wait) {
30
- let timerId
31
- return (...args) => {
32
- clearTimeout(timerId)
33
- timerId = setTimeout(() => {
34
- callback(...args)
35
- }, wait)
36
- }
37
- }
38
- const doSearchBounce = debounce((force) => {
39
- that.doSearch(force)
40
- }, 200)
41
-
42
- this.input.addEventListener('blur', (e) => {
43
- this.input.placeholder = ''
44
- })
45
- this.input.onfocus = (e) => {
46
- this.input.select()
47
- if (this.input.value.length === 0) {
48
- this.escribiAlgo()
49
- }
50
- }
51
- this.input.onkeyup = (e) => {
52
- if (this.input.value.length === 0) {
53
- this.escribiAlgo()
54
- }
55
- if (e.keyCode === 13) {
56
- doSearchBounce(true)
57
- } else {
58
- doSearchBounce()
59
- }
60
- }
61
- this.input.onkeydown = (e) => {
62
- if (e.keyCode === 13) {
63
- e.preventDefault()
64
- return false
65
- }
66
- }
67
- }
68
-
69
- buscando () {
70
- this.result.innerHTML = `
71
- <div class="resultados" tabindex="-1">
72
- <div class="fst-italic text-secondary">Buscando...</div>
73
- </div>
74
- `
75
- }
76
-
77
- escribiAlgo () {
78
- this.input.placeholder = 'Escribí algo para buscar'
79
- }
80
-
81
- doSearch (force = false) {
82
- if (!force && this.input.value.length < 3) {
83
- return
84
- }
85
- if (!force && this.input.value === this.lastValue) {
86
- return
87
- }
88
- this.lastValue = this.input.value
89
-
90
- const timerId = setTimeout(() => {
91
- this.buscando()
92
- }, 200)
93
- document.addEventListener('turbo:before-stream-render', function (e) {
94
- clearTimeout(timerId)
95
- })
96
- const url = `${this.input.dataset.url}?id=${this.elemId}`
97
- const form = document.createElement('form')
98
- form.setAttribute('method', 'post')
99
- form.setAttribute('action', url)
100
- form.setAttribute('data-turbo-stream', true)
101
- const partial = document.createElement('input')
102
- partial.setAttribute('type', 'hidden')
103
- partial.setAttribute('name', 'partial')
104
- partial.setAttribute('value', 'pg_associable/resultados_inline')
105
- form.appendChild(partial)
106
- const query = document.createElement('input')
107
- query.setAttribute('type', 'hidden')
108
- query.setAttribute('name', 'query')
109
- query.setAttribute('value', this.input.value)
110
- form.appendChild(query)
111
- document.body.prepend(form)
112
- form.requestSubmit()
113
- form.remove()
114
- }
115
-
116
- completarCampo (id, text) {
117
- const textField = this.element.querySelector('input[type=text]')
118
- const hiddenField = this.element.querySelector('input[type=hidden]')
119
- if (id === undefined) {
120
- id = null
121
- }
122
- hiddenField.value = id
123
- if (id) {
124
- textField.value = text
125
- this.element.classList.add('filled')
126
- } else {
127
- this.element.classList.remove('filled')
128
- textField.value = null
129
- }
130
- }
131
-
132
- selectItem (e) {
133
- console.log('select')
134
- // TODO: text en data
135
- this.completarCampo(e.target.dataset.id, e.target.text)
136
- this.result.innerHTML = ''
137
- }
138
-
139
- disconnect (e) {
140
- console.log('disconnect asociable_inline')
141
- }
142
- }
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PgAssociable
4
- class PgAssociableInlineInput < SimpleForm::Inputs::StringInput
5
- include ActionView::Helpers::FormTagHelper
6
-
7
- def hidden_input(wrapper_options = {})
8
- merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
- # merged_input_options = merge_wrapper_options(merged_input_options, { class: 'oculto' })
10
- @builder.hidden_field(attribute_name, merged_input_options)
11
- end
12
-
13
- def search_form(wrapper_options = nil)
14
- unless string?
15
- input_html_classes.unshift('string')
16
- # input_html_options[:type] ||= input_type if html5?
17
- end
18
- input_html_options[:type] = 'text'
19
- input_html_options[:data] = { url: options[:url_search] }
20
- input_html_options[:class] = 'form-control'
21
- input_html_options[:placeholder] = ''
22
-
23
- merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
24
-
25
- text_field_tag(nil, object.send(reflection.name).to_s, merged_input_options)
26
- end
27
-
28
- def limpiar(_wrapper_options = nil)
29
- content_tag('a', href: 'javascript:void(0)', class: 'limpiar', title: 'Limpiar', tabindex: '0',
30
- data: { action: 'asociable_inline#selectItem' }) do
31
- '<i class="bi bi-x-lg"></i>'.html_safe
32
- end
33
- end
34
-
35
- def pencil(_wrapper_options = nil)
36
- '<i tabindex="-1" class="bi bi-pencil pencil"></i>'.html_safe
37
- end
38
- end
39
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PgAssociable
4
- class PgAssociableInput < SimpleForm::Inputs::StringInput
5
- include ActionView::Helpers::FormTagHelper
6
-
7
- def hidden_input(wrapper_options = {})
8
- merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
- # merged_input_options = merge_wrapper_options(merged_input_options, { class: 'oculto' })
10
- @builder.hidden_field(attribute_name, merged_input_options)
11
- end
12
-
13
- def input(wrapper_options = nil)
14
- unless string?
15
- input_html_classes.unshift('string')
16
- # input_html_options[:type] ||= input_type if html5?
17
- end
18
- input_html_options[:type] = 'text'
19
-
20
- merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
21
- merged_input_options = merge_wrapper_options(merged_input_options, { class: 'keep-disabled' })
22
-
23
- text_field_tag(nil, object.send(reflection.name).to_s, merged_input_options)
24
- end
25
-
26
- def modal_link(_wrapper_options = nil)
27
- "<a href=\"#{options[:url_modal]}\" class=\"modal-link d-inline-block\" data-turbo-stream style=\"position:absolute; left:0; right:0; top:0; bottom:0;\"></a>".html_safe
28
- end
29
-
30
- def limpiar(_wrapper_options = nil)
31
- content_tag('a', href: 'javascript:void(0)', class: 'limpiar', title: 'Limpiar', tabindex: '0',
32
- data: { action: 'asociable#selectItem' }) do
33
- '<i class="bi bi-x-lg"></i>'.html_safe
34
- end
35
- end
36
-
37
- def pencil(_wrapper_options = nil)
38
- '<i tabindex="-1" class="bi bi-pencil pencil"></i>'.html_safe
39
- end
40
- end
41
- end
@@ -1,34 +0,0 @@
1
- SimpleForm.setup do |config|
2
- config.wrappers :pg_associable, tag: 'div', class: 'pg_associable mb-3', html: { data: { controller: 'asociable', 'asociable-modal-outlet': '.modal' } },
3
- error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
4
- b.use :html5
5
- b.optional :readonly
6
- b.use :label, class: 'form-label'
7
- b.use :hidden_input
8
- b.wrapper tag: 'div', class: 'position-relative' do |ba|
9
- ba.use :input, class: 'form-control', disabled: true, placeholder: 'Clic para seleccionar...',
10
- error_class: 'is-invalid'
11
- ba.use :pencil
12
- ba.use :modal_link
13
- ba.use :limpiar
14
- end
15
- b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
16
- b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
17
- end
18
-
19
- config.wrappers :pg_associable_inline, tag: 'div', class: 'pg_associable_inline mb-3', html: { data: { controller: 'asociable_inline' } },
20
- error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
21
- b.use :html5
22
- b.optional :readonly
23
- b.use :label, class: 'form-label'
24
- b.use :hidden_input
25
- b.wrapper tag: 'div', class: 'search-box position-relative' do |ba|
26
- ba.use :search_form, error_class: 'is-invalid'
27
- ba.use :limpiar
28
- ba.use :pencil
29
- # ba.use :input, class: 'form-control', placeholder: 'Buscar...'
30
- end
31
- b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
32
- b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
33
- end
34
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :pg_associable do
3
- # # Task goes here
4
- # end