diff --git a/README.md b/README.md index 4ac7a98..7c23eb8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GridTab jQuery Plugin # -**GridTab** is a lightweight jQuery plugin to create grid based responsive tabs. +**GridTab** is a lightweight jQuery plugin to create grid based responsive tabs. With version 2.0.0, gridtab supports nested tabs. ## Demo ## @@ -56,22 +56,40 @@ and initialize the plugin as shown below: Where grid is the number of grids/tabs in a row ## Settings ## -| Option | Type | Default | Description | -| ------------- | ------------- | -------- | ----------- | -| grid | integer | 4 | Number of grids or tabs per row | -| borderWidth | integer | 2 | Width of the borders | -| tabBorderColor | string | '#ddd' | border color of the tabs (Hex Color Code).| -| tabPadding | integer | 25 | padding/spacing around the tabs. -| contentBorderColor | string | '#ddd' | border color of the content section (Hex Color Code).| -| contentPadding | integer | 25 | padding/spacing around content section| -| contentBackground | string | '#fff' | Background color for the content section (Hex Color Code).| -| activeTabBackground| string | '#fff' | Background color for the active tab (Hex Color Code).| -| keepOpen | Boolean | false | If set to `true` keeps the active tab open (Disables toggle).| -| speed | integer | 500 | Transition speed in milliseconds| -| layout | string | 'grid' | Change the value to `'tab'` for a tab layout. By default, the layout is `'grid'` based.| -| activeTab | integer | 0 | Initially active tab. For example, `1` enables the first tab.| -| Responsive | Array | null | Array of objects having breakpoints and `settings` object which is enabled at a given `breakpoint`.| -| callbacks | Object | open:false, close:false| Callbacks for the open and close states of the content section.| +| Basic Settings | Type | Default | Description | +| ------------- | ------------- | -------- | ----------- | +| grid | integer | `4` | Number of grids or tabs per row | +| borderWidth | integer | `2` | Width of the borders.| +| tabBorderColor | string | `'#ddd'` | border color of the tabs (Hex Color Code).| +| tabPadding | integer | `25` | padding/spacing around the tabs. +| contentBorderColor | string | `'#ddd'` | border color of the content section (Hex Color Code).| +| contentPadding | integer | `25` | padding/spacing around content section.| +| contentBackground | string | `'#fff'` | Background color for the content section (Hex Color Code).| +| activeTabBackground| string | `'#fff'` | Background color for the active tab (Hex Color Code).| +| responsive | Array | `null` | Array of objects having breakpoints and `settings` object which is enabled at a given `breakpoint`.| +| selectors | Object | see table below | Object with options to set custom selectors.| +| config | Object | see table below | Object with options to enable features like, setting initially active tab, next/prev controls, close button, transition speed etc.| +| callbacks | Object | `open:false, close:false` | Callbacks for the open and close states of the content section.| + + +| selectors | Type | Default | Description | +| ------------- | ------------- | -------- | ----------- | +| tab | string | `'> dt'` | By default, the click event is triggered on the entire tab (`'dt'`). This can be replaced with any custom selector within the `dt`. For example: `'.readmore'`.| +| closeButton | string | `'.gridtab__close'` | Custom class for the close button, if `'showClose'` is set to `true`.| +| nextArrow | string | `'.gridtab__next.gridtab__arrow'` | Custom class for the next button, if `'showArrows'` is set to `true`. | +| prevArrow | string | `'.gridtab__prev.gridtab__arrow'` | Custom class for the prev button, if `'showArrows'` is set to `true`. | +| disabledArrow | string | `'.is-disabled'` | Custom class for the disabled state of next/prev buttons, if `'showArrows'` is set to `true`. | + +| config | Type | Default | Description | +| ------------- | ------------- | -------- | ----------- | +| layout | string | `'grid'` | Change the value to `'tab'` for a tab layout. By default, the layout is `'grid'` based. | +| keepOpen | Boolean | `false` | If set to `true` keeps the active tab open (Disables toggle).| +| speed | integer | `500` | Transition speed in milliseconds.| +| activeTab | integer | `0` | Initially active tab. For example, `1` enables the first tab.| +| showClose | Boolean | `false` | Shows the close button if set to `true`.| +| showArrows | Boolean | `false` | Shows the next/prev buttons if set to `true`.| +| scrollToTab | Boolean | `false` | Scrolls to the active tab on click| + ### Responsive Example ### The responsive settings is where you reset the grids and other properties at a given breakpoint. @@ -101,10 +119,11 @@ $('#gridtab-1').gridtab({ }] }); ``` -The properties that can be changed with the responsive settings are: `borderWidth`, `tabPadding`, `tabBorderColor`, `contentBorderColor`, `contentPadding`, `contentBackground` and `activeTabBackground`. +All properties under basic settings like: `borderWidth`, `tabPadding`, `tabBorderColor`, `contentBorderColor`, `contentPadding`, `contentBackground` and `activeTabBackground`, can be updated with the responsive settings. + ## Version ## -1.0.0 +2.0.0 ## Browser support ## GridTab works on IE10+ in addition to other modern browsers like Chrome, Firefox and Safari diff --git a/gridtab.jquery.json b/gridtab.jquery.json index db7c1d6..c448785 100644 --- a/gridtab.jquery.json +++ b/gridtab.jquery.json @@ -9,7 +9,7 @@ "accordion", "navigation" ], - "version": "1.0.0", + "version": "2.0.0", "author": { "name": "Gopal Raju", "url": "http://gopalraju.github.io" @@ -21,7 +21,7 @@ }], "licenses": [{ "type": "MIT", - "url": "https://github.com/kenwheeler/slick/blob/master/LICENSE" + "url": "https://github.com/gopalraju/gridtab/blob/master/LICENSE" }], "demo": "http://gopalraju.github.io/gridtab/", "bugs": "https://github.com/gopalraju/gridtab/issues", diff --git a/gridtab/gridtab.css b/gridtab/gridtab.css index f404f0f..b0b75e1 100644 --- a/gridtab/gridtab.css +++ b/gridtab/gridtab.css @@ -1,4 +1,4 @@ -/* GridTab */ +/* GridTab v2.0.0*/ .gridtab, .gridtab>dt, @@ -20,9 +20,6 @@ justify-content: flex-start; box-sizing: border-box } -.gridtab .is-disabled { - pointer-events: none -} .gridtab * { box-sizing: border-box } @@ -34,6 +31,71 @@ -ms-flex-preferred-size: 100%; flex-basis: 100% } +.gridtab>dd>.gridtab__controls { + position: absolute; + right: 5px; + top: 5px; + display: -webkit-flex; + display: -ms-flexbox; + display: flex +} +.gridtab>dd>.gridtab__controls>.gridtab__close:before, +.gridtab>dd>.gridtab__controls>.gridtab__close:after { + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%) rotate(45deg); + transform: translate(-50%, -50%) rotate(45deg) +} +.gridtab>dd>.gridtab__controls>.gridtab__close:after { + -webkit-transform: translate(-50%, -50%) rotate(-45deg); + transform: translate(-50%, -50%) rotate(-45deg) +} +.gridtab>dd>.gridtab__controls>.gridtab__arrow, +.gridtab>dd>.gridtab__controls>.gridtab__close { + display: block; + width: 25px; + height: 25px; + overflow: hidden; + background: #666; + text-indent: -999px; + position: relative +} +.gridtab>dd>.gridtab__controls>.gridtab__arrow:before, +.gridtab>dd>.gridtab__controls>.gridtab__arrow:after, +.gridtab>dd>.gridtab__controls>.gridtab__close:before, +.gridtab>dd>.gridtab__controls>.gridtab__close:after { + position: absolute; + width: 3px; + height: 13px; + content: ''; + display: block; + background: #FFF +} +.gridtab>dd>.gridtab__controls>.gridtab__arrow:before, +.gridtab>dd>.gridtab__controls>.gridtab__arrow:after { + top: 50%; + left: 50%; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + -webkit-transform-origin: right; + transform-origin: right; + margin: -7px 0 0 -3px; + height: 10px +} +.gridtab>dd>.gridtab__controls>.gridtab__arrow:after { + margin-top: -4px; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg) +} +.gridtab>dd>.gridtab__controls>.gridtab__arrow.is-disabled { + opacity: .25 +} +.gridtab>dd>.gridtab__controls>.gridtab__next { + -webkit-transform: scale(-1); + transform: scale(-1); + -webkit-transform-origin: center; + transform-origin: center +} .gridtab>dt { transition: background 1s; background: #f2f2f2 @@ -44,10 +106,13 @@ left: 0; right: 0 } +.gridtab>dt.is-disabled { + pointer-events: none +} .gridtab>dt.is-active { z-index: 2 } -.gridtab>dt img { +.gridtab>dt>img { max-width: 100%; float: left } diff --git a/gridtab/gridtab.js b/gridtab/gridtab.js index adc03c3..7689aeb 100644 --- a/gridtab/gridtab.js +++ b/gridtab/gridtab.js @@ -1,237 +1,363 @@ /* - Version: 1.0.0 - Author: Gopal Raju + Version: 2.0.0 + Author: Gopal Raju Website: http://www.productivedreams.com - Docs: https://gopalraju.github.io/gridtab - Repo: https://gopalraju.github.io/gridtab - Issues: https://gopalraju.github.io/gridtab/issues - - */ + Docs: https://gopalraju.github.io/gridtab + Repo: https://gopalraju.github.io/gridtab + Issues: https://gopalraju.github.io/gridtab/issues +*/ ; (function($, window, document, undefined) { - // Check instance count - var instanceCount = 0, - gridtab = 'gridtab', - defaults = { - grid: 4, - borderWidth: 2, - tabBorderColor: "#ddd", - tabPadding: 25, - contentBorderColor: "#ddd", - contentPadding: 25, - contentBackground: "#fff", - activeTabBackground: "#fff", - keepOpen: false, - speed: 500, - layout: 'grid', - activeTab: 0, - responsive: null, - callbacks: { - open: false, - close: false - } + // Check instance count + var instanceCount = 0, + gridtab = 'gridtab', + defaults = { + grid: 4, + borderWidth: 2, + tabBorderColor: "#ddd", + tabPadding: 25, + contentBorderColor: "#ddd", + contentPadding: 35, + contentBackground: "#fff", + activeTabBackground: "#fff", + responsive: null, + selectors: { + tab: '>dt', + closeButton: '.gridtab__close', + nextArrow: '.gridtab__next.gridtab__arrow', + prevArrow: '.gridtab__prev.gridtab__arrow', + disabledArrow: '.is-disabled' + }, + config: { + layout: 'grid', + keepOpen: false, + speed: 500, + activeTab: 0, + showClose: false, + showArrows: false, + scrollToTab:false + }, + callbacks: { + open: false, + close: false + } + }; + // The Gridtab constructor + function Gridtab(element, options) { + var _ = this; + _.element = element; + _.options = $.extend(true, {}, defaults, options); + _.defaults = defaults; + _.name = gridtab; + _.cssRules = ''; + _.breakpoints = []; + _.grids = []; + _.activeTab = _.options.config.activeTab - 1; + _.tabs = $(_.element).find('> dt'); + _.contents = $(_.element).find('> dd'); + _.init(); + _.generateStylesheet(_.cssRules); + instanceCount++; + } + /** + * Initializes Gridtab plugin + */ + Gridtab.prototype.init = function() { + var _ = this; + $(_.element).addClass('gridtab gridtab--' + instanceCount); + _.setTabOrder(); + _.showControls(); + _.addCssRules(_.options.grid, _.options.borderWidth, _.options.tabPadding, _.options.tabBorderColor, _.options.contentPadding, _.options.contentBorderColor, _.options.contentBackground, _.options.activeTabBackground, null); + if (_.options.responsive !== null) { + _.responsiveBreakpoints(); + } else { + _.setContentOrder(_.options.grid); + } - }; - - // The Gridtab constructor - function Gridtab(element, options) { - var _ = this; - _.element = element; - _.options = $.extend({}, defaults, options); - _.defaults = defaults; - _.name = gridtab; - _.cssRules = ''; - _.breakpoints = []; - _.activeTab = _.options.activeTab - 1; - _.tabs = $(_.element).find('> dt'); - _.contents = $(_.element).find('> dd'); - _.init(); - _.generateStylesheet(_.cssRules); - _.setContentOrder(); - instanceCount++; + //If activeTab value exists and is less than total number of tabs + if (_.activeTab > -1 && _.activeTab < _.tabs.length) { + _.slideContent(_.tabs[_.activeTab], false, false); } - /** - * Initializes Gridtab plugin - */ - Gridtab.prototype.init = function() { - var _ = this; - $(_.element).addClass('gridtab gridtab--' + instanceCount); - _.setTabOrder(); - _.addCssRules(_.options.grid, _.options.borderWidth, _.options.tabPadding, _.options.tabBorderColor, _.options.contentPadding, _.options.contentBorderColor, _.options.contentBackground, _.options.activeTabBackground, null); - _.responsiveBreakpoints(); - //If activeTab value exists and is less than total number of tabs - if (_.activeTab > -1 && _.activeTab < _.tabs.length) { - _.slideContent(_.tabs[_.activeTab]); + + $(_.element).on('click', _.options.selectors.tab, function(e) { + //if selector has href, prevent jump + ($(this).attr('href')) && (e.preventDefault()); + e.stopPropagation(); + e.stopImmediatePropagation(); + _.slideContent($(this).closest('dt'), false, _.options.config.scrollToTab); + }); + + }; + + /** + * Show Controls (prev,next and close) + */ + Gridtab.prototype.showControls = function() { + var _ = this; + + if (_.options.config.showClose || _.options.config.showArrows) { + + var controlsGroup = $('
').appendTo(_.contents); + //If showClose is set to true + if (_.options.config.showClose) { + $('Close').appendTo(controlsGroup); + } + + //If showArrows is set to true + if (_.options.config.showArrows) { + //If items exist and the count is greater than or equal to 2 + if (_.contents.length && _.contents.length >= 2) { + var nextArrow = _.options.selectors.nextArrow.replace(/\./g, ' '), + prevArrow = _.options.selectors.prevArrow.replace(/\./g, ' '), + disabledArrow = _.options.selectors.disabledArrow.replace(/\./g, ' '), + prevElem = 'Prev', + nextElem = 'Next', + prevDisabled = 'Prev', + nextDisabled = 'Next'; + //If there are more than 2 items add both (prev and next) arrows to all items from second to second last + if (_.contents.length > 2) { + $(prevElem + nextElem).appendTo(_.contents.slice(1, -1).find(controlsGroup)); + } + //For the last item add prev and next arrows, but keep next disabled + $(prevElem + nextDisabled).appendTo($(_.contents[_.contents.length - 1]).find(controlsGroup)); + //For the first item add prev and next arrows, but keep prev disabled + $(prevDisabled + nextElem).appendTo($(_.contents[0]).find(controlsGroup)); } - $(_.element).on('click', '> dt', function(event) { - event.stopPropagation(); - event.stopImmediatePropagation(); - _.slideContent(this); - }); - }; + } - /** - * Sets the order of the content based on grid count - */ - Gridtab.prototype.setContentOrder = function() { - var _ = this, - //Container width divided by first element width - grid = Math.round($(_.element).outerWidth() / $($(_.element).children()[0]).outerWidth()), - rowCount = Math.ceil(_.contents.length / grid); - for (var i = 0; i < rowCount; i++) { - //Select first n elements, second n elements etc and set order - _.contents.slice(i * grid, grid * (i + 1)).css('order', '' + ((i + 1) * grid)); + $(controlsGroup).on('click', 'a', function(e) { + e.preventDefault(); + var currIndex = _.contents.index($(this).parent().parent()); + //If clicked item is prev arrow, slide the prev content + if ($(this).hasClass('gridtab__prev')) { + _.slideContent(_.tabs[currIndex - 1], false, _.options.config.scrollToTab); } - }; - /** - * Sets the order of each tab - */ - Gridtab.prototype.setTabOrder = function() { - var _ = this; - //Iterate through each tab and set flex order for each tab - _.tabs.each(function(i) { - $(this).css('order', '' + i); - }); - }; - /** - * Generate CSS rules - */ - Gridtab.prototype.addCssRules = function(grid, borderWidth, tabPadding, tabBorderColor, contentPadding, contentBorderColor, contentBackground, activeTabBackground, breakpoint) { - var _ = this; - if (grid !== null || borderWidth !== null || tabPadding !== null || tabBorderColor !== null || contentBorderColor !== null || contentPadding !== null || contentBackground !== null || activeTabBackground !== null) { - var cssRules = ''; - if (grid !== null || borderWidth !== null || tabBorderColor !== null || tabPadding !== null) { - //Container Styles - (borderWidth !== null) && (cssRules += '.gridtab--' + instanceCount + '{padding:' + borderWidth + 'px 0 0 ' + borderWidth + 'px;}'); - //Tab Styles - var tabWidth = ''; - (grid !== null) && (tabWidth = Math.floor((100 / grid) * 100) / 100); - cssRules += '.gridtab--' + instanceCount + ' > dt{'; - (borderWidth !== null) && (cssRules += 'margin:-' + borderWidth + 'px 0 0 -' + borderWidth + 'px;'); - (grid !== null) && (cssRules += 'min-width:calc(' + tabWidth + '% + ' + borderWidth + 'px);flex-basis:' + tabWidth + '%;-ms-flex-basis:' + tabWidth + '%;'); - (borderWidth !== null) && (cssRules += 'border-width:' + borderWidth + 'px;'); - (tabPadding !== null) && (cssRules += 'padding:' + tabPadding + 'px;'); - (tabBorderColor !== null) && (cssRules += 'border-color:' + tabBorderColor + ';'); - cssRules += '}'; - } - //active Tab Styles - (activeTabBackground !== null) && (cssRules += '.gridtab--' + instanceCount + ' >dt.is-active{background:' + activeTabBackground + ';}'); - (_.options.layout == 'tab' && activeTabBackground !== null && borderWidth !== null) && (cssRules += '.gridtab--' + instanceCount + ' >dt.is-active:after{background:' + activeTabBackground + ';height:' + borderWidth + 'px;bottom:-' + borderWidth + 'px;}'); - //Content Styles - if (contentBorderColor !== null || borderWidth !== null || contentBackground !== null || contentPadding !== null) { - cssRules += '.gridtab--' + instanceCount + '>dd{'; - cssRules += 'min-width:calc(100% + ' + borderWidth + 'px);'; - (borderWidth !== null) && (cssRules += 'margin:-' + borderWidth + 'px 0 0 -' + borderWidth + 'px !important;border-width:' + borderWidth + 'px;'); - (contentBorderColor !== null) && (cssRules += 'border-color:' + contentBorderColor + ';'); - (contentPadding !== null) && (cssRules += 'padding:' + contentPadding + 'px;'); - (contentBackground !== null) && (cssRules += 'background:' + contentBackground + ';'); - cssRules += '}'; - } - //If has breakpoints generate mediaquery - _.cssRules += (breakpoint !== null) ? ('@media (max-width:' + breakpoint + 'px){' + cssRules + '}') : cssRules; + //else if next arrow, slide the next + else if ($(this).hasClass('gridtab__next')) { + _.slideContent(_.tabs[currIndex + 1], false, _.options.config.scrollToTab); } - }; - /** - * Generate Stylesheet and append CSS rules - */ - Gridtab.prototype.generateStylesheet = function(cssRules) { - var style = $('head').append(''); - }; - - /** - * For each breakpoint add CSS rules and set content order - */ - Gridtab.prototype.responsiveBreakpoints = function() { - var _ = this; - if (_.options.responsive && _.options.responsive.length) { - //Sort Responsive Options by MediaQuery in descending order - _.options.responsive.sort(function(a, b) { - return parseFloat(b.breakpoint) - parseFloat(a.breakpoint); - }); - for (var i in _.options.responsive) { - var responsiveSettings = _.options.responsive[i].settings, - grid = responsiveSettings.grid || null, - borderWidth = responsiveSettings.borderWidth || _.options.borderWidth, - tabBorderColor = responsiveSettings.tabBorderColor || null, - tabPadding = responsiveSettings.tabPadding || null, - activeTabBackground = responsiveSettings.activeTabBackground || null, - contentPadding = responsiveSettings.contentPadding || null, - contentBorderColor = responsiveSettings.contentBorderColor || null, - contentBackground = responsiveSettings.contentBackground || null, - breakpoint = _.options.responsive[i].breakpoint || null; - _.addCssRules(grid, borderWidth, tabPadding, tabBorderColor, contentPadding, contentBorderColor, contentBackground, activeTabBackground, breakpoint); - _.breakpoints.push(breakpoint); - window.matchMedia("(max-width:" + _.breakpoints[i] + "px)").addListener(function() { - _.setContentOrder(); - }); - } + //else it's a close button + else { + _.slideContent(_.tabs[currIndex], true, false); } - }; - /** - * Slide up and slide down contents based on tab clicked - */ - Gridtab.prototype.slideContent = function(currTab) { - - var _ = this, - $currTab = $(currTab), - $prevTab = $(_.element).find('>dt.is-active').not(currTab); - - if (!$currTab.hasClass('is-disabled')) { - //If an active tab already exists - if ($prevTab.length) { - _.tabs.addClass('is-disabled'); - $prevTab.next().stop(true).slideUp(_.options.speed, function() { - $prevTab.removeClass('is-active'); - _.tabs.next().stop(true); - (_.options.callbacks.close) && (_.options.callbacks.close.call(this)); - $currTab.addClass('is-active').next().stop(true).slideDown(_.options.speed, function() { - (_.options.callbacks.open) && (_.options.callbacks.open.call(this)); - _.tabs.removeClass('is-disabled'); - return false; - }); - return false; - - }); + }); - } else { - if (_.options.keepOpen) { - $currTab.addClass('is-active').next().stop(true).slideDown(_.options.speed, function() { - (_.options.callbacks.open) && (_.options.callbacks.open.call(this)); - }); - return false; - } else { - _.tabs.addClass('is-disabled'); - $currTab.addClass('is-active').next().stop(true).slideToggle(_.options.speed, function() { - - if ($(this).is(':hidden')) { - $currTab.removeClass('is-active'); - (_.options.callbacks.close) && (_.options.callbacks.close.call(this)); - } else { - (_.options.callbacks.open) && (_.options.callbacks.open.call(this)); - } - _.tabs.removeClass('is-disabled'); - return false; - }); - } - } + } + }; + + /** + * Sets the order of the content based on grid count + */ + Gridtab.prototype.setContentOrder = function(grid) { + var _ = this, + //Container width divided by first element width + rowCount = Math.ceil(_.contents.length / grid); + for (var i = 0; i < rowCount; i++) { + //Select first n elements, second n elements etc and set order + var j = i + 1; + _.contents.slice(i * grid, grid * j).css('order', '' + (j * grid)); + } + }; + /** + * Sets the order of each tab + */ + Gridtab.prototype.setTabOrder = function() { + var _ = this; + //Iterate through each tab and set flex order for each tab + _.tabs.each(function(i) { + $(this).css('order', '' + i); + }); + }; + /** + * Generate CSS rules + */ + Gridtab.prototype.addCssRules = function(grid, borderWidth, tabPadding, tabBorderColor, contentPadding, contentBorderColor, contentBackground, activeTabBackground, breakpoint) { + var _ = this; + if (grid !== null || borderWidth !== null || tabPadding !== null || tabBorderColor !== null || contentBorderColor !== null || contentPadding !== null || contentBackground !== null || activeTabBackground !== null) { + var cssRules = ''; + var tabWidth = ''; + (grid !== null) && (tabWidth = Math.floor((100 / grid) * 100) / 100); + if (grid !== null || borderWidth !== null || tabBorderColor !== null || tabPadding !== null) { + //Container Styles + (borderWidth !== null) && (cssRules += '.gridtab--' + instanceCount + '{padding:' + borderWidth + 'px 0 0 ' + borderWidth + 'px;}'); + //Tab Styles + cssRules += '.gridtab--' + instanceCount + ' > dt{'; + (borderWidth !== null) && (cssRules += 'margin:-' + borderWidth + 'px 0 0 -' + borderWidth + 'px;'); + (grid !== null) && (cssRules += 'min-width:calc(' + tabWidth + '% + ' + borderWidth + 'px);flex-basis:' + tabWidth + '%;-ms-flex-basis:' + tabWidth + '%;'); + (borderWidth !== null) && (cssRules += 'border-width:' + borderWidth + 'px;'); + (tabPadding !== null) && (cssRules += 'padding:' + tabPadding + 'px;'); + (tabBorderColor !== null) && (cssRules += 'border-color:' + tabBorderColor + ';'); + cssRules += '}'; + } + //active Tab Styles + (activeTabBackground !== null) && (cssRules += '.gridtab--' + instanceCount + ' >dt.is-active{background:' + activeTabBackground + ';}'); + (_.options.config.layout == 'tab' && activeTabBackground !== null && borderWidth !== null) && (cssRules += '.gridtab--' + instanceCount + ' >dt.is-active:after{background:' + activeTabBackground + ';height:' + borderWidth + 'px;bottom:-' + borderWidth + 'px;}'); + //Content Styles + if (contentBorderColor !== null || borderWidth !== null || contentBackground !== null || contentPadding !== null) { + cssRules += '.gridtab--' + instanceCount + '>dd{'; + cssRules += 'min-width:calc(' + (tabWidth * grid) + '% + ' + borderWidth + 'px);'; + (borderWidth !== null) && (cssRules += 'margin:-' + borderWidth + 'px 0 0 -' + borderWidth + 'px !important;border-width:' + borderWidth + 'px;'); + (contentBorderColor !== null) && (cssRules += 'border-color:' + contentBorderColor + ';'); + (contentPadding !== null) && (cssRules += 'padding:' + contentPadding + 'px;'); + (contentBackground !== null) && (cssRules += 'background:' + contentBackground + ';'); + cssRules += '}'; + } + //If has breakpoints generate mediaquery + _.cssRules += (breakpoint !== null) ? ('@media (max-width:' + breakpoint + 'px){' + cssRules + '}') : cssRules; + } + }; + /** + * Generate Stylesheet and append CSS rules + */ + Gridtab.prototype.generateStylesheet = function(cssRules) { + var style = $('head').append(''); + }; + /** + * For each breakpoint add CSS rules and set content order + */ + Gridtab.prototype.responsiveBreakpoints = function() { + var _ = this; + if (_.options.responsive && _.options.responsive.length) { + //Sort Responsive Options by MediaQuery in descending order + _.options.responsive.sort(function(a, b) { + return parseFloat(b.breakpoint) - parseFloat(a.breakpoint); + }); + var mqls = []; + function getBreakpoint() { + var bpArray = []; + mqls.filter(function(el) { + + //If breakpoint matches convert it to number and push the breakpoint to bpArray + if (el.matches) { + bpArray.push(parseInt(el.media.replace(/[^\d.]/g, ''))); + } + + + }); + + + //If bpArray exists, find the smallest breakpoint and its index in -.breakpoints + //else use the default grid value from options + var grid = (bpArray.length ? _.grids[_.breakpoints.indexOf(Math.min.apply(null, bpArray))] : _.options.grid); + + _.setContentOrder(grid); + } + + for (var i in _.options.responsive) { + var responsiveSettings = _.options.responsive[i].settings, + grid = responsiveSettings.grid || _.options.grid, + borderWidth = responsiveSettings.borderWidth || _.options.borderWidth, + tabBorderColor = responsiveSettings.tabBorderColor || null, + tabPadding = responsiveSettings.tabPadding || null, + activeTabBackground = responsiveSettings.activeTabBackground || null, + contentPadding = responsiveSettings.contentPadding || null, + contentBorderColor = responsiveSettings.contentBorderColor || null, + contentBackground = responsiveSettings.contentBackground || null, + breakpoint = _.options.responsive[i].breakpoint || null; + _.addCssRules(grid, borderWidth, tabPadding, tabBorderColor, contentPadding, contentBorderColor, contentBackground, activeTabBackground, breakpoint); + _.breakpoints.push(breakpoint); + _.grids.push(grid); + mqls.push(window.matchMedia("(max-width:" + _.breakpoints[i] + "px)")); + getBreakpoint(window.matchMedia("(max-width:" + _.breakpoints[i] + "px)")); + } + + //For each MedaiQuery + for (var i = 0; i < mqls.length; i++) { // loop through queries + mqls[i].addListener(getBreakpoint) // call handler function whenever the media query is triggered; + } + + } + }; + /** + * Scroll to active tab + */ + Gridtab.prototype.scrollToTab = function() { + var _ = this; + var page = $('html, body'); + page.on("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove", function() { + page.stop(); + }); + page.animate({ + scrollTop: $(_.element).find('.is-active').offset().top + }, 1000, function() { + page.off("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove"); + }); + }; + /** + * Slide up and slide down contents based on tab clicked + */ + Gridtab.prototype.slideContent = function(currTab, closeBtnClick, scrollToTab) { + var _ = this, + $currTab = $(currTab), + $prevTab = $(_.element).find('>dt.is-active').not(currTab); + if (!$currTab.hasClass('is-disabled')) { + //If an active tab already exists + if ($prevTab.length) { + //Keep all tabs disabled while the transition happens + _.tabs.addClass('is-disabled'); + $prevTab.next().stop(true).slideUp(_.options.config.speed, function() { + $prevTab.removeClass('is-active'); + _.tabs.next().stop(true); + (_.options.callbacks.close) && (_.options.callbacks.close.call(this)); + $currTab.addClass('is-active').next().stop(true).slideDown(_.options.config.speed, function() { + (_.options.callbacks.open) && (_.options.callbacks.open.call(this)); + (scrollToTab) && (_.scrollToTab()); + //Remove disabled after transition is complete + _.tabs.removeClass('is-disabled'); return false; - } - }; + }); + return false; + + }); + + } else { + //If keepOpen is true and close button is not clicked + if (_.options.config.keepOpen && !closeBtnClick) { + if (!$currTab.hasClass('is-active')) { + $currTab.addClass('is-active').next().stop(true).slideDown(_.options.config.speed, function() { + (_.options.callbacks.open) && (_.options.callbacks.open.call(this)); + (scrollToTab) && (_.scrollToTab()); + }); + return false; + } + } else { + //Keep all tabs disabled while the transition happens + _.tabs.addClass('is-disabled'); + $currTab.toggleClass('is-active').next().stop(true).slideToggle(_.options.config.speed, function() { - $.fn[gridtab] = function(options) { - return this.each(function() { + if ($(this).is(':hidden')) { + (_.options.callbacks.close) && (_.options.callbacks.close.call(this)); - if (!$.data(this, 'plugin_' + gridtab)) { - $.data(this, 'plugin_' + gridtab, - new Gridtab(this, options)); + } else { + (_.options.callbacks.open) && (_.options.callbacks.open.call(this)); + (scrollToTab) && (_.scrollToTab()); } - }); + //Remove disabled after transition is complete + _.tabs.removeClass('is-disabled'); + return false; + }); + } - }; + } + + return false; + } + }; + + $.fn[gridtab] = function(options) { + return this.each(function() { + + if (!$.data(this, 'plugin_' + gridtab)) { + $.data(this, 'plugin_' + gridtab, + new Gridtab(this, options)); + } + }); + + }; })(jQuery, window, document); diff --git a/gridtab/gridtab.min.css b/gridtab/gridtab.min.css index 9dc683d..02a8c1b 100644 --- a/gridtab/gridtab.min.css +++ b/gridtab/gridtab.min.css @@ -1 +1 @@ -.gridtab,.gridtab>dt,.gridtab>dd{margin:0;padding:0;position:relative;border:0 solid}.gridtab{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;box-sizing:border-box}.gridtab .is-disabled{pointer-events:none}.gridtab *{box-sizing:border-box}.gridtab>dd{z-index:1;display:none;min-width:100%;-webkit-flex-basis:100%;-ms-flex-preferred-size:100%;flex-basis:100%}.gridtab>dt{transition:background 1s;background:#f2f2f2}.gridtab>dt:after{content:'';position:absolute;left:0;right:0}.gridtab>dt.is-active{z-index:2}.gridtab>dt img{max-width:100%;float:left} +.gridtab,.gridtab>dt,.gridtab>dd{margin:0;padding:0;position:relative;border:0 solid}.gridtab{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;box-sizing:border-box}.gridtab *{box-sizing:border-box}.gridtab>dd{z-index:1;display:none;min-width:100%;-webkit-flex-basis:100%;-ms-flex-preferred-size:100%;flex-basis:100%}.gridtab>dd>.gridtab__controls{position:absolute;right:5px;top:5px;display:-webkit-flex;display:-ms-flexbox;display:flex}.gridtab>dd>.gridtab__controls>.gridtab__close:before,.gridtab>dd>.gridtab__controls>.gridtab__close:after{top:50%;left:50%;-webkit-transform:translate(-50%, -50%) rotate(45deg);transform:translate(-50%, -50%) rotate(45deg)}.gridtab>dd>.gridtab__controls>.gridtab__close:after{-webkit-transform:translate(-50%, -50%) rotate(-45deg);transform:translate(-50%, -50%) rotate(-45deg)}.gridtab>dd>.gridtab__controls>.gridtab__arrow,.gridtab>dd>.gridtab__controls>.gridtab__close{display:block;width:25px;height:25px;overflow:hidden;background:#666;text-indent:-999px;position:relative}.gridtab>dd>.gridtab__controls>.gridtab__arrow:before,.gridtab>dd>.gridtab__controls>.gridtab__arrow:after,.gridtab>dd>.gridtab__controls>.gridtab__close:before,.gridtab>dd>.gridtab__controls>.gridtab__close:after{position:absolute;width:3px;height:13px;content:'';display:block;background:#FFF}.gridtab>dd>.gridtab__controls>.gridtab__arrow:before,.gridtab>dd>.gridtab__controls>.gridtab__arrow:after{top:50%;left:50%;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-transform-origin:right;transform-origin:right;margin:-7px 0 0 -3px;height:10px}.gridtab>dd>.gridtab__controls>.gridtab__arrow:after{margin-top:-4px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.gridtab>dd>.gridtab__controls>.gridtab__arrow.is-disabled{opacity:.25}.gridtab>dd>.gridtab__controls>.gridtab__next{-webkit-transform:scale(-1);transform:scale(-1);-webkit-transform-origin:center;transform-origin:center}.gridtab>dt{transition:background 1s;background:#f2f2f2}.gridtab>dt:after{content:'';position:absolute;left:0;right:0}.gridtab>dt.is-disabled{pointer-events:none}.gridtab>dt.is-active{z-index:2}.gridtab>dt>img{max-width:100%;float:left} diff --git a/gridtab/gridtab.min.js b/gridtab/gridtab.min.js index 51063d7..9f78224 100644 --- a/gridtab/gridtab.min.js +++ b/gridtab/gridtab.min.js @@ -1,9 +1,9 @@ /* Version: 1.0.0 - Author: Gopal Raju + Author: Gopal Raju Website: http://www.productivedreams.com - Docs: https://gopalraju.github.io/gridtab - Repo: https://gopalraju.github.io/gridtab - Issues: https://gopalraju.github.io/gridtab/issues + Docs: https://gopalraju.github.io/gridtab + Repo: https://gopalraju.github.io/gridtab + Issues: https://gopalraju.github.io/gridtab/issues */ -!function(a,b,c,d){function h(b,c){var d=this;d.element=b,d.options=a.extend({},g,c),d.defaults=g,d.name=f,d.cssRules="",d.breakpoints=[],d.activeTab=d.options.activeTab-1,d.tabs=a(d.element).find("> dt"),d.contents=a(d.element).find("> dd"),d.init(),d.generateStylesheet(d.cssRules),d.setContentOrder(),e++}var e=0,f="gridtab",g={grid:4,borderWidth:2,tabBorderColor:"#ddd",tabPadding:25,contentBorderColor:"#ddd",contentPadding:25,contentBackground:"#fff",activeTabBackground:"#fff",keepOpen:!1,speed:500,layout:"grid",activeTab:0,responsive:null,callbacks:{open:!1,close:!1}};h.prototype.init=function(){var b=this;a(b.element).addClass("gridtab gridtab--"+e),b.setTabOrder(),b.addCssRules(b.options.grid,b.options.borderWidth,b.options.tabPadding,b.options.tabBorderColor,b.options.contentPadding,b.options.contentBorderColor,b.options.contentBackground,b.options.activeTabBackground,null),b.responsiveBreakpoints(),b.activeTab>-1&&b.activeTab