Skip to content
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

[Gutenberg] Add amp-fit-text support to text blocks #1151

Merged
merged 14 commits into from
May 25, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions amp.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ function amp_init() {
add_action( 'admin_init', 'AMP_Options_Manager::register_settings' );
add_action( 'wp_loaded', 'amp_editor_core_blocks' );
add_action( 'wp_loaded', 'amp_post_meta_box' );
add_action( 'wp_loaded', 'amp_editor_core_blocks' );
add_action( 'wp_loaded', 'amp_add_options_menu' );
add_action( 'parse_query', 'amp_correct_query_when_is_front_page' );

Expand Down
10 changes: 10 additions & 0 deletions assets/css/amp-default.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@
/** Force the image into a box of fixed dimensions and use object-fit to scale. **/
object-fit: contain;
}

.entry__content amp-fit-text blockquote,
amp-fit-text h1,
amp-fit-text h2,
amp-fit-text h3,
amp-fit-text h4,
amp-fit-text h5,
amp-fit-text h6 {
font-size: inherit;
}
4 changes: 4 additions & 0 deletions assets/css/amp-editor-blocks.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.is-amp-fit-text + .blocks-font-size > .components-font-size-picker__buttons,
.is-amp-fit-text + .blocks-font-size > .components-font-size-picker__custom-input {
display: none;
}
197 changes: 194 additions & 3 deletions assets/js/amp-editor-blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,19 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
'core/image',
'core/video',
'core/audio'
]
],
textBlocks: [
'core/paragraph',
'core/heading',
'core/code',
'core/quote',
'core/subhead'
],
ampSettingsLabel: __( 'AMP Settings' ),
fontSizes: {
small: 14,
larger: 48
}
}
};

Expand Down Expand Up @@ -178,6 +190,38 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
};
}

// Fit-text for text blocks.
if ( -1 !== component.data.textBlocks.indexOf( name ) ) {
if ( ! settings.attributes ) {
settings.attributes = {};
}
settings.attributes.ampFitText = {
type: 'boolean',
default: false
};
settings.attributes.minFont = {
type: 'number',
default: component.data.fontSizes.small,
source: 'attribute',
selector: 'amp-fit-text',
attribute: 'min-font-size'
};
settings.attributes.maxFont = {
type: 'number',
default: component.data.fontSizes.larger,
source: 'attribute',
selector: 'amp-fit-text',
attribute: 'max-font-size'
};
settings.attributes.height = {
type: 'number',
default: 50,
source: 'attribute',
selector: 'amp-fit-text',
attribute: 'height'
};
}

// Layout settings for embeds and media blocks.
if ( 0 === name.indexOf( 'core-embed' ) || -1 !== component.data.mediaBlocks.indexOf( name ) ) {
if ( ! settings.attributes ) {
Expand Down Expand Up @@ -222,6 +266,8 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
}
} else if ( -1 !== component.data.mediaBlocks.indexOf( name ) || 0 === name.indexOf( 'core-embed/' ) ) {
inspectorControls = component.setUpInspectorControls( props );
} else if ( -1 !== component.data.textBlocks.indexOf( name ) ) {
inspectorControls = component.setUpTextBlocksInspectorControls( props );
}

// Return just inspector controls in case of 'nodisplay'.
Expand Down Expand Up @@ -314,6 +360,135 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
);
};

