pg_rails 7.0.7 → 7.0.8.pre.alpha

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 (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