First working version
gitea/salix/1838-searchbar_refactor There was a failure building this commit
Details
gitea/salix/1838-searchbar_refactor There was a failure building this commit
Details
This commit is contained in:
parent
026c608f42
commit
515234187a
|
@ -346,7 +346,7 @@ let actions = {
|
||||||
.write('vn-searchbar input', searchValue)
|
.write('vn-searchbar input', searchValue)
|
||||||
.click('vn-searchbar vn-icon[icon="search"]')
|
.click('vn-searchbar vn-icon[icon="search"]')
|
||||||
.wait(100)
|
.wait(100)
|
||||||
.waitForNumberOfElements('.searchResult', 1)
|
.waitForNumberOfElements('.search-result', 1)
|
||||||
.evaluate(() => {
|
.evaluate(() => {
|
||||||
return document.querySelector('ui-view vn-card vn-table') != null;
|
return document.querySelector('ui-view vn-card vn-table') != null;
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default {
|
||||||
clientsIndex: {
|
clientsIndex: {
|
||||||
searchClientInput: `vn-textfield input`,
|
searchClientInput: `vn-textfield input`,
|
||||||
searchButton: 'vn-searchbar vn-icon[icon="search"]',
|
searchButton: 'vn-searchbar vn-icon[icon="search"]',
|
||||||
searchResult: 'vn-client-index .vn-list-item',
|
search-result: 'vn-client-index .vn-item',
|
||||||
createClientButton: `vn-float-button`,
|
createClientButton: `vn-float-button`,
|
||||||
othersButton: 'vn-left-menu li[name="Others"] > a'
|
othersButton: 'vn-left-menu li[name="Others"] > a'
|
||||||
},
|
},
|
||||||
|
@ -182,9 +182,9 @@ export default {
|
||||||
itemsIndex: {
|
itemsIndex: {
|
||||||
searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]',
|
searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]',
|
||||||
createItemButton: `vn-float-button`,
|
createItemButton: `vn-float-button`,
|
||||||
searchResult: 'vn-item-index a.vn-tr',
|
search-result: 'vn-item-index a.vn-tr',
|
||||||
searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]',
|
search-resultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]',
|
||||||
searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
|
search-resultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
|
||||||
acceptClonationAlertButton: '.vn-confirm.shown [response="accept"]',
|
acceptClonationAlertButton: '.vn-confirm.shown [response="accept"]',
|
||||||
searchItemInput: 'vn-searchbar vn-textfield input',
|
searchItemInput: 'vn-searchbar vn-textfield input',
|
||||||
searchButton: 'vn-searchbar vn-icon[icon="search"]',
|
searchButton: 'vn-searchbar vn-icon[icon="search"]',
|
||||||
|
@ -326,9 +326,9 @@ export default {
|
||||||
openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
||||||
advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input',
|
advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input',
|
||||||
newTicketButton: 'vn-ticket-index > a',
|
newTicketButton: 'vn-ticket-index > a',
|
||||||
searchResult: 'vn-ticket-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
search-result: 'vn-ticket-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
|
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
|
||||||
searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
|
search-resultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)',
|
||||||
searchTicketInput: `vn-ticket-index vn-textfield input`,
|
searchTicketInput: `vn-ticket-index vn-textfield input`,
|
||||||
searchWeeklyTicketInput: `vn-ticket-weekly-index vn-textfield input`,
|
searchWeeklyTicketInput: `vn-ticket-weekly-index vn-textfield input`,
|
||||||
searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]',
|
searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]',
|
||||||
|
@ -517,7 +517,7 @@ export default {
|
||||||
},
|
},
|
||||||
claimsIndex: {
|
claimsIndex: {
|
||||||
searchClaimInput: `vn-claim-index vn-textfield input`,
|
searchClaimInput: `vn-claim-index vn-textfield input`,
|
||||||
searchResult: 'vn-claim-index vn-card > vn-table > div > vn-tbody > a',
|
search-result: 'vn-claim-index vn-card > vn-table > div > vn-tbody > a',
|
||||||
searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]'
|
searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]'
|
||||||
},
|
},
|
||||||
claimDescriptor: {
|
claimDescriptor: {
|
||||||
|
@ -580,9 +580,9 @@ export default {
|
||||||
isPaidWithManaCheckbox: 'vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
isPaidWithManaCheckbox: 'vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
||||||
},
|
},
|
||||||
ordersIndex: {
|
ordersIndex: {
|
||||||
searchResult: 'vn-order-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
search-result: 'vn-order-index vn-card > 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)',
|
search-resultDate: '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)',
|
search-resultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)',
|
||||||
searchOrderInput: `vn-order-index vn-textfield input`,
|
searchOrderInput: `vn-order-index vn-textfield input`,
|
||||||
searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]',
|
searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]',
|
||||||
createOrderButton: `vn-float-button`,
|
createOrderButton: `vn-float-button`,
|
||||||
|
@ -722,7 +722,7 @@ export default {
|
||||||
invoiceOutIndex: {
|
invoiceOutIndex: {
|
||||||
searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`,
|
searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`,
|
||||||
searchButton: 'vn-invoice-out-index vn-searchbar vn-icon[icon="search"]',
|
searchButton: 'vn-invoice-out-index vn-searchbar vn-icon[icon="search"]',
|
||||||
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
search-result: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
},
|
},
|
||||||
invoiceOutDescriptor: {
|
invoiceOutDescriptor: {
|
||||||
moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[icon=more_vert]',
|
moreMenu: 'vn-invoice-out-descriptor vn-icon-menu[icon=more_vert]',
|
||||||
|
|
|
@ -13,8 +13,8 @@ describe('Client create path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.clientsIndex.searchClientInput, 'Carol Danvers')
|
.write(selectors.clientsIndex.searchClientInput, 'Carol Danvers')
|
||||||
.waitToClick(selectors.clientsIndex.searchButton)
|
.waitToClick(selectors.clientsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.clientsIndex.searchResult, 0)
|
.waitForNumberOfElements(selectors.clientsIndex.search-result, 0)
|
||||||
.countElement(selectors.clientsIndex.searchResult);
|
.countElement(selectors.clientsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(0);
|
expect(result).toEqual(0);
|
||||||
});
|
});
|
||||||
|
@ -117,8 +117,8 @@ describe('Client create path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.clientsIndex.searchClientInput, 'Carol Danvers')
|
.write(selectors.clientsIndex.searchClientInput, 'Carol Danvers')
|
||||||
.waitToClick(selectors.clientsIndex.searchButton)
|
.waitToClick(selectors.clientsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.clientsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.clientsIndex.search-result, 1)
|
||||||
.countElement(selectors.clientsIndex.searchResult);
|
.countElement(selectors.clientsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -135,16 +135,16 @@ describe('Client balance path', () => {
|
||||||
let resultCount = await nightmare
|
let resultCount = await nightmare
|
||||||
.write(selectors.clientsIndex.searchClientInput, 'Petter Parker')
|
.write(selectors.clientsIndex.searchClientInput, 'Petter Parker')
|
||||||
.waitToClick(selectors.clientsIndex.searchButton)
|
.waitToClick(selectors.clientsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.clientsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.clientsIndex.search-result, 1)
|
||||||
.countElement(selectors.clientsIndex.searchResult);
|
.countElement(selectors.clientsIndex.search-result);
|
||||||
|
|
||||||
expect(resultCount).toEqual(1);
|
expect(resultCount).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the client's balance`, async() => {
|
it(`should click on the search result to access to the client's balance`, async() => {
|
||||||
let url = await nightmare
|
let url = await nightmare
|
||||||
.waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter Parker')
|
.waitForTextInElement(selectors.clientsIndex.search-result, 'Petter Parker')
|
||||||
.waitToClick(selectors.clientsIndex.searchResult)
|
.waitToClick(selectors.clientsIndex.search-result)
|
||||||
.waitToClick(selectors.clientBalance.balanceButton)
|
.waitToClick(selectors.clientBalance.balanceButton)
|
||||||
.waitForURL('/balance')
|
.waitForURL('/balance')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
|
@ -14,16 +14,16 @@ describe('Item summary path', () => {
|
||||||
.clearInput(selectors.itemsIndex.searchItemInput)
|
.clearInput(selectors.itemsIndex.searchItemInput)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Ranged weapon longbow 2m')
|
.write(selectors.itemsIndex.searchItemInput, 'Ranged weapon longbow 2m')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 1)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result summary button to open the item summary popup`, async() => {
|
it(`should click on the search result summary button to open the item summary popup`, async() => {
|
||||||
const isVisible = await nightmare
|
const isVisible = await nightmare
|
||||||
.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon longbow 2m')
|
.waitForTextInElement(selectors.itemsIndex.search-result, 'Ranged weapon longbow 2m')
|
||||||
.waitToClick(selectors.itemsIndex.searchResultPreviewButton)
|
.waitToClick(selectors.itemsIndex.search-resultPreviewButton)
|
||||||
.isVisible(selectors.itemSummary.basicData);
|
.isVisible(selectors.itemSummary.basicData);
|
||||||
|
|
||||||
expect(isVisible).toBeTruthy();
|
expect(isVisible).toBeTruthy();
|
||||||
|
@ -84,16 +84,16 @@ describe('Item summary path', () => {
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Melee weapon combat fist 15cm')
|
.write(selectors.itemsIndex.searchItemInput, 'Melee weapon combat fist 15cm')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 1)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should now click on the search result summary button to open the item summary popup`, async() => {
|
it(`should now click on the search result summary button to open the item summary popup`, async() => {
|
||||||
const isVisible = await nightmare
|
const isVisible = await nightmare
|
||||||
.waitForTextInElement(selectors.itemsIndex.searchResult, 'Melee weapon combat fist 15cm')
|
.waitForTextInElement(selectors.itemsIndex.search-result, 'Melee weapon combat fist 15cm')
|
||||||
.waitToClick(selectors.itemsIndex.searchResultPreviewButton)
|
.waitToClick(selectors.itemsIndex.search-resultPreviewButton)
|
||||||
.isVisible(selectors.itemSummary.basicData);
|
.isVisible(selectors.itemSummary.basicData);
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ describe('Item summary path', () => {
|
||||||
|
|
||||||
it(`should navigate to the one of the items detailed section`, async() => {
|
it(`should navigate to the one of the items detailed section`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitToClick(selectors.itemsIndex.searchResult)
|
.waitToClick(selectors.itemsIndex.search-result)
|
||||||
.waitForURL('summary')
|
.waitForURL('summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ describe('Item Create/Clone path', () => {
|
||||||
.clearInput(selectors.itemsIndex.searchItemInput)
|
.clearInput(selectors.itemsIndex.searchItemInput)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Infinity Gauntlet')
|
.write(selectors.itemsIndex.searchItemInput, 'Infinity Gauntlet')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 0)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 0)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(0);
|
expect(result).toEqual(0);
|
||||||
});
|
});
|
||||||
|
@ -99,16 +99,16 @@ describe('Item Create/Clone path', () => {
|
||||||
.clearInput(selectors.itemsIndex.searchItemInput)
|
.clearInput(selectors.itemsIndex.searchItemInput)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Infinity Gauntlet')
|
.write(selectors.itemsIndex.searchItemInput, 'Infinity Gauntlet')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 1)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should clone the Infinity Gauntlet`, async() => {
|
it(`should clone the Infinity Gauntlet`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.itemsIndex.searchResult, 'Infinity Gauntlet')
|
.waitForTextInElement(selectors.itemsIndex.search-result, 'Infinity Gauntlet')
|
||||||
.waitToClick(selectors.itemsIndex.searchResultCloneButton)
|
.waitToClick(selectors.itemsIndex.search-resultCloneButton)
|
||||||
.waitToClick(selectors.itemsIndex.acceptClonationAlertButton)
|
.waitToClick(selectors.itemsIndex.acceptClonationAlertButton)
|
||||||
.waitForURL('tags')
|
.waitForURL('tags')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
@ -122,8 +122,8 @@ describe('Item Create/Clone path', () => {
|
||||||
.clearInput(selectors.itemsIndex.searchItemInput)
|
.clearInput(selectors.itemsIndex.searchItemInput)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Infinity Gauntlet')
|
.write(selectors.itemsIndex.searchItemInput, 'Infinity Gauntlet')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 2)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 2)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(2);
|
expect(result).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,16 +32,16 @@ describe('Item regularize path', () => {
|
||||||
.clearInput(selectors.itemsIndex.searchItemInput)
|
.clearInput(selectors.itemsIndex.searchItemInput)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Ranged weapon pistol 9mm')
|
.write(selectors.itemsIndex.searchItemInput, 'Ranged weapon pistol 9mm')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 1)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(resultCount).toEqual(1);
|
expect(resultCount).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the item tax`, async() => {
|
it(`should click on the search result to access to the item tax`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon pistol 9mm')
|
.waitForTextInElement(selectors.itemsIndex.search-result, 'Ranged weapon pistol 9mm')
|
||||||
.waitToClick(selectors.itemsIndex.searchResult)
|
.waitToClick(selectors.itemsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
@ -91,16 +91,16 @@ describe('Item regularize path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 'missing')
|
.write(selectors.ticketsIndex.searchTicketInput, 'missing')
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the ticket summary`, async() => {
|
it(`should click on the search result to access to the ticket summary`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Missing')
|
.waitForTextInElement(selectors.ticketsIndex.search-result, 'Missing')
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
@ -138,16 +138,16 @@ describe('Item regularize path', () => {
|
||||||
.clearInput(selectors.itemsIndex.searchItemInput)
|
.clearInput(selectors.itemsIndex.searchItemInput)
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Ranged weapon pistol 9mm')
|
.write(selectors.itemsIndex.searchItemInput, 'Ranged weapon pistol 9mm')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 1)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(resultCount).toEqual(1);
|
expect(resultCount).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the item tax`, async() => {
|
it(`should click on the search result to access to the item tax`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon pistol 9mm')
|
.waitForTextInElement(selectors.itemsIndex.search-result, 'Ranged weapon pistol 9mm')
|
||||||
.waitToClick(selectors.itemsIndex.searchResult)
|
.waitToClick(selectors.itemsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
@ -181,16 +181,16 @@ describe('Item regularize path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 25)
|
.write(selectors.ticketsIndex.searchTicketInput, 25)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should now click on the search result to access to the ticket summary`, async() => {
|
it(`should now click on the search result to access to the ticket summary`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.ticketsIndex.searchResult, '25')
|
.waitForTextInElement(selectors.ticketsIndex.search-result, '25')
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,10 @@ describe('Item index path', () => {
|
||||||
|
|
||||||
it('should navigate forth and back to see the images column is still visible', async() => {
|
it('should navigate forth and back to see the images column is still visible', async() => {
|
||||||
const imageVisible = await nightmare
|
const imageVisible = await nightmare
|
||||||
.waitToClick(selectors.itemsIndex.searchResult)
|
.waitToClick(selectors.itemsIndex.search-result)
|
||||||
.waitToClick(selectors.itemDescriptor.goBackToModuleIndexButton)
|
.waitToClick(selectors.itemDescriptor.goBackToModuleIndexButton)
|
||||||
.waitToClick(selectors.itemsIndex.searchIcon)
|
.waitToClick(selectors.itemsIndex.searchIcon)
|
||||||
.wait(selectors.itemsIndex.searchResult)
|
.wait(selectors.itemsIndex.search-result)
|
||||||
.isVisible(selectors.itemsIndex.firstItemImage);
|
.isVisible(selectors.itemsIndex.firstItemImage);
|
||||||
|
|
||||||
expect(imageVisible).toBeTruthy();
|
expect(imageVisible).toBeTruthy();
|
||||||
|
@ -75,10 +75,10 @@ describe('Item index path', () => {
|
||||||
|
|
||||||
it('should now navigate forth and back to see the ids column is now visible', async() => {
|
it('should now navigate forth and back to see the ids column is now visible', async() => {
|
||||||
const idVisible = await nightmare
|
const idVisible = await nightmare
|
||||||
.waitToClick(selectors.itemsIndex.searchResult)
|
.waitToClick(selectors.itemsIndex.search-result)
|
||||||
.waitToClick(selectors.itemDescriptor.goBackToModuleIndexButton)
|
.waitToClick(selectors.itemDescriptor.goBackToModuleIndexButton)
|
||||||
.waitToClick(selectors.itemsIndex.searchIcon)
|
.waitToClick(selectors.itemsIndex.searchIcon)
|
||||||
.wait(selectors.itemsIndex.searchResult)
|
.wait(selectors.itemsIndex.search-result)
|
||||||
.isVisible(selectors.itemsIndex.firstItemId);
|
.isVisible(selectors.itemsIndex.firstItemId);
|
||||||
|
|
||||||
expect(idVisible).toBeTruthy();
|
expect(idVisible).toBeTruthy();
|
||||||
|
|
|
@ -12,8 +12,8 @@ describe('Item log path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.itemsIndex.searchItemInput, 'Knowledge artifact')
|
.write(selectors.itemsIndex.searchItemInput, 'Knowledge artifact')
|
||||||
.waitToClick(selectors.itemsIndex.searchButton)
|
.waitToClick(selectors.itemsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.itemsIndex.searchResult, 0)
|
.waitForNumberOfElements(selectors.itemsIndex.search-result, 0)
|
||||||
.countElement(selectors.itemsIndex.searchResult);
|
.countElement(selectors.itemsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(0);
|
expect(result).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -213,8 +213,8 @@ xdescribe('Ticket Edit sale path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.claimsIndex.searchClaimInput, 4)
|
.write(selectors.claimsIndex.searchClaimInput, 4)
|
||||||
.waitToClick(selectors.claimsIndex.searchButton)
|
.waitToClick(selectors.claimsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.claimsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.claimsIndex.search-result, 1)
|
||||||
.countElement(selectors.claimsIndex.searchResult);
|
.countElement(selectors.claimsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,15 +34,15 @@ describe('Ticket descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 11)
|
.write(selectors.ticketsIndex.searchTicketInput, 11)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the ticket`, async() => {
|
it(`should click on the search result to access to the ticket`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
@ -94,15 +94,15 @@ describe('Ticket descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 11)
|
.write(selectors.ticketsIndex.searchTicketInput, 11)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the ticket`, async() => {
|
it(`should click on the search result to access to the ticket`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,16 @@ describe('Ticket diary path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 1)
|
.write(selectors.ticketsIndex.searchTicketInput, 1)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the ticket summary`, async() => {
|
it(`should click on the search result to access to the ticket summary`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Bat cave')
|
.waitForTextInElement(selectors.ticketsIndex.search-result, 'Bat cave')
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,16 @@ describe('Ticket descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 18)
|
.write(selectors.ticketsIndex.searchTicketInput, 18)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the search result to access to the ticket summary`, async() => {
|
it(`should click on the search result to access to the ticket summary`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Cerebro')
|
.waitForTextInElement(selectors.ticketsIndex.search-result, 'Cerebro')
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
@ -69,9 +69,9 @@ describe('Ticket descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 18)
|
.write(selectors.ticketsIndex.searchTicketInput, 18)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.wait(selectors.ticketsIndex.searchResultDate)
|
.wait(selectors.ticketsIndex.search-resultDate)
|
||||||
.waitToGetProperty(selectors.ticketsIndex.searchResultDate, 'innerText');
|
.waitToGetProperty(selectors.ticketsIndex.search-resultDate, 'innerText');
|
||||||
|
|
||||||
expect(result).toContain(2000);
|
expect(result).toContain(2000);
|
||||||
});
|
});
|
||||||
|
@ -83,16 +83,16 @@ describe('Ticket descriptor path', () => {
|
||||||
.clearInput(selectors.ticketsIndex.searchTicketInput)
|
.clearInput(selectors.ticketsIndex.searchTicketInput)
|
||||||
.write(selectors.ticketsIndex.searchTicketInput, 16)
|
.write(selectors.ticketsIndex.searchTicketInput, 16)
|
||||||
.waitToClick(selectors.ticketsIndex.searchButton)
|
.waitToClick(selectors.ticketsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should now click on the search result to access to the ticket summary`, async() => {
|
it(`should now click on the search result to access to the ticket summary`, async() => {
|
||||||
const url = await nightmare
|
const url = await nightmare
|
||||||
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Many Places')
|
.waitForTextInElement(selectors.ticketsIndex.search-result, 'Many Places')
|
||||||
.waitToClick(selectors.ticketsIndex.searchResult)
|
.waitToClick(selectors.ticketsIndex.search-result)
|
||||||
.waitForURL('/summary')
|
.waitForURL('/summary')
|
||||||
.parsedUrl();
|
.parsedUrl();
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,8 @@ describe('claim Descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.claimsIndex.searchClaimInput, claimId)
|
.write(selectors.claimsIndex.searchClaimInput, claimId)
|
||||||
.waitToClick(selectors.claimsIndex.searchButton)
|
.waitToClick(selectors.claimsIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.claimsIndex.searchResult, 0)
|
.waitForNumberOfElements(selectors.claimsIndex.search-result, 0)
|
||||||
.countElement(selectors.claimsIndex.searchResult);
|
.countElement(selectors.claimsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(0);
|
expect(result).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,8 +15,8 @@ describe('InvoiceOut descriptor path', () => {
|
||||||
.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton)
|
.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton)
|
||||||
.write(selectors.ticketsIndex.advancedSearchInvoiceOut, 'T2222222')
|
.write(selectors.ticketsIndex.advancedSearchInvoiceOut, 'T2222222')
|
||||||
.waitToClick(selectors.ticketsIndex.advancedSearchButton)
|
.waitToClick(selectors.ticketsIndex.advancedSearchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 1)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
@ -36,8 +36,8 @@ describe('InvoiceOut descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.invoiceOutIndex.searchInvoiceOutInput, 'T2222222')
|
.write(selectors.invoiceOutIndex.searchInvoiceOutInput, 'T2222222')
|
||||||
.waitToClick(selectors.invoiceOutIndex.searchButton)
|
.waitToClick(selectors.invoiceOutIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.invoiceOutIndex.searchResult, 1)
|
.waitForNumberOfElements(selectors.invoiceOutIndex.search-result, 1)
|
||||||
.countElement(selectors.invoiceOutIndex.searchResult);
|
.countElement(selectors.invoiceOutIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
@ -72,8 +72,8 @@ describe('InvoiceOut descriptor path', () => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.write(selectors.invoiceOutIndex.searchInvoiceOutInput, 'T2222222')
|
.write(selectors.invoiceOutIndex.searchInvoiceOutInput, 'T2222222')
|
||||||
.waitToClick(selectors.invoiceOutIndex.searchButton)
|
.waitToClick(selectors.invoiceOutIndex.searchButton)
|
||||||
.waitForNumberOfElements(selectors.invoiceOutIndex.searchResult, 0)
|
.waitForNumberOfElements(selectors.invoiceOutIndex.search-result, 0)
|
||||||
.countElement(selectors.invoiceOutIndex.searchResult);
|
.countElement(selectors.invoiceOutIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(0);
|
expect(result).toEqual(0);
|
||||||
});
|
});
|
||||||
|
@ -94,8 +94,8 @@ describe('InvoiceOut descriptor path', () => {
|
||||||
.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton)
|
.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton)
|
||||||
.write(selectors.ticketsIndex.advancedSearchInvoiceOut, 'T2222222')
|
.write(selectors.ticketsIndex.advancedSearchInvoiceOut, 'T2222222')
|
||||||
.waitToClick(selectors.ticketsIndex.advancedSearchButton)
|
.waitToClick(selectors.ticketsIndex.advancedSearchButton)
|
||||||
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 0)
|
.waitForNumberOfElements(selectors.ticketsIndex.search-result, 0)
|
||||||
.countElement(selectors.ticketsIndex.searchResult);
|
.countElement(selectors.ticketsIndex.search-result);
|
||||||
|
|
||||||
expect(result).toEqual(0);
|
expect(result).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -208,11 +208,9 @@ export default class Autocomplete extends Field {
|
||||||
|
|
||||||
onContainerKeyDown(event) {
|
onContainerKeyDown(event) {
|
||||||
if (event.defaultPrevented) return;
|
if (event.defaultPrevented) return;
|
||||||
|
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
case 'Enter':
|
|
||||||
this.showDropDown();
|
this.showDropDown();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -221,6 +219,7 @@ export default class Autocomplete extends Field {
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log(event.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
onContainerClick(event) {
|
onContainerClick(event) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ export default class ButtonMenu extends Button {
|
||||||
constructor($element, $scope, $transclude) {
|
constructor($element, $scope, $transclude) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this.$transclude = $transclude;
|
this.$transclude = $transclude;
|
||||||
$element.on('click', e => this.onClick(e));
|
$element.on('click', e => this.onButtonClick(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
get model() {
|
get model() {
|
||||||
|
@ -41,8 +41,7 @@ export default class ButtonMenu extends Button {
|
||||||
Object.assign(this.$.dropDown, props);
|
Object.assign(this.$.dropDown, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(event) {
|
onButtonClick(event) {
|
||||||
if (this.disabled) return;
|
|
||||||
if (event.defaultPrevented) return;
|
if (event.defaultPrevented) return;
|
||||||
this.emit('open');
|
this.emit('open');
|
||||||
this.showDropDown();
|
this.showDropDown();
|
||||||
|
|
|
@ -39,19 +39,19 @@
|
||||||
}
|
}
|
||||||
&.colored {
|
&.colored {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: $color-main;
|
background-color: $color-button;
|
||||||
box-shadow: 0 .15em .15em 0 rgba(0, 0, 0, .3);
|
box-shadow: 0 .15em .15em 0 rgba(0, 0, 0, .3);
|
||||||
transition: background 200ms ease-in-out;
|
transition: background 200ms ease-in-out;
|
||||||
|
|
||||||
&:not(.disabled) {
|
&:not(.disabled) {
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: lighten($color-main, 10%);
|
background-color: lighten($color-button, 10%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.flat {
|
&.flat {
|
||||||
color: $color-main;
|
color: $color-button;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
transition: background 200ms ease-in-out;
|
transition: background 200ms ease-in-out;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
&.checked > .btn {
|
&.checked > .btn {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
background-color: $color-main;
|
background-color: $color-button;
|
||||||
|
|
||||||
& > .mark {
|
& > .mark {
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
@ -14,7 +14,9 @@ class DatePicker extends Field {
|
||||||
let value = this.input.value;
|
let value = this.input.value;
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
date = new Date(value);
|
let ymd = value.split('-')
|
||||||
|
.map(e => parseInt(e));
|
||||||
|
date = new Date(...ymd);
|
||||||
|
|
||||||
if (this.field) {
|
if (this.field) {
|
||||||
let orgDate = this.field instanceof Date
|
let orgDate = this.field instanceof Date
|
||||||
|
|
|
@ -29,6 +29,10 @@ export default class Dialog extends Popup {
|
||||||
* @return {Promise} A promise that will be resolved with response when dialog is closed
|
* @return {Promise} A promise that will be resolved with response when dialog is closed
|
||||||
*/
|
*/
|
||||||
show(data, responseHandler) {
|
show(data, responseHandler) {
|
||||||
|
if (this.shown)
|
||||||
|
return this.$q.reject(new Error('Dialog already shown'));
|
||||||
|
super.show();
|
||||||
|
|
||||||
if (typeof data == 'function') {
|
if (typeof data == 'function') {
|
||||||
responseHandler = data;
|
responseHandler = data;
|
||||||
data = null;
|
data = null;
|
||||||
|
@ -36,27 +40,27 @@ export default class Dialog extends Popup {
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.showHandler = responseHandler;
|
this.showHandler = responseHandler;
|
||||||
super.show();
|
|
||||||
return this.$q(resolve => {
|
return this.$q(resolve => {
|
||||||
this.resolve = resolve;
|
this.resolve = resolve;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the dialog.
|
* Hides the dialog resolving the promise returned by show().
|
||||||
*
|
*
|
||||||
* @param {String} response The response
|
* @param {String} response The response
|
||||||
*/
|
*/
|
||||||
hide(response) {
|
hide(response) {
|
||||||
if (!this.shown) return;
|
if (!this.shown) return;
|
||||||
this.showHandler = null;
|
|
||||||
super.hide();
|
super.hide();
|
||||||
|
|
||||||
|
this.showHandler = null;
|
||||||
if (this.resolve)
|
if (this.resolve)
|
||||||
this.resolve(response);
|
this.resolve(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the response handler.
|
* Calls the response handlers.
|
||||||
*
|
*
|
||||||
* @param {String} response The response code
|
* @param {String} response The response code
|
||||||
* @return {Boolean} The response handler return
|
* @return {Boolean} The response handler return
|
||||||
|
|
|
@ -28,14 +28,6 @@ describe('Component vnDialog', () => {
|
||||||
expect(called).toBeTruthy();
|
expect(called).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should hide the dialog when response is given`, () => {
|
|
||||||
controller.show();
|
|
||||||
spyOn(controller, 'hide');
|
|
||||||
controller.respond('answer');
|
|
||||||
|
|
||||||
expect(controller.hide).toHaveBeenCalledWith('answer');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should not hide the dialog when false is returned from response handler`, () => {
|
it(`should not hide the dialog when false is returned from response handler`, () => {
|
||||||
controller.show(() => false);
|
controller.show(() => false);
|
||||||
spyOn(controller, 'hide');
|
spyOn(controller, 'hide');
|
||||||
|
@ -46,12 +38,13 @@ describe('Component vnDialog', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hide()', () => {
|
describe('hide()', () => {
|
||||||
it(`should do nothing if it's already hidden`, () => {
|
it(`should resolve the promise returned by show`, () => {
|
||||||
controller.onResponse = () => {};
|
let resolved = true;
|
||||||
spyOn(controller, 'onResponse');
|
controller.show().then(() => resolved = true);
|
||||||
controller.hide();
|
controller.hide();
|
||||||
|
$scope.$apply();
|
||||||
|
|
||||||
expect(controller.onResponse).not.toHaveBeenCalledWith();
|
expect(resolved).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -94,7 +87,7 @@ describe('Component vnDialog', () => {
|
||||||
expect(controller.onAccept).toHaveBeenCalledWith({$response: 'accept'});
|
expect(controller.onAccept).toHaveBeenCalledWith({$response: 'accept'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should resolve the promise returned by show() with response when hidden`, () => {
|
it(`should resolve the promise returned by show() with response`, () => {
|
||||||
let response;
|
let response;
|
||||||
controller.show().then(res => response = res);
|
controller.show().then(res => response = res);
|
||||||
controller.respond('response');
|
controller.respond('response');
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: .1em;
|
border-radius: .1em;
|
||||||
color: $color-main;
|
color: $color-button;
|
||||||
font-family: vn-font-bold;
|
font-family: vn-font-bold;
|
||||||
padding: .7em;
|
padding: .7em;
|
||||||
margin: -0.7em;
|
margin: -0.7em;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import template from './index.html';
|
||||||
import ArrayModel from '../array-model/array-model';
|
import ArrayModel from '../array-model/array-model';
|
||||||
import CrudModel from '../crud-model/crud-model';
|
import CrudModel from '../crud-model/crud-model';
|
||||||
import {mergeWhere} from 'vn-loopback/util/filter';
|
import {mergeWhere} from 'vn-loopback/util/filter';
|
||||||
|
import focus from '../../lib/focus';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @event select Thrown when model item is selected
|
* @event select Thrown when model item is selected
|
||||||
|
@ -86,9 +87,11 @@ export default class DropDown extends Popover {
|
||||||
* @param {String} search The initial search term or %null
|
* @param {String} search The initial search term or %null
|
||||||
*/
|
*/
|
||||||
show(parent, search) {
|
show(parent, search) {
|
||||||
this._activeOption = -1;
|
if (this.shown) return;
|
||||||
super.show(parent);
|
super.show(parent);
|
||||||
|
|
||||||
|
this._activeOption = -1;
|
||||||
|
|
||||||
this.list = this.popup.querySelector('.list');
|
this.list = this.popup.querySelector('.list');
|
||||||
this.ul = this.popup.querySelector('ul');
|
this.ul = this.popup.querySelector('ul');
|
||||||
|
|
||||||
|
@ -102,21 +105,25 @@ export default class DropDown extends Popover {
|
||||||
this.search = search;
|
this.search = search;
|
||||||
this.buildList();
|
this.buildList();
|
||||||
|
|
||||||
let input = this.popup.querySelector('input');
|
focus(this.popup.querySelector('input'));
|
||||||
setTimeout(() => input.focus());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
hide() {
|
||||||
|
if (!this.shown) return;
|
||||||
|
super.hide();
|
||||||
|
|
||||||
this.document.removeEventListener('keydown', this.docKeyDownHandler);
|
this.document.removeEventListener('keydown', this.docKeyDownHandler);
|
||||||
this.docKeyDownHandler = null;
|
this.docKeyDownHandler = null;
|
||||||
|
|
||||||
this.list.removeEventListener('scroll', this.listScrollHandler);
|
this.list.removeEventListener('scroll', this.listScrollHandler);
|
||||||
this.listScrollHandler = null;
|
this.listScrollHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.destroyList();
|
||||||
this.list = null;
|
this.list = null;
|
||||||
this.ul = null;
|
this.ul = null;
|
||||||
|
|
||||||
this.destroyList();
|
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,7 @@
|
||||||
ng-transclude="append"
|
ng-transclude="append"
|
||||||
class="append">
|
class="append">
|
||||||
</div>
|
</div>
|
||||||
<div class="icons post">
|
<div class="icons post"></div>
|
||||||
</div>
|
|
||||||
<div class="underline blur"></div>
|
<div class="underline blur"></div>
|
||||||
<div class="underline focus"></div>
|
<div class="underline focus"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
.vn-field {
|
.vn-field {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
& > .container {
|
& > .container {
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
top: 18px;
|
top: 18px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: $color-font-secondary;
|
color: $color-font-bg-marginal;
|
||||||
transition-property: top, color, font-size;
|
transition-property: top, color, font-size;
|
||||||
transition-duration: 400ms;
|
transition-duration: 400ms;
|
||||||
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
|
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
}
|
}
|
||||||
& > input {
|
& > input {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: $color-font;
|
||||||
|
|
||||||
&[type=time],
|
&[type=time],
|
||||||
&[type=date],
|
&[type=date],
|
||||||
|
@ -121,12 +123,13 @@
|
||||||
& > .icons {
|
& > .icons {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: $color-font-secondary;
|
color: $color-font-bg-marginal;
|
||||||
}
|
}
|
||||||
& > .prepend > prepend,
|
& > .prepend > prepend,
|
||||||
& > .append > append,
|
& > .append > append,
|
||||||
& > .icons {
|
& > .icons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
& > vn-icon {
|
& > vn-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
@ -159,7 +162,7 @@
|
||||||
}
|
}
|
||||||
&.focus {
|
&.focus {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background-color: $color-main;
|
background-color: $color-primary;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
width: 0;
|
width: 0;
|
||||||
transition-property: width, left, background-color;
|
transition-property: width, left, background-color;
|
||||||
|
@ -190,13 +193,49 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.standout {
|
||||||
|
border-radius: .1em;
|
||||||
|
background-color: rgba(255, 255, 255, .1);
|
||||||
|
padding: 0 12px;
|
||||||
|
transition-property: background-color, color;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
|
||||||
|
& > .container {
|
||||||
|
& > .underline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
& > .infix > .control > * {
|
||||||
|
color: $color-font-dark;
|
||||||
|
}
|
||||||
|
& > .prepend,
|
||||||
|
& > .append,
|
||||||
|
& > .icons {
|
||||||
|
color: $color-font-bg-dark-marginal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.focused {
|
||||||
|
background-color: $color-bg-panel;
|
||||||
|
|
||||||
|
& > .container {
|
||||||
|
& > .infix > .control > * {
|
||||||
|
color: $color-font;
|
||||||
|
}
|
||||||
|
& > .prepend,
|
||||||
|
& > .append,
|
||||||
|
& > .icons {
|
||||||
|
color: $color-font-bg-marginal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
&.not-empty,
|
&.not-empty,
|
||||||
&.focused,
|
&.focused,
|
||||||
&.invalid {
|
&.invalid {
|
||||||
& > .container > .infix {
|
& > .container > .infix {
|
||||||
& > label {
|
& > label {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
color: $color-main;
|
color: $color-primary;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
.vn-icon-button {
|
.vn-icon-button {
|
||||||
@extend %clickable-light;
|
@extend %clickable-light;
|
||||||
color: $color-main;
|
color: $color-button;
|
||||||
|
|
||||||
& > button {
|
& > button {
|
||||||
padding: .2em !important;
|
padding: .2em !important;
|
||||||
|
|
|
@ -41,6 +41,7 @@ import './list';
|
||||||
import './popover';
|
import './popover';
|
||||||
import './popup';
|
import './popup';
|
||||||
import './radio';
|
import './radio';
|
||||||
|
import './slot';
|
||||||
import './submit';
|
import './submit';
|
||||||
import './table';
|
import './table';
|
||||||
import './td-editable';
|
import './td-editable';
|
||||||
|
|
|
@ -1,45 +1,102 @@
|
||||||
@import "./effects";
|
@import "./effects";
|
||||||
|
/*
|
||||||
|
ul.menu {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
padding-top: $spacing-md;
|
||||||
|
margin: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
|
||||||
|
& > li > a {
|
||||||
|
@extend %clickable;
|
||||||
|
display: block;
|
||||||
|
color: inherit;
|
||||||
|
padding: .6em 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
vn-list,
|
||||||
.vn-list {
|
.vn-list {
|
||||||
|
display: block;
|
||||||
max-width: $width-sm;
|
max-width: $width-sm;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
a.vn-list-item {
|
vn-list,
|
||||||
@extend %clickable;
|
.vn-list {
|
||||||
|
vn-item,
|
||||||
|
.vn-item {
|
||||||
|
padding-left: $spacing-lg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.vn-list-item {
|
&.separated {
|
||||||
border-bottom: $border-thin-light;
|
vn-item,
|
||||||
display: block;
|
.vn-item {
|
||||||
text-decoration: none;
|
border-bottom: $border-thin-light;
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
& > vn-horizontal {
|
|
||||||
padding: $spacing-md;
|
padding: $spacing-md;
|
||||||
|
|
||||||
& > vn-one {
|
&:last-child {
|
||||||
overflow: hidden;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
& > .buttons {
|
}
|
||||||
align-items: center;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-item,
|
||||||
|
.vn-item {
|
||||||
|
@extend %clickable;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: inherit;
|
||||||
|
padding: $spacing-sm $spacing-md;
|
||||||
|
text-decoration: none;
|
||||||
|
min-height: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&.separated {
|
||||||
|
border-bottom: $border-thin-light;
|
||||||
|
|
||||||
vn-icon-button {
|
&:last-child {
|
||||||
opacity: .4;
|
border-bottom: none;
|
||||||
margin-left: .5em;
|
}
|
||||||
transition: opacity 250ms ease-out;
|
}
|
||||||
padding: 0;
|
&.active {
|
||||||
font-size: 1.2em;
|
@extend %active;
|
||||||
|
}
|
||||||
&:hover {
|
& > vn-item-section {
|
||||||
opacity: 1;
|
overflow: hidden;
|
||||||
}
|
flex: 1;
|
||||||
|
|
||||||
|
&[avatar] {
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: $spacing-md;
|
||||||
|
|
||||||
|
& > .vn-icon {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[side] {
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > .vn-button {
|
||||||
|
opacity: .4;
|
||||||
|
margin-left: .5em;
|
||||||
|
transition: opacity 250ms ease-out;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1.05em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vn-empty-rows {
|
}
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1.5em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -40,6 +40,11 @@ export default class Popup extends Component {
|
||||||
if (this.shown) return;
|
if (this.shown) return;
|
||||||
this._shown = true;
|
this._shown = true;
|
||||||
|
|
||||||
|
if (this.closeTimeout) {
|
||||||
|
this.$timeout.cancel(this.closeTimeout);
|
||||||
|
this.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
let linkFn = this.$compile(this.template);
|
let linkFn = this.$compile(this.template);
|
||||||
this.$contentScope = this.$.$new();
|
this.$contentScope = this.$.$new();
|
||||||
this.popup = linkFn(this.$contentScope, null,
|
this.popup = linkFn(this.$contentScope, null,
|
||||||
|
@ -60,9 +65,9 @@ export default class Popup extends Component {
|
||||||
this.deregisterCallback = this.$transitions.onStart({},
|
this.deregisterCallback = this.$transitions.onStart({},
|
||||||
() => this.hide());
|
() => this.hide());
|
||||||
|
|
||||||
this.$timeout.cancel(this.transitionTimeout);
|
this.$timeout.cancel(this.showTimeout);
|
||||||
this.transitionTimeout = this.$timeout(() => {
|
this.showTimeout = this.$timeout(() => {
|
||||||
this.transitionTimeout = null;
|
this.showTimeout = null;
|
||||||
classList.add('shown');
|
classList.add('shown');
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
|
@ -78,15 +83,13 @@ export default class Popup extends Component {
|
||||||
this.document.removeEventListener('keydown', this.keyDownHandler);
|
this.document.removeEventListener('keydown', this.keyDownHandler);
|
||||||
this.keyDownHandler = null;
|
this.keyDownHandler = null;
|
||||||
|
|
||||||
if (this.deregisterCallback) {
|
this.deregisterCallback();
|
||||||
this.deregisterCallback();
|
this.deregisterCallback = null;
|
||||||
this.deregisterCallback = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.popup.classList.remove('shown');
|
this.popup.classList.remove('shown');
|
||||||
|
|
||||||
this.$timeout.cancel(this.transitionTimeout);
|
this.$timeout.cancel(this.closeTimeout);
|
||||||
this.transitionTimeout = this.$timeout(
|
this.closeTimeout = this.$timeout(
|
||||||
() => this.onClose(), 200);
|
() => this.onClose(), 200);
|
||||||
|
|
||||||
this.lastEvent = null;
|
this.lastEvent = null;
|
||||||
|
@ -95,7 +98,7 @@ export default class Popup extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
this.transitionTimeout = null;
|
this.closeTimeout = null;
|
||||||
this.document.body.removeChild(this.popup);
|
this.document.body.removeChild(this.popup);
|
||||||
|
|
||||||
this.$contentScope.$destroy();
|
this.$contentScope.$destroy();
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.checked > .btn {
|
&.checked > .btn {
|
||||||
border-color: $color-main;
|
border-color: $color-button;
|
||||||
|
|
||||||
& > .mark {
|
& > .mark {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background-color: $color-main;
|
background-color: $color-button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.disabled.checked > .btn > .mark {
|
&.disabled.checked > .btn > .mark {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: $color-main;
|
background: $color-button;
|
||||||
border: none;
|
border: none;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
transition-timing-function: ease-out;
|
transition-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
&:focus::#{$thumb-selector} {
|
&:focus::#{$thumb-selector} {
|
||||||
box-shadow: 0 0 0 10px rgba($color-main, .2);
|
box-shadow: 0 0 0 10px rgba($color-button, .2);
|
||||||
}
|
}
|
||||||
&:active::#{$thumb-selector} {
|
&:active::#{$thumb-selector} {
|
||||||
transform: scale(1.5);
|
transform: scale(1.5);
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
background: $color-secondary;
|
background: $color-marginal;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
&.main {
|
&.main {
|
||||||
color: $color-main;
|
color: $color-button;
|
||||||
}
|
}
|
||||||
&.min-label {
|
&.min-label {
|
||||||
float: left;
|
float: left;
|
||||||
|
|
|
@ -1,14 +1,26 @@
|
||||||
<form ng-submit="$ctrl.onSubmit()">
|
<form ng-submit="$ctrl.onSubmit()">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
class="dense"
|
class="dense standout"
|
||||||
placeholder="{{::'Search' | translate}}"
|
placeholder="{{::'Search' | translate}}"
|
||||||
ng-model="$ctrl.searchString">
|
ng-model="$ctrl.searchString">
|
||||||
<prepend>
|
<prepend>
|
||||||
<vn-icon
|
<vn-icon
|
||||||
icon="search"
|
icon="search"
|
||||||
ng-click="$ctrl.clearFilter(); $ctrl.onSubmit()"
|
ng-click="$ctrl.onSubmit()"
|
||||||
pointer>
|
pointer>
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
|
<div class="search-params">
|
||||||
|
<span
|
||||||
|
ng-repeat="param in $ctrl.params"
|
||||||
|
class="search-param"
|
||||||
|
title="{{param.chip}}">
|
||||||
|
<vn-icon
|
||||||
|
icon="close"
|
||||||
|
ng-click="$ctrl.removeParam($index)">
|
||||||
|
</vn-icon>
|
||||||
|
{{param.chip}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</prepend>
|
</prepend>
|
||||||
<append>
|
<append>
|
||||||
<vn-icon
|
<vn-icon
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Component from '../../lib/component';
|
import Component from '../../lib/component';
|
||||||
import './style.scss';
|
|
||||||
import {buildFilter} from 'vn-loopback/util/filter';
|
import {buildFilter} from 'vn-loopback/util/filter';
|
||||||
|
import focus from '../../lib/focus';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An input specialized to perform searches, it allows to use a panel
|
* An input specialized to perform searches, it allows to use a panel
|
||||||
|
@ -11,56 +12,48 @@ import {buildFilter} from 'vn-loopback/util/filter';
|
||||||
* by calling the exprBuilder function for each non-null parameter.
|
* by calling the exprBuilder function for each non-null parameter.
|
||||||
*
|
*
|
||||||
* @property {Object} filter A key-value object with filter parameters
|
* @property {Object} filter A key-value object with filter parameters
|
||||||
* @property {Function} onSearch Function to call when search is submited
|
|
||||||
* @property {SearchPanel} panel The panel used for advanced searches
|
* @property {SearchPanel} panel The panel used for advanced searches
|
||||||
* @property {CrudModel} model The model used for searching
|
|
||||||
* @property {Function} exprBuilder If defined, is used to build each non-null param expresion
|
|
||||||
*/
|
*/
|
||||||
export default class Controller extends Component {
|
export default class Controller extends Component {
|
||||||
constructor($element, $scope, $compile, $state, $transitions) {
|
constructor($element, $) {
|
||||||
super($element, $scope);
|
super($element, $);
|
||||||
this.$element = $element;
|
this.searchState = '.';
|
||||||
this.$compile = $compile;
|
|
||||||
this.$state = $state;
|
|
||||||
this.$ = $scope;
|
|
||||||
let criteria = {to: this.$state.current.name};
|
|
||||||
this.deregisterCallback = $transitions.onSuccess(criteria,
|
|
||||||
() => this.onStateChange());
|
|
||||||
|
|
||||||
this._filter = null;
|
let criteria = {};
|
||||||
this.autoLoad = false;
|
this.deregisterCallback = this.$transitions.onSuccess(
|
||||||
|
criteria, () => this.onStateChange());
|
||||||
}
|
}
|
||||||
|
|
||||||
$postLink() {
|
$postLink() {
|
||||||
if (this.filter === null)
|
this.onStateChange();
|
||||||
this.onStateChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set filter(value) {
|
$onDestroy() {
|
||||||
this._filter = value;
|
this.deregisterCallback();
|
||||||
this.$state.go('.', {q: JSON.stringify(value)}, {location: 'replace'});
|
|
||||||
}
|
|
||||||
|
|
||||||
get filter() {
|
|
||||||
return this._filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
onStateChange() {
|
|
||||||
this._filter = null;
|
|
||||||
|
|
||||||
if (this.$state.params.q) {
|
|
||||||
try {
|
|
||||||
this._filter = JSON.parse(this.$state.params.q);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.doSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get shownFilter() {
|
get shownFilter() {
|
||||||
return this._filter != null ? this._filter : this.suggestedFilter;
|
return this.filter != null
|
||||||
|
? this.filter
|
||||||
|
: this.suggestedFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStateChange() {
|
||||||
|
if (this.$state.is(this.searchState)) {
|
||||||
|
if (this.$state.params.q) {
|
||||||
|
try {
|
||||||
|
this.filter = JSON.parse(this.$state.params.q);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
this.filter = null;
|
||||||
|
|
||||||
|
focus(this.element.querySelector('vn-textfield input'));
|
||||||
|
} else
|
||||||
|
this.filter = null;
|
||||||
|
|
||||||
|
this.toBar(this.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
openPanel(event) {
|
openPanel(event) {
|
||||||
|
@ -88,21 +81,143 @@ export default class Controller extends Component {
|
||||||
onPanelSubmit(filter) {
|
onPanelSubmit(filter) {
|
||||||
this.$.popover.hide();
|
this.$.popover.hide();
|
||||||
filter = compact(filter);
|
filter = compact(filter);
|
||||||
this.filter = filter != null ? filter : {};
|
filter = filter != null ? filter : {};
|
||||||
|
this.doSearch(filter);
|
||||||
let element = this.element.querySelector('vn-textfield input');
|
|
||||||
element.select();
|
|
||||||
element.focus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.filter = this.getObjectFromString(this.searchString);
|
this.doSearch(this.fromBar());
|
||||||
|
}
|
||||||
|
|
||||||
|
removeParam(index) {
|
||||||
|
this.params.splice(index, 1);
|
||||||
|
this.doSearch(this.fromBar());
|
||||||
|
}
|
||||||
|
|
||||||
|
get searchString() {
|
||||||
|
return this._searchString;
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchString(value) {
|
||||||
|
this._searchString = value;
|
||||||
|
if (value == null) this.params = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch(filter) {
|
||||||
|
let opts = this.$state.is(this.searchState)
|
||||||
|
? {location: 'replace'} : null;
|
||||||
|
this.$state.go(this.searchState,
|
||||||
|
{q: JSON.stringify(filter)}, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
fromBar() {
|
||||||
|
let filter = {};
|
||||||
|
|
||||||
|
if (this.searchString)
|
||||||
|
filter.search = this.searchString;
|
||||||
|
|
||||||
|
if (this.params) {
|
||||||
|
for (let param of this.params)
|
||||||
|
filter[param.key] = param.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
toBar(filter) {
|
||||||
|
this.params = [];
|
||||||
|
this.searchString = filter && filter.search;
|
||||||
|
if (!filter) return;
|
||||||
|
|
||||||
|
let keys = Object.keys(filter);
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (key == 'search') return;
|
||||||
|
let value = filter[key];
|
||||||
|
let chip;
|
||||||
|
|
||||||
|
if (typeof value == 'string'
|
||||||
|
&& /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value))
|
||||||
|
value = new Date(value);
|
||||||
|
|
||||||
|
switch (typeof value) {
|
||||||
|
case 'boolean':
|
||||||
|
chip = `${value ? '' : 'not '}${key}`;
|
||||||
|
break;
|
||||||
|
case 'number':
|
||||||
|
case 'string':
|
||||||
|
chip = `${key}: ${value}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (value instanceof Date) {
|
||||||
|
let format = 'yyyy-MM-dd';
|
||||||
|
if (value.getHours() || value.getMinutes())
|
||||||
|
format += ' HH:mm';
|
||||||
|
chip = `${key}: ${this.$filter('date')(value, format)}`;
|
||||||
|
} else
|
||||||
|
chip = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.params.push({chip, key, value});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnSearchbar', {
|
||||||
|
controller: Controller,
|
||||||
|
template: require('./searchbar.html'),
|
||||||
|
bindings: {
|
||||||
|
searchState: '@?',
|
||||||
|
filter: '<?',
|
||||||
|
suggestedFilter: '<?',
|
||||||
|
panel: '@',
|
||||||
|
info: '@?'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {CrudModel} model The model used for searching
|
||||||
|
* @property {Function} exprBuilder If defined, is used to build each non-null param expresion
|
||||||
|
* @property {Function} onSearch Function to call when search is submited
|
||||||
|
*/
|
||||||
|
class AutoSearch {
|
||||||
|
constructor($state, $transitions) {
|
||||||
|
this.$state = $state;
|
||||||
|
this.$transitions = $transitions;
|
||||||
|
|
||||||
|
let criteria = {to: this.$state.current.name};
|
||||||
|
this.deregisterCallback = this.$transitions.onSuccess(criteria,
|
||||||
|
() => this.onStateChange());
|
||||||
|
|
||||||
|
this.fetchFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
$postLink() {
|
||||||
|
if (this.filter !== null)
|
||||||
|
this.doSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
this.deregisterCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchFilter() {
|
||||||
|
if (this.$state.params.q) {
|
||||||
|
try {
|
||||||
|
this.filter = JSON.parse(this.$state.params.q);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
this.filter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStateChange() {
|
||||||
|
this.fetchFilter();
|
||||||
|
this.doSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
doSearch() {
|
doSearch() {
|
||||||
this.searchString = this.getStringFromObject(this.shownFilter);
|
let filter = this.filter;
|
||||||
|
|
||||||
let filter = this._filter;
|
|
||||||
if (filter == null && this.autoload)
|
if (filter == null && this.autoload)
|
||||||
filter = {};
|
filter = {};
|
||||||
|
|
||||||
|
@ -141,94 +256,17 @@ export default class Controller extends Component {
|
||||||
exprBuilder(param, value) {
|
exprBuilder(param, value) {
|
||||||
return {[param]: value};
|
return {[param]: value};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds pattern key:value or key:(extra value) and passes it to object.
|
|
||||||
*
|
|
||||||
* @param {String} searchString The search string
|
|
||||||
* @return {Object} The parsed object
|
|
||||||
*/
|
|
||||||
getObjectFromString(searchString) {
|
|
||||||
let result = {};
|
|
||||||
if (searchString) {
|
|
||||||
let regex = /((([\w_]+):([\w_]+))|([\w_]+):\(([\w_ ]+)\))/gi;
|
|
||||||
let findPattern = searchString.match(regex);
|
|
||||||
let remnantString = searchString.replace(regex, '').trim();
|
|
||||||
if (findPattern) {
|
|
||||||
for (let i = 0; i < findPattern.length; i++) {
|
|
||||||
let aux = findPattern[i].split(':');
|
|
||||||
let property = aux[0];
|
|
||||||
let value = aux[1].replace(/\(|\)/g, '');
|
|
||||||
result[property] = value.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remnantString)
|
|
||||||
result.search = remnantString;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Passes an object to pattern key:value or key:(extra value).
|
|
||||||
*
|
|
||||||
* @param {Object} searchObject The search object
|
|
||||||
* @return {String} The passed string
|
|
||||||
*/
|
|
||||||
getStringFromObject(searchObject) {
|
|
||||||
let search = [];
|
|
||||||
|
|
||||||
if (searchObject) {
|
|
||||||
let keys = Object.keys(searchObject);
|
|
||||||
keys.forEach(key => {
|
|
||||||
if (key == 'search') return;
|
|
||||||
|
|
||||||
let value = searchObject[key];
|
|
||||||
let valueString;
|
|
||||||
|
|
||||||
if (typeof value === 'string' && value.indexOf(' ') !== -1)
|
|
||||||
valueString = `(${value})`;
|
|
||||||
else if (value instanceof Date)
|
|
||||||
valueString = value.toJSON();
|
|
||||||
else {
|
|
||||||
switch (typeof value) {
|
|
||||||
case 'number':
|
|
||||||
case 'string':
|
|
||||||
case 'boolean':
|
|
||||||
valueString = `${value}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueString)
|
|
||||||
search.push(`${key}:${valueString}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (searchObject.search)
|
|
||||||
search.unshift(searchObject.search);
|
|
||||||
}
|
|
||||||
|
|
||||||
return search.length ? search.join(' ') : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$onDestroy() {
|
|
||||||
this.deregisterCallback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controller.$inject = ['$element', '$scope', '$compile', '$state', '$transitions'];
|
AutoSearch.$inject = ['$state', '$transitions'];
|
||||||
|
|
||||||
ngModule.component('vnSearchbar', {
|
ngModule.vnComponent('vnAutoSearch', {
|
||||||
template: require('./searchbar.html'),
|
controller: AutoSearch,
|
||||||
bindings: {
|
bindings: {
|
||||||
filter: '<?',
|
|
||||||
suggestedFilter: '<?',
|
|
||||||
onSearch: '&?',
|
|
||||||
panel: '@',
|
|
||||||
model: '<?',
|
model: '<?',
|
||||||
|
onSearch: '&?',
|
||||||
exprBuilder: '&?',
|
exprBuilder: '&?',
|
||||||
paramBuilder: '&?',
|
paramBuilder: '&?'
|
||||||
autoLoad: '<?',
|
}
|
||||||
info: '@?'
|
|
||||||
},
|
|
||||||
controller: Controller
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -251,7 +289,7 @@ function compact(obj) {
|
||||||
} else if (typeof obj == 'object' && obj.constructor == Object) {
|
} else if (typeof obj == 'object' && obj.constructor == Object) {
|
||||||
let keys = Object.keys(obj);
|
let keys = Object.keys(obj);
|
||||||
for (let key of keys) {
|
for (let key of keys) {
|
||||||
if (key.substr(0, 2) == '$$' || compact(obj[key]) === undefined)
|
if (key.charAt(0) == '$' || compact(obj[key]) === undefined)
|
||||||
delete obj[key];
|
delete obj[key];
|
||||||
}
|
}
|
||||||
if (Object.keys(obj).length == 0)
|
if (Object.keys(obj).length == 0)
|
||||||
|
|
|
@ -2,7 +2,40 @@
|
||||||
|
|
||||||
vn-searchbar {
|
vn-searchbar {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
max-width: 30em;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.search-params {
|
||||||
|
flex: 1;
|
||||||
|
margin: .05em 0;
|
||||||
|
overflow: visible;
|
||||||
|
display: flex;
|
||||||
|
max-width: 16em;
|
||||||
|
|
||||||
|
& > .search-param {
|
||||||
|
color: rgba(0, 0, 0, .6);
|
||||||
|
background-color: rgba(0, 0, 0, .1);
|
||||||
|
padding: .1em .4em;
|
||||||
|
margin-left: .2em;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: .8em;
|
||||||
|
max-width: 18em;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
& > vn-icon {
|
||||||
|
font-size: inherit;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(0, 0, 0, .8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-panel {
|
.search-panel {
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import './portal';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
export class Slot {
|
||||||
|
constructor($element, vnSlotService) {
|
||||||
|
this.$element = $element;
|
||||||
|
this.vnSlotService = vnSlotService;
|
||||||
|
this.$content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
this.unregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
set name(value) {
|
||||||
|
this.unregister();
|
||||||
|
this._name = value;
|
||||||
|
this.vnSlotService.slots[value] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
unregister() {
|
||||||
|
if (this.name)
|
||||||
|
this.vnSlotService.slots[this.name] = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContent($content) {
|
||||||
|
if (this.$content) {
|
||||||
|
this.$content.detach();
|
||||||
|
this.$content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$content = $content;
|
||||||
|
if (this.$content) this.$element.append($content);
|
||||||
|
this.$element[0].style.display = $content ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Slot.$inject = ['$element', 'vnSlotService'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnSlot', {
|
||||||
|
controller: Slot,
|
||||||
|
bindings: {
|
||||||
|
name: '@?'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export class SlotService {
|
||||||
|
constructor() {
|
||||||
|
this.stacks = {};
|
||||||
|
this.slots = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
push(slot, $transclude) {
|
||||||
|
if (!this.stacks[slot]) this.stacks[slot] = [];
|
||||||
|
this.stacks[slot].unshift($transclude);
|
||||||
|
this.refreshContent(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
pop(slot) {
|
||||||
|
let $content = this.stacks[slot].shift();
|
||||||
|
this.refreshContent(slot);
|
||||||
|
if ($content && typeof $content == 'object')
|
||||||
|
$content.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshContent(slot) {
|
||||||
|
if (!this.slots[slot]) return;
|
||||||
|
let $content = this.stacks[slot][0];
|
||||||
|
if (typeof $content == 'function') {
|
||||||
|
$content(clone => {
|
||||||
|
$content = this.stacks[slot][0] = clone;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.slots[slot].setContent($content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ngModule.service('vnSlotService', SlotService);
|
|
@ -0,0 +1,28 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used to fill slots with content.
|
||||||
|
*/
|
||||||
|
export default class Portal {
|
||||||
|
constructor($transclude, vnSlotService) {
|
||||||
|
this.$transclude = $transclude;
|
||||||
|
this.vnSlotService = vnSlotService;
|
||||||
|
}
|
||||||
|
|
||||||
|
$postLink() {
|
||||||
|
this.vnSlotService.push(this.slot, this.$transclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
this.vnSlotService.pop(this.slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Portal.$inject = ['$transclude', 'vnSlotService'];
|
||||||
|
|
||||||
|
ngModule.component('vnPortal', {
|
||||||
|
controller: Portal,
|
||||||
|
transclude: true,
|
||||||
|
bindings: {
|
||||||
|
slot: '@'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
vn-slot {
|
||||||
|
display: block;
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
cy="50"
|
cy="50"
|
||||||
r="20"
|
r="20"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
stroke-miterlimit="10">
|
stroke-miterlimit="10">
|
||||||
</circle>
|
</circle>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -4,6 +4,7 @@ vn-spinner {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
|
color: $color-main;
|
||||||
|
|
||||||
& > .loader {
|
& > .loader {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -29,7 +30,6 @@ vn-spinner {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
& > .path {
|
& > .path {
|
||||||
stroke: $color-main;
|
|
||||||
stroke-dasharray: 1, 200;
|
stroke-dasharray: 1, 200;
|
||||||
stroke-dashoffset: 0;
|
stroke-dashoffset: 0;
|
||||||
stroke-linecap: square;
|
stroke-linecap: square;
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
vn-table {
|
vn-table {
|
||||||
display: block;
|
display: block;
|
||||||
overflow: auto;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
// overflow: auto;
|
||||||
}
|
}
|
||||||
.vn-table {
|
.vn-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -40,10 +40,10 @@
|
||||||
background-color: rgba(0, 0, 0, .1);
|
background-color: rgba(0, 0, 0, .1);
|
||||||
}
|
}
|
||||||
&.checked > .btn {
|
&.checked > .btn {
|
||||||
border-color: $color-main;
|
border-color: $color-button;
|
||||||
|
|
||||||
& > .focus-mark {
|
& > .focus-mark {
|
||||||
background-color: rgba($color-main, .15);
|
background-color: rgba($color-button, .15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.disabled {
|
&.disabled {
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
background-color: rgba(0, 0, 0, .05);
|
background-color: rgba(0, 0, 0, .05);
|
||||||
|
|
||||||
&.marked {
|
&.marked {
|
||||||
background: $color-main;
|
background: $color-button;
|
||||||
color: $color-font-dark;
|
color: $color-font-dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,23 @@ export function directive() {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function($scope, $element, $attrs) {
|
link: function($scope, $element, $attrs) {
|
||||||
let id = kebabToCamel($attrs.vnId);
|
let id = kebabToCamel($attrs.vnId);
|
||||||
let $ctrl = $element[0].$ctrl
|
|
||||||
? $element[0].$ctrl
|
|
||||||
: $element.controller($element[0].tagName.toLowerCase());
|
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
throw new Error(`vnId: Attribute can't be null`);
|
throw new Error(`vnId: Attribute can't be null`);
|
||||||
|
|
||||||
$scope[id] = $ctrl || $element[0];
|
let $ctrl = $element[0].$ctrl
|
||||||
|
? $element[0].$ctrl
|
||||||
|
: $element.controller($element[0].tagName.toLowerCase());
|
||||||
|
let ctrl = $ctrl || $element[0];
|
||||||
|
|
||||||
|
$scope[id] = ctrl;
|
||||||
|
|
||||||
|
if (!$scope.hasOwnProperty('$ctrl')) {
|
||||||
|
while ($scope && !$scope.hasOwnProperty('$ctrl'))
|
||||||
|
$scope = Object.getPrototypeOf($scope);
|
||||||
|
|
||||||
|
if ($scope) $scope[id] = ctrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ function runFn(
|
||||||
$compile,
|
$compile,
|
||||||
$filter,
|
$filter,
|
||||||
$interpolate,
|
$interpolate,
|
||||||
|
$window,
|
||||||
vnApp) {
|
vnApp) {
|
||||||
Object.assign(Component.prototype, {
|
Object.assign(Component.prototype, {
|
||||||
$translate,
|
$translate,
|
||||||
|
@ -119,6 +120,7 @@ function runFn(
|
||||||
$compile,
|
$compile,
|
||||||
$filter,
|
$filter,
|
||||||
$interpolate,
|
$interpolate,
|
||||||
|
$window,
|
||||||
vnApp
|
vnApp
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -133,6 +135,7 @@ runFn.$inject = [
|
||||||
'$compile',
|
'$compile',
|
||||||
'$filter',
|
'$filter',
|
||||||
'$interpolate',
|
'$interpolate',
|
||||||
|
'$window',
|
||||||
'vnApp'
|
'vnApp'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
import isMobile from './is-mobile';
|
||||||
|
|
||||||
|
export default function focus(element) {
|
||||||
|
if (isMobile) return;
|
||||||
|
setTimeout(() => element.focus(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function select(element) {
|
||||||
|
if (isMobile) return;
|
||||||
|
setTimeout(() => {
|
||||||
|
element.focus();
|
||||||
|
if (element.select)
|
||||||
|
element.select();
|
||||||
|
}, 10);
|
||||||
|
}
|
|
@ -2,8 +2,9 @@ import './module-loader';
|
||||||
import './crud';
|
import './crud';
|
||||||
import './copy';
|
import './copy';
|
||||||
import './equals';
|
import './equals';
|
||||||
|
import './focus';
|
||||||
|
import './get-main-route';
|
||||||
import './modified';
|
import './modified';
|
||||||
import './key-codes';
|
import './key-codes';
|
||||||
import './http-error';
|
import './http-error';
|
||||||
import './user-error';
|
import './user-error';
|
||||||
import './get-main-route';
|
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import {ng, ngDeps} from './vendor';
|
import {ng, ngDeps} from './vendor';
|
||||||
import {camelToKebab} from './lib/string';
|
import {camelToKebab} from './lib/string';
|
||||||
|
|
||||||
const ngModule = ng.module('vnCore', ngDeps);
|
|
||||||
export default ngModule;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acts like native Module.component() function but merging component options
|
* Acts like native Module.component() function but merging component options
|
||||||
* with parent component options. This method establishes the $options property
|
* with parent component options. This method establishes the $options property
|
||||||
|
@ -17,7 +14,7 @@ export default ngModule;
|
||||||
* @param {Object} options The component options
|
* @param {Object} options The component options
|
||||||
* @return {angularModule} The same angular module
|
* @return {angularModule} The same angular module
|
||||||
*/
|
*/
|
||||||
ngModule.vnComponent = function(name, options) {
|
function vnComponent(name, options) {
|
||||||
let controller = options.controller;
|
let controller = options.controller;
|
||||||
let parent = Object.getPrototypeOf(controller);
|
let parent = Object.getPrototypeOf(controller);
|
||||||
let parentOptions = parent.$options || {};
|
let parentOptions = parent.$options || {};
|
||||||
|
@ -57,10 +54,21 @@ ngModule.vnComponent = function(name, options) {
|
||||||
controller.$classNames = classNames;
|
controller.$classNames = classNames;
|
||||||
|
|
||||||
return this.component(name, mergedOptions);
|
return this.component(name, mergedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ngModuleFn = ng.module;
|
||||||
|
|
||||||
|
ng.module = function(...args) {
|
||||||
|
let ngModule = ngModuleFn.apply(this, args);
|
||||||
|
ngModule.vnComponent = vnComponent;
|
||||||
|
return ngModule;
|
||||||
};
|
};
|
||||||
|
|
||||||
config.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];
|
const ngModule = ng.module('vnCore', ngDeps);
|
||||||
export function config($translateProvider, $translatePartialLoaderProvider) {
|
export default ngModule;
|
||||||
|
|
||||||
|
config.$inject = ['$translateProvider', '$translatePartialLoaderProvider', '$animateProvider'];
|
||||||
|
export function config($translateProvider, $translatePartialLoaderProvider, $animateProvider) {
|
||||||
// For CSS browser targeting
|
// For CSS browser targeting
|
||||||
document.documentElement.setAttribute('data-browser', navigator.userAgent);
|
document.documentElement.setAttribute('data-browser', navigator.userAgent);
|
||||||
|
|
||||||
|
@ -91,5 +99,8 @@ export function config($translateProvider, $translatePartialLoaderProvider) {
|
||||||
return langAliases[locale];
|
return langAliases[locale];
|
||||||
return fallbackLang;
|
return fallbackLang;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$animateProvider.customFilter(
|
||||||
|
node => node.tagName == 'UI-VIEW');
|
||||||
}
|
}
|
||||||
ngModule.config(config);
|
ngModule.config(config);
|
||||||
|
|
|
@ -13,11 +13,15 @@ function interceptor($q, vnApp, vnToken, $translate) {
|
||||||
vnApp.pushLoader();
|
vnApp.pushLoader();
|
||||||
|
|
||||||
if (config.url.charAt(0) !== '/' && apiPath)
|
if (config.url.charAt(0) !== '/' && apiPath)
|
||||||
config.url = `${apiPath}/${config.url}`;
|
config.url = `${apiPath}${config.url}`;
|
||||||
if (vnToken.token)
|
if (vnToken.token)
|
||||||
config.headers.Authorization = vnToken.token;
|
config.headers.Authorization = vnToken.token;
|
||||||
if ($translate.use())
|
if ($translate.use())
|
||||||
config.headers['Accept-Language'] = $translate.use();
|
config.headers['Accept-Language'] = $translate.use();
|
||||||
|
if (config.filter) {
|
||||||
|
if (!config.params) config.params = {};
|
||||||
|
config.params.filter = config.filter;
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
|
|
@ -40,4 +40,8 @@ button {
|
||||||
a {
|
a {
|
||||||
color: $color-font-link;
|
color: $color-font-link;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.ng-leave,
|
||||||
|
.ng-enter {
|
||||||
|
transition: none;
|
||||||
}
|
}
|
|
@ -19,27 +19,36 @@ $spacing-xs: 4px;
|
||||||
$spacing-sm: 8px;
|
$spacing-sm: 8px;
|
||||||
$spacing-md: 16px;
|
$spacing-md: 16px;
|
||||||
$spacing-lg: 32px;
|
$spacing-lg: 32px;
|
||||||
$spacing-xl: 100px;
|
$spacing-xl: 70px;
|
||||||
|
|
||||||
// Light theme
|
// Light theme
|
||||||
|
|
||||||
$color-header: #3d3d3d;
|
$color-primary: #f7931e;
|
||||||
$color-bg: #e5e5e5;
|
$color-secondary: $color-primary;
|
||||||
$color-bg-dark: #3d3d3d;
|
|
||||||
$color-font: #222;
|
$color-font: #222;
|
||||||
$color-font-light: #555;
|
$color-font-light: #555;
|
||||||
$color-font-secondary: #9b9b9b;
|
$color-font-secondary: #9b9b9b;
|
||||||
$color-font-dark: white;
|
$color-font-dark: white;
|
||||||
$color-font-bg: rgba(0, 0, 0, .7);
|
|
||||||
$color-font-link: #005a9a;
|
$color-font-link: #005a9a;
|
||||||
$color-active: #3d3d3d;
|
$color-font-bg: rgba(0, 0, 0, .7);
|
||||||
$color-active-font: white;
|
$color-font-bg-marginal: rgba(0, 0, 0, .4);
|
||||||
|
$color-font-bg-dark: rgba(255, 255, 255, .7);
|
||||||
|
$color-font-bg-dark-marginal: rgba(255, 255, 255, .4);
|
||||||
|
|
||||||
|
$color-header: darken($color-primary, 5%);
|
||||||
|
$color-menu-header: #3d3d3d;
|
||||||
|
$color-bg: #e5e5e5;
|
||||||
|
$color-bg-dark: #3d3d3d;
|
||||||
|
$color-active: $color-primary;
|
||||||
|
$color-active-font: $color-font-dark;
|
||||||
$color-bg-panel: white;
|
$color-bg-panel: white;
|
||||||
$color-main: #f7931e;
|
$color-main: $color-primary;
|
||||||
$color-secondary: #ccc;
|
$color-marginal: #ccc;
|
||||||
$color-success: #a3d131;
|
$color-success: #a3d131;
|
||||||
$color-notice: #32b1ce;
|
$color-notice: #32b1ce;
|
||||||
$color-alert: #f42121;
|
$color-alert: #f42121;
|
||||||
|
$color-button: $color-secondary;
|
||||||
|
|
||||||
$color-spacer: rgba(0, 0, 0, .3);
|
$color-spacer: rgba(0, 0, 0, .3);
|
||||||
$color-spacer-light: rgba(0, 0, 0, .12);
|
$color-spacer-light: rgba(0, 0, 0, .12);
|
||||||
|
@ -78,7 +87,7 @@ $color-active: #666;
|
||||||
$color-active-font: white;
|
$color-active-font: white;
|
||||||
$color-bg-panel: #3c3b3b;
|
$color-bg-panel: #3c3b3b;
|
||||||
$color-main: #f7931e;
|
$color-main: #f7931e;
|
||||||
$color-secondary: #ccc;
|
$color-marginal: #ccc;
|
||||||
$color-success: #a3d131;
|
$color-success: #a3d131;
|
||||||
$color-notice: #32b1ce;
|
$color-notice: #32b1ce;
|
||||||
$color-alert: #f42121;
|
$color-alert: #f42121;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import '@babel/polyfill';
|
||||||
import * as ng from 'angular';
|
import * as ng from 'angular';
|
||||||
export {ng};
|
export {ng};
|
||||||
|
|
||||||
|
import 'angular-animate';
|
||||||
import 'angular-translate';
|
import 'angular-translate';
|
||||||
import 'angular-translate-loader-partial';
|
import 'angular-translate-loader-partial';
|
||||||
import '@uirouter/angularjs';
|
import '@uirouter/angularjs';
|
||||||
|
@ -9,6 +10,7 @@ import 'mg-crud';
|
||||||
import 'oclazyload';
|
import 'oclazyload';
|
||||||
|
|
||||||
export const ngDeps = [
|
export const ngDeps = [
|
||||||
|
'ngAnimate',
|
||||||
'pascalprecht.translate',
|
'pascalprecht.translate',
|
||||||
'ui.router',
|
'ui.router',
|
||||||
'mgCrud',
|
'mgCrud',
|
||||||
|
|
|
@ -31,6 +31,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz",
|
||||||
"integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw=="
|
"integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw=="
|
||||||
},
|
},
|
||||||
|
"angular-animate": {
|
||||||
|
"version": "1.7.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.8.tgz",
|
||||||
|
"integrity": "sha512-bINtzizq7TbJzfVrDpwLfTxVl0Qd7fRNWFb5jKYI1vaFZobQNX/QONXlLow6ySsDbZ6eLECycB7mvWtfh1YYaw=="
|
||||||
|
},
|
||||||
"angular-translate": {
|
"angular-translate": {
|
||||||
"version": "2.18.1",
|
"version": "2.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz",
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"@babel/polyfill": "^7.2.5",
|
"@babel/polyfill": "^7.2.5",
|
||||||
"@uirouter/angularjs": "^1.0.20",
|
"@uirouter/angularjs": "^1.0.20",
|
||||||
"angular": "^1.7.5",
|
"angular": "^1.7.5",
|
||||||
|
"angular-animate": "^1.7.8",
|
||||||
"angular-translate": "^2.18.1",
|
"angular-translate": "^2.18.1",
|
||||||
"angular-translate-loader-partial": "^2.18.1",
|
"angular-translate-loader-partial": "^2.18.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
|
|
|
@ -1,29 +1,4 @@
|
||||||
<vn-topbar ng-if="$ctrl.showTopbar">
|
<vn-layout ng-if="$ctrl.showLayout"></vn-layout>
|
||||||
<a class="logo" ui-sref="home" title="{{'Home' | translate}}">
|
<ui-view name="login"></ui-view>
|
||||||
<img src="./logo.svg" alt="Logo"></img>
|
|
||||||
</a>
|
|
||||||
<vn-icon
|
|
||||||
icon="menu"
|
|
||||||
class="show-menu"
|
|
||||||
ng-if="$ctrl.leftMenu"
|
|
||||||
ng-click="$ctrl.leftMenu.show()">
|
|
||||||
</vn-icon>
|
|
||||||
<div class="main-title" translate>
|
|
||||||
{{$ctrl.$state.current.description}}
|
|
||||||
</div>
|
|
||||||
<vn-spinner enable="$ctrl.vnApp.loading"></vn-spinner>
|
|
||||||
<vn-main-menu></vn-main-menu>
|
|
||||||
<vn-icon
|
|
||||||
icon="menu"
|
|
||||||
class="show-menu"
|
|
||||||
ng-if="$ctrl.rightMenu"
|
|
||||||
ng-click="$ctrl.rightMenu.show()">
|
|
||||||
</vn-icon>
|
|
||||||
</vn-topbar>
|
|
||||||
<div ui-view
|
|
||||||
class="main-view"
|
|
||||||
ng-class="{padding: $ctrl.showTopbar}">
|
|
||||||
<vn-home></vn-home>
|
|
||||||
</div>
|
|
||||||
<vn-snackbar vn-id="snackbar"></vn-snackbar>
|
<vn-snackbar vn-id="snackbar"></vn-snackbar>
|
||||||
<vn-debug-info></vn-debug-info>
|
<vn-debug-info></vn-debug-info>
|
|
@ -1,5 +1,6 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main graphical application component.
|
* The main graphical application component.
|
||||||
|
@ -7,28 +8,21 @@ import './style.scss';
|
||||||
* @property {SideMenu} leftMenu The left menu, if it's present
|
* @property {SideMenu} leftMenu The left menu, if it's present
|
||||||
* @property {SideMenu} rightMenu The left menu, if it's present
|
* @property {SideMenu} rightMenu The left menu, if it's present
|
||||||
*/
|
*/
|
||||||
export default class App {
|
export default class App extends Component {
|
||||||
constructor($, $state, vnApp) {
|
|
||||||
Object.assign(this, {
|
|
||||||
$,
|
|
||||||
$state,
|
|
||||||
vnApp
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$postLink() {
|
$postLink() {
|
||||||
this.vnApp.logger = this;
|
this.vnApp.logger = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
$onDestroy() {
|
get showLayout() {
|
||||||
this.vnApp.logger = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get showTopbar() {
|
|
||||||
let state = this.$state.current.name;
|
let state = this.$state.current.name;
|
||||||
return state && state != 'login';
|
return state && state != 'login';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
this.deregisterCallback();
|
||||||
|
this.vnApp.logger = null;
|
||||||
|
}
|
||||||
|
|
||||||
showMessage(message) {
|
showMessage(message) {
|
||||||
this.$.snackbar.show({message: message});
|
this.$.snackbar.show({message: message});
|
||||||
}
|
}
|
||||||
|
@ -41,9 +35,8 @@ export default class App {
|
||||||
this.$.snackbar.showError({message: message});
|
this.$.snackbar.showError({message: message});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
App.$inject = ['$scope', '$state', 'vnApp'];
|
|
||||||
|
|
||||||
ngModule.component('vnApp', {
|
ngModule.vnComponent('vnApp', {
|
||||||
template: require('./app.html'),
|
template: require('./app.html'),
|
||||||
controller: App
|
controller: App
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,99 +1,21 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-app {
|
vn-app {
|
||||||
height: inherit;
|
|
||||||
display: block;
|
display: block;
|
||||||
|
height: inherit;
|
||||||
|
|
||||||
& > vn-topbar {
|
ui-view {
|
||||||
position: fixed;
|
display: block;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
box-shadow: 0 .1em .2em $color-shadow;
|
|
||||||
height: $topbar-height;
|
|
||||||
padding: .4em;
|
|
||||||
|
|
||||||
& > header {
|
|
||||||
& > * {
|
|
||||||
padding: .3em;
|
|
||||||
}
|
|
||||||
& > .logo > img {
|
|
||||||
height: 1.4em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
& > .show-menu {
|
|
||||||
display: none;
|
|
||||||
font-size: 1.8em;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $color-main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& > .main-title {
|
|
||||||
font-size: 1.6em;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
padding-left: .6em;
|
|
||||||
}
|
|
||||||
& > vn-spinner {
|
|
||||||
padding: 0 .4em;
|
|
||||||
}
|
|
||||||
& > vn-main-menu {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& > .main-view {
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: inherit;
|
|
||||||
|
|
||||||
&.padding {
|
|
||||||
padding-top: $topbar-height;
|
|
||||||
}
|
|
||||||
.content-block {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: $spacing-md;
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
vn-main-block {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-left: $menu-width;
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
}
|
|
||||||
.main-with-right-menu {
|
|
||||||
padding-right: $menu-width;
|
|
||||||
|
|
||||||
@media screen and (max-width: 800px) {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-width) {
|
&.ng-enter {
|
||||||
& > vn-topbar > header {
|
transition: all 200ms ease-out;
|
||||||
& > .logo {
|
transform: translate3d(-2em, 0, 0);
|
||||||
display: none;
|
opacity: 0;
|
||||||
}
|
|
||||||
& > .show-menu {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
& > .main-view {
|
&.ng-enter.ng-enter-active {
|
||||||
.content-block {
|
transform: translate3d(0, 0, 0);
|
||||||
margin-left: 0;
|
opacity: 1;
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
vn-main-block {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
.main-with-right-menu {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
|
|
||||||
& > vn-icon {
|
& > vn-icon {
|
||||||
padding: $spacing-sm;
|
padding: $spacing-sm;
|
||||||
color: $color-secondary;
|
color: $color-marginal;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
|
|
||||||
&.bright {
|
&.bright {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div ng-if="$ctrl.$state.current.name === 'home'">
|
<div>
|
||||||
<div class="modules">
|
<div class="modules">
|
||||||
<a
|
<a
|
||||||
ng-repeat="mod in ::$ctrl.modules"
|
ng-repeat="mod in ::$ctrl.modules"
|
||||||
|
|
|
@ -23,7 +23,7 @@ vn-home {
|
||||||
@extend %clickable-light;
|
@extend %clickable-light;
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: $color-main;
|
background-color: $color-button;
|
||||||
color: $color-font-dark;
|
color: $color-font-dark;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -2,10 +2,13 @@ import './app/app';
|
||||||
import './background/background';
|
import './background/background';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
import './home/home';
|
import './home/home';
|
||||||
|
import './layout';
|
||||||
import './left-menu/left-menu';
|
import './left-menu/left-menu';
|
||||||
import './login/login';
|
import './login/login';
|
||||||
import './main-menu/main-menu';
|
import './main-menu/main-menu';
|
||||||
import './topbar/topbar';
|
import './module-card';
|
||||||
|
import './module-main';
|
||||||
import './side-menu/side-menu';
|
import './side-menu/side-menu';
|
||||||
import './summary';
|
import './summary';
|
||||||
|
import './topbar/topbar';
|
||||||
import './user-popover';
|
import './user-popover';
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<vn-topbar>
|
||||||
|
<vn-icon-button
|
||||||
|
icon="menu"
|
||||||
|
class="show-menu"
|
||||||
|
ng-if="$ctrl.leftMenu"
|
||||||
|
ng-click="$ctrl.leftMenu.show()">
|
||||||
|
</vn-icon-button>
|
||||||
|
<div class="start">
|
||||||
|
<div class="main-title" translate>
|
||||||
|
{{$ctrl.$state.current.description}}
|
||||||
|
</div>
|
||||||
|
<vn-spinner
|
||||||
|
ng-if="$ctrl.vnApp.loading"
|
||||||
|
enable="true">
|
||||||
|
</vn-spinner>
|
||||||
|
</div>
|
||||||
|
<vn-slot name="topbar"></vn-slot>
|
||||||
|
<div class="end">
|
||||||
|
<vn-icon-button
|
||||||
|
id="apps"
|
||||||
|
icon="apps"
|
||||||
|
vn-popover="apps-menu"
|
||||||
|
translate-attr="{title: 'Applications'}">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-icon-button
|
||||||
|
icon="menu"
|
||||||
|
class="show-menu"
|
||||||
|
ng-if="$ctrl.rightMenu"
|
||||||
|
ng-click="$ctrl.rightMenu.show()">
|
||||||
|
</vn-icon-button>
|
||||||
|
</div>
|
||||||
|
<vn-menu vn-id="apps-menu">
|
||||||
|
<ul class="modules-menu vn-pa-sm">
|
||||||
|
<li
|
||||||
|
ng-repeat="mod in ::$ctrl.modules"
|
||||||
|
ui-sref="{{::mod.route.state}}">
|
||||||
|
<vn-icon icon="{{::mod.icon || 'photo'}}"></vn-icon>
|
||||||
|
<span translate>{{::mod.name}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</vn-menu>
|
||||||
|
</vn-topbar>
|
||||||
|
<vn-side-menu side="left">
|
||||||
|
<div class="header">
|
||||||
|
<a class="logo" ui-sref="home" title="{{'Home' | translate}}">
|
||||||
|
<img src="./logo.svg" alt="Logo"></img>
|
||||||
|
</a>
|
||||||
|
<vn-main-menu
|
||||||
|
class="vn-pt-md">
|
||||||
|
</vn-main-menu>
|
||||||
|
</div>
|
||||||
|
<vn-slot name="menu"></vn-slot>
|
||||||
|
</vn-side-menu>
|
||||||
|
<vn-portal slot="menu">
|
||||||
|
<vn-list class="vn-py-md">
|
||||||
|
<a
|
||||||
|
ng-repeat="mod in ::$ctrl.modules"
|
||||||
|
class="vn-item"
|
||||||
|
ui-sref="{{::mod.route.state}}">
|
||||||
|
<vn-item-section avatar>
|
||||||
|
<vn-icon icon="{{::mod.icon || 'photo'}}"></vn-icon>
|
||||||
|
</vn-item-section>
|
||||||
|
<vn-item-section translate>
|
||||||
|
{{::mod.name}}
|
||||||
|
</vn-item-section>
|
||||||
|
</a>
|
||||||
|
</vn-list>
|
||||||
|
</vn-portal>
|
||||||
|
<ui-view class="main-view"></ui-view>
|
|
@ -0,0 +1,19 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
export class Layout extends Component {
|
||||||
|
constructor($element, $, vnModules) {
|
||||||
|
super($element, $);
|
||||||
|
this.modules = vnModules.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout.$inject = ['$element', '$scope', 'vnModules'];
|
||||||
|
|
||||||
|
ngModule.component('vnLayout', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Layout,
|
||||||
|
require: {
|
||||||
|
app: '^vnApp'
|
||||||
|
}
|
||||||
|
});
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,121 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-layout {
|
||||||
|
& > vn-topbar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 .1em .2em $color-shadow;
|
||||||
|
height: $topbar-height;
|
||||||
|
padding: 0 1em;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& > .start {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-right: 1em;
|
||||||
|
|
||||||
|
& > .main-title {
|
||||||
|
font-size: 1.6em;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: .4em;
|
||||||
|
}
|
||||||
|
& > vn-spinner {
|
||||||
|
padding: 0 .4em;
|
||||||
|
color: $color-font-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > vn-slot {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
& > .end {
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 1em;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.vn-button {
|
||||||
|
color: inherit;
|
||||||
|
font-size: 1.05em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.show-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > vn-side-menu > .menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
& > .header {
|
||||||
|
background-color: $color-menu-header;
|
||||||
|
padding: $spacing-md;
|
||||||
|
color: $color-font-dark;
|
||||||
|
|
||||||
|
& > .logo > img {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > vn-slot {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.left-menu {
|
||||||
|
& > vn-topbar {
|
||||||
|
left: $menu-width;
|
||||||
|
}
|
||||||
|
& > .main-view {
|
||||||
|
padding-left: $menu-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.right-menu {
|
||||||
|
& > .main-view {
|
||||||
|
padding-right: $menu-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .main-view {
|
||||||
|
padding-top: $topbar-height;
|
||||||
|
}
|
||||||
|
ui-view {
|
||||||
|
height: inherit;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: block;
|
||||||
|
padding: $spacing-md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: $mobile-width) {
|
||||||
|
& > vn-topbar {
|
||||||
|
& > .show-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.left-menu {
|
||||||
|
& > vn-topbar {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
& > .main-view {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.right-menu {
|
||||||
|
& > .main-view {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui-view > * {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,30 @@
|
||||||
<ul ng-if="::$ctrl.items.length > 0" class="vn-py-md">
|
<ul class="vn-list" ng-if="::$ctrl.items.length > 0">
|
||||||
<li ng-repeat="item in ::$ctrl.items" name="{{::item.description}}">
|
<li ng-repeat="item in ::$ctrl.items" name="{{::item.description}}">
|
||||||
<a ui-sref="{{::item.state}}"
|
<a ui-sref="{{::item.state}}"
|
||||||
ng-class="{active: item.active && !item.childs, expanded: item.active, collapsed: !item.active}"
|
class="vn-item"
|
||||||
|
ng-class="{active: item.active && !item.childs, expanded: item.active}"
|
||||||
ng-click="$ctrl.setActive(item)">
|
ng-click="$ctrl.setActive(item)">
|
||||||
<vn-icon icon="{{::item.icon}}" ng-if="::item.icon"></vn-icon>
|
<vn-item-section avatar>
|
||||||
<vn-icon icon="keyboard_arrow_down" ng-if="::item.childs.length > 0"></vn-icon>
|
<vn-icon icon="{{::item.icon}}" ng-if="::item.icon"></vn-icon>
|
||||||
<span translate>{{::item.description}}</span>
|
</vn-item-section>
|
||||||
|
<vn-item-section translate>
|
||||||
|
{{::item.description}}
|
||||||
|
</vn-item-section>
|
||||||
|
<vn-item-section side>
|
||||||
|
<vn-icon icon="keyboard_arrow_down" ng-if="::item.childs.length > 0"></vn-icon>
|
||||||
|
</vn-item-section>
|
||||||
</a>
|
</a>
|
||||||
<ul ng-show="item.childs.length > 0 && item.active">
|
<ul class="vn-list" ng-show="item.childs.length > 0 && item.active">
|
||||||
<li ng-repeat="child in ::item.childs">
|
<li ng-repeat="child in ::item.childs">
|
||||||
<a ui-sref="{{::child.state}}" ng-class="{active: child.active}">
|
<a ui-sref="{{::child.state}}"
|
||||||
<vn-icon icon="{{::child.icon}}"></vn-icon>
|
class="vn-item"
|
||||||
<span translate>{{::child.description}}</span>
|
ng-class="{active: child.active}">
|
||||||
|
<vn-item-section avatar>
|
||||||
|
<vn-icon icon="{{::child.icon}}"></vn-icon>
|
||||||
|
</vn-item-section>
|
||||||
|
<vn-item-section translate>
|
||||||
|
{{::child.description}}
|
||||||
|
</vn-item-section>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -6,24 +6,42 @@ export default class LeftMenu {
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
|
this.aclService = aclService;
|
||||||
this.deregisterCallback = $transitions.onSuccess({},
|
this.deregisterCallback = $transitions.onSuccess({},
|
||||||
() => this.activateItem());
|
() => this.activateItem());
|
||||||
|
this.source = 'main';
|
||||||
this._depth = 3;
|
this._depth = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.items = this.fetchItems();
|
||||||
|
this.activateItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
set depth(value) {
|
||||||
|
this._depth = value;
|
||||||
|
this.activateItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
get depth() {
|
||||||
|
return this._depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchItems() {
|
||||||
let states = this.$state.router.stateRegistry.states;
|
let states = this.$state.router.stateRegistry.states;
|
||||||
let moduleIndex = this.$state.current.data.moduleIndex;
|
let moduleIndex = this.$state.current.data.moduleIndex;
|
||||||
let moduleFile = window.routes[moduleIndex] || [];
|
let moduleFile = window.routes[moduleIndex] || [];
|
||||||
let menu = moduleFile.menu || [];
|
let menu = moduleFile.menus && moduleFile.menus[this.source] || [];
|
||||||
let items = [];
|
let items = [];
|
||||||
|
|
||||||
function addItem(items, item) {
|
let addItem = (items, item) => {
|
||||||
let state = states[item.state];
|
let state = states[item.state];
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
|
|
||||||
state = state.self;
|
state = state.self;
|
||||||
let acl = state.data.acl;
|
let acl = state.data.acl;
|
||||||
|
|
||||||
if (acl && !aclService.hasAny(acl))
|
if (acl && !this.aclService.hasAny(acl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
|
@ -31,7 +49,7 @@ export default class LeftMenu {
|
||||||
description: state.description,
|
description: state.description,
|
||||||
state: item.state
|
state: item.state
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
for (let item of menu) {
|
for (let item of menu) {
|
||||||
if (item.state)
|
if (item.state)
|
||||||
|
@ -52,17 +70,7 @@ export default class LeftMenu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.items = items;
|
return items;
|
||||||
this.activateItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
set depth(value) {
|
|
||||||
this._depth = value;
|
|
||||||
this.activateItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
get depth() {
|
|
||||||
return this._depth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activateItem() {
|
activateItem() {
|
||||||
|
@ -93,9 +101,8 @@ export default class LeftMenu {
|
||||||
item.active = !item.active;
|
item.active = !item.active;
|
||||||
|
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
let element = this.$element[0].querySelector('a[class="expanded"]');
|
let element = this.$element[0].querySelector('a.expanded');
|
||||||
if (element)
|
if (element) element.scrollIntoView();
|
||||||
element.scrollIntoView();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +116,7 @@ ngModule.component('vnLeftMenu', {
|
||||||
template: require('./left-menu.html'),
|
template: require('./left-menu.html'),
|
||||||
controller: LeftMenu,
|
controller: LeftMenu,
|
||||||
bindings: {
|
bindings: {
|
||||||
|
source: '@?',
|
||||||
depth: '<?'
|
depth: '<?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,53 +1,18 @@
|
||||||
@import "effects";
|
@import "effects";
|
||||||
|
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
vn-left-menu {
|
vn-left-menu {
|
||||||
ul {
|
& > .vn-list {
|
||||||
margin: 0;
|
padding: $spacing-md 0;
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
& > li {
|
& > li > .vn-item {
|
||||||
list-style: none;
|
& > [side] > vn-icon[icon="keyboard_arrow_down"] {
|
||||||
display: block;
|
transition: transform 200ms;
|
||||||
|
|
||||||
& > ul > li > a {
|
|
||||||
padding-left: 2em
|
|
||||||
}
|
}
|
||||||
}
|
&.expanded > [side] > vn-icon[icon="keyboard_arrow_down"] {
|
||||||
|
|
||||||
& > li > a {
|
|
||||||
@extend %clickable;
|
|
||||||
padding: .5em 1em;
|
|
||||||
display: block;
|
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
& > vn-icon:nth-child(1) {
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-right: .4em
|
|
||||||
}
|
|
||||||
|
|
||||||
& > vn-icon:nth-child(2) {
|
|
||||||
float: right;
|
|
||||||
margin-left: .4em
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > li > a.active {
|
|
||||||
@extend %active;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > li > a.expanded {
|
|
||||||
& > vn-icon[icon="keyboard_arrow_down"] {
|
|
||||||
transition: all 0.2s;
|
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > li > a.collapsed {
|
|
||||||
& > vn-icon[icon="keyboard_arrow_down"] {
|
|
||||||
transition: all 0.2s;
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default class Controller {
|
||||||
remember: true
|
remember: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.vnAuth.login(this.user, this.password, this.remember)
|
this.vnAuth.login(this.user, this.password, this.remember)
|
||||||
|
@ -28,6 +29,7 @@ export default class Controller {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
focusUser() {
|
focusUser() {
|
||||||
this.$.userField.select();
|
this.$.userField.select();
|
||||||
this.$.userField.focus();
|
this.$.userField.focus();
|
||||||
|
|
|
@ -1,31 +1,15 @@
|
||||||
<div>
|
<div
|
||||||
<div
|
ng-click="userPopover.show($event)"
|
||||||
ng-click="userPopover.show($event)"
|
id="user"
|
||||||
id="user"
|
class="unselectable">
|
||||||
class="unselectable">
|
{{$root.user.nickname}}
|
||||||
{{$root.user.nickname}}
|
|
||||||
</div>
|
|
||||||
<vn-icon-button
|
|
||||||
id="apps"
|
|
||||||
icon="apps"
|
|
||||||
vn-popover="apps-menu"
|
|
||||||
translate-attr="{title: 'Applications'}">
|
|
||||||
</vn-icon-button>
|
|
||||||
<vn-icon-button
|
|
||||||
id="logout"
|
|
||||||
icon="exit_to_app"
|
|
||||||
translate-attr="{title: 'Logout'}"
|
|
||||||
ng-click="$ctrl.vnAuth.logout()">
|
|
||||||
</vn-icon-button>
|
|
||||||
</div>
|
</div>
|
||||||
<vn-menu vn-id="apps-menu">
|
<vn-icon-button
|
||||||
<ul class="modules-menu vn-pa-sm">
|
id="logout"
|
||||||
<li ng-repeat="mod in ::$ctrl.modules" ui-sref="{{::mod.route.state}}">
|
icon="exit_to_app"
|
||||||
<vn-icon icon="{{::mod.icon || 'photo'}}"></vn-icon>
|
translate-attr="{title: 'Logout'}"
|
||||||
<span translate>{{::mod.name}}</span>
|
ng-click="$ctrl.vnAuth.logout()">
|
||||||
</li>
|
</vn-icon-button>
|
||||||
</ul>
|
|
||||||
</vn-menu>
|
|
||||||
<vn-user-popover
|
<vn-user-popover
|
||||||
vn-id="user-popover">
|
vn-id="user-popover">
|
||||||
</vn-user-popover>
|
</vn-user-popover>
|
|
@ -2,12 +2,11 @@ import ngModule from '../../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class MainMenu {
|
export default class MainMenu {
|
||||||
constructor($, $http, vnAuth, vnModules) {
|
constructor($, $http, vnAuth) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
$,
|
$,
|
||||||
$http,
|
$http,
|
||||||
vnAuth,
|
vnAuth
|
||||||
modules: vnModules.get()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ export default class MainMenu {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainMenu.$inject = ['$scope', '$http', 'vnAuth', 'vnModules'];
|
MainMenu.$inject = ['$scope', '$http', 'vnAuth'];
|
||||||
|
|
||||||
ngModule.component('vnMainMenu', {
|
ngModule.component('vnMainMenu', {
|
||||||
template: require('./main-menu.html'),
|
template: require('./main-menu.html'),
|
||||||
|
|
|
@ -4,35 +4,30 @@
|
||||||
vn-main-menu {
|
vn-main-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
& > div {
|
& > * {
|
||||||
display: flex;
|
transition: color 250ms ease-out;
|
||||||
align-items: center;
|
}
|
||||||
box-sizing: border-box;
|
& > #user {
|
||||||
|
flex: 1;
|
||||||
& > * {
|
vertical-align: middle;
|
||||||
transition: color 250ms ease-out;
|
font-weight: bold;
|
||||||
}
|
margin-right: .2em;
|
||||||
& > #user {
|
text-overflow: ellipsis;
|
||||||
vertical-align: middle;
|
white-space: nowrap;
|
||||||
font-weight: bold;
|
overflow: hidden;
|
||||||
margin-right: .2em;
|
cursor: pointer;
|
||||||
text-overflow: ellipsis;
|
}
|
||||||
white-space: nowrap;
|
& > .vn-button {
|
||||||
overflow: hidden;
|
height: initial;
|
||||||
cursor: pointer;
|
color: inherit;
|
||||||
}
|
padding: 0;
|
||||||
& > .vn-button {
|
}
|
||||||
font-size: 1.2em;
|
& > :hover {
|
||||||
color: inherit;
|
color: $color-main;
|
||||||
padding: 0;
|
opacity: 1;
|
||||||
margin-left: .3em;
|
|
||||||
}
|
|
||||||
& > :hover {
|
|
||||||
color: $color-main;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for module cards.
|
||||||
|
*/
|
||||||
|
export default class ModuleCard extends Component {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
this.element.classList.add('vn-module-card');
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the card data. Should be implemented or overriden by child
|
||||||
|
* classes.
|
||||||
|
*/
|
||||||
|
reload() {
|
||||||
|
throw new Error('ModuleCard::reload() method not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnModuleCard', {
|
||||||
|
controller: ModuleCard
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
.vn-module-card {
|
||||||
|
padding: 0;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<vn-portal slot="menu">
|
||||||
|
<vn-left-menu></vn-left-menu>
|
||||||
|
</vn-portal>
|
||||||
|
<ui-view></ui-view>
|
|
@ -0,0 +1,15 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
export default class ModuleMain extends Component {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
this.element.classList.add('vn-module-main');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnModuleMain', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: ModuleMain
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
.vn-module-main {
|
||||||
|
padding: 0;
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,15 +8,10 @@ import './style.scss';
|
||||||
* @property {String} side [left|right] The side where the menu is displayed
|
* @property {String} side [left|right] The side where the menu is displayed
|
||||||
* @property {Boolean} shown Whether the menu it's currently displayed (Only for small viewports)
|
* @property {Boolean} shown Whether the menu it's currently displayed (Only for small viewports)
|
||||||
*/
|
*/
|
||||||
export default class SideMenu {
|
export default class SideMenu extends Component {
|
||||||
constructor($, $element, $window, $transitions) {
|
constructor($element, $) {
|
||||||
Object.assign(this, {
|
super($element, $);
|
||||||
$,
|
this.side = 'left';
|
||||||
$element,
|
|
||||||
$window,
|
|
||||||
$transitions,
|
|
||||||
side: 'left'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
|
@ -23,18 +19,25 @@ export default class SideMenu {
|
||||||
|
|
||||||
if (this.side == 'right') {
|
if (this.side == 'right') {
|
||||||
this.menu.classList.add('right');
|
this.menu.classList.add('right');
|
||||||
this.app.rightMenu = this;
|
this.layout.rightMenu = this;
|
||||||
|
this.layout.element.classList.add('right-menu');
|
||||||
} else {
|
} else {
|
||||||
this.menu.classList.add('left');
|
this.menu.classList.add('left');
|
||||||
this.app.leftMenu = this;
|
this.layout.leftMenu = this;
|
||||||
|
this.layout.element.classList.add('left-menu');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$onDestroy() {
|
$onDestroy() {
|
||||||
if (this.side == 'right')
|
if (this.side == 'right') {
|
||||||
this.app.rightMenu = null;
|
this.layout.rightMenu = null;
|
||||||
else
|
this.layout.element.classList.remove('right-menu');
|
||||||
this.app.leftMenu = null;
|
} else {
|
||||||
|
// this.layout.leftMenu = null;
|
||||||
|
this.layout.element.classList.remove('left-menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
onEscape(event) {
|
onEscape(event) {
|
||||||
|
@ -50,6 +53,7 @@ export default class SideMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
|
if (this.shown) return;
|
||||||
this.shown = true;
|
this.shown = true;
|
||||||
this.handler = e => this.onEscape(e);
|
this.handler = e => this.onEscape(e);
|
||||||
this.$window.addEventListener('keydown', this.handler);
|
this.$window.addEventListener('keydown', this.handler);
|
||||||
|
@ -57,12 +61,12 @@ export default class SideMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
|
if (!this.shown) return;
|
||||||
this.$window.removeEventListener('keydown', this.handler);
|
this.$window.removeEventListener('keydown', this.handler);
|
||||||
this.stateHandler();
|
this.stateHandler();
|
||||||
this.shown = false;
|
this.shown = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SideMenu.$inject = ['$scope', '$element', '$window', '$transitions'];
|
|
||||||
|
|
||||||
ngModule.component('vnSideMenu', {
|
ngModule.component('vnSideMenu', {
|
||||||
template: require('./side-menu.html'),
|
template: require('./side-menu.html'),
|
||||||
|
@ -72,6 +76,6 @@ ngModule.component('vnSideMenu', {
|
||||||
side: '@?'
|
side: '@?'
|
||||||
},
|
},
|
||||||
require: {
|
require: {
|
||||||
app: '^vnApp'
|
layout: '^vnLayout'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
|
vn-side-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
vn-side-menu > .menu {
|
vn-side-menu > .menu {
|
||||||
display: block;
|
display: block;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -10,17 +13,17 @@ vn-side-menu > .menu {
|
||||||
background-color: $color-bg-panel;
|
background-color: $color-bg-panel;
|
||||||
box-shadow: 0 .1em .2em $color-shadow;
|
box-shadow: 0 .1em .2em $color-shadow;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
top: $topbar-height;
|
|
||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
left: 0;
|
left: 0;
|
||||||
|
top: 0;
|
||||||
}
|
}
|
||||||
&.right {
|
&.right {
|
||||||
right: 0;
|
right: 0;
|
||||||
|
top: $topbar-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-width) {
|
@media screen and (max-width: $mobile-width) {
|
||||||
top: 0;
|
|
||||||
transition: transform 200ms ease-out;
|
transition: transform 200ms ease-out;
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
|
|
||||||
|
@ -28,6 +31,7 @@ vn-side-menu > .menu {
|
||||||
transform: translateZ(0) translateX(-$menu-width);
|
transform: translateZ(0) translateX(-$menu-width);
|
||||||
}
|
}
|
||||||
&.right {
|
&.right {
|
||||||
|
top: 0;
|
||||||
transform: translateZ(0) translateX($menu-width);
|
transform: translateZ(0) translateX($menu-width);
|
||||||
}
|
}
|
||||||
&.shown {
|
&.shown {
|
||||||
|
|
|
@ -59,4 +59,5 @@
|
||||||
.vn-popup .summary {
|
.vn-popup .summary {
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,8 @@
|
||||||
|
|
||||||
vn-topbar {
|
vn-topbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
color: $color-font-dark;
|
color: $color-font-dark;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: $color-header;
|
background-color: $color-header;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
& > header {
|
|
||||||
height: inherit;
|
|
||||||
width: inherit;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1 @@
|
||||||
import ngModule from '../../module';
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
ngModule.component('vnTopbar', {
|
|
||||||
template: require('./topbar.html'),
|
|
||||||
transclude: true
|
|
||||||
});
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
font-size: 60px;
|
font-size: 60px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
color: $color-font-dark;
|
color: $color-font-dark;
|
||||||
background: $color-secondary;
|
background: $color-marginal;
|
||||||
padding: .1em;
|
padding: .1em;
|
||||||
}
|
}
|
||||||
& > div {
|
& > div {
|
||||||
|
|
|
@ -62,9 +62,10 @@ export function config($translatePartialLoaderProvider, $httpProvider, $compileP
|
||||||
$translatePartialLoaderProvider.addPart(appName);
|
$translatePartialLoaderProvider.addPart(appName);
|
||||||
$httpProvider.interceptors.push('vnInterceptor');
|
$httpProvider.interceptors.push('vnInterceptor');
|
||||||
|
|
||||||
// $compileProvider.debugInfoEnabled(false);
|
$compileProvider
|
||||||
$compileProvider.commentDirectivesEnabled(false);
|
.debugInfoEnabled(false)
|
||||||
$compileProvider.cssClassDirectivesEnabled(false);
|
.commentDirectivesEnabled(false)
|
||||||
|
.cssClassDirectivesEnabled(false);
|
||||||
}
|
}
|
||||||
ngModule.config(config);
|
ngModule.config(config);
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,23 @@
|
||||||
import ngModule from './module';
|
import ngModule from './module';
|
||||||
import getMainRoute from 'core/lib/get-main-route';
|
import getMainRoute from 'core/lib/get-main-route';
|
||||||
|
|
||||||
function loader(moduleName) {
|
|
||||||
load.$inject = ['vnModuleLoader'];
|
|
||||||
function load(moduleLoader) {
|
|
||||||
return moduleLoader.load(moduleName);
|
|
||||||
}
|
|
||||||
return load;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.$inject = ['$stateProvider', '$urlRouterProvider'];
|
config.$inject = ['$stateProvider', '$urlRouterProvider'];
|
||||||
function config($stateProvider, $urlRouterProvider) {
|
function config($stateProvider, $urlRouterProvider) {
|
||||||
$urlRouterProvider.otherwise('/');
|
$urlRouterProvider.otherwise('/');
|
||||||
|
|
||||||
$stateProvider.state('home', {
|
$stateProvider
|
||||||
url: '/',
|
.state('login', {
|
||||||
template: '<vn-home></vn-home>',
|
url: '/login?continue',
|
||||||
description: 'Home'
|
description: 'Login',
|
||||||
});
|
views: {
|
||||||
$stateProvider.state('login', {
|
login: {template: '<vn-login></vn-login>'}
|
||||||
url: '/login?continue',
|
}
|
||||||
template: '<vn-login></vn-login>',
|
})
|
||||||
description: 'Login'
|
.state('home', {
|
||||||
});
|
url: '/',
|
||||||
|
description: 'Home',
|
||||||
function getParams(route) {
|
template: '<vn-home></vn-home>'
|
||||||
let params = '';
|
|
||||||
let temporalParams = [];
|
|
||||||
|
|
||||||
if (!route.params)
|
|
||||||
return params;
|
|
||||||
|
|
||||||
Object.keys(route.params).forEach(key => {
|
|
||||||
temporalParams.push(`${key} = "${route.params[key]}"`);
|
|
||||||
});
|
});
|
||||||
return temporalParams.join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let file in window.routes) {
|
for (let file in window.routes) {
|
||||||
let routeFile = window.routes[file];
|
let routeFile = window.routes[file];
|
||||||
|
@ -68,5 +50,26 @@ function config($stateProvider, $urlRouterProvider) {
|
||||||
$stateProvider.state(route.state, configRoute);
|
$stateProvider.state(route.state, configRoute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getParams(route) {
|
||||||
|
let params = '';
|
||||||
|
let temporalParams = [];
|
||||||
|
|
||||||
|
if (!route.params)
|
||||||
|
return params;
|
||||||
|
|
||||||
|
Object.keys(route.params).forEach(key => {
|
||||||
|
temporalParams.push(`${key} = "${route.params[key]}"`);
|
||||||
|
});
|
||||||
|
return temporalParams.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function loader(moduleName) {
|
||||||
|
load.$inject = ['vnModuleLoader'];
|
||||||
|
function load(moduleLoader) {
|
||||||
|
return moduleLoader.load(moduleName);
|
||||||
|
}
|
||||||
|
return load;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ngModule.config(config);
|
ngModule.config(config);
|
||||||
|
|
|
@ -123,25 +123,14 @@ html [scrollable] {
|
||||||
font-size: 0.7em
|
font-size: 0.7em
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[compact], .compact {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
max-width: $width-md;
|
|
||||||
}
|
|
||||||
vn-empty-rows {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1.5em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: Deprecated, use classes with text prefix */
|
/* XXX: Deprecated, use classes with text prefix */
|
||||||
|
|
||||||
[color-main] {
|
[color-main] {
|
||||||
color: $color-main;
|
color: $color-main;
|
||||||
}
|
}
|
||||||
[color-secondary] {
|
[color-marginal] {
|
||||||
color: $color-secondary;
|
color: $color-marginal;
|
||||||
}
|
}
|
||||||
[uppercase], .uppercase {
|
[uppercase], .uppercase {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
|
@ -84,14 +84,17 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
<vn-submit
|
|
||||||
label="Save"
|
|
||||||
vn-acl="deliveryBoss">
|
|
||||||
</vn-submit>
|
|
||||||
<vn-button
|
<vn-button
|
||||||
label="Undo changes"
|
label="Undo changes"
|
||||||
ng-if="watcher.dataChanged()"
|
ng-if="watcher.dataChanged()"
|
||||||
ng-click="watcher.loadOriginalData()">
|
ng-click="watcher.loadOriginalData()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
icon="save"
|
||||||
|
vn-acl="deliveryBoss"
|
||||||
|
vn-tooltip="Save"
|
||||||
|
class="round"
|
||||||
|
fixed-bottom-right>
|
||||||
|
</vn-submit>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<vn-side-menu side="left">
|
<vn-portal slot="menu">
|
||||||
<vn-zone-descriptor zone="$ctrl.zone"></vn-zone-descriptor>
|
<vn-zone-descriptor zone="$ctrl.zone"></vn-zone-descriptor>
|
||||||
<vn-left-menu></vn-left-menu>
|
<vn-left-menu source="card"></vn-left-menu>
|
||||||
</vn-side-menu>
|
</vn-portal>
|
||||||
<div ui-view></div>
|
<ui-view></ui-view>
|
||||||
|
|
|
@ -1,38 +1,21 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
|
import ModuleCard from 'salix/components/module-card';
|
||||||
|
|
||||||
class Controller {
|
class Controller extends ModuleCard {
|
||||||
constructor($http, $stateParams) {
|
reload() {
|
||||||
this.$http = $http;
|
|
||||||
this.$stateParams = $stateParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
this.getCard();
|
|
||||||
}
|
|
||||||
|
|
||||||
getCard() {
|
|
||||||
let filter = {
|
let filter = {
|
||||||
include: {
|
include: {
|
||||||
relation: 'agencyMode',
|
relation: 'agencyMode',
|
||||||
scope: {fields: ['name']}
|
scope: {fields: ['name']}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let json = encodeURIComponent(JSON.stringify(filter));
|
|
||||||
let query = `Zones/${this.$stateParams.id}?filter=${json}`;
|
|
||||||
this.$http.get(query).then(res => {
|
|
||||||
if (res.data)
|
|
||||||
this.zone = res.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reload() {
|
this.$http.get(`Zones/${this.$params.id}`, {filter})
|
||||||
this.getCard();
|
.then(res => this.zone = res.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$http', '$stateParams'];
|
ngModule.vnComponent('vnZoneCard', {
|
||||||
|
|
||||||
ngModule.component('vnZoneCard', {
|
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller
|
controller: Controller
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,35 +1,26 @@
|
||||||
import './index.js';
|
import './index.js';
|
||||||
|
|
||||||
describe('Agency Component vnZoneCard', () => {
|
describe('Agency Component vnZoneCard', () => {
|
||||||
let $scope;
|
|
||||||
let controller;
|
let controller;
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
let $stateParams;
|
let data = {id: 1, name: 'fooName'};
|
||||||
|
|
||||||
beforeEach(ngModule('agency'));
|
beforeEach(ngModule('agency'));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => {
|
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$stateParams = {id: 1};
|
let $element = angular.element('<div></div>');
|
||||||
controller = $componentController('vnZoneCard', {$scope, $stateParams});
|
controller = $componentController('vnZoneCard', {$element});
|
||||||
|
|
||||||
|
$stateParams.id = data.id;
|
||||||
|
$httpBackend.whenRoute('GET', 'Zones/:id').respond(data);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('getCard()', () => {
|
it('should request data and set it on the controller', () => {
|
||||||
it(`should make a query and define zone property`, () => {
|
controller.reload();
|
||||||
let filter = {
|
$httpBackend.flush();
|
||||||
include: {
|
|
||||||
relation: 'agencyMode',
|
|
||||||
scope: {fields: ['name']}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let json = encodeURIComponent(JSON.stringify(filter));
|
|
||||||
$httpBackend.expectGET(`Zones/1?filter=${json}`).respond({id: 1});
|
|
||||||
controller.getCard();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.zone).toEqual({id: 1});
|
expect(controller.zone).toEqual(data);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,79 +5,82 @@
|
||||||
form="form"
|
form="form"
|
||||||
save="post">
|
save="post">
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<div class="content-block">
|
<form
|
||||||
<form name="form" vn-http-submit="$ctrl.onSubmit()" compact>
|
name="form"
|
||||||
<vn-card class="vn-pa-lg">
|
vn-http-submit="$ctrl.onSubmit()"
|
||||||
<vn-horizontal>
|
class="vn-w-md">
|
||||||
<vn-textfield
|
<vn-card class="vn-pa-lg">
|
||||||
vn-one
|
<vn-horizontal>
|
||||||
vn-focus
|
<vn-textfield
|
||||||
label="Name"
|
vn-one
|
||||||
ng-model="$ctrl.zone.name"
|
vn-focus
|
||||||
rule>
|
label="Name"
|
||||||
</vn-textfield>
|
ng-model="$ctrl.zone.name"
|
||||||
</vn-horizontal>
|
rule>
|
||||||
<vn-horizontal>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
</vn-horizontal>
|
||||||
vn-one
|
<vn-horizontal>
|
||||||
ng-model="$ctrl.zone.warehouseFk"
|
<vn-autocomplete
|
||||||
url="Warehouses"
|
vn-one
|
||||||
show-field="name"
|
ng-model="$ctrl.zone.warehouseFk"
|
||||||
value-field="id"
|
url="Warehouses"
|
||||||
label="Warehouse"
|
show-field="name"
|
||||||
rule>
|
value-field="id"
|
||||||
</vn-autocomplete>
|
label="Warehouse"
|
||||||
<vn-autocomplete
|
rule>
|
||||||
vn-one
|
</vn-autocomplete>
|
||||||
ng-model="$ctrl.zone.agencyModeFk"
|
<vn-autocomplete
|
||||||
url="AgencyModes/isActive"
|
vn-one
|
||||||
show-field="name"
|
ng-model="$ctrl.zone.agencyModeFk"
|
||||||
value-field="id"
|
url="AgencyModes/isActive"
|
||||||
label="Agency"
|
show-field="name"
|
||||||
rule>
|
value-field="id"
|
||||||
</vn-autocomplete>
|
label="Agency"
|
||||||
</vn-horizontal>
|
rule>
|
||||||
<vn-horizontal>
|
</vn-autocomplete>
|
||||||
<vn-input-number
|
</vn-horizontal>
|
||||||
vn-two
|
<vn-horizontal>
|
||||||
label="Traveling days"
|
<vn-input-number
|
||||||
ng-model="$ctrl.zone.travelingDays"
|
vn-two
|
||||||
min="0"
|
label="Traveling days"
|
||||||
step="1"
|
ng-model="$ctrl.zone.travelingDays"
|
||||||
rule>
|
min="0"
|
||||||
</vn-input-number>
|
step="1"
|
||||||
<vn-input-time
|
rule>
|
||||||
vn-two
|
</vn-input-number>
|
||||||
label="Estimated hour (ETD)"
|
<vn-input-time
|
||||||
ng-model="$ctrl.zone.hour"
|
vn-two
|
||||||
rule>
|
label="Estimated hour (ETD)"
|
||||||
</vn-input-time>
|
ng-model="$ctrl.zone.hour"
|
||||||
</vn-horizontal>
|
rule>
|
||||||
<vn-horizontal>
|
</vn-input-time>
|
||||||
<vn-input-number
|
</vn-horizontal>
|
||||||
vn-one
|
<vn-horizontal>
|
||||||
label="Price"
|
<vn-input-number
|
||||||
model="$ctrl.zone.price"
|
vn-one
|
||||||
min="0"
|
label="Price"
|
||||||
step="0.01"
|
ng-model="$ctrl.zone.price"
|
||||||
rule>
|
min="0"
|
||||||
</vn-input-number>
|
step="0.01"
|
||||||
<vn-input-number
|
rule>
|
||||||
vn-one
|
</vn-input-number>
|
||||||
label="Bonus"
|
<vn-input-number
|
||||||
model="$ctrl.zone.bonus"
|
vn-one
|
||||||
min="0"
|
label="Bonus"
|
||||||
step="0.01"
|
ng-model="$ctrl.zone.bonus"
|
||||||
rule>
|
min="0"
|
||||||
</vn-input-number>
|
step="0.01"
|
||||||
</vn-horizontal>
|
rule>
|
||||||
<vn-horizontal>
|
</vn-input-number>
|
||||||
<vn-check ng-model="$ctrl.zone.isVolumetric" label="Volumetric"></vn-check>
|
</vn-horizontal>
|
||||||
</vn-horizontal>
|
<vn-horizontal>
|
||||||
</vn-card>
|
<vn-check ng-model="$ctrl.zone.isVolumetric" label="Volumetric"></vn-check>
|
||||||
<vn-button-bar>
|
</vn-horizontal>
|
||||||
<vn-submit label="Create"></vn-submit>
|
</vn-card>
|
||||||
<vn-button ui-sref="zone.index" label="Cancel"></vn-button>
|
<vn-submit
|
||||||
</vn-button-bar>
|
icon="check"
|
||||||
</form>
|
vn-tooltip="Create"
|
||||||
</div>
|
class="round"
|
||||||
|
fixed-bottom-right>
|
||||||
|
</vn-submit>
|
||||||
|
</form>
|
||||||
|
|
|
@ -32,9 +32,12 @@
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-submit
|
||||||
<vn-submit label="Query"></vn-submit>
|
icon="search"
|
||||||
</vn-button-bar>
|
vn-tooltip="Query"
|
||||||
|
class="round"
|
||||||
|
fixed-bottom-right>
|
||||||
|
</vn-submit>
|
||||||
</form>
|
</form>
|
||||||
<vn-zone-calendar
|
<vn-zone-calendar
|
||||||
data="data"
|
data="data"
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<div class="main-with-right-menu">
|
<vn-zone-calendar
|
||||||
<vn-zone-calendar
|
id="calendar"
|
||||||
id="calendar"
|
data="data"
|
||||||
data="data"
|
on-selection="$ctrl.onSelection($days, $type, $weekday, $data)"
|
||||||
on-selection="$ctrl.onSelection($days, $type, $weekday, $data)"
|
class="vn-w-md">
|
||||||
class="vn-w-md">
|
</vn-zone-calendar>
|
||||||
</vn-zone-calendar>
|
|
||||||
</div>
|
|
||||||
<vn-side-menu side="right">
|
<vn-side-menu side="right">
|
||||||
<div class="vn-pa-md">
|
<div class="vn-pa-md">
|
||||||
<h6
|
<h6
|
||||||
|
@ -35,59 +33,56 @@
|
||||||
</h6>
|
</h6>
|
||||||
<vn-data-viewer
|
<vn-data-viewer
|
||||||
data="data.events"
|
data="data.events"
|
||||||
is-loading="!data.events"
|
is-loading="!data.events">
|
||||||
class="vn-w-sm">
|
<div class="vn-list separated">
|
||||||
<div class="vn-list">
|
|
||||||
<a
|
<a
|
||||||
ng-repeat="row in data.events"
|
ng-repeat="row in data.events"
|
||||||
translate-attr="{title: 'Edit'}"
|
translate-attr="{title: 'Edit'}"
|
||||||
ng-click="$ctrl.onEditClick(row, $event)"
|
ng-click="$ctrl.onEditClick(row, $event)"
|
||||||
class="vn-list-item">
|
class="vn-item">
|
||||||
<vn-horizontal>
|
<vn-item-section>
|
||||||
<vn-one>
|
<div
|
||||||
<div
|
ng-if="::row.from && !row.to"
|
||||||
ng-if="::row.from && !row.to"
|
class="vn-mb-sm">
|
||||||
class="vn-mb-sm">
|
{{::row.from | date:'dd/MM/yy'}}
|
||||||
{{::row.from | date:'dd/MM/yy'}}
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div
|
ng-if="::!row.from || row.to"
|
||||||
ng-if="::!row.from || row.to"
|
class="vn-mb-sm ellipsize">
|
||||||
class="vn-mb-sm ellipsize">
|
<span ng-if="row.to">
|
||||||
<span ng-if="row.to">
|
{{::row.from | date:'dd/MM/yy'}} - {{::row.to | date:'dd/MM/yy'}}
|
||||||
{{::row.from | date:'dd/MM/yy'}} - {{::row.to | date:'dd/MM/yy'}}
|
</span>
|
||||||
</span>
|
<span ng-if="!row.to" translate>
|
||||||
<span ng-if="!row.to" translate>
|
Indefinitely
|
||||||
Indefinitely
|
</span>
|
||||||
</span>
|
<span ng-if="row.weekDays">
|
||||||
<span ng-if="row.weekDays">
|
({{::$ctrl.formatWdays(row.weekDays)}})
|
||||||
({{::$ctrl.formatWdays(row.weekDays)}})
|
</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
<vn-label-value
|
||||||
<vn-label-value
|
label="Closing"
|
||||||
label="Closing"
|
value="{{::row.hour | date:'hh:mm'}}">
|
||||||
value="{{::row.hour | date:'hh:mm'}}">
|
</vn-label-value>
|
||||||
</vn-label-value>
|
<vn-label-value
|
||||||
<vn-label-value
|
label="Traveling days"
|
||||||
label="Traveling days"
|
value="{{::row.travelingDays}}">
|
||||||
value="{{::row.travelingDays}}">
|
</vn-label-value>
|
||||||
</vn-label-value>
|
<vn-label-value
|
||||||
<vn-label-value
|
label="Price"
|
||||||
label="Price"
|
value="{{::row.price | currency:'EUR':2}}">
|
||||||
value="{{::row.price | currency:'EUR':2}}">
|
</vn-label-value>
|
||||||
</vn-label-value>
|
<vn-label-value
|
||||||
<vn-label-value
|
label="Bonus"
|
||||||
label="Bonus"
|
value="{{::row.bonus | currency:'EUR':2}}">
|
||||||
value="{{::row.bonus | currency:'EUR':2}}">
|
</vn-label-value>
|
||||||
</vn-label-value>
|
</vn-item-section>
|
||||||
</vn-one>
|
<vn-item-section side>
|
||||||
<vn-horizontal class="buttons">
|
<vn-icon-button
|
||||||
<vn-icon-button
|
icon="delete"
|
||||||
icon="delete"
|
translate-attr="{title: 'Delete'}"
|
||||||
translate-attr="{title: 'Delete'}"
|
ng-click="$ctrl.onDeleteClick(row.id, $event)">
|
||||||
ng-click="$ctrl.onDeleteClick(row.id, $event)">
|
</vn-icon-button>
|
||||||
</vn-icon-button>
|
</vn-item-section>
|
||||||
</vn-horizontal>
|
|
||||||
</vn-horizontal>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
|
|
|
@ -6,63 +6,56 @@
|
||||||
data="zones"
|
data="zones"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<div>
|
<vn-auto-search
|
||||||
<vn-card class="vn-w-sm vn-pa-md">
|
model="model"
|
||||||
<vn-searchbar
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
panel="vn-zone-search-panel"
|
</vn-auto-search>
|
||||||
model="model"
|
<vn-data-viewer
|
||||||
expr-builder="$ctrl.exprBuilder(param, value)"
|
model="model"
|
||||||
info="Search zone by id or name"
|
class="vn-w-md vn-mb-xl">
|
||||||
vn-focus>
|
<vn-card>
|
||||||
</vn-searchbar>
|
<vn-table model="model">
|
||||||
|
<vn-thead>
|
||||||
|
<vn-tr>
|
||||||
|
<vn-th field="id" number>Id</vn-th>
|
||||||
|
<vn-th field="name" expand>Name</vn-th>
|
||||||
|
<vn-th field="agencyModeFk">Agency</vn-th>
|
||||||
|
<vn-th field="hour" shrink>Closing</vn-th>
|
||||||
|
<vn-th field="price" number>Price</vn-th>
|
||||||
|
<vn-th shrink></vn-th>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-thead>
|
||||||
|
<vn-tbody>
|
||||||
|
<vn-tr
|
||||||
|
ng-repeat="zone in zones"
|
||||||
|
ui-sref="zone.card.location({id: zone.id})"
|
||||||
|
class="clickable search-result">
|
||||||
|
<vn-td number>{{::zone.id}}</vn-td>
|
||||||
|
<vn-td expand>{{::zone.name}}</vn-td>
|
||||||
|
<vn-td>{{::zone.agencyMode.name}}</vn-td>
|
||||||
|
<vn-td shrink>{{::zone.hour | date: 'HH:mm'}}</vn-td>
|
||||||
|
<vn-td number>{{::zone.price | currency: 'EUR':2}}</vn-td>
|
||||||
|
<vn-td shrink>
|
||||||
|
<vn-horizontal class="buttons">
|
||||||
|
<vn-icon-button
|
||||||
|
ng-click="$ctrl.clone($event, zone)"
|
||||||
|
vn-tooltip="Clone"
|
||||||
|
icon="icon-clone"
|
||||||
|
vn-acl="deliveryBoss"
|
||||||
|
vn-acl-action="remove">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-icon-button
|
||||||
|
ng-click="$ctrl.preview($event, zone)"
|
||||||
|
vn-tooltip="Preview"
|
||||||
|
icon="desktop_windows">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-td>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-tbody>
|
||||||
|
</vn-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-data-viewer
|
</vn-data-viewer>
|
||||||
model="model"
|
|
||||||
class="vn-w-md vn-mt-md vn-mb-xl">
|
|
||||||
<vn-card>
|
|
||||||
<vn-table model="model">
|
|
||||||
<vn-thead>
|
|
||||||
<vn-tr>
|
|
||||||
<vn-th field="id" number>Id</vn-th>
|
|
||||||
<vn-th field="name" expand>Name</vn-th>
|
|
||||||
<vn-th field="agencyModeFk">Agency</vn-th>
|
|
||||||
<vn-th field="hour" shrink>Closing</vn-th>
|
|
||||||
<vn-th field="price" number>Price</vn-th>
|
|
||||||
<vn-th shrink></vn-th>
|
|
||||||
</vn-tr>
|
|
||||||
</vn-thead>
|
|
||||||
<vn-tbody>
|
|
||||||
<vn-tr
|
|
||||||
ng-repeat="zone in zones"
|
|
||||||
ui-sref="zone.card.location({id: zone.id})"
|
|
||||||
class="clickable searchResult">
|
|
||||||
<vn-td number>{{::zone.id}}</vn-td>
|
|
||||||
<vn-td expand>{{::zone.name}}</vn-td>
|
|
||||||
<vn-td>{{::zone.agencyMode.name}}</vn-td>
|
|
||||||
<vn-td shrink>{{::zone.hour | date: 'HH:mm'}}</vn-td>
|
|
||||||
<vn-td number>{{::zone.price | currency: 'EUR':2}}</vn-td>
|
|
||||||
<vn-td shrink>
|
|
||||||
<vn-horizontal class="buttons">
|
|
||||||
<vn-icon-button
|
|
||||||
ng-click="$ctrl.clone($event, zone)"
|
|
||||||
vn-tooltip="Clone"
|
|
||||||
icon="icon-clone"
|
|
||||||
vn-acl="deliveryBoss"
|
|
||||||
vn-acl-action="remove">
|
|
||||||
</vn-icon-button>
|
|
||||||
<vn-icon-button
|
|
||||||
ng-click="$ctrl.preview($event, zone)"
|
|
||||||
vn-tooltip="Preview"
|
|
||||||
icon="desktop_windows">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-horizontal>
|
|
||||||
</vn-td>
|
|
||||||
</vn-tr>
|
|
||||||
</vn-tbody>
|
|
||||||
</vn-table>
|
|
||||||
</vn-card>
|
|
||||||
</vn-data-viewer>
|
|
||||||
</div>
|
|
||||||
<vn-popup vn-id="summary">
|
<vn-popup vn-id="summary">
|
||||||
<vn-zone-summary zone="$ctrl.selectedZone"></vn-zone-summary>
|
<vn-zone-summary zone="$ctrl.selectedZone"></vn-zone-summary>
|
||||||
</vn-popup>
|
</vn-popup>
|
||||||
|
|
|
@ -3,25 +3,26 @@
|
||||||
url="Zones/{{$ctrl.$params.id}}/getLeaves"
|
url="Zones/{{$ctrl.$params.id}}/getLeaves"
|
||||||
filter="::$ctrl.filter">
|
filter="::$ctrl.filter">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
|
<vn-portal slot="topbar">
|
||||||
|
<vn-searchbar></vn-searchbar>
|
||||||
|
</vn-portal>
|
||||||
|
<vn-auto-search
|
||||||
|
on-search="$ctrl.onSearch($params)">
|
||||||
|
</vn-auto-search>
|
||||||
<div class="vn-w-md">
|
<div class="vn-w-md">
|
||||||
<vn-card class="vn-pa-md">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-searchbar
|
<vn-treeview
|
||||||
auto-load="false"
|
vn-id="treeview"
|
||||||
on-search="$ctrl.onSearch($params)"
|
root-label="Locations"
|
||||||
vn-focus>
|
fetch-func="$ctrl.onFetch($item)"
|
||||||
</vn-searchbar>
|
sort-func="$ctrl.onSort($a, $b)">
|
||||||
</vn-card>
|
<vn-check
|
||||||
<vn-card class="vn-pa-lg vn-mt-md">
|
ng-model="item.selected"
|
||||||
<vn-treeview vn-id="treeview" root-label="Locations"
|
on-change="$ctrl.onSelection(value, item)"
|
||||||
fetch-func="$ctrl.onFetch($item)"
|
triple-state="true"
|
||||||
sort-func="$ctrl.onSort($a, $b)">
|
ng-click="$event.preventDefault()"
|
||||||
<vn-check
|
label="{{::item.name}}">
|
||||||
ng-model="item.selected"
|
</vn-check>
|
||||||
on-change="$ctrl.onSelection(value, item)"
|
</vn-treeview>
|
||||||
triple-state="true"
|
|
||||||
ng-click="$event.preventDefault()"
|
|
||||||
label="{{::item.name}}">
|
|
||||||
</vn-check>
|
|
||||||
</vn-treeview>
|
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,10 @@ import Component from 'core/lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller extends Component {
|
class Controller extends Component {
|
||||||
|
$postLink() {
|
||||||
|
this.onSearch();
|
||||||
|
}
|
||||||
|
|
||||||
onSearch(params) {
|
onSearch(params) {
|
||||||
this.$.model.applyFilter({}, params).then(() => {
|
this.$.model.applyFilter({}, params).then(() => {
|
||||||
const data = this.$.model.data;
|
const data = this.$.model.data;
|
||||||
|
@ -12,9 +16,8 @@ class Controller extends Component {
|
||||||
|
|
||||||
onFetch(item) {
|
onFetch(item) {
|
||||||
const params = item ? {parentId: item.id} : null;
|
const params = item ? {parentId: item.id} : null;
|
||||||
return this.$.model.applyFilter({}, params).then(() => {
|
return this.$.model.applyFilter({}, params)
|
||||||
return this.$.model.data;
|
.then(() => this.$.model.data);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSort(a, b) {
|
onSort(a, b) {
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
vn-treeview-child {
|
vn-treeview-child {
|
||||||
.content > .vn-check:not(.indeterminate) {
|
.content > .vn-check:not(.indeterminate) {
|
||||||
color: $color-main;
|
color: $color-button;
|
||||||
|
|
||||||
& > .btn {
|
& > .btn {
|
||||||
border-color: $color-main;
|
border-color: $color-button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.content > .vn-check.checked {
|
.content > .vn-check.checked {
|
||||||
color: $color-main;
|
color: $color-button;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,11 @@
|
||||||
<vn-main-block>
|
<vn-portal slot="topbar">
|
||||||
<vn-side-menu side="left">
|
<vn-searchbar
|
||||||
<ul class="menu">
|
search-state="zone.index"
|
||||||
<li>
|
panel="vn-zone-search-panel"
|
||||||
<a translate ui-sref="zone.index">Zones</a>
|
info="Search zone by id or name">
|
||||||
</li>
|
</vn-searchbar>
|
||||||
<li>
|
</vn-portal>
|
||||||
<a translate ui-sref="zone.deliveryDays">Delivery days</a>
|
<vn-portal slot="menu">
|
||||||
</li>
|
<vn-left-menu></vn-left-menu>
|
||||||
</ul>
|
</vn-portal>
|
||||||
</vn-side-menu>
|
<ui-view></ui-view>
|
||||||
<div class="content-block" ui-view></div>
|
|
||||||
</vn-main-block>
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import './style.scss';
|
import ModuleMain from 'salix/components/module-main';
|
||||||
|
|
||||||
ngModule.component('vnZone', {
|
export default class Zone extends ModuleMain {}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnZone', {
|
||||||
|
controller: Zone,
|
||||||
template: require('./index.html')
|
template: require('./index.html')
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
@import "effects";
|
|
||||||
|
|
||||||
vn-zone {
|
|
||||||
ul.menu {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
padding-top: $spacing-md;
|
|
||||||
margin: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
|
|
||||||
& > li > a {
|
|
||||||
@extend %clickable;
|
|
||||||
display: block;
|
|
||||||
color: inherit;
|
|
||||||
padding: .6em 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue