/** * External dependencies */ import React, { createRef, Component } from 'react'; import PropTypes from 'prop-types'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import { clampLines } from './utils'; /** * Show text based content, limited to a number of lines, with a read more link. * * Based on https://github.com/zoltantothcom/react-clamp-lines. */ class ReadMore extends Component { constructor( props ) { super( ...arguments ); this.state = { /** * This is true when read more has been pressed and the full review is shown. */ isExpanded: false, /** * True if we are clamping content. False if the review is short. Null during init. */ clampEnabled: null, /** * Content is passed in via children. */ content: props.children, /** * Summary content generated from content HTML. */ summary: '.', }; this.reviewSummary = createRef(); this.reviewContent = createRef(); this.getButton = this.getButton.bind( this ); this.onClick = this.onClick.bind( this ); } componentDidMount() { if ( this.props.children ) { const { maxLines, ellipsis } = this.props; const lineHeight = this.reviewSummary.current.clientHeight + 1; const reviewHeight = this.reviewContent.current.clientHeight + 1; const maxHeight = lineHeight * maxLines + 1; const clampEnabled = reviewHeight > maxHeight; this.setState( { clampEnabled, } ); if ( clampEnabled ) { this.setState( { summary: clampLines( this.reviewContent.current.innerHTML, this.reviewSummary.current, maxHeight, ellipsis ), } ); } } } getButton() { const { isExpanded } = this.state; const { className, lessText, moreText } = this.props; const buttonText = isExpanded ? lessText : moreText; if ( ! buttonText ) { return; } return ( { buttonText } ); } /** * Handles the click event for the read more/less button. * * @param {Object} e event */ onClick( e ) { e.preventDefault(); const { isExpanded } = this.state; this.setState( { isExpanded: ! isExpanded, } ); } render() { const { className } = this.props; const { content, summary, clampEnabled, isExpanded } = this.state; if ( ! content ) { return null; } if ( clampEnabled === false ) { return (
{ content }
); } return (
{ ( ! isExpanded || clampEnabled === null ) && (
) } { ( isExpanded || clampEnabled === null ) && (
{ content }
) } { this.getButton() }
); } } ReadMore.propTypes = { children: PropTypes.node.isRequired, maxLines: PropTypes.number, ellipsis: PropTypes.string, moreText: PropTypes.string, lessText: PropTypes.string, className: PropTypes.string, }; ReadMore.defaultProps = { maxLines: 3, ellipsis: '…', moreText: __( 'Read more', 'woo-gutenberg-products-block' ), lessText: __( 'Read less', 'woo-gutenberg-products-block' ), className: 'read-more-content', }; export default ReadMore;