@shopify/polaris
Version:
Shopify’s product component library
213 lines (179 loc) • 5.55 kB
JavaScript
import { spacingLoose } from '@shopify/polaris-tokens';
import debounce$1 from 'lodash/debounce';
import { stackedContent } from '../breakpoints.js';
import { scrollable, dataPolarisTopBar } from '../../components/shared.js';
import { getRectForNode } from '../geometry.js';
class StickyManager {
constructor(container) {
this.stickyItems = [];
this.stuckItems = [];
this.container = null;
this.topBarOffset = 0;
this.handleResize = debounce$1(() => {
this.manageStickyItems();
}, 40, {
leading: true,
trailing: true,
maxWait: 40
});
this.handleScroll = debounce$1(() => {
this.manageStickyItems();
}, 40, {
leading: true,
trailing: true,
maxWait: 40
});
if (container) {
this.setContainer(container);
}
}
registerStickyItem(stickyItem) {
this.stickyItems.push(stickyItem);
}
unregisterStickyItem(nodeToRemove) {
var nodeIndex = this.stickyItems.findIndex(({
stickyNode
}) => nodeToRemove === stickyNode);
this.stickyItems.splice(nodeIndex, 1);
}
setContainer(el) {
this.container = el;
if (isDocument(el)) {
this.setTopBarOffset(el);
}
this.container.addEventListener('scroll', this.handleScroll);
window.addEventListener('resize', this.handleResize);
this.manageStickyItems();
}
removeScrollListener() {
if (this.container) {
this.container.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('resize', this.handleResize);
}
}
manageStickyItems() {
if (this.stickyItems.length <= 0) {
return;
}
var scrollTop = this.container ? scrollTopFor(this.container) : 0;
var containerTop = getRectForNode(this.container).top + this.topBarOffset;
this.stickyItems.forEach(stickyItem => {
var {
handlePositioning
} = stickyItem;
var {
sticky,
top,
left,
width
} = this.evaluateStickyItem(stickyItem, scrollTop, containerTop);
this.updateStuckItems(stickyItem, sticky);
handlePositioning(sticky, top, left, width);
});
}
evaluateStickyItem(stickyItem, scrollTop, containerTop) {
var {
stickyNode,
placeHolderNode,
boundingElement,
offset,
disableWhenStacked
} = stickyItem;
if (disableWhenStacked && stackedContent().matches) {
return {
sticky: false,
top: 0,
left: 0,
width: 'auto'
};
}
var stickyOffset = offset ? this.getOffset(stickyNode) + parseInt(spacingLoose, 10) : this.getOffset(stickyNode);
var scrollPosition = scrollTop + stickyOffset;
var placeHolderNodeCurrentTop = placeHolderNode.getBoundingClientRect().top - containerTop + scrollTop;
var top = containerTop + stickyOffset;
var width = placeHolderNode.getBoundingClientRect().width;
var left = placeHolderNode.getBoundingClientRect().left;
var sticky;
if (boundingElement == null) {
sticky = scrollPosition >= placeHolderNodeCurrentTop;
} else {
var stickyItemHeight = stickyNode.getBoundingClientRect().height;
var stickyItemBottomPosition = boundingElement.getBoundingClientRect().bottom - stickyItemHeight + scrollTop - containerTop;
sticky = scrollPosition >= placeHolderNodeCurrentTop && scrollPosition < stickyItemBottomPosition;
}
return {
sticky,
top,
left,
width
};
}
updateStuckItems(item, sticky) {
var {
stickyNode
} = item;
if (sticky && !this.isNodeStuck(stickyNode)) {
this.addStuckItem(item);
} else if (!sticky && this.isNodeStuck(stickyNode)) {
this.removeStuckItem(item);
}
}
addStuckItem(stickyItem) {
this.stuckItems.push(stickyItem);
}
removeStuckItem(stickyItem) {
var {
stickyNode: nodeToRemove
} = stickyItem;
var nodeIndex = this.stuckItems.findIndex(({
stickyNode
}) => nodeToRemove === stickyNode);
this.stuckItems.splice(nodeIndex, 1);
}
getOffset(node) {
if (this.stuckItems.length === 0) {
return 0;
}
var offset = 0;
var count = 0;
var stuckNodesLength = this.stuckItems.length;
var nodeRect = getRectForNode(node);
while (count < stuckNodesLength) {
var stuckNode = this.stuckItems[count].stickyNode;
if (stuckNode !== node) {
var stuckNodeRect = getRectForNode(stuckNode);
if (!horizontallyOverlaps(nodeRect, stuckNodeRect)) {
offset += getRectForNode(stuckNode).height;
}
} else {
break;
}
count++;
}
return offset;
}
isNodeStuck(node) {
var nodeFound = this.stuckItems.findIndex(({
stickyNode
}) => node === stickyNode);
return nodeFound >= 0;
}
setTopBarOffset(container) {
var topbarElement = container.querySelector(":not(".concat(scrollable.selector, ") ").concat(dataPolarisTopBar.selector));
this.topBarOffset = topbarElement ? topbarElement.clientHeight : 0;
}
}
function isDocument(node) {
return node === document;
}
function scrollTopFor(container) {
return isDocument(container) ? document.body.scrollTop || document.documentElement.scrollTop : container.scrollTop;
}
function horizontallyOverlaps(rect1, rect2) {
var rect1Left = rect1.left;
var rect1Right = rect1.left + rect1.width;
var rect2Left = rect2.left;
var rect2Right = rect2.left + rect2.width;
return rect2Right < rect1Left || rect1Right < rect2Left;
}
export { StickyManager };