block background image settings

This commit is contained in:
2026-01-02 20:21:33 +00:00
parent 39458e4f11
commit 0576d05dfb
9 changed files with 248 additions and 37 deletions

View File

@@ -103,6 +103,51 @@ add_action('init', function () {
'style' => "{$slug}-style",
'script' => "{$slug}-script",
'view_script' => "{$slug}-view-script",
'attributes' => [
'container_width' => [
'type' => 'string',
'default' => '',
],
'alignment' => [
'type' => 'string',
],
'padding_top' => [
'type' => 'boolean',
'default' => true
],
'padding_bottom' => [
'type' => 'boolean',
'default' => true
],
'background_colour' => [
'type' => 'string',
'default' => '',
],
'background_hex' => [
'type' => 'string',
'default' => '',
],
'background_tint' => [
'type' => 'string',
'default' => '0',
],
'background_image' => [
'type' => 'integer',
'default' => 0,
],
'background_url' => [
'type' => 'string',
'default' => '',
],
'background_opacity' => [
'type' => 'integer',
'default' => 30
],
'background_position' => [
'type' => 'string',
'default' => '',
],
],
];
if(!property_exists($json, 'acf') && \Roots\view()->exists("blocks.{$slug}.render")) {

View File

@@ -14,6 +14,7 @@
@use "sections/footer";
// Components
@use "components/block";
@use "components/forms";
@use "components/button";
@use "components/card";

View File

@@ -0,0 +1,15 @@
.has-bg-image {
position: relative;
>.container {
position: relative;
z-index: 1;
}
.badegg-block-background {
position: absolute;
inset: 0;
}
}

View File

@@ -1,6 +1,10 @@
@use "app";
@use "global/variables/colours";
html :where(.wp-block) {
max-width: none;
}
.block-editor-block-list__layout .block-editor-block-list__block:not([contenteditable=true]) {
&:hover:after {
content: '';

View File

@@ -0,0 +1,46 @@
import { useSelect } from '@wordpress/data';
/**
* AttachmentImage
*
* This component is used to display an image from the media library.
* It's meant as a JS companion to the PHP function `wp_get_attachment_image()`.
*
* @link https://www.briancoords.com/getting-wordpress-media-library-images-in-javascript/
*
* @param {object} props
* @param {number} props.imageId The ID of the image to display.
* @param {string} props.size The size of the image to display. Defaults to 'full'.
* @returns {*} React JSX
*/
export default function AttachmentImage({ imageId, size = 'full' }) {
const { image } = useSelect((select) => ({
image: select('core').getEntityRecord('postType', 'attachment', imageId),
}));
const imageAttributes = () =>{
let attributes = {
src: image.source_url,
alt: image.alt_text,
className: `attachment-${size} size-${size}`,
width: image.media_details.width,
height: image.media_details.height,
};
if (image.media_details && image.media_details.sizes && image.media_details.sizes[size]) {
attributes.src = image.media_details.sizes[size].source_url;
attributes.width = image.media_details.sizes[size].width;
attributes.height = image.media_details.sizes[size].height;
}
return attributes;
};
return (
<>
{image && (
<img {...imageAttributes()} />
)}
</>
)
}

View File

@@ -0,0 +1,43 @@
import { useSelect } from '@wordpress/data';
/**
* BlockSettings
*
* Bundles the <InspectorControls> used for several blocks
* *
* @param {object} props
* @param {number} props.imageId The ID of the image to display.
* @param {string} props.size The size of the image to display. Defaults to 'full'.
* @returns {*} React JSX
*/
export default function BlockSettings({ imageId, size = 'full' }) {
const { image } = useSelect((select) => ({
image: select('core').getEntityRecord('postType', 'attachment', imageId),
}));
const imageAttributes = () =>{
let attributes = {
src: image.source_url,
alt: image.alt_text,
className: `attachment-${size} size-${size}`,
width: image.media_details.width,
height: image.media_details.height,
};
if (image.media_details && image.media_details.sizes && image.media_details.sizes[size]) {
attributes.src = image.media_details.sizes[size].source_url;
attributes.width = image.media_details.sizes[size].width;
attributes.height = image.media_details.sizes[size].height;
}
return attributes;
};
return (
<>
{image && (
<img {...imageAttributes()} />
)}
</>
)
}

View File

@@ -42,6 +42,9 @@ export function sectionClassNames(attributes, defaultClasses, extraClasses = [])
classNames.push(bg);
}
if('background_image'in attributes && attributes.background_image != '0')
classNames.push('has-bg-image');
// combine arrays
classNames = classNames.concat(defaultClasses).concat(extraClasses);

View File

@@ -8,35 +8,6 @@
"foreground": "#f58762"
},
"description": "A wrapper to contain core blocks",
"attributes": {
"container_width": {
"type": "string",
"default": ""
},
"alignment": {
"type": "string"
},
"padding_top": {
"type": "boolean",
"default": true
},
"padding_bottom": {
"type": "boolean",
"default": true
},
"background_colour": {
"type": "string",
"default": ""
},
"background_hex": {
"type": "string",
"default": ""
},
"background_tint": {
"type": "string",
"default": "0"
}
},
"supports": {
"html": true,
"color": {

View File

@@ -2,6 +2,7 @@
import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
import {
useBlockProps,
@@ -9,6 +10,8 @@ import {
InspectorControls,
BlockControls,
AlignmentToolbar,
MediaUpload,
MediaUploadCheck,
} from '@wordpress/block-editor';
import {
@@ -17,15 +20,20 @@ import {
PanelRow,
SelectControl,
ToggleControl,
ColorIndicator,
RangeControl,
ColorPalette,
Button,
} from '@wordpress/components';
// import { Image } from '@10up/block-components';
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import metadata from './block.json';
import allowedBlocks from '../../../json/core-block-whitelist.json';
import { containerClassNames, sectionClassNames } from '../../../js/lib/blocks/classNames';
import AttachmentImage from '../../../js/lib/blocks/AttachmentImage';
import apiFetch from '@wordpress/api-fetch';
registerBlockType(metadata.name, {
edit({ attributes, setAttributes }) {
@@ -40,6 +48,10 @@ registerBlockType(metadata.name, {
background_colour,
background_hex,
background_tint,
background_image,
background_url,
background_position,
background_opacity,
} = attributes;
const [
@@ -76,7 +88,7 @@ registerBlockType(metadata.name, {
/>
</BlockControls>
<InspectorControls>
<Panel>
<Panel className="badegg-components-panel">
<PanelBody title={ __("Settings", "badegg") }>
<SelectControl
label={ __("Container Width", "badegg") }
@@ -87,23 +99,31 @@ registerBlockType(metadata.name, {
__nextHasNoMarginBottom={ true }
/>
<ToggleControl
label={ __('Top Padding', 'badegg') }
label={ __('Top padding', 'badegg') }
checked={ padding_top }
onChange={(value) => setAttributes({ padding_top: value }) }
__nextHasNoMarginBottom
/>
<ToggleControl
label={ __('Bottom Padding', 'badegg') }
label={ __('Bottom padding', 'badegg') }
checked={ padding_bottom }
onChange={(value) => setAttributes({ padding_bottom: value }) }
__nextHasNoMarginBottom
/>
</PanelBody>
<PanelBody title={ __("Background Colour", "badegg") }>
<PanelBody
title={ __("Background", "badegg") }
initialOpen={ false }
>
<p style={{ textTransform: 'uppercase', fontSize: '11px' }} className="components-truncate components-text components-input-control__label">
{ __('Colour', 'badegg') }
</p>
<ColorPalette
colors={ configOptions.colours }
value={ background_hex }
clearable={ false }
disableCustomColors={ true }
style={{ marginBottom: '16px' }}
onChange={ ( value ) => {
let slug, hex, selected = '';
@@ -127,9 +147,9 @@ registerBlockType(metadata.name, {
} }
/>
{ 'background_colour' in attributes && attributes.background_colour ? (
{ 'background_colour' in attributes && attributes.background_colour && ![0, '0', 'white', 'black'].includes(attributes.background_colour) ? (
<SelectControl
label={ __("Tint", "badegg") }
label={ __("Background Tint", "badegg") }
value={ background_tint }
options={ configOptions.tints }
onChange={ (value) => setAttributes({ background_tint: value }) }
@@ -138,6 +158,57 @@ registerBlockType(metadata.name, {
/>
) : null }
{ background_image != 0 && (
<>
<AttachmentImage
imageId={ background_image }
size="thumbnail"
/>
<RangeControl
__next40pxDefaultSize
__nextHasNoMarginBottom
label={ __("Opacity", "badegg") }
value={ background_opacity }
onChange={ ( value ) => setAttributes({ background_opacity: value }) }
min={ 5 }
max={ 100 }
/>
</>
)}
<PanelRow>
<MediaUploadCheck>
<MediaUpload
onSelect={ (media) => {
setAttributes({
background_image: media.id,
background_url: media.url,
});
}}
allowedTypes={ ['image'] }
value={ background_image }
render={ ({ open }) => (
<Button
onClick={ open }
variant="primary"
>
{ background_image ? __("Replace image", "badegg") : __("Choose image", "badegg") }
</Button>
)}
/>
</MediaUploadCheck>
{ background_image != 0 && (
<Button
onClick={ () => setAttributes({ background_image: 0 }) }
isDestructive
variant="secondary"
>
{ __("Remove image", "badegg") }
</Button>
)}
</PanelRow>
</PanelBody>
</Panel>
</InspectorControls>
@@ -154,6 +225,18 @@ registerBlockType(metadata.name, {
}
/>
</div>
{ attributes.background_image != 0 && (
<div
className="badegg-block-background"
style={{
backgroundImage: `url(${background_url})`,
backgroundPosition: background_position,
opacity: Number(background_opacity) * 0.01,
}}
/>
) }
</div>
);
},