diff --git a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/block-template-preview.js b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/block-template-preview.js index d5bcd2ffc9a1..1f14d38ded00 100644 --- a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/block-template-preview.js +++ b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/block-template-preview.js @@ -11,21 +11,26 @@ */ /* eslint-disable import/no-extraneous-dependencies */ import { BlockPreview } from '@wordpress/block-editor'; +import { Disabled } from '@wordpress/components'; /* eslint-enable import/no-extraneous-dependencies */ const BlockTemplatePreview = ( { blocks = [], viewportWidth } ) => { - if ( ! blocks || ! blocks.length ) { - return null; - } - return ( /* eslint-disable wpcalypso/jsx-classname-namespace */ -
-
-
- +
+ +
+
+
+ +
+
-
+
/* eslint-enable wpcalypso/jsx-classname-namespace */ ); diff --git a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-control.js b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-control.js index 1b280b8fa2ae..0cdaaaee6e57 100644 --- a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-control.js +++ b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-control.js @@ -27,7 +27,6 @@ export const TemplateSelectorControl = ( { help, instanceId, templates = [], - blocksByTemplates = {}, useDynamicPreview = false, onTemplateSelect = noop, siteInformation = {}, @@ -38,12 +37,7 @@ export const TemplateSelectorControl = ( { return null; } - if ( true === useDynamicPreview && isEmpty( blocksByTemplates ) ) { - return null; - } - const id = `template-selector-control-${ instanceId }`; - return ( { const { id, value, onSelect, label, - useDynamicPreview = false, + useDynamicPreview, staticPreviewImg, staticPreviewImgAlt = '', - blocks = [], isSelected, handleTemplateConfirmation, } = props; + const template = getTemplateBySlug( value ); + const [ isParsing, setIsParsing ] = useState( true ); + + const onTemplatesParseListener = event => { + const parsedTemplate = get( event, [ 'detail', 'template' ] ); + if ( value !== parsedTemplate.slug ) { + return; + } + setIsParsing( false ); + }; + window.addEventListener( 'onTemplateParse', onTemplatesParseListener ); + + useEffect( () => { + return () => { + window.removeEventListener( 'resize', onTemplatesParseListener ); + }; + }, [] ); if ( isNil( id ) || isNil( label ) || isNil( value ) ) { return null; } - if ( useDynamicPreview && ( isNil( blocks ) || isEmpty( blocks ) ) ) { - return null; - } + const { isEmpty } = template; - // Define static or dynamic preview. - const innerPreview = useDynamicPreview ? ( - - - - ) : ( - { - ); + const renderInnerPreview = () => { + /* eslint-disable wpcalypso/jsx-classname-namespace */ + if ( isParsing && ! isEmpty ) { + return ( +
+
+ ; +
+
+ ); + } + /* eslint-enable wpcalypso/jsx-classname-namespace */ + + if ( useDynamicPreview ) { + return ; + } + + return ( +
+ { +
+ ); + /* eslint-enable wpcalypso/jsx-classname-namespace */ + }; const labelId = `label-${ id }-${ value }`; @@ -67,20 +101,23 @@ const TemplateSelectorItem = props => { }; return ( + /* eslint-disable wpcalypso/jsx-classname-namespace */ + /* eslint-enable wpcalypso/jsx-classname-namespace */ ); }; diff --git a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-preview.js b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-preview.js index 49b6166881b6..1828019b80ef 100644 --- a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-preview.js +++ b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/components/template-selector-preview.js @@ -104,7 +104,12 @@ const TemplateSelectorPreview = ( { blocks, viewportWidth, title } ) => {
- +
diff --git a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/index.js b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/index.js index 56caef6317b6..dc3adda98fb4 100644 --- a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/index.js +++ b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/index.js @@ -1,8 +1,16 @@ /* eslint-disable import/no-extraneous-dependencies */ +import { + getBlocksByTemplateSlug, + getTitleByTemplateSlug, + hasTemplates, + getFirstTemplateSlug, + getAllTemplateSlugs, +} from './utils/templates-parser'; + /** * External dependencies */ -import { isEmpty, reduce, get, keyBy, mapValues } from 'lodash'; +import { isEmpty } from 'lodash'; import classnames from 'classnames'; import '@wordpress/nux'; import { __, sprintf } from '@wordpress/i18n'; @@ -11,7 +19,6 @@ import { Button, Modal, Spinner } from '@wordpress/components'; import { registerPlugin } from '@wordpress/plugins'; import { withDispatch, withSelect } from '@wordpress/data'; import { Component } from '@wordpress/element'; -import { parse as parseBlocks } from '@wordpress/blocks'; /** * Internal dependencies @@ -20,13 +27,10 @@ import './styles/starter-page-templates-editor.scss'; import TemplateSelectorControl from './components/template-selector-control'; import TemplateSelectorPreview from './components/template-selector-preview'; import { trackDismiss, trackSelection, trackView, initializeWithIdentity } from './utils/tracking'; -import replacePlaceholders from './utils/replace-placeholders'; import ensureAssets from './utils/ensure-assets'; -/* eslint-enable import/no-extraneous-dependencies */ // Load config passed from backend. const { - templates = [], vertical, segment, tracksUserData, @@ -36,44 +40,15 @@ const { class PageTemplateModal extends Component { state = { isLoading: false, - previewedTemplate: null, - blocksByTemplateSlug: {}, - titlesByTemplateSlug: {}, + previewedTemplate: getFirstTemplateSlug(), error: null, - isOpen: false, + isOpen: hasTemplates(), }; - constructor( props ) { - super(); - const hasTemplates = ! isEmpty( props.templates ); - this.state.isOpen = hasTemplates; - if ( hasTemplates ) { - // Select the first template automatically. - this.state.previewedTemplate = get( props.templates, [ 0, 'slug' ] ); - // Extract titles for faster lookup. - this.state.titlesByTemplateSlug = mapValues( keyBy( props.templates, 'slug' ), 'title' ); - } - } - componentDidMount() { if ( this.state.isOpen ) { trackView( this.props.segment.id, this.props.vertical.id ); } - - // Parse templates blocks and store them into the state. - const blocksByTemplateSlug = reduce( - templates, - ( prev, { slug, content } ) => { - prev[ slug ] = content - ? parseBlocks( replacePlaceholders( content, siteInformation ) ) - : []; - return prev; - }, - {} - ); - - // eslint-disable-next-line react/no-did-mount-set-state - this.setState( { blocksByTemplateSlug } ); } setTemplate = slug => { @@ -82,8 +57,8 @@ class PageTemplateModal extends Component { this.props.saveTemplateChoice( slug ); // Load content. - const blocks = this.getBlocksByTemplateSlug( slug ); - const title = this.getTitleByTemplateSlug( slug ); + const blocks = getBlocksByTemplateSlug( slug ); + const title = getTitleByTemplateSlug( slug ); // Skip inserting if there's nothing to insert. if ( ! blocks || ! blocks.length ) { @@ -121,9 +96,9 @@ class PageTemplateModal extends Component { return this.props.shouldPrefetchAssets ? ensureAssets( blocks ) : Promise.resolve( blocks ); }; - handleConfirmation = ( slug = this.state.previewedTemplate ) => this.setTemplate( slug ); + // handleConfirmation = ( slug = this.state.previewedTemplate ) => this.setTemplate( slug ); - previewTemplate = slug => this.setState( { previewedTemplate: slug } ); + previewTemplate = previewedTemplate => this.setState( { previewedTemplate } ); closeModal = event => { // Check to see if the Blur event occurred on the buttons inside of the Modal. @@ -135,17 +110,9 @@ class PageTemplateModal extends Component { trackDismiss( this.props.segment.id, this.props.vertical.id ); }; - getBlocksByTemplateSlug( slug ) { - return get( this.state.blocksByTemplateSlug, [ slug ], [] ); - } - - getTitleByTemplateSlug( slug ) { - return get( this.state.titlesByTemplateSlug, [ slug ], '' ); - } - render() { - const { previewedTemplate, isOpen, isLoading, blocksByTemplateSlug } = this.state; /* eslint-disable no-shadow */ + const { previewedTemplate, isOpen, isLoading } = this.state; const { templates } = this.props; /* eslint-enable no-shadow */ @@ -176,19 +143,19 @@ class PageTemplateModal extends Component { ) } @@ -206,7 +173,7 @@ class PageTemplateModal extends Component { > { sprintf( __( 'Use %s template', 'full-site-editing' ), - this.getTitleByTemplateSlug( previewedTemplate ) + getTitleByTemplateSlug( previewedTemplate ) ) }
@@ -264,7 +231,7 @@ registerPlugin( 'page-templates', { return ( diff --git a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/styles/starter-page-templates-editor.scss b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/styles/starter-page-templates-editor.scss index f1a5fa3d673e..53c99a3c0618 100644 --- a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/styles/starter-page-templates-editor.scss +++ b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/styles/starter-page-templates-editor.scss @@ -21,6 +21,8 @@ $template-large-preview-title-height: 117px; $wp-org-sidebar-reduced: 36px; $wp-org-sidebar-full: 160px; +$spinner-width: 18px; + // Modal Overlay .page-template-modal-screen-overlay { animation: none; @@ -153,6 +155,11 @@ body.admin-bar:not( .is-fullscreen-mode ) .page-template-modal-screen-overlay { height: 40px; line-height: 40px; background-color: #fff; + .editor-styles-wrapper { + font-size: 14px; + height: 40px; + line-height: 40px; + } } &:hover, @@ -172,7 +179,7 @@ body.admin-bar:not( .is-fullscreen-mode ) .page-template-modal-screen-overlay { width: 100%; display: block; margin: 0 auto; - background: $template-selector-empty-background; + //background: $template-selector-empty-background; border-radius: 0; overflow: hidden; height: 0; @@ -180,16 +187,27 @@ body.admin-bar:not( .is-fullscreen-mode ) .page-template-modal-screen-overlay { box-sizing: content-box; position: relative; pointer-events: none; - opacity: 1; + + .edit-post-visual-editor { + padding-top: 100%; + margin-top: -100%; + } @media screen and ( max-width: 659px ) { padding-top: 120%; } - &.is-rendering { + &.is-parsing { opacity: 0.5; } + .components-spinner { + position: absolute; + top: 50%; + left: 50%; + margin: -#{$spinner-width / 2} 0 0 -#{$spinner-width / 2}; // Center the spinner. + } + .block-editor-block-list__layout, .block-editor-block-list__block { padding: inherit; @@ -267,7 +285,7 @@ body.admin-bar:not( .is-fullscreen-mode ) .page-template-modal-screen-overlay { display: flex; align-items: center; padding-right: 24px; - + @media screen and ( max-width: 659px ) { display: none; } @@ -299,23 +317,18 @@ body.admin-bar:not( .is-fullscreen-mode ) .page-template-modal-screen-overlay { overflow-y: auto; box-shadow: 0 2px 2px 0 rgba( 0, 0, 0, 0.14 ), 0 3px 1px -2px rgba( 0, 0, 0, 0.12 ), 0 1px 5px 0 rgba( 0, 0, 0, 0.2 ); - .edit-post-visual-editor { - margin-top: 20px; - } - - .editor-styles-wrapper { .editor-post-title { transform-origin: top left; width: 960px; display: block; - position: absolute; - top: 0; } .editor-post-title, - .editor-post-title__block { + .editor-post-title__block, + .editor-post-title__block .editor-post-title__input { height: $template-large-preview-title-height; + line-height: $template-large-preview-title-height !important; margin-top: 0; margin-bottom: 0; padding-top: 0; @@ -349,8 +362,8 @@ body:not( .is-fullscreen-mode ) .template-selector-preview { // Preview adjustments. // Tweak styles which are inside of the preview container. -.block-editor-block-preview__container, -.template-selector-preview { +.template-selector-item__label, // thumbnail +.template-selector-preview { // Large Preview .editor-styles-wrapper { .wp-block { width: 100%; @@ -370,8 +383,6 @@ body:not( .is-fullscreen-mode ) .template-selector-preview { // `core/columns` .wp-block-columns > .editor-inner-blocks > .editor-block-list__layout > [data-type='core/column'] { - //margin-left: -14px; - //margin-right: -14px; & > .editor-block-list__block-edit > div > .block-core-columns > .editor-inner-blocks { margin-top: 0; @@ -400,8 +411,6 @@ body:not( .is-fullscreen-mode ) .template-selector-preview { } // Set full height to preview container to inherits styles defined for themes. -.template-selector-preview .components-disabled, -.template-selector-preview .edit-post-visual-editor, .template-selector-item__preview-wrap .components-disabled, .template-selector-item__preview-wrap .edit-post-visual-editor { height: 100%; @@ -411,15 +420,19 @@ body:not( .is-fullscreen-mode ) .template-selector-preview { } } -.page-template-modal__loading { - position: absolute; - top: 50%; - left: 50%; - transform: translate( -50%, -50% ); - display: flex; - align-items: flex-end; +.template-selector-preview .edit-post-visual-editor, +.template-selector-item__label .edit-post-visual-editor { + padding: 0; +} - .components-spinner { - float: none; - } +.template-selector-item__label .block-editor-block-preview__container { + position: absolute; + top: 0; } +// +//.template-selector-control__template { +// .editor-styles-wrapper .editor-writing-flow { +// max-width: 80%; +// margin: 0 10%; +// } +//} diff --git a/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/utils/templates-parser.js b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/utils/templates-parser.js new file mode 100644 index 000000000000..122008b319e0 --- /dev/null +++ b/apps/full-site-editing/full-site-editing-plugin/starter-page-templates/page-template-modal/utils/templates-parser.js @@ -0,0 +1,84 @@ +/** + * External dependencies + */ +/* eslint-disable import/no-extraneous-dependencies */ +import { get, map } from 'lodash'; +/* eslint-enable import/no-extraneous-dependencies */ + +/** + * WordPress dependencies + */ +/* eslint-disable import/no-extraneous-dependencies */ +import { parse as parseBlocks } from '@wordpress/blocks'; +/* eslint-enable import/no-extraneous-dependencies */ + +/** + * Internal dependencies + */ +import replacePlaceholders from './replace-placeholders'; + +const { templates = [], siteInformation = {} } = window.starterPageTemplatesConfig; +const allTemplatesBySlug = {}; +const allTemplates = map( templates, ( { slug, title, preview, previewAlt } ) => ( { + slug, + title, + preview, + previewAlt, +} ) ); + +/** + * Parse a template async. + * Collect the parsed templates into the global allTemplatesBySlug. + * Dispatch `onTemplateParse` global event. + * + * @param {string} content Template serialized content. + * @param {string} title Template title. + * @param {string} slug Template slug. + * @return {number} Timeout identifier. + */ +const parseTemplate = ( { content, title, slug } ) => { + return setTimeout( () => { + const blocks = content ? parseBlocks( replacePlaceholders( content, siteInformation ) ) : []; + + const template = { + blocks, + count: blocks.length, + isEmpty: ! content, + title, + slug, + }; + + // Populate global templates container. + allTemplatesBySlug[ slug ] = template; + + // Dispatch a global event to indicate a template has been parsed. + window.dispatchEvent( new CustomEvent( 'onTemplateParse', { detail: { template } } ) ); + }, 0 ); +}; + +// Listen when a template is parsed, and start to parse another one if exists. +window.addEventListener( 'onTemplateParse', () => { + const nextTemplate = templates.shift(); + if ( nextTemplate ) { + parseTemplate( nextTemplate ); + } +} ); + +// Parse the first one template from the templates list. +const firstTemplate = templates.shift(); +parseTemplate( firstTemplate ); + +// Selectors. +export const hasTemplates = () => !! templates.length; + +export const getBlocksByTemplateSlug = slug => get( allTemplatesBySlug, [ slug, 'blocks' ], [] ); + +export const getTitleByTemplateSlug = slug => get( allTemplatesBySlug, [ slug, 'title' ], [] ); + +export const getTemplateBySlug = slug => get( allTemplatesBySlug, [ slug ], {} ); + +export const getFirstTemplateSlug = () => get( templates, [ 0, 'slug' ], null ); + +export const getAllTemplateSlugs = () => allTemplates; + +export default allTemplatesBySlug;