%PDF- %PDF-
| Direktori : /home/opti/workflow/src/public/app-assets/vendors/formBuilder-master/src/js/control/ |
| Current File : /home/opti/workflow/src/public/app-assets/vendors/formBuilder-master/src/js/control/autocomplete.js |
import control from '../control'
import { filter } from '../dom'
/**
* Autocomplete class
* Output an autocomplete form element
*/
export default class controlAutocomplete extends control {
/**
* definition
* @return {Object} select control definition
*/
static get definition() {
return {
mi18n: {
requireValidOption: 'requireValidOption',
},
}
}
/**
* build a text DOM element, supporting other jquery text form-control's
* @return {Object} DOM Element to be injected into the form.
*/
build() {
const { values, type, ...data } = this.config
const keyboardNav = e => {
const list = e.target.nextSibling.nextSibling
const hiddenField = e.target.nextSibling
const activeOption = this.getActiveOption(list)
const keyCodeMapVals = [
// up
[
38,
() => {
const previous = this.getPreviousOption(activeOption)
if (previous) {
this.selectOption(list, previous)
}
},
],
// down
[
40,
() => {
const next = this.getNextOption(activeOption)
if (next) {
this.selectOption(list, next)
}
},
],
// enter
[
13,
() => {
if (activeOption) {
e.target.value = activeOption.innerHTML
hiddenField.value = activeOption.getAttribute('value')
if (list.style.display === 'none') {
this.showList(list, activeOption)
} else {
this.hideList(list)
}
} else {
// Don't allow a value not in the list
if (this.config.requireValidOption) {
if (!this.isOptionValid(list, e.target.value)) {
e.target.value = ''
e.target.nextSibling.value = ''
}
}
}
e.preventDefault()
},
],
// escape
[
27,
() => {
this.hideList(list)
},
],
]
const keyCodeMap = new Map(keyCodeMapVals)
let direction = keyCodeMap.get(e.keyCode)
if (!direction) {
direction = () => false
}
return direction()
}
const fauxEvents = {
focus: evt => {
const list = evt.target.nextSibling.nextSibling
const filteredOptions = filter(list.querySelectorAll('li'), evt.target.value)
evt.target.addEventListener('keydown', keyboardNav)
if (evt.target.value.length > 0) {
const selectedOption = filteredOptions.length > 0 ? filteredOptions[filteredOptions.length - 1] : null
this.showList(list, selectedOption)
}
},
blur: evt => {
evt.target.removeEventListener('keydown', keyboardNav)
setTimeout(() => {
evt.target.nextSibling.nextSibling.style.display = 'none'
}, 200)
// Validate the option entered exists
if (this.config.requireValidOption) {
const list = evt.target.nextSibling.nextSibling
if (!this.isOptionValid(list, evt.target.value)) {
evt.target.value = ''
evt.target.nextSibling.value = ''
}
}
},
input: evt => {
const list = evt.target.nextSibling.nextSibling
const hiddenField = evt.target.nextSibling
hiddenField.value = evt.target.value
const filteredOptions = filter(list.querySelectorAll('li'), evt.target.value)
if (filteredOptions.length == 0) {
this.hideList(list)
} else {
let activeOption = this.getActiveOption(list)
if (!activeOption) {
activeOption = filteredOptions[filteredOptions.length - 1]
}
this.showList(list, activeOption)
}
},
}
const fauxAttrs = Object.assign({}, data, {
id: `${data.id}-input`,
autocomplete: 'off',
events: fauxEvents,
})
const hiddenAttrs = Object.assign({}, data, { type: 'hidden' })
delete fauxAttrs.name
const field = [this.markup('input', null, fauxAttrs), this.markup('input', null, hiddenAttrs)]
const options = values.map(optionData => {
const label = optionData.label
const config = {
events: {
click: evt => {
const list = evt.target.parentElement
const field = list.previousSibling.previousSibling
field.value = optionData.label
field.nextSibling.value = optionData.value
this.hideList(list)
},
},
value: optionData.value,
}
return this.markup('li', label, config)
})
field.push(this.markup('ul', options, { id: `${data.id}-list`, className: `fb-${type}-list` }))
return field
}
/**
* Hides autocomplete list and deselects all the options
* @param {Object} list - list of autocomplete options
*/
hideList(list) {
this.selectOption(list, null)
list.style.display = 'none'
}
/**
* Shows autocomplete list. Automatically selects 'selectedOption'
* @param {Object} list - list of autocomplete options
* @param {Object} selectedOption - option to be selected
*/
showList(list, selectedOption) {
this.selectOption(list, selectedOption)
list.style.display = 'block'
list.style.width = list.parentElement.offsetWidth + 'px'
}
/**
* Returns first option from autocomplete list with 'active-option' class
* @param {Object} list - list of autocomplete options
* @return {Object} first list option with 'active-option' class
*/
getActiveOption(list) {
const activeOption = list.getElementsByClassName('active-option')[0]
if (activeOption && activeOption.style.display !== 'none') {
return activeOption
}
return null
}
/**
* Previous next option to the current option
* @param {Object} current - currently selected option
* @return {Object} previous option to the current option or null if previous doesn't exist
*/
getPreviousOption(current) {
let previous = current
do {
previous = previous ? previous.previousSibling : null
} while (previous != null && previous.style.display === 'none')
return previous
}
/**
* Returns next option to the current option
* @param {Object} current - currently selected option
* @return {Object} next option to the current option or null if next doesn't exist
*/
getNextOption(current) {
let next = current
do {
next = next ? next.nextSibling : null
} while (next != null && next.style.display === 'none')
return next
}
/**
* Selects option in autocomplete list. Removes class 'active-option' from all options
* and then adds that class to 'selected' option. If 'selected' is null then no option is selected
* @param {Object} list - list of autocomplete options
* @param {Object} selectedOption - option - 'li' element - to be selected in autocomplete list
*/
selectOption(list, selectedOption) {
const options = list.querySelectorAll('li')
// --Fix for IE11
for (let i = 0; i < options.length; i++) {
options[i].classList.remove('active-option')
}
if (selectedOption) {
selectedOption.classList.add('active-option')
}
}
/**
* Is the value in the autocomplete field in the pre-defined Options list?
* @param {Object} list - list of autocomplete options
* @param {Object} value -value trying to be set
* @return {Object} - is the option in the pre defined list
*/
isOptionValid(list, value) {
const options = list.querySelectorAll('li')
let validValue = false
for (let i = 0; i < options.length; i++) {
if (options[i].innerHTML === value) {
validValue = true
break
}
}
return validValue
}
/**
* onRender callback
* @param {Object} evt
*/
onRender(evt) {
// Set userData if available
if (this.config.userData) {
const $el = $('#' + this.config.name)
const $options = $el.next()
const preSelectedOption = this.config.userData[0]
let selectedOption = null
$options.find('li').each(function() {
// eslint-disable-next-line no-invalid-this
if ($(this).attr('value') === preSelectedOption) {
// eslint-disable-next-line no-invalid-this
selectedOption = $(this).get(0)
return
}
})
// If the option was not set, and configuration says it doesn't have to be pre-defined, set the value
if (selectedOption === null) {
if (this.config.requireValidOption) {
// Don't allow
return
} else {
// Set it to whatever the value is
$el.prev().val(this.config.userData[0])
return
}
}
$el.prev().val(selectedOption.innerHTML)
$el.val(selectedOption.getAttribute('value'))
const list = $el.next().get(0)
if (list.style.display === 'none') {
this.showList(list, selectedOption)
} else {
this.hideList(list)
}
}
}
}
control.register('autocomplete', controlAutocomplete)