UNPKG

wix-style-react

Version:
502 lines (434 loc) • 19.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var _class, _temp; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _cssElementQueries = require('css-element-queries'); var _Page = require('./Page.scss'); var _Page2 = _interopRequireDefault(_Page); var _WixComponent2 = require('../BaseComponents/WixComponent'); var _WixComponent3 = _interopRequireDefault(_WixComponent2); var _PageHeader = require('../PageHeader'); var _PageHeader2 = _interopRequireDefault(_PageHeader); var _Content = require('./Content'); var _Content2 = _interopRequireDefault(_Content); var _Tail = require('./Tail'); var _Tail2 = _interopRequireDefault(_Tail); var _constants = require('./constants'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * A page container which contains a header and scrollable content * * Page structure is as follows: * @example * +-- FixedContainer -------- * | +-- HeaderContainer -------- * | | header-content:padding-top * | | +-- Page.Header -------- * | | | * | | | * | | +----------------------- * | | tail:padding-top * | | +-- Page.Tail ---------- * | | | * | | | * | | +----------------------- * | | header-content:padding-bottom * | +------------------------- * | +-- Page.FixedContent ---- ==+ * | | | * | +------------------------- | * +--------------------------- | Content (Virtual) * +-- ScrollableContainer --- | * | +-- Page.Content ---------- | | * | | | * | +-------------------------- | * +--------------------------- ==+ * * - ScrollableContainer is called in the code scrollable-content, and should NOT be renamed, since * Tooltip is hard-coded-ly using a selector like this: [data-class="page-scrollable-content"] */ var Page = (_temp = _class = function (_WixComponent) { _inherits(Page, _WixComponent); function Page(props) { _classCallCheck(this, Page); var _this = _possibleConstructorReturn(this, (Page.__proto__ || Object.getPrototypeOf(Page)).call(this, props)); _this._setContainerScrollTopThreshold(false); _this._handleScroll = _this._handleScroll.bind(_this); _this._handleResize = _this._handleResize.bind(_this); _this.state = { fixedContainerHeight: 0, tailHeight: 0, fixedContentHeight: 0, scrollBarWidth: 0, minimized: false }; return _this; } _createClass(Page, [{ key: 'componentDidMount', value: function componentDidMount() { _get(Page.prototype.__proto__ || Object.getPrototypeOf(Page.prototype), 'componentDidMount', this).call(this); this.contentResizeListener = new _cssElementQueries.ResizeSensor(this._getScrollContainer().childNodes[0], this._handleResize); this._calculateComponentsHeights(); this._handleResize(); } }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps) { _get(Page.prototype.__proto__ || Object.getPrototypeOf(Page.prototype), 'componentDidUpdate', this).call(this, prevProps); // Do not trigger height calculation if the component is minimized if (!this.state.minimized) { this._calculateComponentsHeights(); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { _get(Page.prototype.__proto__ || Object.getPrototypeOf(Page.prototype), 'componentWillUnmount', this).call(this); this.contentResizeListener.detach(this._handleResize); } }, { key: '_calculateComponentsHeights', value: function _calculateComponentsHeights() { var _state = this.state, fixedContainerHeight = _state.fixedContainerHeight, tailHeight = _state.tailHeight, fixedContentHeight = _state.fixedContentHeight; var newFixedContainerHeight = this.fixedContainerRef ? this.fixedContainerRef.offsetHeight : 0; var newTailHeight = this.pageHeaderTailRef ? this.pageHeaderTailRef.offsetHeight : 0; var newFixedContentHeight = this.pageHeaderFixedContentRef ? this.pageHeaderFixedContentRef.offsetHeight : 0; if (fixedContainerHeight !== newFixedContainerHeight || tailHeight !== newTailHeight || fixedContentHeight !== newFixedContentHeight) { this.setState({ fixedContainerHeight: newFixedContainerHeight, tailHeight: newTailHeight, fixedContentHeight: newFixedContentHeight }); } } }, { key: '_setContainerScrollTopThreshold', value: function _setContainerScrollTopThreshold(_ref) { var shortThreshold = _ref.shortThreshold; this.containerScrollTopThreshold = shortThreshold ? _constants.SHORT_SCROLL_TOP_THRESHOLD : _constants.SCROLL_TOP_THRESHOLD; } }, { key: '_setScrollContainer', value: function _setScrollContainer(scrollContainer) { this.scrollableContentRef = scrollContainer; this.props.scrollableContentRef && this.props.scrollableContentRef(scrollContainer); } }, { key: '_getScrollContainer', value: function _getScrollContainer() { return this.scrollableContentRef; } }, { key: '_shouldBeMinimized', value: function _shouldBeMinimized(containerScrollTop) { return containerScrollTop > this.containerScrollTopThreshold; } }, { key: '_handleScroll', value: function _handleScroll() { var scrollContainer = this._getScrollContainer(); var containerScrollTop = scrollContainer.scrollTop; var nextMinimized = this._shouldBeMinimized(containerScrollTop); var minimized = this.state.minimized; if (minimized !== nextMinimized) { this.setState({ minimized: nextMinimized }); } } }, { key: '_handleResize', value: function _handleResize() { // Fixes width issues when scroll bar is present in windows var scrollContainer = this._getScrollContainer(); var scrollBarWidth = scrollContainer && scrollContainer.offsetWidth - scrollContainer.clientWidth; if (this.state.scrollBarWidth !== scrollBarWidth) { this.setState({ scrollBarWidth: scrollBarWidth }); } } }, { key: '_safeGetChildren', value: function _safeGetChildren(element) { if (!element || !element.props || !element.props.children) { return []; } return element.props.children; } }, { key: '_calculatePageDimensionsStyle', value: function _calculatePageDimensionsStyle() { var _props = this.props, maxWidth = _props.maxWidth, sidePadding = _props.sidePadding; if (!maxWidth && !sidePadding && sidePadding !== 0) { return null; } var styles = {}; if (maxWidth) { styles.maxWidth = maxWidth + 'px'; } if (sidePadding || sidePadding === 0) { styles.paddingLeft = sidePadding + 'px'; styles.paddingRight = sidePadding + 'px'; } return styles; } }, { key: '_fixedContainerStyle', value: function _fixedContainerStyle() { var scrollBarWidth = this.state.scrollBarWidth; if (scrollBarWidth) { return { width: 'calc(100% - ' + scrollBarWidth + 'px' }; } return null; } /** * See diagram in class documentation to better understand this method. */ }, { key: '_calculateHeaderMeasurements', value: function _calculateHeaderMeasurements(_ref2) { var PageTail = _ref2.PageTail; var gradientCoverTail = this.props.gradientCoverTail; // fixedContainerHeight (and other heights) are calculated only when the Page is NOT minimized var _state2 = this.state, fixedContainerHeight = _state2.fixedContainerHeight, tailHeight = _state2.tailHeight, fixedContentHeight = _state2.fixedContentHeight; var minimizedFixedContainerHeight = PageTail ? fixedContainerHeight - 78 : fixedContainerHeight - (78 - _constants.TAIL_TOP_PADDING_PX); var headerContainerHeight = fixedContainerHeight - fixedContentHeight; var imageHeight = headerContainerHeight + (PageTail ? -tailHeight : 39) + 'px'; var gradientHeight = gradientCoverTail ? headerContainerHeight + (PageTail ? -_constants.SCROLL_TOP_THRESHOLD : 39) + 'px' : imageHeight; return { imageHeight: imageHeight, gradientHeight: gradientHeight, fixedContainerHeight: fixedContainerHeight, minimizedFixedContainerHeight: minimizedFixedContainerHeight }; } }, { key: 'render', value: function render() { var _this2 = this, _classNames2; var _props2 = this.props, backgroundImageUrl = _props2.backgroundImageUrl, gradientClassName = _props2.gradientClassName, className = _props2.className, children = _props2.children; var minimized = this.state.minimized; var hasBackgroundImage = !!backgroundImageUrl; var hasGradientClassName = !!gradientClassName && !backgroundImageUrl; var childrenObject = getChildrenObject(children); var PageContent = childrenObject.PageContent, PageFixedContent = childrenObject.PageFixedContent, PageTail = childrenObject.PageTail; this._setContainerScrollTopThreshold({ shortThreshold: PageTail && hasGradientClassName }); var contentFullScreen = PageContent && PageContent.props.fullScreen; var pageDimensionsStyle = this._calculatePageDimensionsStyle(); var _calculateHeaderMeasu = this._calculateHeaderMeasurements({ PageTail: PageTail }), imageHeight = _calculateHeaderMeasu.imageHeight, gradientHeight = _calculateHeaderMeasu.gradientHeight, fixedContainerHeight = _calculateHeaderMeasu.fixedContainerHeight, minimizedFixedContainerHeight = _calculateHeaderMeasu.minimizedFixedContainerHeight; var contentLayoutProps = { className: (0, _classnames2.default)(_Page2.default.content, _defineProperty({}, _Page2.default.contentFullScreen, contentFullScreen)), style: contentFullScreen ? null : pageDimensionsStyle }; return _react2.default.createElement( 'div', { className: (0, _classnames2.default)(_Page2.default.page, className) }, _react2.default.createElement( 'div', { 'data-hook': 'page-fixed-container', style: this._fixedContainerStyle(), className: (0, _classnames2.default)(_Page2.default.fixedContainer), ref: function ref(r) { return _this2.fixedContainerRef = r; }, onWheel: function onWheel(event) { _this2._getScrollContainer().scrollTop = _this2._getScrollContainer().scrollTop + event.deltaY; } }, _react2.default.createElement( 'div', { className: (0, _classnames2.default)(_Page2.default.pageHeaderContainer, (_classNames2 = {}, _defineProperty(_classNames2, _Page2.default.minimized, minimized), _defineProperty(_classNames2, _Page2.default.withoutBottomPadding, PageTail && minimized), _classNames2)) }, childrenObject.PageHeader && _react2.default.createElement( 'div', { className: _Page2.default.pageHeader, style: pageDimensionsStyle }, _react2.default.cloneElement(childrenObject.PageHeader, { minimized: minimized, hasBackgroundImage: hasBackgroundImage }) ), PageTail && _react2.default.createElement( 'div', { 'data-hook': 'page-tail', className: (0, _classnames2.default)(_Page2.default.tail, _defineProperty({}, _Page2.default.minimized, minimized)), style: pageDimensionsStyle, ref: function ref(r) { return _this2.pageHeaderTailRef = r; } }, _react2.default.cloneElement(PageTail, { minimized: minimized }) ) ), PageFixedContent && _react2.default.createElement( 'div', _extends({ 'data-hook': 'page-fixed-content' }, contentLayoutProps, { ref: function ref(r) { return _this2.pageHeaderFixedContentRef = r; } }), _react2.default.cloneElement(PageFixedContent) ) ), _react2.default.createElement( 'div', { className: _Page2.default.scrollableContent, onScroll: this._handleScroll, 'data-hook': 'page-scrollable-content', 'data-class': 'page-scrollable-content', style: { paddingTop: fixedContainerHeight + 'px' }, ref: function ref(r) { return _this2._setScrollContainer(r); } }, hasBackgroundImage && _react2.default.createElement( 'div', { className: _Page2.default.imageBackgroundContainer, style: { height: imageHeight }, 'data-hook': 'page-background-image' }, _react2.default.createElement('div', { className: _Page2.default.imageBackground, style: { backgroundImage: 'url(' + backgroundImageUrl + ')' } }) ), hasGradientClassName && !hasBackgroundImage && _react2.default.createElement('div', { 'data-hook': 'page-gradient-class-name', className: _Page2.default.gradientBackground + ' ' + gradientClassName, style: { height: gradientHeight } }), _react2.default.createElement( 'div', { className: _Page2.default.contentContainer }, _react2.default.createElement( 'div', contentLayoutProps, this._safeGetChildren(PageContent) ), minimized ? _react2.default.createElement('div', { style: { height: fixedContainerHeight - minimizedFixedContainerHeight + 'px' } }) : null ) ) ); } }]); return Page; }(_WixComponent3.default), _class.defaultProps = { gradientCoverTail: true }, _temp); var FixedContent = function FixedContent(props) { return props.children; }; FixedContent.displayName = 'Page.FixedContent'; FixedContent.propTypes = { children: _propTypes2.default.element.isRequired }; Page.displayName = 'Page'; Page.Header = _PageHeader2.default; Page.Content = _Content2.default; Page.FixedContent = FixedContent; Page.Tail = _Tail2.default; Page.propTypes = { /** Background image url of the header beackground */ backgroundImageUrl: _propTypes2.default.string, /** Sets the max width of the header and the content */ maxWidth: _propTypes2.default.number, /** Sets padding of the sides of the page */ sidePadding: _propTypes2.default.number, /** A css class to be applied to the component's root element */ className: _propTypes2.default.string, /** Header background color class name, allows to add a gradient to the header */ gradientClassName: _propTypes2.default.string, /** If false Gradient will not cover Page.Tail */ gradientCoverTail: _propTypes2.default.bool, /** Is called with the Page's scrollable content ref **/ scrollableContentRef: _propTypes2.default.func, children: _propTypes2.default.arrayOf(function (children, key) { var childrenObj = getChildrenObject(children); if (!childrenObj.PageHeader) { return new Error('Page: Invalid Prop children, must contain Page.Header'); } if (!childrenObj.PageContent) { return new Error('Page: Invalid Prop children, must contain Page.Content'); } if (children[key].type.displayName !== Page.Header.displayName && children[key].type.displayName !== Page.Content.displayName && children[key].type.displayName !== Page.FixedContent.displayName && children[key].type.displayName !== Page.Tail.displayName) { return new Error('Page: Invalid Prop children, unknown child ' + children[key].type); } }).isRequired }; function getChildrenObject(children) { return _react2.default.Children.toArray(children).reduce(function (acc, child) { switch (child.type.displayName) { case 'Page.Header': { acc.PageHeader = child; break; } case 'Page.Content': { acc.PageContent = child; break; } case 'Page.FixedContent': { acc.PageFixedContent = child; break; } case 'Page.Tail': { acc.PageTail = child; break; } default: { break; } } return acc; }, {}); } exports.default = Page;