diff --git a/includes/newspack-listings-utils.php b/includes/newspack-listings-utils.php index 8675c0a2..420bf297 100644 --- a/includes/newspack-listings-utils.php +++ b/includes/newspack-listings-utils.php @@ -107,7 +107,7 @@ function get_data_from_blocks( $blocks, $source ) { } // If the source has 'single' specified, only get data from the first found block instance. - if ( $source['single'] ) { + if ( isset( $source['single'] ) && ! empty( $source['single'] ) ) { $matching_blocks = array_slice( $matching_blocks, 0, 1 ); } @@ -138,7 +138,7 @@ function get_data_from_blocks( $blocks, $source ) { } // If the source has 'single' specified, only return data from the first found block instance. - if ( $source['single'] ) { + if ( isset( $source['single'] ) && ! empty( $source['single'] ) ) { return array_shift( $data ); } diff --git a/src/assets/front-end/curated-list.scss b/src/assets/front-end/curated-list.scss index 41edd158..400faecf 100644 --- a/src/assets/front-end/curated-list.scss +++ b/src/assets/front-end/curated-list.scss @@ -38,7 +38,13 @@ } .newspack-listings__listing + .newspack-listings__listing { - border-top: 1px solid var( --newspack-listings--grey-light ); + border-top: 1px solid var( --newspack-listings--border-dark ); + } + + .has-dark-background & { + .newspack-listings__listing + .newspack-listings__listing { + border-top-color: var( --newspack-listings--border-light ); + } } } diff --git a/src/assets/shared/curated-list.scss b/src/assets/shared/curated-list.scss index 7d3f488d..92c79f9f 100644 --- a/src/assets/shared/curated-list.scss +++ b/src/assets/shared/curated-list.scss @@ -13,6 +13,10 @@ font-weight: bold; margin-bottom: 0.5rem; } + + &.has-background-color { + padding: 1em; + } } &__load-more { diff --git a/src/assets/shared/variables.scss b/src/assets/shared/variables.scss index 841f5400..cfa2cfc2 100644 --- a/src/assets/shared/variables.scss +++ b/src/assets/shared/variables.scss @@ -3,6 +3,8 @@ --newspack-listings--grey-dark: #1e1e1e; --newspack-listings--grey-medium: #757575; --newspack-listings--grey-light: #ddd; + --newspack-listings--border-dark: rgba( 0, 0, 0, 0.124 ); + --newspack-listings--border-light: rgba( 255, 255, 255, 0.124 ); } // Media queries. diff --git a/src/blocks/curated-list/block.json b/src/blocks/curated-list/block.json index 8167a2d7..fdab4d1f 100644 --- a/src/blocks/curated-list/block.json +++ b/src/blocks/curated-list/block.json @@ -81,10 +81,14 @@ "type": "string", "default": "" }, - "customTextColor": { + "backgroundColor": { "type": "string", "default": "" }, + "hasDarkBackground": { + "type": "boolean", + "default": false + }, "startup": { "type": "boolean", "default": true diff --git a/src/blocks/curated-list/edit.js b/src/blocks/curated-list/edit.js index adfeccdb..2cc54279 100644 --- a/src/blocks/curated-list/edit.js +++ b/src/blocks/curated-list/edit.js @@ -34,7 +34,7 @@ import { addQueryArgs } from '@wordpress/url'; import { Listing } from '../listing/listing'; import { SidebarQueryControls } from '../../components'; import { List, Query, Specific } from '../../svg'; -import { getCuratedListClasses, useDidMount } from '../../editor/utils'; +import { getContrastRatio, getCuratedListClasses, useDidMount } from '../../editor/utils'; /** * Debounced fetchPosts function outside of component scope. @@ -81,6 +81,7 @@ const CuratedListEditorComponent = ( { typeScale, imageScale, textColor, + backgroundColor, startup, queryMode, queryOptions, @@ -271,6 +272,21 @@ const CuratedListEditorComponent = ( { } }, [ selectedBlock ]); + /** + * Determine if the background color is dark or light. + */ + useEffect(() => { + if ( backgroundColor ) { + const contrastRatio = getContrastRatio( backgroundColor ); + + if ( contrastRatio < 5 ) { + return setAttributes( { hasDarkBackground: true } ); + } + } + + setAttributes( { hasDarkBackground: false } ); + }, [ backgroundColor ]); + /** * Render the results of the listing query. * @@ -556,6 +572,11 @@ const CuratedListEditorComponent = ( { onChange: value => setAttributes( { textColor: value } ), label: __( 'Text Color', 'newspack-listings' ), }, + { + value: backgroundColor, + onChange: value => setAttributes( { backgroundColor: value } ), + label: __( 'Background Color', 'newspack-listings' ), + }, ] } /> @@ -585,6 +606,7 @@ const CuratedListEditorComponent = ( {
diff --git a/src/blocks/curated-list/editor.scss b/src/blocks/curated-list/editor.scss index ee12979c..f077c91d 100644 --- a/src/blocks/curated-list/editor.scss +++ b/src/blocks/curated-list/editor.scss @@ -2,10 +2,11 @@ .newspack-listings { &__curated-list-editor { - border: 1px solid var( --newspack-listings--grey-dark ); border-radius: 2px; clear: both; - padding: 1em; + margin-left: -1rem; + margin-right: -1rem; + padding: 0 1rem; label, .newspack-listings__label { @@ -14,6 +15,23 @@ line-height: 1.5; margin-bottom: 8px; } + + .wp-block[data-type^='newspack-listings'] + + .wp-block[data-type^='newspack-listings'] + .newspack-listings__listing-editor, + .newspack-listings__listing-editor + .newspack-listings__listing-editor { + border-top: 1px solid var( --newspack-listings--border-dark ); + padding-top: 1rem; + } + + .has-dark-background { + .wp-block[data-type^='newspack-listings'] + + .wp-block[data-type^='newspack-listings'] + .newspack-listings__listing-editor, + .newspack-listings__listing-editor + .newspack-listings__listing-editor { + border-top-color: var( --newspack-listings--border-light ); + } + } } &__placeholder { diff --git a/src/blocks/curated-list/view.php b/src/blocks/curated-list/view.php index 52f7be3b..9d1d291a 100644 --- a/src/blocks/curated-list/view.php +++ b/src/blocks/curated-list/view.php @@ -61,11 +61,24 @@ function render_block( $attributes, $inner_content ) { $classes[] = 'media-position-' . $attributes['mediaPosition']; $classes[] = 'media-size-' . $attributes['imageScale']; } + if ( $attributes['backgroundColor'] ) { + if ( $attributes['hasDarkBackground'] ) { + $classes[] = 'has-dark-background'; + } + $classes[] = 'has-background-color'; + } $classes[] = 'type-scale-' . $attributes['typeScale']; - // Text color for listings. - $text_color = ! empty( $attributes['textColor'] ) ? 'color:' . $attributes['textColor'] : ''; + // Color styles for listings. + $styles = []; + + if ( ! empty( $attributes['textColor'] ) ) { + $styles[] = 'color:' . $attributes['textColor']; + } + if ( ! empty( $attributes['backgroundColor'] ) ) { + $styles[] = 'background-color:' . $attributes['backgroundColor']; + } // Extend wp_kses_post to allow jetpack/map required elements and attributes. $allowed_elements = wp_kses_allowed_html( 'post' ); @@ -184,7 +197,7 @@ function render_block( $attributes, $inner_content ) {
diff --git a/src/blocks/listing/editor.scss b/src/blocks/listing/editor.scss index 8cecce75..c37a796b 100644 --- a/src/blocks/listing/editor.scss +++ b/src/blocks/listing/editor.scss @@ -2,11 +2,6 @@ .newspack-listings { &__listing-editor { - border-radius: 2px; - box-shadow: inset 0 0 0 1px var( --newspack-listings--grey-dark ); - padding: 1em; - position: relative; - .components-spinner { display: block; float: none; diff --git a/src/components/sidebar-query-controls.js b/src/components/sidebar-query-controls.js index 4b6b312d..703f9712 100644 --- a/src/components/sidebar-query-controls.js +++ b/src/components/sidebar-query-controls.js @@ -188,7 +188,7 @@ class QueryControls extends Component { } // Enable listing type option only if there's more than one listing type in the list. - if ( 'any' === type ) { + if ( 'any' === type || ! type ) { sortOptions.push( { label: __( 'Listing Type', 'newspack-listings' ), value: 'type' } ); } diff --git a/src/editor/utils.js b/src/editor/utils.js index 7a8d3f25..75e281b4 100644 --- a/src/editor/utils.js +++ b/src/editor/utils.js @@ -40,6 +40,44 @@ export const isListing = ( listingType = null ) => { return false; }; +/** + * Convert hex color to RGB. + * From https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + * + * @param {string} hex Color in HEX format + * @return {array} RGB values, e.g. [red, green, blue] + */ +const hexToRGB = hex => + hex + .replace( /^#?([a-f\d])([a-f\d])([a-f\d])$/i, ( m, r, g, b ) => '#' + r + r + g + g + b + b ) + .substring( 1 ) + .match( /.{2}/g ) + .map( x => parseInt( x, 16 ) ); + +/** + * Get contrast ratio of the given backgroundColor compared to black. + * @param {string} backgroundColor Color HEX value to compare with black. + * @return {number} Contrast ratio vs. black. + */ +export const getContrastRatio = backgroundColor => { + const blackColor = '#000'; + const backgroundColorRGB = hexToRGB( backgroundColor ); + const blackRGB = hexToRGB( blackColor ); + + const l1 = + 0.2126 * Math.pow( backgroundColorRGB[ 0 ] / 255, 2.2 ) + + 0.7152 * Math.pow( backgroundColorRGB[ 1 ] / 255, 2.2 ) + + 0.0722 * Math.pow( backgroundColorRGB[ 2 ] / 255, 2.2 ); + const l2 = + 0.2126 * Math.pow( blackRGB[ 0 ] / 255, 2.2 ) + + 0.7152 * Math.pow( blackRGB[ 1 ] / 255, 2.2 ) + + 0.0722 * Math.pow( blackRGB[ 2 ] / 255, 2.2 ); + + return l1 > l2 + ? parseInt( ( l1 + 0.05 ) / ( l2 + 0.05 ) ) + : parseInt( ( l2 + 0.05 ) / ( l1 + 0.05 ) ); +}; + /** * Get array of class names for Curated List, based on attributes. * @@ -50,6 +88,8 @@ export const isListing = ( listingType = null ) => { */ export const getCuratedListClasses = ( className, attributes ) => { const { + backgroundColor, + hasDarkBackground, showNumbers, showMap, showSortUi, @@ -69,6 +109,12 @@ export const getCuratedListClasses = ( className, attributes ) => { classes.push( `media-position-${ mediaPosition }` ); classes.push( `media-size-${ imageScale }` ); } + if ( backgroundColor ) { + if ( hasDarkBackground ) { + classes.push( 'has-dark-background' ); + } + classes.push( 'has-background-color' ); + } classes.push( `type-scale-${ typeScale }` );