Skip to content

Commit

Permalink
Use a controlled inner blocks for reusable blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Dec 24, 2020
1 parent 97f0a48 commit d989f1d
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 242 deletions.
46 changes: 0 additions & 46 deletions packages/block-library/src/block/edit-panel/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,4 @@
background-color: $white;
box-shadow: 0 0 0 $border-width $gray-900;
outline: 1px solid transparent; // Shown for Windows 10 High Contrast mode.

.reusable-block-edit-panel__info {
margin-right: auto;
}

.reusable-block-edit-panel__label {
margin-right: $grid-unit-10;
white-space: nowrap;
font-family: $default-font;
font-size: $default-font-size;
}

.reusable-block-edit-panel__title {
flex: 1 1 100%;
}

.components-button.reusable-block-edit-panel__button {
// Prevent button shrinking in IE11 when other items have a 100% flex basis.
// This should be safe to apply in all browsers because we don't want these
// buttons to shrink anyway.
flex-shrink: 0;
}

@include break-large() {
flex-wrap: nowrap;

.reusable-block-edit-panel__title {
margin: 0;
}

.components-button.reusable-block-edit-panel__button {
margin: 0 0 0 $grid-unit-10;
}
}
}

.reusable-block-edit-panel__title[type="text"] {
@include input-control;
}

.is-navigate-mode .is-selected .reusable-block-edit-panel {
box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color);

