diff --git a/assets/css/amp-editor-story-blocks.css b/assets/css/amp-editor-story-blocks.css index 3ddca086414..65d285abd81 100644 --- a/assets/css/amp-editor-story-blocks.css +++ b/assets/css/amp-editor-story-blocks.css @@ -1,3 +1,33 @@ +/*------------------------------------------------------------------ +[Table of contents] + + 1. Style story pages wrapper + 2. Selected state + 2.1 Selected label + 3. Story page dimensions + 4. Layers stacking + 4.1 Hide layers above + 4.2 Blur layers above + 4.3 Stroke text for legibility. + 5. Layers Styling + 5.1 Thirds Layer + 5.2 Fill layer + 5.3 CTA layer + 6. Inserter customisations. + 6.1 Ghosted page inserter + 7. Block navigator + 8. Shame + 8.1 Gutenberg - Warning div not clickable + +-------------------------------------------------------------------*/ + + +/* + * 1. Style story pages wrapper. + * - Add thicker border around the pages + * - Change background to light gray + */ + div[data-type="amp/amp-story-page"] { border: 10px solid #eff0f1; } @@ -12,19 +42,62 @@ div[data-type="amp/amp-story-page"]::after { background: #eff0f1; } +.editor-block-list__layout div[data-type="amp/amp-story-page"] .components-placeholder.wp-block-image { + background: #f9f9f9; +} + +/* + * 2. Selected state outlines. + * Change default gray selected highlights into blue, + * as gray was not visible in a multi-layer setup. + */ + .editor-block-list__layout div[data-type="amp/amp-story-page"].is-selected > .editor-block-list__block-edit::before { outline: 1px solid #007cba; } +.amp-grid-template .editor-block-list__layout .editor-block-list__block.is-selected > .editor-block-list__block-edit::before, +.amp-grid-template .editor-block-list__layout .editor-block-list__block.is-typing > .editor-block-list__block-edit::before, +div[data-type="amp/amp-story-cta-layer"] .editor-block-list__layout .editor-block-list__block.is-selected > .editor-block-list__block-edit::before +{ + outline: 1px solid #007cba; +} + +div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > .is-selected > .editor-block-list__block-edit::before { + outline: 1px solid #007cba; +} + +/* Remove one default outline as it was unnecessary with the above implementation. */ + +.editor-block-list__layout .editor-block-list__block .editor-block-list__block-edit::before { + outline: none; +} + +/* + * 2.1 Selected label + * Show label while the layer is selected + */ + + .post-type-amp_story .editor-block-list__breadcrumb { + z-index: 6; + pointer-events: none; + } + + .post-type-amp_story .editor-block-list__block:hover .editor-block-list__breadcrumb .components-toolbar { + opacity: 1; + animation: none; + } + +/* + * 3. Story page dimensions. + * Fix the page editor size to the ratio of the stories. + */ + .editor-block-list__layout div[data-type="amp/amp-story-page"] { padding: 0; margin: 60px auto 0; } -.editor-block-list__layout div[data-type="amp/amp-story-page"] .components-placeholder.wp-block-image { - background: #f9f9f9; -} - @media (min-width: 600px) { div[data-type="amp/amp-story-page"] .editor-block-list__block { margin: 0; @@ -32,14 +105,6 @@ div[data-type="amp/amp-story-page"]::after { } } -.components-popover.editor-inserter__amp .components-popover__content { - height: 200px; -} - -.editor-inserter__popover .amp-story-has-cta-layer .editor-block-list-item-amp-amp-story-cta-layer { - opacity: 0.5; -} - div[data-type="amp/amp-story-page"], div[data-type="amp/amp-story-page"] .editor-inner-blocks .editor-block-list__layout:first-of-type { margin: auto; @@ -50,14 +115,19 @@ div[data-type="amp/amp-story-page"] .editor-inner-blocks .editor-block-list__lay div[data-type="amp/amp-story-page"] .editor-block-list__layout > .block-list-appender { width: 316px; - } +/* Also override needed core inline styles. */ + .editor-block-list__layout div[data-type="amp/amp-story-cta-layer"] .editor-inner-blocks .editor-block-list__layout { min-height: initial !important; } -/* Make layer be exactly on top of each other */ +/* + * 4. Layers stacking. + * Setup on top of each other layering on the story children. + */ + .editor-block-list__layout div[data-type="amp/amp-story-page"].editor-block-list__block:first-child .editor-block-list__block-edit { margin: 0; } @@ -77,6 +147,10 @@ div[data-type="amp/amp-story-page"] .editor-inner-blocks .editor-block-list__lay width: 100%; } +.editor-block-list__layout div[data-amp-selected="parent"] { + z-index: 1; +} + .amp-grid-template .editor-block-list__layout:first-of-type { display: grid; padding: 68px 32px 32px; @@ -95,13 +169,84 @@ div[data-type="amp/amp-story-page"] .editor-inner-blocks .editor-block-list__lay box-sizing: border-box; } -.amp-grid-template .editor-block-list__layout .editor-block-list__block.is-selected > .editor-block-list__block-edit::before, -.amp-grid-template .editor-block-list__layout .editor-block-list__block.is-typing > .editor-block-list__block-edit::before, -div[data-type="amp/amp-story-cta-layer"] .editor-block-list__layout .editor-block-list__block.is-selected > .editor-block-list__block-edit::before -{ - outline: 1px solid #007cba; +.amp-grid-template-horizontal .editor-block-list__layout:first-of-type { + grid-auto-flow: column !important; + grid-template-rows: 100% !important; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: start; + grid-gap: 16px; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: start; +} + +.amp-grid-template-vertical .editor-block-list__layout:first-of-type { + grid-auto-flow: row; + grid-template-columns: 100%; + align-content: start; + grid-gap: 16px; + -webkit-box-pack: stretch; + -ms-flex-pack: stretch; + justify-content: stretch; + justify-items: start; } +.amp-grid-template-vertical .editor-block-list__block { + width: 100%; +} + +/* + * 4.1 Hide layers above + * Hide elements above the currently selected layer. + */ + +.editor-block-list__layout div[data-amp-type="grid"]:not(.is-selected):not(.is-selected-parent) .editor-block-list__block { + z-index: 0; +} + +.editor-block-list__layout div[data-amp-type="grid"]:not(.is-selected):not(.is-selected-parent) .editor-block-list__block p[data-is-placeholder-visible="true"] ~ p { + display: none; +} + +.editor-block-list__layout div[data-amp-type="grid"].is-selected ~ div[data-amp-type="grid"], +.editor-block-list__layout div[data-amp-type="grid"][data-amp-selected="parent"] ~ div[data-amp-type="grid"], +.editor-block-list__layout div[data-amp-type="grid"].is-selected ~ div[data-type="amp/amp-story-cta-layer"], +.editor-block-list__layout div[data-amp-type="grid"][data-amp-selected="parent"] ~ div[data-type="amp/amp-story-cta-layer"]{ + display: none; +} + +/* + * 4.2 Blur layers above + * Layers - Blur and fade out the layers beneath the currently selected layer. + */ + +.editor-block-list__layout > div { + transition: .3s; + transition-property: opacity, filter; +} + +div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > .wp-block { + opacity: 0.3; + filter: blur(8px); + -webkit-filter: blur(8px); +} + +div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > div[data-amp-selected="parent"], +div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > .is-selected { + opacity: 1; + filter: none; + -webkit-filter: none; +} + +/* + * 4.3 Stroke text for legibility. + * Add white stroke to text in layers in order to be legible + * while editing on top of varying colored background. + */ + .amp-grid-template .editor-block-list__layout .editor-block-list__block.is-selected > .editor-block-list__block-edit .editor-rich-text, .amp-grid-template .editor-block-list__layout .editor-block-list__block.is-typing > .editor-block-list__block-edit .editor-rich-text { text-shadow: @@ -129,34 +274,13 @@ div[data-type="amp/amp-story-cta-layer"] .editor-block-list__layout .editor-bloc 0.05em 0.05em rgba( 255, 255, 255, 0.8 ); } -.amp-grid-template-horizontal .editor-block-list__layout:first-of-type { - grid-auto-flow: column !important; - grid-template-rows: 100% !important; - -ms-flex-line-pack: stretch; - align-content: stretch; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; - grid-gap: 16px; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: start; -} - -.amp-grid-template-vertical .editor-block-list__layout:first-of-type { - grid-auto-flow: row; - grid-template-columns: 100%; - align-content: start; - grid-gap: 16px; - -webkit-box-pack: stretch; - -ms-flex-pack: stretch; - justify-content: stretch; - justify-items: start; -} +/* + * 5. Layers Styling + */ -.amp-grid-template-vertical .editor-block-list__block { - width: 100%; -} +/* + * 5.1 Thirds layer. + */ .amp-grid-template-thirds .editor-block-list__layout:first-of-type { grid-template-rows: 1fr 1fr 1fr; @@ -175,6 +299,10 @@ div[data-amp-position="lower-third"] { grid-area: lower-third / lower-third / lower-third / lower-third; } +/* + * 5.2 Fill layer. + * Style the fill image & fill video type layers. + */ .amp-grid-template-fill .editor-block-list__layout > :first-child { bottom: 0; @@ -221,10 +349,19 @@ div[data-amp-position="lower-third"] { height: 533px !important; } +div[data-amp-type="grid"] .block-library-image__resizer { + max-width: 318px !important; + max-height: 533px !important; +} + .amp-grid-template-fill > :not(:first-child) { display: none; } +/* + * 5.3 CTA layer. + */ + .editor-block-list__layout div[data-type="amp/amp-story-cta-layer"] { position: absolute; height: 20%; @@ -240,118 +377,24 @@ div[data-amp-position="lower-third"] { height: auto !important; } -.editor-block-list__layout > div { - transition: .3s; - transition-property: opacity, filter; -} - -div[data-amp-type="grid"] .block-library-image__resizer { - max-width: 318px !important; - max-height: 533px !important; -} - -.editor-block-list__layout div[data-amp-type="grid"]:not(.is-selected):not(.is-selected-parent) .editor-block-list__block { - z-index: 0; -} - -.editor-block-list__layout div[data-amp-type="grid"]:not(.is-selected):not(.is-selected-parent) .editor-block-list__block p[data-is-placeholder-visible="true"] ~ p { - display: none; -} - -.editor-block-list__layout div[data-amp-type="grid"].is-selected ~ div[data-amp-type="grid"], -.editor-block-list__layout div[data-amp-type="grid"][data-amp-selected="parent"] ~ div[data-amp-type="grid"], -.editor-block-list__layout div[data-amp-type="grid"].is-selected ~ div[data-type="amp/amp-story-cta-layer"], -.editor-block-list__layout div[data-amp-type="grid"][data-amp-selected="parent"] ~ div[data-type="amp/amp-story-cta-layer"]{ - display: none; -} - -div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > .wp-block { - opacity: 0.3; - filter: blur(8px); - -webkit-filter: blur(8px); -} - -div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > div[data-amp-selected="parent"], -div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > .is-selected { - opacity: 1; - filter: none; - -webkit-filter: none; -} - -div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor-inner-blocks > .editor-block-list__layout > .is-selected > .editor-block-list__block-edit::before { - outline: 1px solid #007cba; -} - -.edit-post-visual-editor .editor-block-list__block ul.editor-selectors { - position: absolute; - right: -155px; - bottom: 0; - width: 120px; - z-index: 80; - list-style-type: none; - padding: 0; - margin: 0; -} - -.editor-selectors .component-editor__selector button { - background: no-repeat left top; - padding-left: 37px; - height: 40px; - margin: 1px 0 1px 4px; -} - -.editor-selectors .component-editor__selector .components-button:focus { - box-shadow: none; -} - -.editor-selectors .component-editor__selector.template-fill button { - background-image: url( '../images/grid-fill-inactive.svg' ); -} - -.editor-selectors .component-editor__selector.template-fill.is-selected button { - background-image: url( '../images/grid-fill-active.svg' ); -} - -.editor-selectors .component-editor__selector.template-horizontal button { - background-image: url( '../images/grid-horizontal-inactive.svg' ); -} - -.editor-selectors .component-editor__selector.template-horizontal.is-selected button { - background-image: url( '../images/grid-horizontal-active.svg' ); -} - -.editor-selectors .component-editor__selector.template-vertical button { - background-image: url( '../images/grid-vertical-inactive.svg' ); -} - -.editor-selectors .component-editor__selector.template-vertical.is-selected button { - background-image: url( '../images/grid-vertical-active.svg' ); -} - -.editor-selectors .component-editor__selector.template-thirds button { - background-image: url( '../images/grid-thirds-inactive.svg' ); -} - -.editor-selectors .component-editor__selector.template-thirds.is-selected button { - background-image: url( '../images/grid-thirds-active.svg' ); -} +/* + * 6. Inserter customisations. + */ -.editor-selectors .component-editor__selector.page-selector button { - background-image: url( '../images/page-inactive.svg' ); -} - -.editor-selectors .component-editor__selector.page-selector.is-selected button { - background-image: url( '../images/page-active.svg' ); -} - -.editor-selectors .component-editor__selector button { - cursor: pointer; + .components-popover.editor-inserter__amp .components-popover__content { + height: 200px; } -.post-type-amp_story .components-range-control__number { - width: 60px; +/* Fade out CTA Layer in inserter when it's already present */ +.editor-inserter__popover .amp-story-has-cta-layer .editor-block-list-item-amp-amp-story-cta-layer { + opacity: 0.5; } +/* + * 6.1 Ghosted page inserter + * Change the last inserter into a ghosted page. + * Shame disclaimer: long selectors needed because the classes are quite common. + */ .post-type-amp_story .editor-default-block-appender { max-width: 318px; @@ -374,8 +417,7 @@ div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor- opacity: 1; } -.post-type-amp_story .editor-writing-flow > div > div > .editor-block-list__layout > .block-list-appender .components-button - +.post-type-amp_story .editor-writing-flow > div > div > .editor-block-list__layout > .block-list-appender .components-button, .post-type-amp_story .editor-writing-flow > div > div > .editor-block-list__layout > .editor-block-list__block[data-type="core/paragraph"] .editor-block-list__insertion-point-inserter .components-icon-button, .post-type-amp_story .editor-writing-flow > div > div > .editor-block-list__layout > .block-list-appender .components-button { background-color: #FAFAFA; @@ -405,7 +447,154 @@ div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor- display: none; } -/* Fixes Gutenberg bug where an extra wrapping div is causing no pointer events on the notice layer. */ +/* + * 7. Block navigator + * Hides the toggler and shows the navigator by default on large screens. + */ + +@media( max-width: 960px) { + .editor-block-list__block-edit > div > .editor-selectors { + display: none; + } +} + +@media (min-width: 961px) { + .post-type-amp_story .editor-post-title__input { + text-align: center; + } + + .post-type-amp_story .editor-writing-flow > div > div > .editor-block-list__layout { + padding-left: 15px; + padding-right: 15px; + } + + .post-type-amp_story .editor-writing-flow > div > div { + display: grid; + } + + .post-type-amp_story .editor-writing-flow > div > div::before { + content: ''; + grid-column: 1 / 1; + grid-row: 1 / 2; + } + + .post-type-amp_story .editor-writing-flow > div > div > div:first-child { + grid-column: 2 / 2; + grid-row: 1 / 1; + } + + .post-type-amp_story .editor-writing-flow > div > div > .editor-block-list__layout { + grid-column: 2 / 2; + grid-row: 2 / 2; + } + + .components-icon-button.editor-block-navigation { + display: none; + } + + #amp-root-navigation { + position: absolute; + left: 5px; + top: 182px; + width: 300px; + z-index: 80; + list-style-type: none; + padding: 0; + margin: 0; + } + + #amp-root-navigation .editor-inserter__toggle::after { + content: 'Add Layer'; + } + + .post-type-amp_story #amp-root-navigation .components-popover:not(.is-mobile).is-center .components-popover__content { + transform: translateX( -13% ); + } + + .post-type-amp_story.folded #amp-root-navigation .components-popover:not(.is-mobile):not(.is-middle).is-right .components-popover__content { + margin-left: -50px; + } + + /* + Legend of the magic numbers: + Sidebar + Expanded - 160px wide + Folded - 36px wide + Settings + Shown - 280px wide + Content - 383px (including 15px padding) + Block Navigation - <= 300px + */ + + /* Navigator in the context of the open sidebar, settings hidden. */ + .post-type-amp_story .edit-post-layout #amp-root-navigation { + max-width: calc( 100vw - 160px - 383px ); + } + + .post-type-amp_story .edit-post-layout .editor-writing-flow > div > div { + grid-template-columns: auto minmax( 368px, calc( 100vw - 160px - 315px ) ); + } + + /* Navigator in the context of minimum available space - open sidebar, settings shown. */ + .post-type-amp_story .edit-post-layout.is-sidebar-opened #amp-root-navigation { + max-width: calc( 100vw - 280px - 160px - 383px ); + } + + .post-type-amp_story .edit-post-layout.is-sidebar-opened .editor-writing-flow > div > div { + grid-template-columns: auto minmax( 368px, calc( 100vw - 280px - 160px - 315px ) ); + } + + /* Navigator in the context of folded sidebar, settings hidden. */ + .post-type-amp_story.folded #amp-root-navigation { + max-width: calc( 100vw - 36px - 383px ); + } + + .post-type-amp_story.folded .edit-post-visual-editor .editor-writing-flow > div > div { + grid-template-columns: auto minmax( 368px, calc( 100vw - 36px - 315px ) ); + } + + /* Navigator in the context of folded sidebar, settings shown. */ + .post-type-amp_story.folded .edit-post-layout.is-sidebar-opened #amp-root-navigation { + max-width: calc( 100vw - 36px - 280px - 383px ); + } + + .post-type-amp_story.folded .edit-post-layout.is-sidebar-opened .editor-writing-flow > div > div { + grid-template-columns: auto minmax( 368px, calc( 100vw - 36px - 280px - 315px ) ); + } + + #amp-root-navigation .editor-block-navigation__list { + list-style-type: none; + } + + #amp-root-navigation .components-icon-button .dashicon { + margin-right: 5px; + } + + .editor-selectors .component-editor__selector button { + background: no-repeat left top; + padding-left: 37px; + height: 40px; + margin: 1px 0 1px 4px; + } + + .editor-selectors .component-editor__selector .components-button:focus { + box-shadow: none; + } + + .post-type-amp_story .components-range-control__number { + width: 60px; + } +} + +/* + * 8. Shame + */ + +/* + * 8.1 Gutenberg - Warning div not clickable + * Fixes Gutenberg bug where an extra wrapping div is causing no pointer events on the notice layer. + */ + .post-type-amp_story .editor-block-list__layout .editor-block-list__block.has-warning .editor-block-list__block-edit > :not(.editor-warning) { pointer-events: all; } @@ -414,6 +603,4 @@ div[data-type="amp/amp-story-page"] > div > div > .is-selected-parent > .editor- pointer-events: none; } -.editor-block-list__layout .editor-block-list__block .editor-block-list__block-edit::before { - outline: none; -} + diff --git a/blocks/amp-story/amp-story-page.js b/blocks/amp-story/amp-story-page.js index 67001b3c16c..7ad3eb45fe9 100644 --- a/blocks/amp-story/amp-story-page.js +++ b/blocks/amp-story/amp-story-page.js @@ -1,5 +1,7 @@ +/* global ReactDOM */ + import uuid from 'uuid/v4'; -import BlockSelector from './block-selector'; +import BlockNavigation from './block-navigation'; import { BLOCK_ICONS, maybeIsSelectedParentClass @@ -12,9 +14,12 @@ const { const { InnerBlocks, PanelColorSettings, - InspectorControls + InspectorControls, + Inserter } = wp.editor; +const { Component } = wp.element; + const ALLOWED_BLOCKS = [ 'amp/amp-story-grid-layer-vertical', 'amp/amp-story-grid-layer-fill', @@ -22,6 +27,12 @@ const ALLOWED_BLOCKS = [ 'amp/amp-story-cta-layer' ]; +const { + hasSelectedInnerBlock, + getSelectedBlockClientId, + getBlockIndex +} = wp.data.select( 'core/editor' ); + const TEMPLATE = [ [ 'amp/amp-story-grid-layer-background-image' ], [ @@ -46,8 +57,6 @@ export default registerBlockType( title: __( 'Page', 'amp' ), category: 'layout', icon: BLOCK_ICONS[ 'amp/amp-story-page' ], - - // @todo Enforce that the amp-story-page can only be a root-level block; Using `parent: []` does not work, and it causes the inserter to be disabled entirely. attributes: { id: { type: 'string', @@ -73,37 +82,75 @@ export default registerBlockType( * https://github.com/ampproject/amphtml/blob/87fe1d02f902be97b596b36ec3421592c83d241e/extensions/amp-story/validator-amp-story.protoascii#L146-L171 * */ - edit( props ) { - const { setAttributes, attributes } = props; - const onChangeBackgroundColor = newBackgroundColor => { - setAttributes( { backgroundColor: newBackgroundColor } ); - }; + edit: class extends Component { + constructor( props ) { + // Call parent constructor. + super( props ); - // If the page ID is not set, add one. - if ( ! attributes.id ) { - setAttributes( { id: uuid() } ); + if ( ! props.attributes.id ) { + this.props.setAttributes( { id: uuid() } ); + } } - return [ - - - , - , - // Get the template dynamically. -
- -
- ]; + navList = +
+ + +
; + } else { + navList = +
+ +
; + } + ReactDOM.render( navList, document.getElementById( 'amp-root-navigation' ) ); + } + } + } + + render() { + const props = this.props; + const { setAttributes, attributes } = props; + const onChangeBackgroundColor = newBackgroundColor => { + setAttributes( { backgroundColor: newBackgroundColor } ); + }; + + return [ + + + , +
+ +
+ ]; + } }, save( { attributes } ) { diff --git a/blocks/amp-story/block-navigation.js b/blocks/amp-story/block-navigation.js new file mode 100644 index 00000000000..1886fe875d8 --- /dev/null +++ b/blocks/amp-story/block-navigation.js @@ -0,0 +1,123 @@ +/** + * WordPress dependencies + */ +const { withSelect, withDispatch } = wp.data; +const { Button, NavigableMenu } = wp.components; +const { getBlockType } = wp.blocks; +const { compose } = wp.compose; +const { __ } = wp.i18n; +const { BlockIcon } = wp.editor; + +function BlockNavigationList( { + blocks, + selectedBlockClientId, + selectBlock, + showNestedBlocks +} ) { + return ( + /* + * Disable reason: The `list` ARIA role is redundant but + * Safari+VoiceOver won't announce the list otherwise. + */ + /* eslint-disable jsx-a11y/no-redundant-roles */ + + /* eslint-enable jsx-a11y/no-redundant-roles */ + ); +} + +function BlockNavigation( { rootBlock, rootBlocks, selectedBlockClientId, selectBlock } ) { + const hasHierarchy = ( + rootBlock && ( + rootBlock.clientId !== selectedBlockClientId || + ( rootBlock.innerBlocks && rootBlock.innerBlocks.length !== 0 ) + ) + ); + + return ( + +

{ __( 'Block Navigation' ) }

+ { hasHierarchy && ( + + ) } + { ! hasHierarchy && ( + + ) } + { ( ! rootBlocks || rootBlocks.length === 0 ) && ( + // If there are no blocks in this document, don't render a list of blocks. + // Instead: inform the user no blocks exist yet. +

+ { __( 'No blocks created yet.' ) } +

+ ) } +
+ ); +} + +export default compose( + withSelect( ( select ) => { + const { + getSelectedBlockClientId, + getBlockHierarchyRootClientId, + getBlock, + getBlocks + } = select( 'core/editor' ); + const selectedBlockClientId = getSelectedBlockClientId(); + return { + rootBlocks: getBlocks(), + rootBlock: selectedBlockClientId ? getBlock( getBlockHierarchyRootClientId( selectedBlockClientId ) ) : null, + selectedBlockClientId + }; + } ), + withDispatch( ( dispatch, { onSelect = _.noop } ) => { + return { + selectBlock( clientId ) { + dispatch( 'core/editor' ).selectBlock( clientId ); + onSelect( clientId ); + } + }; + } ) +)( BlockNavigation ); diff --git a/blocks/amp-story/block-selector.js b/blocks/amp-story/block-selector.js deleted file mode 100644 index 80824e10158..00000000000 --- a/blocks/amp-story/block-selector.js +++ /dev/null @@ -1,104 +0,0 @@ - -const { __ } = wp.i18n; -const { - getBlockType -} = wp.blocks; -const { Component } = wp.element; -const { Button } = wp.components; -const { - dispatch, - select -} = wp.data; -const { - getBlock, - isBlockSelected, - hasSelectedInnerBlock, - getSelectedBlock -} = select( 'core/editor' ); -const { - selectBlock -} = dispatch( 'core/editor' ); - -import LayerInserter from './layer-inserter'; - -class BlockSelector extends Component { - render() { - if ( ! this.props.rootClientId ) { - return null; - } - - const rootBlock = getBlock( this.props.rootClientId ); - - if ( ! rootBlock.innerBlocks.length ) { - return null; - } - - let links = []; - let hasCtaLayer = false; - - window.lodash.forEachRight( rootBlock.innerBlocks, function( block, index ) { - let template = 'vertical'; - if ( 'amp/amp-story-grid-layer-background-image' === block.name || 'amp/amp-story-grid-layer-background-video' === block.name ) { - template = 'fill'; - } else if ( 'amp/amp-story-grid-layer-thirds' === block.name ) { - template = 'thirds'; - } else if ( 'amp/amp-story-grid-layer-horizontal' === block.name ) { - template = 'horizontal'; - } - let className = 'component-editor__selector template-' + template; - if ( isBlockSelected( block.clientId ) || hasSelectedInnerBlock( block.clientId ) ) { - className += ' is-selected'; - } - - let blockType = getBlockType( block.name ); - - if ( 'amp/amp-story-cta-layer' === block.name ) { - hasCtaLayer = true; - } - - links.push( -
  • - -
  • - ); - } ); - - let className = 'component-editor__selector page-selector'; - if ( isBlockSelected( this.props.rootClientId ) ) { - className += ' is-selected'; - } - - const inserterProps = { - rootClientId: this.props.rootClientId, - hasCtaLayer: hasCtaLayer - }; - - links.push( -
  • - -
  • - ); - - // @todo Creating a custom inserter since the default inserter doesn't allow taking the root client ID dynamically. Change if that becomes available. - return ( - - ); - } -} - -export default BlockSelector; diff --git a/blocks/amp-story/helpers.js b/blocks/amp-story/helpers.js index 44c57a05c8e..22a6a5d73e7 100644 --- a/blocks/amp-story/helpers.js +++ b/blocks/amp-story/helpers.js @@ -4,16 +4,25 @@ const { __ } = wp.i18n; const { SelectControl, RangeControl, - PanelBody + PanelBody, + Toolbar } = wp.components; +const { + Fragment +} = wp.element; + const { PanelColorSettings, InspectorControls, - InnerBlocks + InnerBlocks, + BlockTitle } = wp.editor; -const { hasSelectedInnerBlock } = wp.data.select( 'core/editor' ); +const { + hasSelectedInnerBlock, + getBlockRootClientId +} = wp.data.select( 'core/editor' ); const ANIMATION_DEFAULTS = { drop: 1600, @@ -316,8 +325,8 @@ export function saveGridLayer( attributes, template ) { * @return {[XML,XML]} Edit. */ export function editGridLayer( props, type ) { - const { setAttributes, attributes } = props; - + const { setAttributes, attributes, isSelected } = props; + const rootClientId = getBlockRootClientId( props.clientId ); return [ { @@ -329,6 +338,9 @@ export function editGridLayer( props, type ) { } , + isSelected && ( + getLayerBreadCrumb( props.clientId, rootClientId ) + ),
    @@ -343,7 +355,7 @@ export function editGridLayer( props, type ) { * @return {[XML,XML]} Edit. */ export function editFillLayer( props, template ) { - const { setAttributes, attributes } = props; + const { setAttributes, attributes, isSelected } = props; return [ @@ -356,8 +368,34 @@ export function editFillLayer( props, template ) { } , + isSelected && ( + getLayerBreadCrumb( props.clientId, getBlockRootClientId( props.clientId ) ) + ),
    ]; } + +/** + * Get Layer Breadcrumb. + * + * @param {string} clientId Layer ID. + * @param {string} rootClientId Parent ID. + * @return {XML} Breadcrumb. + */ +function getLayerBreadCrumb( clientId, rootClientId ) { + return ( +
    + + { rootClientId && ( + + + + + ) } + + +
    + ); +} diff --git a/blocks/amp-story/layer-inserter.js b/blocks/amp-story/layer-inserter.js deleted file mode 100644 index a39d4a36e13..00000000000 --- a/blocks/amp-story/layer-inserter.js +++ /dev/null @@ -1,171 +0,0 @@ -const { __ } = wp.i18n; -const { IconButton } = wp.components; -const { Component } = wp.element; -const { BlockIcon } = wp.editor; -const { - createBlock, - getBlockType, - getBlockMenuDefaultClassName -} = wp.blocks; - -const { - Dropdown -} = wp.components; - -const { - dispatch, - select -} = wp.data; -const { - getBlock -} = select( 'core/editor' ); -const { - insertBlock -} = dispatch( 'core/editor' ); - -class LayerInserter extends Component { - constructor() { - super( ...arguments ); - - this.onToggle = this.onToggle.bind( this ); - } - - onInsertBlock( item, rootClientId ) { - const { name } = item; - const insertedBlock = createBlock( name ); - const rootBlock = getBlock( rootClientId ); - const index = rootBlock.innerBlocks.length ? rootBlock.innerBlocks.length : 0; - - insertBlock( insertedBlock, index, rootClientId ); - } - - onToggle( isOpen ) { - const { onToggle } = this.props; - - // Surface toggle callback to parent component - if ( onToggle ) { - onToggle( isOpen ); - } - } - - render() { - const { - rootClientId, - hasCtaLayer - } = this.props; - - const { - getInserterItems - } = wp.data.select( 'core/editor' ); - let items = getInserterItems( rootClientId ); - - if ( items.length === 0 ) { - return null; - } - - const onInsertBlock = this.onInsertBlock; - const gridLayers = [ - 'amp/amp-story-grid-layer-horizontal', - 'amp/amp-story-grid-layer-vertical', - 'amp/amp-story-grid-layer-thirds', - 'amp/amp-story-grid-layer-background-image', - 'amp/amp-story-grid-layer-background-video' - ]; - - return ( - ( - - - ) } - renderContent={ ( { onClose } ) => { - const onSelect = ( item ) => { - if ( ! hasCtaLayer || gridLayers.includes( item.name ) ) { - onInsertBlock( item, rootClientId ); - onClose(); - } - }; - - // @todo If CTA layer is already added, don't display it here. - items = [ - getBlockType( 'amp/amp-story-cta-layer' ) - ]; - - _.each( gridLayers, function( layer ) { - items.push( getBlockType( layer ) ); - } ); - - const listClassName = 'editor-block-types-list' + ( hasCtaLayer ? ' amp-story-has-cta-layer' : '' ); - - return ( -
    -
    -
      - { items.map( ( item ) => { - const itemIconStyle = item.icon ? { - backgroundColor: item.icon.background, - color: item.icon.foreground - } : {}; - const itemIconStackStyle = item.icon && item.icon.shadowColor ? { - backgroundColor: item.icon.shadowColor - } : {}; - - const className = 'editor-block-types-list__item ' + getBlockMenuDefaultClassName( item.name ); - - return ( -
    • - -
    • - ); - } ) } -
    - -
    -
    - ); - } } - /> - ); - } -} - -export default LayerInserter;