diff --git a/Rakefile b/Rakefile index 72821117a6cab0795f899be4d288771a0433a447..fc185438b81d55810ce9583f08a2faee8fc0f229 100644 --- a/Rakefile +++ b/Rakefile @@ -60,7 +60,7 @@ desc "Run JSDuck on ExtJS SDK" task :sdk do load_sdk_vars run_jsduck([ - "--extjs-path", "extjs/ext-all.js", + "--extjs-path", "extjs/ext-all-debug.js", # to create symbolic links to template files instead of copying them over. # Useful for development. Turn off for deployment. "--template-links", diff --git a/template/app/Application.js b/template/app/Application.js index 12579613457797295eb9e7035a9e13e678aaaa27..63d0458829f2d5c70222299a8063c334cad8b37c 100644 --- a/template/app/Application.js +++ b/template/app/Application.js @@ -46,7 +46,7 @@ Ext.define('Docs.Application', { // setInterval(function(){ // Ext.DomQuery.select('link')[4].href = "resources/css/viewport.css?" + Math.ceil(Math.random() * 100000000) - // }, 1000) + // }, 1000); } }); diff --git a/template/app/controller/Tabs.js b/template/app/controller/Tabs.js index 56feeee6612a5e88c86d05f2e3c80445a3baa44e..18082bd3c29caffa0968029db725d564e57f0e50 100644 --- a/template/app/controller/Tabs.js +++ b/template/app/controller/Tabs.js @@ -65,6 +65,15 @@ Ext.define('Docs.controller.Tabs', { // }, this); // } }, + resize: function() { + Ext.getCmp('doctabs').resizeTabs(); + }, + scope: this + }, + '#tabOverflowMenu menuitem': { + click: function(cmp) { + this.activateTab(cmp.href, true); + }, scope: this } }); @@ -110,6 +119,7 @@ Ext.define('Docs.controller.Tabs', { Ext.getCmp('doctabs').activateTab(next); Docs.History.push(next); } + docTab.dom.removed = true; docTab.animate({ to: { top: 30 } }).animate({ @@ -138,8 +148,7 @@ Ext.define('Docs.controller.Tabs', { return; } var url = Ext.get(el).down('.tabUrl').getAttribute('href'); - Ext.getCmp('doctabs').activateTab(url); - Docs.History.push(url); + this.activateTab(url); }, this, { delegate: '.doctab' }); @@ -148,6 +157,11 @@ Ext.define('Docs.controller.Tabs', { delegate: '.tabUrl', preventDefault: true }); + }, + + activateTab: function(url, activateOverview) { + Ext.getCmp('doctabs').activateTab(url, activateOverview); + Docs.History.push(url); } }); diff --git a/template/app/view/Tabs.js b/template/app/view/Tabs.js index 0a86f65ecca53646b38a740306f4f4fcf9bbf720..05ee0fb0b94c09774a99f162087b40cb251fc4ff 100644 --- a/template/app/view/Tabs.js +++ b/template/app/view/Tabs.js @@ -10,6 +10,11 @@ Ext.define('Docs.view.Tabs', { componentCls: 'doctabs', openTabs: [], + tabBarTabs: [], + overflowTabs: [], + + tabWidth: 140, + minTabWidth: 80, initComponent: function() { var tpl = new Ext.XTemplate( @@ -20,7 +25,8 @@ Ext.define('Docs.view.Tabs', { '
', '', '', - '
 
' + '
 
', + '
' ); this.html = tpl.applyTemplate([ @@ -34,6 +40,20 @@ Ext.define('Docs.view.Tabs', { this.callParent(); }, + listeners: { + afterrender: function() { + Ext.create('Ext.button.Button', { + baseCls: null, + renderTo: 'tabOverflow', + menu: { + id: 'tabOverflowMenu', + plain: true, + items: [] + } + }); + } + }, + tabQueue: [], /** @@ -48,66 +68,134 @@ Ext.define('Docs.view.Tabs', { * @param {Boolean} opts.activate True to activate the tab */ addTab: function(tab, opts) { + + // If there is room add tab + // If no room, move last tab to overflow menu and update with current tab + if (!Ext.Array.contains(this.openTabs, tab.href)) { - var tpl = Ext.create('Ext.XTemplate', - '' - ); - var docTab = Ext.get(tpl.append(this.el.dom, tab)); - docTab.dom.initialWidth = docTab.getWidth(); - - if (opts.animate) { - // Effect to 'slide' the tab out when it is created. - docTab.setStyle('width', '10px'); - docTab.setStyle({ visibility: 'visible' }); - docTab.animate({ - to: { width: docTab.dom.initialWidth } - }); - } - else { - docTab.setStyle({ visibility: 'visible' }); - } this.openTabs.push(tab.href); Docs.Settings.set('openTabs', this.openTabs); + + if (this.overflowing()) { + Ext.get('tabOverflow').show(); + } else { + this.addTabToBar(tab, opts); + } + + Ext.getCmp('tabOverflowMenu').add({ + text: tab.text, + iconCls: tab.iconCls, + origIcon: tab.iconCls, + href: tab.href, + cls: 'x-menu-item-checked' + (this.overflowing() ? ' overflow' : '') + }); } if (opts.activate) { this.activateTab(tab.href); } - this.recalculateWidths(); + this.resizeTabs(); + }, + + addTabToBar: function(tab, opts) { + + this.tabBarTabs.push(tab.url); + + var tpl = Ext.create('Ext.XTemplate', + '' + ); + var docTab = Ext.get(tpl.append(this.el.dom, tab)); + + if (opts.animate) { + // Effect to 'slide' the tab out when it is created. + docTab.setStyle('width', '10px'); + docTab.setStyle({ visibility: 'visible' }); + docTab.animate({ + to: { width: this.tabWidth - this.tabDelta() } + }); + } + else { + docTab.setStyle({ visibility: 'visible' }); + } + }, + + /** + * Returns the width of the Tab Bar + * @return {Number} Tab bar width + */ + tabBarWidth: function() { + return Ext.getCmp('doctabs').getWidth() - 240; + }, + + /** + * Returns the cumulative width of all visible tabs + * @return {Number} Tabs width + */ + totalTabsWidth: function() { + return this.openTabs.length * this.tabWidth; + }, + + /** + * Returns the cumulative width of all visible tabs + * @return {Number} Tabs width + */ + minTabsWidth: function() { + return this.openTabs.length * this.minTabWidth; + }, + + maxVisibleTabs: function() { + return Math.ceil(this.tabBarWidth() / this.minTabWidth); + }, + + /** + * Returns the width delta to be applied to each tab for them to fit within the tab bar + * @return {Number} Number of pixels + */ + tabDelta: function() { + + var numTabs = this.maxVisibleTabs(); + if (this.openTabs.length < numTabs) numTabs = this.openTabs.length; + + var delta = Math.ceil((this.totalTabsWidth() - this.tabBarWidth()) / this.openTabs.length); + return (delta < 0) ? 0 : delta; + }, + + /** + * Returns true if the tab bar is overflowing + */ + overflowing: function() { + return ((this.openTabs.length - 1) * this.minTabWidth) > this.tabBarWidth(); }, - recalculateWidths: function() { + /** + * Resizes the tabs + */ + resizeTabs: function() { - var maxWidth = Ext.getCmp('doctabs').getWidth() - 240; - var numTabs = Ext.query('.doctab').length - 5; + clearTimeout(this.resizeTabsTimer); - var tabsWidth = Ext.Array.sum(Ext.Array.map(Ext.query('.doctab'), function(t){ - var docTab = Ext.get(t); - return docTab.dom.initialWidth - 5 || 0; - })); + if (this.totalTabsWidth() > this.tabBarWidth()) { - if (tabsWidth > maxWidth) { - var tabDelta = Math.ceil((tabsWidth - maxWidth) / numTabs); + var tabDelta = this.tabDelta(); Ext.Array.each(Ext.query('.doctab'), function(t){ var docTab = Ext.get(t); - if (!docTab.hasCls('overview')) { - var width = docTab.dom.initialWidth; - var newWidth = (width - tabDelta) > 60 ? (width - tabDelta) : 60; + if (!docTab.dom.removed && !docTab.hasCls('overview')) { + var newWidth = (this.tabWidth - tabDelta) > this.minTabWidth ? (this.tabWidth - tabDelta) : this.minTabWidth; docTab.animate({ to: { width: newWidth } }); } - }); + }, this); } }, @@ -116,7 +204,7 @@ Ext.define('Docs.view.Tabs', { * * @param {String} url URL of the tab to activate */ - activateTab: function(url) { + activateTab: function(url, activateOverview) { this.activeTab = Ext.Array.indexOf(this.openTabs, url); Ext.Array.each(Ext.query('.doctab a[class=tabUrl]'), function(d) { Ext.get(d).up('.doctab').removeCls(['active', 'highlight']); @@ -126,12 +214,19 @@ Ext.define('Docs.view.Tabs', { var docTab = Ext.get(activeTab).up('.doctab'); docTab.addCls('active'); if (!docTab.hasCls('overview')) { - var overviewTab = Ext.query('.doctab.' + this.getControllerName(url).toLowerCase()); - if (overviewTab && overviewTab[0]) { - Ext.get(overviewTab[0]).addCls('highlight'); - } + activateOverview = true; } } + if (activateOverview) { + var overviewTab = Ext.query('.doctab.' + this.getControllerName(url).toLowerCase()); + if (overviewTab && overviewTab[0]) { + Ext.get(overviewTab[0]).addCls('highlight'); + } + } + + Ext.Array.each(Ext.ComponentQuery.query('#tabOverflowMenu menuitem'), function(menuItem) { + menuItem.setIconCls(menuItem.href == url ? undefined : menuItem.origIcon); + }); }, /** @@ -149,7 +244,21 @@ Ext.define('Docs.view.Tabs', { if (this.activeTab > idx) { this.activeTab -= 1; } + + Ext.Array.each(Ext.ComponentQuery.query('#tabOverflowMenu menuitem[href=' + url + ']'), function(menuItem) { + Ext.getCmp('tabOverflowMenu').remove(menuItem); + }); + } + + if (this.resizeTabsTimer) { + clearTimeout(this.resizeTabsTimer); } + + var self = this; + this.resizeTabsTimer = setTimeout(function(){ + self.resizeTabs(); + }, 1000); + if (idx === this.activeTab) { if (this.openTabs.length === 0) { Docs.App.getController(this.getControllerName(url)).loadIndex(); @@ -183,14 +292,3 @@ Ext.define('Docs.view.Tabs', { } } }); - - - -// Ext.Array.each(Ext.query('.doctab'), function(t){ -// var docTab = Ext.get(t); -// if (!docTab.hasCls('overview')) { -// docTab.animate({ -// to: { width: 60 } -// }); -// } -// }); diff --git a/template/app/view/cls/Header.js b/template/app/view/cls/Header.js index 89eccf83983703021b83872a095282fe14627a9b..7f3f3900c8964705700337ab2a7fa35926c35d60 100644 --- a/template/app/view/cls/Header.js +++ b/template/app/view/cls/Header.js @@ -8,6 +8,7 @@ Ext.define('Docs.view.cls.Header', { // will not be correct if not set explicitly height: 55, alias: 'widget.classheader', + cls: 'classheader', initComponent: function() { this.tpl = Ext.create('Ext.XTemplate', @@ -20,6 +21,7 @@ Ext.define('Docs.view.cls.Header', { 'xtype: {[values.xtypes.join(", ")]}', '', '', + 'Print', { getClass: function(cls) { if (cls.component) { diff --git a/template/resources/images/print.png b/template/resources/images/print.png new file mode 100644 index 0000000000000000000000000000000000000000..f19d9a607903b3683c314144720827c3c63a7691 Binary files /dev/null and b/template/resources/images/print.png differ diff --git a/template/resources/images/tab-overflow.png b/template/resources/images/tab-overflow.png new file mode 100644 index 0000000000000000000000000000000000000000..eec3d544d6827b827cb1339259fc834eadcf0d77 Binary files /dev/null and b/template/resources/images/tab-overflow.png differ diff --git a/template/resources/sass/viewport.scss b/template/resources/sass/viewport.scss index 12a3a823644077f3e6612301807171d341b7f467..91ab2b120cedf8b517168c233a92089af1f02a57 100644 --- a/template/resources/sass/viewport.scss +++ b/template/resources/sass/viewport.scss @@ -284,6 +284,16 @@ a { color: #e7ba27; letter-spacing: 0px; } } + .classheader .print { + background: url(../images/print.png) no-repeat; + position: absolute; + right: 0; + top: 5px; + display: block; + text-indent: -9999px; + width: 32px; + height: 32px; } + h1.class { a { background: url(../images/class-m.png) no-repeat 0 -5px; @@ -843,8 +853,7 @@ a { } a.tabUrl { padding: 0 14px 0 17px; - max-width: 180px; - text-overflow: ellipsis; + width: 140px; overflow: hidden; } span.icn { @@ -901,6 +910,9 @@ a { .doctab.overview .m { z-index: 6; } + .doctab.overview .m a.tabUrl{ + width: auto !important; + } .doctab.index .m a { background: url(../images/tab-icons.png) no-repeat 7px 1px; padding-left: 16px; padding-right: 12px; padding-bottom: 20px; @@ -921,6 +933,23 @@ a { background: url(../images/tab-icons.png) no-repeat 7px -93px; padding-left: 16px; padding-right: 12px; } + + #tabOverflow { + position: absolute; + right: 5px; + top: 8px; + button { + display: block; + width: 14px; + height: 20px; + background: url(../images/tab-overflow.png) no-repeat; + border: 0; + } + } +} + +.overflow { + background: #e3e3e3; } .all-demos {