summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/extensions/blocks/slideshow')
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/create-swiper.js48
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/edit.js249
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/editor.js7
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/editor.scss44
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/index.js93
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/save.js15
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/slideshow.js232
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/slideshow.php161
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/style.scss165
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/swiper-callbacks.js95
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/transforms.js78
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/view.js70
12 files changed, 161 insertions, 1096 deletions
diff --git a/plugins/jetpack/extensions/blocks/slideshow/create-swiper.js b/plugins/jetpack/extensions/blocks/slideshow/create-swiper.js
deleted file mode 100644
index 72a54f56..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/create-swiper.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * External dependencies
- */
-import { mapValues, merge } from 'lodash';
-
-/**
- * Internal dependencies
- */
-import './style.scss';
-
-export default async function createSwiper(
- container = '.swiper-container',
- params = {},
- callbacks = {}
-) {
- const defaultParams = {
- effect: 'slide',
- grabCursor: true,
- init: true,
- initialSlide: 0,
- navigation: {
- nextEl: '.swiper-button-next',
- prevEl: '.swiper-button-prev',
- },
- pagination: {
- bulletElement: 'button',
- clickable: true,
- el: '.swiper-pagination',
- type: 'bullets',
- },
- preventClicksPropagation: false /* Necessary for normal block interactions */,
- releaseFormElements: false,
- setWrapperSize: true,
- touchStartPreventDefault: false,
- on: mapValues(
- callbacks,
- callback =>
- function() {
- callback( this );
- }
- ),
- };
- const [ { default: Swiper } ] = await Promise.all( [
- import( /* webpackChunkName: "swiper" */ 'swiper/dist/js/swiper.js' ),
- import( /* webpackChunkName: "swiper" */ 'swiper/dist/css/swiper.css' ),
- ] );
- return new Swiper( container, merge( {}, defaultParams, params ) );
-}
diff --git a/plugins/jetpack/extensions/blocks/slideshow/edit.js b/plugins/jetpack/extensions/blocks/slideshow/edit.js
deleted file mode 100644
index 9c03f029..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/edit.js
+++ /dev/null
@@ -1,249 +0,0 @@
-/**
- * External dependencies
- */
-import { __, _x } from '@wordpress/i18n';
-import { Component, Fragment } from '@wordpress/element';
-import { compose } from '@wordpress/compose';
-import { filter, pick } from 'lodash';
-import { isBlobURL } from '@wordpress/blob';
-import { withDispatch } from '@wordpress/data';
-import {
- BlockControls,
- BlockIcon,
- InspectorControls,
- MediaPlaceholder,
- MediaUpload,
- mediaUpload,
-} from '@wordpress/editor';
-import {
- DropZone,
- FormFileUpload,
- IconButton,
- PanelBody,
- RangeControl,
- SelectControl,
- ToggleControl,
- Toolbar,
- withNotices,
-} from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import { icon } from '.';
-import Slideshow from './slideshow';
-import './editor.scss';
-
-const ALLOWED_MEDIA_TYPES = [ 'image' ];
-
-const effectOptions = [
- { label: _x( 'Slide', 'Slideshow transition effect', 'jetpack' ), value: 'slide' },
- { label: _x( 'Fade', 'Slideshow transition effect', 'jetpack' ), value: 'fade' },
-];
-
-export const pickRelevantMediaFiles = image =>
- pick( image, [ 'alt', 'id', 'link', 'url', 'caption' ] );
-
-class SlideshowEdit extends Component {
- constructor() {
- super( ...arguments );
- this.state = {
- selectedImage: null,
- };
- }
- setAttributes( attributes ) {
- if ( attributes.ids ) {
- throw new Error(
- 'The "ids" attribute should not be changed directly. It is managed automatically when "images" attribute changes'
- );
- }
-
- if ( attributes.images ) {
- attributes = {
- ...attributes,
- ids: attributes.images.map( ( { id } ) => parseInt( id, 10 ) ),
- };
- }
-
- this.props.setAttributes( attributes );
- }
- onSelectImages = images => {
- const mapped = images.map( image => pickRelevantMediaFiles( image ) );
- this.setAttributes( {
- images: mapped,
- } );
- };
- onRemoveImage = index => {
- return () => {
- const images = filter( this.props.attributes.images, ( img, i ) => index !== i );
- this.setState( { selectedImage: null } );
- this.setAttributes( { images } );
- };
- };
- addFiles = files => {
- const currentImages = this.props.attributes.images || [];
- const { lockPostSaving, unlockPostSaving, noticeOperations } = this.props;
- const lockName = 'slideshowBlockLock';
- lockPostSaving( lockName );
- mediaUpload( {
- allowedTypes: ALLOWED_MEDIA_TYPES,
- filesList: files,
- onFileChange: images => {
- const imagesNormalized = images.map( image => pickRelevantMediaFiles( image ) );
- this.setAttributes( {
- images: [ ...currentImages, ...imagesNormalized ],
- } );
- if ( ! imagesNormalized.every( image => isBlobURL( image.url ) ) ) {
- unlockPostSaving( lockName );
- }
- },
- onError: noticeOperations.createErrorNotice,
- } );
- };
- uploadFromFiles = event => this.addFiles( event.target.files );
- render() {
- const {
- attributes,
- className,
- isSelected,
- noticeOperations,
- noticeUI,
- setAttributes,
- } = this.props;
- const { align, autoplay, delay, effect, images } = attributes;
- const prefersReducedMotion =
- typeof window !== 'undefined' &&
- window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
- const controls = (
- <Fragment>
- <InspectorControls>
- <PanelBody title={ __( 'Autoplay', 'jetpack' ) }>
- <ToggleControl
- label={ __( 'Autoplay', 'jetpack' ) }
- help={ __( 'Autoplay between slides', 'jetpack' ) }
- checked={ autoplay }
- onChange={ value => {
- setAttributes( { autoplay: value } );
- } }
- />
- { autoplay && (
- <RangeControl
- label={ __( 'Delay between transitions (in seconds)', 'jetpack' ) }
- value={ delay }
- onChange={ value => {
- setAttributes( { delay: value } );
- } }
- min={ 1 }
- max={ 5 }
- />
- ) }
- { autoplay && prefersReducedMotion && (
- <span>
- { __(
- 'The Reduce Motion accessibility option is selected, therefore autoplay will be disabled in this browser.',
- 'jetpack'
- ) }
- </span>
- ) }
- </PanelBody>
- <PanelBody title={ __( 'Effects', 'jetpack' ) }>
- <SelectControl
- label={ __( 'Transition effect', 'jetpack' ) }
- value={ effect }
- onChange={ value => {
- setAttributes( { effect: value } );
- } }
- options={ effectOptions }
- />
- </PanelBody>
- </InspectorControls>
- <BlockControls>
- { !! images.length && (
- <Toolbar>
- <MediaUpload
- onSelect={ this.onSelectImages }
- allowedTypes={ ALLOWED_MEDIA_TYPES }
- multiple
- gallery
- value={ images.map( img => img.id ) }
- render={ ( { open } ) => (
- <IconButton
- className="components-toolbar__control"
- label={ __( 'Edit Slideshow', 'jetpack' ) }
- icon="edit"
- onClick={ open }
- />
- ) }
- />
- </Toolbar>
- ) }
- </BlockControls>
- </Fragment>
- );
-
- if ( images.length === 0 ) {
- return (
- <Fragment>
- { controls }
- <MediaPlaceholder
- icon={ <BlockIcon icon={ icon } /> }
- className={ className }
- labels={ {
- title: __( 'Slideshow', 'jetpack' ),
- instructions: __(
- 'Drag images, upload new ones or select files from your library.',
- 'jetpack'
- ),
- } }
- onSelect={ this.onSelectImages }
- accept="image/*"
- allowedTypes={ ALLOWED_MEDIA_TYPES }
- multiple
- notices={ noticeUI }
- onError={ noticeOperations.createErrorNotice }
- />
- </Fragment>
- );
- }
- return (
- <Fragment>
- { controls }
- { noticeUI }
- <Slideshow
- align={ align }
- autoplay={ autoplay }
- className={ className }
- delay={ delay }
- effect={ effect }
- images={ images }
- onError={ noticeOperations.createErrorNotice }
- />
- <DropZone onFilesDrop={ this.addFiles } />
- { isSelected && (
- <div className="wp-block-jetpack-slideshow__add-item">
- <FormFileUpload
- multiple
- isLarge
- className="wp-block-jetpack-slideshow__add-item-button"
- onChange={ this.uploadFromFiles }
- accept="image/*"
- icon="insert"
- >
- { __( 'Upload an image', 'jetpack' ) }
- </FormFileUpload>
- </div>
- ) }
- </Fragment>
- );
- }
-}
-export default compose(
- withDispatch( dispatch => {
- const { lockPostSaving, unlockPostSaving } = dispatch( 'core/editor' );
- return {
- lockPostSaving,
- unlockPostSaving,
- };
- } ),
- withNotices
-)( SlideshowEdit );
diff --git a/plugins/jetpack/extensions/blocks/slideshow/editor.js b/plugins/jetpack/extensions/blocks/slideshow/editor.js
deleted file mode 100644
index d05f4039..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/editor.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * Internal dependencies
- */
-import registerJetpackBlock from '../../shared/register-jetpack-block';
-import { name, settings } from '.';
-
-registerJetpackBlock( name, settings );
diff --git a/plugins/jetpack/extensions/blocks/slideshow/editor.scss b/plugins/jetpack/extensions/blocks/slideshow/editor.scss
deleted file mode 100644
index 11c07c25..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/editor.scss
+++ /dev/null
@@ -1,44 +0,0 @@
-@import '../../shared/styles/gutenberg-colors.scss';
-
-.wp-block-jetpack-slideshow__add-item {
- margin-top: 4px;
- width: 100%;
-
- .components-form-file-upload,
- .components-button.wp-block-jetpack-slideshow__add-item-button {
- width: 100%;
- height: 100%;
- }
-
- .components-button.wp-block-jetpack-slideshow__add-item-button {
- display: flex;
- flex-direction: column;
- justify-content: center;
- box-shadow: none;
- border: none;
- border-radius: 0;
- min-height: 100px;
-
- .dashicon {
- margin-top: 10px;
- }
-
- &:hover,
- &:focus {
- border: 1px solid $dark-gray-500;
- }
- }
-}
-
-.wp-block-jetpack-slideshow_slide {
- .components-spinner {
- position: absolute;
- top: 50%;
- left: 50%;
- margin-top: -9px;
- margin-left: -9px;
- }
- &.is-transient img {
- opacity: 0.3;
- }
-}
diff --git a/plugins/jetpack/extensions/blocks/slideshow/index.js b/plugins/jetpack/extensions/blocks/slideshow/index.js
deleted file mode 100644
index 9f4b2807..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/index.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * External dependencies
- */
-import { __, _x } from '@wordpress/i18n';
-import { Path, SVG } from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import edit from './edit';
-import save from './save';
-import transforms from './transforms';
-
-export const icon = (
- <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
- <Path d="M0 0h24v24H0z" fill="none" />
- <Path d="M10 8v8l5-4-5-4zm9-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z" />
- </SVG>
-);
-
-const attributes = {
- align: {
- default: 'center',
- type: 'string',
- },
- autoplay: {
- type: 'boolean',
- default: false,
- },
- delay: {
- type: 'number',
- default: 3,
- },
- ids: {
- default: [],
- type: 'array',
- },
- images: {
- type: 'array',
- default: [],
- source: 'query',
- selector: '.swiper-slide',
- query: {
- alt: {
- source: 'attribute',
- selector: 'img',
- attribute: 'alt',
- default: '',
- },
- caption: {
- type: 'string',
- source: 'html',
- selector: 'figcaption',
- },
- id: {
- source: 'attribute',
- selector: 'img',
- attribute: 'data-id',
- },
- url: {
- source: 'attribute',
- selector: 'img',
- attribute: 'src',
- },
- },
- },
- effect: {
- type: 'string',
- default: 'slide',
- },
-};
-
-export const name = 'slideshow';
-
-export const settings = {
- title: __( 'Slideshow', 'jetpack' ),
- category: 'jetpack',
- keywords: [
- _x( 'image', 'block search term', 'jetpack' ),
- _x( 'gallery', 'block search term', 'jetpack' ),
- _x( 'slider', 'block search term', 'jetpack' ),
- ],
- description: __( 'Add an interactive slideshow.', 'jetpack' ),
- attributes,
- supports: {
- align: [ 'center', 'wide', 'full' ],
- html: false,
- },
- icon,
- edit,
- save,
- transforms,
-};
diff --git a/plugins/jetpack/extensions/blocks/slideshow/save.js b/plugins/jetpack/extensions/blocks/slideshow/save.js
deleted file mode 100644
index 59879ded..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/save.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * Internal dependencies
- */
-import Slideshow from './slideshow';
-
-export default ( { attributes: { align, autoplay, delay, effect, images }, className } ) => (
- <Slideshow
- align={ align }
- autoplay={ autoplay }
- className={ className }
- delay={ delay }
- effect={ effect }
- images={ images }
- />
-);
diff --git a/plugins/jetpack/extensions/blocks/slideshow/slideshow.js b/plugins/jetpack/extensions/blocks/slideshow/slideshow.js
deleted file mode 100644
index b7d97c1a..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/slideshow.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-import ResizeObserver from 'resize-observer-polyfill';
-import { __ } from '@wordpress/i18n';
-import { Component, createRef } from '@wordpress/element';
-import { isBlobURL } from '@wordpress/blob';
-import { isEqual } from 'lodash';
-import { RichText } from '@wordpress/editor';
-import { Spinner } from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import createSwiper from './create-swiper';
-import {
- swiperApplyAria,
- swiperInit,
- swiperPaginationRender,
- swiperResize,
-} from './swiper-callbacks';
-
-class Slideshow extends Component {
- pendingRequestAnimationFrame = null;
- resizeObserver = null;
- static defaultProps = {
- effect: 'slide',
- };
-
- constructor( props ) {
- super( props );
-
- this.slideshowRef = createRef();
- this.btnNextRef = createRef();
- this.btnPrevRef = createRef();
- this.paginationRef = createRef();
- }
-
- componentDidMount() {
- const { onError } = this.props;
- this.buildSwiper()
- .then( swiper => {
- this.swiperInstance = swiper;
- this.initializeResizeObserver( swiper );
- } )
- .catch( () => {
- onError( __( 'The Swiper library could not be loaded.', 'jetpack' ) );
- } );
- }
-
- componentWillUnmount() {
- this.clearResizeObserver();
- this.clearPendingRequestAnimationFrame();
- }
-
- componentDidUpdate( prevProps ) {
- const { align, autoplay, delay, effect, images, onError } = this.props;
-
- /* A change in alignment or images only needs an update */
- if ( align !== prevProps.align || ! isEqual( images, prevProps.images ) ) {
- this.swiperInstance && this.swiperInstance.update();
- }
- /* A change in effect requires a full rebuild */
- if (
- effect !== prevProps.effect ||
- autoplay !== prevProps.autoplay ||
- delay !== prevProps.delay ||
- images !== prevProps.images
- ) {
- let realIndex;
- if ( ! this.swiperIndex ) {
- realIndex = 0;
- } else if ( images.length === prevProps.images.length ) {
- realIndex = this.swiperInstance.realIndex;
- } else {
- realIndex = prevProps.images.length;
- }
- this.swiperInstance && this.swiperInstance.destroy( true, true );
- this.buildSwiper( realIndex )
- .then( swiper => {
- this.swiperInstance = swiper;
- this.initializeResizeObserver( swiper );
- } )
- .catch( () => {
- onError( __( 'The Swiper library could not be loaded.', 'jetpack' ) );
- } );
- }
- }
-
- initializeResizeObserver = swiper => {
- this.clearResizeObserver();
- this.resizeObserver = new ResizeObserver( () => {
- this.clearPendingRequestAnimationFrame();
- this.pendingRequestAnimationFrame = requestAnimationFrame( () => {
- swiperResize( swiper );
- swiper.update();
- } );
- } );
- this.resizeObserver.observe( swiper.el );
- };
-
- clearPendingRequestAnimationFrame = () => {
- if ( this.pendingRequestAnimationFrame ) {
- cancelAnimationFrame( this.pendingRequestAnimationFrame );
- this.pendingRequestAnimationFrame = null;
- }
- };
-
- clearResizeObserver = () => {
- if ( this.resizeObserver ) {
- this.resizeObserver.disconnect();
- this.resizeObserver = null;
- }
- };
-
- render() {
- const { autoplay, className, delay, effect, images } = this.props;
- // Note: React omits the data attribute if the value is null, but NOT if it is false.
- // This is the reason for the unusual logic related to autoplay below.
- /* eslint-disable jsx-a11y/anchor-is-valid */
- return (
- <div
- className={ className }
- data-autoplay={ autoplay || null }
- data-delay={ autoplay ? delay : null }
- data-effect={ effect }
- >
- <div
- className="wp-block-jetpack-slideshow_container swiper-container"
- ref={ this.slideshowRef }
- >
- <ul className="wp-block-jetpack-slideshow_swiper-wrappper swiper-wrapper">
- { images.map( ( { alt, caption, id, url } ) => (
- <li
- className={ classnames(
- 'wp-block-jetpack-slideshow_slide',
- 'swiper-slide',
- isBlobURL( url ) && 'is-transient'
- ) }
- key={ id }
- >
- <figure>
- <img
- alt={ alt }
- className={
- `wp-block-jetpack-slideshow_image wp-image-${ id }` /* wp-image-${ id } makes WordPress add a srcset */
- }
- data-id={ id }
- src={ url }
- />
- { isBlobURL( url ) && <Spinner /> }
- { caption && (
- <RichText.Content
- className="wp-block-jetpack-slideshow_caption gallery-caption"
- tagName="figcaption"
- value={ caption }
- />
- ) }
- </figure>
- </li>
- ) ) }
- </ul>
- <a
- className="wp-block-jetpack-slideshow_button-prev swiper-button-prev swiper-button-white"
- ref={ this.btnPrevRef }
- role="button"
- />
- <a
- className="wp-block-jetpack-slideshow_button-next swiper-button-next swiper-button-white"
- ref={ this.btnNextRef }
- role="button"
- />
- <a
- aria-label="Pause Slideshow"
- className="wp-block-jetpack-slideshow_button-pause"
- role="button"
- />
- <div
- className="wp-block-jetpack-slideshow_pagination swiper-pagination swiper-pagination-white"
- ref={ this.paginationRef }
- />
- </div>
- </div>
- );
- /* eslint-enable jsx-a11y/anchor-is-valid */
- }
-
- prefersReducedMotion = () => {
- return (
- typeof window !== 'undefined' &&
- window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches
- );
- };
-
- buildSwiper = ( initialSlide = 0 ) =>
- // Using refs instead of className-based selectors allows us to
- // have multiple swipers on one page without collisions, and
- // without needing to add IDs or the like.
- createSwiper(
- this.slideshowRef.current,
- {
- autoplay:
- this.props.autoplay && ! this.prefersReducedMotion()
- ? {
- delay: this.props.delay * 1000,
- disableOnInteraction: false,
- }
- : false,
- effect: this.props.effect,
- loop: true,
- initialSlide,
- navigation: {
- nextEl: this.btnNextRef.current,
- prevEl: this.btnPrevRef.current,
- },
- pagination: {
- clickable: true,
- el: this.paginationRef.current,
- type: 'bullets',
- },
- },
- {
- init: swiperInit,
- imagesReady: swiperResize,
- paginationRender: swiperPaginationRender,
- transitionEnd: swiperApplyAria,
- }
- );
-}
-
-export default Slideshow;
diff --git a/plugins/jetpack/extensions/blocks/slideshow/slideshow.php b/plugins/jetpack/extensions/blocks/slideshow/slideshow.php
index be9ae6c0..ba18661c 100644
--- a/plugins/jetpack/extensions/blocks/slideshow/slideshow.php
+++ b/plugins/jetpack/extensions/blocks/slideshow/slideshow.php
@@ -24,5 +24,166 @@ jetpack_register_block(
*/
function jetpack_slideshow_block_load_assets( $attr, $content ) {
Jetpack_Gutenberg::load_assets_as_required( 'slideshow' );
+ if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
+ return jetpack_slideshow_block_render_amp( $attr );
+ }
return $content;
}
+
+/**
+ * Render slideshow block for AMP
+ *
+ * @param array $attr Array containing the slideshow block attributes.
+ *
+ * @return string
+ */
+function jetpack_slideshow_block_render_amp( $attr ) {
+ static $wp_block_jetpack_slideshow_id = 0;
+ $wp_block_jetpack_slideshow_id++;
+
+ $ids = empty( $attr['ids'] ) ? array() : $attr['ids'];
+ $autoplay = empty( $attr['autoplay'] ) ? false : $attr['autoplay'];
+
+ $extras = array(
+ 'wp-amp-block',
+ $autoplay ? 'wp-block-jetpack-slideshow__autoplay' : null,
+ $autoplay ? 'wp-block-jetpack-slideshow__autoplay-playing' : null,
+ );
+ $classes = Jetpack_Gutenberg::block_classes( 'slideshow', $attr, $extras );
+
+ return sprintf(
+ '<div class="%1$s" id="wp-block-jetpack-slideshow__%2$d"><div class="wp-block-jetpack-slideshow_container swiper-container">%3$s%4$s%5$s</div></div>',
+ esc_attr( $classes ),
+ absint( $wp_block_jetpack_slideshow_id ),
+ jetpack_slideshow_block_amp_carousel( $attr, $wp_block_jetpack_slideshow_id ),
+ $autoplay ? jetpack_slideshow_block_autoplay_ui( $wp_block_jetpack_slideshow_id ) : '',
+ jetpack_slideshow_block_bullets( $ids, $wp_block_jetpack_slideshow_id )
+ );
+}
+
+/**
+ * Generate amp-carousel markup
+ *
+ * @param array $attr Array of block attributes.
+ * @param int $block_ordinal The ordinal number of the block, used in unique ID.
+ *
+ * @return string amp-carousel markup.
+ */
+function jetpack_slideshow_block_amp_carousel( $attr, $block_ordinal ) {
+ $ids = empty( $attr['ids'] ) ? array() : $attr['ids'];
+ $first_image = wp_get_attachment_metadata( $ids[0] );
+ $delay = empty( $attr['delay'] ) ? 3 : absint( $attr['delay'] );
+ $autoplay = empty( $attr['autoplay'] ) ? false : $attr['autoplay'];
+ $width = empty( $first_image['width'] ) ? 800 : $first_image['width'];
+ $height = empty( $first_image['height'] ) ? 600 : $first_image['height'];
+ return sprintf(
+ '<amp-carousel width="%1$d" height="%2$d" layout="responsive" type="slides" data-next-button-aria-label="%3$s" data-prev-button-aria-label="%4$s" controls loop %5$s id="wp-block-jetpack-slideshow__amp-carousel__%6$s" on="slideChange:wp-block-jetpack-slideshow__amp-pagination__%6$s.toggle(index=event.index, value=true)">%7$s</amp-carousel>',
+ esc_attr( $width ),
+ esc_attr( $height ),
+ esc_attr__( 'Next Slide', 'jetpack' ),
+ esc_attr__( 'Previous Slide', 'jetpack' ),
+ $autoplay ? 'autoplay delay=' . esc_attr( $delay * 1000 ) : '',
+ absint( $block_ordinal ),
+ implode( '', jetpack_slideshow_block_slides( $ids, $width, $height ) )
+ );
+}
+
+/**
+ * Generate array of slides markup
+ *
+ * @param array $ids Array of image ids.
+ * @param int $width Width of the container.
+ * @param int $height Height of the container.
+ *
+ * @return array Array of slides markup.
+ */
+function jetpack_slideshow_block_slides( $ids = array(), $width = 400, $height = 300 ) {
+ return array_map(
+ function( $id ) use ( $width, $height ) {
+ $caption = wp_get_attachment_caption( $id );
+ $figcaption = $caption ? sprintf(
+ '<figcaption class="wp-block-jetpack-slideshow_caption gallery-caption">%s</figcaption>',
+ wp_kses_post( $caption )
+ ) : '';
+ $image = wp_get_attachment_image(
+ $id,
+ array( $width, $height ),
+ false,
+ array(
+ 'class' => 'wp-block-jetpack-slideshow_image',
+ 'object-fit' => 'contain',
+ )
+ );
+ return sprintf(
+ '<div class="wp-block-jetpack-slideshow_slide"><figure>%s%s</figure></div>',
+ $image,
+ $figcaption
+ );
+ },
+ $ids
+ );
+}
+
+/**
+ * Generate array of bullets markup
+ *
+ * @param array $ids Array of image ids.
+ * @param int $block_ordinal The ordinal number of the block, used in unique ID.
+ *
+ * @return array Array of bullets markup.
+ */
+function jetpack_slideshow_block_bullets( $ids = array(), $block_ordinal = 0 ) {
+ $buttons = array_map(
+ function( $index ) {
+ $aria_label = sprintf(
+ /* translators: %d: Slide number. */
+ __( 'Go to slide %d', 'jetpack' ),
+ absint( $index + 1 )
+ );
+ return sprintf(
+ '<button option="%d" class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="%s" %s></button>',
+ absint( $index ),
+ esc_attr( $aria_label ),
+ 0 === $index ? 'selected' : ''
+ );
+ },
+ array_keys( $ids )
+ );
+
+ return sprintf(
+ '<amp-selector id="wp-block-jetpack-slideshow__amp-pagination__%1$d" class="wp-block-jetpack-slideshow_pagination swiper-pagination swiper-pagination-bullets amp-pagination" on="select:wp-block-jetpack-slideshow__amp-carousel__%1$d.goToSlide(index=event.targetOption)" layout="container">%2$s</amp-selector>',
+ absint( $block_ordinal ),
+ implode( '', $buttons )
+ );
+}
+
+/**
+ * Generate autoplay play/pause UI.
+ *
+ * @param int $block_ordinal The ordinal number of the block, used in unique ID.
+ *
+ * @return string Autoplay UI markup.
+ */
+function jetpack_slideshow_block_autoplay_ui( $block_ordinal = 0 ) {
+ $block_id = sprintf(
+ 'wp-block-jetpack-slideshow__%d',
+ absint( $block_ordinal )
+ );
+ $amp_carousel_id = sprintf(
+ 'wp-block-jetpack-slideshow__amp-carousel__%d',
+ absint( $block_ordinal )
+ );
+ $autoplay_pause = sprintf(
+ '<a aria-label="%s" class="wp-block-jetpack-slideshow_button-pause" role="button" on="tap:%s.toggleAutoplay(toggleOn=false),%s.toggleClass(class=wp-block-jetpack-slideshow__autoplay-playing,force=false)"></a>',
+ esc_attr__( 'Pause Slideshow', 'jetpack' ),
+ esc_attr( $amp_carousel_id ),
+ esc_attr( $block_id )
+ );
+ $autoplay_play = sprintf(
+ '<a aria-label="%s" class="wp-block-jetpack-slideshow_button-play" role="button" on="tap:%s.toggleAutoplay(toggleOn=true),%s.toggleClass(class=wp-block-jetpack-slideshow__autoplay-playing,force=true)"></a>',
+ esc_attr__( 'Play Slideshow', 'jetpack' ),
+ esc_attr( $amp_carousel_id ),
+ esc_attr( $block_id )
+ );
+ return $autoplay_pause . $autoplay_play;
+}
diff --git a/plugins/jetpack/extensions/blocks/slideshow/style.scss b/plugins/jetpack/extensions/blocks/slideshow/style.scss
deleted file mode 100644
index c1d1fc50..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/style.scss
+++ /dev/null
@@ -1,165 +0,0 @@
-@import '../../shared/styles/gutenberg-colors.scss';
-@import '../../shared/styles/jetpack-variables.scss';
-
-.wp-block-jetpack-slideshow {
- margin-bottom: $jetpack-block-margin-bottom;
- position: relative;
-
- .wp-block-jetpack-slideshow_container {
- width: 100%;
- overflow: hidden;
- opacity: 0;
-
- &.wp-swiper-initialized {
- opacity: 1;
- }
-
- // High specifity to override theme styles
- .wp-block-jetpack-slideshow_swiper-wrappper,
- .wp-block-jetpack-slideshow_slide {
- padding: 0;
- margin: 0;
- line-height: normal;
- }
- }
-
- .wp-block-jetpack-slideshow_slide {
- background: rgba( 0, 0, 0, 0.1 );
- display: flex;
- height: 100%;
- width: 100%;
- figure {
- align-items: center;
- display: flex;
- height: 100%;
- justify-content: center;
- margin: 0;
- position: relative;
- width: 100%;
- }
- }
-
- .swiper-container-fade .wp-block-jetpack-slideshow_slide {
- background: var( --color-neutral-0 );
- }
-
- .wp-block-jetpack-slideshow_image {
- display: block;
- height: auto;
- max-height: 100%;
- max-width: 100%;
- width: auto;
- object-fit: contain;
- }
-
- .wp-block-jetpack-slideshow_button-prev,
- .wp-block-jetpack-slideshow_button-next,
- .wp-block-jetpack-slideshow_button-pause {
- background-color: rgba( 0, 0, 0, 0.5 );
- background-position: center;
- background-repeat: no-repeat;
- background-size: 24px;
- border: 0;
- border-radius: 4px;
- box-shadow: none;
- height: 48px;
- margin: -24px 0 0;
- padding: 0;
- transition: background-color 250ms;
- width: 48px;
-
- &:focus,
- &:hover {
- background-color: rgba( 0, 0, 0, 0.75 );
- }
-
- &:focus {
- outline: thin dotted $white;
- outline-offset: -4px;
- }
- }
-
- &.swiper-container-rtl .swiper-button-prev.swiper-button-white,
- &.swiper-container-rtl .wp-block-jetpack-slideshow_button-prev,
- .swiper-button-next.swiper-button-white,
- .wp-block-jetpack-slideshow_button-next {
- background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M5.88 4.12L13.76 12l-7.88 7.88L8 22l10-10L8 2z' fill='white'/%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3C/svg%3E" );
- }
-
- &.swiper-container-rtl .swiper-button-next.swiper-button-white,
- &.swiper-container-rtl .wp-block-jetpack-slideshow_button-next,
- .swiper-button-prev.swiper-button-white,
- .wp-block-jetpack-slideshow_button-prev {
- background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M18 4.12L10.12 12 18 19.88 15.88 22l-10-10 10-10z' fill='white'/%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3C/svg%3E" );
- }
-
- .wp-block-jetpack-slideshow_button-pause {
- background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M6 19h4V5H6v14zm8-14v14h4V5h-4z' fill='white'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E" );
- display: none;
- margin-top: 0;
- position: absolute;
- right: 10px;
- top: 10px;
- z-index: 1;
- }
-
- .wp-block-jetpack-slideshow_autoplay-paused .wp-block-jetpack-slideshow_button-pause {
- background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8 5v14l11-7z' fill='white'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E" );
- }
-
- &[data-autoplay='true'] .wp-block-jetpack-slideshow_button-pause {
- display: block;
- }
-
- .wp-block-jetpack-slideshow_caption.gallery-caption {
- background-color: rgba( 0, 0, 0, 0.5 );
- box-sizing: border-box;
- bottom: 0;
- color: $white;
- cursor: text;
- left: 0;
- margin: 0 !important;
- padding: 0.75em;
- position: absolute;
- right: 0;
- text-align: initial;
- z-index: 1;
- a {
- color: inherit;
- }
- }
-
- .wp-block-jetpack-slideshow_pagination.swiper-pagination-bullets {
- bottom: 0;
- line-height: 24px;
- padding: 10px 0 2px;
- position: relative;
-
- .swiper-pagination-bullet {
- background: currentColor;
- color: currentColor;
- height: 16px;
- opacity: 0.5;
- transform: scale( 0.75 );
- transition: opacity 250ms, transform 250ms;
- vertical-align: top;
- width: 16px;
-
- &:focus,
- &:hover {
- opacity: 1;
- }
-
- &:focus {
- outline: thin dotted;
- outline-offset: 0;
- }
- }
-
- .swiper-pagination-bullet-active {
- background-color: currentColor;
- opacity: 1;
- transform: scale( 1 );
- }
- }
-}
diff --git a/plugins/jetpack/extensions/blocks/slideshow/swiper-callbacks.js b/plugins/jetpack/extensions/blocks/slideshow/swiper-callbacks.js
deleted file mode 100644
index 2410f234..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/swiper-callbacks.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * External dependencies
- */
-import { escapeHTML } from '@wordpress/escape-html';
-import { forEach } from 'lodash';
-
-const SIXTEEN_BY_NINE = 16 / 9;
-const MAX_HEIGHT_PERCENT_OF_WINDOW_HEIGHT = 0.8;
-const SANITY_MAX_HEIGHT = 600;
-const PAUSE_CLASS = 'wp-block-jetpack-slideshow_autoplay-paused';
-
-function swiperInit( swiper ) {
- swiperResize( swiper );
- swiperApplyAria( swiper );
- swiper.el
- .querySelector( '.wp-block-jetpack-slideshow_button-pause' )
- .addEventListener( 'click', function() {
- // Handle destroyed Swiper instances
- if ( ! swiper.el ) {
- return;
- }
- if ( swiper.el.classList.contains( PAUSE_CLASS ) ) {
- swiper.el.classList.remove( PAUSE_CLASS );
- swiper.autoplay.start();
- this.setAttribute( 'aria-label', 'Pause Slideshow' );
- } else {
- swiper.el.classList.add( PAUSE_CLASS );
- swiper.autoplay.stop();
- this.setAttribute( 'aria-label', 'Play Slideshow' );
- }
- } );
-}
-
-function swiperResize( swiper ) {
- if ( ! swiper || ! swiper.el ) {
- return;
- }
- const img = swiper.el.querySelector( '.swiper-slide[data-swiper-slide-index="0"] img' );
- if ( ! img ) {
- return;
- }
- const aspectRatio = img.clientWidth / img.clientHeight;
- const sanityAspectRatio = Math.max( Math.min( aspectRatio, SIXTEEN_BY_NINE ), 1 );
- const sanityHeight =
- typeof window !== 'undefined'
- ? window.innerHeight * MAX_HEIGHT_PERCENT_OF_WINDOW_HEIGHT
- : SANITY_MAX_HEIGHT;
- const swiperHeight = Math.min( swiper.width / sanityAspectRatio, sanityHeight );
- const wrapperHeight = `${ Math.floor( swiperHeight ) }px`;
- const buttonTop = `${ Math.floor( swiperHeight / 2 ) }px`;
-
- swiper.el.classList.add( 'wp-swiper-initialized' );
- swiper.wrapperEl.style.height = wrapperHeight;
- swiper.el.querySelector( '.wp-block-jetpack-slideshow_button-prev' ).style.top = buttonTop;
- swiper.el.querySelector( '.wp-block-jetpack-slideshow_button-next' ).style.top = buttonTop;
-}
-
-function announceCurrentSlide( swiper ) {
- const currentSlide = swiper.slides[ swiper.activeIndex ];
- if ( ! currentSlide ) {
- return;
- }
- const figcaption = currentSlide.getElementsByTagName( 'FIGCAPTION' )[ 0 ];
- const img = currentSlide.getElementsByTagName( 'IMG' )[ 0 ];
- if ( swiper.a11y.liveRegion ) {
- swiper.a11y.liveRegion[ 0 ].innerHTML = figcaption
- ? figcaption.innerHTML
- : escapeHTML( img.alt );
- }
-}
-
-function swiperApplyAria( swiper ) {
- forEach( swiper.slides, ( slide, index ) => {
- slide.setAttribute( 'aria-hidden', index === swiper.activeIndex ? 'false' : 'true' );
- if ( index === swiper.activeIndex ) {
- slide.setAttribute( 'tabindex', '-1' );
- } else {
- slide.removeAttribute( 'tabindex' );
- }
- } );
- announceCurrentSlide( swiper );
-}
-
-function swiperPaginationRender( swiper ) {
- forEach( swiper.pagination.bullets, bullet => {
- bullet.addEventListener( 'click', () => {
- const currentSlide = swiper.slides[ swiper.realIndex ];
- setTimeout( () => {
- currentSlide.focus();
- }, 500 );
- } );
- } );
-}
-
-export { swiperApplyAria, swiperInit, swiperPaginationRender, swiperResize };
diff --git a/plugins/jetpack/extensions/blocks/slideshow/transforms.js b/plugins/jetpack/extensions/blocks/slideshow/transforms.js
deleted file mode 100644
index bcff28e5..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/transforms.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * External dependencies
- */
-import { createBlock } from '@wordpress/blocks';
-import { filter } from 'lodash';
-
-/**
- * Filter valid images
- *
- * @param {array} images Array of image objects
- * @return {array} Array of image objects which have id and url
- */
-function getValidImages( images ) {
- return filter( images, ( { id, url } ) => id && url );
-}
-
-const transforms = {
- from: [
- {
- type: 'block',
- isMultiBlock: true,
- blocks: [ 'core/image' ],
- isMatch: images => getValidImages( images ).length > 0,
- transform: images => {
- const validImages = getValidImages( images );
- return createBlock( 'jetpack/slideshow', {
- images: validImages.map( ( { alt, caption, id, url } ) => ( {
- alt,
- caption,
- id,
- url,
- } ) ),
- ids: validImages.map( ( { id } ) => id ),
- } );
- },
- },
- {
- type: 'block',
- blocks: [ 'core/gallery', 'jetpack/tiled-gallery' ],
- transform: ( { images } ) => {
- const validImages = getValidImages( images );
- if ( validImages.length > 0 ) {
- return createBlock( 'jetpack/slideshow', {
- images: validImages.map( ( { alt, caption, id, url } ) => ( {
- alt,
- caption,
- id,
- url,
- } ) ),
- ids: validImages.map( ( { id } ) => id ),
- } );
- }
- return createBlock( 'jetpack/slideshow' );
- },
- },
- ],
- to: [
- {
- type: 'block',
- blocks: [ 'core/gallery' ],
- transform: ( { images, ids } ) => createBlock( 'core/gallery', { images, ids } ),
- },
- {
- type: 'block',
- blocks: [ 'core/image' ],
- transform: ( { images } ) => {
- if ( images.length > 0 ) {
- return images.map( ( { id, url, alt, caption } ) =>
- createBlock( 'core/image', { id, url, alt, caption } )
- );
- }
- return createBlock( 'core/image' );
- },
- },
- ],
-};
-
-export default transforms;
diff --git a/plugins/jetpack/extensions/blocks/slideshow/view.js b/plugins/jetpack/extensions/blocks/slideshow/view.js
deleted file mode 100644
index 6d807897..00000000
--- a/plugins/jetpack/extensions/blocks/slideshow/view.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * External dependencies
- */
-import { forEach } from 'lodash';
-import ResizeObserver from 'resize-observer-polyfill';
-
-/**
- * Internal dependencies
- */
-import createSwiper from './create-swiper';
-import {
- swiperApplyAria,
- swiperInit,
- swiperPaginationRender,
- swiperResize,
-} from './swiper-callbacks';
-
-typeof window !== 'undefined' &&
- window.addEventListener( 'load', function() {
- const slideshowBlocks = document.getElementsByClassName( 'wp-block-jetpack-slideshow' );
- forEach( slideshowBlocks, slideshowBlock => {
- const { autoplay, delay, effect } = slideshowBlock.dataset;
- const prefersReducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
- const shouldAutoplay = autoplay && ! prefersReducedMotion;
- const slideshowContainer = slideshowBlock.getElementsByClassName( 'swiper-container' )[ 0 ];
- let pendingRequestAnimationFrame = null;
- createSwiper(
- slideshowContainer,
- {
- autoplay: shouldAutoplay
- ? {
- delay: delay * 1000,
- disableOnInteraction: false,
- }
- : false,
- effect,
- init: true,
- initialSlide: 0,
- loop: true,
- keyboard: {
- enabled: true,
- onlyInViewport: true,
- },
- },
- {
- init: swiperInit,
- imagesReady: swiperResize,
- paginationRender: swiperPaginationRender,
- transitionEnd: swiperApplyAria,
- }
- )
- .then( swiper => {
- new ResizeObserver( () => {
- if ( pendingRequestAnimationFrame ) {
- cancelAnimationFrame( pendingRequestAnimationFrame );
- pendingRequestAnimationFrame = null;
- }
- pendingRequestAnimationFrame = requestAnimationFrame( () => {
- swiperResize( swiper );
- swiper.update();
- } );
- } ).observe( swiper.el );
- } )
- .catch( () => {
- slideshowBlock
- .querySelector( '.wp-block-jetpack-slideshow_container' )
- .classList.add( 'wp-swiper-initialized' );
- } );
- } );
- } );