Skip to content

Commit

Permalink
Add ability to set templateLock = 'contentOnly' in editor settings
Browse files Browse the repository at this point in the history
  • Loading branch information
noisysocks committed Apr 26, 2023
1 parent 004f36c commit 4fc3d10
Show file tree
Hide file tree
Showing 30 changed files with 1,047 additions and 305 deletions.
32 changes: 32 additions & 0 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,23 @@ _Returns_

- `boolean`: Whether the caret is within formatted text.

### isContentLockedBlock

Returns whether or not the given block is _content locked_.

A block is _content locked_ if it is nested within a block that has a `templateLock` attribute set to `'contentOnly'` (a _content locking_ block), or if the editor has a `templateLock` of `'contentOnly'`.

If the block is nested within a content block type (see `settings.contentBlockTypes`) then it is not _content locked_.

_Parameters_

- _state_ `Object`: Global application state.
- _clientId_ `string`: The client ID of the block to check.

_Returns_

- `boolean`: Whether or not the block is content locked.

### isDraggingBlocks

Returns true if the user is dragging blocks, or false otherwise.
Expand All @@ -1025,6 +1042,21 @@ _Returns_

- `boolean`: Whether block is first in multi-selection.

### isInsertionLocked

Determines if the editor or a given container is locked and does not allow block insertion.

Only the `templateLock` settings of the editor or container block are checked. For more rigorous checking that checks the `allowedBlockTypes` attribute, use `canInsertBlockType()`.

_Parameters_

- _state_ `Object`: Editor state.
- _rootClientId_ `?string`: Container block's client ID, or `null` to check the editor.

_Returns_

- `boolean`: Whether block insertion is locked.

### isLastBlockChangePersistent

Returns true if the most recent block change is be considered persistent, or false otherwise. A persistent change is one committed by BlockEditorProvider via its `onChange` callback, in addition to `onInput`.
Expand Down
140 changes: 32 additions & 108 deletions packages/block-editor/src/components/block-inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,8 @@ import {
hasBlockSupport,
store as blocksStore,
} from '@wordpress/blocks';
import {
FlexItem,
PanelBody,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
Button,
__unstableMotion as motion,
} from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { useMemo, useCallback } from '@wordpress/element';
import { PanelBody, __unstableMotion as motion } from '@wordpress/components';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -28,7 +20,6 @@ import MultiSelectionInspector from '../multi-selection-inspector';
import BlockVariationTransforms from '../block-variation-transforms';
import useBlockDisplayInformation from '../use-block-display-information';
import { store as blockEditorStore } from '../../store';
import BlockIcon from '../block-icon';
import BlockStyles from '../block-styles';
import DefaultStylePicker from '../default-style-picker';
import { default as InspectorControls } from '../inspector-controls';
Expand All @@ -38,102 +29,34 @@ import AdvancedControls from '../inspector-controls-tabs/advanced-controls-panel
import PositionControls from '../inspector-controls-tabs/position-controls-panel';
import useBlockInspectorAnimationSettings from './useBlockInspectorAnimationSettings';
import BlockInfo from '../block-info-slot-fill';
import ContentBlocksList from '../content-blocks-list';
import { unlock } from '../../lock-unlock';

function useContentBlocks( blockTypes, block ) {
const contentBlocksObjectAux = useMemo( () => {
return blockTypes.reduce( ( result, blockType ) => {
if (
blockType.name !== 'core/list-item' &&
Object.entries( blockType.attributes ).some(
( [ , { __experimentalRole } ] ) =>
__experimentalRole === 'content'
)
) {
result[ blockType.name ] = true;
}
return result;
}, {} );
}, [ blockTypes ] );
const isContentBlock = useCallback(
( blockName ) => {
return !! contentBlocksObjectAux[ blockName ];
},
[ contentBlocksObjectAux ]
);
return useMemo( () => {
return getContentBlocks( [ block ], isContentBlock );
}, [ block, isContentBlock ] );
}

function getContentBlocks( blocks, isContentBlock ) {
const result = [];
for ( const block of blocks ) {
if ( isContentBlock( block.name ) ) {
result.push( block );
}
result.push( ...getContentBlocks( block.innerBlocks, isContentBlock ) );
}
return result;
}

