UNPKG

@wordpress/editor

Version:
447 lines (422 loc) 13.8 kB
/** * WordPress dependencies */ import { useMemo, useCallback } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { store as coreStore, __experimentalFetchLinkSuggestions as fetchLinkSuggestions, __experimentalFetchUrlData as fetchUrlData, privateApis as coreDataPrivateApis, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; import { store as preferencesStore } from '@wordpress/preferences'; import { useViewportMatch } from '@wordpress/compose'; import { store as blocksStore } from '@wordpress/blocks'; import { privateApis, store as blockEditorStore, } from '@wordpress/block-editor'; /** * Internal dependencies */ import inserterMediaCategories from '../media-categories'; import { mediaUpload } from '../../utils'; import mediaUploadOnSuccess from '../../utils/media-upload/on-success'; import { default as mediaSideload } from '../../utils/media-sideload'; import { default as mediaFinalize } from '../../utils/media-finalize'; import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import { useGlobalStylesContext } from '../global-styles-provider'; const EMPTY_OBJECT = {}; function __experimentalReusableBlocksSelect( select ) { const { RECEIVE_INTERMEDIATE_RESULTS } = unlock( coreDataPrivateApis ); const { getEntityRecords } = select( coreStore ); return getEntityRecords( 'postType', 'wp_block', { per_page: -1, [ RECEIVE_INTERMEDIATE_RESULTS ]: true, } ); } const BLOCK_EDITOR_SETTINGS = [ '__experimentalBlockBindingsSupportedAttributes', '__experimentalBlockDirectory', '__experimentalDiscussionSettings', '__experimentalFeatures', '__experimentalGlobalStylesBaseStyles', 'allImageSizes', 'alignWide', 'blockInspectorTabs', 'maxUploadFileSize', 'allowedMimeTypes', 'bodyPlaceholder', 'canEditCSS', 'canLockBlocks', 'canUpdateBlockBindings', 'capabilities', 'clearBlockSelection', 'codeEditingEnabled', 'colors', 'disableContentOnlyForUnsyncedPatterns', 'disableCustomColors', 'disableCustomFontSizes', 'disableCustomSpacingSizes', 'disableCustomGradients', 'disableLayoutStyles', 'enableCustomLineHeight', 'enableCustomSpacing', 'enableCustomUnits', 'enableOpenverseMediaCategory', 'fontSizes', 'gradients', 'generateAnchors', 'onNavigateToEntityRecord', 'imageDefaultSize', 'imageDimensions', 'imageEditing', 'imageSizes', 'isPreviewMode', 'isRTL', 'locale', 'maxWidth', 'postContentAttributes', 'postsPerPage', 'readOnly', 'styles', 'titlePlaceholder', 'supportsLayout', 'widgetTypesToHideFromLegacyWidgetBlock', '__unstableHasCustomAppender', '__unstableResolvedAssets', '__unstableIsBlockBasedTheme', ]; const { globalStylesDataKey, globalStylesLinksDataKey, selectBlockPatternsKey, reusableBlocksSelectKey, sectionRootClientIdKey, mediaEditKey, getMediaSelectKey, isIsolatedEditorKey, deviceTypeKey, isNavigationOverlayContextKey, isNavigationPostEditorKey, mediaUploadOnSuccessKey, } = unlock( privateApis ); /** * React hook used to compute the block editor settings to use for the post editor. * * @param {Object} settings EditorProvider settings prop. * @param {string} postType Editor root level post type. * @param {string} postId Editor root level post ID. * @param {string} renderingMode Editor rendering mode. * * @return {Object} Block Editor Settings. */ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { const isLargeViewport = useViewportMatch( 'medium' ); const { allImageSizes, bigImageSizeThreshold, allowRightClickOverrides, blockTypes, focusMode, hasFixedToolbar, isDistractionFree, keepCaretInsideBlock, hasUploadPermissions, hiddenBlockTypes, canUseUnfilteredHTML, userCanCreatePages, pageOnFront, pageForPosts, userPatternCategories, restBlockPatternCategories, sectionRootClientId, deviceType, isNavigationOverlayContext, isRevisionsMode, } = useSelect( ( select ) => { const { canUser, getRawEntityRecord, getEntityRecord, getUserPatternCategories, getBlockPatternCategories, } = select( coreStore ); const { get } = select( preferencesStore ); const { getBlockTypes } = select( blocksStore ); const { getDeviceType, isRevisionsMode: _isRevisionsMode } = unlock( select( editorStore ) ); const { getBlocksByName, getBlockAttributes } = select( blockEditorStore ); const siteSettings = canUser( 'read', { kind: 'root', name: 'site', } ) ? getEntityRecord( 'root', 'site' ) : undefined; // Fetch image sizes from REST API index for client-side media processing. const baseData = getEntityRecord( 'root', '__unstableBase' ); function getSectionRootBlock() { if ( renderingMode === 'template-locked' ) { return getBlocksByName( 'core/post-content' )?.[ 0 ] ?? ''; } return ( getBlocksByName( 'core/group' ).find( ( clientId ) => getBlockAttributes( clientId )?.tagName === 'main' ) ?? '' ); } return { allImageSizes: baseData?.image_sizes, bigImageSizeThreshold: baseData?.image_size_threshold, allowRightClickOverrides: get( 'core', 'allowRightClickOverrides' ), blockTypes: getBlockTypes(), canUseUnfilteredHTML: getRawEntityRecord( 'postType', postType, postId )?._links?.hasOwnProperty( 'wp:action-unfiltered-html' ), focusMode: get( 'core', 'focusMode' ), hasFixedToolbar: get( 'core', 'fixedToolbar' ) || ! isLargeViewport, hiddenBlockTypes: get( 'core', 'hiddenBlockTypes' ), isDistractionFree: get( 'core', 'distractionFree' ), keepCaretInsideBlock: get( 'core', 'keepCaretInsideBlock' ), hasUploadPermissions: canUser( 'create', { kind: 'postType', name: 'attachment', } ) ?? true, userCanCreatePages: canUser( 'create', { kind: 'postType', name: 'page', } ), pageOnFront: siteSettings?.page_on_front, pageForPosts: siteSettings?.page_for_posts, userPatternCategories: getUserPatternCategories(), restBlockPatternCategories: getBlockPatternCategories(), sectionRootClientId: getSectionRootBlock(), deviceType: getDeviceType(), isNavigationOverlayContext: postType === 'wp_template_part' && postId ? getEntityRecord( 'postType', 'wp_template_part', postId )?.area === 'navigation-overlay' : false, isRevisionsMode: _isRevisionsMode(), }; }, [ postType, postId, isLargeViewport, renderingMode ] ); const { merged: mergedGlobalStyles } = useGlobalStylesContext(); const globalStylesData = mergedGlobalStyles.styles ?? EMPTY_OBJECT; const globalStylesLinksData = mergedGlobalStyles._links ?? EMPTY_OBJECT; const settingsBlockPatterns = settings.__experimentalAdditionalBlockPatterns ?? // WP 6.0 settings.__experimentalBlockPatterns; // WP 5.9 const settingsBlockPatternCategories = settings.__experimentalAdditionalBlockPatternCategories ?? // WP 6.0 settings.__experimentalBlockPatternCategories; // WP 5.9 const blockPatterns = useMemo( () => [ ...( settingsBlockPatterns || [] ) ].filter( ( { postTypes } ) => { return ( ! postTypes || ( Array.isArray( postTypes ) && postTypes.includes( postType ) ) ); } ), [ settingsBlockPatterns, postType ] ); const blockPatternCategories = useMemo( () => [ ...( settingsBlockPatternCategories || [] ), ...( restBlockPatternCategories || [] ), ].filter( ( x, index, arr ) => index === arr.findIndex( ( y ) => x.name === y.name ) ), [ settingsBlockPatternCategories, restBlockPatternCategories ] ); const { undo, setIsInserterOpened } = useDispatch( editorStore ); const { editMediaEntity } = unlock( useDispatch( coreStore ) ); const { saveEntityRecord } = useDispatch( coreStore ); /** * Creates a Post entity. * This is utilised by the Link UI to allow for on-the-fly creation of Posts/Pages. * * @param {Object} options parameters for the post being created. These mirror those used on 3rd param of saveEntityRecord. * @return {Object} the post type object that was created. */ const createPageEntity = useCallback( ( options ) => { if ( ! userCanCreatePages ) { return Promise.reject( { message: __( 'You do not have permission to create Pages.' ), } ); } return saveEntityRecord( 'postType', 'page', options ); }, [ saveEntityRecord, userCanCreatePages ] ); const allowedBlockTypes = useMemo( () => { // Omit hidden block types if exists and non-empty. if ( hiddenBlockTypes && hiddenBlockTypes.length > 0 ) { // Defer to passed setting for `allowedBlockTypes` if provided as // anything other than `true` (where `true` is equivalent to allow // all block types). const defaultAllowedBlockTypes = true === settings.allowedBlockTypes ? blockTypes.map( ( { name } ) => name ) : settings.allowedBlockTypes || []; return defaultAllowedBlockTypes.filter( ( type ) => ! hiddenBlockTypes.includes( type ) ); } return settings.allowedBlockTypes; }, [ settings.allowedBlockTypes, hiddenBlockTypes, blockTypes ] ); const forceDisableFocusMode = settings.focusMode === false; return useMemo( () => { const blockEditorSettings = { ...Object.fromEntries( Object.entries( settings ).filter( ( [ key ] ) => BLOCK_EDITOR_SETTINGS.includes( key ) ) ), [ globalStylesDataKey ]: globalStylesData, [ globalStylesLinksDataKey ]: globalStylesLinksData, allImageSizes, bigImageSizeThreshold, allowedBlockTypes, allowRightClickOverrides, focusMode: focusMode && ! forceDisableFocusMode, hasFixedToolbar, isDistractionFree, keepCaretInsideBlock, [ getMediaSelectKey ]: ( select, attachmentId ) => { return select( coreStore ).getEntityRecord( 'postType', 'attachment', attachmentId ); }, [ mediaEditKey ]: hasUploadPermissions ? editMediaEntity : undefined, mediaUpload: hasUploadPermissions ? mediaUpload : undefined, [ mediaUploadOnSuccessKey ]: hasUploadPermissions ? mediaUploadOnSuccess : undefined, mediaSideload: hasUploadPermissions ? mediaSideload : undefined, mediaFinalize: hasUploadPermissions ? mediaFinalize : undefined, __experimentalBlockPatterns: blockPatterns, [ selectBlockPatternsKey ]: ( select ) => { const { hasFinishedResolution, getBlockPatternsForPostType } = unlock( select( coreStore ) ); const patterns = getBlockPatternsForPostType( postType ); return hasFinishedResolution( 'getBlockPatterns' ) ? patterns : undefined; }, [ reusableBlocksSelectKey ]: __experimentalReusableBlocksSelect, __experimentalBlockPatternCategories: blockPatternCategories, __experimentalUserPatternCategories: userPatternCategories, __experimentalFetchLinkSuggestions: ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ), inserterMediaCategories, __experimentalFetchRichUrlData: fetchUrlData, // Todo: This only checks the top level post, not the post within a template or any other entity that can be edited. // This might be better as a generic "canUser" selector. __experimentalCanUserUseUnfilteredHTML: canUseUnfilteredHTML, //Todo: this is only needed for native and should probably be removed. __experimentalUndo: undo, // Check whether we want all site editor frames to have outlines // including the navigation / pattern / parts editors. outlineMode: ! isDistractionFree && postType === 'wp_template', // Check these two properties: they were not present in the site editor. __experimentalCreatePageEntity: createPageEntity, __experimentalUserCanCreatePages: userCanCreatePages, pageOnFront, pageForPosts, __experimentalPreferPatternsOnRoot: postType === 'wp_template', templateLock: postType === 'wp_navigation' ? 'insert' : settings.templateLock, template: postType === 'wp_navigation' ? [ [ 'core/navigation', {}, [] ] ] : settings.template, __experimentalSetIsInserterOpened: setIsInserterOpened, [ sectionRootClientIdKey ]: sectionRootClientId, editorTool: renderingMode === 'post-only' && postType !== 'wp_template' ? 'edit' : undefined, // When editing template parts, patterns, or navigation directly, // we're in an isolated editing context (focused on that entity alone). [ isIsolatedEditorKey ]: [ 'wp_template_part', 'wp_block', 'wp_navigation', ].includes( postType ), [ isNavigationPostEditorKey ]: postType === 'wp_navigation', // When in template-locked mode (e.g., "Show Template" in the post editor), // don't treat template parts as contentOnly sections. disableContentOnlyForTemplateParts: renderingMode === 'template-locked', ...( deviceType ? { [ deviceTypeKey ]: deviceType } : {} ), [ isNavigationOverlayContextKey ]: isNavigationOverlayContext, }; if ( isRevisionsMode ) { blockEditorSettings.isPreviewMode = true; } return blockEditorSettings; }, [ isRevisionsMode, allowedBlockTypes, allowRightClickOverrides, focusMode, forceDisableFocusMode, hasFixedToolbar, isDistractionFree, keepCaretInsideBlock, settings, hasUploadPermissions, userPatternCategories, blockPatterns, blockPatternCategories, canUseUnfilteredHTML, undo, createPageEntity, userCanCreatePages, pageOnFront, pageForPosts, postType, setIsInserterOpened, sectionRootClientId, globalStylesData, globalStylesLinksData, renderingMode, editMediaEntity, settings.onNavigateToEntityRecord, deviceType, allImageSizes, bigImageSizeThreshold, isNavigationOverlayContext, ] ); } export default useBlockEditorSettings;