diff --git a/docs/designers-developers/developers/slotfills/plugin-preview.md b/docs/designers-developers/developers/slotfills/plugin-preview.md new file mode 100644 index 00000000000000..f950deda2533c9 --- /dev/null +++ b/docs/designers-developers/developers/slotfills/plugin-preview.md @@ -0,0 +1,61 @@ +# PluginPreview + +Renders the main content area when that menu item is chosen. + +A component used to define a custom preview menu item and optional content. + +The children of this component will be displayed in the main area of the +block editor, instead of the `VisualEditor` component. + +The `title` and `icon` are used to populate the Preview menu item. + +## Available Props + +- **children** `WPElement`: Preview content. +- **icon** `WPIcon`: Menu item icon to be rendered. +- **name** `string`: A unique name of the custom preview. +- **onClick** `Function`: Menu item click handler, e.g. for previews that provide no content (`children`). +- **title** `string`: Menu item title. + +## Example + +```js +import { __ } from '@wordpress/i18n'; +import { PluginPreview } from '@wordpress/block-editor'; +import { registerPlugin } from '@wordpress/plugins'; +import { external } from '@wordpress/icons'; + +const PluginPreviewTest = () => ( + <> + +

+ { __( 'Custom Preview 1 Content' ) } +

+
+ + console.log( event ) } + > +

+ { __( 'Custom Preview 2 Content' ) } +

