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-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-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-action-group col="12" pos="right">
<at-form-action type="cancel"></at-form-action> <at-form-action type="cancel"></at-form-action>

View File

@@ -1,12 +1,27 @@
.at-DynamicInputGroup { .at-DynamicInputGroup {
padding: 0; padding: 0;
margin: 0;
margin: @at-space-6x 0 0 0; margin: @at-space-6x 0 0 0;
} }
.at-DynamicInputGroup-inset { .at-DynamicInputGroup-border {
position: absolute; position: absolute;
width: @at-inset-width; width: @at-inset-width;
height: 100%; height: 100%;
background: @at-gray; background: @at-gray;
left: -@at-inset-width; 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) => { inputs.forEach((input, i) => {
if (input.type === 'string') { 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'; input.component = 'at-input-secret';
} else if (input.multiline) { } else if (input.multiline) {
input.component = 'at-input-textarea'; input.component = 'at-input-textarea';
@@ -61,10 +63,9 @@ function AtDynamicInputGroupController ($scope, $compile) {
} }
} }
components.push({ components.push(Object.assign({
options: input,
element: vm.createElement(input, i) element: vm.createElement(input, i)
}); }, input));
}); });
return components; return components;
@@ -83,10 +84,15 @@ function AtDynamicInputGroupController ($scope, $compile) {
vm.insert = components => { vm.insert = components => {
let group = document.createElement('div'); let group = document.createElement('div');
let divider = angular.element(`<div class="at-DynamicInputGroup-divider"></div>`)[0];
components.forEach(component => { for (let i = 0; i < components.length; i++) {
group.appendChild(component.element[0]); if (i !== 0 && (i % (12 / scope.col)) === 0) {
}); group.appendChild(divider);
}
group.appendChild(components[i].element[0]);
}
element.appendChild(group); element.appendChild(group);
}; };

View File

@@ -1,4 +1,11 @@
<div class="col-sm-12 at-DynamicInputGroup"> <div ng-show="state.value" class="col-sm-12 at-DynamicInputGroup">
<div class="at-DynamicInputGroup-inset"></div> <div class="at-DynamicInputGroup-border"></div>
<div class="at-DynamicInputGroup-container"></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> </div>

View File

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

View File

@@ -19,6 +19,9 @@
} }
.at-InputLabel { .at-InputLabel {
}
.at-InputLabel-name {
color: @at-gray-dark-4x; color: @at-gray-dark-4x;
font-size: @at-font-size-2x; font-size: @at-font-size-2x;
font-weight: @at-font-weight; 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"> <label class="at-InputLabel at-u-flat">
<span ng-if="config.options.required" class="pull-left at-InputLabel-required">*</span> <span ng-if="state.required" class="pull-left at-InputLabel-required">*</span>
<span class="pull-left">{{::state.options.label}}</span> <span class="pull-left at-InputLabel-name">{{::state.label}}</span>
<at-popover class="pull-left" state="state"></at-popover> <at-popover class="pull-left" state="state"></at-popover>
</label> </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); inputController.init(formController, scope, elements);
} }
function AtInputSelectController (eventService) { function AtInputSelectController (baseInputController, eventService) {
let vm = this || {}; let vm = this || {};
let scope; let scope;
let state;
let form;
let input; let input;
let select; let select;
vm.init = (_form_, _scope_, elements) => { vm.init = (form, _scope_, elements) => {
form = _form_; baseInputController.call(vm, 'input', _scope_, form);
scope = _scope_;
input = elements.input; input = elements.input;
select = elements.select; 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.setListeners();
vm.check(); vm.check();
@@ -56,32 +48,9 @@ function AtInputSelectController (eventService) {
scope.$on('$destroy', () => eventService.remove(listeners)); 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) { function atInputSelect (pathService) {
return { return {

View File

@@ -6,54 +6,21 @@ function atInputTextLink (scope, el, attrs, controllers) {
el.find('input')[0].focus(); el.find('input')[0].focus();
} }
inputController.init(formController, scope); inputController.init(scope, formController);
} }
function AtInputTextController () { function AtInputTextController (baseInputController) {
let vm = this || {}; let vm = this || {};
let scope; vm.init = (scope, form) => {
let state; baseInputController.call(vm, 'input', scope, form);
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.check(); 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) { function atInputText (pathService) {
return { return {
restrict: 'E', 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 { .at-Panel-headingTitle {
color: @at-gray-dark-4x; .at-mixin-Heading(@at-font-size-3x);
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;
} }

View File

@@ -19,6 +19,7 @@
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
} }
.at-Popover-arrow { .at-Popover-arrow {

View File

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