From 3328f3eede08e38c51f905f214eadef47f2e3681 Mon Sep 17 00:00:00 2001 From: Gwen Date: Thu, 23 Jul 2015 18:57:28 +0200 Subject: [PATCH 01/16] fix input focus / scroll issue --- components/ionKeyboard/ionFocus.js | 46 +++++++++++++++++++++++++++ components/ionKeyboard/ionKeyboard.js | 21 ++---------- package.js | 10 +++++- 3 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 components/ionKeyboard/ionFocus.js diff --git a/components/ionKeyboard/ionFocus.js b/components/ionKeyboard/ionFocus.js new file mode 100644 index 0000000..f4bad11 --- /dev/null +++ b/components/ionKeyboard/ionFocus.js @@ -0,0 +1,46 @@ +Meteor.startup(function() { + if (Meteor.isCordova) { + + // Scroll to make input on top of the page + // #TODO Correct behavior should be: if the input is behind the keyboard, scroll to make it visible on top of the keyboard + var scrollToFocusedElement = function($focused) { + $focused = $focused || $(':focus'); + var $container = $($focused.parents('.content.overflow-scroll').get(0)); + if (!$focused.length || !$container.length) return; + var contentOffset = $container.offset().top; + var padding = 10; + var scrollTo = $container.scrollTop() + $focused.offset().top - contentOffset - padding; + setTimeout(function() { + $container.animate({ scrollTop: scrollTo }, 400, function() { + // #TODO fix floating input cursor bug (https://github.com/twbs/bootstrap/issues/14708, https://github.com/cubiq/iscroll/issues/178) + var display = $focused.css('display'); + $focused.css({ display: 'none' }).css({ display: display }); + }); + // $container.scrollTop(scrollTo); + }, 0); + } + + var scrollPosStart, scrollPosEnd, scrollDistance, scrollHappened, scrollThreshold = 10; + + // Fix for input not getting focused on long touch (more than a few ms) + $(document).on('touchstart', function(event) { + scrollPosStart = $('.content.overflow-scroll').scrollTop(); + }); + + $(document).on('touchend', function(event) { + scrollPosEnd = $('.content.overflow-scroll').scrollTop(); + scrollDistance = Math.abs(scrollPosStart - scrollPosEnd); + scrollHappened = scrollDistance > scrollThreshold; + var $target = $(event.target); + var isInput = _.contains(['INPUT', 'TEXTAREA'], event.target.tagName); + var isFocused = $target.is(':focus'); + if (!isInput || isFocused || scrollHappened) return; + $target.focus(); + }); + + $(document).delegate('input, textarea', 'focus', function(event) { + scrollToFocusedElement($(event.currentTarget)); + }); + + } +}); diff --git a/components/ionKeyboard/ionKeyboard.js b/components/ionKeyboard/ionKeyboard.js index 9de86ea..23ae409 100644 --- a/components/ionKeyboard/ionKeyboard.js +++ b/components/ionKeyboard/ionKeyboard.js @@ -66,26 +66,8 @@ window.addEventListener('native.keyboardshow', function (event) { }); -Meteor.startup(function() { - if (Meteor.isCordova) { - - // Scroll to make input on top of the page - // #TODO Correct behavior should be: if the input is behind the keyboard, scroll to make it visible on top of the keyboard - $(document).delegate('input, textarea', 'touchstart, focus', function(event) { - var $input = $(event.currentTarget); - var $container = $($(event.currentTarget).parents('.content.overflow-scroll').get(0)); - var contentOffset = $container.offset().top; - var padding = 10; - var scrollTo = $container.scrollTop() + $input.offset().top - contentOffset - padding; - setTimeout(function() { - $container.scrollTop(scrollTo); - }, 0); - }); - - } -}); - window.addEventListener('native.keyboardhide', function (event) { + // TODO: Android is having problems if (Platform.isAndroid()) { return; @@ -103,4 +85,5 @@ window.addEventListener('native.keyboardhide', function (event) { $('.content.overflow-scroll').each(function (index, el) { $(el).css({bottom: $(el).data('ionkeyboard.bottom')}); }); + }); diff --git a/package.js b/package.js index 4e54899..2efaab8 100644 --- a/package.js +++ b/package.js @@ -11,7 +11,14 @@ Cordova.depends({ Package.onUse(function(api) { api.versionsFrom("1.0"); - api.use(["templating", "underscore", "fastclick", "iron:router@1.0.0", "tracker", "session"], "client"); + api.use([ + "templating", + "underscore", + "fastclick", + "iron:router@1.0.0", + "tracker", + "session" + ], "client"); api.addFiles([ "vendor/snap.js", @@ -46,6 +53,7 @@ Package.onUse(function(api) { "components/ionItem/ionItem.js", "components/ionKeyboard/ionKeyboard.js", + "components/ionKeyboard/ionFocus.js", "components/ionList/ionList.html", "components/ionList/ionList.js", From f8cc762515a55cdb1d83e9701e3e28bb000a64df Mon Sep 17 00:00:00 2001 From: Gwen Date: Thu, 23 Jul 2015 21:13:56 +0200 Subject: [PATCH 02/16] fix long tapped input not focusing + floating input cursor on scroll --- components/ionKeyboard/ionFocus.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/components/ionKeyboard/ionFocus.js b/components/ionKeyboard/ionFocus.js index f4bad11..c5ba15d 100644 --- a/components/ionKeyboard/ionFocus.js +++ b/components/ionKeyboard/ionFocus.js @@ -1,41 +1,45 @@ Meteor.startup(function() { if (Meteor.isCordova) { + var getScrollContainer = function($element) { + return $($element.parents('.content.overflow-scroll').get(0)); + } + // Scroll to make input on top of the page // #TODO Correct behavior should be: if the input is behind the keyboard, scroll to make it visible on top of the keyboard var scrollToFocusedElement = function($focused) { $focused = $focused || $(':focus'); - var $container = $($focused.parents('.content.overflow-scroll').get(0)); + var $container = getScrollContainer($focused); if (!$focused.length || !$container.length) return; var contentOffset = $container.offset().top; var padding = 10; var scrollTo = $container.scrollTop() + $focused.offset().top - contentOffset - padding; setTimeout(function() { $container.animate({ scrollTop: scrollTo }, 400, function() { - // #TODO fix floating input cursor bug (https://github.com/twbs/bootstrap/issues/14708, https://github.com/cubiq/iscroll/issues/178) + // Fix floating input cursor bug (https://github.com/twbs/bootstrap/issues/14708, https://github.com/cubiq/iscroll/issues/178) var display = $focused.css('display'); $focused.css({ display: 'none' }).css({ display: display }); }); - // $container.scrollTop(scrollTo); }, 0); } - var scrollPosStart, scrollPosEnd, scrollDistance, scrollHappened, scrollThreshold = 10; + var $scrollContainer, scrollPosStart, scrollPosEnd, scrollDistance, scrollHappened, scrollThreshold = 10; - // Fix for input not getting focused on long touch (more than a few ms) + // Trigger focus on input through touchend for long taps $(document).on('touchstart', function(event) { - scrollPosStart = $('.content.overflow-scroll').scrollTop(); + $scrollContainer = getScrollContainer($(event.target)); + scrollPosStart = $scrollContainer.scrollTop(); }); $(document).on('touchend', function(event) { - scrollPosEnd = $('.content.overflow-scroll').scrollTop(); + $scrollContainer = getScrollContainer($(event.target)); + scrollPosEnd = $scrollContainer.scrollTop(); scrollDistance = Math.abs(scrollPosStart - scrollPosEnd); scrollHappened = scrollDistance > scrollThreshold; var $target = $(event.target); var isInput = _.contains(['INPUT', 'TEXTAREA'], event.target.tagName); var isFocused = $target.is(':focus'); - if (!isInput || isFocused || scrollHappened) return; - $target.focus(); + if (isInput && !isFocused && !scrollHappened) $target.focus(); }); $(document).delegate('input, textarea', 'focus', function(event) { From 8ce5e3a010bcac52109cf9d5e4a181cd4e4403b7 Mon Sep 17 00:00:00 2001 From: Gwen Date: Thu, 23 Jul 2015 21:14:33 +0200 Subject: [PATCH 03/16] fix long tapped input not focusing + floating input cursor on scroll --- components/ionKeyboard/{ionFocus.js => ionInputFocus.js} | 0 package.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename components/ionKeyboard/{ionFocus.js => ionInputFocus.js} (100%) diff --git a/components/ionKeyboard/ionFocus.js b/components/ionKeyboard/ionInputFocus.js similarity index 100% rename from components/ionKeyboard/ionFocus.js rename to components/ionKeyboard/ionInputFocus.js diff --git a/package.js b/package.js index 2efaab8..82975ba 100644 --- a/package.js +++ b/package.js @@ -53,7 +53,7 @@ Package.onUse(function(api) { "components/ionItem/ionItem.js", "components/ionKeyboard/ionKeyboard.js", - "components/ionKeyboard/ionFocus.js", + "components/ionKeyboard/ionInputFocus.js", "components/ionList/ionList.html", "components/ionList/ionList.js", From a76b7a5e5d22cac1c2fea9a495e5916ff14f1f1c Mon Sep 17 00:00:00 2001 From: Gwen Date: Tue, 4 Aug 2015 02:38:53 +0200 Subject: [PATCH 04/16] fix modal animations and event handlers bug + cleanup keyboard scroll --- components/ionKeyboard/ionInputFocus.js | 38 +++++++++---- components/ionKeyboard/ionKeyboard.js | 5 +- components/ionModal/ionModal.js | 73 ++++++++++++++----------- package.js | 3 +- 4 files changed, 73 insertions(+), 46 deletions(-) diff --git a/components/ionKeyboard/ionInputFocus.js b/components/ionKeyboard/ionInputFocus.js index c5ba15d..12ea2fb 100644 --- a/components/ionKeyboard/ionInputFocus.js +++ b/components/ionKeyboard/ionInputFocus.js @@ -5,20 +5,38 @@ Meteor.startup(function() { return $($element.parents('.content.overflow-scroll').get(0)); } + var focusPadding = 20; + var isBehindKeyboard = function($focused, keyboardHeight) { + var keyboardTop = $(window).innerHeight() - keyboardHeight; + var focusedBottom = $focused.offset().top + $focused.innerHeight(); + var focusedIsBehindKeyboard = focusedBottom > keyboardTop - focusPadding; + return focusedIsBehindKeyboard; + } + + var getScrollToPosition = function($focused, $container, keyboardHeight) { + + var scrollTo = $container.scrollTop() + $focused.offset().top - $container.offset().top - focusPadding; + return scrollTo; + + } + // Scroll to make input on top of the page // #TODO Correct behavior should be: if the input is behind the keyboard, scroll to make it visible on top of the keyboard - var scrollToFocusedElement = function($focused) { + scrollToFocusedElement = function($focused, keyboardHeight) { $focused = $focused || $(':focus'); var $container = getScrollContainer($focused); if (!$focused.length || !$container.length) return; - var contentOffset = $container.offset().top; - var padding = 10; - var scrollTo = $container.scrollTop() + $focused.offset().top - contentOffset - padding; + var focusedIsBehindKeyboard = isBehindKeyboard($focused, keyboardHeight); + if (!focusedIsBehindKeyboard) return; + var scrollTo = getScrollToPosition($focused, $container, keyboardHeight); setTimeout(function() { - $container.animate({ scrollTop: scrollTo }, 400, function() { - // Fix floating input cursor bug (https://github.com/twbs/bootstrap/issues/14708, https://github.com/cubiq/iscroll/issues/178) - var display = $focused.css('display'); - $focused.css({ display: 'none' }).css({ display: display }); + $container.animate({ scrollTop: scrollTo }, { + duration: 400, + complete: function() { + // Fix floating input cursor bug (https://github.com/twbs/bootstrap/issues/14708, https://github.com/cubiq/iscroll/issues/178) + var display = $focused.css('display'); + $focused.css({ display: 'none' }).css({ display: display }); + } }); }, 0); } @@ -42,9 +60,5 @@ Meteor.startup(function() { if (isInput && !isFocused && !scrollHappened) $target.focus(); }); - $(document).delegate('input, textarea', 'focus', function(event) { - scrollToFocusedElement($(event.currentTarget)); - }); - } }); diff --git a/components/ionKeyboard/ionKeyboard.js b/components/ionKeyboard/ionKeyboard.js index 23ae409..4e65130 100644 --- a/components/ionKeyboard/ionKeyboard.js +++ b/components/ionKeyboard/ionKeyboard.js @@ -64,6 +64,9 @@ window.addEventListener('native.keyboardshow', function (event) { $(el).css({bottom: keyboardHeight}); }); + // Scroll to the focused element + scrollToFocusedElement(null, keyboardHeight); + }); window.addEventListener('native.keyboardhide', function (event) { @@ -73,7 +76,7 @@ window.addEventListener('native.keyboardhide', function (event) { return; } - $('input, textarea').blur(); + // $('input, textarea').blur(); $('body').removeClass('keyboard-open'); // Detach any elements that were attached diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index 5a10c23..ec76dbe 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -7,62 +7,70 @@ IonModal = { leaveActiveClass: 'ng-leave-active', view: {}, views: [], - open: function (templateName, data) { - this.template = Template[templateName]; + this.template = Template[templateName]; var view = Blaze.renderWithData(this.template, data, $('body').get(0)); - if (!this.view[templateName]) { - this.view[templateName] = [view]; - } else { - this.view[templateName].push(view); - } - this.views.push(templateName); + if (!this.view[templateName]) this.view[templateName] = []; + this.view[templateName].push(view); var $modalBackdrop = $(view.firstNode()); + var $modal = $('.modal', $modalBackdrop); if (this.views.length === 1) { $modalBackdrop.addClass('active'); } - var $modalEl = $modalBackdrop.find('.modal').eq(0); - $modalEl.addClass(this.enterClasses.join(' ')); + $modal.addClass(this.enterClasses.join(' ')); + setTimeout(function () { + $modal.addClass(this.enterActiveClass); + }.bind(this), 50); - $modalEl.on(this.transitionEndEvent, function () { - $('body').addClass('modal-open'); - $modalEl.removeClass(this.enterClasses.join(' ')).removeClass(this.enterActiveClass).off('webkitAnimationEnd'); - }.bind(this)); - - Meteor.setTimeout(function () { - $modalEl.addClass(this.enterActiveClass); - }.bind(this), 10); }, - close: function () { - var templateName = this.views.pop(); - var view = this.view[templateName].pop(); - var $modalBackdrop = $(view.firstNode()); - if (!this.views.length) { + var templateName = this.views[this.views.length-1]; + var viewArray = this.view[templateName]; + var view = viewArray[viewArray.length-1]; + + var $modalBackdrop = $(view.firstNode()); + var $modal = $('.modal', $modalBackdrop); + + if (this.views.length === 0) { $modalBackdrop.removeClass('active'); } - var $modalEl = $modalBackdrop.find('.modal').eq(0); - $modalEl.addClass(this.leaveClasses.join(' ')); + $modal.addClass(this.leaveClasses.join(' ')); + setTimeout(function () { + $modal.addClass(this.leaveActiveClass); + }.bind(this), 50); - Meteor.setTimeout(function() { - $modalEl.addClass(this.leaveActiveClass); - }.bind(this), 10); - - $modalEl.on(this.transitionEndEvent, function () { - $('body').removeClass('modal-open'); + setTimeout(function () { + $modal.off(this.transitionEndEvent); + $modalBackdrop.remove(); Blaze.remove(view); - }.bind(this)); + }.bind(this), 500); + } }; +$(document).delegate('.modal', IonModal.transitionEndEvent, function(e) { + var $modal = $(e.target); + if ($modal.hasClass(IonModal.enterClasses.join(' ')) || $modal.hasClass(IonModal.enterActiveClasse)) { + $modal.removeClass(IonModal.enterClasses.join(' ')).removeClass(IonModal.enterActiveClass); + $('body').addClass('modal-open'); + } else { + $modal.removeClass(IonModal.leaveClasses.join(' ')).removeClass(IonModal.leaveActiveClass).off(IonModal.transitionEndEvent); + $('body').removeClass('modal-open'); + $(e.target).parents('.modal-backdrop').remove(); + var templateName = IonModal.views.pop(); + var view = IonModal.view[templateName].pop(); + Blaze.remove(view); + } +}); + Template.ionModal.created = function () { this.data = this.data || {}; this.customTemplate = this.data.customTemplate || false; @@ -139,6 +147,7 @@ Template.ionModal.helpers({ return classes.join(' '); } + }); Template.ionModal.events({ diff --git a/package.js b/package.js index 82975ba..90e49ed 100644 --- a/package.js +++ b/package.js @@ -17,7 +17,8 @@ Package.onUse(function(api) { "fastclick", "iron:router@1.0.0", "tracker", - "session" + "session", + "jquery" ], "client"); api.addFiles([ From 1f50b8f97a730371ffd370d2da775ccf66352b09 Mon Sep 17 00:00:00 2001 From: Gwen Date: Tue, 4 Aug 2015 04:12:09 +0200 Subject: [PATCH 05/16] fix modals --- components/ionModal/ionModal.html | 34 +++++++++++++++---------------- components/ionModal/ionModal.js | 30 +++++++++++---------------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/components/ionModal/ionModal.html b/components/ionModal/ionModal.html index ceae3a4..cf2dbae 100644 --- a/components/ionModal/ionModal.html +++ b/components/ionModal/ionModal.html @@ -1,25 +1,23 @@ diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index ec76dbe..006429c 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -10,10 +10,10 @@ IonModal = { open: function (templateName, data) { this.template = Template[templateName]; - var view = Blaze.renderWithData(this.template, data, $('body').get(0)); - this.views.push(templateName); if (!this.view[templateName]) this.view[templateName] = []; + + var view = Blaze.renderWithData(this.template, data, $('body').get(0)); this.view[templateName].push(view); var $modalBackdrop = $(view.firstNode()); @@ -24,7 +24,7 @@ IonModal = { } $modal.addClass(this.enterClasses.join(' ')); - setTimeout(function () { + Meteor.setTimeout(function () { $modal.addClass(this.enterActiveClass); }.bind(this), 50); @@ -32,41 +32,34 @@ IonModal = { close: function () { var templateName = this.views[this.views.length-1]; - var viewArray = this.view[templateName]; + var viewArray = this.view[templateName] || []; var view = viewArray[viewArray.length-1]; + if (!view) return; var $modalBackdrop = $(view.firstNode()); var $modal = $('.modal', $modalBackdrop); - if (this.views.length === 0) { - $modalBackdrop.removeClass('active'); - } - $modal.addClass(this.leaveClasses.join(' ')); - setTimeout(function () { + Meteor.setTimeout(function () { $modal.addClass(this.leaveActiveClass); }.bind(this), 50); - setTimeout(function () { - $modal.off(this.transitionEndEvent); - $modalBackdrop.remove(); - Blaze.remove(view); - }.bind(this), 500); - } }; $(document).delegate('.modal', IonModal.transitionEndEvent, function(e) { - var $modal = $(e.target); + var $modal = $(e.currentTarget); if ($modal.hasClass(IonModal.enterClasses.join(' ')) || $modal.hasClass(IonModal.enterActiveClasse)) { $modal.removeClass(IonModal.enterClasses.join(' ')).removeClass(IonModal.enterActiveClass); $('body').addClass('modal-open'); } else { + var templateName = IonModal.views.pop(); + var view = IonModal.view[templateName].pop(); + var $modalBackdrop = $(view.firstNode()); + $modalBackdrop.removeClass('active'); $modal.removeClass(IonModal.leaveClasses.join(' ')).removeClass(IonModal.leaveActiveClass).off(IonModal.transitionEndEvent); $('body').removeClass('modal-open'); $(e.target).parents('.modal-backdrop').remove(); - var templateName = IonModal.views.pop(); - var view = IonModal.view[templateName].pop(); Blaze.remove(view); } }); @@ -83,6 +76,7 @@ Template.ionModal.created = function () { Template.ionModal.rendered = function () { if (this.focusFirstInput) { Meteor.setTimeout(function () { + if (!this._domrange) return; this.$('input:first').focus(); }.bind(this), 600); } From 39dae9190e520aeb5eb74a21ecf4d6d30de0b7c0 Mon Sep 17 00:00:00 2001 From: Gwen Date: Tue, 4 Aug 2015 04:12:35 +0200 Subject: [PATCH 06/16] fix modals --- components/ionModal/ionModal.html | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/components/ionModal/ionModal.html b/components/ionModal/ionModal.html index cf2dbae..ceae3a4 100644 --- a/components/ionModal/ionModal.html +++ b/components/ionModal/ionModal.html @@ -1,23 +1,25 @@ From 2c3f79efeed71ac0de45ed118f32976ca8c16247 Mon Sep 17 00:00:00 2001 From: Gwen Date: Tue, 4 Aug 2015 13:54:59 +0200 Subject: [PATCH 07/16] fix modals --- components/ionModal/ionModal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index 006429c..3f0c1fb 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -31,6 +31,7 @@ IonModal = { }, close: function () { + console.log('closing modal.'); var templateName = this.views[this.views.length-1]; var viewArray = this.view[templateName] || []; var view = viewArray[viewArray.length-1]; @@ -52,7 +53,7 @@ $(document).delegate('.modal', IonModal.transitionEndEvent, function(e) { if ($modal.hasClass(IonModal.enterClasses.join(' ')) || $modal.hasClass(IonModal.enterActiveClasse)) { $modal.removeClass(IonModal.enterClasses.join(' ')).removeClass(IonModal.enterActiveClass); $('body').addClass('modal-open'); - } else { + } else if ($modal.hasClass(IonModal.leaveClasses.join(' ')) || $modal.hasClass(IonModal.leaveActiveClasse)) { var templateName = IonModal.views.pop(); var view = IonModal.view[templateName].pop(); var $modalBackdrop = $(view.firstNode()); From 2471a5b0e0cc8bef086d44a9f581a8771030fd40 Mon Sep 17 00:00:00 2001 From: Gwen Date: Tue, 4 Aug 2015 13:56:05 +0200 Subject: [PATCH 08/16] fix modals --- components/ionModal/ionModal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index 3f0c1fb..dbd221b 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -31,7 +31,6 @@ IonModal = { }, close: function () { - console.log('closing modal.'); var templateName = this.views[this.views.length-1]; var viewArray = this.view[templateName] || []; var view = viewArray[viewArray.length-1]; From c81a8d03cf4a183ec49cd1172fec77c6608e84fe Mon Sep 17 00:00:00 2001 From: Gwen Date: Tue, 4 Aug 2015 21:57:53 +0200 Subject: [PATCH 09/16] wrap modal open and close in timeout to prevent rendering impacting perf --- components/ionModal/ionModal.js | 75 ++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index dbd221b..a0f896d 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -9,40 +9,52 @@ IonModal = { views: [], open: function (templateName, data) { - this.template = Template[templateName]; - this.views.push(templateName); - if (!this.view[templateName]) this.view[templateName] = []; - - var view = Blaze.renderWithData(this.template, data, $('body').get(0)); - this.view[templateName].push(view); - - var $modalBackdrop = $(view.firstNode()); - var $modal = $('.modal', $modalBackdrop); - - if (this.views.length === 1) { - $modalBackdrop.addClass('active'); - } - - $modal.addClass(this.enterClasses.join(' ')); Meteor.setTimeout(function () { - $modal.addClass(this.enterActiveClass); - }.bind(this), 50); + + this.template = Template[templateName]; + this.views.push(templateName); + if (!this.view[templateName]) this.view[templateName] = []; + + var view = Blaze.renderWithData(this.template, data, $('body').get(0)); + this.view[templateName].push(view); + + var $modalBackdrop = $(view.firstNode()); + var $modal = $('.modal', $modalBackdrop); + + if (this.views.length === 1) { + $modalBackdrop.addClass('active'); + } + + $modal.addClass(this.enterClasses.join(' ')); + Meteor.setTimeout(function () { + $modal.addClass(this.enterActiveClass); + }.bind(this), 50); + + }.bind(this), 0); }, close: function () { - var templateName = this.views[this.views.length-1]; - var viewArray = this.view[templateName] || []; - var view = viewArray[viewArray.length-1]; - if (!view) return; - - var $modalBackdrop = $(view.firstNode()); - var $modal = $('.modal', $modalBackdrop); - - $modal.addClass(this.leaveClasses.join(' ')); Meteor.setTimeout(function () { - $modal.addClass(this.leaveActiveClass); - }.bind(this), 50); + + var templateName = this.views[this.views.length-1]; + var viewArray = this.view[templateName] || []; + var view = viewArray[viewArray.length-1]; + if (!view) return; + + var $modalBackdrop = $(view.firstNode()); + var $modal = $('.modal', $modalBackdrop); + + $modal.addClass(this.leaveClasses.join(' ')); + Meteor.setTimeout(function () { + $modal.addClass(this.leaveActiveClass); + }.bind(this), 50); + + $modalBackdrop.fadeOut(500, function() { + $('body').removeClass('modal-open'); + }); + + }.bind(this), 0); } }; @@ -80,7 +92,6 @@ Template.ionModal.rendered = function () { this.$('input:first').focus(); }.bind(this), 600); } - $(window).on('keyup.ionModal', function(event) { event.stopImmediatePropagation(); if (event.which == 27) { @@ -121,11 +132,7 @@ Template.ionModal.helpers({ }, animation: function () { - if (this.animation) { - return this.animation; - } else { - return 'slide-in-up'; - } + return this.animation || 'slide-in-up'; }, customTemplate: function () { From 58f99804f6e97b3015dffa80f303ca8feed27a9a Mon Sep 17 00:00:00 2001 From: Gwen Date: Thu, 6 Aug 2015 21:54:57 +0200 Subject: [PATCH 10/16] close modal by templateName --- components/ionModal/ionModal.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index a0f896d..270811f 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -33,13 +33,15 @@ IonModal = { }.bind(this), 0); }, - close: function () { + close: function (templateName) { + this.templateClosed = templateName; Meteor.setTimeout(function () { - var templateName = this.views[this.views.length-1]; - var viewArray = this.view[templateName] || []; - var view = viewArray[viewArray.length-1]; + var templateName = this.templateClosed || this.views[this.views.length-1]; + delete this.templateClosed; + + var view = (this.view[templateName] || []).slice(-1)[0]; if (!view) return; var $modalBackdrop = $(view.firstNode()); @@ -65,7 +67,9 @@ $(document).delegate('.modal', IonModal.transitionEndEvent, function(e) { $modal.removeClass(IonModal.enterClasses.join(' ')).removeClass(IonModal.enterActiveClass); $('body').addClass('modal-open'); } else if ($modal.hasClass(IonModal.leaveClasses.join(' ')) || $modal.hasClass(IonModal.leaveActiveClasse)) { - var templateName = IonModal.views.pop(); + var firstChild = $modal.children().first(); + var templateName = getElementModalTemplateName(firstChild); + IonModal.views = _.without(IonModal.views, templateName); var view = IonModal.view[templateName].pop(); var $modalBackdrop = $(view.firstNode()); $modalBackdrop.removeClass('active'); @@ -158,8 +162,16 @@ Template.ionModal.events({ IonModal.close(); } }, - 'click [data-dismiss=modal]': function (event, template) { - IonModal.close(); + var tplName = getElementModalTemplateName(event.currentTarget); + IonModal.close(tplName); } }); + +var getElementModalTemplateName = function(element) { + var modal = $(element).parents('.modal').get(0); + var modalView = Blaze.getView(modal); + var tplView = Meteor._get(modalView, 'parentView', 'parentView'); // Twice because the parent view is a #with block + var tplName = tplView.name.slice('Template.'.length, tplView.name.length); + return tplName; +} From e853bfdf20b2843dd7f6b158ddc6a01c67bb0804 Mon Sep 17 00:00:00 2001 From: Gwen Date: Thu, 6 Aug 2015 21:55:57 +0200 Subject: [PATCH 11/16] close modal by templateName --- components/ionModal/ionModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index 270811f..59836fe 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -38,7 +38,7 @@ IonModal = { this.templateClosed = templateName; Meteor.setTimeout(function () { - var templateName = this.templateClosed || this.views[this.views.length-1]; + var templateName = this.templateClosed || this.views.slice(-1)[0]; delete this.templateClosed; var view = (this.view[templateName] || []).slice(-1)[0]; From 9a06e10959e5aa1332478bc94df5266daed9823e Mon Sep 17 00:00:00 2001 From: Gwen Date: Thu, 6 Aug 2015 22:46:59 +0200 Subject: [PATCH 12/16] fix popover close bug --- components/ionPopup/ionPopup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ionPopup/ionPopup.js b/components/ionPopup/ionPopup.js index 504a0cb..4f44cbd 100644 --- a/components/ionPopup/ionPopup.js +++ b/components/ionPopup/ionPopup.js @@ -121,12 +121,12 @@ IonPopup = { }, close: function () { - var $backdrop = $(this.view.firstNode()); - var $popup = $backdrop.find('.popup-container'); + var $popup = this._domrange ? $(this.view.firstNode()).find('.popup-container') : $('.popup-container'); $popup.addClass('popup-hidden').removeClass('active'); setTimeout(function () { $('body').removeClass('popup-open'); + $('.backdrop').remove(); Blaze.remove(this.view); }.bind(this), 100); }, From a60b630f22e674012659709813763ffc32455fdb Mon Sep 17 00:00:00 2001 From: Gwen Date: Sun, 9 Aug 2015 02:25:01 +0200 Subject: [PATCH 13/16] do modal insert into .ionic-body to fix css scoping for ios --- components/ionModal/ionModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index 59836fe..c903494 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -15,7 +15,7 @@ IonModal = { this.views.push(templateName); if (!this.view[templateName]) this.view[templateName] = []; - var view = Blaze.renderWithData(this.template, data, $('body').get(0)); + var view = Blaze.renderWithData(this.template, data, $('.ionic-body').get(0)); this.view[templateName].push(view); var $modalBackdrop = $(view.firstNode()); From 434e1cdd4cbb4d8b65415cdd01d07e53320ef15e Mon Sep 17 00:00:00 2001 From: Gwen Date: Sun, 9 Aug 2015 03:19:31 +0200 Subject: [PATCH 14/16] added modal hooks --- components/ionModal/ionModal.js | 41 ++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index c903494..ba21a7a 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -7,6 +7,9 @@ IonModal = { leaveActiveClass: 'ng-leave-active', view: {}, views: [], + hooksOpen: {}, + hooksClose: {}, + hooksAllId: '__all__', open: function (templateName, data) { Meteor.setTimeout(function () { @@ -30,6 +33,16 @@ IonModal = { $modal.addClass(this.enterActiveClass); }.bind(this), 50); + var hooksAll = this.hooksOpen[this.hooksAllId] || []; + _.each(hooksAll, function(hook) { + hook.apply(this, [templateName]); + }); + + var hooks = this.hooksOpen[templateName] || []; + _.each(hooks, function(hook) { + hook.apply(this, [templateName]); + }); + }.bind(this), 0); }, @@ -56,8 +69,34 @@ IonModal = { $('body').removeClass('modal-open'); }); + var hooksAll = this.hooksClose[this.hooksAllId] || []; + _.each(hooksAll, function(hook) { + hook.apply(this, [templateName]); + }); + + var hooks = this.hooksClose[templateName] || []; + _.each(hooks, function(hook) { + hook.apply(this, [templateName]); + }); + }.bind(this), 0); + }, + onOpen: function(templateName, callback) { + if (!callback) { + callback = templateName; + templateName = this.hooksAllId; + } + this.hooksOpen[templateName] = this.hooksOpen[templateName] || []; + this.hooksOpen[templateName].push(callback); + }, + onClose: function(templateName, callback) { + if (!callback) { + callback = templateName; + templateName = this.hooksAllId; + } + this.hooksClose[templateName] = this.hooksClose[templateName] || []; + this.hooksClose[templateName].push(callback); } }; @@ -174,4 +213,4 @@ var getElementModalTemplateName = function(element) { var tplView = Meteor._get(modalView, 'parentView', 'parentView'); // Twice because the parent view is a #with block var tplName = tplView.name.slice('Template.'.length, tplView.name.length); return tplName; -} +}; From fd2cebb6993b1251d3ae5d65a5fd6355883c210c Mon Sep 17 00:00:00 2001 From: Gwen Date: Sun, 9 Aug 2015 03:21:16 +0200 Subject: [PATCH 15/16] removed modal hooks -> can use Template.ionModal.onCreated / onDestroyed --- components/ionModal/ionModal.js | 39 --------------------------------- 1 file changed, 39 deletions(-) diff --git a/components/ionModal/ionModal.js b/components/ionModal/ionModal.js index ba21a7a..083bb4d 100644 --- a/components/ionModal/ionModal.js +++ b/components/ionModal/ionModal.js @@ -7,9 +7,6 @@ IonModal = { leaveActiveClass: 'ng-leave-active', view: {}, views: [], - hooksOpen: {}, - hooksClose: {}, - hooksAllId: '__all__', open: function (templateName, data) { Meteor.setTimeout(function () { @@ -33,16 +30,6 @@ IonModal = { $modal.addClass(this.enterActiveClass); }.bind(this), 50); - var hooksAll = this.hooksOpen[this.hooksAllId] || []; - _.each(hooksAll, function(hook) { - hook.apply(this, [templateName]); - }); - - var hooks = this.hooksOpen[templateName] || []; - _.each(hooks, function(hook) { - hook.apply(this, [templateName]); - }); - }.bind(this), 0); }, @@ -69,34 +56,8 @@ IonModal = { $('body').removeClass('modal-open'); }); - var hooksAll = this.hooksClose[this.hooksAllId] || []; - _.each(hooksAll, function(hook) { - hook.apply(this, [templateName]); - }); - - var hooks = this.hooksClose[templateName] || []; - _.each(hooks, function(hook) { - hook.apply(this, [templateName]); - }); - }.bind(this), 0); - }, - onOpen: function(templateName, callback) { - if (!callback) { - callback = templateName; - templateName = this.hooksAllId; - } - this.hooksOpen[templateName] = this.hooksOpen[templateName] || []; - this.hooksOpen[templateName].push(callback); - }, - onClose: function(templateName, callback) { - if (!callback) { - callback = templateName; - templateName = this.hooksAllId; - } - this.hooksClose[templateName] = this.hooksClose[templateName] || []; - this.hooksClose[templateName].push(callback); } }; From 12fce09295014b3e2dc437302520515c1f910e05 Mon Sep 17 00:00:00 2001 From: Gwen Date: Mon, 10 Aug 2015 00:49:04 +0200 Subject: [PATCH 16/16] bug fix --- components/ionLoading/ionLoading.js | 7 +++++-- components/ionSlideBox/ionSlideBox.js | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/ionLoading/ionLoading.js b/components/ionLoading/ionLoading.js index c9d2306..0f4740f 100644 --- a/components/ionLoading/ionLoading.js +++ b/components/ionLoading/ionLoading.js @@ -7,7 +7,7 @@ IonLoading = { customTemplate: null, backdrop: false }, userOptions); - + if (options.backdrop) { IonBackdrop.retain(); $('.backdrop').addClass('backdrop-loading'); @@ -42,7 +42,10 @@ IonLoading = { $loadingEl.removeClass('visible'); Blaze.remove(this.view); this.view = null; - }.bind(this), 400); + }.bind(this), 0); } + Meteor.setTimeout(function() { + $('.loading-container').remove(); + }, 0) } }; diff --git a/components/ionSlideBox/ionSlideBox.js b/components/ionSlideBox/ionSlideBox.js index a8323eb..7b7ca82 100644 --- a/components/ionSlideBox/ionSlideBox.js +++ b/components/ionSlideBox/ionSlideBox.js @@ -20,12 +20,12 @@ Template.ionSlideBox.rendered = function () { return ''; } }); - this.$('.ion-slide-box').on('afterChange', function (event, slick, currentSlide) { $(this).trigger({type: 'onSlideChanged', index: currentSlide}); }); }; Template.ionSlideBox.destroyed = function () { - this.$('.ion-slide-box').slick('unslick'); + var $slideBox = this.$('.ion-slide-box'); + if ($slideBox.hasClass('slick-initialized')) $slideBox.slick('unslick'); };