Permalink
Browse files

Merge pull request #4075 from Dogfalo/WIP-swipeable-tabs

Wip swipeable tabs
  • Loading branch information...
2 parents 817fd13 + 347b93f commit 5b1ff3034770b35eb5d237af8a96d5814e49bdd2 @acburst acburst committed on GitHub Jan 9, 2017
Showing with 184 additions and 41 deletions.
  1. +3 −3 bin/materialize.js
  2. +35 −0 jade/page-contents/tabs_content.html
  3. +10 −1 js/carousel.js
  4. +5 −0 js/init.js
  5. +96 −37 js/tabs.js
  6. +35 −0 tabs.html
View
Oops, something went wrong.
@@ -120,6 +120,14 @@
<td>onShow</td>
<td>Execute a callback function when the tab is changed. <br /> The callback provides a parameter which refers to the current tab being shown.</td>
</tr>
+ <tr>
+ <td>swipeable</td>
+ <td>Set to true to enable swipeable tabs. This also uses the responsiveThreshold option. Default: false</td>
+ </tr>
+ <tr>
+ <td>responsiveThreshold</td>
+ <td>The maximum width of the screen, in pixels, where the swipeable functionality initializes. Default: Infinity</td>
+ </tr>
</tbody>
</table>
</div>
@@ -147,6 +155,32 @@
</code></pre>
</div>
+
+ <div id="swipeable" class="section scrollspy">
+ <h4>Swipeable Tabs</h4>
+ <p>By setting the <code class="language-javascript">swipeable</code> option to <code class="language-markup">true</code>, you can enable tabs where you can swipe on touch enabled devices to switch tabs. Make sure you keep the tab content divs in the same wrapping container. You can also set the <code class="language-javascript">responsiveThreshold</code> option to a screen width in pixels where the swipeable functionality will activate.</p>
+ <p>Note: This is also touch compatible! Try swiping with your finger to scroll through the carousel.</p>
+ <ul id="tabs-swipe-demo" class="tabs">
+ <li class="tab col s3"><a href="#test-swipe-1">Test 1</a></li>
+ <li class="tab col s3"><a class="active" href="#test-swipe-2">Test 2</a></li>
+ <li class="tab col s3"><a href="#test-swipe-3">Test 3</a></li>
+ </ul>
+ <div id="test-swipe-1" class="col s12 blue">Test 1</div>
+ <div id="test-swipe-2" class="col s12 red">Test 2</div>
+ <div id="test-swipe-3" class="col s12 green">Test 3</div>
+
+ <pre><code class="language-markup col s12">
+ &lt;ul id="tabs-swipe-demo" class="tabs">
+ &lt;li class="tab col s3">&lt;a href="#test-swipe-1">Test 1&lt;/a>&lt;/li>
+ &lt;li class="tab col s3">&lt;a class="active" href="#test-swipe-2">Test 2&lt;/a>&lt;/li>
+ &lt;li class="tab col s3">&lt;a href="#test-swipe-3">Test 3&lt;/a>&lt;/li>
+ &lt;/ul>
+ &lt;div id="test-swipe-1" class="col s12 blue">Test 1&lt;/div>
+ &lt;div id="test-swipe-2" class="col s12 red">Test 2&lt;/div>
+ &lt;div id="test-swipe-3" class="col s12 green">Test 3&lt;/div>
+ </code></pre>
+ </div>
+
</div>
<!-- Table of Contents -->
@@ -165,6 +199,7 @@
<li><a href="#options">Options</a></li>
<li><a href="#preselecting">Preselecting</a></li>
<li><a href="#external">External Links</a></li>
+ <li><a href="#swipeable">Swipeable Tabs</a></li>
</ul>
</div>
</div>
View
@@ -10,7 +10,8 @@
padding: 0, // Padding between non center items
fullWidth: false, // Change to full width styles
indicators: false, // Toggle indicators
- noWrap: false // Don't wrap around and cycle through items.
+ noWrap: false, // Don't wrap around and cycle through items.
+ onCycleTo: null // Callback for when a new slide is cycled to.
};
options = $.extend(defaults, options);
@@ -128,6 +129,7 @@
function scroll(x) {
var i, half, delta, dir, tween, el, alignment, xTranslation;
+ var lastCenter = center;
offset = (typeof x === 'number') ? x : offset;
center = Math.floor((offset + dim / 2) / dim);
@@ -223,6 +225,13 @@
el.style.opacity = tweenedOpacity;
el.style.display = 'block';
}
+
+ // onCycleTo callback
+ if (lastCenter !== center &&
+ typeof(options.onCycleTo) === "function") {
+ var $curr_item = view.find('.carousel-item').eq(wrap(center));
+ options.onCycleTo.call(this, $curr_item, dragged);
+ }
}
function track() {
View
@@ -163,6 +163,11 @@
});
}
+ // Swipeable Tabs Demo Init
+ if ($('#tabs-swipe-demo').length) {
+ $('#tabs-swipe-demo').tabs({ 'swipeable': true });
+ }
+
// Plugin initialization
$('.carousel.carousel-slider').carousel({fullWidth: true});
$('.carousel').carousel();
View
@@ -3,7 +3,9 @@
var methods = {
init : function(options) {
var defaults = {
- onShow: null
+ onShow: null,
+ swipeable: false,
+ responsiveThreshold: Infinity, // breakpoint for swipeable
};
options = $.extend(defaults, options);
@@ -16,8 +18,15 @@
var $active, $content, $links = $this.find('li.tab a'),
$tabs_width = $this.width(),
+ $tabs_content = $(),
+ $tabs_wrapper,
$tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length,
- $index = 0;
+ $indicator,
+ index = prev_index = 0,
+ clicked = false,
+ clickedTimeout,
+ transition = 300;
+
// Finds right attribute for indicator based on active tab.
// el: jQuery Object
@@ -31,6 +40,27 @@
return el.position().left + $this.scrollLeft();
};
+ // Animates Indicator to active tab.
+ // prev_index: Number
+ var animateIndicator = function(prev_index) {
+ if ((index - prev_index) >= 0) {
+ $indicator.velocity({"right": calcRightPos($active) }, { duration: transition, queue: false, easing: 'easeOutQuad'});
+ $indicator.velocity({"left": calcLeftPos($active) }, {duration: transition, queue: false, easing: 'easeOutQuad', delay: 90});
+
+ } else {
+ $indicator.velocity({"left": calcLeftPos($active) }, { duration: transition, queue: false, easing: 'easeOutQuad'});
+ $indicator.velocity({"right": calcRightPos($active) }, {duration: transition, queue: false, easing: 'easeOutQuad', delay: 90});
+ }
+ };
+
+ // Change swipeable according to responsive threshold
+ if (options.swipeable) {
+ if (window_width > options.responsiveThreshold) {
+ options.swipeable = false;
+ }
+ }
+
+
// If the location.hash matches one of the links, use that as the active tab.
$active = $($links.filter('[href="'+location.hash+'"]'));
@@ -43,9 +73,9 @@
}
$active.addClass('active');
- $index = $links.index($active);
- if ($index < 0) {
- $index = 0;
+ index = $links.index($active);
+ if (index < 0) {
+ index = 0;
}
if ($active[0] !== undefined) {
@@ -54,11 +84,13 @@
}
// append indicator then set indicator width to tab width
- $this.append('<div class="indicator"></div>');
- var $indicator = $this.find('.indicator');
+ if (!$this.find('.indicator').length) {
+ $this.append('<div class="indicator"></div>');
+ }
+ $indicator = $this.find('.indicator');
if ($this.is(":visible")) {
- // $indicator.css({"right": $tabs_width - (($index + 1) * $tab_width)});
- // $indicator.css({"left": $index * $tab_width});
+ // $indicator.css({"right": $tabs_width - ((index + 1) * $tab_width)});
+ // $indicator.css({"left": index * $tab_width});
setTimeout(function() {
$indicator.css({"right": calcRightPos($active) });
$indicator.css({"left": calcLeftPos($active) });
@@ -67,19 +99,43 @@
$(window).resize(function () {
$tabs_width = $this.width();
$tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length;
- if ($index < 0) {
- $index = 0;
+ if (index < 0) {
+ index = 0;
}
if ($tab_width !== 0 && $tabs_width !== 0) {
$indicator.css({"right": calcRightPos($active) });
$indicator.css({"left": calcLeftPos($active) });
}
});
- // Hide the remaining content
- $links.not($active).each(function () {
- $(Materialize.escapeHash(this.hash)).hide();
- });
+ // Initialize Tabs Content.
+ if (options.swipeable) {
+ // TODO: Duplicate calls with swipeable? handle multiple div wrapping.
+ $links.each(function () {
+ var $curr_content = $(Materialize.escapeHash(this.hash));
+ $curr_content.addClass('carousel-item');
+ $tabs_content = $tabs_content.add($curr_content);
+ });
+ $tabs_wrapper = $tabs_content.wrapAll('<div class="tabs-content carousel"></div>');
+ $tabs_content.css('display', '');
+ $('.tabs-content.carousel').carousel({
+ fullWidth: true,
+ noWrap: true,
+ onCycleTo: function(item) {
+ if (!clicked) {
+ var prev_index = index;
+ index = $tabs_wrapper.index(item);
+ $active = $links.eq(index);
+ animateIndicator(prev_index);
+ }
+ },
+ });
+ } else {
+ // Hide the remaining content
+ $links.not($active).each(function () {
+ $(Materialize.escapeHash(this.hash)).hide();
+ });
+ }
// Bind the click event handler
@@ -94,6 +150,7 @@
return;
}
+ clicked = true;
$tabs_width = $this.width();
$tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length;
@@ -109,38 +166,40 @@
// Make the tab active.
$active.addClass('active');
- var $prev_index = $index;
- $index = $links.index($(this));
- if ($index < 0) {
- $index = 0;
+ prev_index = index;
+ index = $links.index($(this));
+ if (index < 0) {
+ index = 0;
}
// Change url to current tab
// window.location.hash = $active.attr('href');
- if ($content !== undefined) {
- $content.show();
- $content.addClass('active');
- if (typeof(options.onShow) === "function") {
- options.onShow.call(this, $content);
+ // Swap content
+ if (options.swipeable) {
+ if ($tabs_content.length) {
+ $tabs_content.carousel('set', index);
+ }
+ } else {
+ if ($content !== undefined) {
+ $content.show();
+ $content.addClass('active');
+ if (typeof(options.onShow) === "function") {
+ options.onShow.call(this, $content);
+ }
}
- }
- if ($oldContent !== undefined &&
- !$oldContent.is($content)) {
- $oldContent.hide();
- $oldContent.removeClass('active');
+ if ($oldContent !== undefined &&
+ !$oldContent.is($content)) {
+ $oldContent.hide();
+ $oldContent.removeClass('active');
+ }
}
+ // Reset clicked state
+ clickedTimeout = setTimeout(function(){ clicked = false; }, transition);
// Update indicator
- if (($index - $prev_index) >= 0) {
- $indicator.velocity({"right": calcRightPos($active) }, { duration: 300, queue: false, easing: 'easeOutQuad'});
- $indicator.velocity({"left": calcLeftPos($active) }, {duration: 300, queue: false, easing: 'easeOutQuad', delay: 90});
-
- } else {
- $indicator.velocity({"left": calcLeftPos($active) }, { duration: 300, queue: false, easing: 'easeOutQuad'});
- $indicator.velocity({"right": calcRightPos($active) }, {duration: 300, queue: false, easing: 'easeOutQuad', delay: 90});
- }
+ animateIndicator(prev_index);
// Prevent the anchor's default click action
e.preventDefault();
View
@@ -232,6 +232,14 @@
<td>onShow</td>
<td>Execute a callback function when the tab is changed. <br /> The callback provides a parameter which refers to the current tab being shown.</td>
</tr>
+ <tr>
+ <td>swipeable</td>
+ <td>Set to true to enable swipeable tabs. This also uses the responsiveThreshold option. Default: false</td>
+ </tr>
+ <tr>
+ <td>responsiveThreshold</td>
+ <td>The maximum width of the screen, in pixels, where the swipeable functionality initializes. Default: Infinity</td>
+ </tr>
</tbody>
</table>
</div>
@@ -259,6 +267,32 @@
</code></pre>
</div>
+
+ <div id="swipeable" class="section scrollspy">
+ <h4>Swipeable Tabs</h4>
+ <p>By setting the <code class="language-javascript">swipeable</code> option to <code class="language-markup">true</code>, you can enable tabs where you can swipe on touch enabled devices to switch tabs. Make sure you keep the tab content divs in the same wrapping container. You can also set the <code class="language-javascript">responsiveThreshold</code> option to a screen width in pixels where the swipeable functionality will activate.</p>
+ <p>Note: This is also touch compatible! Try swiping with your finger to scroll through the carousel.</p>
+ <ul id="tabs-swipe-demo" class="tabs">
+ <li class="tab col s3"><a href="#test-swipe-1">Test 1</a></li>
+ <li class="tab col s3"><a class="active" href="#test-swipe-2">Test 2</a></li>
+ <li class="tab col s3"><a href="#test-swipe-3">Test 3</a></li>
+ </ul>
+ <div id="test-swipe-1" class="col s12 blue">Test 1</div>
+ <div id="test-swipe-2" class="col s12 red">Test 2</div>
+ <div id="test-swipe-3" class="col s12 green">Test 3</div>
+
+ <pre><code class="language-markup col s12">
+ &lt;ul id="tabs-swipe-demo" class="tabs">
+ &lt;li class="tab col s3">&lt;a href="#test-swipe-1">Test 1&lt;/a>&lt;/li>
+ &lt;li class="tab col s3">&lt;a class="active" href="#test-swipe-2">Test 2&lt;/a>&lt;/li>
+ &lt;li class="tab col s3">&lt;a href="#test-swipe-3">Test 3&lt;/a>&lt;/li>
+ &lt;/ul>
+ &lt;div id="test-swipe-1" class="col s12 blue">Test 1&lt;/div>
+ &lt;div id="test-swipe-2" class="col s12 red">Test 2&lt;/div>
+ &lt;div id="test-swipe-3" class="col s12 green">Test 3&lt;/div>
+ </code></pre>
+ </div>
+
</div>
<!-- Table of Contents -->
@@ -277,6 +311,7 @@
<li><a href="#options">Options</a></li>
<li><a href="#preselecting">Preselecting</a></li>
<li><a href="#external">External Links</a></li>
+ <li><a href="#swipeable">Swipeable Tabs</a></li>
</ul>
</div>
</div>

0 comments on commit 5b1ff30

Please sign in to comment.