From 7b608580baa9ec9d817faac63fc1b13975d81d48 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Sun, 20 Oct 2013 14:57:39 +0200 Subject: [PATCH] Added flexible tab-based menus --- com.woltlab.wcf/templates/headInclude.tpl | 4 +- .../acp/style/blueTemptation/individual.less | 181 +++++------ wcfsetup/install/files/js/WCF.js | 241 +++++++++++++-- wcfsetup/install/files/style/layout.less | 280 ++++++++---------- 4 files changed, 436 insertions(+), 270 deletions(-) diff --git a/com.woltlab.wcf/templates/headInclude.tpl b/com.woltlab.wcf/templates/headInclude.tpl index ade02b605a..e6ad479e00 100644 --- a/com.woltlab.wcf/templates/headInclude.tpl +++ b/com.woltlab.wcf/templates/headInclude.tpl @@ -138,8 +138,9 @@ {event name='javascriptLanguageImport'} }); - + if (jQuery.browser.touch) $('html').addClass('touch'); + WCF.System.FlexibleMenu.init(); new WCF.Date.Time(); new WCF.Effect.SmoothScroll(); new WCF.Effect.BalloonTooltip(); @@ -148,7 +149,6 @@ WCF.Dropdown.init(); WCF.System.PageNavigation.init('.pageNavigation'); WCF.Date.Picker.init(); - WCF.System.MobileNavigation.init(); new WCF.User.ProfilePreview(); {event name='javascriptInit'} diff --git a/wcfsetup/install/files/acp/style/blueTemptation/individual.less b/wcfsetup/install/files/acp/style/blueTemptation/individual.less index d0897c6ff8..c89cad5d60 100644 --- a/wcfsetup/install/files/acp/style/blueTemptation/individual.less +++ b/wcfsetup/install/files/acp/style/blueTemptation/individual.less @@ -6,65 +6,67 @@ body, .tabularBox, .dialogTitlebar { } /* main menu */ +.mainMenu > ul { + background-color: rgba(0, 0, 0, .4); + + > li { + > a { + border-width: 0 !important; + color: @wcfPageLinkColor; + + .transition(color, .3s, ease); + .textShadow(@wcfPageBackgroundColor); + + &:hover { + color: @wcfPageLinkHoverColor; + } + } + + &.active > a { + .linearGradient(@wcfNavigationHeaderBackgroundColor, lighten(@wcfNavigationHeaderBackgroundColor, 20%), @wcfNavigationHeaderBackgroundColor); + } + + &:hover { + color: @wcfMainMenuActiveColor; + } + } +} + @media all and (min-width: 801px) { .mainMenu { margin: @wcfGapMedium 9px 0; > ul { - background-color: rgba(0, 0, 0, .4); border-radius: 5px 5px 0 0; - > li { - > a { - border-width: 0 !important; - color: @wcfPageLinkColor; - position: relative; - - .transition(color, .3s, ease); - .textShadow(@wcfPageBackgroundColor); + > li.active > a { + border-radius: 5px 5px 0 0; + position: relative; + + &:after { + border-bottom-left-radius: 6px; + border-width: 0 0 1px 1px; + bottom: 0; + content: ""; + height: 7px; + position: absolute; + right: -7px; + width: 7px; - &:hover { - color: @wcfPageLinkHoverColor; - } + .boxShadow(-2px, 2px, @wcfNavigationHeaderBackgroundColor, 0); } - &.active { - > a { - border-radius: 5px 5px 0 0; - - .linearGradient(@wcfNavigationHeaderBackgroundColor, lighten(@wcfNavigationHeaderBackgroundColor, 20%), @wcfNavigationHeaderBackgroundColor); - - &:after { - border-bottom-left-radius: 6px; - border-width: 0 0 1px 1px; - bottom: 0; - content: ""; - height: 7px; - position: absolute; - right: -7px; - width: 7px; - - .boxShadow(-2px, 2px, @wcfNavigationHeaderBackgroundColor, 0); - } - - &:before { - border-bottom-right-radius: 6px; - border-width: 0 1px 1px 0; - bottom: 0; - content: ""; - height: 7px; - left: -7px; - position: absolute; - width: 7px; - - .boxShadow(2px, 2px, @wcfNavigationHeaderBackgroundColor, 0); - } - - &:hover { - color: @wcfMainMenuActiveColor; - } - } - + &:before { + border-bottom-right-radius: 6px; + border-width: 0 1px 1px 0; + bottom: 0; + content: ""; + height: 7px; + left: -7px; + position: absolute; + width: 7px; + + .boxShadow(2px, 2px, @wcfNavigationHeaderBackgroundColor, 0); } } } @@ -532,47 +534,49 @@ button.active:hover { /* tab menus */ -.tabMenu { - padding: 0 10px; - - > ul { - border-top-left-radius: @wcfContainerBorderRadius; - border-top-right-radius: @wcfContainerBorderRadius; +@media all and (min-width: 801px) { + .tabMenu { + padding: 0 10px; - > li { - &.ui-state-active { - > a { - border-top-right-radius: @wcfContainerBorderRadius; - border-top-left-radius: @wcfContainerBorderRadius; - - &:before { - border: 1px solid @wcfContainerBorderColor; - border-bottom-right-radius: 6px; - border-width: 0 1px 1px 0; - bottom: -1px; - clip: rect(0, 6px, 6px, 0); - content: " "; - height: 5px; - left: -6px; - position: absolute; - width: 5px; + > ul { + border-top-left-radius: @wcfContainerBorderRadius; + border-top-right-radius: @wcfContainerBorderRadius; + + > li { + &.ui-state-active { + > a { + border-top-right-radius: @wcfContainerBorderRadius; + border-top-left-radius: @wcfContainerBorderRadius; - .boxShadow(2px, 1px, @wcfContainerBackgroundColor, 0); - } - - &:after { - border: 1px solid @wcfContainerBorderColor; - border-bottom-left-radius: 6px; - border-width: 0 0 1px 1px; - bottom: -1px; - clip: rect(0, 6px, 6px, 0); - content: " "; - height: 5px; - position: absolute; - right: -6px; - width: 5px; + &:before { + border: 1px solid @wcfContainerBorderColor; + border-bottom-right-radius: 6px; + border-width: 0 1px 1px 0; + bottom: -1px; + clip: rect(0, 6px, 6px, 0); + content: " "; + height: 5px; + left: -6px; + position: absolute; + width: 5px; + + .boxShadow(2px, 1px, @wcfContainerBackgroundColor, 0); + } - .boxShadow(-2px, 1px, @wcfContainerBackgroundColor, 0); + &:after { + border: 1px solid @wcfContainerBorderColor; + border-bottom-left-radius: 6px; + border-width: 0 0 1px 1px; + bottom: -1px; + clip: rect(0, 6px, 6px, 0); + content: " "; + height: 5px; + position: absolute; + right: -6px; + width: 5px; + + .boxShadow(-2px, 1px, @wcfContainerBackgroundColor, 0); + } } } } @@ -580,7 +584,6 @@ button.active:hover { } } - /* ### inputs ### */ /* normal state */ input[type='text'], @@ -825,4 +828,4 @@ div.tabularBox, -webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(.5, transparent), to(rgba(0, 0, 0, .4))); } } -} \ No newline at end of file +} diff --git a/wcfsetup/install/files/js/WCF.js b/wcfsetup/install/files/js/WCF.js index 1e647fa0dc..6eca13fb05 100755 --- a/wcfsetup/install/files/js/WCF.js +++ b/wcfsetup/install/files/js/WCF.js @@ -168,7 +168,7 @@ $.extend(true, { /** * Escapes an ID to work with jQuery selectors. - * + * * @see http://docs.jquery.com/Frequently_Asked_Questions#How_do_I_select_an_element_by_an_ID_that_has_characters_used_in_CSS_notation.3F * @param string id * @return string @@ -201,7 +201,7 @@ $.extend(true, { $length++; } } - + return $length; } }); @@ -555,7 +555,7 @@ $.extend(WCF, { /** * Counter for dynamic element ids - * + * * @var integer */ _idCounter: 0, @@ -756,7 +756,7 @@ WCF.Dropdown = { var $verticalScrollTolerance = $(element).height() / 2; - // check if dropdown toggle is still (partially) visible + // check if dropdown toggle is still (partially) visible if ($dropdownOffset.top + $verticalScrollTolerance <= $dialogContentOffset.top) { // top check WCF.Dropdown.toggleDropdown($dropdownID); @@ -807,12 +807,14 @@ WCF.Dropdown = { var $dropdown = button.parents('.dropdown'); if (!$dropdown.length) { // broken dropdown, ignore + console.debug("[WCF.Dropdown] Invalid dropdown passed, button '" + button.wcfIdentify() + "' does not have a parent with .dropdown, aborting."); return; } var $dropdownMenu = button.next('.dropdownMenu'); if (!$dropdownMenu.length) { // broken dropdown, ignore + console.debug("[WCF.Dropdown] Invalid dropdown passed, dropdown '" + $dropdown.wcfIdentify() + "' does not have a dropdown menu, aborting."); return; } @@ -3546,7 +3548,7 @@ WCF.String = { /** * Escapes a String to work with RegExp. - * + * * @see https://github.com/sstephenson/prototype/blob/master/src/prototype/lang/regexp.js#L25 * @param string string * @return string @@ -3682,8 +3684,6 @@ WCF.TabMenu = { } } } - - //$container.trigger('tabsbeforeactivate', event, eventData); } }); @@ -3694,6 +3694,12 @@ WCF.TabMenu = { $tabMenu.data('parent', $tabMenu.parent()); } } + + // register as flexible menu unless it has no tabs + var $navigation = $tabMenu.children('nav'); + if ($navigation.length && $navigation.find('> ul:eq(0) > li').length) { + WCF.System.FlexibleMenu.registerMenu($navigation.wcfIdentify()); + } }); // try to resolve location hash @@ -3933,7 +3939,7 @@ WCF.Template = Class.extend({ return "' + " + content + " + '"; }) // {lang}foo{/lang} - .replace(/{lang}(.+?){\/lang}/, function(_, content) { + .replace(/{lang}(.+?){\/lang}/g, function(_, content) { return "' + WCF.Language.get('" + unescape(content) + "') + '"; }) // {if} @@ -4053,7 +4059,7 @@ WCF.Template = Class.extend({ /** * Fetches the template with the given variables. - * + * * @param v variables to insert * @return parsed template */ @@ -5856,23 +5862,201 @@ WCF.Search.User = WCF.Search.Base.extend({ WCF.System = { }; /** - * Enables mobile navigation. + * Provides flexible dropdowns for tab-based menus. */ -WCF.System.MobileNavigation = { +WCF.System.FlexibleMenu = { + /** + * list of containers + * @var object + */ + _containers: { }, + + /** + * list of registered container ids + * @var array + */ + _containerIDs: [ ], + + /** + * list of dropdowns + * @var object + */ + _dropdowns: { }, + + /** + * list of dropdown menus + * @var object + */ + _dropdownMenus: { }, + + /** + * list of hidden status for containers + * @var object + */ + _hasHiddenItems: { }, + + /** + * true if menus are currently rebuilt + * @var boolean + */ + _isWorking: false, + + /** + * list of tab menu items per container + * @var object + */ + _menuItems: { }, + + /** + * Initializes the WCF.System.FlexibleMenu class. + */ init: function() { - this._convertNavigation(); + // register .mainMenu and .navigationHeader by default + this.registerMenu('mainMenu'); + this.registerMenu($('.navigationHeader:eq(0)').wcfIdentify()); - WCF.DOMNodeInsertedHandler.addCallback('WCF.System.MobileNavigation', function() { - WCF.System.MobileNavigation._convertNavigation(); - }); + $(window).resize($.proxy(this.rebuildAll, this)); }, - _convertNavigation: function() { - $('nav.jsMobileNavigation').removeClass('jsMobileNavigation').each(function(index, navigation) { - var $navigation = $(navigation); - $('').click(function() {}).prependTo($navigation); - }); + /** + * Registers a tab-based menu by id. + * + * Required DOM: + * + *
    + *
  • tab 1
  • + *
  • tab 2
  • + * ... + *
  • tab n
  • + *
+ *
+ * + * @param string containerID + */ + registerMenu: function(containerID) { + var $container = $('#' + containerID); + if (!$container.length) { + console.debug("[WCF.System.FlexibleMenu] Unable to find container identified by '" + containerID + "', aborting."); + return; + } + + this._containerIDs.push(containerID); + this._containers[containerID] = $container; + this._menuItems[containerID] = $container.find('> ul:eq(0) > li'); + this._dropdowns[containerID] = $('').data('containerID', containerID).hide().appendTo($container.children('ul')).click($.proxy(this._click, this)); + this._dropdownMenus[containerID] = $('