function BlockNavigationButton( { blockTypes, block, selectedBlock } ) {
const { selectBlock } = useDispatch( blockEditorStore );
const blockType = blockTypes.find( ( { name } ) => name === block.name );
const isSelected =
selectedBlock && selectedBlock.clientId === block.clientId;
return (
<Button
isPressed={ isSelected }
onClick={ () => selectBlock( block.clientId ) }
>
<HStack justify="flex-start">
<BlockIcon icon={ blockType.icon } />
<FlexItem>{ blockType.title }</FlexItem>
</HStack>
</Button>
function BlockInspectorLockedBlocks( { contentLockingBlock } ) {
const selectedBlock = useSelect(
( select ) => select( blockEditorStore ).getSelectedBlockClientId(),
[]
);
}

function BlockInspectorLockedBlocks( { topLevelLockedBlock } ) {
const { blockTypes, block, selectedBlock } = useSelect(
( select ) => {
return {
blockTypes: select( blocksStore ).getBlockTypes(),
block: select( blockEditorStore ).getBlock(
topLevelLockedBlock
),
selectedBlock: select( blockEditorStore ).getSelectedBlock(),
};
},
[ topLevelLockedBlock ]
const blockInformation = useBlockDisplayInformation(
contentLockingBlock ?? selectedBlock
);
const blockInformation = useBlockDisplayInformation( topLevelLockedBlock );
const contentBlocks = useContentBlocks( blockTypes, block );
return (
<div className="block-editor-block-inspector">
<BlockCard
{ ...blockInformation }
className={ blockInformation.isSynced && 'is-synced' }
/>
<BlockVariationTransforms blockClientId={ topLevelLockedBlock } />
{ contentLockingBlock && (
<BlockVariationTransforms
blockClientId={ contentLockingBlock }
/>
) }
<BlockInfo.Slot />
<VStack
spacing={ 1 }
padding={ 4 }
className="block-editor-block-inspector__block-buttons-container"
>
<h2 className="block-editor-block-card__title">
{ __( 'Content' ) }
</h2>
{ contentBlocks.map( ( contentBlock ) => (
<BlockNavigationButton
selectedBlock={ selectedBlock }
key={ contentBlock.clientId }
block={ contentBlock }
blockTypes={ blockTypes }
/>
) ) }
</VStack>
{ contentLockingBlock && (
<PanelBody title={ __( 'Content' ) }>
<ContentBlocksList rootClientId={ contentLockingBlock } />
</PanelBody>
) }
</div>
);
}
Expand All @@ -144,15 +67,16 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
selectedBlockName,
selectedBlockClientId,
blockType,
topLevelLockedBlock,
isContentLocked,
contentLockingBlock,
} = useSelect( ( select ) => {
const {
getSelectedBlockClientId,
getSelectedBlockCount,
getBlockName,
__unstableGetContentLockingParent,
getTemplateLock,
} = select( blockEditorStore );
isContentLockedBlock,
getContentLockingBlock,
} = unlock( select( blockEditorStore ) );

const _selectedBlockClientId = getSelectedBlockClientId();
const _selectedBlockName =
Expand All @@ -165,11 +89,10 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
selectedBlockClientId: _selectedBlockClientId,
selectedBlockName: _selectedBlockName,
blockType: _blockType,
topLevelLockedBlock:
__unstableGetContentLockingParent( _selectedBlockClientId ) ||
( getTemplateLock( _selectedBlockClientId ) === 'contentOnly'
? _selectedBlockClientId
: undefined ),
isContentLocked: isContentLockedBlock( _selectedBlockClientId ),
contentLockingBlock: getContentLockingBlock(
_selectedBlockClientId
),
};
}, [] );

