-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Make the Preview Menu extensible #31984
Changes from 10 commits
81d03cb
64cb27d
a690338
bfb8976
36bca61
5a93322
d7c329e
18f2596
f4e401b
f949647
9abd3d0
5907441
0a7d495
cd9a6d1
dc79b50
866e5c8
a309d23
4cd2075
ee1fba2
0301e1d
2236783
76c3106
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = () => ( | ||
<> | ||
<PluginPreview | ||
name="preview-custom-1" | ||
title={ __( 'Custom Preview 1' ) } | ||
> | ||
<h1> | ||
{ __( 'Custom Preview 1 Content' ) } | ||
</h1> | ||
</PluginPreview> | ||
|
||
<PluginPreview | ||
name="preview-custom-2" | ||
title={ __( 'Custom Preview 2' ) } | ||
onClick={ ( event ) => console.log( event ) } | ||
> | ||
<h1> | ||
{ __( 'Custom Preview 2 Content' ) } | ||
</h1> | ||
</PluginPreview> | ||
|
||
<PluginPreview | ||
name="custom-action" | ||
title={ __( 'Custom Action' ) } | ||
icon={ external } | ||
onClick={ ( event ) => console.log( event ) } | ||
/> | ||
</> | ||
); | ||
|
||
registerPlugin( "plugin-preview-test", { | ||
render: PluginPreviewTest, | ||
} ); | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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( { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As noted in my previous comment, I don't think that this SlotFill should be located inside |
||
children, | ||
icon, | ||
name, | ||
onClick, | ||
title, | ||
...props | ||
} ) { | ||
const previewSlotName = `core/block-editor/plugin-preview/${ name }`; | ||
const previewSlot = useSlot( previewSlotName ); | ||
|
||
if ( coreDeviceTypes.includes( name ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it mean that custom plugins can't use the existing names like |
||
return null; | ||
} | ||
|
||
return ( | ||
<> | ||
<PluginPreviewMenuFill> | ||
{ ( { deviceType, setDeviceType } ) => ( | ||
<MenuItem | ||
onClick={ ( ...args ) => { | ||
if ( name && previewSlot.fills?.length > 0 ) { | ||
setDeviceType( name ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the examples provided with the documentation, I see for example |
||
} | ||
if ( onClick ) { | ||
onClick( ...args ); | ||
} | ||
} } | ||
icon={ icon || ( deviceType === name && check ) } | ||
{ ...props } | ||
> | ||
{ title } | ||
</MenuItem> | ||
) } | ||
</PluginPreviewMenuFill> | ||
{ children && <Fill name={ previewSlotName }>{ children }</Fill> } | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,7 +33,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 ManageBlocksModal from '../manage-blocks-modal'; | ||
|
@@ -225,7 +225,7 @@ function Layout( { styles } ) { | |
<TextEditor /> | ||
) } | ||
{ isRichEditingEnabled && mode === 'visual' && ( | ||
<VisualEditor styles={ styles } /> | ||
<VisualEditorOrPluginPreview styles={ styles } /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It feels like Could you disable rich editing, use text mode to edit content and still use the custom preview from the plugin? |
||
) } | ||
<div className="edit-post-layout__metaboxes"> | ||
<MetaBoxes location="normal" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <Slot name={ previewSlotName } fillProps={ props } />; | ||
} | ||
|
||
return <VisualEditor { ...props } />; | ||
} | ||
export default VisualEditorOrPluginPreview; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why
__experimentalPreviewOptions
andcoreDeviceTypes
are proposed to become part of public API? They don't seem to be used outside of the package where they are defined.