Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Buttons block native edit component to use hooks. #25636

Merged
merged 2 commits into from
Oct 13, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 58 additions & 69 deletions packages/block-library/src/buttons/edit.native.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
/**
* External dependencies
*/
import { debounce } from 'lodash';
import { View } from 'react-native';

/**
* WordPress dependencies
*/
import {
InnerBlocks,
__experimentalAlignmentHookSettingsProvider as AlignmentHookSettingsProvider,
InnerBlocks,
} from '@wordpress/block-editor';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose, useResizeObserver } from '@wordpress/compose';
import { createBlock } from '@wordpress/blocks';
import { useResizeObserver } from '@wordpress/compose';
import { useDispatch, useSelect } from '@wordpress/data';
import { useState, useEffect, useRef } from '@wordpress/element';
import { debounce } from 'lodash';

/**
* Internal dependencies
Expand All @@ -24,20 +25,47 @@ import styles from './editor.scss';
const ALLOWED_BLOCKS = [ buttonBlockName ];
const BUTTONS_TEMPLATE = [ [ 'core/button' ] ];

function ButtonsEdit( {
export default function ButtonsEdit( {
attributes: { align },
clientId,
isSelected,
attributes,
onDelete,
onAddNextButton,
shouldDelete,
isInnerButtonSelected,
} ) {
const { align } = attributes;
const [ resizeObserver, sizes ] = useResizeObserver();
const [ maxWidth, setMaxWidth ] = useState( 0 );
const shouldRenderFooterAppender = isSelected || isInnerButtonSelected;
const { marginLeft: spacing } = styles.spacing;

const { getBlockOrder, isInnerButtonSelected, shouldDelete } = useSelect(
( select ) => {
const {
getBlockCount,
getBlockOrder: _getBlockOrder,
ZebulanStanphill marked this conversation as resolved.
Show resolved Hide resolved
getBlockParents,
getSelectedBlockClientId,
} = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
const selectedBlockParents = getBlockParents(
selectedBlockClientId,
true
);

return {
getBlockOrder: _getBlockOrder,
isInnerButtonSelected: selectedBlockParents[ 0 ] === clientId,
// The purpose of `shouldDelete` check is giving the ability to
// pass to mobile toolbar function called `onDelete` which removes
// the whole `Buttons` container along with the last inner button
// when there is exactly one button.
shouldDelete: getBlockCount( clientId ) === 1,
};
},
[ clientId ]
);

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

useEffect( () => {
const margins = 2 * styles.parent.marginRight;
const { width } = sizes || {};
Expand All @@ -46,13 +74,26 @@ function ButtonsEdit( {
}
}, [ sizes ] );

const debounceAddNextButton = debounce( onAddNextButton, 200 );
const onAddNextButton = debounce( ( selectedId ) => {
const order = getBlockOrder( clientId );
const selectedButtonIndex = order.findIndex(
( i ) => i === selectedId
);

const index =
selectedButtonIndex === -1 ? order.length + 1 : selectedButtonIndex;

const insertedBlock = createBlock( 'core/button' );

insertBlock( insertedBlock, index, clientId );
selectBlock( insertedBlock.clientId );
}, 200 );

const renderFooterAppender = useRef( () => (
<View style={ styles.appenderContainer }>
<InnerBlocks.ButtonBlockAppender
isFloating={ true }
onAddBlock={ debounceAddNextButton }
onAddBlock={ onAddNextButton }
/>
</View>
) );
Expand All @@ -73,66 +114,14 @@ function ButtonsEdit( {
}
orientation="horizontal"
horizontalAlignment={ align }
onDeleteBlock={ shouldDelete ? onDelete : undefined }
onAddBlock={ debounceAddNextButton }
onDeleteBlock={
shouldDelete ? () => removeBlock( clientId ) : undefined
}
onAddBlock={ onAddNextButton }
parentWidth={ maxWidth }
marginHorizontal={ spacing }
marginVertical={ spacing }
/>
</AlignmentHookSettingsProvider>
);
}

export default compose(
withSelect( ( select, { clientId } ) => {
const {
getBlockCount,
getBlockParents,
getSelectedBlockClientId,
} = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
const selectedBlockParents = getBlockParents(
selectedBlockClientId,
true
);

return {
// The purpose of `shouldDelete` check is giving the ability to pass to
// mobile toolbar function called `onDelete` which removes the whole
// `Buttons` container along with the last inner button when
// there is exactly one button.
shouldDelete: getBlockCount( clientId ) === 1,
isInnerButtonSelected: selectedBlockParents[ 0 ] === clientId,
};
} ),
withDispatch( ( dispatch, { clientId }, registry ) => {
const { selectBlock, removeBlock, insertBlock } = dispatch(
'core/block-editor'
);
const { getBlockOrder } = registry.select( 'core/block-editor' );

return {
// The purpose of `onAddNextButton` is giving the ability to automatically
// adding `Button` inside `Buttons` block on the appender press event.
onAddNextButton: ( selectedId ) => {
const order = getBlockOrder( clientId );
const selectedButtonIndex = order.findIndex(
( i ) => i === selectedId
);

const index =
selectedButtonIndex === -1
? order.length + 1
: selectedButtonIndex;

const insertedBlock = createBlock( 'core/button' );

insertBlock( insertedBlock, index, clientId );
selectBlock( insertedBlock.clientId );
},
onDelete: () => {
removeBlock( clientId );
},
};
} )
)( ButtonsEdit );