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

Add edit button to template parts, reusable blocks and navigation blocks #45465

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
69 changes: 54 additions & 15 deletions packages/block-editor/src/components/block-edit/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ import { pick } from 'lodash';
/**
* WordPress dependencies
*/
import { withFilters } from '@wordpress/components';
import { ToolbarButton, withFilters } from '@wordpress/components';
import {
getBlockDefaultClassName,
hasBlockSupport,
getBlockType,
} from '@wordpress/blocks';
import { useContext, useMemo } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import BlockContext from '../block-context';
import { store as blockEditorStore } from '../../store';
import BlockControls from '../block-controls';

/**
* Default value used for blocks which do not define their own context needs,
Expand All @@ -31,9 +35,21 @@ import BlockContext from '../block-context';
const DEFAULT_BLOCK_CONTEXT = {};

export const Edit = ( props ) => {
const { attributes = {}, name } = props;
const { attributes = {}, name, clientId } = props;
const blockType = getBlockType( name );
const blockContext = useContext( BlockContext );
const isEditingBlock = useSelect(
( select ) => {
return select( blockEditorStore ).__unstableIsEditingBlock(
clientId
);
},
[ clientId ]
);
const { __unstableStartEditingBlocks: startEditingBlocks } =
useDispatch( blockEditorStore );
const showEditBlockButton =
! isEditingBlock && blockType.supports?.__experimentalBlockOverlay;

// Assign context values using the block type's declared context needs.
const context = useMemo( () => {
Expand All @@ -50,23 +66,46 @@ export const Edit = ( props ) => {
// with which a block is displayed. If `blockType` is valid, assign
// them preferentially as the render value for the block.
const Component = blockType.edit || blockType.save;

let blockEdit;
if ( blockType.apiVersion > 1 ) {
return <Component { ...props } context={ context } />;
}
blockEdit = <Component { ...props } context={ context } />;
} else {
// Generate a class name for the block's editable form.
const generatedClassName = hasBlockSupport(
blockType,
'className',
true
)
? getBlockDefaultClassName( name )
: null;
const className = classnames(
generatedClassName,
attributes.className,
props.className
);

// Generate a class name for the block's editable form.
const generatedClassName = hasBlockSupport( blockType, 'className', true )
? getBlockDefaultClassName( name )
: null;
const className = classnames(
generatedClassName,
attributes.className,
props.className
);
blockEdit = (
<Component
{ ...props }
context={ context }
className={ className }
/>
);
}

return (
<Component { ...props } context={ context } className={ className } />
<>
{ blockEdit }
{ showEditBlockButton && (
<BlockControls group="block">
<ToolbarButton
onClick={ () => startEditingBlocks( clientId ) }
>
{ __( 'Edit' ) }
</ToolbarButton>
</BlockControls>
) }
</>
);
};

Expand Down
7 changes: 7 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1726,3 +1726,10 @@ export function __unstableSetTemporarilyEditingAsBlocks(
temporarilyEditingAsBlocks,
};
}

export function __unstableStartEditingBlocks( clientId ) {
return {
type: 'START_EDITING_BLOCKS',
clientId,
};
}
129 changes: 106 additions & 23 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1806,26 +1806,109 @@ export function temporarilyEditingAsBlocks( state = '', action ) {
return state;
}

export default combineReducers( {
blocks,
isTyping,
draggedBlocks,
selection,
isMultiSelecting,
isSelectionEnabled,
initialPosition,
blocksMode,
blockListSettings,
insertionPoint,
template,
settings,
preferences,
lastBlockAttributesChange,
editorMode,
hasBlockMovingClientId,
automaticChangeStatus,
highlightedBlock,
lastBlockInserted,
temporarilyEditingAsBlocks,
blockVisibility,
} );
export const withEditedBlock = ( reducer ) => ( state, action ) => {
const newState = reducer( state, action );

if ( state === undefined ) {
return {
...newState,
editedState: {},
};
}

if ( action.type === 'START_EDITING_BLOCKS' ) {
return {
...newState,
editedBlocks: {
...state.editedBlocks,
[ action.clientId ]: true,
},
};
}

if (
newState === state ||
( state.selection.selectionStart?.clientId ===
newState.selection.selectionStart?.clientId &&
state.selection.selectionEnd?.clientId ===
newState.selection.selectionEnd?.clientId )
) {
return {
...newState,
editedBlocks: state.editedBlocks,
};
}

// We the block selection changes, we need to update the edited blocks state and reset the ones outside of the selection or not parent of the selection.
if ( ! state.selection.selectionStart?.clientId ) {
return {
...newState,
editedBlocks: {},
};
}
const newEditedBlocks = { ...state.editedBlocks };
const isParent = ( parent, clientId ) => {
const currentParent = newState.blocks.parents[ clientId ];
if ( ! currentParent ) {
return false;
}

if ( currentParent === parent ) {
return true;
}

return isParent( parent, currentParent );
};

const isMultiSelection =
newState.selection.selectionStart?.clientId !==
newState.selection.selectionEnd?.clientId;

for ( const clientId of Object.keys( newEditedBlocks ) ) {
if (
isParent( clientId, newState.selection.selectionStart?.clientId )
) {
continue;
}

if (
! isMultiSelection &&
newState.selection.selectionStart?.clientId === clientId
) {
continue;
}

delete newEditedBlocks[ clientId ];
}

return {
...newState,
editedBlocks: newEditedBlocks,
};
};

export default withEditedBlock(
combineReducers( {
blocks,
isTyping,
draggedBlocks,
selection,
isMultiSelecting,
isSelectionEnabled,
initialPosition,
blocksMode,
blockListSettings,
insertionPoint,
template,
settings,
preferences,
lastBlockAttributesChange,
editorMode,
hasBlockMovingClientId,
automaticChangeStatus,
highlightedBlock,
lastBlockInserted,
temporarilyEditingAsBlocks,
blockVisibility,
} )
);
31 changes: 17 additions & 14 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2733,25 +2733,24 @@ export function __unstableHasActiveBlockOverlayActive( state, clientId ) {
return true;
}

// In navigation mode, the block overlay is active when the block is not
// selected (and doesn't contain a selected child). The same behavior is
// also enabled in all modes for blocks that have controlled children
// (reusable block, template part, navigation), unless explicitly disabled
// with `supports.__experimentalDisableBlockOverlay`.
const blockSupportDisable = hasBlockSupport(
// In navigation mode, the block overlay is active for all blocks when the block is not
// selected (and doesn't contain a selected child).
if ( editorMode === 'navigation' ) {
return (
! isBlockSelected( state, clientId ) &&
! hasSelectedInnerBlock( state, clientId, true )
);
}
const blockSupportBlockOverlay = hasBlockSupport(
getBlockName( state, clientId ),
'__experimentalDisableBlockOverlay',
'__experimentalBlockOverlay',
false
);
const shouldEnableIfUnselected =
editorMode === 'navigation' ||
( blockSupportDisable
? false
: areInnerBlocksControlled( state, clientId ) );

// For blocks that support the block overlay, The user can click "edit" button to remove the overlay.
return (
shouldEnableIfUnselected &&
! isBlockSelected( state, clientId ) &&
blockSupportBlockOverlay &&
! __unstableIsEditingBlock( state, clientId ) &&
! hasSelectedInnerBlock( state, clientId, true )
);
}
Expand All @@ -2766,3 +2765,7 @@ export function __unstableIsWithinBlockOverlay( state, clientId ) {
}
return false;
}

export function __unstableIsEditingBlock( state, clientId ) {
return !! state.editedBlocks?.[ clientId ];
}
3 changes: 2 additions & 1 deletion packages/block-library/src/block/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"supports": {
"customClassName": false,
"html": false,
"inserter": false
"inserter": false,
"__experimentalBlockOverlay": true
},
"editorStyle": "wp-block-editor"
}
1 change: 1 addition & 0 deletions packages/block-library/src/navigation/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"align": [ "wide", "full" ],
"html": false,
"inserter": true,
"__experimentalBlockOverlay": true,
"typography": {
"fontSize": true,
"lineHeight": true,
Expand Down
3 changes: 2 additions & 1 deletion packages/block-library/src/template-part/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"supports": {
"align": true,
"html": false,
"reusable": false
"reusable": false,
"__experimentalBlockOverlay": true
},
"editorStyle": "wp-block-template-part-editor"
}