diff --git a/packages/editor/src/components/block-manager/category.js b/packages/editor/src/components/block-manager/category.js index e7125fa151f72..341584fee03b9 100644 --- a/packages/editor/src/components/block-manager/category.js +++ b/packages/editor/src/components/block-manager/category.js @@ -1,73 +1,79 @@ /** * WordPress dependencies */ -import { useMemo, useCallback } from '@wordpress/element'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useCallback } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; import { CheckboxControl } from '@wordpress/components'; -import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies */ import BlockTypesChecklist from './checklist'; -import { store as editorStore } from '../../store'; -import { unlock } from '../../lock-unlock'; -function BlockManagerCategory( { title, blockTypes } ) { +function BlockManagerCategory( { + title, + blockTypes, + selectedBlockTypes, + onChange, +} ) { const instanceId = useInstanceId( BlockManagerCategory ); - const { allowedBlockTypes, hiddenBlockTypes } = useSelect( ( select ) => { - const { getEditorSettings } = select( editorStore ); - const { get } = select( preferencesStore ); - return { - allowedBlockTypes: getEditorSettings().allowedBlockTypes, - hiddenBlockTypes: get( 'core', 'hiddenBlockTypes' ), - }; - }, [] ); - const filteredBlockTypes = useMemo( () => { - if ( allowedBlockTypes === true ) { - return blockTypes; - } - return blockTypes.filter( ( { name } ) => { - return allowedBlockTypes?.includes( name ); - } ); - }, [ allowedBlockTypes, blockTypes ] ); - const { showBlockTypes, hideBlockTypes } = unlock( - useDispatch( editorStore ) - ); + const toggleVisible = useCallback( - ( blockName, nextIsChecked ) => { + ( blockType, nextIsChecked ) => { if ( nextIsChecked ) { - showBlockTypes( blockName ); + onChange( [ ...selectedBlockTypes, blockType ] ); } else { - hideBlockTypes( blockName ); + onChange( + selectedBlockTypes.filter( + ( { name } ) => name !== blockType.name + ) + ); } }, - [ showBlockTypes, hideBlockTypes ] + [ selectedBlockTypes, onChange ] ); + const toggleAllVisible = useCallback( ( nextIsChecked ) => { - const blockNames = blockTypes.map( ( { name } ) => name ); if ( nextIsChecked ) { - showBlockTypes( blockNames ); + onChange( [ + ...selectedBlockTypes, + ...blockTypes.filter( + ( blockType ) => + ! selectedBlockTypes.find( + ( { name } ) => name === blockType.name + ) + ), + ] ); } else { - hideBlockTypes( blockNames ); + onChange( + selectedBlockTypes.filter( + ( selectedBlockType ) => + ! blockTypes.find( + ( { name } ) => name === selectedBlockType.name + ) + ) + ); } }, - [ blockTypes, showBlockTypes, hideBlockTypes ] + [ blockTypes, selectedBlockTypes, onChange ] ); - if ( ! filteredBlockTypes.length ) { + if ( ! blockTypes.length ) { return null; } - const checkedBlockNames = filteredBlockTypes + const checkedBlockNames = blockTypes .map( ( { name } ) => name ) - .filter( ( type ) => ! ( hiddenBlockTypes ?? [] ).includes( type ) ); + .filter( ( type ) => + ( selectedBlockTypes ?? [] ).some( + ( selectedBlockType ) => selectedBlockType.name === type + ) + ); const titleId = 'editor-block-manager__category-title-' + instanceId; - const isAllChecked = checkedBlockNames.length === filteredBlockTypes.length; + const isAllChecked = checkedBlockNames.length === blockTypes.length; const isIndeterminate = ! isAllChecked && checkedBlockNames.length > 0; return ( @@ -85,7 +91,7 @@ function BlockManagerCategory( { title, blockTypes } ) { label={ { title } } /> diff --git a/packages/editor/src/components/block-manager/checklist.js b/packages/editor/src/components/block-manager/checklist.js index 01bd06abdeba8..2839e2c9e14c1 100644 --- a/packages/editor/src/components/block-manager/checklist.js +++ b/packages/editor/src/components/block-manager/checklist.js @@ -17,7 +17,7 @@ function BlockTypesChecklist( { blockTypes, value, onItemChange } ) { label={ blockType.title } checked={ value.includes( blockType.name ) } onChange={ ( ...args ) => - onItemChange( blockType.name, ...args ) + onItemChange( blockType, ...args ) } /> diff --git a/packages/editor/src/components/block-manager/index.js b/packages/editor/src/components/block-manager/index.js index 4a1145839976f..5c719c62a5a0b 100644 --- a/packages/editor/src/components/block-manager/index.js +++ b/packages/editor/src/components/block-manager/index.js @@ -2,69 +2,49 @@ * WordPress dependencies */ import { store as blocksStore } from '@wordpress/blocks'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { SearchControl, Button } from '@wordpress/components'; import { __, _n, sprintf } from '@wordpress/i18n'; import { useEffect, useState } from '@wordpress/element'; import { useDebounce } from '@wordpress/compose'; import { speak } from '@wordpress/a11y'; -import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies */ -import { unlock } from '../../lock-unlock'; -import { store as editorStore } from '../../store'; import BlockManagerCategory from './category'; -export default function BlockManager() { +/** + * Provides a list of blocks with checkboxes. + * + * @param {Object} props Props. + * @param {Array} props.blockTypes An array of blocks. + * @param {Array} props.selectedBlockTypes An array of selected blocks. + * @param {Function} props.onChange Function to be called when the selected blocks change. + */ +export default function BlockManager( { + blockTypes, + selectedBlockTypes, + onChange, +} ) { const debouncedSpeak = useDebounce( speak, 500 ); const [ search, setSearch ] = useState( '' ); - const { showBlockTypes } = unlock( useDispatch( editorStore ) ); - - const { - blockTypes, - categories, - hasBlockSupport, - isMatchingSearchTerm, - numberOfHiddenBlocks, - } = useSelect( ( select ) => { - // Some hidden blocks become unregistered - // by removing for instance the plugin that registered them, yet - // they're still remain as hidden by the user's action. - // We consider "hidden", blocks which were hidden and - // are still registered. - const _blockTypes = select( blocksStore ).getBlockTypes(); - const hiddenBlockTypes = ( - select( preferencesStore ).get( 'core', 'hiddenBlockTypes' ) ?? [] - ).filter( ( hiddenBlock ) => { - return _blockTypes.some( - ( registeredBlock ) => registeredBlock.name === hiddenBlock - ); - } ); - + const { categories, isMatchingSearchTerm } = useSelect( ( select ) => { return { - blockTypes: _blockTypes, categories: select( blocksStore ).getCategories(), - hasBlockSupport: select( blocksStore ).hasBlockSupport, isMatchingSearchTerm: select( blocksStore ).isMatchingSearchTerm, - numberOfHiddenBlocks: - Array.isArray( hiddenBlockTypes ) && hiddenBlockTypes.length, }; }, [] ); - function enableAllBlockTypes( newBlockTypes ) { - const blockNames = newBlockTypes.map( ( { name } ) => name ); - showBlockTypes( blockNames ); + function enableAllBlockTypes() { + onChange( blockTypes ); } - const filteredBlockTypes = blockTypes.filter( - ( blockType ) => - hasBlockSupport( blockType, 'inserter', true ) && - ( ! search || isMatchingSearchTerm( blockType, search ) ) && - ( ! blockType.parent || - blockType.parent.includes( 'core/post-content' ) ) - ); + const filteredBlockTypes = blockTypes.filter( ( blockType ) => { + return ! search || isMatchingSearchTerm( blockType, search ); + } ); + + const numberOfHiddenBlocks = blockTypes.length - selectedBlockTypes.length; // Announce search results on change useEffect( () => { @@ -96,9 +76,7 @@ export default function BlockManager() { @@ -131,6 +109,8 @@ export default function BlockManager() { ( blockType ) => blockType.category === category.slug ) } + selectedBlockTypes={ selectedBlockTypes } + onChange={ onChange } /> ) ) } ! category ) } + selectedBlockTypes={ selectedBlockTypes } + onChange={ onChange } /> diff --git a/packages/editor/src/components/preferences-modal/block-visibility.js b/packages/editor/src/components/preferences-modal/block-visibility.js new file mode 100644 index 0000000000000..49d6888c5dbfe --- /dev/null +++ b/packages/editor/src/components/preferences-modal/block-visibility.js @@ -0,0 +1,94 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as preferencesStore } from '@wordpress/preferences'; +import { hasBlockSupport, store as blocksStore } from '@wordpress/blocks'; +import { useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; +import BlockManager from '../block-manager'; + +export default function BlockVisibility() { + const { showBlockTypes, hideBlockTypes } = unlock( + useDispatch( editorStore ) + ); + + const { + blockTypes, + allowedBlockTypes: _allowedBlockTypes, + hiddenBlockTypes: _hiddenBlockTypes, + } = useSelect( ( select ) => { + return { + blockTypes: select( blocksStore ).getBlockTypes(), + allowedBlockTypes: + select( editorStore ).getEditorSettings().allowedBlockTypes, + hiddenBlockTypes: + select( preferencesStore ).get( 'core', 'hiddenBlockTypes' ) ?? + [], + }; + }, [] ); + + const allowedBlockTypes = useMemo( () => { + if ( _allowedBlockTypes === true ) { + return blockTypes; + } + return blockTypes.filter( ( { name } ) => { + return _allowedBlockTypes?.includes( name ); + } ); + }, [ _allowedBlockTypes, blockTypes ] ); + + const filteredBlockTypes = allowedBlockTypes.filter( + ( blockType ) => + hasBlockSupport( blockType, 'inserter', true ) && + ( ! blockType.parent || + blockType.parent.includes( 'core/post-content' ) ) + ); + + // Some hidden blocks become unregistered + // by removing for instance the plugin that registered them, yet + // they're still remain as hidden by the user's action. + // We consider "hidden", blocks which were hidden and + // are still registered. + const hiddenBlockTypes = _hiddenBlockTypes.filter( ( hiddenBlock ) => { + return filteredBlockTypes.some( + ( registeredBlock ) => registeredBlock.name === hiddenBlock + ); + } ); + + const selectedBlockTypes = filteredBlockTypes.filter( + ( blockType ) => ! hiddenBlockTypes.includes( blockType.name ) + ); + + const onChangeSelectedBlockTypes = ( newSelectedBlockTypes ) => { + if ( selectedBlockTypes.length > newSelectedBlockTypes.length ) { + const blockTypesToHide = selectedBlockTypes.filter( + ( blockType ) => + ! newSelectedBlockTypes.find( + ( { name } ) => name === blockType.name + ) + ); + hideBlockTypes( blockTypesToHide.map( ( { name } ) => name ) ); + } else if ( selectedBlockTypes.length < newSelectedBlockTypes.length ) { + const blockTypesToShow = newSelectedBlockTypes.filter( + ( blockType ) => + ! selectedBlockTypes.find( + ( { name } ) => name === blockType.name + ) + ); + showBlockTypes( blockTypesToShow.map( ( { name } ) => name ) ); + } + }; + + return ( + + ); +} diff --git a/packages/editor/src/components/preferences-modal/index.js b/packages/editor/src/components/preferences-modal/index.js index 7ea7ea456ce28..72042bca03b70 100644 --- a/packages/editor/src/components/preferences-modal/index.js +++ b/packages/editor/src/components/preferences-modal/index.js @@ -18,7 +18,7 @@ import { store as interfaceStore } from '@wordpress/interface'; import EnablePanelOption from './enable-panel'; import EnablePluginDocumentSettingPanelOption from './enable-plugin-document-setting-panel'; import EnablePublishSidebarOption from './enable-publish-sidebar'; -import BlockManager from '../block-manager'; +import BlockVisibility from './block-visibility'; import PostTaxonomies from '../post-taxonomies'; import PostFeaturedImageCheck from '../post-featured-image/check'; import PostExcerptCheck from '../post-excerpt/check'; @@ -297,7 +297,7 @@ function PreferencesModalContents( { extraSections = {} } ) { "Disable blocks that you don't want to appear in the inserter. They can always be toggled back on later." ) } > - + ),