UNPKG

@wordpress/block-library

Version:
191 lines (174 loc) 4.95 kB
/** * External dependencies */ import clsx from 'clsx'; /** * WordPress dependencies */ import { __, _x, _n, sprintf } from '@wordpress/i18n'; import { useMemo } from '@wordpress/element'; import { AlignmentControl, BlockControls, InspectorControls, useBlockProps, } from '@wordpress/block-editor'; import { ToggleControl, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { __unstableSerializeAndClean } from '@wordpress/blocks'; import { useEntityProp, useEntityBlockEditor } from '@wordpress/core-data'; import { count as wordCount } from '@wordpress/wordcount'; /** * Internal dependencies */ import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; function PostTimeToReadEdit( { attributes, setAttributes, context } ) { const { textAlign, displayAsRange, displayMode, averageReadingSpeed } = attributes; const { postId, postType } = context; const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const [ contentStructure ] = useEntityProp( 'postType', postType, 'content', postId ); const [ blocks ] = useEntityBlockEditor( 'postType', postType, { id: postId, } ); const displayString = useMemo( () => { // Replicates the logic found in getEditedPostContent(). let content; if ( contentStructure instanceof Function ) { content = contentStructure( { blocks } ); } else if ( blocks ) { // If we have parsed blocks already, they should be our source of truth. // Parsing applies block deprecations and legacy block conversions that // unparsed content will not have. content = __unstableSerializeAndClean( blocks ); } else { content = contentStructure; } /* * translators: If your word count is based on single characters (e.g. East Asian characters), * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. * Do not translate into your own language. */ const wordCountType = _x( 'words', 'Word count type. Do not translate!' ); const totalWords = wordCount( content || '', wordCountType ); // Add "time to read" part, if enabled. if ( displayMode === 'time' ) { if ( displayAsRange ) { let maxMinutes = Math.max( 1, Math.round( ( totalWords / averageReadingSpeed ) * 1.2 ) ); const minMinutes = Math.max( 1, Math.round( ( totalWords / averageReadingSpeed ) * 0.8 ) ); if ( minMinutes === maxMinutes ) { maxMinutes = maxMinutes + 1; } // translators: %1$s: minimum minutes, %2$s: maximum minutes to read the post. const rangeLabel = _x( '%1$s–%2$s minutes', 'Range of minutes to read' ); return sprintf( rangeLabel, minMinutes, maxMinutes ); } const minutesToRead = Math.max( 1, Math.round( totalWords / averageReadingSpeed ) ); return sprintf( /* translators: %s: the number of minutes to read the post. */ _n( '%s minute', '%s minutes', minutesToRead ), minutesToRead ); } // Add "word count" part, if enabled. if ( displayMode === 'words' ) { return wordCountType === 'words' ? sprintf( /* translators: %s: the number of words in the post. */ _n( '%s word', '%s words', totalWords ), totalWords.toLocaleString() ) : sprintf( /* translators: %s: the number of characters in the post. */ _n( '%s character', '%s characters', totalWords ), totalWords.toLocaleString() ); } }, [ contentStructure, blocks, displayAsRange, displayMode, averageReadingSpeed, ] ); const blockProps = useBlockProps( { className: clsx( { [ `has-text-align-${ textAlign }` ]: textAlign, } ), } ); return ( <> <BlockControls group="block"> <AlignmentControl value={ textAlign } onChange={ ( nextAlign ) => { setAttributes( { textAlign: nextAlign } ); } } /> </BlockControls> { displayMode === 'time' && ( <InspectorControls> <ToolsPanel label={ __( 'Settings' ) } resetAll={ () => { setAttributes( { displayAsRange: true, } ); } } dropdownMenuProps={ dropdownMenuProps } > <ToolsPanelItem isShownByDefault label={ _x( 'Display as range', 'Turns reading time range display on or off' ) } hasValue={ () => ! displayAsRange } onDeselect={ () => { setAttributes( { displayAsRange: true, } ); } } > <ToggleControl __nextHasNoMarginBottom label={ __( 'Display as range' ) } checked={ !! displayAsRange } onChange={ () => setAttributes( { displayAsRange: ! displayAsRange, } ) } /> </ToolsPanelItem> </ToolsPanel> </InspectorControls> ) } <div { ...blockProps }>{ displayString }</div> </> ); } export default PostTimeToReadEdit;