diff --git a/@salix/app/src/app.js b/@salix/app/src/app.js
index 81835fd89..4683d2169 100644
--- a/@salix/app/src/app.js
+++ b/@salix/app/src/app.js
@@ -6,14 +6,14 @@ import * as run from './run';
import * as configNgTranslate from './translate';
import * as components from './components';
-import title from './styles/title.css'
-import padding from './styles/layout.css'
-import margin from './styles/margin.scss'
-import layout from './styles/padding.scss'
-import background from './styles/background.scss'
-import border from './styles/border.scss'
-import fontStyle from './styles/font-style.scss'
-import misc from './styles/misc.scss'
-import display from './styles/display.css'
+import title from './styles/title.css';
+import padding from './styles/layout.css';
+import margin from './styles/margin.scss';
+import layout from './styles/padding.scss';
+import background from './styles/background.scss';
+import border from './styles/border.scss';
+import fontStyle from './styles/font-style.scss';
+import misc from './styles/misc.scss';
+import display from './styles/display.css';
bootstrap();
diff --git a/@salix/app/src/components/searchbar/searchbar.js b/@salix/app/src/components/searchbar/searchbar.js
index ff5987a55..9a013a5e7 100644
--- a/@salix/app/src/components/searchbar/searchbar.js
+++ b/@salix/app/src/components/searchbar/searchbar.js
@@ -19,7 +19,7 @@ function controller($element, $scope, $document, $compile, popover) {
this.onClick = function(event) {
var child = $document[0].createElement(this.popover);
$compile(child)($scope);
- popover.show(child, $element);
+ popover.show(child, $element[0]);
// XXX: ¿Existe una forma más adecuada de acceder al controlador de un componente?
var childCtrl = angular.element(child).isolateScope().$ctrl;
diff --git a/@salix/core/src/autocomplete/index.js b/@salix/core/src/autocomplete/index.js
index cb153da0e..844897b3b 100644
--- a/@salix/core/src/autocomplete/index.js
+++ b/@salix/core/src/autocomplete/index.js
@@ -1,124 +1,369 @@
import {module} from '../module';
export {factory as mdlFactory} from './index.mdl';
-import * as resolveFactory from '../resolveDefaultComponents';
-import * as normalizerFactory from '../inputAttrsNormalizer';
-require('./style.css');
+require('./style.scss');
-directive.$inject = [resolveFactory.NAME, normalizerFactory.NAME];
-export function directive(resolve, normalizer) {
- return {
- restrict: 'E',
- transclude: true,
- scope: {
- url: '@',
- showField: '@',
- valueField: '@',
- model: '<'
- },
- template: function(element, attrs) {
- normalizer.normalize(attrs);
- return resolve.getTemplate('autocomplete', attrs);
- },
- link: function(scope, element, attrs) {
- scope.$watch(attrs.model, () => {
- let mdlField = element[0].firstChild.MaterialTextfield;
- if (mdlField)
- mdlField.updateClasses_();
- });
- componentHandler.upgradeElement(element[0].firstChild);
- },
- controller: controller
- };
+export const component = {
+ restrict: 'E',
+ transclude: true,
+ bindings: {
+ url: '@',
+ showField: '@',
+ valueField: '@',
+ model: '<'
+ },
+ template: template,
+ controller: controller
+};
+module.component('vnAutocomplete', component);
+
+template.$inject = ['$element', '$attrs', 'vnInputAttrsNormalizer', 'vnResolveDefaultComponent'];
+function template($element, $attrs, normalizer, resolve) {
+ normalizer.normalize($attrs);
+ return resolve.getTemplate('autocomplete', $attrs);
}
-module.directive('vnAutocomplete', directive);
controller.$inject = ['$http', '$element', '$attrs', '$scope', '$parse', '$document', 'vnPopover'];
-export function controller($http, $element, $attrs, $scope, $parse, $document, popover) {
- let dropdown = null;
- let input = $element.find('input');
- let data = [];
+function controller($http, $element, $attrs, $scope, $parse, $document, popoverProvider) {
let locked = false;
- $http.get($scope.url).then(
- json => {
- data = json.data;
- setTimeout(function() {
- $scope.setValue($scope.model);
- });
- },
- json => {data = [];}
- );
-
$scope.$watch($attrs.model, (newValue) => {
if(!locked) {
locked = true;
- $scope.setValue(newValue);
+ this.setValue(newValue);
locked = false;
}
});
- $scope.setValue = function(value) {
- let index = -1;
+ componentHandler.upgradeElement($element[0].firstChild);
- if(value && data)
- for(let i = 0; i < data.length; i++)
- if(data[i][$scope.valueField] == value) {
- $scope.selectOptionByIndex(i);
- return;
- }
-
- $scope.selectOptionByIndex(-1);
+ function mdlUpdate() {
+ let mdlField = $element[0].firstChild.MaterialTextfield;
+ if (mdlField)
+ mdlField.updateClasses_();
}
- $scope.selectOptionByIndex = function(index) {
- let value;
+ Object.assign(this, {
+ init: function() {
+ this.input = $element.find('input')[0];
+ this.item = null;
+ this.data = null;
+ this.popover = null;
+ this.popoverData = null;
+ this.timeoutId = null;
+ this.lastSearch = null;
+ this.lastRequest = null;
+ this.currentRequest = null;
+ this.moreData = false;
+ this.activeOption = -1;
+ this.maxRows = 10;
+ this.requestDelay = 350;
+ this.requestItem();
+ },
+ loadData: function(textFilter) {
+ textFilter = textFilter ? textFilter : '';
- if(index >= 0) {
- let item = data[index];
- input.val(item[$scope.showField]);
- value = item[$scope.valueField];
- }
- else {
- input.val('');
- value = undefined;
- }
+ if(this.lastSearch === textFilter) {
+ this.popoverDataReady();
+ return;
+ }
- if(!locked) {
- $scope.$apply(function () {
- locked = true;
- $parse($attrs.model).assign($scope, value);
- locked = false;
+ this.lastSearch = textFilter;
+
+ let lastRequest = this.lastRequest;
+ let requestWillSame = lastRequest !== null
+ && !this.moreData
+ && textFilter.substr(0, lastRequest.length) === lastRequest;
+
+ if(requestWillSame)
+ this.localFilter(textFilter);
+ else
+ this.requestData(textFilter, false);
+ },
+ getRequestFields: function() {
+ let fields = {};
+ fields[this.valueField] = true;
+ fields[this.showField] = true;
+ return fields;
+ },
+ requestData: function(textFilter, append) {
+ let where = {};
+ let skip = 0;
+
+ if(textFilter)
+ where[this.showField] = {ilike: 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 = $http.get(`${this.url}?filter=${json}`);
+ this.currentRequest.then(
+ json => this.onRequest(json.data, append),
+ json => this.onRequest([])
+ );
+ },
+ onRequest: function(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.setPopoverData(this.data);
+ },
+ localFilter: function(textFilter) {
+ let regex = new RegExp(textFilter, 'i');
+ let data = this.data.filter((item) => {
+ return regex.test(item[this.showField]);
});
+ this.setPopoverData(data);
+ },
+ setPopoverData: function(data) {
+ this.popoverData = data;
+ this.popoverDataReady();
+ },
+ popoverDataReady: function() {
+ if(this.hasFocus)
+ this.showPopover();
+ },
+ showPopover: function() {
+ if(!this.data) return;
+
+ let fragment = $document[0].createDocumentFragment();
+ let data = this.popoverData;
+
+ for(let i = 0; i < data.length; i++) {
+ let li = $document[0].createElement('li');
+ li.appendChild($document[0].createTextNode(data[i][this.showField]));
+ fragment.appendChild(li);
+ }
+
+ if(this.moreData) {
+ let li = $document[0].createElement('li');
+ li.appendChild($document[0].createTextNode('Load more'));
+ li.className = 'load-more';
+ fragment.appendChild(li);
+ }
+
+ if (!this.popover) {
+ let popover = $document[0].createElement('ul');
+ popover.addEventListener('click',
+ (e) => this.onPopoverClick(e));
+ popover.addEventListener('mousedown',
+ (e) => this.onPopoverMousedown(e));
+ popover.className = 'vn-autocomplete';
+ popover.appendChild(fragment);
+ popoverProvider.show(popover, this.input);
+ this.popover = popover;
+ }
+ else {
+ this.popover.innerHTML = '';
+ this.popover.appendChild(fragment);
+ }
+ },
+ hidePopover: function() {
+ if(!this.popover) return;
+ this.activeOption = -1;
+ popoverProvider.hide();
+ this.popover = null;
+ },
+ selectPopoverOption: function(index) {
+ if(!this.popover || index == -1) return;
+ if(index < this.popoverData.length) {
+ this.selectOptionByDataIndex(this.popoverData, index);
+ this.hidePopover();
+ }
+ else
+ this.requestData(this.lastRequest, true);
+ },
+ onPopoverClick: function(event) {
+ let childs = this.popover.childNodes;
+ for(let i = 0; i < childs.length; i++)
+ if(childs[i] === event.target) {
+ this.selectPopoverOption(i);
+ break;
+ }
+ },
+ onPopoverMousedown: function(event) {
+ // Prevents input from loosing focus
+ event.preventDefault();
+ },
+ onClick: function(event) {
+ if(!this.popover)
+ this.showPopover();
+ },
+ onFocus: function() {
+ this.hasFocus = true;
+ this.input.select();
+
+ if(this.data)
+ this.showPopover();
+ else
+ this.loadData();
+ },
+ onBlur: function() {
+ this.hasFocus = false;
+ this.restoreShowValue();
+ this.hidePopover();
+ },
+ onKeydown: function(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: function(event) {
+ if(!this.isKeycodePrintable(event.keyCode)) return;
+ if(this.timeoutId) clearTimeout(this.timeoutId);
+ this.timeoutId = setTimeout(() => this.onTimeout(), this.requestDelay);
+ },
+ onTimeout: function() {
+ this.loadData(this.input.value);
+ this.timeoutId = null;
+ },
+ isKeycodePrintable: function(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: function() {
+ this.putItem(this.item);
+ },
+ requestItem: function() {
+ if(!this.model) return;
+
+ let where = {};
+ where[this.valueField] = this.model;
+
+ let filter = {
+ fields: this.getRequestFields(),
+ where: where,
+ };
+
+ let json = JSON.stringify(filter);
+
+ $http.get(`${this.url}?filter=${json}`).then(
+ json => this.onItemRequest(json.data),
+ json => this.onItemRequest(null)
+ );
+ },
+ onItemRequest: function(data) {
+ if(data && data.length > 0)
+ this.showItem(data[0]);
+ else
+ this.showItem(null);
+ },
+ activateOption: function(index) {
+
+ if(!this.popover)
+ this.showPopover();
+
+ let popover = this.popover;
+ let childs = popover.childNodes;
+ let len = this.popoverData.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: function(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: function(index) {
+ this.selectOptionByDataIndex(this.data, index);
+ },
+ selectOptionByDataIndex: function(data, index) {
+ if(data && index >= 0 && index < data.length)
+ this.putItem(data[index]);
+ else
+ this.putItem(null);
+ },
+ putItem: function(item) {
+ this.showItem(item);
+ let value = item ? item[this.valueField] : undefined;
+
+ if(!locked) {
+ setTimeout (() => {
+ $scope.$apply(function () {
+ locked = true;
+ $parse($attrs.model).assign($scope, value);
+ locked = false;
+ });
+ });
+ }
+ },
+ showItem: function(item) {
+ this.input.value = item ? item[this.showField] : '';
+ this.item = item;
+ mdlUpdate();
}
- }
+ });
- function onOptionClick(event) {
- popover.hide();
-
- let childs = dropdown.childNodes;
- for(let i = 0; i < childs.length; i++)
- if(childs[i] === event.target) {
- $scope.selectOptionByIndex(i);
- break;
- }
- }
-
- $scope.onClick = () => {
- dropdown = $document[0].createElement('ul');
- dropdown.addEventListener('click', onOptionClick);
- dropdown.className = 'vn-dropdown';
-
- for(let i = 0; i < data.length; i++) {
- let item = $document[0].createElement('li');
- item.className = 'vn-dropdown-item';
- item.appendChild($document[0].createTextNode(data[i][$scope.showField]));
- dropdown.appendChild(item);
- }
-
- popover.show(dropdown, input);
- };
-
- $scope.onKeypress = () => {
- console.log(input.val());
- };
+ this.init();
}
diff --git a/@salix/core/src/autocomplete/index.mdl.html b/@salix/core/src/autocomplete/index.mdl.html
index 04ef50d81..bef534feb 100644
--- a/@salix/core/src/autocomplete/index.mdl.html
+++ b/@salix/core/src/autocomplete/index.mdl.html
@@ -1,4 +1,14 @@
-
-
+
+
diff --git a/@salix/core/src/autocomplete/style.css b/@salix/core/src/autocomplete/style.css
deleted file mode 100644
index 39811f1a1..000000000
--- a/@salix/core/src/autocomplete/style.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.vn-dropdown {
- list-style-type: none;
- padding: 1em;
- margin: 0;
- padding: 0;
-}
-.vn-dropdown-item {
- display: block;
- padding: .8em;
- margin: 0;
-}
-.vn-dropdown-item:hover {
- background-color: rgba(1,1,1,.1);
- cursor: pointer;
-}
-.vn-dropdown-item:active {
- background-color: rgba(1,1,1,.2);
-}
\ No newline at end of file
diff --git a/@salix/core/src/autocomplete/style.scss b/@salix/core/src/autocomplete/style.scss
new file mode 100644
index 000000000..1ddff61ec
--- /dev/null
+++ b/@salix/core/src/autocomplete/style.scss
@@ -0,0 +1,25 @@
+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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/@salix/core/src/mdl-override.css b/@salix/core/src/mdl-override.css
index f2ea87bfd..c556fa556 100644
--- a/@salix/core/src/mdl-override.css
+++ b/@salix/core/src/mdl-override.css
@@ -9,6 +9,8 @@
/* TODO: No utilizar !important */
.mdl-button {
font-weight: bolder;
+ color: #ffa410;
+
}
.mdl-button--colored {
color: white !important;
@@ -26,3 +28,14 @@
color: white !important;
background-color: #ff9400 !important;
}
+
+.mdl-dialog__actions--full-width>*{
+ text-align: center;
+}
+
+.mdl-dialog{
+ width: 400px;
+ font-family: raleway-regular;
+ line-height:60px;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/@salix/core/src/popover/index.js b/@salix/core/src/popover/index.js
index d2ef01b6f..629d74835 100644
--- a/@salix/core/src/popover/index.js
+++ b/@salix/core/src/popover/index.js
@@ -47,7 +47,7 @@ function provider($document, $compile) {
}
if(parent) {
- let parentNode = parent[0];
+ let parentNode = parent;
let rect = parentNode.getBoundingClientRect();
let left = rect.left;
let top = rect.top + spacing + parentNode.offsetHeight;
@@ -77,6 +77,7 @@ function provider($document, $compile) {
this.show(childElement, parent);
},
hide: function() {
+ if(!this.popover) return;
$document.off('mousedown', this.docMouseDownHandler);
$document[0].body.removeChild (this.popover);
this.popover = null;
diff --git a/@salix/crud/src/client/addresses-data-edit/index.js b/@salix/crud/src/client/addresses-data-edit/index.js
index 0e7d785a8..268389dc1 100644
--- a/@salix/crud/src/client/addresses-data-edit/index.js
+++ b/@salix/crud/src/client/addresses-data-edit/index.js
@@ -13,7 +13,7 @@ export const COMPONENT = {
this.copyAddress();
}
);
-
+
$http.get('/client/api/Agencies').then(
json => {
this.agencies = json.data;
diff --git a/@salix/crud/src/client/basic-data/index.js b/@salix/crud/src/client/basic-data/index.js
index 9d8da6f5d..33ae58e41 100644
--- a/@salix/crud/src/client/basic-data/index.js
+++ b/@salix/crud/src/client/basic-data/index.js
@@ -21,11 +21,7 @@ export const COMPONENT = {
this.$onDestroy = function() {
deregister();
};
-/*
- $http.get('/client/api/SalesPeople').then(
- json => {this.sales = json.data;}
- );
-*/
+
function callback(transition) {
if (!equalsObject(self.client, self.clientOld)) {
self.state = transition.to().name;
diff --git a/@salix/crud/src/client/confirm/index.html b/@salix/crud/src/client/confirm/index.html
index 5547c7527..82a6b0f26 100644
--- a/@salix/crud/src/client/confirm/index.html
+++ b/@salix/crud/src/client/confirm/index.html
@@ -1,11 +1,14 @@
-