Skip to content

Commit 075d937

Browse files
authored
Add ability to prevent editing blocks using useBlockEditingMode() (#50643)
* Add ability to prevent editing blocks using useBlockEditingMode() * Make useBlockEditingMode use context * Remove rootBlockEditingMode setting * Fix private createRegistrySelector selectors * Consolidate templateLock=contentOnly logic into getBlockEditingMode * Hide disabled blocks from List View * Hide disabled blocks from breadcrumbs * Add doc comments * Add unit tests * Use @typedef to document mode param * Restore packages/components/package.json from trunk * Restore packages/block-library/src/post-title/edit.js from trunk * Move BlockListBlockContext out of block.js so that it exists on mobile platforms * DRY up blockEditingMode check * Fix typo * Remove unnecessary comment
1 parent 6857747 commit 075d937

File tree

28 files changed

+676
-190
lines changed

28 files changed

+676
-190
lines changed

packages/block-editor/src/components/block-breadcrumb/index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { chevronRightSmall, Icon } from '@wordpress/icons';
1111
*/
1212
import BlockTitle from '../block-title';
1313
import { store as blockEditorStore } from '../../store';
14+
import { unlock } from '../../lock-unlock';
1415

1516
/**
1617
* Block breadcrumb component, displaying the hierarchy of the current block selection as a breadcrumb.
@@ -22,11 +23,18 @@ import { store as blockEditorStore } from '../../store';
2223
function BlockBreadcrumb( { rootLabelText } ) {
2324
const { selectBlock, clearSelectedBlock } = useDispatch( blockEditorStore );
2425
const { clientId, parents, hasSelection } = useSelect( ( select ) => {
25-
const { getSelectionStart, getSelectedBlockClientId, getBlockParents } =
26-
select( blockEditorStore );
26+
const {
27+
getSelectionStart,
28+
getSelectedBlockClientId,
29+
getBlockParents,
30+
getBlockEditingMode,
31+
} = unlock( select( blockEditorStore ) );
2732
const selectedBlockClientId = getSelectedBlockClientId();
2833
return {
29-
parents: getBlockParents( selectedBlockClientId ),
34+
parents: getBlockParents( selectedBlockClientId ).filter(
35+
( parentClientId ) =>
36+
getBlockEditingMode( parentClientId ) !== 'disabled'
37+
),
3038
clientId: selectedBlockClientId,
3139
hasSelection: !! getSelectionStart().clientId,
3240
};
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { useSelect, useDispatch } from '@wordpress/data';
5+
import { useContext, useEffect } from '@wordpress/element';
6+
7+
/**
8+
* Internal dependencies
9+
*/
10+
import { store as blockEditorStore } from '../../store';
11+
import { unlock } from '../../lock-unlock';
12+
import { BlockListBlockContext } from '../block-list/block-list-block-context';
13+
14+
/**
15+
* @typedef {'disabled'|'contentOnly'|'default'} BlockEditingMode
16+
*/
17+
18+
/**
19+
* Allows a block to restrict the user interface that is displayed for editing
20+
* that block and its inner blocks.
21+
*
22+
* @example
23+
* ```js
24+
* function MyBlock( { attributes, setAttributes } ) {
25+
* useBlockEditingMode( 'disabled' );
26+
* return <div { ...useBlockProps() }></div>;
27+
* }
28+
* ```
29+
*
30+
* `mode` can be one of three options:
31+
*
32+
* - `'disabled'`: Prevents editing the block entirely, i.e. it cannot be
33+
* selected.
34+
* - `'contentOnly'`: Hides all non-content UI, e.g. auxiliary controls in the
35+
* toolbar, the block movers, block settings.
36+
* - `'default'`: Allows editing the block as normal.
37+
*
38+
* The mode is inherited by all of the block's inner blocks, unless they have
39+
* their own mode.
40+
*
41+
* If called outside of a block context, the mode is applied to all blocks.
42+
*
43+
* @param {?BlockEditingMode} mode The editing mode to apply. If undefined, the
44+
* current editing mode is not changed.
45+
*
46+
* @return {BlockEditingMode} The current editing mode.
47+
*/
48+
export function useBlockEditingMode( mode ) {
49+
const { clientId = '' } = useContext( BlockListBlockContext ) ?? {};
50+
const blockEditingMode = useSelect(
51+
( select ) =>
52+
unlock( select( blockEditorStore ) ).getBlockEditingMode(
53+
clientId
54+
),
55+
[ clientId ]
56+
);
57+
const { setBlockEditingMode, unsetBlockEditingMode } = unlock(
58+
useDispatch( blockEditorStore )
59+
);
60+
useEffect( () => {
61+
if ( mode ) {
62+
setBlockEditingMode( clientId, mode );
63+
}
64+
return () => {
65+
if ( mode ) {
66+
unsetBlockEditingMode( clientId );
67+
}
68+
};
69+
}, [ clientId, mode ] );
70+
return blockEditingMode;
71+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { createContext } from '@wordpress/element';
5+
6+
export const BlockListBlockContext = createContext( null );

packages/block-editor/src/components/block-list/block.js

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,13 @@ import classnames from 'classnames';
66
/**
77
* WordPress dependencies
88
*/
9-
import {
10-
createContext,
11-
useMemo,
12-
useCallback,
13-
RawHTML,
14-
} from '@wordpress/element';
9+
import { useMemo, useCallback, RawHTML } from '@wordpress/element';
1510
import {
1611
getBlockType,
1712
getSaveContent,
1813
isUnmodifiedDefaultBlock,
1914
serializeRawBlock,
2015
switchToBlockType,
21-
store as blocksStore,
2216
getDefaultBlockName,
2317
isUnmodifiedBlock,
2418
} from '@wordpress/blocks';
@@ -43,7 +37,8 @@ import BlockHtml from './block-html';
4337
import { useBlockProps } from './use-block-props';
4438
import { store as blockEditorStore } from '../../store';
4539
import { useLayout } from './layout';
46-
export const BlockListBlockContext = createContext();
40+
import { unlock } from '../../lock-unlock';
41+
import { BlockListBlockContext } from './block-list-block-context';
4742

4843
/**
4944
* Merges wrapper props with special handling for classNames and styles.
@@ -99,35 +94,23 @@ function BlockListBlock( {
9994
} ) {
10095
const {
10196
themeSupportsLayout,
102-
hasContentLockedParent,
103-
isContentBlock,
104-
isContentLocking,
10597
isTemporarilyEditingAsBlocks,
98+
blockEditingMode,
10699
} = useSelect(
107100
( select ) => {
108101
const {
109102
getSettings,
110-
__unstableGetContentLockingParent,
111-
getTemplateLock,
112103
__unstableGetTemporarilyEditingAsBlocks,
113-
} = select( blockEditorStore );
114-
const _hasContentLockedParent =
115-
!! __unstableGetContentLockingParent( clientId );
104+
getBlockEditingMode,
105+
} = unlock( select( blockEditorStore ) );
116106
return {
117107
themeSupportsLayout: getSettings().supportsLayout,
118-
isContentBlock:
119-
select( blocksStore ).__experimentalHasContentRoleAttribute(
120-
name
121-
),
122-
hasContentLockedParent: _hasContentLockedParent,
123-
isContentLocking:
124-
getTemplateLock( clientId ) === 'contentOnly' &&
125-
! _hasContentLockedParent,
126108
isTemporarilyEditingAsBlocks:
127109
__unstableGetTemporarilyEditingAsBlocks() === clientId,
110+
blockEditingMode: getBlockEditingMode( clientId ),
128111
};
129112
},
130-
[ name, clientId ]
113+
[ clientId ]
131114
);
132115
const { removeBlock } = useDispatch( blockEditorStore );
133116
const onRemove = useCallback( () => removeBlock( clientId ), [ clientId ] );
@@ -160,7 +143,7 @@ function BlockListBlock( {
160143

161144
const blockType = getBlockType( name );
162145

163-
if ( hasContentLockedParent && ! isContentBlock ) {
146+
if ( blockEditingMode === 'disabled' ) {
164147
wrapperProps = {
165148
...wrapperProps,
166149
tabIndex: -1,
@@ -234,10 +217,9 @@ function BlockListBlock( {
234217
clientId,
235218
className: classnames(
236219
{
237-
'is-content-locked': isContentLocking,
220+
'is-editing-disabled': blockEditingMode === 'disabled',
238221
'is-content-locked-temporarily-editing-as-blocks':
239222
isTemporarilyEditingAsBlocks,
240-
'is-content-block': hasContentLockedParent && isContentBlock,
241223
},
242224
dataAlign && themeSupportsLayout && `align${ dataAlign }`,
243225
className

packages/block-editor/src/components/block-list/content.scss

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,13 @@
161161
padding: 0;
162162
}
163163

164-
.is-content-locked {
165-
.block-editor-block-list__block {
164+
.block-editor-block-list__layout,
165+
.block-editor-block-list__block {
166+
pointer-events: initial;
167+
168+
&.is-editing-disabled {
166169
pointer-events: none;
167170
}
168-
.is-content-block {
169-
pointer-events: initial;
170-
}
171171
}
172172

173173
.block-editor-block-list__layout .block-editor-block-list__block {

packages/block-editor/src/components/block-list/use-block-props/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import warning from '@wordpress/warning';
2121
* Internal dependencies
2222
*/
2323
import useMovingAnimation from '../../use-moving-animation';
24-
import { BlockListBlockContext } from '../block';
24+
import { BlockListBlockContext } from '../block-list-block-context';
2525
import { useFocusFirstElement } from './use-focus-first-element';
2626
import { useIsHovered } from './use-is-hovered';
2727
import { useBlockEditContext } from '../../block-edit/context';

packages/block-editor/src/components/block-list/use-in-between-inserter.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { isRTL } from '@wordpress/i18n';
1111
*/
1212
import { store as blockEditorStore } from '../../store';
1313
import { InsertionPointOpenRef } from '../block-tools/insertion-point';
14+
import { unlock } from '../../lock-unlock';
1415

1516
export function useInBetweenInserter() {
1617
const openRef = useContext( InsertionPointOpenRef );
@@ -29,7 +30,8 @@ export function useInBetweenInserter() {
2930
getSelectedBlockClientIds,
3031
getTemplateLock,
3132
__unstableIsWithinBlockOverlay,
32-
} = useSelect( blockEditorStore );
33+
getBlockEditingMode,
34+
} = unlock( useSelect( blockEditorStore ) );
3335
const { showInsertionPoint, hideInsertionPoint } =
3436
useDispatch( blockEditorStore );
3537

@@ -74,8 +76,10 @@ export function useInBetweenInserter() {
7476
rootClientId = blockElement.getAttribute( 'data-block' );
7577
}
7678

77-
// Don't set the insertion point if the template is locked.
78-
if ( getTemplateLock( rootClientId ) ) {
79+
if (
80+
getTemplateLock( rootClientId ) ||
81+
getBlockEditingMode( rootClientId ) === 'disabled'
82+
) {
7983
return;
8084
}
8185

packages/block-editor/src/components/block-toolbar/index.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import BlockEditVisuallyButton from '../block-edit-visually-button';
3232
import { useShowMoversGestures } from './utils';
3333
import { store as blockEditorStore } from '../../store';
3434
import __unstableBlockNameContext from './block-name-context';
35+
import { unlock } from '../../lock-unlock';
3536

3637
const BlockToolbar = ( { hideDragHandle } ) => {
3738
const {
@@ -42,7 +43,7 @@ const BlockToolbar = ( { hideDragHandle } ) => {
4243
isDistractionFree,
4344
isValid,
4445
isVisual,
45-
isContentLocked,
46+
blockEditingMode,
4647
} = useSelect( ( select ) => {
4748
const {
4849
getBlockName,
@@ -51,8 +52,8 @@ const BlockToolbar = ( { hideDragHandle } ) => {
5152
isBlockValid,
5253
getBlockRootClientId,
5354
getSettings,
54-
__unstableGetContentLockingParent,
55-
} = select( blockEditorStore );
55+
getBlockEditingMode,
56+
} = unlock( select( blockEditorStore ) );
5657
const selectedBlockClientIds = getSelectedBlockClientIds();
5758
const selectedBlockClientId = selectedBlockClientIds[ 0 ];
5859
const blockRootClientId = getBlockRootClientId( selectedBlockClientId );
@@ -73,9 +74,7 @@ const BlockToolbar = ( { hideDragHandle } ) => {
7374
isVisual: selectedBlockClientIds.every(
7475
( id ) => getBlockMode( id ) === 'visual'
7576
),
76-
isContentLocked: !! __unstableGetContentLockingParent(
77-
selectedBlockClientId
78-
),
77+
blockEditingMode: getBlockEditingMode( selectedBlockClientId ),
7978
};
8079
}, [] );
8180

@@ -125,12 +124,12 @@ const BlockToolbar = ( { hideDragHandle } ) => {
125124

126125
return (
127126
<div className={ classes }>
128-
{ ! isMultiToolbar && isLargeViewport && ! isContentLocked && (
129-
<BlockParentSelector />
130-
) }
127+
{ ! isMultiToolbar &&
128+
isLargeViewport &&
129+
blockEditingMode === 'default' && <BlockParentSelector /> }
131130
<div ref={ nodeRef } { ...showMoversGestures }>
132131
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
133-
! isContentLocked && (
132+
blockEditingMode === 'default' && (
134133
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
135134
<BlockSwitcher clientIds={ blockClientIds } />
136135
{ ! isMultiToolbar && (
@@ -175,7 +174,7 @@ const BlockToolbar = ( { hideDragHandle } ) => {
175174
</>
176175
) }
177176
<BlockEditVisuallyButton clientIds={ blockClientIds } />
178-
{ ! isContentLocked && (
177+
{ blockEditingMode === 'default' && (
179178
<BlockSettingsMenu clientIds={ blockClientIds } />
180179
) }
181180
</div>

packages/block-editor/src/components/block-tools/block-contextual-toolbar.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import NavigableToolbar from '../navigable-toolbar';
2525
import BlockToolbar from '../block-toolbar';
2626
import { store as blockEditorStore } from '../../store';
2727
import BlockIcon from '../block-icon';
28+
import { unlock } from '../../lock-unlock';
2829

2930
function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) {
3031
// When the toolbar is fixed it can be collapsed
@@ -38,8 +39,8 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) {
3839
getBlockName,
3940
getBlockParents,
4041
getSelectedBlockClientIds,
41-
__unstableGetContentLockingParent,
42-
} = select( blockEditorStore );
42+
getBlockEditingMode,
43+
} = unlock( select( blockEditorStore ) );
4344
const { getBlockType } = select( blocksStore );
4445
const selectedBlockClientIds = getSelectedBlockClientIds();
4546
const _selectedBlockClientId = selectedBlockClientIds[ 0 ];
@@ -62,9 +63,7 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) {
6263
true
6364
) &&
6465
selectedBlockClientIds.length <= 1 &&
65-
! __unstableGetContentLockingParent(
66-
_selectedBlockClientId
67-
),
66+
getBlockEditingMode( _selectedBlockClientId ) === 'default',
6867
};
6968
}, [] );
7069

0 commit comments

Comments
 (0)