Tests fixed

This commit is contained in:
Juan Ferrer 2019-10-18 21:36:30 +02:00
parent dbe6161c69
commit 457034daad
133 changed files with 1259 additions and 1702 deletions

View File

@ -1,6 +0,0 @@
export default {
vnTextfield: 'vn-textfield input',
vnInputNumber: 'vn-input-number input',
vnSubmit: 'vn-submit > input',
vnFloatButton: 'vn-float-button > button'
};

View File

@ -15,6 +15,16 @@ let asyncActions = {
return exists;
},
hasClass: async function(selector, className) {
return await this.evaluate((selector, className) => {
document.querySelector(selector).classList.contains(className);
}, selector, className);
},
parsedUrl: async function() {
return new URL(await this.url());
},
// Salix specific extensions
changeLanguageToEnglish: async function() {
@ -28,6 +38,15 @@ let asyncActions = {
await this.autocompleteSearch(langSelector, 'English');
},
doLogin: async function(userName, password) {
if (password == null) password = 'nightmare';
await this.wait(`vn-login [name=user]`)
.clearInput(`vn-login [name=user]`)
.write(`vn-login [name=user]`, userName)
.write(`vn-login [name=password]`, password)
.click(`vn-login button[type=submit]`);
},
login: async function(userName) {
if (currentUser !== userName) {
let logoutClicked = await this.clickIfExists('#logout');
@ -47,11 +66,7 @@ let asyncActions = {
this.goto(`${config.url}/#!/login`);
}
await 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=password]`, 'nightmare')
.click(`vn-login input[type=submit]`)
await this.doLogin(userName, null)
.waitForURL('#!/')
.changeLanguageToEnglish();
@ -99,6 +114,10 @@ let asyncActions = {
input.value = time;
input.dispatchEvent(new Event('change'));
}, selector, time);
},
isDisabled: async function(selector) {
return await this.hasClass(selector, 'disabled');
}
};
@ -124,21 +143,6 @@ let actions = {
.catch(done);
},
resetLogin: function(done) {
this.then(() => {
currentUser = undefined;
done();
})
.catch(done);
},
parsedUrl: function(done) {
this.url()
.then(url => {
done(null, new URL(url));
}).catch(done);
},
getProperty: function(selector, property, done) {
this.evaluate_now((selector, property) => {
return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim();

View File

@ -1,4 +1,3 @@
import components from './components_selectors.js';
export default {
globalItems: {
@ -22,29 +21,29 @@ export default {
acceptButton: 'vn-confirm button[response=ACCEPT]'
},
clientsIndex: {
searchClientInput: `${components.vnTextfield}`,
searchClientInput: `vn-textfield input`,
searchButton: 'vn-searchbar vn-icon[icon="search"]',
searchResult: 'vn-client-index .vn-list-item',
createClientButton: `${components.vnFloatButton}`,
createClientButton: `vn-float-button`,
othersButton: 'vn-left-menu li[name="Others"] > a'
},
createClientView: {
name: `${components.vnTextfield}[name="name"]`,
taxNumber: `${components.vnTextfield}[name="fi"]`,
socialName: `${components.vnTextfield}[name="socialName"]`,
street: `${components.vnTextfield}[name="street"]`,
postcode: `${components.vnTextfield}[name="postcode"]`,
city: `${components.vnTextfield}[name="city"]`,
name: `vn-textfield input[name="name"]`,
taxNumber: `vn-textfield input[name="fi"]`,
socialName: `vn-textfield input[name="socialName"]`,
street: `vn-textfield input[name="street"]`,
postcode: `vn-textfield input[name="postcode"]`,
city: `vn-textfield input[name="city"]`,
province: `vn-autocomplete[ng-model="$ctrl.client.provinceFk"]`,
country: `vn-autocomplete[ng-model="$ctrl.client.countryFk"]`,
userName: `${components.vnTextfield}[name="userName"]`,
email: `${components.vnTextfield}[name="email"]`,
userName: `vn-textfield input[name="userName"]`,
email: `vn-textfield input[name="email"]`,
salesPersonAutocomplete: `vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]`,
createButton: `${components.vnSubmit}`,
createButton: `button[type=submit]`,
cancelButton: 'vn-button[href="#!/client/index"]'
},
clientDescriptor: {
moreMenu: 'vn-client-descriptor vn-icon-menu > div > vn-icon',
moreMenu: 'vn-client-descriptor vn-icon-menu[icon=more_vert]',
simpleTicketButton: '.vn-popover.shown .vn-drop-down li'
},
clientBasicData: {
@ -56,17 +55,17 @@ export default {
emailInput: 'vn-textfield[ng-model="$ctrl.client.email"] input',
salesPersonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
channelAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.contactChannelFk"]',
saveButton: `${components.vnSubmit}`
saveButton: `button[type=submit]`
},
clientFiscalData: {
fiscalDataButton: 'vn-left-menu a[ui-sref="client.card.fiscalData"]',
socialNameInput: `${components.vnTextfield}[name="socialName"]`,
fiscalIdInput: `${components.vnTextfield}[name="fi"]`,
socialNameInput: `vn-textfield input[name="socialName"]`,
fiscalIdInput: `vn-textfield input[name="fi"]`,
equalizationTaxCheckbox: 'vn-check[label="Is equalizated"]',
acceptPropagationButton: 'vn-client-fiscal-data > vn-confirm button[response=ACCEPT]',
addressInput: `${components.vnTextfield}[name="street"]`,
postcodeInput: `${components.vnTextfield}[name="postcode"]`,
cityInput: `${components.vnTextfield}[name="city"]`,
addressInput: `vn-textfield input[name="street"]`,
postcodeInput: `vn-textfield input[name="postcode"]`,
cityInput: `vn-textfield input[name="city"]`,
provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.provinceFk"]',
countryAutocomplete: 'vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
activeCheckbox: 'vn-check[label="Active"]',
@ -76,12 +75,12 @@ export default {
hasToInvoiceCheckbox: 'vn-check[label="Has to invoice"]',
invoiceByMailCheckbox: 'vn-check[label="Invoice by mail"]',
viesCheckbox: 'vn-check[label="Vies"]',
saveButton: `${components.vnSubmit}`
saveButton: `button[type=submit]`
},
clientBillingData: {
payMethodAutocomplete: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.payMethodFk"]',
IBANInput: `vn-client-billing-data ${components.vnTextfield}[name="iban"]`,
dueDayInput: `vn-client-billing-data ${components.vnInputNumber}[name="dueDay"]`,
IBANInput: `vn-client-billing-data vn-textfield input[name="iban"]`,
dueDayInput: `vn-client-billing-data vn-input-number input[name="dueDay"]`,
receivedCoreLCRCheckbox: 'vn-client-billing-data vn-check[label="Received LCR"]',
receivedCoreVNLCheckbox: 'vn-client-billing-data vn-check[label="Received core VNL"]',
receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]',
@ -92,20 +91,20 @@ export default {
newBankEntityBIC: 'vn-client-billing-data > vn-dialog vn-textfield[label="Swift / BIC"] input',
newBankEntityCode: 'vn-client-billing-data > vn-dialog vn-textfield[label="Entity Code"] input',
acceptBankEntityButton: 'vn-client-billing-data > vn-dialog button[response="ACCEPT"]',
saveButton: `${components.vnSubmit}`
saveButton: `button[type=submit]`
},
clientAddresses: {
addressesButton: 'vn-left-menu a[ui-sref="client.card.address.index"]',
createAddress: `vn-client-address-index ${components.vnFloatButton}`,
createAddress: `vn-client-address-index vn-float-button`,
defaultCheckboxInput: 'vn-check[label="Default"]',
consigneeInput: `${components.vnTextfield}[name="nickname"]`,
streetAddressInput: `${components.vnTextfield}[name="street"]`,
postcodeInput: `${components.vnTextfield}[name="postalCode"]`,
cityInput: `${components.vnTextfield}[name="city"]`,
consigneeInput: `vn-textfield input[name="nickname"]`,
streetAddressInput: `vn-textfield input[name="street"]`,
postcodeInput: `vn-textfield input[name="postalCode"]`,
cityInput: `vn-textfield input[name="city"]`,
provinceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.address.provinceFk"]',
agencyAutocomplete: 'vn-autocomplete[ng-model="$ctrl.address.agencyModeFk"]',
phoneInput: `${components.vnTextfield}[name="phone"]`,
mobileInput: `${components.vnTextfield}[name="mobile"]`,
phoneInput: `vn-textfield input[name="phone"]`,
mobileInput: `vn-textfield input[name="mobile"]`,
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
secondMakeDefaultStar: 'vn-client-address-index vn-card div:nth-child(2) vn-icon-button[icon="star_border"]',
firstEditAddress: 'vn-client-address-index div:nth-child(1) > a',
@ -117,34 +116,34 @@ export default {
secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [ng-model="observation.observationTypeFk"]',
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"]',
saveButton: `${components.vnSubmit}`,
saveButton: `button[type=submit]`,
cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]',
cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button'
},
clientWebAccess: {
webAccessButton: 'vn-left-menu a[ui-sref="client.card.webAccess"]',
enableWebAccessCheckbox: 'vn-check[label="Enable web access"]',
userNameInput: `${components.vnTextfield}[name="name"]`,
saveButton: `${components.vnSubmit}`
userNameInput: `vn-textfield input[name="name"]`,
saveButton: `button[type=submit]`
},
clientNotes: {
addNoteFloatButton: `${components.vnFloatButton}`,
addNoteFloatButton: `vn-float-button`,
noteInput: 'vn-textarea[label="Note"]',
saveButton: `${components.vnSubmit}`,
saveButton: `button[type=submit]`,
firstNoteText: 'vn-client-note .text'
},
clientCredit: {
addCreditFloatButton: `${components.vnFloatButton}`,
creditInput: `${components.vnInputNumber}[name="credit"]`,
saveButton: `${components.vnSubmit}`,
addCreditFloatButton: `vn-float-button`,
creditInput: `vn-input-number input[name="credit"]`,
saveButton: `button[type=submit]`,
firstCreditText: 'vn-client-credit-index vn-card > div vn-table vn-tbody > vn-tr'
},
clientGreuge: {
addGreugeFloatButton: `${components.vnFloatButton}`,
amountInput: `${components.vnInputNumber}[name="amount"]`,
descriptionInput: `${components.vnTextfield}[name="description"]`,
addGreugeFloatButton: `vn-float-button`,
amountInput: `vn-input-number input[name="amount"]`,
descriptionInput: `vn-textfield input[name="description"]`,
typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.greuge.greugeTypeFk"]',
saveButton: `${components.vnSubmit}`,
saveButton: `button[type=submit]`,
firstGreugeText: 'vn-client-greuge-index vn-card > div vn-table vn-tbody > vn-tr'
},
clientMandate: {
@ -163,7 +162,7 @@ export default {
clientBalance: {
balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
companyAutocomplete: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyFk"]',
newPaymentButton: `${components.vnFloatButton}`,
newPaymentButton: `vn-float-button`,
newPaymentBank: 'vn-client-balance-create vn-autocomplete[ng-model="$ctrl.receipt.bankFk"]',
newPaymentAmountInput: 'vn-client-balance-create vn-input-number[ng-model="$ctrl.receipt.amountPaid"] input',
saveButton: 'vn-client-balance-create vn-button[label="Save"]',
@ -182,7 +181,7 @@ export default {
},
itemsIndex: {
searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]',
createItemButton: `${components.vnFloatButton}`,
createItemButton: `vn-float-button`,
searchResult: 'vn-item-index a.vn-tr',
searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]',
searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
@ -208,16 +207,16 @@ export default {
saveFieldsButton: 'vn-item-index vn-dialog vn-horizontal:nth-child(16) > vn-button > button'
},
itemCreateView: {
temporalName: `${components.vnTextfield}[name="provisionalName"]`,
temporalName: `vn-textfield input[name="provisionalName"]`,
typeAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
intrastatAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
originAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
createButton: `${components.vnSubmit}`,
cancelButton: 'button[ui-sref="item.index"]'
createButton: `button[type=submit]`,
cancelButton: 'vn-button[ui-sref="item.index"]'
},
itemDescriptor: {
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[icon=more_vert]',
moreMenuRegularizeButton: '.vn-popover.shown .vn-drop-down li[name="Regularize stock"]',
regularizeQuantityInput: 'vn-item-descriptor vn-dialog tpl-body > div > vn-textfield input',
regularizeWarehouseAutocomplete: 'vn-item-descriptor vn-dialog vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
@ -238,7 +237,7 @@ export default {
longNameInput: 'vn-textfield[ng-model="$ctrl.item.longName"] input',
isActiveCheckbox: 'vn-check[label="Active"]',
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
submitBasicDataButton: `${components.vnSubmit}`
submitBasicDataButton: `button[type=submit]`
},
itemTags: {
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
@ -257,19 +256,19 @@ export default {
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',
addItemTagButton: 'vn-item-tags vn-icon-button[icon="add_circle"]',
submitItemTagsButton: `vn-item-tags ${components.vnSubmit}`
submitItemTagsButton: `vn-item-tags button[type=submit]`
},
itemTax: {
undoChangesButton: 'vn-item-tax vn-button-bar > vn-button[label="Undo changes"]',
firstClassAutocomplete: 'vn-item-tax vn-horizontal:nth-child(1) > vn-autocomplete[ng-model="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 button[type=submit]`
},
itemBarcodes: {
addBarcodeButton: 'vn-item-barcode vn-icon[icon="add_circle"]',
thirdCodeInput: `vn-item-barcode vn-horizontal:nth-child(3) > ${components.vnTextfield}`,
submitBarcodesButton: `vn-item-barcode ${components.vnSubmit}`,
thirdCodeInput: `vn-item-barcode vn-horizontal:nth-child(3) > vn-textfield input`,
submitBarcodesButton: `vn-item-barcode button[type=submit]`,
firstCodeRemoveButton: 'vn-item-barcode vn-horizontal vn-none vn-icon[icon="delete"]'
},
itemNiches: {
@ -281,13 +280,13 @@ export default {
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[ng-model="niche.warehouseFk"]',
thirdCodeInput: 'vn-item-niche vn-horizontal:nth-child(3) > vn-textfield[label="Code"] input',
submitNichesButton: `vn-item-niche ${components.vnSubmit}`
submitNichesButton: `vn-item-niche button[type=submit]`
},
itemBotanical: {
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > ${components.vnTextfield}`,
botanicalInput: `vn-item-botanical vn-horizontal:nth-child(1) > vn-textfield input`,
genusAutocomplete: 'vn-item-botanical vn-autocomplete[ng-model="$ctrl.botanical.genusFk"]',
speciesAutocomplete: 'vn-item-botanical vn-autocomplete[ng-model="$ctrl.botanical.specieFk"]',
submitBotanicalButton: `vn-item-botanical ${components.vnSubmit}`
submitBotanicalButton: `vn-item-botanical button[type=submit]`
},
itemSummary: {
basicData: 'vn-item-summary [name="basicData"]',
@ -330,13 +329,13 @@ export default {
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',
searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
searchWeeklyTicketInput: `vn-ticket-weekly-index ${components.vnTextfield}`,
searchTicketInput: `vn-ticket-index vn-textfield input`,
searchWeeklyTicketInput: `vn-ticket-weekly-index vn-textfield input`,
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 button[type=submit]',
searchButton: 'vn-ticket-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[icon=more_vert]',
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[ng-model="weekly.weekDay"] input',
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr',
@ -349,13 +348,13 @@ export default {
deliveryDateInput: 'vn-ticket-create vn-date-picker[ng-model="$ctrl.landed"]',
warehouseAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.warehouseFk"]',
agencyAutocomplete: 'vn-ticket-create vn-autocomplete[ng-model="$ctrl.ticket.agencyModeFk"]',
createButton: `${components.vnSubmit}`
createButton: `button[type=submit]`
},
ticketDescriptor: {
idLabelValue: 'vn-ticket-descriptor vn-label-value[label="Id"]',
stateLabelValue: 'vn-ticket-descriptor vn-label-value[label="State"]',
goBackToModuleIndexButton: 'vn-ticket-descriptor a[ui-sref="ticket.index"]',
moreMenu: 'vn-ticket-descriptor vn-icon-menu > div > vn-icon',
moreMenu: 'vn-ticket-descriptor vn-icon-menu[icon=more_vert]',
moreMenuAddStowaway: '.vn-popover.shown .vn-drop-down li[name="Add stowaway"]',
moreMenuDeleteStowawayButton: '.vn-popover.shown .vn-drop-down li[name="Remove stowaway"]',
moreMenuAddToTurn: '.vn-popover.shown .vn-drop-down li[name="Add turn"]',
@ -365,7 +364,7 @@ export default {
changeShippedHourDialog: 'vn-ticket-descriptor vn-dialog[vn-id="changeShippedDialog"]',
changeShippedHourInput: 'vn-dialog[vn-id="changeShippedDialog"] [ng-model="$ctrl.newShipped"]',
addStowawayDialogFirstTicket: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog vn-table vn-tbody vn-tr',
shipButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks vn-icon[icon="icon-stowaway"]',
shipButton: 'vn-ticket-descriptor vn-icon[icon="icon-stowaway"]',
thursdayButton: 'vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(4)',
saturdayButton: 'vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)',
closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]',
@ -380,7 +379,7 @@ export default {
addNoteButton: 'vn-icon[icon="add_circle"]',
firstNoteTypeAutocomplete: 'vn-autocomplete[ng-model="observation.observationTypeFk"]',
firstDescriptionInput: 'vn-textfield[label="Description"] input',
submitNotesButton: `${components.vnSubmit}`
submitNotesButton: `button[type=submit]`
},
ticketExpedition: {
expeditionButton: 'vn-left-menu a[ui-sref="ticket.card.expedition"]',
@ -395,7 +394,7 @@ export default {
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] .icons > vn-icon[icon=clear]',
savePackagesButton: `${components.vnSubmit}`
savePackagesButton: `button[type=submit]`
},
ticketSales: {
saleButton: 'vn-left-menu a[ui-sref="ticket.card.sale"]',
@ -460,9 +459,9 @@ export default {
},
ticketTracking: {
trackingButton: 'vn-left-menu a[ui-sref="ticket.card.tracking.index"]',
createStateButton: `${components.vnFloatButton}`,
createStateButton: `vn-float-button`,
stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[ng-model="$ctrl.stateFk"]',
saveButton: `${components.vnSubmit}`,
saveButton: `button[type=submit]`,
cancelButton: 'vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]'
},
ticketBasicData: {
@ -471,8 +470,8 @@ export default {
addressAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.addressFk"]',
agencyAutocomplete: 'vn-autocomplete[ng-model="$ctrl.agencyModeId"]',
zoneAutocomplete: 'vn-autocomplete[ng-model="$ctrl.zoneId"]',
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',
nextStepButton: 'vn-step-control .buttons > section:last-child vn-button',
finalizeButton: 'vn-step-control .buttons > section:last-child button[type=submit]',
stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > div > vn-horizontal > table > tfoot > tr > td:nth-child(4)',
chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]',
},
@ -487,7 +486,7 @@ export default {
quantityInput: 'vn-ticket-request-create vn-input-number input[name=quantity]',
priceInput: 'vn-ticket-request-create vn-input-number input[name=price]',
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 button[type=submit]',
firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2)',
},
@ -499,7 +498,7 @@ export default {
},
ticketService: {
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"]',
firstDescriptionAutocomplete: 'vn-ticket-service vn-autocomplete[ng-model="service.description"]',
firstQuantityInput: 'vn-ticket-service vn-input-number[label="Quantity"] input',
firstPriceInput: 'vn-ticket-service vn-input-number[label="Price"] input',
@ -507,22 +506,22 @@ export default {
fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]',
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',
saveServiceButton: `${components.vnSubmit}`,
saveServiceButton: `button[type=submit]`,
saveDescriptionButton: 'vn-ticket-service > vn-dialog[vn-id="createServiceTypeDialog"] > div > form > div.buttons > tpl-buttons > button'
},
createStateView: {
stateAutocomplete: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
workerAutocomplete: 'vn-autocomplete[ng-model="$ctrl.workerFk"]',
clearStateInputButton: 'vn-autocomplete[ng-model="$ctrl.stateFk"] .icons > vn-icon[icon=clear]',
saveStateButton: `${components.vnSubmit}`
saveStateButton: `button[type=submit]`
},
claimsIndex: {
searchClaimInput: `vn-claim-index ${components.vnTextfield}`,
searchClaimInput: `vn-claim-index vn-textfield input`,
searchResult: 'vn-claim-index vn-card > div > vn-table > div > vn-tbody > a',
searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]'
},
claimDescriptor: {
moreMenu: 'vn-claim-descriptor vn-icon-menu[vn-id="more-button"]',
moreMenu: 'vn-claim-descriptor vn-icon-menu[icon=more_vert]',
moreMenuDeleteClaim: '.vn-popover.shown .vn-drop-down li[name="Delete claim"]',
acceptDeleteClaim: 'vn-claim-descriptor > vn-confirm[vn-id="confirm-delete-claim"] button[response="ACCEPT"]'
},
@ -541,9 +540,9 @@ export default {
},
claimBasicData: {
claimStateAutocomplete: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]',
responsabilityInputRange: 'vn-input-range',
responsabilityInputRange: 'vn-range',
observationInput: 'vn-textarea[ng-model="$ctrl.claim.observation"] textarea',
saveButton: `${components.vnSubmit}`
saveButton: `button[type=submit]`
},
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',
@ -569,7 +568,7 @@ export default {
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[ng-model="claimDevelopment.workerFk"]',
secondClaimRedeliveryAutocomplete: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
saveDevelopmentButton: `${components.vnSubmit}`
saveDevelopmentButton: `button[type=submit]`
},
claimAction: {
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
@ -584,9 +583,9 @@ export default {
searchResult: 'vn-order-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
searchResultDate: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)',
searchResultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)',
searchOrderInput: `vn-order-index ${components.vnTextfield}`,
searchOrderInput: `vn-order-index vn-textfield input`,
searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]',
createOrderButton: `${components.vnFloatButton}`,
createOrderButton: `vn-float-button`,
},
orderDescriptor: {
returnToModuleIndexButton: 'vn-order-descriptor a[ui-sref="order.index"]',
@ -597,7 +596,7 @@ export default {
addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
landedDatePicker: 'vn-date-picker[label="Landed"]',
createButton: `${components.vnSubmit}`,
createButton: `button[type=submit]`,
cancelButton: 'vn-button[href="#!/client/index"]'
},
orderCatalog: {
@ -609,16 +608,16 @@ export default {
openTagSearch: 'vn-catalog-filter > div > vn-vertical > vn-textfield[ng-model="$ctrl.value"] .append i',
tagAutocomplete: 'vn-order-catalog-search-panel vn-autocomplete[ng-model="filter.tagFk"]',
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',
thirdFilterRemoveButton: 'vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(3) button',
fourthFilterRemoveButton: 'vn-catalog-filter > div > vn-horizontal.chips > vn-chip:nth-child(4) button',
searchTagButton: 'vn-order-catalog-search-panel button[type=submit]',
thirdFilterRemoveButton: 'vn-catalog-filter .chips > vn-chip:nth-child(3) vn-icon[icon=cancel]',
fourthFilterRemoveButton: 'vn-catalog-filter .chips > vn-chip:nth-child(4) vn-icon[icon=cancel]',
},
orderBasicData: {
clientAutocomplete: 'vn-autocomplete[label="Client"]',
addressAutocomplete: 'vn-autocomplete[label="Address"]',
agencyAutocomplete: 'vn-autocomplete[label="Agency"]',
observationInput: 'vn-textarea[label="Observation"] textarea',
saveButton: `${components.vnSubmit}`,
saveButton: `button[type=submit]`,
acceptButton: 'vn-order-basic-data vn-confirm[vn-id="confirm"] button[response="ACCEPT"]'
},
orderLine: {
@ -636,7 +635,7 @@ export default {
vehicleAutoComplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
agencyAutoComplete: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
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 button[type=submit]'
},
routeDescriptor: {
volume: 'vn-route-descriptor vn-label-value[label="Volume"] > section > span'
@ -653,7 +652,7 @@ export default {
createdDateInput: 'vn-route-basic-data vn-date-picker[ng-model="$ctrl.route.created"]',
startedHourInput: 'vn-route-basic-data vn-input-time[ng-model="$ctrl.route.started"] 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 button[type=submit]'
},
routeTickets: {
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[ng-model="ticket.priority"] input',
@ -668,7 +667,7 @@ export default {
},
workerPbx: {
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 button[type=submit]'
},
workerTimeControl: {
timeDialogInput: '.vn-dialog.shown [ng-model="$ctrl.newTime"]',
@ -720,12 +719,12 @@ export default {
navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]'
},
invoiceOutIndex: {
searchInvoiceOutInput: `vn-invoice-out-index ${components.vnTextfield}`,
searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`,
searchButton: 'vn-invoice-out-index vn-searchbar vn-icon[icon="search"]',
searchResult: 'vn-invoice-out-index vn-card > div > vn-table > div > vn-tbody > a.vn-tr',
},
invoiceOutDescriptor: {
moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[vn-id="more-button"]',
moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[icon=more_vert]',
moreMenuDeleteInvoiceOut: '.vn-popover.shown .vn-drop-down li[name="Delete Invoice"]',
moreMenuBookInvoiceOut: '.vn-popover.shown .vn-drop-down li[name="Book invoice"]',
moreMenuShowInvoiceOutPdf: '.vn-popover.shown .vn-drop-down li[name="Show invoice PDF"]',

View File

@ -1,56 +1,35 @@
import createNightmare from '../../helpers/nightmare';
describe('Login path', () => {
const nightmare = createNightmare();
it('should receive an error when the username is incorrect', async() => {
const username = 'nobody';
const password = 'nightmare';
const result = await nightmare
.wait(`vn-login input[name=user]`)
.write(`vn-login input[name=user]`, username)
.write(`vn-login input[name=password]`, password)
.click(`vn-login input[type=submit]`)
.doLogin('badUser', null)
.waitForLastSnackbar();
expect(result.length).toBeGreaterThan(0);
});
it('should receive an error when the username is blank', async() => {
const password = 'nightmare';
const result = await nightmare
.clearInput(`vn-login input[name=user]`)
.write(`vn-login input[name=password]`, password)
.click(`vn-login input[type=submit]`)
.doLogin('', null)
.waitForLastSnackbar();
expect(result.length).toBeGreaterThan(0);
});
it('should receive an error when the password is incorrect', async() => {
const username = 'employee';
const password = 'badpassword';
const result = await nightmare
.write(`vn-login input[name=user]`, username)
.write(`vn-login input[name=password]`, password)
.click(`vn-login input[type=submit]`)
.doLogin('employee', 'badPassword')
.waitForLastSnackbar();
expect(result.length).toBeGreaterThan(0);
});
it('should log in', async() => {
const username = 'employee';
const password = 'nightmare';
const url = await nightmare
.write(`vn-login input[name=user]`, username)
.write(`vn-login input[name=password]`, password)
.click(`vn-login input[type=submit]`)
.doLogin('employee', null)
.wait('#logout')
.parsedUrl();

View File

@ -42,9 +42,7 @@ describe('Client Edit fiscalData path', () => {
it('should not be able to edit the verified data checkbox', async() => {
const result = await nightmare
.wait(selectors.clientFiscalData.verifiedDataCheckbox)
.evaluate(selector => {
return document.querySelector(selector).getAttribute('disabled');
}, selectors.clientFiscalData.verifiedDataCheckbox);
.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox);
expect(result).toBeTruthy();
});

View File

@ -16,9 +16,7 @@ describe('Client lock verified data path', () => {
const result = await nightmare
.wait(200)
.wait(selectors.clientFiscalData.verifiedDataCheckbox)
.evaluate(selector => {
return document.querySelector(selector).getAttribute('disabled');
}, selectors.clientFiscalData.verifiedDataCheckbox);
.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox);
expect(result).toBeTruthy();
});

View File

@ -17,9 +17,7 @@ describe('Ticket services path', () => {
const result = await nightmare
.waitToClick(selectors.ticketService.addServiceButton)
.wait(selectors.ticketService.firstAddDescriptionButton)
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.ticketService.firstAddDescriptionButton);
.isDisabled(selectors.ticketService.firstAddDescriptionButton);
expect(result).toBeTruthy();
});

View File

@ -66,7 +66,7 @@ describe('Order catalog', () => {
const result = await nightmare
.waitToClick(selectors.orderCatalog.fourthFilterRemoveButton)
.waitToClick(selectors.orderCatalog.thirdFilterRemoveButton)
.waitForNumberOfElements('section.product', 4)
.waitForNumberOfElements('.product', 4)
.countElement('section.product');
expect(result).toEqual(4);

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare';
describe('InvoiceOut descriptor path', () => {
fdescribe('InvoiceOut descriptor path', () => {
const nightmare = createNightmare();
let bookedDate;

View File

@ -1,13 +0,0 @@
<div class="button-menu">
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored button-menu__button">
<vn-label vn-none translate>{{::$ctrl.label}}</vn-label>
<vn-icon vn-none icon="{{::$ctrl.icon}}"></vn-icon>
<vn-icon vn-none class="button-menu__arrow_down" icon="keyboard_arrow_down"></vn-icon>
</button>
<vn-drop-down
vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(item)"
ng-click="$ctrl.onDropDownClick($event)">
</vn-drop-down>
</div>

View File

@ -0,0 +1,20 @@
<button>
<span
ng-if="::$ctrl.label"
translate>
{{::$ctrl.label}}
</span>
<vn-icon
ng-if="::$ctrl.icon"
icon="{{::$ctrl.icon}}">
</vn-icon>
<vn-icon
class="button-menu__arrow_down"
icon="keyboard_arrow_down">
</vn-icon>
</button>
<vn-drop-down
vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(item)"
ng-click="$ctrl.onDropDownClick($event)">
</vn-drop-down>

View File

@ -1,17 +1,13 @@
import ngModule from '../../module';
import Input from '../../lib/input';
import Button from '../button';
import assignProps from '../../lib/assign-props';
import './style.scss';
export default class ButtonMenu extends Input {
export default class ButtonMenu extends Button {
constructor($element, $scope, $transclude) {
super($element, $scope);
this.$transclude = $transclude;
this.input = this.element.querySelector('.mdl-button');
$element.on('click', e => {
if (!this.disabled)
this.onClick(e);
});
$element.on('click', e => this.onClick(e));
}
get model() {
@ -46,6 +42,7 @@ export default class ButtonMenu extends Input {
}
onClick(event) {
if (this.disabled) return;
if (event.defaultPrevented) return;
event.preventDefault();
this.emit('open');
@ -85,15 +82,14 @@ export default class ButtonMenu extends Input {
}
ButtonMenu.$inject = ['$element', '$scope', '$transclude'];
ngModule.component('vnButtonMenu', {
template: require('./button-menu.html'),
ngModule.vnComponent('vnButtonMenu', {
template: require('./index.html'),
controller: ButtonMenu,
bindings: {
label: '@',
showField: '@?',
selection: '<?',
valueField: '@?',
selectFields: '<?',
disabled: '<?',
initialData: '<?',
showFilter: '<?',
field: '=?',
@ -104,12 +100,10 @@ ngModule.component('vnButtonMenu', {
limit: '<?',
multiple: '<?',
onChange: '&?',
icon: '@?',
translateFields: '<?',
onOpen: '&?'
},
transclude: {
tplItem: '?tplItem'
},
controller: ButtonMenu
}
});

View File

@ -1,19 +1,3 @@
vn-button-menu {
position: relative;
cursor: pointer;
.button-menu__button {
padding: 0 10px;
}
vn-label {
float: left;
margin-top: 1px;
}
vn-icon {
float: left;
margin-top: 3px;
}
vn-icon.button-menu__arrow_down {
margin: 6px 0 0 5px;
}
}

View File

@ -1,6 +0,0 @@
<button type="{{::$ctrl.type}}" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
<span translate>{{$ctrl.label}}</span>
<vn-icon
icon="{{::$ctrl.icon}}">
</vn-icon>
</button>

View File

@ -1,34 +0,0 @@
import ngModule from '../../module';
import Input from '../../lib/input';
import './style.scss';
export default class Button extends Input {
constructor($element) {
super($element);
this.$element = $element;
this.input = this.element.querySelector('.mdl-button');
$element[0].addEventListener('click', event => {
if (this.disabled)
event.stopImmediatePropagation();
});
}
$onInit() {
if (!this.type)
this.type = 'button';
}
}
Button.$inject = ['$element'];
ngModule.component('vnButton', {
controller: Button,
template: require('./button.html'),
bindings: {
label: '@?',
disabled: '<?',
icon: '@?',
type: '@?'
}
});

View File

@ -0,0 +1,11 @@
<button type="{{::$ctrl.type}}" tabindex="-1">
<span
ng-if="::$ctrl.label"
translate>
{{::$ctrl.label}}
</span>
<vn-icon
ng-if="::$ctrl.icon"
icon="{{::$ctrl.icon}}">
</vn-icon>
</button>

View File

@ -0,0 +1,46 @@
import ngModule from '../../module';
import FormInput from '../form-input';
import './style.scss';
export default class Button extends FormInput {
constructor($element, $scope) {
super($element, $scope);
this.design = 'colored';
this.input = this.element.querySelector('button');
let element = this.element;
element.tabIndex = 0;
element.classList.add('vn-button');
this.element.addEventListener('keyup', e => this.onKeyup(e));
this.element.addEventListener('click', e => this.onClick(e));
}
$onInit() {
this.element.classList.add(this.design);
if (!this.type) this.type = 'button';
}
onKeyup(event) {
if (event.code == 'Space')
this.onClick(event);
}
onClick(event) {
if (event.defaultPrevented) return;
// event.preventDefault();
// FIXME: Don't stop event propagation
if (this.disabled) event.stopImmediatePropagation();
}
}
Button.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnButton', {
controller: Button,
template: require('./index.html'),
bindings: {
icon: '@?',
type: '@?'
}
});

View File

@ -1,5 +1,83 @@
vn-button {
& > button > vn-icon {
vertical-align: middle;
@import "variables";
.vn-button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 36px;
border: none;
border-radius: .1em;
font-family: vn-font-bold;
text-transform: uppercase;
font-size: 14px;
cursor: pointer;
box-sizing: border-box;
outline: none;
& > button {
width: 100%;
padding: 0 12px;
box-sizing: border-box;
background-color: transparent;
border: none;
height: inherit;
color: inherit;
font: inherit;
display: block;
text-transform: inherit;
cursor: inherit;
outline: none;
display: flex;
align-items: center;
justify-content: center;
& > vn-icon {
vertical-align: middle;
color: inherit;
font-size: 1.7em;
}
}
&.colored {
color: white;
background-color: $color-main;
box-shadow: 0 .15em .15em 0 rgba(0, 0, 0, .3);
transition: background 200ms ease-in-out;
&:not(.disabled) {
&:hover,
&:focus {
background-color: lighten($color-main, 10%);
}
}
}
&.flat {
color: $color-main;
background-color: transparent;
box-shadow: none;
transition: background 200ms ease-in-out;
&:not(.disabled) {
&:hover,
&:focus {
background-color: $color-hover-cd;
}
}
}
&:hover,
&:focus {
outline: none;
}
&.round {
border-radius: 50%;
height: 3.8em;
width: 3.8em;
& > button > span {
display: none;
}
}
&.disabled {
opacity: .7;
cursor: initial;
}
}

View File

@ -7,7 +7,7 @@ export default function directive() {
transclude: true,
template: require('./card.html'),
link: function($scope, $element, $attrs, $ctrl, $transclude) {
$element.addClass('demo-card-wide mdl-shadow--2dp bg-panel');
$element.addClass('demo-card-wide vn-shadow bg-panel');
$transclude($scope, function(clone) {
angular.element($element[0].querySelector('div')).append(clone);

View File

@ -1,8 +1,7 @@
<vn-one>
<span ng-class="{'mdl-chip--deletable': !$ctrl.disabled}" class="mdl-chip">
<span class="mdl-chip__text ellipsize" ng-transclude></span>
<button ng-click="$ctrl.remove()" ng-show="!$ctrl.disabled" type="button" class="mdl-chip__action">
<i class="material-icons">cancel</i>
</button>
</span>
</vn-one>
<div ng-transclude></div>
<vn-icon
ng-click="$ctrl.onRemove()"
ng-if="$ctrl.removable"
icon="cancel"
tabindex="0">
</vn-icon>

View File

@ -1,17 +1,12 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
export default class Chip {
/**
* Remove chip event
*/
remove() {
if (this.onRemove)
this.onRemove();
export default class Chip extends Component {
onRemove() {
if (!this.disabled) this.emit('remove');
}
}
Chip.$inject = ['$element', '$scope', '$transclude'];
ngModule.component('vnChip', {
@ -20,6 +15,6 @@ ngModule.component('vnChip', {
transclude: true,
bindings: {
disabled: '<?',
onRemove: '&?'
removable: '<?'
}
});

View File

@ -16,13 +16,13 @@ describe('Component vnChip', () => {
controller = $componentController('vnChip', {$element, $scope, $transclude: () => {}});
}));
describe('remove()', () => {
it(`should call onRemove()`, () => {
controller.onRemove = () => {};
spyOn(controller, 'onRemove');
controller.remove();
describe('onRemove()', () => {
it(`should emit remove event`, () => {
controller.emit = () => {};
spyOn(controller, 'emit');
controller.onRemove();
expect(controller.onRemove).toHaveBeenCalledWith();
expect(controller.emit).toHaveBeenCalledWith('remove');
});
});
});

View File

@ -1,18 +1,53 @@
@import "variables";
vn-chip {
border-radius: 16px;
background-color: $color-bg;
margin: 0 0.5em 0.5em 0;
color: $color-font;
font-size: 14px;
margin: .25em;
display: inline-flex;
align-items: center;
text-overflow: ellipsis;
white-space: nowrap;
height: 28px;
padding: 0 .7em;
overflow: hidden;
.mdl-chip {
background-color: rgba($color-main, 0.9);
color: #FFF
&.colored {
background-color: $color-main;
color: $color-font-dark;
}
.mdl-chip:active {
background-color: $color-main
}
& > vn-one > span > span {
& > div {
display: flex;
align-items: center;
max-width: 100%;
height: 100%;
& > vn-avatar {
margin-left: -0.7em;
margin-right: .4em;
}
}
& > vn-icon {
margin-left: .2em;
margin-right: -0.3em;
vertical-align: middle;
opacity: .6;
cursor: pointer;
transition: opacity 250ms ease-out;
&:hover,
&:focus {
opacity: 1;
}
}
}
vn-avatar {
display: inline-block;
height: 28px;
width: 28px;
border-radius: 50%;
}

View File

@ -1,8 +0,0 @@
<span
ng-class="{'pointer': $ctrl.$attrs['onClick']}"
class="mdl-chip mdl-chip--contact"
ng-repeat="legend in $ctrl.data track by $index"
ng-click="$ctrl.onClick(legend)">
<span class="mdl-chip__contact" ng-style="{backgroundColor: legend.color}"></span>
<span class="mdl-chip__text">{{legend.name}}</span>
</span>

View File

@ -1,24 +0,0 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
export default class Controller extends Component {
constructor($element, $scope, $attrs) {
super($element, $scope);
this.$attrs = $attrs;
}
onClick(legend) {
this.emit('click', {legend});
}
}
Controller.$inject = ['$element', '$scope', '$attrs'];
ngModule.component('vnColorLegend', {
template: require('./index.html'),
controller: Controller,
bindings: {
data: '<?'
}
});

View File

@ -1,13 +0,0 @@
@import "variables";
.mdl-chip--contact {
margin-left: 5px;
height: 20px;
line-height: 5px;
float: left
}
.mdl-chip--contact .mdl-chip__contact {
height: 20px;
width: 20px
}

View File

@ -11,7 +11,6 @@ export default class Field extends FormInput {
this.suffix = null;
this.control = this.element.querySelector('.control');
this.classList = this.element.classList;
this.classList.add('vn-field');
this.element.addEventListener('click', e => this.onClick(e));
@ -43,9 +42,11 @@ export default class Field extends FormInput {
}
set input(value) {
if (this.input) this.control.removeChild(this.input);
if (this.input)
this.control.removeChild(this.input);
this._input = value;
this.control.appendChild(value);
if (value)
this.control.appendChild(value);
}
get input() {
@ -69,12 +70,10 @@ export default class Field extends FormInput {
}
set name(value) {
// super.name = value;
this.input.name = value;
}
get name() {
// return super.name;
return this.input.name;
}
@ -86,36 +85,8 @@ export default class Field extends FormInput {
return this.input.placeholder;
}
set tabIndex(value) {
this.input.tabIndex = value;
}
get tabIndex() {
return this.input.tabIndex;
}
set disabled(value) {
this._disabled = boolTag(value);
this.input.disabled = this._disabled;
this.classList.toggle('disabled', this._disabled);
}
get disabled() {
return this._disabled;
}
set readonly(value) {
this._readonly = boolTag(value);
this.input.readOnly = this._readonly;
this.classList.toggle('readonly', this._readonly);
}
get readonly() {
return this._readonly;
}
set required(value) {
this._required = boolTag(value);
this._required = value;
let required = this.element.querySelector('.required');
display(required, this._required);
}
@ -182,6 +153,10 @@ export default class Field extends FormInput {
fix.innerText = text || '';
}
refreshTabIndex() {
this.input.tabIndex = this.disabled ? -1 : this.tabIndex;
}
onClick() {
// if (event.defaultPrevented) return;
// event.preventDefault();
@ -252,15 +227,10 @@ ngModule.vnComponent('vnField', {
suffix: '@?',
hint: '@?',
error: '<?',
tabIndex: '<?',
rule: '@?'
}
});
function boolTag(value) {
return Boolean(value || value === '');
}
function display(element, display) {
element.style.display = display ? 'initial' : 'none';
}

View File

@ -245,7 +245,6 @@
}
}
& > .hint {
z-index: -1;
padding: 4px 0;
height: 12px;
color: rgba(0, 0, 0, .4);
@ -255,11 +254,12 @@
transition-duration: 200ms;
transition-timing-function: ease-in-out;
opacity: 0;
visibility: hidden;
&.filled {
z-index: 0;
opacity: 1;
transform: translateY(0);
visibility: visible;
}
}
&.invalid {

View File

@ -1,3 +0,0 @@
<button class="mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect mdl-button--colored">
<vn-icon icon="{{::$ctrl.icon}}"></vn-icon>
</button>

View File

@ -1,8 +0,0 @@
import ngModule from '../../module';
ngModule.component('vnFloatButton', {
template: require('./float-button.html'),
bindings: {
icon: '@'
}
});

View File

@ -0,0 +1,13 @@
import ngModule from '../../module';
import Button from '../button';
export default class FloatButton extends Button {
constructor($element, $scope) {
super($element, $scope);
this.element.classList.add('round');
}
}
ngModule.vnComponent('vnFloatButton', {
controller: FloatButton
});

View File

@ -9,6 +9,11 @@ import Component from '../../lib/component';
* @property {Boolean} disabled Put component in disabled mode
*/
export default class FormInput extends Component {
constructor($element, $scope) {
super($element, $scope);
this.classList = this.element.classList;
}
$onInit() {
// XXX: Compatibility with old inputs
let attrs = this.$element[0].attributes;
@ -41,6 +46,48 @@ export default class FormInput extends Component {
get name() {
return this.element.getAttribute('name');
}
set disabled(value) {
this._disabled = boolTag(value);
this.input.disabled = this._disabled;
this.classList.toggle('disabled', this._disabled);
this.refreshTabIndex();
}
get disabled() {
return this._disabled;
}
set readonly(value) {
this._readonly = boolTag(value);
this.input.readOnly = this._readonly;
this.classList.toggle('readonly', this._readonly);
}
get readonly() {
return this._readonly;
}
set tabIndex(value) {
this._tabIndex = value;
this.refreshTabIndex();
}
get tabIndex() {
return this._tabIndex;
}
select() {
this.input.select();
}
focus() {
this.input.focus();
}
refreshTabIndex() {
this.element.tabIndex = this.disabled ? -1 : this.tabIndex;
}
}
ngModule.vnComponent('vnFormInput', {
@ -50,9 +97,14 @@ ngModule.vnComponent('vnFormInput', {
field: '=?',
name: '@?',
disabled: '<?',
readonly: '<?'
readonly: '<?',
tabIndex: '<?'
},
require: {
ngModel: '?ngModel'
}
});
function boolTag(value) {
return Boolean(value || value === '');
}

View File

@ -1,3 +0,0 @@
<button type="button" ng-disabled="$ctrl.disabled">
<vn-icon icon="{{::$ctrl.icon}}">
</button>

View File

@ -1,39 +0,0 @@
import ngModule from '../../module';
import './style.scss';
export default class IconButton {
constructor($element) {
this.element = $element[0];
if (this.element.getAttribute('tabindex') == null)
this.element.tabIndex = 0;
this.element.addEventListener('keyup', e => this.onKeyup(e));
this.element.addEventListener('click', e => this.onClick(e));
}
onKeyup(event) {
if (event.code == 'Space')
this.onClick(event);
}
onClick(event) {
if (event.defaultPrevented) return;
event.preventDefault();
// FIXME: Don't use Event.stopPropagation()
let button = this.element.querySelector('button');
if (this.disabled || button.disabled)
event.stopImmediatePropagation();
}
}
IconButton.$inject = ['$element'];
ngModule.component('vnIconButton', {
controller: IconButton,
template: require('./icon-button.html'),
bindings: {
icon: '@',
disabled: '<?'
}
});

View File

@ -0,0 +1,14 @@
import ngModule from '../../module';
import Button from '../button';
import './style.scss';
export default class IconButton extends Button {
constructor($element, $scope) {
super($element, $scope);
this.design = null;
}
}
ngModule.vnComponent('vnIconButton', {
controller: IconButton
});

View File

@ -2,27 +2,12 @@
vn-icon-button {
@extend %clickable-light;
outline: 0;
color: $color-main;
display: inline-flex;
align-items: center;
font-size: 18pt;
padding: .25em;
& > button {
background-color: transparent;
display: block;
color: inherit;
border: 0;
padding: 0;
font-size: inherit;
&:hover {
background-color: initial;
}
& > vn-icon {
display: block;
font-size: inherit;
}
padding: 0 !important;
}
&:focus {
opacity: .6;
}
}

View File

@ -1 +0,0 @@
<vn-icon icon="{{::$ctrl.icon}}"></vn-icon>

View File

@ -1,29 +0,0 @@
import ngModule from '../../module';
import './style.scss';
export default class IconFocusable {
constructor($element) {
$element[0].tabIndex = 0;
$element.on("keyup", event => this.onKeyDown(event, $element));
}
onKeyDown(event, $element) {
if (event.defaultPrevented) return;
if (event.keyCode == 32 || event.keyCode == 13) {
event.preventDefault();
$element.triggerHandler('click');
}
}
}
IconFocusable.$inject = ['$element'];
ngModule.component('vnIconFocusable', {
controller: IconFocusable,
template: require('./icon-focusable.html'),
bindings: {
icon: '@',
className: '@?',
enabled: '<?',
label: '@?'
}
});

View File

@ -1,10 +0,0 @@
vn-icon-focusable {
display: inline-block;
text-align: center;
& > i,
& > i.material-icons {
display: block;
font-size: inherit;
}
}

View File

@ -1,11 +0,0 @@
<div class="icon-menu">
<vn-icon
class="button"
icon="{{::$ctrl.icon}}">
</vn-icon>
<vn-drop-down
vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(item)"
ng-click="$ctrl.onDropDownClick($event)">
</vn-drop-down>
</div>

View File

@ -1,41 +0,0 @@
import ngModule from '../../module';
import ButtonMenu from '../button-menu/button-menu';
import './style.scss';
export default class IconMenu extends ButtonMenu {
constructor($element, $scope, $transclude) {
super($element, $scope);
this.$transclude = $transclude;
this.input = this.element.querySelector('.button');
}
}
IconMenu.$inject = ['$element', '$scope', '$transclude'];
ngModule.component('vnIconMenu', {
template: require('./icon-menu.html'),
bindings: {
label: '@',
showField: '@?',
selection: '<?',
valueField: '@?',
selectFields: '<?',
disabled: '<?',
initialData: '<?',
showFilter: '<?',
field: '=?',
url: '@?',
data: '<?',
where: '@?',
order: '@?',
limit: '<?',
multiple: '<?',
onChange: '&?',
icon: '@?',
translateFields: '<?',
onOpen: '&?'
},
transclude: {
tplItem: '?tplItem'
},
controller: IconMenu
});

View File

@ -0,0 +1,11 @@
<button>
<vn-icon
class="button"
icon="{{::$ctrl.icon}}">
</vn-icon>
</button>
<vn-drop-down
vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(item)"
ng-click="$ctrl.onDropDownClick($event)">
</vn-drop-down>

View File

@ -0,0 +1,14 @@
import ngModule from '../../module';
import ButtonMenu from '../button-menu';
export default class IconMenu extends ButtonMenu {
constructor($element, $scope) {
super($element, $scope);
this.element.classList.add('flat');
}
}
ngModule.vnComponent('vnIconMenu', {
template: require('./index.html'),
controller: IconMenu
});

View File

@ -1,8 +0,0 @@
vn-icon-menu {
cursor: pointer;
vn-drop-down {
font-family: 'vn-font';
outline: 0
}
}

View File

@ -10,36 +10,35 @@ import './subtitle/subtitle';
import './spinner/spinner';
import './snackbar/snackbar';
import './tooltip/tooltip';
import './icon-menu/icon-menu';
import './button-menu/button-menu';
import './popover/popover';
import './drop-down/drop-down';
import './menu/menu';
import './multi-check/multi-check';
import './button/button';
import './icon-button/icon-button';
import './submit/submit';
import './card/card';
import './float-button/float-button';
import './step-control/step-control';
import './label-value/label-value';
import './pagination/pagination';
import './searchbar/searchbar';
import './scroll-up/scroll-up';
import './autocomplete';
import './button';
import './button-menu';
import './calendar';
import './check';
import './chip';
import './color-legend';
import './data-viewer';
import './date-picker';
import './field';
import './float-button';
import './icon-menu';
import './icon-button';
import './input-number';
import './input-range';
import './range';
import './input-time';
import './input-file';
import './list';
import './radio';
import './submit';
import './table';
import './td-editable';
import './textarea';

View File

@ -1,42 +1,53 @@
<div class="container"
ng-class="{selected: $ctrl.hasFocus}">
<div class="textField">
<div class="leftIcons" ng-transclude="leftIcons"></div>
<div class="infix">
<section class="value" ng-click="$ctrl.openFileSelector()" translate>
<div class="container">
<div
ng-transclude="prepend"
class="prepend">
</div>
<div class="infix">
<div class="fix prefix"></div>
<div class="control">
<section
class="value"
ng-click="$ctrl.openFileSelector()"
translate>
{{$ctrl.value}}
</section>
<input
class="mdl-textfield__input"
type="file"
name="{{::$ctrl.name}}"
ng-model="$ctrl.files"
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}}"
accept="{{$ctrl.accept}}"/>
<label class="label">
<span translate>{{::$ctrl.label}}</span>
<span translate ng-show="::$ctrl.required">*</span>
</label>
accept="{{$ctrl.accept}}">
</input>
</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>
<vn-icon-button
icon="cloud_upload"
vn-tooltip="Select a file"
ng-click="$ctrl.openFileSelector()">
</vn-icon-button>
</div>
<div class="rightIcons" ng-transclude="rightIcons"></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
icon="cloud_upload"
vn-tooltip="Select a file"
ng-click="$ctrl.openFileSelector()">
</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">
</div>
<div class="underline blur"></div>
<div class="underline focus"></div>
</div>
<div class="hint"></div>

View File

@ -1,15 +1,13 @@
import ngModule from '../../module';
import Input from '../../lib/input';
import FormInput from '../form-input';
import './style.scss';
export default class InputFile extends Input {
export default class InputFile extends FormInput {
constructor($element, $scope) {
super($element, $scope);
this.element = $element[0];
this.hasFocus = false;
this._multiple = false;
this._value = 'Select a file';
this.input = this.element.querySelector('input');
this.registerEvents();
}
@ -106,26 +104,12 @@ export default class InputFile extends Input {
}
}
InputFile.$inject = ['$element', '$scope'];
ngModule.component('vnInputFile', {
ngModule.vnComponent('vnInputFile', {
template: require('./index.html'),
controller: InputFile,
transclude: {
leftIcons: '?tLeftIcons',
rightIcons: '?tRightIcons'
},
bindings: {
label: '@?',
name: '@?',
disabled: '<?',
multiple: '<?',
required: '@?',
accept: '@?',
rule: '@?',
files: '=model',
validate: '&',
onChange: '&',
onClear: '&'
files: '=model'
}
});

View File

@ -7,159 +7,7 @@ vn-input-file {
padding: 4px 0;
outline: 0
}
input {
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
input[type=file] {
display: none;
}
}

View File

@ -1,9 +0,0 @@
<div>
<label translate>{{::$ctrl.label}}</label>
<input
name="{{$ctrl.name}}"
class="mdl-slider mdl-js-slider"
type="range"/>
<label class="min-label" translate>Company</label>
<label class="max-label" translate>Sales/Client</label>
</div>

View File

@ -1,79 +0,0 @@
import ngModule from '../../module';
import Input from '../../lib/input';
import './style.scss';
export default class inputRange extends Input {
constructor($element, $scope) {
super($element, $scope);
this.mdlElement = this.element.querySelector('.mdl-slider');
componentHandler.upgradeElement(this.mdlElement);
this.mdlElement.addEventListener('change', () => {
this._value = this.input.value;
this.$.$applyAsync();
if (this._value && this.onChange)
this.emit('change', {value: this._value});
});
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
this.mdlElement.MaterialSlider.change(value);
}
get max() {
return this.input.max;
}
set max(value) {
this.input.max = value;
}
get min() {
return this.input.min;
}
set min(value) {
this.input.min = value;
}
get step() {
return this.input.step;
}
set step(value) {
this.input.step = value;
}
get() {
return this._model;
}
set model(value) {
this._model = value;
}
set disabled(value) {
this.input.disabled = value;
}
}
inputRange.$inject = ['$element', '$scope'];
ngModule.component('vnInputRange', {
template: require('./index.html'),
controller: inputRange,
bindings: {
label: '@?',
disabled: '<?',
min: '<?',
max: '<?',
step: '<?',
value: '=',
model: '=',
onChange: '&'
}
});

View File

@ -1,14 +0,0 @@
@import "variables";
vn-input-range {
label {
color: $color-main;
font-size: 12px;
padding: 0 25px;
}
label.min-label, label.max-label {
color: $color-font;
}
label.max-label {
float: right;
}
}

View File

@ -24,11 +24,10 @@
vn-icon-button {
opacity: .4;
color: $color-main;
margin-left: .5em;
transition: opacity 250ms ease-out;
padding: 0;
font-size: 2em;
font-size: 1.2em;
&:hover {
opacity: 1;

View File

@ -1,5 +1,5 @@
import ngModule from '../../module';
import Input from '../../lib/input';
import FormInput from '../form-input';
import './style.scss';
/**
@ -8,7 +8,7 @@ import './style.scss';
* @param {Array} data List of options shown in drop-down
* @param {Array} models Elements to check / unCheck
*/
export default class MultiCheck extends Input {
export default class MultiCheck extends FormInput {
constructor($element, $scope) {
super($element, $scope);
this._checked = false;

View File

@ -0,0 +1,4 @@
<label class="main" translate>{{::$ctrl.label}}</label>
<input name="{{::$ctrl.name}}" type="range"></input>
<label class="min-label" translate>{{::$ctrl.minLabel}}</label>
<label class="max-label" translate>{{::$ctrl.maxLabel}}</label>

View File

@ -0,0 +1,50 @@
import ngModule from '../../module';
import FormInput from '../form-input';
import './style.scss';
export default class Range extends FormInput {
constructor($element, $scope) {
super($element, $scope);
this.input = this.element.querySelector('input');
}
get max() {
return this.input.max;
}
set max(value) {
this.input.max = value;
}
get min() {
return this.input.min;
}
set min(value) {
this.input.min = value;
}
get step() {
return this.input.step;
}
set step(value) {
this.input.step = value;
}
refreshTabIndex() {
this.input.tabIndex = this.disabled ? -1 : this.tabIndex;
}
}
ngModule.vnComponent('vnRange', {
template: require('./index.html'),
controller: Range,
bindings: {
min: '<?',
max: '<?',
step: '<?',
minLabel: '@?',
maxLabel: '@?'
}
});

View File

@ -0,0 +1,84 @@
@import "variables";
@mixin range($thumb-selector, $track-selector) {
&::#{$thumb-selector} {
-webkit-appearance: none;
margin-top: -5px;
border-radius: 50%;
background: $color-main;
border: none;
height: 12px;
width: 12px;
border-radius: 50%;
transition-property: transform, box-shadow;
transition-duration: 250ms;
transition-timing-function: ease-out;
}
&:focus::#{$thumb-selector} {
box-shadow: 0 0 0 10px rgba($color-main, .2);
}
&:active::#{$thumb-selector} {
transform: scale(1.5);
box-shadow: none;
}
&:disabled::#{$thumb-selector} {
transform: none;
cursor: initial;
}
&::#{$track-selector} {
width: 100%;
height: 3px;
cursor: inherit;
background: $color-secondary;
border-radius: 2px;
border: none;
}
}
vn-range {
& > label {
font-size: 12px;
&.main {
color: $color-main;
}
&.min-label {
float: left;
}
&.max-label {
float: right;
}
}
& > input {
cursor: pointer;
height: 30px;
display: block;
width: 100%;
background: transparent;
border-color: transparent;
-webkit-appearance: none;
margin: .2em 0;
&:focus {
outline: none;
}
&::-moz-focus-outer {
border: 0;
}
@include range(
"-moz-range-thumb",
"-moz-range-track"
);
@include range(
"-webkit-slider-thumb",
"-webkit-slider-runnable-track"
);
@include range(
"-ms-thumb",
"-ms-track"
);
&:disabled {
cursor: initial;
}
}
}

View File

@ -1,5 +1,6 @@
<button class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored"
<vn-button
icon="keyboard_arrow_up"
ng-click="$ctrl.goUp()"
vn-tooltip="Go up">
<vn-icon icon="keyboard_arrow_up"></vn-icon>
</button>
vn-tooltip="Go up"
class="round">
</vn-button>

View File

@ -1,6 +1,6 @@
vn-scroll-up {
top: 5em;
right: 2.5em;
top: 5.5em;
right: 2em;
display: none;
position: fixed;
}

View File

@ -2,6 +2,7 @@
vn-searchbar {
display: block;
width: 100%;
}
.search-panel {

View File

@ -1,2 +1,12 @@
<div class="mdl-spinner mdl-spinner--single-color mdl-js-spinner">
<div class="loader">
<svg class="circular" viewBox="25 25 50 50">
<circle
class="path"
cx="50"
cy="50"
r="20"
fill="none"
stroke-miterlimit="10">
</circle>
</svg>
</div>

View File

@ -1,19 +1,18 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import './style.css';
import './style.scss';
/**
* A spinner to inform the user about loading process.
*/
export default class Spinner extends Component {
constructor($element, $scope) {
super($element);
super($element, $scope);
this._enable = false;
this.spinner = $element[0].firstChild;
componentHandler.upgradeElement(this.spinner);
}
/**
* Enables/disables the spinner.
* Activates/deactivates the spinner.
*
* @param {Boolean} value %true to enable, %false to disable
*/
@ -35,14 +34,14 @@ export default class Spinner extends Component {
* Activates the spinner.
*/
start() {
this.spinner.MaterialSpinner.start();
this.spinner.style.display = 'block';
this._enable = true;
}
/**
* Deactivates the spinner.
*/
stop() {
this.spinner.MaterialSpinner.stop();
this.spinner.style.display = 'none';
this._enable = false;
}
}

View File

@ -1,7 +1,6 @@
import './spinner.js';
describe('Component vnSpinner', () => {
let $scope;
let $element;
let controller;
@ -9,53 +8,44 @@ describe('Component vnSpinner', () => {
$translateProvider.translations('en', {});
}));
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
$element = angular.element('<div><div></div></div>');
controller = $componentController('vnSpinner', {$scope, $element});
beforeEach(angular.mock.inject(($compile, $rootScope) => {
$element = $compile(`<vn-spinner></vn-spinner>`)($rootScope);
controller = $element.controller('vnSpinner');
}));
afterEach(() => {
$element.remove();
});
describe('enable()', () => {
it(`should call start() based on a boolean value passed as argument`, () => {
it(`should call start() when enable is set to true`, () => {
spyOn(controller, 'start');
spyOn(controller, 'stop');
controller.enable = true;
expect(controller.start).toHaveBeenCalledWith();
expect(controller.stop).not.toHaveBeenCalledWith();
});
it(`should call stop() based on a boolean value passed as argument`, () => {
spyOn(controller, 'start');
it(`should call stop() when enable is set to false`, () => {
spyOn(controller, 'stop');
controller.enable = false;
expect(controller.start).not.toHaveBeenCalledWith();
expect(controller.stop).toHaveBeenCalledWith();
});
});
describe('start()', () => {
it(`should call start() on the controller.materialSpinner then set controllers._enable to be truthy`, () => {
controller.spinner = {MaterialSpinner: {start: () => {}}};
spyOn(controller.spinner.MaterialSpinner, 'start');
controller._enable = false;
it(`should set enable to true`, () => {
controller.start();
expect(controller.spinner.MaterialSpinner.start).toHaveBeenCalledWith();
expect(controller._enable).toBeTruthy();
expect(controller.enable).toBeTruthy();
});
});
describe('stop()', () => {
it(`should call stop() on the controller.materialSpinner then set controllers._enable to be truthy`, () => {
controller.spinner = {MaterialSpinner: {stop: () => {}}};
spyOn(controller.spinner.MaterialSpinner, 'stop');
controller._enable = true;
it(`should set enable to false`, () => {
controller.stop();
expect(controller.spinner.MaterialSpinner.stop).toHaveBeenCalledWith();
expect(controller._enable).toBeFalsy();
expect(controller.enable).toBeFalsy();
});
});
});

View File

@ -1,3 +0,0 @@
vn-spinner {
display: inline-block;
}

View File

@ -0,0 +1,60 @@
@import "variables";
vn-spinner {
display: inline-block;
min-height: 28px;
min-width: 28px;
& > .loader {
position: relative;
margin: 0 auto;
width: 100%;
height: 100%;
&:before {
content: '';
display: block;
padding-top: 100%;
}
& > .circular {
animation: rotate 2s linear infinite;
transform-origin: center center;
height: 100%;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
& > .path {
stroke: $color-main;
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
stroke-linecap: square;
stroke-width: 6px;
animation: dash 1.5s ease-in-out infinite;
}
}
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124px;
}
}
}

View File

@ -41,11 +41,5 @@ vn-step-control {
& > .buttons > .step {
display: flex
}
& > .buttons > .step > .mdl-button {
line-height: 32px;
font-size: 12px;
padding: 0 12px;
height: 32px
}
}
}

View File

@ -0,0 +1,13 @@
import ngModule from '../../module';
import Button from '../button';
export default class Controller extends Button {
constructor($element, $scope) {
super($element, $scope);
this.type = 'submit';
}
}
ngModule.vnComponent('vnSubmit', {
controller: Controller
});

View File

@ -1,6 +0,0 @@
<input
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored"
type="submit"
translate-attr="{value: $ctrl.label}"
value="$ctrl.label">
</input>

View File

@ -1,25 +0,0 @@
import ngModule from '../../module';
import Input from '../../lib/input';
export default class Controller extends Input {
constructor($element, $scope) {
super($element, $scope);
this.$element = $element;
this.input = $element[0].querySelector('input');
}
set disabled(value) {
this.input.disabled = value;
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.component('vnSubmit', {
template: require('./submit.html'),
controller: Controller,
bindings: {
label: '@?',
disabled: '<?',
}
});

View File

@ -1,6 +1,6 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import Input from '../../lib/input';
import FormInput from '../form-input';
import './style.scss';
export default class Controller extends Component {
@ -37,7 +37,7 @@ export default class Controller extends Component {
}
let inputCtrl = this.field[0].firstElementChild.$ctrl;
if (inputCtrl instanceof Input) {
if (inputCtrl instanceof FormInput) {
let evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,

View File

@ -19,15 +19,24 @@ export default class Toggle extends FormInput {
}
set disabled(value) {
this.element.tabIndex = !value ? 0 : -1;
this.element.classList.toggle('disabled', Boolean(value));
this._disabled = value;
this.classList.toggle('disabled', Boolean(value));
this.refreshTabIndex();
}
get disabled() {
return this._disabled;
}
set readonly(value) {
this._readonly = value;
this.classList.toggle('readonly', Boolean(value));
}
get readonly() {
return this._readonly;
}
onKeydown(event) {
if (event.code == 'Space')
this.onClick(event);

View File

@ -12,6 +12,9 @@
}
& > span {
font-size: $input-font-size;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
& > .btn {
position: relative;

View File

@ -13,7 +13,7 @@ export default class Tooltip extends Component {
constructor($element, $scope, $timeout) {
super($element, $scope);
this.$timeout = $timeout;
$element.addClass('vn-tooltip mdl-shadow--4dp');
$element.addClass('vn-tooltip vn-shadow');
this.position = 'down';
this.margin = 10;
}

View File

@ -1,110 +1,41 @@
import ngModule from '../module';
import FormInput from '../components/form-input';
function vnAcl(aclService, $timeout) {
function vnAcl(aclService) {
let acls = [];
function getMaterialType(className) {
let type = '';
if (className) {
type = className.replace('mdl-', '').replace('__input', '');
type = type.charAt(0).toUpperCase() + type.slice(1);
}
return type;
}
function updateMaterial(input) {
if (input && input.className) {
let find = input.className.match(/mdl-[\w]+input/g);
if (find && find.length && find[0]) {
let type = getMaterialType(find[0]);
if (type && input.parentNode[`Material${type}`] && input.parentNode[`Material${type}`].updateClasses_)
input.parentNode[`Material${type}`].updateClasses_();
}
}
}
function getDynamicConditions($attrs) {
let atributes = $attrs.$attr;
let conditions = {};
Object.keys(atributes).forEach(atribute => {
if (atribute.startsWith('aclConditionalTo')) {
let role = atributes[atribute].split('-').slice(-1)[0];
conditions[atribute] = {
role: role
};
}
});
return conditions;
}
function permissionElement($element, action) {
if (!aclService.hasAny(acls)) {
if (action === 'disabled') {
let element = $element[0];
let selector = 'input, textarea, button, submit, md-checkbox';
if (element.$ctrl) {
element.setAttribute('disabled', 'true');
element.$ctrl.disabled = true;
}
if (!element.matches(selector))
element = element.querySelector(selector);
if (element) {
$timeout(() => {
element.setAttribute('disabled', 'true');
updateMaterial(element);
});
$element[0].querySelectorAll('vn-drop-down').forEach(element => {
element.parentNode.removeChild(element);
});
}
} else
$element.remove();
}
}
function updateAcls(role, toAdd) {
let position = acls.indexOf(role);
if (!toAdd && position > -1)
acls.splice(position, 1);
// XXX: add acl and enabled element if previusly was disabled
}
return {
restrict: 'A',
priority: -1,
link: function($scope, $element, $attrs) {
link: function(_, $element, $attrs) {
acls = $attrs.vnAcl.split(',').map(i => i.trim());
if (acls[0] == '') return;
let action = $attrs.vnAclAction || 'disabled';
let conditions = getDynamicConditions($attrs);
let action = $attrs.vnAclAction || 'disable';
permissionElement($element, action);
if (aclService.hasAny(acls)) return;
if (Object.keys(conditions).length) {
let watchConditions = $scope.$watch(() => {
Object.keys(conditions).forEach(attrName => {
let hasPermission = $scope.$eval($attrs[attrName]);
if (!hasPermission) {
updateAcls(conditions[attrName].role, hasPermission);
permissionElement($element, action);
delete conditions[attrName];
}
});
if (action === 'disable') {
let element = $element[0];
let elementToDisable = element.$ctrl;
if (Object.keys(conditions).length === 0) {
// unWacth
watchConditions();
}
});
}
if (!(elementToDisable instanceof FormInput)) {
let selector = 'input, textarea, button, submit';
if (!element.matches(selector))
element = element.querySelector(selector);
elementToDisable = element;
}
if (elementToDisable)
elementToDisable.disabled = true;
} else
$element.remove();
}
};
}
vnAcl.$inject = ['aclService', '$timeout'];
vnAcl.$inject = ['aclService'];
ngModule.directive('vnAcl', vnAcl);

View File

@ -1,54 +1,97 @@
describe('Directive acl', () => {
let scope;
let $scope;
let $element;
let element;
let compile;
let $timeout;
beforeEach(angular.mock.module('vnCore', $translateProvider => {
$translateProvider.translations('en', {});
}));
compile = (hasPermissions, _element) => {
inject(($compile, $rootScope, aclService, _$timeout_) => {
spyOn(aclService, 'hasAny').and.returnValue(hasPermissions);
scope = $rootScope.$new();
$timeout = _$timeout_;
element = angular.element(_element);
$compile(element)(scope);
scope.$digest();
beforeEach(inject(($httpBackend, aclService) => {
$httpBackend.whenGET('/api/Accounts/acl')
.respond({
user: {id: 1, name: 'myUser'},
roles: [
{role: {name: 'myRole'}},
{role: {name: 'myOtherRole'}}
]
});
aclService.load();
$httpBackend.flush();
}));
afterEach(() => {
$element.remove();
$scope.$destroy();
});
compile = html => {
inject(($compile, $rootScope) => {
$scope = $rootScope.$new();
$element = $compile(html)($scope);
$scope.$digest();
element = $element[0];
});
};
it('should not disable the input element as the user has permision', () => {
let html = `<div vn-acl="administrative,client" vn-acl-action="disabled"><input/></div>`;
compile(true, html);
let input = element.find('input');
it('should not disable the input element as the user owns the role', () => {
let html = `
<div
vn-acl="randomRole, myRole"
vn-acl-action="disable">
<input/>
</div>
`;
compile(html);
let input = element.querySelector('input');
expect(input).toBeDefined();
expect(input.attr('disabled')).toBeFalsy();
expect(input.disabled).toBeFalsy();
});
it('should delete the element as the user does not have permission and there is no action', () => {
let html = `<container><div vn-acl="administrative,client" vn-acl-action="anything but disabled"><input/></div></container>`;
compile(false, html);
it('should disable the element as the action is to disable and the user does not own the role', () => {
let html = `
<div
vn-acl="unownedRole, randomRole"
vn-acl-action="disable">
<input/>
</div>
`;
compile(html);
let input = element.querySelector('input');
expect(element.children().length).toEqual(0);
expect(input.disabled).toBeTruthy();
});
it('should disable the element as the action is to disable it but the user has no permission but present', () => {
let html = `<div vn-acl="administrative,client" vn-acl-action="disabled"><input/></div>`;
compile(false, html);
let input = element.find('input');
$timeout.flush();
it('should keep the element as the action is to remove and the user owns the role', () => {
let html = `
<section>
<div
vn-acl="myOtherRole, randomRole"
vn-acl-action="remove">
<input/>
</div>
</section>
`;
compile(html);
let div = element.querySelector('div');
expect(input).toBeDefined();
expect(input.attr('disabled')).toBeTruthy();
expect(div).not.toBeNull();
});
it('should delete any element with the tag vn-drop-down', () => {
let html = `<div vn-acl="administrative,client" vn-acl-action="disabled"><vn-drop-down></vn-drop-down><input/></div>`;
compile(false, html);
it('should delete the element as the action is to remove and the user does not own the role', () => {
let html = `
<section>
<div
vn-acl="unownedRole, randomRole"
vn-acl-action="remove">
<input/>
</div>
</section>
`;
compile(html);
let div = element.querySelector('div');
expect(element.find('vn-drop-down').length).toBe(0);
expect(div).toBeNull();
});
});

View File

@ -1,42 +0,0 @@
import Component from './component';
/**
* Component that host an input.
*/
export default class Input extends Component {
constructor($element, $scope) {
super($element, $scope);
this.input = this.element.querySelector('input');
}
set disabled(value) {
this.input.disabled = value == true;
this.mdlUpdate();
}
get disabled() {
return this.input.disabled;
}
select() {
this.input.select();
}
focus() {
this.input.focus();
}
mdlUpdate() {
if (this.mdlElement)
this.mdlElement.updateClasses_();
}
}
Input.$inject = ['$element', '$scope'];
export const $options = {
bindings: {
label: '@?',
disabled: '<?',
readonly: '<?'
}
};

View File

@ -1,7 +1,6 @@
import {ng, ngDeps} from './vendor';
const ngModule = ng.module('vnCore', ngDeps);
ngModule.constant('moment', require('moment-timezone'));
export default ngModule;
/**
@ -43,6 +42,9 @@ ngModule.vnComponent = function(name, options) {
config.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
export function config($translateProvider, $translatePartialLoaderProvider) {
// For CSS browser targeting
document.documentElement.setAttribute('data-browser', navigator.userAgent);
$translatePartialLoaderProvider.addPart('core');
let conf = {urlTemplate: '/locale/{part}/{lang}.json'};

View File

@ -9,3 +9,9 @@
.item-disabled {
opacity: $color-disabled;
}
.vn-shadow {
box-shadow: $shadow;
}
.invisible {
visibility: hidden;
}

View File

@ -16,3 +16,26 @@
font-weight: 400;
src: url('./icons/Material-Design-Icons.woff2') format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
/* Support for IE. */
font-feature-settings: 'liga';
}

View File

@ -0,0 +1,40 @@
@import "variables";
html, body {
background-color: $color-bg;
overflow: auto;
height: 100%;
font-family: vn-font;
color: $color-font;
font-size: 12pt;
margin: 0;
padding: 0;
}
a:focus {
outline: 1px solid $color-spacer-light;
}
input,
button {
&:focus {
outline: none;
}
}
button,
input {
&::-moz-focus-inner {
border: none;
}
}
input[type=submit],
input[type=button],
button {
cursor: pointer;
&:disabled {
opacity: .7;
}
}
a {
color: $color-font-link;
text-decoration: none;
}

View File

@ -1,10 +1,9 @@
import './effects.scss';
import './background.scss';
import './border.scss';
import './font-family.scss';
import './global.scss';
import './icons/salixfont.css';
import './layout.scss';
import './mdl-override.scss';
import './responsive.scss';
import './spacing.scss';
import './text.scss';

View File

@ -1,61 +0,0 @@
@import "variables";
/**
* Rewrited CSS rules from Material Design Lite.
* FIXME: don't use !important
*/
body {
line-height: initial;
font-size: 12pt;
}
// Textfield
.mdl-textfield--floating-label {
&.is-focused,
&.is-dirty,
&.has-placeholder {
.mdl-textfield__label {
color: $color-main !important;
}
}
}
.mdl-textfield__label::after {
background-color: $color-main !important;
}
fieldset[disabled] .mdl-textfield,
.mdl-textfield.is-disabled {
.mdl-textfield__input {
border: none !important;
color: inherit !important;
}
.mdl-textfield__label {
color: $color-main !important;
}
}
// Button
.mdl-button {
font-weight: bolder;
color: $color-main;
}
.mdl-button--fab {
color: $color-font-dark !important;
background-color: $color-main !important;
}
.mdl-button--colored {
color: $color-font-dark !important;
}
.mdl-button--raised:hover {
background-color: $color-main !important;
}
.mdl-button--colored {
&,
&:focus,
&:active,
&:hover {
background-color: $color-main !important;
}
}

View File

@ -1,5 +1,4 @@
@import "./variables";
@import "./font-family";
/* Headings */
@ -44,7 +43,6 @@ h1, h2, h3, h4, h5, h6 {
padding: 0;
margin-top: 0;
margin-bottom: .2em;
font-family: vn-font-bold;
}
/* Colors */

View File

@ -0,0 +1,6 @@
@mixin browser($browser) {
html[data-browser*="#{$browser}"] & {
@content;
}
}

View File

@ -1,3 +1,4 @@
@import "./util";
$menu-width: 16em;
$topbar-height: 4em;
@ -107,3 +108,4 @@ $color-alert-light: darken($color-alert, 35%);
$border-thin: .05em solid $color-spacer;
$border-thin-light: .05em solid $color-spacer-light;
$shadow: 0 .15em .15em 0 rgba(0, 0, 0, .3);

View File

@ -7,18 +7,13 @@ import 'angular-translate-loader-partial';
import '@uirouter/angularjs';
import 'mg-crud';
import 'oclazyload';
import 'angular-moment';
export const ngDeps = [
'pascalprecht.translate',
'ui.router',
'mgCrud',
'oc.lazyLoad',
'angularMoment'
'oc.lazyLoad'
];
import 'material-design-lite';
import 'material-design-lite/dist/material.orange-deep_orange.min.css';
import * as validator from 'validator';
export {validator};

View File

@ -31,24 +31,6 @@
"resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz",
"integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw=="
},
"angular-animate": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.7.tgz",
"integrity": "sha512-KLbU9gtgCyNSaMZKnNe9Yi6UTlDMN2EWyokQ06TG5fbDWOePm+kv2xqeUg1S2h+ZLjK0NnoVDIvwDhVQ+AWAag=="
},
"angular-aria": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.7.tgz",
"integrity": "sha512-Jju0VudfKVp+6FUtfzpDBuOJE8+8cGPSM10nRicXvJRX/Nx/YOu38f2aL3HQwThrRK+3E5jfo7yRwVNYoSmSxg=="
},
"angular-moment": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
"requires": {
"moment": ">=2.8.0 <3.0.0"
}
},
"angular-translate": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz",
@ -83,11 +65,6 @@
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"flatpickr": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.5.2.tgz",
"integrity": "sha512-jDy4QYGpmiy7+Qk8QvKJ4spjDdxcx9cxMydmq1x427HkKWBw0qizLYeYM2F6tMcvvqGjU5VpJS55j4LnsaBblA=="
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
@ -97,11 +74,6 @@
"esprima": "^4.0.0"
}
},
"material-design-lite": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/material-design-lite/-/material-design-lite-1.3.0.tgz",
"integrity": "sha1-0ATOP+6Zoe63Sni4oyUTSl8RcdM="
},
"mg-crud": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
@ -110,19 +82,6 @@
"angular": "^1.6.1"
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"moment-timezone": {
"version": "0.5.25",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.25.tgz",
"integrity": "sha512-DgEaTyN/z0HFaVcVbSyVCUU6HeFdnNC3vE4c9cgu2dgMTvjBUBdBzWfasTBmAW45u5OIMeCJtU8yNjM22DHucw==",
"requires": {
"moment": ">= 2.9.0"
}
},
"npm": {
"version": "6.11.3",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.11.3.tgz",

View File

@ -12,16 +12,10 @@
"@babel/polyfill": "^7.2.5",
"@uirouter/angularjs": "^1.0.20",
"angular": "^1.7.5",
"angular-animate": "^1.7.7",
"angular-aria": "^1.7.7",
"angular-moment": "^1.3.0",
"angular-translate": "^2.18.1",
"angular-translate-loader-partial": "^2.18.1",
"flatpickr": "^4.5.2",
"js-yaml": "^3.13.1",
"material-design-lite": "^1.3.0",
"mg-crud": "^1.1.2",
"moment-timezone": "^0.5.25",
"npm": "^6.11.3",
"oclazyload": "^0.6.3",
"require-yaml": "0.0.1",

View File

@ -1,12 +1,5 @@
@import "variables";
body {
background-color: $color-bg;
overflow: auto;
height: 100%;
font-family: vn-font;
color: $color-font;
}
vn-app {
height: inherit;
display: block;
@ -104,37 +97,3 @@ 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;
}
}
}
}

View File

@ -0,0 +1,24 @@
<a ng-if="$ctrl.links.btnOne"
vn-tooltip="{{::$ctrl.links.btnOne.tooltip}}"
class="vn-button colored"
ui-sref="{{::$ctrl.links.btnOne.state}}" target="_blank">
<vn-icon
icon="{{::$ctrl.links.btnOne.icon}}">
</vn-icon>
</a>
<a ng-if="$ctrl.links.btnTwo"
vn-tooltip="{{::$ctrl.links.btnTwo.tooltip}}"
class="vn-button colored"
ui-sref="{{::$ctrl.links.btnTwo.state}}" target="_blank">
<vn-icon
icon="{{::$ctrl.links.btnTwo.icon}}">
</vn-icon>
</a>
<a ng-if="$ctrl.links.btnThree"
vn-tooltip="{{::$ctrl.links.btnThree.tooltip}}"
class="vn-button colored"
ui-sref="{{::$ctrl.links.btnThree.state}}" target="_blank">
<vn-icon
icon="{{::$ctrl.links.btnThree.icon}}">
</vn-icon>
</a>

View File

@ -1 +1,12 @@
import ngModule from '../../module';
import './style.scss';
export default class QuickLinks {}
ngModule.component('vnQuickLinks', {
template: require('./index.html'),
controller: QuickLinks,
bindings: {
links: '<?'
}
});

View File

@ -13,15 +13,19 @@
color: $color-font-dark;
& > * {
min-width: 1.8em;
@extend %clickable;
min-width: 45px;
height: 45px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
padding: .5em;
color: inherit;
& > vn-icon {
padding: 10px;
}
vn-icon {
font-size: 1.8em;
}
}
@ -55,28 +59,25 @@
}
}
}
& > .quicklinks {
.quicklinks,
vn-quick-links {
display: flex;
align-items: center;
justify-content: center;
padding: 0;
& > a {
padding: $spacing-sm;
padding: 0 $spacing-md;
margin: 0 $spacing-sm;
& > vn-icon {
font-size: 1.8em;
padding: 0;
& > i {
line-height: 36px
}
}
}
}
}
}
vn-popover {
.vn-popover {
.vn-descriptor > .header > a:first-child {
visibility: hidden;
}

View File

@ -4,7 +4,7 @@
ng-repeat="mod in ::$ctrl.modules"
ui-sref="{{::mod.route.state}}"
translate-attr="::{title: mod.name}"
class="mdl-shadow--4dp">
class="vn-shadow">
<div>
<vn-icon icon="{{::mod.icon || 'photo'}}"></vn-icon>
</div>

View File

@ -1,11 +1,11 @@
import './app/app';
import './login/login';
import './home/home';
import './main-menu/main-menu';
import './background/background';
import './side-menu/side-menu';
import './left-menu/left-menu';
import './topbar/topbar';
import './user-popover';
import './descriptor';
import './home/home';
import './left-menu/left-menu';
import './login/login';
import './main-menu/main-menu';
import './topbar/topbar';
import './side-menu/side-menu';
import './summary';
import './user-popover';

View File

@ -4,14 +4,12 @@
<vn-textfield
label="User"
ng-model="$ctrl.user"
name="user"
vn-id="userField"
vn-focus>
</vn-textfield>
<vn-textfield
label="Password"
ng-model="$ctrl.password"
name="password"
type="password">
</vn-textfield>
<vn-check

View File

@ -11,8 +11,8 @@ vn-main-menu {
align-items: center;
box-sizing: border-box;
& > *:hover {
color: $color-main;
& > * {
transition: color 250ms ease-out;
}
& > #user {
vertical-align: middle;
@ -20,12 +20,16 @@ vn-main-menu {
margin-right: .2em;
cursor: pointer;
}
& > vn-icon-button {
font-size: 2.1em;
& > .vn-button {
font-size: 1.2em;
color: inherit;
padding: 0;
margin-left: .3em;
}
& > :hover {
color: $color-main;
opacity: 1;
}
}
}

View File

@ -20,6 +20,7 @@
& > vn-horizontal {
flex-wrap: wrap;
padding: $spacing-md;
overflow: hidden;
h4 {
margin-bottom: $spacing-md;
@ -28,7 +29,7 @@
line-height: 1;
padding: 7px;
padding-bottom: 4px; /* Bottom line-height fix */
font-family: unset;
font-weight: lighter;
background-color: $color-main-light;
border-bottom: .1em solid $color-main;
white-space: nowrap;
@ -38,7 +39,6 @@
& > * {
margin: $spacing-sm;
min-width: 14em;
overflow: hidden;
padding: 0;
}
& > vn-auto {

View File

@ -1,31 +1,43 @@
@import "./variables";
@import "./effects";
html, body {
margin: 0;
padding: 0;
form vn-horizontal {
align-items: stretch;
& > * {
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;
}
}
}
}
a:focus,
input:focus,
button:focus {
outline: none;
}
button::-moz-focus-inner,
input[type=submit]::-moz-focus-inner,
input[type=button]::-moz-focus-inner,
input[type=reset]::-moz-focus-inner {
border: none;
}
.unselectable {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
a, .link {
.link {
color: $color-font-link;
text-decoration: none;
outline: 0
}
.link {
cursor: pointer;
@ -34,6 +46,12 @@ a, .link {
text-decoration: underline;
}
}
.unselectable {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
vn-bg-title {
display: block;
text-align: center;
@ -77,9 +95,6 @@ html [pointer], .pointer{
html [noDrop], .noDrop{
cursor: no-drop;
}
button {
@extend %clickable;
}
vn-button-bar {
display: block;
margin-top: $spacing-sm;
@ -91,9 +106,6 @@ vn-tool-bar {
margin-right: .6em;
}
}
input[type="submit"]:disabled, button:disabled {
opacity: 0.7;
}
html [scrollable] {
min-height: 1px;
flex: 1;

View File

@ -80,7 +80,8 @@
vn-tooltip="New zone"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"
<vn-float-button
icon="add"
vn-acl="deliveryBoss"
vn-acl-action="remove">
</vn-float-button>

View File

@ -28,16 +28,18 @@
ng-click="$ctrl.showLastTickets($event)"
vn-tooltip="Imports ticket lines">
</vn-button>
<vn-input-range
<vn-range
vn-one
label="Responsability"
value="$ctrl.claim.responsibility"
min-label="Company"
max-label="Sales/Client"
ng-model="$ctrl.claim.responsibility"
max="$ctrl.maxResponsibility"
min="1"
step="1"
vn-acl="salesAssistant"
on-change="$ctrl.saveResponsibility(value)">
</vn-input-range>
</vn-range>
</vn-tool-bar>
<vn-one
style = "text-align:right">

Some files were not shown because too many files have changed in this diff Show More