diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index 86597a03cde2a5..41a40d48d20b20 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -26,7 +26,7 @@ import BlockToolbarPopover from './block-toolbar-popover'; import { store as blockEditorStore } from '../../store'; import usePopoverScroll from '../block-popover/use-popover-scroll'; import ZoomOutModeInserters from './zoom-out-mode-inserters'; -import { useShowBlockTools } from './use-show-block-tools'; +import { shouldShowBlockTools } from './should-show-block-tools'; import { unlock } from '../../lock-unlock'; import { cleanEmptyObject } from '../../hooks/utils'; import usePasteStyles from '../use-paste-styles'; @@ -39,17 +39,47 @@ function selector( select ) { isTyping, isDragging, isZoomOut, + getBlocksByClientId, + getSelectedBlockClientIds, + getBlockRootClientId, + isGroupable, + getBlockName, + getBlock, + getBlockMode, + isBlockInterfaceHidden, } = unlock( select( blockEditorStore ) ); + const { getGroupingBlockName } = select( blocksStore ); + const clientId = getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId(); + const _isTyping = isTyping(); + const _hasFixedToolbar = getSettings().hasFixedToolbar; + + const { showEmptyBlockSideInserter, showBlockToolbarPopover } = + shouldShowBlockTools( { + block: getBlock( clientId ), + blockMode: clientId ? getBlockMode( clientId ) : null, + isBlockInterfaceHidden: isBlockInterfaceHidden(), + clientId, + isTyping: _isTyping, + hasFixedToolbar: _hasFixedToolbar, + } ); return { clientId, - hasFixedToolbar: getSettings().hasFixedToolbar, - isTyping: isTyping(), + hasFixedToolbar: _hasFixedToolbar, + isTyping: _isTyping, isZoomOutMode: isZoomOut(), isDragging: isDragging(), + showEmptyBlockSideInserter, + showBlockToolbarPopover, + getBlocksByClientId, + getSelectedBlockClientIds, + getBlockRootClientId, + isGroupable, + getBlockName, + getGroupingBlockName, }; } @@ -67,20 +97,23 @@ export default function BlockTools( { __unstableContentRef, ...props } ) { - const { clientId, hasFixedToolbar, isTyping, isZoomOutMode, isDragging } = - useSelect( selector, [] ); - - const isMatch = useShortcutEventMatch(); const { + clientId, + hasFixedToolbar, + isTyping, + isZoomOutMode, + isDragging, getBlocksByClientId, getSelectedBlockClientIds, getBlockRootClientId, isGroupable, getBlockName, - } = useSelect( blockEditorStore ); - const { getGroupingBlockName } = useSelect( blocksStore ); - const { showEmptyBlockSideInserter, showBlockToolbarPopover } = - useShowBlockTools(); + getGroupingBlockName, + showEmptyBlockSideInserter, + showBlockToolbarPopover, + } = useSelect( selector, [] ); + + const isMatch = useShortcutEventMatch(); const pasteStyles = usePasteStyles(); const { diff --git a/packages/block-editor/src/components/block-tools/insertion-point.js b/packages/block-editor/src/components/block-tools/insertion-point.js index 55d6a2a4864fa3..b69772f6c8641a 100644 --- a/packages/block-editor/src/components/block-tools/insertion-point.js +++ b/packages/block-editor/src/components/block-tools/insertion-point.js @@ -45,6 +45,7 @@ function InbetweenInsertionPointPopover( { isInserterShown, isDistractionFree, isZoomOutMode, + getBlockEditingMode, } = useSelect( ( select ) => { const { getBlockOrder, @@ -55,6 +56,7 @@ function InbetweenInsertionPointPopover( { getNextBlockClientId, getSettings, isZoomOut, + getBlockEditingMode: _getBlockEditingMode, } = unlock( select( blockEditorStore ) ); const insertionPoint = getBlockInsertionPoint(); const order = getBlockOrder( insertionPoint.rootClientId ); @@ -86,9 +88,9 @@ function InbetweenInsertionPointPopover( { isDistractionFree: settings.isDistractionFree, isInserterShown: insertionPoint?.__unstableWithInserter, isZoomOutMode: isZoomOut(), + getBlockEditingMode: _getBlockEditingMode, }; }, [] ); - const { getBlockEditingMode } = useSelect( blockEditorStore ); const disableMotion = useReducedMotion(); diff --git a/packages/block-editor/src/components/block-tools/should-show-block-tools.js b/packages/block-editor/src/components/block-tools/should-show-block-tools.js new file mode 100644 index 00000000000000..c30cb3d212bd22 --- /dev/null +++ b/packages/block-editor/src/components/block-tools/should-show-block-tools.js @@ -0,0 +1,46 @@ +/** + * WordPress dependencies + */ +import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; + +/** + * Determines which block tools should be showing based on the current editor state. + * This is a pure function that can be called from within a selector. + * + * @param {Object} options Calculation options. + * @param {Object} options.block The selected block object. + * @param {string} options.blockMode The block mode ('visual' or 'html'). + * @param {boolean} options.isBlockInterfaceHidden Whether the block interface is hidden. + * @param {string} options.clientId The selected block client ID. + * @param {boolean} options.isTyping Whether the user is currently typing. + * @param {boolean} options.hasFixedToolbar Whether the toolbar is fixed. + * + * @return {Object} Object of which block tools will be shown. + */ +export function shouldShowBlockTools( { + block, + blockMode, + isBlockInterfaceHidden, + clientId, + isTyping, + hasFixedToolbar, +} ) { + const hasSelectedBlock = !! clientId && !! block; + const isEmptyDefaultBlock = + hasSelectedBlock && + isUnmodifiedDefaultBlock( block, 'content' ) && + blockMode !== 'html'; + const showEmptyBlockSideInserter = + clientId && ! isTyping && isEmptyDefaultBlock; + const showBlockToolbarPopover = + ! isBlockInterfaceHidden && + ! hasFixedToolbar && + ! showEmptyBlockSideInserter && + hasSelectedBlock && + ! isEmptyDefaultBlock; + + return { + showEmptyBlockSideInserter, + showBlockToolbarPopover, + }; +} diff --git a/packages/block-editor/src/components/block-tools/use-show-block-tools.js b/packages/block-editor/src/components/block-tools/use-show-block-tools.js index 2b9236bb25b953..fb4b78f24a2b0d 100644 --- a/packages/block-editor/src/components/block-tools/use-show-block-tools.js +++ b/packages/block-editor/src/components/block-tools/use-show-block-tools.js @@ -1,53 +1,46 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; /** - * Internal dependencies - */ -import { store as blockEditorStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -/** - * Source of truth for which block tools are showing in the block editor. + * Calculates which block tools should be showing based on the current editor state. + * This is a pure function that can be called from within a selector. + * + * @param {Object} options Calculation options. + * @param {Object} options.block The selected block object. + * @param {string} options.blockMode The block mode ('visual' or 'html'). + * @param {boolean} options.isBlockInterfaceHidden Whether the block interface is hidden. + * @param {string} options.clientId The selected block client ID. + * @param {boolean} options.isTyping Whether the user is currently typing. + * @param {boolean} options.hasFixedToolbar Whether the toolbar is fixed. * * @return {Object} Object of which block tools will be shown. */ -export function useShowBlockTools() { - return useSelect( ( select ) => { - const { - getSelectedBlockClientId, - getFirstMultiSelectedBlockClientId, - getBlock, - getBlockMode, - getSettings, - isTyping, - isBlockInterfaceHidden, - } = unlock( select( blockEditorStore ) ); - - const clientId = - getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId(); - - const block = getBlock( clientId ); - const hasSelectedBlock = !! clientId && !! block; - const isEmptyDefaultBlock = - hasSelectedBlock && - isUnmodifiedDefaultBlock( block, 'content' ) && - getBlockMode( clientId ) !== 'html'; - const _showEmptyBlockSideInserter = - clientId && ! isTyping() && isEmptyDefaultBlock; - const _showBlockToolbarPopover = - ! isBlockInterfaceHidden() && - ! getSettings().hasFixedToolbar && - ! _showEmptyBlockSideInserter && - hasSelectedBlock && - ! isEmptyDefaultBlock; +export function calculateShowBlockTools( { + block, + blockMode, + isBlockInterfaceHidden, + clientId, + isTyping, + hasFixedToolbar, +} ) { + const hasSelectedBlock = !! clientId && !! block; + const isEmptyDefaultBlock = + hasSelectedBlock && + isUnmodifiedDefaultBlock( block, 'content' ) && + blockMode !== 'html'; + const showEmptyBlockSideInserter = + clientId && ! isTyping && isEmptyDefaultBlock; + const showBlockToolbarPopover = + ! isBlockInterfaceHidden && + ! hasFixedToolbar && + ! showEmptyBlockSideInserter && + hasSelectedBlock && + ! isEmptyDefaultBlock; - return { - showEmptyBlockSideInserter: _showEmptyBlockSideInserter, - showBlockToolbarPopover: _showBlockToolbarPopover, - }; - }, [] ); + return { + showEmptyBlockSideInserter, + showBlockToolbarPopover, + }; } diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index 4ab4c69a41f311..fbce7c1880b46f 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -142,12 +142,18 @@ function BordersInspectorControl( { label, children, resetAllFilter } ) { export function BorderPanel( { clientId, name, setAttributes, settings } ) { const isEnabled = useHasBorderPanel( settings ); - function selector( select ) { - const { style, borderColor } = - select( blockEditorStore ).getBlockAttributes( clientId ) || {}; - return { style, borderColor }; - } - const { style, borderColor } = useSelect( selector, [ clientId ] ); + const { style, borderColor } = useSelect( + ( select ) => { + // Early return to avoid subscription when disabled + if ( ! isEnabled ) { + return {}; + } + const { style: _style, borderColor: _borderColor } = + select( blockEditorStore ).getBlockAttributes( clientId ) || {}; + return { style: _style, borderColor: _borderColor }; + }, + [ clientId, isEnabled ] + ); const value = useMemo( () => { return attributesToStyle( { style, borderColor } ); }, [ style, borderColor ] ); diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js index ef8984c9367853..237a462dab7b14 100644 --- a/packages/block-editor/src/hooks/color.js +++ b/packages/block-editor/src/hooks/color.js @@ -262,14 +262,27 @@ function ColorInspectorControl( { children, resetAllFilter } ) { export function ColorEdit( { clientId, name, setAttributes, settings } ) { const isEnabled = useHasColorPanel( settings ); - function selector( select ) { - const { style, textColor, backgroundColor, gradient } = - select( blockEditorStore ).getBlockAttributes( clientId ) || {}; - return { style, textColor, backgroundColor, gradient }; - } + const { style, textColor, backgroundColor, gradient } = useSelect( - selector, - [ clientId ] + ( select ) => { + // Early return to avoid subscription when disabled + if ( ! isEnabled ) { + return {}; + } + const { + style: _style, + textColor: _textColor, + backgroundColor: _backgroundColor, + gradient: _gradient, + } = select( blockEditorStore ).getBlockAttributes( clientId ) || {}; + return { + style: _style, + textColor: _textColor, + backgroundColor: _backgroundColor, + gradient: _gradient, + }; + }, + [ clientId, isEnabled ] ); const value = useMemo( () => { return attributesToStyle( { diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js index ffa4048b7740e3..941bf645413743 100644 --- a/packages/block-editor/src/hooks/dimensions.js +++ b/packages/block-editor/src/hooks/dimensions.js @@ -71,9 +71,15 @@ function DimensionsInspectorControl( { children, resetAllFilter } ) { export function DimensionsPanel( { clientId, name, setAttributes, settings } ) { const isEnabled = useHasDimensionsPanel( settings ); const value = useSelect( - ( select ) => - select( blockEditorStore ).getBlockAttributes( clientId )?.style, - [ clientId ] + ( select ) => { + // Early return to avoid subscription when disabled + if ( ! isEnabled ) { + return undefined; + } + return select( blockEditorStore ).getBlockAttributes( clientId ) + ?.style; + }, + [ clientId, isEnabled ] ); const [ visualizedProperty, setVisualizedProperty ] = useVisualizer(); const onChange = ( newStyle ) => { diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 003a680940b977..772b86b334eee4 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -74,13 +74,17 @@ export function useLayoutClasses( blockAttributes = {}, blockName = '' ) { const hasGlobalPadding = useSelect( ( select ) => { - return ( - ( usedLayout?.inherit || - usedLayout?.contentSize || - usedLayout?.type === 'constrained' ) && - select( blockEditorStore ).getSettings().__experimentalFeatures - ?.useRootPaddingAwareAlignments - ); + // Early return to avoid subscription when layout doesn't use global padding + if ( + ! usedLayout?.inherit && + ! usedLayout?.contentSize && + usedLayout?.type !== 'constrained' + ) { + return false; + } + + return select( blockEditorStore ).getSettings() + .__experimentalFeatures?.useRootPaddingAwareAlignments; }, [ usedLayout?.contentSize, usedLayout?.inherit, usedLayout?.type ] ); diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index 5c551a4bb35761..b19582911a778e 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -115,15 +115,29 @@ function TypographyInspectorControl( { children, resetAllFilter } ) { } export function TypographyPanel( { clientId, name, setAttributes, settings } ) { - function selector( select ) { - const { style, fontFamily, fontSize, fitText } = - select( blockEditorStore ).getBlockAttributes( clientId ) || {}; - return { style, fontFamily, fontSize, fitText }; - } - const { style, fontFamily, fontSize, fitText } = useSelect( selector, [ - clientId, - ] ); const isEnabled = useHasTypographyPanel( settings ); + + const { style, fontFamily, fontSize, fitText } = useSelect( + ( select ) => { + // Early return INSIDE selector to avoid subscription when disabled + if ( ! isEnabled ) { + return {}; + } + const { + style: _style, + fontFamily: _fontFamily, + fontSize: _fontSize, + fitText: _fitText, + } = select( blockEditorStore ).getBlockAttributes( clientId ) || {}; + return { + style: _style, + fontFamily: _fontFamily, + fontSize: _fontSize, + fitText: _fitText, + }; + }, + [ clientId, isEnabled ] + ); const value = useMemo( () => attributesToStyle( { style, fontFamily, fontSize } ), [ style, fontSize, fontFamily ] diff --git a/packages/edit-post/src/components/init-pattern-modal/index.js b/packages/edit-post/src/components/init-pattern-modal/index.js index 1bfeadd73f94a6..fdc633534895ae 100644 --- a/packages/edit-post/src/components/init-pattern-modal/index.js +++ b/packages/edit-post/src/components/init-pattern-modal/index.js @@ -19,19 +19,14 @@ export default function InitPatternModal() { const [ syncType, setSyncType ] = useState( undefined ); const [ title, setTitle ] = useState( '' ); - const { postType, isNewPost } = useSelect( ( select ) => { - const { getEditedPostAttribute, isCleanNewPost } = - select( editorStore ); - return { - postType: getEditedPostAttribute( 'type' ), - isNewPost: isCleanNewPost(), - }; - }, [] ); - const [ isModalOpen, setIsModalOpen ] = useState( - () => isNewPost && postType === 'wp_block' + const isNewPost = useSelect( + ( select ) => select( editorStore ).isCleanNewPost(), + [] ); - if ( postType !== 'wp_block' || ! isNewPost ) { + const [ isModalOpen, setIsModalOpen ] = useState( () => isNewPost ); + + if ( ! isNewPost ) { return null; } diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 6bf2b2349d240f..61d1cef6441d28 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -673,7 +673,9 @@ function Layout( { - + { currentPostType === 'wp_block' && ( + + ) } { backButton } diff --git a/packages/editor/src/components/pattern-duplicate-modal/index.js b/packages/editor/src/components/pattern-duplicate-modal/index.js index 2e5593536d1834..4c6ff6f83ef43f 100644 --- a/packages/editor/src/components/pattern-duplicate-modal/index.js +++ b/packages/editor/src/components/pattern-duplicate-modal/index.js @@ -17,25 +17,33 @@ const { DuplicatePatternModal } = unlock( patternsPrivateApis ); export const modalName = 'editor/pattern-duplicate'; export default function PatternDuplicateModal() { - const { record, postType } = useSelect( ( select ) => { - const { getCurrentPostType, getCurrentPostId } = select( editorStore ); - const { getEditedEntityRecord } = select( coreStore ); - const _postType = getCurrentPostType(); - return { - record: getEditedEntityRecord( - 'postType', - _postType, - getCurrentPostId() - ), - postType: _postType, - }; - }, [] ); - const { closeModal } = useDispatch( interfaceStore ); - const isActive = useSelect( ( select ) => select( interfaceStore ).isModalActive( modalName ) ); + const { record, postType } = useSelect( + ( select ) => { + if ( ! isActive ) { + return {}; + } + + const { getCurrentPostType, getCurrentPostId } = + select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const _postType = getCurrentPostType(); + return { + record: getEditedEntityRecord( + 'postType', + _postType, + getCurrentPostId() + ), + postType: _postType, + }; + }, + [ isActive ] + ); + const { closeModal } = useDispatch( interfaceStore ); + if ( ! isActive || postType !== PATTERN_POST_TYPE ) { return null; } diff --git a/packages/editor/src/components/pattern-rename-modal/index.js b/packages/editor/src/components/pattern-rename-modal/index.js index ea9a46e977b240..085efbb4cf33c4 100644 --- a/packages/editor/src/components/pattern-rename-modal/index.js +++ b/packages/editor/src/components/pattern-rename-modal/index.js @@ -17,25 +17,34 @@ const { RenamePatternModal } = unlock( patternsPrivateApis ); export const modalName = 'editor/pattern-rename'; export default function PatternRenameModal() { - const { record, postType } = useSelect( ( select ) => { - const { getCurrentPostType, getCurrentPostId } = select( editorStore ); - const { getEditedEntityRecord } = select( coreStore ); - const _postType = getCurrentPostType(); - return { - record: getEditedEntityRecord( - 'postType', - _postType, - getCurrentPostId() - ), - postType: _postType, - }; - }, [] ); - const { closeModal } = useDispatch( interfaceStore ); - const isActive = useSelect( ( select ) => select( interfaceStore ).isModalActive( modalName ) ); + const { record, postType } = useSelect( + ( select ) => { + if ( ! isActive ) { + return {}; + } + + const { getCurrentPostType, getCurrentPostId } = + select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const _postType = getCurrentPostType(); + return { + record: getEditedEntityRecord( + 'postType', + _postType, + getCurrentPostId() + ), + postType: _postType, + }; + }, + [ isActive ] + ); + + const { closeModal } = useDispatch( interfaceStore ); + if ( ! isActive || postType !== PATTERN_POST_TYPE ) { return null; }