N°3560 Add placeholder and a button to load remote tabs in scroll mode

This commit is contained in:
Stephen Abello
2021-02-19 11:45:06 +01:00
parent be38bca83d
commit e650297ded
7 changed files with 325 additions and 10 deletions

View File

@@ -38,6 +38,14 @@ $ibo-tab-container--tab-container--label--spacing: 20px !default;
$ibo-tab-container--tab-container--label--margin-bottom: 20px !default;
$ibo-tab-container--tab-container--label--span--margin-left: 40px !default;
$ibo-tab--temporary-remote-content-max-height: 300px !default;
$ibo-tab--temporary-remote-content--button--color: $ibo-color-grey-800 !default;
$ibo-tab--temporary-remote-content--button--hover--opacity: 0.5 !default;
$ibo-tab--temporary-remote-content--button--hover--background-color: $ibo-color-grey-900 !default;
$ibo-tab--temporary-remote-content--button--hover--color: $ibo-color-grey-200 !default;
/* Rules */
.ibo-tab-container--tabs-list {
position: relative;
@@ -116,7 +124,12 @@ $ibo-tab-container--tab-container--label--span--margin-left: 40px !default;
padding: $ibo-tab-container--tab-container--padding-y $ibo-tab-container--tab-container--padding-x;
}
.ibo-is-scrollable .ibo-tab-container--tab-container--label{
display: block;
}
.ibo-tab-container--tab-container--label{
display: none;
margin-bottom: $ibo-tab-container--tab-container--label--margin-bottom;
overflow-x: hidden;
> span{
@@ -144,4 +157,41 @@ $ibo-tab-container--tab-container--label--span--margin-left: 40px !default;
left: 100%;
margin-left: $ibo-tab-container--tab-container--label--spacing;
}
}
.ibo-tab--temporary-remote-content{
position: relative;
}
.ibo-tab--temporary-remote-content--placeholder{
height: auto;
position: relative;
max-height: $ibo-tab--temporary-remote-content-max-height;
>svg{
max-height: $ibo-tab--temporary-remote-content-max-height;
}
}
.ibo-tab--temporary-remote-content--button{
position: absolute;
top: 0;
display: flex;
justify-content: center;
align-content: center;
flex-direction: column;
text-align: center;
height: 100%;
width: 100%;
cursor: pointer;
@extend %ibo-font-ral-med-300;
background-color: transparent;
color: $ibo-tab--temporary-remote-content--button--color;
&:hover{
opacity: $ibo-tab--temporary-remote-content--button--hover--opacity;
background-color: $ibo-tab--temporary-remote-content--button--hover--background-color;
color: $ibo-tab--temporary-remote-content--button--hover--color;
}
}

View File

@@ -6,4 +6,5 @@
Dict::Add('EN US', 'English', 'English', [
'UIBlock:Error:AddBlockNotTabForbidden' => 'Cannot add block %1$s to %2$s (only Tab blocks are allowed)',
'UIBlock:TabContainer:RemoteTabLoad' => 'Click to load this tab',
]);

View File

