UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

191 lines 10.7 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; }; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = __importDefault(require("react")); var prop_types_1 = __importDefault(require("react-peek/prop-types")); var lodash_1 = __importDefault(require("lodash")); var style_helpers_1 = require("../../util/style-helpers"); var component_types_1 = require("../../util/component-types"); var dom_helpers_1 = require("../../util/dom-helpers"); var cx = style_helpers_1.lucidClassNames.bind('&-StickySection'); var node = prop_types_1.default.node, number = prop_types_1.default.number, object = prop_types_1.default.object, string = prop_types_1.default.string; var StickySection = /** @class */ (function (_super) { __extends(StickySection, _super); function StickySection() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.scrollContainer = react_1.default.createRef(); _this.stickySection = react_1.default.createRef(); _this.stickyFrame = react_1.default.createRef(); _this.state = { isAboveFold: false, containerRect: { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0, frameLeft: 0, scrollWidth: 0, }, }; _this.handleScroll = function () { var _a = _this.props, lowerBound = _a.lowerBound, _b = _a.topOffset, topOffset = _b === void 0 ? 0 : _b; var _c = _this.state, isAboveFold = _c.isAboveFold, containerRect = _c.containerRect; var nextContainerRect = _this.getContainerRect(); if (window.pageYOffset + topOffset >= nextContainerRect.top) { if (!isAboveFold) { _this.setState({ isAboveFold: true, }); } } else { if (isAboveFold) { _this.setState({ isAboveFold: false, }); } } if (lodash_1.default.isNumber(lowerBound) && window.pageYOffset >= lowerBound) { _this.setState({ isAboveFold: false, }); } if (containerRect.bottom !== nextContainerRect.bottom || containerRect.height !== nextContainerRect.height || containerRect.left !== nextContainerRect.left || containerRect.right !== nextContainerRect.right || containerRect.top !== nextContainerRect.top || containerRect.width !== nextContainerRect.width || containerRect.scrollWidth !== nextContainerRect.scrollWidth || containerRect.frameLeft !== nextContainerRect.frameLeft) { _this.setState({ containerRect: nextContainerRect, }); } }; _this.getContainerRect = function () { var containerRect = dom_helpers_1.getAbsoluteBoundingClientRect(_this.scrollContainer.current); var stickyRect = _this.stickySection .current.getBoundingClientRect(); var frameRect = _this.stickyFrame .current.getBoundingClientRect(); return { bottom: containerRect.top + stickyRect.height, height: stickyRect.height, left: containerRect.left, right: containerRect.left + stickyRect.width, top: containerRect.top, scrollWidth: _this.stickySection.current.scrollWidth, width: containerRect.width, frameLeft: frameRect.left, }; }; return _this; } StickySection.prototype.componentDidMount = function () { var _this = this; setTimeout(function () { _this.setState({ containerRect: _this.getContainerRect(), }); _this.handleScroll(); }, 1); window.addEventListener('scroll', this.handleScroll, true); }; StickySection.prototype.componentWillUnmount = function () { window.removeEventListener('scroll', this.handleScroll, true); }; StickySection.prototype.render = function () { var _a = this.props, children = _a.children, className = _a.className, style = _a.style, _b = _a.topOffset, topOffset = _b === void 0 ? 0 : _b, viewportWidth = _a.viewportWidth, passThroughs = __rest(_a, ["children", "className", "style", "topOffset", "viewportWidth"]); var _c = this.state, isAboveFold = _c.isAboveFold, containerRect = _c.containerRect; return (react_1.default.createElement("div", __assign({}, component_types_1.omitProps(passThroughs, undefined, Object.keys(StickySection.propTypes)), { className: cx('&', className), style: __assign(__assign({}, (isAboveFold ? { height: containerRect.height, visibility: 'hidden', } : {})), style), ref: this.scrollContainer }), react_1.default.createElement("div", { className: cx('&-sticky-frame'), ref: this.stickyFrame, style: __assign(__assign({}, (isAboveFold ? { visibility: 'visible', position: 'fixed', top: topOffset, width: lodash_1.default.isNumber(viewportWidth) ? viewportWidth : containerRect.width, height: containerRect.height, overflow: 'hidden', } : {})), style) }, react_1.default.createElement("div", { className: cx('&-sticky-section'), ref: this.stickySection, style: __assign(__assign({}, (isAboveFold ? { position: 'absolute', top: 0, left: containerRect.left - containerRect.frameLeft || 0, width: containerRect.scrollWidth, height: containerRect.height, } : { position: 'relative', })), style) }, children)))); }; StickySection.displayName = 'StickySection'; StickySection.peek = { description: "\n\t\t\t`StickySection` can be wrapped around any content to make it _stick_\n\t\t\tto the top edge of the screen when a user scrolls beyond its initial\n\t\t\tlocation.\n\t\t", categories: ['helpers'], }; StickySection.propTypes = { children: node(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n\t\t\tany valid React children\n\t\t"], ["\n\t\t\tany valid React children\n\t\t"]))), className: string(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n\t\t\tAppended to the component-specific class names set on the root element.\n\t\t"], ["\n\t\t\tAppended to the component-specific class names set on the root element.\n\t\t"]))), style: object(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n\t\t\tStyles that are passed through to the root container.\n\t\t"], ["\n\t\t\tStyles that are passed through to the root container.\n\t\t"]))), lowerBound: number(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n\t\t\tPixel value from the top of the document. When scrolled passed, the\n\t\t\tsticky header is no longer sticky, and renders normally.\n\t\t"], ["\n\t\t\tPixel value from the top of the document. When scrolled passed, the\n\t\t\tsticky header is no longer sticky, and renders normally.\n\t\t"]))), viewportWidth: number(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n\t\t\tWidth of section when it sticks to the top edge of the screen. When\n\t\t\tomitted, it defaults to the last width of the section.\n\t\t"], ["\n\t\t\tWidth of section when it sticks to the top edge of the screen. When\n\t\t\tomitted, it defaults to the last width of the section.\n\t\t"]))), topOffset: number(templateObject_6 || (templateObject_6 = __makeTemplateObject(["\n\t\t\tTop offset threshold before sticking to the top. The sticky content will\n\t\t\tdisplay with this offset.\n\t\t"], ["\n\t\t\tTop offset threshold before sticking to the top. The sticky content will\n\t\t\tdisplay with this offset.\n\t\t"]))), }; return StickySection; }(react_1.default.Component)); exports.default = StickySection; var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6; //# sourceMappingURL=StickySection.js.map