quitado autocomplete anterior
This commit is contained in:
parent
1dad2147ca
commit
07f8309d36
|
@ -1,20 +0,0 @@
|
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
|
||||
<input type="text"
|
||||
class="mdl-textfield__input"
|
||||
ng-keydown="$ctrl.onKeydown($event)"
|
||||
ng-click="$ctrl.onClick($event)"
|
||||
ng-keyup="$ctrl.onKeyup($event)"
|
||||
ng-focus="$ctrl.onFocus($event)"
|
||||
ng-blur="$ctrl.onBlur($event)"/>
|
||||
<button
|
||||
type="button"
|
||||
class="mdl-chip__action ng-hide"
|
||||
tabindex="-1"
|
||||
translate-attr="{title: 'Clear'}"
|
||||
ng-show="$ctrl.value"
|
||||
ng-click="$ctrl.onClear()"
|
||||
>
|
||||
<i class="material-icons">clear</i>
|
||||
</button>
|
||||
<label class="mdl-textfield__label">{{$ctrl.label | translate}}</label>
|
||||
</div>
|
|
@ -1,435 +0,0 @@
|
|||
import {module} from '../module';
|
||||
import Component from '../lib/component';
|
||||
import './style.scss';
|
||||
|
||||
/**
|
||||
* Combobox like component with search and partial data loading features.
|
||||
*/
|
||||
export default class Autocomplete extends Component {
|
||||
constructor($element, $scope, $http, vnPopover, $transclude, $timeout) {
|
||||
super($element);
|
||||
this.input = $element[0].querySelector('input');
|
||||
this.item = null;
|
||||
this.$timeout = $timeout;
|
||||
// this.data = null;
|
||||
this.popover = null;
|
||||
this.popoverId = null;
|
||||
this.displayData = null;
|
||||
this.timeoutId = null;
|
||||
this.lastSearch = null;
|
||||
this.lastRequest = null;
|
||||
this.currentRequest = null;
|
||||
this.moreData = false;
|
||||
this.activeOption = -1;
|
||||
this.locked = false;
|
||||
this.$http = $http;
|
||||
this.$scope = $scope;
|
||||
this.vnPopover = vnPopover;
|
||||
this.$transclude = $transclude;
|
||||
this.scopes = null;
|
||||
|
||||
Object.assign(this, {
|
||||
maxRows: 10,
|
||||
requestDelay: 350,
|
||||
showField: 'name',
|
||||
valueField: 'id',
|
||||
itemAs: 'i'
|
||||
});
|
||||
|
||||
componentHandler.upgradeElement($element[0].firstChild);
|
||||
}
|
||||
set field(value) {
|
||||
this.locked = true;
|
||||
this.setValue(value);
|
||||
this.locked = false;
|
||||
}
|
||||
get field() {
|
||||
return this.value;
|
||||
}
|
||||
set initialData(value) {
|
||||
if (value) {
|
||||
if (!this.data)
|
||||
this.data = [];
|
||||
this.data.push(value);
|
||||
}
|
||||
}
|
||||
set selectFields(value) {
|
||||
this._selectFields = [];
|
||||
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
let res = value.split(',');
|
||||
for (let i of res)
|
||||
this._selectFields.push(i.trim());
|
||||
}
|
||||
mdlUpdate() {
|
||||
let mdlField = this.element.firstChild.MaterialTextfield;
|
||||
if (mdlField)
|
||||
mdlField.updateClasses_();
|
||||
}
|
||||
loadData(textFilter) {
|
||||
textFilter = textFilter ? textFilter : '';
|
||||
|
||||
if (this.lastSearch === textFilter) {
|
||||
this.showPopoverIfFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastSearch = textFilter;
|
||||
|
||||
let lastRequest = this.lastRequest;
|
||||
let requestWillSame = lastRequest !== null
|
||||
&& !this.moreData
|
||||
&& textFilter.substr(0, lastRequest.length) === lastRequest;
|
||||
|
||||
if (requestWillSame || !this.url)
|
||||
this.localFilter(textFilter);
|
||||
else if (this.url)
|
||||
this.requestData(textFilter, false);
|
||||
else
|
||||
this.setDisplayData(this.data);
|
||||
}
|
||||
getRequestFields() {
|
||||
let fields = {};
|
||||
fields[this.valueField] = true;
|
||||
fields[this.showField] = true;
|
||||
|
||||
if (this._selectFields)
|
||||
for (let field of this._selectFields)
|
||||
fields[field] = true;
|
||||
|
||||
return fields;
|
||||
}
|
||||
requestData(textFilter, append) {
|
||||
let where = {};
|
||||
let skip = 0;
|
||||
|
||||
if (textFilter)
|
||||
where[this.showField] = {regexp: textFilter};
|
||||
if (append && this.data)
|
||||
skip = this.data.length;
|
||||
|
||||
let filter = {
|
||||
fields: this.getRequestFields(),
|
||||
where: where,
|
||||
order: `${this.showField} ASC`,
|
||||
skip: skip,
|
||||
limit: this.maxRows
|
||||
};
|
||||
|
||||
this.lastRequest = textFilter ? textFilter : '';
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
if (this.currentRequest)
|
||||
this.currentRequest.resolve();
|
||||
|
||||
this.currentRequest = this.$http.get(`${this.url}?filter=${json}`);
|
||||
this.currentRequest.then(
|
||||
json => this.onRequest(json.data, append),
|
||||
json => this.onRequest([])
|
||||
);
|
||||
}
|
||||
onRequest(data, append) {
|
||||
this.currentRequest = null;
|
||||
this.moreData = data.length >= this.maxRows;
|
||||
|
||||
if (!append || !this.data)
|
||||
this.data = data;
|
||||
else
|
||||
this.data = this.data.concat(data);
|
||||
|
||||
this.setDisplayData(this.data);
|
||||
}
|
||||
localFilter(textFilter) {
|
||||
let regex = new RegExp(textFilter, 'i');
|
||||
let data = this.data.filter(item => {
|
||||
return regex.test(item[this.showField]);
|
||||
});
|
||||
this.setDisplayData(data);
|
||||
}
|
||||
setDisplayData(data) {
|
||||
this.displayData = data;
|
||||
this.showPopoverIfFocus();
|
||||
}
|
||||
showPopoverIfFocus() {
|
||||
if (this.hasFocus)
|
||||
this.showPopover();
|
||||
}
|
||||
destroyScopes() {
|
||||
if (this.scopes)
|
||||
for (let scope of this.scopes)
|
||||
scope.$destroy();
|
||||
}
|
||||
showPopover() {
|
||||
let data = this.displayData;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
let fragment = this.document.createDocumentFragment();
|
||||
this.destroyScopes();
|
||||
this.scopes = [];
|
||||
|
||||
let hasTemplate = this.$transclude.isSlotFilled('tplItem');
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let li = this.document.createElement('li');
|
||||
fragment.appendChild(li);
|
||||
|
||||
if (hasTemplate) {
|
||||
this.$transclude((clone, scope) => {
|
||||
scope[this.itemAs] = data[i];
|
||||
li.appendChild(clone[0]);
|
||||
this.scopes[i] = scope;
|
||||
}, null, 'tplItem');
|
||||
} else {
|
||||
let text = this.document.createTextNode(data[i][this.showField]);
|
||||
li.appendChild(text);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.moreData) {
|
||||
let li = this.document.createElement('li');
|
||||
li.appendChild(this.document.createTextNode('Load more'));
|
||||
li.className = 'load-more';
|
||||
fragment.appendChild(li);
|
||||
}
|
||||
|
||||
if (this.popover) {
|
||||
this.popover.innerHTML = '';
|
||||
this.popover.appendChild(fragment);
|
||||
} else {
|
||||
let popover = this.document.createElement('ul');
|
||||
popover.addEventListener('click',
|
||||
e => this.onPopoverClick(e));
|
||||
popover.addEventListener('mousedown',
|
||||
e => this.onPopoverMousedown(e));
|
||||
popover.className = 'vn-autocomplete';
|
||||
popover.appendChild(fragment);
|
||||
this.popoverId = this.vnPopover.show(popover, this.input);
|
||||
this.popover = popover;
|
||||
}
|
||||
}
|
||||
hidePopover() {
|
||||
if (!this.popover) return;
|
||||
this.activeOption = -1;
|
||||
this.vnPopover.hide(this.popoverId);
|
||||
this.destroyScopes();
|
||||
this.popover = null;
|
||||
}
|
||||
selectPopoverOption(index) {
|
||||
if (!this.popover || index === -1) return;
|
||||
if (index < this.displayData.length) {
|
||||
this.selectOptionByDataIndex(this.displayData, index);
|
||||
this.hidePopover();
|
||||
} else
|
||||
this.requestData(this.lastRequest, true);
|
||||
}
|
||||
onPopoverClick(event) {
|
||||
let target = event.target;
|
||||
let childs = this.popover.childNodes;
|
||||
|
||||
if (target === this.popover)
|
||||
return;
|
||||
|
||||
while (target.parentNode !== this.popover)
|
||||
target = target.parentNode;
|
||||
|
||||
for (let i = 0; i < childs.length; i++)
|
||||
if (childs[i] === target) {
|
||||
this.selectPopoverOption(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
onPopoverMousedown(event) {
|
||||
// Prevents input from loosing focus
|
||||
event.preventDefault();
|
||||
}
|
||||
onClear() {
|
||||
this.setValue(null);
|
||||
this.$timeout(
|
||||
() => {
|
||||
this.mdlUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
onClick(event) {
|
||||
if (!this.popover)
|
||||
this.showPopover();
|
||||
}
|
||||
onFocus() {
|
||||
this.hasFocus = true;
|
||||
this.input.select();
|
||||
|
||||
this.loadData();
|
||||
}
|
||||
onBlur() {
|
||||
this.hasFocus = false;
|
||||
this.restoreShowValue();
|
||||
this.hidePopover();
|
||||
}
|
||||
onKeydown(event) {
|
||||
switch (event.keyCode) {
|
||||
case 13: // Enter
|
||||
this.selectPopoverOption(this.activeOption);
|
||||
break;
|
||||
case 27: // Escape
|
||||
this.restoreShowValue();
|
||||
this.input.select();
|
||||
break;
|
||||
case 38: // Arrow up
|
||||
this.activateOption(this.activeOption - 1);
|
||||
break;
|
||||
case 40: // Arrow down
|
||||
this.activateOption(this.activeOption + 1);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
onKeyup(event) {
|
||||
if (!this.isKeycodePrintable(event.keyCode)) return;
|
||||
if (this.timeoutId) clearTimeout(this.timeoutId);
|
||||
this.timeoutId = setTimeout(() => this.onTimeout(), this.requestDelay);
|
||||
}
|
||||
onTimeout() {
|
||||
this.loadData(this.input.value);
|
||||
this.timeoutId = null;
|
||||
}
|
||||
isKeycodePrintable(keyCode) {
|
||||
return keyCode === 32 // Spacebar
|
||||
|| keyCode === 8 // Backspace
|
||||
|| (keyCode > 47 && keyCode < 58) // Numbers
|
||||
|| (keyCode > 64 && keyCode < 91) // Letters
|
||||
|| (keyCode > 95 && keyCode < 112) // Numpad
|
||||
|| (keyCode > 185 && keyCode < 193) // ;=,-./`
|
||||
|| (keyCode > 218 && keyCode < 223); // [\]'
|
||||
}
|
||||
restoreShowValue() {
|
||||
this.putItem(this.item);
|
||||
}
|
||||
requestItem() {
|
||||
if (!this.value) return;
|
||||
|
||||
let where = {};
|
||||
where[this.valueField] = this.value;
|
||||
|
||||
let filter = {
|
||||
fields: this.getRequestFields(),
|
||||
where: where
|
||||
};
|
||||
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
this.$http.get(`${this.url}?filter=${json}`).then(
|
||||
json => this.onItemRequest(json.data),
|
||||
json => this.onItemRequest(null)
|
||||
);
|
||||
}
|
||||
onItemRequest(data) {
|
||||
if (data && data.length > 0)
|
||||
this.showItem(data[0]);
|
||||
else
|
||||
this.showItem(null);
|
||||
}
|
||||
activateOption(index) {
|
||||
if (!this.popover)
|
||||
this.showPopover();
|
||||
|
||||
let popover = this.popover;
|
||||
let childs = popover.childNodes;
|
||||
let len = this.displayData.length;
|
||||
|
||||
if (this.activeOption >= 0)
|
||||
childs[this.activeOption].className = '';
|
||||
|
||||
if (index >= len)
|
||||
index = 0;
|
||||
else if (index < 0)
|
||||
index = len - 1;
|
||||
|
||||
if (index >= 0) {
|
||||
let opt = childs[index];
|
||||
let top = popover.scrollTop;
|
||||
let height = popover.clientHeight;
|
||||
|
||||
if (opt.offsetTop + opt.offsetHeight > top + height)
|
||||
top = opt.offsetTop + opt.offsetHeight - height;
|
||||
else if (opt.offsetTop < top)
|
||||
top = opt.offsetTop;
|
||||
|
||||
opt.className = 'active';
|
||||
popover.scrollTop = top;
|
||||
}
|
||||
|
||||
this.activeOption = index;
|
||||
}
|
||||
setValue(value) {
|
||||
this.value = value;
|
||||
|
||||
if (value) {
|
||||
let data = this.data;
|
||||
|
||||
if (data)
|
||||
for (let i = 0; i < data.length; i++)
|
||||
if (data[i][this.valueField] == value) {
|
||||
this.putItem(data[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestItem();
|
||||
} else
|
||||
this.putItem(null);
|
||||
}
|
||||
selectOptionByIndex(index) {
|
||||
this.selectOptionByDataIndex(this.data, index);
|
||||
}
|
||||
selectOptionByDataIndex(data, index) {
|
||||
if (data && index >= 0 && index < data.length)
|
||||
this.putItem(data[index]);
|
||||
else
|
||||
this.putItem(null);
|
||||
}
|
||||
putItem(item) {
|
||||
this.showItem(item);
|
||||
let value = item ? item[this.valueField] : undefined;
|
||||
|
||||
if (!this.locked)
|
||||
this.value = value;
|
||||
|
||||
if (this.onChange)
|
||||
this.onChange({item: item});
|
||||
}
|
||||
showItem(item) {
|
||||
this.input.value = item ? item[this.showField] : '';
|
||||
this.item = item;
|
||||
this.mdlUpdate();
|
||||
}
|
||||
$onDestroy() {
|
||||
this.destroyScopes();
|
||||
}
|
||||
}
|
||||
Autocomplete.$inject = ['$element', '$scope', '$http', 'vnPopover', '$transclude', '$timeout'];
|
||||
|
||||
module.component('vnAutocomplete', {
|
||||
template: require('./autocomplete.html'),
|
||||
bindings: {
|
||||
url: '@?',
|
||||
showField: '@?',
|
||||
valueField: '@?',
|
||||
selectFields: '@?',
|
||||
initialData: '<?',
|
||||
onChange: '&?',
|
||||
data: '<?',
|
||||
itemAs: '@?',
|
||||
field: '=',
|
||||
label: '@'
|
||||
},
|
||||
transclude: {
|
||||
tplItem: '?tplItem'
|
||||
},
|
||||
controller: Autocomplete
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
import './autocomplete.js';
|
||||
|
||||
describe('Core', () => {
|
||||
describe('Component mdlUpdate', () => {
|
||||
let $componentController;
|
||||
let $state;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.mock.module('client');
|
||||
});
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
|
||||
$componentController = _$componentController_;
|
||||
$state = _$state_;
|
||||
$state.params.id = '1234';
|
||||
}));
|
||||
|
||||
it('should define and set address property', () => {
|
||||
let controller = $componentController('vnAddressCreate', {$state: $state});
|
||||
expect(controller.address.clientFk).toBe(1234);
|
||||
expect(controller.address.enabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,37 +0,0 @@
|
|||
ul.vn-autocomplete {
|
||||
list-style-type: none;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
padding: .8em;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
background-color: rgba(1,1,1,.1);
|
||||
}
|
||||
&.load-more {
|
||||
color: #ffa410;
|
||||
font-weight: bold;
|
||||
padding: .4em .8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
vn-autocomplete {
|
||||
.mdl-chip__action {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: -6px;
|
||||
margin: 22px 0px;
|
||||
background-color: white;
|
||||
}
|
||||
.material-icons {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue