');
- controller = $componentController('vnInputFile', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
- controller.input = $element[0].querySelector('input');
- controller.validate = () => {};
+ $element = $compile('
')($scope);
+ controller = $element.controller('vnInputFile');
}));
- describe('files() setter', () => {
- it(`should set a value, and then add the class 'not-empty'`, () => {
- controller.files = [{name: 'MyFile'}];
+ afterAll(() => {
+ $element.remove();
+ $scope.$destroy();
+ });
- let classes = controller.element.classList.toString();
+ describe('field() setter', () => {
+ it(`should set an empty string as value when null value is set`, () => {
+ controller.field = null;
- expect(classes).toContain('not-empty');
- });
-
- it(`should set an empty value, and then remove the class 'not-empty'`, () => {
- controller.files = null;
-
- let classes = controller.element.classList.toString();
-
- expect(classes).not.toContain('not-empty');
+ expect(controller.value).toBe('');
});
});
});
diff --git a/front/core/components/input-file/style.scss b/front/core/components/input-file/style.scss
index b0d5d50ca5..44d11f7744 100644
--- a/front/core/components/input-file/style.scss
+++ b/front/core/components/input-file/style.scss
@@ -1,13 +1,7 @@
@import "variables";
-vn-input-file {
+.vn-input-file {
.value {
- color: $color-font-secondary;
cursor: pointer;
- padding: 4px 0;
- outline: 0
- }
- input[type=file] {
- display: none;
}
}
\ No newline at end of file
diff --git a/front/core/components/input-number/index.html b/front/core/components/input-number/index.html
index ee6f503977..2c6f7d824b 100644
--- a/front/core/components/input-number/index.html
+++ b/front/core/components/input-number/index.html
@@ -19,7 +19,7 @@
ng-click="$ctrl.onClear($event)">
diff --git a/front/core/components/input-number/index.spec.js b/front/core/components/input-number/index.spec.js
index 9d1943e0ec..d1bbe251c1 100644
--- a/front/core/components/input-number/index.spec.js
+++ b/front/core/components/input-number/index.spec.js
@@ -4,9 +4,7 @@ describe('Component vnInputNumber', () => {
let $element;
let $ctrl;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($compile, $rootScope) => {
$element = $compile(`
`)($rootScope);
diff --git a/front/core/components/input-time/index.spec.js b/front/core/components/input-time/index.spec.js
index 0ab0fc7628..39993b5cc4 100644
--- a/front/core/components/input-time/index.spec.js
+++ b/front/core/components/input-time/index.spec.js
@@ -5,9 +5,7 @@ describe('Component vnInputTime', () => {
let $element;
let $ctrl;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => {
$filter = _$filter_;
diff --git a/front/core/components/label-value/index.html b/front/core/components/label-value/index.html
new file mode 100644
index 0000000000..d1818821a5
--- /dev/null
+++ b/front/core/components/label-value/index.html
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/front/core/components/label-value/index.js b/front/core/components/label-value/index.js
new file mode 100644
index 0000000000..71863f8672
--- /dev/null
+++ b/front/core/components/label-value/index.js
@@ -0,0 +1,54 @@
+import ngModule from '../../module';
+import Component from 'core/lib/component';
+import './style.scss';
+
+/**
+ * Simple component to display a label with it's correspoding value. If @info
+ * property is provided it displays an aditional icon with the provided
+ * information.
+ *
+ * IMPORTANT!
+ *
+ * Please keep this component as simple as possible and without persistent
+ * watchers because it's used a lot of times in the application and could cause
+ * performance issues.
+ *
+ * @property {String} label The label
+ * @property {*} value The value
+ * @property {String} info Aditional information to display
+ */
+export default class Controller extends Component {
+ get label() {
+ return this._label;
+ }
+
+ set label(value) {
+ this._label = value;
+
+ let label = this.element.querySelector('vn-label');
+ label.textContent = this.$t(value);
+ }
+
+ get value() {
+ return this._value;
+ }
+
+ set value(value) {
+ this._value = value;
+
+ let span = this.element.querySelector('span');
+ span.title = value;
+ span.textContent = value != null && value != '' ? value : '-';
+ }
+}
+
+ngModule.component('vnLabelValue', {
+ controller: Controller,
+ template: require('./index.html'),
+ transclude: true,
+ bindings: {
+ label: '@',
+ value: '@?',
+ info: '@?'
+ }
+});
diff --git a/front/core/components/label-value/index.spec.js b/front/core/components/label-value/index.spec.js
new file mode 100644
index 0000000000..39d8ff56b6
--- /dev/null
+++ b/front/core/components/label-value/index.spec.js
@@ -0,0 +1,36 @@
+import './index.js';
+
+describe('Component vnLabelValue', () => {
+ let $element;
+ let controller;
+ let element;
+
+ beforeEach(ngModule('vnCore'));
+
+ beforeEach(inject(($compile, $rootScope) => {
+ $element = $compile(`
`)($rootScope);
+ controller = $element.controller('vnLabelValue');
+ element = $element[0];
+ }));
+
+ afterEach(() => {
+ $element.remove();
+ });
+
+ describe('value() setter', () => {
+ it(`should set the value on the span element`, () => {
+ const value = 'I am the value';
+ controller.value = value;
+
+ expect(element.querySelector('span').textContent).toEqual(value);
+ expect(element.querySelector('span').title).toEqual(value);
+ });
+
+ it(`should set a dash when no value is provided`, () => {
+ const value = null;
+ controller.value = value;
+
+ expect(element.querySelector('span').textContent).toEqual('-');
+ });
+ });
+});
diff --git a/front/core/components/label-value/label-value.html b/front/core/components/label-value/label-value.html
deleted file mode 100644
index 138e593c22..0000000000
--- a/front/core/components/label-value/label-value.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
\ No newline at end of file
diff --git a/front/core/components/label-value/label-value.js b/front/core/components/label-value/label-value.js
deleted file mode 100644
index 87ae5f08ea..0000000000
--- a/front/core/components/label-value/label-value.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import ngModule from '../../module';
-import './style.scss';
-
-export default class Controller {
- constructor($element, $translate, $attrs) {
- this.element = $element[0];
- this._ = $translate;
- this.hasInfo = Boolean($attrs.info);
- this.info = $attrs.info || null;
- }
-
- set label(value) {
- let label = this.element.querySelector('vn-label');
- label.textContent = this._.instant(value);
- this._label = value;
- }
-
- get label() {
- return this._label;
- }
-
- get state() {
- return this._state;
- }
-
- set state(value) {
- this._state = value;
- this.applyTextContent();
- }
-
- get value() {
- return this._value;
- }
-
- set value(value) {
- this._value = value;
- this.applyTextContent();
- }
-
- get title() {
- return this._title;
- }
-
- set title(value) {
- let span = this.element.querySelector('span');
- span.title = value;
- this._title = value;
- }
-
- applyTextContent() {
- const targetElement = this.state ? 'a' : 'span';
- const element = this.element.querySelector(targetElement);
- const hasValue = this.value && this.value != '';
- element.title = this.value;
- element.textContent = hasValue ? this.value : '-';
- }
-}
-Controller.$inject = ['$element', '$translate', '$attrs'];
-
-ngModule.component('vnLabelValue', {
- controller: Controller,
- template: require('./label-value.html'),
- bindings: {
- title: '@?',
- label: '@',
- value: '@',
- state: '@?',
- stateParams: ''
- }
-});
diff --git a/front/core/components/label-value/label-value.spec.js b/front/core/components/label-value/label-value.spec.js
deleted file mode 100644
index 8093d0c874..0000000000
--- a/front/core/components/label-value/label-value.spec.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import './label-value.js';
-import template from './label-value.html';
-
-describe('Component vnInputTime', () => {
- let $element;
- let controller;
-
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
-
- beforeEach(angular.mock.inject($componentController => {
- const $attrs = {};
- $element = angular.element(`${template}`);
- controller = $componentController('vnLabelValue', {$element, $attrs});
- }));
-
- describe('applyTextContent()', () => {
- it(`should set the value on the span element as there's no navigation setted`, () => {
- const value = 'I am the value';
- controller.value = value;
- controller.title = value;
- controller.applyTextContent();
-
- expect(controller.element.querySelector('span').textContent).toEqual(value);
- expect(controller.element.querySelector('span').title).toEqual(value);
- });
-
- it(`should set the value on the anchor element as there's a navigation setted`, () => {
- const value = 'I am the value';
- controller.value = value;
- controller.title = value;
- controller.state = 'some.state.to.go';
- controller.applyTextContent();
-
- expect(controller.element.querySelector('a').textContent).toEqual(value);
- expect(controller.element.querySelector('a').title).toEqual(value);
- });
- });
-});
diff --git a/front/core/components/label-value/style.scss b/front/core/components/label-value/style.scss
index d512e51d16..e4a2cca038 100644
--- a/front/core/components/label-value/style.scss
+++ b/front/core/components/label-value/style.scss
@@ -1,13 +1,6 @@
@import "variables";
vn-label-value > section {
- & > vn-label {
- color: $color-font-secondary;
-
- &::after {
- content: ':';
- }
- }
& > vn-icon {
vertical-align: middle;
color: $color-font-secondary;
diff --git a/front/core/components/label/index.js b/front/core/components/label/index.js
new file mode 100644
index 0000000000..423b033ce7
--- /dev/null
+++ b/front/core/components/label/index.js
@@ -0,0 +1 @@
+import './style.scss';
diff --git a/front/core/components/label/style.scss b/front/core/components/label/style.scss
new file mode 100644
index 0000000000..ef9080f65d
--- /dev/null
+++ b/front/core/components/label/style.scss
@@ -0,0 +1,9 @@
+@import "variables";
+
+vn-label {
+ color: $color-font-secondary;
+
+ &::after {
+ content: ':';
+ }
+}
\ No newline at end of file
diff --git a/front/core/components/menu/menu.js b/front/core/components/menu/menu.js
index ff54d55d4a..3eb169926e 100755
--- a/front/core/components/menu/menu.js
+++ b/front/core/components/menu/menu.js
@@ -1,20 +1,13 @@
import ngModule from '../../module';
-import Popover from '../popover/popover';
+import Popover from '../popover';
export default class Menu extends Popover {
- $postLink() {
- super.$postLink();
- this.popover.addEventListener('click',
- () => this.hide());
+ show(parent) {
+ super.show(parent);
+ this.windowEl.addEventListener('click', () => this.hide());
}
}
-ngModule.component('vnMenu', {
- template: require('../popover/popover.html'),
- controller: Menu,
- transclude: true,
- bindings: {
- onOpen: '&?',
- onClose: '&?'
- }
+ngModule.vnComponent('vnMenu', {
+ controller: Menu
});
diff --git a/front/core/components/multi-check/multi-check.spec.js b/front/core/components/multi-check/multi-check.spec.js
index d2ac91b2a0..c85b160c13 100644
--- a/front/core/components/multi-check/multi-check.spec.js
+++ b/front/core/components/multi-check/multi-check.spec.js
@@ -5,9 +5,7 @@ describe('Component vnMultiCheck', () => {
let controller;
let $element;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject($componentController => {
$element = angular.element(`
`);
diff --git a/front/core/components/multi-check/style.scss b/front/core/components/multi-check/style.scss
index b1e3d94324..bc6a0e8c5c 100644
--- a/front/core/components/multi-check/style.scss
+++ b/front/core/components/multi-check/style.scss
@@ -1,5 +1,5 @@
vn-multi-check {
- vn-check {
+ .vn-check {
margin-bottom: 0.8em
}
}
\ No newline at end of file
diff --git a/front/core/components/popover/index.html b/front/core/components/popover/index.html
new file mode 100644
index 0000000000..de916e3b4a
--- /dev/null
+++ b/front/core/components/popover/index.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/front/core/components/popover/index.js b/front/core/components/popover/index.js
new file mode 100644
index 0000000000..413f4d85fc
--- /dev/null
+++ b/front/core/components/popover/index.js
@@ -0,0 +1,101 @@
+import ngModule from '../../module';
+import Popup from '../popup';
+import template from './index.html';
+import isMobile from '../../lib/is-mobile';
+import './style.scss';
+
+/**
+ * A simple popover.
+ *
+ * @property {HTMLElement} parent The parent element to show drop down relative to
+ *
+ * @event open Thrown when popover is displayed
+ * @event close Thrown when popover is hidden
+ */
+export default class Popover extends Popup {
+ constructor($element, $, $transclude) {
+ super($element, $, $transclude);
+ this.displayMode = isMobile ? 'centered' : 'relative';
+ this.template = template;
+ }
+
+ /**
+ * Shows the popover emitting the open signal. If a parent is specified
+ * it is shown in a visible relative position to it.
+ *
+ * @param {HTMLElement} parent Overrides the parent property
+ */
+ show(parent) {
+ if (parent) this.parent = parent;
+ super.show();
+ this.content = this.popup.querySelector('.content');
+ this.$timeout(() => this.relocate(), 10);
+ }
+
+ hide() {
+ this.content = null;
+ super.hide();
+ }
+
+ /**
+ * Repositions the popover to a correct location relative to the parent.
+ */
+ relocate() {
+ if (!(this.parent && this._shown && this.displayMode == 'relative'))
+ return;
+
+ let margin = 10;
+ let arrow = this.popup.querySelector('.arrow');
+
+ let style = this.windowEl.style;
+ style.width = '';
+ style.height = '';
+
+ let arrowStyle = arrow.style;
+ arrowStyle.top = '';
+ arrowStyle.bottom = '';
+
+ let parentRect = this.parent.getBoundingClientRect();
+ let popoverRect = this.windowEl.getBoundingClientRect();
+ let arrowRect = arrow.getBoundingClientRect();
+ let clamp = (value, min, max) => Math.min(Math.max(value, min), max);
+
+ let arrowHeight = Math.floor(arrowRect.height / 2);
+ let arrowOffset = arrowHeight + margin / 2;
+
+ let docEl = this.document.documentElement;
+ let maxRight = Math.min(window.innerWidth, docEl.clientWidth) - margin;
+ let maxBottom = Math.min(window.innerHeight, docEl.clientHeight) - margin;
+ let maxWith = maxRight - margin;
+ let maxHeight = maxBottom - margin - arrowHeight;
+
+ let width = clamp(popoverRect.width, parentRect.width, maxWith);
+ let height = popoverRect.height;
+
+ let left = parentRect.left + parentRect.width / 2 - width / 2;
+ left = clamp(left, margin, maxRight - width);
+
+ let top = parentRect.top + parentRect.height + arrowOffset;
+ let showTop = top + height > maxBottom;
+ if (showTop) top = parentRect.top - height - arrowOffset;
+ top = Math.max(top, margin);
+
+ if (showTop)
+ arrowStyle.bottom = `0`;
+ else
+ arrowStyle.top = `0`;
+
+ let arrowLeft = (parentRect.left - left) + parentRect.width / 2;
+ arrowLeft = clamp(arrowLeft, arrowHeight, width - arrowHeight);
+ arrowStyle.left = `${arrowLeft}px`;
+
+ style.top = `${top}px`;
+ style.left = `${left}px`;
+ style.width = `${width}px`;
+ if (height > maxHeight) style.height = `${maxHeight}px`;
+ }
+}
+
+ngModule.vnComponent('vnPopover', {
+ controller: Popover
+});
diff --git a/front/core/components/popover/popover.spec.js b/front/core/components/popover/index.spec.js
similarity index 95%
rename from front/core/components/popover/popover.spec.js
rename to front/core/components/popover/index.spec.js
index fd92fd4ab0..54901b2224 100644
--- a/front/core/components/popover/popover.spec.js
+++ b/front/core/components/popover/index.spec.js
@@ -4,9 +4,7 @@ describe('Component vnPopover', () => {
let $parent;
let controller;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject(($compile, $rootScope, $document) => {
$element = $compile(`
test`)($rootScope);
diff --git a/front/core/components/popover/popover.html b/front/core/components/popover/popover.html
deleted file mode 100644
index 7a29ba817f..0000000000
--- a/front/core/components/popover/popover.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
\ No newline at end of file
diff --git a/front/core/components/popover/popover.js b/front/core/components/popover/popover.js
deleted file mode 100644
index 16969acbe0..0000000000
--- a/front/core/components/popover/popover.js
+++ /dev/null
@@ -1,201 +0,0 @@
-import ngModule from '../../module';
-import Component from '../../lib/component';
-import template from './popover.html';
-import './style.scss';
-
-/**
- * A simple popover.
- *
- * @property {HTMLElement} parent The parent element to show drop down relative to
- *
- * @event open Thrown when popover is displayed
- * @event close Thrown when popover is hidden
- */
-export default class Popover extends Component {
- constructor($element, $scope, $timeout, $transitions, $transclude, $compile) {
- super($element, $scope);
- this.$timeout = $timeout;
- this.$transitions = $transitions;
- this._shown = false;
-
- this.element = $compile(template)($scope)[0];
-
- this.popover = this.element.querySelector('.popover');
- this.popover.addEventListener('mousedown', e => this.onMouseDown(e));
-
- this.arrow = this.element.querySelector('.arrow');
- this.content = this.element.querySelector('.content');
-
- $transclude($scope.$parent,
- clone => angular.element(this.content).append(clone));
- }
-
- $onDestroy() {
- this.hide();
- }
-
- /**
- * @type {HTMLElement} The popover child.
- */
- get child() {
- return this.content.firstChild;
- }
-
- set child(value) {
- this.content.innerHTML = '';
- this.content.appendChild(value);
- }
-
- /**
- * @type {Boolean} Wether to show or hide the popover.
- */
- get shown() {
- return this._shown;
- }
-
- set shown(value) {
- if (value)
- this.show();
- else
- this.hide();
- }
-
- /**
- * Shows the popover emitting the open signal. If a parent is specified
- * it is shown in a visible relative position to it.
- *
- * @param {HTMLElement} parent Overrides the parent property
- */
- show(parent) {
- if (this._shown) return;
-
- if (parent) this.parent = parent;
-
- this._shown = true;
-
- if (!this.showTimeout) {
- this.document.body.appendChild(this.element);
- this.element.style.display = 'block';
- }
-
- this.$timeout.cancel(this.showTimeout);
- this.showTimeout = this.$timeout(() => {
- this.showTimeout = null;
- this.element.classList.add('shown');
- }, 30);
-
- this.docKeyDownHandler = e => this.onDocKeyDown(e);
- this.document.addEventListener('keydown', this.docKeyDownHandler);
-
- this.bgMouseDownHandler = e => this.onBgMouseDown(e);
- this.element.addEventListener('mousedown', this.bgMouseDownHandler);
-
- this.deregisterCallback = this.$transitions.onStart({}, () => this.hide());
- this.relocate();
- this.emit('open');
- }
-
- /**
- * Hides the popover emitting the close signal.
- */
- hide() {
- if (!this._shown) return;
-
- this._shown = false;
- this.element.classList.remove('shown');
-
- this.$timeout.cancel(this.showTimeout);
- this.showTimeout = this.$timeout(() => {
- this.showTimeout = null;
- this.element.style.display = 'none';
- this.document.body.removeChild(this.element);
- this.emit('close');
- }, 250);
-
- this.document.removeEventListener('keydown', this.docKeyDownHandler);
- this.docKeyDownHandler = null;
-
- this.element.removeEventListener('mousedown', this.bgMouseDownHandler);
- this.bgMouseDownHandler = null;
-
- if (this.deregisterCallback) this.deregisterCallback();
- this.emit('closeStart');
- }
-
- /**
- * Repositions the popover to a correct location relative to the parent.
- */
- relocate() {
- if (!(this.parent && this._shown)) return;
-
- let margin = 10;
-
- let style = this.popover.style;
- style.width = '';
- style.height = '';
-
- let arrowStyle = this.arrow.style;
- arrowStyle.top = '';
- arrowStyle.bottom = '';
-
- let parentRect = this.parent.getBoundingClientRect();
- let popoverRect = this.popover.getBoundingClientRect();
- let arrowRect = this.arrow.getBoundingClientRect();
- let clamp = (value, min, max) => Math.min(Math.max(value, min), max);
-
- let arrowHeight = Math.floor(arrowRect.height / 2);
- let arrowOffset = arrowHeight + margin / 2;
-
- let docEl = this.document.documentElement;
- let maxRight = Math.min(window.innerWidth, docEl.clientWidth) - margin;
- let maxBottom = Math.min(window.innerHeight, docEl.clientHeight) - margin;
- let maxWith = maxRight - margin;
- let maxHeight = maxBottom - margin - arrowHeight;
-
- let width = clamp(popoverRect.width, parentRect.width, maxWith);
- let height = popoverRect.height;
-
- let left = parentRect.left + parentRect.width / 2 - width / 2;
- left = clamp(left, margin, maxRight - width);
-
- let top = parentRect.top + parentRect.height + arrowOffset;
- let showTop = top + height > maxBottom;
- if (showTop) top = parentRect.top - height - arrowOffset;
- top = Math.max(top, margin);
-
- if (showTop)
- arrowStyle.bottom = `0`;
- else
- arrowStyle.top = `0`;
-
- let arrowLeft = (parentRect.left - left) + parentRect.width / 2;
- arrowLeft = clamp(arrowLeft, arrowHeight, width - arrowHeight);
- arrowStyle.left = `${arrowLeft}px`;
-
- style.top = `${top}px`;
- style.left = `${left}px`;
- style.width = `${width}px`;
- if (height > maxHeight) style.height = `${maxHeight}px`;
- }
-
- onDocKeyDown(event) {
- if (event.defaultPrevented) return;
- if (event.code == 'Escape')
- this.hide();
- }
-
- onMouseDown(event) {
- this.lastMouseEvent = event;
- }
-
- onBgMouseDown(event) {
- if (event == this.lastMouseEvent || event.defaultPrevented) return;
- this.hide();
- }
-}
-Popover.$inject = ['$element', '$scope', '$timeout', '$transitions', '$transclude', '$compile'];
-
-ngModule.component('vnPopover', {
- controller: Popover,
- transclude: true
-});
diff --git a/front/core/components/popover/style.scss b/front/core/components/popover/style.scss
index cc8199134a..decfc47337 100644
--- a/front/core/components/popover/style.scss
+++ b/front/core/components/popover/style.scss
@@ -1,7 +1,6 @@
@import "variables";
.vn-popover {
- display: none;
z-index: 20;
position: fixed;
top: 0;
@@ -20,7 +19,7 @@
transform: translateY(0);
opacity: 1;
}
- & > .popover {
+ & > .window {
position: absolute;
box-shadow: 0 .1em .4em $color-shadow;
z-index: 0;
diff --git a/front/core/components/popup/index.html b/front/core/components/popup/index.html
new file mode 100644
index 0000000000..3e542d51f9
--- /dev/null
+++ b/front/core/components/popup/index.html
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/front/core/components/popup/index.js b/front/core/components/popup/index.js
new file mode 100644
index 0000000000..dfafcd7d48
--- /dev/null
+++ b/front/core/components/popup/index.js
@@ -0,0 +1,131 @@
+import ngModule from '../../module';
+import Component from '../../lib/component';
+import template from './index.html';
+import './style.scss';
+
+/**
+ * Base class for windows displayed over application content.
+ */
+export default class Popup extends Component {
+ constructor($element, $scope, $transclude) {
+ super($element, $scope);
+ this.$transclude = $transclude;
+ this._shown = false;
+ this.displayMode = 'centered';
+ this.template = template;
+ }
+
+ $onDestroy() {
+ this.hide();
+ }
+
+ /**
+ * @type {Boolean} Wether to show or hide the popup.
+ */
+ get shown() {
+ return this._shown;
+ }
+
+ set shown(value) {
+ if (value)
+ this.show();
+ else
+ this.hide();
+ }
+
+ /**
+ * Displays the dialog to the user.
+ */
+ show() {
+ if (this.shown) return;
+ this._shown = true;
+
+ let linkFn = this.$compile(this.template);
+ this.$contentScope = this.$.$new();
+ this.popup = linkFn(this.$contentScope, null,
+ {parentBoundTranscludeFn: this.$transclude}
+ )[0];
+ this.windowEl = this.popup.querySelector('.window');
+
+ let classList = this.popup.classList;
+ classList.add(this.displayMode);
+ classList.add(...this.constructor.$classNames);
+
+ if (!this.transitionTimeout)
+ this.document.body.appendChild(this.popup);
+
+ this.keyDownHandler = e => this.onkeyDown(e);
+ this.document.addEventListener('keydown', this.keyDownHandler);
+
+ this.deregisterCallback = this.$transitions.onStart({},
+ () => this.hide());
+
+ this.$timeout.cancel(this.transitionTimeout);
+ this.transitionTimeout = this.$timeout(() => {
+ this.transitionTimeout = null;
+ classList.add('shown');
+ }, 10);
+
+ this.emit('open');
+ }
+
+ /**
+ * Hides the dialog calling the response handler.
+ */
+ hide() {
+ if (!this.shown) return;
+
+ this.document.removeEventListener('keydown', this.keyDownHandler);
+ this.keyDownHandler = null;
+
+ if (this.deregisterCallback) {
+ this.deregisterCallback();
+ this.deregisterCallback = null;
+ }
+
+ this.popup.classList.remove('shown');
+
+ this.$timeout.cancel(this.transitionTimeout);
+ this.transitionTimeout = this.$timeout(
+ () => this.onClose(), 200);
+
+ this.lastEvent = null;
+ this._shown = false;
+ this.emit('closeStart');
+ }
+
+ onClose() {
+ this.transitionTimeout = null;
+ this.document.body.removeChild(this.popup);
+
+ this.$contentScope.$destroy();
+ this.popup.remove();
+ this.popup = null;
+ this.windowEl = null;
+
+ this.emit('close');
+ }
+
+ onWindowMouseDown(event) {
+ this.lastEvent = event;
+ }
+
+ onBgMouseDown(event) {
+ if (!event.defaultPrevented && event != this.lastEvent)
+ this.hide();
+ }
+
+ onkeyDown(event) {
+ if (!event.defaultPrevented && event.key == 'Escape')
+ this.hide();
+ }
+}
+Popup.$inject = ['$element', '$scope', '$transclude'];
+
+ngModule.vnComponent('vnPopup', {
+ controller: Popup,
+ transclude: true,
+ bindings: {
+ shown: '=?'
+ }
+});
diff --git a/front/core/components/popup/style.scss b/front/core/components/popup/style.scss
new file mode 100644
index 0000000000..42d69141d0
--- /dev/null
+++ b/front/core/components/popup/style.scss
@@ -0,0 +1,40 @@
+@import "effects";
+
+.vn-popup {
+ z-index: 20;
+ position: fixed;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 100%;
+ opacity: 0;
+ transition: opacity 200ms ease-in-out;
+
+ &.shown {
+ opacity: 1;
+ transform: scale3d(1, 1, 1);
+ }
+ &.centered {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: rgba(0, 0, 0, .6);
+ padding: 1em;
+ box-sizing: border-box;
+
+ & > .window {
+ position: relative;
+ box-shadow: 0 0 .4em $color-shadow;
+ background-color: $color-bg-panel;
+ border-radius: .2em;
+ overflow: auto;
+ box-sizing: border-box;
+ max-height: 100%;
+ transform: scale3d(.9, .9, .9);
+ transition: transform 200ms ease-in-out;
+ }
+ &.shown > .window {
+ transform: scale3d(1, 1, 1);
+ }
+ }
+}
diff --git a/front/core/components/radio/index.js b/front/core/components/radio/index.js
index 0196683308..fa2d0f41c8 100644
--- a/front/core/components/radio/index.js
+++ b/front/core/components/radio/index.js
@@ -38,8 +38,7 @@ export default class Radio extends Toggle {
onClick(event) {
if (super.onClick(event)) return;
- this.field = this.val;
- this.changed();
+ this.change(this.val);
}
}
diff --git a/front/core/components/radio/index.spec.js b/front/core/components/radio/index.spec.js
index 1d5d490a8d..5a512c8baa 100644
--- a/front/core/components/radio/index.spec.js
+++ b/front/core/components/radio/index.spec.js
@@ -3,9 +3,7 @@ describe('Component vnRadio', () => {
let $ctrl;
let element;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject(($compile, $rootScope) => {
$element = $compile(`
.btn {
border-radius: 50%;
@@ -22,4 +22,7 @@ vn-radio {
background-color: $color-main;
}
}
+ &.disabled.checked > .btn > .mark {
+ background-color: $color-font-secondary;
+ }
}
diff --git a/front/core/components/range/style.scss b/front/core/components/range/style.scss
index 878035f3a3..a370b4c39c 100644
--- a/front/core/components/range/style.scss
+++ b/front/core/components/range/style.scss
@@ -35,7 +35,7 @@
}
}
-vn-range {
+.vn-range {
& > label {
font-size: 12px;
diff --git a/front/core/components/searchbar/search-panel.js b/front/core/components/searchbar/search-panel.js
index 96dc67ed54..f473c2b049 100644
--- a/front/core/components/searchbar/search-panel.js
+++ b/front/core/components/searchbar/search-panel.js
@@ -1,6 +1,6 @@
import Component from '../../lib/component';
-export default class extends Component {
+export default class SearchPanel extends Component {
set filter(value) {
this.$.filter = value;
}
@@ -13,6 +13,6 @@ export default class extends Component {
if (!this.onSubmit)
throw new Error('SearchPanel::onSubmit() method not defined');
- this.onSubmit(this.filter);
+ this.onSubmit({$filter: this.filter});
}
}
diff --git a/front/core/components/searchbar/searchbar.js b/front/core/components/searchbar/searchbar.js
index f3db40a93a..d74a4e7a65 100644
--- a/front/core/components/searchbar/searchbar.js
+++ b/front/core/components/searchbar/searchbar.js
@@ -65,23 +65,24 @@ export default class Controller extends Component {
openPanel(event) {
if (event.defaultPrevented) return;
+ this.$.popover.show(this.element);
this.$panelScope = this.$.$new();
- this.$panel = this.$compile(`<${this.panel}/>`)(this.$panelScope);
- let panel = this.$panel[0].$ctrl;
+ this.panelEl = this.$compile(`<${this.panel}/>`)(this.$panelScope)[0];
+ let panel = this.panelEl.$ctrl;
if (this.shownFilter)
panel.filter = JSON.parse(JSON.stringify(this.shownFilter));
- panel.onSubmit = filter => this.onPanelSubmit(filter);
+ panel.onSubmit = filter => this.onPanelSubmit(filter.$filter);
- this.$.popover.parent = this.element;
- this.$.popover.child = this.$panel[0];
- this.$.popover.show();
+ this.$.popover.content.appendChild(this.panelEl);
}
onPopoverClose() {
this.$panelScope.$destroy();
- this.$panel.remove();
- this.$panel = null;
+ this.$panelScope = null;
+
+ this.panelEl.remove();
+ this.panelEl = null;
}
onPanelSubmit(filter) {
diff --git a/front/core/components/searchbar/searchbar.spec.js b/front/core/components/searchbar/searchbar.spec.js
index 17737f11f7..4cd7ec74a6 100644
--- a/front/core/components/searchbar/searchbar.spec.js
+++ b/front/core/components/searchbar/searchbar.spec.js
@@ -6,15 +6,13 @@ describe('Component vnSearchbar', () => {
let $state;
let $scope;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
- beforeEach(angular.mock.inject(($componentController, _$state_, $rootScope, $compile) => {
+ beforeEach(angular.mock.inject(($componentController, _$state_, $rootScope) => {
$scope = $rootScope.$new();
$state = _$state_;
$element = angular.element(`
`);
- controller = $componentController('vnSearchbar', {$element, $state, $scope, $compile});
+ controller = $componentController('vnSearchbar', {$element, $scope});
controller.panel = 'vn-client-search-panel';
}));
@@ -41,7 +39,7 @@ describe('Component vnSearchbar', () => {
describe('onStateChange()', () => {
it(`should set a formated _filter in the controller`, () => {
spyOn(controller, 'doSearch');
- Object.assign(controller.$state.params, {q: '{"id": 999}'});
+ Object.assign($state.params, {q: '{"id": 999}'});
controller.onStateChange();
@@ -73,27 +71,11 @@ describe('Component vnSearchbar', () => {
describe('filter() setter', () => {
it(`should call $state.go() to replace the current state location instead of creating a new one`, () => {
controller._filter = {};
- spyOn(controller.$state, 'go');
+ spyOn($state, 'go');
controller.filter = {expected: 'filter'};
expect(controller._filter).toEqual(controller.filter);
- expect(controller.$state.go).toHaveBeenCalledWith('.', Object({q: '{"expected":"filter"}'}), Object({location: 'replace'}));
- });
- });
-
- describe('onPopoverClose()', () => {
- it(`should get rid of $panel and $panelScope`, () => {
- controller.$panel = {
- I: 'should disappear',
- remove: () => {}
- };
-
- controller.$panelScope = {$destroy: jasmine.createSpy('$destroy')};
-
- controller.onPopoverClose();
-
- expect(controller.$panelScope.$destroy).toHaveBeenCalledWith();
- expect(controller.$panel).toBeNull();
+ expect($state.go).toHaveBeenCalledWith('.', Object({q: '{"expected":"filter"}'}), Object({location: 'replace'}));
});
});
diff --git a/front/core/components/spinner/spinner.spec.js b/front/core/components/spinner/spinner.spec.js
index 437139c8c5..dbcdb0acda 100644
--- a/front/core/components/spinner/spinner.spec.js
+++ b/front/core/components/spinner/spinner.spec.js
@@ -4,9 +4,7 @@ describe('Component vnSpinner', () => {
let $element;
let controller;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($compile, $rootScope) => {
$element = $compile(`
`)($rootScope);
diff --git a/front/core/components/step-control/step-control.spec.js b/front/core/components/step-control/step-control.spec.js
index f309c40b2f..944457cfe9 100644
--- a/front/core/components/step-control/step-control.spec.js
+++ b/front/core/components/step-control/step-control.spec.js
@@ -4,9 +4,7 @@ describe('Component vnStepControl', () => {
let controller;
let $state;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($componentController, _$state_) => {
$state = _$state_;
diff --git a/front/core/components/table/index.spec.js b/front/core/components/table/index.spec.js
index b0c92cee0c..40e1ef74e8 100644
--- a/front/core/components/table/index.spec.js
+++ b/front/core/components/table/index.spec.js
@@ -5,9 +5,7 @@ describe('Component vnTable', () => {
let $element;
let controller;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss
index 839e1a7bbd..a3fd4513b9 100644
--- a/front/core/components/table/style.scss
+++ b/front/core/components/table/style.scss
@@ -169,11 +169,11 @@ vn-table {
}
}
}
- vn-textfield {
+ .vn-textfield {
float: right;
margin: 0!important;
}
- vn-check {
+ .vn-check {
margin: 0;
}
}
\ No newline at end of file
diff --git a/front/core/components/textarea/index.js b/front/core/components/textarea/index.js
index fc08345665..8fcae7b0fe 100644
--- a/front/core/components/textarea/index.js
+++ b/front/core/components/textarea/index.js
@@ -6,7 +6,7 @@ export default class Textarea extends Field {
super($element, $scope, $compile);
let html = `
`;
- this.input = $compile(html)($scope)[0];
+ this.input = this.$compile(html)($scope)[0];
}
set rows(value) {
diff --git a/front/core/components/textarea/index.spec.js b/front/core/components/textarea/index.spec.js
index 1d246cfe08..7b7547b6bf 100644
--- a/front/core/components/textarea/index.spec.js
+++ b/front/core/components/textarea/index.spec.js
@@ -4,9 +4,7 @@ describe('Component vnTextarea', () => {
let $element;
let $ctrl;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($compile, $rootScope) => {
$element = $compile(`
`)($rootScope);
diff --git a/front/core/components/th/index.spec.js b/front/core/components/th/index.spec.js
index 6ab2652e71..dc9e43672d 100644
--- a/front/core/components/th/index.spec.js
+++ b/front/core/components/th/index.spec.js
@@ -5,9 +5,7 @@ describe('Component vnTh', () => {
let controller;
let $element;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject($componentController => {
$element = angular.element(`
${template}
`);
diff --git a/front/core/components/toggle/index.js b/front/core/components/toggle/index.js
index 70698ede86..bc3851bbca 100644
--- a/front/core/components/toggle/index.js
+++ b/front/core/components/toggle/index.js
@@ -11,13 +11,12 @@ export default class Toggle extends FormInput {
constructor($element, $) {
super($element, $);
this.initTabIndex();
- this.classList.add('vn-toggle');
this.element.addEventListener('click', e => this.onClick(e));
this.element.addEventListener('keydown', e => this.onKeydown(e));
}
onKeydown(event) {
- if (!event.defaultPrevented && event.code == 'Space')
+ if (!event.defaultPrevented && event.key == ' ')
this.element.click();
}
@@ -26,10 +25,9 @@ export default class Toggle extends FormInput {
return true;
}
- changed() {
+ change(value) {
this.$.$applyAsync();
- this.element.dispatchEvent(new Event('change'));
- this.emit('change', {value: this.field});
+ super.change(value);
}
}
diff --git a/front/core/components/toggle/style.scss b/front/core/components/toggle/style.scss
index a373b6f4c5..af3dc3ae5e 100644
--- a/front/core/components/toggle/style.scss
+++ b/front/core/components/toggle/style.scss
@@ -7,11 +7,8 @@
align-items: center;
outline: none;
- &.disabled {
- cursor: inherit;
- }
& > span {
- font-size: $font-size;
+ font-size: 1rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
@@ -28,13 +25,6 @@
margin-right: .6em;
border: 2px solid #666;
}
- &.checked > .btn {
- border-color: $color-main;
-
- & > .focus-mark {
- background-color: rgba($color-main, .15);
- }
- }
& > .btn > .focus-mark {
position: absolute;
top: 50%;
@@ -49,6 +39,23 @@
transition: transform 250ms;
background-color: rgba(0, 0, 0, .1);
}
+ &.checked > .btn {
+ border-color: $color-main;
+
+ & > .focus-mark {
+ background-color: rgba($color-main, .15);
+ }
+ }
+ &.disabled {
+ cursor: inherit;
+
+ &.checked > .btn {
+ border-color: $color-font-secondary;
+ }
+ }
+ &.readonly {
+ cursor: inherit;
+ }
&:focus:not(.disabled) > .btn > .focus-mark {
transform: scale3d(1, 1, 1);
}
diff --git a/front/core/components/tooltip/tooltip.spec.js b/front/core/components/tooltip/tooltip.spec.js
index 25689cfcfc..15595c61bd 100644
--- a/front/core/components/tooltip/tooltip.spec.js
+++ b/front/core/components/tooltip/tooltip.spec.js
@@ -8,9 +8,7 @@ xdescribe('Component vnTooltip', () => {
let element;
let window;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject(($componentController, $compile, $templateRequest, $document) => {
$element = angular.element(`
test`);
diff --git a/front/core/components/treeview/index.spec.js b/front/core/components/treeview/index.spec.js
index 87796f06e3..e03345c336 100644
--- a/front/core/components/treeview/index.spec.js
+++ b/front/core/components/treeview/index.spec.js
@@ -2,9 +2,7 @@ describe('Component vnTreeview', () => {
let controller;
let $element;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject(($compile, $rootScope) => {
$element = $compile(`
`)($rootScope);
diff --git a/front/core/components/watcher/watcher.html b/front/core/components/watcher/watcher.html
index 76315ece8e..c3c54a65db 100644
--- a/front/core/components/watcher/watcher.html
+++ b/front/core/components/watcher/watcher.html
@@ -2,5 +2,5 @@
vn-id="confirm"
question="Are you sure exit without saving?"
message="Unsaved changes will be lost"
- on-response="$ctrl.onConfirmResponse(response)">
+ on-response="$ctrl.onConfirmResponse($response)">
\ No newline at end of file
diff --git a/front/core/components/watcher/watcher.js b/front/core/components/watcher/watcher.js
index 02b92dda66..ddef745fda 100644
--- a/front/core/components/watcher/watcher.js
+++ b/front/core/components/watcher/watcher.js
@@ -181,7 +181,7 @@ export default class Watcher extends Component {
* Notifies the user that the data has been saved.
*/
notifySaved() {
- this.vnApp.showSuccess(this._.instant('Data saved!'));
+ this.vnApp.showSuccess(this.$t('Data saved!'));
}
setPristine() {
@@ -203,7 +203,7 @@ export default class Watcher extends Component {
}
onConfirmResponse(response) {
- if (response === 'ACCEPT') {
+ if (response === 'accept') {
if (this.data)
Object.assign(this.data, this.orgData);
this.$state.go(this.state);
diff --git a/front/core/components/watcher/watcher.spec.js b/front/core/components/watcher/watcher.spec.js
index 32de7dda79..2f7be47049 100644
--- a/front/core/components/watcher/watcher.spec.js
+++ b/front/core/components/watcher/watcher.spec.js
@@ -10,9 +10,7 @@ describe('Component vnWatcher', () => {
let $attrs;
let $q;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$state_, _$q_) => {
$scope = $rootScope.$new();
@@ -225,9 +223,9 @@ describe('Component vnWatcher', () => {
});
describe(`onConfirmResponse()`, () => {
- describe(`when response is ACCEPT`, () => {
+ describe(`when response is accept`, () => {
it(`should call Object.assing on controlle.data with controller.orgData then call go() on state`, () => {
- let response = 'ACCEPT';
+ let response = 'accept';
controller.data = {};
controller.orgData = {name: 'Batman'};
controller.$state = {go: jasmine.createSpy('go')};
@@ -239,9 +237,9 @@ describe('Component vnWatcher', () => {
});
});
- describe(`when response is not ACCEPT`, () => {
+ describe(`when response is not accept`, () => {
it(`should set controller.state to null`, () => {
- let response = 'anything but ACCEPT';
+ let response = 'anything but accept';
controller.state = 'Batman';
controller.onConfirmResponse(response);
diff --git a/front/core/components/wday-picker/style.scss b/front/core/components/wday-picker/style.scss
index ba2f3347f9..c6899ad6aa 100644
--- a/front/core/components/wday-picker/style.scss
+++ b/front/core/components/wday-picker/style.scss
@@ -1,8 +1,11 @@
@import "effects";
-vn-wday-picker {
+.vn-wday-picker {
text-align: center;
+ &:focus {
+ outline: solid 1px rgba(0, 0, 0, .1);
+ }
& > span {
@extend %clickable;
border-radius: 50%;
diff --git a/front/core/directives/dialog.js b/front/core/directives/dialog.js
index 3aadcaad09..167bd04e7f 100644
--- a/front/core/directives/dialog.js
+++ b/front/core/directives/dialog.js
@@ -1,5 +1,5 @@
import ngModule from '../module';
-import Dialog from '../components/dialog/dialog';
+import Dialog from '../components/dialog';
import {kebabToCamel} from '../lib/string';
/**
diff --git a/front/core/directives/focus.js b/front/core/directives/focus.js
index b9527479be..869e9a7f04 100644
--- a/front/core/directives/focus.js
+++ b/front/core/directives/focus.js
@@ -1,7 +1,5 @@
import ngModule from '../module';
-
-const regex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i;
-export const isMobile = regex.test(navigator.userAgent);
+import isMobile from '../lib/is-mobile';
export function focus($scope, input) {
if (isMobile) return;
@@ -23,9 +21,12 @@ export function focus($scope, input) {
}
input.focus();
- $scope.$applyAsync(() => {
- input.select();
- });
+
+ if (input.select) {
+ $scope.$applyAsync(() => {
+ input.select();
+ });
+ }
}
/**
diff --git a/front/core/directives/popover.js b/front/core/directives/popover.js
index ad147e9c0f..5d86665baf 100644
--- a/front/core/directives/popover.js
+++ b/front/core/directives/popover.js
@@ -1,5 +1,5 @@
import ngModule from '../module';
-import Popover from '../components/popover/popover';
+import Popover from '../components/popover';
import {kebabToCamel} from '../lib/string';
/**
@@ -15,10 +15,8 @@ export function directive() {
if (event.defaultPrevented) return;
let popoverKey = kebabToCamel($attrs.vnPopover);
let popover = $scope[popoverKey];
- if (popover instanceof Popover) {
- popover.parent = $element[0];
- popover.show();
- }
+ if (popover instanceof Popover)
+ popover.show($element[0]);
});
}
};
diff --git a/front/core/directives/specs/acl.spec.js b/front/core/directives/specs/acl.spec.js
index a7884a12d7..94000d543f 100644
--- a/front/core/directives/specs/acl.spec.js
+++ b/front/core/directives/specs/acl.spec.js
@@ -4,12 +4,10 @@ describe('Directive acl', () => {
let element;
let compile;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject(($httpBackend, aclService) => {
- $httpBackend.whenGET('/api/Accounts/acl')
+ $httpBackend.whenGET('Accounts/acl')
.respond({
user: {id: 1, name: 'myUser'},
roles: [
diff --git a/front/core/directives/specs/bind.spec.js b/front/core/directives/specs/bind.spec.js
index 8a34ad1d8c..d8d2cb359d 100644
--- a/front/core/directives/specs/bind.spec.js
+++ b/front/core/directives/specs/bind.spec.js
@@ -1,9 +1,7 @@
describe('Directive bind', () => {
let compile;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
compile = html => {
inject(($compile, $rootScope) => {
diff --git a/front/core/directives/specs/dialog.spec.js b/front/core/directives/specs/dialog.spec.js
index 4838705886..4d5a02e315 100644
--- a/front/core/directives/specs/dialog.spec.js
+++ b/front/core/directives/specs/dialog.spec.js
@@ -5,9 +5,7 @@ describe('Directive dialog', () => {
let compile;
let controller;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
compile = _element => {
inject(($compile, $rootScope) => {
@@ -19,9 +17,9 @@ describe('Directive dialog', () => {
});
};
- beforeEach(angular.mock.inject($componentController => {
- $element = angular.element('
');
- controller = $componentController('vnDialog', {$element: $element, $transclude: null});
+ beforeEach(angular.mock.inject(($rootScope, $compile) => {
+ $element = $compile('
')($rootScope);
+ controller = $element.controller('vnDialog');
}));
it('should call show() function if dialog is a instance of vnDialog', () => {
diff --git a/front/core/directives/specs/focus.spec.js b/front/core/directives/specs/focus.spec.js
index 4034657b44..82cd7d84da 100644
--- a/front/core/directives/specs/focus.spec.js
+++ b/front/core/directives/specs/focus.spec.js
@@ -3,9 +3,7 @@ describe('Directive focus', () => {
let $element;
let compile;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
compile = (_element, _childElement) => {
inject(($compile, $rootScope) => {
diff --git a/front/core/directives/specs/id.spec.js b/front/core/directives/specs/id.spec.js
index dcadae2d52..9d400701a8 100644
--- a/front/core/directives/specs/id.spec.js
+++ b/front/core/directives/specs/id.spec.js
@@ -4,9 +4,7 @@ describe('Directive vnId', () => {
let $element;
let compile;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
compile = _element => {
inject(($compile, $rootScope) => {
diff --git a/front/core/directives/specs/rule.spec.js b/front/core/directives/specs/rule.spec.js
index f3671a5e2e..522ca56379 100644
--- a/front/core/directives/specs/rule.spec.js
+++ b/front/core/directives/specs/rule.spec.js
@@ -3,9 +3,7 @@ describe('Directive rule', () => {
let $element;
let element;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
function compile(html, value) {
inject(($compile, $rootScope, $window) => {
diff --git a/front/core/directives/specs/zoom-image.spec.js b/front/core/directives/specs/zoom-image.spec.js
index 08acb3b34e..f9d9c18014 100644
--- a/front/core/directives/specs/zoom-image.spec.js
+++ b/front/core/directives/specs/zoom-image.spec.js
@@ -5,9 +5,7 @@ describe('Directive zoomImage', () => {
let srcZoom = 'http://zoom.img.jpg/';
let findContainer;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject(($compile, $rootScope) => {
compile = $compile;
diff --git a/front/core/directives/uvc.js b/front/core/directives/uvc.js
index 664dfae53b..e464a93abf 100644
--- a/front/core/directives/uvc.js
+++ b/front/core/directives/uvc.js
@@ -23,7 +23,7 @@ export function directive($http, $compile, vnApp, $translate) {
}
function getTableConfig(tableCode) {
- return $http.get(`/api/UserConfigViews/getConfig?tableCode=${tableCode}`);
+ return $http.get(`UserConfigViews/getConfig?tableCode=${tableCode}`);
}
function createViewConfig(config, fields) {
@@ -73,7 +73,7 @@ export function directive($http, $compile, vnApp, $translate) {
function saveConfiguration(tableConfiguration) {
tableConfiguration.configuration = JSON.parse(JSON.stringify(tableConfiguration.configuration));
- return $http.post(`/api/UserConfigViews/save`, tableConfiguration);
+ return $http.post(`UserConfigViews/save`, tableConfiguration);
}
return {
diff --git a/front/core/directives/zoom-image.js b/front/core/directives/zoom-image.js
index 574a6e5492..607dbe3378 100644
--- a/front/core/directives/zoom-image.js
+++ b/front/core/directives/zoom-image.js
@@ -25,7 +25,7 @@ export function directive($timeout) {
}
function onKeyDown(event) {
- if (event.keyCode === 27)
+ if (event.key === 'Escape')
destroyContainers();
}
diff --git a/front/core/filters/specs/currency.spec.js b/front/core/filters/specs/currency.spec.js
index 2d7c0a5144..28a4a36405 100644
--- a/front/core/filters/specs/currency.spec.js
+++ b/front/core/filters/specs/currency.spec.js
@@ -2,9 +2,7 @@ describe('Currency filter', () => {
let compile;
let $element;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
compile = html => {
inject(($compile, $rootScope) => {
diff --git a/front/core/lib/component.js b/front/core/lib/component.js
index 923657d1aa..979420b22f 100644
--- a/front/core/lib/component.js
+++ b/front/core/lib/component.js
@@ -1,3 +1,4 @@
+import ngModule from '../module';
import EventEmitter from './event-emitter';
import {kebabToCamel} from './string';
@@ -38,11 +39,101 @@ export default class Component extends EventEmitter {
get window() {
return this.document.defaultView;
}
+
/**
* The component owner document.
*/
get document() {
return this.element.ownerDocument;
}
+
+ /**
+ * Translates an string.
+ *
+ * @param {String} string String to translate
+ * @param {Array} params Translate parameters
+ * @return {String} The translated string
+ */
+ $t(string, params) {
+ return this.$translate.instant(string, params);
+ }
+
+ createBoundTranscludeFn(linkFn) {
+ let scope = this.$;
+ let previousBoundTranscludeFn = this.$transclude.$$boundTransclude;
+
+ function vnBoundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
+ if (!transcludedScope) {
+ transcludedScope = scope.$new(false, containingScope);
+ transcludedScope.$$transcluded = true;
+ }
+ return linkFn(transcludedScope, cloneFn, {
+ parentBoundTranscludeFn: previousBoundTranscludeFn,
+ transcludeControllers: controllers,
+ futureParentElement: futureParentElement
+ });
+ }
+ vnBoundTranscludeFn.$$slots = previousBoundTranscludeFn.$$slots;
+
+ return vnBoundTranscludeFn;
+ }
+
+ fillDefaultSlot(template) {
+ let linkFn = this.$compile(template);
+ this.$transclude.$$boundTransclude = this.createBoundTranscludeFn(linkFn);
+ }
+
+ fillSlot(slot, template) {
+ let slots = this.$transclude.$$boundTransclude.$$slots;
+ let linkFn = this.$compile(template);
+ slots[slot] = this.createBoundTranscludeFn(linkFn);
+ }
+
+ copySlot(slot, $transclude) {
+ this.$transclude.$$boundTransclude.$$slots[slot] =
+ $transclude.$$boundTransclude.$$slots[slot];
+ }
}
Component.$inject = ['$element', '$scope'];
+
+function runFn(
+ $translate,
+ $q,
+ $http,
+ $state,
+ $stateParams,
+ $timeout,
+ $transitions,
+ $compile,
+ $filter,
+ $interpolate,
+ vnApp) {
+ Object.assign(Component.prototype, {
+ $translate,
+ $q,
+ $http,
+ $state,
+ $params: $stateParams,
+ $timeout,
+ $transitions,
+ $compile,
+ $filter,
+ $interpolate,
+ vnApp
+ });
+}
+runFn.$inject = [
+ '$translate',
+ '$q',
+ '$http',
+ '$state',
+ '$stateParams',
+ '$timeout',
+ '$transitions',
+ '$compile',
+ '$filter',
+ '$interpolate',
+ 'vnApp'
+];
+
+ngModule.run(runFn);
diff --git a/front/core/lib/event-emitter.js b/front/core/lib/event-emitter.js
index 022e4e98c2..83e3612532 100644
--- a/front/core/lib/event-emitter.js
+++ b/front/core/lib/event-emitter.js
@@ -51,17 +51,17 @@ export default class EventEmitter {
* Emits an event.
*
* @param {String} eventName The event name
- * @param {...*} args Arguments to pass to the callbacks
+ * @param {Object} args Arguments to pass to the callbacks
*/
- emit(eventName) {
+ emit(eventName, args) {
if (!this.$events || !this.$events[eventName])
return;
- let args = Array.prototype.slice.call(arguments, 1);
+ args = Object.assign({$this: this}, args);
let callbacks = this.$events[eventName];
for (let callback of callbacks)
- callback.callback.apply(callback.thisArg, args);
+ callback.callback.call(callback.thisArg, args);
}
/**
diff --git a/front/core/lib/is-mobile.js b/front/core/lib/is-mobile.js
new file mode 100644
index 0000000000..5fae749dfc
--- /dev/null
+++ b/front/core/lib/is-mobile.js
@@ -0,0 +1,4 @@
+
+const regex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i;
+const isMobile = regex.test(navigator.userAgent);
+export default isMobile;
diff --git a/front/core/lib/modified.js b/front/core/lib/modified.js
index 46e61b4f5d..620dc75b37 100644
--- a/front/core/lib/modified.js
+++ b/front/core/lib/modified.js
@@ -1,22 +1,21 @@
import isEqual from './equals';
export default function getModifiedData(object, objectOld) {
- var newObject = {};
+ let newObject = {};
if (objectOld === null)
return object;
- for (var k in object) {
- var val = object[k];
- var valOld = objectOld[k] === undefined ? null : objectOld[k];
+ for (let k in object) {
+ let val = object[k];
+ let valOld = objectOld[k] === undefined ? null : objectOld[k];
if (!isEqual(val, valOld)) {
- if (val instanceof Date) {
+ if (val instanceof Date)
newObject[k] = new Date(val.getTime());
- } else if (val instanceof Object) {
+ else if (val instanceof Object)
newObject[k] = getModifiedData(val, valOld);
- } else {
+ else
newObject[k] = val;
- }
}
}
diff --git a/front/core/lib/module-loader.js b/front/core/lib/module-loader.js
index 477b1c943a..18f0a3ea3b 100644
--- a/front/core/lib/module-loader.js
+++ b/front/core/lib/module-loader.js
@@ -3,64 +3,80 @@ import moduleImport from 'module-import';
factory.$inject = ['$http', '$window', '$ocLazyLoad', '$translatePartialLoader', '$translate', '$q'];
export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $translate, $q) {
+ /**
+ * Used to load application modules lazily.
+ */
class ModuleLoader {
constructor() {
- this._loaded = {};
+ this.loaded = {};
+ this.imports = {};
+ this.moduleImport = moduleImport;
+ this.modelInfo = $http.get(`modelInfo`)
+ .then(json => {
+ this.onModelInfoReady(json);
+ this.modelInfo = true;
+ });
}
- load(moduleName, validations) {
- let moduleConf = $window.routes.find(i => i && i.module == moduleName);
+
+ /**
+ * Loads the passed module and it's dependencies. Loading a module
+ * implies load the webpack chunk, translations, recursively load
+ * module dependencies and finally register all of them into Angular.
+ *
+ * @param {String} mod The module name to load
+ * @return {Promise} Will be resolved when loaded, when module is
+ * already loaded it returns a resolved promise
+ */
+ load(mod) {
+ let mods = [];
+ return this.loadRec(mod, mods);
+ }
+
+ loadRec(mod, mods) {
+ let loaded = this.loaded[mod];
+
+ if (loaded === true || mods.indexOf(mod) != -1)
+ return $q.resolve(true);
+ if (loaded instanceof $q)
+ return loaded;
+
+ let moduleConf = $window.routes.find(i => i && i.module == mod);
if (!moduleConf)
- return $q.reject(new Error(`Module not found: ${moduleName}`));
+ return $q.reject(new Error(`Module not found: ${mod}`));
- let loaded = this._loaded;
+ let promises = [];
- if (loaded[moduleName] === true)
- return Promise.resolve(true);
- if (loaded[moduleName] instanceof Promise)
- return loaded[moduleName];
- if (loaded[moduleName] === false)
- return Promise.resolve(true);
+ if (this.modelInfo instanceof $q)
+ promises.push(this.modelInfo);
- loaded[moduleName] = false;
+ $translatePartialLoader.addPart(mod);
+ promises.push($translate.refresh());
+
+ let modImport = this.imports[mod];
+
+ if (!modImport) {
+ modImport = this.imports[mod] = this.moduleImport(mod)
+ .then(() => this.imports[mod] = true);
+ }
+ if (modImport && modImport.then)
+ promises.push(modImport);
- let depPromises = [];
let deps = moduleConf.dependencies;
if (deps) {
+ mods.push(mod);
for (let dep of deps)
- depPromises.push(this.load(dep, validations));
+ promises.push(this.loadRec(dep, mods));
+ mods.pop();
}
- loaded[moduleName] = new Promise((resolve, reject) => {
- Promise.all(depPromises).then(() => {
- let promises = [];
-
- $translatePartialLoader.addPart(moduleName);
- promises.push(new Promise(resolve => {
- $translate.refresh().then(resolve, resolve);
- }));
-
- if (validations) {
- promises.push(new Promise(resolve => {
- $http.get(`/${moduleName}/api/modelInfo`).then(
- json => this.onValidationsReady(json, resolve),
- () => resolve()
- );
- }));
- }
-
- promises.push(moduleImport(moduleName));
-
- Promise.all(promises).then(() => {
- loaded[moduleName] = true;
- resolve($ocLazyLoad.load({name: moduleName}));
- }).catch(reject);
- }).catch(reject);
- });
-
- return loaded[moduleName];
+ this.loaded[mod] = $q.all(promises)
+ .then(() => $ocLazyLoad.load({name: mod}))
+ .then(() => this.loaded[mod] = true);
+ return this.loaded[mod];
}
- onValidationsReady(json, resolve) {
+
+ onModelInfoReady(json) {
let entities = json.data;
for (let entity in entities) {
let fields = entities[entity].validations;
@@ -72,12 +88,13 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t
}
Object.assign($window.validations, json.data);
- resolve();
}
+
parseValidation(val) {
switch (val.validation) {
case 'custom':
- // TODO: Replace eval
+ // TODO: Don't use eval() because it's "evil".
+ // How to do the same without eval?
val.bindedFunction = eval(`(${val.bindedFunction})`);
break;
case 'format':
diff --git a/front/core/lib/section.js b/front/core/lib/section.js
deleted file mode 100644
index 7e59bb8b30..0000000000
--- a/front/core/lib/section.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import Component from './component';
-
-/**
- * Class with commonly injected services assigned as properties. It also has
- * abbreviations for commonly used methods like tranlation.
- *
- * @property {Object} $translate Angular tranlation service
- * @property {Object} $http Angular HTTP service
- * @property {Object} $state Router state service
- * @property {Object} $stateParams Router state parameters
- */
-export default class Section extends Component {
- constructor($element, $scope, $translate, $http, $state) {
- super($element, $scope);
- Object.assign(this, {
- $translate,
- $http,
- $state,
- $stateParams: $state.params
- });
- }
-
- /**
- * Translates an string.
- *
- * @param {String} string String to translate
- * @param {Array} params Translate parameters
- * @return {String} The translated string
- */
- _(string, params) {
- return this.$translate.instant(string, params, );
- }
-}
-Section.$inject = ['$element', '$scope', '$translate', '$http', '$state'];
diff --git a/front/core/lib/specs/acl-service.spec.js b/front/core/lib/specs/acl-service.spec.js
index ed15357bde..7756347b03 100644
--- a/front/core/lib/specs/acl-service.spec.js
+++ b/front/core/lib/specs/acl-service.spec.js
@@ -1,12 +1,10 @@
describe('Service acl', () => {
let aclService;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject((_aclService_, $httpBackend) => {
- $httpBackend.when('GET', `/api/Accounts/acl`).respond({
+ $httpBackend.when('GET', `Accounts/acl`).respond({
roles: [
{role: {name: 'foo'}},
{role: {name: 'bar'}},
diff --git a/front/core/lib/specs/module-loader.spec.js b/front/core/lib/specs/module-loader.spec.js
index 1bb422c427..2f756f7be8 100644
--- a/front/core/lib/specs/module-loader.spec.js
+++ b/front/core/lib/specs/module-loader.spec.js
@@ -1,32 +1,108 @@
describe('factory vnModuleLoader', () => {
let vnModuleLoader;
+ let $rootScope;
+ let $window;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
- beforeEach(angular.mock.inject((_vnModuleLoader_, $rootScope, $window) => {
+ beforeEach(angular.mock.inject((_vnModuleLoader_, _$rootScope_, $httpBackend, _$window_, $q) => {
vnModuleLoader = _vnModuleLoader_;
- $window.routes = [{module: 'myModule'}];
+ $rootScope = _$rootScope_;
+ $window = _$window_;
+
+ $window.validations = {};
+ $window.routes = [
+ {
+ module: 'myModule',
+ dependencies: ['fooModule', 'barModule']
+ }, {
+ module: 'fooModule',
+ dependencies: ['myModule']
+ }, {
+ module: 'barModule'
+ }
+ ];
+
+ $httpBackend.whenGET('modelInfo')
+ .respond({
+ FooModel: {
+ properties: {
+ id: {type: 'Number'},
+ email: {type: 'String'},
+ field: {type: 'Boolean'}
+ },
+ validations: {
+ id: [{
+ validation: 'presence'
+ }],
+ email: [{
+ validation: 'format',
+ with: '/@/'
+ }],
+ field: [{
+ validation: 'custom',
+ bindedFunction: '() => true'
+ }]
+ }
+ }
+ });
+ $httpBackend.flush();
+
+ vnModuleLoader.moduleImport = () => $q.resolve();
}));
describe('load()', () => {
- it('should return truthy promise if the module was loaded', async() => {
- vnModuleLoader._loaded.myModule = true;
+ it('should throw error if module does not exist', async() => {
+ let errorThrown;
- let result = await vnModuleLoader.load('myModule', {myValidations: () => {}});
+ vnModuleLoader.load('unexistentModule')
+ .catch(() => errorThrown = true);
+ $rootScope.$apply();
- expect(result).toEqual(true);
+ expect(errorThrown).toBeTruthy();
});
- it('should return a promise if the module was still a promise', () => {
- vnModuleLoader._loaded.myModule = new Promise(() => {
- return 'I promise you a module!';
- });
+ it('should set module loaded to true when it is loaded', async() => {
+ vnModuleLoader.load('barModule');
+ $rootScope.$apply();
- let result = vnModuleLoader.load('myModule', {myValidations: () => {}});
+ expect(vnModuleLoader.loaded['barModule']).toBeTruthy();
+ });
- expect(result).toEqual(jasmine.any(Promise));
+ it('should resolve returned promise when module is loaded', async() => {
+ let loaded;
+
+ vnModuleLoader.load('barModule')
+ .then(() => loaded = true);
+ $rootScope.$apply();
+
+ expect(loaded).toBeTruthy();
+ });
+
+ it('should load dependencies', async() => {
+ vnModuleLoader.load('fooModule');
+ $rootScope.$apply();
+
+ expect(vnModuleLoader.loaded['barModule']).toBeTruthy();
+ });
+
+ it('should work with circular dependencies', async() => {
+ vnModuleLoader.load('myModule');
+ $rootScope.$apply();
+
+ expect(vnModuleLoader.loaded['fooModule']).toBeTruthy();
+ });
+
+ it('should load models information and parse validations', async() => {
+ vnModuleLoader.load('barModule');
+
+ let FooModel = $window.validations.FooModel;
+ let validations = FooModel && FooModel.validations;
+
+ expect(FooModel).toBeDefined();
+ expect(validations).toBeDefined();
+ expect(validations.email[0].with).toBeInstanceOf(RegExp);
+ expect(validations.field[0].bindedFunction).toBeInstanceOf(Function);
});
});
});
diff --git a/front/core/lib/specs/validator.spec.js b/front/core/lib/specs/validator.spec.js
index c61faa7937..dd8fc985da 100644
--- a/front/core/lib/specs/validator.spec.js
+++ b/front/core/lib/specs/validator.spec.js
@@ -3,9 +3,7 @@ import {validate} from '../validator.js';
describe('Validator', () => {
let $translate;
- beforeEach(angular.mock.module('vnCore', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('vnCore'));
beforeEach(inject(_$translate_ => {
$translate = _$translate_;
diff --git a/front/core/lib/string.js b/front/core/lib/string.js
index 582485cb1f..50bee98734 100644
--- a/front/core/lib/string.js
+++ b/front/core/lib/string.js
@@ -6,10 +6,19 @@
* @return {String} The camelized string
*/
export function kebabToCamel(str) {
- let camelCased = str.replace(/-([a-z])/g, function(g) {
- return g[1].toUpperCase();
- });
- return camelCased;
+ return str.replace(/-([a-z])/g, g => g[1].toUpperCase());
+}
+
+/**
+ * Transforms a camelCase to kebab-case.
+ *
+ * @param {String} str The camelized string
+ * @return {String} The hyphenized string
+ */
+export function camelToKebab(str) {
+ let kebabCased = str.substr(1)
+ .replace(/[A-Z]/g, g => `-${g[0]}`);
+ return `${str.charAt(0)}${kebabCased}`.toLowerCase();
}
/**
diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml
index 1fcd12b986..1e1e4da4c9 100644
--- a/front/core/locale/es.yml
+++ b/front/core/locale/es.yml
@@ -11,7 +11,6 @@ Hide: Ocultar
Next: Siguiente
Finalize: Finalizar
Previous: Anterior
-Load more: Cargar más
Auto-scroll interrupted, please adjust the search: Auto-scroll interrumpido, por favor ajusta la búsqueda
Value can't be empty: El valor no puede estar vacío
Value should be empty: El valor debe estar vacío
@@ -60,3 +59,7 @@ No records found: No se han encontrado elementos
Day: Día
Days: Días
Go up: Ir arriba
+Loading...: Cargando...
+No results found: Sin resultados
+No data: Sin datos
+Load more: Cargar más
\ No newline at end of file
diff --git a/front/core/module.js b/front/core/module.js
index b9fd9f6a82..33eb68c243 100644
--- a/front/core/module.js
+++ b/front/core/module.js
@@ -1,4 +1,5 @@
import {ng, ngDeps} from './vendor';
+import {camelToKebab} from './lib/string';
const ngModule = ng.module('vnCore', ngDeps);
export default ngModule;
@@ -9,8 +10,8 @@ export default ngModule;
* to the component controller class with the merged component options. To
* retrieve parent options, it reads the same property of the parent class, so
* for the parent options to be copied, it must have been declared using this
- * same function. If any of the options (template, transclude, bindings ...) is
- * redeclared in the child component, it has preference.
+ * same function. If any of the options (template, transclude, bindings...) is
+ * redeclared in the child component, the last has preference.
*
* @param {String} name Coponent name in camelCase
* @param {Object} options The component options
@@ -21,22 +22,40 @@ ngModule.vnComponent = function(name, options) {
let parent = Object.getPrototypeOf(controller);
let parentOptions = parent.$options || {};
+ let parentTransclude = parentOptions.transclude;
+ let transclude = parentTransclude instanceof Object
+ ? Object.assign({}, parentTransclude)
+ : parentTransclude;
+
+ if (options.transclude instanceof Object) {
+ if (transclude instanceof Object)
+ Object.assign(transclude, options.transclude);
+ else
+ transclude = options.transclude;
+ } else if (options.transclude !== undefined)
+ transclude = options.transclude;
+
let mergedOptions = Object.assign({},
parentOptions,
options,
{
- transclude: Object.assign({},
- parentOptions.transclude,
- options.transclude
- ),
+ transclude,
bindings: Object.assign({},
parentOptions.bindings,
options.bindings
+ ),
+ require: Object.assign({},
+ parentOptions.require,
+ options.require
)
}
);
controller.$options = mergedOptions;
+ let classNames = [camelToKebab(name)];
+ if (parent.$classNames) classNames = classNames.concat(parent.$classNames);
+ controller.$classNames = classNames;
+
return this.component(name, mergedOptions);
};
diff --git a/front/core/services/acl-service.js b/front/core/services/acl-service.js
index d286ec85f4..ee4404d348 100644
--- a/front/core/services/acl-service.js
+++ b/front/core/services/acl-service.js
@@ -4,12 +4,14 @@ class AclService {
constructor($http) {
this.$http = $http;
}
+
reset() {
this.user = null;
this.roles = null;
}
+
load() {
- return this.$http.get('/api/Accounts/acl').then(res => {
+ return this.$http.get('Accounts/acl').then(res => {
this.user = res.data.user;
this.roles = {};
@@ -19,6 +21,7 @@ class AclService {
}
});
}
+
hasAny(roles) {
if (this.roles) {
for (let role of roles) {
diff --git a/front/core/services/auth.js b/front/core/services/auth.js
index 44d9f16d95..f59c4c0dd4 100644
--- a/front/core/services/auth.js
+++ b/front/core/services/auth.js
@@ -20,6 +20,7 @@ export default class Auth {
loggedIn: false
});
}
+
initialize() {
let criteria = {
to: state => state.name != 'login'
@@ -42,6 +43,7 @@ export default class Auth {
return redirectToLogin();
});
}
+
login(user, password, remember) {
if (!user)
return this.$q.reject(new UserError('Please enter your username'));
@@ -51,9 +53,10 @@ export default class Auth {
password: password || undefined
};
- return this.$http.post('/api/Accounts/login', params).then(
+ return this.$http.post('Accounts/login', params).then(
json => this.onLoginOk(json, remember));
}
+
onLoginOk(json, remember) {
this.vnToken.set(json.data.token, remember);
@@ -65,8 +68,9 @@ export default class Auth {
this.$state.go('home');
});
}
+
logout() {
- let promise = this.$http.post('/api/Accounts/logout', null, {
+ let promise = this.$http.post('Accounts/logout', null, {
headers: {Authorization: this.vnToken.token}
}).catch(() => {});
@@ -78,6 +82,7 @@ export default class Auth {
return promise;
}
+
loadAcls() {
return this.aclService.load()
.then(() => {
diff --git a/front/core/services/config.js b/front/core/services/config.js
index e2d3035fbc..8fa07e85c8 100644
--- a/front/core/services/config.js
+++ b/front/core/services/config.js
@@ -25,7 +25,7 @@ export default class Config {
for (let param of this.params)
this.local[param] = this.getItem(param);
- return this.$http.get('api/UserConfigs/getUserConfig')
+ return this.$http.get('UserConfigs/getUserConfig')
.then(res => {
for (let param of this.params)
this.user[param] = res.data[param];
@@ -47,7 +47,7 @@ export default class Config {
this.mergeParams();
let params = {[param]: value};
- return this.$http.post('api/UserConfigs/setUserConfig', params)
+ return this.$http.post('UserConfigs/setUserConfig', params)
.then(() => this.showSaved());
}
diff --git a/front/core/services/interceptor.js b/front/core/services/interceptor.js
index 23e065392e..448b70a34a 100644
--- a/front/core/services/interceptor.js
+++ b/front/core/services/interceptor.js
@@ -3,10 +3,17 @@ import HttpError from 'core/lib/http-error';
interceptor.$inject = ['$q', 'vnApp', 'vnToken', '$translate'];
function interceptor($q, vnApp, vnToken, $translate) {
+ let apiPath = 'api/';
+
return {
- request: function(config) {
+ setApiPath(path) {
+ apiPath = path;
+ },
+ request(config) {
vnApp.pushLoader();
+ if (config.url.charAt(0) !== '/' && apiPath)
+ config.url = `${apiPath}/${config.url}`;
if (vnToken.token)
config.headers.Authorization = vnToken.token;
if ($translate.use())
@@ -14,14 +21,14 @@ function interceptor($q, vnApp, vnToken, $translate) {
return config;
},
- requestError: function(rejection) {
+ requestError(rejection) {
return $q.reject(rejection);
},
- response: function(response) {
+ response(response) {
vnApp.popLoader();
return response;
},
- responseError: function(rejection) {
+ responseError(rejection) {
vnApp.popLoader();
let err = new HttpError(rejection.statusText);
Object.assign(err, rejection);
diff --git a/front/core/services/modules.js b/front/core/services/modules.js
index 2c1862fa80..1021bc4fab 100644
--- a/front/core/services/modules.js
+++ b/front/core/services/modules.js
@@ -8,9 +8,11 @@ export default class Modules {
$window
});
}
+
reset() {
this.modules = null;
}
+
get() {
if (this.modules)
return this.modules;
diff --git a/front/core/styles/effects.scss b/front/core/styles/effects.scss
index 92ae84c03d..205a23bd25 100644
--- a/front/core/styles/effects.scss
+++ b/front/core/styles/effects.scss
@@ -4,8 +4,10 @@
cursor: pointer;
transition: background-color 250ms ease-out;
- &:hover {
+ &:hover,
+ &:focus {
background-color: $color-hover-cd;
+ outline: none;
}
}
@@ -13,8 +15,10 @@
cursor: pointer;
transition: opacity 250ms ease-out;
- &:hover {
+ &:hover,
+ &:focus {
opacity: $color-hover-dc;
+ outline: none;
}
}
diff --git a/front/salix/components/home/home.html b/front/salix/components/home/home.html
index 95bec4e5ae..47307d22ed 100644
--- a/front/salix/components/home/home.html
+++ b/front/salix/components/home/home.html
@@ -1,21 +1,20 @@
diff --git a/front/salix/components/home/home.js b/front/salix/components/home/home.js
index 8b71f1f394..367880b311 100644
--- a/front/salix/components/home/home.js
+++ b/front/salix/components/home/home.js
@@ -1,16 +1,17 @@
import ngModule from '../../module';
+import Component from 'core/lib/component';
import './style.scss';
-export default class Controller {
- constructor(vnModules, $state, $translate, $sce) {
+export default class Controller extends Component {
+ constructor($element, $, vnModules, $sce) {
+ super($element, $);
this.modules = vnModules.get();
- this.$state = $state;
- this._ = $translate;
this.$sce = $sce;
}
+
getModuleName(mod) {
let getName = mod => {
- let name = this._.instant(mod.name);
+ let name = this.$t(mod.name);
let upper = name.toUpperCase();
if (!mod.keyBind) return name;
let index = upper.indexOf(mod.keyBind);
@@ -25,8 +26,7 @@ export default class Controller {
return this.$sce.trustAsHtml(getName(mod));
}
}
-
-Controller.$inject = ['vnModules', '$state', '$translate', '$sce'];
+Controller.$inject = ['$element', '$scope', 'vnModules', '$sce'];
ngModule.component('vnHome', {
template: require('./home.html'),
diff --git a/front/salix/components/left-menu/left-menu.spec.js b/front/salix/components/left-menu/left-menu.spec.js
index 76a6d10f8d..12846d2e3c 100644
--- a/front/salix/components/left-menu/left-menu.spec.js
+++ b/front/salix/components/left-menu/left-menu.spec.js
@@ -4,9 +4,7 @@ describe('Component vnLeftMenu', () => {
let controller;
let $element;
- beforeEach(angular.mock.module('salix', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('salix'));
beforeEach(angular.mock.inject(($componentController, $state, $window) => {
$element = angular.element('
');
diff --git a/front/salix/components/login/style.scss b/front/salix/components/login/style.scss
index ea894d62e8..e098fbdb4e 100644
--- a/front/salix/components/login/style.scss
+++ b/front/salix/components/login/style.scss
@@ -30,10 +30,10 @@ vn-login {
padding-bottom: 1em;
}
& > form {
- & > vn-textfield {
+ & > .vn-textfield {
width: 100%;
}
- & > vn-check {
+ & > .vn-check {
display: block;
.md-label {
white-space: inherit;
diff --git a/front/salix/components/main-menu/main-menu.js b/front/salix/components/main-menu/main-menu.js
index 9c2888c1b2..93ff955e78 100644
--- a/front/salix/components/main-menu/main-menu.js
+++ b/front/salix/components/main-menu/main-menu.js
@@ -16,7 +16,7 @@ export default class MainMenu {
}
getCurrentUserName() {
- this.$http.get('/api/Accounts/getCurrentUserData').then(json => {
+ this.$http.get('Accounts/getCurrentUserData').then(json => {
this.$.$root.user = json.data;
window.localStorage.currentUserWorkerId = json.data.workerId;
});
diff --git a/front/salix/components/main-menu/main-menu.spec.js b/front/salix/components/main-menu/main-menu.spec.js
index b695e27956..693bf8291a 100644
--- a/front/salix/components/main-menu/main-menu.spec.js
+++ b/front/salix/components/main-menu/main-menu.spec.js
@@ -4,9 +4,7 @@ describe('Component vnMainMenu', () => {
let $httpBackend;
let controller;
- beforeEach(angular.mock.module('salix', $translateProvider => {
- $translateProvider.translations('en', {});
- }));
+ beforeEach(ngModule('salix'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
let vnModules = {get: () => {}};
@@ -16,8 +14,8 @@ describe('Component vnMainMenu', () => {
describe('getCurrentUserName()', () => {
it(`should set the user name property in the controller`, () => {
- $httpBackend.when('GET', `/api/Accounts/getCurrentUserData`).respond({name: 'batman'});
- $httpBackend.expect('GET', `/api/Accounts/getCurrentUserData`);
+ $httpBackend.when('GET', `Accounts/getCurrentUserData`).respond({name: 'batman'});
+ $httpBackend.expect('GET', `Accounts/getCurrentUserData`);
controller.getCurrentUserName();
$httpBackend.flush();
diff --git a/front/salix/components/summary/style.scss b/front/salix/components/summary/style.scss
index fbab066f83..58a613088b 100644
--- a/front/salix/components/summary/style.scss
+++ b/front/salix/components/summary/style.scss
@@ -56,24 +56,7 @@
}
}
-.vn-dialog.dialog-summary {
- vn-card {
- border: none;
- box-shadow: none;
- }
- & > div > button.close {
- display: none;
- }
- & > div {
- padding: 0
- }
- tpl-body {
- width: auto;
- }
- .buttons {
- display: none;
- }
- vn-check label span {
- font-size: .9em
- }
+.vn-popup .summary {
+ border: none;
+ box-shadow: none;
}
diff --git a/front/salix/components/user-popover/index.html b/front/salix/components/user-popover/index.html
index 9f4f931bd9..1e17313f13 100644
--- a/front/salix/components/user-popover/index.html
+++ b/front/salix/components/user-popover/index.html
@@ -1,11 +1,11 @@
@@ -20,7 +20,7 @@
{{$root.user.nickname}}
- {{$root.user.name}}
+ {{::$root.user.name}}