UNPKG

@shopify/polaris

Version:

Shopify’s product component library

213 lines (179 loc) 5.55 kB
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 };