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