Expand Down Expand Up @@ -241,10 +164,11 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
}
return null;
}
if ( topLevelLockedBlock ) {

if ( isContentLocked ) {
return (
<BlockInspectorLockedBlocks
topLevelLockedBlock={ topLevelLockedBlock }
contentLockingBlock={ contentLockingBlock }
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,3 @@
.block-editor-block-inspector__tab-item {
flex: 1 1 0px;
}

.block-editor-block-inspector__block-buttons-container {
border-top: $border-width solid $gray-200;
padding: $grid-unit-20;
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ function useAppender( rootClientId, CustomAppender ) {
const { hideInserter, isParentSelected } = useSelect(
( select ) => {
const {
getTemplateLock,
getSelectedBlockClientId,
__unstableGetEditorMode,
isInsertionLocked,
} = select( blockEditorStore );

const selectedBlockClientId = getSelectedBlockClientId();

return {
hideInserter:
!! getTemplateLock( rootClientId ) ||
isInsertionLocked( rootClientId ) ||
__unstableGetEditorMode() === 'zoom-out',
isParentSelected:
rootClientId === selectedBlockClientId ||
Expand Down
42 changes: 18 additions & 24 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
isUnmodifiedDefaultBlock,
serializeRawBlock,
switchToBlockType,
store as blocksStore,
getDefaultBlockName,
isUnmodifiedBlock,
} from '@wordpress/blocks';
Expand All @@ -43,6 +42,8 @@ import BlockHtml from './block-html';
import { useBlockProps } from './use-block-props';
import { store as blockEditorStore } from '../../store';
import { useLayout } from './layout';
import { unlock } from '../../lock-unlock';

export const BlockListBlockContext = createContext();

/**
Expand Down Expand Up @@ -99,32 +100,26 @@ function BlockListBlock( {
} ) {
const {
themeSupportsLayout,
hasContentLockedParent,
isContentBlock,
isContentLocked,
isContent,
isContentLocking,
isTemporarilyEditingAsBlocks,
isTemporarilyUnlocked,
} = useSelect(
( select ) => {
const {
getSettings,
__unstableGetContentLockingParent,
getTemplateLock,
__unstableGetTemporarilyEditingAsBlocks,
} = select( blockEditorStore );
const _hasContentLockedParent =
!! __unstableGetContentLockingParent( clientId );
getTemporarilyUnlockedBlock,
isContentBlock,
isContentLockedBlock,
isContentLockingBlock,
} = unlock( select( blockEditorStore ) );
return {
themeSupportsLayout: getSettings().supportsLayout,
isContentBlock:
select( blocksStore ).__experimentalHasContentRoleAttribute(
name
),
hasContentLockedParent: _hasContentLockedParent,
isContentLocking:
getTemplateLock( clientId ) === 'contentOnly' &&
! _hasContentLockedParent,
isTemporarilyEditingAsBlocks:
__unstableGetTemporarilyEditingAsBlocks() === clientId,
isContent: isContentBlock( clientId ),
isContentLocked: isContentLockedBlock( clientId ),
isContentLocking: isContentLockingBlock( clientId ),
isTemporarilyUnlocked:
getTemporarilyUnlockedBlock() === clientId,
};
},
[ name, clientId ]
Expand Down Expand Up @@ -160,7 +155,7 @@ function BlockListBlock( {

const blockType = getBlockType( name );

if ( hasContentLockedParent && ! isContentBlock ) {
if ( isContentLocked && ! isContent ) {
wrapperProps = {
...wrapperProps,
tabIndex: -1,
Expand Down Expand Up @@ -235,9 +230,8 @@ function BlockListBlock( {
className: classnames(
{
'is-content-locked': isContentLocking,
'is-content-locked-temporarily-editing-as-blocks':
isTemporarilyEditingAsBlocks,
'is-content-block': hasContentLockedParent && isContentBlock,
'is-content-temporarily-unlocked': isTemporarilyUnlocked,
'is-content-block': isContentLocked && isContent,
},
dataAlign && themeSupportsLayout && `align${ dataAlign }`,
className
Expand Down
5 changes: 3 additions & 2 deletions packages/block-editor/src/components/block-list/content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@
.block-editor-block-list__block {
pointer-events: none;
}
.is-content-block {
.is-content-block,
.is-content-block .block-editor-block-list__block {
pointer-events: initial;
}
}
Expand Down Expand Up @@ -323,7 +324,7 @@
}

.is-focus-mode .block-editor-block-list__block.is-content-locked.has-child-selected,
.is-focus-mode .block-editor-block-list__block.is-content-locked-temporarily-editing-as-blocks.has-child-selected {
.is-focus-mode .block-editor-block-list__block.is-content-temporarily-unlocked.has-child-selected {
&,
& .block-editor-block-list__block {
opacity: 1;
Expand Down
Loading

0 comments on commit 4fc3d10

Please sign in to comment.