@@ -0,0 +1,87 @@
<svg
role="img"
aria-labelledby="loading-aria"
viewBox="0 0 1000 300"
preserveAspectRatio="none"
>
<title id="loading-aria">Loading...</title>
<rect
x="0"
y="0"
width="100%"
height="100%"
clip-path="url(#clip-path)"
style='fill: url("#fill");'
></rect>
<defs>
<clipPath id="clip-path">
<rect x="0" y="0" rx="0" ry="0" width="NaN" height="NaN" />
<rect x="36" y="16" rx="5" ry="5" width="326" height="15" />
<circle cx="14" cy="68" r="12" />
<rect x="36" y="61" rx="5" ry="5" width="326" height="15" />
<circle cx="13" cy="26" r="12" />
<rect x="405" y="16" rx="0" ry="0" width="163" height="146" />
<circle cx="45" cy="135" r="42" />
<rect x="103" y="104" rx="5" ry="5" width="156" height="15" />
<rect x="104" y="146" rx="5" ry="5" width="257" height="15" />
<rect x="584" y="14" rx="5" ry="5" width="326" height="15" />
<rect x="922" y="14" rx="5" ry="5" width="69" height="15" />
<rect x="584" y="46" rx="5" ry="5" width="326" height="15" />
<rect x="922" y="46" rx="5" ry="5" width="69" height="15" />
<rect x="584" y="78" rx="5" ry="5" width="326" height="15" />
<rect x="922" y="78" rx="5" ry="5" width="69" height="15" />
<rect x="584" y="110" rx="5" ry="5" width="326" height="15" />
<rect x="922" y="110" rx="5" ry="5" width="69" height="15" />
<rect x="440" y="186" rx="5" ry="5" width="326" height="15" />
<circle cx="418" cy="238" r="12" />
<rect x="440" y="231" rx="5" ry="5" width="326" height="15" />
<circle cx="417" cy="196" r="12" />
<rect x="410" y="269" rx="5" ry="5" width="352" height="27" />
<rect x="805" y="176" rx="0" ry="0" width="163" height="146" />
<circle cx="44" cy="250" r="42" />
<rect x="102" y="219" rx="5" ry="5" width="156" height="15" />
<rect x="103" y="261" rx="5" ry="5" width="257" height="15" />
</clipPath>
<linearGradient id="fill">
<stop
offset="0.599964"
stop-color="#f3f3f3"
stop-opacity="1"
>
<animate
attributeName="offset"
values="-2; -2; 1"
keyTimes="0; 0.25; 1"
dur="2s"
repeatCount="indefinite"
></animate>
</stop>
<stop
offset="1.59996"
stop-color="#ecebeb"
stop-opacity="1"
>
<animate
attributeName="offset"
values="-1; -1; 2"
keyTimes="0; 0.25; 1"
dur="2s"
repeatCount="indefinite"
></animate>
</stop>
<stop
offset="2.59996"
stop-color="#f3f3f3"
stop-opacity="1"
>
<animate
attributeName="offset"
values="0; 0; 3"
keyTimes="0; 0.25; 1"
dur="2s"
repeatCount="indefinite"
></animate>
</stop>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -4,10 +4,25 @@ $.widget( "itop.scrollabletabs", $.ui.tabs, {
tab_toggler: '[data-role="ibo-tab-container--tab-toggler"]',
tab_container_list: '[data-role="ibo-tab-container--tab-container-list"]'
},
options:
{
remote_tab_load_dict: 'Click to load',
remotePanelCreated: function( panel, tab , placeholder ) {
if(tab.attr('data-role') === 'ibo-tab-container--tab-header')
{
panel.prepend('<div class="ibo-tab-container--tab-container--label"><span>' + tab.text() + '</span></div>');
var oTempDiv = $('<div>').addClass('ibo-tab--temporary-remote-content')
var oPlaceholder = $('<div>').addClass('ibo-tab--temporary-remote-content--placeholder').load(GetAbsoluteUrlAppRoot()+'images/placeholders/skeleton.svg');
var oLoadButton = $('<div>').addClass('ibo-tab--temporary-remote-content--button').text(placeholder).on('click', function(){tab.find('a').click()})
oTempDiv.append(oPlaceholder)
oTempDiv.append(oLoadButton)
panel.append(oTempDiv);
}
},
},
controller: null,
_create: function() {
var me = this;
// Initialize a single controller for this tab container
this.controller = new ScrollMagic.Controller({'container': '#' + this.element.find(this.js_selectors.tab_container_list).attr('id'), 'refreshInterval' : 200});
@@ -20,12 +35,12 @@ $.widget( "itop.scrollabletabs", $.ui.tabs, {
this._super(this.options);
// Load remote tabs as soon as possible
//Load remote tabs as soon as possible
$(this.js_selectors.tab_toggler).each(function() {
var that = this;
if($(that).attr('href').charAt(0) !== '#') {
var index = $(this).parent('li').prevAll().length
me.load(index);
//me.load(index);
}
});
@@ -173,7 +188,169 @@ $.widget( "itop.scrollabletabs", $.ui.tabs, {
tabIndex: 0
} );
},
// jQuery UI overload
// Trigger a new event
_processTabs: function() {
var that = this,
prevTabs = this.tabs,
prevAnchors = this.anchors,
prevPanels = this.panels;
this.tablist = this._getList().attr( "role", "tablist" );
this._addClass( this.tablist, "ui-tabs-nav",
"ui-helper-reset ui-helper-clearfix ui-widget-header" );
// Prevent users from focusing disabled tabs via click
this.tablist
.on( "mousedown" + this.eventNamespace, "> li", function( event ) {
if ( $( this ).is( ".ui-state-disabled" ) ) {
event.preventDefault();
}
} )
// Support: IE <9
// Preventing the default action in mousedown doesn't prevent IE
// from focusing the element, so if the anchor gets focused, blur.
// We don't have to worry about focusing the previously focused
// element since clicking on a non-focusable element should focus
// the body anyway.
.on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
this.blur();
}
} );
this.tabs = this.tablist.find( "> li:has(a[href])" )
.attr( {
role: "tab",
tabIndex: -1
} );
this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );
this.anchors = this.tabs.map( function() {
return $( "a", this )[ 0 ];
} )
.attr( {
tabIndex: -1
} );
this._addClass( this.anchors, "ui-tabs-anchor" );
this.panels = $();
this.anchors.each( function( i, anchor ) {
var selector, panel, panelId,
anchorId = $( anchor ).uniqueId().attr( "id" ),
tab = $( anchor ).closest( "li" ),
originalAriaControls = tab.attr( "aria-controls" );
// Inline tab
if ( that._isLocal( anchor ) ) {
selector = anchor.hash;
panelId = selector.substring( 1 );
panel = that.element.find( that._sanitizeSelector( selector ) );
// remote tab
} else {
// If the tab doesn't already have aria-controls,
// generate an id by using a throw-away element
panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
selector = "#" + panelId;
panel = that.element.find( selector );
if ( !panel.length ) {
panel = that._createPanel( panelId );
panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
that.options.remotePanelCreated(panel, tab, that.options.remote_tab_load_dict);
}
panel.attr( "aria-live", "polite" );
}
if ( panel.length ) {
that.panels = that.panels.add( panel );
}
if ( originalAriaControls ) {
tab.data( "ui-tabs-aria-controls", originalAriaControls );
}
tab.attr( {
"aria-controls": panelId,
"aria-labelledby": anchorId
} );
panel.attr( "aria-labelledby", anchorId );
} );
this.panels.attr( "role", "tabpanel" );
this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
// Avoid memory leaks (#10056)
if ( prevTabs ) {
this._off( prevTabs.not( this.tabs ) );
this._off( prevAnchors.not( this.anchors ) );
this._off( prevPanels.not( this.panels ) );
}
},
// jQuery UI overload
// Append content to panel instead of replacing all html
load: function( index, event ) {
index = this._getIndex( index );
var that = this,
tab = this.tabs.eq( index ),
anchor = tab.find( ".ui-tabs-anchor" ),
panel = this._getPanelForTab( tab ),
eventData = {
tab: tab,
panel: panel
},
complete = function( jqXHR, status ) {
if ( status === "abort" ) {
that.panels.stop( false, true );
}
that._removeClass( tab, "ui-tabs-loading" );
panel.removeAttr( "aria-busy" );
if ( jqXHR === that.xhr ) {
delete that.xhr;
}
};
// Not remote
if ( this._isLocal( anchor[ 0 ] ) ) {
return;
}
this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
// Support: jQuery <1.8
// jQuery <1.8 returns false if the request is canceled in beforeSend,
// but as of 1.8, $.ajax() always returns a jqXHR object.
if ( this.xhr && this.xhr.statusText !== "canceled" ) {
this._addClass( tab, "ui-tabs-loading" );
panel.attr( "aria-busy", "true" );
this.xhr
.done( function( response, status, jqXHR ) {
// support: jQuery <1.8
// http://bugs.jquery.com/ticket/11778
setTimeout( function() {
var tempdiv = $('<div>').addClass('ibo-tab').html(response);
panel.find('.ibo-tab--temporary-remote-content').remove();
panel.append( tempdiv );
that._trigger( "load", event, eventData );
complete( jqXHR, status );
}, 1 );
} )
.fail( function( jqXHR, status ) {
// support: jQuery <1.8
// http://bugs.jquery.com/ticket/11778
setTimeout( function() {
complete( jqXHR, status );
}, 1 );
} );
}
},
// Set the current tab information
setTab : function(tab){
this.active = tab;

View File

@@ -23,6 +23,7 @@ $(function()
// default options
options:
{
remote_tab_load_dict: 'Click to load',
},
css_classes:
{
@@ -64,7 +65,8 @@ $(function()
classes: {
'ui-tabs-panel': 'ibo-tab-container--tab-container', // For ajax tabs, so their containers have the right CSS classes
},
active: $.bbq.getState( this.element.attr('id'), true ) || 0
active: $.bbq.getState( this.element.attr('id'), true ) || 0,
remote_tab_load_dict: this.options.remote_tab_load_dict
};
if ($.bbq) {
// Enable tabs on all tab widgets. The `event` property must be overridden so

View File

@@ -47,14 +47,12 @@
{% block iboTabContainerTabsContainers %}
{% if not aPage.isPrintable %}
<div id="{{ oUIBlock.GetId() }}--tab-container-list" class="ibo-tab-container--tab-container-list" data-role="ibo-tab-container--tab-container-list">
<div id="{{ oUIBlock.GetId() }}--tab-container-list" class="ibo-tab-container--tab-container-list{% if oUIBlock.GetIsScrollable() %} ibo-is-scrollable{% endif %}" data-role="ibo-tab-container--tab-container-list">
{% for oTab in oUIBlock.GetSubBlocks() %}
{% if oTab.GetType() == constant('TabManager::ENUM_TAB_TYPE_HTML') %}
<div id="tab_{{ oTab.GetId()|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER')) }}"
class="ibo-tab-container--tab-container">
{% if oUIBlock.GetIsScrollable() %}
<div class="ibo-tab-container--tab-container--label"><span>{{ oTab.GetTitle() }}</span></div>
{% endif %}
<div class="ibo-tab-container--tab-container--label"><span>{{ oTab.GetTitle() }}</span></div>
{{ render_block(oTab, {aPage: aPage}) }}
</div>
{% endif %}

View File

@@ -2,7 +2,7 @@
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% if not aPage.isPrintable %}
$('#{{ oUIBlock.GetId() }}').tab_container();
$('#{{ oUIBlock.GetId() }}').tab_container({'remote_tab_load_dict': '{{ 'UIBlock:TabContainer:RemoteTabLoad'|dict_s|escape('js') }}'});
{% else %}
{% for oTab in oUIBlock.GetSubBlocks() %}
oHiddeableChapters['tab_{{ oTab.GetId()|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER')) }}'] = '{{ oTab.GetTitle()|escape('js') }}';