Merge pull request #6731 from gconsidine/ui/component-hardening

Ui/component hardening
This commit is contained in:
Greg Considine
2017-06-26 16:32:08 -04:00
committed by GitHub
33 changed files with 926 additions and 638 deletions

View File

@@ -6,7 +6,6 @@ function AddCredentialsController (models, $state) {
let me = models.me; let me = models.me;
let credential = models.credential; let credential = models.credential;
let credentialType = models.credentialType; let credentialType = models.credentialType;
let organization = models.organization;
vm.panelTitle = 'NEW CREDENTIAL'; vm.panelTitle = 'NEW CREDENTIAL';
@@ -23,22 +22,18 @@ function AddCredentialsController (models, $state) {
omit: ['user', 'team', 'inputs'] omit: ['user', 'team', 'inputs']
}); });
vm.form.organization._placeholder = DEFAULT_ORGANIZATION_PLACEHOLDER; vm.form.organization._resource = 'organization';
vm.form.organization._data = organization.get('results'); vm.form.organization._route = 'credentials.add.organization';
vm.form.organization._format = 'objects';
vm.form.organization._exp = 'org as org.name for org in state._data';
vm.form.organization._display = 'name';
vm.form.organization._key = 'id';
vm.form.credential_type._data = credentialType.get('results'); vm.form.credential_type._resource = 'credential_type';
vm.form.credential_type._placeholder = 'SELECT A TYPE'; vm.form.credential_type._route = 'credentials.add.credentialType';
vm.form.credential_type._format = 'grouped-object';
vm.form.credential_type._display = 'name';
vm.form.credential_type._key = 'id';
vm.form.credential_type._exp = 'type as type.name group by type.kind for type in state._data';
vm.form.inputs = { vm.form.inputs = {
_get: credentialType.mergeInputProperties, _get: id => {
let type = credentialType.getById(id);
return credentialType.mergeInputProperties(type);
},
_source: vm.form.credential_type, _source: vm.form.credential_type,
_reference: 'vm.form.inputs', _reference: 'vm.form.inputs',
_key: 'inputs' _key: 'inputs'

View File

@@ -1,4 +1,4 @@
<at-panel ng-if="$state.current.name === 'credentials.add' || $state.current.name === 'credentials.edit'"> <at-panel ng-if="!$state.current.name.includes('permissions')">
<at-panel-heading>{{ vm.panelTitle }}</at-panel-heading> <at-panel-heading>{{ vm.panelTitle }}</at-panel-heading>
<at-tab-group> <at-tab-group>
@@ -10,27 +10,26 @@
<at-form state="vm.form"> <at-form state="vm.form">
<at-input-text col="4" tab="1" state="vm.form.name"></at-input-text> <at-input-text col="4" tab="1" state="vm.form.name"></at-input-text>
<at-input-text col="4" tab="2" state="vm.form.description"></at-input-text> <at-input-text col="4" tab="2" state="vm.form.description"></at-input-text>
<at-input-select col="4" tab="3" state="vm.form.organization"></at-input-select> <at-input-lookup col="4" tab="3" state="vm.form.organization"></at-input-lookup>
<at-divider></at-divider> <at-divider></at-divider>
<at-input-select col="4" tab="4" state="vm.form.credential_type"></at-input-select> <at-input-lookup col="4" tab="4" state="vm.form.credential_type"></at-input-lookup>
<at-input-group col="4" tab="4" state="vm.form.inputs"> <at-input-group col="4" tab="5" state="vm.form.inputs">
Type Details Type Details
</at-input-group> </at-input-group>
<at-action-group col="12" pos="right"> <at-action-group col="12" pos="right">
<at-form-action type="cancel"></at-form-action> <at-form-action type="cancel" to="credentials"></at-form-action>
<at-form-action type="save"></at-form-action> <at-form-action type="save"></at-form-action>
</at-action-group> </at-action-group>
</at-form> </at-form>
</at-panel-body> </at-panel-body>
</at-panel> </at-panel>
<at-panel ng-if="$state.current.name === 'credentials.edit.permissions' || <at-panel ng-if="$state.current.name.includes('permissions')">
$state.current.name === 'credentials.edit.permissions.add'"> <at-panel-heading>CREDENTIALS PERMISSIONS</at-panel-heading>
<at-panel-heading>Credentials Permissions</at-panel-heading>
<at-tab-group> <at-tab-group>
<at-tab state="vm.tab.details">Details</at-tab> <at-tab state="vm.tab.details">Details</at-tab>
@@ -42,5 +41,5 @@
</at-panel-body> </at-panel-body>
</at-panel> </at-panel>
<div ng-if="$state.current.name === 'credentials.edit.permissions.add'" ui-view="modal"></div> <div ng-if="$state.current.name.includes('permissions.add')" ui-view="modal"></div>

View File

@@ -6,7 +6,7 @@ function EditCredentialsController (models, $state, $scope) {
let me = models.me; let me = models.me;
let credential = models.credential; let credential = models.credential;
let credentialType = models.credentialType; let credentialType = models.credentialType;
let organization = models.organization; let selectedCredentialType = credentialType.getById(credential.get('credential_type'));
vm.tab = { vm.tab = {
details: { details: {
@@ -21,9 +21,9 @@ function EditCredentialsController (models, $state, $scope) {
}; };
$scope.$watch('$state.current.name', (value) => { $scope.$watch('$state.current.name', (value) => {
if (value === 'credentials.edit') { if (/credentials.edit($|\.organization$)/.test(value)) {
vm.tab.details._active = true; vm.tab.details._active = true;
vm.tab.details._permissions = false; vm.tab.permissions._active = false;
} else { } else {
vm.tab.permissions._active = true; vm.tab.permissions._active = true;
vm.tab.details._active = false; vm.tab.details._active = false;
@@ -39,23 +39,19 @@ function EditCredentialsController (models, $state, $scope) {
omit: ['user', 'team', 'inputs'] omit: ['user', 'team', 'inputs']
}); });
vm.form.organization._placeholder = DEFAULT_ORGANIZATION_PLACEHOLDER; vm.form.organization._resource = 'organization';
vm.form.organization._data = organization.get('results'); vm.form.organization._route = 'credentials.edit.organization';
vm.form.organization._format = 'objects'; vm.form.organization._value = credential.get('summary_fields.organization.id');
vm.form.organization._exp = 'org as org.name for org in state._data'; vm.form.organization._displayValue = credential.get('summary_fields.organization.name');
vm.form.organization._display = 'name';
vm.form.organization._key = 'id';
vm.form.organization._value = organization.getById(credential.get('organization'));
vm.form.credential_type._data = credentialType.get('results');
vm.form.credential_type._format = 'grouped-object';
vm.form.credential_type._display = 'name';
vm.form.credential_type._key = 'id';
vm.form.credential_type._exp = 'type as type.name group by type.kind for type in state._data';
vm.form.credential_type._value = credentialType.getById(credential.get('credential_type'));
vm.form.credential_type._resource = 'credential_type';
vm.form.credential_type._route = 'credentials.edit.credentialType';
vm.form.credential_type._value = selectedCredentialType.id;
vm.form.credential_type._displayValue = selectedCredentialType.name;
vm.form.inputs = { vm.form.inputs = {
_get (type) { _get (id) {
let type = credentialType.getById(id);
let inputs = credentialType.mergeInputProperties(type); let inputs = credentialType.mergeInputProperties(type);
if (type.id === credential.get('credential_type')) { if (type.id === credential.get('credential_type')) {
@@ -77,7 +73,7 @@ function EditCredentialsController (models, $state, $scope) {
}; };
vm.form.onSaveSuccess = res => { vm.form.onSaveSuccess = res => {
$state.go('credentials', { reload: true }); $state.go('credentials.edit', { credential_id: credential.get('id') }, { reload: true });
}; };
} }

View File

@@ -1,18 +1,14 @@
import PermissionsList from '../../src/access/permissions-list.controller'; import LegacyCredentials from './legacy.credentials';
import CredentialForm from '../../src/credentials/credentials.form';
import CredentialList from '../../src/credentials/credentials.list';
import ListController from '../../src/credentials/list/credentials-list.controller';
import AddController from './add-credentials.controller.js'; import AddController from './add-credentials.controller.js';
import EditController from './edit-credentials.controller.js'; import EditController from './edit-credentials.controller.js';
import { N_ } from '../../src/i18n'; import { N_ } from '../../src/i18n';
function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization) { function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType) {
let id = $stateParams.credential_id; let id = $stateParams.credential_id;
let promises = { let promises = {
me: new Me('get'), me: new Me('get'),
credentialType: new CredentialType('get'), credentialType: new CredentialType('get')
organization: new Organization('get')
}; };
if (id) { if (id) {
@@ -29,46 +25,13 @@ CredentialsResolve.$inject = [
'$stateParams', '$stateParams',
'MeModel', 'MeModel',
'CredentialModel', 'CredentialModel',
'CredentialTypeModel', 'CredentialTypeModel'
'OrganizationModel'
]; ];
function CredentialsConfig ($stateProvider, $stateExtenderProvider, pathServiceProvider) { function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider) {
let pathService = pathServiceProvider.$get(); let path = pathProvider.$get();
let stateExtender = $stateExtenderProvider.$get(); let stateExtender = $stateExtenderProvider.$get();
let legacy = legacyProvider.$get();
stateExtender.addState({
name: 'credentials',
route: '/credentials',
ncyBreadcrumb: {
label: N_('CREDENTIALS')
},
views: {
'@': {
templateUrl: pathService.getViewPath('credentials/index')
},
'list@credentials': {
templateProvider: function(CredentialList, generateList) {
let html = generateList.build({
list: CredentialList,
mode: 'edit'
});
return html;
},
controller: ListController
}
},
searchPrefix: 'credential',
resolve: {
Dataset: ['CredentialList', 'QuerySet', '$stateParams', 'GetBasePath',
function(list, qs, $stateParams, GetBasePath) {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams[`${list.iterator}_search`]);
}
]
}
});
stateExtender.addState({ stateExtender.addState({
name: 'credentials.add', name: 'credentials.add',
@@ -78,7 +41,7 @@ function CredentialsConfig ($stateProvider, $stateExtenderProvider, pathServiceP
}, },
views: { views: {
'add@credentials': { 'add@credentials': {
templateUrl: pathService.getViewPath('credentials/add-edit-credentials'), templateUrl: path.getViewPath('credentials/add-edit-credentials'),
controller: AddController, controller: AddController,
controllerAs: 'vm' controllerAs: 'vm'
} }
@@ -96,7 +59,7 @@ function CredentialsConfig ($stateProvider, $stateExtenderProvider, pathServiceP
}, },
views: { views: {
'edit@credentials': { 'edit@credentials': {
templateUrl: pathService.getViewPath('credentials/add-edit-credentials'), templateUrl: path.getViewPath('credentials/add-edit-credentials'),
controller: EditController, controller: EditController,
controllerAs: 'vm' controllerAs: 'vm'
} }
@@ -106,178 +69,24 @@ function CredentialsConfig ($stateProvider, $stateExtenderProvider, pathServiceP
} }
}); });
stateExtender.addState({ stateExtender.addState(legacy.getStateConfiguration('list'));
name: "credentials.edit.permissions", stateExtender.addState(legacy.getStateConfiguration('edit-permissions'));
url: "/permissions?{permission_search:queryset}", stateExtender.addState(legacy.getStateConfiguration('add-permissions'));
resolve: { stateExtender.addState(legacy.getStateConfiguration('add-organization'));
ListDefinition: () => { stateExtender.addState(legacy.getStateConfiguration('edit-organization'));
return { stateExtender.addState(legacy.getStateConfiguration('add-credential-type'));
name: 'permissions', stateExtender.addState(legacy.getStateConfiguration('edit-credential-type'));
disabled: '(organization === undefined ? true : false)',
// Do not transition the state if organization is undefined
ngClick: `(organization === undefined ? true : false)||$state.go('credentials.edit.permissions')`,
awToolTip: '{{permissionsTooltip}}',
dataTipWatch: 'permissionsTooltip',
awToolTipTabEnabledInEditMode: true,
dataPlacement: 'right',
basePath: 'api/v2/credentials/{{$stateParams.id}}/access_list/',
search: {
order_by: 'username'
},
type: 'collection',
title: N_('Permissions'),
iterator: 'permission',
index: false,
open: false,
actions: {
add: {
ngClick: "$state.go('.add')",
label: 'Add',
awToolTip: N_('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ' + N_('ADD'),
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
fields: {
username: {
key: true,
label: N_('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
label: N_('Role'),
type: 'role',
nosort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4'
},
team_roles: {
label: N_('Team Roles'),
type: 'team_roles',
nosort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4'
}
}
};
},
Dataset: ['QuerySet', '$stateParams', (qs, $stateParams) => {
let id = $stateParams.credential_id;
let path = `api/v2/credentials/${id}/access_list/`;
return qs.search(path, $stateParams[`permission_search`]);
}
]
},
params: {
permission_search: {
value: {
page_size: "20",
order_by: "username"
},
dynamic:true,
squash:""
}
},
ncyBreadcrumb: {
parent: "credentials.edit",
label: "PERMISSIONS"
},
views: {
'related': {
templateProvider: function(CredentialForm, GenerateForm) {
let html = GenerateForm.buildCollection({
mode: 'edit',
related: `permissions`,
form: typeof(CredentialForm) === 'function' ?
CredentialForm() : CredentialForm
});
return html;
},
controller: 'PermissionsList'
}
}
});
stateExtender.addState({
name: 'credentials.edit.permissions.add',
url: '/add-permissions',
resolve: {
usersDataset: [
'addPermissionsUsersList',
'QuerySet',
'$stateParams',
'GetBasePath',
(list, qs, $stateParams, GetBasePath) => {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams.user_search);
}
],
teamsDataset: [
'addPermissionsTeamsList',
'QuerySet',
'$stateParams',
'GetBasePath',
(list, qs, $stateParams, GetBasePath) => {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams.team_search);
}
],
resourceData: ['CredentialModel', '$stateParams', (Credential, $stateParams) => {
return new Credential('get', $stateParams.credential_id)
.then(credential => ({ data: credential.get() }));
}],
},
params: {
user_search: {
value: {
order_by: 'username',
page_size: 5
},
dynamic: true
},
team_search: {
value: {
order_by: 'name',
page_size: 5
},
dynamic: true
}
},
ncyBreadcrumb: {
skip: true
},
views: {
'modal@credentials.edit': {
template: `
<add-rbac-resource
users-dataset="$resolve.usersDataset"
teams-dataset="$resolve.teamsDataset"
selected="allSelected"
resource-data="$resolve.resourceData"
title="Add Users / Teams">
</add-rbac-resource>`
}
},
onExit: $state => {
if ($state.transition) {
$('#add-permissions-modal').modal('hide');
$('.modal-backdrop').remove();
$('body').removeClass('modal-open');
}
}
});
} }
CredentialsConfig.$inject = [ CredentialsConfig.$inject = [
'$stateProvider', '$stateExtenderProvider',
'$stateExtenderProvider', 'LegacyCredentialsServiceProvider',
'PathServiceProvider' 'PathServiceProvider'
]; ];
angular angular
.module('at.features.credentials', []) .module('at.features.credentials', [])
.config(CredentialsConfig) .config(CredentialsConfig)
.controller('AddController', AddController) .controller('AddController', AddController)
.controller('EditController', EditController); .controller('EditController', EditController)
.service('LegacyCredentialsService', LegacyCredentials);

View File

@@ -0,0 +1,348 @@
import PermissionsList from '../../src/access/permissions-list.controller';
import CredentialForm from '../../src/credentials/credentials.form';
import CredentialList from '../../src/credentials/credentials.list';
import OrganizationList from '../../src/organizations/organizations.list';
import ListController from '../../src/credentials/list/credentials-list.controller';
import { N_ } from '../../src/i18n';
function LegacyCredentialsService (pathService) {
this.list = {
name: 'credentials',
route: '/credentials',
ncyBreadcrumb: {
label: N_('CREDENTIALS')
},
views: {
'@': {
templateUrl: pathService.getViewPath('credentials/index')
},
'list@credentials': {
templateProvider: function(CredentialList, generateList) {
let html = generateList.build({
list: CredentialList,
mode: 'edit'
});
return html;
},
controller: ListController
}
},
searchPrefix: 'credential',
resolve: {
Dataset: ['CredentialList', 'QuerySet', '$stateParams', 'GetBasePath',
function(list, qs, $stateParams, GetBasePath) {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams[`${list.iterator}_search`]);
}
]
}
};
this.editPermissions = {
name: 'credentials.edit.permissions',
url: '/permissions?{permission_search:queryset}',
resolve: {
ListDefinition: () => {
return {
name: 'permissions',
disabled: 'organization === undefined',
ngClick: `organization === undefined || $state.go('credentials.edit.permissions')`,
awToolTip: '{{permissionsTooltip}}',
dataTipWatch: 'permissionsTooltip',
awToolTipTabEnabledInEditMode: true,
dataPlacement: 'right',
basePath: 'api/v2/credentials/{{$stateParams.id}}/access_list/',
search: {
order_by: 'username'
},
type: 'collection',
title: N_('Permissions'),
iterator: 'permission',
index: false,
open: false,
actions: {
add: {
ngClick: `$state.go('.add')`,
label: 'Add',
awToolTip: N_('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ' + N_('ADD'),
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
fields: {
username: {
key: true,
label: N_('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
label: N_('Role'),
type: 'role',
nosort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4'
},
team_roles: {
label: N_('Team Roles'),
type: 'team_roles',
nosort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4'
}
}
};
},
Dataset: ['QuerySet', '$stateParams', (qs, $stateParams) => {
let id = $stateParams.credential_id;
let path = `api/v2/credentials/${id}/access_list/`;
return qs.search(path, $stateParams[`permission_search`]);
}
]
},
params: {
permission_search: {
value: {
page_size: '20',
order_by: 'username'
},
dynamic:true,
squash:''
}
},
ncyBreadcrumb: {
parent: 'credentials.edit',
label: 'PERMISSIONS'
},
views: {
'related': {
templateProvider: function(CredentialForm, GenerateForm) {
let html = GenerateForm.buildCollection({
mode: 'edit',
related: `permissions`,
form: typeof(CredentialForm) === 'function' ?
CredentialForm() : CredentialForm
});
return html;
},
controller: 'PermissionsList'
}
}
};
this.addPermissions = {
name: 'credentials.edit.permissions.add',
url: '/add-permissions',
resolve: {
usersDataset: [
'addPermissionsUsersList',
'QuerySet',
'$stateParams',
'GetBasePath',
(list, qs, $stateParams, GetBasePath) => {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams.user_search);
}
],
teamsDataset: [
'addPermissionsTeamsList',
'QuerySet',
'$stateParams',
'GetBasePath',
(list, qs, $stateParams, GetBasePath) => {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams.team_search);
}
],
resourceData: ['CredentialModel', '$stateParams', (Credential, $stateParams) => {
return new Credential('get', $stateParams.credential_id)
.then(credential => ({ data: credential.get() }));
}],
},
params: {
user_search: {
value: {
order_by: 'username',
page_size: 5
},
dynamic: true
},
team_search: {
value: {
order_by: 'name',
page_size: 5
},
dynamic: true
}
},
ncyBreadcrumb: {
skip: true
},
views: {
'modal@credentials.edit': {
template: `
<add-rbac-resource
users-dataset='$resolve.usersDataset'
teams-dataset='$resolve.teamsDataset'
selected='allSelected'
resource-data='$resolve.resourceData'
title='Add Users / Teams'>
</add-rbac-resource>`
}
},
onExit: $state => {
if ($state.transition) {
$('#add-permissions-modal').modal('hide');
$('.modal-backdrop').remove();
$('body').removeClass('modal-open');
}
}
};
this.lookupTemplateProvider = (ListDefinition, generateList) => {
let html = generateList.build({
mode: 'lookup',
list: ListDefinition,
input_type: 'radio'
});
return `<lookup-modal>${html}</lookup-modal>`;
};
this.organization = {
url: '/organization?selected',
searchPrefix: 'organization',
params: {
organization_search: {
value: {
page_size: 5,
order_by: 'name',
role_level: 'admin_role'
},
dynamic: true,
squash: ''
}
},
data: {
basePath: 'organizations',
formChildState: true
},
ncyBreadcrumb: {
skip: true
},
views: {},
resolve: {
ListDefinition: ['OrganizationList', list => {
return list;
}],
Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
(list, qs, $stateParams, GetBasePath) => {
return qs.search(
GetBasePath('organizations'),
$stateParams[`${list.iterator}_search`]
);
}
]
},
onExit: function($state) {
if ($state.transition) {
$('#form-modal').modal('hide');
$('.modal-backdrop').remove();
$('body').removeClass('modal-open');
}
}
};
this.credentialType = {
url: '/credential_type?selected',
searchPrefix: 'credential_type',
params: {
credential_type_search: {
value: {
page_size: 5,
order_by: 'name'
},
dynamic: true,
squash: ''
}
},
data: {
basePath: 'credential_types',
formChildState: true
},
ncyBreadcrumb: {
skip: true
},
views: {},
resolve: {
ListDefinition: ['CredentialTypesList', list => {
return list;
}],
Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
(list, qs, $stateParams, GetBasePath) => {
return qs.search(
GetBasePath('credential_types'),
$stateParams[`${list.iterator}_search`]
);
}
]
},
onExit: function($state) {
if ($state.transition) {
$('#form-modal').modal('hide');
$('.modal-backdrop').remove();
$('body').removeClass('modal-open');
}
}
};
this.getStateConfiguration = (name) => {
switch (name) {
case 'list':
return this.list;
case 'edit-permissions':
return this.editPermissions;
case 'add-permissions':
return this.addPermissions;
case 'add-organization':
this.organization.name = 'credentials.add.organization';
this.organization.views['organization@credentials.add'] = {
templateProvider: this.lookupTemplateProvider
};
return this.organization;
case 'edit-organization':
this.organization.name = 'credentials.edit.organization';
this.organization.views['organization@credentials.edit'] = {
templateProvider: this.lookupTemplateProvider
};
return this.organization;
case 'add-credential-type':
this.credentialType.name = 'credentials.add.credentialType';
this.credentialType.views['credential_type@credentials.add'] = {
templateProvider: this.lookupTemplateProvider
};
return this.credentialType;
case 'edit-credential-type':
this.credentialType.name = 'credentials.edit.credentialType';
this.credentialType.views['credential_type@credentials.edit'] = {
templateProvider: this.lookupTemplateProvider
};
return this.credentialType;
default:
throw new Error(`Legacy state configuration for ${name} does not exist`);
};
};
}
LegacyCredentialsService.$inject = [
'PathService'
];
export default LegacyCredentialsService;

View File

@@ -1,7 +1,7 @@
@import 'action/_index'; @import 'action/_index';
@import 'input/_index'; @import 'input/_index';
@import 'panel/_index';
@import 'modal/_index'; @import 'modal/_index';
@import 'panel/_index';
@import 'popover/_index'; @import 'popover/_index';
@import 'tabs/_index'; @import 'tabs/_index';
@import 'utility/_index'; @import 'utility/_index';

View File

@@ -1,7 +1,7 @@
.at-ActionGroup { .at-ActionGroup {
margin-top: @at-space-6x; margin-top: @at-margin-panel;
button:last-child { button:last-child {
margin-left: @at-space-5x; margin-left: @at-margin-panel-inset;
} }
} }

View File

@@ -38,14 +38,14 @@ function atFormActionController ($state) {
vm.setCancelDefaults = () => { vm.setCancelDefaults = () => {
scope.text = 'CANCEL'; scope.text = 'CANCEL';
scope.fill = 'Hollow'; scope.fill = 'Hollow';
scope.color = 'white'; scope.color = 'default';
scope.action = () => $state.go('^'); scope.action = () => $state.go(scope.to || '^');
}; };
vm.setSaveDefaults = () => { vm.setSaveDefaults = () => {
scope.text = 'SAVE'; scope.text = 'SAVE';
scope.fill = ''; scope.fill = '';
scope.color = 'green'; scope.color = 'success';
scope.action = () => form.submit(); scope.action = () => form.submit();
}; };
} }
@@ -64,7 +64,8 @@ function atFormAction (pathService) {
link, link,
scope: { scope: {
state: '=', state: '=',
type: '@' type: '@',
to: '@'
} }
}; };
} }

View File

@@ -2,6 +2,9 @@ function atFormLink (scope, el, attrs, controllers) {
let formController = controllers[0]; let formController = controllers[0];
let form = el[0]; let form = el[0];
scope.ns = 'form';
scope[scope.ns] = { modal: {} };
formController.init(scope, form); formController.init(scope, form);
} }
@@ -9,10 +12,10 @@ function AtFormController (eventService) {
let vm = this || {}; let vm = this || {};
let scope; let scope;
let modal;
let form; let form;
vm.components = []; vm.components = [];
vm.modal = {};
vm.state = { vm.state = {
isValid: false, isValid: false,
disabled: false, disabled: false,
@@ -22,6 +25,7 @@ function AtFormController (eventService) {
vm.init = (_scope_, _form_) => { vm.init = (_scope_, _form_) => {
scope = _scope_; scope = _scope_;
form = _form_; form = _form_;
modal = scope[scope.ns].modal;
vm.setListeners(); vm.setListeners();
}; };
@@ -102,7 +106,7 @@ function AtFormController (eventService) {
message = err.data; message = err.data;
} }
vm.modal.show('Unable to Submit', `Unexpected Error: ${message}`); modal.show('Unable to Submit', `Unexpected Error: ${message}`);
} }
}; };
@@ -110,7 +114,7 @@ function AtFormController (eventService) {
let title = 'Unable to Submit'; let title = 'Unable to Submit';
let message = 'Unexpected server error. View the console for more information'; let message = 'Unexpected server error. View the console for more information';
vm.modal.show(title, message); modal.show(title, message);
return true; return true;
}; };

View File

@@ -5,5 +5,5 @@
</div> </div>
</form> </form>
<at-modal state="vm.modal"></at-modal> <at-modal></at-modal>
</div> </div>

View File

@@ -7,8 +7,6 @@ import inputGroup from './input/group.directive';
import inputLabel from './input/label.directive'; import inputLabel from './input/label.directive';
import inputLookup from './input/lookup.directive'; import inputLookup from './input/lookup.directive';
import inputMessage from './input/message.directive'; import inputMessage from './input/message.directive';
import inputNumber from './input/number.directive';
import inputSelect from './input/select.directive';
import inputSecret from './input/secret.directive'; import inputSecret from './input/secret.directive';
import inputText from './input/text.directive'; import inputText from './input/text.directive';
import inputTextarea from './input/textarea.directive'; import inputTextarea from './input/textarea.directive';
@@ -34,9 +32,7 @@ angular
.directive('atInputLabel', inputLabel) .directive('atInputLabel', inputLabel)
.directive('atInputLookup', inputLookup) .directive('atInputLookup', inputLookup)
.directive('atInputMessage', inputMessage) .directive('atInputMessage', inputMessage)
.directive('atInputNumber', inputNumber)
.directive('atInputSecret', inputSecret) .directive('atInputSecret', inputSecret)
.directive('atInputSelect', inputSelect)
.directive('atInputText', inputText) .directive('atInputText', inputText)
.directive('atInputTextarea', inputTextarea) .directive('atInputTextarea', inputTextarea)
.directive('atInputTextareaSecret', inputTextareaSecret) .directive('atInputTextareaSecret', inputTextareaSecret)

View File

@@ -1,17 +1,21 @@
.at-Input { .at-Input {
.at-mixin-Placeholder(@at-gray-dark-3x); .at-mixin-Placeholder(@at-color-input-placeholder);
height: @at-input-height; height: @at-height-input;
background: @at-white; background: @at-color-input-background;
border-radius: @at-border-radius; border-radius: @at-border-radius;
color: @at-gray-dark-5x; color: @at-color-input-text;
&, &:active { &, &:active {
border-color: @at-gray-dark-2x; border-color: @at-color-input-border;
} }
&:focus { &:focus {
border-color: @at-blue; border-color: @at-color-input-focus;
}
&[disabled] {
background: @at-color-input-disabled;
} }
} }
@@ -21,43 +25,43 @@
& > label { & > label {
& > input[type=checkbox] { & > input[type=checkbox] {
height: @at-input-height; height: @at-height-input;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
& > p { & > p {
margin: 0; margin: 0;
padding: 0 0 0 @at-space-6x; padding: 0 0 0 @at-padding-panel;
line-height: @at-line-height-tall; line-height: @at-line-height-tall;
} }
} }
} }
.at-InputContainer { .at-InputContainer {
margin-top: @at-space-6x; margin-top: @at-margin-panel;
} }
.at-Input-button { .at-Input-button {
min-width: @at-input-button-width;
display: block; display: block;
height: @at-input-height; height: @at-height-button;
line-height: 1;
&, &:active, &:hover, &:focus { &, &:active, &:hover, &:focus {
color: @at-gray-dark-3x; color: @at-color-button-text-default;
border-color: @at-gray-dark-2x; border-color: @at-color-input-border;
background-color: @at-white; background-color: @at-color-default;
cursor: pointer; cursor: pointer;
} }
} }
.at-Input--focus { .at-Input--focus {
border-color: @at-blue; border-color: @at-color-input-focus;
} }
.at-Input--rejected { .at-Input--rejected {
&, &:focus { &, &:focus {
border-color: @at-red; border-color: @at-color-input-error;
} }
} }
@@ -66,7 +70,6 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
left: 0; left: 0;
right: @at-input-button-width;
z-index: -2; z-index: -2;
opacity: 0; opacity: 0;
} }
@@ -77,15 +80,15 @@
.at-InputGroup { .at-InputGroup {
padding: 0; padding: 0;
margin: @at-space-6x 0 0 0; margin: @at-margin-panel 0 0 0;
} }
.at-InputGroup-border { .at-InputGroup-border {
position: absolute; position: absolute;
width: @at-inset-width; width: 5px;
height: 100%; height: 100%;
background: @at-gray-dark; background: @at-color-panel-border;
left: -@at-inset-width; left: -5px;
} }
.at-InputGroup-button { .at-InputGroup-button {
@@ -93,19 +96,22 @@
& > button { & > button {
height: 100%; height: 100%;
border-right: none;
color: @at-color-button-text-default;
min-width: @at-input-button-width;
} }
} }
.at-InputGroup-title { .at-InputGroup-title {
.at-mixin-Heading(@at-font-size-2x); .at-mixin-Heading(@at-font-size-panel-inset-heading);
margin: 0 0 0 @at-space-5x; margin: 0 0 0 @at-margin-panel-inset;
} }
.at-InputGroup-divider { .at-InputGroup-divider {
clear: both; clear: both;
margin: 0; margin: 0;
padding: 0; padding: 0;
height: @at-space-6x; height: @at-height-divider;
} }
.at-InputLabel { .at-InputLabel {
@@ -114,17 +120,17 @@
} }
.at-InputLabel-name { .at-InputLabel-name {
color: @at-gray-dark-4x; color: @at-color-form-label;
font-size: @at-font-size-2x; font-size: @at-font-size-form-label;
font-weight: @at-font-weight; font-weight: @at-font-weight-body;
text-transform: uppercase; text-transform: uppercase;
} }
.at-InputLabel-hint { .at-InputLabel-hint {
margin-left: @at-space-4x; margin-left: @at-margin-form-label-hint;
color: @at-gray-dark-3x; color: @at-color-input-hint;
font-size: @at-font-size; font-size: @at-font-size-help-text;
font-weight: @at-font-weight; font-weight: @at-font-weight-body;
line-height: @at-line-height-short; line-height: @at-line-height-short;
} }
@@ -137,15 +143,15 @@
margin-bottom: 0; margin-bottom: 0;
& > input[type=checkbox] { & > input[type=checkbox] {
margin: 0 @at-space 0 0; margin: 0 3px 0 0;
position: relative; position: relative;
top: @at-space; top: 3px
} }
& > p { & > p {
font-size: @at-font-size; font-size: @at-font-size-help-text;
color: @at-gray-dark-4x; color: @at-color-form-label;
font-weight: @at-font-weight; font-weight: @at-font-weight-body;
display: inline; display: inline;
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -153,16 +159,16 @@
} }
.at-InputMessage--rejected { .at-InputMessage--rejected {
font-size: @at-font-size; font-size: @at-font-size-help-text;
color: @at-red; color: @at-color-error;
margin: @at-space-3x 0 0 0; margin: @at-margin-input-message 0 0 0;
padding: 0; padding: 0;
} }
.at-InputLabel-required { .at-InputLabel-required {
color: @at-red; color: @at-color-error;
font-weight: @at-font-weight-2x; font-weight: @at-font-weight-heading;
font-size: @at-font-size-2x; font-size: @at-font-size-form-label;
margin: 0; margin: 0;
} }
@@ -171,13 +177,13 @@
width: 100%; width: 100%;
& > i { & > i {
font-size: @at-font-size; font-size: @at-font-size-button;
position: absolute; position: absolute;
z-index: 3; z-index: 3;
pointer-events: none; pointer-events: none;
top: @at-space-4x; top: @at-height-input / 3;
right: @at-space-4x; right: @at-height-input / 3;
color: @at-gray-dark-2x; color: @at-color-input-icon;
} }
} }
@@ -188,7 +194,7 @@
} }
.at-InputSelect-select { .at-InputSelect-select {
height: @at-input-height; height: @at-height-input;
cursor: pointer; cursor: pointer;
position: absolute; position: absolute;
z-index: 1; z-index: 1;

View File

@@ -49,7 +49,6 @@ function AtInputGroupController ($scope, $compile) {
vm.insert(group); vm.insert(group);
state._group = group; state._group = group;
vm.compile(group);
}; };
vm.createComponentConfigs = inputs => { vm.createComponentConfigs = inputs => {
@@ -138,20 +137,22 @@ function AtInputGroupController ($scope, $compile) {
vm.createComponent = (input, index) => { vm.createComponent = (input, index) => {
let tabindex = Number(scope.tab) + index; let tabindex = Number(scope.tab) + index;
let col = input._expand ? 12 : scope.col; let col = input._expand ? 12 : scope.col;
let component = angular.element(
return angular.element(
`<${input._component} col="${col}" tab="${tabindex}" `<${input._component} col="${col}" tab="${tabindex}"
state="${state._reference}._group[${index}]"> state="${state._reference}._group[${index}]">
</${input._component}>` </${input._component}>`
); );
$compile(component)(scope.$parent)
return component;
}; };
vm.createDivider = () => { vm.createDivider = () => {
return angular.element('<at-divider></at-divider>'); let divider = angular.element('<at-divider></at-divider>');
}; $compile(divider[0])(scope.$parent);
vm.compile = group => { return divider;
group.forEach(component => $compile(component._element[0])(scope.$parent));
}; };
vm.clear = () => { vm.clear = () => {

View File

@@ -9,43 +9,46 @@ function atInputLookupLink (scope, element, attrs, controllers) {
inputController.init(scope, element, formController); inputController.init(scope, element, formController);
} }
function AtInputLookupController (baseInputController) { function AtInputLookupController (baseInputController, $state, $stateParams) {
let vm = this || {}; let vm = this || {};
vm.lookup = {}; let scope;
vm.init = (scope, element, form) => { vm.init = (_scope_, element, form) => {
baseInputController.call(vm, 'input', scope, element, form); baseInputController.call(vm, 'input', _scope_, element, form);
vm.lookup.modal = { scope = _scope_;
title: 'Select Organization',
buttons: [
{
type: 'cancel'
},
{
type: 'select'
}
]
};
vm.lookup.search = { scope.$watch(scope.state._resource, vm.watchResource);
placeholder: 'test'
};
vm.lookup.table = {
};
vm.check(); vm.check();
}; };
vm.watchResource = () => {
if (scope[scope.state._resource]) {
scope.state._value = scope[scope.state._resource];
scope.state._displayValue = scope[`${scope.state._resource}_name`];
vm.check();
}
};
vm.search = () => { vm.search = () => {
vm.modal.show('test'); let params = {};
if (scope.state._value) {
params.selected = scope.state._value;
}
$state.go(scope.state._route, params);
}; };
} }
AtInputLookupController.$inject = ['BaseInputController']; AtInputLookupController.$inject = [
'BaseInputController',
'$state',
'$stateParams'
];
function atInputLookup (pathService) { function atInputLookup (pathService) {
return { return {

View File

@@ -4,7 +4,7 @@
<div class="input-group"> <div class="input-group">
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn at-ButtonHollow--white at-Input-button" <button class="btn at-ButtonHollow--default at-Input-button"
ng-disabled="state._disabled || form.disabled" ng-disabled="state._disabled || form.disabled"
ng-click="vm.search()"> ng-click="vm.search()">
<i class="fa fa-search"></i> <i class="fa fa-search"></i>
@@ -13,7 +13,7 @@
<input type="text" <input type="text"
class="form-control at-Input" class="form-control at-Input"
ng-class="{ 'at-Input--rejected': state.rejected }" ng-class="{ 'at-Input--rejected': state.rejected }"
ng-model="state._value" ng-model="state._displayValue"
ng-attr-tabindex="{{ tab || undefined }}" ng-attr-tabindex="{{ tab || undefined }}"
ng-attr-placeholder="{{::state._placeholder || undefined }}" ng-attr-placeholder="{{::state._placeholder || undefined }}"
ng-change="vm.check()" ng-change="vm.check()"
@@ -23,8 +23,5 @@
<at-input-message></at-input-message> <at-input-message></at-input-message>
</div> </div>
<at-modal state="vm.lookup"> <div ui-view="{{ state._resource }}"></div>
<at-search></at-search>
<at-table></at-table>
</at-modal>
</div> </div>

View File

@@ -1,54 +0,0 @@
const DEFAULT_STEP = '1';
const DEFAULT_MIN = '0';
const DEFAULT_MAX = '1000000000';
const DEFAULT_PLACEHOLDER = '';
function atInputNumberLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
if (scope.tab === '1') {
element.find('input')[0].focus();
}
inputController.init(scope, element, formController);
}
function AtInputNumberController (baseInputController) {
let vm = this || {};
vm.init = (scope, element, form) => {
baseInputController.call(vm, 'input', scope, element, form);
scope.state._step = scope.state._step || DEFAULT_STEP;
scope.state._min = scope.state._min || DEFAULT_MIN;
scope.state._max = scope.state._max || DEFAULT_MAX;
scope.state._placeholder = scope.state._placeholder || DEFAULT_PLACEHOLDER;
vm.check();
};
}
AtInputNumberController.$inject = ['BaseInputController'];
function atInputNumber (pathService) {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputNumber'],
templateUrl: pathService.getPartialPath('components/input/number'),
controller: AtInputNumberController,
controllerAs: 'vm',
link: atInputNumberLink,
scope: {
state: '=',
col: '@',
tab: '@'
}
};
}
atInputNumber.$inject = ['PathService'];
export default atInputNumber;

View File

@@ -1,19 +0,0 @@
<div class="col-sm-{{::col}} at-InputContainer">
<div class="form-group at-u-flat">
<at-input-label></at-input-label>
<input type="number"
class="form-control at-Input"
ng-class="{ 'at-Input--rejected': state.rejected }"
ng-model="state._value"
ng-attr-min="state._min"
ng-attr-max="state._max"
ng-attr-step="state._step"
ng-attr-tabindex="{{ tab || undefined }}"
ng-attr-placeholder="{{::state._placeholder || undefined }}"
ng-change="vm.check()"
ng-disabled="state._disabled || form.disabled" />
<at-input-message></at-input-message>
</div>
</div>

View File

@@ -1,10 +1,28 @@
.at-Modal-body {
font-size: @at-font-size;
padding: 0;
}
.at-Modal-dismiss {
.at-mixin-ButtonIcon();
font-size: @at-font-size-modal-dismiss;
color: @at-color-icon-dismiss;
text-align: right;
}
.at-Modal-heading {
margin: 0;
overflow: visible;
& > .at-Modal-dismiss {
margin: 0;
}
}
.at-Modal-title { .at-Modal-title {
margin: 0; margin: 0;
padding: 0; padding: 0;
.at-mixin-Heading(@at-font-size-3x); .at-mixin-Heading(@at-font-size-modal-heading);
} }
.at-Modal-body {
font-size: @at-font-size;
}

View File

@@ -1,45 +1,74 @@
const DEFAULT_ANIMATION_DURATION = 150; const DEFAULT_ANIMATION_DURATION = 150;
function atModalLink (scope, el, attr, controllers) { function atModalLink (scope, el, attrs, controllers) {
let modalController = controllers[0]; let modalController = controllers[0];
let container = el[0]; let property = `scope.${scope.ns}.modal`;
modalController.init(scope, container); let done = scope.$watch(property, () => {
modalController.init(scope, el);
done();
});
} }
function AtModalController () { function AtModalController (eventService) {
let vm = this; let vm = this;
let scope; let overlay;
let container; let modal;
let listeners;
vm.init = (_scope_, _container_) => { vm.init = (scope, el) => {
scope = _scope_; overlay = el[0];
container = _container_; modal = el.find('.at-Modal-window')[0];
scope.state.show = vm.show; vm.modal = scope[scope.ns].modal;
scope.state.hide = vm.hide; vm.modal.show = vm.show;
vm.modal.hide = vm.hide;
}; };
vm.show = (title, message) => { vm.show = (title, message) => {
scope.title = title; vm.modal.title = title;
scope.message = message; vm.modal.message = message;
container.style.display = 'block'; event.stopPropagation();
container.style.opacity = 1;
listeners = eventService.addListeners([
[window, 'click', vm.clickToHide]
]);
overlay.style.display = 'block';
overlay.style.opacity = 1;
}; };
vm.hide = () => { vm.hide = () => {
container.style.opacity = 0; overlay.style.opacity = 0;
setTimeout(() => { eventService.remove(listeners);
container.style.display = 'none';
scope.message = ''; setTimeout(() => overlay.style.display = 'none', DEFAULT_ANIMATION_DURATION);
scope.title = ''; };
}, DEFAULT_ANIMATION_DURATION);
vm.clickToHide = event => {
if (vm.clickIsOutsideModal(event)) {
vm.hide();
}
};
vm.clickIsOutsideModal = e => {
let m = modal.getBoundingClientRect();
let cx = e.clientX;
let cy = e.clientY;
if (cx < m.left || cx > m.right || cy > m.bottom || cy < m.top) {
return true;
}
return false;
}; };
} }
AtModalController.$inject = ['EventService'];
function atModal (pathService) { function atModal (pathService) {
return { return {
restrict: 'E', restrict: 'E',
@@ -50,9 +79,7 @@ function atModal (pathService) {
controller: AtModalController, controller: AtModalController,
controllerAs: 'vm', controllerAs: 'vm',
link: atModalLink, link: atModalLink,
scope: { scope: true
state: '='
}
}; };
} }

View File

@@ -1,20 +1,31 @@
<div class="modal at-Modal fade" tabindex="-1" role="dialog"> <div class="modal at-Modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content at-Modal-window">
<div class="modal-header"> <div class="row">
<button type="button" class="close" ng-click="vm.hide()"> <div class="col-xs-10">
<i class="fa fa-times"></i> <div class="at-Modal-heading">
</button> <h4 class="modal-title at-Modal-title">{{ vm.modal.title }}</h4>
<h4 class="modal-title at-Modal-title">{{ title }}</h4> </div>
</div>
<div class="col-xs-2">
<div class="at-Modal-dismiss">
<i class="fa fa-lg fa-times-circle" ng-click="vm.hide()"></i>
</div>
</div>
</div> </div>
<div class="modal-body at-Modal-body">
<p ng-show="message">{{ message }}</p> <ng-transclude></ng-transclude>
<ng-transclude></ng-transclude>
</div> <div ng-show="vm.modal.message">
<div class="modal-footer"> <div class="modal-body at-Modal-body">
<button type="button" class="btn at-ButtonHollow--white" ng-click="vm.hide()"> <p>{{ vm.modal.message }}</p>
OK </div>
</button> <div class="modal-footer">
<button type="button" class="btn at-ButtonHollow--default"
ng-click="vm.hide()">
OK
</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
.at-Panel { .at-Panel {
margin: @at-space-6x 0 0 0; margin: @at-margin-panel 0 0 0;
padding: @at-space-6x; padding: @at-padding-panel;
border-color: @at-gray-dark; border-color: @at-color-panel-border;
} }
.at-Panel-heading { .at-Panel-heading {
@@ -11,6 +11,7 @@
.at-Panel-dismiss { .at-Panel-dismiss {
.at-mixin-ButtonIcon(); .at-mixin-ButtonIcon();
color: @at-color-icon-dismiss;
text-align: right; text-align: right;
} }
@@ -20,6 +21,6 @@
} }
.at-Panel-headingTitle { .at-Panel-headingTitle {
.at-mixin-Heading(@at-font-size-3x); .at-mixin-Heading(@at-font-size-panel-heading);
text-transform: none; text-transform: none;
} }

View File

@@ -1,5 +1,5 @@
.at-Popover { .at-Popover {
padding: 0 0 0 @at-space-3x; padding: 0 0 0 5px;
} }
.at-Popover--inline { .at-Popover--inline {
@@ -8,7 +8,8 @@
.at-Popover-icon { .at-Popover-icon {
.at-mixin-ButtonIcon(); .at-mixin-ButtonIcon();
font-size: @at-font-size-4x; color: @at-color-icon-popover;
font-size: @at-font-size-icon;
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
@@ -17,35 +18,35 @@
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
color: @at-white; color: @at-white;
background-color: @at-gray-dark-4x; background-color: @at-color-body-background-dark;
max-width: @at-popover-width; max-width: @at-popover-maxwidth;
padding: @at-space-4x; padding: @at-padding-popover;
height: auto; height: auto;
position: fixed; position: fixed;
z-index: 2000; z-index: 2000;
margin: 0 0 0 @at-space-6x; margin: 0 0 0 18px;
border-radius: @at-border-radius; border-radius: @at-border-radius;
box-shadow: 0 5px 10px rgba(0,0,0, 0.2); box-shadow: 0 5px 10px rgba(0,0,0, 0.2);
transition: opacity .15s linear; transition: opacity .15s linear;
font-weight: @at-font-weight font-weight: @at-font-weight-body;
} }
.at-Popover-arrow { .at-Popover-arrow {
color: @at-gray-dark-4x; color: @at-color-body-background-dark;
position: fixed; position: fixed;
z-index: 1999; z-index: 1999;
padding: 0; padding: 0;
margin: @at-space-4x 0 0 @at-space; margin: 8px 0 0 3px;
} }
.at-Popover-title { .at-Popover-title {
.at-mixin-Heading(@at-font-size); .at-mixin-Heading(@at-font-size-body);
color: @at-white; color: @at-color-body-text-dark;
margin-bottom: @at-space-4x; margin-bottom: @at-margin-popover;
} }
.at-Popover-text { .at-Popover-text {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-size: @at-font-size; font-size: @at-font-size-body;
} }

View File

@@ -1,26 +1,27 @@
.at-TabGroup { .at-TabGroup {
margin-top: @at-space-6x; margin-top: @at-margin-panel;
} }
.at-Tab { .at-Tab {
margin: 0 @at-space-5x 0 0; margin: 0 @at-margin-item-column 0 0;
font-size: @at-font-size; font-size: @at-font-size-body;
line-height: 1;
} }
.at-Tab--active { .at-Tab--active {
&, &:hover, &:active, &:focus { &, &:hover, &:active, &:focus {
color: @at-white; color: @at-color-tab-text-default-active;
background-color: @at-gray-dark-3x; background-color: @at-color-tab-default-active;
border-color: @at-gray-dark-3x; border-color: @at-color-tab-border-default-active;
cursor: default; cursor: default;
} }
} }
.at-Tab--disabled { .at-Tab--disabled {
&, &:hover, &:active, &:focus { &, &:hover, &:active, &:focus {
background-color: @at-white; background-color: @at-color-tab-default-disabled;
color: @at-gray-dark-2x; color: @at-color-tab-text-default-disabled;
border-color: @at-gray-dark-2x; border-color: @at-color-tab-border-default-disabled;
opacity: 0.65; opacity: 0.65;
cursor: not-allowed; cursor: not-allowed;
} }

View File

@@ -1,4 +1,4 @@
<button class="btn at-ButtonHollow--white at-Tab" <button class="btn at-ButtonHollow--default at-Tab"
ng-attr-disabled="{{ state._disabled || undefined }}" ng-attr-disabled="{{ state._disabled || undefined }}"
ng-class="{ 'at-Tab--active': state._active, 'at-Tab--disabled': state._disabled }" ng-class="{ 'at-Tab--active': state._active, 'at-Tab--disabled': state._disabled }"
ng-click="vm.go()"> ng-click="vm.go()">

View File

@@ -77,25 +77,24 @@ function httpOptions (resource) {
}); });
} }
function get (method, keys) { function options (keys) {
let model; return this.find('options', keys);
}
if (keys) { function get (keys) {
model = this.model[method.toUpperCase()]; return this.find('get', keys);
} else { }
model = this.model.GET;
keys = method; function find (method, keys) {
} let value = this.model[method.toUpperCase()];
if (!keys) { if (!keys) {
return model; return value;
} }
keys = keys.split('.');
let value = model;
try { try {
keys = keys.split('.');
keys.forEach(key => { keys.forEach(key => {
let bracketIndex = key.indexOf('['); let bracketIndex = key.indexOf('[');
let hasArray = bracketIndex !== -1; let hasArray = bracketIndex !== -1;
@@ -137,6 +136,8 @@ function getById (id) {
function BaseModel (path) { function BaseModel (path) {
this.model = {}; this.model = {};
this.get = get; this.get = get;
this.options = options;
this.find = find;
this.normalizePath = normalizePath; this.normalizePath = normalizePath;
this.getById = getById; this.getById = getById;
this.request = request; this.request = request;

View File

@@ -3,7 +3,7 @@ const ENCRYPTED_VALUE = '$encrypted$';
let BaseModel; let BaseModel;
function createFormSchema (method, config) { function createFormSchema (method, config) {
let schema = Object.assign({}, this.get('options', `actions.${method.toUpperCase()}`)); let schema = Object.assign({}, this.options(`actions.${method.toUpperCase()}`));
if (config && config.omit) { if (config && config.omit) {
config.omit.forEach(key => { config.omit.forEach(key => {

View File

@@ -0,0 +1,68 @@
/**
* All base variables used. These should only be referenced by the contextual variables defined
* in the _contextual-variables.less file. In development, unless you are intentionally making a
* fundamental change, these variables should not be modified, removed, or added to.
*
* These variables should not be used directly in development of components or features. If an
* alias doesn't exist for the context you're working within, check with UX to create a new alias
* or to define a more applicable alias.
*
* The goal is for UX to define the contexts for the contextual variables, so it's easy to make
* modifications like, "Change heading text to be smaller" or "Make all warnings a lighter shade
* of orange"
*
* 1. Colors
* 2. Typography
* 3. Layout
*
*/
// 1. Colors --------------------------------------------------------------------------------------
@at-gray-light-3x: #fcfcfc;
@at-gray-light-2x: #f2f2f2;
@at-gray-light: #ebebeb;
@at-gray: #e1e1e1;
@at-gray-dark: #d7d7d7;
@at-gray-dark-2x: #b7b7b7;
@at-gray-dark-3x: #A9A9A9;
@at-gray-dark-4x: #848992;
@at-gray-dark-5x: #707070;
@at-gray-dark-6x: #161b1f;
@at-white: #ffffff;
@at-white-hover: #f2f2f2;
@at-blue: #337ab7;
@at-blue-hover: #286090;
@at-green: #5cb85c;
@at-green-hover: #449D44;
@at-orange: #f0ad4e;
@at-orange-hover: #ec971f;
@at-red: #d9534f;
@at-red-hover: #c9302c;
@at-red-bright: #ff0000;
@at-red-bright-hover: #d81f1f;
// 2. Typography ----------------------------------------------------------------------------------
@at-font-size: 12px;
@at-font-size-2x: 13px;
@at-font-size-3x: 14px;
@at-font-size-4x: 16px;
@at-font-size-5x: 20px;
@at-font-weight: 400;
@at-font-weight-2x: 700;
// 3. Layout --------------------------------------------------------------------------------------
@at-space: 5px;
@at-space-2x: 10px;
@at-space-3x: 15px;
@at-space-4x: 20px;

View File

@@ -1,38 +0,0 @@
/**
* For styles that are used in more than one place throughout the application.
*
* 1. Buttons
*
*/
// 1. Buttons -------------------------------------------------------------------------------------
.at-Button--green {
.at-mixin-Button();
.at-mixin-ButtonColor('at-green', 'at-white');
&[disabled] {
background: @at-gray-dark;
}
}
.at-Button--blue {
.at-mixin-Button();
.at-mixin-ButtonColor('at-blue', 'at-white');
}
.at-Button--red {
.at-mixin-Button();
.at-mixin-ButtonColor('at-red', 'at-white');
}
.at-ButtonHollow--white {
.at-mixin-Button();
.at-mixin-ButtonHollow('at-gray-dark-3x', 'at-gray-dark-2x');
border-color: @at-gray-dark;
}
.at-ButtonIcon {
padding: @at-space-2x @at-space-4x;
font-size: @at-font-size-3x;
}

View File

@@ -0,0 +1,139 @@
/**
* All variables used in the UI. Use these variables directly during the development of components
* and features. Be sure the context of the variable name applies to the work that's being done.
* For example, it wouldn't make sense to use `@at-input-height` to describe the height of a
* button. Either add an alias if it makes sense to use the same base variable, or add a new
* base variable to reference.
*
* Keep in mind the goal is to be able to modify an item by referencing its context instead of
* an arbitrary variable name. For example, tt should be a simple change when an ask comes in to
* "increase the height of inputs"
*
* 1. Colors
* 2. Typography
* 3. Layout
* 4. Buttons
* 5. Misc
*
*/
// 1. Colors --------------------------------------------------------------------------------------
@at-color-default: @at-white;
@at-color-default-hover: @at-white-hover;
@at-color-unreachable: @at-red-bright;
@at-color-unreachable-hover: @at-red-bright-hover;
@at-color-error: @at-red;
@at-color-error-hover: @at-red-hover;
@at-color-warning: @at-orange;
@at-color-warning-hover: @at-orange-hover;
@at-color-info: @at-blue;
@at-color-info-hover: @at-blue-hover;
@at-color-success: @at-green;
@at-color-success-hover: @at-green-hover;
@at-color-disabled: @at-gray-dark;
@at-color-body-background-dark: @at-gray-dark-5x;
@at-color-body-text-dark: @at-white;
@at-color-body-background: @at-gray-light-3x;
@at-color-body-text: @at-gray-dark-5x;
@at-color-button-border-default: @at-gray-dark-2x;
@at-color-button-text-default: @at-gray-dark-5x;
@at-color-tab-default-active: @at-gray-dark-2x;
@at-color-tab-border-default-active: @at-gray-dark-2x;
@at-color-tab-text-default-active: @at-white;
@at-color-tab-default-disabled: @at-white;
@at-color-tab-border-default-disabled: @at-gray-dark-2x;
@at-color-tab-text-default-disabled: @at-gray-dark-5x;
@at-color-form-label: @at-gray-dark-5x;
@at-color-input-border: @at-gray-dark-2x;
@at-color-input-error: @at-color-error;
@at-color-input-focus: @at-color-info;
@at-color-input-hint: @at-gray-dark-4x;
@at-color-input-icon: @at-gray-dark-2x;
@at-color-input-placeholder: @at-gray-dark-4x;
@at-color-input-text: @at-gray-dark-6x;
@at-color-input-background: @at-gray-light-3x;
@at-color-input-disabled: @at-gray-light-2x;
@at-color-icon-dismiss: @at-gray-dark;
@at-color-icon-popover: @at-gray-dark-3x;
@at-color-icon-hover: @at-gray-dark-5x;
@at-color-panel-heading: @at-gray-dark-5x;
@at-color-panel-border: @at-gray-dark;
@at-color-search-key-active: @at-blue;
@at-color-table-header-background: @at-gray-light;
@at-color-line-separator: @at-gray;
// 2. Typography ----------------------------------------------------------------------------------
@at-font-size-body: @at-font-size-3x;
@at-font-size-button: @at-font-size;
@at-font-size-breadcrumb: @at-font-size-3x;
@at-font-size-form-label: @at-font-size-2x;
@at-font-size-help-text: @at-font-size;
@at-font-size-icon: @at-font-size-4x;
@at-font-size-input: @at-font-size-3x;
@at-font-size-panel-heading: @at-font-size-3x;
@at-font-size-panel-inset-heading: @at-font-size-2x;
@at-font-size-modal-heading: @at-font-size-3x;
@at-font-size-modal-dismiss: @at-font-size-3x;
@at-font-size-navigation: @at-font-size-3x;
@at-font-size-table-heading: @at-font-size-3x;
@at-font-size-menu-icon: @at-font-size-5x;
@at-font-weight-body: @at-font-weight;
@at-font-weight-heading: @at-font-weight-2x;
// 3. Layout --------------------------------------------------------------------------------------
@at-padding-button-horizontal: @at-space-2x;
@at-padding-button-vertical: @at-space;
@at-padding-inset: @at-space-3x;
@at-padding-panel: @at-space-4x;
@at-padding-popover: @at-space-2x;
@at-padding-well: @at-space-2x;
@at-margin-input-message: @at-space;
@at-margin-item-column: @at-space-3x;
@at-margin-panel: @at-space-4x;
@at-margin-panel-inset: @at-space-3x;
@at-margin-popover: @at-space-2x;
@at-margin-tag: @at-space-2x;
@at-margin-form-label: @at-space;
@at-margin-form-label-hint: @at-space-2x;
@at-margin-top-search-key: @at-space-2x;
@at-height-divider: @at-margin-panel;
@at-height-input: 30px;
@at-height-button: 30px;
@at-height-tab: 30px;
// 4. Transitions ---------------------------------------------------------------------------------
@at-transition-icon-button: 0.2s;
// 5. Misc ----------------------------------------------------------------------------------------
@at-border-radius: 5px;
@at-popover-maxwidth: 320px;
@at-line-height-short: 0.9;
@at-line-height-tall: 2;
@at-line-height: 24px;
@at-input-button-width: 72px;

View File

@@ -0,0 +1,45 @@
/**
* For styles that are used in more than one place throughout the application.
*
* 1. Buttons
*
*/
// 1. Buttons -------------------------------------------------------------------------------------
.at-Button--success {
.at-mixin-Button();
.at-mixin-ButtonColor('at-color-success', 'at-color-default');
&[disabled] {
background: @at-color-disabled;
}
}
.at-Button--info {
.at-mixin-Button();
.at-mixin-ButtonColor('at-color-info', 'at-color-default');
}
.at-Button--error {
.at-mixin-Button();
.at-mixin-ButtonColor('at-color-error', 'at-color-default');
}
.at-ButtonHollow--default {
.at-mixin-Button();
.at-mixin-ButtonHollow(
'at-color-default',
'at-color-button-border-default',
'at-color-button-text-default'
);
}
.at-ButtonIcon {
padding: 4px @at-padding-button-horizontal;
font-size: @at-font-size-body;
}
.at-Button--expand {
width: 100%;
}

View File

@@ -11,9 +11,9 @@
} }
.at-mixin-Heading (@size) { .at-mixin-Heading (@size) {
color: @at-gray-dark-4x; color: @at-color-body-text;
font-size: @size; font-size: @size;
font-weight: @at-font-weight-2x; font-weight: @at-font-weight-heading;
line-height: @at-line-height-short; line-height: @at-line-height-short;
text-transform: uppercase; text-transform: uppercase;
margin: 0; margin: 0;
@@ -21,12 +21,13 @@
} }
.at-mixin-Button () { .at-mixin-Button () {
height: @at-input-height; height: @at-height-input;
padding: @at-space-2x @at-space-4x; padding: @at-padding-button-vertical @at-padding-button-horizontal;
font-size: @at-font-size; font-size: @at-font-size-body;
line-height: 1;
} }
.at-mixin-ButtonColor (@background, @color, @hover: '@{background}--hover') { .at-mixin-ButtonColor (@background, @color, @hover: '@{background}-hover') {
background-color: @@background; background-color: @@background;
&, &:hover, &:focus { &, &:hover, &:focus {
@@ -42,21 +43,23 @@
} }
} }
.at-mixin-ButtonHollow (@color, @accent) { .at-mixin-ButtonHollow (@bg, @border, @text) {
background-color: @at-white; @hover: '@{bg}-hover';
color: @@color;
border-color: @@color; background-color: @@bg;
color: @@text;
border-color: @@border;
&:hover, &:active { &:hover, &:active {
color: @@color; color: @@text;
background-color: @at-white--hover; background-color: @@hover;
box-shadow: none; box-shadow: none;
} }
&:focus { &:focus {
color: @at-white; color: @@text;
background-color: @@accent; background-color: @@hover;
border-color: @@accent; border-color: @@border;
cursor: default; cursor: default;
} }
@@ -67,14 +70,14 @@
.at-mixin-ButtonIcon () { .at-mixin-ButtonIcon () {
line-height: @at-line-height-short; line-height: @at-line-height-short;
color: @at-gray-dark-2x;
& > i { & > i {
cursor: pointer; cursor: pointer;
transition: color @at-transition-icon-button;
} }
& > i:hover { & > i:hover {
color: @at-gray-dark-3x; color: @at-color-icon-hover
} }
} }

View File

@@ -1,72 +0,0 @@
/**
* All variables used in the UI.
*
* 1. Colors
* 2. Typography
* 3. Layout
* 4. Input
* 5. Misc
*/
// 1. Colors --------------------------------------------------------------------------------------
@at-gray-light-5x: #fcfcfc;
@at-gray-light-4x: #fafafa;
@at-gray-light-3x: #f6f6f6;
@at-gray-light-2x: #f2f2f2;
@at-gray-light: #ebebeb;
@at-gray: #e1e1e1;
@at-gray-dark: #d7d7d7;
@at-gray-dark-2x: #b7b7b7;
@at-gray-dark-3x: #848992;
@at-gray-dark-4x: #707070;
@at-gray-dark-5x: #161b1f;
@at-white: #ffffff;
@at-white--hover: #f2f2f2;
@at-blue: #337ab7;
@at-blue--hover: #286090;
@at-green: #5cb85c;
@at-green--hover: #449D44;
@at-yellow: #f0ad4e;
@at-yellow--hover: #ec971f;
@at-red: #d9534f;
@at-red--hover: #c9302c;
@at-redAlert: #ff0000;
@at-redAlert--hover: #d81f1f;
// 2. Typography ----------------------------------------------------------------------------------
@at-font-size: 12px;
@at-font-size-2x: 13px;
@at-font-size-3x: 14px;
@at-font-size-4x: 16px;
@at-font-weight: 400;
@at-font-weight-2x: 700;
@at-font-weight-3x: 900;
@at-line-height-short: 0.9;
@at-line-height-tall: 2;
@at-line-height: 24px;
// 3. Layout --------------------------------------------------------------------------------------
@at-space: 3px;
@at-space-2x: 4px;
@at-space-3x: 5px;
@at-space-4x: 10px;
@at-space-5x: 15px;
@at-space-6x: 20px;
// 4. Input ---------------------------------------------------------------------------------------
@at-input-button-width: 72px;
@at-input-height: 30px;
// 5. Misc ----------------------------------------------------------------------------------------
@at-border-radius: 5px;
@at-popover-width: 320px;
@at-inset-width: 5px;

View File

@@ -1,8 +1,9 @@
// App-wide styles // App-wide styles
@import '_variables'; @import '_base-variables';
@import '_contextual-variables';
@import '_mixins'; @import '_mixins';
@import '_utility'; @import '_utility';
@import '_common'; @import '_global';
// Aggregated component and feature specific styles // Aggregated component and feature specific styles
@import '../components/_index'; @import '../components/_index';