Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: slidebox #2288

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions config/build.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = {
'js/utils/tap.js',
'js/utils/activator.js',
'js/utils/utils.js',
'js/utils/list.js',
'js/utils/keyboard.js',
'js/utils/viewport.js',

Expand All @@ -57,12 +58,7 @@ module.exports = {
'js/views/listView.js',
'js/views/modalView.js',
'js/views/sideMenuView.js',
'js/views/sliderView.js',
'js/views/toggleView.js',

// Controllers
'js/controllers/viewController.js',
'js/controllers/sideMenuController.js',
],

angularIonicFiles: [
Expand Down
12 changes: 12 additions & 0 deletions config/docs/templates/api_menu_version.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,18 @@
</a>
</li>

<li>
<a href="{{ page.versionHref }}/api/directive/ionSlidePager/">
ion-slide-pager
</a>
</li>

<li>
<a href="{{ page.versionHref }}/api/directive/ionSlide/">
ion-slide
</a>
</li>

<li>
<a href="{{ page.versionHref }}/api/service/$ionicSlideBoxDelegate/">
$ionicSlideBoxDelegate
Expand Down
263 changes: 263 additions & 0 deletions js/angular/controller/slideBoxController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
IonicModule
.controller('$ionSlideBox', [
'$scope',
'$element',
'$$ionicAttachDrag',
'$interval',
/*
* This can be abstracted into a controller that will work for views, tabs, and
* slidebox.
*/
function(scope, element, $$ionicAttachDrag, $interval) {
var self = this;
var slideList = ionic.Utils.list([]);
var selectedIndex = -1;
var slidesParent = angular.element(element[0].querySelector('.slider-slides'));

// Successful slide requires velocity to be greater than this amount
var SLIDE_SUCCESS_VELOCITY = (1 / 4); // pixels / ms
var SLIDE_TRANSITION_DURATION = 250; //ms

$$ionicAttachDrag(scope, element, {
getDistance: function() { return slidesParent.prop('offsetWidth'); },
onDrag: onDrag,
onDragEnd: onDragEnd
});

self.element = element;
self.isRelevant = isRelevant;
self.previous = previous;
self.next = next;

// Methods calling straight back to Utils.list
self.at = slideList.at;
self.count = slideList.count;
self.indexOf = slideList.indexOf;
self.isInRange = slideList.isInRange;
self.loop = slideList.loop;
self.delta = slideList.delta;

self.enableSlide = enableSlide;
self.autoPlay = autoPlay;
self.add = add;
self.remove = remove;
self.move = move;
self.selected = selected;
self.select = select;
self.onDrag = onDrag;
self.onDragEnd = onDragEnd;

// ***
// Public Methods
// ***

// Gets whether the given index is relevant to selected
// That is, whether the given index is previous, selected, or next
function isRelevant(index) {
return slideList.isRelevant(index, selectedIndex);
}

// Gets the index to the previous of the given slide, default selectedIndex
function previous(index) {
index = arguments.length ? index : selectedIndex;
// If we only have two slides and loop is enabled, we cannot have a previous
// because previous === next. In this case, return -1.
if (self.loop() && self.count() === 2) {
return -1;
}
return slideList.previous(index);
}

// Gets the index to the next of the given slide, default selectedIndex
function next(index) {
index = arguments.length ? index : selectedIndex;
return slideList.next(index);
}

function enableSlide(isEnabled) {
if (arguments.length) {
self.dragDisabled = !isEnabled;
}
return !!self.dragDisabled;
}

function autoPlay(newInterval) {
$interval.cancel(self.autoPlayTimeout);

if (angular.isNumber(newInterval) && newInterval > 0) {
self.autoPlayTimeout = $interval(function() {
self.select(self.next());
}, newInterval);
}
}

/*
* Add/remove/move slides
*/
function add(slide, index) {
var newIndex = slideList.add(slide, index);
slide.onAdded(slidesParent);

if (selectedIndex === -1) {
self.select(newIndex);
} else if (newIndex === self.previous() || newIndex === self.next()) {
// if the new slide is adjacent to selected, refresh the selection
enqueueRefresh();
}
}
function remove(slide) {
var index = self.indexOf(slide);
if (index === -1) return;

var isSelected = self.selected() === index;
slideList.remove(index);
slide.onRemoved();

if (isSelected) {
self.select( self.isInRange(selectedIndex) ? selectedIndex : selectedIndex - 1 );
}
}
function move(slide, targetIndex) {
var index = self.indexOf(slide);
if (index === -1) return;

// If the slide is current, next, or previous, save so we can re-select after moving.
var isRelevant = self.isRelevant(targetIndex);
slideList.remove(index);
slideList.add(slide, targetIndex);

if (isRelevant) {
enqueueRefresh();
}
}

function selected() {
return selectedIndex;
}

/*
* Select and change slides
*/
function select(newIndex, transitionDuration) {
if (!self.isInRange(newIndex)) return;

var delta = self.delta(selectedIndex, newIndex);

slidesParent.css(
ionic.CSS.TRANSITION_DURATION,
(transitionDuration || SLIDE_TRANSITION_DURATION) + 'ms'
);
selectedIndex = newIndex;

if (self.isInRange(selectedIndex) && Math.abs(delta) > 1) {
// if the new slide is > 1 away, then it is currently not attached to the DOM.
// Attach it in the position from which it will slide in.
self.at(newIndex).setState(delta > 1 ? 'next' : 'previous');
// Wait one frame so the new slide can 'settle' in its new place and
// be ready to properly transition in
ionic.requestAnimationFrame(doSelect);
} else {
doSelect();
}

function doSelect() {
// If a new selection has happened before this frame, abort.
if (selectedIndex !== newIndex) return;
scope.$evalAsync(function() {
if (selectedIndex !== newIndex) return;
arrangeSlides(newIndex);
});
}
}

// percent is negative 0-1 for backward slide
// positive 0-1 for forward slide
function onDrag(percent) {
if (self.dragDisabled) return;

var target = self.at(percent > 0 ? self.next() : self.previous());
var current = self.at(self.selected());

target && target.transform(percent);
current && current.transform(percent);
}

function onDragEnd(percent, velocity) {
var nextIndex = -1;
if (Math.abs(percent) > 0.5 || velocity > SLIDE_SUCCESS_VELOCITY) {
nextIndex = percent > 0 ? self.next() : self.previous();
}
var transitionDuration = Math.min(
slidesParent.prop('offsetWidth') / (3 * velocity),
SLIDE_TRANSITION_DURATION
);

// Select a new slide if it's avaiable
self.select(
self.isInRange(nextIndex) ? nextIndex : self.selected(),
transitionDuration
);
}

// ***
// Private Methods
// ***

var oldSlides;
function arrangeSlides(newShownIndex) {
var newSlides = {
previous: self.at(self.previous(newShownIndex)),
selected: self.at(newShownIndex),
next: self.at(self.next(newShownIndex))
};

newSlides.previous && newSlides.previous.setState('previous');
newSlides.selected && newSlides.selected.setState('selected');
newSlides.next && newSlides.next.setState('next');

if (oldSlides) {
var oldShown = oldSlides.selected;
var delta = self.delta(self.indexOf(oldSlides.selected), self.indexOf(newSlides.selected));
if (Math.abs(delta) > 1) {
// If we're changing by more than one slide, we need to manually transition
// the current slide out and then put it into its new state.
oldShown.setState(delta > 1 ? 'previous' : 'next').then(function() {
oldShown.setState(
newSlides.previous === oldShown ? 'previous' :
newSlides.next === oldShown ? 'next' :
'detached'
);
});
} else {
detachIfUnused(oldSlides.selected);
}
//Additionally, we need to detach both of the old slides.
detachIfUnused(oldSlides.previous);
detachIfUnused(oldSlides.next);
}

function detachIfUnused(oldSlide) {
if (oldSlide && oldSlide !== newSlides.previous &&
oldSlide !== newSlides.selected &&
oldSlide !== newSlides.next) {
oldSlide.setState('detached');
}
}

oldSlides = newSlides;
}

// When adding/moving slides, we sometimes need to refresh
// the currently selected slides to reflect new data.
// We don't want to refresh more than once per digest cycle,
// so we do this.
function enqueueRefresh() {
if (!enqueueRefresh.queued) {
enqueueRefresh.queued = true;
scope.$$postDigest(function() {
self.select(selectedIndex);
enqueueRefresh.queued = false;
});
}
}
}]);
Loading