A Pen by Mark Hamilton on CodePen.
Created
February 8, 2016 04:53
-
-
Save TheDarkCode/c2d3a74150efadf4ba7c to your computer and use it in GitHub Desktop.
YwJmQZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="format-detection" content="telephone=no" /> | |
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> | |
<title>Tabbed Slide box</title> | |
<!-- ionic css --> | |
<link href="http://code.ionicframework.com/1.0.0-rc.4/css/ionic.css" rel="stylesheet"> | |
<!-- your app's css --> | |
<link href="tabSlideBox.css" rel="stylesheet"> | |
<!-- ionic/angularjs scripts --> | |
<script src="http://code.ionicframework.com/1.0.0-rc.4/js/ionic.bundle.min.js"></script> | |
<script src="tabSlideBox.js"></script> | |
</head> | |
<body ng-app="slidebox" animation="slide-left-right-ios7"> | |
<ion-nav-bar class="nav-title-slide-ios7 bar-positive"> | |
<ion-nav-back-button class="button-icon ion-arrow-left-c"> | |
</ion-nav-back-button> | |
</ion-nav-bar> | |
<ion-nav-view ng-controller="IndexCtrl"></ion-nav-view> | |
<script id="index.html" type="text/ng-template"> | |
<ion-view title="ion-ios-tab-view"> | |
<ion-content scroll="false"> | |
<tab-slide-box> | |
<div class="tsb-icons"> | |
<div class="tsb-ic-wrp"> | |
<ion-scroll direction="x" class="tsb-hscroll"> | |
<a href="javascript:;" class="{{tab.icon}}" ng-repeat="tab in tabs" on-finish-render="test()">{{tab.text}}</a> | |
</ion-scroll> | |
</div> | |
</div> | |
<ion-slide-box show-pager="false" on-slide-changed="slideHasChanged($index)"> | |
<ion-slide> | |
<h3>Home content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Games content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Mail content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Car content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Profile content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Favourites content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Chats content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Settings content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide> | |
<h3>Photos content</h3> | |
<div style="height: 100%; background:rgba(255,255,255,0.2);"> | |
</div> | |
</ion-slide> | |
<ion-slide><h3>End</h3></ion-slide> | |
<ion-slide></ion-slide> | |
</ion-slide-box> | |
</tab-slide-box> | |
</ion-content> | |
</ion-view> | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* SimplePubSub from https://github.com/mbenford/ngTagsInput/blob/master/src/util.js | |
* */ | |
'use strict'; | |
function SimplePubSub() { | |
var events = {}; | |
return { | |
on: function(names, handler) { | |
names.split(' ').forEach(function(name) { | |
if (!events[name]) { | |
events[name] = []; | |
} | |
events[name].push(handler); | |
}); | |
return this; | |
}, | |
trigger: function(name, args) { | |
angular.forEach(events[name], function(handler) { | |
handler.call(null, args); | |
}); | |
return this; | |
} | |
}; | |
}; | |
angular.module('tabSlideBox', []) | |
.directive('onFinishRender', function ($timeout) { | |
return { | |
restrict: 'A', | |
link: function (scope, element, attr) { | |
if (scope.$last === true) { | |
$timeout(function () { | |
scope.$emit('ngRepeatFinished'); | |
}); | |
} | |
} | |
} | |
}) | |
.directive('tabSlideBox', [ '$timeout', '$window', '$ionicSlideBoxDelegate', '$ionicScrollDelegate', | |
function($timeout, $window, $ionicSlideBoxDelegate, $ionicScrollDelegate) { | |
'use strict'; | |
return { | |
restrict : 'A, E, C', | |
link : function(scope, element, attrs, ngModel) { | |
var ta = element[0], $ta = element; | |
$ta.addClass("tabbed-slidebox"); | |
if(attrs.tabsPosition === "bottom"){ | |
$ta.addClass("btm"); | |
} | |
//Handle multiple slide/scroll boxes | |
var handle = ta.querySelector('.slider').getAttribute('delegate-handle'); | |
var ionicSlideBoxDelegate = $ionicSlideBoxDelegate; | |
if(handle){ | |
ionicSlideBoxDelegate = ionicSlideBoxDelegate.$getByHandle(handle); | |
} | |
var ionicScrollDelegate = $ionicScrollDelegate; | |
if(handle){ | |
ionicScrollDelegate = ionicScrollDelegate.$getByHandle(handle); | |
} | |
function renderScrollableTabs(){ | |
var iconsDiv = angular.element(ta.querySelector(".tsb-icons")), icons = iconsDiv.find("a"), wrap = iconsDiv[0].querySelector(".tsb-ic-wrp"), totalTabs = icons.length; | |
var scrollDiv = wrap.querySelector(".scroll"); | |
angular.forEach(icons, function(value, key){ | |
var a = angular.element(value); | |
a.on('click', function(){ | |
ionicSlideBoxDelegate.slide(key); | |
}); | |
if(a.attr('icon-off')) { | |
a.attr("class", a.attr('icon-off')); | |
} | |
}); | |
var initialIndex = attrs.tab; | |
//Initializing the middle tab | |
if(typeof attrs.tab === 'undefined' || (totalTabs <= initialIndex) || initialIndex < 0){ | |
initialIndex = Math.floor(icons.length/2); | |
} | |
//If initial element is 0, set position of the tab to 0th tab | |
if(initialIndex == 0){ | |
setPosition(0); | |
} | |
$timeout(function() { | |
ionicSlideBoxDelegate.slide(initialIndex); | |
}, 0); | |
} | |
function setPosition(index){ | |
var iconsDiv = angular.element(ta.querySelector(".tsb-icons")), icons = iconsDiv.find("a"), wrap = iconsDiv[0].querySelector(".tsb-ic-wrp"), totalTabs = icons.length; | |
var scrollDiv = wrap.querySelector(".scroll"); | |
var middle = iconsDiv[0].offsetWidth/2; | |
var curEl = angular.element(icons[index]); | |
var prvEl = angular.element(iconsDiv[0].querySelector(".active")); | |
if(curEl && curEl.length){ | |
var curElWidth = curEl[0].offsetWidth, curElLeft = curEl[0].offsetLeft; | |
if(prvEl.attr('icon-off')) { | |
prvEl.attr("class", prvEl.attr('icon-off')); | |
}else{ | |
prvEl.removeClass("active"); | |
} | |
if(curEl.attr('icon-on')) { | |
curEl.attr("class", curEl.attr('icon-on')); | |
} | |
curEl.addClass("active"); | |
var leftStr = (middle - (curElLeft) - curElWidth/2 + 5); | |
//If tabs are not scrollable | |
if(!scrollDiv){ | |
var leftStr = (middle - (curElLeft) - curElWidth/2 + 5) + "px"; | |
wrap.style.webkitTransform = "translate3d("+leftStr+",0,0)" ; | |
}else{ | |
//If scrollable tabs | |
var wrapWidth = wrap.offsetWidth; | |
var currentX = Math.abs(getX(scrollDiv.style.webkitTransform)); | |
var leftOffset = 100; | |
var elementOffset = 40; | |
//If tabs are reaching right end or left end | |
if(((currentX + wrapWidth) < (curElLeft + curElWidth + elementOffset)) || (currentX > (curElLeft - leftOffset))){ | |
if(leftStr > 0){ | |
leftStr = 0; | |
} | |
//Use this scrollTo, so when scrolling tab manually will not flicker | |
ionicScrollDelegate.scrollTo(Math.abs(leftStr), 0, true); | |
} | |
} | |
} | |
}; | |
function getX(matrix) { | |
matrix = matrix.replace("translate3d(",""); | |
matrix = matrix.replace("translate(",""); | |
return (parseInt(matrix)); | |
} | |
var events = scope.events; | |
events.on('slideChange', function(data){ | |
setPosition(data.index); | |
}); | |
events.on('ngRepeatFinished', function(ngRepeatFinishedEvent) { | |
renderScrollableTabs(); | |
}); | |
renderScrollableTabs(); | |
}, | |
controller : function($scope, $attrs, $element) { | |
$scope.events = new SimplePubSub(); | |
$scope.slideHasChanged = function(index){ | |
$scope.events.trigger("slideChange", {"index" : index}); | |
$timeout(function(){if($scope.onSlideMove) $scope.onSlideMove({"index" : eval(index)});},100); | |
}; | |
$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { | |
$scope.events.trigger("ngRepeatFinished", {"event" : ngRepeatFinishedEvent}); | |
}); | |
} | |
}; | |
} | |
]); | |
var app = angular.module('slidebox', ['ionic', 'tabSlideBox']) | |
.run(['$q', '$http', '$rootScope', '$location', '$window', '$timeout', | |
function($q, $http, $rootScope, $location, $window, $timeout){ | |
$rootScope.$on("$locationChangeStart", function(event, next, current){ | |
$rootScope.error = null; | |
console.log("Route change!!!", $location.path()); | |
var path = $location.path(); | |
console.log("App Loaded!!!"); | |
}); | |
} | |
]); | |
app.config(function($stateProvider, $urlRouterProvider) { | |
$stateProvider.state('index', { | |
url : '/', | |
templateUrl : 'index.html', | |
controller : 'IndexCtrl' | |
}); | |
$urlRouterProvider.otherwise("/"); | |
}); | |
app.controller("IndexCtrl", ['$rootScope', "$scope", "$stateParams", "$q", "$location", "$window", '$timeout', | |
function($rootScope, $scope, $stateParams, $q, $location, $window, $timeout){ | |
$scope.tabs = [ | |
{"text" : "Home"}, | |
{"text" : "Games"}, | |
{"text" : "Mail"}, | |
{"text" : "Car"}, | |
{"text" : "Profile"}, | |
{"text" : "Favourites"}, | |
{"text" : "Chats"}, | |
{"text" : "Settings"}, | |
{"text" : "Photos"} | |
]; | |
$scope.onSlideMove = function(data){ | |
//alert("You have selected " + data.index + " tab"); | |
}; | |
} | |
]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.slider { | |
background: #3b679e; /* Old browsers */ | |
background: -moz-linear-gradient(-45deg, #3b679e 0%, #2b88d9 35%, #7db9e8 100%); /* FF3.6-15 */ | |
background: -webkit-linear-gradient(-45deg, #3b679e 0%,#2b88d9 35%,#7db9e8 100%); /* Chrome10-25,Safari5.1-6 */ | |
background: linear-gradient(135deg, #3b679e 0%,#2b88d9 35%,#7db9e8 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ | |
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3b679e', endColorstr='#7db9e8',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ | |
top: 0; | |
position: absolute; | |
} | |
.tsb-icons { | |
display: absolute; | |
z-index: 1000; | |
top: calc(100vh - 65px); | |
} | |
.tabbed-slidebox .slider { | |
padding: 0; | |
margin-top: 0; | |
height: 100%; | |
} | |
.tabbed-slidebox.btm .slider { | |
margin-bottom: -50px; | |
} | |
.tabbed-slidebox .slider-slides { | |
width: 100%; | |
margin-top: 0; | |
max-height: calc(100vh - 65px) !important; | |
} | |
.tabbed-slidebox .slider-slide { | |
padding-top: 10px; | |
color: #000; | |
background-color: #fff; | |
text-align: center; | |
font-weight: 300; | |
/* blue bg * | |
background-color: rgba(55,150,250,0.70); */ | |
background-color: rgba(0,0,0,0.1); | |
margin-left: 10px; | |
margin-top:10px; | |
border-radius: 15px; | |
} | |
.tabbed-slidebox .tsb-icons { | |
text-align: center; | |
margin: 0; | |
margin-top: -20px; | |
margin-bottom: -20px; | |
position: relative; | |
background-color:rgba(255,255,255,1); | |
} | |
.tabbed-slidebox .tsb-ic-wrp { | |
display: flex; | |
position: relative; | |
-webkit-transition: -webkit-transform 0.3s; /* For Safari 3.1 to 6.0 */ | |
-webkit-transform:translate3d(0,0,0); | |
} | |
.tabbed-slidebox .tsb-icons a { | |
display: inline-block; | |
width: 54px; | |
font-size: 2.5em; | |
color: rgba(0, 0, 0, 0.3); | |
-webkit-transform:translate3d(0,8px,0); | |
} | |
.tabbed-slidebox .tsb-icons a.active { | |
color: rgba(0, 0, 0, 1); | |
font-size: 3.5em; | |
-webkit-transform:translate3d(0,0,0); | |
} | |
.tabbed-slidebox .tabbed-slidebox { | |
position: relative; | |
height: 100%; | |
overflow: hidden; | |
} | |
.tabbed-slidebox .tsb-icons:after { | |
width: 0; | |
height: 0; | |
border-style: solid; | |
border-width: 0 1.4em 1.4em 1.4em; | |
border-color: transparent transparent #0398dc transparent; | |
position: absolute; | |
content: ""; | |
display: block; | |
bottom: -12px; | |
left: 50%; | |
margin-left: -14px; | |
} | |
.tsb-hscroll.scroll-view{ | |
white-space: nowrap; | |
margin:0 auto; | |
} | |
.tsb-hscroll.scroll-view .scroll-bar{ | |
visibility:hidden; | |
} | |
.tsb-hscroll.scroll-view .scroll.onscroll{ | |
-webkit-transition: -webkit-transform 0.3s; /* For Safari 3.1 to 6.0 */ | |
} | |
.tabbed-slidebox .tsb-icons .scroll a{ | |
width:auto; | |
font-size: 1.5em; | |
line-height: 1.5em; | |
text-decoration: none; | |
margin: 0 15px; | |
border-bottom: 3px solid transparent; | |
} | |
.tabbed-slidebox .tsb-icons .scroll a.active { | |
font-size: 1.8em; | |
border-bottom: 3px solid #ccc; | |
} | |
.slider-slide h3{ | |
color:#fff; | |
margin-top:10px; | |
} | |
.scroll{ | |
height:100%; | |
} | |
.tabbed-slidebox .tsb-icons:after{ | |
display:none; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment