fix autocomplete

pull/1/head
yann300 6 years ago
parent bd5c16aeb4
commit 5c395f149c
  1. 138
      src/app/panels/terminal.js
  2. 188
      src/app/ui/auto-complete-popup.js
  3. 27
      src/app/ui/modaldialog.js
  4. 5
      src/app/ui/styles/auto-complete-popup-styles.js

@ -13,7 +13,6 @@ var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI')
var executionContext = require('../../execution-context')
var Dropdown = require('../ui/dropdown')
var AutoCompletePopup = require('../ui/auto-complete-popup')
var Commands = require('../constants/commands')
var csjs = require('csjs-inject')
@ -63,17 +62,12 @@ class Terminal {
})
self._components.autoCompletePopup = new AutoCompletePopup()
self._components.autoCompletePopup.event.register('handleSelect', function (input) {
self._components.autoCompletePopup.data._options = []
self._components.autoCompletePopup._startingElement = 0
let textList = self._view.input.innerText.split(' ')
textList.pop()
textList.push(input)
self._view.input.innerText = `${textList}`.replace(/,/g, ' ')
self._view.input.focus()
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
})
self._components.autoCompletePopup.event.register('updateList', function () {
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
self.putCursor2End(self._view.input)
})
self._commands = {}
self.commands = {}
@ -115,7 +109,7 @@ class Terminal {
if (self._view.el) return self._view.el
self._view.journal = yo`<div class=${css.journal}></div>`
self._view.input = yo`
<span class=${css.input} contenteditable="true" onpaste=${paste} onkeydown=${change}></span>
<span class=${css.input} spellcheck="false" contenteditable="true" onpaste=${paste} onkeydown=${change}></span>
`
self._view.input.innerText = '\n'
self._view.cli = yo`
@ -150,7 +144,7 @@ class Terminal {
${self._view.dropdown}
<div class=${css.search}>
<i class="fa fa-search ${css.searchIcon} bg-light btn" aria-hidden="true"></i>
<input type="text" class=${css.filter} onkeydown=${filter} placeholder="Search transactions">
<input spellcheck="false" type="text" class=${css.filter} onkeydown=${filter} placeholder="Search transactions">
</div>
</div>
</div>
@ -163,7 +157,6 @@ class Terminal {
</div>
</div>
`
self._view.autoCompletePopup = self._components.autoCompletePopup.render()
self._view.el = yo`
<div class="${css.panel}">
${self._view.bar}
@ -406,14 +399,16 @@ class Terminal {
return self._view.el
function change (event) {
handleAutoComplete(event)
if (self._components.autoCompletePopup.handleAutoComplete(
event,
self._view.input.innerText)) return
if (self._view.input.innerText.length === 0) self._view.input.innerText += '\n'
if (event.which === 13) {
if (event.ctrlKey) { // <ctrl+enter>
self._view.input.innerText += '\n'
putCursor2End(self._view.input)
self.putCursor2End(self._view.input)
self.scroll2bottom()
removeAutoComplete()
self._components.autoCompletePopup.removeAutoComplete()
} else { // <enter>
self._cmdIndex = -1
self._cmdTemp = ''
@ -424,102 +419,55 @@ class Terminal {
self._cmdHistory.unshift(script)
self.commands.script(script)
}
removeAutoComplete()
self._components.autoCompletePopup.removeAutoComplete()
}
} else if (event.which === 38) { // <arrowUp>
if (self._components.autoCompletePopup.data._options.length > self._components.autoCompletePopup._elementsToShow) {
self._components.autoCompletePopup._view.autoComplete.children[1].children[0].onclick(event)
} else {
var len = self._cmdHistory.length
if (len === 0) return event.preventDefault()
if (self._cmdHistory.length - 1 > self._cmdIndex) {
self._cmdIndex++
}
self._view.input.innerText = self._cmdHistory[self._cmdIndex]
putCursor2End(self._view.input)
self.scroll2bottom()
var len = self._cmdHistory.length
if (len === 0) return event.preventDefault()
if (self._cmdHistory.length - 1 > self._cmdIndex) {
self._cmdIndex++
}
self._view.input.innerText = self._cmdHistory[self._cmdIndex]
self.putCursor2End(self._view.input)
self.scroll2bottom()
} else if (event.which === 40) { // <arrowDown>
if (self._components.autoCompletePopup.data._options.length > self._components.autoCompletePopup._elementsToShow) {
self._components.autoCompletePopup._view.autoComplete.children[1].children[1].onclick(event)
} else {
if (self._cmdIndex > -1) {
self._cmdIndex--
}
self._view.input.innerText = self._cmdIndex >= 0 ? self._cmdHistory[self._cmdIndex] : self._cmdTemp
putCursor2End(self._view.input)
self.scroll2bottom()
if (self._cmdIndex > -1) {
self._cmdIndex--
}
self._view.input.innerText = self._cmdIndex >= 0 ? self._cmdHistory[self._cmdIndex] : self._cmdTemp
self.putCursor2End(self._view.input)
self.scroll2bottom()
} else {
self._cmdTemp = self._view.input.innerText
}
}
function putCursor2End (editable) {
var range = document.createRange()
range.selectNode(editable)
var child = editable
var chars
}
putCursor2End (editable) {
var range = document.createRange()
range.selectNode(editable)
var child = editable
var chars
while (child) {
if (child.lastChild) child = child.lastChild
else break
if (child.nodeType === Node.TEXT_NODE) {
chars = child.textContent.length
} else {
chars = child.innerHTML.length
}
while (child) {
if (child.lastChild) child = child.lastChild
else break
if (child.nodeType === Node.TEXT_NODE) {
chars = child.textContent.length
} else {
chars = child.innerHTML.length
}
}
range.setEnd(child, chars)
var toStart = true
var toEnd = !toStart
range.collapse(toEnd)
range.setEnd(child, chars)
var toStart = true
var toEnd = !toStart
range.collapse(toEnd)
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
editable.focus()
}
function handleAutoComplete (event) {
if (event.which === 9) {
event.preventDefault()
let textList = self._view.input.innerText.split(' ')
let autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0]
if (self._view.input.innerText.length >= 2) {
self._components.autoCompletePopup.data._options = []
Commands.allPrograms.forEach(item => {
if (Object.keys(item)[0].substring(0, Object.keys(item)[0].length - 1).includes(autoCompleteInput.trim())) {
self._components.autoCompletePopup.data._options.push(item)
} else if (autoCompleteInput.trim().includes(Object.keys(item)[0]) || (Object.keys(item)[0] === autoCompleteInput.trim())) {
Commands.allCommands.forEach(item => {
if (Object.keys(item)[0].includes(autoCompleteInput.trim())) {
self._components.autoCompletePopup.data._options.push(item)
}
})
}
})
}
if (self._components.autoCompletePopup.data._options.length === 1) {
textList.pop()
textList.push(Object.keys(self._components.autoCompletePopup.data._options[0])[0])
self._view.input.innerText = `${textList}`.replace(/,/g, ' ')
self._components.autoCompletePopup.data._options = []
putCursor2End(self._view.input)
}
}
if (event.which === 27 || event.which === 8 || event.which === 46) {
self._components.autoCompletePopup.data._options = []
self._components.autoCompletePopup._startingElement = 0
}
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
}
function removeAutoComplete () {
self._components.autoCompletePopup.data._options = []
self._components.autoCompletePopup._startingElement = 0
self._components.autoCompletePopup._removePopUp()
yo.update(self._view.autoCompletePopup, self._components.autoCompletePopup.render())
}
editable.focus()
}
updateJournal (filterEvent) {
var self = this

@ -1,12 +1,12 @@
var yo = require('yo-yo')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var Commands = require('../constants/commands')
var modal = require('./modaldialog.js')
// -------------- styling ----------------------
var css = require('./styles/auto-complete-popup-styles')
// var cssModal = require('./styles/modaldialog-styles')
/* USAGE:
@ -22,118 +22,180 @@ class AutoCompletePopup {
constructor (opts = {}) {
var self = this
self.event = new EventManager()
self.isOpen = false
self.data = {
_options: opts.options || []
}
self._components = {
modal: null
}
self._view = {}
self._startingElement = 0
self._elementsToShow = 3
self._removePopUp = this.resetCSSValuesModalContainer
}
resetCSSValuesModalContainer () {
/*
var modalContainer = document.querySelector(`.${cssModal.modal}`)
modalContainer.style.display = 'none'
var modalContent = document.querySelector(`.${css.modalContent}`)
let newModalContent = modalContent ? document.querySelector(`.${css.modalContent}`) : document.querySelector(`.${cssModal.modalContent}`)
newModalContent.className = cssModal.modalContent
*/
self._elementsToShow = 4
self._selectedElement = 0
this.render()
}
render () {
var self = this
var header = yo`<div class="${css.text}">Remix Commands</div>`
self._view.autoComplete = yo`
<div class="${css.popup}">
<div>
${self.data._options.map((item, index) => {
return yo`
<div class="${css.listHandlerHide}">
<a value=${index}>
<div onclick=${handleSelect}>
${Object.keys(item)[0]}
</div>
</a>
<div class="${css.autoCompleteItem} ${css.listHandlerHide} item ${self._selectedElement === index ? 'bg-secondary' : ''}">
<div value=${index} onclick=${(event) => { self.handleSelect(event.srcElement.innerText) }}>
${getKeyOf(item)}
</div>
<div>
${Object.values(item)[0]}
${getValueOf(item)}
</div>
<hr/>
</div>
`
})}
</div>
<div class="${css.listHandlerHide}">
<button value=false onclick=${handleListIteration}></button>
<button value=true onclick=${handleListIteration}></button>
<div class="${css.pageNumberAlignment}">Page ${(self._startingElement / self._elementsToShow) + 1} of ${Math.ceil(self.data._options.length / self._elementsToShow)}</div>
</div>
</div>
`
function setUpPopUp () {
handleOpenPopup()
handleNagivationButtons()
handleListSize()
}
function handleOpenPopup () {
if (self.data._options.length > 1) {
if (self.data._options.length > 0) {
self._view.autoComplete.style.display = 'block'
modal(header.innerText, self._view.autoComplete, {label: null},
{
fn: () => { self._removePopUp() }
})
editCSSValuesModalContainer()
}
}
function handleSelect (event) {
self._removePopUp()
self._view.autoComplete.style.display = 'none'
self.event.trigger('handleSelect', [event.srcElement.innerText])
}
function handleNagivationButtons () {
if (self.data._options.length > self._elementsToShow) {
self._view.autoComplete.children[1].className = css.listHandlerButtonShow
self._components.modal = modal('', self._view.autoComplete, {label: null}, {label: null}, null, { class: css.modalContent, hideClose: true })
}
}
function handleListSize () {
if (self.data._options.length >= self._startingElement) {
for (let i = self._startingElement; i < (self._elementsToShow + self._startingElement); i++) {
if (self._view.autoComplete.children[0].children[i]) {
self._view.autoComplete.children[0].children[i].className = css.listHandlerShow
let el = self._view.autoComplete.querySelectorAll('.item')[i]
if (el) {
el.classList.remove(css.listHandlerHide)
el.classList.add(css.listHandlerShow)
}
}
}
}
function handleListIteration (event) {
if (event.srcElement.value === 'true' || event.which === 40) {
if ((self._startingElement + self._elementsToShow) < self.data._options.length) {
self._startingElement += self._elementsToShow
}
} else {
if (self._startingElement > 0) {
self._startingElement -= self._elementsToShow
}
setUpPopUp()
return self._view
}
handleSelect (text) {
this.removeAutoComplete()
this.event.trigger('handleSelect', [text])
}
moveUp () {
if (this._selectedElement === 0) return
this._selectedElement--
this._startingElement = this._selectedElement > 0 ? this._selectedElement - 1 : 0
this.event.trigger('updateList')
yo.update(this._view, this.render())
}
moveDown () {
if (this.data._options.length <= this._selectedElement + 1) return
this._selectedElement++
this._startingElement = this._selectedElement - 1
this.event.trigger('updateList')
yo.update(this._view, this.render())
}
handleAutoComplete (event, inputString) {
if (this.isOpen && (event.which === 27 || event.which === 8 || event.which === 46)) {
// backspace or any key that should remove the autocompletion
this.removeAutoComplete()
return true
}
if (this.isOpen && (event.which === 13 || event.which === 9)) {
// enter and tab (validate completion)
event.preventDefault()
if (this.data._options[this._selectedElement]) {
this.handleSelect(getKeyOf(this.data._options[this._selectedElement]))
}
self.event.trigger('updateList')
this.removeAutoComplete()
return true
}
if (this.isOpen && event.which === 38) {
// move up
event.preventDefault()
this.isOpen = true
this.moveUp()
return true
}
if (this.isOpen && event.which === 40) {
// move down
event.preventDefault()
this.isOpen = true
this.moveDown()
return true
}
if (event.which === 13 || event.which === 9) {
// enter || tab and autocompletion is off, just returning false
return false
}
let textList = inputString.split(' ')
let autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0]
if (inputString.length >= 2) {
// more than 2 letters, start completion
this.isOpen = true
this.data._options = []
Commands.allPrograms.forEach(item => {
let program = getKeyOf(item)
if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) {
this.data._options.push(item)
} else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) {
Commands.allCommands.forEach(item => {
let command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
this.data._options.push(item)
}
})
}
})
if (this.data._options.length === 1 && event.which === 9) {
// if only one option and tab is pressed, we resolve it
event.preventDefault()
textList.pop()
textList.push(getKeyOf(this.data._options[0]))
this.handleSelect(`${textList}`.replace(/,/g, ' '))
this.removeAutoComplete()
return
}
function editCSSValuesModalContainer () {
/*
var modalContent = document.querySelector(`.${cssModal.modalContent}`)
let newModalContent = modalContent ? document.querySelector(`.${cssModal.modalContent}`) : document.querySelector(`.${css.modalContent}`)
newModalContent.className = css.modalContent
*/
yo.update(this._view, this.render())
return true
}
return false
}
setUpPopUp()
return self._view
removeAutoComplete () {
if (!this.isOpen) return
this._view.autoComplete.style.display = 'none'
if (this._components.modal) this._components.modal.cancelListener()
this.isOpen = false
this.data._options = []
this._startingElement = 0
this._selectedElement = 0
yo.update(this._view, this.render())
}
}
function getKeyOf (item) {
return Object.keys(item)[0]
}
function getValueOf (item) {
return Object.values(item)[0]
}
module.exports = AutoCompletePopup

@ -1,17 +1,20 @@
var yo = require('yo-yo')
var css = require('./styles/modaldialog-styles')
module.exports = (title, content, ok, cancel, focusSelector) => {
module.exports = (title, content, ok, cancel, focusSelector, opts) => {
opts = opts || {}
var container = document.querySelector(`.${css.modal}`)
if (!container) {
document.querySelector('body').appendChild(html())
document.querySelector('body').appendChild(html(opts))
container = document.querySelector(`.${css.modal}`)
}
var closeDiv = document.getElementById('modal-close')
if (opts.hideClose) closeDiv.style.display = 'none'
var okDiv = document.getElementById('modal-footer-ok')
okDiv.innerHTML = (ok && ok.label !== undefined) ? ok.label : 'OK'
okDiv.style.display = okDiv.innerHTML === '' ? 'none' : 'inline-block'
var cancelDiv = document.getElementById('modal-footer-cancel')
cancelDiv.innerHTML = (cancel && cancel.label !== undefined) ? cancel.label : 'Cancel'
@ -38,6 +41,10 @@ module.exports = (title, content, ok, cancel, focusSelector) => {
removeEventListener()
hide()
if (cancel && cancel.fn) cancel.fn()
if (container) {
container.class = css.modal
container = null
}
}
function modalKeyEvent (e) {
@ -50,10 +57,11 @@ module.exports = (title, content, ok, cancel, focusSelector) => {
}
function hide () {
container.style.display = 'none'
if (container) container.style.display = 'none'
}
function show () {
if (!container) return
container.style.display = 'block'
if (focusSelector) {
const focusTarget = document.querySelector(`.${css.modal} ${focusSelector}`)
@ -71,19 +79,24 @@ module.exports = (title, content, ok, cancel, focusSelector) => {
cancelDiv.removeEventListener('click', cancelListener)
closeDiv.removeEventListener('click', cancelListener)
document.removeEventListener('keydown', modalKeyEvent)
document.getElementById('modal-background').removeEventListener('click', cancelListener)
if (document.getElementById('modal-background')) {
document.getElementById('modal-background').removeEventListener('click', cancelListener)
}
}
okDiv.addEventListener('click', okListener)
cancelDiv.addEventListener('click', cancelListener)
closeDiv.addEventListener('click', cancelListener)
document.addEventListener('keydown', modalKeyEvent)
document.getElementById('modal-background').addEventListener('click', cancelListener)
if (document.getElementById('modal-background')) {
document.getElementById('modal-background').addEventListener('click', cancelListener)
}
return { container, okListener, cancelListener }
}
function html () {
function html (opts) {
return yo`<div id="modal-dialog" class="${css.modal}">
<div id="modal-background" class="${css['modalBackground']}"> </div>
<div class="${css['modalContent']} bg-light text-secondary">
<div class="${css['modalContent']} bg-light text-secondary ${opts.class}">
<div class="${css['modalHeader']}">
<h3></h3>
<i id="modal-close" title="Close" class="fa fa-times ${css['modalClose']}" aria-hidden="true"></i>

@ -11,6 +11,11 @@ var css = csjs`
padding-bottom : 13px;
}
.autoCompleteItem {
padding : 4px;
border-radius : 2px;
}
.popup a {
cursor : pointer;
}

Loading…
Cancel
Save