+
+ + console.log( event ) } + /> + +); + +registerPlugin( "plugin-preview-test", { + render: PluginPreviewTest, +} ); +``` diff --git a/docs/reference-guides/slotfills/README.md b/docs/reference-guides/slotfills/README.md index 5be4de892073d1..4734e347e60303 100644 --- a/docs/reference-guides/slotfills/README.md +++ b/docs/reference-guides/slotfills/README.md @@ -104,3 +104,7 @@ The following SlotFills are available in the `edit-post` package. Please refer t - [PluginPrePublishPanel](/docs/reference-guides/slotfills/plugin-pre-publish-panel.md) - [PluginSidebar](/docs/reference-guides/slotfills/plugin-sidebar.md) - [PluginSidebarMoreMenuItem](/docs/reference-guides/slotfills/plugin-sidebar-more-menu-item.md) + +The following SlotFill is available in the `block-editor` package: + +- [PluginPreview](/docs/designers-developers/developers/slotfills/plugin-preview.md) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index da2515ead12b03..051f330718b6c6 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -281,7 +281,24 @@ Undocumented declaration. Undocumented declaration. -### createCustomColorsHOC +# **coreDeviceTypes** + +An array of strings that represent `deviceType` values that belong to the +block editor core system. + +When the `deviceType` returned by `__experimentalGetPreviewDeviceType()`, is +one of these values, the built-in `VisualEditor` is responsible for rendering +a preview of that type. + +When the `deviceType` is something other than one of the `coreDeviceTypes`, +we are rendering a custom preview registered by the `` +component and defer to a `` filled by the plugin to draw the preview. + +_Type_ + +- `Array` + +# **createCustomColorsHOC** A higher-order component factory for creating a 'withCustomColors' HOC, which handles color logic for class generation color value, retrieval and color attribute setting. @@ -520,7 +537,25 @@ _Related_ - -### PreserveScrollInReorder +# **PluginPreview** + +Component used to define a custom preview menu item and optional content. + +The children of this component will be displayed in the main area of the +block editor, instead of the `VisualEditor` component. + +The `title` and `icon` are used to populate the Preview menu item. + +_Parameters_ + +- _props_ `Object`: Component properties. +- _props.children_ `WPElement`: Preview content. +- _props.icon_ `WPIcon`: Menu item icon to be rendered. +- _props.name_ `string`: A unique name of the custom preview. +- _props.onClick_ `Function`: Menu item click handler, e.g. for previews that provide no content (`children`). +- _props.title_ `string`: Menu item title. + +# **PreserveScrollInReorder** Undocumented declaration. diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index fed9e9752194c7..c3401a4f4a6780 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -99,7 +99,11 @@ export { default as withColorContext } from './color-palette/with-color-context' export { default as __unstableBlockSettingsMenuFirstItem } from './block-settings-menu/block-settings-menu-first-item'; export { default as __unstableInserterMenuExtension } from './inserter-menu-extension'; -export { default as __experimentalPreviewOptions } from './preview-options'; +export { + default as __experimentalPreviewOptions, + coreDeviceTypes, +} from './preview-options'; +export { default as PluginPreview } from './plugin-preview'; export { default as __experimentalUseResizeCanvas } from './use-resize-canvas'; export { default as BlockInspector } from './block-inspector'; export { default as BlockList } from './block-list'; diff --git a/packages/block-editor/src/components/plugin-preview/index.js b/packages/block-editor/src/components/plugin-preview/index.js new file mode 100644 index 00000000000000..785f0c012a5518 --- /dev/null +++ b/packages/block-editor/src/components/plugin-preview/index.js @@ -0,0 +1,78 @@ +/** + * WordPress dependencies + */ +import { + __experimentalUseSlot as useSlot, + createSlotFill, + Fill, + MenuItem, +} from '@wordpress/components'; +import { check } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { coreDeviceTypes } from '../preview-options'; + +const { + Fill: PluginPreviewMenuFill, + Slot: PluginPreviewMenuSlot, +} = createSlotFill( 'PluginPreviewMenu' ); + +export { PluginPreviewMenuSlot }; + +/** + * Component used to define a custom preview menu item and optional content. + * + * The children of this component will be displayed in the main area of the + * block editor, instead of the `VisualEditor` component. + * + * The `title` and `icon` are used to populate the Preview menu item. + * + * @param {Object} props Component properties. + * @param {WPElement} props.children Preview content. + * @param {WPIcon} props.icon Menu item icon to be rendered. + * @param {string} props.name A unique name of the custom preview. + * @param {Function} props.onClick Menu item click handler, e.g. for previews + * that provide no content (`children`). + * @param {string} props.title Menu item title. + */ +export default function PluginPreview( { + children, + icon, + name, + onClick, + title, + ...props +} ) { + const previewSlotName = `core/block-editor/plugin-preview/${ name }`; + const previewSlot = useSlot( previewSlotName ); + + if ( coreDeviceTypes.includes( name ) ) { + return null; + } + + return ( + <> + + { ( { deviceType, setDeviceType } ) => ( + { + if ( name && previewSlot.fills?.length > 0 ) { + setDeviceType( name ); + } + if ( onClick ) { + onClick( ...args ); + } + } } + icon={ icon || ( deviceType === name && check ) } + { ...props } + > + { title } + + ) } + + { children && { children } } + + ); +} diff --git a/packages/block-editor/src/components/preview-options/index.js b/packages/block-editor/src/components/preview-options/index.js index 5fb6ba1063bd45..060e33455fd80e 100644 --- a/packages/block-editor/src/components/preview-options/index.js +++ b/packages/block-editor/src/components/preview-options/index.js @@ -7,10 +7,36 @@ import classnames from 'classnames'; * WordPress dependencies */ import { useViewportMatch } from '@wordpress/compose'; -import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components'; +import { + __experimentalUseSlot as useSlot, + DropdownMenu, + MenuGroup, + MenuItem, +} from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { check } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { PluginPreviewMenuSlot } from '../plugin-preview'; + +/** + * An array of strings that represent `deviceType` values that belong to the + * block editor core system. + * + * When the `deviceType` returned by `__experimentalGetPreviewDeviceType()`, is + * one of these values, the built-in `VisualEditor` is responsible for rendering + * a preview of that type. + * + * When the `deviceType` is something other than one of the `coreDeviceTypes`, + * we are rendering a custom preview registered by the `` + * component and defer to a `` filled by the plugin to draw the preview. + * + * @type {Array} + */ +export const coreDeviceTypes = [ 'Desktop', 'Tablet', 'Mobile' ]; + export default function PreviewOptions( { children, className, @@ -18,6 +44,7 @@ export default function PreviewOptions( { deviceType, setDeviceType, } ) { + const previewMenuSlot = useSlot( PluginPreviewMenuSlot.__unstableName ); const isMobile = useViewportMatch( 'small', '<' ); if ( isMobile ) return null; @@ -67,6 +94,18 @@ export default function PreviewOptions( { { __( 'Mobile' ) } + + { previewMenuSlot.fills?.length > 0 && ( + + + + ) } + { children } ) } diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index f0bd691a1019ff..cddb6fe7a4dcfa 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -34,7 +34,7 @@ import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; * Internal dependencies */ import TextEditor from '../text-editor'; -import VisualEditor from '../visual-editor'; +import VisualEditorOrPluginPreview from '../visual-editor/visual-editor-or-plugin-preview'; import EditPostKeyboardShortcuts from '../keyboard-shortcuts'; import KeyboardShortcutHelpModal from '../keyboard-shortcut-help-modal'; import PreferencesModal from '../preferences-modal'; @@ -85,8 +85,6 @@ function Layout( { styles } ) { showIconLabels, hasReducedUI, showBlockBreadcrumbs, - isTemplateMode, - documentLabel, } = useSelect( ( select ) => { const { getEditorSettings, getPostTypeLabel } = select( editorStore ); const editorSettings = getEditorSettings(); @@ -125,8 +123,6 @@ function Layout( { styles } ) { showBlockBreadcrumbs: select( editPostStore ).isFeatureActive( 'showBlockBreadcrumbs' ), - // translators: Default label for the Document in the Block Breadcrumb. - documentLabel: postTypeLabel || _x( 'Document', 'noun' ), }; }, [] ); const className = classnames( 'edit-post-layout', 'is-mode-' + mode, { @@ -228,13 +224,7 @@ function Layout( { styles } ) { ) } { isRichEditingEnabled && mode === 'visual' && ( - - ) } - { ! isTemplateMode && ( -
- - -
+ ) } { isMobileViewport && sidebarIsOpened && ( diff --git a/packages/edit-post/src/components/visual-editor/visual-editor-or-plugin-preview.js b/packages/edit-post/src/components/visual-editor/visual-editor-or-plugin-preview.js new file mode 100644 index 00000000000000..dadf43021a0308 --- /dev/null +++ b/packages/edit-post/src/components/visual-editor/visual-editor-or-plugin-preview.js @@ -0,0 +1,33 @@ +/** + * WordPress dependencies + */ +import { __experimentalUseSlot as useSlot, Slot } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import VisualEditor from './index'; +import { store as editPostStore } from '../../store'; + +/** + * Component that renders a preview slot fill if found or a VisualEditor instead. + * + * @param {Object} props Component properties. + */ +function VisualEditorOrPluginPreview( props ) { + const previewId = useSelect( + ( select ) => + select( editPostStore ).__experimentalGetPreviewDeviceType(), + [] + ); + const previewSlotName = `core/block-editor/plugin-preview/${ previewId }`; + const previewSlot = useSlot( previewSlotName ); + + if ( previewSlot.fills?.length > 0 ) { + return ; + } + + return ; +} +export default VisualEditorOrPluginPreview;