.is-dark-theme & {
box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color);
}
}
108 changes: 8 additions & 100 deletions packages/block-library/src/block/edit-panel/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { useInstanceId, usePrevious } from '@wordpress/compose';
import { useEffect, useRef } from '@wordpress/element';
import { TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/** @typedef {import('@wordpress/element').WPComponent} WPComponent */
Expand All @@ -13,21 +11,9 @@ import { __ } from '@wordpress/i18n';
*
* @typedef WPReusableBlockEditPanelProps
*
* @property {boolean} isEditDisabled Is editing the reusable
* block disabled.
* @property {boolean} isEditing Is the reusable block
* being edited.
* @property {boolean} isSaving Is the reusable block
* being saved.
* @property {()=>void} onCancel Callback to run when
* editing is canceled.
* @property {(newTitle:string)=>void} onChangeTitle Callback to run when the
* title input value is
* changed.
* @property {()=>void} onEdit Callback to run when
* editing begins.
* @property {()=>void} onSave Callback to run when
* saving.
* @property {string} title Title of the reusable
* block.
*/
Expand All @@ -39,91 +25,13 @@ import { __ } from '@wordpress/i18n';
*
* @return {WPComponent} The panel.
*/
export default function ReusableBlockEditPanel( {
isEditDisabled,
isEditing,
isSaving,
onChangeTitle,
onEdit,
onSave,
title,
} ) {
const instanceId = useInstanceId( ReusableBlockEditPanel );
const titleField = useRef();
const editButton = useRef();
const wasEditing = usePrevious( isEditing );
const wasSaving = usePrevious( isSaving );

// Select the title input when the form opens.
useEffect( () => {
if ( ! wasEditing && isEditing ) {
titleField.current.select();
}
}, [ isEditing ] );

// Move focus back to the Edit button after pressing the Escape key or Save.
useEffect( () => {
if ( ( wasEditing || wasSaving ) && ! isEditing && ! isSaving ) {
editButton.current.focus();
}
}, [ isEditing, isSaving ] );

function handleFormSubmit( event ) {
event.preventDefault();
onSave();
}

function handleTitleChange( event ) {
onChangeTitle( event.target.value );
}

export default function ReusableBlockEditPanel( { onChange, title } ) {
return (
<>
{ ! isEditing && ! isSaving && (
<div className="reusable-block-edit-panel">
<b className="reusable-block-edit-panel__info">{ title }</b>
<Button
ref={ editButton }
isSecondary
className="reusable-block-edit-panel__button"
disabled={ isEditDisabled }
onClick={ onEdit }
>
{ __( 'Edit' ) }
</Button>
</div>
) }
{ ( isEditing || isSaving ) && (
<form
className="reusable-block-edit-panel"
onSubmit={ handleFormSubmit }
>
<label
htmlFor={ `reusable-block-edit-panel__title-${ instanceId }` }
className="reusable-block-edit-panel__label"
>
{ __( 'Name:' ) }
</label>
<input
ref={ titleField }
type="text"
disabled={ isSaving }
className="reusable-block-edit-panel__title"
value={ title }
onChange={ handleTitleChange }
id={ `reusable-block-edit-panel__title-${ instanceId }` }
/>
<Button
type="submit"
isSecondary
isBusy={ isSaving }
disabled={ ! title || isSaving }
className="reusable-block-edit-panel__button"
>
{ __( 'Save' ) }
</Button>
</form>
) }
</>
<TextControl
className="reusable-block-edit-panel"
label={ __( 'Name' ) }
value={ title }
onChange={ onChange }
/>
);
}
137 changes: 41 additions & 96 deletions packages/block-library/src/block/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEntityBlockEditor } from '@wordpress/core-data';
import { useCallback } from '@wordpress/element';
import {
useEntityBlockEditor,
useEntityProp,
store as coreStore,
} from '@wordpress/core-data';
import {
Placeholder,
Spinner,
Disabled,
ToolbarGroup,
ToolbarButton,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import {
BlockEditorProvider,
WritingFlow,
BlockList,
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
InnerBlocks,
BlockControls,
useBlockProps,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { store as noticesStore } from '@wordpress/notices';
import { store as reusableBlocksStore } from '@wordpress/reusable-blocks';

/**
Expand All @@ -34,69 +35,49 @@ export default function ReusableBlockEdit( {
} ) {
const recordArgs = [ 'postType', 'wp_block', ref ];

const {
reusableBlock,
hasResolved,
isEditing,
isSaving,
canUserUpdate,
settings,
} = useSelect(
const { reusableBlock, hasResolved, hasInnerBlockSelected } = useSelect(
( select ) => ( {
reusableBlock: select( 'core' ).getEditedEntityRecord(
reusableBlock: select( coreStore ).getEditedEntityRecord(
...recordArgs
),
hasResolved: select( 'core' ).hasFinishedResolution(
hasResolved: select( coreStore ).hasFinishedResolution(
'getEditedEntityRecord',
recordArgs
),
isSaving: select( 'core' ).isSavingEntityRecord( ...recordArgs ),
canUserUpdate: select( 'core' ).canUser( 'update', 'blocks', ref ),
isEditing: select(
reusableBlocksStore
).__experimentalIsEditingReusableBlock( clientId ),
settings: select( 'core/block-editor' ).getSettings(),
hasInnerBlockSelected: select(
blockEditorStore
).hasSelectedInnerBlock( clientId, true ),
} ),
[ ref, clientId ]
);

const { clearSelectedBlock } = useDispatch( 'core/block-editor' );
const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' );
const { __experimentalSetEditingReusableBlock } = useDispatch(
reusableBlocksStore
);
const setIsEditing = useCallback(
( value ) => {
__experimentalSetEditingReusableBlock( clientId, value );
},
[ clientId ]
);

const {
__experimentalConvertBlockToStatic: convertBlockToStatic,
} = useDispatch( reusableBlocksStore );

const { createSuccessNotice, createErrorNotice } = useDispatch(
noticesStore
);
const save = useCallback( async function () {
try {
await saveEditedEntityRecord( ...recordArgs );
createSuccessNotice( __( 'Block updated.' ), {
type: 'snackbar',
} );
} catch ( error ) {
createErrorNotice( error.message, {
type: 'snackbar',
} );
}
}, recordArgs );

const [ blocks, onInput, onChange ] = useEntityBlockEditor(
'postType',
'wp_block',
{ id: ref }
);
const [ title, setTitle ] = useEntityProp(
'postType',
'wp_block',
'title',
ref
);

const innerBlocksProps = useInnerBlocksProps(
{},
{
value: blocks,
onInput,
onChange,
renderAppender: blocks?.length
? undefined
: InnerBlocks.ButtonBlockAppender,
}
);

const blockProps = useBlockProps();

Expand All @@ -120,34 +101,6 @@ export default function ReusableBlockEdit( {
);
}

/**
* Clear the selected block when focus moves to the reusable block list.
* These blocks are in different stores and only one block should be
* selected at a time.
*/
function onFocus() {
clearSelectedBlock();
}

let element = (
<BlockEditorProvider
value={ blocks }
onInput={ onInput }
onChange={ onChange }
settings={ settings }
>
<div className="block-editor-block-list__block" onFocus={ onFocus }>
<WritingFlow>
<BlockList />
</WritingFlow>
</div>
</BlockEditorProvider>
);

if ( ! isEditing ) {
element = <Disabled>{ element }</Disabled>;
}

return (
<div { ...blockProps }>
<BlockControls>
Expand All @@ -161,23 +114,15 @@ export default function ReusableBlockEdit( {
</BlockControls>

<div className="block-library-block__reusable-block-container">
{ ( isSelected || isEditing ) && (
<ReusableBlockEditPanel
isEditing={ isEditing }
title={ reusableBlock.title }
isSaving={ isSaving }
isEditDisabled={ ! canUserUpdate }
onEdit={ () => setIsEditing( true ) }
onChangeTitle={ ( title ) =>
editEntityRecord( ...recordArgs, { title } )
}
onSave={ () => {
save();
setIsEditing( false );
} }
/>
) }
{ element }
{ isSelected ||
( hasInnerBlockSelected && (
<ReusableBlockEditPanel
title={ title }
onChange={ setTitle }
/>
) ) }

{ <div { ...innerBlocksProps } /> }
</div>
</div>
);
Expand Down

0 comments on commit d989f1d

Please sign in to comment.