UNPKG

wix-style-react

Version:
690 lines (591 loc) • 27.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireDefault(require("react")); var _context = require("../WixStyleReactProvider/context"); var _propTypes = _interopRequireDefault(require("prop-types")); var _cssElementQueries = require("css-element-queries"); var _PageSt = require("./Page.st.css"); var _PageContext = require("./PageContext"); var _PageHeader = _interopRequireDefault(require("../PageHeader")); var _PageSection = _interopRequireDefault(require("../PageSection")); var _Content = _interopRequireDefault(require("./Content")); var _Tail = _interopRequireDefault(require("./Tail")); var _PageSticky = require("./PageSticky"); var _FixedFooter = _interopRequireDefault(require("./FixedFooter")); var _ScrollableContainer = _interopRequireDefault(require("../common/ScrollableContainer")); var _ScrollableContainerCommon = require("../common/PropTypes/ScrollableContainerCommon"); var _excluded = ["className", "horizontalScroll", "style"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /* * Page structure without mini-header-overlay: * * + PageWrapper (Horizontal Scroll) -- * | +- Page -------------------------- * | | +-- ScrollableContainer (Vertical Scroll) * | | | +-- MinimizationPlaceholder * | | | | * | | | +----------------------------- * | | | +-- HeaderContainer ------ (position: fixed - when minimized) * | | | | +-- Page.Header ------------ * | | | | | * | | | | +--------------------------- * | | | | +-- Page.Tail -------------- * | | | | | * | | | | +--------------------------- * | | | +----------------------------- * | | | +-- ContentWrapper------------ * | | | | +-- Page.FixedContent (Deprecated) * | | | | | * | | | | +--------------------------- * | | | | +-- Page.Content ----------- * | | | | | +-- Page.Section --------- * | | | | | * | | | | +--------------------------- * | | | +----------------------------- * | | +------------------------------- * | +--------------------------------- (Page - End) * +----------------------------------- (PageWrapper - End) * * - ScrollableContainer has a data-classnamed '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 = /*#__PURE__*/function (_React$PureComponent) { (0, _inherits2["default"])(Page, _React$PureComponent); var _super = _createSuper(Page); function Page(props) { var _this; (0, _classCallCheck2["default"])(this, Page); _this = _super.call(this, props); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_renderFixedFooter", function () { var children = _this.props.children; var childrenObject = getChildrenObject(children); var FixedFooterChild = childrenObject.FixedFooter, PageContent = childrenObject.PageContent; var contentFullScreen = PageContent && PageContent.props.fullScreen; var pageDimensionsStyle = contentFullScreen ? null : _this._getPageDimensionsStyle(); if (FixedFooterChild) { return /*#__PURE__*/_react["default"].createElement("div", { className: _PageSt.classes.fixedFooter, ref: function ref(_ref) { _this.footerWrapperRef = _ref; }, style: pageDimensionsStyle }, /*#__PURE__*/_react["default"].cloneElement(FixedFooterChild, {})); } }); _this.scrollableContainerRef = /*#__PURE__*/_react["default"].createRef(); _this._handleScroll = _this._handleScroll.bind((0, _assertThisInitialized2["default"])(_this)); _this._handleWidthResize = _this._handleWidthResize.bind((0, _assertThisInitialized2["default"])(_this)); _this._handleWindowResize = _this._handleWindowResize.bind((0, _assertThisInitialized2["default"])(_this)); _this._calculateComponentsHeights = _this._calculateComponentsHeights.bind((0, _assertThisInitialized2["default"])(_this)); _this.state = { headerContainerHeight: 0, headerWrapperHeight: 0, tailHeight: 0, footerHeight: 0, minimized: false }; return _this; } (0, _createClass2["default"])(Page, [{ key: "componentDidMount", value: function componentDidMount() { this._calculateComponentsHeights(); this.contentResizeListener = new _cssElementQueries.ResizeSensor(this._getScrollContainer().childNodes[0], this._handleWidthResize); this._handleWidthResize(); window.addEventListener('resize', this._handleWindowResize); // TODO: Hack to fix cases where initial measurement of headerWrapperHeight is not correct (need to investigate) // Happens in PageTestStories -> PageWithScroll -> 5. Scroll - Trigger Mini Header // Maybe there is a transition var ARBITRARY_SHORT_DURATION_MS = 100; setTimeout(this._calculateComponentsHeights, ARBITRARY_SHORT_DURATION_MS); // This is done for backward compatibility only, // Notifying current users that passed the `scrollableContentRef` prop about the ref current value. // New users should be encouraged to use the new event handlers onScrollChanged/onScrollAreaChanged // according to their use case. this.props.scrollableContentRef && this.props.scrollableContentRef(this.scrollableContainerRef.current); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { this._calculateComponentsHeights(); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { window.removeEventListener('resize', this._handleWindowResize); this.contentResizeListener.detach(this._handleResize); } }, { key: "_getNamedChildren", value: function _getNamedChildren() { return getChildrenObject(this.props.children); } }, { key: "_calculateComponentsHeights", value: function _calculateComponentsHeights() { var _this$state = this.state, headerContainerHeight = _this$state.headerContainerHeight, headerWrapperHeight = _this$state.headerWrapperHeight, tailHeight = _this$state.tailHeight, pageHeight = _this$state.pageHeight, footerHeight = _this$state.footerHeight, minimized = _this$state.minimized; var newHeaderWrapperHeight = this.headerWrapperRef && !minimized ? this.headerWrapperRef.getBoundingClientRect().height : headerWrapperHeight; var newHeaderContainerHeight = this.headerWrapperRef && !minimized ? this.headerContainerRef.getBoundingClientRect().height : headerContainerHeight; var newTailHeight = this.pageHeaderTailRef ? this.pageHeaderTailRef.offsetHeight : 0; var newPageHeight = this.pageRef ? this.pageRef.offsetHeight : 0; var newFooterHeight = this.footerWrapperRef ? this.footerWrapperRef.offsetHeight : 0; if (headerContainerHeight !== newHeaderContainerHeight || headerWrapperHeight !== newHeaderWrapperHeight || tailHeight !== newTailHeight || pageHeight !== newPageHeight || footerHeight !== newFooterHeight) { this.setState({ headerContainerHeight: newHeaderContainerHeight, headerWrapperHeight: newHeaderWrapperHeight, tailHeight: newTailHeight, pageHeight: newPageHeight, footerHeight: newFooterHeight }); } } }, { key: "_getScrollContainer", value: function _getScrollContainer() { return this.scrollableContainerRef.current; } }, { key: "_getMinimizedHeaderWrapperHeight", value: function _getMinimizedHeaderWrapperHeight() { if (!this._hasHeader()) { return 0; } return this._hasTail() ? parseInt(_PageSt.stVars.minimizedHeaderWrapperWithTailHeightPx, 10) : parseInt(_PageSt.stVars.minimizedHeaderWrapperHeightPx, 10); } }, { key: "_getMinimizationDiff", value: function _getMinimizationDiff() { var headerWrapperHeight = this.state.headerWrapperHeight; return headerWrapperHeight ? headerWrapperHeight - this._getMinimizedHeaderWrapperHeight() : null; } }, { key: "_handleScroll", value: function _handleScroll(e) { var containerScrollTop = this._getScrollContainer().scrollTop; var minimized = this.state.minimized; var minimizationDiff = this._getMinimizationDiff(); var nextDisplayMiniHeader = minimizationDiff && containerScrollTop >= minimizationDiff; if (minimized !== nextDisplayMiniHeader) { this.setState({ minimized: nextDisplayMiniHeader }); } var onScrollChanged = this.props.scrollProps.onScrollChanged; if (onScrollChanged) { onScrollChanged(e); } } }, { key: "_handleWidthResize", value: function _handleWidthResize() {} }, { key: "_handleWindowResize", value: function _handleWindowResize() { // TODO: Optimize : https://developer.mozilla.org/en-US/docs/Web/Events/resize // Taken from here: https://github.com/kunokdev/react-window-size-listener/blob/d64c077fba4d4e0ce060464078c5fc19620528e6/src/index.js#L66 var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; if (this.state.windowHeight !== windowHeight) { // We are not using windowHeight directly, since we need to measure the `<Page/>`'s height, // But we hold it in the state to avoid rendering when only window.width changes this.setState({ windowHeight: windowHeight }); } } }, { key: "_safeGetChildren", value: function _safeGetChildren(element) { if (!element || !element.props || !element.props.children) { return []; } return element.props.children; } }, { key: "_getPageDimensionsStyle", value: function _getPageDimensionsStyle() { var _this$props = this.props, maxWidth = _this$props.maxWidth, sidePadding = _this$props.sidePadding; // TODO: Simplify - maxWidth is always truthy (from defaultProp) if (!maxWidth && !sidePadding && sidePadding !== 0) { return null; } var styles = {}; if (maxWidth) { styles.maxWidth = "".concat(maxWidth, "px"); } if (sidePadding || sidePadding === 0) { styles.paddingLeft = "".concat(sidePadding, "px"); styles.paddingRight = "".concat(sidePadding, "px"); } return styles; } }, { key: "_hasBackgroundImage", value: function _hasBackgroundImage() { return !!this.props.backgroundImageUrl; } }, { key: "_hasGradientClassName", value: function _hasGradientClassName() { return !!this.props.gradientClassName && !this.props.backgroundImageUrl; } }, { key: "_renderContentHorizontalLayout", value: function _renderContentHorizontalLayout(props) { var _this$_getNamedChildr = this._getNamedChildren(), PageContent = _this$_getNamedChildr.PageContent; var contentFullScreen = PageContent && PageContent.props.fullScreen; var className = props.className, horizontalScroll = props.horizontalScroll, style = props.style, rest = (0, _objectWithoutProperties2["default"])(props, _excluded); var pageDimensionsStyle = contentFullScreen ? null : this._getPageDimensionsStyle(); return /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({ className: (0, _PageSt.st)(_PageSt.classes.contentHorizontalLayout, { contentFullWidth: contentFullScreen, horizontalScroll: horizontalScroll }, className), style: _objectSpread(_objectSpread({}, pageDimensionsStyle), style) }, rest), props.children); } }, { key: "_renderHeader", value: function _renderHeader() { var _this2 = this; var minimized = this.state.minimized; var _this$_getNamedChildr2 = this._getNamedChildren(), PageHeaderChild = _this$_getNamedChildr2.PageHeader; var dataHook = 'page-header-wrapper'; return PageHeaderChild && /*#__PURE__*/_react["default"].createElement(_context.WixStyleReactContext.Consumer, { key: dataHook }, function (_ref2) { var reducedSpacingAndImprovedLayout = _ref2.reducedSpacingAndImprovedLayout; return /*#__PURE__*/_react["default"].createElement("div", { "data-hook": dataHook, className: (0, _PageSt.st)(_PageSt.classes.headerWrapper, { reducedSpacingAndImprovedLayout: reducedSpacingAndImprovedLayout, minimized: minimized }), ref: function ref(_ref3) { return _this2.headerWrapperRef = _ref3; } }, /*#__PURE__*/_react["default"].cloneElement(PageHeaderChild, { minimized: minimized, hasBackgroundImage: _this2._hasBackgroundImage() })); }); } }, { key: "_renderHeaderContainer", value: function _renderHeaderContainer() { var _this3 = this; var minimized = this.state.minimized; // placeholder when header is minimized var placeholder = /*#__PURE__*/_react["default"].createElement("div", { key: 'placeholder', style: { height: "".concat(minimized ? this._getMinimizationDiff() : 0, "px") } }); /** * HeaderContainer has position sticky. The `top` value is negative, in order to let * the container scroll out of view before the minimization occurs. */ var top = minimized ? "-".concat(this._getMinimizationDiff(), "px") : 0; return /*#__PURE__*/_react["default"].createElement("div", { "data-hook": "page-header-container", className: (0, _PageSt.st)(_PageSt.classes.pageHeaderContainer, { minimized: minimized, hasTail: this._hasTail() }), style: (0, _defineProperty2["default"])({}, _PageSt.vars.minimizationTop, top), ref: function ref(_ref5) { return _this3.headerContainerRef = _ref5; } }, this._renderContentHorizontalLayout({ children: [placeholder, this._renderHeader(), this._renderTail()] })); } }, { key: "_renderScrollableContainer", value: function _renderScrollableContainer() { var onScrollAreaChanged = this.props.scrollProps.onScrollAreaChanged; return /*#__PURE__*/_react["default"].createElement(_ScrollableContainer["default"], { className: (0, _PageSt.st)(_PageSt.classes.scrollableContainer, { hasTail: this._hasTail() }), dataHook: "page-scrollable-content", "data-class": "page-scrollable-content", ref: this.scrollableContainerRef, onScrollAreaChanged: onScrollAreaChanged, onScrollChanged: this._handleScroll }, /*#__PURE__*/_react["default"].createElement("div", { "data-hook": "safari-12-13-sticky-fix" }, this._renderScrollableBackground(), this._renderHeaderContainer(), this._renderContentContainer(), this._renderFixedFooter())); } }, { key: "_hasTail", value: function _hasTail() { return !!this._getNamedChildren().PageTail; } }, { key: "_hasHeader", value: function _hasHeader() { return !!this._getNamedChildren().PageHeader; } }, { key: "_renderScrollableBackground", value: function _renderScrollableBackground() { var _this$state2 = this.state, headerContainerHeight = _this$state2.headerContainerHeight, tailHeight = _this$state2.tailHeight; var backgroundHeight = "".concat(headerContainerHeight - tailHeight + (this._hasTail() ? 0 : parseInt(_PageSt.stVars.backgroundCoverContentPx, 10)), "px"); if (this._hasBackgroundImage()) { return /*#__PURE__*/_react["default"].createElement("div", { className: _PageSt.classes.imageBackgroundContainer, style: { height: backgroundHeight }, "data-hook": "page-background-image" }, /*#__PURE__*/_react["default"].createElement("div", { className: _PageSt.classes.imageBackground, style: { backgroundImage: "url(".concat(this.props.backgroundImageUrl, ")") } })); } if (this._hasGradientClassName()) { return /*#__PURE__*/_react["default"].createElement("div", { "data-hook": "page-gradient-class-name", className: (0, _PageSt.st)(_PageSt.classes.gradientBackground, {}, this.props.gradientClassName), style: { height: backgroundHeight } }); } } }, { key: "_renderTail", value: function _renderTail() { var _this4 = this; var _this$_getNamedChildr3 = this._getNamedChildren(), PageTail = _this$_getNamedChildr3.PageTail; var dataHook = 'page-tail'; return PageTail && /*#__PURE__*/_react["default"].createElement("div", { "data-hook": dataHook, key: dataHook, className: _PageSt.classes.tail, ref: function ref(r) { return _this4.pageHeaderTailRef = r; } }, PageTail); } }, { key: "_renderContentContainer", value: function _renderContentContainer() { var footerHeight = this.state.footerHeight; var children = this.props.children; var childrenObject = getChildrenObject(children); var PageContent = childrenObject.PageContent, PageFixedContent = childrenObject.PageFixedContent; return /*#__PURE__*/_react["default"].createElement(_PageContext.PageContext.Provider, { value: { stickyStyle: { top: "".concat(this._getMinimizedHeaderWrapperHeight() + this.state.tailHeight, "px") } } }, this._renderContentHorizontalLayout({ className: _PageSt.classes.contentContainer, style: { paddingBottom: footerHeight || '48px' }, horizontalScroll: this.props.horizontalScroll, children: /*#__PURE__*/_react["default"].createElement("div", { className: _PageSt.classes.contentFloating }, PageFixedContent && /*#__PURE__*/_react["default"].createElement(_PageSticky.PageSticky, { "data-hook": "page-fixed-content" }, /*#__PURE__*/_react["default"].cloneElement(PageFixedContent)), this._safeGetChildren(PageContent)) })); } }, { key: "render", value: function render() { var _this5 = this; var _this$props2 = this.props, dataHook = _this$props2.dataHook, className = _this$props2.className, minWidth = _this$props2.minWidth, zIndex = _this$props2.zIndex, height = _this$props2.height; return /*#__PURE__*/_react["default"].createElement("div", { "data-hook": dataHook, className: (0, _PageSt.st)(_PageSt.classes.root, {}, className), style: { zIndex: zIndex, height: height } }, /*#__PURE__*/_react["default"].createElement("div", { "data-hook": "page", className: _PageSt.classes.page, style: { minWidth: minWidth + 2 * parseInt(_PageSt.stVars.pageSidePadding, 10) }, ref: function ref(_ref6) { return _this5.pageRef = _ref6; } }, this._renderScrollableContainer())); } /** * Scrolls the page to a particular set of coordinates * @param {ScrollToOptions} scrollTo { left: number, top: number, behavior: 'smooth' | 'auto' } */ }, { key: "scrollTo", value: function scrollTo(_scrollTo) { var scrollContainer = this._getScrollContainer(); scrollContainer.scrollTo(_scrollTo); } }]); return Page; }(_react["default"].PureComponent); (0, _defineProperty2["default"])(Page, "defaultProps", { minWidth: parseInt(_PageSt.stVars.mainContainerMinWidth, 10), maxWidth: parseInt(_PageSt.stVars.mainContainerMaxWidth, 10), scrollProps: {} }); var FixedContent = function FixedContent(props) { return props.children; }; FixedContent.displayName = 'Page.FixedContent'; FixedContent.propTypes = { children: _propTypes["default"].element.isRequired }; Page.displayName = 'Page'; Page.Header = _PageHeader["default"]; Page.Section = _PageSection["default"]; Page.Content = _Content["default"]; Page.FixedContent = FixedContent; // TODO: deprecate, use Page.Sticky instead Page.Tail = _Tail["default"]; Page.FixedFooter = _FixedFooter["default"]; Page.Sticky = _PageSticky.PageSticky; var allowedChildren = [Page.Header, Page.Section, Page.Content, Page.FixedContent, Page.Tail, Page.FixedFooter]; Page.propTypes = { /** Applied as data-hook HTML attribute that can be used in the tests */ dataHook: _propTypes["default"].string, /** Background image url of the header background */ backgroundImageUrl: _propTypes["default"].string, /** Sets the max width of the content (Both in header and body) NOT including the page padding */ maxWidth: _propTypes["default"].number, /** Sets the min width of the content (Both in header and body) NOT including the page padding */ minWidth: _propTypes["default"].number, /** Allow the page to scroll horizontally for large width content */ horizontalScroll: _propTypes["default"].bool, /** Sets the height of the page (in px/vh/etc.) */ height: _propTypes["default"].string, /** Sets padding of the sides of the page */ sidePadding: _propTypes["default"].number, /** A css class to be applied to the component's root element */ className: _propTypes["default"].string, /** Header background color class name, allows to add a gradient to the header */ gradientClassName: _propTypes["default"].string, /** Will be called with the Page's scrollable content ref after page mount. * * **Note** - If you need this ref just for listening to scroll events on the scrollable content then use the prop * `scrollProps = {onScrollChanged/onScrollAreaChanged}` instead according to your needs. **/ scrollableContentRef: _propTypes["default"].func, /** Props related to the scrollable content of the page. * * **onScrollAreaChanged** - A Handler for scroll area changes, will be triggered only when the user scrolls to a * different area of the scrollable content, see signature for possible areas * ##### Signature: * `function({area: {y: AreaY, x: AreaX}, target: HTMLElement}) => void` * * `AreaY`: top | middle | bottom | none * * `AreaX`: start | middle | end | none (not implemented yet) * * **onScrollAreaChanged** - A Generic Handler for scroll changes with throttling (100ms) * ##### Signature: * `function({target: HTMLElement}) => void` * */ scrollProps: _propTypes["default"].shape(_ScrollableContainerCommon.ScrollableContainerCommonProps), /** Accepts these components as children: `Page.Header`, `Page.Tail`, `Page.Content`, `Page.FixedContent`. Order is insignificant. */ children: _propTypes["default"].arrayOf(function (children, key) { var child = children[key]; if (!child) { return; } var allowedDisplayNames = allowedChildren.map(function (c) { return c.displayName; }); var childDisplayName = child.type.displayName; if (!allowedDisplayNames.includes(childDisplayName)) { return new Error("Page: Invalid Prop children, unknown child ".concat(child.type)); } }).isRequired, /** z-index of the Page */ zIndex: _propTypes["default"].number }; function getChildrenObject(children) { return _react["default"].Children.toArray(children).reduce(function (acc, child) { switch (child.type.displayName) { case 'Page.Header': { acc.PageHeader = child; break; } case 'Page.Section': { acc.Section = child; break; } case 'Page.Content': { acc.PageContent = child; break; } case 'Page.FixedContent': { acc.PageFixedContent = child; break; } case 'Page.Tail': { acc.PageTail = child; break; } case 'Page.FixedFooter': { acc.FixedFooter = child; break; } default: { break; } } return acc; }, {}); } var _default = Page; exports["default"] = _default;