merge
gitea/salix/dev This commit looks good
Details
gitea/salix/dev This commit looks good
Details
This commit is contained in:
commit
e267bf93a4
|
@ -94,7 +94,7 @@ module.exports = Self => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const storageConnector = Self.app.dataSources.storage.connector;
|
const storageConnector = Self.app.dataSources.storage.connector;
|
||||||
const myUserId = ctx.req.accessToken.userId;
|
const myUserId = ctx.req.accessToken.userId;
|
||||||
const myWorker = await models.Worker.findOne({where: {userFk: myUserId}});
|
const myWorker = await models.Worker.findOne({where: {userFk: myUserId}}, myOptions);
|
||||||
const args = ctx.args;
|
const args = ctx.args;
|
||||||
|
|
||||||
const newDms = await Self.create({
|
const newDms = await Self.create({
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
ALTER TABLE `vn2008`.`department`
|
||||||
|
ADD COLUMN `parentFk` INT UNSIGNED NULL AFTER `sons`,
|
||||||
|
ADD COLUMN `path` VARCHAR(255) NULL AFTER `parentFk`,
|
||||||
|
CHANGE COLUMN `sons` `sons` DECIMAL(10,0) NOT NULL DEFAULT '0' ;
|
||||||
|
|
||||||
|
USE `vn`;
|
||||||
|
CREATE
|
||||||
|
OR REPLACE ALGORITHM = UNDEFINED
|
||||||
|
DEFINER = `root`@`%`
|
||||||
|
SQL SECURITY DEFINER
|
||||||
|
VIEW `department` AS
|
||||||
|
SELECT
|
||||||
|
`b`.`department_id` AS `id`,
|
||||||
|
`b`.`name` AS `name`,
|
||||||
|
`b`.`production` AS `isProduction`,
|
||||||
|
`b`.`parentFk` AS `parentFk`,
|
||||||
|
`b`.`path` AS `path`,
|
||||||
|
`b`.`lft` AS `lft`,
|
||||||
|
`b`.`rgt` AS `rgt`,
|
||||||
|
`b`.`isSelected` AS `isSelected`,
|
||||||
|
`b`.`depth` AS `depth`,
|
||||||
|
`b`.`sons` AS `sons`
|
||||||
|
FROM
|
||||||
|
`vn2008`.`department` `b`;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `vn2008`.`department_AFTER_DELETE`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn2008`$$
|
||||||
|
CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_AFTER_DELETE`
|
||||||
|
AFTER DELETE ON `department` FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE vn.department_recalc SET isChanged = TRUE;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `vn2008`.`department_BEFORE_INSERT`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn2008`$$
|
||||||
|
CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_BEFORE_INSERT`
|
||||||
|
BEFORE INSERT ON `department` FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE vn.department_recalc SET isChanged = TRUE;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `vn2008`.`department_AFTER_UPDATE`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn2008`$$
|
||||||
|
CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_AFTER_UPDATE`
|
||||||
|
AFTER UPDATE ON `department` FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
IF !(OLD.parentFk <=> NEW.parentFk) THEN
|
||||||
|
UPDATE vn.department_recalc SET isChanged = TRUE;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`department_recalc` (
|
||||||
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`isChanged` TINYINT(4) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`));
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`department_recalc` (`id`, `isChanged`) VALUES ('1', '0');
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE `vn2008`.`department`
|
||||||
|
CHANGE COLUMN `lft` `lft` INT(11) NULL ,
|
||||||
|
CHANGE COLUMN `rgt` `rgt` INT(11) NULL ;
|
||||||
|
|
||||||
|
ALTER TABLE `vn2008`.`department`
|
||||||
|
DROP INDEX `rgt_UNIQUE` ,
|
||||||
|
DROP INDEX `lft_UNIQUE` ;
|
||||||
|
;
|
||||||
|
|
||||||
|
ALTER TABLE `vn2008`.`department`
|
||||||
|
ADD INDEX `lft_rgt_depth_idx` (`lft` ASC, `rgt` ASC, `depth` ASC);
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE vn.department SET lft = NULL, rgt = NULL;
|
|
@ -0,0 +1,37 @@
|
||||||
|
USE `vn`;
|
||||||
|
DROP procedure IF EXISTS `department_calcTree`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn`$$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `department_calcTree`()
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Calculates the #path, #lft, #rgt, #sons and #depth columns of
|
||||||
|
* the #department table. To build the tree, it uses the #parentFk
|
||||||
|
* column.
|
||||||
|
*/
|
||||||
|
DECLARE vIndex INT DEFAULT 0;
|
||||||
|
DECLARE vSons INT;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tNestedTree;
|
||||||
|
CREATE TEMPORARY TABLE tNestedTree
|
||||||
|
SELECT id, path, lft, rgt, depth, sons
|
||||||
|
FROM department LIMIT 0;
|
||||||
|
|
||||||
|
SET max_sp_recursion_depth = 5;
|
||||||
|
CALL department_calcTreeRec(NULL, '/', 0, vIndex, vSons);
|
||||||
|
SET max_sp_recursion_depth = 0;
|
||||||
|
|
||||||
|
UPDATE department z
|
||||||
|
JOIN tNestedTree t ON t.id = z.id
|
||||||
|
SET z.path = t.path,
|
||||||
|
z.lft = t.lft,
|
||||||
|
z.rgt = t.rgt,
|
||||||
|
z.depth = t.depth,
|
||||||
|
z.sons = t.sons;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tNestedTree;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
USE `vn`;
|
||||||
|
DROP procedure IF EXISTS `department_calcTreeRec`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn`$$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `department_calcTreeRec`(
|
||||||
|
vSelf INT,
|
||||||
|
vPath VARCHAR(255),
|
||||||
|
vDepth INT,
|
||||||
|
INOUT vIndex INT,
|
||||||
|
OUT vSons INT
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Calculates and sets the #path, #lft, #rgt, #sons and #depth
|
||||||
|
* columns for all children of the passed node. Once calculated
|
||||||
|
* the last node rgt index and the number of sons are returned.
|
||||||
|
* To update it's children, this procedure calls itself recursively
|
||||||
|
* for each one.
|
||||||
|
*
|
||||||
|
* @vSelf The node identifier
|
||||||
|
* @vPath The initial path
|
||||||
|
* @vDepth The initial depth
|
||||||
|
* @vIndex The initial lft index
|
||||||
|
* @vSons The number of direct sons
|
||||||
|
*/
|
||||||
|
DECLARE vChildFk INT;
|
||||||
|
DECLARE vLft INT;
|
||||||
|
DECLARE vMySons INT;
|
||||||
|
DECLARE vDone BOOL;
|
||||||
|
DECLARE vChildren CURSOR FOR
|
||||||
|
SELECT id FROM department
|
||||||
|
WHERE (vSelf IS NULL AND parentFk IS NULL)
|
||||||
|
OR (vSelf IS NOT NULL AND parentFk = vSelf);
|
||||||
|
|
||||||
|
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||||
|
|
||||||
|
SET vSons = 0;
|
||||||
|
|
||||||
|
OPEN vChildren;
|
||||||
|
myLoop: LOOP
|
||||||
|
SET vDone = FALSE;
|
||||||
|
FETCH vChildren INTO vChildFk;
|
||||||
|
|
||||||
|
IF vDone THEN
|
||||||
|
LEAVE myLoop;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SET vIndex = vIndex + 1;
|
||||||
|
SET vLft = vIndex;
|
||||||
|
SET vSons = vSons + 1;
|
||||||
|
|
||||||
|
CALL department_calcTreeRec(
|
||||||
|
vChildFk,
|
||||||
|
CONCAT(vPath, vChildFk, '/'),
|
||||||
|
vDepth + 1,
|
||||||
|
vIndex,
|
||||||
|
vMySons
|
||||||
|
);
|
||||||
|
|
||||||
|
SET vIndex = vIndex + 1;
|
||||||
|
|
||||||
|
INSERT INTO tNestedTree
|
||||||
|
SET id = vChildFk,
|
||||||
|
path = vPath,
|
||||||
|
lft = vLft,
|
||||||
|
rgt = vIndex,
|
||||||
|
depth = vDepth,
|
||||||
|
sons = vMySons;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE vChildren;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
USE `vn`;
|
||||||
|
DROP procedure IF EXISTS `department_doCalc`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn`$$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `department_doCalc`()
|
||||||
|
proc: BEGIN
|
||||||
|
/**
|
||||||
|
* Recalculates the department tree.
|
||||||
|
*/
|
||||||
|
DECLARE vIsChanged BOOL;
|
||||||
|
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
|
||||||
|
BEGIN
|
||||||
|
DO RELEASE_LOCK('vn.department_doCalc');
|
||||||
|
RESIGNAL;
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF !GET_LOCK('vn.department_doCalc', 0) THEN
|
||||||
|
LEAVE proc;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT isChanged INTO vIsChanged
|
||||||
|
FROM department_recalc;
|
||||||
|
|
||||||
|
IF vIsChanged THEN
|
||||||
|
UPDATE department_recalc SET isChanged = FALSE;
|
||||||
|
CALL vn.department_calcTree;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DO RELEASE_LOCK('vn.department_doCalc');
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
USE `vn`;
|
||||||
|
DROP procedure IF EXISTS `department_getLeaves`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn`$$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `department_getLeaves`(
|
||||||
|
vParentFk INT,
|
||||||
|
vSearch VARCHAR(255)
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
DECLARE vIsNumber BOOL;
|
||||||
|
DECLARE vIsSearch BOOL DEFAULT vSearch IS NOT NULL AND vSearch != '';
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tNodes;
|
||||||
|
CREATE TEMPORARY TABLE tNodes
|
||||||
|
(UNIQUE (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id FROM department LIMIT 0;
|
||||||
|
|
||||||
|
IF vIsSearch THEN
|
||||||
|
SET vIsNumber = vSearch REGEXP '^[0-9]+$';
|
||||||
|
|
||||||
|
INSERT INTO tNodes
|
||||||
|
SELECT id FROM department
|
||||||
|
WHERE (vIsNumber AND `name` = vSearch)
|
||||||
|
OR (!vIsNumber AND `name` LIKE CONCAT('%', vSearch, '%'))
|
||||||
|
LIMIT 1000;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF vParentFk IS NULL THEN
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tChilds;
|
||||||
|
CREATE TEMPORARY TABLE tChilds
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id FROM tNodes;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tParents;
|
||||||
|
CREATE TEMPORARY TABLE tParents
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id FROM department LIMIT 0;
|
||||||
|
|
||||||
|
myLoop: LOOP
|
||||||
|
DELETE FROM tParents;
|
||||||
|
INSERT INTO tParents
|
||||||
|
SELECT parentFk id
|
||||||
|
FROM department g
|
||||||
|
JOIN tChilds c ON c.id = g.id
|
||||||
|
WHERE g.parentFk IS NOT NULL;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO tNodes
|
||||||
|
SELECT id FROM tParents;
|
||||||
|
|
||||||
|
IF ROW_COUNT() = 0 THEN
|
||||||
|
LEAVE myLoop;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DELETE FROM tChilds;
|
||||||
|
INSERT INTO tChilds
|
||||||
|
SELECT id FROM tParents;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE
|
||||||
|
tChilds,
|
||||||
|
tParents;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF !vIsSearch THEN
|
||||||
|
INSERT IGNORE INTO tNodes
|
||||||
|
SELECT id FROM department
|
||||||
|
WHERE parentFk <=> vParentFk;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT d.id,
|
||||||
|
d.`name`,
|
||||||
|
d.parentFk,
|
||||||
|
d.sons
|
||||||
|
FROM department d
|
||||||
|
JOIN tNodes n ON n.id = d.id
|
||||||
|
ORDER BY depth, `name`;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tNodes;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
|
@ -471,7 +471,7 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF
|
||||||
(14, 1, 2, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()),
|
(14, 1, 2, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()),
|
||||||
(15, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 105, 'Plastic Cell', 125, NULL, 0, 3, CURDATE()),
|
(15, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 105, 'Plastic Cell', 125, NULL, 0, 3, CURDATE()),
|
||||||
(16, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
|
(16, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
|
||||||
(17, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
|
(17, 1, 7, 2, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
|
||||||
(18, 1, 4, 4, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 108, 'Cerebro', 128, NULL, 0, 12, CURDATE()),
|
(18, 1, 4, 4, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 108, 'Cerebro', 128, NULL, 0, 12, CURDATE()),
|
||||||
(19, 1, 5, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 1, 13, CURDATE()),
|
(19, 1, 5, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 1, 13, CURDATE()),
|
||||||
(20, 1, 5, 5, 3, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
|
(20, 1, 5, 5, 3, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
vnTextfield: 'vn-textfield > div > div > div > input',
|
vnTextfield: 'vn-textfield input',
|
||||||
vnInputNumber: 'vn-input-number > div > div > div > input',
|
vnInputNumber: 'vn-input-number input',
|
||||||
vnSubmit: 'vn-submit > input',
|
vnSubmit: 'vn-submit > input',
|
||||||
vnFloatButton: 'vn-float-button > button'
|
vnFloatButton: 'vn-float-button > button'
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,8 +18,11 @@ let actions = {
|
||||||
|
|
||||||
clearInput: function(selector, done) {
|
clearInput: function(selector, done) {
|
||||||
this.wait(selector)
|
this.wait(selector)
|
||||||
.evaluate(inputSelector => {
|
.evaluate(selector => {
|
||||||
return document.querySelector(inputSelector).closest('*[model], *[field], *[value]').$ctrl.value = '';
|
let $ctrl = document.querySelector(selector).closest('.vn-field').$ctrl;
|
||||||
|
$ctrl.field = null;
|
||||||
|
$ctrl.$.$apply();
|
||||||
|
$ctrl.input.dispatchEvent(new Event('change'));
|
||||||
}, selector)
|
}, selector)
|
||||||
.then(done)
|
.then(done)
|
||||||
.catch(done);
|
.catch(done);
|
||||||
|
@ -31,6 +34,7 @@ let actions = {
|
||||||
|
|
||||||
let doLogin = () => {
|
let doLogin = () => {
|
||||||
this.wait(`vn-login input[name=user]`)
|
this.wait(`vn-login input[name=user]`)
|
||||||
|
.clearInput(`vn-login input[name=user]`)
|
||||||
.write(`vn-login input[name=user]`, userName)
|
.write(`vn-login input[name=user]`, userName)
|
||||||
.write(`vn-login input[name=password]`, 'nightmare')
|
.write(`vn-login input[name=password]`, 'nightmare')
|
||||||
.click(`vn-login input[type=submit]`)
|
.click(`vn-login input[type=submit]`)
|
||||||
|
@ -75,7 +79,7 @@ let actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
changeLanguageToEnglish: function(done) {
|
changeLanguageToEnglish: function(done) {
|
||||||
let langSelector = '.user-configuration vn-autocomplete[field="$ctrl.lang"]';
|
let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]';
|
||||||
|
|
||||||
this.waitToClick('#user')
|
this.waitToClick('#user')
|
||||||
.wait(langSelector)
|
.wait(langSelector)
|
||||||
|
@ -167,8 +171,8 @@ let actions = {
|
||||||
|
|
||||||
focusElement: function(selector, done) {
|
focusElement: function(selector, done) {
|
||||||
this.wait(selector)
|
this.wait(selector)
|
||||||
.evaluate_now(elemenetSelector => {
|
.evaluate_now(selector => {
|
||||||
let element = document.querySelector(elemenetSelector);
|
let element = document.querySelector(selector);
|
||||||
element.focus();
|
element.focus();
|
||||||
}, done, selector)
|
}, done, selector)
|
||||||
.then(done)
|
.then(done)
|
||||||
|
@ -401,8 +405,7 @@ let actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
autocompleteSearch: function(autocompleteSelector, searchValue, done) {
|
autocompleteSearch: function(autocompleteSelector, searchValue, done) {
|
||||||
this.wait(`${autocompleteSelector} input`)
|
this.waitToClick(`${autocompleteSelector} input`)
|
||||||
.waitToClick(`${autocompleteSelector} input`)
|
|
||||||
.write(`.vn-popover.shown .vn-drop-down input`, searchValue)
|
.write(`.vn-popover.shown .vn-drop-down input`, searchValue)
|
||||||
.waitToClick(`.vn-popover.shown .vn-drop-down li.active`)
|
.waitToClick(`.vn-popover.shown .vn-drop-down li.active`)
|
||||||
.wait((autocompleteSelector, searchValue) => {
|
.wait((autocompleteSelector, searchValue) => {
|
||||||
|
@ -412,7 +415,7 @@ let actions = {
|
||||||
}, autocompleteSelector, searchValue)
|
}, autocompleteSelector, searchValue)
|
||||||
.then(done)
|
.then(done)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
done(new Error(`.autocompleteSearch() for ${autocompleteSelector}, timed out`));
|
done(new Error(`.autocompleteSearch() for value ${searchValue} in ${autocompleteSelector} timed out`));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -427,25 +430,22 @@ let actions = {
|
||||||
.catch(done);
|
.catch(done);
|
||||||
},
|
},
|
||||||
|
|
||||||
datePicker: function(datePickerSelector, changeMonth, day, done) {
|
datePicker: function(selector, changeMonth, day, done) {
|
||||||
this.wait(datePickerSelector)
|
this.wait(selector)
|
||||||
.mousedown(datePickerSelector)
|
.mousedown(`${selector} input`)
|
||||||
.wait('div.flatpickr-calendar.open');
|
.wait('.flatpickr-calendar.open');
|
||||||
|
|
||||||
if (changeMonth > 0)
|
if (changeMonth > 0)
|
||||||
this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-next-month > svg');
|
this.mousedown(`.flatpickr-calendar.open .flatpickr-next-month`);
|
||||||
|
|
||||||
|
|
||||||
if (changeMonth < 0)
|
if (changeMonth < 0)
|
||||||
this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-prev-month > svg');
|
this.mousedown(`.flatpickr-calendar.open .flatpickr-prev-month`);
|
||||||
|
|
||||||
let daySelector;
|
let daySelector;
|
||||||
|
|
||||||
if (!day)
|
if (!day)
|
||||||
daySelector = 'div.flatpickr-calendar.open span.flatpickr-day:nth-child(16)';
|
daySelector = `.flatpickr-calendar.open .flatpickr-day:nth-child(16)`;
|
||||||
|
|
||||||
if (day)
|
if (day)
|
||||||
daySelector = `span.flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`;
|
daySelector = `.flatpickr-calendar.open .flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`;
|
||||||
|
|
||||||
|
|
||||||
this.wait(selector => {
|
this.wait(selector => {
|
||||||
return document.querySelector(selector);
|
return document.querySelector(selector);
|
||||||
|
|
|
@ -11,14 +11,14 @@ export default {
|
||||||
claimsButton: '.modules-menu > li[ui-sref="claim.index"]',
|
claimsButton: '.modules-menu > li[ui-sref="claim.index"]',
|
||||||
returnToModuleIndexButton: 'a[ui-sref="order.index"]',
|
returnToModuleIndexButton: 'a[ui-sref="order.index"]',
|
||||||
userMenuButton: 'vn-topbar #user',
|
userMenuButton: 'vn-topbar #user',
|
||||||
userLocalWarehouse: '.user-configuration vn-autocomplete[field="$ctrl.localWarehouseFk"]',
|
userLocalWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.localWarehouseFk"]',
|
||||||
userLocalBank: '.user-configuration vn-autocomplete[field="$ctrl.localBankFk"]',
|
userLocalBank: '.user-popover vn-autocomplete[ng-model="$ctrl.localBankFk"]',
|
||||||
userLocalCompany: '.user-configuration vn-autocomplete[field="$ctrl.localCompanyFk"]',
|
userLocalCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.localCompanyFk"]',
|
||||||
userWarehouse: '.user-configuration vn-autocomplete[field="$ctrl.warehouseFk"]',
|
userWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
|
||||||
userCompany: '.user-configuration vn-autocomplete[field="$ctrl.companyFk"]',
|
userCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.companyFk"]',
|
||||||
userConfigFirstAutocompleteClear: '#localWarehouse > div > div > div > vn-icon.clear',
|
userConfigFirstAutocompleteClear: '#localWarehouse .icons > vn-icon[icon=clear]',
|
||||||
userConfigSecondAutocompleteClear: '#localBank > div > div > div > vn-icon.clear',
|
userConfigSecondAutocompleteClear: '#localBank .icons > vn-icon[icon=clear]',
|
||||||
userConfigThirdAutocompleteClear: '#localCompany > div > div > div > vn-icon.clear',
|
userConfigThirdAutocompleteClear: '#localCompany .icons > vn-icon[icon=clear]',
|
||||||
acceptButton: 'vn-confirm button[response=ACCEPT]'
|
acceptButton: 'vn-confirm button[response=ACCEPT]'
|
||||||
},
|
},
|
||||||
clientsIndex: {
|
clientsIndex: {
|
||||||
|
@ -35,11 +35,11 @@ export default {
|
||||||
street: `${components.vnTextfield}[name="street"]`,
|
street: `${components.vnTextfield}[name="street"]`,
|
||||||
postcode: `${components.vnTextfield}[name="postcode"]`,
|
postcode: `${components.vnTextfield}[name="postcode"]`,
|
||||||
city: `${components.vnTextfield}[name="city"]`,
|
city: `${components.vnTextfield}[name="city"]`,
|
||||||
province: `vn-autocomplete[field="$ctrl.client.provinceFk"]`,
|
province: `vn-autocomplete[ng-model="$ctrl.client.provinceFk"]`,
|
||||||
country: `vn-autocomplete[field="$ctrl.client.countryFk"]`,
|
country: `vn-autocomplete[ng-model="$ctrl.client.countryFk"]`,
|
||||||
userName: `${components.vnTextfield}[name="userName"]`,
|
userName: `${components.vnTextfield}[name="userName"]`,
|
||||||
email: `${components.vnTextfield}[name="email"]`,
|
email: `${components.vnTextfield}[name="email"]`,
|
||||||
salesPersonAutocomplete: `vn-autocomplete[field="$ctrl.client.salesPersonFk"]`,
|
salesPersonAutocomplete: `vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]`,
|
||||||
createButton: `${components.vnSubmit}`,
|
createButton: `${components.vnSubmit}`,
|
||||||
cancelButton: 'vn-button[href="#!/client/index"]'
|
cancelButton: 'vn-button[href="#!/client/index"]'
|
||||||
},
|
},
|
||||||
|
@ -49,13 +49,13 @@ export default {
|
||||||
},
|
},
|
||||||
clientBasicData: {
|
clientBasicData: {
|
||||||
basicDataButton: 'vn-left-menu a[ui-sref="client.card.basicData"]',
|
basicDataButton: 'vn-left-menu a[ui-sref="client.card.basicData"]',
|
||||||
nameInput: 'vn-textfield[field="$ctrl.client.name"] input',
|
nameInput: 'vn-textfield[ng-model="$ctrl.client.name"] input',
|
||||||
contactInput: 'vn-textfield[field="$ctrl.client.contact"] input',
|
contactInput: 'vn-textfield[ng-model="$ctrl.client.contact"] input',
|
||||||
phoneInput: 'vn-textfield[field="$ctrl.client.phone"] input',
|
phoneInput: 'vn-textfield[ng-model="$ctrl.client.phone"] input',
|
||||||
mobileInput: 'vn-textfield[field="$ctrl.client.mobile"] input',
|
mobileInput: 'vn-textfield[ng-model="$ctrl.client.mobile"] input',
|
||||||
emailInput: 'vn-textfield[field="$ctrl.client.email"] input',
|
emailInput: 'vn-textfield[ng-model="$ctrl.client.email"] input',
|
||||||
salesPersonAutocomplete: 'vn-autocomplete[field="$ctrl.client.salesPersonFk"]',
|
salesPersonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
|
||||||
channelAutocomplete: 'vn-autocomplete[field="$ctrl.client.contactChannelFk"]',
|
channelAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.contactChannelFk"]',
|
||||||
saveButton: `${components.vnSubmit}`
|
saveButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
clientFiscalData: {
|
clientFiscalData: {
|
||||||
|
@ -67,8 +67,8 @@ export default {
|
||||||
addressInput: `${components.vnTextfield}[name="street"]`,
|
addressInput: `${components.vnTextfield}[name="street"]`,
|
||||||
postcodeInput: `${components.vnTextfield}[name="postcode"]`,
|
postcodeInput: `${components.vnTextfield}[name="postcode"]`,
|
||||||
cityInput: `${components.vnTextfield}[name="city"]`,
|
cityInput: `${components.vnTextfield}[name="city"]`,
|
||||||
provinceAutocomplete: 'vn-autocomplete[field="$ctrl.client.provinceFk"]',
|
provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.provinceFk"]',
|
||||||
countryAutocomplete: 'vn-autocomplete[field="$ctrl.client.countryFk"]',
|
countryAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
|
||||||
activeCheckbox: 'vn-check[label="Active"]',
|
activeCheckbox: 'vn-check[label="Active"]',
|
||||||
frozenCheckbox: 'vn-check[label="Frozen"]',
|
frozenCheckbox: 'vn-check[label="Frozen"]',
|
||||||
invoiceByAddressCheckbox: 'vn-check[label="Invoice by address"]',
|
invoiceByAddressCheckbox: 'vn-check[label="Invoice by address"]',
|
||||||
|
@ -79,14 +79,14 @@ export default {
|
||||||
saveButton: `${components.vnSubmit}`
|
saveButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
clientBillingData: {
|
clientBillingData: {
|
||||||
payMethodAutocomplete: 'vn-autocomplete[field="$ctrl.client.payMethodFk"]',
|
payMethodAutocomplete: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.payMethodFk"]',
|
||||||
IBANInput: `${components.vnTextfield}[name="iban"]`,
|
IBANInput: `vn-client-billing-data ${components.vnTextfield}[name="iban"]`,
|
||||||
dueDayInput: `${components.vnInputNumber}[name="dueDay"]`,
|
dueDayInput: `vn-client-billing-data ${components.vnInputNumber}[name="dueDay"]`,
|
||||||
receivedCoreLCRCheckbox: 'vn-check[label="Received LCR"]',
|
receivedCoreLCRCheckbox: 'vn-client-billing-data vn-check[label="Received LCR"]',
|
||||||
receivedCoreVNLCheckbox: 'vn-check[label="Received core VNL"]',
|
receivedCoreVNLCheckbox: 'vn-client-billing-data vn-check[label="Received core VNL"]',
|
||||||
receivedB2BVNLCheckbox: 'vn-check[label="Received B2B VNL"]',
|
receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]',
|
||||||
swiftBicAutocomplete: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"]',
|
swiftBicAutocomplete: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"]',
|
||||||
clearswiftBicButton: 'vn-client-billing-data vn-autocomplete[field="$ctrl.client.bankEntityFk"] > div > div > div > vn-icon > i',
|
clearswiftBicButton: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"] .icons > vn-icon[icon=clear]',
|
||||||
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
|
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
|
||||||
newBankEntityName: 'vn-client-billing-data > vn-dialog vn-textfield[label="Name"] input',
|
newBankEntityName: 'vn-client-billing-data > vn-dialog vn-textfield[label="Name"] input',
|
||||||
newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input',
|
newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input',
|
||||||
|
@ -102,8 +102,8 @@ export default {
|
||||||
streetAddressInput: `${components.vnTextfield}[name="street"]`,
|
streetAddressInput: `${components.vnTextfield}[name="street"]`,
|
||||||
postcodeInput: `${components.vnTextfield}[name="postalCode"]`,
|
postcodeInput: `${components.vnTextfield}[name="postalCode"]`,
|
||||||
cityInput: `${components.vnTextfield}[name="city"]`,
|
cityInput: `${components.vnTextfield}[name="city"]`,
|
||||||
provinceAutocomplete: 'vn-autocomplete[field="$ctrl.address.provinceFk"]',
|
provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.address.provinceFk"]',
|
||||||
agencyAutocomplete: 'vn-autocomplete[field="$ctrl.address.agencyModeFk"]',
|
agencyAutocomplete: 'vn-autocomplete[ng-model="$ctrl.address.agencyModeFk"]',
|
||||||
phoneInput: `${components.vnTextfield}[name="phone"]`,
|
phoneInput: `${components.vnTextfield}[name="phone"]`,
|
||||||
mobileInput: `${components.vnTextfield}[name="mobile"]`,
|
mobileInput: `${components.vnTextfield}[name="mobile"]`,
|
||||||
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
|
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
|
||||||
|
@ -112,10 +112,10 @@ export default {
|
||||||
secondEditAddress: 'vn-client-address-index div:nth-child(2) > a',
|
secondEditAddress: 'vn-client-address-index div:nth-child(2) > a',
|
||||||
activeCheckbox: 'vn-check[label="Enabled"]',
|
activeCheckbox: 'vn-check[label="Enabled"]',
|
||||||
equalizationTaxCheckbox: 'vn-client-address-edit vn-check[label="Is equalizated"]',
|
equalizationTaxCheckbox: 'vn-client-address-edit vn-check[label="Is equalizated"]',
|
||||||
firstObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(1) [field="observation.observationTypeFk"]',
|
firstObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(1) [ng-model="observation.observationTypeFk"]',
|
||||||
firstObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(1) [model="observation.description"] input',
|
firstObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(1) [ng-model="observation.description"] input',
|
||||||
secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [field="observation.observationTypeFk"]',
|
secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [ng-model="observation.observationTypeFk"]',
|
||||||
secondObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(2) [model="observation.description"] input',
|
secondObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(2) [ng-model="observation.description"] input',
|
||||||
addObservationButton: 'vn-client-address-edit div[name="observations"] vn-icon-button[icon="add_circle"]',
|
addObservationButton: 'vn-client-address-edit div[name="observations"] vn-icon-button[icon="add_circle"]',
|
||||||
saveButton: `${components.vnSubmit}`,
|
saveButton: `${components.vnSubmit}`,
|
||||||
cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]',
|
cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]',
|
||||||
|
@ -143,7 +143,7 @@ export default {
|
||||||
addGreugeFloatButton: `${components.vnFloatButton}`,
|
addGreugeFloatButton: `${components.vnFloatButton}`,
|
||||||
amountInput: `${components.vnInputNumber}[name="amount"]`,
|
amountInput: `${components.vnInputNumber}[name="amount"]`,
|
||||||
descriptionInput: `${components.vnTextfield}[name="description"]`,
|
descriptionInput: `${components.vnTextfield}[name="description"]`,
|
||||||
typeAutocomplete: 'vn-autocomplete[field="$ctrl.greuge.greugeTypeFk"]',
|
typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.greuge.greugeTypeFk"]',
|
||||||
saveButton: `${components.vnSubmit}`,
|
saveButton: `${components.vnSubmit}`,
|
||||||
firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr'
|
firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr'
|
||||||
},
|
},
|
||||||
|
@ -162,10 +162,10 @@ export default {
|
||||||
},
|
},
|
||||||
clientBalance: {
|
clientBalance: {
|
||||||
balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
|
balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
|
||||||
companyAutocomplete: 'vn-client-balance-index vn-autocomplete[field="$ctrl.companyFk"]',
|
companyAutocomplete: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyFk"]',
|
||||||
newPaymentButton: `${components.vnFloatButton}`,
|
newPaymentButton: `${components.vnFloatButton}`,
|
||||||
newPaymentBank: 'vn-client-balance-create vn-autocomplete[field="$ctrl.receipt.bankFk"]',
|
newPaymentBank: 'vn-client-balance-create vn-autocomplete[ng-model="$ctrl.receipt.bankFk"]',
|
||||||
newPaymentAmountInput: 'vn-client-balance-create vn-input-number[field="$ctrl.receipt.amountPaid"] input',
|
newPaymentAmountInput: 'vn-client-balance-create vn-input-number[ng-model="$ctrl.receipt.amountPaid"] input',
|
||||||
saveButton: 'vn-client-balance-create vn-button[label="Save"]',
|
saveButton: 'vn-client-balance-create vn-button[label="Save"]',
|
||||||
firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
|
firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
|
||||||
|
|
||||||
|
@ -209,9 +209,9 @@ export default {
|
||||||
},
|
},
|
||||||
itemCreateView: {
|
itemCreateView: {
|
||||||
temporalName: `${components.vnTextfield}[name="provisionalName"]`,
|
temporalName: `${components.vnTextfield}[name="provisionalName"]`,
|
||||||
typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
|
typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
|
||||||
intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
|
intrastatAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
|
||||||
originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
|
originAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
|
||||||
createButton: `${components.vnSubmit}`,
|
createButton: `${components.vnSubmit}`,
|
||||||
cancelButton: 'button[ui-sref="item.index"]'
|
cancelButton: 'button[ui-sref="item.index"]'
|
||||||
},
|
},
|
||||||
|
@ -219,8 +219,8 @@ export default {
|
||||||
goBackToModuleIndexButton: 'vn-item-descriptor a[href="#!/item/index"]',
|
goBackToModuleIndexButton: 'vn-item-descriptor a[href="#!/item/index"]',
|
||||||
moreMenu: 'vn-item-descriptor vn-icon-menu > div > vn-icon',
|
moreMenu: 'vn-item-descriptor vn-icon-menu > div > vn-icon',
|
||||||
moreMenuRegularizeButton: '.vn-popover.shown .vn-drop-down li[name="Regularize stock"]',
|
moreMenuRegularizeButton: '.vn-popover.shown .vn-drop-down li[name="Regularize stock"]',
|
||||||
regularizeQuantityInput: 'vn-item-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-textfield > div > div > div.infix > input',
|
regularizeQuantityInput: 'vn-item-descriptor vn-dialog tpl-body > div > vn-textfield input',
|
||||||
regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[field="$ctrl.warehouseFk"]',
|
regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
|
||||||
editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]',
|
editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]',
|
||||||
regularizeSaveButton: 'vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button',
|
regularizeSaveButton: 'vn-item-descriptor > vn-dialog > div > form > div.buttons > tpl-buttons > button',
|
||||||
inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]',
|
inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]',
|
||||||
|
@ -229,13 +229,13 @@ export default {
|
||||||
itemBasicData: {
|
itemBasicData: {
|
||||||
basicDataButton: 'vn-left-menu a[ui-sref="item.card.basicData"]',
|
basicDataButton: 'vn-left-menu a[ui-sref="item.card.basicData"]',
|
||||||
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
|
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
|
||||||
typeAutocomplete: 'vn-autocomplete[field="$ctrl.item.typeFk"]',
|
typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
|
||||||
intrastatAutocomplete: 'vn-autocomplete[field="$ctrl.item.intrastatFk"]',
|
intrastatAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
|
||||||
nameInput: 'vn-textfield[label="Name"] input',
|
nameInput: 'vn-textfield[label="Name"] input',
|
||||||
relevancyInput: 'vn-input-number[label="Relevancy"] input',
|
relevancyInput: 'vn-input-number[ng-model="$ctrl.item.relevancy"] input',
|
||||||
originAutocomplete: 'vn-autocomplete[field="$ctrl.item.originFk"]',
|
originAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
|
||||||
expenceAutocomplete: 'vn-autocomplete[field="$ctrl.item.expenceFk"]',
|
expenceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.expenceFk"]',
|
||||||
longNameInput: 'vn-textfield[field="$ctrl.item.longName"] input',
|
longNameInput: 'vn-textfield[ng-model="$ctrl.item.longName"] input',
|
||||||
isActiveCheckbox: 'vn-check[label="Active"]',
|
isActiveCheckbox: 'vn-check[label="Active"]',
|
||||||
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
|
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
|
||||||
submitBasicDataButton: `${components.vnSubmit}`
|
submitBasicDataButton: `${components.vnSubmit}`
|
||||||
|
@ -243,26 +243,27 @@ export default {
|
||||||
itemTags: {
|
itemTags: {
|
||||||
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
|
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
|
||||||
tagsButton: 'vn-left-menu a[ui-sref="item.card.tags"]',
|
tagsButton: 'vn-left-menu a[ui-sref="item.card.tags"]',
|
||||||
fourthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"]',
|
fourthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[ng-model="itemTag.tagFk"]',
|
||||||
fourthValueInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input',
|
fourthValueInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input',
|
||||||
fourthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input',
|
fourthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input',
|
||||||
fourthRemoveTagButton: 'vn-item-tags vn-horizontal:nth-child(4) vn-icon-button[icon="delete"]',
|
fourthRemoveTagButton: 'vn-item-tags vn-horizontal:nth-child(4) vn-icon-button[icon="delete"]',
|
||||||
fifthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"]',
|
fifthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[ng-model="itemTag.tagFk"]',
|
||||||
fifthValueInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input',
|
fifthValueInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input',
|
||||||
fifthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input',
|
fifthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input',
|
||||||
sixthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"]',
|
sixthTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[ng-model="itemTag.tagFk"]',
|
||||||
sixthValueInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input',
|
sixthValueInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input',
|
||||||
sixthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input',
|
sixthRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input',
|
||||||
seventhTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(7) > vn-autocomplete[field="itemTag.tagFk"]',
|
seventhTagAutocomplete: 'vn-item-tags vn-horizontal:nth-child(7) > vn-autocomplete[ng-model="itemTag.tagFk"]',
|
||||||
seventhValueInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Value"] input',
|
seventhValueInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Value"] input',
|
||||||
seventhRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input',
|
seventhRelevancyInput: 'vn-item-tags vn-horizontal:nth-child(7) > vn-textfield[label="Relevancy"] input',
|
||||||
addItemTagButton: 'vn-item-tags vn-icon-button[icon="add_circle"]',
|
addItemTagButton: 'vn-item-tags vn-icon-button[icon="add_circle"]',
|
||||||
submitItemTagsButton: `vn-item-tags ${components.vnSubmit}`
|
submitItemTagsButton: `vn-item-tags ${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
itemTax: {
|
itemTax: {
|
||||||
firstClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[field="tax.taxClassFk"]',
|
undoChangesButton: 'vn-item-tax vn-button-bar > vn-button[label="Undo changes"]',
|
||||||
secondClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(2) > vn-autocomplete[field="tax.taxClassFk"]',
|
firstClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[ng-model="tax.taxClassFk"]',
|
||||||
thirdClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[field="tax.taxClassFk"]',
|
secondClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(2) > vn-autocomplete[ng-model="tax.taxClassFk"]',
|
||||||
|
thirdClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="tax.taxClassFk"]',
|
||||||
submitTaxButton: `vn-item-tax ${components.vnSubmit}`
|
submitTaxButton: `vn-item-tax ${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
itemBarcodes: {
|
itemBarcodes: {
|
||||||
|
@ -273,19 +274,19 @@ export default {
|
||||||
},
|
},
|
||||||
itemNiches: {
|
itemNiches: {
|
||||||
addNicheButton: 'vn-item-niche vn-icon[icon="add_circle"]',
|
addNicheButton: 'vn-item-niche vn-icon[icon="add_circle"]',
|
||||||
firstWarehouseAutocomplete: 'vn-item-niche vn-autocomplete[field="niche.warehouseFk"]',
|
firstWarehouseAutocomplete: 'vn-item-niche vn-autocomplete[ng-model="niche.warehouseFk"]',
|
||||||
firstCodeInput: 'vn-item-niche vn-horizontal:nth-child(1) > vn-textfield[label="Code"] input',
|
firstCodeInput: 'vn-item-niche vn-horizontal:nth-child(1) > vn-textfield[label="Code"] input',
|
||||||
secondWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(2) > vn-autocomplete[field="niche.warehouseFk"]',
|
secondWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(2) > vn-autocomplete[ng-model="niche.warehouseFk"]',
|
||||||
secondCodeInput: 'vn-item-niche vn-horizontal:nth-child(2) > vn-textfield[label="Code"] input',
|
secondCodeInput: 'vn-item-niche vn-horizontal:nth-child(2) > vn-textfield[label="Code"] input',
|
||||||
secondNicheRemoveButton: 'vn-item-niche vn-horizontal:nth-child(2) > vn-none > vn-icon-button[icon="delete"]',
|
secondNicheRemoveButton: 'vn-item-niche vn-horizontal:nth-child(2) > vn-none > vn-icon-button[icon="delete"]',
|
||||||
thirdWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(3) > vn-autocomplete[field="niche.warehouseFk"]',
|
thirdWarehouseAutocomplete: 'vn-item-niche vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="niche.warehouseFk"]',
|
||||||
thirdCodeInput: 'vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input',
|
thirdCodeInput: 'vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input',
|
||||||
submitNichesButton: `vn-item-niche ${components.vnSubmit}`
|
submitNichesButton: `vn-item-niche ${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
itemBotanical: {
|
itemBotanical: {
|
||||||
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`,
|
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`,
|
||||||
genusAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.genusFk"]',
|
genusAutocomplete: 'vn-item-botanical vn-autocomplete[ng-model="$ctrl.botanical.genusFk"]',
|
||||||
speciesAutocomplete: 'vn-item-botanical vn-autocomplete[field="$ctrl.botanical.specieFk"]',
|
speciesAutocomplete: 'vn-item-botanical vn-autocomplete[ng-model="$ctrl.botanical.specieFk"]',
|
||||||
submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}`
|
submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
itemSummary: {
|
itemSummary: {
|
||||||
|
@ -300,7 +301,7 @@ export default {
|
||||||
secondTicketId: 'vn-item-diary vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > span',
|
secondTicketId: 'vn-item-diary vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > span',
|
||||||
firstBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(1) > vn-td.balance',
|
firstBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(1) > vn-td.balance',
|
||||||
fourthBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(4) > vn-td.balance',
|
fourthBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(4) > vn-td.balance',
|
||||||
warehouseAutocomplete: 'vn-item-diary vn-autocomplete[field="$ctrl.warehouseFk"]',
|
warehouseAutocomplete: 'vn-item-diary vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
|
||||||
},
|
},
|
||||||
itemLog: {
|
itemLog: {
|
||||||
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
|
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
|
||||||
|
@ -323,31 +324,31 @@ export default {
|
||||||
setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button'
|
setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button'
|
||||||
},
|
},
|
||||||
ticketsIndex: {
|
ticketsIndex: {
|
||||||
openAdvancedSearchButton: 'vn-ticket-index vn-searchbar t-right-icons > vn-icon[icon="keyboard_arrow_down"]',
|
openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
||||||
advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[model="filter.refFk"] input',
|
advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input',
|
||||||
newTicketButton: 'vn-ticket-index > a',
|
newTicketButton: 'vn-ticket-index > a',
|
||||||
searchResult: 'vn-ticket-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
|
searchResult: 'vn-ticket-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
|
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
|
||||||
searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
|
searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
|
||||||
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
|
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
|
||||||
searchWeeklyTicketInput: `vn-ticket-weekly-index ${components.vnTextfield}`,
|
searchWeeklyTicketInput: `vn-ticket-weekly-index ${components.vnTextfield}`,
|
||||||
searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar i[class="material-icons clear"]',
|
searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]',
|
||||||
advancedSearchButton: 'vn-ticket-search-panel vn-submit[label="Search"] input',
|
advancedSearchButton: 'vn-ticket-search-panel vn-submit[label="Search"] input',
|
||||||
searchButton: 'vn-ticket-index vn-searchbar vn-icon[icon="search"]',
|
searchButton: 'vn-ticket-index vn-searchbar vn-icon[icon="search"]',
|
||||||
searchWeeklyButton: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon="search"]',
|
searchWeeklyButton: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon="search"]',
|
||||||
moreMenu: 'vn-ticket-index vn-icon-menu[vn-id="more-button"] > div > vn-icon',
|
moreMenu: 'vn-ticket-index vn-icon-menu[vn-id="more-button"] > div > vn-icon',
|
||||||
moreMenuWeeklyTickets: '.vn-popover.shown .vn-drop-down li:nth-child(2)',
|
moreMenuWeeklyTickets: '.vn-popover.shown .vn-drop-down li:nth-child(2)',
|
||||||
sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6) vn-autocomplete[field="weekly.weekDay"] input',
|
sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6) vn-autocomplete[ng-model="weekly.weekDay"] input',
|
||||||
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr',
|
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr',
|
||||||
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
||||||
acceptDeleteTurn: 'vn-ticket-weekly-index > vn-confirm[vn-id="deleteWeekly"] button[response="ACCEPT"]'
|
acceptDeleteTurn: 'vn-ticket-weekly-index > vn-confirm[vn-id="deleteWeekly"] button[response="ACCEPT"]'
|
||||||
},
|
},
|
||||||
createTicketView: {
|
createTicketView: {
|
||||||
clientAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.clientFk"]',
|
clientAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.clientFk"]',
|
||||||
addressAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.addressFk"]',
|
addressAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.addressFk"]',
|
||||||
deliveryDateInput: 'vn-ticket-create > div > div > vn-card > div > vn-ticket-create-card > vn-date-picker > div > input',
|
deliveryDateInput: 'vn-ticket-create vn-date-picker[ng-model="$ctrl.landed"]',
|
||||||
warehouseAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.warehouseFk"]',
|
warehouseAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
|
||||||
agencyAutocomplete: 'vn-ticket-create vn-autocomplete[field="$ctrl.ticket.agencyModeFk"]',
|
agencyAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.ticket.agencyModeFk"]',
|
||||||
createButton: `${components.vnSubmit}`
|
createButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
ticketDescriptor: {
|
ticketDescriptor: {
|
||||||
|
@ -376,7 +377,7 @@ export default {
|
||||||
ticketNotes: {
|
ticketNotes: {
|
||||||
firstNoteRemoveButton: 'vn-icon[icon="delete"]',
|
firstNoteRemoveButton: 'vn-icon[icon="delete"]',
|
||||||
addNoteButton: 'vn-icon[icon="add_circle"]',
|
addNoteButton: 'vn-icon[icon="add_circle"]',
|
||||||
firstNoteTypeAutocomplete: 'vn-autocomplete[field="observation.observationTypeFk"]',
|
firstNoteTypeAutocomplete: 'vn-autocomplete[ng-model="observation.observationTypeFk"]',
|
||||||
firstDescriptionInput: 'vn-textfield[label="Description"] input',
|
firstDescriptionInput: 'vn-textfield[label="Description"] input',
|
||||||
submitNotesButton: `${components.vnSubmit}`
|
submitNotesButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
|
@ -389,10 +390,10 @@ export default {
|
||||||
ticketPackages: {
|
ticketPackages: {
|
||||||
packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package"]',
|
packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package"]',
|
||||||
firstPackageAutocomplete: 'vn-autocomplete[label="Package"]',
|
firstPackageAutocomplete: 'vn-autocomplete[label="Package"]',
|
||||||
firstQuantityInput: 'vn-input-number[label="Quantity"] input',
|
firstQuantityInput: 'vn-input-number[ng-model="package.quantity"] input',
|
||||||
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
|
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
|
||||||
addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
|
addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
|
||||||
clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] > div > div > div > vn-icon > i',
|
clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] .icons > vn-icon[icon=clear]',
|
||||||
savePackagesButton: `${components.vnSubmit}`
|
savePackagesButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
ticketSales: {
|
ticketSales: {
|
||||||
|
@ -408,7 +409,7 @@ export default {
|
||||||
moreMenuReserve: '.vn-popover.shown .vn-drop-down li[name="Mark as reserved"]',
|
moreMenuReserve: '.vn-popover.shown .vn-drop-down li[name="Mark as reserved"]',
|
||||||
moreMenuUnmarkReseved: '.vn-popover.shown .vn-drop-down li[name="Unmark as reserved"]',
|
moreMenuUnmarkReseved: '.vn-popover.shown .vn-drop-down li[name="Unmark as reserved"]',
|
||||||
moreMenuUpdateDiscount: '.vn-popover.shown .vn-drop-down li[name="Update discount"]',
|
moreMenuUpdateDiscount: '.vn-popover.shown .vn-drop-down li[name="Update discount"]',
|
||||||
moreMenuUpdateDiscountInput: 'vn-ticket-sale vn-dialog form vn-ticket-sale-edit-discount vn-input-number[model="$ctrl.newDiscount"] input',
|
moreMenuUpdateDiscountInput: 'vn-ticket-sale vn-dialog form vn-ticket-sale-edit-discount vn-input-number[ng-model="$ctrl.newDiscount"] input',
|
||||||
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
|
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
|
||||||
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
|
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
|
||||||
firstSaleClaimIcon: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) vn-icon[icon="icon-claims"]',
|
firstSaleClaimIcon: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) vn-icon[icon="icon-claims"]',
|
||||||
|
@ -416,11 +417,11 @@ export default {
|
||||||
firstSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(1)',
|
firstSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(1)',
|
||||||
firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img',
|
firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img',
|
||||||
firstSaleZoomedImage: 'body > div > div > img',
|
firstSaleZoomedImage: 'body > div > div > img',
|
||||||
firstSaleQuantity: 'vn-input-number[model="sale.quantity"]:nth-child(1) input',
|
firstSaleQuantity: 'vn-input-number[ng-model="sale.quantity"]:nth-child(1) input',
|
||||||
firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(5)',
|
firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(5)',
|
||||||
firstSaleQuantityClearInput: 'vn-textfield[model="sale.quantity"] div.suffix > i',
|
firstSaleQuantityClearInput: 'vn-textfield[ng-model="sale.quantity"] div.suffix > i',
|
||||||
firstSaleIdInput: 'body > vn-app > div > ui-view > vn-ticket-card > vn-main-block > div > vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete > div > div > input',
|
firstSaleIdInput: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete input',
|
||||||
firstSaleIdAutocomplete: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete',
|
firstSaleIdAutocomplete: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete',
|
||||||
idAutocompleteFirstResult: '.vn-popover.shown .vn-drop-down li',
|
idAutocompleteFirstResult: '.vn-popover.shown .vn-drop-down li',
|
||||||
firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(7) > span',
|
firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(7) > span',
|
||||||
firstSalePriceInput: '.vn-popover.shown vn-input-number input',
|
firstSalePriceInput: '.vn-popover.shown vn-input-number input',
|
||||||
|
@ -430,7 +431,7 @@ export default {
|
||||||
firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)',
|
firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)',
|
||||||
firstSaleColour: 'vn-tr:nth-child(1) vn-fetched-tags section',
|
firstSaleColour: 'vn-tr:nth-child(1) vn-fetched-tags section',
|
||||||
firstSaleLength: 'vn-ticket-sale vn-tr:nth-child(1) vn-td-editable:nth-child(6) section:nth-child(3)',
|
firstSaleLength: 'vn-ticket-sale vn-tr:nth-child(1) vn-td-editable:nth-child(6) section:nth-child(3)',
|
||||||
firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"]',
|
firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[ng-model="sale.checked"]',
|
||||||
secondSaleColour: 'vn-tr:nth-child(2) vn-fetched-tags section',
|
secondSaleColour: 'vn-tr:nth-child(2) vn-fetched-tags section',
|
||||||
secondSalePrice: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7) > span',
|
secondSalePrice: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7) > span',
|
||||||
secondSaleDiscount: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)',
|
secondSaleDiscount: 'vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)',
|
||||||
|
@ -438,18 +439,18 @@ export default {
|
||||||
secondSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(2)',
|
secondSaleText: 'vn-table div > vn-tbody > vn-tr:nth-child(2)',
|
||||||
secondSaleId: 'vn-ticket-sale:nth-child(2) vn-td-editable:nth-child(4) text > span',
|
secondSaleId: 'vn-ticket-sale:nth-child(2) vn-td-editable:nth-child(4) text > span',
|
||||||
secondSaleIdCell: 'vn-ticket-sale vn-tr:nth-child(2) > vn-td-editable:nth-child(4)',
|
secondSaleIdCell: 'vn-ticket-sale vn-tr:nth-child(2) > vn-td-editable:nth-child(4)',
|
||||||
secondSaleIdInput: 'body > vn-app > div > ui-view > vn-ticket-card > vn-main-block > div > vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete > div > div > input',
|
secondSaleIdInput: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete input',
|
||||||
secondSaleIdAutocomplete: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete',
|
secondSaleIdAutocomplete: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(4) > vn-autocomplete',
|
||||||
secondSaleQuantity: 'vn-ticket-sale vn-table vn-tr:nth-child(2) vn-input-number input',
|
secondSaleQuantity: 'vn-ticket-sale vn-table vn-tr:nth-child(2) vn-input-number input',
|
||||||
secondSaleConceptCell: 'vn-ticket-sale vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)',
|
secondSaleConceptCell: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)',
|
||||||
secondSaleConceptInput: 'vn-ticket-sale vn-table vn-tr:nth-child(2) > vn-td-editable.ng-isolate-scope.selected vn-textfield input',
|
secondSaleConceptInput: 'vn-ticket-sale vn-table vn-tr:nth-child(2) > vn-td-editable.ng-isolate-scope.selected vn-textfield input',
|
||||||
totalImport: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong',
|
totalImport: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > vn-one > p:nth-child(3) > strong',
|
||||||
selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check',
|
selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check',
|
||||||
secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[field="sale.checked"]',
|
secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[ng-model="sale.checked"]',
|
||||||
thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[field="sale.checked"]',
|
thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[ng-model="sale.checked"]',
|
||||||
deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]',
|
deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]',
|
||||||
transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]',
|
transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]',
|
||||||
moveToTicketInput: '.vn-popover.shown vn-textfield[model="$ctrl.transfer.ticketId"] input',
|
moveToTicketInput: '.vn-popover.shown vn-textfield[ng-model="$ctrl.transfer.ticketId"] input',
|
||||||
moveToTicketInputClearButton: '.vn-popover.shown i[title="Clear"]',
|
moveToTicketInputClearButton: '.vn-popover.shown i[title="Clear"]',
|
||||||
moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]',
|
moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]',
|
||||||
moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]',
|
moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]',
|
||||||
|
@ -460,36 +461,34 @@ export default {
|
||||||
ticketTracking: {
|
ticketTracking: {
|
||||||
trackingButton: 'vn-left-menu a[ui-sref="ticket.card.tracking.index"]',
|
trackingButton: 'vn-left-menu a[ui-sref="ticket.card.tracking.index"]',
|
||||||
createStateButton: `${components.vnFloatButton}`,
|
createStateButton: `${components.vnFloatButton}`,
|
||||||
stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[field="$ctrl.stateFk"]',
|
stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[ng-model="$ctrl.stateFk"]',
|
||||||
saveButton: `${components.vnSubmit}`,
|
saveButton: `${components.vnSubmit}`,
|
||||||
cancelButton: 'vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]'
|
cancelButton: 'vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]'
|
||||||
},
|
},
|
||||||
ticketBasicData: {
|
ticketBasicData: {
|
||||||
basicDataButton: 'vn-left-menu a[ui-sref="ticket.card.basicData.stepOne"]',
|
basicDataButton: 'vn-left-menu a[ui-sref="ticket.card.basicData.stepOne"]',
|
||||||
clientAutocomplete: 'vn-autocomplete[field="$ctrl.clientFk"]',
|
clientAutocomplete: 'vn-autocomplete[ng-model="$ctrl.clientFk"]',
|
||||||
addressAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.addressFk"]',
|
addressAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.addressFk"]',
|
||||||
agencyAutocomplete: 'vn-autocomplete[field="$ctrl.agencyModeId"]',
|
agencyAutocomplete: 'vn-autocomplete[ng-model="$ctrl.agencyModeId"]',
|
||||||
zoneAutocomplete: 'vn-autocomplete[field="$ctrl.zoneId"]',
|
zoneAutocomplete: 'vn-autocomplete[ng-model="$ctrl.zoneId"]',
|
||||||
nextStepButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-button',
|
nextStepButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-button',
|
||||||
finalizeButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-submit',
|
finalizeButton: 'vn-step-control > section > section.buttons > section:nth-child(2) > vn-submit',
|
||||||
stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)',
|
stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)',
|
||||||
chargesReasonAutocomplete: 'vn-autocomplete[field="$ctrl.ticket.option"]',
|
chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]',
|
||||||
},
|
},
|
||||||
ticketComponents: {
|
ticketComponents: {
|
||||||
base: 'vn-ticket-components tfoot > tr:nth-child(1) > td',
|
base: 'vn-ticket-components [name="base-sum"]'
|
||||||
margin: 'vn-ticket-components tfoot > tr:nth-child(2) > td',
|
|
||||||
total: 'vn-ticket-components tfoot > tr:nth-child(3) > td'
|
|
||||||
},
|
},
|
||||||
ticketRequests: {
|
ticketRequests: {
|
||||||
addRequestButton: 'vn-ticket-request-index > a > vn-float-button > button',
|
addRequestButton: 'vn-ticket-request-index > a > vn-float-button > button',
|
||||||
request: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr',
|
request: 'vn-ticket-request-index vn-table vn-tr',
|
||||||
descriptionInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield > div > div > div.infix > input',
|
descriptionInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(1) > vn-textfield input',
|
||||||
atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[field="$ctrl.ticketRequest.atenderFk"]',
|
atenderAutocomplete: 'vn-ticket-request-create vn-autocomplete[ng-model="$ctrl.ticketRequest.atenderFk"]',
|
||||||
quantityInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(1) > div > div > div.infix > input',
|
quantityInput: 'vn-ticket-request-create vn-input-number input[name=quantity]',
|
||||||
priceInput: 'vn-ticket-request-create > form > div > vn-card > div > vn-horizontal:nth-child(2) > vn-input-number:nth-child(2) > div > div > div.infix > input',
|
priceInput: 'vn-ticket-request-create vn-input-number input[name=price]',
|
||||||
firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)',
|
firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)',
|
||||||
saveButton: 'vn-ticket-request-create > form > div > vn-button-bar > vn-submit[label="Create"] input',
|
saveButton: 'vn-ticket-request-create > form > div > vn-button-bar > vn-submit[label="Create"] input',
|
||||||
firstDescription: 'vn-ticket-request-index > form > vn-card > div > vn-horizontal > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2)',
|
firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2)',
|
||||||
|
|
||||||
},
|
},
|
||||||
ticketLog: {
|
ticketLog: {
|
||||||
|
@ -501,20 +500,20 @@ export default {
|
||||||
ticketService: {
|
ticketService: {
|
||||||
addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button',
|
addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button',
|
||||||
firstAddDescriptionButton: 'vn-ticket-service vn-icon-button[vn-tooltip="New service type"] > button',
|
firstAddDescriptionButton: 'vn-ticket-service vn-icon-button[vn-tooltip="New service type"] > button',
|
||||||
firstDescriptionAutocomplete: 'vn-ticket-service vn-autocomplete[field="service.description"]',
|
firstDescriptionAutocomplete: 'vn-ticket-service vn-autocomplete[ng-model="service.description"]',
|
||||||
firstQuantityInput: 'vn-ticket-service vn-input-number[label="Quantity"] input',
|
firstQuantityInput: 'vn-ticket-service vn-input-number[label="Quantity"] input',
|
||||||
firstPriceInput: 'vn-ticket-service vn-input-number[label="Price"] input',
|
firstPriceInput: 'vn-ticket-service vn-input-number[label="Price"] input',
|
||||||
firstVatTypeAutocomplete: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
|
firstVatTypeAutocomplete: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
|
||||||
fistDeleteServiceButton: 'vn-ticket-card > vn-main-block > div.content-block.ng-scope > vn-ticket-service > form > vn-card > div > vn-one:nth-child(1) > vn-horizontal:nth-child(1) > vn-auto > vn-icon-button[icon="delete"]',
|
fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]',
|
||||||
newDescriptionInput: 'vn-ticket-service > vn-dialog vn-textfield[model="$ctrl.newServiceType.name"] input',
|
newDescriptionInput: 'vn-ticket-service > vn-dialog vn-textfield[ng-model="$ctrl.newServiceType.name"] input',
|
||||||
serviceLine: 'vn-ticket-service > form > vn-card > div > vn-one:nth-child(2) > vn-horizontal',
|
serviceLine: 'vn-ticket-service > form > vn-card > div > vn-one:nth-child(2) > vn-horizontal',
|
||||||
saveServiceButton: `${components.vnSubmit}`,
|
saveServiceButton: `${components.vnSubmit}`,
|
||||||
saveDescriptionButton: 'vn-ticket-service > vn-dialog[vn-id="createServiceTypeDialog"] > div > form > div.buttons > tpl-buttons > button'
|
saveDescriptionButton: 'vn-ticket-service > vn-dialog[vn-id="createServiceTypeDialog"] > div > form > div.buttons > tpl-buttons > button'
|
||||||
},
|
},
|
||||||
createStateView: {
|
createStateView: {
|
||||||
stateAutocomplete: 'vn-autocomplete[field="$ctrl.stateFk"]',
|
stateAutocomplete: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
|
||||||
workerAutocomplete: 'vn-autocomplete[field="$ctrl.workerFk"]',
|
workerAutocomplete: 'vn-autocomplete[ng-model="$ctrl.workerFk"]',
|
||||||
clearStateInputButton: 'vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i',
|
clearStateInputButton: 'vn-autocomplete[ng-model="$ctrl.stateFk"] .icons > vn-icon[icon=clear]',
|
||||||
saveStateButton: `${components.vnSubmit}`
|
saveStateButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
claimsIndex: {
|
claimsIndex: {
|
||||||
|
@ -530,7 +529,7 @@ export default {
|
||||||
claimSummary: {
|
claimSummary: {
|
||||||
header: 'vn-claim-summary > vn-card > div > h5',
|
header: 'vn-claim-summary > vn-card > div > h5',
|
||||||
state: 'vn-claim-summary vn-label-value[label="State"] > section > span',
|
state: 'vn-claim-summary vn-label-value[label="State"] > section > span',
|
||||||
observation: 'vn-claim-summary vn-textarea[model="$ctrl.summary.claim.observation"] > div > textarea',
|
observation: 'vn-claim-summary vn-textarea[ng-model="$ctrl.summary.claim.observation"] textarea',
|
||||||
firstSaleItemId: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(4) > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(1) > span',
|
firstSaleItemId: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(4) > vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(1) > span',
|
||||||
firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img',
|
firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img',
|
||||||
itemDescriptorPopover: '.vn-popover.shown vn-item-descriptor',
|
itemDescriptorPopover: '.vn-popover.shown vn-item-descriptor',
|
||||||
|
@ -541,45 +540,45 @@ export default {
|
||||||
firstActionTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor'
|
firstActionTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor'
|
||||||
},
|
},
|
||||||
claimBasicData: {
|
claimBasicData: {
|
||||||
claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[field="$ctrl.claim.claimStateFk"]',
|
claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]',
|
||||||
responsabilityInputRange: 'vn-input-range',
|
responsabilityInputRange: 'vn-input-range',
|
||||||
observationInput: 'vn-textarea[field="$ctrl.claim.observation"] textarea',
|
observationInput: 'vn-textarea[ng-model="$ctrl.claim.observation"] textarea',
|
||||||
saveButton: `${components.vnSubmit}`
|
saveButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
claimDetail: {
|
claimDetail: {
|
||||||
secondItemDiscount: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(6) > span',
|
secondItemDiscount: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(6) > span',
|
||||||
discountInput: '.vn-popover.shown vn-input-number[model="$ctrl.newDiscount"] > div > div > div.infix > input',
|
discountInput: '.vn-popover.shown vn-input-number[ng-model="$ctrl.newDiscount"] input',
|
||||||
discoutPopoverMana: '.vn-popover.shown .content > div > vn-horizontal > h5',
|
discoutPopoverMana: '.vn-popover.shown .content > div > vn-horizontal > h5',
|
||||||
addItemButton: 'vn-claim-detail a vn-float-button',
|
addItemButton: 'vn-claim-detail a vn-float-button',
|
||||||
firstClaimableSaleFromTicket: 'vn-claim-detail > vn-dialog vn-tbody > vn-tr',
|
firstClaimableSaleFromTicket: 'vn-claim-detail > vn-dialog vn-tbody > vn-tr',
|
||||||
claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr',
|
claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr',
|
||||||
firstItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(1) vn-input-number[model="saleClaimed.quantity"] input',
|
firstItemQuantityInput: 'vn-claim-detail vn-tr:nth-child(1) vn-input-number[ng-model="saleClaimed.quantity"] input',
|
||||||
totalClaimed: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > div > vn-label-value:nth-child(2) > section > span',
|
totalClaimed: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-horizontal > div > vn-label-value:nth-child(2) > section > span',
|
||||||
secondItemDeleteButton: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(8) > vn-icon-button > button > vn-icon > i'
|
secondItemDeleteButton: 'vn-claim-detail > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(8) > vn-icon-button > button > vn-icon > i'
|
||||||
},
|
},
|
||||||
claimDevelopment: {
|
claimDevelopment: {
|
||||||
addDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > vn-one > vn-icon-button > button > vn-icon',
|
addDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > vn-one > vn-icon-button > button > vn-icon',
|
||||||
firstDeleteDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > form > vn-horizontal:nth-child(2) > vn-icon-button > button > vn-icon',
|
firstDeleteDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > div > vn-vertical > form > vn-horizontal:nth-child(2) > vn-icon-button > button > vn-icon',
|
||||||
firstClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimReasonFk"]',
|
firstClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
|
||||||
firstClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimResultFk"]',
|
firstClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
|
||||||
firstClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimResponsibleFk"]',
|
firstClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
|
||||||
firstClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.workerFk"]',
|
firstClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.workerFk"]',
|
||||||
firstClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[field="claimDevelopment.claimRedeliveryFk"]',
|
firstClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
|
||||||
secondClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimReasonFk"]',
|
secondClaimReasonAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
|
||||||
secondClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimResultFk"]',
|
secondClaimResultAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
|
||||||
secondClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimResponsibleFk"]',
|
secondClaimResponsibleAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
|
||||||
secondClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.workerFk"]',
|
secondClaimWorkerAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.workerFk"]',
|
||||||
secondClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[field="claimDevelopment.claimRedeliveryFk"]',
|
secondClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
|
||||||
saveDevelopmentButton: `${components.vnSubmit}`
|
saveDevelopmentButton: `${components.vnSubmit}`
|
||||||
},
|
},
|
||||||
claimAction: {
|
claimAction: {
|
||||||
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
||||||
importTicketButton: 'vn-claim-action vn-button[label="Import ticket"]',
|
importTicketButton: 'vn-claim-action vn-button[label="Import ticket"]',
|
||||||
secondImportableTicket: '.vn-popover.shown .content > div > vn-table > div > vn-tbody > vn-tr:nth-child(2)',
|
secondImportableTicket: '.vn-popover.shown .content > div > vn-table > div > vn-tbody > vn-tr:nth-child(2)',
|
||||||
firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[field="saleClaimed.claimDestinationFk"]',
|
firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
|
||||||
secondLineDestination: 'vn-claim-action vn-tr:nth-child(2) vn-autocomplete[field="saleClaimed.claimDestinationFk"]',
|
secondLineDestination: 'vn-claim-action vn-tr:nth-child(2) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
|
||||||
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
||||||
isPaidWithManaCheckbox: 'vn-check[field="$ctrl.claim.isChargedToMana"]'
|
isPaidWithManaCheckbox: 'vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
||||||
},
|
},
|
||||||
ordersIndex: {
|
ordersIndex: {
|
||||||
searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
|
searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
|
@ -597,7 +596,7 @@ export default {
|
||||||
clientAutocomplete: 'vn-autocomplete[label="Client"]',
|
clientAutocomplete: 'vn-autocomplete[label="Client"]',
|
||||||
addressAutocomplete: 'vn-autocomplete[label="Address"]',
|
addressAutocomplete: 'vn-autocomplete[label="Address"]',
|
||||||
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
|
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
|
||||||
landedDatePicker: 'vn-date-picker[label="Landed"] input',
|
landedDatePicker: 'vn-date-picker[label="Landed"]',
|
||||||
createButton: `${components.vnSubmit}`,
|
createButton: `${components.vnSubmit}`,
|
||||||
cancelButton: 'vn-button[href="#!/client/index"]'
|
cancelButton: 'vn-button[href="#!/client/index"]'
|
||||||
},
|
},
|
||||||
|
@ -605,14 +604,14 @@ export default {
|
||||||
orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
|
orderByAutocomplete: 'vn-autocomplete[label="Order by"]',
|
||||||
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
|
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
|
||||||
typeAutocomplete: 'vn-autocomplete[data="$ctrl.itemTypes"]',
|
typeAutocomplete: 'vn-autocomplete[data="$ctrl.itemTypes"]',
|
||||||
itemIdInput: 'vn-order-catalog > vn-side-menu vn-catalog-filter vn-textfield[model="$ctrl.itemFk"] input',
|
itemIdInput: 'vn-catalog-filter vn-textfield[ng-model="$ctrl.itemFk"] input',
|
||||||
itemTagValueInput: 'vn-order-catalog > vn-side-menu vn-catalog-filter vn-textfield[model="$ctrl.value"] input',
|
itemTagValueInput: 'vn-catalog-filter vn-textfield[ng-model="$ctrl.value"] input',
|
||||||
openTagSearch: 'vn-order-catalog > vn-side-menu > div > vn-catalog-filter > div > vn-vertical > vn-textfield[model="$ctrl.value"] > div > div > div.rightIcons > t-right-icons > i',
|
openTagSearch: 'vn-catalog-filter > div > vn-vertical > vn-textfield[ng-model="$ctrl.value"] .append i',
|
||||||
tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[field="filter.tagFk"]',
|
tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]',
|
||||||
tagValueInput: 'vn-order-catalog-search-panel vn-textfield[model="filter.value"] input',
|
tagValueInput: 'vn-order-catalog-search-panel vn-textfield[ng-model="filter.value"] input',
|
||||||
searchTagButton: 'vn-order-catalog-search-panel > div > form > vn-horizontal:nth-child(3) > vn-submit > input',
|
searchTagButton: 'vn-order-catalog-search-panel > div > form > vn-horizontal:nth-child(3) > vn-submit > input',
|
||||||
thirdFilterRemoveButton: 'vn-order-catalog > vn-side-menu vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(3) button',
|
thirdFilterRemoveButton: 'vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(3) button',
|
||||||
fourthFilterRemoveButton: 'vn-order-catalog > vn-side-menu vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(4) button',
|
fourthFilterRemoveButton: 'vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(4) button',
|
||||||
},
|
},
|
||||||
orderBasicData: {
|
orderBasicData: {
|
||||||
clientAutocomplete: 'vn-autocomplete[label="Client"]',
|
clientAutocomplete: 'vn-autocomplete[label="Client"]',
|
||||||
|
@ -632,11 +631,11 @@ export default {
|
||||||
addNewRouteButton: 'vn-route-index > a[ui-sref="route.create"]'
|
addNewRouteButton: 'vn-route-index > a[ui-sref="route.create"]'
|
||||||
},
|
},
|
||||||
createRouteView: {
|
createRouteView: {
|
||||||
workerAutocomplete: 'vn-route-create vn-autocomplete[field="$ctrl.route.workerFk"]',
|
workerAutocomplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
|
||||||
createdDatePicker: 'vn-route-create vn-date-picker[model="$ctrl.route.created"] > div > input',
|
createdDatePicker: 'vn-route-create vn-date-picker[ng-model="$ctrl.route.created"]',
|
||||||
vehicleAutoComplete: 'vn-route-create vn-autocomplete[field="$ctrl.route.vehicleFk"]',
|
vehicleAutoComplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
|
||||||
agencyAutoComplete: 'vn-route-create vn-autocomplete[field="$ctrl.route.agencyModeFk"]',
|
agencyAutoComplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
|
||||||
descriptionInput: 'vn-route-create vn-textfield[field="$ctrl.route.description"] input',
|
descriptionInput: 'vn-route-create vn-textfield[ng-model="$ctrl.route.description"] input',
|
||||||
submitButton: 'vn-route-create vn-submit > input[type="submit"]'
|
submitButton: 'vn-route-create vn-submit > input[type="submit"]'
|
||||||
},
|
},
|
||||||
routeDescriptor: {
|
routeDescriptor: {
|
||||||
|
@ -646,29 +645,29 @@ export default {
|
||||||
routeId: 'vn-route-summary > vn-card > div > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(1) > section > span'
|
routeId: 'vn-route-summary > vn-card > div > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(1) > section > span'
|
||||||
},
|
},
|
||||||
routeBasicData: {
|
routeBasicData: {
|
||||||
workerAutoComplete: 'vn-route-basic-data vn-autocomplete[field="$ctrl.route.workerFk"]',
|
workerAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
|
||||||
vehicleAutoComplete: 'vn-route-basic-data vn-autocomplete[field="$ctrl.route.vehicleFk"]',
|
vehicleAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
|
||||||
agencyAutoComplete: 'vn-route-basic-data vn-autocomplete[field="$ctrl.route.agencyModeFk"]',
|
agencyAutoComplete: 'vn-route-basic-data vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
|
||||||
kmStartInput: 'vn-route-basic-data vn-input-number[field="$ctrl.route.kmStart"] input',
|
kmStartInput: 'vn-route-basic-data vn-input-number[ng-model="$ctrl.route.kmStart"] input',
|
||||||
kmEndInput: 'vn-route-basic-data vn-input-number[model="$ctrl.route.kmEnd"] input',
|
kmEndInput: 'vn-route-basic-data vn-input-number[ng-model="$ctrl.route.kmEnd"] input',
|
||||||
createdDateInput: 'vn-route-basic-data vn-date-picker[model="$ctrl.route.created"] > div > input',
|
createdDateInput: 'vn-route-basic-data vn-date-picker[ng-model="$ctrl.route.created"]',
|
||||||
startedHourInput: 'vn-route-basic-data vn-input-time[model="$ctrl.route.started"] input',
|
startedHourInput: 'vn-route-basic-data vn-input-time[ng-model="$ctrl.route.started"] input',
|
||||||
finishedHourInput: 'vn-route-basic-data vn-input-time[model="$ctrl.route.finished"] input',
|
finishedHourInput: 'vn-route-basic-data vn-input-time[ng-model="$ctrl.route.finished"] input',
|
||||||
saveButton: 'vn-route-basic-data vn-submit[label="Save"] input'
|
saveButton: 'vn-route-basic-data vn-submit[label="Save"] input'
|
||||||
},
|
},
|
||||||
routeTickets: {
|
routeTickets: {
|
||||||
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[model="ticket.priority"] input',
|
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[ng-model="ticket.priority"] input',
|
||||||
secondTicketPriority: 'vn-route-tickets vn-tr:nth-child(2) vn-textfield[model="ticket.priority"] input',
|
secondTicketPriority: 'vn-route-tickets vn-tr:nth-child(2) vn-textfield[ng-model="ticket.priority"] input',
|
||||||
thirdTicketPriority: 'vn-route-tickets vn-tr:nth-child(3) vn-textfield[model="ticket.priority"] input',
|
thirdTicketPriority: 'vn-route-tickets vn-tr:nth-child(3) vn-textfield[ng-model="ticket.priority"] input',
|
||||||
fourthTicketPriority: 'vn-route-tickets vn-tr:nth-child(4) vn-textfield[model="ticket.priority"] input',
|
fourthTicketPriority: 'vn-route-tickets vn-tr:nth-child(4) vn-textfield[ng-model="ticket.priority"] input',
|
||||||
eleventhTicketPriority: 'vn-route-tickets vn-tr:nth-child(11) vn-textfield[model="ticket.priority"] input',
|
eleventhTicketPriority: 'vn-route-tickets vn-tr:nth-child(11) vn-textfield[ng-model="ticket.priority"] input',
|
||||||
firstTicketCheckbox: 'vn-route-tickets vn-tr:nth-child(1) vn-check',
|
firstTicketCheckbox: 'vn-route-tickets vn-tr:nth-child(1) vn-check',
|
||||||
buscamanButton: 'vn-route-tickets vn-button[icon="icon-buscaman"]',
|
buscamanButton: 'vn-route-tickets vn-button[icon="icon-buscaman"]',
|
||||||
firstTicketDeleteButton: 'vn-route-tickets vn-tr:nth-child(1) vn-icon[icon="delete"]',
|
firstTicketDeleteButton: 'vn-route-tickets vn-tr:nth-child(1) vn-icon[icon="delete"]',
|
||||||
confirmButton: 'vn-route-tickets > vn-confirm button[response="ACCEPT"]'
|
confirmButton: 'vn-route-tickets > vn-confirm button[response="ACCEPT"]'
|
||||||
},
|
},
|
||||||
workerPbx: {
|
workerPbx: {
|
||||||
extensionInput: 'vn-worker-pbx vn-textfield[model="$ctrl.worker.sip.extension"] input',
|
extensionInput: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"] input',
|
||||||
saveButton: 'vn-worker-pbx vn-submit[label="Save"] input'
|
saveButton: 'vn-worker-pbx vn-submit[label="Save"] input'
|
||||||
},
|
},
|
||||||
workerTimeControl: {
|
workerTimeControl: {
|
||||||
|
|
|
@ -176,7 +176,7 @@ describe('Client lock verified data path', () => {
|
||||||
.wait(selectors.clientFiscalData.socialNameInput)
|
.wait(selectors.clientFiscalData.socialNameInput)
|
||||||
.evaluate(selector => {
|
.evaluate(selector => {
|
||||||
return document.querySelector(selector).disabled;
|
return document.querySelector(selector).disabled;
|
||||||
}, 'vn-textfield[model="$ctrl.client.socialName"] > div');
|
}, 'vn-textfield[ng-model="$ctrl.client.socialName"] > div');
|
||||||
|
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
import selectors from '../../helpers/selectors.js';
|
||||||
import createNightmare from '../../helpers/nightmare';
|
import createNightmare from '../../helpers/nightmare';
|
||||||
|
|
||||||
// #1702 Autocomplete no siempre refresca al cancelar formulario
|
describe('Item edit tax path', () => {
|
||||||
xdescribe('Item edit tax path', () => {
|
|
||||||
const nightmare = createNightmare();
|
const nightmare = createNightmare();
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
|
@ -14,9 +13,9 @@ xdescribe('Item edit tax path', () => {
|
||||||
|
|
||||||
it(`should add the item tax to all countries`, async() => {
|
it(`should add the item tax to all countries`, async() => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.autocompleteSearch(selectors.itemTax.firstClassAutocomplete, 'Reduced VAT')
|
.autocompleteSearch(selectors.itemTax.firstClassAutocomplete, 'General VAT')
|
||||||
.autocompleteSearch(selectors.itemTax.secondClassAutocomplete, 'General VAT')
|
.autocompleteSearch(selectors.itemTax.secondClassAutocomplete, 'General VAT')
|
||||||
.autocompleteSearch(selectors.itemTax.thirdClassAutocomplete, 'Reduced VAT')
|
.autocompleteSearch(selectors.itemTax.thirdClassAutocomplete, 'General VAT')
|
||||||
.waitToClick(selectors.itemTax.submitTaxButton)
|
.waitToClick(selectors.itemTax.submitTaxButton)
|
||||||
.waitForLastSnackbar();
|
.waitForLastSnackbar();
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ xdescribe('Item edit tax path', () => {
|
||||||
.reloadSection('item.card.tax')
|
.reloadSection('item.card.tax')
|
||||||
.waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
|
.waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
|
||||||
|
|
||||||
expect(firstVatType).toEqual('Reduced VAT');
|
expect(firstVatType).toEqual('General VAT');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should confirm the second item tax class was edited`, async() => {
|
it(`should confirm the second item tax class was edited`, async() => {
|
||||||
|
@ -42,6 +41,22 @@ xdescribe('Item edit tax path', () => {
|
||||||
const thirdVatType = await nightmare
|
const thirdVatType = await nightmare
|
||||||
.waitToGetProperty(`${selectors.itemTax.thirdClassAutocomplete} input`, 'value');
|
.waitToGetProperty(`${selectors.itemTax.thirdClassAutocomplete} input`, 'value');
|
||||||
|
|
||||||
expect(thirdVatType).toEqual('Reduced VAT');
|
expect(thirdVatType).toEqual('General VAT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should edit the first class without saving the form`, async() => {
|
||||||
|
const firstVatType = await nightmare
|
||||||
|
.autocompleteSearch(selectors.itemTax.firstClassAutocomplete, 'Reduced VAT')
|
||||||
|
.waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
|
||||||
|
|
||||||
|
expect(firstVatType).toEqual('Reduced VAT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should now click the undo changes button and see the changes works`, async() => {
|
||||||
|
const firstVatType = await nightmare
|
||||||
|
.waitToClick(selectors.itemTax.undoChangesButton)
|
||||||
|
.waitToGetProperty(`${selectors.itemTax.firstClassAutocomplete} input`, 'value');
|
||||||
|
|
||||||
|
expect(firstVatType).toEqual('General VAT');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -160,7 +160,7 @@ describe('Ticket descriptor path', () => {
|
||||||
|
|
||||||
it('should confirm the sixth weekly ticket was deleted', async() => {
|
it('should confirm the sixth weekly ticket was deleted', async() => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.waitToClick('vn-ticket-weekly-index vn-searchbar i[class="material-icons clear"]')
|
.waitToClick('vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]')
|
||||||
.waitToClick(selectors.ticketsIndex.searchWeeklyButton)
|
.waitToClick(selectors.ticketsIndex.searchWeeklyButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchWeeklyResult, 5)
|
.waitForNumberOfElements(selectors.ticketsIndex.searchWeeklyResult, 5)
|
||||||
.countElement(selectors.ticketsIndex.searchWeeklyResult);
|
.countElement(selectors.ticketsIndex.searchWeeklyResult);
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe('Ticket services path', () => {
|
||||||
.waitToClick(selectors.ticketService.saveDescriptionButton)
|
.waitToClick(selectors.ticketService.saveDescriptionButton)
|
||||||
.waitForLastSnackbar();
|
.waitForLastSnackbar();
|
||||||
|
|
||||||
expect(result).toEqual(`Name can't be empty`);
|
expect(result).toEqual(`can't be blank`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a new description then add price then create the service', async() => {
|
it('should create a new description then add price then create the service', async() => {
|
||||||
|
|
|
@ -49,7 +49,7 @@ xdescribe('Route basic Data path', () => {
|
||||||
|
|
||||||
it('should count how many tickets are in route', async() => {
|
it('should count how many tickets are in route', async() => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.countElement('vn-route-tickets vn-textfield[model="ticket.priority"]');
|
.countElement('vn-route-tickets vn-textfield[ng-model="ticket.priority"]');
|
||||||
|
|
||||||
expect(result).toEqual(11);
|
expect(result).toEqual(11);
|
||||||
});
|
});
|
||||||
|
@ -74,7 +74,7 @@ xdescribe('Route basic Data path', () => {
|
||||||
|
|
||||||
it('should now count how many tickets are in route to find one less', async() => {
|
it('should now count how many tickets are in route to find one less', async() => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.countElement('vn-route-tickets vn-textfield[model="ticket.priority"]');
|
.countElement('vn-route-tickets vn-textfield[ng-model="ticket.priority"]');
|
||||||
|
|
||||||
expect(result).toEqual(9);
|
expect(result).toEqual(9);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
<div>
|
|
||||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
|
|
||||||
<input
|
|
||||||
type="button"
|
|
||||||
class="mdl-textfield__input"
|
|
||||||
ng-click="$ctrl.onMouseDown($event)"
|
|
||||||
ng-keydown="$ctrl.onKeyDown($event)"/>
|
|
||||||
<div class="icons">
|
|
||||||
<vn-icon
|
|
||||||
ng-show="!$ctrl.disabled"
|
|
||||||
icon="clear"
|
|
||||||
class="clear"
|
|
||||||
ng-click="$ctrl.onClearClick($event)"
|
|
||||||
translate-attr="{title: 'Clear'}">
|
|
||||||
</vn-icon>
|
|
||||||
</div>
|
|
||||||
<label class="mdl-textfield__label">
|
|
||||||
<span translate>{{::$ctrl.label}}</span>
|
|
||||||
<span translate ng-show="::$ctrl.required">*</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<vn-drop-down
|
|
||||||
vn-id="drop-down"
|
|
||||||
on-select="$ctrl.onDropDownSelect(item)"
|
|
||||||
on-data-ready="$ctrl.onDataReady()">
|
|
||||||
</vn-drop-down>
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<div
|
||||||
|
class="container"
|
||||||
|
ng-click="$ctrl.onContainerMouseDown($event)"
|
||||||
|
ng-keydown="$ctrl.onContainerKeyDown($event)">
|
||||||
|
<div
|
||||||
|
ng-transclude="prepend"
|
||||||
|
class="prepend">
|
||||||
|
</div>
|
||||||
|
<div class="infix">
|
||||||
|
<div class="fix prefix"></div>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
type="button">
|
||||||
|
</input>
|
||||||
|
</div>
|
||||||
|
<div class="fix suffix"></div>
|
||||||
|
<label>
|
||||||
|
<span translate>{{::$ctrl.label}}</span>
|
||||||
|
<span class="required">*</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="icons pre">
|
||||||
|
<vn-icon
|
||||||
|
icon="clear"
|
||||||
|
translate-attr="{title: 'Clear'}"
|
||||||
|
ng-click="$ctrl.onClear($event)">
|
||||||
|
</vn-icon>
|
||||||
|
<vn-icon
|
||||||
|
ng-if="::$ctrl.info"
|
||||||
|
icon="info_outline"
|
||||||
|
vn-tooltip="{{::$ctrl.info}}">
|
||||||
|
</vn-icon>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ng-transclude="append"
|
||||||
|
class="append">
|
||||||
|
</div>
|
||||||
|
<div class="icons post">
|
||||||
|
<vn-icon
|
||||||
|
icon="arrow_drop_down">
|
||||||
|
</vn-icon>
|
||||||
|
</div>
|
||||||
|
<div class="underline blur"></div>
|
||||||
|
<div class="underline focus"></div>
|
||||||
|
</div>
|
||||||
|
<div class="hint"></div>
|
||||||
|
<vn-drop-down
|
||||||
|
vn-id="drop-down"
|
||||||
|
on-select="$ctrl.onDropDownSelect(item)"
|
||||||
|
on-data-ready="$ctrl.onDataReady()"
|
||||||
|
on-close="$ctrl.focus()">
|
||||||
|
</vn-drop-down>
|
|
@ -1,5 +1,5 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Input from '../../lib/input';
|
import Field from '../field';
|
||||||
import assignProps from '../../lib/assign-props';
|
import assignProps from '../../lib/assign-props';
|
||||||
import {mergeWhere} from 'vn-loopback/util/filter';
|
import {mergeWhere} from 'vn-loopback/util/filter';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
@ -16,23 +16,18 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @event change Thrown when value is changed
|
* @event change Thrown when value is changed
|
||||||
*/
|
*/
|
||||||
export default class Autocomplete extends Input {
|
export default class Autocomplete extends Field {
|
||||||
constructor($element, $scope, $http, $transclude, $translate, $interpolate) {
|
constructor($element, $scope, $compile, $http, $transclude, $translate, $interpolate) {
|
||||||
super($element, $scope);
|
super($element, $scope, $compile);
|
||||||
this.$http = $http;
|
Object.assign(this, {
|
||||||
this.$interpolate = $interpolate;
|
$http,
|
||||||
this.$transclude = $transclude;
|
$interpolate,
|
||||||
this.$translate = $translate;
|
$transclude,
|
||||||
this._field = undefined;
|
$translate
|
||||||
|
});
|
||||||
|
|
||||||
this._selection = null;
|
this._selection = null;
|
||||||
this.readonly = true;
|
this.input = this.element.querySelector('input');
|
||||||
this.form = null;
|
|
||||||
this.input = this.element.querySelector('.mdl-textfield__input');
|
|
||||||
|
|
||||||
componentHandler.upgradeElement(
|
|
||||||
this.element.querySelector('.mdl-textfield'));
|
|
||||||
|
|
||||||
this.registerEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$postLink() {
|
$postLink() {
|
||||||
|
@ -44,12 +39,16 @@ export default class Autocomplete extends Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers all event emitters
|
* @type {any} The autocomplete value.
|
||||||
*/
|
*/
|
||||||
registerEvents() {
|
get field() {
|
||||||
this.input.addEventListener('focus', event => {
|
return super.field;
|
||||||
this.emit('focus', {event});
|
}
|
||||||
});
|
|
||||||
|
set field(value) {
|
||||||
|
super.field = value;
|
||||||
|
this.refreshSelection();
|
||||||
|
this.emit('change', {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
get model() {
|
get model() {
|
||||||
|
@ -83,20 +82,6 @@ export default class Autocomplete extends Input {
|
||||||
Object.assign(this.$.dropDown, props);
|
Object.assign(this.$.dropDown, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {any} The autocomplete value.
|
|
||||||
*/
|
|
||||||
get field() {
|
|
||||||
return this._field;
|
|
||||||
}
|
|
||||||
|
|
||||||
set field(value) {
|
|
||||||
this._field = value;
|
|
||||||
|
|
||||||
this.refreshSelection();
|
|
||||||
this.emit('change', {value});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Object} The selected data object, you can use this property
|
* @type {Object} The selected data object, you can use this property
|
||||||
* to prevent requests to display the initial value.
|
* to prevent requests to display the initial value.
|
||||||
|
@ -136,8 +121,8 @@ export default class Autocomplete extends Input {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const selection = this.fetchSelection();
|
const selection = this.fetchSelection();
|
||||||
if (!this.selection)
|
|
||||||
this.selection = selection;
|
this.selection = selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSelection() {
|
fetchSelection() {
|
||||||
|
@ -216,39 +201,21 @@ export default class Autocomplete extends Input {
|
||||||
if (this.translateFields.indexOf(this.showField) > -1)
|
if (this.translateFields.indexOf(this.showField) > -1)
|
||||||
this.input.value = this.$translate.instant(display);
|
this.input.value = this.$translate.instant(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mdlUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
mdlUpdate() {
|
|
||||||
let field = this.element.querySelector('.mdl-textfield');
|
|
||||||
let mdlField = field.MaterialTextfield;
|
|
||||||
if (mdlField) mdlField.updateClasses_();
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(value) {
|
|
||||||
this.field = value;
|
|
||||||
if (this.form) this.form.$setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onDropDownSelect(item) {
|
onDropDownSelect(item) {
|
||||||
const value = item[this.valueField];
|
const value = item[this.valueField];
|
||||||
this.selection = item;
|
this.selection = item;
|
||||||
this.setValue(value);
|
this.field = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClearClick(event) {
|
onContainerKeyDown(event) {
|
||||||
event.preventDefault();
|
if (event.defaultPrevented) return;
|
||||||
this.setValue(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyDown(event) {
|
switch (event.code) {
|
||||||
// if (event.defaultPrevented) return;
|
case 'ArrowUp':
|
||||||
|
case 'ArrowDown':
|
||||||
switch (event.keyCode) {
|
case 'Enter':
|
||||||
case 38: // Up
|
|
||||||
case 40: // Down
|
|
||||||
case 13: // Enter
|
|
||||||
this.showDropDown();
|
this.showDropDown();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -261,7 +228,8 @@ export default class Autocomplete extends Input {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseDown(event) {
|
onContainerMouseDown(event) {
|
||||||
|
if (event.defaultPrevented) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.showDropDown();
|
this.showDropDown();
|
||||||
}
|
}
|
||||||
|
@ -293,7 +261,7 @@ export default class Autocomplete extends Input {
|
||||||
|
|
||||||
showDropDown(search) {
|
showDropDown(search) {
|
||||||
this.assignDropdownProps();
|
this.assignDropdownProps();
|
||||||
this.$.dropDown.show(this.input, search);
|
this.$.dropDown.show(this.container, search);
|
||||||
}
|
}
|
||||||
|
|
||||||
get fetchFunction() {
|
get fetchFunction() {
|
||||||
|
@ -307,16 +275,12 @@ export default class Autocomplete extends Input {
|
||||||
this.refreshSelection();
|
this.refreshSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Autocomplete.$inject = ['$element', '$scope', '$http', '$transclude', '$translate', '$interpolate'];
|
Autocomplete.$inject = ['$element', '$scope', '$compile', '$http', '$transclude', '$translate', '$interpolate'];
|
||||||
|
|
||||||
ngModule.component('vnAutocomplete', {
|
ngModule.vnComponent('vnAutocomplete', {
|
||||||
template: require('./autocomplete.html'),
|
template: require('./index.html'),
|
||||||
controller: Autocomplete,
|
controller: Autocomplete,
|
||||||
bindings: {
|
bindings: {
|
||||||
label: '@',
|
|
||||||
field: '=?',
|
|
||||||
disabled: '<?',
|
|
||||||
required: '@?',
|
|
||||||
showField: '@?',
|
showField: '@?',
|
||||||
valueField: '@?',
|
valueField: '@?',
|
||||||
initialData: '<?',
|
initialData: '<?',
|
||||||
|
@ -336,8 +300,5 @@ ngModule.component('vnAutocomplete', {
|
||||||
},
|
},
|
||||||
transclude: {
|
transclude: {
|
||||||
tplItem: '?tplItem'
|
tplItem: '?tplItem'
|
||||||
},
|
|
||||||
require: {
|
|
||||||
form: '?^form'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -1,66 +1,23 @@
|
||||||
@import "effects";
|
@import "effects";
|
||||||
|
|
||||||
vn-autocomplete {
|
vn-autocomplete.vn-field {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
& > div > .mdl-textfield {
|
& > .container {
|
||||||
position: relative;
|
cursor: pointer;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
& > input {
|
& > .infix > .control {
|
||||||
cursor: pointer;
|
|
||||||
height: 30px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
|
||||||
& > .icons {
|
& > input {
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 1.3em;
|
|
||||||
height: 1em;
|
|
||||||
color: $color-font-secondary;
|
|
||||||
border-radius: .2em;
|
|
||||||
|
|
||||||
& > vn-icon {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
&:hover {
|
overflow: hidden;
|
||||||
color: $color-font;
|
text-align: left;
|
||||||
}
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:hover > .icons,
|
|
||||||
& > input:focus + .icons {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
label span:nth-child(2) {
|
|
||||||
color: $color-alert
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.vn-autocomplete {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 1em;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: auto;
|
|
||||||
max-height: 300px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
@extend %clickable;
|
|
||||||
display: block;
|
|
||||||
padding: .8em;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&.load-more {
|
|
||||||
color: $color-main;
|
|
||||||
font-family: vn-font-bold;
|
|
||||||
padding: .4em .8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,13 +12,13 @@ import './style.scss';
|
||||||
*/
|
*/
|
||||||
export default class Check extends Toggle {
|
export default class Check extends Toggle {
|
||||||
set field(value) {
|
set field(value) {
|
||||||
this._field = value;
|
super.field = value;
|
||||||
this.element.classList.toggle('checked', Boolean(value));
|
this.element.classList.toggle('checked', Boolean(value));
|
||||||
this.indeterminate = Boolean(value == null && this.tripleState);
|
this.indeterminate = Boolean(value == null && this.tripleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
get field() {
|
get field() {
|
||||||
return this._field;
|
return super.field;
|
||||||
}
|
}
|
||||||
|
|
||||||
set checked(value) {
|
set checked(value) {
|
||||||
|
@ -64,14 +64,10 @@ export default class Check extends Toggle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.component('vnCheck', {
|
ngModule.vnComponent('vnCheck', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Check,
|
controller: Check,
|
||||||
bindings: {
|
bindings: {
|
||||||
label: '@?',
|
|
||||||
field: '=?',
|
|
||||||
disabled: '<?',
|
|
||||||
checked: '<?',
|
|
||||||
tripleState: '<?',
|
tripleState: '<?',
|
||||||
indeterminate: '<?',
|
indeterminate: '<?',
|
||||||
info: '@?'
|
info: '@?'
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('Component vnCheck', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(($compile, $rootScope) => {
|
beforeEach(inject(($compile, $rootScope) => {
|
||||||
$element = $compile(`<vn-check></vn-check`)($rootScope);
|
$element = $compile(`<vn-check></vn-check>`)($rootScope);
|
||||||
$ctrl = $element.controller('vnCheck');
|
$ctrl = $element.controller('vnCheck');
|
||||||
element = $element[0];
|
element = $element[0];
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<div
|
|
||||||
class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"
|
|
||||||
ng-focus="$ctrl.hasFocus = true"
|
|
||||||
ng-blur="$ctrl.hasFocus = false"
|
|
||||||
ng-mouseenter="$ctrl.hasMouseIn = true"
|
|
||||||
ng-mouseleave="$ctrl.hasMouseIn = false">
|
|
||||||
<input type="text"
|
|
||||||
class="mdl-textfield__input"
|
|
||||||
name="{{::$ctrl.name}}"
|
|
||||||
ng-disabled="$ctrl.disabled"
|
|
||||||
rule="{{::$ctrl.rule}}"/>
|
|
||||||
<div class="mdl-chip__action">
|
|
||||||
<i
|
|
||||||
class="material-icons pointer"
|
|
||||||
ng-show="!$ctrl.disabled && $ctrl.model && ($ctrl.hasFocus || $ctrl.hasMouseIn)"
|
|
||||||
ng-click="$ctrl.onClear()">
|
|
||||||
clear
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
<label class="mdl-textfield__label" translate>{{$ctrl.label}}</label>
|
|
||||||
</div>
|
|
|
@ -1,107 +0,0 @@
|
||||||
import ngModule from '../../module';
|
|
||||||
import Component from '../../lib/component';
|
|
||||||
import {Flatpickr} from '../../vendor';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
class DatePicker extends Component {
|
|
||||||
constructor($element, $scope, $translate, $attrs) {
|
|
||||||
super($element, $scope);
|
|
||||||
this.input = $element[0].querySelector('input');
|
|
||||||
this.$translate = $translate;
|
|
||||||
this.$attrs = $attrs;
|
|
||||||
this._model = undefined;
|
|
||||||
this.dateValue = undefined;
|
|
||||||
this.hasMouseIn = false;
|
|
||||||
let locale = this.$translate.use();
|
|
||||||
this.defaultOptions = {
|
|
||||||
locale: locale,
|
|
||||||
dateFormat: locale == 'es' ? 'd-m-Y' : 'Y-m-d',
|
|
||||||
enableTime: false,
|
|
||||||
disableMobile: true,
|
|
||||||
onValueUpdate: () => this.onValueUpdate()
|
|
||||||
};
|
|
||||||
this.userOptions = {};
|
|
||||||
this._iniOptions = this.defaultOptions;
|
|
||||||
componentHandler.upgradeElement($element[0].firstChild);
|
|
||||||
this.vp = new Flatpickr(this.input, this._iniOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
onValueUpdate() {
|
|
||||||
if (this.vp.selectedDates.length) {
|
|
||||||
let date = this.vp.selectedDates[0];
|
|
||||||
let offset = date.getTimezoneOffset() * 60000;
|
|
||||||
date.setTime(date.getTime() - offset);
|
|
||||||
this._model = date;
|
|
||||||
} else
|
|
||||||
this.model = null;
|
|
||||||
this.$.$apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
set iniOptions(value) {
|
|
||||||
this.userOptions = value;
|
|
||||||
let options = Object.assign({}, this.defaultOptions, value);
|
|
||||||
this._iniOptions = options;
|
|
||||||
|
|
||||||
// TODO: When some properties change Flatpickr doesn't refresh the view
|
|
||||||
// for (let option in options)
|
|
||||||
// this.vp.set(option, options[option]);
|
|
||||||
|
|
||||||
if (this.vp) this.vp.destroy();
|
|
||||||
this.vp = new Flatpickr(this.input, this._iniOptions);
|
|
||||||
this.vp.setDate(this.dateValue);
|
|
||||||
this.mdlUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
get iniOptions() {
|
|
||||||
return this.userOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
get model() {
|
|
||||||
return this._model;
|
|
||||||
}
|
|
||||||
|
|
||||||
set model(value) {
|
|
||||||
this._model = value;
|
|
||||||
this.dateValue = value;
|
|
||||||
let date;
|
|
||||||
if (value && this.iniOptions.enableTime) {
|
|
||||||
date = new Date(value);
|
|
||||||
let offset = date.getTimezoneOffset() * 60000;
|
|
||||||
date.setTime(date.getTime() + offset);
|
|
||||||
} else
|
|
||||||
date = value;
|
|
||||||
|
|
||||||
this.vp.setDate(date);
|
|
||||||
this.mdlUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
onClear() {
|
|
||||||
this.model = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
mdlUpdate() {
|
|
||||||
let mdlField = this.element.firstChild.MaterialTextfield;
|
|
||||||
if (mdlField)
|
|
||||||
mdlField.updateClasses_();
|
|
||||||
}
|
|
||||||
|
|
||||||
$onDestroy() {
|
|
||||||
this.vp.destroy();
|
|
||||||
this.dateValue = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DatePicker.$inject = ['$element', '$scope', '$translate', '$attrs'];
|
|
||||||
|
|
||||||
ngModule.component('vnDatePicker', {
|
|
||||||
template: require('./date-picker.html'),
|
|
||||||
bindings: {
|
|
||||||
iniOptions: '<?',
|
|
||||||
model: '=',
|
|
||||||
label: '@?',
|
|
||||||
name: '@?',
|
|
||||||
disabled: '<?',
|
|
||||||
rule: '<?',
|
|
||||||
isLocale: '<?'
|
|
||||||
},
|
|
||||||
controller: DatePicker
|
|
||||||
});
|
|
|
@ -1,37 +0,0 @@
|
||||||
describe('Component vnDatePicker', () => {
|
|
||||||
let controller;
|
|
||||||
let $attrs;
|
|
||||||
let $element;
|
|
||||||
let today = new Date();
|
|
||||||
today.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
|
||||||
$translateProvider.translations('en', {});
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $translate) => {
|
|
||||||
$attrs = {};
|
|
||||||
$element = angular.element(`<vn-date-picker><div><input type="text" class="mdl-textfield__input" name="MyName" ng-disabled="$ctrl.disabled" rule=""></input></div></vn-date-picker>`);
|
|
||||||
controller = $componentController('vnDatePicker', {$element, $attrs, $translate});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('onValueUpdate() while date is selected', () => {
|
|
||||||
it(`should store the selected date in the controller`, () => {
|
|
||||||
controller.vp = {selectedDates: [today]};
|
|
||||||
controller.isLocale = true;
|
|
||||||
controller.onValueUpdate();
|
|
||||||
|
|
||||||
expect(controller._model).toEqual(today);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should format the date`, () => {
|
|
||||||
controller.vp = {selectedDates: [today], destroy: () => {}};
|
|
||||||
controller.isLocale = undefined;
|
|
||||||
controller._iniOptions.enableTime = undefined;
|
|
||||||
|
|
||||||
controller.onValueUpdate();
|
|
||||||
|
|
||||||
expect(controller._model).toEqual(today);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Field from '../field';
|
||||||
|
import {Flatpickr} from '../../vendor';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class DatePicker extends Field {
|
||||||
|
constructor($element, $scope, $compile, $translate) {
|
||||||
|
super($element, $scope, $compile);
|
||||||
|
this.$translate = $translate;
|
||||||
|
|
||||||
|
this.input = $compile(`<input type="text"></input>`)($scope)[0];
|
||||||
|
this.initPicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
get field() {
|
||||||
|
return super.field;
|
||||||
|
}
|
||||||
|
|
||||||
|
set field(value) {
|
||||||
|
super.field = value;
|
||||||
|
|
||||||
|
let date = value;
|
||||||
|
if (date && !(date instanceof Date))
|
||||||
|
date = new Date(date);
|
||||||
|
|
||||||
|
this.picker.setDate(fixDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
set options(value) {
|
||||||
|
let selectedDates = this.picker.selectedDates || [];
|
||||||
|
this._options = value;
|
||||||
|
this.initPicker();
|
||||||
|
this.picker.setDate(selectedDates[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
|
||||||
|
initPicker() {
|
||||||
|
let locale = this.$translate.use();
|
||||||
|
let format = locale == 'es' ? 'd-m-Y' : 'Y-m-d';
|
||||||
|
|
||||||
|
let options = this.options || {};
|
||||||
|
let defaultOptions = {
|
||||||
|
locale: locale,
|
||||||
|
dateFormat: format,
|
||||||
|
enableTime: false,
|
||||||
|
disableMobile: true,
|
||||||
|
onValueUpdate: () => this.onValueUpdate()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.enableTime) {
|
||||||
|
Object.assign(defaultOptions, {
|
||||||
|
dateFormat: `${format} h:i`,
|
||||||
|
time_24hr: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mergedOptions = Object.assign({},
|
||||||
|
defaultOptions,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.picker) this.picker.destroy();
|
||||||
|
this.picker = new Flatpickr(this.input, mergedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueUpdate() {
|
||||||
|
let date = null;
|
||||||
|
|
||||||
|
if (this.picker.selectedDates.length)
|
||||||
|
date = this.picker.selectedDates[0];
|
||||||
|
|
||||||
|
super.field = fixDate(date, -1);
|
||||||
|
this.$.$applyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
this.picker.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DatePicker.$inject = ['$element', '$scope', '$compile', '$translate'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnDatePicker', {
|
||||||
|
controller: DatePicker,
|
||||||
|
bindings: {
|
||||||
|
options: '<?'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function fixDate(date, mult = 1) {
|
||||||
|
if (date) {
|
||||||
|
let offset = date.getTimezoneOffset() * 60000;
|
||||||
|
date.setTime(date.getTime() + (offset * mult));
|
||||||
|
}
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
describe('Component vnDatePicker', () => {
|
||||||
|
let $filter;
|
||||||
|
let $element;
|
||||||
|
let $ctrl;
|
||||||
|
let today;
|
||||||
|
|
||||||
|
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
||||||
|
$translateProvider.translations('en', {});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => {
|
||||||
|
$filter = _$filter_;
|
||||||
|
|
||||||
|
$element = $compile(`<vn-date-picker></vn-date-picker>`)($rootScope);
|
||||||
|
$ctrl = $element.controller('vnDatePicker');
|
||||||
|
|
||||||
|
today = new Date();
|
||||||
|
today.setUTCHours(0, 0, 0, 0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
$element.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('field() setter', () => {
|
||||||
|
it(`should display the formated the date`, () => {
|
||||||
|
$ctrl.field = today;
|
||||||
|
|
||||||
|
let displayed = $filter('dateTime')(today, 'yyyy-MM-dd');
|
||||||
|
|
||||||
|
expect($ctrl.value).toEqual(displayed);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('options() setter', () => {
|
||||||
|
it(`should display the date with the new format`, () => {
|
||||||
|
$ctrl.options = {dateFormat: 'Y-m'};
|
||||||
|
$ctrl.field = today;
|
||||||
|
|
||||||
|
let displayed = $filter('dateTime')(today, 'yyyy-MM');
|
||||||
|
|
||||||
|
expect($ctrl.value).toEqual(displayed);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,24 +1,5 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-date-picker {
|
|
||||||
.mdl-chip__action {
|
|
||||||
position: absolute;
|
|
||||||
width: auto;
|
|
||||||
top: 0px;
|
|
||||||
right: -6px;
|
|
||||||
margin: 22px 0px;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
.mdl-textfield {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.material-icons {
|
|
||||||
font-size: 18px;
|
|
||||||
float: right;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-month,
|
.flatpickr-months .flatpickr-month,
|
||||||
.flatpickr-weekdays,
|
.flatpickr-weekdays,
|
||||||
span.flatpickr-weekday {
|
span.flatpickr-weekday {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
tpl-body {
|
tpl-body {
|
||||||
display: block;
|
display: block;
|
||||||
width: 20em;
|
min-width: 20em;
|
||||||
}
|
}
|
||||||
& > button.close {
|
& > button.close {
|
||||||
@extend %clickable;
|
@extend %clickable;
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
<div ng-show="$ctrl.showFilter" class="filter">
|
<div ng-show="$ctrl.showFilter" class="filter">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-id="input"
|
vn-id="input"
|
||||||
model="$ctrl.search"
|
ng-model="$ctrl.search"
|
||||||
class="search"
|
class="dense search"
|
||||||
ng-blur="$ctrl.onFocusOut()"
|
ng-blur="$ctrl.onFocusOut()"
|
||||||
label="Search">
|
placeholder="{{::'Search' | translate}}">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</div>
|
</div>
|
||||||
<div vn-id="list" class="list" tabindex="-1">
|
<div vn-id="list" class="list" tabindex="-1">
|
||||||
|
|
|
@ -106,8 +106,8 @@ export default class DropDown extends Component {
|
||||||
*/
|
*/
|
||||||
show(parent, search) {
|
show(parent, search) {
|
||||||
this._activeOption = -1;
|
this._activeOption = -1;
|
||||||
this.search = search;
|
|
||||||
this.$.popover.show(parent || this.parent);
|
this.$.popover.show(parent || this.parent);
|
||||||
|
this.search = search;
|
||||||
this.buildList();
|
this.buildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,10 +194,12 @@ export default class DropDown extends Component {
|
||||||
this.document.addEventListener('keydown', this.docKeyDownHandler);
|
this.document.addEventListener('keydown', this.docKeyDownHandler);
|
||||||
this.$.list.scrollTop = 0;
|
this.$.list.scrollTop = 0;
|
||||||
this.$.input.focus();
|
this.$.input.focus();
|
||||||
|
this.emit('open');
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
this.document.removeEventListener('keydown', this.docKeyDownHandler);
|
this.document.removeEventListener('keydown', this.docKeyDownHandler);
|
||||||
|
this.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
onClearClick() {
|
onClearClick() {
|
||||||
|
|
|
@ -13,18 +13,7 @@
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: none;
|
padding: $spacing-sm;
|
||||||
font-size: inherit;
|
|
||||||
padding: .6em;
|
|
||||||
margin: 0!important;
|
|
||||||
&.not-empty label{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
& .selected label {
|
|
||||||
font-size: inherit;
|
|
||||||
bottom: 2px;
|
|
||||||
color: $color-font-secondary;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
& > vn-icon[icon=clear] {
|
& > vn-icon[icon=clear] {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -5,20 +5,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="infix">
|
<div class="infix">
|
||||||
<div class="fix prefix"></div>
|
<div class="fix prefix"></div>
|
||||||
<div class="control">
|
<div class="control"></div>
|
||||||
<input type="text" ng-model="$ctrl.field"/>
|
|
||||||
</div>
|
|
||||||
<div class="fix suffix"></div>
|
<div class="fix suffix"></div>
|
||||||
<label>
|
<label>
|
||||||
<span translate>{{::$ctrl.label}}</span>
|
<span translate>{{::$ctrl.label}}</span>
|
||||||
<span class="required">*</span>
|
<span class="required">*</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="icons">
|
<div class="icons pre">
|
||||||
<vn-icon
|
<vn-icon
|
||||||
icon="clear"
|
icon="clear"
|
||||||
translate-attr="{title: 'Clear'}"
|
translate-attr="{title: 'Clear'}"
|
||||||
ng-click="$ctrl.onClear()">
|
ng-click="$ctrl.onClear($event)">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
<vn-icon
|
<vn-icon
|
||||||
ng-if="::$ctrl.info"
|
ng-if="::$ctrl.info"
|
||||||
|
@ -30,6 +28,8 @@
|
||||||
ng-transclude="append"
|
ng-transclude="append"
|
||||||
class="append">
|
class="append">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="icons post">
|
||||||
|
</div>
|
||||||
<div class="underline blur"></div>
|
<div class="underline blur"></div>
|
||||||
<div class="underline focus"></div>
|
<div class="underline focus"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,37 +1,63 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Component from '../../lib/component';
|
import FormInput from '../form-input';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class Field extends Component {
|
export default class Field extends FormInput {
|
||||||
constructor($element, $scope) {
|
constructor($element, $scope, $compile) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this._value = undefined;
|
this.$compile = $compile;
|
||||||
|
|
||||||
this.prefix = null;
|
this.prefix = null;
|
||||||
this.suffix = null;
|
this.suffix = null;
|
||||||
|
|
||||||
this.input = this.element.querySelector('input');
|
this.control = this.element.querySelector('.control');
|
||||||
this.classList = this.element.classList;
|
this.classList = this.element.classList;
|
||||||
this.classList.add('vn-field');
|
this.classList.add('vn-field');
|
||||||
|
this.element.addEventListener('click', e => this.onClick(e));
|
||||||
|
|
||||||
this.element.addEventListener('focusin',
|
this.container = this.element.querySelector('.container');
|
||||||
() => this.onFocus(true));
|
this.container.addEventListener('mousedown', e => this.onMouseDown(e));
|
||||||
this.element.addEventListener('focusout',
|
|
||||||
() => this.onFocus(false));
|
|
||||||
this.element.addEventListener('click',
|
|
||||||
() => this.onClick());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
|
super.$onInit();
|
||||||
|
|
||||||
if (this.info) this.classList.add('has-icons');
|
if (this.info) this.classList.add('has-icons');
|
||||||
|
|
||||||
|
this.input.addEventListener('focus', () => this.onFocus(true));
|
||||||
|
this.input.addEventListener('blur', () => this.onFocus(false));
|
||||||
|
this.input.addEventListener('change', e => {
|
||||||
|
this.emit('change', {event: e});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set field(value) {
|
set field(value) {
|
||||||
this._field = value;
|
if (value === this.field) return;
|
||||||
|
super.field = value;
|
||||||
this.classList.toggle('not-empty', value != null && value !== '');
|
this.classList.toggle('not-empty', value != null && value !== '');
|
||||||
|
this.validateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
get field() {
|
get field() {
|
||||||
return this._field;
|
return super.field;
|
||||||
|
}
|
||||||
|
|
||||||
|
set input(value) {
|
||||||
|
if (this.input) this.control.removeChild(this.input);
|
||||||
|
this._input = value;
|
||||||
|
this.control.appendChild(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get input() {
|
||||||
|
return this._input;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value) {
|
||||||
|
this.field = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.input.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set type(value) {
|
set type(value) {
|
||||||
|
@ -42,6 +68,30 @@ export default class Field extends Component {
|
||||||
return this.input.type;
|
return this.input.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set name(value) {
|
||||||
|
this.input.name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.input.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
set placeholder(value) {
|
||||||
|
this.input.placeholder = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get placeholder() {
|
||||||
|
return this.input.placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
set tabIndex(value) {
|
||||||
|
this.input.tabIndex = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get tabIndex() {
|
||||||
|
return this.input.tabIndex;
|
||||||
|
}
|
||||||
|
|
||||||
set disabled(value) {
|
set disabled(value) {
|
||||||
this._disabled = boolTag(value);
|
this._disabled = boolTag(value);
|
||||||
this.input.disabled = this._disabled;
|
this.input.disabled = this._disabled;
|
||||||
|
@ -100,20 +150,28 @@ export default class Field extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
set error(value) {
|
set error(value) {
|
||||||
|
if (value === this.error) return;
|
||||||
this._error = value;
|
this._error = value;
|
||||||
this.refreshHint();
|
this.refreshHint();
|
||||||
this.classList.toggle('invalid', Boolean(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get error() {
|
get error() {
|
||||||
return this._error;
|
return this._error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get shownError() {
|
||||||
|
return this.error || this.inputError || null;
|
||||||
|
}
|
||||||
|
|
||||||
refreshHint() {
|
refreshHint() {
|
||||||
let hint = this.error || this.hint || '';
|
let error = this.shownError;
|
||||||
|
let hint = error || this.hint;
|
||||||
|
|
||||||
let hintEl = this.element.querySelector('.hint');
|
let hintEl = this.element.querySelector('.hint');
|
||||||
hintEl.innerText = hint;
|
hintEl.innerText = hint || '';
|
||||||
hintEl.classList.toggle('filled', Boolean(hint));
|
hintEl.classList.toggle('filled', Boolean(hint));
|
||||||
|
|
||||||
|
this.classList.toggle('invalid', Boolean(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshFix(selector, text) {
|
refreshFix(selector, text) {
|
||||||
|
@ -123,16 +181,27 @@ export default class Field extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
|
if (event.defaultPrevented) return;
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.input !== document.activeElement)
|
if (this.input !== document.activeElement)
|
||||||
this.input.focus();
|
this.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseDown(event) {
|
||||||
|
if (event.target == this.input) return;
|
||||||
|
event.preventDefault();
|
||||||
|
this.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocus(hasFocus) {
|
onFocus(hasFocus) {
|
||||||
this.classList.toggle('focused', hasFocus);
|
this.classList.toggle('focused', hasFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClear() {
|
onClear(event) {
|
||||||
this.input.value = '';
|
if (event.defaultPrevented) return;
|
||||||
|
event.preventDefault();
|
||||||
|
this.field = null;
|
||||||
this.input.dispatchEvent(new Event('change'));
|
this.input.dispatchEvent(new Event('change'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,10 +212,28 @@ export default class Field extends Component {
|
||||||
select() {
|
select() {
|
||||||
this.input.select();
|
this.input.select();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Field.$inject = ['$element', '$scope'];
|
|
||||||
|
|
||||||
ngModule.component('vnField', {
|
buildInput(type) {
|
||||||
|
let template = `<input type="${type}" ng-model="$ctrl.field"></input>`;
|
||||||
|
this.input = this.$compile(template)(this.$)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If input value is invalid, sets the error message as hint.
|
||||||
|
*/
|
||||||
|
validateValue() {
|
||||||
|
let error = this.input.checkValidity()
|
||||||
|
? null
|
||||||
|
: this.input.validationMessage;
|
||||||
|
|
||||||
|
if (error === this.inputError) return;
|
||||||
|
this.inputError = error;
|
||||||
|
this.refreshHint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Field.$inject = ['$element', '$scope', '$compile'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnField', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
transclude: {
|
transclude: {
|
||||||
prepend: '?prepend',
|
prepend: '?prepend',
|
||||||
|
@ -154,18 +241,17 @@ ngModule.component('vnField', {
|
||||||
},
|
},
|
||||||
controller: Field,
|
controller: Field,
|
||||||
bindings: {
|
bindings: {
|
||||||
field: '=?',
|
|
||||||
label: '@?',
|
|
||||||
name: '@?',
|
|
||||||
type: '@?',
|
type: '@?',
|
||||||
|
placeholder: '@?',
|
||||||
|
value: '=?',
|
||||||
info: '@?',
|
info: '@?',
|
||||||
disabled: '@?',
|
required: '<?',
|
||||||
readonly: '@?',
|
|
||||||
required: '@?',
|
|
||||||
prefix: '@?',
|
prefix: '@?',
|
||||||
suffix: '@?',
|
suffix: '@?',
|
||||||
hint: '@?',
|
hint: '@?',
|
||||||
error: '<?'
|
error: '<?',
|
||||||
|
tabIndex: '<?',
|
||||||
|
rule: '@?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 56px;
|
|
||||||
|
|
||||||
& > .infix {
|
& > .infix {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -50,36 +49,40 @@
|
||||||
& > .control {
|
& > .control {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
}
|
|
||||||
& > .control > input {
|
|
||||||
padding-top: 24px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
height: inherit;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
display: block;
|
|
||||||
font-size: $input-font-size;
|
|
||||||
width: 100%;
|
|
||||||
background: 0;
|
|
||||||
color: inherit;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&[type=time],
|
& > * {
|
||||||
&[type=date] {
|
padding-top: 24px;
|
||||||
clip-path: inset(0 20px 0 0);
|
padding-bottom: 8px;
|
||||||
}
|
height: inherit;
|
||||||
&[type=number] {
|
outline: none;
|
||||||
-moz-appearance: textfield;
|
border: none;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
&::-webkit-outer-spin-button,
|
display: block;
|
||||||
&::-webkit-inner-spin-button {
|
font-size: $input-font-size;
|
||||||
-webkit-appearance: none;
|
width: 100%;
|
||||||
margin: 0;
|
background: 0;
|
||||||
|
color: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
&[type=time],
|
||||||
|
&[type=date] {
|
||||||
|
clip-path: inset(0 20px 0 0);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease-in-out;
|
||||||
|
}
|
||||||
|
&[type=number] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
|
||||||
|
&::-webkit-outer-spin-button,
|
||||||
|
&::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:invalid {
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
&:invalid {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +105,16 @@
|
||||||
& > .prepend > prepend {
|
& > .prepend > prepend {
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
}
|
}
|
||||||
& > .append > append {
|
& > .icons {
|
||||||
padding-left: 12px;
|
&.pre {
|
||||||
}
|
padding-left: 12px;
|
||||||
& > .icons > vn-icon[icon=clear] {
|
}
|
||||||
display: none;
|
& > vn-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
& > vn-icon[icon=clear] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
& > .underline {
|
& > .underline {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -131,28 +138,56 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.not-empty > .container,
|
&.dense {
|
||||||
&.focused > .container {
|
& > .hint {
|
||||||
& > .infix {
|
display: none;
|
||||||
& > .fix {
|
}
|
||||||
opacity: 1;
|
& > .container > .infix {
|
||||||
|
& > label {
|
||||||
|
top: 8px;
|
||||||
}
|
}
|
||||||
|
& > .control > * {
|
||||||
|
padding-top: 8px;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.not-empty,
|
||||||
|
&.focused,
|
||||||
|
&.invalid {
|
||||||
|
& > .container > .infix > label {
|
||||||
|
top: 0;
|
||||||
|
line-height: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.not-empty,
|
||||||
|
&.focused,
|
||||||
|
&.invalid {
|
||||||
|
& > .container > .infix {
|
||||||
& > label {
|
& > label {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
color: $color-main;
|
color: $color-main;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
& > .control > * {
|
||||||
|
&[type=time],
|
||||||
|
&[type=date] {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.has-icons,
|
&.has-icons,
|
||||||
&.not-empty:hover,
|
&.not-empty:hover,
|
||||||
&.not-empty.focused {
|
&.not-empty.focused {
|
||||||
& > .container > .append > append {
|
& > .container {
|
||||||
padding-left: 0;
|
& > .append > append {
|
||||||
}
|
padding-left: 0;
|
||||||
& > .container > .icons {
|
}
|
||||||
padding-left: 12px;
|
& > .icons.pre {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:not(.disabled):not(.readonly) {
|
&:not(.disabled):not(.readonly) {
|
||||||
|
@ -170,14 +205,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:not(.not-empty):not(.focused) > .container > .infix > .control > input {
|
|
||||||
&[type=time],
|
|
||||||
&[type=date] {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.readonly > .container {
|
&.readonly > .container {
|
||||||
& > .infix > .control > input {
|
& > .infix > .control > * {
|
||||||
caret-color: transparent;
|
caret-color: transparent;
|
||||||
}
|
}
|
||||||
& > .underline.blur {
|
& > .underline.blur {
|
||||||
|
@ -186,11 +215,11 @@
|
||||||
}
|
}
|
||||||
& > .hint {
|
& > .hint {
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
padding-top: 8px;
|
padding: 4px 0;
|
||||||
height: 20px;
|
height: 12px;
|
||||||
color: rgba(0, 0, 0, .4);
|
color: rgba(0, 0, 0, .4);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
transform: translateY(-28px);
|
transform: translateY(-12px);
|
||||||
transition-property: opacity, transform, color;
|
transition-property: opacity, transform, color;
|
||||||
transition-duration: 200ms;
|
transition-duration: 200ms;
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
|
@ -220,8 +249,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vn-table {
|
|
||||||
.vn-field {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Component from '../../lib/component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base component for form inputs.
|
||||||
|
*
|
||||||
|
* @property {String} label Label to display along the component
|
||||||
|
* @property {any} field The value with which the element is linked
|
||||||
|
* @property {Boolean} disabled Put component in disabled mode
|
||||||
|
*/
|
||||||
|
export default class FormInput extends Component {
|
||||||
|
$onInit() {
|
||||||
|
// XXX: Compatibility with old inputs
|
||||||
|
let attrs = this.$element[0].attributes;
|
||||||
|
if (!this.name && attrs['ng-model']) {
|
||||||
|
let split = attrs['ng-model'].nodeValue.split('.');
|
||||||
|
this.name = split[split.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.ngModel) return;
|
||||||
|
this.ngModel.$render = () => {
|
||||||
|
this.field = this.ngModel.$viewValue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
set field(value) {
|
||||||
|
this._field = value;
|
||||||
|
|
||||||
|
if (this.ngModel)
|
||||||
|
this.ngModel.$setViewValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get field() {
|
||||||
|
return this._field;
|
||||||
|
}
|
||||||
|
|
||||||
|
set name(value) {
|
||||||
|
this.element.setAttribute('name', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.element.getAttribute('name');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnFormInput', {
|
||||||
|
controller: FormInput,
|
||||||
|
bindings: {
|
||||||
|
label: '@?',
|
||||||
|
field: '=?',
|
||||||
|
name: '@?',
|
||||||
|
disabled: '<?',
|
||||||
|
readonly: '<?'
|
||||||
|
},
|
||||||
|
require: {
|
||||||
|
ngModel: '?ngModel'
|
||||||
|
}
|
||||||
|
});
|
|
@ -3,23 +3,28 @@ import './style.scss';
|
||||||
|
|
||||||
export default class IconButton {
|
export default class IconButton {
|
||||||
constructor($element) {
|
constructor($element) {
|
||||||
if ($element[0].getAttribute('tabindex') == null)
|
this.element = $element[0];
|
||||||
$element[0].tabIndex = 0;
|
|
||||||
|
|
||||||
$element.on('keyup', event => this.onKeyDown(event, $element));
|
if (this.element.getAttribute('tabindex') == null)
|
||||||
let button = $element[0].querySelector('button');
|
this.element.tabIndex = 0;
|
||||||
$element[0].addEventListener('click', event => {
|
|
||||||
if (this.disabled || button.disabled)
|
this.element.addEventListener('keyup', e => this.onKeyup(e));
|
||||||
event.stopImmediatePropagation();
|
this.element.addEventListener('click', e => this.onClick(e));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(event, $element) {
|
onKeyup(event) {
|
||||||
|
if (event.code == 'Space')
|
||||||
|
this.onClick(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(event) {
|
||||||
if (event.defaultPrevented) return;
|
if (event.defaultPrevented) return;
|
||||||
if (event.keyCode == 13) {
|
event.preventDefault();
|
||||||
event.preventDefault();
|
|
||||||
$element.triggerHandler('click');
|
// FIXME: Don't use Event.stopPropagation()
|
||||||
}
|
let button = this.element.querySelector('button');
|
||||||
|
if (this.disabled || button.disabled)
|
||||||
|
event.stopImmediatePropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
@import "variables";
|
@import "effects";
|
||||||
|
|
||||||
vn-icon-button {
|
vn-icon-button {
|
||||||
|
@extend %clickable-light;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
color: $color-main;
|
color: $color-main;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
font-size: 18pt;
|
font-size: 18pt;
|
||||||
padding: .25em;
|
padding: .25em;
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,10 @@ import './tooltip/tooltip';
|
||||||
import './icon-menu/icon-menu';
|
import './icon-menu/icon-menu';
|
||||||
import './button-menu/button-menu';
|
import './button-menu/button-menu';
|
||||||
import './popover/popover';
|
import './popover/popover';
|
||||||
import './autocomplete/autocomplete';
|
|
||||||
import './drop-down/drop-down';
|
import './drop-down/drop-down';
|
||||||
import './menu/menu';
|
import './menu/menu';
|
||||||
import './multi-check/multi-check';
|
import './multi-check/multi-check';
|
||||||
import './date-picker/date-picker';
|
|
||||||
import './button/button';
|
import './button/button';
|
||||||
import './textarea/textarea';
|
|
||||||
import './icon-button/icon-button';
|
import './icon-button/icon-button';
|
||||||
import './submit/submit';
|
import './submit/submit';
|
||||||
import './card/card';
|
import './card/card';
|
||||||
|
@ -29,20 +26,22 @@ import './label-value/label-value';
|
||||||
import './pagination/pagination';
|
import './pagination/pagination';
|
||||||
import './searchbar/searchbar';
|
import './searchbar/searchbar';
|
||||||
import './scroll-up/scroll-up';
|
import './scroll-up/scroll-up';
|
||||||
import './input-range';
|
import './autocomplete';
|
||||||
import './calendar';
|
import './calendar';
|
||||||
import './check';
|
import './check';
|
||||||
import './chip';
|
import './chip';
|
||||||
import './color-legend';
|
import './color-legend';
|
||||||
import './data-viewer';
|
import './data-viewer';
|
||||||
|
import './date-picker';
|
||||||
import './field';
|
import './field';
|
||||||
import './input-number';
|
import './input-number';
|
||||||
|
import './input-range';
|
||||||
import './input-time';
|
import './input-time';
|
||||||
import './input-file';
|
import './input-file';
|
||||||
import './list';
|
import './list';
|
||||||
import './radio';
|
import './radio';
|
||||||
import './table';
|
import './table';
|
||||||
import './td-editable';
|
import './td-editable';
|
||||||
|
import './textarea';
|
||||||
import './th';
|
import './th';
|
||||||
import './treeview';
|
import './treeview';
|
||||||
import './treeview/child';
|
|
||||||
|
|
|
@ -3,15 +3,13 @@ import Input from '../../lib/input';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class InputFile extends Input {
|
export default class InputFile extends Input {
|
||||||
constructor($element, $scope, $attrs, vnTemplate) {
|
constructor($element, $scope) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this.element = $element[0];
|
this.element = $element[0];
|
||||||
this.hasFocus = false;
|
this.hasFocus = false;
|
||||||
this._multiple = false;
|
this._multiple = false;
|
||||||
this._value = 'Select a file';
|
this._value = 'Select a file';
|
||||||
|
|
||||||
vnTemplate.normalizeInputAttrs($attrs);
|
|
||||||
|
|
||||||
this.registerEvents();
|
this.registerEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ export default class InputFile extends Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputFile.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
|
InputFile.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
ngModule.component('vnInputFile', {
|
ngModule.component('vnInputFile', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -14,7 +14,7 @@ describe('Component vnInputFile', () => {
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
$attrs = {field: '$ctrl.dms.file'};
|
$attrs = {field: '$ctrl.dms.file'};
|
||||||
$element = angular.element('<vn-input-file label="File" field="$ctrl.dms.file"><input type="file"><div class="infix"><div class="rightIcons"></div></vn-input-file>');
|
$element = angular.element('<vn-input-file label="File" ng-model="$ctrl.dms.file"><input type="file"><div class="infix"><div class="rightIcons"></div></vn-input-file>');
|
||||||
controller = $componentController('vnInputFile', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
|
controller = $componentController('vnInputFile', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
|
||||||
controller.input = $element[0].querySelector('input');
|
controller.input = $element[0].querySelector('input');
|
||||||
controller.validate = () => {};
|
controller.validate = () => {};
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
@import '../textfield/style.scss';
|
|
||||||
|
|
||||||
vn-input-file {
|
vn-input-file {
|
||||||
@extend vn-textfield;
|
|
||||||
.value {
|
.value {
|
||||||
color: $color-font-secondary;
|
color: $color-font-secondary;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -12,4 +10,156 @@ vn-input-file {
|
||||||
input {
|
input {
|
||||||
display: none !important
|
display: none !important
|
||||||
}
|
}
|
||||||
|
margin: 20px 0;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
& > .container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& > .textField {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftIcons, .rightIcons, .suffix {
|
||||||
|
display: flex;
|
||||||
|
color: $color-font-secondary;
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: 20px !important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suffix vn-icon-button {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
t-left-icons {
|
||||||
|
padding-right: 0.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
t-right-icons {
|
||||||
|
padding-left: 0.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.infix {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
flex: auto;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
i.clear {
|
||||||
|
visibility: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover i.clear {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
i.visible {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 4px 0!important;
|
||||||
|
pointer-events: none;
|
||||||
|
color: $color-font-secondary;
|
||||||
|
transition-duration: .2s;
|
||||||
|
transition-timing-function: cubic-bezier(.4,0,.2,1);
|
||||||
|
}
|
||||||
|
&.not-empty label{
|
||||||
|
bottom: 24px;
|
||||||
|
color: $color-main;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
font-family: "Helvetica", "Arial", sans-serif;
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100%;
|
||||||
|
background: 0 0;
|
||||||
|
color: inherit;
|
||||||
|
padding: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 0!important;
|
||||||
|
|
||||||
|
&[type=number] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
&::-webkit-outer-spin-button,
|
||||||
|
&::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:invalid {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.underline {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
height: 1px;
|
||||||
|
content: ' ';
|
||||||
|
pointer-events: none;
|
||||||
|
width: 100%;
|
||||||
|
background-color: $color-input-underline;
|
||||||
|
}
|
||||||
|
.selected.underline {
|
||||||
|
background-color: $color-main;
|
||||||
|
height: 2px;
|
||||||
|
left: 50%;
|
||||||
|
width: 0px !important;
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
transition-timing-function: cubic-bezier(.4,0,.2,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.selected {
|
||||||
|
&.container{
|
||||||
|
border-bottom: 0px;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
bottom: 24px;
|
||||||
|
color: $color-main;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.selected.underline{
|
||||||
|
left: 0;
|
||||||
|
width: 100%!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > div.container > div.textField > div.infix.invalid {
|
||||||
|
@extend div.selected;
|
||||||
|
|
||||||
|
& > span.mdl-textfield__error {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
& > label {
|
||||||
|
color: #d50000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.infix.invalid + .underline {
|
||||||
|
background-color: #d50000;
|
||||||
|
}
|
||||||
|
|
||||||
|
label span:nth-child(2) {
|
||||||
|
color: $color-alert
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,48 +1,48 @@
|
||||||
<div class="container"
|
<div class="container">
|
||||||
ng-class="{selected: $ctrl.hasFocus}">
|
<div
|
||||||
<div class="textField">
|
ng-transclude="prepend"
|
||||||
<div class="leftIcons">
|
class="prepend">
|
||||||
</div>
|
|
||||||
<div class="infix">
|
|
||||||
<input
|
|
||||||
class="mdl-textfield__input"
|
|
||||||
type="number"
|
|
||||||
name="{{::$ctrl.name}}"
|
|
||||||
ng-model="$ctrl.value"
|
|
||||||
vn-validation="{{$ctrl.rule}}"
|
|
||||||
ng-disabled="$ctrl.disabled"
|
|
||||||
ng-readonly="$ctrl.readonly"
|
|
||||||
ng-focus="$ctrl.hasFocus = true"
|
|
||||||
ng-blur="$ctrl.hasFocus = false"
|
|
||||||
tabindex="{{$ctrl.input.tabindex}}"/>
|
|
||||||
<label class="label">
|
|
||||||
<span translate>{{::$ctrl.label}}</span>
|
|
||||||
<span translate ng-show="::$ctrl.required">*</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="underline"></div>
|
|
||||||
<div class="selected underline"></div>
|
|
||||||
<div class="suffix">
|
|
||||||
<vn-icon-button
|
|
||||||
ng-if="$ctrl.displayControls"
|
|
||||||
icon="remove"
|
|
||||||
ng-click="$ctrl.stepDown()"
|
|
||||||
tabindex="-1"
|
|
||||||
translate-attr="{title: 'Remove'}">
|
|
||||||
</vn-icon-button>
|
|
||||||
<vn-icon-button
|
|
||||||
ng-if="$ctrl.displayControls"
|
|
||||||
icon="add"
|
|
||||||
ng-click="$ctrl.stepUp()"
|
|
||||||
tabindex="-1"
|
|
||||||
translate-attr="{title: 'Add'}">
|
|
||||||
</vn-icon-button>
|
|
||||||
<i class="material-icons"
|
|
||||||
ng-if="::$ctrl.hasInfo"
|
|
||||||
vn-tooltip="{{::$ctrl.info}}">
|
|
||||||
info_outline
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
<div class="rightIcons"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="infix">
|
||||||
|
<div class="fix prefix"></div>
|
||||||
|
<div class="control"></div>
|
||||||
|
<div class="fix suffix"></div>
|
||||||
|
<label>
|
||||||
|
<span translate>{{::$ctrl.label}}</span>
|
||||||
|
<span class="required">*</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="icons pre">
|
||||||
|
<vn-icon
|
||||||
|
icon="clear"
|
||||||
|
translate-attr="{title: 'Clear'}"
|
||||||
|
ng-click="$ctrl.onClear($event)">
|
||||||
|
</vn-icon>
|
||||||
|
<vn-icon
|
||||||
|
ng-if="::$ctrl.info"
|
||||||
|
icon="info_outline"
|
||||||
|
vn-tooltip="{{::$ctrl.info}}">
|
||||||
|
</vn-icon>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ng-transclude="append"
|
||||||
|
class="append">
|
||||||
|
</div>
|
||||||
|
<div class="icons post">
|
||||||
|
<vn-icon
|
||||||
|
ng-if="$ctrl.displayControls"
|
||||||
|
icon="remove"
|
||||||
|
ng-click="$ctrl.onStep($event, 'down')"
|
||||||
|
translate-attr="{title: 'Remove'}">
|
||||||
|
</vn-icon>
|
||||||
|
<vn-icon
|
||||||
|
ng-if="$ctrl.displayControls"
|
||||||
|
icon="add"
|
||||||
|
ng-click="$ctrl.onStep($event, 'up')"
|
||||||
|
translate-attr="{title: 'Add'}">
|
||||||
|
</vn-icon>
|
||||||
|
</div>
|
||||||
|
<div class="underline blur"></div>
|
||||||
|
<div class="underline focus"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="hint"></div>
|
|
@ -1,48 +1,10 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Input from '../../lib/input';
|
import Field from '../field';
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default class InputNumber extends Input {
|
export default class InputNumber extends Field {
|
||||||
constructor($element, $scope, $attrs, vnTemplate) {
|
constructor($element, $scope, $compile) {
|
||||||
super($element, $scope);
|
super($element, $scope, $compile);
|
||||||
this.displayControls = false;
|
this.buildInput('number');
|
||||||
this.hasFocus = false;
|
|
||||||
|
|
||||||
vnTemplate.normalizeInputAttrs($attrs);
|
|
||||||
this.registerEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers all event emitters
|
|
||||||
*/
|
|
||||||
registerEvents() {
|
|
||||||
this.input.addEventListener('change', event => {
|
|
||||||
this.validateValue();
|
|
||||||
this.emit('change', {event});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets current value
|
|
||||||
*/
|
|
||||||
get value() {
|
|
||||||
return this._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets input value
|
|
||||||
*
|
|
||||||
* @param {Number} value - Value
|
|
||||||
*/
|
|
||||||
set value(value) {
|
|
||||||
this.hasValue = !(value === null || value === undefined || value === '');
|
|
||||||
|
|
||||||
if (!this.hasOwnProperty('_value') && this.hasValue || value === '')
|
|
||||||
this.input.value = value;
|
|
||||||
|
|
||||||
this._value = value;
|
|
||||||
this.element.classList.toggle('not-empty', this.hasValue);
|
|
||||||
this.validateValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +20,8 @@ export default class InputNumber extends Input {
|
||||||
* @param {Number} value - Value
|
* @param {Number} value - Value
|
||||||
*/
|
*/
|
||||||
set max(value) {
|
set max(value) {
|
||||||
if (value) this.input.max = value;
|
this.input.max = value;
|
||||||
|
this.validateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,7 +37,8 @@ export default class InputNumber extends Input {
|
||||||
* @param {Number} value - Value
|
* @param {Number} value - Value
|
||||||
*/
|
*/
|
||||||
set min(value) {
|
set min(value) {
|
||||||
if (value) this.input.min = value;
|
this.input.min = value;
|
||||||
|
this.validateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,47 +54,38 @@ export default class InputNumber extends Input {
|
||||||
* @param {Number} value - Value
|
* @param {Number} value - Value
|
||||||
*/
|
*/
|
||||||
set step(value) {
|
set step(value) {
|
||||||
if (value) this.input.step = value;
|
this.input.step = value;
|
||||||
|
this.validateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Increases the input value
|
|
||||||
*/
|
|
||||||
stepUp() {
|
stepUp() {
|
||||||
this.input.stepUp();
|
this.input.stepUp();
|
||||||
this.input.dispatchEvent(new Event('change'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Decreases the input value
|
|
||||||
*/
|
|
||||||
stepDown() {
|
stepDown() {
|
||||||
this.input.stepDown();
|
this.input.stepDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
onStep(event, way) {
|
||||||
|
if (event.defaultPrevented) return;
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (way == 'up')
|
||||||
|
this.stepUp();
|
||||||
|
else
|
||||||
|
this.stepDown();
|
||||||
|
|
||||||
this.input.dispatchEvent(new Event('change'));
|
this.input.dispatchEvent(new Event('change'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputNumber.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
|
|
||||||
|
|
||||||
ngModule.component('vnInputNumber', {
|
ngModule.vnComponent('vnInputNumber', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: InputNumber,
|
controller: InputNumber,
|
||||||
transclude: {
|
|
||||||
leftIcons: '?tLeftIcons',
|
|
||||||
rightIcons: '?tRightIcons'
|
|
||||||
},
|
|
||||||
bindings: {
|
bindings: {
|
||||||
label: '@?',
|
|
||||||
name: '@?',
|
|
||||||
disabled: '<?',
|
|
||||||
required: '@?',
|
|
||||||
min: '<?',
|
min: '<?',
|
||||||
max: '<?',
|
max: '<?',
|
||||||
step: '<?',
|
step: '<?',
|
||||||
displayControls: '<?',
|
displayControls: '<?'
|
||||||
rule: '@?',
|
|
||||||
value: '=model',
|
|
||||||
validate: '&',
|
|
||||||
onChange: '&',
|
|
||||||
onClear: '&'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,68 +1,69 @@
|
||||||
import './index.js';
|
import './index.js';
|
||||||
|
|
||||||
describe('Component vnInputNumber', () => {
|
describe('Component vnInputNumber', () => {
|
||||||
let $scope;
|
|
||||||
let $attrs;
|
|
||||||
let $timeout;
|
|
||||||
let $element;
|
let $element;
|
||||||
let controller;
|
let $ctrl;
|
||||||
|
|
||||||
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
||||||
$translateProvider.translations('en', {});
|
$translateProvider.translations('en', {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
beforeEach(angular.mock.inject(($compile, $rootScope) => {
|
||||||
$scope = $rootScope.$new();
|
$element = $compile(`<vn-input-number></vn-input-number>`)($rootScope);
|
||||||
$attrs = {field: '$ctrl.client.socialName'};
|
$ctrl = $element.controller('vnInputNumber');
|
||||||
$element = angular.element('<vn-input-number label="SocialName" field="$ctrl.client.socialName"><input type="number"><div class="infix"><div class="rightIcons"></div></vn-input-number>');
|
|
||||||
controller = $componentController('vnInputNumber', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
|
|
||||||
controller.input = $element[0].querySelector('input');
|
|
||||||
controller.validate = () => {};
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('value() setter', () => {
|
afterEach(() => {
|
||||||
it(`should set a value, add the class 'not-empty' and then call validateValue() method`, () => {
|
$element.remove();
|
||||||
spyOn(controller, 'validateValue');
|
});
|
||||||
|
|
||||||
controller.value = 10;
|
describe('min() setter', () => {
|
||||||
|
it(`should set error property when value is lower than min`, () => {
|
||||||
|
$ctrl.field = -1;
|
||||||
|
$ctrl.min = 0;
|
||||||
|
|
||||||
let classes = controller.element.classList.toString();
|
// FIXME: Input validation doesn't work with Jest?
|
||||||
|
// expect($ctrl.shownError).toContain('Please select a value that is no less than 0');
|
||||||
expect(classes).toContain('not-empty');
|
expect($ctrl.shownError).toBeNull();
|
||||||
expect(controller.validateValue).toHaveBeenCalledWith();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should set an empty value, remove the class 'not-empty' and then call validateValue() method`, () => {
|
it(`should unset error property when value is upper than min`, () => {
|
||||||
spyOn(controller, 'validateValue');
|
$ctrl.field = 1;
|
||||||
|
$ctrl.min = 0;
|
||||||
|
|
||||||
controller.value = null;
|
expect($ctrl.shownError).toBeNull();
|
||||||
|
|
||||||
let classes = controller.element.classList.toString();
|
|
||||||
|
|
||||||
expect(classes).not.toContain('not-empty');
|
|
||||||
expect(controller.validateValue).toHaveBeenCalledWith();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateValue()', () => {
|
describe('max() setter', () => {
|
||||||
it(`should call hasValidValue() and not add the class invalid and validated`, () => {
|
it(`should set error property when value is upper than max`, () => {
|
||||||
controller.input.min = 0;
|
$ctrl.field = 1;
|
||||||
controller.input.value = 10;
|
$ctrl.max = 0;
|
||||||
|
|
||||||
controller.validateValue();
|
// FIXME: Input validation doesn't work with Jest?
|
||||||
let classes = controller.element.querySelector('.infix').classList.toString();
|
// expect($ctrl.shownError).toContain('Please select a value that is no more than 0');
|
||||||
|
expect($ctrl.shownError).toBeNull();
|
||||||
expect(classes).not.toContain('validated invalid');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should call hasValidValue() and add the class invalid and validated`, () => {
|
// FIXME: Input validation doesn't work with Jest?
|
||||||
controller.input.min = 0;
|
it(`should unset error property when value is lower than max`, () => {
|
||||||
controller.input.value = -10;
|
$ctrl.field = -1;
|
||||||
|
$ctrl.min = 0;
|
||||||
|
|
||||||
controller.validateValue();
|
expect($ctrl.shownError).toBeNull();
|
||||||
let classes = controller.element.querySelector('.infix').classList.toString();
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(classes).toContain('validated invalid');
|
describe('step() setter', () => {
|
||||||
|
it(`should increase value when add icon is clicked`, () => {
|
||||||
|
$ctrl.step = 1;
|
||||||
|
$ctrl.field = 1;
|
||||||
|
|
||||||
|
// FIXME: Doesn't work with Jest?
|
||||||
|
// $ctrl.stepUp();
|
||||||
|
// $element[0].querySelector('vn-icon[icon=add]').click();
|
||||||
|
|
||||||
|
expect($ctrl.field).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
@import '../textfield/style.scss';
|
|
||||||
|
|
||||||
vn-input-number {
|
|
||||||
@extend vn-textfield;
|
|
||||||
|
|
||||||
vn-icon[icon=add],
|
|
||||||
vn-icon[icon=remove] {
|
|
||||||
&:not(:hover){
|
|
||||||
color: $color-font-secondary;
|
|
||||||
}
|
|
||||||
i {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
<div class="container"
|
|
||||||
ng-class="{selected: $ctrl.hasFocus}">
|
|
||||||
<div class="textField">
|
|
||||||
<div class="leftIcons" ng-transclude="leftIcons"></div>
|
|
||||||
<div class="infix">
|
|
||||||
<input
|
|
||||||
class="mdl-textfield__input"
|
|
||||||
type="time"
|
|
||||||
ng-disabled="$ctrl.disabled"
|
|
||||||
ng-readonly="$ctrl.readonly"
|
|
||||||
ng-focus="$ctrl.hasFocus = true"
|
|
||||||
ng-blur="$ctrl.hasFocus = false"/>
|
|
||||||
<label class="label" translate>{{::$ctrl.label}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="underline"></div>
|
|
||||||
<div class="selected underline"></div>
|
|
||||||
<div class="suffix">
|
|
||||||
<i class="material-icons"
|
|
||||||
ng-if="$ctrl.hasInfo"
|
|
||||||
vn-tooltip="{{$ctrl.info}}">
|
|
||||||
info_outline
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
<div class="rightIcons" ng-transclude="rightIcons"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,80 +1,41 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Input from '../../lib/input';
|
import Field from '../field';
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default class InputTime extends Input {
|
export default class InputTime extends Field {
|
||||||
constructor($element, $scope, $filter) {
|
constructor($element, $scope, $compile, $filter) {
|
||||||
super($element, $scope);
|
super($element, $scope, $compile);
|
||||||
this.$filter = $filter;
|
this.$filter = $filter;
|
||||||
|
|
||||||
this.registerEvents();
|
this.input = $compile(`<input type="time"></input>`)($scope)[0];
|
||||||
|
this.input.addEventListener('change', () => this.onValueUpdate());
|
||||||
}
|
}
|
||||||
|
|
||||||
registerEvents() {
|
get field() {
|
||||||
this.input.addEventListener('change', event => {
|
return super.field;
|
||||||
this.onTimeChange();
|
|
||||||
this.emit('change', {event});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.input.addEventListener('focus', event => {
|
|
||||||
this.emit('focus', {event});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
set field(value) {
|
||||||
* Gets current value
|
|
||||||
*/
|
|
||||||
get value() {
|
|
||||||
return this._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets input value
|
|
||||||
*
|
|
||||||
* @param {Number} value - Value
|
|
||||||
*/
|
|
||||||
set value(value) {
|
|
||||||
this.updateValue(value);
|
|
||||||
this.input.value = this.$filter('dateTime')(value, 'HH:mm');
|
this.input.value = this.$filter('dateTime')(value, 'HH:mm');
|
||||||
|
super.field = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValue(value) {
|
onValueUpdate() {
|
||||||
this._value = value;
|
|
||||||
this.element.classList.toggle('not-empty', value != null);
|
|
||||||
this.validateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
onTimeChange() {
|
|
||||||
let date = null;
|
let date = null;
|
||||||
let value = this.input.value;
|
let value = this.input.value;
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
let split = value.split(':').map(i => parseInt(i) || null);
|
let split = value.split(':').map(i => parseInt(i) || null);
|
||||||
date = new Date(this.value || null);
|
date = new Date(this.field || null);
|
||||||
date.setHours(split[0], split[1], 0, 0);
|
date.setHours(split[0], split[1], 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateValue(date);
|
super.field = date;
|
||||||
|
this.$.$applyAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputTime.$inject = ['$element', '$scope', '$filter'];
|
InputTime.$inject = ['$element', '$scope', '$compile', '$filter'];
|
||||||
|
|
||||||
ngModule.component('vnInputTime', {
|
ngModule.vnComponent('vnInputTime', {
|
||||||
template: require('./index.html'),
|
controller: InputTime
|
||||||
controller: InputTime,
|
|
||||||
transclude: {
|
|
||||||
leftIcons: '?tLeftIcons',
|
|
||||||
rightIcons: '?tRightIcons'
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
label: '@?',
|
|
||||||
disabled: '<?',
|
|
||||||
readonly: '<?',
|
|
||||||
rule: '@?',
|
|
||||||
value: '=model',
|
|
||||||
vnTabIndex: '@?',
|
|
||||||
onChange: '&',
|
|
||||||
onClear: '&'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,43 +1,32 @@
|
||||||
import './index.js';
|
import './index.js';
|
||||||
|
|
||||||
describe('Component vnInputTime', () => {
|
describe('Component vnInputTime', () => {
|
||||||
let $scope;
|
let $filter;
|
||||||
let $attrs;
|
|
||||||
let $timeout;
|
|
||||||
let $element;
|
let $element;
|
||||||
let controller;
|
let $ctrl;
|
||||||
|
|
||||||
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
||||||
$translateProvider.translations('en', {});
|
$translateProvider.translations('en', {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => {
|
||||||
$scope = $rootScope.$new();
|
$filter = _$filter_;
|
||||||
$attrs = {field: '$ctrl.zone.hour'};
|
|
||||||
$element = angular.element('<vn-input-time label="Hour" field="$ctrl.zone.hour"><input><div class="infix invalid validated"><div class="rightIcons"></div></vn-input-time>');
|
$element = $compile(`<vn-input-time></vn-input-time>`)($rootScope);
|
||||||
controller = $componentController('vnInputTime', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
|
$ctrl = $element.controller('vnInputTime');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('value() setter', () => {
|
afterEach(() => {
|
||||||
it(`should set _value to a given value, add the class not-empty and remove invalid and validated`, () => {
|
$element.remove();
|
||||||
const today = new Date();
|
});
|
||||||
controller.value = today;
|
|
||||||
let classes = controller.element.classList.toString();
|
|
||||||
|
|
||||||
expect(classes).toContain('not-empty');
|
describe('field() setter', () => {
|
||||||
expect(controller._value).toEqual(today);
|
it(`should display the formated the date`, () => {
|
||||||
|
let date = new Date();
|
||||||
|
$ctrl.field = date;
|
||||||
|
let displayed = $filter('dateTime')(date, 'HH:mm');
|
||||||
|
|
||||||
classes = controller.element.querySelector('.infix').classList.toString();
|
expect($ctrl.value).toEqual(displayed);
|
||||||
|
|
||||||
expect(classes).not.toContain('invalid validated');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should set _value to a given value and not add the class not-empty if the given value is null`, () => {
|
|
||||||
controller.value = null;
|
|
||||||
let classes = controller.element.classList.toString();
|
|
||||||
|
|
||||||
expect(classes).not.toContain('not-empty');
|
|
||||||
expect(controller._value).toEqual(null);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
@import '../textfield/style.scss';
|
|
||||||
|
|
||||||
vn-input-time {
|
|
||||||
@extend vn-textfield;
|
|
||||||
|
|
||||||
input[type="time"] {
|
|
||||||
clip-path: inset(0 17px 0 0);
|
|
||||||
outline: none;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
<vn-check
|
<vn-check
|
||||||
field="$ctrl.checked"
|
ng-model="$ctrl.checked"
|
||||||
intermediate="$ctrl.isIntermediate"
|
intermediate="$ctrl.isIntermediate"
|
||||||
translate-attr="{title: 'Check all'}">
|
translate-attr="{title: 'Check all'}">
|
||||||
</vn-check>
|
</vn-check>
|
|
@ -109,7 +109,6 @@ export default class Popover extends Component {
|
||||||
this.showTimeout = null;
|
this.showTimeout = null;
|
||||||
this.element.style.display = 'none';
|
this.element.style.display = 'none';
|
||||||
this.document.body.removeChild(this.element);
|
this.document.body.removeChild(this.element);
|
||||||
this.emit('close');
|
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
||||||
this.document.removeEventListener('keydown', this.docKeyDownHandler);
|
this.document.removeEventListener('keydown', this.docKeyDownHandler);
|
||||||
|
@ -118,8 +117,8 @@ export default class Popover extends Component {
|
||||||
this.element.removeEventListener('mousedown', this.bgMouseDownHandler);
|
this.element.removeEventListener('mousedown', this.bgMouseDownHandler);
|
||||||
this.bgMouseDownHandler = null;
|
this.bgMouseDownHandler = null;
|
||||||
|
|
||||||
if (this.deregisterCallback)
|
if (this.deregisterCallback) this.deregisterCallback();
|
||||||
this.deregisterCallback();
|
this.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,8 +188,9 @@ export default class Popover extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBgMouseDown(event) {
|
onBgMouseDown(event) {
|
||||||
if (event != this.lastMouseEvent)
|
if (event == this.lastMouseEvent || event.defaultPrevented) return;
|
||||||
this.hide();
|
event.preventDefault();
|
||||||
|
this.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Popover.$inject = ['$element', '$scope', '$timeout', '$transitions', '$transclude', '$compile'];
|
Popover.$inject = ['$element', '$scope', '$timeout', '$transitions', '$transclude', '$compile'];
|
||||||
|
|
|
@ -10,13 +10,13 @@ import './style.scss';
|
||||||
*/
|
*/
|
||||||
export default class Radio extends Toggle {
|
export default class Radio extends Toggle {
|
||||||
set field(value) {
|
set field(value) {
|
||||||
this._field = value;
|
super.field = value;
|
||||||
this.element.classList.toggle('checked',
|
this.element.classList.toggle('checked',
|
||||||
Boolean(value) && value == this.val);
|
Boolean(value) && value == this.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
get field() {
|
get field() {
|
||||||
return this._field;
|
return super.field;
|
||||||
}
|
}
|
||||||
|
|
||||||
set checked(value) {
|
set checked(value) {
|
||||||
|
@ -43,14 +43,10 @@ export default class Radio extends Toggle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.component('vnRadio', {
|
ngModule.vnComponent('vnRadio', {
|
||||||
template: require('../toggle/index.html'),
|
template: require('../toggle/index.html'),
|
||||||
controller: Radio,
|
controller: Radio,
|
||||||
bindings: {
|
bindings: {
|
||||||
label: '@?',
|
|
||||||
field: '=?',
|
|
||||||
disabled: '<?',
|
|
||||||
checked: '<?',
|
|
||||||
val: '@?'
|
val: '@?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
<form ng-submit="$ctrl.onSubmit()">
|
<form ng-submit="$ctrl.onSubmit()">
|
||||||
<vn-horizontal>
|
<vn-textfield
|
||||||
<vn-textfield vn-one label="Search" model="$ctrl.searchString">
|
class="dense vn-py-md"
|
||||||
<t-left-icons>
|
placeholder="{{::'Search' | translate}}"
|
||||||
<vn-icon
|
ng-model="$ctrl.searchString">
|
||||||
icon="search"
|
<prepend>
|
||||||
ng-click="$ctrl.clearFilter(); $ctrl.onSubmit()"
|
<vn-icon
|
||||||
pointer>
|
icon="search"
|
||||||
</vn-icon>
|
ng-click="$ctrl.clearFilter(); $ctrl.onSubmit()"
|
||||||
</t-left-icons>
|
pointer>
|
||||||
<t-right-icons vn-horizontal>
|
</vn-icon>
|
||||||
<vn-icon vn-one
|
</prepend>
|
||||||
|
<append>
|
||||||
|
<vn-icon
|
||||||
ng-if="$ctrl.info"
|
ng-if="$ctrl.info"
|
||||||
icon="info_outline"
|
icon="info_outline"
|
||||||
vn-tooltip = "{{$ctrl.info}}"
|
vn-tooltip="{{$ctrl.info}}"
|
||||||
pointer>
|
pointer>
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
<vn-icon vn-one
|
<vn-icon
|
||||||
ng-if="$ctrl.panel"
|
ng-if="$ctrl.panel"
|
||||||
ng-click="$ctrl.openPanel($event)"
|
ng-click="$ctrl.openPanel($event)"
|
||||||
icon="keyboard_arrow_down"
|
icon="arrow_drop_down"
|
||||||
pointer>
|
pointer>
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
</t-right-icons>
|
</append>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
|
||||||
</form>
|
</form>
|
||||||
<vn-popover
|
<vn-popover
|
||||||
vn-id="popover"
|
vn-id="popover"
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-searchbar {
|
vn-searchbar {
|
||||||
padding-top: 6px;
|
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
& > form > vn-horizontal > vn-icon-button {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-panel {
|
.search-panel {
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<div class="table" ng-transclude>
|
<div class="vn-table" ng-transclude>
|
||||||
</div>
|
</div>
|
|
@ -5,181 +5,169 @@ vn-table {
|
||||||
display: block;
|
display: block;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
.vn-table {
|
||||||
|
width: 100%;
|
||||||
|
display: table;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
& > div {
|
& > vn-thead,
|
||||||
width: inherit;
|
& > thead {
|
||||||
display: table;
|
display: table-header-group;
|
||||||
border-collapse: collapse;
|
border-bottom: .15em solid $color-spacer;
|
||||||
|
|
||||||
& > vn-thead {
|
& > * > th {
|
||||||
display: table-header-group;
|
font-weight: normal;
|
||||||
border-bottom: .15em solid $color-spacer;
|
}
|
||||||
|
& > * > vn-th[field] {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
& > * > vn-th[field] {
|
&.active > :after {
|
||||||
position: relative;
|
color: $color-font;
|
||||||
overflow: visible;
|
opacity: 1;
|
||||||
cursor: pointer;
|
}
|
||||||
|
&.desc > :after {
|
||||||
&.active > :after {
|
content: 'arrow_drop_down';
|
||||||
color: $color-font;
|
}
|
||||||
opacity: 1;
|
&.asc > :after {
|
||||||
}
|
content: 'arrow_drop_up';
|
||||||
&.desc > :after {
|
}
|
||||||
content: 'arrow_drop_down';
|
& > :after {
|
||||||
}
|
font-family: 'Material Icons';
|
||||||
&.asc > :after {
|
content: 'arrow_drop_down';
|
||||||
content: 'arrow_drop_up';
|
position: absolute;
|
||||||
}
|
color: $color-spacer;
|
||||||
& > :after {
|
opacity: 0;
|
||||||
font-family: 'Material Icons';
|
}
|
||||||
content: 'arrow_drop_down';
|
&:hover > :after {
|
||||||
position: absolute;
|
opacity: 1;
|
||||||
color: $color-spacer;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
&:hover > :after {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > vn-tbody {
|
}
|
||||||
display: table-row-group;
|
& > vn-tbody,
|
||||||
}
|
& > tbody {
|
||||||
& > vn-tfoot {
|
display: table-row-group;
|
||||||
border-top: .15em solid $color-spacer;
|
}
|
||||||
display: table-footer-group
|
& > vn-tfoot,
|
||||||
}
|
& > tfoot {
|
||||||
& > * > vn-tr,
|
border-top: .15em solid $color-spacer;
|
||||||
& > * > a.vn-tr {
|
display: table-footer-group
|
||||||
|
}
|
||||||
|
& > * > vn-tr,
|
||||||
|
& > * > a.vn-tr,
|
||||||
|
& > * > tr {
|
||||||
|
display: table-row;
|
||||||
|
height: 3em;
|
||||||
|
}
|
||||||
|
vn-thead, vn-tbody, vn-tfoot,
|
||||||
|
thead, tbody, tfoot {
|
||||||
|
& > * {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
height: 3em;
|
|
||||||
}
|
|
||||||
vn-thead, vn-tbody, vn-tfoot {
|
|
||||||
& > * {
|
|
||||||
display: table-row;
|
|
||||||
|
|
||||||
& > vn-th {
|
& > vn-th,
|
||||||
color: $color-font-light;
|
& > th {
|
||||||
padding-top: 1em;
|
color: $color-font-light;
|
||||||
padding-bottom: .8em;
|
padding-top: 1em;
|
||||||
|
padding-bottom: .8em;
|
||||||
|
}
|
||||||
|
& > vn-th,
|
||||||
|
& > vn-td,
|
||||||
|
& > th,
|
||||||
|
& > td {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
& > vn-th,
|
||||||
|
& > vn-td,
|
||||||
|
& > vn-td-editable,
|
||||||
|
& > th,
|
||||||
|
& > td {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: table-cell;
|
||||||
|
text-align: left;
|
||||||
|
padding: .6em .5em;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 5em;
|
||||||
|
|
||||||
|
&[number] {
|
||||||
|
text-align: right;
|
||||||
|
width: 6em;
|
||||||
}
|
}
|
||||||
& > vn-th,
|
&[center] {
|
||||||
& > vn-td {
|
text-align: center;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
& > vn-th,
|
&[shrink] {
|
||||||
& > vn-td,
|
width: 1px;
|
||||||
& > vn-td-editable {
|
text-align: center;
|
||||||
vertical-align: middle;
|
|
||||||
display: table-cell;
|
|
||||||
text-align: left;
|
|
||||||
padding: .6em .5em;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
max-width: 5em;
|
|
||||||
|
|
||||||
&[number] {
|
|
||||||
text-align: right;
|
|
||||||
width: 6em;
|
|
||||||
}
|
|
||||||
&[center] {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
&[shrink] {
|
|
||||||
width: 1px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
&[expand] {
|
|
||||||
max-width: 10em;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
vn-icon.bright, i.bright {
|
|
||||||
color: #f7931e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
& > :last-child {
|
&[expand] {
|
||||||
padding-right: 1.4em;
|
max-width: 10em;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
& > :first-child {
|
vn-icon.bright, i.bright {
|
||||||
padding-left: 1.4em;
|
color: #f7931e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > a.vn-tr {
|
& > :last-child {
|
||||||
color: inherit;
|
padding-right: 1.4em;
|
||||||
|
}
|
||||||
|
& > :first-child {
|
||||||
|
padding-left: 1.4em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vn-tbody > * {
|
& > a.vn-tr {
|
||||||
border-bottom: .1em solid $color-spacer-light;
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vn-tbody > *,
|
||||||
|
tbody > * {
|
||||||
|
border-bottom: .1em solid $color-spacer-light;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
&.clickable {
|
&.clickable {
|
||||||
@extend %clickable;
|
@extend %clickable;
|
||||||
}
|
}
|
||||||
& > vn-td .chip {
|
& > vn-td,
|
||||||
|
& > td {
|
||||||
|
.chip {
|
||||||
padding: .3em;
|
padding: .3em;
|
||||||
border-radius: .3em
|
border-radius: .3em;
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-td .chip.notice {
|
|
||||||
color: $color-font-bg;
|
color: $color-font-bg;
|
||||||
background-color: $color-notice-medium
|
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-td .chip.success {
|
&.notice {
|
||||||
color: $color-font-bg;
|
background-color: $color-notice-medium
|
||||||
background-color: $color-success-medium
|
}
|
||||||
|
&.success {
|
||||||
|
background-color: $color-success-medium;
|
||||||
|
}
|
||||||
|
&.warning {
|
||||||
|
background-color: $color-main-medium;
|
||||||
|
}
|
||||||
|
&.alert {
|
||||||
|
background-color: $color-alert-medium;
|
||||||
|
}
|
||||||
|
&.message {
|
||||||
|
color: $color-font-dark;
|
||||||
|
background-color: $color-bg-dark
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
vn-icon-menu {
|
||||||
& > vn-td .chip.warning {
|
|
||||||
color: $color-font-bg;
|
|
||||||
background-color: $color-main-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-td .chip.alert {
|
|
||||||
color: $color-font-bg;
|
|
||||||
background-color: $color-alert-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-td .chip.message {
|
|
||||||
color: $color-font-dark;
|
|
||||||
background-color: $color-bg-dark
|
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-td vn-icon-menu {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: $color-main;
|
color: $color-main;
|
||||||
padding: .25em
|
padding: .25em
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
& > [actions] {
|
||||||
|
width: 1px;
|
||||||
|
|
||||||
& > [actions] {
|
& > * {
|
||||||
width: 1px;
|
vertical-align: middle;
|
||||||
|
|
||||||
& > * {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > vn-empty-rows {
|
|
||||||
display: table-caption;
|
|
||||||
caption-side: bottom;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1.5em;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vn-autocomplete {
|
|
||||||
div.mdl-textfield {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
label.mdl-textfield__label:after {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
div.icons {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vn-textfield {
|
vn-textfield {
|
||||||
float: right;
|
float: right;
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Field from '../field';
|
||||||
|
|
||||||
|
export default class Textarea extends Field {
|
||||||
|
constructor($element, $scope, $compile) {
|
||||||
|
super($element, $scope, $compile);
|
||||||
|
|
||||||
|
let html = `<textarea ng-model="$ctrl.field"></textarea>`;
|
||||||
|
this.input = $compile(html)($scope)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
set rows(value) {
|
||||||
|
this.input.rows = typeof value == 'number' && value > 0 ? value : 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rows() {
|
||||||
|
return this.input.rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnTextarea', {
|
||||||
|
controller: Textarea,
|
||||||
|
bindings: {
|
||||||
|
rows: '<?'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,39 @@
|
||||||
|
import './index.js';
|
||||||
|
|
||||||
|
describe('Component vnTextarea', () => {
|
||||||
|
let $element;
|
||||||
|
let $ctrl;
|
||||||
|
|
||||||
|
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
||||||
|
$translateProvider.translations('en', {});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject(($compile, $rootScope) => {
|
||||||
|
$element = $compile(`<vn-textarea></vn-textarea>`)($rootScope);
|
||||||
|
$ctrl = $element.controller('vnTextarea');
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
$element.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('rows() setter', () => {
|
||||||
|
it(`should set rows property of the element to the given value if it's a valid number`, () => {
|
||||||
|
$ctrl.rows = 27;
|
||||||
|
|
||||||
|
expect($ctrl.rows).toEqual(27);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should set rows property of the element to 3 if the given value if it's null`, () => {
|
||||||
|
$ctrl.rows = null;
|
||||||
|
|
||||||
|
expect($ctrl.rows).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should set rows property of the element to 3 if the given value if it's not a valid number`, () => {
|
||||||
|
$ctrl.rows = 'a';
|
||||||
|
|
||||||
|
expect($ctrl.rows).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,24 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
|
|
||||||
vn-textarea {
|
|
||||||
& > .mdl-textfield {
|
|
||||||
width: initial;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
color: $color-font-secondary;
|
|
||||||
transition-duration: .2s;
|
|
||||||
transition-timing-function: cubic-bezier(.4,0,.2,1);
|
|
||||||
}
|
|
||||||
& textarea.ng-not-empty+label.placeholder{
|
|
||||||
top: 5px;
|
|
||||||
color: $color-main;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
visibility: visible!important;
|
|
||||||
content: normal!important;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
<div class="mdl-textfield mdl-js-textfield">
|
|
||||||
<textarea
|
|
||||||
class="mdl-textfield__input"
|
|
||||||
type="text"
|
|
||||||
ng-disabled="$ctrl.disabled"
|
|
||||||
ng-model="$ctrl.model">
|
|
||||||
</textarea>
|
|
||||||
<label class="mdl-textfield__label placeholder" translate>{{$ctrl.label}}</label>
|
|
||||||
</div>
|
|
|
@ -1,43 +0,0 @@
|
||||||
import ngModule from '../../module';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default class Textarea {
|
|
||||||
constructor($element, $scope, $attrs, vnTemplate) {
|
|
||||||
this.$ = $scope;
|
|
||||||
this.$attrs = $attrs;
|
|
||||||
this.element = $element;
|
|
||||||
vnTemplate.normalizeInputAttrs($attrs);
|
|
||||||
this.textarea = this.element[0].querySelector('textarea');
|
|
||||||
this.rows = null;
|
|
||||||
}
|
|
||||||
set model(value) {
|
|
||||||
this._model = value;
|
|
||||||
this.mdlUpdate();
|
|
||||||
}
|
|
||||||
set rows(value) {
|
|
||||||
this.textarea.rows = value ? value : 3;
|
|
||||||
}
|
|
||||||
get model() {
|
|
||||||
return this._model;
|
|
||||||
}
|
|
||||||
mdlUpdate() {
|
|
||||||
let mdlField = this.element[0].firstChild.MaterialTextfield;
|
|
||||||
if (mdlField)
|
|
||||||
mdlField.updateClasses_();
|
|
||||||
componentHandler.upgradeElement(this.element[0].firstChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Textarea.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
|
|
||||||
|
|
||||||
ngModule.component('vnTextarea', {
|
|
||||||
template: require('./textarea.html'),
|
|
||||||
controller: Textarea,
|
|
||||||
bindings: {
|
|
||||||
model: '=model',
|
|
||||||
label: '@?',
|
|
||||||
rows: '@?',
|
|
||||||
name: '@?',
|
|
||||||
disabled: '<?',
|
|
||||||
rule: '@?'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,56 +0,0 @@
|
||||||
import './textarea.js';
|
|
||||||
|
|
||||||
describe('Component vnTextarea', () => {
|
|
||||||
let $scope;
|
|
||||||
let $attrs;
|
|
||||||
let $element;
|
|
||||||
let controller;
|
|
||||||
|
|
||||||
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
|
||||||
$translateProvider.translations('en', {});
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$attrs = {field: '$ctrl.claim.observation'};
|
|
||||||
$element = angular.element('<vn-textarea vn-three label="Observation" field="$ctrl.claim.observation" rows="9"><textarea></vn-textarea>');
|
|
||||||
$element[0].firstChild.MaterialTextfield = {updateClasses_: () => {}};
|
|
||||||
controller = $componentController('vnTextarea', {$scope, $element, $attrs});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('model() setter', () => {
|
|
||||||
it(`should set model and call mdlUpdate`, () => {
|
|
||||||
spyOn(controller, 'mdlUpdate');
|
|
||||||
let model = 'model';
|
|
||||||
controller.model = model;
|
|
||||||
|
|
||||||
expect(controller._model).toEqual(model);
|
|
||||||
expect(controller.mdlUpdate).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('rows() setter', () => {
|
|
||||||
it(`should set rows property of the element to the given value if its not null`, () => {
|
|
||||||
controller.rows = 27;
|
|
||||||
|
|
||||||
expect(controller.textarea.rows).toEqual(27);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should set rows property of the element to 3 if the given value if its null`, () => {
|
|
||||||
controller.rows = null;
|
|
||||||
|
|
||||||
expect(controller.textarea.rows).toEqual(3);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mdlUpdate()', () => {
|
|
||||||
it(`should should call mdlField.updateClasses if theres an mdlField defined and call upgradeElement with the textarea element`, () => {
|
|
||||||
spyOn($element[0].firstChild.MaterialTextfield, 'updateClasses_');
|
|
||||||
spyOn(componentHandler, 'upgradeElement');
|
|
||||||
controller.mdlUpdate();
|
|
||||||
|
|
||||||
expect($element[0].firstChild.MaterialTextfield.updateClasses_).toHaveBeenCalledWith();
|
|
||||||
expect(componentHandler.upgradeElement).toHaveBeenCalledWith(controller.element[0].firstChild);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,162 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
|
|
||||||
vn-textfield {
|
|
||||||
margin: 20px 0;
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
& > .container {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
& > .textField {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
padding-top: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftIcons, .rightIcons, .suffix {
|
|
||||||
display: flex;
|
|
||||||
color: $color-font-secondary;
|
|
||||||
|
|
||||||
.material-icons {
|
|
||||||
font-size: 20px !important
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.suffix vn-icon-button {
|
|
||||||
padding: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
t-left-icons {
|
|
||||||
padding-right: 0.5em
|
|
||||||
}
|
|
||||||
|
|
||||||
t-right-icons {
|
|
||||||
padding-left: 0.5em
|
|
||||||
}
|
|
||||||
|
|
||||||
.infix {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
flex: auto;
|
|
||||||
width: 100%;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
i.clear {
|
|
||||||
visibility: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
outline: 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #222;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover i.clear {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
i.visible {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: 4px 0!important;
|
|
||||||
pointer-events: none;
|
|
||||||
color: $color-font-secondary;
|
|
||||||
transition-duration: .2s;
|
|
||||||
transition-timing-function: cubic-bezier(.4,0,.2,1);
|
|
||||||
}
|
|
||||||
&.not-empty label{
|
|
||||||
bottom: 24px;
|
|
||||||
color: $color-main;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
font-family: "Helvetica", "Arial", sans-serif;
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
width: 100%;
|
|
||||||
background: 0 0;
|
|
||||||
color: inherit;
|
|
||||||
padding: 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-bottom: 0!important;
|
|
||||||
|
|
||||||
&[type=number] {
|
|
||||||
-moz-appearance: textfield;
|
|
||||||
&::-webkit-outer-spin-button,
|
|
||||||
&::-webkit-inner-spin-button {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:invalid {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.underline {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
height: 1px;
|
|
||||||
content: ' ';
|
|
||||||
pointer-events: none;
|
|
||||||
width: 100%;
|
|
||||||
background-color: $color-input-underline;
|
|
||||||
}
|
|
||||||
.selected.underline {
|
|
||||||
background-color: $color-main;
|
|
||||||
height: 2px;
|
|
||||||
left: 50%;
|
|
||||||
width: 0px !important;
|
|
||||||
transition-duration: 0.2s;
|
|
||||||
transition-timing-function: cubic-bezier(.4,0,.2,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
div.selected {
|
|
||||||
&.container{
|
|
||||||
border-bottom: 0px;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
bottom: 24px;
|
|
||||||
color: $color-main;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.selected.underline{
|
|
||||||
left: 0;
|
|
||||||
width: 100%!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& > div.container > div.textField > div.infix.invalid {
|
|
||||||
@extend div.selected;
|
|
||||||
|
|
||||||
& > span.mdl-textfield__error {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
& > label {
|
|
||||||
color: #d50000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.infix.invalid + .underline {
|
|
||||||
background-color: #d50000;
|
|
||||||
}
|
|
||||||
|
|
||||||
label span:nth-child(2) {
|
|
||||||
color: $color-alert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vn-table {
|
|
||||||
vn-textfield {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
<div class="container"
|
|
||||||
ng-class="{selected: $ctrl.hasFocus}">
|
|
||||||
<div class="textField">
|
|
||||||
<div class="leftIcons" ng-transclude="leftIcons"></div>
|
|
||||||
<div class="infix">
|
|
||||||
<input
|
|
||||||
class="mdl-textfield__input"
|
|
||||||
type="{{$ctrl.type}}"
|
|
||||||
name="{{$ctrl.name}}"
|
|
||||||
ng-model="$ctrl.value"
|
|
||||||
vn-validation="{{$ctrl.rule}}"
|
|
||||||
ng-disabled="$ctrl.disabled"
|
|
||||||
ng-readonly="$ctrl.readonly"
|
|
||||||
ng-focus="$ctrl.hasFocus = true"
|
|
||||||
ng-blur="$ctrl.hasFocus = false"
|
|
||||||
tabindex="{{$ctrl.input.tabindex}}"/>
|
|
||||||
<label class="label">
|
|
||||||
<span translate>{{::$ctrl.label}}</span>
|
|
||||||
<span translate ng-show="::$ctrl.required">*</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="underline"></div>
|
|
||||||
<div class="selected underline"></div>
|
|
||||||
<div class="suffix">
|
|
||||||
<i class="material-icons clear"
|
|
||||||
translate-attr="{title: 'Clear'}"
|
|
||||||
ng-show="!$ctrl.disabled
|
|
||||||
&& $ctrl.hasValue
|
|
||||||
&& !$ctrl.unclearable"
|
|
||||||
ng-click="$ctrl.clear()">
|
|
||||||
clear
|
|
||||||
</i>
|
|
||||||
<i class="material-icons"
|
|
||||||
ng-if="$ctrl.hasInfo"
|
|
||||||
vn-tooltip="{{$ctrl.info}}">
|
|
||||||
info_outline
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
<div class="rightIcons" ng-transclude="rightIcons"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,94 +1,13 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Input from '../../lib/input';
|
import Field from '../field';
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
export default class Textfield extends Input {
|
export default class Textfield extends Field {
|
||||||
constructor($element, $scope, $attrs, vnTemplate) {
|
constructor($element, $scope, $compile) {
|
||||||
super($element, $scope);
|
super($element, $scope, $compile);
|
||||||
vnTemplate.normalizeInputAttrs($attrs);
|
this.buildInput('text');
|
||||||
this._value = null;
|
|
||||||
this.type = $attrs.type;
|
|
||||||
this.showActions = false;
|
|
||||||
this.hasInfo = Boolean($attrs.info);
|
|
||||||
this.hasFocus = false;
|
|
||||||
this.hasMouseIn = false;
|
|
||||||
|
|
||||||
this.input.addEventListener('keyup', e => {
|
|
||||||
if (e.defaultPrevented || e.key != 'Escape')
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.value = this.oldValue;
|
|
||||||
this.cancelled = true;
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
this.input.addEventListener('change', e => {
|
|
||||||
if (this.onChange && !this.cancelled && (this.oldValue != this.value)) {
|
|
||||||
this.onChange();
|
|
||||||
this.saveOldValue();
|
|
||||||
} else
|
|
||||||
this.cancelled = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$postLink() {
|
|
||||||
this.saveOldValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
saveOldValue() {
|
|
||||||
this.oldValue = this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(value) {
|
|
||||||
this._value = (value === undefined || value === '') ? null : value;
|
|
||||||
this.input.value = this._value;
|
|
||||||
this.hasValue = this._value != null;
|
|
||||||
this.element.classList.toggle('not-empty', this.hasValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set type(value) {
|
|
||||||
this._type = value || 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
get type() {
|
|
||||||
return this._type;
|
|
||||||
}
|
|
||||||
|
|
||||||
set vnTabIndex(value) {
|
|
||||||
this.input.tabindex = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.saveOldValue();
|
|
||||||
this.value = null;
|
|
||||||
if (this.onClear) this.onClear();
|
|
||||||
this.input.focus();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Textfield.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
|
|
||||||
|
|
||||||
ngModule.component('vnTextfield', {
|
ngModule.vnComponent('vnTextfield', {
|
||||||
template: require('./textfield.html'),
|
controller: Textfield
|
||||||
transclude: {
|
|
||||||
leftIcons: '?tLeftIcons',
|
|
||||||
rightIcons: '?tRightIcons'
|
|
||||||
},
|
|
||||||
controller: Textfield,
|
|
||||||
bindings: {
|
|
||||||
value: '=model',
|
|
||||||
label: '@?',
|
|
||||||
name: '@?',
|
|
||||||
disabled: '<?',
|
|
||||||
required: '@?',
|
|
||||||
readonly: '<?',
|
|
||||||
rule: '@?',
|
|
||||||
type: '@?',
|
|
||||||
vnTabIndex: '@?',
|
|
||||||
onChange: '&',
|
|
||||||
onClear: '&',
|
|
||||||
info: '@?'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
import './textfield.js';
|
|
||||||
|
|
||||||
describe('Component vnTextfield', () => {
|
|
||||||
let $scope;
|
|
||||||
let $attrs;
|
|
||||||
let $timeout;
|
|
||||||
let $element;
|
|
||||||
let controller;
|
|
||||||
|
|
||||||
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
|
||||||
$translateProvider.translations('en', {});
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$attrs = {field: '$ctrl.client.phone'};
|
|
||||||
$element = angular.element('<vn-textfield label="Phone" field="$ctrl.client.phone"><input><div class="leftIcons"><div class="rightIcons"></div></vn-textfield>');
|
|
||||||
controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout, $transclude: () => {}});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('saveOldValue()', () => {
|
|
||||||
it(`should equal oldValue property to the actual input value`, () => {
|
|
||||||
controller.value = 'pepino';
|
|
||||||
controller.saveOldValue();
|
|
||||||
|
|
||||||
expect(controller.oldValue).toEqual('pepino');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('value() setter', () => {
|
|
||||||
it(`should set _value, input.value and hasValue properties to null, '' and false`, () => {
|
|
||||||
let testValue = '';
|
|
||||||
controller.value = testValue;
|
|
||||||
|
|
||||||
expect(controller._value).toEqual(null);
|
|
||||||
expect(controller.input.value).toEqual(testValue);
|
|
||||||
expect(controller.hasValue).toEqual(Boolean(testValue));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should set _value, input.value and hasValue propertiest to test, test and true`, () => {
|
|
||||||
let testValue = 'test';
|
|
||||||
controller.value = testValue;
|
|
||||||
|
|
||||||
expect(controller._value).toEqual(testValue);
|
|
||||||
expect(controller.input.value).toEqual(testValue);
|
|
||||||
expect(controller.hasValue).toEqual(Boolean(testValue));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should add the class not-empty to the div`, () => {
|
|
||||||
let testValue = 'test';
|
|
||||||
controller.value = testValue;
|
|
||||||
|
|
||||||
expect(controller.element.classList).toContain('not-empty');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('type() setter', () => {
|
|
||||||
it(`should set _type to 'text' if theres not given value`, () => {
|
|
||||||
controller.type = null;
|
|
||||||
|
|
||||||
expect(controller._type).toEqual('text');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('vnTabIndex() setter', () => {
|
|
||||||
it(`should set input.tabindex to a given value`, () => {
|
|
||||||
controller.vnTabIndex = 9;
|
|
||||||
|
|
||||||
expect(controller.input.tabindex).toEqual(9);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('clear()', () => {
|
|
||||||
it(`should set value property to null, call focus and saveOldValue`, () => {
|
|
||||||
spyOn(controller.input, 'focus');
|
|
||||||
spyOn(controller, 'saveOldValue');
|
|
||||||
controller.clear();
|
|
||||||
|
|
||||||
expect(controller.value).toEqual(null);
|
|
||||||
expect(controller.saveOldValue).toHaveBeenCalledWith();
|
|
||||||
expect(controller.input.focus).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,16 +1,13 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Component from '../../lib/component';
|
import FormInput from '../form-input';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base component with common logic and styles for checkbox and radio button.
|
* Base component with common logic and styles for checkbox and radio button.
|
||||||
*
|
*
|
||||||
* @property {String} label Label to display along the component
|
|
||||||
* @property {any} field The value with which the element is linked
|
|
||||||
* @property {Boolean} checked Whether the checkbox is checked
|
* @property {Boolean} checked Whether the checkbox is checked
|
||||||
* @property {Boolean} disabled Put component in disabled mode
|
|
||||||
*/
|
*/
|
||||||
export default class Toggle extends Component {
|
export default class Toggle extends FormInput {
|
||||||
constructor($element, $) {
|
constructor($element, $) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
|
|
||||||
|
@ -49,14 +46,10 @@ export default class Toggle extends Component {
|
||||||
this.emit('change', {value: this.field});
|
this.emit('change', {value: this.field});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Toggle.$inject = ['$element', '$scope'];
|
|
||||||
|
|
||||||
ngModule.component('vnToggle', {
|
ngModule.vnComponent('vnToggle', {
|
||||||
controller: Toggle,
|
controller: Toggle,
|
||||||
bindings: {
|
bindings: {
|
||||||
label: '@?',
|
|
||||||
field: '=?',
|
|
||||||
disabled: '<?',
|
|
||||||
checked: '<?'
|
checked: '<?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
.vn-toggle {
|
.vn-toggle {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
|
@ -19,8 +20,9 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
min-width: 20px;
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
margin-right: .4em;
|
margin-right: .6em;
|
||||||
border: 2px solid #666;
|
border: 2px solid #666;
|
||||||
}
|
}
|
||||||
&.checked > .btn {
|
&.checked > .btn {
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
<ul ng-if="::$ctrl.items">
|
|
||||||
<li
|
|
||||||
ng-repeat="item in $ctrl.items"
|
|
||||||
on-drop="$ctrl.onDrop(item, dragged, dropped)"
|
|
||||||
vn-draggable="{{::$ctrl.draggable}}"
|
|
||||||
vn-droppable="{{::$ctrl.droppable}}"
|
|
||||||
ng-class="{expanded: item.active}">
|
|
||||||
<div
|
|
||||||
ng-click="$ctrl.toggle($event, item)"
|
|
||||||
class="node clickable">
|
|
||||||
<vn-icon
|
|
||||||
class="arrow"
|
|
||||||
ng-class="{invisible: item.sons == 0}"
|
|
||||||
icon="keyboard_arrow_down"
|
|
||||||
translate-attr="{title: 'Toggle'}">
|
|
||||||
</vn-icon>
|
|
||||||
<vn-check
|
|
||||||
vn-acl="{{$ctrl.aclRole}}"
|
|
||||||
ng-if="$ctrl.selectable"
|
|
||||||
field="item.selected"
|
|
||||||
disabled="$ctrl.disabled"
|
|
||||||
on-change="$ctrl.select(item, value)"
|
|
||||||
triple-state="true"
|
|
||||||
label="{{::item.name}}">
|
|
||||||
</vn-check>
|
|
||||||
<vn-icon-button
|
|
||||||
icon="{{icon.icon}}"
|
|
||||||
ng-repeat="icon in $ctrl.icons"
|
|
||||||
ng-click="$ctrl.onIconClick(icon, item, $ctrl.parent, $parent.$index)"
|
|
||||||
vn-acl="{{$ctrl.aclRole}}"
|
|
||||||
vn-acl-action="remove">
|
|
||||||
</vn-icon-button>
|
|
||||||
</div>
|
|
||||||
<vn-treeview-child
|
|
||||||
items="item.childs"
|
|
||||||
parent="item"
|
|
||||||
selectable="$ctrl.selectable"
|
|
||||||
disabled="$ctrl.disabled"
|
|
||||||
editable="$ctrl.editable"
|
|
||||||
draggable="::$ctrl.draggable"
|
|
||||||
droppable="::$ctrl.droppable"
|
|
||||||
icons="::$ctrl.icons"
|
|
||||||
parent-scope="::$ctrl.parentScope"
|
|
||||||
acl-role="$ctrl.aclRole">
|
|
||||||
</vn-treeview-child>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
ng-if="$ctrl.isInsertable && $ctrl.editable"
|
|
||||||
ng-click="$ctrl.onCreate($ctrl.parent)"
|
|
||||||
vn-acl="{{$ctrl.aclRole}}"
|
|
||||||
vn-acl-action="remove">
|
|
||||||
<div class="node">
|
|
||||||
<vn-icon-button
|
|
||||||
icon="add_circle">
|
|
||||||
</vn-icon-button>
|
|
||||||
<div class="description" translate>
|
|
||||||
Create new one
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
|
@ -1,56 +0,0 @@
|
||||||
import ngModule from '../../module';
|
|
||||||
import Component from '../../lib/component';
|
|
||||||
|
|
||||||
class Controller extends Component {
|
|
||||||
constructor($element, $scope) {
|
|
||||||
super($element, $scope);
|
|
||||||
this.$scope = $scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle(event, item) {
|
|
||||||
if (event.defaultPrevented || !item.sons) return;
|
|
||||||
event.preventDefault();
|
|
||||||
this.treeview.onToggle(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
select(item, value) {
|
|
||||||
this.treeview.onSelection(item, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
onIconClick(icon, item, parent, index) {
|
|
||||||
let parentController = this.parentScope.$ctrl;
|
|
||||||
icon.callback.call(parentController, item, parent, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCreate(parent) {
|
|
||||||
this.treeview.onCreate(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
onDrop(item, dragged, dropped) {
|
|
||||||
this.treeview.onDrop(item, dragged, dropped);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isInsertable() {
|
|
||||||
return Array.isArray(this.parent) || this.parent.childs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.component('vnTreeviewChild', {
|
|
||||||
template: require('./child.html'),
|
|
||||||
controller: Controller,
|
|
||||||
bindings: {
|
|
||||||
items: '<',
|
|
||||||
parent: '<',
|
|
||||||
icons: '<?',
|
|
||||||
disabled: '<?',
|
|
||||||
selectable: '<?',
|
|
||||||
editable: '<?',
|
|
||||||
draggable: '<?',
|
|
||||||
droppable: '<?',
|
|
||||||
aclRole: '<?',
|
|
||||||
parentScope: '<'
|
|
||||||
},
|
|
||||||
require: {
|
|
||||||
treeview: '^vnTreeview'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
<ul ng-if="$ctrl.items">
|
||||||
|
<li ng-repeat="item in $ctrl.items" >
|
||||||
|
<div
|
||||||
|
ng-class="{expanded: item.active}"
|
||||||
|
ng-click="$ctrl.onClick($event, item)"
|
||||||
|
class="node clickable">
|
||||||
|
<vn-icon
|
||||||
|
class="arrow"
|
||||||
|
ng-class="{invisible: !item.sons}"
|
||||||
|
icon="keyboard_arrow_down"
|
||||||
|
translate-attr="::{title: 'Toggle'}">
|
||||||
|
</vn-icon>
|
||||||
|
<vn-treeview-content
|
||||||
|
item="::item">
|
||||||
|
</vn-treeview-content>
|
||||||
|
<section class="buttons" ng-if="::!$ctrl.treeview.readOnly">
|
||||||
|
<vn-icon-button translate-attr="::{title: 'Remove'}"
|
||||||
|
icon="delete"
|
||||||
|
ng-click="$ctrl.treeview.onRemove(item)"
|
||||||
|
ng-if="item.parent">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-icon-button translate-attr="::{title: 'Create'}"
|
||||||
|
icon="add_circle"
|
||||||
|
ng-click="$ctrl.treeview.onCreate(item)">
|
||||||
|
</vn-icon-button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<vn-treeview-childs
|
||||||
|
items="item.childs"
|
||||||
|
parent="::item">
|
||||||
|
</vn-treeview-childs>
|
||||||
|
</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Component from '../../lib/component';
|
||||||
|
|
||||||
|
class Controller extends Component {
|
||||||
|
onClick(event, item) {
|
||||||
|
if (event.defaultPrevented || !item.sons) return;
|
||||||
|
event.preventDefault();
|
||||||
|
this.treeview.onToggle(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrop(item, dragged, dropped) {
|
||||||
|
this.treeview.onDrop(item, dragged, dropped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.component('vnTreeviewChilds', {
|
||||||
|
template: require('./childs.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
items: '<',
|
||||||
|
parent: '<?'
|
||||||
|
},
|
||||||
|
require: {
|
||||||
|
treeview: '^vnTreeview'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,39 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
constructor($element, $scope, $compile) {
|
||||||
|
this.$element = $element;
|
||||||
|
this.$scope = $scope;
|
||||||
|
this.$compile = $compile;
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
if (this.item.parent) {
|
||||||
|
this.treeview.$transclude(($clone, $scope) => {
|
||||||
|
this.$contentScope = $scope;
|
||||||
|
$scope.item = this.item;
|
||||||
|
this.$element.append($clone);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let template = `<span translate>{{$ctrl.treeview.rootLabel}}</span>`;
|
||||||
|
let $clone = this.$compile(template)(this.$scope);
|
||||||
|
this.$element.append($clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
if (this.$contentScope)
|
||||||
|
this.$contentScope.$destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controller.$inject = ['$element', '$scope', '$compile'];
|
||||||
|
|
||||||
|
ngModule.component('vnTreeviewContent', {
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
item: '<'
|
||||||
|
},
|
||||||
|
require: {
|
||||||
|
treeview: '^vnTreeview'
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,13 +1,3 @@
|
||||||
<vn-treeview-child
|
<vn-treeview-childs
|
||||||
acl-role="$ctrl.aclRole"
|
items="$ctrl.items">
|
||||||
items="$ctrl.data"
|
</vn-treeview-childs>
|
||||||
parent="$ctrl.data"
|
|
||||||
selectable="$ctrl.selectable"
|
|
||||||
editable="$ctrl.editable"
|
|
||||||
disabled="$ctrl.disabled"
|
|
||||||
icons="$ctrl.icons"
|
|
||||||
parent-scope="$ctrl.$scope.$parent"
|
|
||||||
draggable="$ctrl.draggable"
|
|
||||||
droppable="$ctrl.droppable"
|
|
||||||
vn-droppable="{{$ctrl.droppable}}">
|
|
||||||
</vn-treeview-child>
|
|
||||||
|
|
|
@ -2,73 +2,122 @@ import ngModule from '../../module';
|
||||||
import Component from '../../lib/component';
|
import Component from '../../lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
|
import './childs';
|
||||||
|
import './content';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Treeview
|
* Treeview
|
||||||
*
|
|
||||||
* @property {String} position The relative position to the parent
|
|
||||||
*/
|
*/
|
||||||
export default class Treeview extends Component {
|
export default class Treeview extends Component {
|
||||||
constructor($element, $scope) {
|
constructor($element, $scope, $transclude) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this.$scope = $scope;
|
this.$transclude = $transclude;
|
||||||
this.data = [];
|
this.readOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
get data() {
|
||||||
this.refresh();
|
return this._data;
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
set data(value) {
|
||||||
this.model.refresh().then(() => {
|
this._data = value;
|
||||||
this.data = this.model.data;
|
|
||||||
|
const sons = value.length;
|
||||||
|
const rootElement = [{
|
||||||
|
childs: value,
|
||||||
|
active: true,
|
||||||
|
sons: sons
|
||||||
|
}];
|
||||||
|
|
||||||
|
this.setParent(rootElement[0], value);
|
||||||
|
|
||||||
|
this.items = rootElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch() {
|
||||||
|
return this.fetchFunc().then(res =>
|
||||||
|
this.data = res
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setParent(parent, childs) {
|
||||||
|
childs.forEach(child => {
|
||||||
|
child.parent = parent;
|
||||||
|
|
||||||
|
if (child.childs)
|
||||||
|
this.setParent(parent, child.childs);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits selection event
|
|
||||||
* @param {Object} item - Selected item
|
|
||||||
* @param {Boolean} value - Changed value
|
|
||||||
*/
|
|
||||||
onSelection(item, value) {
|
|
||||||
this.emit('selection', {item, value});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCreate(parent) {
|
|
||||||
this.emit('create', {parent});
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggle(item) {
|
onToggle(item) {
|
||||||
if (item.active)
|
if (item.active)
|
||||||
item.childs = undefined;
|
this.fold(item);
|
||||||
else {
|
else
|
||||||
this.model.applyFilter({}, {parentFk: item.id}).then(() => {
|
this.unfold(item);
|
||||||
const newData = this.model.data;
|
}
|
||||||
|
|
||||||
if (item.childs) {
|
fold(item) {
|
||||||
let childs = item.childs;
|
item.childs = undefined;
|
||||||
childs.forEach(child => {
|
item.active = false;
|
||||||
let index = newData.findIndex(newChild => {
|
}
|
||||||
return newChild.id == child.id;
|
|
||||||
});
|
unfold(item) {
|
||||||
newData[index] = child;
|
return this.fetchFunc({$item: item}).then(newData => {
|
||||||
|
this.setParent(item, newData);
|
||||||
|
|
||||||
|
const childs = item.childs;
|
||||||
|
if (childs) {
|
||||||
|
childs.forEach(child => {
|
||||||
|
let index = newData.findIndex(newChild => {
|
||||||
|
return newChild.id == child.id;
|
||||||
});
|
});
|
||||||
}
|
newData[index] = child;
|
||||||
|
|
||||||
item.childs = newData.sort((a, b) => {
|
|
||||||
if (b.selected !== a.selected) {
|
|
||||||
if (a.selected == null)
|
|
||||||
return 1;
|
|
||||||
if (b.selected == null)
|
|
||||||
return -1;
|
|
||||||
return b.selected - a.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.name.localeCompare(b.name);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (this.sortFunc) {
|
||||||
|
item.childs = newData.sort((a, b) =>
|
||||||
|
this.sortFunc({$a: a, $b: b})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}).then(() => item.active = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(item) {
|
||||||
|
if (this.removeFunc)
|
||||||
|
this.removeFunc({$item: item});
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(item) {
|
||||||
|
const parent = item.parent;
|
||||||
|
let childs = parent.childs;
|
||||||
|
|
||||||
|
if (!childs) childs = [];
|
||||||
|
|
||||||
|
let index = childs.indexOf(item);
|
||||||
|
childs.splice(index, 1);
|
||||||
|
if (parent) parent.sons--;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCreate(parent) {
|
||||||
|
if (this.createFunc)
|
||||||
|
this.createFunc({$parent: parent});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(item) {
|
||||||
|
const parent = item.parent;
|
||||||
|
let childs = parent.childs;
|
||||||
|
if (!childs) childs = [];
|
||||||
|
|
||||||
|
childs.push(item);
|
||||||
|
|
||||||
|
if (this.sortFunc) {
|
||||||
|
childs = childs.sort((a, b) =>
|
||||||
|
this.sortFunc({$a: a, $b: b})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
item.active = !item.active;
|
if (parent) parent.sons++;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop(item, dragged, dropped) {
|
onDrop(item, dragged, dropped) {
|
||||||
|
@ -76,19 +125,21 @@ export default class Treeview extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Treeview.$inject = ['$element', '$scope'];
|
Treeview.$inject = ['$element', '$scope', '$transclude'];
|
||||||
|
|
||||||
ngModule.component('vnTreeview', {
|
ngModule.component('vnTreeview', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Treeview,
|
controller: Treeview,
|
||||||
bindings: {
|
bindings: {
|
||||||
model: '<',
|
rootLabel: '@',
|
||||||
icons: '<?',
|
data: '<?',
|
||||||
disabled: '<?',
|
|
||||||
selectable: '<?',
|
|
||||||
editable: '<?',
|
|
||||||
draggable: '<?',
|
draggable: '<?',
|
||||||
droppable: '<?',
|
droppable: '<?',
|
||||||
aclRole: '@?'
|
fetchFunc: '&',
|
||||||
}
|
removeFunc: '&?',
|
||||||
|
createFunc: '&?',
|
||||||
|
sortFunc: '&?',
|
||||||
|
readOnly: '<?'
|
||||||
|
},
|
||||||
|
transclude: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
@import "effects";
|
@import "effects";
|
||||||
|
|
||||||
vn-treeview {
|
vn-treeview-childs {
|
||||||
vn-treeview-child {
|
display: block;
|
||||||
display: block
|
|
||||||
}
|
|
||||||
ul {
|
ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -11,37 +10,43 @@ vn-treeview {
|
||||||
li {
|
li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
& > div > .arrow {
|
|
||||||
min-width: 24px;
|
|
||||||
margin-right: 10px;
|
|
||||||
transition: transform 200ms;
|
|
||||||
}
|
|
||||||
&.expanded > div > .arrow {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
& > .node {
|
& > .node {
|
||||||
@extend %clickable;
|
@extend %clickable;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
& > vn-check:not(.indeterminate) {
|
|
||||||
color: $color-main;
|
& > div > .arrow {
|
||||||
|
min-width: 24px;
|
||||||
& > .btn {
|
margin-right: 10px;
|
||||||
border-color: $color-main;
|
transition: transform 200ms;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
& > vn-check.checked {
|
& > div.expanded > .arrow {
|
||||||
color: $color-main;
|
transform: rotate(180deg);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
padding-left: 2.2em;
|
padding-left: 2.2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vn-icon-button {
|
vn-icon-button {
|
||||||
padding: 0
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node > .buttons {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.node:hover > .buttons {
|
||||||
|
display: block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vn-treeview-content {
|
||||||
|
flex-grow: 1
|
||||||
|
}
|
|
@ -223,11 +223,8 @@ export default class Watcher extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOriginalData() {
|
loadOriginalData() {
|
||||||
Object.keys(this.data).forEach(key => {
|
const orgData = JSON.parse(JSON.stringify(this.orgData));
|
||||||
delete this.data[key];
|
this.data = Object.assign(this.data, orgData);
|
||||||
});
|
|
||||||
|
|
||||||
this.data = Object.assign(this.data, this.orgData);
|
|
||||||
this.setPristine();
|
this.setPristine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import './id';
|
||||||
import './focus';
|
import './focus';
|
||||||
import './dialog';
|
import './dialog';
|
||||||
import './popover';
|
import './popover';
|
||||||
import './validation';
|
import './rule';
|
||||||
import './acl';
|
import './acl';
|
||||||
import './on-error-src';
|
import './on-error-src';
|
||||||
import './zoom-image';
|
import './zoom-image';
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import {validateAll} from '../lib/validator';
|
||||||
|
import {firstUpper} from '../lib/string';
|
||||||
|
|
||||||
|
directive.$inject = ['$translate', '$window'];
|
||||||
|
export function directive($translate, $window) {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: link,
|
||||||
|
require: {
|
||||||
|
ngModel: 'ngModel',
|
||||||
|
form: '^^?form'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function link($scope, $element, $attrs, $ctrl) {
|
||||||
|
let vnValidations = $window.validations;
|
||||||
|
if (!vnValidations) return;
|
||||||
|
|
||||||
|
if (!/^([A-Z]\w+(\.[a-z]\w*)?)?$/.test($attrs.rule))
|
||||||
|
throw new Error(`rule: Attribute must have this syntax: [ModelName[.fieldName]]`);
|
||||||
|
|
||||||
|
let rule = $attrs.rule.split('.');
|
||||||
|
let modelName = rule.shift();
|
||||||
|
let fieldName = rule.shift();
|
||||||
|
|
||||||
|
let split = $attrs.ngModel.split('.');
|
||||||
|
if (!fieldName) fieldName = split.pop() || null;
|
||||||
|
if (!modelName) modelName = firstUpper(split.pop() || '');
|
||||||
|
|
||||||
|
if (!modelName || !fieldName)
|
||||||
|
throw new Error(`rule: Cannot retrieve model or field attribute`);
|
||||||
|
|
||||||
|
let modelValidations = vnValidations[modelName];
|
||||||
|
|
||||||
|
if (!modelValidations)
|
||||||
|
throw new Error(`rule: Model '${modelName}' doesn't exist`);
|
||||||
|
|
||||||
|
let validations = modelValidations.validations[fieldName];
|
||||||
|
if (!validations || validations.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let ngModel = $ctrl.ngModel;
|
||||||
|
let form = $ctrl.form;
|
||||||
|
let field = $element[0].$ctrl;
|
||||||
|
let error;
|
||||||
|
|
||||||
|
function refreshError() {
|
||||||
|
if (!field) return;
|
||||||
|
let canShow = ngModel.$dirty || (form && form.$submitted);
|
||||||
|
field.error = error && canShow ? error.message : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModel.$options.$$options.allowInvalid = true;
|
||||||
|
ngModel.$validators.entity = value => {
|
||||||
|
try {
|
||||||
|
error = null;
|
||||||
|
validateAll($translate, value, validations);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshError();
|
||||||
|
return error == null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (form)
|
||||||
|
$scope.$watch(form.$submitted, refreshError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ngModule.directive('rule', directive);
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
describe('Directive rule', () => {
|
||||||
|
let $scope;
|
||||||
|
let $element;
|
||||||
|
let element;
|
||||||
|
|
||||||
|
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
||||||
|
$translateProvider.translations('en', {});
|
||||||
|
}));
|
||||||
|
|
||||||
|
function compile(html, value) {
|
||||||
|
inject(($compile, $rootScope, $window) => {
|
||||||
|
$window.validations = {Model:
|
||||||
|
{validations: {field: [{validation: 'absence'}]}}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope = $rootScope.$new();
|
||||||
|
|
||||||
|
$element = angular.element(html);
|
||||||
|
$compile($element)($scope);
|
||||||
|
element = $element[0];
|
||||||
|
|
||||||
|
$scope.model = {field: value};
|
||||||
|
$scope.$digest();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
$scope.$destroy();
|
||||||
|
$element.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Errors', () => {
|
||||||
|
it(`should throw an error if the rule doesn't have the right syntax`, () => {
|
||||||
|
expect(() => {
|
||||||
|
compile(`
|
||||||
|
<form>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ng-model="model.field"
|
||||||
|
rule="invalidLowerCamelCaseModel">
|
||||||
|
</input>
|
||||||
|
</form>
|
||||||
|
`);
|
||||||
|
}).toThrow(new Error(`rule: Attribute must have this syntax: [ModelName[.fieldName]]`));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if cannot retrieve model or field', () => {
|
||||||
|
expect(() => {
|
||||||
|
compile(`
|
||||||
|
<form>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ng-model="model"
|
||||||
|
rule>
|
||||||
|
</input>
|
||||||
|
</form>
|
||||||
|
`);
|
||||||
|
}).toThrow(new Error(`rule: Cannot retrieve model or field attribute`));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the model is not defined', () => {
|
||||||
|
expect(() => {
|
||||||
|
compile(`
|
||||||
|
<form>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ng-model="model.field"
|
||||||
|
rule="NonExistentModel.field">
|
||||||
|
</input>
|
||||||
|
</form>
|
||||||
|
`);
|
||||||
|
}).toThrow(new Error(`rule: Model 'NonExistentModel' doesn't exist`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Validator extended', () => {
|
||||||
|
let html = `
|
||||||
|
<form>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ng-model="model.field"
|
||||||
|
rule="Model.field">
|
||||||
|
</input>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
|
it('should not validate the entity as it has a wrong value', () => {
|
||||||
|
compile(html, 'invalidValue');
|
||||||
|
|
||||||
|
expect(element.classList).toContain('ng-invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the entity as it has a valid value', () => {
|
||||||
|
compile(html, '');
|
||||||
|
|
||||||
|
expect(element.classList).toContain('ng-valid');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Validator minimal', () => {
|
||||||
|
let html = `
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ng-model="model.field"
|
||||||
|
rule>
|
||||||
|
</input>
|
||||||
|
`;
|
||||||
|
|
||||||
|
it('should validate with empty rule and without specifying a parent form', () => {
|
||||||
|
compile(html, 'invalidValue');
|
||||||
|
|
||||||
|
expect(element.classList).toContain('ng-invalid');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,186 +0,0 @@
|
||||||
describe('Directive validation', () => {
|
|
||||||
let scope;
|
|
||||||
let element;
|
|
||||||
let compile;
|
|
||||||
|
|
||||||
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
|
||||||
$translateProvider.translations('en', {});
|
|
||||||
}));
|
|
||||||
|
|
||||||
compile = (_element, validations, value) => {
|
|
||||||
inject(($compile, $rootScope, $window) => {
|
|
||||||
$window.validations = validations;
|
|
||||||
scope = $rootScope.$new();
|
|
||||||
scope.user = {name: value};
|
|
||||||
element = angular.element(_element);
|
|
||||||
$compile(element)(scope);
|
|
||||||
scope.$digest();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
it(`should throw an error if the vnValidation doesn't have the right syntax`, () => {
|
|
||||||
let html = `<input type="name" ng-model="user.name" vn-validation="user"/>`;
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
compile(html, {});
|
|
||||||
}).toThrow(new Error(`vnValidation: Attribute must have this syntax: [entity].[field]`));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the window.validations aint defined', () => {
|
|
||||||
let html = `<input type="name" ng-model="user.name" vn-validation="user.name"/>`;
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
compile(html, {});
|
|
||||||
}).toThrow(new Error(`vnValidation: Entity 'User' doesn't exist`));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator presence()', () => {
|
|
||||||
it('should not validate the user name as it is an empty string', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'presence'}]}}};
|
|
||||||
compile(html, validations, 'Spiderman');
|
|
||||||
scope.user.name = '';
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator absence()', () => {
|
|
||||||
it('should not validate the entity as it should be an empty string', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'absence'}]}}};
|
|
||||||
compile(html, validations, 'Spiderman');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate the entity as it is an empty string', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'absence'}]}}};
|
|
||||||
compile(html, validations, '');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-valid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-invalid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator length()', () => {
|
|
||||||
it('should not validate the user name as it should have min length of 15', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'length', min: 10, max: 50, is: 15}]}}};
|
|
||||||
compile(html, validations, 'fifteen!');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate the user name as it has length of 15', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'length', min: 10, max: 50, is: 15}]}}};
|
|
||||||
compile(html, validations, 'fifteen length!');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-valid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-invalid');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not validate the user name as it should have min length of 10', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'length', min: 10}]}}};
|
|
||||||
compile(html, validations, 'shortname');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate the user name as its length is greater then the minimum', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'length', min: 10}]}}};
|
|
||||||
compile(html, validations, 'verylongname');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-valid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-invalid');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not validate the user name as its length is greater then the maximum', () => {
|
|
||||||
let html = `<form><input type="name" ng-model="user.name" vn-validation="user.name"/></form>`;
|
|
||||||
let validations = {User: {validations: {name: [{validation: 'length', max: 10}]}}};
|
|
||||||
compile(html, validations, 'toolongname');
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator numericality()', () => {
|
|
||||||
it('should not validate the phone number as it should a integer', () => {
|
|
||||||
let html = `<form><input type="text" ng-model="user.phone" vn-validation="user.phone"/></form>`;
|
|
||||||
let validations = {User: {validations: {phone: [{validation: 'numericality', is: 'what is this?'}]}}};
|
|
||||||
compile(html, validations, 'spiderman');
|
|
||||||
scope.user.phone = 'this is not a phone number!';
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate the phone number as it an integer', () => {
|
|
||||||
let html = `<form><input type="text" ng-model="user.phone" vn-validation="user.phone"/></form>`;
|
|
||||||
let validations = {User: {validations: {phone: [{validation: 'numericality', is: 'what is this?'}]}}};
|
|
||||||
compile(html, validations, 'spiderman');
|
|
||||||
scope.user.phone = '555555555';
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-valid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-invalid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator inclusion()', () => {
|
|
||||||
it('should not validate the phone number as it is not an integer', () => {
|
|
||||||
let html = `<form><input type="text" ng-model="user.phone" vn-validation="user.phone"/></form>`;
|
|
||||||
let validations = {User: {validations: {phone: [{validation: 'inclusion'}]}}};
|
|
||||||
compile(html, validations, 'spiderman');
|
|
||||||
scope.user.phone = 'this is not a phone number!';
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator exclusion()', () => {
|
|
||||||
it('should validate the phone number as it is an integer', () => {
|
|
||||||
let html = `<form><input type="text" ng-model="user.phone" vn-validation="user.phone"/></form>`;
|
|
||||||
let validations = {User: {validations: {phone: [{validation: 'exclusion'}]}}};
|
|
||||||
compile(html, validations, 'spiderman');
|
|
||||||
scope.user.phone = '555555555';
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-valid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-invalid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Validator format()', () => {
|
|
||||||
it('should not validate the email number as it doesnt contain @', () => {
|
|
||||||
let html = `<form><input type="text" ng-model="user.email" vn-validation="user.email"/></form>`;
|
|
||||||
let validations = {User: {validations: {email: [{validation: 'format', with: '@'}]}}};
|
|
||||||
compile(html, validations, 'spiderman');
|
|
||||||
scope.user.email = 'userverdnatura.es';
|
|
||||||
scope.$digest();
|
|
||||||
|
|
||||||
expect(element[0].classList).toContain('ng-invalid');
|
|
||||||
expect(element[0].classList).not.toContain('ng-valid');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -15,7 +15,7 @@
|
||||||
<vn-horizontal ng-repeat="field in fields">
|
<vn-horizontal ng-repeat="field in fields">
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one label="{{titles[field]}}"
|
vn-one label="{{titles[field]}}"
|
||||||
field="tableConfiguration.configuration[field]">
|
ng-model="tableConfiguration.configuration[field]">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import {validateAll} from '../lib/validator';
|
|
||||||
import {firstUpper} from '../lib/string';
|
|
||||||
|
|
||||||
directive.$inject = ['$interpolate', '$compile', '$translate', '$window'];
|
|
||||||
export function directive(interpolate, compile, $translate, $window) {
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
require: ['ngModel', '^^?form'],
|
|
||||||
link: link
|
|
||||||
};
|
|
||||||
|
|
||||||
function link(scope, element, attrs, ctrl) {
|
|
||||||
let vnValidations = $window.validations;
|
|
||||||
|
|
||||||
if (!attrs.vnValidation || !vnValidations)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let split = attrs.vnValidation.split('.');
|
|
||||||
|
|
||||||
if (split.length !== 2)
|
|
||||||
throw new Error(`vnValidation: Attribute must have this syntax: [entity].[field]`);
|
|
||||||
|
|
||||||
let entityName = firstUpper(split[0]);
|
|
||||||
let fieldName = split[1];
|
|
||||||
let entity = vnValidations[entityName];
|
|
||||||
|
|
||||||
if (!entity)
|
|
||||||
throw new Error(`vnValidation: Entity '${entityName}' doesn't exist`);
|
|
||||||
|
|
||||||
let validations = entity.validations[fieldName];
|
|
||||||
if (!validations || validations.length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let ngModel = ctrl[0];
|
|
||||||
let form = ctrl[1];
|
|
||||||
let errorSpan = angular.element('<span class="mdl-textfield__error"></span>');
|
|
||||||
let errorMsg;
|
|
||||||
let errorShown = false;
|
|
||||||
|
|
||||||
ngModel.$options.$$options.allowInvalid = true;
|
|
||||||
ngModel.$validators.entity = value => {
|
|
||||||
try {
|
|
||||||
validateAll($translate, value, validations);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
errorMsg = e.message;
|
|
||||||
if (errorShown) changeError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.$watch(() => {
|
|
||||||
return (form.$submitted || ngModel.$dirty) && ngModel.$invalid;
|
|
||||||
}, value => {
|
|
||||||
let parent = element.parent();
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
changeError();
|
|
||||||
parent.addClass('invalid');
|
|
||||||
element.after(errorSpan);
|
|
||||||
} else if (errorShown) {
|
|
||||||
parent.removeClass('invalid');
|
|
||||||
parent.removeAttr('title');
|
|
||||||
errorSpan.remove();
|
|
||||||
errorSpan.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorShown = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
function changeError() {
|
|
||||||
let parent = element.parent();
|
|
||||||
errorSpan.text(errorMsg);
|
|
||||||
parent.attr('title', errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ngModule.directive('vnValidation', directive);
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import './module-loader';
|
import './module-loader';
|
||||||
import './crud';
|
import './crud';
|
||||||
import './acl-service';
|
|
||||||
import './template';
|
|
||||||
import './copy';
|
import './copy';
|
||||||
import './equals';
|
import './equals';
|
||||||
import './modified';
|
import './modified';
|
||||||
|
|
|
@ -30,79 +30,13 @@ export default class Input extends Component {
|
||||||
if (this.mdlElement)
|
if (this.mdlElement)
|
||||||
this.mdlElement.updateClasses_();
|
this.mdlElement.updateClasses_();
|
||||||
}
|
}
|
||||||
|
|
||||||
get validationError() {
|
|
||||||
return this.input.validationMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a valid input value
|
|
||||||
*
|
|
||||||
* @return {Boolean} - True if has a valid value
|
|
||||||
*/
|
|
||||||
hasValidValue() {
|
|
||||||
return this.input.checkValidity();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the input element
|
|
||||||
* if has a validation error
|
|
||||||
*/
|
|
||||||
validateValue() {
|
|
||||||
if (!this.hasValidValue()) {
|
|
||||||
this.hideError();
|
|
||||||
this.showError();
|
|
||||||
} else
|
|
||||||
this.hideError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the input validation error
|
|
||||||
*/
|
|
||||||
showError() {
|
|
||||||
const infixElement = this.element.querySelector('.infix');
|
|
||||||
const infixClassList = infixElement.classList;
|
|
||||||
|
|
||||||
const errorSpan = document.createElement('span');
|
|
||||||
errorSpan.className = 'mdl-textfield__error';
|
|
||||||
|
|
||||||
const errorText = document.createTextNode(this.validationError);
|
|
||||||
|
|
||||||
errorSpan.append(errorText);
|
|
||||||
infixElement.append(errorSpan);
|
|
||||||
|
|
||||||
infixClassList.add('validated', 'invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides the input validation error
|
|
||||||
*/
|
|
||||||
hideError() {
|
|
||||||
const infixElement = this.element.querySelector('.infix');
|
|
||||||
const infixClassList = infixElement.classList;
|
|
||||||
const errorElement = this.element.querySelector('.infix span.mdl-textfield__error');
|
|
||||||
|
|
||||||
if (errorElement)
|
|
||||||
errorElement.remove();
|
|
||||||
|
|
||||||
infixClassList.remove('validated', 'invalid');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Input.$inject = ['$element', '$scope'];
|
Input.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
export const component = {
|
export const $options = {
|
||||||
transclude: {
|
|
||||||
leftIcons: '?tLeftIcons',
|
|
||||||
rightIcons: '?tRightIcons'
|
|
||||||
},
|
|
||||||
bindings: {
|
bindings: {
|
||||||
label: '@?',
|
label: '@?',
|
||||||
disabled: '<?',
|
disabled: '<?',
|
||||||
readonly: '<?',
|
readonly: '<?'
|
||||||
rule: '@?',
|
|
||||||
value: '=model',
|
|
||||||
vnTabIndex: '@?',
|
|
||||||
onChange: '&',
|
|
||||||
onClear: '&'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
import {validate} from '../validator.js';
|
||||||
|
|
||||||
|
describe('Validator', () => {
|
||||||
|
let $translate;
|
||||||
|
|
||||||
|
beforeEach(angular.mock.module('vnCore', $translateProvider => {
|
||||||
|
$translateProvider.translations('en', {});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(inject(_$translate_ => {
|
||||||
|
$translate = _$translate_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('presence', () => {
|
||||||
|
let conf = {validation: 'presence'};
|
||||||
|
|
||||||
|
it('should not validate the value as it should be defined', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, '', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should be defined', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'aDefinedValue', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('absence', () => {
|
||||||
|
let conf = {validation: 'absence'};
|
||||||
|
|
||||||
|
it('should not validate the value as it should be undefined', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'aDefinedValue', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should be undefined', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, '', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('length', () => {
|
||||||
|
it('should not validate the value as it should have an specific valid length', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'length',
|
||||||
|
is: 25
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'invalidSpecificLengthString', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should have an specific valid length', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'length',
|
||||||
|
is: 25
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'validSpecificLengthString', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not validate the value as it should have a min length', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'length',
|
||||||
|
min: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'shortName', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should have a min length', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'length',
|
||||||
|
min: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'veryLongName', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not validate the value as it should be smaller than the maximum', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'length',
|
||||||
|
max: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'chainThatExceedsMaxLength', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should be smaller than the maximum', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'length',
|
||||||
|
max: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'shortString', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('numericality', () => {
|
||||||
|
let conf = {validation: 'numericality'};
|
||||||
|
|
||||||
|
it('should not validate the value as it should be an integer', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'notANumber', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should be an integer', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, '123456789', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inclusion', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'inclusion',
|
||||||
|
in: ['firstValue', 'seekValue', 'lastValue']
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should not validate the value as it should be in array', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'notIncludedValue', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should be in array', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'seekValue', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('exclusion', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'exclusion',
|
||||||
|
in: ['firstValue', 'seekValue', 'lastValue']
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should not validate the value as it should not be in array', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'seekValue', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should not be in array', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'notIncludedValue', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('format', () => {
|
||||||
|
let conf = {
|
||||||
|
validation: 'format',
|
||||||
|
with: /[a-z-]+@[a-z-]+\.[a-z]+/
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should not validate the value as it should match regexp', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'wrongValue', conf);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the value as it should match regexp', () => {
|
||||||
|
expect(() => {
|
||||||
|
validate($translate, 'valid-value@domain.com', conf);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,7 +6,7 @@
|
||||||
* @return {String} The camelized string
|
* @return {String} The camelized string
|
||||||
*/
|
*/
|
||||||
export function kebabToCamel(str) {
|
export function kebabToCamel(str) {
|
||||||
var camelCased = str.replace(/-([a-z])/g, function(g) {
|
let camelCased = str.replace(/-([a-z])/g, function(g) {
|
||||||
return g[1].toUpperCase();
|
return g[1].toUpperCase();
|
||||||
});
|
});
|
||||||
return camelCased;
|
return camelCased;
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
|
|
||||||
export default class Template {
|
|
||||||
getNormalized(template, $attrs, defaults) {
|
|
||||||
this.normalizeInputAttrs($attrs);
|
|
||||||
return this.get(template, $attrs, defaults);
|
|
||||||
}
|
|
||||||
normalizeInputAttrs($attrs) {
|
|
||||||
const field = $attrs.field || $attrs.model;
|
|
||||||
const split = field.split('.');
|
|
||||||
const len = split.length;
|
|
||||||
|
|
||||||
let i = len - 1;
|
|
||||||
const fieldName = split[i--];
|
|
||||||
const entity = i >= 0 ? split[i--] : 'model';
|
|
||||||
const ctrl = i >= 0 ? split[i--] : '$ctrl';
|
|
||||||
|
|
||||||
if ($attrs.field) {
|
|
||||||
if (len == 0)
|
|
||||||
throw new Error(`Attribute 'field' can not be empty`);
|
|
||||||
if (len > 3)
|
|
||||||
throw new Error(`Attribute 'field' must have this syntax: [ctrl].[entity].[field]`);
|
|
||||||
|
|
||||||
if ($attrs.model === undefined)
|
|
||||||
$attrs.model = `${ctrl}.${entity}.${fieldName}`;
|
|
||||||
if ($attrs.rule === undefined && len >= 2)
|
|
||||||
$attrs.rule = `${entity}.${fieldName}`;
|
|
||||||
if ($attrs.label === undefined && len >= 2)
|
|
||||||
$attrs.label = `${entity}.${fieldName}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($attrs.name === undefined)
|
|
||||||
$attrs.name = fieldName;
|
|
||||||
|
|
||||||
if ($attrs.focus !== undefined)
|
|
||||||
$attrs.focus = 'vn-focus';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.service('vnTemplate', Template);
|
|
|
@ -4,6 +4,43 @@ const ngModule = ng.module('vnCore', ngDeps);
|
||||||
ngModule.constant('moment', require('moment-timezone'));
|
ngModule.constant('moment', require('moment-timezone'));
|
||||||
export default ngModule;
|
export default ngModule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acts like native Module.component() function but merging component options
|
||||||
|
* with parent component options. This method establishes the $options property
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param {String} name Coponent name in camelCase
|
||||||
|
* @param {Object} options The component options
|
||||||
|
* @return {angularModule} The same angular module
|
||||||
|
*/
|
||||||
|
ngModule.vnComponent = function(name, options) {
|
||||||
|
let controller = options.controller;
|
||||||
|
let parent = Object.getPrototypeOf(controller);
|
||||||
|
let parentOptions = parent.$options || {};
|
||||||
|
|
||||||
|
let mergedOptions = Object.assign({},
|
||||||
|
parentOptions,
|
||||||
|
options,
|
||||||
|
{
|
||||||
|
transclude: Object.assign({},
|
||||||
|
parentOptions.transclude,
|
||||||
|
options.transclude
|
||||||
|
),
|
||||||
|
bindings: Object.assign({},
|
||||||
|
parentOptions.bindings,
|
||||||
|
options.bindings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
controller.$options = mergedOptions;
|
||||||
|
|
||||||
|
return this.component(name, mergedOptions);
|
||||||
|
};
|
||||||
|
|
||||||
config.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
|
config.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
|
||||||
export function config($translateProvider, $translatePartialLoaderProvider) {
|
export function config($translateProvider, $translatePartialLoaderProvider) {
|
||||||
$translatePartialLoaderProvider.addPart('core');
|
$translatePartialLoaderProvider.addPart('core');
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves and loads the application configuration.
|
||||||
|
*/
|
||||||
|
export default class Config {
|
||||||
|
constructor($http, vnApp, $translate, $window) {
|
||||||
|
Object.assign(this, {
|
||||||
|
$http,
|
||||||
|
vnApp,
|
||||||
|
$translate,
|
||||||
|
storage: $window.localStorage,
|
||||||
|
user: {},
|
||||||
|
local: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.params = [
|
||||||
|
'warehouseFk',
|
||||||
|
'companyFk',
|
||||||
|
'bankFk'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
for (let param of this.params)
|
||||||
|
this.local[param] = this.getItem(param);
|
||||||
|
|
||||||
|
return this.$http.get('api/UserConfigs/getUserConfig')
|
||||||
|
.then(res => {
|
||||||
|
for (let param of this.params)
|
||||||
|
this.user[param] = res.data[param];
|
||||||
|
})
|
||||||
|
.finally(() => this.mergeParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeParams() {
|
||||||
|
for (let param of this.params) {
|
||||||
|
let local = this.local[param];
|
||||||
|
this[param] = local != null && local != ''
|
||||||
|
? local
|
||||||
|
: this.user[param];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(param, value) {
|
||||||
|
this.user[param] = value;
|
||||||
|
this.mergeParams();
|
||||||
|
|
||||||
|
let params = {[param]: value};
|
||||||
|
return this.$http.post('api/UserConfigs/setUserConfig', params)
|
||||||
|
.then(() => this.showSaved());
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocal(param, value) {
|
||||||
|
this.setItem(param, value);
|
||||||
|
this.local[param] = value;
|
||||||
|
this.mergeParams();
|
||||||
|
this.showSaved();
|
||||||
|
}
|
||||||
|
|
||||||
|
showSaved() {
|
||||||
|
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(key) {
|
||||||
|
let value = this.storage[key];
|
||||||
|
return value !== undefined ? JSON.parse(value) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(key, value) {
|
||||||
|
if (value == null)
|
||||||
|
this.storage.removeItem(key);
|
||||||
|
else
|
||||||
|
this.storage[key] = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Config.$inject = ['$http', 'vnApp', '$translate', '$window'];
|
||||||
|
|
||||||
|
ngModule.service('vnConfig', Config);
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
|
import './acl-service';
|
||||||
import './app';
|
import './app';
|
||||||
import './auth';
|
import './auth';
|
||||||
import './token';
|
import './token';
|
||||||
import './modules';
|
import './modules';
|
||||||
import './interceptor';
|
import './interceptor';
|
||||||
|
import './config';
|
||||||
|
|
|
@ -64,14 +64,6 @@ vn-app {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: $spacing-md;
|
padding: $spacing-md;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
|
|
||||||
form vn-horizontal {
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
padding: .2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vn-main-block {
|
vn-main-block {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -102,11 +94,6 @@ vn-app {
|
||||||
.content-block {
|
.content-block {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
||||||
form vn-horizontal {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: initial;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vn-main-block {
|
vn-main-block {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
@ -117,3 +104,37 @@ vn-app {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
form vn-horizontal {
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 2.8em;
|
||||||
|
padding: 0 $spacing-sm;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: $spacing-xs;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
padding-left: $spacing-xs;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
&:first-child:last-child {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $mobile-width) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: initial;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
&,
|
||||||
|
&:first-child,
|
||||||
|
&:last-child {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,6 @@ import './background/background';
|
||||||
import './side-menu/side-menu';
|
import './side-menu/side-menu';
|
||||||
import './left-menu/left-menu';
|
import './left-menu/left-menu';
|
||||||
import './topbar/topbar';
|
import './topbar/topbar';
|
||||||
import './user-configuration-popover';
|
import './user-popover';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
import './summary';
|
import './summary';
|
||||||
|
|
|
@ -3,20 +3,20 @@
|
||||||
<form name="form" ng-submit="$ctrl.submit()">
|
<form name="form" ng-submit="$ctrl.submit()">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="User"
|
label="User"
|
||||||
model="$ctrl.user"
|
ng-model="$ctrl.user"
|
||||||
name="user"
|
name="user"
|
||||||
vn-id="userField"
|
vn-id="userField"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Password"
|
label="Password"
|
||||||
model="$ctrl.password"
|
ng-model="$ctrl.password"
|
||||||
name="password"
|
name="password"
|
||||||
type="password">
|
type="password">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Do not close session"
|
label="Do not close session"
|
||||||
field="$ctrl.remember"
|
ng-model="$ctrl.remember"
|
||||||
name="remember">
|
name="remember">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
|
|
|
@ -32,10 +32,8 @@ vn-login {
|
||||||
& > form {
|
& > form {
|
||||||
& > vn-textfield {
|
& > vn-textfield {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 1em 0;
|
|
||||||
}
|
}
|
||||||
& > vn-check {
|
& > vn-check {
|
||||||
padding-top: .5em;
|
|
||||||
display: block;
|
display: block;
|
||||||
.md-label {
|
.md-label {
|
||||||
white-space: inherit;
|
white-space: inherit;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
ng-click="$ctrl.openUserConfiguration($event)"
|
ng-click="userPopover.show($event)"
|
||||||
id="user"
|
id="user"
|
||||||
class="unselectable">
|
class="unselectable">
|
||||||
{{$root.user.nickname}}
|
{{$root.user.nickname}}
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
id="logout"
|
id="logout"
|
||||||
icon="exit_to_app"
|
icon="exit_to_app"
|
||||||
translate-attr="{title: 'Logout'}"
|
translate-attr="{title: 'Logout'}"
|
||||||
ng-click="$ctrl.onLogoutClick()">
|
ng-click="$ctrl.vnAuth.logout()">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<vn-menu vn-id="apps-menu">
|
<vn-menu vn-id="apps-menu">
|
||||||
|
@ -26,6 +26,6 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</vn-menu>
|
</vn-menu>
|
||||||
<vn-user-configuration-popover
|
<vn-user-popover
|
||||||
vn-id="popover">
|
vn-id="user-popover">
|
||||||
</vn-user-configuration-popover>
|
</vn-user-popover>
|
|
@ -21,14 +21,6 @@ export default class MainMenu {
|
||||||
window.localStorage.currentUserWorkerId = json.data.workerId;
|
window.localStorage.currentUserWorkerId = json.data.workerId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openUserConfiguration(event) {
|
|
||||||
this.$.popover.show(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onLogoutClick() {
|
|
||||||
this.vnAuth.logout();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MainMenu.$inject = ['$scope', '$http', 'vnAuth', 'vnModules'];
|
MainMenu.$inject = ['$scope', '$http', 'vnAuth', 'vnModules'];
|
||||||
|
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
import ngModule from '../../module';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
let languages = {
|
|
||||||
es: 'Español',
|
|
||||||
en: 'English',
|
|
||||||
ca: 'Català',
|
|
||||||
pt: 'Português',
|
|
||||||
fr: 'Français',
|
|
||||||
nl: 'Nederlands',
|
|
||||||
mn: 'Монгол хэл'
|
|
||||||
};
|
|
||||||
|
|
||||||
class Controller {
|
|
||||||
constructor($scope, $http, $state, vnApp, $translate) {
|
|
||||||
this.$scope = $scope;
|
|
||||||
this.$http = $http;
|
|
||||||
this.$state = $state;
|
|
||||||
this.vnApp = vnApp;
|
|
||||||
this.$translate = $translate;
|
|
||||||
this.getUserConfig();
|
|
||||||
|
|
||||||
this.lang = $translate.use();
|
|
||||||
this.langs = [];
|
|
||||||
|
|
||||||
for (let code of $translate.getAvailableLanguageKeys()) {
|
|
||||||
this.langs.push({
|
|
||||||
code: code,
|
|
||||||
name: languages[code] ? languages[code] : code
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
if (window.localStorage.localBankFk && window.localStorage.localBankFk !== 'null')
|
|
||||||
window.localStorage.defaultBankFk = window.localStorage.localBankFk;
|
|
||||||
else
|
|
||||||
localStorage.removeItem('defaultBankFk');
|
|
||||||
}
|
|
||||||
|
|
||||||
set lang(value) {
|
|
||||||
this._lang = value;
|
|
||||||
this.$translate.use(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get lang() {
|
|
||||||
return this._lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
set localBankFk(value) {
|
|
||||||
window.localStorage.localBankFk = value;
|
|
||||||
window.localStorage.defaultBankFk = value;
|
|
||||||
this.showOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
get localBankFk() {
|
|
||||||
return parseInt(window.localStorage.localBankFk);
|
|
||||||
}
|
|
||||||
|
|
||||||
set localWarehouseFk(value) {
|
|
||||||
window.localStorage.localWarehouseFk = value;
|
|
||||||
if (!value && this.warehouseFk)
|
|
||||||
window.localStorage.defaultWarehouseFk = this.warehouseFk;
|
|
||||||
else
|
|
||||||
window.localStorage.defaultWarehouseFk = value;
|
|
||||||
this.showOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
get localWarehouseFk() {
|
|
||||||
return parseInt(window.localStorage.localWarehouseFk);
|
|
||||||
}
|
|
||||||
|
|
||||||
set localCompanyFk(value) {
|
|
||||||
window.localStorage.localCompanyFk = value;
|
|
||||||
if (!value && this.companyFk)
|
|
||||||
window.localStorage.defaultCompanyFk = this.companyFk;
|
|
||||||
else
|
|
||||||
window.localStorage.defaultCompanyFk = value;
|
|
||||||
this.showOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
get localCompanyFk() {
|
|
||||||
return parseInt(window.localStorage.localCompanyFk);
|
|
||||||
}
|
|
||||||
|
|
||||||
set warehouseFk(value) {
|
|
||||||
this.warehouse = value;
|
|
||||||
if (value &&
|
|
||||||
(!window.localStorage.localWarehouseFk || window.localStorage.localWarehouseFk === 'null'))
|
|
||||||
window.localStorage.defaultWarehouseFk = value;
|
|
||||||
this.setUserConfig('warehouseFk', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get warehouseFk() {
|
|
||||||
return this.warehouse;
|
|
||||||
}
|
|
||||||
|
|
||||||
set companyFk(value) {
|
|
||||||
this.company = value;
|
|
||||||
if (value &&
|
|
||||||
(!window.localStorage.localCompanyFk || window.localStorage.localCompanyFk === 'null'))
|
|
||||||
window.localStorage.setItem('defaultCompanyFk', value);
|
|
||||||
this.setUserConfig('companyFk', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get companyFk() {
|
|
||||||
return this.company;
|
|
||||||
}
|
|
||||||
|
|
||||||
showOk() {
|
|
||||||
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
|
|
||||||
}
|
|
||||||
|
|
||||||
show(event) {
|
|
||||||
this.$scope.warehouses.refresh();
|
|
||||||
this.$scope.companies.refresh();
|
|
||||||
this.$scope.popover.parent = event.target;
|
|
||||||
this.$scope.popover.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserConfig() {
|
|
||||||
this.$http.get('/api/UserConfigs/getUserConfig')
|
|
||||||
.then(res => {
|
|
||||||
if (res.data && res.data.warehouseFk) {
|
|
||||||
this.warehouse = res.data.warehouseFk;
|
|
||||||
if (res.data.warehouseFk && !window.localStorage.localWarehouseFk)
|
|
||||||
window.localStorage.setItem('defaultWarehouseFk', res.data.warehouseFk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.data && res.data.companyFk) {
|
|
||||||
this.company = res.data.companyFk;
|
|
||||||
if (res.data.companyFk && !window.localStorage.localCompanyFk)
|
|
||||||
window.localStorage.setItem('defaultCompanyFk', res.data.companyFk);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setUserConfig(property, value) {
|
|
||||||
let params = {};
|
|
||||||
params[property] = value;
|
|
||||||
|
|
||||||
this.$http.post('/api/UserConfigs/setUserConfig', params)
|
|
||||||
.then(() => {
|
|
||||||
this.showOk();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
searchLocalBank(a, b) {
|
|
||||||
return angular.equals(a.id, b.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.$inject = ['$scope', '$http', '$state', 'vnApp', '$translate'];
|
|
||||||
|
|
||||||
ngModule.component('vnUserConfigurationPopover', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller
|
|
||||||
});
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue