mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-20 00:58:48 +02:00
N°4021 - Introduce sticky header for panels and object details (tab container to be done)
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-panel-within-main-content--sticky-sentinel-top--top: -1 * $ibo-main-content--padding-top !default;
|
||||
$ibo-panel-within-main-content--sticky-sentinel-top--height: $ibo-main-content--padding-top !default;
|
||||
|
||||
$ibo-panel-within-main-content--header--top--is-sticky: -1 * $ibo-main-content--padding-top !default;
|
||||
|
||||
#ibo-main-content {
|
||||
/*
|
||||
* Careful: Here we get all the "descendants" instead of the first closest panel in the main content, which could cause some glitches in the future.
|
||||
* For now we decided to stay that way for the following reasons, if that changes in the future keep the repercussions on descendants panels in mind.
|
||||
* - We don't have nested sticky panels (yet)
|
||||
* - We don't want to hardcode the main content's markup selector if not necessary as it could change in the future (and is already different in read-only vs edition)
|
||||
* - Unlike in JS, there no easy way to find the closest descendant
|
||||
*/
|
||||
.ibo-panel.ibo-has-sticky-header {
|
||||
/* Stickable header rules */
|
||||
> .ibo-sticky-sentinel-top {
|
||||
top: $ibo-panel-within-main-content--sticky-sentinel-top--top;
|
||||
height: $ibo-panel-within-main-content--sticky-sentinel-top--height;
|
||||
}
|
||||
> .ibo-panel--header.ibo-is-sticking {
|
||||
top: $ibo-panel-within-main-content--header--top--is-sticky;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
css/backoffice/blocks-integrations/_panel-within-modal.scss
Normal file
24
css/backoffice/blocks-integrations/_panel-within-modal.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-panel-within-modal--header--top--is-sticky: -1 * $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||
|
||||
.ui-dialog-content {
|
||||
/*
|
||||
* Careful: Here we get all the "descendants" instead of the first closest panel in the modal, which could cause some glitches in the future.
|
||||
* For now we decided to stay that way for the following reasons, if that changes in the future keep the repercussions on descendants panels in mind.
|
||||
* - We don't have nested sticky panels (yet)
|
||||
* - We don't want to hardcode the modal's markup selector if not necessary as it could change in the future (and is already different in read-only vs edition)
|
||||
* - Unlike in JS, there no easy way to find the closest descendant
|
||||
*/
|
||||
.ibo-panel {
|
||||
/* Sticky header rules */
|
||||
> .ibo-panel--header {
|
||||
&.ibo-is-sticking {
|
||||
top: $ibo-panel-within-modal--header--top--is-sticky;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,23 +19,28 @@
|
||||
/* SCSS variables */
|
||||
$ibo-panel--spacing-top: 24px !default;
|
||||
|
||||
/* - Base variables for the block */
|
||||
$ibo-panel--base-border-size: 1px !default;
|
||||
$ibo-panel--base-border-style: solid !default;
|
||||
$ibo-panel--base-border-color: $ibo-color-grey-400 !default;
|
||||
$ibo-panel--base-border: $ibo-panel--base-border-size $ibo-panel--base-border-style $ibo-panel--base-border-color !default;
|
||||
|
||||
$ibo-panel--base-transition-property: all !default;
|
||||
$ibo-panel--base-transition-duration: 0.15s !default;
|
||||
$ibo-panel--base-transition-timing-function: linear !default;
|
||||
$ibo-panel--base-transition: $ibo-panel--base-transition-property $ibo-panel--base-transition-duration $ibo-panel--base-transition-timing-function !default;
|
||||
|
||||
%ibo-panel--base-transition {
|
||||
transition: $ibo-panel--base-transition;
|
||||
}
|
||||
|
||||
/* - Specific variables for the block */
|
||||
$ibo-panel--header--margin-bottom: 4px !default;
|
||||
|
||||
$ibo-panel--icon--size: 48px !default;
|
||||
$ibo-panel--icon--spacing: 16px !default;
|
||||
$ibo-panel--icon--size-as-medallion: 72px !default;
|
||||
$ibo-panel--icon--spacing--as-medallion: 16px !default;
|
||||
$ibo-panel--icon--bottom--as-medallion: -24px !default;
|
||||
$ibo-panel--icon--background-color--as-medallion: $ibo-color-grey-100 !default;
|
||||
$ibo-panel--icon--border--as-medallion: 2px solid $ibo-color-blue-grey-300 !default;
|
||||
$ibo-panel--icon--border-radius--as-medallion: $ibo-border-radius-full !default;
|
||||
$ibo-panel--header--background-color--is-sticking: $ibo-color-grey-100 !default;
|
||||
$ibo-panel--header--border--is-sticking: $ibo-panel--base-border !default;
|
||||
|
||||
$ibo-panel--icon-background--size--must-contain: contain !default;
|
||||
$ibo-panel--icon-background--size--must-cover: cover !default;
|
||||
$ibo-panel--icon-background--size--must-zoomout: 66.67% !default;
|
||||
|
||||
$ibo-panel--title--color: $ibo-color-grey-900 !default;
|
||||
$ibo-panel--subtitle--color: $ibo-color-grey-800 !default;
|
||||
$ibo-panel--header--padding-y--is-sticking: 4px !default;
|
||||
|
||||
$ibo-panel--highlight--width: 100% !default;
|
||||
$ibo-panel--highlight--height: 8px !default;
|
||||
@@ -47,8 +52,30 @@ $ibo-panel--body--padding-bottom: 24px !default;
|
||||
$ibo-panel--body--padding-top: $ibo-panel--body--padding-bottom + $ibo-panel--highlight--height !default;
|
||||
$ibo-panel--body--padding-x: 16px !default;
|
||||
$ibo-panel--body--border-radius: $ibo-border-radius-500 !default;
|
||||
$ibo-panel--body--border-size: 1px !default;
|
||||
$ibo-panel--body--border-color: $ibo-color-grey-400 !default;
|
||||
$ibo-panel--body--border: $ibo-panel--base-border !default;
|
||||
|
||||
$ibo-panel--icon--size: 48px !default;
|
||||
$ibo-panel--icon--spacing: 16px !default;
|
||||
$ibo-panel--icon--size-as-medallion: 72px !default;
|
||||
$ibo-panel--icon--spacing--as-medallion: 16px !default;
|
||||
$ibo-panel--icon--bottom--as-medallion: -24px !default;
|
||||
$ibo-panel--icon--background-color--as-medallion: $ibo-color-grey-100 !default;
|
||||
$ibo-panel--icon--border--as-medallion: 2px solid $ibo-color-blue-grey-300 !default;
|
||||
$ibo-panel--icon--border-radius--as-medallion: $ibo-border-radius-full !default;
|
||||
|
||||
$ibo-panel--icon--size-as-medallion--is-sticking: 48px !default;
|
||||
$ibo-panel--icon--spacing--as-medallion--is-sticking: $ibo-panel--icon--spacing--as-medallion !default;
|
||||
$ibo-panel--icon--bottom--as-medallion--is-sticking: -12px !default;
|
||||
$ibo-panel--icon--border--as-medallion--is-sticking: 1px $ibo-panel--base-border-style $ibo-panel--base-border-color !default;
|
||||
|
||||
$ibo-panel--icon-background--size--must-contain: contain !default;
|
||||
$ibo-panel--icon-background--size--must-cover: cover !default;
|
||||
$ibo-panel--icon-background--size--must-zoomout: 66.67% !default;
|
||||
|
||||
$ibo-panel--title--font-size--is-sticking: $ibo-font-size-150 !default;
|
||||
$ibo-panel--title--color: $ibo-color-grey-900 !default;
|
||||
$ibo-panel--subtitle--font-size--is-sticking: $ibo-font-size-100 !default;
|
||||
$ibo-panel--subtitle--color: $ibo-color-grey-800 !default;
|
||||
|
||||
$ibo-panel--collapsible-toggler--margin-right: 8px !default;
|
||||
$ibo-panel--collapsible-toggler--font-size: $ibo-font-size-250 !default;
|
||||
@@ -95,6 +122,8 @@ $ibo-panel-colors: (
|
||||
}
|
||||
|
||||
.ibo-panel {
|
||||
position: relative;
|
||||
|
||||
&.ibo-has-icon {
|
||||
.ibo-panel--titles {
|
||||
padding-left: $ibo-panel--icon--spacing;
|
||||
@@ -107,7 +136,7 @@ $ibo-panel-colors: (
|
||||
margin-left: $ibo-panel--icon--spacing--as-medallion;
|
||||
}
|
||||
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel with a medallion icon will have its icon as a medallion too (eg. dashboard in an object)
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel could be affected too when it shouldn't (eg. dashboard in an object, n:n panel)
|
||||
> .ibo-panel--header {
|
||||
.ibo-panel--header-left {
|
||||
.ibo-panel--icon {
|
||||
@@ -176,13 +205,13 @@ $ibo-panel-colors: (
|
||||
.ibo-panel--title {
|
||||
display: inline-block;
|
||||
color: $ibo-panel--title--color;
|
||||
@extend %ibo-font-ral-sembol-250;
|
||||
@extend %ibo-font-ral-med-350;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.ibo-panel--subtitle {
|
||||
display: flex;
|
||||
@extend %ibo-font-ral-med-250;
|
||||
@extend %ibo-font-ral-med-150;
|
||||
color: $ibo-panel--subtitle--color;
|
||||
}
|
||||
|
||||
@@ -190,7 +219,7 @@ $ibo-panel-colors: (
|
||||
position: relative;
|
||||
padding: $ibo-panel--body--padding-top $ibo-panel--body--padding-x $ibo-panel--body--padding-bottom $ibo-panel--body--padding-x;
|
||||
background-color: $ibo-panel--body--background-color;
|
||||
border: solid $ibo-panel--body--border-size $ibo-panel--body--border-color;
|
||||
border: $ibo-panel--body--border;
|
||||
border-radius: $ibo-panel--body--border-radius;
|
||||
overflow: hidden; /* To force highlight color to be cropped by the border radius */
|
||||
|
||||
@@ -226,4 +255,67 @@ $ibo-panel-colors: (
|
||||
.ibo-panel--body {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sticky header rules */
|
||||
.ibo-panel.ibo-has-sticky-header {
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel could be affected too when it shouldn't (eg. dashboard in an object, n:n panel)
|
||||
> .ibo-panel--header {
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
top: 0; /* Default, stick on the top of the panel. Custom integrations should be done in the "blocks-integrations" partials */
|
||||
|
||||
/* All transitions should have a specific duration except for the header's "top" property otherwise it feels laggy */
|
||||
/* - The header itself */
|
||||
transition-property: $ibo-panel--base-transition-property, top;
|
||||
transition-duration: $ibo-panel--base-transition-duration, 0s;
|
||||
transition-timing-function: $ibo-panel--base-transition-timing-function;
|
||||
/* - Impacted descendants (we don't put "*" as it can get shaky otherwise) */
|
||||
.ibo-panel--title,
|
||||
.ibo-panel--subtitle,
|
||||
.ibo-panel--icon,
|
||||
.ibo-panel--titles{
|
||||
@extend %ibo-panel--base-transition;
|
||||
}
|
||||
|
||||
/* Transition rules */
|
||||
&.ibo-is-sticking {
|
||||
padding-top: $ibo-panel--header--padding-y--is-sticking;
|
||||
padding-bottom: $ibo-panel--header--padding-y--is-sticking;
|
||||
background-color: $ibo-panel--header--background-color--is-sticking;
|
||||
border: $ibo-panel--header--border--is-sticking;
|
||||
align-items: center;
|
||||
|
||||
.ibo-panel--title {
|
||||
font-size: $ibo-panel--title--font-size--is-sticking;
|
||||
}
|
||||
.ibo-panel--subtitle {
|
||||
font-size: $ibo-panel--subtitle--font-size--is-sticking;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.ibo-has-icon {
|
||||
&.ibo-has-medallion-icon {
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel could be affected too when it shouldn't (eg. dashboard in an object, n:n panel)
|
||||
> .ibo-panel--header {
|
||||
/* Transition rules */
|
||||
&.ibo-is-sticking {
|
||||
.ibo-panel--icon {
|
||||
bottom: $ibo-panel--icon--bottom--as-medallion--is-sticking;
|
||||
width: $ibo-panel--icon--size-as-medallion--is-sticking;
|
||||
height: $ibo-panel--icon--size-as-medallion--is-sticking;
|
||||
min-width: $ibo-panel--icon--size-as-medallion--is-sticking;
|
||||
min-height: $ibo-panel--icon--size-as-medallion--is-sticking;
|
||||
border: $ibo-panel--icon--border--as-medallion--is-sticking;
|
||||
}
|
||||
.ibo-panel--titles {
|
||||
padding-left: calc(#{$ibo-panel--icon--size-as-medallion--is-sticking} + #{$ibo-panel--icon--spacing--as-medallion--is-sticking});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@ $ibo-object-details--icon--background-color--as-medallion: $ibo-color-grey-100 !
|
||||
$ibo-object-details--icon--border--as-medallion: 2px solid $ibo-color-blue-grey-300 !default;
|
||||
$ibo-object-details--icon--border-radius--as-medallion: $ibo-border-radius-full !default;
|
||||
|
||||
$ibo-object-details--icon--size--is-sticking: $ibo-panel--icon--size-as-medallion--is-sticking !default;
|
||||
$ibo-object-details--icon--spacing--as-medallion--is-sticking: $ibo-object-details--icon--spacing--as-medallion !default;
|
||||
|
||||
$ibo-object-details--status-dot--size: 10px !default;
|
||||
$ibo-object-details--status-dot--spacing: 8px !default;
|
||||
$ibo-object-details--status-dot--border-radius: $ibo-border-radius-full !default;
|
||||
@@ -48,14 +51,6 @@ $ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !def
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-panel--title {
|
||||
@extend %ibo-font-ral-med-350;
|
||||
}
|
||||
|
||||
.ibo-panel--subtitle {
|
||||
@extend %ibo-font-ral-med-150;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-object-details--status {
|
||||
@@ -122,3 +117,40 @@ $ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !def
|
||||
background-color: $ibo-object-details--tag--separator--background-color;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sticky header rules */
|
||||
.ibo-object-details.ibo-has-sticky-header {
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel could be affected too when it shouldn't (eg. dashboard in an object, n:n panel)
|
||||
> .ibo-panel--header {
|
||||
/* All transitions should have a specific duration except for the header's "top" property otherwise it feels laggy */
|
||||
/* - Impacted descendants (we don't put "*" as it can get shaky otherwise) */
|
||||
.ibo-panel--header-left {
|
||||
@extend %ibo-panel--base-transition;
|
||||
}
|
||||
|
||||
/* Transition rules */
|
||||
&.ibo-is-sticking {
|
||||
.ibo-object-details--object-class {
|
||||
display: none; /* Make space by hiding unnecessary info */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ibo-has-icon {
|
||||
&.ibo-has-medallion-icon {
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel with a medallion icon will have its icon as a medallion too (eg. dashboard in an object)
|
||||
> .ibo-panel--header {
|
||||
/* Transition rules */
|
||||
&.ibo-is-sticking {
|
||||
.ibo-panel--header-left {
|
||||
padding-left: $ibo-object-details--icon--size--is-sticking;
|
||||
}
|
||||
|
||||
.ibo-panel--titles {
|
||||
padding-left: $ibo-object-details--icon--spacing--as-medallion--is-sticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,14 @@
|
||||
$ibo-is-code--background-color: $ibo-color-white-200 !default;
|
||||
$ibo-is-code--padding: 1.25rem 1.5rem !default;
|
||||
|
||||
$ibo-sticky-sentinel--left: 0 !default;
|
||||
$ibo-sticky-sentinel--right: 0 !default;
|
||||
$ibo-sticky-sentinel--height: 0 !default;
|
||||
$ibo-sticky-sentinel-top--top: 0 !default;
|
||||
$ibo-sticky-sentinel-top--height: $ibo-sticky-sentinel--height !default;
|
||||
$ibo-sticky-sentinel-bottom--bottom: 0 !default;
|
||||
$ibo-sticky-sentinel-bottom--height: $ibo-sticky-sentinel--height !default;
|
||||
|
||||
/**************/
|
||||
/* Visibility */
|
||||
/**************/
|
||||
@@ -126,6 +134,28 @@ body.ibo-has-fullscreen-descendant {
|
||||
@extend %ibo-font-code-150;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* Sticky headers */
|
||||
/* */
|
||||
/* Used as a trigger to make an element stick to another during scroll */
|
||||
/***********************************************************************/
|
||||
|
||||
.ibo-sticky-sentinel {
|
||||
position: absolute;
|
||||
left: $ibo-sticky-sentinel--left;
|
||||
right: $ibo-sticky-sentinel--right;
|
||||
visibility: hidden;
|
||||
}
|
||||
.ibo-sticky-sentinel-top {
|
||||
top: $ibo-sticky-sentinel-top--top;
|
||||
height: $ibo-sticky-sentinel-top--height; /* To be overloaded by use cases */
|
||||
}
|
||||
.ibo-sticky-sentinel-bottom {
|
||||
bottom: $ibo-sticky-sentinel-bottom--bottom;
|
||||
height: $ibo-sticky-sentinel-bottom--height; /* To be overloaded by use cases */
|
||||
}
|
||||
|
||||
|
||||
%ibo-medallion {
|
||||
position: relative;
|
||||
border-radius: var(--ibo-border-radius-full);
|
||||
|
||||
170
js/components/panel.js
Normal file
170
js/components/panel.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2021 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 () {
|
||||
// the widget definition, where 'itop' is the namespace,
|
||||
// 'panel' the widget name
|
||||
$.widget('itop.panel',
|
||||
{
|
||||
// default options
|
||||
options:
|
||||
{
|
||||
// The viewport element (jQuery object) to consider for the panel
|
||||
viewport_elem: null,
|
||||
// Whether the header should stay visible or not during the scroll in the "viewport_elem"
|
||||
is_header_visible_on_scroll: false,
|
||||
},
|
||||
css_classes:
|
||||
{
|
||||
has_sticky_header: 'ibo-has-sticky-header',
|
||||
is_sticking: 'ibo-is-sticking',
|
||||
sticky_sentinel: 'ibo-sticky-sentinel',
|
||||
sticky_sentinel_top: 'ibo-sticky-sentinel-top',
|
||||
sticky_sentinel_bottom: 'ibo-sticky-sentinel-bottom',
|
||||
},
|
||||
js_selectors:
|
||||
{
|
||||
modal: '.ui-dialog',
|
||||
modal_content: '.ui-dialog-content',
|
||||
panel_header: '[data-role="ibo-panel--header"]:first',
|
||||
panel_header_sticky_sentinel_top: '[data-role="ibo-panel--header--sticky-sentinel-top"]',
|
||||
panel_header_sticky_sentinel_bottom: '[data-role="ibo-panel--header--sticky-sentinel-bottom"]',
|
||||
},
|
||||
// {IntersectionObserver} Observer for the sticky header
|
||||
sticky_header_observer: null,
|
||||
|
||||
// the constructor
|
||||
_create: function () {
|
||||
this._initializeMarkup();
|
||||
this._bindEvents();
|
||||
},
|
||||
// events bound via _bind are removed automatically
|
||||
// revert other modifications here
|
||||
_destroy: function () {
|
||||
},
|
||||
_initializeMarkup: function () {
|
||||
const me = this;
|
||||
|
||||
if (this._isHeaderVisibleOnScroll()) {
|
||||
this.element.addClass(this.css_classes.has_sticky_header);
|
||||
|
||||
// Add sentinels to the markup to detect when the element changes between scrolling & sticked states
|
||||
$('<div></div>')
|
||||
.addClass(this.css_classes.sticky_sentinel)
|
||||
.addClass(this.css_classes.sticky_sentinel_top)
|
||||
.attr('data-role', 'ibo-panel--header--sticky-sentinel-top')
|
||||
.prependTo(this.element);
|
||||
$('<div></div>')
|
||||
.addClass(this.css_classes.sticky_sentinel)
|
||||
.addClass(this.css_classes.sticky_sentinel_bottom)
|
||||
.attr('data-role', 'ibo-panel--header--sticky-sentinel-bottom')
|
||||
.appendTo(this.element);
|
||||
}
|
||||
},
|
||||
_bindEvents: function () {
|
||||
const me = this;
|
||||
const oBodyElem = $('body');
|
||||
|
||||
if (this._isHeaderVisibleOnScroll()) {
|
||||
this._observeStickyHeaderChanges();
|
||||
|
||||
// When a modal opens, it could have been for this panel. As the panel is moved into the modal's markup after this JS widget is instantiated
|
||||
// the viewport is not the right one and we need to update it.
|
||||
oBodyElem.on('dialogopen', function(){
|
||||
me._observeStickyHeaderChanges();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Stickey header helpers
|
||||
/**
|
||||
* Instantiate an observer on the header to toggle its "sticky" state and fire an event
|
||||
* @private
|
||||
*/
|
||||
_observeStickyHeaderChanges: function () {
|
||||
const me = this;
|
||||
|
||||
// Determine in which kind of container the panel is
|
||||
let oNewViewportElem = null;
|
||||
// - In a modal
|
||||
if (this.element.closest(this.js_selectors.modal_content).length > 0) {
|
||||
oNewViewportElem = this.element.closest(this.js_selectors.modal_content)[0];
|
||||
}
|
||||
// - In a standard page
|
||||
else if (this.element.closest('#ibo-center-container').length > 0) {
|
||||
oNewViewportElem = this.element.closest('#ibo-center-container')[0];
|
||||
}
|
||||
|
||||
// If viewport hasn't changed, there is no need to refresh the observer
|
||||
if (oNewViewportElem === this.options.viewport_elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the reference viewport
|
||||
this.options.viewport_elem = oNewViewportElem;
|
||||
|
||||
// Clean observer if there was already one
|
||||
if (null !== this.sticky_header_observer) {
|
||||
this.sticky_header_observer.disconnect();
|
||||
}
|
||||
|
||||
// Prepare observer options
|
||||
const oOptions = {
|
||||
root: this.options.viewport_elem,
|
||||
threshold: 0,
|
||||
};
|
||||
|
||||
// Instantiate observer and callback for the top sentinel
|
||||
this.sticky_header_observer = new IntersectionObserver(function (aEntries, oObserver) {
|
||||
for (const oEntry of aEntries) {
|
||||
const oSentinelInfo = oEntry.boundingClientRect;
|
||||
const oRootInfo = oEntry.rootBounds;
|
||||
|
||||
// Started sticking.
|
||||
if (oSentinelInfo.bottom < oRootInfo.top) {
|
||||
me._onHeaderBecomesSticky();
|
||||
|
||||
}
|
||||
|
||||
// Stopped sticking.
|
||||
if (oSentinelInfo.bottom >= oRootInfo.top &&
|
||||
oSentinelInfo.bottom < oRootInfo.bottom) {
|
||||
me._onHeaderStopsBeingSticky();
|
||||
}
|
||||
}
|
||||
}, oOptions);
|
||||
this.sticky_header_observer.observe(this.element.find(this.js_selectors.panel_header_sticky_sentinel_top)[0]);
|
||||
},
|
||||
_onHeaderBecomesSticky: function () {
|
||||
this.element.find(this.js_selectors.panel_header).addClass(this.css_classes.is_sticking);
|
||||
},
|
||||
_onHeaderStopsBeingSticky: function () {
|
||||
this.element.find(this.js_selectors.panel_header).removeClass(this.css_classes.is_sticking);
|
||||
},
|
||||
|
||||
// Helpers
|
||||
/**
|
||||
* @return {boolean} True if the panel should have its header visible during scroll
|
||||
* @private
|
||||
*/
|
||||
_isHeaderVisibleOnScroll: function () {
|
||||
return this.options.is_header_visible_on_scroll;
|
||||
},
|
||||
});
|
||||
});
|
||||
36
js/layouts/object/object-details.js
Normal file
36
js/layouts/object/object-details.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
;
|
||||
$(function()
|
||||
{
|
||||
// the widget definition, where 'itop' is the namespace,
|
||||
// 'panel' the widget name
|
||||
$.widget( 'itop.object_details', $.itop.panel,
|
||||
{
|
||||
// default options
|
||||
options:
|
||||
{
|
||||
},
|
||||
css_classes:
|
||||
{
|
||||
},
|
||||
js_selectors:
|
||||
{
|
||||
},
|
||||
|
||||
// the constructor
|
||||
_create: function()
|
||||
{
|
||||
this._super();
|
||||
},
|
||||
// events bound via _bind are removed automatically
|
||||
// revert other modifications here
|
||||
_destroy: function()
|
||||
{
|
||||
this._super();
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -44,6 +44,9 @@ class Panel extends UIContentBlock
|
||||
public const BLOCK_CODE = 'ibo-panel';
|
||||
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/panel/layout';
|
||||
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'base/components/panel/layout';
|
||||
public const DEFAULT_JS_FILES_REL_PATH = [
|
||||
'js/components/panel.js',
|
||||
];
|
||||
|
||||
// Specific constants
|
||||
/** @var string ENUM_COLOR_PRIMARY */
|
||||
@@ -103,6 +106,11 @@ class Panel extends UIContentBlock
|
||||
public const DEFAULT_ICON_COVER_METHOD = self::ENUM_ICON_COVER_METHOD_CONTAIN;
|
||||
/** @var bool */
|
||||
public const DEFAULT_ICON_AS_MEDALLION = false;
|
||||
/**
|
||||
* @var bool
|
||||
* @see static::$bIsHeaderVisibleOnScroll
|
||||
*/
|
||||
public const DEFAULT_IS_HEADER_VISIBLE_ON_SCROLL = false;
|
||||
|
||||
/** @var iUIContentBlock $oTitleBlock */
|
||||
protected $oTitleBlock;
|
||||
@@ -118,6 +126,8 @@ class Panel extends UIContentBlock
|
||||
protected $sColor;
|
||||
/** @var bool $bIsCollapsible */
|
||||
protected $bIsCollapsible;
|
||||
/** @var bool $bIsHeaderVisibleOnScroll True if the header of the panel should remain visible when scrolling */
|
||||
protected $bIsHeaderVisibleOnScroll;
|
||||
|
||||
/**
|
||||
* Panel constructor.
|
||||
@@ -146,6 +156,7 @@ class Panel extends UIContentBlock
|
||||
$this->SetMainBlocks([]);
|
||||
$this->SetToolBlocks([]);
|
||||
$this->bIsCollapsible = false;
|
||||
$this->bIsHeaderVisibleOnScroll = static::DEFAULT_IS_HEADER_VISIBLE_ON_SCROLL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,6 +459,27 @@ class Panel extends UIContentBlock
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$bIsHeaderVisibleOnScroll
|
||||
* @return bool
|
||||
*/
|
||||
public function IsHeaderVisibleOnScroll(): bool
|
||||
{
|
||||
return $this->bIsHeaderVisibleOnScroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$bIsHeaderVisibleOnScroll
|
||||
*
|
||||
* @param bool $bIsHeaderVisibleOnScroll
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetIsHeaderVisibleOnScroll(bool $bIsHeaderVisibleOnScroll)
|
||||
{
|
||||
$this->bIsHeaderVisibleOnScroll = $bIsHeaderVisibleOnScroll;
|
||||
return $this;
|
||||
}
|
||||
|
||||
//----------------------
|
||||
// Specific content area
|
||||
|
||||
@@ -20,6 +20,10 @@ class ObjectDetails extends Panel implements iKeyboardShortcut
|
||||
public const BLOCK_CODE = 'ibo-object-details';
|
||||
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/layouts/object/object-details/layout';
|
||||
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'base/layouts/object/object-details/layout';
|
||||
public const DEFAULT_JS_FILES_REL_PATH = [
|
||||
'js/components/panel.js',
|
||||
'js/layouts/object/object-details.js',
|
||||
];
|
||||
|
||||
/** @var string Class name of the object (eg. "UserRequest") */
|
||||
protected $sClassName;
|
||||
|
||||
@@ -31,6 +31,9 @@ class ObjectFactory
|
||||
*/
|
||||
public static function MakeDetails(DBObject $oObject, ?string $sMode = cmdbAbstractObject::DEFAULT_OBJECT_MODE)
|
||||
{
|
||||
return new ObjectDetails($oObject, $sMode);
|
||||
$oObjectDetails = new ObjectDetails($oObject, $sMode);
|
||||
$oObjectDetails->SetIsHeaderVisibleOnScroll(true);
|
||||
|
||||
return $oObjectDetails;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
{% if oUIBlock.isCollapsible() %}
|
||||
$('#{{ oUIBlock.GetId() }}').find('[data-role="ibo-panel--collapsible-toggler"]').on('click', function(){
|
||||
$('#{{ oUIBlock.GetId() }}').toggleClass('ibo-is-opened');
|
||||
{# Collapsible handler #}
|
||||
{% block iboCollapsibleHandlers %}
|
||||
{% if oUIBlock.isCollapsible() %}
|
||||
$('#{{ oUIBlock.GetId() }}').find('[data-role="ibo-panel--collapsible-toggler"]').on('click', function(){
|
||||
$('#{{ oUIBlock.GetId() }}').toggleClass('ibo-is-opened');
|
||||
});
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block iboWidgetInstantiation %}
|
||||
$('#{{ oUIBlock.GetId() }}').panel({
|
||||
is_header_visible_on_scroll: {{ oUIBlock.IsHeaderVisibleOnScroll|var_export }}
|
||||
});
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,29 +1,33 @@
|
||||
{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
{% extends 'base/components/panel/layout.js.twig' %}
|
||||
|
||||
$('#{{ oUIBlock.GetId() }}').on('edit_object', function(){
|
||||
$(this).find('button[name="UI:Menu:Modify"]').click();
|
||||
});
|
||||
{% block iboKeyboardShortcutsHandlers %}
|
||||
$('#{{ oUIBlock.GetId() }}').on('edit_object', function(){
|
||||
$(this).find('button[name="UI:Menu:Modify"]').click();
|
||||
});
|
||||
|
||||
$('#{{ oUIBlock.GetId() }}').on('delete_object', function(){
|
||||
$(this).find('button[name="UI:Menu:Delete"]').click();
|
||||
});
|
||||
$('#{{ oUIBlock.GetId() }}').on('delete_object', function(){
|
||||
$(this).find('button[name="UI:Menu:Delete"]').click();
|
||||
});
|
||||
|
||||
$('#{{ oUIBlock.GetId() }}').on('new_object', function(){
|
||||
$(this).find('button[name="UI:Menu:New"]').click();
|
||||
});
|
||||
{% if oUIBlock.GetObjectMode() == constant('cmdbAbstractObject::ENUM_OBJECT_MODE_EDIT') or oUIBlock.GetObjectMode() == constant('cmdbAbstractObject::ENUM_OBJECT_MODE_CREATE') %}
|
||||
$('#{{ oUIBlock.GetId() }}').on('save_object', function(){
|
||||
$(this).find('button[type="submit"][name=""][value=""]').click();
|
||||
});
|
||||
{% elseif oUIBlock.GetObjectMode() == constant('cmdbAbstractObject::ENUM_OBJECT_MODE_STIMULUS') %}
|
||||
$('#{{ oUIBlock.GetId() }}').on('save_object', function(){
|
||||
$(this).find('button[type="submit"][name="submit"][value="submit"]').click();
|
||||
});
|
||||
{% endif %}
|
||||
$('#{{ oUIBlock.GetId() }}').on('new_object', function(){
|
||||
$(this).find('button[name="UI:Menu:New"]').click();
|
||||
});
|
||||
{% if oUIBlock.GetObjectMode() == constant('cmdbAbstractObject::ENUM_OBJECT_MODE_EDIT') or oUIBlock.GetObjectMode() == constant('cmdbAbstractObject::ENUM_OBJECT_MODE_CREATE') %}
|
||||
$('#{{ oUIBlock.GetId() }}').on('save_object', function(){
|
||||
$(this).find('button[type="submit"][name=""][value=""]').click();
|
||||
});
|
||||
{% elseif oUIBlock.GetObjectMode() == constant('cmdbAbstractObject::ENUM_OBJECT_MODE_STIMULUS') %}
|
||||
$('#{{ oUIBlock.GetId() }}').on('save_object', function(){
|
||||
$(this).find('button[type="submit"][name="submit"][value="submit"]').click();
|
||||
});
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
// Keep URL's hash parameters when clicking on a link of the header
|
||||
$('#{{ oUIBlock.GetId() }}').on('click', '[data-role="ibo-panel--header-right"] a', function() {
|
||||
{% block iboPanelHeaderRightActionsHandlers %}
|
||||
// Keep URL's hash parameters when clicking on a link of the header
|
||||
$('#{{ oUIBlock.GetId() }}').on('click', '[data-role="ibo-panel--header-right"] a', function() {
|
||||
aMatches = /#(.*)$/.exec(window.location.href);
|
||||
if (aMatches != null) {
|
||||
currentHash = aMatches[1];
|
||||
@@ -31,4 +35,11 @@ $('#{{ oUIBlock.GetId() }}').on('click', '[data-role="ibo-panel--header-right"]
|
||||
this.href = this.href.replace(/#(.*)$/, '#'+currentHash);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
{% endblock %}
|
||||
|
||||
{% block iboWidgetInstantiation %}
|
||||
$('#{{ oUIBlock.GetId() }}').object_details({
|
||||
is_header_visible_on_scroll: {{ oUIBlock.IsHeaderVisibleOnScroll|var_export }}
|
||||
});
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user