Title 1Title 2
Second block
This is a default paragraph.
Second block 2
This is a default paragraph.
First toggle 1
This is paragraph for Title 1
First toggle 2
This is paragraph for Title 2
Title 1Title 2
First toggle 1
This is paragraph for Title 1
First toggle 2
This is paragraph for Title 2
block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "wlc/toggle-content",
"title": "Toggle Content",
"category": "widgets",
"icon": "admin-links",
"description": "A block to display toggle content",
"keywords": [
"toggle", "content"
],
"attributes": {
"blockId": {
"type": "string"
},
"activeTab": {
"type": "number"
},
"switchType": {
"type": "string",
"default": "rounded"
},
"switcherWidth": {
"type": "number",
"default": 84
},
"switcherHeight": {
"type": "number",
"default": 42
},
"switcherPrimaryColor": {
"type": "string",
"default": "#4b8fff"
},
"switcherSecondaryColor": {
"type": "string",
"default": "#ffffff"
},
"toggleBackground": {
"type": "string",
"default": "#f8f8f8"
},
"toggleTextColor": {
"type": "string",
"default": "#6a72a5"
},
"toggleActiveTextColor": {
"type": "string",
"default": "#4b8fff"
},
"toggleActiveBackground": {
"type": "string",
"default": "#ffffff"
},
"toggleTitle1": {
"type": "string",
"default": "Title 1"
},
"toggleTitle2": {
"type": "string",
"default": "Title 2"
}
},
"version": "1.0.0",
"textdomain": "WLC",
"editorStyle": "file:./index.css",
"editorScript": "file:./index.js",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}
edit.js
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
InspectorControls,
InnerBlocks,
RichText,
} from '@wordpress/block-editor';
import {
PanelBody,
SelectControl,
RangeControl,
ColorPicker,
} from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';
const TEMPLATE = [
[ 'wlc/toggle-content-tab' ],
[ 'wlc/toggle-content-tab' ],
];
const uniqueIds = [];
const edit = ( { attributes, setAttributes, clientId } ) => {
const {
blockId,
activeTab,
switchType,
switcherWidth,
switcherHeight,
switcherPrimaryColor,
switcherSecondaryColor,
toggleBackground,
toggleTextColor,
toggleActiveTextColor,
toggleActiveBackground,
toggleTitle1,
toggleTitle2
} = attributes;
useEffect( () => {
if ( ( null === blockId || '' === blockId ) || uniqueIds.includes( blockId ) ) {
const newUniqueId = 'field-' + clientId.substr(2, 9).replace('-', '');
setAttributes( { blockId: newUniqueId } );
uniqueIds.push( newUniqueId );
} else {
uniqueIds.push( blockId );
}
}, [] );
const [ localActiveTab, setLocalActiveTab ] = useState( activeTab || 0 );
useEffect( () => {
setAttributes( { activeTab: localActiveTab } );
}, [ localActiveTab ] );
const blockProps = useBlockProps( {
style: {
'--switcher-primary-color': switcherPrimaryColor,
'--switcher-secondary-color': switcherSecondaryColor,
'--toggle-background': toggleBackground,
'--toggle-text-color': toggleTextColor,
'--toggle-active-text-color': toggleActiveTextColor,
'--toggle-active-background': toggleActiveBackground,
},
} );
const handleTabClick = ( tabIndex ) => {
if ( tabIndex !== activeTab ) {
setLocalActiveTab( tabIndex );
}
};
return (
<div { ...blockProps }>
<InspectorControls>
<PanelBody title={ __( 'Toggle Content Settings', 'WLC' ) }>
<SelectControl
label={ __( 'Switch Type', 'WLC' ) }
value={ switchType }
options={ [
{ label: __( 'Rounded', 'WLC' ), value: 'rounded' },
{ label: __( 'Rectangle', 'WLC' ), value: 'rectangle' },
{ label: __( 'Toggle', 'WLC' ), value: 'toggle' },
] }
onChange={ ( value ) => setAttributes( { switchType: value } ) }
/>
{ ( switchType !== 'toggle' ) ? (
<>
<RangeControl
label={ __( 'Switcher Width (px)', 'WLC' ) }
value={ switcherWidth }
onChange={ ( value ) => setAttributes( { switcherWidth: value } ) }
min={ 40 }
max={ 150 }
/>
<RangeControl
label={ __( 'Switcher Height (px)', 'WLC' ) }
value={ switcherHeight }
onChange={ ( value ) => setAttributes( { switcherHeight: value } ) }
min={ 20 }
max={ 150 }
/>
<div className="components-base-control">
<div className="components-base-control__field">
<label className="components-base-control__label">
{ __( 'Switcher Primary Color', 'WLC' ) }
</label>
<ColorPicker
color={ switcherPrimaryColor }
onChangeComplete={ ( value ) => setAttributes( { switcherPrimaryColor: value.hex } ) }
/>
</div>
</div>
<div className="components-base-control">
<div className="components-base-control__field">
<label className="components-base-control__label">
{ __( 'Switcher Secondary Color', 'WLC' ) }
</label>
<ColorPicker
color={ switcherSecondaryColor }
onChangeComplete={ ( value ) => setAttributes( { switcherSecondaryColor: value.hex } ) }
/>
</div>
</div>
</>
) : (
<>
<div className="components-base-control">
<div className="components-base-control__field">
<label className="components-base-control__label">
{ __( 'Toggle Background', 'WLC' ) }
</label>
<ColorPicker
color={ toggleBackground }
onChangeComplete={ ( value ) => setAttributes( { toggleBackground: value.hex } ) }
/>
</div>
</div>
<div className="components-base-control">
<div className="components-base-control__field">
<label className="components-base-control__label">
{ __( 'Toggle Text Color', 'WLC' ) }
</label>
<ColorPicker
color={ toggleTextColor }
onChangeComplete={ ( value ) => setAttributes( { toggleTextColor: value.hex } ) }
/>
</div>
</div>
<div className="components-base-control">
<div className="components-base-control__field">
<label className="components-base-control__label">
{ __( 'Toggle Active Background', 'WLC' ) }
</label>
<ColorPicker
color={ toggleActiveBackground }
onChangeComplete={ ( value ) => setAttributes( { toggleActiveBackground: value.hex } ) }
/>
</div>
</div>
<div className="components-base-control">
<div className="components-base-control__field">
<label className="components-base-control__label">
{ __( 'Toggle Active Text Color', 'WLC' ) }
</label>
<ColorPicker
color={ toggleActiveTextColor }
onChangeComplete={ ( value ) => setAttributes( { toggleActiveTextColor: value.hex } ) }
/>
</div>
</div>
</>
) }
</PanelBody>
</InspectorControls>
<div className="wlc-toggle-content">
<div className="wlc-toggle-content__switcher">
{ ( switchType !== 'toggle' ) ? (
<div className="toggle-switch switcher" data-type={ switchType }>
<RichText
tagName="span"
className={`span-item--first span-item ${localActiveTab === 0 ? ' active' : ''}`}
placeholder={ __( 'Enter title...', 'WLC' ) }
value={ toggleTitle1 }
onClick={ () => handleTabClick( 0 ) }
onChange={ ( value ) => setAttributes( { toggleTitle1: value } ) }
/>
<label
className="toggle-switch__label"
style={ { width: `${switcherWidth}px`, height: `${switcherHeight}px` } }
>
<input
type="checkbox"
className="toggle-switch__input"
onChange={ () => handleTabClick( localActiveTab === 0 ? 1 : 0 ) }
checked={ localActiveTab === 1 }
/>
<span className="toggle-switch__controller"></span>
<span className="toggle-switch__slider"></span>
</label>
<RichText
tagName="span"
className={`span-item--second span-item ${localActiveTab === 1 ? ' active' : ''}`}
placeholder={ __( 'Enter title...', 'WLC' ) }
value={ toggleTitle2 }
onClick={ () => handleTabClick( 1 ) }
onChange={ ( value ) => setAttributes( { toggleTitle2: value } ) }
/>
</div>
) : (
<div className="content-switch switcher" data-type={ switchType }>
<input
type="checkbox"
id={`switcher-${blockId}`}
className="content-switch__input"
onChange={ () => handleTabClick( localActiveTab === 0 ? 1 : 0 ) }
checked={ localActiveTab === 1 }
/>
<label htmlFor={`switcher-${blockId}`} className="content-switch__label">
<div className="content-switch__toggle"></div>
<div className="content-switch__names">
<RichText
tagName="span"
className={`content-switch__name--first content-switch__name ${localActiveTab === 0 ? ' active' : ''}`}
placeholder={ __( 'Enter title...', 'WLC' ) }
value={ toggleTitle1 }
onChange={ ( value ) => setAttributes( { toggleTitle1: value } ) }
/>
<RichText
tagName="span"
className={`content-switch__name--second content-switch__name ${localActiveTab === 1 ? ' active' : ''}`}
placeholder={ __( 'Enter title...', 'WLC' ) }
value={ toggleTitle2 }
onChange={ ( value ) => setAttributes( { toggleTitle2: value } ) }
/>
</div>
</label>
</div>
) }
</div>
<div className="wlc-toggle-content__inner" data-active-tab={ localActiveTab }>
<InnerBlocks
template={ TEMPLATE }
allowedBlocks={ [ 'wlc/toggle-content-tab' ] }
renderAppender={ false }
templateLock="all"
/>
</div>
</div>
</div>
);
}
export default edit;
editor.scss
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,
RichText,
} from '@wordpress/block-editor';
export default function save( { attributes } ) {
const {
blockId,
activeTab,
switchType,
switcherWidth,
switcherHeight,
switcherPrimaryColor,
switcherSecondaryColor,
toggleBackground,
toggleTextColor,
toggleActiveTextColor,
toggleActiveBackground,
toggleTitle1,
toggleTitle2
} = attributes;
const blockProps = useBlockProps.save( {
style: {
'--switcher-primary-color': switcherPrimaryColor,
'--switcher-secondary-color': switcherSecondaryColor,
'--toggle-background': toggleBackground,
'--toggle-text-color': toggleTextColor,
'--toggle-active-text-color': toggleActiveTextColor,
'--toggle-active-background': toggleActiveBackground,
},
} );
return (
<div { ...blockProps }>
<div className="wlc-toggle-content">
<div className="wlc-toggle-content__switcher">
{ ( switchType !== 'toggle' ) ? (
<div className="toggle-switch switcher" data-type={ switchType }>
<RichText.Content tagName="span" value={ toggleTitle1 } className={`span-item--first span-item ${activeTab === 0 ? 'active' : ''}`} />
<label
htmlFor={`toggle-switch-${blockId}`}
className="toggle-switch__label"
style={ { width: `${switcherWidth}px`, height: `${switcherHeight}px` } }
>
<input id={`toggle-switch-${blockId}`} type="checkbox" className="toggle-switch__input" checked={ activeTab === 1 } />
<span className="toggle-switch__controller"></span>
<span className="toggle-switch__slider"></span>
</label>
<RichText.Content tagName="span" value={ toggleTitle2 } className={`span-item--second span-item ${activeTab === 1 ? 'active' : ''}`} />
</div>
) : (
<div className="content-switch switcher" data-type={ switchType }>
<input type="checkbox" id={`content-switcher-${blockId}`} className="content-switch__input" checked={ activeTab === 1 } />
<label id={`content-switcher-${blockId}`} className="content-switch__label">
<div className="content-switch__toggle"></div>
<div className="content-switch__names">
<RichText.Content tagName="span" value={ toggleTitle1 } className={`content-switch__name--first content-switch__name ${activeTab === 0 ? 'active' : ''}`} />
<RichText.Content tagName="span" value={ toggleTitle2 } className={`content-switch__name--second content-switch__name ${activeTab === 1 ? 'active' : ''}`} />
</div>
</label>
</div>
) }
</div>
<div className="wlc-toggle-content__inner" data-active-tab={ activeTab }>
<InnerBlocks.Content />
</div>
</div>
</div>
);
}
style.scss
.wlc-toggle-content {
&__switcher {
text-align: center;
margin-bottom: 2rem;
.toggle-switch {
&[data-type="rounded"] {
.toggle-switch__controller {
border-radius: 1.375rem;
}
.toggle-switch__slider {
border-radius: 1.375rem;
}
}
.span-item {
cursor: pointer;
&.active {
font-weight: bold;
}
}
&__label {
margin: 0 0.625rem;
display: inline-block;
position: relative;
vertical-align: middle;
}
&__input {
display: none;
&:checked + .toggle-switch__controller {
left: calc(100% - 2.313rem);
}
}
&__controller {
z-index: 1;
top: 0.313rem;
bottom: 0.313rem;
left: 0.313rem;
width: 2rem;
position: absolute;
transform: translateX(0);
background-color: var(--switcher-secondary-color);
transition: 0.4s;
}
&__slider {
top: 0;
right: 0;
left: 0;
bottom: 0;
cursor: pointer;
background-color: var(--switcher-primary-color);
border: 0.188rem solid var(--switcher-primary-color);
transition: 0.4s;
position: absolute;
}
}
.content-switch {
width: 30%;
border-radius: 100px;
background: var(--toggle-background);
margin-right: auto;
margin-left: auto;
padding: 0.438rem;
&__input {
display: none;
&:checked + .content-switch__label {
.content-switch__toggle {
transform: translateX(100%);
}
}
}
&__label {
cursor: pointer;
width: 100%;
height: 3rem;
border-radius: 6.25rem;
background-color: var(--toggle-background);
display: block;
position: relative;
}
&__toggle {
z-index: 1;
width: 50%;
height: 100%;
box-shadow: 0 0.125rem 0.938rem rgba(0, 0, 0, 0.15);
border-radius: 6.25rem;
background-color: var(--toggle-active-background);;
transition: 0.4s;
position: absolute;
}
&__names {
width: 100%;
height: 100%;
font-size: 0.875rem;
align-items: center;
display: flex;
justify-content: space-around;
user-select: none;
position: absolute;
span {
color: var(--toggle-text-color);
z-index: 2;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
&.active {
color: var(--toggle-active-text-color);
font-weight: bold;
}
}
}
}
}
&__inner {
@for $i from 0 through 5 {
&[data-active-tab="#{$i}"] {
.wp-block-wlc-toggle-content-tab:nth-child(#{$i + 1}) {
display: block;
}
}
}
.wp-block-wlc-toggle-content-tab {
display: none;
}
}
}
view.js
document.addEventListener( 'DOMContentLoaded', function () {
const toggleContentBlocks = document.querySelectorAll( '.wlc-toggle-content' );
if ( ! toggleContentBlocks.length ) return;
toggleContentBlocks.forEach( block => {
const switcher = block.querySelector( '.wlc-toggle-content__switcher .switcher' );
const tab1 = switcher.querySelector( '.span-item--first, .content-switch__name--first' );
const tab2 = switcher.querySelector( '.span-item--second, .content-switch__name--second' );
const input = switcher.querySelector( 'input[type="checkbox"]' );
const innerContent = block.querySelector( '.wlc-toggle-content__inner' );
const setActiveTab = ( index ) => {
if ( index === 0 ) {
tab1.classList.add( 'active' );
tab2.classList.remove( 'active' );
input.checked = false;
innerContent.setAttribute( 'data-active-tab', '0' );
} else {
tab1.classList.remove( 'active' );
tab2.classList.add( 'active' );
input.checked = true;
innerContent.setAttribute( 'data-active-tab', '1' );
}
};
tab1.addEventListener( 'click', () => setActiveTab( 0 ) );
tab2.addEventListener( 'click', () => setActiveTab( 1 ) );
input.addEventListener( 'change', () => setActiveTab( input.checked ? 1 : 0) );
} );
} );