wix-style-react
Version:
wix-style-react
502 lines (434 loc) • 19.6 kB
JavaScript
'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;