From 0594d7650bfdb60b3ff3ed5fd5cee6c1e98b7361 Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Mon, 4 Jan 2016 22:46:50 -0700 Subject: [PATCH 1/9] Fix the event propagation when there are nested draggable items. For instance, ionSlideBox inside ionSideMenu. If developer still wants the propagation, simply pass stopPropagation=false to ionSlideBox. --- components/ionSlideBox/ionSlideBox.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/ionSlideBox/ionSlideBox.js b/components/ionSlideBox/ionSlideBox.js index 7b7ca82..7768da5 100644 --- a/components/ionSlideBox/ionSlideBox.js +++ b/components/ionSlideBox/ionSlideBox.js @@ -5,6 +5,7 @@ Template.ionSlideBox.created = function () { this.slideInterval = this.data.slideInterval || 4000; this.showPager = typeof this.data.showPager != 'undefined' ? this.data.showPager : true; this.initialSlide = this.data.initialSlide || Session.get('ion-slide-initial-slide') || 0; + this.preventPropagation = this.data.preventPropagation || true; }; Template.ionSlideBox.rendered = function () { @@ -29,3 +30,11 @@ Template.ionSlideBox.destroyed = function () { var $slideBox = this.$('.ion-slide-box'); if ($slideBox.hasClass('slick-initialized')) $slideBox.slick('unslick'); }; + +Template.ionSlideBox.events({ + 'touchmove .ion-slide-box': function(event, template) { + if (template.preventPropagation) { + event.stopPropagation(); + } + } +}); \ No newline at end of file From e80e6af0d29bf2c5d29e805bbd6d1097d10b7d58 Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Sat, 9 Jan 2016 06:51:49 -0700 Subject: [PATCH 2/9] ionRadio updated to match 1.1.1 --- components/ionRadio/ionRadio.html | 8 +++++--- package.js | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/components/ionRadio/ionRadio.html b/components/ionRadio/ionRadio.html index 0a31b66..9fa2631 100644 --- a/components/ionRadio/ionRadio.html +++ b/components/ionRadio/ionRadio.html @@ -1,9 +1,11 @@ diff --git a/package.js b/package.js index 866fdc4..ae61e17 100644 --- a/package.js +++ b/package.js @@ -1,7 +1,7 @@ Package.describe({ - name: "meteoric:ionic", + name: "jandres:ionic", summary: "Ionic components for Meteor. No Angular!", - version: "0.1.19", + version: "0.1.20", git: "https://github.com/meteoric/meteor-ionic.git" }); From 1e3c63ecef58be31fe2da5ae1da0c5d9b3229c13 Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Sat, 9 Jan 2016 06:51:49 -0700 Subject: [PATCH 3/9] Complex state for ionItem. Complex state occurs when ionItem contains at least one of ionOptions or ionDelete or ionReorder children. - That being said, one can now have ionOptions and swipe to left to show them. - One can now display ionDelete by setting ionItem's showDelete=true and giving ionDelete as its children. - Reorder also works by setting showReorder in ionItem=true and ionReorder is given as ionItem's children. Misc: - I forked Snap.js and integrated PEP (pointer event polyfill) that is supposed to abstract away from mouse and touch event. Making the interface much more consistent. --- .gitignore | 2 + .versions | 49 -- README.md | 48 +- .../ionDeleteButton/ionDeleteButton.html | 8 + components/ionDeleteButton/ionDeleteButton.js | 13 + components/ionHeaderBar/ionHeaderBar.js | 8 +- components/ionItem/ionItem.html | 17 +- components/ionItem/ionItem.js | 121 ++-- components/ionItemContent/ionItemContent.html | 15 + components/ionItemContent/ionItemContent.js | 13 + components/ionItemOptions/ionItemOptions.html | 10 + components/ionItemOptions/ionItemOptions.js | 7 + components/ionList/ionList.html | 5 +- components/ionList/ionList.js | 108 ++-- .../ionOptionButton/ionOptionButton.html | 5 + components/ionOptionButton/ionOptionButton.js | 6 + components/ionPopup/ionPopup.js | 10 +- .../ionReorderButton/ionReorderButton.html | 8 + .../ionReorderButton/ionReorderButton.js | 13 + .../ionSideMenuContainer.js | 23 +- package.js | 38 +- styles/main.scss | 7 + vendor/slip.js | 395 +++++++----- vendor/snap.css | 64 -- vendor/snap.js | 568 ------------------ 25 files changed, 561 insertions(+), 1000 deletions(-) delete mode 100644 .versions create mode 100644 components/ionDeleteButton/ionDeleteButton.html create mode 100644 components/ionDeleteButton/ionDeleteButton.js create mode 100644 components/ionItemContent/ionItemContent.html create mode 100644 components/ionItemContent/ionItemContent.js create mode 100644 components/ionItemOptions/ionItemOptions.html create mode 100644 components/ionItemOptions/ionItemOptions.js create mode 100644 components/ionOptionButton/ionOptionButton.html create mode 100644 components/ionOptionButton/ionOptionButton.js create mode 100644 components/ionReorderButton/ionReorderButton.html create mode 100644 components/ionReorderButton/ionReorderButton.js create mode 100644 styles/main.scss delete mode 100644 vendor/snap.css delete mode 100644 vendor/snap.js diff --git a/.gitignore b/.gitignore index 964bc06..2d06007 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +/.idea +.versions .build* .DS_Store diff --git a/.versions b/.versions deleted file mode 100644 index ba3a556..0000000 --- a/.versions +++ /dev/null @@ -1,49 +0,0 @@ -application-configuration@1.0.4 -base64@1.0.2 -binary-heap@1.0.2 -blaze@2.0.4 -blaze-tools@1.0.2 -boilerplate-generator@1.0.2 -callback-hook@1.0.2 -check@1.0.4 -ddp@1.0.14 -deps@1.0.6 -ejson@1.0.5 -fastclick@1.0.2 -follower-livedata@1.0.3 -geojson-utils@1.0.2 -html-tools@1.0.3 -htmljs@1.0.3 -id-map@1.0.2 -iron:controller@1.0.7 -iron:core@1.0.7 -iron:dynamic-template@1.0.7 -iron:layout@1.0.7 -iron:location@1.0.7 -iron:middleware-stack@1.0.7 -iron:router@1.0.7 -iron:url@1.0.7 -jquery@1.11.3 -json@1.0.2 -logging@1.0.6 -meteor@1.1.4 -meteoric:ionic@0.1.17 -minifiers@1.1.3 -minimongo@1.0.6 -mongo@1.0.11 -observe-sequence@1.0.4 -ordered-dict@1.0.2 -random@1.0.2 -reactive-dict@1.0.5 -reactive-var@1.0.4 -retry@1.0.2 -routepolicy@1.0.4 -session@1.0.5 -spacebars@1.0.5 -spacebars-compiler@1.0.4 -templating@1.0.11 -tracker@1.0.5 -ui@1.0.5 -underscore@1.0.2 -webapp@1.1.6 -webapp-hashing@1.0.2 diff --git a/README.md b/README.md index 8409241..72ce3c9 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,12 @@ ### Build [Ionic](http://ionicframework.com/) apps in [Meteor](https://www.meteor.com/)! -## NOTE: This package is no longer being actively maintained. - -If you are interested in maintaining it, contact me through [my Github profile](https://github.com/nickw). Now that Meteor is officially supporting (and actively recommending) React and Angular, I suggest using [Ionic](https://github.com/driftyco/ionic), [Ionic 2](https://github.com/driftyco/ionic2) or [Reapp](https://github.com/reapp) as alternatives for building hybrid mobile apps with Meteor. - ## Overview This is an attempt at **real Ionic and Meteor integration**. This is not just Ionic's CSS framework wrapped in a Meteor package. It aims to be a complete port of [Ionic’s Angular directives](http://ionicframework.com/docs/api/) to [Meteor Blaze](https://www.meteor.com/blaze) templates. +Note: This is a forked of meteoric:ionic, which is now not maintained. In response, I decided to fork it, and maintain this package for my own. I have written acceptance test on [JoeyAndres/ionic-demo](https://github.com/JoeyAndres/ionic-demo), which is a fork of meteoric/demo. + ## Why? [Ionic](http://ionicframework.com/) is arguably the most comprehensive, polished, cross-platform mobile framework available. But unfortunately a large portion of its functionality comes from Angular directives. [I'm not a fan of trying to force-fit Angular into Meteor](https://medium.com/space-camp/your-meteor-app-probably-doesnt-need-angular-13986a0323f6), so I wanted to see if I could rewrite Ionic specifically for Meteor. @@ -22,14 +20,27 @@ Check out the [GUIDE.md](GUIDE.md) for a guide on how to get started. **Beta** See the TODO section below to see which Angular Directives have been ported to Blaze. +## Install + +```bash +meteor add jandres:ionic +``` + ## Dependencies -Rather than include compiled or CDN versions of Ionic's CSS Framework we’ve extraced it into two separate packages: -- [meteoric:ionicons-sass](http://github.com/meteoric/ionicons-sass) Ionic’s Ionicons set wrapped for Meteor. -- [meteoric:ionic-sass](http://github.com/meteoric/ionic-sass) The base Ionic CSS Framework wrapped for Meteor. +- [seba:ionic-sass](https://github.com/sebakerckhof/meteor-ionic-sass/) Ionic's scss only package and currently up to date with ionic v1.1.0 ## Examples +### Demo of all components +The demo app of various ionic components + +[Demo](http://jandres-meteor-ionic.meteor.com/) | [Code](https://github.com/JoeyAndres/ionic-demo) + +## Pre-fork Examples + +The following are examples prior to this fork. + ### Contacts App A simple CRUD app to manage contacts. @@ -40,13 +51,6 @@ A [Product Hunt](http://producthunt.com) clone built in Meteor Ionic. (In Progre [Demo](http://meteorhunt.meteor.com/) | [Code](https://github.com/meteoric/meteorhunt) -### Demo of all components -The demo app of various meteoric components - -[Demo](http://meteor-ionic.meteor.com/) | [Code](https://github.com/meteoric/demo) - -You can also keep track of the various other repos from the [Meteoric team](https://github.com/meteoric) - ## TODO ### Angular Directives to convert to Blaze: @@ -67,15 +71,15 @@ You can also keep track of the various other repos from the [Meteoric team](http * [x] ion-footer-bar * [x] Keyboard (requires [cordova](http://cordova.apache.org/) integration) * [ ] Lists (needs edit/remove/sort functionality) - * [ ] ion-list - * [ ] ion-item - * [ ] ion-delete-button - * [ ] ion-reorder-button - * [ ] ion-option-button + * [x] ion-list + * [x] ion-item + * [x] ion-delete-button + * [x] ion-reorder-button + * [x] ion-option-button * [ ] collection-repeat * [x] Loading * [x] Modal -* [x] Navigation (requires [iron:router](https://github.com/EventedMind/iron-router) integration) +* [x] Navigation (requires [iron:router](https://github.com/EventedMind/iron-router) integration)a * [x] ion-nav-view * [x] ion-view * [x] ion-nav-bar @@ -101,6 +105,10 @@ You can also keep track of the various other repos from the [Meteoric team](http * [x] Tabs (requires [iron:router](https://github.com/EventedMind/iron-router) integration) * [x] ion-tabs * [x] ion-tab + +### Code Style Change: +These are code styles that I want to impose on this forked repo. +* Get rid of all session variables [ ] ## License [MIT License](https://github.com/meteoric/meteor-ionic/blob/master/LICENSE) diff --git a/components/ionDeleteButton/ionDeleteButton.html b/components/ionDeleteButton/ionDeleteButton.html new file mode 100644 index 0000000..8ccd42c --- /dev/null +++ b/components/ionDeleteButton/ionDeleteButton.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/components/ionDeleteButton/ionDeleteButton.js b/components/ionDeleteButton/ionDeleteButton.js new file mode 100644 index 0000000..190f75d --- /dev/null +++ b/components/ionDeleteButton/ionDeleteButton.js @@ -0,0 +1,13 @@ +Template.ionDeleteButton.onCreated(function() { + let parent = this.parent((t) => t.view.name === "Template.ionItem", true); + if (!parent) { throw "Template.ionDeleteButton must be a descendant of Template.ionItem."; } + _.extend(this, { + showDelete: parent.showDelete + }); +}); + +Template.ionDeleteButton.helpers({ + showDelete: function() { + return Template.instance().showDelete.get(); + } +}); \ No newline at end of file diff --git a/components/ionHeaderBar/ionHeaderBar.js b/components/ionHeaderBar/ionHeaderBar.js index 96d830e..adad638 100644 --- a/components/ionHeaderBar/ionHeaderBar.js +++ b/components/ionHeaderBar/ionHeaderBar.js @@ -1,6 +1,6 @@ IonHeaderBar = { alignTitle: function () { - var align = this.data.alignTitle || 'center'; + var align = this.alignTitle; var $title = this.$('.title'); if (Platform.isAndroid() && !this.alignTitle) { @@ -49,9 +49,9 @@ IonHeaderBar = { } }; -Template.ionHeaderBar.created = function () { - this.data = this.data || {}; -}; +Template.ionHeaderBar.onCreated(function() { + this.alignTitle = this.data? (this.data.alignTitle || 'center') : 'center'; +}); Template.ionHeaderBar.rendered = function () { Session.set('hasHeader', true); diff --git a/components/ionItem/ionItem.html b/components/ionItem/ionItem.html index e1788ab..baea793 100644 --- a/components/ionItem/ionItem.html +++ b/components/ionItem/ionItem.html @@ -1,11 +1,12 @@ + \ No newline at end of file diff --git a/components/ionItem/ionItem.js b/components/ionItem/ionItem.js index 811345d..b68e245 100644 --- a/components/ionItem/ionItem.js +++ b/components/ionItem/ionItem.js @@ -1,48 +1,97 @@ -Template.ionItem.helpers({ - idAttribute: function () { - if (this.id) { - return this.id; - } - }, - itemClasses: function () { - var classes = ['item']; - - if (this.class) { - var customClasses = this.class.split(' '); - _(customClasses).each(function (customClass) { - classes.push(customClass); - }); +Template.ionItem.onCreated(function() { + this.snapper = null; + this.itemComplex = new ReactiveVar(false); + + let parent = this.parent((template) => template.view.name === "Template.ionList", true); + if (!parent) { throw "Template.ionItem must be a descendant of Template.ionList."; } + + _.extend(this, { + // Props. + showDelete: parent.showDelete, + showReorder: parent.showReorder, + canSwipe: parent.canSwipe, + + // Methods. + closeIonItemSiblings: () => { + let ionItemSiblings = this.getSiblings().filter(sibling => sibling.view.name === "Template.ionItem"); + _.each(ionItemSiblings, sibling => !!sibling.snapper && sibling.snapper.close()); + }, + dragSetTransitionNone: () => this.$('.item-content').css({ transition: "none" }), + dragEndSetTransitionToInitial: () => this.$('.item-content').css({ transition: "initial"}), + initDragTransitionHandler: () => { + this.snapper.on('drag', this.dragSetTransitionNone); + this.snapper.on('end', this.dragEndSetTransitionToInitial); + }, + destroyDragTransitionHandler: () => { + this.snapper.off('drag', this.dragSetTransitionNone); + this.snapper.off('end', this.dragEndSetTransitionToInitial); + }, + isitemComplex: () => { + let complex = !!_.find(this.children(1), + elem => + elem.view.name === 'Template.ionItemOptions' || + elem.view.name === 'Template.ionDeleteButton' || + elem.view.name === 'Template.ionReorderButton'); + return complex; } + }); +}); - if (this.avatar) { - classes.push('item-avatar' + (this.avatar === 'right' ? '-right' : '')); - } +Template.ionItem.onRendered(function() { + this.autorun(() => { + if (this.canSwipe.get() && !this.showDelete.get() && !this.showReorder.get()) { + let ionOptions = this.getChildren() + .filter(child => child.view && child.view.name === 'Template.ionItemOptions'); + let ionOptionsWidth = ionOptions.reduce((width, child) => width + child.width(), 0); + if (!this.snapper) { + this.snapper = new Snap({ + element: this.$('.item-content').get(0), + disable: 'left', + minPosition: -ionOptionsWidth + }); + } - if (this.iconLeft) { - classes.push('item-icon-left'); - } + this.snapper.settings({ + element: this.$('.item-content').get(0), // In case the child template ionItemContent got changed. + minPosition: -ionOptionsWidth + }); + this.snapper.enable(); - if (this.iconRight) { - classes.push('item-icon-right'); - } + this.snapper.on('start', () => { + this.closeIonItemSiblings(); + }); - if (this.buttonLeft) { - classes.push('item-button-left'); + this.initDragTransitionHandler(); + } else { + if (this.snapper) { + this.destroyDragTransitionHandler(); + this.snapper.disable(); + this.snapper.close(); + } } + }); - if(Session.get('ionSortable')){ - classes.push('item-complex', 'item-left-editable'); - } + this.autorun(() => { + this.itemComplex.set(this.isitemComplex()); + }); +}); - if (this.buttonRight) { - classes.push('item-button-right'); - } +Template.ionItem.onDestroyed(function() { + if (!!this.snapper) { + this.destroyDragTransitionHandler(); + this.snapper.disable(); + this.snapper.close(); + } +}); - if (this.textWrap) { - classes.push('item-text-wrap'); +Template.ionItem.helpers({ + idAttribute: function () { + if (this.id) { + return this.id; } - - return classes.join(' '); + }, + itemComplex: function() { + return Template.instance().itemComplex.get(); }, isAnchor: function () { @@ -85,4 +134,4 @@ Template.ionItem.helpers({ } } } -}); +}); \ No newline at end of file diff --git a/components/ionItemContent/ionItemContent.html b/components/ionItemContent/ionItemContent.html new file mode 100644 index 0000000..239aceb --- /dev/null +++ b/components/ionItemContent/ionItemContent.html @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/components/ionItemContent/ionItemContent.js b/components/ionItemContent/ionItemContent.js new file mode 100644 index 0000000..640bb15 --- /dev/null +++ b/components/ionItemContent/ionItemContent.js @@ -0,0 +1,13 @@ +Template.ionItemContent.onCreated(function() { + let parent = this.parent((t) => t.view.name === "Template.ionItem", true); + if (!parent) { throw "Template.ionItemContent must be a descendant of Template.ionItem."; } + _.extend(this, { + itemComplex: parent.itemComplex + }); +}); + +Template.ionItemContent.helpers({ + itemComplex: function() { + return Template.instance().itemComplex.get(); + } +}); \ No newline at end of file diff --git a/components/ionItemOptions/ionItemOptions.html b/components/ionItemOptions/ionItemOptions.html new file mode 100644 index 0000000..1415299 --- /dev/null +++ b/components/ionItemOptions/ionItemOptions.html @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/components/ionItemOptions/ionItemOptions.js b/components/ionItemOptions/ionItemOptions.js new file mode 100644 index 0000000..9bd3d00 --- /dev/null +++ b/components/ionItemOptions/ionItemOptions.js @@ -0,0 +1,7 @@ +Template.ionItemOptions.onCreated(function() { + // todo: create package that gets the width of current element, if first = last. + // otherwise, nearest parent (which is just the parent). + this.width = function() { + return this.$('.item-options').width(); + }; +}); \ No newline at end of file diff --git a/components/ionList/ionList.html b/components/ionList/ionList.html index 6331f63..dba2a27 100644 --- a/components/ionList/ionList.html +++ b/components/ionList/ionList.html @@ -1,5 +1,8 @@ \ No newline at end of file diff --git a/components/ionList/ionList.js b/components/ionList/ionList.js index 529972d..4f2a135 100644 --- a/components/ionList/ionList.js +++ b/components/ionList/ionList.js @@ -1,94 +1,56 @@ -Template.ionList.helpers({ - classes: function () { - var classes = ['list']; +Template.ionList.onCreated(function() { + this.showDelete = new ReactiveVar(false); + this.showReorder = new ReactiveVar(false); + this.canSwipe = new ReactiveVar(false); +}); - if (this.class) { - var customClasses = this.class.split(' '); - _(customClasses).each(function (customClass) { - classes.push(customClass); - }); +Template.ionList.onRendered(function() { + this.autorun(() => { + if (Template.instance().showReorder.get()) { + IonSideMenu.disable(); + if (!this.slip) { + var list = this.$('.list')[0]; + this.slip = new Slip(list); + } + } else { + IonSideMenu.enable(); } + }); - return classes.join(' '); - } + this.autorun(() => { + if (!!Template.currentData()) { + this.canSwipe.set(!!Template.currentData().canSwipe); + this.showReorder.set(!!Template.currentData().showReorder); + this.showDelete.set(!!Template.currentData().showDelete); + } + }); }); - -Template.ionList.rendered = function() { - - if (this.data && this.data.ionSortable){ - Session.set("ionSortable", true ); - var list = this.$('.list')[0]; - new Slip(list); -} - -}; - - Template.ionList.events({ 'click .item-delete' : function(e, template){ e.preventDefault(); - - var target = $(e.target).closest('.item').get(0); - var targetData = Blaze.getData(target.getElementsByClassName('item-content')[0])._id || undefined; - - template.data.ionSortable.find({}).forEach(function(item, i) { - if (item._id === targetData) { - template.data.ionSortable._collection.remove({ - _id: item._id - }, function(error, result) { }); - } - }); }, 'slip:swipe .list, slip:beforeswipe .list, slip:beforewait .list, slip:afterswipe .list': function(e, template) { e.preventDefault(); }, 'slip:beforereorder .list': function(e, template) { - if (e.originalEvent.target.className.indexOf('instant') == -1) { + // Two case to consider: + // 1. instant class is in ionItem. In which case, we allow reorder, but we don't show the dragging animation. + // 2. This thing still shows the animation ven when reorder is disabled. Element goes back to orig spot, but could + // easily mislead the user. + if (e.originalEvent.target.className.indexOf('instant') !== -1 || + !template.showReorder.get()) { e.preventDefault(); } }, 'slip:reorder .list': function(e, template) { - spliceIndex = e.originalEvent.detail.spliceIndex - originalIndex = e.originalEvent.detail.originalIndex - - if (spliceIndex != originalIndex) { - - template.data.ionSortable.find({}, { - sort: { - order: 1 - } - }).forEach(function(item, i) { - template.data.ionSortable._collection.pauseObservers() - if (item._id == Blaze.getData(e.target.getElementsByClassName('item-content')[0])._id) { - temp = template.data.ionSortable.update({ - _id: item._id - }, { - $set: { - order: spliceIndex - } - }) - } else { - if (spliceIndex > originalIndex) { - newOrder = ((spliceIndex >= i) && (originalIndex < i)) ? (i - 1) : i - } else if (spliceIndex == '0') { - newOrder = (originalIndex > i) ? (i + 1) : i - } else { - newOrder = ((spliceIndex <= i) && (originalIndex > i)) ? (i + 1) : i - } - - temp = template.data.ionSortable.update({ - _id: item._id - }, { - $set: { - order: newOrder - } - }) - } - template.data.ionSortable._collection.resumeObservers() - }) + let toIndex = e.originalEvent.detail.spliceIndex; + let fromIndex = e.originalEvent.detail.originalIndex; + let index_change = toIndex !== fromIndex; + let sortable = index_change && template.showReorder.get() && !!template.data.onReorder; + if (sortable) { + template.data.onReorder(Template.instance().children()[fromIndex], fromIndex, toIndex); } } - }); \ No newline at end of file diff --git a/components/ionOptionButton/ionOptionButton.html b/components/ionOptionButton/ionOptionButton.html new file mode 100644 index 0000000..8517d0c --- /dev/null +++ b/components/ionOptionButton/ionOptionButton.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/components/ionOptionButton/ionOptionButton.js b/components/ionOptionButton/ionOptionButton.js new file mode 100644 index 0000000..64319b3 --- /dev/null +++ b/components/ionOptionButton/ionOptionButton.js @@ -0,0 +1,6 @@ +Template.ionOptionButton.events({ + 'click .button': function(e, template) { + e.stopPropagation(); + if (_.isFunction(template.data.onClick)) template.data.onClick(e); + } +}); \ No newline at end of file diff --git a/components/ionPopup/ionPopup.js b/components/ionPopup/ionPopup.js index 6fb683f..bc4e46e 100644 --- a/components/ionPopup/ionPopup.js +++ b/components/ionPopup/ionPopup.js @@ -144,17 +144,17 @@ IonPopup = { } }; -Template.ionPopup.rendered = function () { +Template.ionPopup.onRendered(function() { $(window).on('keyup.ionPopup', function(event) { - if (event.which == 27) { + if (event.which == KeyboardEvent.code["Escape"]) { IonPopup.close(); } }); -}; +}); -Template.ionPopup.destroyed = function () { +Template.ionPopup.onDestroyed(function() { $(window).off('keyup.ionPopup'); -}; +}); Template.ionPopup.events({ // Handle clicking the backdrop diff --git a/components/ionReorderButton/ionReorderButton.html b/components/ionReorderButton/ionReorderButton.html new file mode 100644 index 0000000..a882773 --- /dev/null +++ b/components/ionReorderButton/ionReorderButton.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/components/ionReorderButton/ionReorderButton.js b/components/ionReorderButton/ionReorderButton.js new file mode 100644 index 0000000..a7234a3 --- /dev/null +++ b/components/ionReorderButton/ionReorderButton.js @@ -0,0 +1,13 @@ +Template.ionReorderButton.onCreated(function() { + let parent = this.parent((t) => t.view.name === "Template.ionItem", true); + if (!parent) { throw "Template.ionReorderButton must be a descendant of Template.ionItem."; } + _.extend(this, { + showReorder: parent.showReorder + }); +}); + +Template.ionReorderButton.helpers({ + showReorder: function() { + return Template.instance().showReorder.get(); + } +}); \ No newline at end of file diff --git a/components/ionSideMenuContainer/ionSideMenuContainer.js b/components/ionSideMenuContainer/ionSideMenuContainer.js index 618d801..35beab2 100644 --- a/components/ionSideMenuContainer/ionSideMenuContainer.js +++ b/components/ionSideMenuContainer/ionSideMenuContainer.js @@ -1,17 +1,23 @@ IonSideMenu = { - snapper: null + snapper: null, // Make this private in the future. + + // Note that meteor renders most deeply nested template first before the outer most. + // Thus, if the child happens toc all any of these, before we are rendered, then + // error occurs. To solve that, dummy functions are provided. + enable: () => {}, + disable: () => {} }; -Template.ionSideMenuContainer.created = function () { +Template.ionSideMenuContainer.onCreated(function () { this.data = this.data || {}; this.side = this.data.side || 'both'; this.dragContent = true; if (typeof this.data.dragContent != 'undefined') { this.dragContent = this.data.dragContent } -}; +}); -Template.ionSideMenuContainer.rendered = function () { +Template.ionSideMenuContainer.onRendered(function () { $snapperEl = this.$('.snap-content'); if (!$snapperEl) { return; @@ -33,8 +39,11 @@ Template.ionSideMenuContainer.rendered = function () { disable: disable, touchToDrag: this.dragContent }); -}; -Template.ionSideMenuContainer.destroyed = function () { + IonSideMenu.enable = () => IonSideMenu.snapper.enable(); + IonSideMenu.disable = () => IonSideMenu.snapper.disable(); +}); + +Template.ionSideMenuContainer.onDestroyed(function () { IonSideMenu.snapper = null; -}; +}); diff --git a/package.js b/package.js index ae61e17..7acd1f5 100644 --- a/package.js +++ b/package.js @@ -1,10 +1,12 @@ Package.describe({ name: "jandres:ionic", summary: "Ionic components for Meteor. No Angular!", - version: "0.1.20", - git: "https://github.com/meteoric/meteor-ionic.git" + version: "0.1.39", + git: "https://github.com/JoeyAndres/meteor-ionic.git" }); + + Cordova.depends({ 'ionic-plugin-keyboard': '1.0.8' }); @@ -12,23 +14,34 @@ Cordova.depends({ Package.onUse(function(api) { api.versionsFrom("1.0"); api.use([ + "fourseven:scss@3.3.3" + ]); + + api.use([ + "jandres:template-extension@4.0.4", + "ecmascript@0.1.6", "templating", "underscore", + "reactive-var", "fastclick", "iron:router@1.0.0", "tracker", "session", - "jquery" + "jquery", + "jandres:snapjs@2.0.1", + "fourseven:scss@3.3.3" ], "client"); api.addFiles([ - "vendor/snap.js", - "vendor/snap.css", "vendor/slick.js", "vendor/slick.css", "vendor/slip.js" ], "client"); + api.addFiles([ + "styles/main.scss" + ], "client"); + api.addFiles([ "components/ionActionSheet/ionActionSheet.html", "components/ionActionSheet/ionActionSheet.js", @@ -42,6 +55,9 @@ Package.onUse(function(api) { "components/ionContent/ionContent.html", "components/ionContent/ionContent.js", + "components/ionDeleteButton/ionDeleteButton.html", + "components/ionDeleteButton/ionDeleteButton.js", + "components/ionFooterBar/ionFooterBar.html", "components/ionFooterBar/ionFooterBar.js", @@ -54,6 +70,12 @@ Package.onUse(function(api) { "components/ionItem/ionItem.html", "components/ionItem/ionItem.js", + "components/ionItemOptions/ionItemOptions.html", + "components/ionItemOptions/ionItemOptions.js", + + "components/ionItemContent/ionItemContent.html", + "components/ionItemContent/ionItemContent.js", + "components/ionKeyboard/ionKeyboard.js", "components/ionKeyboard/ionInputFocus.js", @@ -72,6 +94,9 @@ Package.onUse(function(api) { "components/ionNavBar/ionNavBar.html", "components/ionNavBar/ionNavBar.js", + "components/ionOptionButton/ionOptionButton.html", + "components/ionOptionButton/ionOptionButton.js", + "components/ionNavBackButton/ionNavBackButton.html", "components/ionNavBackButton/ionNavBackButton.js", @@ -90,6 +115,9 @@ Package.onUse(function(api) { "components/ionRadio/ionRadio.html", "components/ionRadio/ionRadio.js", + "components/ionReorderButton/ionReorderButton.html", + "components/ionReorderButton/ionReorderButton.js", + "components/ionSideMenu/ionSideMenu.html", "components/ionSideMenu/ionSideMenu.js", diff --git a/styles/main.scss b/styles/main.scss new file mode 100644 index 0000000..42398f1 --- /dev/null +++ b/styles/main.scss @@ -0,0 +1,7 @@ +.snap-drawers { + .snap-drawer-left {} + .item-options.snap-drawer.snap-drawer-right { + width: initial; + overflow: initial; + } +} \ No newline at end of file diff --git a/vendor/slip.js b/vendor/slip.js index 30ab0a8..aa1af09 100644 --- a/vendor/slip.js +++ b/vendor/slip.js @@ -1,115 +1,129 @@ /* - Slip - swiping and reordering in lists of elements on touch screens, no fuss. + Slip - swiping and reordering in lists of elements on touch screens, no fuss. - Fires these events on list elements: + Fires these events on list elements: - • slip:swipe - When swipe has been done and user has lifted finger off the screen. - If you execute event.preventDefault() the element will be animated back to original position. - Otherwise it will be animated off the list and set to display:none. + • slip:swipe + When swipe has been done and user has lifted finger off the screen. + If you execute event.preventDefault() the element will be animated back to original position. + Otherwise it will be animated off the list and set to display:none. - • slip:beforeswipe - Fired before first swipe movement starts. - If you execute event.preventDefault() then element will not move at all. + • slip:beforeswipe + Fired before first swipe movement starts. + If you execute event.preventDefault() then element will not move at all. - • slip:reorder - Element has been dropped in new location. event.detail contains the location: - • insertBefore: DOM node before which element has been dropped (null is the end of the list). Use with node.insertBefore(). - • spliceIndex: Index of element before which current element has been dropped, not counting the element iself. - For use with Array.splice() if the list is reflecting objects in some array. + • slip:reorder + Element has been dropped in new location. event.detail contains the location: + • insertBefore: DOM node before which element has been dropped (null is the end of the list). Use with node.insertBefore(). + • spliceIndex: Index of element before which current element has been dropped, not counting the element iself. + For use with Array.splice() if the list is reflecting objects in some array. - • slip:beforereorder - When reordering movement starts. - Element being reordered gets class `slip-reordering`. - If you execute event.preventDefault() then element will not move at all. + • slip:beforereorder + When reordering movement starts. + Element being reordered gets class `slip-reordering`. + If you execute event.preventDefault() then element will not move at all. - • slip:beforewait - If you execute event.preventDefault() then reordering will begin immediately, blocking ability to scroll the page. + • slip:beforewait + If you execute event.preventDefault() then reordering will begin immediately, blocking ability to scroll the page. - • slip:tap - When element was tapped without being swiped/reordered. + • slip:tap + When element was tapped without being swiped/reordered. - • slip:cancelswipe - Fired when the user stops dragging and the element returns to its original position. + • slip:cancelswipe + Fired when the user stops dragging and the element returns to its original position. - Usage: + Usage: - CSS: - You should set `user-select:none` (and WebKit prefixes, sigh) on list elements, - otherwise unstoppable and glitchy text selection in iOS will get in the way. + CSS: + You should set `user-select:none` (and WebKit prefixes, sigh) on list elements, + otherwise unstoppable and glitchy text selection in iOS will get in the way. - You should set `overflow-x: hidden` on the container or body to prevent horizontal scrollbar - appearing when elements are swiped off the list. + You should set `overflow-x: hidden` on the container or body to prevent horizontal scrollbar + appearing when elements are swiped off the list. - var list = document.querySelector('ul#slippylist'); - new Slip(list); + var list = document.querySelector('ul#slippylist'); + new Slip(list); - list.addEventListener('slip:beforeswipe', function(e) { - if (shouldNotSwipe(e.target)) e.preventDefault(); - }); + list.addEventListener('slip:beforeswipe', function(e) { + if (shouldNotSwipe(e.target)) e.preventDefault(); + }); - list.addEventListener('slip:swipe', function(e) { - // e.target swiped - if (thatWasSwipeToRemove) { - e.target.parentNode.removeChild(e.target); - } else { - e.preventDefault(); // will animate back to original position - } - }); + list.addEventListener('slip:swipe', function(e) { + // e.target swiped + if (thatWasSwipeToRemove) { + e.target.parentNode.removeChild(e.target); + } else { + e.preventDefault(); // will animate back to original position + } + }); - list.addEventListener('slip:beforereorder', function(e) { - if (shouldNotReorder(e.target)) e.preventDefault(); - }); + list.addEventListener('slip:beforereorder', function(e) { + if (shouldNotReorder(e.target)) e.preventDefault(); + }); - list.addEventListener('slip:reorder', function(e) { - // e.target reordered. - if (reorderedOK) { - e.target.parentNode.insertBefore(e.target, e.detail.insertBefore); - } else { - e.preventDefault(); - } - }); + list.addEventListener('slip:reorder', function(e) { + // e.target reordered. + if (reorderedOK) { + e.target.parentNode.insertBefore(e.target, e.detail.insertBefore); + } else { + e.preventDefault(); + } + }); - Requires: - • Touch events - • CSS transforms - • Function.bind() + Requires: + • Touch events + • CSS transforms + • Function.bind() - Caveats: - • Elements must not change size while reordering or swiping takes place (otherwise it will be visually out of sync) -*/ + Caveats: + • Elements must not change size while reordering or swiping takes place (otherwise it will be visually out of sync) + */ /*! @license - Slip.js 1.2.0 + Slip.js 1.2.0 - © 2014 Kornel Lesiński . All rights reserved. + © 2014 Kornel Lesiński . All rights reserved. - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and - the following disclaimer in the documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -window['Slip'] = (function(){ +window['Slip'] = (function() { 'use strict'; + var accessibility = { + // Set values to false if you don't want Slip to manage them + container: { + ariaRole: "listbox", + tabIndex: 0, + focus: true, // focuses after drop + }, + items: { + ariaRole: "option", // If "option" flattens items, try "group": https://www.marcozehe.de/2013/03/08/sometimes-you-have-to-use-illegal-wai-aria-to-make-stuff-work/ + tabIndex: -1, // 0 will make every item tabbable, which isn't always useful + focus: true, // focuses when dragging + }, + }; + var damnYouChrome = /Chrome\/[34]/.test(navigator.userAgent); // For bugs that can't be programmatically detected :( Intended to catch all versions of Chrome 30-40 var needsBodyHandlerHack = damnYouChrome; // Otherwise I _sometimes_ don't get any touchstart events and only clicks instead. /* When dragging elements down in Chrome (tested 34-37) dragged element may appear below stationary elements. - Looks like WebKit bug #61824, but iOS Safari doesn't have that problem. */ + Looks like WebKit bug #61824, but iOS Safari doesn't have that problem. */ var compositorDoesNotOrderLayers = damnYouChrome; // -webkit-mess @@ -127,7 +141,7 @@ window['Slip'] = (function(){ var globalInstances = 0; var attachedBodyHandlerHack = false; - var nullHandler = function(){}; + var nullHandler = function() {}; function Slip(container, options) { if ('string' === typeof container) container = document.querySelector(container); @@ -135,7 +149,10 @@ window['Slip'] = (function(){ if (!this || this === window) return new Slip(container, options); - this.options = options; + this.options = options = options || {}; + this.options.keepSwipingPercent = options.keepSwipingPercent || 0; + this.options.minimumSwipeVelocity = options.minimumSwipeVelocity || 1; + this.options.minimumSwipeTime = options.minimumSwipeTime || 110; // Functions used for as event handlers need usable `this` and must not change to be removable this.cancel = this.setState.bind(this, this.states.idle); @@ -147,6 +164,7 @@ window['Slip'] = (function(){ this.onMouseUp = this.onMouseUp.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); this.onSelection = this.onSelection.bind(this); + this.onContainerFocus = this.onContainerFocus.bind(this); this.setState(this.states.idle); this.attach(container); @@ -156,32 +174,38 @@ window['Slip'] = (function(){ var transform = node.style[transformPrefix]; if (transform) { return { - value:transform, - original:transform, + value: transform, + original: transform, }; } if (window.getComputedStyle) { var style = window.getComputedStyle(node).getPropertyValue(transformProperty); - if (style && style !== 'none') return {value:style, original:''}; + if (style && style !== 'none') return { + value: style, + original: '' + }; } - return {value:'', original:''}; + return { + value: '', + original: '' + }; } function findIndex(target, nodes) { - var originalIndex = 0; - var listCount = 0; - - for (var i=0; i < nodes.length; i++) { - if (nodes[i].nodeType === 1) { - listCount++; - if (nodes[i] === target.node) { - originalIndex = listCount-1; - } + var originalIndex = 0; + var listCount = 0; + + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType === 1) { + listCount++; + if (nodes[i] === target.node) { + originalIndex = listCount - 1; + } + } } - } - return originalIndex; + return originalIndex; } // All functions in states are going to be executed in context of Slip object @@ -204,9 +228,12 @@ window['Slip'] = (function(){ states: { idle: function idleStateInit() { - this.target = null; - this.usingTouch = false; this.removeMouseHandlers(); + if (this.target) { + this.target.node.style.willChange = ''; + this.target = null; + } + this.usingTouch = false; return { allowTextSelection: true, @@ -215,14 +242,15 @@ window['Slip'] = (function(){ undecided: function undecidedStateInit() { this.target.height = this.target.node.offsetHeight; + this.target.node.style.willChange = transformProperty; this.target.node.style[transitionPrefix] = ''; if (!this.dispatch(this.target.originalTarget, 'beforewait')) { - if (this.dispatch(this.target.originalTarget, 'beforereorder')) { - this.setState(this.states.reorder); - } + if (this.dispatch(this.target.originalTarget, 'beforereorder')) { + this.setState(this.states.reorder); + } } else { - var holdTimer = setTimeout(function(){ + var holdTimer = setTimeout(function() { var move = this.getAbsoluteMovement(); if (this.canPreventScrolling && move.x < 15 && move.y < 25) { if (this.dispatch(this.target.originalTarget, 'beforereorder')) { @@ -241,7 +269,10 @@ window['Slip'] = (function(){ var move = this.getAbsoluteMovement(); if (move.x > 20 && move.y < Math.max(100, this.target.height)) { - if (this.dispatch(this.target.originalTarget, 'beforeswipe')) { + if (this.dispatch(this.target.originalTarget, 'beforeswipe', { + directionX: move.directionX, + directionY: move.directionY + })) { this.setState(this.states.swipe); return false; } else { @@ -253,7 +284,7 @@ window['Slip'] = (function(){ } // Chrome likes sideways scrolling :( - if (move.x > move.y*1.2) return false; + if (move.x > move.y * 1.2) return false; }, onLeave: function() { @@ -275,8 +306,9 @@ window['Slip'] = (function(){ var originalIndex = findIndex(this.target, this.container.childNodes); container.className += ' slip-swiping-container'; + function removeClass() { - container.className = container.className.replace(/(?:^| )slip-swiping-container/,''); + container.className = container.className.replace(/(?:^| )slip-swiping-container/, ''); } this.target.height = this.target.node.offsetHeight; @@ -284,7 +316,7 @@ window['Slip'] = (function(){ return { leaveState: function() { if (swipeSuccess) { - this.animateSwipe(function(target){ + this.animateSwipe(function(target) { target.node.style[transformPrefix] = target.baseTransform.original; target.node.style[transitionPrefix] = ''; if (this.dispatch(target.node, 'afterswipe')) { @@ -303,7 +335,7 @@ window['Slip'] = (function(){ onMove: function() { var move = this.getTotalMovement(); - if (Math.abs(move.y) < this.target.height+20) { + if (Math.abs(move.y) < this.target.height + 20) { this.target.node.style[transformPrefix] = 'translate(' + move.x + 'px,0) ' + hwLayerMagic + this.target.baseTransform.value; return false; } else { @@ -316,22 +348,19 @@ window['Slip'] = (function(){ }, onEnd: function() { - var dx = this.latestPosition.x - this.previousPosition.x; - var dy = this.latestPosition.y - this.previousPosition.y; - var velocity = Math.sqrt(dx*dx + dy*dy) / (this.latestPosition.time - this.previousPosition.time + 1); - var move = this.getAbsoluteMovement(); - var swiped = velocity > 0.6 && move.time > 110; + var velocity = move.x / move.time; - var direction; - if (dx > 0) { - direction = "right"; - } else { - direction = "left"; - } + // How far out has the item been swiped? + var swipedPercent = Math.abs((this.startPosition.x - this.previousPosition.x) / this.container.clientWidth) * 100; + + var swiped = (velocity > this.options.minimumSwipeVelocity && move.time > this.options.minimumSwipeTime) || (this.options.keepSwipingPercent && swipedPercent > this.options.keepSwipingPercent); if (swiped) { - if (this.dispatch(this.target.node, 'swipe', {direction: direction, originalIndex: originalIndex})) { + if (this.dispatch(this.target.node, 'swipe', { + direction: move.directionX, + originalIndex: originalIndex + })) { swipeSuccess = true; // can't animate here, leaveState overrides anim } } @@ -342,14 +371,18 @@ window['Slip'] = (function(){ }, reorder: function reorderStateInit() { + if (this.target.node.focus && accessibility.items.focus) { + this.target.node.focus(); + } + this.target.height = this.target.node.offsetHeight; var nodes = this.container.childNodes; var originalIndex = findIndex(this.target, nodes); var mouseOutsideTimer; - var zero = this.target.node.offsetTop + this.target.height/2; + var zero = this.target.node.offsetTop + this.target.height / 2; var otherNodes = []; - for(var i=0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { if (nodes[i].nodeType != 1 || nodes[i] === this.target.node) continue; var t = nodes[i].offsetTop; nodes[i].style[transitionPrefix] = transformProperty + ' 0.2s ease-in-out'; @@ -373,23 +406,23 @@ window['Slip'] = (function(){ if (mouseOutsideTimer) { // don't care where the mouse is as long as it moves - clearTimeout(mouseOutsideTimer); mouseOutsideTimer = null; + clearTimeout(mouseOutsideTimer); + mouseOutsideTimer = null; } var move = this.getTotalMovement(); this.target.node.style[transformPrefix] = 'translate(0,' + move.y + 'px) ' + hwTopLayerMagic + this.target.baseTransform.value; var height = this.target.height; - otherNodes.forEach(function(o){ + otherNodes.forEach(function(o) { var off = 0; if (o.pos < 0 && move.y < 0 && o.pos > move.y) { off = height; - } - else if (o.pos > 0 && move.y > 0 && o.pos < move.y) { + } else if (o.pos > 0 && move.y > 0 && o.pos < move.y) { off = -height; } // FIXME: should change accelerated/non-accelerated state lazily - o.node.style[transformPrefix] = off ? 'translate(0,'+off+'px) ' + hwLayerMagic + o.baseTransform.value : o.baseTransform.original; + o.node.style[transformPrefix] = off ? 'translate(0,' + off + 'px) ' + hwLayerMagic + o.baseTransform.value : o.baseTransform.original; }); return false; } @@ -404,13 +437,17 @@ window['Slip'] = (function(){ this.container.style.webkitTransformStyle = ''; } - this.target.node.className = this.target.node.className.replace(/(?:^| )slip-reordering/,''); + if (this.container.focus && accessibility.container.focus) { + this.container.focus(); + } + + this.target.node.className = this.target.node.className.replace(/(?:^| )slip-reordering/, ''); this.target.node.style[userSelectPrefix] = ''; - this.animateToZero(function(target){ + this.animateToZero(function(target) { target.node.style.zIndex = ''; }); - otherNodes.forEach(function(o){ + otherNodes.forEach(function(o) { o.node.style[transformPrefix] = o.baseTransform.original; o.node.style[transitionPrefix] = ''; // FIXME: animate to new position }); @@ -422,7 +459,7 @@ window['Slip'] = (function(){ // don't let element get stuck if mouse left the window // but don't cancel immediately as it'd be annoying near window edges if (mouseOutsideTimer) clearTimeout(mouseOutsideTimer); - mouseOutsideTimer = setTimeout(function(){ + mouseOutsideTimer = setTimeout(function() { mouseOutsideTimer = null; this.cancel(); }.bind(this), 700); @@ -431,16 +468,24 @@ window['Slip'] = (function(){ onEnd: function() { var move = this.getTotalMovement(); if (move.y < 0) { - for(var i=0; i < otherNodes.length; i++) { + for (var i = 0; i < otherNodes.length; i++) { if (otherNodes[i].pos > move.y) { - this.dispatch(this.target.node, 'reorder', {spliceIndex:i, insertBefore:otherNodes[i].node, originalIndex: originalIndex}); + this.dispatch(this.target.node, 'reorder', { + spliceIndex: i, + insertBefore: otherNodes[i].node, + originalIndex: originalIndex + }); break; } } } else { - for(var i=otherNodes.length-1; i >= 0; i--) { + for (var i = otherNodes.length - 1; i >= 0; i--) { if (otherNodes[i].pos < move.y) { - this.dispatch(this.target.node, 'reorder', {spliceIndex:i+1, insertBefore:otherNodes[i+1] ? otherNodes[i+1].node : null, originalIndex: originalIndex}); + this.dispatch(this.target.node, 'reorder', { + spliceIndex: i + 1, + insertBefore: otherNodes[i + 1] ? otherNodes[i + 1].node : null, + originalIndex: originalIndex + }); break; } } @@ -464,6 +509,17 @@ window['Slip'] = (function(){ } this.container = container; + + // Accessibility + if (false !== accessibility.container.tabIndex) { + this.container.tabIndex = accessibility.container.tabIndex; + } + if (accessibility.container.ariaRole) { + this.container.setAttribute('aria-role', accessibility.container.ariaRole); + } + this.setChildNodesAriaRoles(); + this.container.addEventListener('focus', this.onContainerFocus, false); + this.otherNodes = []; // selection on iOS interferes with reordering @@ -496,7 +552,7 @@ window['Slip'] = (function(){ } }, - setState: function(newStateCtor){ + setState: function(newStateCtor) { if (this.state) { if (this.state.ctor === newStateCtor) return; if (this.state.leaveState) this.state.leaveState.call(this); @@ -512,12 +568,29 @@ window['Slip'] = (function(){ }, findTargetNode: function(targetNode) { - while(targetNode && targetNode.parentNode !== this.container) { + while (targetNode && targetNode.parentNode !== this.container) { targetNode = targetNode.parentNode; } return targetNode; }, + onContainerFocus: function(e) { + this.setChildNodesAriaRoles(); + }, + + setChildNodesAriaRoles: function() { + var nodes = this.container.childNodes; + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType != 1) continue; + if (accessibility.items.ariaRole) { + nodes[i].setAttribute('aria-role', accessibility.items.ariaRole); + } + if (false !== accessibility.items.tabIndex) { + nodes[i].tabIndex = accessibility.items.tabIndex; + } + } + }, + onSelection: function(e) { var isRelated = e.target === document || this.findTargetNode(e); if (!isRelated) return; @@ -606,9 +679,9 @@ window['Slip'] = (function(){ //check for a scrollable parent var scrollContainer = targetNode.parentNode; - while (scrollContainer){ - if (scrollContainer.scrollHeight > scrollContainer.clientHeight && window.getComputedStyle(scrollContainer)['overflow-y'] != 'visible') break; - else scrollContainer = scrollContainer.parentNode; + while (scrollContainer) { + if (scrollContainer.scrollHeight > scrollContainer.clientHeight && window.getComputedStyle(scrollContainer)['overflow-y'] != 'visible') break; + else scrollContainer = scrollContainer.parentNode; } this.target = { @@ -626,8 +699,9 @@ window['Slip'] = (function(){ }, updatePosition: function(e, pos) { - if(this.target == null) + if (this.target == null) { return; + } this.latestPosition = pos; var triggerOffset = 40, @@ -639,16 +713,15 @@ window['Slip'] = (function(){ bottomOffset = Math.min(containerRect.bottom, window.innerHeight) - targetRect.bottom, topOffset = targetRect.top - Math.max(containerRect.top, 0); - if (bottomOffset < triggerOffset){ - offset = triggerOffset - bottomOffset; - } - else if (topOffset < triggerOffset){ - offset = topOffset - triggerOffset; + if (bottomOffset < triggerOffset) { + offset = triggerOffset - bottomOffset; + } else if (topOffset < triggerOffset) { + offset = topOffset - triggerOffset; } var prevScrollTop = scrollable.scrollTop; scrollable.scrollTop += offset; - if (prevScrollTop != scrollable.scrollTop) this.startPosition.y += prevScrollTop-scrollable.scrollTop; + if (prevScrollTop != scrollable.scrollTop) this.startPosition.y += prevScrollTop - scrollable.scrollTop; if (this.state.onMove) { if (this.state.onMove.call(this) === false) { @@ -699,8 +772,8 @@ window['Slip'] = (function(){ getTotalMovement: function() { return { - x:this.latestPosition.x - this.startPosition.x, - y:this.latestPosition.y - this.startPosition.y, + x: this.latestPosition.x - this.startPosition.x, + y: this.latestPosition.y - this.startPosition.y, }; }, @@ -708,7 +781,9 @@ window['Slip'] = (function(){ return { x: Math.abs(this.latestPosition.x - this.startPosition.x), y: Math.abs(this.latestPosition.y - this.startPosition.y), - time:this.latestPosition.time - this.startPosition.time, + time: this.latestPosition.time - this.startPosition.time, + directionX: this.latestPosition.x - this.startPosition.x < 0 ? 'left' : 'right', + directionY: this.latestPosition.y - this.startPosition.y < 0 ? 'up' : 'down', }; }, @@ -727,7 +802,7 @@ window['Slip'] = (function(){ getSiblings: function(target) { var siblings = []; var tmp = target.node.nextSibling; - while(tmp) { + while (tmp) { if (tmp.nodeType == 1) siblings.push({ node: tmp, baseTransform: getTransform(tmp), @@ -741,9 +816,9 @@ window['Slip'] = (function(){ // save, because this.target/container could change during animation target = target || this.target; - // target.node.style[transitionPrefix] = transformProperty + ' 5s ease-out'; + target.node.style[transitionPrefix] = transformProperty + ' 0.1s ease-out'; target.node.style[transformPrefix] = 'translate(0,0) ' + hwLayerMagic + target.baseTransform.value; - setTimeout(function(){ + setTimeout(function() { target.node.style[transitionPrefix] = ''; target.node.style[transformPrefix] = target.baseTransform.original; if (callback) callback.call(this, target); @@ -759,23 +834,23 @@ window['Slip'] = (function(){ target.node.style[transitionPrefix] = 'all 0.1s linear'; target.node.style[transformPrefix] = ' translate(' + (this.getTotalMovement().x > 0 ? '' : '-') + '100%,0) ' + hwLayerMagic + target.baseTransform.value; - setTimeout(function(){ + setTimeout(function() { if (callback.call(this, target)) { - siblings.forEach(function(o){ + siblings.forEach(function(o) { o.node.style[transitionPrefix] = ''; o.node.style[transformPrefix] = emptySpaceTransform + o.baseTransform.value; }); - setTimeout(function(){ - siblings.forEach(function(o){ + setTimeout(function() { + siblings.forEach(function(o) { o.node.style[transitionPrefix] = transformProperty + ' 0.1s ease-in-out'; o.node.style[transformPrefix] = 'translate(0,0) ' + hwLayerMagic + o.baseTransform.value; }); - setTimeout(function(){ - siblings.forEach(function(o){ + setTimeout(function() { + siblings.forEach(function(o) { o.node.style[transitionPrefix] = ''; o.node.style[transformPrefix] = o.baseTransform.original; }); - },101); + }, 101); }, 1); } }.bind(this), 101); @@ -784,9 +859,9 @@ window['Slip'] = (function(){ // AMD if ('function' === typeof define && define.amd) { - define(function(){ + define(function() { return Slip; }); } return Slip; -})(); +})(); \ No newline at end of file diff --git a/vendor/snap.css b/vendor/snap.css deleted file mode 100644 index b09c3a6..0000000 --- a/vendor/snap.css +++ /dev/null @@ -1,64 +0,0 @@ -.snap-content { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - z-index: 2; - overflow: auto; - -webkit-overflow-scrolling: touch; - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -ms-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); -} - -.snap-drawers { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; -} - -.snap-drawer { - position: absolute; - top: 0; - right: auto; - bottom: 0; - left: auto; - width: 265px; - height: auto; - overflow: auto; - -webkit-overflow-scrolling: touch; - -webkit-transition: width 0.3s ease; - -moz-transition: width 0.3s ease; - -ms-transition: width 0.3s ease; - -o-transition: width 0.3s ease; - transition: width 0.3s ease; -} - -.snap-drawer-left { - left: 0; - z-index: 1; -} - -.snap-drawer-right { - right: 0; - z-index: 1; -} - -.snapjs-left .snap-drawer-right, -.snapjs-right .snap-drawer-left { - display: none; -} - -.snapjs-expand-left .snap-drawer-left, -.snapjs-expand-right .snap-drawer-right { - width: 100%; -} diff --git a/vendor/snap.js b/vendor/snap.js deleted file mode 100644 index 07b5a7f..0000000 --- a/vendor/snap.js +++ /dev/null @@ -1,568 +0,0 @@ -/* -* Snap.js -* -* Copyright 2013, Jacob Kelley - http://jakiestfu.com/ -* Released under the MIT Licence -* http://opensource.org/licenses/MIT -* -* Github: http://github.com/jakiestfu/Snap.js/ -* Version: 1.9.3 -*/ -/*jslint browser: true*/ -/*global define, module, ender*/ -(function(win, doc) { - 'use strict'; - var Snap = Snap || function(userOpts) { - var settings = { - element: null, - dragger: null, - disable: 'none', - addBodyClasses: true, - hyperextensible: true, - resistance: 0.5, - flickThreshold: 50, - transitionSpeed: 0.3, - easing: 'ease', - maxPosition: 266, - minPosition: -266, - tapToClose: true, - touchToDrag: true, - slideIntent: 40, // degrees - minDragDistance: 5 - }, - cache = { - simpleStates: { - opening: null, - towards: null, - hyperExtending: null, - halfway: null, - flick: null, - translation: { - absolute: 0, - relative: 0, - sinceDirectionChange: 0, - percentage: 0 - } - } - }, - eventList = {}, - utils = { - hasTouch: ('ontouchstart' in doc.documentElement || win.navigator.msPointerEnabled), - eventType: function(action) { - var eventTypes = { - down: (utils.hasTouch ? 'touchstart' : 'mousedown'), - move: (utils.hasTouch ? 'touchmove' : 'mousemove'), - up: (utils.hasTouch ? 'touchend' : 'mouseup'), - out: (utils.hasTouch ? 'touchcancel' : 'mouseout') - }; - return eventTypes[action]; - }, - page: function(t, e){ - return (utils.hasTouch && e.touches.length && e.touches[0]) ? e.touches[0]['page'+t] : e['page'+t]; - }, - klass: { - has: function(el, name){ - return (el.className).indexOf(name) !== -1; - }, - add: function(el, name){ - if(!utils.klass.has(el, name) && settings.addBodyClasses){ - el.className += " "+name; - } - }, - remove: function(el, name){ - if(settings.addBodyClasses){ - el.className = (el.className).replace(name, "").replace(/^\s+|\s+$/g, ''); - } - } - }, - dispatchEvent: function(type) { - if (typeof eventList[type] === 'function') { - return eventList[type].call(); - } - }, - vendor: function(){ - var tmp = doc.createElement("div"), - prefixes = 'webkit Moz O ms'.split(' '), - i; - for (i in prefixes) { - if (typeof tmp.style[prefixes[i] + 'Transition'] !== 'undefined') { - return prefixes[i]; - } - } - }, - transitionCallback: function(){ - return (cache.vendor==='Moz' || cache.vendor==='ms') ? 'transitionend' : cache.vendor+'TransitionEnd'; - }, - canTransform: function(){ - return typeof settings.element.style[cache.vendor+'Transform'] !== 'undefined'; - }, - deepExtend: function(destination, source) { - var property; - for (property in source) { - if (source[property] && source[property].constructor && source[property].constructor === Object) { - destination[property] = destination[property] || {}; - utils.deepExtend(destination[property], source[property]); - } else { - destination[property] = source[property]; - } - } - return destination; - }, - angleOfDrag: function(x, y) { - var degrees, theta; - // Calc Theta - theta = Math.atan2(-(cache.startDragY - y), (cache.startDragX - x)); - if (theta < 0) { - theta += 2 * Math.PI; - } - // Calc Degrees - degrees = Math.floor(theta * (180 / Math.PI) - 180); - if (degrees < 0 && degrees > -180) { - degrees = 360 - Math.abs(degrees); - } - return Math.abs(degrees); - }, - events: { - addEvent: function addEvent(element, eventName, func) { - if (element.addEventListener) { - return element.addEventListener(eventName, func, false); - } else if (element.attachEvent) { - return element.attachEvent("on" + eventName, func); - } - }, - removeEvent: function addEvent(element, eventName, func) { - if (element.addEventListener) { - return element.removeEventListener(eventName, func, false); - } else if (element.attachEvent) { - return element.detachEvent("on" + eventName, func); - } - }, - prevent: function(e) { - if (e.preventDefault) { - e.preventDefault(); - } else { - e.returnValue = false; - } - } - }, - parentUntil: function(el, attr) { - var isStr = typeof attr === 'string'; - while (el.parentNode) { - if (isStr && el.getAttribute && el.getAttribute(attr)){ - return el; - } else if(!isStr && el === attr){ - return el; - } - el = el.parentNode; - } - return null; - } - }, - action = { - translate: { - get: { - matrix: function(index) { - - if( !utils.canTransform() ){ - return parseInt(settings.element.style.left, 10); - } else { - var matrix = win.getComputedStyle(settings.element)[cache.vendor+'Transform'].match(/\((.*)\)/), - ieOffset = 8; - if (matrix) { - matrix = matrix[1].split(','); - if(matrix.length===16){ - index+=ieOffset; - } - return parseInt(matrix[index], 10); - } - return 0; - } - } - }, - easeCallback: function(){ - settings.element.style[cache.vendor+'Transition'] = ''; - cache.translation = action.translate.get.matrix(4); - cache.easing = false; - clearInterval(cache.animatingInterval); - - if(cache.easingTo===0){ - utils.klass.remove(doc.body, 'snapjs-right'); - utils.klass.remove(doc.body, 'snapjs-left'); - } - - utils.dispatchEvent('animated'); - utils.events.removeEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback); - }, - easeTo: function(n) { - - if( !utils.canTransform() ){ - cache.translation = n; - action.translate.x(n); - } else { - cache.easing = true; - cache.easingTo = n; - - settings.element.style[cache.vendor+'Transition'] = 'all ' + settings.transitionSpeed + 's ' + settings.easing; - - cache.animatingInterval = setInterval(function() { - utils.dispatchEvent('animating'); - }, 1); - - utils.events.addEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback); - action.translate.x(n); - } - if(n===0){ - settings.element.style[cache.vendor+'Transform'] = ''; - } - }, - x: function(n) { - if( (settings.disable==='left' && n>0) || - (settings.disable==='right' && n<0) - ){ return; } - - if( !settings.hyperextensible ){ - if( n===settings.maxPosition || n>settings.maxPosition ){ - n=settings.maxPosition; - } else if( n===settings.minPosition || n 0, - translateTo = whileDragX, - diff; - - // Shown no intent already - if((cache.intentChecked && !cache.hasIntent)){ - return; - } - - if(settings.addBodyClasses){ - if((absoluteTranslation)>0){ - utils.klass.add(doc.body, 'snapjs-left'); - utils.klass.remove(doc.body, 'snapjs-right'); - } else if((absoluteTranslation)<0){ - utils.klass.add(doc.body, 'snapjs-right'); - utils.klass.remove(doc.body, 'snapjs-left'); - } - } - - if (cache.hasIntent === false || cache.hasIntent === null) { - var deg = utils.angleOfDrag(thePageX, thePageY), - inRightRange = (deg >= 0 && deg <= settings.slideIntent) || (deg <= 360 && deg > (360 - settings.slideIntent)), - inLeftRange = (deg >= 180 && deg <= (180 + settings.slideIntent)) || (deg <= 180 && deg >= (180 - settings.slideIntent)); - if (!inLeftRange && !inRightRange) { - cache.hasIntent = false; - } else { - cache.hasIntent = true; - } - cache.intentChecked = true; - } - - if ( - (settings.minDragDistance>=Math.abs(thePageX-cache.startDragX)) || // Has user met minimum drag distance? - (cache.hasIntent === false) - ) { - return; - } - - utils.events.prevent(e); - utils.dispatchEvent('drag'); - - cache.dragWatchers.current = thePageX; - // Determine which direction we are going - if (cache.dragWatchers.last > thePageX) { - if (cache.dragWatchers.state !== 'left') { - cache.dragWatchers.state = 'left'; - cache.dragWatchers.hold = thePageX; - } - cache.dragWatchers.last = thePageX; - } else if (cache.dragWatchers.last < thePageX) { - if (cache.dragWatchers.state !== 'right') { - cache.dragWatchers.state = 'right'; - cache.dragWatchers.hold = thePageX; - } - cache.dragWatchers.last = thePageX; - } - if (openingLeft) { - // Pulling too far to the right - if (settings.maxPosition < absoluteTranslation) { - diff = (absoluteTranslation - settings.maxPosition) * settings.resistance; - translateTo = whileDragX - diff; - } - cache.simpleStates = { - opening: 'left', - towards: cache.dragWatchers.state, - hyperExtending: settings.maxPosition < absoluteTranslation, - halfway: absoluteTranslation > (settings.maxPosition / 2), - flick: Math.abs(cache.dragWatchers.current - cache.dragWatchers.hold) > settings.flickThreshold, - translation: { - absolute: absoluteTranslation, - relative: whileDragX, - sinceDirectionChange: (cache.dragWatchers.current - cache.dragWatchers.hold), - percentage: (absoluteTranslation/settings.maxPosition)*100 - } - }; - } else { - // Pulling too far to the left - if (settings.minPosition > absoluteTranslation) { - diff = (absoluteTranslation - settings.minPosition) * settings.resistance; - translateTo = whileDragX - diff; - } - cache.simpleStates = { - opening: 'right', - towards: cache.dragWatchers.state, - hyperExtending: settings.minPosition > absoluteTranslation, - halfway: absoluteTranslation < (settings.minPosition / 2), - flick: Math.abs(cache.dragWatchers.current - cache.dragWatchers.hold) > settings.flickThreshold, - translation: { - absolute: absoluteTranslation, - relative: whileDragX, - sinceDirectionChange: (cache.dragWatchers.current - cache.dragWatchers.hold), - percentage: (absoluteTranslation/settings.minPosition)*100 - } - }; - } - action.translate.x(translateTo + translated); - } - }, - endDrag: function(e) { - if (cache.isDragging) { - utils.dispatchEvent('end'); - var translated = action.translate.get.matrix(4); - - // Tap Close - if (cache.dragWatchers.current === 0 && translated !== 0 && settings.tapToClose) { - utils.dispatchEvent('close'); - utils.events.prevent(e); - action.translate.easeTo(0); - cache.isDragging = false; - cache.startDragX = 0; - return; - } - - // Revealing Left - if (cache.simpleStates.opening === 'left') { - // Halfway, Flicking, or Too Far Out - if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) { - if (cache.simpleStates.flick && cache.simpleStates.towards === 'left') { // Flicking Closed - action.translate.easeTo(0); - } else if ( - (cache.simpleStates.flick && cache.simpleStates.towards === 'right') || // Flicking Open OR - (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending - ) { - action.translate.easeTo(settings.maxPosition); // Open Left - } - } else { - action.translate.easeTo(0); // Close Left - } - // Revealing Right - } else if (cache.simpleStates.opening === 'right') { - // Halfway, Flicking, or Too Far Out - if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) { - if (cache.simpleStates.flick && cache.simpleStates.towards === 'right') { // Flicking Closed - action.translate.easeTo(0); - } else if ( - (cache.simpleStates.flick && cache.simpleStates.towards === 'left') || // Flicking Open OR - (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending - ) { - action.translate.easeTo(settings.minPosition); // Open Right - } - } else { - action.translate.easeTo(0); // Close Right - } - } - cache.isDragging = false; - cache.startDragX = utils.page('X', e); - } - } - } - }, - init = function(opts) { - if (opts.element) { - utils.deepExtend(settings, opts); - cache.vendor = utils.vendor(); - action.drag.listen(); - } - }; - /* - * Public - */ - this.open = function(side) { - utils.dispatchEvent('open'); - utils.klass.remove(doc.body, 'snapjs-expand-left'); - utils.klass.remove(doc.body, 'snapjs-expand-right'); - - if (side === 'left') { - cache.simpleStates.opening = 'left'; - cache.simpleStates.towards = 'right'; - utils.klass.add(doc.body, 'snapjs-left'); - utils.klass.remove(doc.body, 'snapjs-right'); - action.translate.easeTo(settings.maxPosition); - } else if (side === 'right') { - cache.simpleStates.opening = 'right'; - cache.simpleStates.towards = 'left'; - utils.klass.remove(doc.body, 'snapjs-left'); - utils.klass.add(doc.body, 'snapjs-right'); - action.translate.easeTo(settings.minPosition); - } - }; - this.close = function() { - utils.dispatchEvent('close'); - action.translate.easeTo(0); - }; - this.expand = function(side){ - var to = win.innerWidth || doc.documentElement.clientWidth; - - if(side==='left'){ - utils.dispatchEvent('expandLeft'); - utils.klass.add(doc.body, 'snapjs-expand-left'); - utils.klass.remove(doc.body, 'snapjs-expand-right'); - } else { - utils.dispatchEvent('expandRight'); - utils.klass.add(doc.body, 'snapjs-expand-right'); - utils.klass.remove(doc.body, 'snapjs-expand-left'); - to *= -1; - } - action.translate.easeTo(to); - }; - - this.on = function(evt, fn) { - eventList[evt] = fn; - return this; - }; - this.off = function(evt) { - if (eventList[evt]) { - eventList[evt] = false; - } - }; - - this.enable = function() { - utils.dispatchEvent('enable'); - action.drag.listen(); - }; - this.disable = function() { - utils.dispatchEvent('disable'); - action.drag.stopListening(); - }; - - this.settings = function(opts){ - utils.deepExtend(settings, opts); - }; - - this.state = function() { - var state, - fromLeft = action.translate.get.matrix(4); - if (fromLeft === settings.maxPosition) { - state = 'left'; - } else if (fromLeft === settings.minPosition) { - state = 'right'; - } else { - state = 'closed'; - } - return { - state: state, - info: cache.simpleStates - }; - }; - init(userOpts); - }; - if ((typeof module !== 'undefined') && module.exports) { - module.exports = Snap; - } - if (typeof ender === 'undefined') { - this.Snap = Snap; - } - if ((typeof define === "function") && define.amd) { - define("snap", [], function() { - return Snap; - }); - } - }).call(this, window, document); From b7ba546bee4962cf881029598f5de1684de0fcc4 Mon Sep 17 00:00:00 2001 From: Joey Andres Date: Sat, 30 Jan 2016 22:22:26 -0700 Subject: [PATCH 4/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72ce3c9..5e4bbd9 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ meteor add jandres:ionic ### Demo of all components The demo app of various ionic components -[Demo](http://jandres-meteor-ionic.meteor.com/) | [Code](https://github.com/JoeyAndres/ionic-demo) +[Demo](http://jandres-ionic.meteor.com/) | [Code](https://github.com/JoeyAndres/ionic-demo) ## Pre-fork Examples From 5c94fe8d852a20d2e8a65d6df5842b9e770d1312 Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Mon, 1 Feb 2016 18:58:30 -0700 Subject: [PATCH 5/9] ionContent hasBounce attribute. --- components/ionContent/ionContent.js | 73 ++++++++++------- .../ionSideMenuContainer.js | 80 ++++++++++--------- package.js | 2 +- 3 files changed, 89 insertions(+), 66 deletions(-) diff --git a/components/ionContent/ionContent.js b/components/ionContent/ionContent.js index 5ec59b4..51badc2 100644 --- a/components/ionContent/ionContent.js +++ b/components/ionContent/ionContent.js @@ -1,39 +1,56 @@ +Template.ionContent.onCreated(function() { + this.ionSideMenuContainerParent = this.parent((t) => t.view.name === "Template.ionSideMenuContainer", true); + + _.extend(this, { + hasBouncing: (!!this.ionSideMenuContainerParent && this.ionSideMenuContainerParent.hasBouncing) || new ReactiveVar(null), + itemComplex: parent.itemComplex + }); + + this.autorun(() => { + if (!Template.currentData()) return; + let hasBouncing = Template.currentData().hasBouncing; + if (!!this.ionSideMenuContainerParent) { + this.ionSideMenuContainerParent.hasBouncing.set(hasBouncing); + } + }); +}); + Template.ionContent.helpers({ - classes: function () { - var classes = ['content']; + classes: function () { + var classes = ['content']; - if (this.class) { - classes.push(this.class); - } + if (this.class) { + classes.push(this.class); + } - if (this.scroll !== false) { - classes.push('overflow-scroll'); - } + if (this.scroll !== false) { + classes.push('overflow-scroll'); + } - if (Session.get('hasHeader')) { - classes.push('has-header'); - } + if (Session.get('hasHeader')) { + classes.push('has-header'); + } - if (Session.get('hasSubheader')) { - classes.push('has-subheader'); - } + if (Session.get('hasSubheader')) { + classes.push('has-subheader'); + } - if (Session.get('hasTabs')) { - classes.push('has-tabs'); - } + if (Session.get('hasTabs')) { + classes.push('has-tabs'); + } - if (Session.get('hasTabsTop')) { - classes.push('has-tabs-top'); - } + if (Session.get('hasTabsTop')) { + classes.push('has-tabs-top'); + } - if (Session.get('hasFooter')) { - classes.push('has-footer'); - } + if (Session.get('hasFooter')) { + classes.push('has-footer'); + } - if (Session.get('hasSubfooter')) { - classes.push('has-subfooter'); - } + if (Session.get('hasSubfooter')) { + classes.push('has-subfooter'); + } - return classes.join(' '); - } + return classes.join(' '); + } }); diff --git a/components/ionSideMenuContainer/ionSideMenuContainer.js b/components/ionSideMenuContainer/ionSideMenuContainer.js index 35beab2..08f4cb7 100644 --- a/components/ionSideMenuContainer/ionSideMenuContainer.js +++ b/components/ionSideMenuContainer/ionSideMenuContainer.js @@ -1,49 +1,55 @@ IonSideMenu = { - snapper: null, // Make this private in the future. + snapper: null, // Make this private in the future. - // Note that meteor renders most deeply nested template first before the outer most. - // Thus, if the child happens toc all any of these, before we are rendered, then - // error occurs. To solve that, dummy functions are provided. - enable: () => {}, - disable: () => {} + // Note that meteor renders most deeply nested template first before the outer most. + // Thus, if the child happens toc all any of these, before we are rendered, then + // error occurs. To solve that, dummy functions are provided. + enable: () => {}, + disable: () => {} }; Template.ionSideMenuContainer.onCreated(function () { - this.data = this.data || {}; - this.side = this.data.side || 'both'; - this.dragContent = true; - if (typeof this.data.dragContent != 'undefined') { - this.dragContent = this.data.dragContent - } + this.data = this.data || {}; + this.side = this.data.side || 'both'; + this.dragContent = true; + + this.hasBouncing = new ReactiveVar(true); + + if (typeof this.data.dragContent != 'undefined') { + this.dragContent = this.data.dragContent + } }); Template.ionSideMenuContainer.onRendered(function () { - $snapperEl = this.$('.snap-content'); - if (!$snapperEl) { - return; - } - - var disable; - if (this.side == 'both') { - disable = 'none'; - } - if (this.side == 'left') { - disable = 'right'; - } - if (this.side == 'right') { - disable = 'left'; - } - - IonSideMenu.snapper = new Snap({ - element: $snapperEl.get(0), - disable: disable, - touchToDrag: this.dragContent - }); - - IonSideMenu.enable = () => IonSideMenu.snapper.enable(); - IonSideMenu.disable = () => IonSideMenu.snapper.disable(); + $snapperEl = this.$('.snap-content'); + if (!$snapperEl) { + return; + } + + var disable; + if (this.side == 'both') { + disable = 'none'; + } + if (this.side == 'left') { + disable = 'right'; + } + if (this.side == 'right') { + disable = 'left'; + } + + IonSideMenu.snapper = new Snap({ + element: $snapperEl.get(0), + disable: disable, + touchToDrag: this.dragContent + }); + + IonSideMenu.enable = () => IonSideMenu.snapper.enable(); + IonSideMenu.disable = () => IonSideMenu.snapper.disable(); + + // Watch hasBounce attribute. + this.autorun(() => { IonSideMenu.snapper.settings({ hyperextensible: this.hasBouncing.get() }); }); }); Template.ionSideMenuContainer.onDestroyed(function () { - IonSideMenu.snapper = null; + IonSideMenu.snapper = null; }); diff --git a/package.js b/package.js index 7acd1f5..300ace6 100644 --- a/package.js +++ b/package.js @@ -1,7 +1,7 @@ Package.describe({ name: "jandres:ionic", summary: "Ionic components for Meteor. No Angular!", - version: "0.1.39", + version: "0.1.40", git: "https://github.com/JoeyAndres/meteor-ionic.git" }); From 1a484adaf8ebe3cce7c77c18dcee58a0f65b18d1 Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Mon, 1 Feb 2016 19:11:37 -0700 Subject: [PATCH 6/9] Some copy/paste error from last commit --- components/ionContent/ionContent.js | 3 +-- package.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/ionContent/ionContent.js b/components/ionContent/ionContent.js index 51badc2..02ccddc 100644 --- a/components/ionContent/ionContent.js +++ b/components/ionContent/ionContent.js @@ -2,8 +2,7 @@ Template.ionContent.onCreated(function() { this.ionSideMenuContainerParent = this.parent((t) => t.view.name === "Template.ionSideMenuContainer", true); _.extend(this, { - hasBouncing: (!!this.ionSideMenuContainerParent && this.ionSideMenuContainerParent.hasBouncing) || new ReactiveVar(null), - itemComplex: parent.itemComplex + hasBouncing: (!!this.ionSideMenuContainerParent && this.ionSideMenuContainerParent.hasBouncing) || new ReactiveVar(null) }); this.autorun(() => { diff --git a/package.js b/package.js index 300ace6..9eeece8 100644 --- a/package.js +++ b/package.js @@ -1,7 +1,7 @@ Package.describe({ name: "jandres:ionic", summary: "Ionic components for Meteor. No Angular!", - version: "0.1.40", + version: "0.1.41", git: "https://github.com/JoeyAndres/meteor-ionic.git" }); @@ -28,7 +28,7 @@ Package.onUse(function(api) { "tracker", "session", "jquery", - "jandres:snapjs@2.0.1", + "jandres:snapjs@2.0.2", "fourseven:scss@3.3.3" ], "client"); From e4d51bc043ed5a225533f83d59096971d7c9982c Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Wed, 3 Feb 2016 23:29:57 -0700 Subject: [PATCH 7/9] ionScroll ionScroll can now be used with almost all the ionic 1.2 camelCase version of paremeters (except events like onScroll/onZooming). To be able to be fully featured, iscroll would need to be modified heavily. --- README.md | 4 +- components/ionScroll/ionScroll.html | 9 ++++ components/ionScroll/ionScroll.js | 76 +++++++++++++++++++++++++++++ package.js | 9 ++-- 4 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 components/ionScroll/ionScroll.html create mode 100644 components/ionScroll/ionScroll.js diff --git a/README.md b/README.md index 5e4bbd9..38d065b 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ A [Product Hunt](http://producthunt.com) clone built in Meteor Ionic. (In Progre * [x] Popover * [x] Popup * [ ] Scroll - * [ ] ion-scroll + * [x] ion-scroll: Need to modify iscroll (massive re-engineering and cleanup) to add features. * [ ] ion-infinite-scroll * [x] Side Menus * [x] ion-side-menus @@ -108,7 +108,7 @@ A [Product Hunt](http://producthunt.com) clone built in Meteor Ionic. (In Progre ### Code Style Change: These are code styles that I want to impose on this forked repo. -* Get rid of all session variables [ ] +* [ ] Get rid of all session variables ## License [MIT License](https://github.com/meteoric/meteor-ionic/blob/master/LICENSE) diff --git a/components/ionScroll/ionScroll.html b/components/ionScroll/ionScroll.html new file mode 100644 index 0000000..2d08b13 --- /dev/null +++ b/components/ionScroll/ionScroll.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/components/ionScroll/ionScroll.js b/components/ionScroll/ionScroll.js new file mode 100644 index 0000000..2f80836 --- /dev/null +++ b/components/ionScroll/ionScroll.js @@ -0,0 +1,76 @@ +Template.ionScroll.onCreated(function() { + this.nativeScrolling = new ReactiveVar(false); + this.direction = new ReactiveVar('y'); + this.locking = new ReactiveVar(true); + this.paging = new ReactiveVar(true); + this.onRefresh = new ReactiveVar(true); + this.onScroll = new ReactiveVar(true); + this.scrollBarX = new ReactiveVar(true); + this.scrollBarY = new ReactiveVar(true); + this.zooming = new ReactiveVar(true); + this.minZoom = new ReactiveVar(true); + this.maxZoom = new ReactiveVar(true); + this.hasBouncing = new ReactiveVar(true); + + _.extend(this, { + set_direction: direction => direction !== this.direction.get() && this.direction.set(_.isUndefined(direction) ? 'y' : direction), + set_locking: locking => locking !== this.locking.get() && this.locking.set(_.isUndefined(locking) ? true : locking), + set_paging: paging => paging !== this.paging.get() && this.paging.set(!!paging), + set_onRefresh: onRefresh => onRefresh !== this.onRefresh.get() && this.onRefresh.set(onRefresh), + set_onScroll: onScroll => onScroll !== this.onScroll.get() && this.onScroll.set(onScroll), + set_scrollBarX: scrollBarX => scrollBarX !== this.scrollBarX.get() && this.scrollBarX.set(_.isUndefined(scrollBarX) ? true : scrollBarX), + set_scrollBarY: scrollBarY => scrollBarY !== this.scrollBarY.get() && this.scrollBarY.set(_.isUndefined(scrollBarY) ? true : scrollBarY), + set_zooming: zooming => zooming !== this.zooming.get() && this.zooming.set(zooming), + set_minZoom: minZoom => minZoom !== this.minZoom.get() && this.minZoom.set(_.isUndefined(minZoom) ? minZoom : 0.5), + set_maxZoom: maxZoom => maxZoom !== this.maxZoom.get() && this.maxZoom.set(_.isUndefined(maxZoom) ? maxZoom : 3), + set_hasBouncing: hasBouncing => hasBouncing !== this.hasBouncing.get() && this.hasBouncing.set(_.isUndefined(hasBouncing) ? true: hasBouncing) // todo: Make this platform dependent. + }); + + this.autorun(() => { + if (!Template.currentData()) return; + this.nativeScrolling.set(!!Template.currentData().overflowScroll); + + this.set_direction(Template.currentData().direction); + this.set_locking(Template.currentData().locking); + this.set_paging(Template.currentData().paging); + this.set_onRefresh(Template.currentData().onRefresh); + this.set_onScroll(Template.currentData().onScroll); + this.set_scrollBarX(Template.currentData().scrollBarX); + this.set_scrollBarY(Template.currentData().scrollBarY); + this.set_zooming(Template.currentData().zooming); + this.set_minZoom(Template.currentData().minZoom); + this.set_maxZoom(Template.currentData().maxZoom); + this.set_hasBouncing(Template.currentData().hasBouncing); + }); +}); + +Template.ionScroll.onRendered(function() { + if (!this.nativeScrolling.get()) { // todo: make this reactive? Is there a use case? + let scroller = new IScroll(this.$(".scroller-wrapper").get(0)); + + this.autorun(() => { + scroller.options = _.extend(scroller.options, { + scrollX: !!this.direction.get() && this.direction.get().indexOf('x') !== -1, + scrollY: !!this.direction.get() && this.direction.get().indexOf('y') !== -1, + freeScroll: !this.locking.get(), + zoomMin: this.minZoom.get(), + zoomMax: this.maxZoom.get(), + bounce: !!this.hasBouncing.get() + }); + + // todo: add a removeEvent or .off + //scroller.on('scroll', !!this.onScroll.get() ? this.onScroll.get() : () => {}); // Have a way to remove events. + //scroller.on('zooming'); // todo: modify iscroll. + + scroller.refresh(); + }); + + this.$(".scroller-wrapper").children().load(() => scroller.refresh()); + } +}); + +Template.ionScroll.helpers({ + nativeScrollingClass: function() { + return Template.instance().nativeScrolling.get() ? 'overflow-scroll' : ''; + } +}); \ No newline at end of file diff --git a/package.js b/package.js index 9eeece8..e9dedff 100644 --- a/package.js +++ b/package.js @@ -1,7 +1,7 @@ Package.describe({ name: "jandres:ionic", summary: "Ionic components for Meteor. No Angular!", - version: "0.1.41", + version: "0.1.42", git: "https://github.com/JoeyAndres/meteor-ionic.git" }); @@ -13,9 +13,6 @@ Cordova.depends({ Package.onUse(function(api) { api.versionsFrom("1.0"); - api.use([ - "fourseven:scss@3.3.3" - ]); api.use([ "jandres:template-extension@4.0.4", @@ -28,6 +25,7 @@ Package.onUse(function(api) { "tracker", "session", "jquery", + "raix:iscroll", "jandres:snapjs@2.0.2", "fourseven:scss@3.3.3" ], "client"); @@ -118,6 +116,9 @@ Package.onUse(function(api) { "components/ionReorderButton/ionReorderButton.html", "components/ionReorderButton/ionReorderButton.js", + "components/ionScroll/ionScroll.html", + "components/ionScroll/ionScroll.js", + "components/ionSideMenu/ionSideMenu.html", "components/ionSideMenu/ionSideMenu.js", From cb52f46011a705061ff16aef0d28371686b5db8f Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Thu, 4 Feb 2016 07:04:19 -0700 Subject: [PATCH 8/9] Got rid of diff check after looking at reactiveVar. --- components/ionScroll/ionScroll.js | 28 ++++++++++++++-------------- package.js | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/ionScroll/ionScroll.js b/components/ionScroll/ionScroll.js index 2f80836..9be5b91 100644 --- a/components/ionScroll/ionScroll.js +++ b/components/ionScroll/ionScroll.js @@ -13,17 +13,17 @@ Template.ionScroll.onCreated(function() { this.hasBouncing = new ReactiveVar(true); _.extend(this, { - set_direction: direction => direction !== this.direction.get() && this.direction.set(_.isUndefined(direction) ? 'y' : direction), - set_locking: locking => locking !== this.locking.get() && this.locking.set(_.isUndefined(locking) ? true : locking), - set_paging: paging => paging !== this.paging.get() && this.paging.set(!!paging), - set_onRefresh: onRefresh => onRefresh !== this.onRefresh.get() && this.onRefresh.set(onRefresh), - set_onScroll: onScroll => onScroll !== this.onScroll.get() && this.onScroll.set(onScroll), - set_scrollBarX: scrollBarX => scrollBarX !== this.scrollBarX.get() && this.scrollBarX.set(_.isUndefined(scrollBarX) ? true : scrollBarX), - set_scrollBarY: scrollBarY => scrollBarY !== this.scrollBarY.get() && this.scrollBarY.set(_.isUndefined(scrollBarY) ? true : scrollBarY), - set_zooming: zooming => zooming !== this.zooming.get() && this.zooming.set(zooming), - set_minZoom: minZoom => minZoom !== this.minZoom.get() && this.minZoom.set(_.isUndefined(minZoom) ? minZoom : 0.5), - set_maxZoom: maxZoom => maxZoom !== this.maxZoom.get() && this.maxZoom.set(_.isUndefined(maxZoom) ? maxZoom : 3), - set_hasBouncing: hasBouncing => hasBouncing !== this.hasBouncing.get() && this.hasBouncing.set(_.isUndefined(hasBouncing) ? true: hasBouncing) // todo: Make this platform dependent. + set_direction: direction => this.direction.set(_.isUndefined(direction) ? 'y' : direction), + set_locking: locking => this.locking.set(_.isUndefined(locking) ? true : locking), + set_paging: paging => this.paging.set(!!paging), + set_onRefresh: onRefresh => this.onRefresh.set(onRefresh), + set_onScroll: onScroll => this.onScroll.set(onScroll), + set_scrollBarX: scrollBarX => this.scrollBarX.set(_.isUndefined(scrollBarX) ? true : scrollBarX), + set_scrollBarY: scrollBarY => this.scrollBarY.set(_.isUndefined(scrollBarY) ? true : scrollBarY), + set_zooming: zooming => this.zooming.set(zooming), + set_minZoom: minZoom => this.minZoom.set(_.isUndefined(minZoom) ? minZoom : 0.5), + set_maxZoom: maxZoom => this.maxZoom.set(_.isUndefined(maxZoom) ? maxZoom : 3), + set_hasBouncing: hasBouncing => this.hasBouncing.set(_.isUndefined(hasBouncing) ? true: hasBouncing) // todo: Make this platform dependent. }); this.autorun(() => { @@ -50,12 +50,12 @@ Template.ionScroll.onRendered(function() { this.autorun(() => { scroller.options = _.extend(scroller.options, { - scrollX: !!this.direction.get() && this.direction.get().indexOf('x') !== -1, - scrollY: !!this.direction.get() && this.direction.get().indexOf('y') !== -1, + scrollX: this.direction.get().indexOf('x') !== -1, + scrollY: this.direction.get().indexOf('y') !== -1, freeScroll: !this.locking.get(), zoomMin: this.minZoom.get(), zoomMax: this.maxZoom.get(), - bounce: !!this.hasBouncing.get() + bounce: this.hasBouncing.get() }); // todo: add a removeEvent or .off diff --git a/package.js b/package.js index e9dedff..2d2f0e8 100644 --- a/package.js +++ b/package.js @@ -25,7 +25,7 @@ Package.onUse(function(api) { "tracker", "session", "jquery", - "raix:iscroll", + "raix:iscroll@0.0.3", "jandres:snapjs@2.0.2", "fourseven:scss@3.3.3" ], "client"); From 1303f55c142403ac3f1141ad099c15f64175a93b Mon Sep 17 00:00:00 2001 From: Joey Arnold Date: Fri, 5 Feb 2016 00:35:59 -0700 Subject: [PATCH 9/9] changed to jandres:iscroll (gulpified) --- components/ionScroll/ionScroll.js | 37 ++++++++++++++++++++----------- package.js | 4 ++-- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/components/ionScroll/ionScroll.js b/components/ionScroll/ionScroll.js index 9be5b91..6af1c15 100644 --- a/components/ionScroll/ionScroll.js +++ b/components/ionScroll/ionScroll.js @@ -4,23 +4,30 @@ Template.ionScroll.onCreated(function() { this.locking = new ReactiveVar(true); this.paging = new ReactiveVar(true); this.onRefresh = new ReactiveVar(true); - this.onScroll = new ReactiveVar(true); + this.onScroll = new ReactiveVar(() => {}); this.scrollBarX = new ReactiveVar(true); this.scrollBarY = new ReactiveVar(true); - this.zooming = new ReactiveVar(true); - this.minZoom = new ReactiveVar(true); - this.maxZoom = new ReactiveVar(true); + this.zooming = new ReactiveVar(false); + this.minZoom = new ReactiveVar(0.5); + this.maxZoom = new ReactiveVar(3); this.hasBouncing = new ReactiveVar(true); + this.scroller = null; + _.extend(this, { set_direction: direction => this.direction.set(_.isUndefined(direction) ? 'y' : direction), set_locking: locking => this.locking.set(_.isUndefined(locking) ? true : locking), set_paging: paging => this.paging.set(!!paging), set_onRefresh: onRefresh => this.onRefresh.set(onRefresh), - set_onScroll: onScroll => this.onScroll.set(onScroll), + set_onScroll: onScroll => { + if (_.isFunction(onScroll)) { + if (!!this.scroller) this.scroller.off(this.onScroll.get(onScroll)); + this.onScroll.set(onScroll); + } + }, set_scrollBarX: scrollBarX => this.scrollBarX.set(_.isUndefined(scrollBarX) ? true : scrollBarX), set_scrollBarY: scrollBarY => this.scrollBarY.set(_.isUndefined(scrollBarY) ? true : scrollBarY), - set_zooming: zooming => this.zooming.set(zooming), + set_zooming: zooming => this.zooming.set(!!zooming), set_minZoom: minZoom => this.minZoom.set(_.isUndefined(minZoom) ? minZoom : 0.5), set_maxZoom: maxZoom => this.maxZoom.set(_.isUndefined(maxZoom) ? maxZoom : 3), set_hasBouncing: hasBouncing => this.hasBouncing.set(_.isUndefined(hasBouncing) ? true: hasBouncing) // todo: Make this platform dependent. @@ -46,26 +53,30 @@ Template.ionScroll.onCreated(function() { Template.ionScroll.onRendered(function() { if (!this.nativeScrolling.get()) { // todo: make this reactive? Is there a use case? - let scroller = new IScroll(this.$(".scroller-wrapper").get(0)); + this.scroller = new IScroll(this.$(".scroller-wrapper").get(0)); + + if (!!this.onScroll.get()) { this.scroller.on('scroll', this.onScroll.get()); } this.autorun(() => { - scroller.options = _.extend(scroller.options, { + this.scroller.options = _.extend(this.scroller.options, { scrollX: this.direction.get().indexOf('x') !== -1, scrollY: this.direction.get().indexOf('y') !== -1, freeScroll: !this.locking.get(), + zoom: this.zooming.get(), zoomMin: this.minZoom.get(), zoomMax: this.maxZoom.get(), bounce: this.hasBouncing.get() }); - // todo: add a removeEvent or .off - //scroller.on('scroll', !!this.onScroll.get() ? this.onScroll.get() : () => {}); // Have a way to remove events. - //scroller.on('zooming'); // todo: modify iscroll. + this.scroller.refresh(); + }); - scroller.refresh(); + this.autorun(() => { + // only available in ionscroll-probe. + //if (_.isFunction(this.onScroll.get())) { console.log(this.onScroll.get()); this.scroller.on('onScroll', this.onScroll.get()); } }); - this.$(".scroller-wrapper").children().load(() => scroller.refresh()); + this.$(".scroller-wrapper").children().load(() => this.scroller.refresh()); } }); diff --git a/package.js b/package.js index 2d2f0e8..c745900 100644 --- a/package.js +++ b/package.js @@ -1,7 +1,7 @@ Package.describe({ name: "jandres:ionic", summary: "Ionic components for Meteor. No Angular!", - version: "0.1.42", + version: "0.1.43", git: "https://github.com/JoeyAndres/meteor-ionic.git" }); @@ -25,7 +25,7 @@ Package.onUse(function(api) { "tracker", "session", "jquery", - "raix:iscroll@0.0.3", + "jandres:iscroll-zoom@5.1.4", "jandres:snapjs@2.0.2", "fourseven:scss@3.3.3" ], "client");