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

Align hook: improve BlockEdit filter #48422

Closed
wants to merge 4 commits 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
33 changes: 4 additions & 29 deletions packages/block-editor/src/components/block-controls/hook.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,17 @@
/**
* WordPress dependencies
*/
import { store as blocksStore } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import groups from './groups';
import { store as blockEditorStore } from '../../store';
import { useBlockEditContext } from '../block-edit/context';
import useDisplayBlockControls from '../use-display-block-controls';

export default function useBlockControlsFill( group, shareWithChildBlocks ) {
const isDisplayed = useDisplayBlockControls();
const { clientId } = useBlockEditContext();
const isParentDisplayed = useSelect(
( select ) => {
const { getBlockName, hasSelectedInnerBlock } =
select( blockEditorStore );
const { hasBlockSupport } = select( blocksStore );
return (
shareWithChildBlocks &&
hasBlockSupport(
getBlockName( clientId ),
'__experimentalExposeControlsToChildren',
false
) &&
hasSelectedInnerBlock( clientId )
);
},
[ shareWithChildBlocks, clientId ]
);
const { shouldDisplayControls, shouldDisplayControlsWithinChildren } =
useBlockEditContext();

if ( isDisplayed ) {
if ( shouldDisplayControls ) {
return groups[ group ]?.Fill;
}
if ( isParentDisplayed ) {
if ( shareWithChildBlocks && shouldDisplayControlsWithinChildren ) {
return groups.parent.Fill;
}
return null;
Expand Down
41 changes: 41 additions & 0 deletions packages/block-editor/src/components/block-edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
import { useMemo } from '@wordpress/element';

import { hasBlockSupport } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import Edit from './edit';
import { BlockEditContextProvider, useBlockEditContext } from './context';
import { store as blockEditorStore } from '../../store';

/**
* The `useBlockEditContext` hook provides information about the block this hook is being used in.
Expand All @@ -34,12 +37,50 @@ export default function BlockEdit( props ) {
'__experimentalLayout',
false
);
const shouldDisplayControls = useSelect(
ellatrix marked this conversation as resolved.
Show resolved Hide resolved
( select ) => {
if ( isSelected ) {
return true;
}

const {
getBlockName,
isFirstMultiSelectedBlock,
getMultiSelectedBlockClientIds,
} = select( blockEditorStore );

if ( isFirstMultiSelectedBlock( clientId ) ) {
return getMultiSelectedBlockClientIds().every(
( id ) => getBlockName( id ) === name
);
}

return false;
},
[ clientId, isSelected, name ]
);
const shouldDisplayControlsWithinChildren = useSelect(
( select ) => {
const { getBlockName, hasSelectedInnerBlock } =
select( blockEditorStore );
return (
hasBlockSupport(
getBlockName( clientId ),
'__experimentalExposeControlsToChildren',
false
) && hasSelectedInnerBlock( clientId )
);
},
[ clientId, isSelected, name ]
);
const context = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this context a public API, if it is, I think we should "lock" the new key somehow to make it private.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you "lock" a key?

I'm thinking we should maybe create a separate boolean context for this that is private to blockEditor which can be used in the Fills and pass it down as a prop to BlockEdit for the HoCs so they don't need to call a hook.

Copy link
Contributor

@youknowriad youknowriad Feb 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah either a separate context or store a locked function that returns the boolean in this context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably prefer the former to avoid the proliferation of contexts

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should we lock it though? Don't we want plugin authors to use these in their filters?

ellatrix marked this conversation as resolved.
Show resolved Hide resolved
name,
isSelected,
clientId,
layout: layoutSupport ? layout : null,
__unstableLayoutClassNames,
shouldDisplayControls,
shouldDisplayControlsWithinChildren,
};
return (
<BlockEditContextProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,8 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { useBlockEditContext } from '../block-edit/context';
import { store as blockEditorStore } from '../../store';

export default function useDisplayBlockControls() {
const { isSelected, clientId, name } = useBlockEditContext();
return useSelect(
( select ) => {
if ( isSelected ) {
return true;
}

const {
getBlockName,
isFirstMultiSelectedBlock,
getMultiSelectedBlockClientIds,
} = select( blockEditorStore );

if ( isFirstMultiSelectedBlock( clientId ) ) {
return getMultiSelectedBlockClientIds().every(
( id ) => getBlockName( id ) === name
);
}

return false;
},
[ clientId, isSelected, name ]
);
return useBlockEditContext().shouldDisplayControls;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useBlockControlsFill calls useDisplayBlockControls but also adds some additional checks that are now part of useDisplayBlockControls so there's probably some code to remove from useBlockControlsFill

}
103 changes: 58 additions & 45 deletions packages/block-editor/src/hooks/align.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useSelect } from '@wordpress/data';
import { BlockControls, BlockAlignmentControl } from '../components';
import useAvailableAlignments from '../components/block-alignment-control/use-available-alignments';
import { store as blockEditorStore } from '../store';
import { useBlockEditContext } from '../components/block-edit/context';

/**
* An array which includes all possible valid alignments,
Expand Down Expand Up @@ -109,6 +110,55 @@ export function addAttribute( settings ) {
return settings;
}

function AlignControls( props ) {
const { name: blockName } = props;
// Compute the block valid alignments by taking into account,
// if the theme supports wide alignments or not and the layout's
// availble alignments. We do that for conditionally rendering
// Slot.
const blockAllowedAlignments = getValidAlignments(
getBlockSupport( blockName, 'align' ),
hasBlockSupport( blockName, 'alignWide', true )
);

const validAlignments = useAvailableAlignments(
blockAllowedAlignments
).map( ( { name } ) => name );
const isContentLocked = useSelect(
( select ) => {
return select( blockEditorStore ).__unstableGetContentLockingParent(
props.clientId
);
},
[ props.clientId ]
);

if ( ! validAlignments.length || isContentLocked ) {
return null;
}

const updateAlignment = ( nextAlign ) => {
if ( ! nextAlign ) {
const blockType = getBlockType( props.name );
const blockDefaultAlign = blockType?.attributes?.align?.default;
if ( blockDefaultAlign ) {
nextAlign = '';
}
}
props.setAttributes( { align: nextAlign } );
};

return (
<BlockControls group="block" __experimentalShareWithChildBlocks>
<BlockAlignmentControl
value={ props.attributes.align }
onChange={ updateAlignment }
controls={ validAlignments }
/>
</BlockControls>
);
}

/**
* Override the default edit UI to include new toolbar controls for block
* alignment, if block defines support.
Expand All @@ -119,53 +169,16 @@ export function addAttribute( settings ) {
*/
export const withToolbarControls = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const blockEdit = <BlockEdit key="edit" { ...props } />;
const { name: blockName } = props;
// Compute the block valid alignments by taking into account,
// if the theme supports wide alignments or not and the layout's
// availble alignments. We do that for conditionally rendering
// Slot.
const blockAllowedAlignments = getValidAlignments(
getBlockSupport( blockName, 'align' ),
hasBlockSupport( blockName, 'alignWide', true )
);

const validAlignments = useAvailableAlignments(
blockAllowedAlignments
).map( ( { name } ) => name );
const isContentLocked = useSelect(
( select ) => {
return select(
blockEditorStore
).__unstableGetContentLockingParent( props.clientId );
},
[ props.clientId ]
);
if ( ! validAlignments.length || isContentLocked ) {
return blockEdit;
}

const updateAlignment = ( nextAlign ) => {
if ( ! nextAlign ) {
const blockType = getBlockType( props.name );
const blockDefaultAlign = blockType?.attributes?.align?.default;
if ( blockDefaultAlign ) {
nextAlign = '';
}
}
props.setAttributes( { align: nextAlign } );
};

const { shouldDisplayControls, shouldDisplayControlsWithinChildren } =
useBlockEditContext();
return (
<>
<BlockControls group="block" __experimentalShareWithChildBlocks>
<BlockAlignmentControl
value={ props.attributes.align }
onChange={ updateAlignment }
controls={ validAlignments }
/>
</BlockControls>
{ blockEdit }
{ ( shouldDisplayControls ||
shouldDisplayControlsWithinChildren ) &&
hasBlockSupport( props.name, 'align' ) && (
<AlignControls { ...props } />
) }
<BlockEdit { ...props } />
</>
);
},
Expand Down