@progress/telerik-jquery-report-viewer
Version:
Progress® Telerik® Report Viewer for jQuery
279 lines (276 loc) • 14.1 kB
JavaScript
import { rectangle, getColorAlphaValue } from './utils.js';
var UIFreezeCoordinator = {
$placeholder: null,
$scrollableContainer: null,
// Holds all items initial position per container
itemsInitialState: {},
// Holds the bounds of the frozen areas by X per container
xFrozenAreasBounds: {},
// Holds the bounds of the frozen areas by Y per container
yFrozenAreasBounds: {},
freezeMaxZIndex: {},
zIndex: 1,
// Holds default background-color value per container
freezeBGColor: {},
// Holds whether freezing has been applied per container.
currentlyFrozenContainer: {
vertical: {},
horizontal: {}
},
isInitialize: false,
scaleFactor: null,
/**
* Initialize the uiFreezeCoordinator object
* Takes one parameter
* - $placeholder - PageArea jQuery DOM element
*/
init: function($placeholder) {
this.$placeholder = $placeholder;
this.$scrollableContainer = $placeholder.find(".trv-page-container");
if (this.isInitialize) {
this.reset($placeholder);
}
this._attachToScrollEvent();
this.isInitialize = true;
},
reset: function($placeholder) {
this.$placeholder = $placeholder;
this.$scrollableContainer = $placeholder.find(".trv-page-container");
this.itemsInitialState = {};
this.xFrozenAreasBounds = {};
this.yFrozenAreasBounds = {};
this.currentlyfreezedContainer = {
vertical: {},
horizontal: {}
};
},
setScaleFactor: function(scale) {
this.scaleFactor = scale;
},
/**
* Initializing scroll listener
*/
_attachToScrollEvent: function() {
var thisInstance = this;
this.$scrollableContainer.scroll(function updateFreezeUIOnScroll() {
var $freezeItems = thisInstance.$scrollableContainer.find("div[data-sticky-id]");
if ($freezeItems.length) {
var tableIDs = $freezeItems.map(function(index2, $element) {
return $($element).attr("data-sticky-id");
}).get();
var uniqueIDs = tableIDs.filter(function(item, index2) {
return index2 === tableIDs.indexOf(item);
});
var scrollableContainerScrollTop = thisInstance.$scrollableContainer.scrollTop();
var scrollableContainerScrollLeft = thisInstance.$scrollableContainer.scrollLeft();
for (var index = 0; index < uniqueIDs.length; index++) {
var freezeItemsContainerID = uniqueIDs[index];
if (!thisInstance.itemsInitialState[freezeItemsContainerID]) {
thisInstance._saveFreezeItemsInitialState(freezeItemsContainerID);
}
thisInstance._updateFreezeItemsOnScroll(
freezeItemsContainerID,
scrollableContainerScrollTop,
scrollableContainerScrollLeft
);
}
}
});
},
_saveFreezeItemsInitialState: function(freezeItemsContainerID) {
var $allFreezeItems = $("[data-sticky-direction][data-sticky-id='" + freezeItemsContainerID + "']");
var $freezeActions = $("[data-reporting-action][data-sticky-id='" + freezeItemsContainerID + "']");
var yAreaBounds;
var xAreaBounds;
this.itemsInitialState[freezeItemsContainerID] = {};
this.freezeBGColor[freezeItemsContainerID] = $("[data-id='" + freezeItemsContainerID + "']").attr("data-sticky-bg-color");
for (var index = 0; index < $allFreezeItems.length; index++) {
var $item = $($allFreezeItems[index]);
var scrollDirection = $item.attr("data-sticky-direction");
var itemID = $item.attr("data-id");
var itemPosition = $item.position();
var scaledItemPosition = { top: itemPosition.top / this.scaleFactor, left: itemPosition.left / this.scaleFactor };
var itemBounds = rectangle(scaledItemPosition.left, scaledItemPosition.top, $item.outerWidth(true) * this.scaleFactor, $item.outerHeight(true) * this.scaleFactor);
switch (scrollDirection) {
case "Vertical":
yAreaBounds = yAreaBounds ? yAreaBounds.union(itemBounds) : itemBounds;
break;
case "Horizontal":
xAreaBounds = xAreaBounds ? xAreaBounds.union(itemBounds) : itemBounds;
break;
}
this._saveFreezeItemInitialState(freezeItemsContainerID, $item, itemID, scaledItemPosition);
}
this.freezeMaxZIndex[freezeItemsContainerID] = $freezeActions.length ? $freezeActions.css("zIndex") : this.zIndex;
this.yFrozenAreasBounds[freezeItemsContainerID] = yAreaBounds;
this.xFrozenAreasBounds[freezeItemsContainerID] = xAreaBounds;
},
/**
* Save the freeze UI item initial position based on the wrapper element and current page
* - freezeItemsContainerID - string. A parent/wrapper element identifier
* - $item - JQuery DOM element of the freezed item
* - itemID - string. A ID of the freeze UI item
* - position - object. Contains the top and left values
*/
_saveFreezeItemInitialState: function(freezeItemsContainerID, $item, itemID, position) {
var itemBgColor = $item.css("background-color");
var hasInitialBgColor = this._hasSetBgColor(itemBgColor);
var itemState = {
top: position.top,
left: position.left,
zIndex: $item.css("zIndex"),
hasBgColor: hasInitialBgColor
};
this.itemsInitialState[freezeItemsContainerID][itemID] = itemState;
},
_updateFreezeItemsOnScroll: function(freezeItemsContainerID, scrollableContainerScrollTop, scrollableContainerScrollLeft) {
var $elementWrapper = $("div[data-id='" + freezeItemsContainerID + "']");
if (this._isInScrollVisibleArea($elementWrapper)) {
var $pageContainer = $elementWrapper.closest(".trv-report-page");
var pageContainerPosition = $pageContainer.position();
var pageContainerMargin = parseFloat($pageContainer.css("margin-top"));
var pageContainerTopOffset = parseFloat($pageContainer.css("padding-top"));
var pageContainerLeftOffset = parseFloat($pageContainer.css("padding-left"));
var pageContainerBorderTopWidth = parseFloat($pageContainer.css("border-top-width"));
var pageContainerBorderLeftWidth = parseFloat($pageContainer.css("border-left-width"));
var $rowHeaders = $("[data-sticky-direction*='Horizontal'][data-sticky-id='" + freezeItemsContainerID + "']");
var $colHeaders = $("[data-sticky-direction*='Vertical'][data-sticky-id='" + freezeItemsContainerID + "']");
var hasFixRow = $rowHeaders.length > 0;
var hasFixColumn = $colHeaders.length > 0;
var elementWrapperPosition = $elementWrapper.position();
var elementWrapperTopPosition = elementWrapperPosition.top + pageContainerPosition.top + pageContainerMargin + pageContainerTopOffset + pageContainerBorderTopWidth;
var elementWrapperLeftPosition = elementWrapperPosition.left + pageContainerLeftOffset + pageContainerBorderLeftWidth;
var verticalMoveOffset = scrollableContainerScrollTop - elementWrapperTopPosition;
var horizontalMoveOffset = scrollableContainerScrollLeft - elementWrapperLeftPosition;
if (hasFixColumn && verticalMoveOffset > 0) {
if (scrollableContainerScrollTop <= $elementWrapper.outerHeight() * this.scaleFactor + elementWrapperTopPosition - this.yFrozenAreasBounds[freezeItemsContainerID].height) {
this.currentlyFrozenContainer.vertical[freezeItemsContainerID] = true;
this._updateUIElementsPosition($colHeaders, "top", verticalMoveOffset / this.scaleFactor, freezeItemsContainerID);
}
} else {
if (this.currentlyFrozenContainer.vertical[freezeItemsContainerID]) {
delete this.currentlyFrozenContainer.vertical[freezeItemsContainerID];
this._updateUIElementsPosition($colHeaders, "top", -1, freezeItemsContainerID);
}
}
if (hasFixRow && horizontalMoveOffset > 0) {
if (scrollableContainerScrollLeft <= $elementWrapper.outerWidth() * this.scaleFactor + elementWrapperLeftPosition - this.xFrozenAreasBounds[freezeItemsContainerID].width) {
this.currentlyFrozenContainer.horizontal[freezeItemsContainerID] = true;
this._updateUIElementsPosition($rowHeaders, "left", horizontalMoveOffset / this.scaleFactor, freezeItemsContainerID);
}
} else {
if (this.currentlyFrozenContainer.horizontal[freezeItemsContainerID]) {
delete this.currentlyFrozenContainer.horizontal[freezeItemsContainerID];
this._updateUIElementsPosition($rowHeaders, "left", -1, freezeItemsContainerID);
}
}
} else {
if (this.currentlyFrozenContainer.horizontal[freezeItemsContainerID] || this.currentlyFrozenContainer.vertical[freezeItemsContainerID]) {
this._resetToDefaultPosition(freezeItemsContainerID);
}
}
},
/**
* Move all freeze items to their initial position
* Takes one parameters
* - freezeItemsContainerID - string. A parent/wrapper element identifier
*/
_resetToDefaultPosition: function(freezeItemsContainerID) {
var $rowHeaders = $("[data-sticky-direction*='Horizontal'][data-sticky-id='" + freezeItemsContainerID + "']");
var $colHeaders = $("[data-sticky-direction*='Vertical'][data-sticky-id='" + freezeItemsContainerID + "']");
this._updateUIElementsPosition($colHeaders, "top", -1, freezeItemsContainerID);
this._updateUIElementsPosition($rowHeaders, "left", -1, freezeItemsContainerID);
delete this.currentlyFrozenContainer.horizontal[freezeItemsContainerID];
delete this.currentlyFrozenContainer.vertical[freezeItemsContainerID];
},
/**
* Update the freeze elements position
* Takes four parameters
* - targetElements -Array. Collection of DOM element, that has a freeze attribute
* - position - string. Indicates which position property to be updated - top or left
* - offset - integer. The value of the increase that should be applied.
* If it is negative number, should not move the items and it should set their initial position
* - freezeItemsContainerID - string. A parent/wrapper element identifier
*/
_updateUIElementsPosition: function(targetElements, position, offset, freezeItemsContainerID) {
for (var index = 0; index < targetElements.length; index++) {
var $item = $(targetElements[index]);
var itemFreezeDirection = $item.attr("data-sticky-direction");
var isFrozenBothDirection = itemFreezeDirection.indexOf(",") > 0;
var itemID = $item.attr("data-id");
var itemInitialState = this.itemsInitialState[freezeItemsContainerID][itemID];
var itemNewPostion = itemInitialState[position];
var initialZIndex = itemInitialState["zIndex"];
var hasInitialBgColor = itemInitialState["hasBgColor"];
var zIndexValue = 1;
var maxZIndex = this.freezeMaxZIndex[freezeItemsContainerID] ? this.freezeMaxZIndex[freezeItemsContainerID] : zIndexValue;
if (isFrozenBothDirection) {
zIndexValue = initialZIndex !== "auto" ? initialZIndex : maxZIndex + 2;
} else {
zIndexValue = initialZIndex !== "auto" ? initialZIndex + 1 : maxZIndex;
}
var newStyleRules = {
"z-index": zIndexValue
};
if (offset >= 0) {
itemNewPostion = itemNewPostion + offset;
} else {
newStyleRules["z-index"] = initialZIndex;
}
if (!hasInitialBgColor) {
this._applyBgColorOnScroll($item, isFrozenBothDirection, hasInitialBgColor, offset >= 0, freezeItemsContainerID);
}
newStyleRules[position] = itemNewPostion + "px";
$item.css(newStyleRules);
}
},
// eslint-disable-next-line max-params
_applyBgColorOnScroll: function($item, isItemFrozenBothDirection, hasInitialBgColor, shouldApplyBGColor, freezeItemsContainerID) {
if ($item.is("img")) {
return true;
}
if (isItemFrozenBothDirection && this._isFrozen(freezeItemsContainerID) && !hasInitialBgColor) {
$item.css("background-color", this.freezeBGColor[freezeItemsContainerID]);
return true;
}
if (shouldApplyBGColor) {
$item.css("background-color", this.freezeBGColor[freezeItemsContainerID]);
} else {
$item.css("background-color", "initial");
}
},
_hasSetBgColor: function(bgColorValue) {
return getColorAlphaValue(bgColorValue) > 0;
},
_isFrozen: function(freezeItemsContainerID) {
return this.currentlyFrozenContainer.horizontal[freezeItemsContainerID] || this.currentlyFrozenContainer.vertical[freezeItemsContainerID];
},
/**
* Checks if an UI element is in the visible part of the scrollable container
* Takes one parameters
* - $element - JQuery DOM element
*/
_isInScrollVisibleArea: function($element) {
var $page = $element.closest(".trv-report-page");
var elementPosition = $element.position();
return this._isVisibleVertically($element, $page, elementPosition) && this._isVisibleHorizontally($element, $page, elementPosition);
},
_isVisibleHorizontally: function($element, $page, elementPosition) {
var pageLeftOffset = parseFloat($page.css("padding-left"));
var scrollableContainerLeftScrollPosition = this.$scrollableContainer.scrollLeft();
var scrollableContainerWidth = this.$scrollableContainer.width();
var elementWidth = $element.outerWidth(true) * this.scaleFactor;
var elementLeftOffset = elementPosition.left + pageLeftOffset;
return elementLeftOffset > scrollableContainerLeftScrollPosition - elementWidth && elementLeftOffset < scrollableContainerLeftScrollPosition + elementWidth + scrollableContainerWidth;
},
_isVisibleVertically: function($element, $page, elementPosition) {
var pageTopOffset = parseFloat($page.css("padding-top"));
var pagePosition = $page.position();
var scrollableContainerTopScrollPosition = this.$scrollableContainer.scrollTop();
var scrollableContainerHeight = this.$scrollableContainer.height();
var elementHeight = $element.outerHeight(true) * this.scaleFactor;
var elementTopOffset = elementPosition.top + pageTopOffset + pagePosition.top;
return elementTopOffset > scrollableContainerTopScrollPosition - elementHeight && elementTopOffset < scrollableContainerTopScrollPosition + elementHeight + scrollableContainerHeight;
}
};
export { UIFreezeCoordinator };