Skip to content

Commit

Permalink
Add basic ability to rename blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
getdave committed Aug 16, 2023
1 parent cf28324 commit f9a0b34
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 36 deletions.
93 changes: 87 additions & 6 deletions packages/block-editor/src/components/list-view/block-contents.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { forwardRef } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { forwardRef, useState } from '@wordpress/element';
import { getBlockSupport } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { useBlockLock } from '../block-lock';
import ListViewBlockSelectButton from './block-select-button';
import ListViewBlockInput from './block-input';
import BlockDraggable from '../block-draggable';
import { store as blockEditorStore } from '../../store';
import { useListViewContext } from './context';
import useBlockDisplayTitle from '../block-title/use-block-display-title';
import useBlockDisplayInformation from '../use-block-display-information';

const ListViewBlockContents = forwardRef(
(
Expand All @@ -35,6 +40,9 @@ const ListViewBlockContents = forwardRef(
) => {
const { clientId } = block;

// Setting managed via `toggleLabelEditingMode` handler.
const [ labelEditingMode, setLabelEditingMode ] = useState( false );

const { blockMovingClientId, selectedBlockInBlockEditor } = useSelect(
( select ) => {
const { hasBlockMovingClientId, getSelectedBlockClientId } =
Expand All @@ -53,10 +61,6 @@ const ListViewBlockContents = forwardRef(
const isBlockMoveTarget =
blockMovingClientId && selectedBlockInBlockEditor === clientId;

const className = classnames( 'block-editor-list-view-block-contents', {
'is-dropping-before': isBlockMoveTarget,
} );

// Only include all selected blocks if the currently clicked on block
// is one of the selected blocks. This ensures that if a user attempts
// to drag a block that isn't part of the selection, they're still able
Expand All @@ -65,6 +69,79 @@ const ListViewBlockContents = forwardRef(
? selectedClientIds
: [ clientId ];

const { blockName, blockAttributes } = useSelect(
( select ) => {
const blockObject =
select( blockEditorStore ).getBlock( clientId );
return {
blockName: blockObject?.name,
blockAttributes: blockObject?.attributes,
};
},
[ clientId ]
);

const { updateBlockAttributes } = useDispatch( blockEditorStore );

const metaDataSupport = getBlockSupport(
blockName,
'__experimentalMetadata',
false
);

const supportsBlockNaming = !! (
true === metaDataSupport || metaDataSupport?.name
);

const { isLocked } = useBlockLock( clientId );

const toggleLabelEditingMode = ( value ) => {
if ( ! supportsBlockNaming ) {
return;
}

setLabelEditingMode( value );
};

const blockTitle = useBlockDisplayTitle( {
clientId,
context: 'list-view',
} );

const blockInformation = useBlockDisplayInformation( clientId );

const className = classnames( 'block-editor-list-view-block-contents', {
'is-dropping-before': isBlockMoveTarget,
'has-block-naming-support': supportsBlockNaming,
} );

function inputSubmitHandler( updatedInputValue ) {
updateBlockAttributes( clientId, {
// Include existing metadata (if present) to avoid overwriting existing.
metadata: {
...( blockAttributes?.metadata &&
blockAttributes?.metadata ),
name: updatedInputValue,
},
} );
}

if ( labelEditingMode ) {
return (
<ListViewBlockInput
ref={ ref }
className={ className }
onToggleExpanded={ onToggleExpanded }
toggleLabelEditingMode={ toggleLabelEditingMode }
blockInformation={ blockInformation }
isLocked={ isLocked }
blockTitle={ blockTitle }
onSubmit={ inputSubmitHandler }
{ ...props }
/>
);
}

return (
<>
{ AdditionalBlockContent && (
Expand All @@ -90,6 +167,10 @@ const ListViewBlockContents = forwardRef(
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
isExpanded={ isExpanded }
labelEditingMode={ labelEditingMode }
toggleLabelEditingMode={ toggleLabelEditingMode }
supportsBlockNaming={ supportsBlockNaming }
blockTitle={ blockTitle }
{ ...props }
/>
) }
Expand Down
144 changes: 144 additions & 0 deletions packages/block-editor/src/components/list-view/block-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* WordPress dependencies
*/
import {
__experimentalHStack as HStack,
__experimentalTruncate as Truncate,
__experimentalInputControl as InputControl,
VisuallyHidden,
} from '@wordpress/components';
import { speak } from '@wordpress/a11y';
import { useInstanceId, useFocusOnMount } from '@wordpress/compose';
import { forwardRef, useState } from '@wordpress/element';
import { ENTER, ESCAPE } from '@wordpress/keycodes';
import { __, sprintf } from '@wordpress/i18n';
import { Icon, lock } from '@wordpress/icons';

/**
* External dependencies
*/
import classnames from 'classnames';

/**
* Internal dependencies
*/
import BlockIcon from '../block-icon';
import ListViewExpander from './expander';

const ListViewBlockInput = forwardRef(
(
{
className,
onToggleExpanded,
toggleLabelEditingMode,
blockInformation,
isLocked,
blockTitle,
onSubmit,
},
ref
) => {
const inputRef = useFocusOnMount();
const inputDescriptionId = useInstanceId(
ListViewBlockInput,
`block-editor-list-view-block-node__input-description`
);

// Local state for value of input **pre-submission**.
const [ inputValue, setInputValue ] = useState( blockTitle );

const onKeyDownHandler = ( event ) => {
// Trap events to input when editing to avoid
// default list view key handing (e.g. arrow
// keys for navigation).
event.stopPropagation();

// Handle ENTER and ESC exits editing mode.
if ( event.keyCode === ENTER || event.keyCode === ESCAPE ) {
if ( event.keyCode === ESCAPE ) {
// Must be assertive to immediately announce change.
speak( 'Leaving edit mode', 'assertive' );
}

if ( event.keyCode === ENTER ) {
// Submit changes only for ENTER.
onSubmit( inputValue );
const successAnnouncement = sprintf(
/* translators: %s: new name/label for the block */
__( 'Block name updated to: "%s".' ),
inputValue
);
// Must be assertive to immediately announce change.
speak( successAnnouncement, 'assertive' );
}

toggleLabelEditingMode( false );
}
};

return (
<div
ref={ ref }
className={ classnames(
'block-editor-list-view-block-node',
'block-editor-list-view-block-input',
className
) }
>
<ListViewExpander onClick={ onToggleExpanded } />
<BlockIcon icon={ blockInformation?.icon } showColors />
<HStack
alignment="center"
className="block-editor-list-view-block-node__label-wrapper"
justify="flex-start"
spacing={ 1 }
>
<span className="block-editor-list-view-block-node__title">
<InputControl
ref={ inputRef }
value={ inputValue }
label={ __( 'Edit block name' ) }
hideLabelFromVision
onChange={ ( nextValue ) => {
setInputValue( nextValue ?? '' );
} }
onBlur={ () => {
toggleLabelEditingMode( false );

// Reset the input's local state to avoid
// stale values.
setInputValue( blockTitle );
} }
onKeyDown={ onKeyDownHandler }
aria-describedby={ inputDescriptionId }
/>
<VisuallyHidden>
<p id={ inputDescriptionId }>
{ __(
'Press the ENTER key to submit or the ESCAPE key to cancel.'
) }
</p>
</VisuallyHidden>
</span>
{ blockInformation?.anchor && (
<span className="block-editor-list-view-block-node__anchor-wrapper">
<Truncate
className="block-editor-list-view-block-node__anchor"
ellipsizeMode="auto"
>
{ blockInformation.anchor }
</Truncate>
</span>
) }
{ isLocked && (
<span className="block-editor-list-view-block-node__lock">
<Icon icon={ lock } />
</span>
) }
</HStack>
</div>
);
}
);

export default ListViewBlockInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* WordPress dependencies
*/
import { MenuItem } from '@wordpress/components';

import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/

import BlockSettingsMenuControls from '../block-settings-menu-controls';

function BlockOptionsRenameItem( { clientId, onClick } ) {
return (
<BlockSettingsMenuControls>
{ ( { selectedClientIds, __unstableDisplayLocation } ) => {
// Only enabled for single selections.
const canRename =
selectedClientIds.length === 1 &&
clientId === selectedClientIds[ 0 ];

// This check ensures
// - the `BlockSettingsMenuControls` fill
// doesn't render multiple times and also that it renders for
// the block from which the menu was triggered.
// - `Rename` only appears in the ListView options.
// - `Rename` only appears for blocks that support renaming.
if (
__unstableDisplayLocation !== 'list-view' ||
! canRename
) {
return null;
}

return (
<MenuItem onClick={ onClick }>{ __( 'Rename' ) }</MenuItem>
);
} }
</BlockSettingsMenuControls>
);
}

export default BlockOptionsRenameItem;
Loading

0 comments on commit f9a0b34

Please sign in to comment.