Add component controller inheritance and new inputs

This commit is contained in:
gconsidine 2017-05-18 16:57:40 -04:00
parent 1644f78cf3
commit effd537a9c
19 changed files with 295 additions and 139 deletions

View File

@ -12,7 +12,9 @@
<at-input-text col="4" tab="2" state="vm.description"></at-input-text>
<at-input-select col="4" tab="3" state="vm.kind"></at-input-select>
<at-dynamic-input-group col="4" tab="4" state="vm.dynamic"></at-dynamic-input-group>
<at-dynamic-input-group col="4" tab="4" state="vm.dynamic">
Type Details
</at-dynamic-input-group>
<at-action-group col="12" pos="right">
<at-form-action type="cancel"></at-form-action>

View File

@ -1,12 +1,27 @@
.at-DynamicInputGroup {
padding: 0;
margin: 0;
margin: @at-space-6x 0 0 0;
}
.at-DynamicInputGroup-inset {
.at-DynamicInputGroup-border {
position: absolute;
width: @at-inset-width;
height: 100%;
background: @at-gray;
left: -@at-inset-width;
}
.at-DynamicInputGroup-title {
.at-mixin-Heading(@at-font-size-2x);
margin-top: 0;
margin-left: @at-space-5x;
margin-bottom: @at-space-4x;
}
.at-DynamicInputGroup-divider {
clear: both;
margin: 0;
padding: 0;
height: @at-space-6x;
}

View File

@ -52,7 +52,9 @@ function AtDynamicInputGroupController ($scope, $compile) {
inputs.forEach((input, i) => {
if (input.type === 'string') {
if (input.secret) {
if (input.secret && input.multiline) {
input.component = 'at-input-textarea';
} else if (input.secret) {
input.component = 'at-input-secret';
} else if (input.multiline) {
input.component = 'at-input-textarea';
@ -61,10 +63,9 @@ function AtDynamicInputGroupController ($scope, $compile) {
}
}
components.push({
options: input,
components.push(Object.assign({
element: vm.createElement(input, i)
});
}, input));
});
return components;
@ -83,10 +84,15 @@ function AtDynamicInputGroupController ($scope, $compile) {
vm.insert = components => {
let group = document.createElement('div');
let divider = angular.element(`<div class="at-DynamicInputGroup-divider"></div>`)[0];
components.forEach(component => {
group.appendChild(component.element[0]);
});
for (let i = 0; i < components.length; i++) {
if (i !== 0 && (i % (12 / scope.col)) === 0) {
group.appendChild(divider);
}
group.appendChild(components[i].element[0]);
}
element.appendChild(group);
};

View File

@ -1,4 +1,11 @@
<div class="col-sm-12 at-DynamicInputGroup">
<div class="at-DynamicInputGroup-inset"></div>
<div class="at-DynamicInputGroup-container"></div>
<div ng-show="state.value" class="col-sm-12 at-DynamicInputGroup">
<div class="at-DynamicInputGroup-border"></div>
<div class="at-DynamicInputGroup-inset">
<div class="row">
<div class="col-xs-12">
<h4 class="at-DynamicInputGroup-title"><ng-transclude></ng-transclude></h4>
</div>
</div>
<div class="at-DynamicInputGroup-container"></div>
</div>
</div>

View File

@ -6,7 +6,9 @@ import formAction from './form/action.directive';
import inputLabel from './input/label.directive';
import inputSearch from './input/search.directive';
import inputSelect from './input/select.directive';
import inputSecret from './input/secret.directive';
import inputText from './input/text.directive';
import inputTextarea from './input/textarea.directive';
import panel from './panel/panel.directive';
import panelHeading from './panel/heading.directive';
import panelBody from './panel/body.directive';
@ -14,6 +16,9 @@ import popover from './popover/popover.directive';
import toggleButton from './toggle/button.directive';
import toggleContent from './toggle/content.directive';
import BaseInputController from './input/base.controller';
angular
.module('at.lib.components', [])
.directive('atActionGroup', actionGroup)
@ -23,13 +28,16 @@ angular
.directive('atFormAction', formAction)
.directive('atInputLabel', inputLabel)
.directive('atInputSearch', inputSearch)
.directive('atInputSecret', inputSecret)
.directive('atInputSelect', inputSelect)
.directive('atInputText', inputText)
.directive('atInputTextarea', inputTextarea)
.directive('atPanel', panel)
.directive('atPanelHeading', panelHeading)
.directive('atPanelBody', panelBody)
.directive('atPopover', popover)
.directive('atToggleButton', toggleButton)
.directive('atToggleContent', toggleContent);
.directive('atToggleContent', toggleContent)
.service('BaseInputController', BaseInputController);

View File

@ -19,6 +19,9 @@
}
.at-InputLabel {
}
.at-InputLabel-name {
color: @at-gray-dark-4x;
font-size: @at-font-size-2x;
font-weight: @at-font-weight;

View File

@ -0,0 +1,38 @@
function BaseInputController () {
return function extend (type, scope, form) {
let vm = this;
scope.state = scope.state || {};
scope.state.required = scope.state.required || false;
scope.state.isValid = scope.state.isValid || false;
scope.state.disabled = scope.state.disabled || false;
form.use(type, scope);
vm.validate = () => {
let isValid = true;
if (scope.state.required && !scope.state.value) {
isValid = false;
}
if (scope.state.validate && !scope.state.validate(scope.state.value)) {
isValid = false;
}
return isValid;
};
vm.check = () => {
let isValid = vm.validate();
if (isValid !== scope.state.isValid) {
scope.state.isValid = isValid;
form.check();
}
};
};
}
export default BaseInputController;

View File

@ -1,5 +1,5 @@
<label class="at-InputLabel at-u-flat">
<span ng-if="config.options.required" class="pull-left at-InputLabel-required">*</span>
<span class="pull-left">{{::state.options.label}}</span>
<span ng-if="state.required" class="pull-left at-InputLabel-required">*</span>
<span class="pull-left at-InputLabel-name">{{::state.label}}</span>
<at-popover class="pull-left" state="state"></at-popover>
</label>

View File

@ -0,0 +1,44 @@
function atInputSecretLink (scope, el, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
if (scope.tab === '1') {
el.find('input')[0].focus();
}
inputController.init(scope, formController);
}
function AtInputSecretController (baseInputController) {
let vm = this || {};
vm.init = (scope, form) => {
baseInputController.call(vm, 'input', scope, form);
vm.check();
};
}
AtInputSecretController.$inject = ['BaseInputController'];
function atInputSecret (pathService) {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputSecret'],
templateUrl: pathService.getPartialPath('components/input/secret'),
controller: AtInputSecretController,
controllerAs: 'vm',
link: atInputSecretLink,
scope: {
state: '=',
col: '@',
tab: '@'
}
};
}
atInputSecret.$inject = ['PathService'];
export default atInputSecret;

View File

@ -0,0 +1,10 @@
<div class="col-sm-{{::col}}">
<div class="form-group at-u-flat">
<at-input-label state="state"></at-input-label>
<input type="text" class="form-control at-Input" ng-model="state.value"
ng-attr-maxlength="{{ state.options.max_length || undefined }}"
ng-attr-tabindex="{{ tab || undefined }}"
ng-attr-placeholder="{{::state.placeholder || undefined }}"
ng-change="vm.check()" ng-disabled="state.disabled" />
</div>
</div>

View File

@ -13,27 +13,19 @@ function atInputSelectLink (scope, el, attrs, controllers) {
inputController.init(formController, scope, elements);
}
function AtInputSelectController (eventService) {
function AtInputSelectController (baseInputController, eventService) {
let vm = this || {};
let scope;
let state;
let form;
let input;
let select;
vm.init = (_form_, _scope_, elements) => {
form = _form_;
vm.init = (form, _scope_, elements) => {
baseInputController.call(vm, 'input', _scope_, form);
scope = _scope_;
input = elements.input;
select = elements.select;
scope = _scope_;
state = scope.state || {};
state.required = state.required || false;
state.isValid = state.isValid || false;
state.disabled = state.disabled || false;
form.use('input', scope);
vm.setListeners();
vm.check();
@ -56,32 +48,9 @@ function AtInputSelectController (eventService) {
scope.$on('$destroy', () => eventService.remove(listeners));
};
vm.validate = () => {
let isValid = true;
if (state.required && !state.value) {
isValid = false;
}
if (state.validate && !state.validate(state.value)) {
isValid = false;
}
return isValid;
};
vm.check = () => {
let isValid = vm.validate();
if (isValid !== state.isValid) {
state.isValid = isValid;
form.check();
}
};
}
AtInputSelectController.$inject = ['EventService'];
AtInputSelectController.$inject = ['BaseInputController', 'EventService'];
function atInputSelect (pathService) {
return {

View File

@ -6,54 +6,21 @@ function atInputTextLink (scope, el, attrs, controllers) {
el.find('input')[0].focus();
}
inputController.init(formController, scope);
inputController.init(scope, formController);
}
function AtInputTextController () {
function AtInputTextController (baseInputController) {
let vm = this || {};
let scope;
let state;
let form;
vm.init = (_form_, _scope_) => {
form = _form_;
scope = _scope_;
state = scope.state || {};
state.required = state.required || false;
state.isValid = state.isValid || false;
state.disabled = state.disabled || false;
form.use('input', scope);
vm.init = (scope, form) => {
baseInputController.call(vm, 'input', scope, form);
vm.check();
};
vm.validate = () => {
let isValid = true;
if (state.required && !state.value) {
isValid = false;
}
if (state.validate && !state.validate(state.value)) {
isValid = false;
}
return isValid;
};
vm.check = () => {
let isValid = vm.validate();
if (isValid !== state.isValid) {
state.isValid = isValid;
form.check();
}
};
}
AtInputTextController.$inject = ['BaseInputController'];
function atInputText (pathService) {
return {
restrict: 'E',

View File

@ -0,0 +1,44 @@
function atInputTextareaLink (scope, el, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
if (scope.tab === '1') {
el.find('input')[0].focus();
}
inputController.init(scope, formController);
}
function AtInputTextareaController (baseInputController) {
let vm = this || {};
vm.init = (scope, form) => {
baseInputController.call(vm, 'input', scope, form);
vm.check();
};
}
AtInputTextareaController.$inject = ['BaseInputController'];
function atInputTextarea (pathService) {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputTextarea'],
templateUrl: pathService.getPartialPath('components/input/textarea'),
controller: AtInputTextareaController,
controllerAs: 'vm',
link: atInputTextareaLink,
scope: {
state: '=',
col: '@',
tab: '@'
}
};
}
atInputTextarea.$inject = ['PathService'];
export default atInputTextarea;

View File

@ -0,0 +1,11 @@
<div class="col-sm-{{::col}}">
<div class="form-group at-u-flat">
<at-input-label state="state"></at-input-label>
<textarea class="form-control at-Input" ng-model="state.value"
ng-attr-maxlength="{{ state.options.max_length || undefined }}"
ng-attr-tabindex="{{ tab || undefined }}"
ng-attr-placeholder="{{::state.placeholder || undefined }}"
ng-change="vm.check()" ng-disabled="state.disabled" />
</textarea>
</div>
</div>

View File

@ -20,11 +20,5 @@
}
.at-Panel-headingTitle {
color: @at-gray-dark-4x;
font-size: @at-font-size-3x;
font-weight: @at-font-weight-2x;
line-height: @at-line-height-short;
text-transform: uppercase;
margin: 0;
padding: 0;
.at-mixin-Heading(@at-font-size-3x);
}

View File

@ -19,6 +19,7 @@
border-radius: @at-border-radius;
box-shadow: 0 5px 10px rgba(0,0,0, 0.2);
transition: opacity .15s linear;
font-weight: @at-font-weight
}
.at-Popover-arrow {

View File

@ -1,64 +1,91 @@
let pathService;
function link (scope, el, attr) {
function atPopoverLink (scope, el, attr, controllers) {
let popoverController = controllers[0];
let icon = el[0];
let popover = icon.getElementsByClassName('at-Popover-container')[0];
el.on('click', createDisplayListener(icon, popover));
popoverController.init(icon, popover);
}
function createDisplayListener (icon, popover) {
return event => {
let arrow = popover.getElementsByClassName('at-Popover-arrow')[0];
function AtPopoverController () {
let vm = this;
let iPos = icon.getBoundingClientRect();
let pPos = popover.getBoundingClientRect();
let icon;
let popover;
let wHeight = window.clientHeight;
let pHeight = pPos.height;
vm.init = (_icon_, _popover_) => {
icon = _icon_;
popover = _popover_;
let cx = Math.floor(iPos.left + (iPos.width / 2));
let cy = Math.floor(iPos.top + (iPos.height / 2));
icon.addEventListener('click', vm.createDisplayListener());
};
if (cy < (pHeight / 2)) {
popover.style.top = '10px';
} else {
popover.style.top = (cy - pHeight / 2) + 'px';
}
vm.createDismissListener = (createEvent) => {
return event => {
event.stopPropagation();
popover.style.left = cx + 'px';
vm.open = false;
arrow.style.top = iPos.top + 'px';
arrow.style.left = iPos.left + 20 + 'px';
popover.style.visibility = 'hidden';
popover.style.opacity = 0;
popover.style.visibility = 'visible';
popover.style.opacity = 1;
window.removeEventListener('click', vm.dismissListener);
window.removeEventListener('resize', vm.dismissListener);
};
};
let dismissListener = createDismissListener(popover);
vm.createDisplayListener = () => {
return event => {
if (vm.open) {
return;
}
window.addEventListener('mousedown', dismissListener);
window.addEventListener('resize', dismissListener);
event.stopPropagation();
vm.open = true;
let arrow = popover.getElementsByClassName('at-Popover-arrow')[0];
let iPos = icon.getBoundingClientRect();
let pPos = popover.getBoundingClientRect();
let wHeight = window.clientHeight;
let pHeight = pPos.height;
let cx = Math.floor(iPos.left + (iPos.width / 2));
let cy = Math.floor(iPos.top + (iPos.height / 2));
if (cy < (pHeight / 2)) {
popover.style.top = '10px';
} else {
popover.style.top = (cy - pHeight / 2) + 'px';
}
popover.style.left = cx + 'px';
arrow.style.top = iPos.top + 'px';
arrow.style.left = iPos.left + 20 + 'px';
popover.style.visibility = 'visible';
popover.style.opacity = 1;
vm.dismissListener = vm.createDismissListener(event);
window.addEventListener('click', vm.dismissListener);
window.addEventListener('resize', vm.dismissListener);
};
};
}
function createDismissListener (popover) {
return function dismissListener () {
popover.style.visibility = 'hidden';
popover.style.opacity = 0;
window.removeEventListener('mousedown', dismissListener);
};
}
function atPopover (_pathService_) {
pathService = _pathService_;
function atPopover (pathService) {
return {
restrict: 'E',
replace: true,
transclude: true,
require: ['atPopover'],
templateUrl: pathService.getPartialPath('components/popover/popover'),
link,
controller: AtPopoverController,
controllerAs: 'vm',
link: atPopoverLink,
scope: {
state: '='
}

View File

@ -1,4 +1,4 @@
<div ng-show="state.options.help_text" class="at-Popover">
<div ng-show="state.help_text" class="at-Popover">
<span class="at-Popover-icon">
<i class="fa fa-question-circle"></i>
</span>
@ -6,6 +6,6 @@
<div class="at-Popover-arrow">
<i class="fa fa-caret-left fa-2x"></i>
</div>
<div class="at-Popover-content">{{::state.options.help_text}}</div>
<div class="at-Popover-content">{{::state.help_text}}</div>
</div>
</div>

View File

@ -10,6 +10,16 @@
}
}
.at-mixin-Heading (@size) {
color: @at-gray-dark-4x;
font-size: @size;
font-weight: @at-font-weight-2x;
line-height: @at-line-height-short;
text-transform: uppercase;
margin: 0;
padding: 0;
}
.at-mixin-Button () {
padding: @at-space-2x @at-space-4x;
}