diff --git a/inc/css/blocks/class-posts-css.php b/inc/css/blocks/class-posts-css.php index 481a1c638..1802b7a19 100644 --- a/inc/css/blocks/class-posts-css.php +++ b/inc/css/blocks/class-posts-css.php @@ -62,6 +62,10 @@ public function render_css( $block ) { 'property' => '--background-color', 'value' => 'backgroundColor', ), + array( + 'property' => '--background-overlay', + 'value' => 'backgroundOverlay', + ), array( 'property' => '--border-color', 'value' => 'borderColor', @@ -204,6 +208,10 @@ public function render_css( $block ) { return isset( $attrs['imageBoxShadow'] ) && true === $attrs['imageBoxShadow']['active']; }, ), + array( + 'property' => '--image-ratio', + 'value' => 'imageRatio', + ), array( 'property' => '--border-width', 'value' => 'borderWidth', @@ -395,6 +403,38 @@ public function render_css( $block ) { ) ); + if ( isset( $block['attrs']['cardBorderRadius'] ) && is_array( $block['attrs']['cardBorderRadius'] ) ) { + $border_radius_properties = array( + 'top' => '--border-radius-start-start', + 'right' => '--border-radius-start-end', + 'bottom' => '--border-radius-end-start', + 'left' => '--border-radius-end-end', + ); + + $properties = array_map( + function( $position, $css_variable ) { + return array( + 'property' => $css_variable, + 'value' => 'cardBorderRadius', + 'format' => function( $value, $attrs ) use ( $position ) { + return $value[ $position ]; + }, + 'condition' => function( $attrs ) { + // @phpstan-ignore-next-line + return isset( $attrs['className'] ) && strpos( $attrs['className'], 'is-style-tiled' ) !== false; + }, + ); + }, + array_keys( $border_radius_properties ), + $border_radius_properties + ); + + $css->add_item( + array( + 'properties' => $properties, + ) + ); + } $style = $css->generate(); diff --git a/inc/render/class-posts-grid-block.php b/inc/render/class-posts-grid-block.php index 980a7694b..3989a1044 100644 --- a/inc/render/class-posts-grid-block.php +++ b/inc/render/class-posts-grid-block.php @@ -25,6 +25,7 @@ public function render( $attributes ) { $has_pagination = isset( $attributes['hasPagination'] ) && $attributes['hasPagination']; $page_number = 1; + $is_tiled = isset( $attributes['className'] ) && false !== strpos( $attributes['className'], 'is-style-tiled' ); if ( $has_pagination ) { if ( ! empty( get_query_var( 'page' ) ) || ! empty( get_query_var( 'paged' ) ) ) { @@ -71,10 +72,18 @@ function ( $x ) use ( $sticky_posts_id ) { $size = isset( $attributes['imageSize'] ) ? $attributes['imageSize'] : 'medium'; $thumb_id = get_post_thumbnail_id( $id ); $thumbnail = wp_get_attachment_image_src( $thumb_id, $size ); + $style = ''; - $list_items_markup .= '
'; + if ( $is_tiled && $thumbnail ) { + $style = sprintf( + ' style="background-image: url(%1$s); background-size: cover; background-position: center center;"', + esc_url( $thumbnail[0] ) + ); + } - if ( isset( $attributes['displayFeaturedImage'] ) && $attributes['displayFeaturedImage'] ) { + $list_items_markup .= '
'; + + if ( isset( $attributes['displayFeaturedImage'] ) && $attributes['displayFeaturedImage'] && ! $is_tiled ) { if ( $thumbnail ) { $image_alt = get_post_meta( $thumb_id, '_wp_attachment_image_alt', true ); @@ -115,10 +124,6 @@ function ( $x ) use ( $sticky_posts_id ) { $class .= ' o-posts-grid-columns-' . $attributes['columns']; } - if ( isset( $attributes['cropImage'] ) && true === $attributes['cropImage'] ) { - $class .= ' o-crop-img'; - } - $wrapper_attributes = get_block_wrapper_attributes(); $block_content = sprintf( @@ -293,6 +298,9 @@ protected function render_featured_post( $post, $attributes ) { $size = isset( $attributes['imageSize'] ) ? $attributes['imageSize'] : 'medium'; $thumb_id = get_post_thumbnail_id( $id ); $image_alt = get_post_meta( $thumb_id, '_wp_attachment_image_alt', true ); + $style = ''; + $image_url = wp_get_attachment_image_src( $thumb_id, $size ); + $is_tiled = isset( $attributes['className'] ) && false !== strpos( $attributes['className'], 'is-style-tiled' ); if ( ! $image_alt ) { $image_alt = get_the_title( $id ); @@ -300,7 +308,7 @@ protected function render_featured_post( $post, $attributes ) { $thumbnail = wp_get_attachment_image( $thumb_id, $size, false, array( 'alt' => $image_alt ) ); - if ( $thumbnail ) { + if ( $image_url && ! $is_tiled ) { $html .= sprintf( '
%2$s
', esc_url( get_the_permalink( $id ) ), @@ -308,11 +316,18 @@ protected function render_featured_post( $post, $attributes ) { ); } + if ( $is_tiled && $image_url ) { + $style = sprintf( + ' style="background-image: url(%1$s); background-size: cover; background-position: center center;"', + esc_url( $image_url[0] ) + ); + } + $html .= '
'; $html .= $this->get_post_fields( $id, $attributes ); $html .= '
'; - return sprintf( '', $html ); + return sprintf( '', $html ); } /** diff --git a/src/blocks/blocks/posts/block.json b/src/blocks/blocks/posts/block.json index 7cc2103d5..5ab79516a 100644 --- a/src/blocks/blocks/posts/block.json +++ b/src/blocks/blocks/posts/block.json @@ -110,9 +110,8 @@ "type": "boolean", "default": false }, - "cropImage": { - "type": "boolean", - "default": false + "imageRatio": { + "type": "string" }, "customTitleFontSize": { "type": [ "string", "number" ] @@ -147,6 +146,9 @@ "backgroundColor": { "type": "string" }, + "backgroundOverlay": { + "type": "string" + }, "borderColor": { "type": "string" }, diff --git a/src/blocks/blocks/posts/components/layout/featured.js b/src/blocks/blocks/posts/components/layout/featured.js index 6e5f16e0b..741707852 100644 --- a/src/blocks/blocks/posts/components/layout/featured.js +++ b/src/blocks/blocks/posts/components/layout/featured.js @@ -14,7 +14,15 @@ import { applyFilters } from '@wordpress/hooks'; * Internal dependencies */ -import Thumbnail from './thumbnail.js'; + +import { + Thumbnail, + useThumbnail +} from './thumbnail.js'; + +import { getActiveStyle } from '../../../../helpers/helper-functions.js'; + +import { styles } from '../../constants.js'; import { PostsCategory, @@ -29,16 +37,34 @@ const FeaturedPost = ({ author, categoriesList }) => { + const activeStyle = getActiveStyle( styles, attributes?.className ); + const isTiled = 'tiled' === activeStyle; + const { featuredImage } = useThumbnail( post?.featured_media, post.title?.rendered, attributes?.imageSize ); + if ( ! post ) { return ''; } const category = categoriesList && 0 < post?.categories?.length ? categoriesList.find( item => item.id === post.categories[0]) : undefined; + const hasFeaturedImage = 0 !== post.featured_media && attributes.displayFeaturedImage; + + const css = { + backgroundPosition: 'center center', + backgroundSize: 'cover' + }; + + if ( hasFeaturedImage && isTiled ) { + css.backgroundImage = `url(${ featuredImage })`; + } + return (
-
- { attributes.displayFeaturedImage && ( +
+ { ( attributes.displayFeaturedImage && ! isTiled ) && ( { + const activeStyle = getActiveStyle( styles, attributes?.className ); + const isTiled = 'tiled' === activeStyle; const postsToDisplay = posts .filter( post => post ); @@ -36,72 +49,89 @@ const Layout = ({ 'grid' === attributes.style ? classnames( 'is-grid', - `o-posts-grid-columns-${ attributes.columns }`, - { - 'o-crop-img': attributes.cropImage - } + `o-posts-grid-columns-${ attributes.columns }` ) : - classnames( - 'is-list', - { - 'o-crop-img': attributes.cropImage - } - ) + 'is-list' } > { postsToDisplay .slice( attributes.enableFeaturedPost ? 1 : 0 ) .map( post => { - const category = categoriesList && 0 < post?.categories?.length ? categoriesList.find( item => item.id === post.categories[0]) : undefined; - const categories = categoriesList && 0 < post?.categories?.length ? categoriesList.filter( item => post.categories.includes( item.id ) ) : []; - const author = authors && post.author ? authors.find( item => item.id === post.author ) : undefined; return ( -
-
- { ( 0 !== post.featured_media && attributes.displayFeaturedImage ) && ( - - ) } - -
- { attributes.template.map( element => { - switch ( element ) { - case 'category': - return ; - case 'title': - return ; - case 'meta': - return ; - case 'description': - return ; - default: - return applyFilters( 'otter.postsBlock.templateLoop', '', element, attributes ); - } - }) } -
-
-
+ ); }) }
); }; +const PostBody = ({ post, attributes, categoriesList, authors, isTiled }) => { + const { featuredImage } = useThumbnail( post?.featured_media, post.title?.rendered, attributes?.imageSize ); + const category = categoriesList && 0 < post?.categories?.length ? categoriesList.find( item => item.id === post.categories[0]) : undefined; + const categories = categoriesList && 0 < post?.categories?.length ? categoriesList.filter( item => post.categories.includes( item.id ) ) : []; + const author = authors && post.author ? authors.find( item => item.id === post.author ) : undefined; + const hasFeaturedImage = 0 !== post.featured_media && attributes.displayFeaturedImage; + const css = { + backgroundPosition: 'center center', + backgroundSize: 'cover' + }; + + if ( hasFeaturedImage && isTiled ) { + css.backgroundImage = `url(${ featuredImage })`; + } + + return ( +
+
+ { ( hasFeaturedImage && ! isTiled ) && ( + + ) } + +
+ { attributes.template.map( element => { + switch ( element ) { + case 'category': + return ; + case 'title': + return ; + case 'meta': + return ; + case 'description': + return ; + default: + return applyFilters( 'otter.postsBlock.templateLoop', '', element, attributes ); + } + }) } +
+
+
+ ); +}; + export const PostsCategory = ({ attributes, element, category, categoriesList }) => { if ( undefined !== category && ( attributes.displayCategory && categoriesList ) ) { return { unescapeHTML( category.name ) }; diff --git a/src/blocks/blocks/posts/components/layout/thumbnail.js b/src/blocks/blocks/posts/components/layout/thumbnail.js index 4bc2475f5..2aa363322 100644 --- a/src/blocks/blocks/posts/components/layout/thumbnail.js +++ b/src/blocks/blocks/posts/components/layout/thumbnail.js @@ -12,17 +12,18 @@ import { useSelect } from '@wordpress/data'; import { isEmpty } from 'lodash'; -const Thumbnail = ({ - id, - link, - alt, - size, - imgStyle -}) => { +export const useThumbnail = ( id, size, alt ) => { const { featuredImage, altText } = useSelect( select => { + if ( ! id ) { + return { + featuredImage: null, + altText: null + }; + } + const image = select( 'core' ).getMedia( id, { context: 'view' }); const featuredImage = image ? @@ -39,6 +40,20 @@ const Thumbnail = ({ }; }, [ size, id ]); + return { + featuredImage, + altText + }; +}; + +export const Thumbnail = ({ + id, + link, + alt, + size, + imgStyle +}) => { + const { featuredImage, altText } = useThumbnail( id, size, alt ); if ( null === featuredImage ) { return ; @@ -52,5 +67,3 @@ const Thumbnail = ({
); }; - -export default Thumbnail; diff --git a/src/blocks/blocks/posts/components/sortable.js b/src/blocks/blocks/posts/components/sortable.js index 15f4b06bc..fdb8ef08a 100644 --- a/src/blocks/blocks/posts/components/sortable.js +++ b/src/blocks/blocks/posts/components/sortable.js @@ -168,10 +168,38 @@ export const SortableItem = ({ onChange={ imageSize => setAttributes({ imageSize }) } /> - setAttributes({ cropImage }) } + { + if ( 'inherit' === imageRatio ) { + return setAttributes({ imageRatio: undefined }); + } + + setAttributes({ imageRatio }); + } } /> ) } diff --git a/src/blocks/blocks/posts/constants.js b/src/blocks/blocks/posts/constants.js new file mode 100644 index 000000000..f5ab1462f --- /dev/null +++ b/src/blocks/blocks/posts/constants.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +export const styles = [ + { + label: __( 'Default', 'otter-blocks' ), + value: 'default', + isDefault: true + }, + { + label: __( 'Boxed', 'otter-blocks' ), + value: 'boxed' + }, + { + label: __( 'Tiled', 'otter-blocks' ), + value: 'tiled' + } +]; diff --git a/src/blocks/blocks/posts/edit.js b/src/blocks/blocks/posts/edit.js index 655565861..65054a8c8 100644 --- a/src/blocks/blocks/posts/edit.js +++ b/src/blocks/blocks/posts/edit.js @@ -59,18 +59,7 @@ import { import '../../components/store/index.js'; import FeaturedPost from './components/layout/featured.js'; import { domReady } from '../../helpers/frontend-helper-functions'; - -const styles = [ - { - label: __( 'Default', 'otter-blocks' ), - value: 'default', - isDefault: true - }, - { - label: __( 'Boxed', 'otter-blocks' ), - value: 'boxed' - } -]; +import { styles } from './constants.js'; const { attributes: defaultAttributes } = metadata; @@ -259,13 +248,19 @@ const Edit = ({ const inlineStyles = { '--img-border-radius': isObject( attributes.borderRadius ) ? boxValues( attributes.borderRadius ) : _px( attributes.borderRadius ), '--img-box-shadow': imageBoxShadow.active && `${ imageBoxShadow.horizontal }px ${ imageBoxShadow.vertical }px ${ imageBoxShadow.blur }px ${ imageBoxShadow.spread }px ${ hex2rgba( imageBoxShadow.color, imageBoxShadow.colorOpacity ) }`, + '--image-ratio': attributes.imageRatio, '--border-width': _px( attributes.borderWidth ), '--border-radius': boxValues( attributes.cardBorderRadius ), + '--border-radius-start-start': _px( attributes.cardBorderRadius?.top ), + '--border-radius-start-end': _px( attributes.cardBorderRadius?.right ), + '--border-radius-end-start': _px( attributes.cardBorderRadius?.bottom ), + '--border-radius-end-end': _px( attributes.cardBorderRadius?.left ), '--box-shadow': boxShadow.active && `${ boxShadow.horizontal }px ${ boxShadow.vertical }px ${ boxShadow.blur }px ${ boxShadow.spread }px ${ hex2rgba( boxShadow.color, boxShadow.colorOpacity ) }`, '--vert-align': _align( attributes.verticalAlign ), '--text-align': attributes.textAlign, '--text-color': attributes.textColor, '--background-color': attributes.backgroundColor, + '--background-overlay': attributes.backgroundOverlay || '#0000005e', '--border-color': attributes.borderColor, '--content-gap': attributes.contentGap, '--img-width': responsiveGetAttributes([ _px( attributes.imageWidth ), attributes.imageWidthTablet, attributes.imageWidthMobile ]), diff --git a/src/blocks/blocks/posts/inspector.js b/src/blocks/blocks/posts/inspector.js index 229066a67..2103e0115 100644 --- a/src/blocks/blocks/posts/inspector.js +++ b/src/blocks/blocks/posts/inspector.js @@ -51,18 +51,7 @@ import { import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; import { useTabSwitch } from '../../helpers/block-utility'; import { makeBox } from '../../plugins/copy-paste/utils'; - -const styles = [ - { - label: __( 'Default', 'otter-blocks' ), - value: 'default', - isDefault: true - }, - { - label: __( 'Boxed', 'otter-blocks' ), - value: 'boxed' - } -]; +import { styles } from './constants.js'; const defaultFontSizes = [ { @@ -104,6 +93,7 @@ const Inspector = ({ isLoading }) => { const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); + const style = getActiveStyle( styles, attributes?.className ); const { slugs @@ -443,6 +433,15 @@ const Inspector = ({ label: __( 'Border', 'otter-blocks' ), isShownByDefault: false }, + ...( 'tiled' === style ? [ + { + value: attributes.backgroundOverlay, + onChange: backgroundOverlay => setAttributes({ backgroundOverlay }), + label: __( 'Background Overlay', 'otter-blocks' ), + enableAlpha: true, + isShownByDefault: false + } + ] : []), ...( attributes.hasPagination ? [ { value: attributes.pagColor, @@ -644,6 +643,7 @@ const Inspector = ({ values={ attributes.cardBorderRadius ?? makeBox( '0px' ) } onChange={ cardBorderRadius => setAttributes({ cardBorderRadius }) } id="o-border-raduis-box" + allowReset={ false } /> { diff --git a/src/blocks/blocks/posts/style.scss b/src/blocks/blocks/posts/style.scss index 5f7ae8546..940191095 100644 --- a/src/blocks/blocks/posts/style.scss +++ b/src/blocks/blocks/posts/style.scss @@ -3,11 +3,17 @@ --vert-align: initial; --img-border-radius: 0; --img-box-shadow: none; + --image-ratio: inherit; --border-width: 0; --text-color: inherit; --background-color: inherit; + --background-overlay: #0000005e; --border-color: inherit; --border-radius: 0; + --border-radius-start-start: 0; + --border-radius-start-end: 0; + --border-radius-end-start: 0; + --border-radius-end-end: 0; --box-shadow: none; --content-gap: 10px; @@ -64,11 +70,72 @@ } &.is-style-boxed { + --background-color: rgb( 240, 245, 250); --border-width: 1px; .o-posts-grid-post-body { padding: var( --content-padding, 20px ); - background: rgb( 240, 245, 250); + } + } + + &.is-style-tiled { + .o-posts-grid-post-blog { + background-color: var( --background-color ); + border-width: var( --border-width ); + border-style: solid; + border-color: var( --border-color ); + border-radius: var( --border-radius ); + box-shadow: var( --box-shadow ); + } + + .o-featured-post { + background-color: var( --background-color ); + + .o-posts-grid-post-body { + background: var( --background-overlay ); + border-radius: calc( var( --border-radius-start-start ) - var( --border-width) ) calc( var( --border-radius-start-end ) - var( --border-width) ) calc( var( --border-radius-end-start ) - var( --border-width) ) calc( var( --border-radius-end-end ) - var( --border-width) ); + } + } + + .o-posts-grid-post { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: end; + background: var( --background-overlay ); + border-radius: calc( var( --border-radius-start-start ) - var( --border-width) ) calc( var( --border-radius-start-end ) - var( --border-width) ) calc( var( --border-radius-end-start ) - var( --border-width) ) calc( var( --border-radius-end-end ) - var( --border-width) ); + } + + .o-posts-grid-post, + .o-featured-post { + .o-posts-grid-post-body { + padding: var( --content-padding, 36px ); + color: var( --text-color, #fff ); + + .o-posts-grid-post-title a { + color: var( --text-color, #fff ); + } + } + } + } + + &:not(.is-style-tiled) { + .o-posts-grid-post-blog { + .o-posts-grid-post { + background: var( --background-color ); + border-width: var( --border-width ); + border-style: solid; + border-color: var( --border-color ); + border-radius: var( --border-radius ); + box-shadow: var( --box-shadow ); + } + } + + .o-featured-container { + .o-featured-post { + background-color: var( --background-color ); + } } } @@ -116,7 +183,6 @@ img { height: 100%; - object-fit: cover; } } @@ -130,21 +196,6 @@ } } - .o-crop-img { - .o-posts-grid-post-blog { - .o-posts-grid-post { - .o-posts-grid-post-image { - img { - width: 150px; - height: 150px; - object-fit: cover; - border-radius: var( --img-border-radius ); - } - } - } - } - } - .o-posts-grid-post-blog { width: 100%; position: relative; @@ -156,12 +207,6 @@ .o-posts-grid-post { overflow: hidden; - border-width: var( --border-width ); - border-style: solid; - border-color: var( --border-color ); - border-radius: var( --border-radius ); - background: var( --background-color ); - box-shadow: var( --box-shadow ); } } @@ -184,6 +229,9 @@ border-radius: 5px; border-radius: var( --img-border-radius ); box-shadow: var( --img-box-shadow ); + object-fit: cover; + object-position: center center; + aspect-ratio: var( --image-ratio ); } } @@ -201,6 +249,11 @@ color: var( --text-color ); text-transform: capitalize; margin: 0; + + a { + font-size: var( --meta-text-size ); + color: var( --text-color ); + } } .o-posts-grid-post-title { @@ -223,6 +276,11 @@ font-size: var( --meta-text-size ); color: var( --text-color ); margin: 0; + + a { + font-size: var( --meta-text-size ); + color: var( --text-color ); + } } .o-posts-grid-post-description { @@ -254,7 +312,6 @@ border-style: solid; border-color: var( --border-color ); border-radius: var( --border-radius ); - background-color: var( --background-color ); box-shadow: var( --box-shadow ); margin-bottom: var( --row-gap ); @@ -265,6 +322,8 @@ width: 100%; height: 250px; object-fit: cover; + object-position: center center; + aspect-ratio: var( --image-ratio ); } } diff --git a/src/blocks/blocks/posts/types.d.ts b/src/blocks/blocks/posts/types.d.ts index 37895f475..1bf0d122e 100644 --- a/src/blocks/blocks/posts/types.d.ts +++ b/src/blocks/blocks/posts/types.d.ts @@ -26,7 +26,7 @@ type Attributes = { displayComments: boolean displayPostCategory: boolean displayReadMoreLink: boolean - cropImage: boolean + imageRatio: string customTitleFontSize: number customTitleFontSizeTablet: number customTitleFontSizeMobile: number diff --git a/tests/test-post-grid-block.php b/tests/test-post-grid-block.php index 8bcc970c0..5ddec6774 100644 --- a/tests/test-post-grid-block.php +++ b/tests/test-post-grid-block.php @@ -53,7 +53,6 @@ class Test_Post_Grid_Block extends WP_UnitTestCase { 'displayComments' => true, 'displayPostCategory' => false, 'displayReadMoreLink' => false, - 'cropImage' => false, 'boxShadow' => array( 'active' => false, 'colorOpacity' => 50,