mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 10:38:45 +02:00
N°2847 - Tab container: Improve global look & feel; responsive tabs.
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
|
||||
#ibo-main-content{
|
||||
flex-grow: 1; /* To occupy maximum width, side content will handle its width */
|
||||
overflow-x: auto; /* To avoid main content to be too wide when the blocks within it have no width constraints. This way it will occupy only the remaining space left by the side part */
|
||||
overflow-x: auto; /* To avoid main content to be too wide when the blocks within it have no width constraints. This way it will occupy only the remaining space left by the side part. */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,23 @@ $ibo-tab-container--tab-toggler--padding-x: 24px !default;
|
||||
$ibo-tab-container--tab-container--padding-x: 32px !default;
|
||||
$ibo-tab-container--tab-container--padding-y: 32px !default;
|
||||
|
||||
$ibo-tab-container--extra-tabs-container--background-color: $ibo-tab-container--tabs-list--background-color !default;
|
||||
|
||||
$ibo-tab-container--extra-tabs-list-toggler--padding-x: 12px !default;
|
||||
|
||||
$ibo-tab-container--extra-tabs-list--max-height: 300px !default;
|
||||
$ibo-tab-container--extra-tabs-list--border-radius: $ibo-border-radius-300;
|
||||
$ibo-tab-container--extra-tabs-list--background-color: $ibo-tab-container--tabs-list--background-color !default;
|
||||
|
||||
$ibo-tab-container--extra-tab-toggler--padding: 8px 16px !default;
|
||||
$ibo-tab-container--extra-tab-toggler--max-width: 220px !default;
|
||||
$ibo-tab-container--extra-tab-toggler--text-color: $ibo-color-grey-700 !default;
|
||||
$ibo-tab-container--extra-tab-toggler--text-color--on-hover: $ibo-color-blue-800 !default;
|
||||
$ibo-tab-container--extra-tab-toggler--background-color--on-hover: $ibo-color-grey-200 !default;
|
||||
|
||||
/* Rules */
|
||||
.ibo-tab-container--tabs-list {
|
||||
position: relative;
|
||||
@extend %ibo-full-height-content;
|
||||
height: $ibo-tab-container--tabs-list--height;
|
||||
|
||||
@@ -47,11 +62,47 @@ $ibo-tab-container--tab-container--padding-y: 32px !default;
|
||||
padding-right: $ibo-tab-container--tab-toggler--padding-x;
|
||||
|
||||
@extend %ibo-text-truncated-with-ellipsis;
|
||||
color: inherit; /* To get color from parent */
|
||||
@extend %ibo-hyperlink-inherited-colors
|
||||
}
|
||||
.ibo-tab-container--extra-tabs-container{
|
||||
@extend .ibo-tab-container--tab-header;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
background-color: $ibo-tab-container--extra-tabs-container--background-color;
|
||||
}
|
||||
.ibo-tab-container--extra-tabs-list-toggler{
|
||||
@extend .ibo-tab-container--tab-toggler;
|
||||
|
||||
padding-left: $ibo-tab-container--extra-tabs-list-toggler--padding-x;
|
||||
padding-right: $ibo-tab-container--extra-tabs-list-toggler--padding-x;
|
||||
}
|
||||
.ibo-tab-container--extra-tabs-list{
|
||||
position: absolute;
|
||||
top: calc(100% + 6px);
|
||||
right: 12px;
|
||||
max-height: $ibo-tab-container--extra-tabs-list--max-height;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: $ibo-tab-container--extra-tabs-list--background-color;
|
||||
border-radius: $ibo-tab-container--extra-tabs-list--border-radius;
|
||||
@extend %ibo-elevation-100;
|
||||
}
|
||||
.ibo-tab-container--extra-tab-toggler{
|
||||
padding: $ibo-tab-container--extra-tab-toggler--padding;
|
||||
max-width: $ibo-tab-container--extra-tab-toggler--max-width;
|
||||
|
||||
color: $ibo-tab-container--extra-tab-toggler--text-color;
|
||||
@extend %ibo-text-truncated-with-ellipsis;
|
||||
|
||||
&:hover,
|
||||
&:active{
|
||||
color: inherit;
|
||||
color: $ibo-tab-container--extra-tab-toggler--text-color--on-hover;
|
||||
background-color: $ibo-tab-container--extra-tab-toggler--background-color--on-hover;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2020 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
$(function()
|
||||
{
|
||||
@@ -9,12 +26,17 @@ $(function()
|
||||
},
|
||||
css_classes:
|
||||
{
|
||||
is_hidden: 'ibo-is-hidden',
|
||||
},
|
||||
js_selectors:
|
||||
{
|
||||
tabs_list: '[data-role="ibo-tab-container--tabs-list"]',
|
||||
tab_header: '[data-role="ibo-tab-container--tab-header"]',
|
||||
tab_toggler: '[data-role="ibo-tab-container--tab-toggler"]',
|
||||
extra_tabs_container: '[data-role="ibo-tab-container--extra-tabs-container"]',
|
||||
extra_tabs_list_toggler: '[data-role="ibo-tab-container--extra-tabs-list-toggler"]',
|
||||
extra_tabs_list: '[data-role="ibo-tab-container--extra-tabs-list"]',
|
||||
extra_tab_toggler: '[data-role="ibo-tab-container--extra-tab-toggler"]',
|
||||
},
|
||||
|
||||
// the constructor
|
||||
@@ -69,13 +91,47 @@ $(function()
|
||||
$(window).on('hashchange', function(){
|
||||
me._onHashChange();
|
||||
});
|
||||
// Define our own click handler for the tabs, overriding the default.
|
||||
// Click on tab togglers
|
||||
this.element.find(this.js_selectors.tab_toggler).on('click', function(){
|
||||
me._onTogglerClick($(this));
|
||||
me._onTabTogglerClick($(this));
|
||||
});
|
||||
// Resize of the tab container
|
||||
if(window.ResizeObserver)
|
||||
{
|
||||
const oTabsListRO = new ResizeObserver(function(){
|
||||
// Note: For a reason I don't understand, when called instantly the sub function IsElementVisibleToTheUser() won't be able to retrieve an element using the document.elementFromPoint() function
|
||||
// As it won't return anything, the function always thinks it's invisible...
|
||||
setTimeout(function(){
|
||||
me._onTabContainerResize();
|
||||
}, 200);
|
||||
|
||||
});
|
||||
oTabsListRO.observe($('.ibo-tab-container--tabs-list')[0]);
|
||||
}
|
||||
// Click on extra tabs list toggler
|
||||
this.element.find(this.js_selectors.extra_tabs_list_toggler).on('click', function(oEvent){
|
||||
me._onExtraTabsListTogglerClick($(this), oEvent);
|
||||
});
|
||||
// Click on "extra tab togglers"
|
||||
this.element.find(this.js_selectors.extra_tab_toggler).on('click', function(oEvent){
|
||||
me._onExtraTabTogglerClick($(this), oEvent);
|
||||
});
|
||||
// Mostly for outside clicks that should close elements
|
||||
$('body').on('click', function(oEvent){
|
||||
me._onBodyClick(oEvent);
|
||||
});
|
||||
},
|
||||
|
||||
// Events callbacks
|
||||
// - Update tab headers display on container resize
|
||||
_onTabContainerResize: function()
|
||||
{
|
||||
const me = this;
|
||||
this.element.find(this.js_selectors.tab_header).each(function(){
|
||||
me._updateTabHeaderDisplay($(this));
|
||||
});
|
||||
this._updateExtraTabsList();
|
||||
},
|
||||
// - Update URL hash when tab is activated
|
||||
_onTabActivated: function(oUI)
|
||||
{
|
||||
@@ -120,7 +176,8 @@ $(function()
|
||||
}
|
||||
});
|
||||
},
|
||||
_onTogglerClick: function(oTabHeaderElem)
|
||||
// - Define our own click handler for the tabs, overriding the default.
|
||||
_onTabTogglerClick: function(oTabHeaderElem)
|
||||
{
|
||||
if ($.bbq) {
|
||||
let oState = {};
|
||||
@@ -135,6 +192,70 @@ $(function()
|
||||
oState[sId] = iIdx;
|
||||
$.bbq.pushState(oState);
|
||||
}
|
||||
},
|
||||
// - Forward click event to real tab toggler
|
||||
_onExtraTabTogglerClick: function(oExtraTabTogglerElem, oEvent)
|
||||
{
|
||||
// Prevent anchor default behaviour
|
||||
oEvent.preventDefault();
|
||||
|
||||
// Trigger click event on real tab toggler (the hidden one)
|
||||
const sTargetTabId = oExtraTabTogglerElem.attr('href').replace(/#/, '');
|
||||
this.element.find(this.js_selectors.tab_header+'[data-tab-id="'+sTargetTabId+'"] '+this.js_selectors.tab_toggler).trigger('click');
|
||||
},
|
||||
// - Toggle extra tabs list
|
||||
_onExtraTabsListTogglerClick: function(oElem, oEvent)
|
||||
{
|
||||
// Prevent anchor default behaviour
|
||||
oEvent.preventDefault();
|
||||
|
||||
// TODO 2.8.0: Should/could we use a popover menu instead here?
|
||||
this.element.find(this.js_selectors.extra_tabs_list).toggleClass(this.css_classes.is_hidden);
|
||||
},
|
||||
_onBodyClick: function(oEvent)
|
||||
{
|
||||
// Close extra tabs list if opened
|
||||
if($(oEvent.target.closest(this.js_selectors.extra_tabs_container)).length === 0)
|
||||
{
|
||||
this.element.find(this.js_selectors.extra_tabs_list).addClass(this.css_classes.is_hidden);
|
||||
}
|
||||
},
|
||||
|
||||
// Helpers
|
||||
/**
|
||||
* Update tab header display based on its visibility to the user
|
||||
*
|
||||
* @param oTabHeaderElem jQuery element
|
||||
* @private
|
||||
*/
|
||||
_updateTabHeaderDisplay(oTabHeaderElem)
|
||||
{
|
||||
const sTabId = oTabHeaderElem.attr('data-tab-id');
|
||||
const oMatchingExtraTabElem = this.element.find(this.js_selectors.extra_tab_toggler+'[href="#'+sTabId+'"]');
|
||||
|
||||
if(!IsElementVisibleToTheUser(oTabHeaderElem[0], true, 2))
|
||||
{
|
||||
oMatchingExtraTabElem.removeClass(this.css_classes.is_hidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
oMatchingExtraTabElem.addClass(this.css_classes.is_hidden);
|
||||
}
|
||||
},
|
||||
// - Update extra tabs list
|
||||
_updateExtraTabsList: function()
|
||||
{
|
||||
const iVisibleExtraTabsCount = this.element.find(this.js_selectors.extra_tab_toggler+':not(.'+this.css_classes.is_hidden+')').length;
|
||||
const oExtraTabsContainerElem = this.element.find(this.js_selectors.extra_tabs_container);
|
||||
|
||||
if(iVisibleExtraTabsCount > 0)
|
||||
{
|
||||
oExtraTabsContainerElem.removeClass(this.css_classes.is_hidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
oExtraTabsContainerElem.addClass(this.css_classes.is_hidden);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,16 +5,26 @@
|
||||
<ul class="ibo-tab-container--tabs-list" data-role="ibo-tab-container--tabs-list">
|
||||
{% block iboTabContainerTabsList %}
|
||||
{% for oTab in oUIBlock.GetSubBlocks() %}
|
||||
{% if oTab.GetType() == constant('TabManager::ENUM_TAB_TYPE_AJAX') %}
|
||||
<li class="ibo-tab-container--tab-header" data-role="ibo-tab-container--tab-header" data-tab-id="tab_{{ loop.index }}" data-tab-type="{{ oTab.GetType() }}" data-cache="{{ oTab.GetCache() }}">
|
||||
<a href="{{ oTab.GetUrl() }}" class="ibo-tab-container--tab-toggler" data-role="ibo-tab-container--tab-toggler"><span>{{ oTab.GetTitle() }}</span></a>
|
||||
</li>
|
||||
{% elseif oTab.GetType() == constant('TabManager::ENUM_TAB_TYPE_HTML') %}
|
||||
<li class="ibo-tab-container--tab-header" data-role="ibo-tab-container--tab-header" data-tab-id="tab_{{ loop.index }}" data-tab-type="{{ oTab.GetType() }}">
|
||||
<a href="#tab_{{ loop.index }}" class="ibo-tab-container--tab-toggler" data-role="ibo-tab-container--tab-toggler"><span>{{ oTab.GetTitle() }}</span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% block iboTabContainerTab %}
|
||||
{% if oTab.GetType() == constant('TabManager::ENUM_TAB_TYPE_AJAX') %}
|
||||
<li class="ibo-tab-container--tab-header" data-role="ibo-tab-container--tab-header" data-tab-id="tab_{{ loop.index }}" data-tab-type="{{ oTab.GetType() }}" data-cache="{{ oTab.GetCache() }}">
|
||||
<a href="{{ oTab.GetUrl() }}" class="ibo-tab-container--tab-toggler" data-role="ibo-tab-container--tab-toggler"><span>{{ oTab.GetTitle() }}</span></a>
|
||||
</li>
|
||||
{% elseif oTab.GetType() == constant('TabManager::ENUM_TAB_TYPE_HTML') %}
|
||||
<li class="ibo-tab-container--tab-header" data-role="ibo-tab-container--tab-header" data-tab-id="tab_{{ loop.index }}" data-tab-type="{{ oTab.GetType() }}">
|
||||
<a href="#tab_{{ loop.index }}" class="ibo-tab-container--tab-toggler" data-role="ibo-tab-container--tab-toggler"><span>{{ oTab.GetTitle() }}</span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endfor %}
|
||||
<li class="ibo-tab-container--extra-tabs-container ibo-tab-container--tab-header" data-role="ibo-tab-container--extra-tabs-container">
|
||||
<a href="#" class="ibo-tab-container--extra-tabs-list-toggler" data-role="ibo-tab-container--extra-tabs-list-toggler"><span class="fas fa-ellipsis-v"></span></a>
|
||||
<div class="ibo-tab-container--extra-tabs-list ibo-is-hidden" data-role="ibo-tab-container--extra-tabs-list">
|
||||
{% for oTab in oUIBlock.GetSubBlocks() %}
|
||||
<a href="#tab_{{ loop.index }}" class="ibo-tab-container--extra-tab-toggler" data-role="ibo-tab-container--extra-tab-toggler"><span>{{ oTab.GetTitle() }}</span></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
{% endblock %}
|
||||
</ul>
|
||||
{% block iboTabContainerTabsContainers %}
|
||||
|
||||
Reference in New Issue
Block a user