Heading 1
Something can be here
Heading 1
Something can be here
Heading 2
Something can be here
Heading 3
Something can be here
block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "wlc/featured-list",
"title": "Featured List",
"category": "widgets",
"icon": "editor-ul",
"description": "A block to display featured list",
"keywords": [
"featured", "list"
],
"allowedBlocks": [
"wlc/featured-list-item"
],
"attributes": {
"layout": {
"type": "string",
"default": "columns"
},
"itemsPerRow": {
"type": "string",
"default": 2
},
"connectorsWidth": {
"type": "string",
"default": 2
},
"gap": {
"type": "string",
"default": "20px"
},
"iconWrapperWidth": {
"type": "string",
"default": 100
},
"iconWrapperShape": {
"type": "string",
"default": 10
},
"iconWrapperPadding": {
"type": "string",
"default": 20
}
},
"version": "1.0.0",
"textdomain": "WLC",
"editorStyle": "file:./index.css",
"editorScript": "file:./index.js",
"style": "file:./style-index.css"
}
edit.js
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
useInnerBlocksProps,
InspectorControls,
BlockControls,
Inserter,
} from '@wordpress/block-editor';
import {
PanelBody,
Toolbar,
Button,
SelectControl,
__experimentalUnitControl as UnitControl,
RangeControl,
} from '@wordpress/components';
import { useEffect } from '@wordpress/element';
import { dispatch, select } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
const edit = ( { attributes, setAttributes, clientId } ) => {
const { layout, itemsPerRow, connectorsWidth, gap, iconWrapperWidth, iconWrapperShape, iconWrapperPadding } = attributes;
useEffect( () => {
const { replaceInnerBlocks } = dispatch( 'core/block-editor' );
const { getBlocks } = select( 'core/block-editor' );
if ( ! getBlocks( clientId ).length) {
const innerBlocks = [
createBlock( 'wlc/featured-list-item', { iconWrapperWidth, iconWrapperShape, iconWrapperPadding } ),
createBlock( 'wlc/featured-list-item', { iconWrapperWidth, iconWrapperShape, iconWrapperPadding } ),
createBlock( 'wlc/featured-list-item', { iconWrapperWidth, iconWrapperShape, iconWrapperPadding } ),
];
replaceInnerBlocks( clientId, innerBlocks, false );
}
}, [ clientId ] );
const blockProps = useBlockProps( {
style: {
'--gap': gap,
'--icon-width': `${iconWrapperWidth}px`,
'--icon-shape': `${iconWrapperShape}px`,
'--icon-padding': `${iconWrapperPadding}px`,
...(layout === 'connectors' && { '--connectors-width': `${connectorsWidth}px` }),
},
} );
const innerBlocksProps = useInnerBlocksProps(
blockProps,
{
allowedBlocks: [ 'wlc/featured-list-item' ]
}
);
function Appender( { rootClientId } ) {
return (
<Inserter
rootClientId={ rootClientId}
renderToggle={ ( { onToggle, disabled } ) => (
<Button
className="featured-list-appender"
onClick={ onToggle }
disabled={ disabled }
label={ __( 'Add item', 'WLC' ) }
icon="plus"
/>
) }
isAppender
/>
);
}
const wrapperClassName = `wlc-featured-list__wrapper layout-${layout}` + (layout === 'row' ? ` items-per-row-${itemsPerRow}` : '');
return (
<div { ...blockProps }>
<InspectorControls>
<PanelBody title={ __( 'Featured List Settings', 'WLC' ) }>
<SelectControl
label={ __( 'Section Layout', 'WLC' ) }
value={ layout }
options={ [
{ label: __( 'Columns', 'WLC' ), value: 'columns' },
{ label: __( 'Row', 'WLC' ), value: 'row' },
{ label: __( 'Connectors', 'WLC' ), value: 'connectors' },
] }
onChange={ ( value ) => setAttributes( { layout: value } ) }
/>
{ layout == 'row' && (
<RangeControl
label={ __( 'Items Per Row', 'WLC' ) }
value={ itemsPerRow }
onChange={ ( value ) => setAttributes( { itemsPerRow: value } ) }
min={ 2 }
max={ 6 }
/>
) }
{ layout == 'connectors' && (
<RangeControl
label={ __( 'Connectors Width (px)', 'WLC' ) }
value={ connectorsWidth }
onChange={ ( value ) => setAttributes( { connectorsWidth: value } ) }
min={ 1 }
max={ 15 }
/>
) }
<UnitControl
label={ __( 'Gap Between Items (px)', 'WLC' ) }
value={ gap }
onChange={ ( value ) => setAttributes( { gap: value } ) }
min={ 0 }
max={ 50 }
/>
<RangeControl
label={ __( 'Icons Wrapper Width (px)', 'WLC' ) }
value={ iconWrapperWidth }
onChange={ ( value ) => {
setAttributes( { iconWrapperWidth: value } );
// Update all child blocks with the new iconWrapperWidth
const blocks = select( 'core/block-editor' ).getBlocks( clientId );
blocks.forEach( block => {
dispatch( 'core/block-editor' ).updateBlockAttributes( block.clientId, { iconWrapperWidth: value } );
} );
} }
min={ 0 }
max={ 200 }
/>
<RangeControl
label={ __( 'Icons Wrapper Radius (px)', 'WLC' ) }
value={ iconWrapperShape }
onChange={ ( value ) => {
setAttributes( { iconWrapperShape: value } );
// Update all child blocks with the new iconWrapperShape
const blocks = select( 'core/block-editor' ).getBlocks( clientId );
blocks.forEach( block => {
dispatch( 'core/block-editor' ).updateBlockAttributes( block.clientId, { iconWrapperShape: value } );
} );
} }
min={ 0 }
max={ 50 }
/>
<RangeControl
label={ __( 'Icons Wrapper Padding (px)', 'WLC' ) }
value={ iconWrapperPadding }
onChange={ ( value ) => {
setAttributes( { iconWrapperPadding: value } );
// Update all child blocks with the new iconWrapperPadding
const blocks = select( 'core/block-editor' ).getBlocks( clientId );
blocks.forEach( block => {
dispatch( 'core/block-editor' ).updateBlockAttributes( block.clientId, { iconWrapperPadding: value } );
} );
} }
min={ 0 }
max={ 60 }
/>
</PanelBody>
</InspectorControls>
<BlockControls>
<Toolbar label={ __( 'Options', 'WLC' ) }>
<Appender rootClientId={ clientId } />
</Toolbar>
</BlockControls>
<div { ...innerBlocksProps } className={ wrapperClassName }>
{ innerBlocksProps.children }
<Appender rootClientId={ clientId } />
</div>
</div>
);
}
export default edit;
editor.scss
.wlc-featured-list__wrapper .featured-list-appender {
width: 100%;
border: 1px solid var(--wp-components-color-foreground, #1e1e1e);
grid-column: 1 / -1;
}
index.js
/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import './editor.scss';
import './style.scss';
import edit from './edit';
import save from './save';
import metadata from './block.json';
registerBlockType( metadata.name, {
edit,
save,
} );
save.js
/**
* WordPress dependencies
*/
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const { layout, itemsPerRow, connectorsWidth, gap, iconWrapperWidth, iconWrapperShape, iconWrapperPadding } = attributes;
const blockProps = useBlockProps.save( {
style: {
'--gap': gap,
'--icon-width': `${iconWrapperWidth}px`,
'--icon-shape': `${iconWrapperShape}px`,
'--icon-padding': `${iconWrapperPadding}px`,
...(layout === 'connectors' && { '--connectors-width': `${connectorsWidth}px` }),
},
} );
const wrapperClassName = `wlc-featured-list__wrapper layout-${layout}` + (layout === 'row' ? ` items-per-row-${itemsPerRow}` : '');
return (
<div { ...blockProps }>
<div className={ wrapperClassName }>
<InnerBlocks.Content />
</div>
</div>
);
}
style.scss
.wlc-featured-list__wrapper {
gap: var(--gap);
&.layout-columns {
display: flex;
flex-direction: column;
}
&.layout-row {
display: grid;
@for $i from 2 through 6 {
&.items-per-row-#{$i} {
grid-template-columns: repeat(#{$i}, 1fr);
}
}
}
&.layout-connectors {
display: flex;
flex-direction: column;
.wlc-featured-list-item {
&:not(:last-of-type) {
&::after {
content: "";
bottom: 0;
left: calc(calc(var(--icon-width) / 2) - calc(var(--connectors-width) / 2));
width: var(--connectors-width);
height: var(--gap);
background: var(--connectors-color);
transform: translateY(100%);
position: absolute;
}
}
}
}
}