Skip to content

Commit

Permalink
Refactor to split component and use hoisted state
Browse files Browse the repository at this point in the history
  • Loading branch information
mattheu committed Oct 25, 2023
1 parent e7647ae commit 3a6454f
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 106 deletions.
118 changes: 12 additions & 106 deletions src/components/InnerBlockSlider/index.js
Original file line number Diff line number Diff line change
@@ -1,117 +1,23 @@
import PropTypes from 'prop-types';
import { useState, useRef, useEffect, useMemo } from 'react';
import { useState } from 'react';

import { createBlock } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';

import InnerBlocksDisplaySingle from './inner-block-single-display';
import Navigation from './navigation';
import InnerBlockSlider from './inner-block-slider.js';

/**
* InnerBlockSlider component.
* InnerBlockSlider container component.
*
* @param {object} props Component props.
* @param {string} props.parentBlockId Parent block clientId.
* @param {string} props.allowedBlock Allowed block type.
* @param {Array} props.template Initial block template.
* @param {number} props.slideLimit Maximum allowed slides.
* @param {number} props.externalCurrentItemIndex Override current index, if managing this externally.
* @param {Function} props.setExternalCurrentItemIndex Override set item Index
* @param {object} props Component props.
* @returns {React.ReactNode} Component.
*/
const InnerBlockSlider = ( {
parentBlockId,
allowedBlock,
template,
slideLimit,
externalCurrentItemIndex = null,
setExternalCurrentItemIndex = null,
} ) => {
const innerBlockTemplate = template || [ [ allowedBlock ] ];

const slideBlocks = useSelect(
( select ) =>
select( 'core/block-editor' ).getBlock( parentBlockId ).innerBlocks
);

const [ internalCurrentItemIndex, setInternalCurrentItemIndex ] = useState( 0 );

// Current Item Index is either the internally managed index, OR the externally managed state.
const currentItemIndex = useMemo( () => {
return ( externalCurrentItemIndex !== null ) ? externalCurrentItemIndex : internalCurrentItemIndex;
}, [ externalCurrentItemIndex, internalCurrentItemIndex ] );

// Update current item index. Either the internally managed or externally managed state.
const setCurrentItemIndex = useMemo( () => {
return ( setExternalCurrentItemIndex !== null ) ? setExternalCurrentItemIndex : setInternalCurrentItemIndex;
}, [ setExternalCurrentItemIndex, setInternalCurrentItemIndex ] );

// Track state in a ref, to allow us to determine if slides are added or removed.
const slideCount = useRef( slideBlocks.length );

const { insertBlock } = useDispatch( 'core/block-editor' );

/**
* Function to add a new slide to the group of slides.
*/
const addSlide = () => {
const created = createBlock( allowedBlock );
insertBlock( created, undefined, parentBlockId );
};

/**
* If a slide is added, switch to the new slide. If one is deleted, make sure we don't
* show a non-existent slide.
*/
useEffect( () => {
if ( slideBlocks.length > slideCount.current ) {
// Slide added
const newIndex = slideBlocks.length - 1;
setCurrentItemIndex( newIndex );
} else if ( slideBlocks.length < slideCount.current ) {
// Slide deleted
if ( currentItemIndex + 1 > slideBlocks.length ) {
const newIndex = slideBlocks.length - 1;
setCurrentItemIndex( newIndex );
}
}

// Update ref with new value..
slideCount.current = slideBlocks.length;
}, [ slideBlocks.length, currentItemIndex, slideCount, setCurrentItemIndex ] );
const InnerBlockSliderContainer = ( props ) => {
const [ currentItemIndex, setCurrentItemIndex ] = useState( 0 );

return (
<div className="inner-block-slider">
<InnerBlocksDisplaySingle
allowedBlocks={ [ allowedBlock ] }
className="slides"
currentItemIndex={ currentItemIndex }
parentBlockId={ parentBlockId }
template={ innerBlockTemplate }
/>

<Navigation
addSlide={ addSlide }
addSlideEnabled={ slideBlocks.length < slideLimit }
currentPage={ currentItemIndex + 1 }
nextEnabled={ currentItemIndex + 1 < slideBlocks.length }
prevEnabled={ currentItemIndex + 1 > 1 }
setCurrentPage={ ( page ) => setCurrentItemIndex( page - 1 ) }
totalPages={ slideBlocks.length }
/>
</div>
<InnerBlockSlider
{ ...props }
currentItemIndex={ currentItemIndex }
setCurrentItemIndex={ setCurrentItemIndex }
/>
);
};

InnerBlockSlider.defaultProps = {
slideLimit: 10,
template: null,
};

InnerBlockSlider.propTypes = {
parentBlockId: PropTypes.string.isRequired,
allowedBlock: PropTypes.string.isRequired,
template: PropTypes.array,
};

export default InnerBlockSlider;
export default InnerBlockSliderContainer;
111 changes: 111 additions & 0 deletions src/components/InnerBlockSlider/inner-block-slider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import PropTypes from 'prop-types';
import { useRef, useEffect } from 'react';

import { createBlock } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';

import InnerBlocksDisplaySingle from './inner-block-single-display';
import Navigation from './navigation';

/**
* InnerBlockSlider component.
*
* @param {object} props Component props.
* @param {string} props.parentBlockId Parent block clientId.
* @param {string} props.allowedBlock Allowed block type.
* @param {Array} props.template Initial block template.
* @param {number} props.slideLimit Maximum allowed slides.
* @param {number} props.currentItemIndex Override current index, if managing this externally.
* @param {Function} props.setCurrentItemIndex Override set item Index
* @param {Function} props.showNavigation Override display nav
* @returns {React.ReactNode} Component.
*/
const InnerBlockSlider = ( {
parentBlockId,
allowedBlock,
template,
slideLimit,
currentItemIndex,
setCurrentItemIndex,
showNavigation ,

Check failure on line 30 in src/components/InnerBlockSlider/inner-block-slider.js

View workflow job for this annotation

GitHub Actions / ESLint

src/components/InnerBlockSlider/inner-block-slider.js#L30

There should be no space before ',' (comma-spacing)
} ) => {
const innerBlockTemplate = template || [ [ allowedBlock ] ];

const slideBlocks = useSelect(
( select ) =>
select( 'core/block-editor' ).getBlock( parentBlockId ).innerBlocks
);

// Track state in a ref, to allow us to determine if slides are added or removed.
const slideCount = useRef( slideBlocks.length );

const { insertBlock } = useDispatch( 'core/block-editor' );

/**
* Function to add a new slide to the group of slides.
*/
const addSlide = () => {
const created = createBlock( allowedBlock );
insertBlock( created, undefined, parentBlockId );
};

/**
* If a slide is added, switch to the new slide. If one is deleted, make sure we don't
* show a non-existent slide.
*/
useEffect( () => {
if ( slideBlocks.length > slideCount.current ) {
// Slide added
const newIndex = slideBlocks.length - 1;
setCurrentItemIndex( newIndex );
} else if ( slideBlocks.length < slideCount.current ) {
// Slide deleted
if ( currentItemIndex + 1 > slideBlocks.length ) {
const newIndex = slideBlocks.length - 1;
setCurrentItemIndex( newIndex );
}
}

// Update ref with new value..
slideCount.current = slideBlocks.length;
}, [ slideBlocks.length, currentItemIndex, slideCount, setCurrentItemIndex ] );

return (
<div className="inner-block-slider">
<InnerBlocksDisplaySingle
allowedBlocks={ [ allowedBlock ] }
className="slides"
currentItemIndex={ currentItemIndex }
parentBlockId={ parentBlockId }
template={ innerBlockTemplate }
/>

<Navigation
addSlide={ addSlide }
addSlideEnabled={ slideBlocks.length < slideLimit }
currentPage={ currentItemIndex + 1 }
nextEnabled={ currentItemIndex + 1 < slideBlocks.length }
prevEnabled={ currentItemIndex + 1 > 1 }
setCurrentPage={ ( page ) => setCurrentItemIndex( page - 1 ) }
totalPages={ slideBlocks.length }
/>
</div>
);
};

InnerBlockSlider.defaultProps = {
slideLimit: 10,
template: null,
showNavigation: true,
};

InnerBlockSlider.propTypes = {
parentBlockId: PropTypes.string.isRequired,
allowedBlock: PropTypes.string.isRequired,
template: PropTypes.array,
showNavigation: PropTypes.bool,
currentItemIndex: PropTypes.number.isRequired,
setCurrentItemIndex: PropTypes.function.isRequired,

Check failure on line 108 in src/components/InnerBlockSlider/inner-block-slider.js

View workflow job for this annotation

GitHub Actions / ESLint

src/components/InnerBlockSlider/inner-block-slider.js#L108

Typo in declared prop type: function (react/no-typos)
};

export default InnerBlockSlider;

0 comments on commit 3a6454f

Please sign in to comment.