-
Notifications
You must be signed in to change notification settings - Fork 384
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
[AMP Stories] Add template Inserter #2029
Changes from 7 commits
bf424fc
7e0b682
738d975
1932e3f
9273dc0
f0b70f9
1306f9c
8ae5c64
3c43aeb
7eb3a3e
24bf37c
364a61f
3a53207
788d22d
46834cf
4f3baf5
9f4d919
de77363
379d51d
6859bfd
cdec4ae
abd0879
d72d6ee
53def88
a5ee722
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,44 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { noop } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { createBlock } from '@wordpress/blocks'; | ||
import { Disabled } from '@wordpress/components'; | ||
// import { BlockEdit } from '@wordpress/block-editor'; | ||
|
||
/** | ||
* Block Preview Component: It renders a preview given a block name and attributes. | ||
* | ||
* @param {Object} props Component props. | ||
* | ||
* @return {WPElement} Rendered element. | ||
*/ | ||
function BlockPreview( props ) { | ||
return ( | ||
<button onClick={ props.onClick } className="components-button editor-block-preview block-editor-block-preview"> | ||
<BlockPreviewContent { ...props } /> | ||
</button> | ||
); | ||
} | ||
|
||
export function BlockPreviewContent( { name, attributes } ) { | ||
// @todo Importing this outside of the function causes error for some reason. | ||
const BlockEdit = wp.blockEditor.BlockEdit; | ||
const block = createBlock( name, attributes ); | ||
return ( | ||
<Disabled className="editor-block-preview__content block-editor-block-preview__content editor-styles-wrapper" aria-hidden> | ||
<BlockEdit | ||
name={ block.name } | ||
focus={ false } | ||
attributes={ block.attributes } | ||
setAttributes={ noop } | ||
/> | ||
</Disabled> | ||
); | ||
} | ||
|
||
export default BlockPreview; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
const pageIcon = ( | ||
<svg width="86" height="96" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||
<g clipPath="url(#a)"> | ||
<path d="M71.115 91.034H1.654V1.655h52.923l16.538 16.552v72.828z" fill="#fff" /> | ||
<path d="M54.577 1.655v16.552h16.538L54.577 1.655z" fill="#A9A9A9" /> | ||
<path d="M71.115 19.862H54.577a1.66 1.66 0 0 1-1.654-1.655V1.655c0-.91.744-1.655 1.654-1.655.447 0 .86.182 1.174.48L72.29 17.032a1.65 1.65 0 0 1 0 2.334 1.652 1.652 0 0 1-1.175.496zm-14.884-3.31H67.13L56.23 5.644v10.908z" fill="#686868" /> | ||
<path d="M38.038 92.69H1.654A1.66 1.66 0 0 1 0 91.034V1.655C0 .745.744 0 1.654 0h52.923c.447 0 .86.182 1.174.48L72.29 17.032c.297.314.48.728.48 1.175V48c0 .91-.745 1.655-1.655 1.655S69.462 48.91 69.462 48V18.886L53.898 3.31H3.308v86.07h34.73c.91 0 1.654.744 1.654 1.655a1.66 1.66 0 0 1-1.654 1.655z" fill="#686868" /> | ||
<path d="M64.5 94.345c10.96 0 19.846-8.893 19.846-19.862 0-10.97-8.885-19.862-19.846-19.862-10.96 0-19.846 8.892-19.846 19.862 0 10.97 8.885 19.862 19.846 19.862z" fill="#A9A9A9" /> | ||
<path d="M64.5 96C52.625 96 43 86.367 43 74.483s9.625-21.517 21.5-21.517S86 62.599 86 74.483c-.017 11.884-9.625 21.5-21.5 21.517zm0-39.724c-10.055 0-18.192 8.143-18.192 18.207 0 10.063 8.137 18.207 18.192 18.207s18.192-8.144 18.192-18.207c-.016-10.047-8.153-18.19-18.192-18.207z" fill="#686868" /> | ||
<path d="M64.5 86.069a1.66 1.66 0 0 1-1.654-1.655V64.552c0-.91.744-1.655 1.654-1.655.91 0 1.654.744 1.654 1.655v19.862a1.66 1.66 0 0 1-1.654 1.655z" fill="#fff" /><path d="M74.423 76.138H54.577a1.66 1.66 0 0 1-1.654-1.655c0-.91.744-1.655 1.654-1.655h19.846c.91 0 1.654.745 1.654 1.655a1.66 1.66 0 0 1-1.654 1.655z" fill="#fff" /> | ||
</g> | ||
<defs> | ||
<clipPath id="a"> | ||
<path fill="#fff" d="M0 0h86v96H0z" /> | ||
</clipPath> | ||
</defs> | ||
</svg> | ||
); | ||
|
||
export default pageIcon; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Dropdown, IconButton, Spinner } from '@wordpress/components'; | ||
import { Component } from '@wordpress/element'; | ||
import { withSelect, withDispatch } from '@wordpress/data'; | ||
import { createBlock, cloneBlock } from '@wordpress/blocks'; | ||
import { compose } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import BlockPreview from './block-preview'; | ||
import pageIcon from './icon'; | ||
|
||
const storyPageBlockName = 'amp/amp-story-page'; | ||
|
||
class TemplateInserter extends Component { | ||
constructor() { | ||
super( ...arguments ); | ||
|
||
this.onToggle = this.onToggle.bind( this ); | ||
|
||
this.state = { | ||
reusableBlocks: null, | ||
}; | ||
} | ||
|
||
componentDidMount() { | ||
this.props.fetchReusableBlocks(); | ||
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. @swissspidy I reworked this not to use API fetch here, however, we'll need to change it again once we get to categories. Also looked into using the local store together with API but didn't come to a reasonable solution at this moment. Maybe for now we could actually use the API fetch as it was before and improve it later. I'm thinking of improving the style to be acceptable and then leave the rest to the next PR-s / issues, potentially reverting to using API fetch for now. Thoughts? EDIT: Actually I'll probably add the possibility to add reusable blocks as well (to some extent), for the inserter to make more sense. |
||
} | ||
|
||
componentDidUpdate( prevProps ) { | ||
// This check is needed to make sure that the blocks are loaded in time. | ||
if ( prevProps.reusableBlocks !== this.props.reusableBlocks || prevProps.allBlocks !== this.props.allBlocks ) { | ||
this.setState( { | ||
reusableBlocks: this.props.reusableBlocks, | ||
} ); | ||
} | ||
} | ||
|
||
onToggle( isOpen ) { | ||
const { onToggle } = this.props; | ||
|
||
// Surface toggle callback to parent component | ||
if ( onToggle ) { | ||
onToggle( isOpen ); | ||
} | ||
} | ||
|
||
render() { | ||
const { insertBlock, getBlock } = this.props; | ||
return ( | ||
<Dropdown | ||
className="editor-inserter block-editor-inserter" | ||
contentClassName="amp-stories__template-inserter__popover is-bottom editor-inserter__popover block-editor-inserter__popover" | ||
onToggle={ this.onToggle } | ||
expandOnMobile | ||
renderToggle={ ( { onToggle, isOpen } ) => ( | ||
<IconButton | ||
icon="insert" | ||
label={ __( 'Insert Template', 'amp' ) } | ||
onClick={ onToggle } | ||
className="editor-inserter__amp-inserter" | ||
aria-haspopup="true" | ||
aria-expanded={ isOpen } | ||
/> | ||
) } | ||
renderContent={ ( { onClose } ) => { | ||
const isStoryBlock = ( clientId ) => { | ||
const block = getBlock( clientId ); | ||
return block && storyPageBlockName === block.name; | ||
}; | ||
|
||
const onSelect = ( item ) => { | ||
const block = ! item ? createBlock( storyPageBlockName ) : getBlock( item.clientId ); | ||
onClose(); | ||
// Clone block to avoid duplicate ID-s. | ||
insertBlock( cloneBlock( block ) ); | ||
}; | ||
if ( ! this.state.reusableBlocks ) { | ||
return ( | ||
<Spinner /> | ||
); | ||
} | ||
const storyTemplates = this.state.reusableBlocks.filter( ( { clientId } ) => isStoryBlock( clientId ) ); | ||
|
||
return ( | ||
<div key="template-list" className="amp-stories__editor-inserter__menu"> | ||
<div | ||
className="amp-stories__editor-inserter__results" | ||
tabIndex="0" | ||
role="region" | ||
aria-label={ __( 'Available templates', 'amp' ) } | ||
> | ||
<div role="list" className="editor-block-types-list block-editor-block-types-list"> | ||
<div className="editor-block-preview block-editor-block-preview"> | ||
<IconButton | ||
icon={ pageIcon } | ||
label={ __( 'Blank Page', 'amp' ) } | ||
onClick={ () => { | ||
onSelect( null ); | ||
} } | ||
className="amp-stories__blank-page-inserter editor-block-preview__content block-editor-block-preview__content editor-styles-wrapper" | ||
/> | ||
</div> | ||
{ storyTemplates && storyTemplates.map( ( item ) => | ||
<BlockPreview | ||
key="template-preview" | ||
name="core/block" | ||
attributes={ { ref: item.id } } | ||
onClick={ () => { | ||
onSelect( item ); | ||
} } | ||
/> | ||
) } | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} } | ||
/> | ||
); | ||
} | ||
} | ||
|
||
export default compose( | ||
withSelect( ( select ) => { | ||
const { | ||
__experimentalGetReusableBlocks: getReusableBlocks, | ||
} = select( 'core/editor' ); | ||
|
||
const { | ||
getBlock, | ||
getBlocks, | ||
} = select( 'core/block-editor' ); | ||
|
||
return { | ||
reusableBlocks: getReusableBlocks(), | ||
getBlock, | ||
allBlocks: getBlocks(), | ||
}; | ||
} ), | ||
withDispatch( ( dispatch ) => { | ||
const { | ||
__experimentalFetchReusableBlocks: fetchReusableBlocks, | ||
} = dispatch( 'core/editor' ); | ||
|
||
const { insertBlock } = dispatch( 'core/block-editor' ); | ||
|
||
return { | ||
fetchReusableBlocks, | ||
insertBlock, | ||
}; | ||
} ) | ||
)( TemplateInserter ); |
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.
This is because the new block-editor package is not yet listed in
wpDependencies
inwebpack.config.js
nor inpackage.json
.You can still use
@wordpress/editor
for now. It just proxies to the new package behind the scenes for BC.Alternatively, since I am probably going to merge #2000 today and doing some rewriting there from
@wordpress/editor
to@wordpress/block-editor
, you could then just merge in the latest changes from the amp-stories-redux branch.