/**
* Setup inspector controls for text blocks.
*
* @param {Object} props Props.
* @return {Object|Element|*|{$$typeof, type, key, ref, props, _owner}} Inspector Controls.
*/
component.setUpTextBlocksInspectorControls = function setUpInspectorControls( props ) {
var ampFitText = props.attributes.ampFitText,
minFont = props.attributes.minFont,
maxFont = props.attributes.maxFont,
height = props.attributes.height,
isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.editor.InspectorControls,
TextControl = wp.components.TextControl,
FontSizePicker = wp.components.FontSizePicker,
ToggleControl = wp.components.ToggleControl,
PanelBody = wp.components.PanelBody,
label = __( 'Use AMP Fit Text' ),
FONT_SIZES = [
{
name: 'small',
shortName: __( 'S' ),
size: 14
},
{
name: 'regular',
shortName: __( 'M' ),
size: 16
},
{
name: 'large',
shortName: __( 'L' ),
size: 36
},
{
name: 'larger',
shortName: __( 'XL' ),
size: 48
}
];

if ( ampFitText ) {
return isSelected && (
el( InspectorControls, { key: 'inspector' },
el( PanelBody, { title: component.data.ampSettingsLabel, className: 'is-amp-fit-text' },
el( ToggleControl, {
label: label,
checked: ampFitText,
onChange: function() {
props.setAttributes( { ampFitText: ! ampFitText } );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should ampFitText be replaced with event.checked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the core blocks seem to use negating the current value for toggle controls (as far as I've seen), thought of staying consistent with the core blocks when using that. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this also result in the font-size-picker and range-control under Text Settings to be hidden when ampFitText is true?

}
} ),
el( TextControl, {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the font-size-picker control be used instead?

label: __( 'Height' ),
value: height,
type: 'number',
min: 1,
onChange: function( nextHeight ) {
props.setAttributes( { height: nextHeight } );
}
} ),
maxFont > height && el(
wp.components.Notice,
{
status: 'error',
isDismissible: false
},
__( 'The height must be greater than the max font size.' )
),
el( PanelBody, { title: __( 'Minimum font size' ) },
el( FontSizePicker, {
fallbackFontSize: 14,
value: minFont,
fontSizes: FONT_SIZES,
onChange: function( nextMinFont ) {
if ( ! nextMinFont ) {
nextMinFont = component.data.fontSizes.small; // @todo Supplying fallbackFontSize should be done automatically by the component?
}
if ( nextMinFont <= maxFont ) {
props.setAttributes( { minFont: nextMinFont } );
}
}
} )
),
minFont > maxFont && el(
wp.components.Notice,
{
status: 'error',
isDismissible: false
},
__( 'The min font size must less than the max font size.' )
),
el( PanelBody, { title: __( 'Maximum font size' ) },
el( FontSizePicker, {
value: maxFont,
fallbackFontSize: 48,
fontSizes: FONT_SIZES,
onChange: function( nextMaxFont ) {
if ( ! nextMaxFont ) {
nextMaxFont = component.data.fontSizes.larger; // @todo Supplying fallbackFontSize should be done automatically by the component?
}
props.setAttributes( {
maxFont: nextMaxFont,
height: Math.max( nextMaxFont, height )
} );
}
} )
)
)
)
);
}

return isSelected && (
el( InspectorControls, { key: 'inspector' },
el( PanelBody, { title: component.data.ampSettingsLabel },
el( ToggleControl, {
label: label,
checked: ampFitText,
onChange: function() {
props.setAttributes( { ampFitText: ! ampFitText } );
}
} )
)
)
);
};

/**
* Set up inspector controls for shortcode block.
* Adds ampCarousel attribute in case of gallery shortcode.
Expand All @@ -325,7 +500,7 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
var ampCarousel = props.attributes.ampCarousel,
isSelected = props.isSelected,
el = wp.element.createElement,
InspectorControls = wp.blocks.InspectorControls,
InspectorControls = wp.editor.InspectorControls,
ToggleControl = wp.components.ToggleControl,
PanelBody = wp.components.PanelBody,
toggleControl;
Expand Down Expand Up @@ -359,7 +534,12 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
* @return {*} Output element.
*/
component.filterBlocksSave = function filterBlocksSave( element, blockType, attributes ) {
var text;
var text,
fitTextProps = {
layout: 'fixed-height',
children: element
};

if ( 'core/shortcode' === blockType.name && component.isGalleryShortcode( attributes ) ) {
if ( attributes.ampCarousel ) {
// If the text contains amp-carousel, lets remove it.
Expand Down Expand Up @@ -390,6 +570,17 @@ var ampEditorBlocks = ( function() { // eslint-disable-line no-unused-vars
{},
text
);
} else if ( -1 !== component.data.textBlocks.indexOf( blockType.name ) && attributes.ampFitText ) {
if ( attributes.minFont ) {
fitTextProps[ 'min-font-size' ] = attributes.minFont;
}
if ( attributes.maxFont ) {
fitTextProps[ 'max-font-size' ] = attributes.maxFont;
}
if ( attributes.height ) {
fitTextProps.height = attributes.height;
}
return wp.element.createElement( 'amp-fit-text', fitTextProps );
}
return element;
};
Expand Down
76 changes: 64 additions & 12 deletions includes/admin/class-amp-editor-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,39 @@
*/
class AMP_Editor_Blocks {

/**
* List of AMP scripts that need to be printed when AMP components are used in non-AMP document context ("dirty AMP").
*
* @var array
*/
public $content_required_amp_scripts = array();

/**
* AMP components that have blocks.
*
* @var array
*/
public $amp_blocks = array(
'amp-mathml',
'amp-o2-player',
'amp-ooyala-player',
'amp-reach-player',
'amp-springboard-player',
'amp-jwplayer',
'amp-brid-player',
'amp-ima-video',
'amp-fit-text',
);

/**
* Init.
*/
public function init() {
if ( function_exists( 'gutenberg_init' ) ) {
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
add_filter( 'wp_kses_allowed_html', array( $this, 'whitelist_block_atts_in_wp_kses_allowed_html' ), 10, 2 );
add_filter( 'the_content', array( $this, 'tally_content_requiring_amp_scripts' ) );
add_action( 'wp_print_footer_scripts', array( $this, 'print_dirty_amp_scripts' ) );
}
}

Expand All @@ -40,18 +66,7 @@ public function whitelist_block_atts_in_wp_kses_allowed_html( $tags, $context )
$tag['data-amp-noloading'] = true;
}

$amp_blocks = array(
'amp-mathml',
'amp-o2-player',
'amp-ooyala-player',
'amp-reach-player',
'amp-springboard-player',
'amp-jwplayer',
'amp-brid-player',
'amp-ima-video',
);

foreach ( $amp_blocks as $amp_block ) {
foreach ( $this->amp_blocks as $amp_block ) {
if ( ! isset( $tags[ $amp_block ] ) ) {
$tags[ $amp_block ] = array();
}
Expand All @@ -62,6 +77,7 @@ public function whitelist_block_atts_in_wp_kses_allowed_html( $tags, $context )
'layout',
'width',
'height',
'class',
),
true
),
Expand Down Expand Up @@ -89,6 +105,14 @@ public function whitelist_block_atts_in_wp_kses_allowed_html( $tags, $context )
*/
public function enqueue_block_editor_assets() {

// Styles.
wp_enqueue_style(
'amp-editor-blocks-style',
amp_get_asset_url( 'css/amp-editor-blocks.css' ),
array(),
AMP__VERSION
);

// Scripts.
wp_enqueue_script(
'amp-editor-blocks-build',
Expand All @@ -107,4 +131,32 @@ public function enqueue_block_editor_assets() {

wp_add_inline_script( 'amp-editor-blocks', sprintf( 'ampEditorBlocks.boot();' ) );
}

/**
* Tally the AMP component scripts that are needed in a dirty AMP document.
*
* @param string $content Content.
* @return string Content (unmodified).
*/
public function tally_content_requiring_amp_scripts( $content ) {
if ( ! is_amp_endpoint() ) {
$pattern = sprintf( '/<(%s)\b.*?>/s', join( '|', $this->amp_blocks ) );
if ( preg_match_all( $pattern, $content, $matches ) ) {
$this->content_required_amp_scripts = array_merge(
$this->content_required_amp_scripts,
$matches[1]
);
}
}
return $content;
}

/**
* Print AMP scripts required for AMP components used in a non-AMP document (dirty AMP).
*/
public function print_dirty_amp_scripts() {
if ( ! is_amp_endpoint() && ! empty( $this->content_required_amp_scripts ) ) {
wp_scripts()->do_items( $this->content_required_amp_scripts );
}
}
}