diff options
Diffstat (limited to 'catalog-ui/app/scripts/directives/utils/sdc_messages')
4 files changed, 435 insertions, 0 deletions
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts new file mode 100644 index 0000000000..d41ef1ce04 --- /dev/null +++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts @@ -0,0 +1,179 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// <reference path="../../../references"/> +module Sdc.Directives { + 'use strict'; + export interface ISdcMessageScope extends ng.IScope { + sdcTranslate: string; + sdcTranslateValues:string; + sdcAlign:string; + } + + export class SdcMessageDirective implements ng.IDirective { + + constructor(private $animate:any, private $filter:any, private $parse:any) { + } + + scope = { + field: '=', + required: '@', + pattern: '@', + sdcTranslate: '@', + sdcTranslateValues: '@', + sdcAlign: '@' + }; + + public terminal = true; + public restrict = 'A'; + public transclude = 'element'; + public require = '^^sdcMessages'; + + link = (scope:ISdcMessageScope, $element:any, $attrs:any,sdcMessagesCtrl:any, $transclude:any) => { + let self = this; + + let commentNode = $element[0]; + + let records; + let staticExp = $attrs.sdcMessage || $attrs.when; + let dynamicExp = $attrs.sdcMessageExp || $attrs.whenExp; + let assignRecords = function(items) { + records = items + ? (angular.isArray(items) + ? items + : items.split(/[\s,]+/)) + : null; + sdcMessagesCtrl.reRender(); + }; + + if (dynamicExp) { + assignRecords(scope.$eval(dynamicExp)); + scope.$watchCollection(dynamicExp, assignRecords); + } else { + assignRecords(staticExp); + } + + let currentElement, messageCtrl; + sdcMessagesCtrl.register(commentNode, messageCtrl = { + test: function (name) { + return self.contains(records, name); + }, + attach: function () { + if (!currentElement) { + $transclude(scope, function (elm) { + + self.$animate.enter(elm, null, $element); + currentElement = elm; + + elm.addClass("i-sdc-form-item-error-message"); + + //$compile + let text; + if (scope.sdcTranslate) { + text = self.$filter('translate')(scope.sdcTranslate, scope.sdcTranslateValues); + } else { + //TODO: Need to handle this + //let t = elm.html(); + //let t = angular.element("<span>" + elm.html() + "</span>"); + //text = self.$parse(t); + } + + //scope.sdcTranslateValues + elm.html(text); + + elm.prepend("<span class='error'></span>"); + + // Adding OK to close the message + //let okElm = $('<span />').attr('class', 'ok').html('OK'); + //okElm.click(function(e){ + // messageCtrl.detach(); + //}); + //elm.append(okElm); + + // Handle the position + if (scope.sdcAlign){ + let choosenElm = $(scope.sdcAlign); + if (choosenElm.length > 0) { + let height1 = choosenElm.outerHeight(); + let elmHeight1 = elm.outerHeight(); + elm.css('left', choosenElm.outerWidth()); + elm.css('bottom', (height1 - (elmHeight1 / 2)) / 2); + } + } else { + // Set the position of the error, according to the input element. + let inputElm = elm.parent().siblings('input'); + let textareaElm = elm.parent().siblings('textarea'); + let selectElm = elm.parent().siblings('select'); + if (inputElm.length > 0) { + elm.css('left', inputElm.outerWidth()); + elm.css('top', inputElm.position().top); + } else if (textareaElm.length > 0) { + elm.css('left', textareaElm.outerWidth()); + let height = textareaElm.outerHeight(); + let elmHeight = elm.outerHeight(); + //let top = textareaElm.position().top; + elm.css('bottom', (height - (elmHeight / 2)) / 2); + } else if (selectElm.length > 0) { + elm.css('left', selectElm.outerWidth()); + elm.css('top', selectElm.position().top); + } + } + + // Each time we attach this node to a message we get a new id that we can match + // when we are destroying the node later. + let $$attachId = currentElement.$$attachId = sdcMessagesCtrl.getAttachId(); + + // in the event that the parent element is destroyed + // by any other structural directive then it's time + // to deregister the message from the controller + currentElement.on('$destroy', function () { + if (currentElement && currentElement.$$attachId === $$attachId) { + sdcMessagesCtrl.deregister(commentNode); + messageCtrl.detach(); + } + }); + }); + } + }, + detach: function () { + if (currentElement) { + let elm = currentElement; + currentElement = null; + self.$animate.leave(elm); + } + } + }); + } + + contains = (collection, key):any => { + if (collection) { + return angular.isArray(collection) + ? collection.indexOf(key) >= 0 + : collection.hasOwnProperty(key); + } + } + + public static factory = ($animate:any, $filter:any, $parse:any)=> { + return new SdcMessageDirective($animate, $filter, $parse); + }; + + } + + SdcMessageDirective.factory.$inject = ['$animate', '$filter', '$parse']; +} diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less new file mode 100644 index 0000000000..d8dfdbb73b --- /dev/null +++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less @@ -0,0 +1,10 @@ +.ellipsis-directive-more-less { + .a_9; + .bold; + .hand; + float: right; + margin-right: 17px; + line-height: 23px; + text-decoration: underline; + text-align: left; +} diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts new file mode 100644 index 0000000000..f8b435b1fa --- /dev/null +++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts @@ -0,0 +1,245 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// <reference path="../../../references"/> +module Sdc.Directives { + 'use strict'; + export interface ISdcMessagesScope extends ng.IScope { + sdcMessages: any; + editForm:ng.IFormController; + } + + export class SdcMessagesDirective implements ng.IDirective { + + constructor() {} + + scope = { + sdcMessages: '=' + }; + + public restrict = 'AE'; + public require = 'sdcMessages'; + public controller = SdcMessagesController; + + /*template = ():string => { + return this.$templateCache.get('/app/scripts/directives/utils/sdc-messages/sdc-messages.html'); + }; + + public static factory = ($templateCache:ng.ITemplateCacheService)=> { + return new SdcMessagesDirective($templateCache); + };*/ + + public static factory = ()=> { + return new SdcMessagesDirective(); + } + + } + + export class SdcMessagesController { + + messages:any; + getAttachId:Function; + render:any; + reRender:Function; + register:Function; + deregister:Function; + head:any; + + static '$inject' = [ + '$element', + '$scope', + '$attrs', + '$animate' + ]; + + constructor(private $element:JQuery, + private $scope:ISdcMessagesScope, + private $attrs:ng.IAttributes, + private $animate:any + ) { + + this.init(); + + } + + init=():void => { + let self = this; + + let ACTIVE_CLASS:string = 'ng-active'; + let INACTIVE_CLASS:string = 'ng-inactive'; + + let ctrl = this; + let latestKey = 0; + let nextAttachId = 0; + + this.getAttachId = function getAttachId() { return nextAttachId++; }; + + let messages = this.messages = {}; + let renderLater, cachedCollection; + + this.render = function(collection) { + collection = collection || {}; + + renderLater = false; + cachedCollection = collection; + + // this is true if the attribute is empty or if the attribute value is truthy + let multiple = self.isAttrTruthy(self.$scope, self.$attrs['sdcMessagesMultiple']) || self.isAttrTruthy(self.$scope, self.$attrs['multiple']); + + let unmatchedMessages = []; + let matchedKeys = {}; + let messageItem = ctrl.head; + let messageFound = false; + let totalMessages = 0; + + // we use != instead of !== to allow for both undefined and null values + while (messageItem != null) { + totalMessages++; + let messageCtrl = messageItem.message; + + let messageUsed = false; + if (!messageFound) { + _.each(collection, function(value, key) { + if (!messageUsed && self.truthy(value) && messageCtrl.test(key)) { + // this is to prevent the same error name from showing up twice + if (matchedKeys[key]) return; + matchedKeys[key] = true; + + messageUsed = true; + messageCtrl.attach(); + } + }); + } + + if (messageUsed) { + // unless we want to display multiple messages then we should + // set a flag here to avoid displaying the next message in the list + messageFound = !multiple; + } else { + unmatchedMessages.push(messageCtrl); + } + + messageItem = messageItem.next; + } + + _.each(unmatchedMessages, function(messageCtrl) { + messageCtrl.detach(); + }); + + unmatchedMessages.length !== totalMessages + ? ctrl.$animate.setClass(self.$element, ACTIVE_CLASS, INACTIVE_CLASS) + : ctrl.$animate.setClass(self.$element, INACTIVE_CLASS, ACTIVE_CLASS); + }; + + self.$scope.$watchCollection('sdcMessages' || self.$attrs['for'], function(newVal:any, oldVal:any){ + ctrl.render(newVal); + }); + + this.reRender = function() { + if (!renderLater) { + renderLater = true; + self.$scope.$evalAsync(function() { + if (renderLater) { + cachedCollection && ctrl.render(cachedCollection); + } + }); + } + }; + + this.register = function(comment, messageCtrl) { + let nextKey = latestKey.toString(); + messages[nextKey] = { + message: messageCtrl + }; + insertMessageNode(self.$element[0], comment, nextKey); + comment.$$sdcMessageNode = nextKey; + latestKey++; + + ctrl.reRender(); + }; + + this.deregister = function(comment) { + let key = comment.$$sdcMessageNode; + delete comment.$$sdcMessageNode; + removeMessageNode(self.$element[0], comment, key); + delete messages[key]; + ctrl.reRender(); + }; + + function findPreviousMessage(parent, comment) { + let prevNode = comment; + let parentLookup = []; + while (prevNode && prevNode !== parent) { + let prevKey = prevNode.$$sdcMessageNode; + if (prevKey && prevKey.length) { + return messages[prevKey]; + } + + // dive deeper into the DOM and examine its children for any sdcMessage + // comments that may be in an element that appears deeper in the list + if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) { + parentLookup.push(prevNode); + prevNode = prevNode.childNodes[prevNode.childNodes.length - 1]; + } else { + prevNode = prevNode.previousSibling || prevNode.parentNode; + } + } + } + + function insertMessageNode(parent, comment, key) { + let messageNode = messages[key]; + if (!ctrl.head) { + ctrl.head = messageNode; + } else { + let match = findPreviousMessage(parent, comment); + if (match) { + messageNode.next = match.next; + match.next = messageNode; + } else { + messageNode.next = ctrl.head; + ctrl.head = messageNode; + } + } + } + + function removeMessageNode(parent, comment, key) { + let messageNode = messages[key]; + + let match = findPreviousMessage(parent, comment); + if (match) { + match.next = messageNode.next; + } else { + ctrl.head = messageNode.next; + } + } + } + + isAttrTruthy = (scope, attr):any => { + return (angular.isString(attr) && attr.length === 0) || //empty attribute + this.truthy(scope.$eval(attr)); + } + + truthy = (val):any => { + return angular.isString(val) ? val.length : !!val; + } + + } + + SdcMessagesDirective.factory.$inject = ['$templateCache','$animate']; +} diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html new file mode 100644 index 0000000000..09b1cad4d2 --- /dev/null +++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html @@ -0,0 +1 @@ +<span>aaa</span> |