diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js index 2f2df7c12b..07e45ff0f7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js @@ -4,7 +4,7 @@ @restrict A @description -Use this directive make an element sticky and follow the page when scrolling. +Use this directive make an element sticky and follow the page when scrolling. `umb-sticky-bar--active` class is applied when the element is stuck
@@ -12,140 +12,102 @@ Use this directive make an element sticky and follow the page when scrolling.
-
- .my-sticky-bar {
- padding: 15px 0;
- background: #000000;
- position: relative;
- top: 0;
- }
-
- .my-sticky-bar.-umb-sticky-bar {
- top: 100px;
- }
-
-
-@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element.
**/
(function () {
'use strict';
- function StickyBarDirective($rootScope) {
+ function StickyBarDirective() {
+
+ /**
+ On initial load, the intersector fires if the grid editor is in the viewport
+ This flag is used to suppress the setClass behaviour on the initial load
+ **/
+ var initial = true;
+
+ /**
+ Toggle `umb-sticky-bar--active` class on the sticky-bar element
+ **/
+ function setClass(addClass, current) {
+ if (!initial) {
+ current.classList.toggle('umb-sticky-bar--active', addClass);
+ } else {
+ initial = false;
+ }
+ }
+
+ /**
+ Inserts two elements in the umbStickyBar parent element
+ These are used by the IntersectionObserve to calculate scroll position
+ **/
+ function addSentinels(current) {
+ ['-top', '-bottom'].forEach(s => {
+ const sentinel = document.createElement('div');
+ sentinel.classList.add('umb-sticky-sentinel', s);
+ current.parentElement.appendChild(sentinel);
+ });
+ }
+
+ /**
+ Calls into setClass when the footer sentinel enters/exits the bottom of the container
+ Container is the parent element of the umbStickyBar element
+ **/
+ function observeFooter(current, container) {
+ const observer = new IntersectionObserver((records, observer) => {
+ let [target, rootBounds, intersected] = [records[0].boundingClientRect, records[0].rootBounds, records[0].intersectionRatio === 1];
+
+ if (target.bottom > rootBounds.top && intersected) {
+ setClass(true, current);
+ }
+ if (target.top < rootBounds.top && target.bottom < rootBounds.bottom) {
+ setClass(false, current);
+ }
+ }, {
+ threshold: [1],
+ root: container
+ });
+
+ observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-bottom'));
+ }
+
+ /**
+ Calls into setClass when the header sentinel enters/exits the top of the container
+ Container is the parent element of the umbStickyBar element
+ **/
+ function observeHeader(current, container) {
+ const observer = new IntersectionObserver((records, observer) => {
+ let [target, rootBounds] = [records[0].boundingClientRect, records[0].rootBounds];
+
+ if (target.bottom < rootBounds.top) {
+ setClass(true, current);
+ }
+
+ if (target.bottom >= rootBounds.top && target.bottom < rootBounds.bottom) {
+ setClass(false, current);
+ }
+ }, {
+ threshold: [0],
+ root: container
+ });
+
+ observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-top'));
+ }
function link(scope, el, attr, ctrl) {
- var bar = $(el);
- var scrollableContainer = null;
- var clonedBar = null;
- var cloneIsMade = false;
+ let current = el[0];
+ let container = current.closest('[data-element="editor-container"]');
- function activate() {
-
- if (bar.parents(".umb-property").length > 1) {
- bar.addClass("nested");
- return;
- }
-
- if (attr.scrollableContainer) {
- scrollableContainer = bar.closest(attr.scrollableContainer);
- } else {
- scrollableContainer = $(window);
- }
-
- scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
- $(window).on('resize.umbStickyBar', determineVisibility);
-
- scope.$on('$destroy', function () {
- scrollableContainer.off('.umbStickyBar');
- $(window).off('.umbStickyBar');
- });
-
- }
-
- function determineVisibility() {
-
- var barTop = bar[0].offsetTop;
- var scrollTop = scrollableContainer.scrollTop();
-
- if (scrollTop > barTop) {
-
- if (!cloneIsMade) {
-
- createClone();
-
- clonedBar.css({
- 'visibility': 'visible'
- });
-
- } else {
-
- calculateSize();
-
- }
-
- } else {
-
- if (cloneIsMade) {
-
- //remove cloned element (switched places with original on creation)
- bar.remove();
- bar = clonedBar;
- clonedBar = null;
-
- bar.removeClass('-umb-sticky-bar');
- bar.css({
- position: 'relative',
- 'width': 'auto',
- 'height': 'auto',
- 'z-index': 'auto',
- 'visibility': 'visible'
- });
-
- cloneIsMade = false;
-
- }
-
- }
-
- }
-
- function calculateSize() {
- var width = bar.innerWidth();
- clonedBar.css({
- width: width + 10 // + 10 (5*2) because we need to add border to avoid seeing the shadow beneath. Look at the CSS.
- });
- }
-
- function createClone() {
- //switch place with cloned element, to keep binding intact
- clonedBar = bar;
- bar = clonedBar.clone();
- clonedBar.after(bar);
- clonedBar.addClass('-umb-sticky-bar');
- clonedBar.css({
- 'position': 'fixed',
- // if you change this z-index value, make sure the sticky editor sub headers do not
- // clash with umb-dropdown (e.g. the content actions dropdown in content list view)
- 'z-index': 99,
- 'visibility': 'hidden'
- });
-
- cloneIsMade = true;
- calculateSize();
-
- }
-
- activate();
+ addSentinels(current);
+ observeHeader(current, container);
+ observeFooter(current, container);
}
var directive = {
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less
index 78cccac57a..6cf3598638 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less
@@ -32,15 +32,32 @@
border-radius: 3px;
}
-.umb-editor-sub-header.-umb-sticky-bar {
- box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
+[umb-sticky-bar] {
transition: box-shadow 240ms;
- margin-top: 0;
+ margin-top: 0;
margin-bottom: 0;
- top: calc(@appHeaderHeight + @editorHeaderHeight);
+ position:sticky;
+ z-index: 99;
- .umb-editor--infinityMode & {
- top: calc(@editorHeaderHeight);
+ &.umb-sticky-bar--active {
+ box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
+ }
+}
+
+.umb-sticky-sentinel {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ pointer-events: none;
+
+ &.-top {
+ top:0px;
+ height:1px;
+ }
+
+ &.-bottom {
+ bottom:50px;
+ height:10px;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
index 9e8dd37ab9..92351d09ca 100644
--- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less
+++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
@@ -5,6 +5,7 @@
// --------------------------------------------------
.umb-property-editor {
width: 100%;
+ position:relative;
}
.umb-property-editor-tiny {
@@ -165,8 +166,6 @@
.sp-replacer {
display: inline-flex;
margin-right: 18px;
- border: solid 1px @gray-8;
- border-radius: 3px;
}
label {
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html
index c385223baf..140931ec4b 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/subheader/umb-editor-sub-header.html
@@ -1,6 +1,5 @@