diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index 012918eef54836..7b68e2d7a9ddfe 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -28,6 +28,7 @@ @import "./navigation-link/editor.scss"; @import "./nextpage/editor.scss"; @import "./paragraph/editor.scss"; +@import "./post-excerpt/editor.scss"; @import "./pullquote/editor.scss"; @import "./quote/editor.scss"; @import "./rss/editor.scss"; diff --git a/packages/block-library/src/post-excerpt/block.json b/packages/block-library/src/post-excerpt/block.json index ef39be86f839cc..24a74217c547a7 100644 --- a/packages/block-library/src/post-excerpt/block.json +++ b/packages/block-library/src/post-excerpt/block.json @@ -1,4 +1,17 @@ { "name": "core/post-excerpt", - "category": "layout" + "category": "layout", + "attributes": { + "wordCount": { + "type": "number", + "default": 55 + }, + "moreText": { + "type": "string" + }, + "showMoreOnNewLine": { + "type": "boolean", + "default": true + } + } } diff --git a/packages/block-library/src/post-excerpt/edit.js b/packages/block-library/src/post-excerpt/edit.js index 7bbeff3a979003..7b4ee53704f37a 100644 --- a/packages/block-library/src/post-excerpt/edit.js +++ b/packages/block-library/src/post-excerpt/edit.js @@ -2,20 +2,118 @@ * WordPress dependencies */ import { useEntityProp, useEntityId } from '@wordpress/core-data'; -import { PlainText } from '@wordpress/block-editor'; +import { useMemo } from '@wordpress/element'; +import { InspectorControls, RichText } from '@wordpress/block-editor'; +import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; -function PostExcerptDisplay() { +function usePostContentExcerpt( wordCount ) { + const [ , , { raw: rawPostContent } ] = useEntityProp( + 'postType', + 'post', + 'content' + ); + return useMemo( () => { + if ( ! rawPostContent ) { + return ''; + } + const excerptElement = document.createElement( 'div' ); + excerptElement.innerHTML = rawPostContent; + const excerpt = + excerptElement.textContent || excerptElement.innerText || ''; + return excerpt + .trim() + .split( ' ', wordCount ) + .join( ' ' ); + }, [ rawPostContent, wordCount ] ); +} + +function PostExcerptEditor( { + attributes: { wordCount, moreText, showMoreOnNewLine }, + setAttributes, + isSelected, +} ) { const [ excerpt, setExcerpt ] = useEntityProp( 'postType', 'post', 'excerpt' ); - return ; + const postContentExcerpt = usePostContentExcerpt( wordCount ); + return ( + <> + <InspectorControls> + <PanelBody title={ __( 'Post Excerpt Settings' ) }> + { ! excerpt && ( + <RangeControl + label={ __( 'Max words' ) } + value={ wordCount } + onChange={ ( newExcerptLength ) => + setAttributes( { wordCount: newExcerptLength } ) + } + min={ 10 } + max={ 100 } + /> + ) } + <ToggleControl + label={ __( 'Show link on new line' ) } + checked={ showMoreOnNewLine } + onChange={ ( newShowMoreOnNewLine ) => + setAttributes( { + showMoreOnNewLine: newShowMoreOnNewLine, + } ) + } + /> + </PanelBody> + </InspectorControls> + <RichText + className={ + ! showMoreOnNewLine && + 'wp-block-post-excerpt__excerpt is-inline' + } + placeholder={ postContentExcerpt } + value={ excerpt || ( isSelected ? '' : postContentExcerpt ) } + onChange={ setExcerpt } + keepPlaceholderOnFocus + /> + { ! showMoreOnNewLine && ' ' } + { showMoreOnNewLine ? ( + <p> + <RichText + tagName="a" + placeholder={ __( 'Read more…' ) } + value={ moreText } + onChange={ ( newMoreText ) => + setAttributes( { moreText: newMoreText } ) + } + /> + </p> + ) : ( + <RichText + tagName="a" + placeholder={ __( 'Read more…' ) } + value={ moreText } + onChange={ ( newMoreText ) => + setAttributes( { moreText: newMoreText } ) + } + /> + ) } + </> + ); } -export default function PostExcerptEdit() { +export default function PostExcerptEdit( { + attributes, + setAttributes, + isSelected, +} ) { if ( ! useEntityId( 'postType', 'post' ) ) { return 'Post Excerpt Placeholder'; } - return <PostExcerptDisplay />; + return ( + <PostExcerptEditor + attributes={ attributes } + setAttributes={ setAttributes } + isSelected={ isSelected } + /> + ); } diff --git a/packages/block-library/src/post-excerpt/editor.scss b/packages/block-library/src/post-excerpt/editor.scss new file mode 100644 index 00000000000000..8d36110746aef4 --- /dev/null +++ b/packages/block-library/src/post-excerpt/editor.scss @@ -0,0 +1,3 @@ +.wp-block-post-excerpt__excerpt.is-inline { + display: inline-block; +} diff --git a/packages/block-library/src/post-excerpt/index.php b/packages/block-library/src/post-excerpt/index.php index 06d1b9726468fc..c3f1755d0422f0 100644 --- a/packages/block-library/src/post-excerpt/index.php +++ b/packages/block-library/src/post-excerpt/index.php @@ -8,14 +8,39 @@ /** * Renders the `core/post-excerpt` block on the server. * + * @param array $attributes The block attributes. + * * @return string Returns the filtered post excerpt for the current post wrapped inside "p" tags. */ -function render_block_core_post_excerpt() { +function render_block_core_post_excerpt( $attributes ) { $post = gutenberg_get_post_from_context(); if ( ! $post ) { return ''; } - return '<p>' . get_the_excerpt( $post ) . '</p>'; + + $more_text = isset( $attributes['moreText'] ) ? '<a href="' . esc_url( get_the_permalink( $post ) ) . '">' . $attributes['moreText'] . '</a>' : ''; + + $filter_excerpt_length = function() use ( $attributes ) { + return isset( $attributes['wordCount'] ) ? $attributes['wordCount'] : 55; + }; + add_filter( + 'excerpt_length', + $filter_excerpt_length + ); + + $output = '<p>' . get_the_excerpt( $post ); + if ( ! isset( $attributes['showMoreOnNewLine'] ) || $attributes['showMoreOnNewLine'] ) { + $output .= '</p>' . '<p>' . $more_text . '</p>'; + } else { + $output .= ' ' . $more_text . '</p>'; + } + + remove_filter( + 'excerpt_length', + $filter_excerpt_length + ); + + return $output; } /** @@ -25,6 +50,19 @@ function register_block_core_post_excerpt() { register_block_type( 'core/post-excerpt', array( + 'attributes' => array( + 'wordCount' => array( + 'type' => 'number', + 'default' => 55, + ), + 'moreText' => array( + 'type' => 'string', + ), + 'showMoreOnNewLine' => array( + 'type' => 'boolean', + 'default' => true, + ), + ), 'render_callback' => 'render_block_core_post_excerpt', ) ); diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index 03454425ccb315..3a73e5a79c280a 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -85,12 +85,18 @@ export function useEntityId( kind, type ) { export function useEntityProp( kind, type, prop ) { const id = useEntityId( kind, type ); - const value = useSelect( + const { value, fullValue } = useSelect( ( select ) => { const { getEntityRecord, getEditedEntityRecord } = select( 'core' ); - getEntityRecord( kind, type, id ); // Trigger resolver. - const entity = getEditedEntityRecord( kind, type, id ); - return entity && entity[ prop ]; + const entity = getEntityRecord( kind, type, id ); // Trigger resolver. + const editedEntity = getEditedEntityRecord( kind, type, id ); + return ( + entity && + editedEntity && { + value: editedEntity[ prop ], + fullValue: entity[ prop ], + } + ); }, [ kind, type, id, prop ] ); @@ -105,7 +111,7 @@ export function useEntityProp( kind, type, prop ) { [ kind, type, id, prop ] ); - return [ value, setValue ]; + return [ value, setValue, fullValue ]; } /** diff --git a/packages/e2e-tests/fixtures/blocks/core__post-excerpt.json b/packages/e2e-tests/fixtures/blocks/core__post-excerpt.json index 67bee1bd83f275..44a38831ec996e 100644 --- a/packages/e2e-tests/fixtures/blocks/core__post-excerpt.json +++ b/packages/e2e-tests/fixtures/blocks/core__post-excerpt.json @@ -3,7 +3,10 @@ "clientId": "_clientId_0", "name": "core/post-excerpt", "isValid": true, - "attributes": {}, + "attributes": { + "wordCount": 55, + "showMoreOnNewLine": true + }, "innerBlocks": [], "originalContent": "" } diff --git a/test/integration/full-content/server-registered.json b/test/integration/full-content/server-registered.json index 3fae2b82a0e4ea..95514917078471 100644 --- a/test/integration/full-content/server-registered.json +++ b/test/integration/full-content/server-registered.json @@ -1 +1 @@ -{"core\/archives":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/calendar":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"month":{"type":"integer"},"year":{"type":"integer"}}},"core\/categories":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showHierarchy":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/latest-comments":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"commentsToShow":{"type":"number","default":5,"minimum":1,"maximum":100},"displayAvatar":{"type":"boolean","default":true},"displayDate":{"type":"boolean","default":true},"displayExcerpt":{"type":"boolean","default":true}}},"core\/latest-posts":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"categories":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostContent":{"type":"boolean","default":false},"displayPostContentRadio":{"type":"string","default":"excerpt"},"excerptLength":{"type":"number","default":55},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"},"displayFeaturedImage":{"type":"boolean","default":false},"featuredImageAlign":{"type":"string","enum":["left","center","right"]},"featuredImageSizeSlug":{"type":"string","default":"thumbnail"},"featuredImageSizeWidth":{"type":"number","default":null},"featuredImageSizeHeight":{"type":"number","default":null}}},"core\/legacy-widget":{"attributes":{"widgetClass":{"type":"string"},"id":{"type":"string"},"idBase":{"type":"string"},"number":{"type":"number"},"instance":{"type":"object"}}},"core\/navigation":{"attributes":{"className":{"type":"string"},"textColor":{"type":"string"},"customTextColor":{"type":"string"},"rgbTextColor":{"type":"string"},"backgroundColor":{"type":"string"},"customBackgroundColor":{"type":"string"},"rgbBackgroundColor":{"type":"string"},"fontSize":{"type":"string"},"customFontSize":{"type":"number"},"itemsJustification":{"type":"string"},"showSubmenuIcon":{"type":"boolean","default":true}}},"core\/post-comments-count":{"attributes":{"className":{"type":"string"}}},"core\/rss":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"columns":{"type":"number","default":2},"blockLayout":{"type":"string","default":"list"},"feedURL":{"type":"string","default":""},"itemsToShow":{"type":"number","default":5},"displayExcerpt":{"type":"boolean","default":false},"displayAuthor":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":false},"excerptLength":{"type":"number","default":55}}},"core\/search":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"label":{"type":"string","default":"Search"},"placeholder":{"type":"string","default":""},"buttonText":{"type":"string","default":"Search"}}},"core\/tag-cloud":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"taxonomy":{"type":"string","default":"post_tag"},"showTagCounts":{"type":"boolean","default":false}}},"core\/template-part":{"attributes":{"postId":{"type":"number"},"slug":{"type":"string"},"theme":{"type":"string"}}}} +{"core\/archives":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/calendar":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"month":{"type":"integer"},"year":{"type":"integer"}}},"core\/categories":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showHierarchy":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/latest-comments":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"commentsToShow":{"type":"number","default":5,"minimum":1,"maximum":100},"displayAvatar":{"type":"boolean","default":true},"displayDate":{"type":"boolean","default":true},"displayExcerpt":{"type":"boolean","default":true}}},"core\/latest-posts":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"categories":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostContent":{"type":"boolean","default":false},"displayPostContentRadio":{"type":"string","default":"excerpt"},"excerptLength":{"type":"number","default":55},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"},"displayFeaturedImage":{"type":"boolean","default":false},"featuredImageAlign":{"type":"string","enum":["left","center","right"]},"featuredImageSizeSlug":{"type":"string","default":"thumbnail"},"featuredImageSizeWidth":{"type":"number","default":null},"featuredImageSizeHeight":{"type":"number","default":null}}},"core\/legacy-widget":{"attributes":{"widgetClass":{"type":"string"},"id":{"type":"string"},"idBase":{"type":"string"},"number":{"type":"number"},"instance":{"type":"object"}}},"core\/navigation":{"attributes":{"className":{"type":"string"},"textColor":{"type":"string"},"customTextColor":{"type":"string"},"rgbTextColor":{"type":"string"},"backgroundColor":{"type":"string"},"customBackgroundColor":{"type":"string"},"rgbBackgroundColor":{"type":"string"},"fontSize":{"type":"string"},"customFontSize":{"type":"number"},"itemsJustification":{"type":"string"},"showSubmenuIcon":{"type":"boolean","default":true}}},"core\/post-comments-count":{"attributes":{"className":{"type":"string"}}},"core\/post-excerpt":{"attributes":{"wordCount":{"type":"number","default":55},"moreText":{"type":"string"},"showMoreOnNewLine":{"type":"boolean","default":true}}},"core\/rss":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"columns":{"type":"number","default":2},"blockLayout":{"type":"string","default":"list"},"feedURL":{"type":"string","default":""},"itemsToShow":{"type":"number","default":5},"displayExcerpt":{"type":"boolean","default":false},"displayAuthor":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":false},"excerptLength":{"type":"number","default":55}}},"core\/search":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"label":{"type":"string","default":"Search"},"placeholder":{"type":"string","default":""},"buttonText":{"type":"string","default":"Search"}}},"core\/tag-cloud":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"taxonomy":{"type":"string","default":"post_tag"},"showTagCounts":{"type":"boolean","default":false}}},"core\/template-part":{"attributes":{"postId":{"type":"number"},"slug":{"type":"string"},"theme":{"type":"string"}}}}