Skip to content

Instantly share code, notes, and snippets.

@dkemper01 dkemper01/MaterialPanel.js
Last active Sep 20, 2015

Embed
What would you like to do?
A Sench Touch 2.4 custom TabPanel with inkbar and ripple effects consistent with Material Design specs.
/*
Demo: http://dankemper.net/t3/examples/sencha-touch-2.4.2/splitViewPortfolio/
Note: this implementation assumes you are using the RippleService which can be found here:
https://github.com/fpt-software/Material-Controls-for-Sencha-Touch/blob/c849787aee8ac75ed168b31b26adea026f4e91d0/SenchaUI/packages/Material/src/helpers/RippleService.js
*/
var allListeners = ((Ext.filterPlatform('chrome') || Ext.filterPlatform('android')) && (!Ext.filterPlatform('ios')))? [
{
event: 'show',
order: 'after',
fn: 'afterShow'
},
{
event: 'activeitemchange',
fn: 'activeItemChange'
}
] : null;
Ext.define('splitViewPortfolio.view.MaterialPanel', {
extend: 'Ext.tab.Panel',
requires: ['Material.helpers.RippleService'],
initialize: function () {
var self = this;
self.callParent();
if ((Ext.filterPlatform('chrome') || Ext.filterPlatform('android')) && (!Ext.filterPlatform('ios'))) {
var rippleService = Material.RippleService;
rippleService.attachTabBehavior(this, this.element, {
inkColor: '#607d8b'
});
if (Ext.os.is('Desktop')) {
Ext.Viewport.on('resize', 'handleResize', self, { scope: self, buffer: 100 });
}
}
return this;
},
config: {
listeners: allListeners
},
activeItemChange: function (component, value, oldValue, eOpts) {
var inkBarConfig = component.stageInkBar(component);
var materialClass = '.' + component.getCls()[0];
var tabBarInner = Ext.DomQuery.select(materialClass + ' .x-tabbar > .x-tabbar-inner');
component.inkBarTransition(component, inkBarConfig.mdInkBar, inkBarConfig.selectedTabExt, Ext.get(tabBarInner[0]));
},
afterShow: function (component, eOpts) {
// Stage ink bar.
//
var inkBarConfig = component.stageInkBar(component);
var materialClass = '.' + component.getCls()[0];
var tabBarInner = Ext.DomQuery.select(materialClass + ' .x-tabbar > .x-tabbar-inner');
component.inkBarTransition(component, inkBarConfig.mdInkBar, inkBarConfig.selectedTabExt, Ext.get(tabBarInner[0]));
},
inkBarTransition: function (component, mdInkBar, selectedTabEl, tabBarEl) {
// Measuring and calculations for the ink bar.
//
var totalWidth = tabBarEl.getWidth();
var mdInkBarCurrentOffsets = mdInkBar.getOffsetsTo(tabBarEl);
var offsets = selectedTabEl.getOffsetsTo(tabBarEl);
var l = offsets[0] + 'px';
var r = totalWidth - offsets[0] - selectedTabEl.getWidth() + 'px';
component.updateInkBarClassName(mdInkBar, mdInkBarCurrentOffsets[0], offsets[0]);
mdInkBar.applyStyles({ left: l, right: r });
},
stageInkBar: function (component) {
var extInkBar = null;
var materialClass = '.' + component.getCls()[0];
var tabBarInner = Ext.DomQuery.select(materialClass + ' .x-tabbar > .x-tabbar-inner');
var existingInkBar = Ext.DomQuery.select(materialClass + ' .x-tabbar > .x-tabbar-inner > .md-ink-bar');
var selectedTab = Ext.DomQuery.select(materialClass + ' .x-tabbar > .x-tabbar-inner > .x-tab-active');
var extSelectedTab = Ext.get(selectedTab[0]);
if (existingInkBar.length == 0) {
var mdInkBar = document.createElement('div');
mdInkBar.className = 'md-ink-bar';
extInkBar = new Ext.Element(mdInkBar);
extInkBar.appendTo(tabBarInner[0]);
extInkBar.applyStyles({ top: (extSelectedTab.getHeight() - extInkBar.getHeight()) + 'px' });
component.setMdInkBar(extInkBar);
} else {
extInkBar = component.getMdInkBar();
}
return { mdInkBar: extInkBar, selectedTabExt: extSelectedTab };
},
updateInkBarClassName: function (mdInkBar, mdInkBarOffsetLeft, selectedTabOffsetLeft) {
if (selectedTabOffsetLeft > mdInkBarOffsetLeft) {
mdInkBar.replaceCls('md-left', 'md-right');
} else {
mdInkBar.replaceCls('md-right', 'md-left');
}
},
handleResize: function (component, eOpts, opts) {
opts.scope.activeItemChange(opts.scope);
}
});
.md-ink-bar {
position: absolute;
left: auto;
right: auto;
bottom: 0;
height: 4px;
z-index: 300;
&.md-left {
transition: left ($swift-ease-in-out-duration * 0.45) $swift-ease-in-out-timing-function, right $swift-ease-in-out-duration $swift-ease-in-out-timing-function;
}
&.md-right {
transition: left $swift-ease-in-out-duration $swift-ease-in-out-timing-function, right ($swift-ease-in-out-duration * 0.45) $swift-ease-in-out-timing-function;
}
background-color: $amber-three-hundred;
}
/*
Demonstrates use of the MaterialPanel. Just be sure to include the class "cls" config with value of "md-tabpanel-x" where x represents the number
of tabpanels in your app. Most of the time you will only have/need one. Also include the "mdInkBar" config. Set "mdInkBar" to null.
*/
Ext.define('splitViewPortfolio.view.SafeGrid', {
extend: 'splitViewPortfolio.view.MaterialPanel',
initialize: function () {
var self = this;
self.callParent();
// Add a Listener. Listen for [Viewport ~ Orientation] Change.
//
Ext.Viewport.on('orientationchange', 'handleOrientationChange', self, { buffer: 50 });
return this;
},
config: {
activeTab: 0,
cls: 'md-tabpanel-1',
mdInkBar: null,
scrollable: false,
ui: 'light',
tabBar: {
ui: Ext.filterPlatform('blackberry') || Ext.filterPlatform('ie10') ? 'dark' : 'light',
layout: {
pack : 'center',
align: 'center'
},
docked: Ext.filterPlatform('chrome') || Ext.filterPlatform('android') ? 'top' : 'bottom'
},
items: [/* your tabpanel items here ... */]
//
// etc ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.