import { Controller } from '@hotwired/stimulus' import * as React from 'react' import { renderToStaticMarkup } from 'react-dom/server' export default class extends Controller { static outlets = ['modal'] lastValue = null subWrapper = null elemId = null input = null originalPlaceholder = null savedInputState = null connect () { // ID único para identificar el campo y el modal this.elemId = Math.trunc(Math.random() * 1000000000) this.input = this.element.querySelector('input[type=text]') this.element.setAttribute('data-asociable-modal-outlet', `.modal-${this.elemId}`) this.element.classList.add(`asociable-${this.elemId}`) const result = document.createElement('div') result.classList.add('resultados-wrapper') this.subWrapper = document.createElement('div') this.subWrapper.setAttribute('id', `resultados-inline-${this.elemId}`) this.subWrapper.classList.add('sub-wrapper') this.subWrapper.classList.add('position-absolute') this.subWrapper.classList.add('z-1') result.appendChild(this.subWrapper) this.input.parentNode.appendChild(result) const callback = (mutationList) => { for (const mutation of mutationList) { if (mutation.type === 'childList') { this.autoScroll() } } } const observer = new MutationObserver(callback) const config = { attributes: false, childList: true, subtree: true } observer.observe(this.subWrapper, config) this.resetResultados() const input = this.element.querySelector('input[type=text]') this.originalPlaceholder = input.placeholder const hiddenField = this.element.querySelector('input[type=hidden]') if (hiddenField.value) { this.element.classList.add('filled') input.setAttribute('readonly', 'true') } this.element.querySelector('.pencil').onclick = () => { input.focus() } const debounce = function (callback, wait) { let timerId return (...args) => { clearTimeout(timerId) timerId = setTimeout(() => { callback(...args) }, wait) } } const doSearchBounce = debounce((force) => { this.doSearch(force) }, 900) this.input.addEventListener('blur', () => { this.input.placeholder = this.originalPlaceholder if (!this.element.classList.contains('filled')) { this.savedInputState = this.input.value this.input.value = null } }) this.input.onfocus = () => { if (this.savedInputState && !this.input.value) { this.input.value = this.savedInputState } this.input.select() if (this.input.value.length === 0) { this.escribiAlgo() } this.autoScroll() } this.input.onkeyup = (e) => { if (this.input.value.length === 0) { this.escribiAlgo() } if ([37, 38, 39, 40].includes(e.keyCode)) { // Arrow keys, do nothing } else { if ([27].includes(e.keyCode)) { // ESC document.activeElement?.blur && document.activeElement.blur() } else { if (e.keyCode === 13) { // Enter doSearchBounce(true) } else { doSearchBounce() } } } } this.input.onkeydown = (e) => { if (e.keyCode === 13) { // Enter e.preventDefault() return false } if (this.element.classList.contains('filled')) { if (e.keyCode === 8 || e.keyCode === 46) { // Supr or Backspace this.completarCampo(null) } } } this.setMaxHeight() } autoScroll () { if (!this.element.closest('.modal')) { const wHeight = window.visualViewport.height const scrollTop = document.scrollingElement.scrollTop const viewPortBottom = scrollTop + wHeight const swHeight = parseInt(this.subWrapper.getBoundingClientRect().height) + 20 const inputBottom = this.input.getBoundingClientRect().bottom + scrollTop const swBottom = inputBottom + swHeight if (swBottom > viewPortBottom) { const offset = swBottom - viewPortBottom document.scrollingElement.scroll({ top: scrollTop + offset }) } } } resetResultados () { this.lastValue = null const rows = [] if (this.element.dataset.puedeCrear) { rows.push( Nuevo ) } if (this.element.dataset.preload) { JSON.parse(this.element.dataset.preload).forEach((object) => { rows.push( {object.to_s} ) }) } this.subWrapper.innerHTML = renderToStaticMarkup(