From f9e7357ab5bcfdcde56d03b5613ccafe65218ef9 Mon Sep 17 00:00:00 2001 From: itburnz Date: Tue, 15 Jul 2014 21:12:25 +0300 Subject: [PATCH] lazyload template, fixed animation positioning. much more stable in some cases --- bower.json | 8 ++- dist/angular-tether.js | 60 ++++++++++------- dist/angular-tether.min.js | 2 +- examples/styles/main.css | 117 +++++++++++++++++++++++++++++++--- src/angular-tether-popover.js | 2 +- src/angular-tether-tooltip.js | 2 +- src/angular-tether.js | 69 ++++++++++++-------- src/styles/main.scss | 1 + src/styles/modal.scss | 80 +++++++++++++++++++++++ 9 files changed, 277 insertions(+), 64 deletions(-) create mode 100644 src/styles/modal.scss diff --git a/bower.json b/bower.json index ef93dcf..d54ce1c 100755 --- a/bower.json +++ b/bower.json @@ -1,15 +1,17 @@ { "name": "angular-tether", - "version": "0.1.4", + "version": "0.1.5", "main": "./dist/angular-tether.js", "repository": { "type": "git", "url": "git://github.com/nissoh/angular-tether.git" }, "dependencies": { - "angular": "~1.2.18", + "angular": "~1.2", "tether": "~0.6.5", - "angular-animate": "~1.2.18", + "angular-animate": "~1.2.18" + }, + "devDependencies": { "angular-ui-ace": "~0.1.1" }, "ignore": [ diff --git a/dist/angular-tether.js b/dist/angular-tether.js index 7204396..1cd3d20 100644 --- a/dist/angular-tether.js +++ b/dist/angular-tether.js @@ -1,4 +1,4 @@ -/*! angular-tether - v0.1.4 - 2014-07-09 */(function (root, factory) {if (typeof define === "function" && define.amd) {define(["tether"], factory);} else if (typeof exports === "object") {module.exports = factory(require("tether"));} else {root.test = factory(root.Tether)};}(this, function(Tether) { angular.module('ngTetherPopover', ['ngTether']).directive('tetherPopover', [ +/*! angular-tether - v0.1.5 - 2014-07-15 */(function (root, factory) {if (typeof define === "function" && define.amd) {define(["tether"], factory);} else if (typeof exports === "object") {module.exports = factory(require("tether"));} else {root.test = factory(root.Tether)};}(this, function(Tether) { angular.module('ngTetherPopover', ['ngTether']).directive('tetherPopover', [ 'Tether', '$parse', 'Utils', @@ -9,7 +9,7 @@ tetherPopover: '=', config: '=' }, - link: function (scope, elem, attrs) { + link: function postLink(scope, elem, attrs) { scope.tetherPopover = new Tether(Utils.extendDeep({ parentScope: scope.$parent, leaveOnBlur: true, @@ -46,7 +46,7 @@ angular.module('ngTetherTooltip', ['ngTether']).directive('tetherTooltip', [ content: '@tetherTooltip', config: '=config' }, - link: function (scope, elem, attrs) { + link: function postLink(scope, elem, attrs) { var tooltip = new Tether(Utils.extendDeep({ template: '
{{ content }}
', parentScope: scope, @@ -92,6 +92,7 @@ angular.module('ngTether', []).factory('Utils', [ ]).factory('Tether', [ '$compile', '$rootScope', + '$log', '$window', '$animate', '$controller', @@ -99,7 +100,7 @@ angular.module('ngTether', []).factory('Utils', [ '$q', '$http', '$templateCache', - function ($compile, $rootScope, $window, $animate, $controller, $timeout, $q, $http, $templateCache) { + function ($compile, $rootScope, $log, $window, $animate, $controller, $timeout, $q, $http, $templateCache) { return function (config) { 'use strict'; if (!(!config.template ^ !config.templateUrl)) { @@ -111,20 +112,20 @@ angular.module('ngTether', []).factory('Utils', [ function attachTether() { tether = new Tether(extend({ element: element[0] }, config.tether)); } - if (config.template) { - var deferred = $q.defer(); - deferred.resolve($templateCache.get(config.template) || config.template); - html = deferred.promise; - } else { - html = $http.get(config.templateUrl, { cache: $templateCache }).then(function (response) { - return response.data; - }); + function templateDfd() { + var template = config.template ? $templateCache.get(config.template) || config.template : $http.get(config.templateUrl, { cache: $templateCache }).then(function (resp) { + return resp.data; + }); + return $q.when(template); } function create(html, locals) { element = angular.element(html.trim()); scope = parentScope.$new(); + // assign locals being passed on enter method. if (locals) { - scope.$locals = locals; + for (var prop in locals) { + scope[prop] = locals[prop]; + } } if (config.controller) { var ctrl = $controller(controller, { $scope: scope }); @@ -133,20 +134,25 @@ angular.module('ngTether', []).factory('Utils', [ scope[controllerAs] = ctrl; } $compile(element)(scope); - scope.$on('$destroy', leave); + scope.$on('$destroy', destroy); + // Hack. html is being compiled asynchronously the dimensions of the element + // would most likley have a different dimensions after compilation $timeout(function () { + if (!element) + return; $animate.enter(element, bodyEl); attachTether(); tether.position(); if (config.leaveOnBlur) { - bodyEl.on('click', leaveOnBlur); + bodyEl.on('click touchend', leaveOnBlur); } - }); + }, 15); } function leaveOnBlur(evt) { var target = evt.target; - if (!element || target === element[0]) + if (!element || target === element[0]) { return; + } while (target.parentElement !== null) { if (target.parentElement == element[0]) { return; @@ -157,28 +163,38 @@ angular.module('ngTether', []).factory('Utils', [ } // Attach tether and add it to the dom function enter(locals) { - html.then(function (html) { + if (element) { + return $log.debug('Tethered instance is already active; now toggling'); + } + templateDfd().then(function (html) { create(html, locals); }); } // Detach the tether and remove it from the dom function leave() { + if (config.leaveOnBlur) { + bodyEl.off('click touchend', leaveOnBlur); + } if (!isActive()) { if (element) { element = null; } return false; } - if (config.leaveOnBlur) { - bodyEl.off('click', leaveOnBlur); - } tether.destroy(); $timeout(function () { element && $animate.leave(element, function () { - element = null; + destroy(); }); }); } + function destroy() { + if (!element) { + return; + } + element.remove(); + element = null; + } function position() { if (element) { $animate.move(element, bodyEl); diff --git a/dist/angular-tether.min.js b/dist/angular-tether.min.js index 89f0a71..a61a3cb 100644 --- a/dist/angular-tether.min.js +++ b/dist/angular-tether.min.js @@ -1 +1 @@ -/*! angular-tether - v0.1.4 - 2014-07-09 */!function(a,b){"function"==typeof define&&define.amd?define(["tether"],b):"object"==typeof exports?module.exports=b(require("tether")):a.test=b(a.Tether)}(this,function(a){angular.module("ngTetherPopover",["ngTether"]).directive("tetherPopover",["Tether","$parse","Utils",function(a,b,c){return{restrict:"A",scope:{tetherPopover:"=",config:"="},link:function(b,d){b.tetherPopover=new a(c.extendDeep({parentScope:b.$parent,leaveOnBlur:!0,tether:{target:d[0],attachment:"top center",targetAttachment:"bottom center",constraints:[{to:"window",attachment:"together"}]}},b.config)),b.$watch("tetherPopover.config.targetAttachment",function(){b.tetherPopover.isActive()&&b.tetherPopover.position()},!0),b.$watch("tetherPopover.config.attachment",function(){b.tetherPopover.isActive()&&b.tetherPopover.position()},!0)}}}]),angular.module("ngTetherTooltip",["ngTether"]).directive("tetherTooltip",["Tether","Utils",function(a,b){return{scope:{content:"@tetherTooltip",config:"=config"},link:function(c,d){var e=new a(b.extendDeep({template:'
{{ content }}
',parentScope:c,tether:{target:d[0],attachment:"top center",targetAttachment:"bottom center",constraints:[{to:"window",attachment:"together",pin:!0}]}},c.config));d.on("mouseenter",function(){e.enter()}),d.on("mouseleave",function(){e.leave()}),c.$on("$destroy",function(){d.unbind("hover"),d.unbind("mouseleave")})}}}]),angular.module("ngTether",[]).factory("Utils",["$compile",function(){var a={};return a.extendDeep=function(a,b){for(var c in b)c in a?angular.extend(a[c],b[c]):a[c]=b[c];return a},a}]).factory("Tether",["$compile","$rootScope","$window","$animate","$controller","$timeout","$q","$http","$templateCache",function(b,c,d,e,f,g,h,i,j){return function(k){"use strict";function l(){u=new a(y({element:z[0]},k.tether))}function m(a,c){if(z=angular.element(a.trim()),s=x.$new(),c&&(s.$locals=c),k.controller)var d=f(v,{$scope:s});w&&(s[w]=d),b(z)(s),s.$on("$destroy",p),g(function(){e.enter(z,A),l(),u.position(),k.leaveOnBlur&&A.on("click",n)})}function n(a){var b=a.target;if(z&&b!==z[0]){for(;null!==b.parentElement;){if(b.parentElement==z[0])return;b=b.parentElement}return p()}}function o(a){t.then(function(b){m(b,a)})}function p(){return r()?(k.leaveOnBlur&&A.off("click",n),u.destroy(),void g(function(){z&&e.leave(z,function(){z=null})})):(z&&(z=null),!1)}function q(){z&&(e.move(z,A),l())}function r(){return u&&u.enabled}if(!(!k.template^!k.templateUrl))throw new Error("Expected one of either `template` or `templateUrl`");k.tether=k.tether||{};var s,t,u,v=k.controller||angular.noop,w=k.controllerAs,x=k.parentScope||c,y=angular.extend,z=null,A=angular.element(d.document.body);if(k.template){var B=h.defer();B.resolve(j.get(k.template)||k.template),t=B.promise}else t=i.get(k.templateUrl,{cache:j}).then(function(a){return a.data});return{enter:o,leave:p,position:q,isActive:r,tether:t,config:k.tether}}}])}); \ No newline at end of file +/*! angular-tether - v0.1.5 - 2014-07-15 */!function(a,b){"function"==typeof define&&define.amd?define(["tether"],b):"object"==typeof exports?module.exports=b(require("tether")):a.test=b(a.Tether)}(this,function(a){angular.module("ngTetherPopover",["ngTether"]).directive("tetherPopover",["Tether","$parse","Utils",function(a,b,c){return{restrict:"A",scope:{tetherPopover:"=",config:"="},link:function(b,d){b.tetherPopover=new a(c.extendDeep({parentScope:b.$parent,leaveOnBlur:!0,tether:{target:d[0],attachment:"top center",targetAttachment:"bottom center",constraints:[{to:"window",attachment:"together"}]}},b.config)),b.$watch("tetherPopover.config.targetAttachment",function(){b.tetherPopover.isActive()&&b.tetherPopover.position()},!0),b.$watch("tetherPopover.config.attachment",function(){b.tetherPopover.isActive()&&b.tetherPopover.position()},!0)}}}]),angular.module("ngTetherTooltip",["ngTether"]).directive("tetherTooltip",["Tether","Utils",function(a,b){return{scope:{content:"@tetherTooltip",config:"=config"},link:function(c,d){var e=new a(b.extendDeep({template:'
{{ content }}
',parentScope:c,tether:{target:d[0],attachment:"top center",targetAttachment:"bottom center",constraints:[{to:"window",attachment:"together",pin:!0}]}},c.config));d.on("mouseenter",function(){e.enter()}),d.on("mouseleave",function(){e.leave()}),c.$on("$destroy",function(){d.unbind("hover"),d.unbind("mouseleave")})}}}]),angular.module("ngTether",[]).factory("Utils",["$compile",function(){var a={};return a.extendDeep=function(a,b){for(var c in b)c in a?angular.extend(a[c],b[c]):a[c]=b[c];return a},a}]).factory("Tether",["$compile","$rootScope","$log","$window","$animate","$controller","$timeout","$q","$http","$templateCache",function(b,c,d,e,f,g,h,i,j,k){return function(l){"use strict";function m(){x=new a(B({element:C[0]},l.tether))}function n(){var a=l.template?k.get(l.template)||l.template:j.get(l.templateUrl,{cache:k}).then(function(a){return a.data});return i.when(a)}function o(a,c){if(C=angular.element(a.trim()),v=A.$new(),c)for(var d in c)v[d]=c[d];if(l.controller)var e=g(y,{$scope:v});z&&(v[z]=e),b(C)(v),v.$on("$destroy",s),h(function(){C&&(f.enter(C,D),m(),x.position(),l.leaveOnBlur&&D.on("click touchend",p))},15)}function p(a){var b=a.target;if(C&&b!==C[0]){for(;null!==b.parentElement;){if(b.parentElement==C[0])return;b=b.parentElement}return r()}}function q(a){return C?d.debug("Tethered instance is already active; now toggling"):void n().then(function(b){o(b,a)})}function r(){return l.leaveOnBlur&&D.off("click touchend",p),u()?(x.destroy(),void h(function(){C&&f.leave(C,function(){s()})})):(C&&(C=null),!1)}function s(){C&&(C.remove(),C=null)}function t(){C&&(f.move(C,D),m())}function u(){return x&&x.enabled}if(!(!l.template^!l.templateUrl))throw new Error("Expected one of either `template` or `templateUrl`");l.tether=l.tether||{};var v,w,x,y=l.controller||angular.noop,z=l.controllerAs,A=l.parentScope||c,B=angular.extend,C=null,D=angular.element(e.document.body);return{enter:q,leave:r,position:t,isActive:u,tether:w,config:l.tether}}}])}); \ No newline at end of file diff --git a/examples/styles/main.css b/examples/styles/main.css index 72d456d..fa8bbc8 100644 --- a/examples/styles/main.css +++ b/examples/styles/main.css @@ -632,11 +632,11 @@ blockquote { display: inline-block; } .field { - display: inline-block; + display: block; color: #5d5d5d; vertical-align: middle; - padding-left: 0.5em; - padding-right: 0.5em; } + padding-left: 1em; + padding-right: 1em; } [contenteditable] { display: block; } @@ -1266,6 +1266,8 @@ blockquote { padding-right: 1em; padding-left: 1em; background-color: transparent; } + .btn-ghost:focus { + outline: 0; } @@ -1284,6 +1286,110 @@ blockquote { +body { + margin: 0; + font-family: 'Open Sans', sans-serif; + font-size: 12px; + line-height: 1.3; + font-weight: 300; + color: #777777; + min-height: 100%; + overflow-x: hidden; + overflow-y: scroll; } + +a { + color: #208bf4; + text-decoration: none; } + a:hover { + color: #0fbcf1; + text-decoration: none; } + +p a:hover { + color: #0fbcf1; + text-decoration: underline; } + +::-webkit-scrollbar { + border: 1px solid #E6E6E6; + width: 8px; + height: 8px; + padding: 0 2px; + background-color: #F1F1F1; } + +::-webkit-scrollbar-track { + padding: 10px; + border-radius: 10px; } + +::-webkit-scrollbar-thumb { + background-color: rgba(146, 146, 146, 0.8); } + ::-webkit-scrollbar-thumb:hover { + background-color: rgba(82, 82, 82, 0.8); } + +html { + height: 100% !important; } + +.modal-wrapper { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1000; + zoom: 1; + padding: 10px; + background: rgba(255, 255, 255, 0.85); + overflow-y: auto; + -webkit-backface-visibility: hidden; } + .modal-wrapper + .modal-wrapper { + z-index: 1010; } + .modal-wrapper + .modal-wrapper + .modal-wrapper { + z-index: 1020; } + .modal-wrapper + .modal-wrapper + .modal-wrapper + .modal-wrapper { + z-index: 1030; } + .modal-wrapper.animate .modal-container { + -webkit-animation: slideUp 0.35s ease-in forwards; + -moz-animation: slideUp 0.35s ease-in forwards; + animation: slideUp 0.35s ease-in forwards; } + .modal-wrapper .cc .ccc { + max-width: 100%; } + +.modal-container { + position: relative; + outline: none; + background-color: #ffffff; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + min-width: 250px; + box-shadow: 0px 0px 0px 1px lightgrey; } + +.modal-header { + padding: 3px 13px; + background-color: #e3e3e3; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#dcdcdc), to(#e3e3e3)); + background-image: -webkit-linear-gradient(top, #dcdcdc, #e3e3e3); + background-image: -moz-linear-gradient(top, #dcdcdc, #e3e3e3); + background-image: linear-gradient(to bottom, #dcdcdc, #e3e3e3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFDCDCDC', endColorstr='#FFE3E3E3', GradientType=0); + background-color: #E2E2E2; } + .modal-header h3 { + margin: 0; + line-height: 30px; } + +.modal-body { + padding: 15px; + position: relative; } + +.modal-footer { + position: relative; + padding: 13px 15px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 0; + text-align: right; + border-top: 1px solid #d1d1d1; } + body { margin: 0; font-family: 'Open Sans', sans-serif; @@ -1472,9 +1578,6 @@ html { padding-left: 50px; } .btn-ghost { - line-height: 26px; - padding-right: 1em; - padding-left: 1em; color: #888888; background-color: #DCDCDC; box-shadow: 0px -1px 0 0 rgba(255, 255, 255, 0.5), 0px -1px 0 0 rgba(0, 0, 0, 0.1) inset; @@ -1492,8 +1595,6 @@ html { cursor: default; } .btn-ghost.active { background-color: #d1d1d1; } - .btn-ghost:focus { - outline: 0; } .field { padding-top: 5px; diff --git a/src/angular-tether-popover.js b/src/angular-tether-popover.js index 4f5d8f8..15286c6 100644 --- a/src/angular-tether-popover.js +++ b/src/angular-tether-popover.js @@ -7,7 +7,7 @@ angular.module('ngTetherPopover', ['ngTether']) tetherPopover: '=', config: '=' }, - link: function (scope, elem, attrs) { + link: function postLink(scope, elem, attrs) { scope.tetherPopover = new Tether(Utils.extendDeep({ parentScope: scope.$parent, diff --git a/src/angular-tether-tooltip.js b/src/angular-tether-tooltip.js index de282a9..dc09023 100644 --- a/src/angular-tether-tooltip.js +++ b/src/angular-tether-tooltip.js @@ -5,7 +5,7 @@ angular.module('ngTetherTooltip', ['ngTether']) content: '@tetherTooltip', config: '=config' }, - link: function (scope, elem, attrs) { + link: function postLink(scope, elem, attrs) { var tooltip = new Tether(Utils.extendDeep({ template: '
{{ content }}
', diff --git a/src/angular-tether.js b/src/angular-tether.js index a2d769b..7c490a6 100644 --- a/src/angular-tether.js +++ b/src/angular-tether.js @@ -14,7 +14,7 @@ angular.module('ngTether', []) return Utils; }) - .factory('Tether', function ($compile, $rootScope, $window, $animate, $controller, $timeout, $q, $http, $templateCache) { + .factory('Tether', function ($compile, $rootScope, $log, $window, $animate, $controller, $timeout, $q, $http, $templateCache) { return function (config) { 'use strict'; @@ -41,24 +41,23 @@ angular.module('ngTether', []) }, config.tether)); } - if (config.template) { - var deferred = $q.defer(); - deferred.resolve($templateCache.get(config.template) || config.template); - html = deferred.promise; - } else { - html = $http.get(config.templateUrl, { - cache: $templateCache - }).then(function (response) { - return response.data; - }); + function templateDfd() { + var template = config.template + ? $templateCache.get(config.template) || config.template + : $http.get(config.templateUrl, {cache: $templateCache}).then(function(resp){return resp.data}); + + return $q.when(template); } function create(html, locals) { element = angular.element(html.trim()); - scope = parentScope.$new(); + + // assign locals being passed on enter method. if (locals) { - scope.$locals = locals; + for (var prop in locals) { + scope[prop] = locals[prop]; + } } if (config.controller) { @@ -67,24 +66,30 @@ angular.module('ngTether', []) if (controllerAs) { scope[controllerAs] = ctrl; } + $compile(element)(scope); - scope.$on('$destroy', leave); + scope.$on('$destroy', destroy); + // Hack. html is being compiled asynchronously the dimensions of the element + // would most likley have a different dimensions after compilation + $timeout(function () { + if(!element) return; - $timeout(function(){ $animate.enter(element, bodyEl); + attachTether(); tether.position(); if (config.leaveOnBlur) { - bodyEl.on('click', leaveOnBlur); + bodyEl.on('click touchend', leaveOnBlur); } - }) + + }, 15) } function leaveOnBlur(evt) { var target = evt.target; - if (!element || target === element[0]) return; + if (!element || target === element[0]) {return;} while (target.parentElement !== null) { if (target.parentElement == element[0]) { return @@ -96,33 +101,41 @@ angular.module('ngTether', []) // Attach tether and add it to the dom function enter(locals) { - html.then(function (html) { + if(element) { + return $log.debug('Tethered instance is already active; now toggling') + } + templateDfd().then(function (html) { create(html, locals); }); } // Detach the tether and remove it from the dom function leave() { + if (config.leaveOnBlur) { + bodyEl.off('click touchend', leaveOnBlur); + } + if (!isActive()) { - if(element) { + if (element) { element = null; } return false; } - - if (config.leaveOnBlur) { - bodyEl.off('click', leaveOnBlur); - } tether.destroy(); - $timeout(function(){ - element && $animate.leave(element, function(){ - element = null; + $timeout(function () { + element && $animate.leave(element, function () { + destroy() }); - }); } + function destroy() { + if (!element) {return;} + element.remove(); + element = null; + } + function position() { if (element) { $animate.move(element, bodyEl); diff --git a/src/styles/main.scss b/src/styles/main.scss index 10e6068..6498a3c 100755 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -5,6 +5,7 @@ */ @import "strings/defaults"; +@import "modal"; // Scaffolding diff --git a/src/styles/modal.scss b/src/styles/modal.scss new file mode 100644 index 0000000..54752ba --- /dev/null +++ b/src/styles/modal.scss @@ -0,0 +1,80 @@ +// +// Modals +// -------------------------------------------------- + +// Background +.modal-wrapper { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1000; + zoom: 1; + padding: 10px; + + background: rgba(255, 255, 255, 0.85); + + // incase of an overflow a scroll will appear + overflow-y: auto; + + -webkit-backface-visibility: hidden; + + // Increase z-index after the next modal + + .modal-wrapper { + z-index: 1010; + // and the next after that + + .modal-wrapper { + z-index: 1020; + + .modal-wrapper { + z-index: 1030; + } + } + } + &.animate .modal-container { + @include animation(slideUp .35s ease-in forwards); + } + .cc { + .ccc { + max-width: 100%; + } + } +} + +// Base modal +.modal-container { + position: relative; + outline: none; + background-color: $white; + @include background-clip(padding-box); + min-width: 250px; + box-shadow: 0px 0px 0px 1px rgb(211, 211, 211); + +} + +.modal-header { + padding: 3px 13px; + @include gradient-vertical(#dcdcdc, #e3e3e3); + background-color: #E2E2E2; + // Heading + h3 { + margin: 0; + line-height: 30px; + } +} + +// Body (where all modal content resides) +.modal-body { + padding: 15px; + position: relative; +} + +// Footer (for actions) +.modal-footer { + position: relative; + padding: 13px 15px; + @include box-sizing(border-box); + margin-bottom: 0; + text-align: #{htmlDirection-rev()}; + border-top: 1px solid #d1d1d1; +}