UNPKG

@shopgate/engage

Version:
185 lines (175 loc) 6.03 kB
import _createClass from "@babel/runtime/helpers/createClass"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; import React, { Component } from 'react'; import PropTypes from 'prop-types'; import debounce from 'lodash/debounce'; import I18n from '@shopgate/pwa-common/components/I18n'; import RippleButton from '@shopgate/pwa-ui-shared/RippleButton'; import { themeConfig } from '@shopgate/pwa-common/helpers/config'; import styles from "./style"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const { variables } = themeConfig; /** * The height of one row. * @type {number} */ export const CHIP_ROW_HEIGHT = 34; /** * The minimum width that a chip should have. * @type {number} */ export const CHIP_MINIMUM_WIDTH = 60; /** * The ChipLayout component. */ let ChipLayout = /*#__PURE__*/function (_Component) { function ChipLayout(...args) { var _this; _this = _Component.call.apply(_Component, [this].concat(args)) || this; /** * Debounced, eg. layout effect */ _this.processHiddenElementsDebounced = debounce(_this.processHiddenElements, 50); return _this; } _inheritsLoose(ChipLayout, _Component); var _proto = ChipLayout.prototype; /** * When the component mounts we need to initially process all children. */ _proto.componentDidMount = function componentDidMount() { this.processHiddenElementsDebounced(); } /** * Eveyry time pathname or other prop changes this callback is called. * This funtion will start processing hidden elements in order to check if "more" button * should be rendered. * * It must be done on every prop change, including the pathname. * Sometimes this component is rendered invisible, then since `.processHiddenElements` uses * refs.clientHeight it would always be zero. */; _proto.componentDidUpdate = function componentDidUpdate() { this.processHiddenElementsDebounced(); } /** * Returns the maximum height the container should have. * @returns {number} */; /** * Loops through all children to make sure the more button appears if there is too much content. */ _proto.processHiddenElements = function processHiddenElements() { if (!this.containerRef) { return; } // Find out if there are overflowing elements. let lastVisibleElement = 0; const showMoreButton = this.containerRef.scrollHeight > this.containerRef.clientHeight; const containerHeight = this.containerRef.clientHeight; const chips = Array.from(this.layoutRef.children); this.moreButtonRef.style.display = showMoreButton ? 'block' : 'none'; this.layoutRef.style.minHeight = showMoreButton ? `${this.maxContentHeight}px` : '0px'; // If the more button is not visible we don't need to process anything. if (!showMoreButton) { return; } // Hide or show chips that are hidden due to overflow. chips.forEach((child, index) => { const isVisible = child.offsetTop + child.clientHeight < containerHeight; child.setAttribute('style', `display: ${isVisible ? 'flex' : 'none'};`); if (isVisible) { lastVisibleElement = index; } }); // Hide the more button if previous assumption was incorrect. if (lastVisibleElement === chips.length - 1) { this.moreButtonRef.style.display = 'none'; return; } // Hide elements so that the 'more button' has enough space. chips.slice(0, lastVisibleElement + 1).reverse().every(element => { const offsetBottom = element.offsetTop + element.clientHeight; if (this.moreButtonRef.offsetTop > offsetBottom) { return true; } const buttonSpaceRequired = this.moreButtonRef.clientWidth + variables.gap.big; const elementRight = this.containerRef.clientWidth - (element.offsetLeft + element.clientWidth); const spaceDiff = buttonSpaceRequired - elementRight; const remainingChipWidth = element.clientWidth - spaceDiff; if (remainingChipWidth > CHIP_MINIMUM_WIDTH) { element.setAttribute('style', `max-width: ${remainingChipWidth}px`); return false; } if (element.offsetTop !== chips[lastVisibleElement].offsetTop) { element.setAttribute('style', 'display: none'); return false; } element.setAttribute('style', 'display: none'); return true; }); }; /** * Renders the component. * @returns {JSX} */ _proto.render = function render() { return /*#__PURE__*/_jsxs("div", { ref: element => { this.containerRef = element; }, className: `${styles.container(this.maxContentHeight)} engage__chip-layout`, children: [/*#__PURE__*/_jsx("div", { ref: element => { this.layoutRef = element; }, className: styles.layout, children: this.props.children }), /*#__PURE__*/_jsx("div", { ref: element => { this.moreButtonRef = element; }, className: styles.moreButtonWrapper, children: /*#__PURE__*/_jsx(RippleButton, { fill: true, type: "plain", className: this.moreButtonStyles, onClick: this.props.handleMoreButton, children: /*#__PURE__*/_jsx(I18n.Text, { string: this.props.moreLabel }) }) })] }); }; return _createClass(ChipLayout, [{ key: "maxContentHeight", get: function () { // 8 -> container padding. return CHIP_ROW_HEIGHT * this.props.maxRows + 8; } /** * Returns the more button styles. * @return {string} The store button class name. */ }, { key: "moreButtonStyles", get: function () { if (this.props.invertMoreButton) { return styles.moreButtonInverted; } return styles.moreButton; } }]); }(Component); ChipLayout.defaultProps = { children: null, handleMoreButton: () => {}, invertMoreButton: false, maxRows: 2, moreLabel: 'more', pathname: '' }; export default ChipLayout;