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.
- checksums.yaml +4 -4
- data/pg_associable/app/assets/{css → stylesheets}/pg_associable.scss +1 -1
- data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +2 -19
- data/pg_associable/app/helpers/pg_associable/helpers.rb +2 -1
- data/pg_associable/app/inputs/pg_associable_input.rb +53 -0
- data/pg_associable/app/javascript/asociable_controller.tsx +171 -0
- data/pg_associable/app/{assets/js → javascript}/modal_controller.js +6 -3
- data/pg_associable/app/views/pg_associable/_resultados.html.slim +3 -1
- data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +5 -2
- data/pg_associable/index.js +2 -4
- data/pg_associable/lib/pg_associable/engine.rb +0 -4
- data/pg_engine/app/views/admin/user_accounts/_form.html.slim +2 -2
- data/pg_engine/config/initializers/active_admin.rb +1 -4
- data/pg_engine/db/seeds.rb +7 -4
- data/pg_engine/lib/tasks/auto_anotar_modelos.rake +1 -1
- data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +1 -0
- data/pg_layout/app/views/pg_layout/_flash.html.slim +1 -1
- data/pg_rails/lib/version.rb +1 -1
- data/pg_rails/scss/pg_rails.scss +1 -1
- data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +1 -1
- metadata +8 -12
- data/pg_associable/app/assets/js/asociable_controller.js +0 -58
- data/pg_associable/app/assets/js/asociable_inline_controller.js +0 -142
- data/pg_associable/app/inputs/pg_associable/pg_associable_inline_input.rb +0 -39
- data/pg_associable/app/inputs/pg_associable/pg_associable_input.rb +0 -41
- data/pg_associable/lib/pg_associable/simple_form_initializer.rb +0 -34
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6d74fa06142003ba7e436907908f3f7ca491e8ab6367796424de5af92d19217
|
4
|
+
data.tar.gz: a8a7545e0881a3fb79d3d2185314619341286488d7b1eeacb265c0adacb1b371
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f27603a1c050b0c5aada27652696a5b5cff62041d51b31ad99e4b09494a5b5f6b3f2376ce56bf725ae7bc7a3effcbb10d2851e5c9df7530902dec5a2d885f09
|
7
|
+
data.tar.gz: ce9641619c78d375984b774fd007c7054e8ac6fe605d0f2c95c0f71bddfe498b081f64589c8a566a4da48d82f60b332d20f1359c937aa8cb34c137fa7a497014
|
@@ -6,26 +6,9 @@ module PgAssociable
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def pg_associable(atributo, options = {})
|
9
|
-
|
10
|
-
options[:
|
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
|
-
|
82
|
-
|
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
|
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',
|
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: '
|
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: '
|
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]}"
|
data/pg_associable/index.js
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
import
|
2
|
-
import
|
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)
|
@@ -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.
|
9
|
-
= f.
|
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
|
data/pg_engine/db/seeds.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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' => '
|
14
|
+
'position_in_routes' => 'after',
|
15
15
|
'position_in_class' => 'before',
|
16
16
|
'position_in_test' => 'before',
|
17
17
|
'position_in_fixture' => 'before',
|
data/pg_rails/lib/version.rb
CHANGED
data/pg_rails/scss/pg_rails.scss
CHANGED
@@ -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? ? :
|
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.
|
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
|
+
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/
|
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/
|
29
|
-
- pg_associable/app/
|
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:
|
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
|