diff --git a/.svnignore b/.svnignore index 8dfbb716215fa..f9c2d78d307e7 100644 --- a/.svnignore +++ b/.svnignore @@ -41,16 +41,3 @@ yarn.lock docker bin/pre-commit-hook.js yarn-error.log -extensions/**/*.css -extensions/**/*.gif -extensions/**/*.jpeg -extensions/**/*.jpg -extensions/**/*.js -extensions/**/*.json -extensions/**/*.jsx -extensions/**/*.md -extensions/**/*.png -extensions/**/*.sass -extensions/**/*.scss -extensions/**/*.svg -**/__snapshots__ diff --git a/bin/build-asset-cdn-json.php b/bin/build-asset-cdn-json.php index aebe99f45fdfc..09c64e0ab4b6e 100644 --- a/bin/build-asset-cdn-json.php +++ b/bin/build-asset-cdn-json.php @@ -1,48 +1,20 @@ $value ) { - $path_from_repo_root = str_replace( $path, '', $path_to_file ); - - // Ignore top-level files. - if ( false === strpos( $path_from_repo_root, '/' ) ) { +foreach ( $regex as $file => $value ) { + $file = str_replace( $path, '', $file ); + $directory = substr( $file, 0, strpos( $file, '/' ) ); + if ( in_array( $directory, array( 'node_modules', 'tests' ) ) ) { continue; } - - // Ignore explicit ignore list. - foreach ( $ignore_paths as $ignore_path ) { - if ( 0 === strpos( $path_from_repo_root, $ignore_path ) ) { - continue 2; - } - } - - $manifest[] = $path_from_repo_root; + $manifest[] = $file; } $export = var_export( $manifest, true ); -file_put_contents( $path . 'modules/photon-cdn/jetpack-manifest.php', " { - const { day } = this.props; - const { opening, closing } = interval; - return ( - -
-
- { intervalIndex === 0 && this.renderDayToggle() } -
-
- { - this.setHour( value, 'opening', intervalIndex ); - } } - /> - { - this.setHour( value, 'closing', intervalIndex ); - } } - /> -
-
- { day.hours.length > 1 && ( - { - this.removeInterval( intervalIndex ); - } } - /> - ) } -
-
- { intervalIndex === day.hours.length - 1 && ( -
-
 
-
- - { __( 'Add Hours' ) } - -
-
 
-
- ) } -
- ); - }; - - setHour = ( hourValue, hourType, hourIndex ) => { - const { day, attributes, setAttributes } = this.props; - const { days } = attributes; - setAttributes( { - days: days.map( value => { - if ( value.name === day.name ) { - return { - ...value, - hours: value.hours.map( ( hour, index ) => { - if ( index === hourIndex ) { - return { - ...hour, - [ hourType ]: hourValue, - }; - } - return hour; - } ), - }; - } - return value; - } ), - } ); - }; - - toggleClosed = nextValue => { - const { day, attributes, setAttributes } = this.props; - const { days } = attributes; - - setAttributes( { - days: days.map( value => { - if ( value.name === day.name ) { - const hours = nextValue - ? [ - { - opening: defaultOpen, - closing: defaultClose, - }, - ] - : []; - return { - ...value, - hours, - }; - } - return value; - } ), - } ); - }; - - addInterval = () => { - const { day, attributes, setAttributes } = this.props; - const { days } = attributes; - day.hours.push( { opening: '', closing: '' } ); - setAttributes( { - days: days.map( value => { - if ( value.name === day.name ) { - return { - ...value, - hours: day.hours, - }; - } - return value; - } ), - } ); - }; - - removeInterval = hourIndex => { - const { day, attributes, setAttributes } = this.props; - const { days } = attributes; - - setAttributes( { - days: days.map( value => { - if ( day.name === value.name ) { - return { - ...value, - hours: value.hours.filter( ( hour, index ) => { - return hourIndex !== index; - } ), - }; - } - return value; - } ), - } ); - }; - - isClosed() { - const { day } = this.props; - return isEmpty( day.hours ); - } - - renderDayToggle() { - const { day, localization } = this.props; - return ( - - { localization.days[ day.name ] } - - - ); - } - - renderClosed() { - const { day } = this.props; - return ( -
-
- { this.renderDayToggle() } -
-
 
-
 
-
- ); - } - - render() { - const { day } = this.props; - return this.isClosed() ? this.renderClosed() : day.hours.map( this.renderInterval ); - } -} - -export default DayEdit; diff --git a/extensions/blocks/business-hours/components/day-preview.js b/extensions/blocks/business-hours/components/day-preview.js deleted file mode 100644 index e9413d8ed90e1..0000000000000 --- a/extensions/blocks/business-hours/components/day-preview.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * External dependencies - */ -import { Component, Fragment } from '@wordpress/element'; -import { date } from '@wordpress/date'; -import { isEmpty } from 'lodash'; -import { sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import { _x } from '../../../utils/i18n'; - -class DayPreview extends Component { - formatTime( time ) { - const { timeFormat } = this.props; - const [ hours, minutes ] = time.split( ':' ); - const _date = new Date(); - if ( ! hours || ! minutes ) { - return false; - } - _date.setHours( hours ); - _date.setMinutes( minutes ); - return date( timeFormat, _date ); - } - - renderInterval = ( interval, key ) => { - return ( -
- { sprintf( - _x( 'From %s to %s', 'from business opening hour to closing hour' ), - this.formatTime( interval.opening ), - this.formatTime( interval.closing ) - ) } -
- ); - }; - - render() { - const { day, localization } = this.props; - const hours = day.hours.filter( - // remove any malformed or empty intervals - interval => this.formatTime( interval.opening ) && this.formatTime( interval.closing ) - ); - return ( - -
{ localization.days[ day.name ] }
- { isEmpty( hours ) ? ( -
{ _x( 'Closed', 'business is closed on a full day' ) }
- ) : ( - hours.map( this.renderInterval ) - ) } -
- ); - } -} - -export default DayPreview; diff --git a/extensions/blocks/business-hours/edit.js b/extensions/blocks/business-hours/edit.js deleted file mode 100644 index 821631e8f4c0e..0000000000000 --- a/extensions/blocks/business-hours/edit.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * External dependencies - */ -import { BlockIcon } from '@wordpress/editor'; -import { Component } from '@wordpress/element'; -import { Placeholder } from '@wordpress/components'; -import apiFetch from '@wordpress/api-fetch'; -import classNames from 'classnames'; -import { __experimentalGetSettings } from '@wordpress/date'; - -/** - * Internal dependencies - */ -import DayEdit from './components/day-edit'; -import DayPreview from './components/day-preview'; -import { icon } from '.'; -import { __ } from '../../utils/i18n'; - -const defaultLocalization = { - days: { - Sun: __( 'Sunday' ), - Mon: __( 'Monday' ), - Tue: __( 'Tuesday' ), - Wed: __( 'Wednesday' ), - Thu: __( 'Thursday' ), - Fri: __( 'Friday' ), - Sat: __( 'Saturday' ), - }, - startOfWeek: 0, -}; - -class BusinessHours extends Component { - state = { - localization: defaultLocalization, - hasFetched: false, - }; - - componentDidMount() { - this.apiFetch(); - } - - apiFetch() { - this.setState( { data: defaultLocalization }, () => { - apiFetch( { path: '/wpcom/v2/business-hours/localized-week' } ).then( - data => { - this.setState( { localization: data, hasFetched: true } ); - }, - () => { - this.setState( { localization: defaultLocalization, hasFetched: true } ); - } - ); - } ); - } - - render() { - const { attributes, className, isSelected } = this.props; - const { days } = attributes; - const { localization, hasFetched } = this.state; - const { startOfWeek } = localization; - const localizedWeek = days.concat( days.slice( 0, startOfWeek ) ).slice( startOfWeek ); - - if ( ! hasFetched ) { - return ( - } - label={ __( 'Loading business hours' ) } - /> - ); - } - - if ( ! isSelected ) { - const settings = __experimentalGetSettings(); - const { - formats: { time }, - } = settings; - return ( -
- { localizedWeek.map( ( day, key ) => { - return ( - - ); - } ) } -
- ); - } - - return ( -
- { localizedWeek.map( ( day, key ) => { - return ( - - ); - } ) } -
- ); - } -} - -export default BusinessHours; diff --git a/extensions/blocks/business-hours/editor.js b/extensions/blocks/business-hours/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/business-hours/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/business-hours/editor.scss b/extensions/blocks/business-hours/editor.scss deleted file mode 100644 index cb919a6d5153d..0000000000000 --- a/extensions/blocks/business-hours/editor.scss +++ /dev/null @@ -1,132 +0,0 @@ -// @TODO: Use Gutenberg variables -$break-xlarge: 1080px; -$break-large: 960px; // admin sidebar auto folds -$break-medium: 782px; // editor sidebar auto folds -$break-small: 600px; - -.wp-block-jetpack-business-hours { - overflow: hidden; - - .business-hours__row { - display: flex; - - &.business-hours-row__add, - &.business-hours-row__closed { - margin-bottom: 20px; - } - - .business-hours__day { - width: 44%; - display: flex; - align-items: baseline; - - .business-hours__day-name { - width: 60%; - font-weight: bold; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .components-form-toggle { - margin-right: 4px; - } - } - - .business-hours__hours { - width: 44%; - margin: 0; - display: flex; - align-items: center; - flex-wrap: wrap; - - .components-base-control { - display: inline-block; - margin-bottom: 0; - width: 48%; - - &.business-hours__open { - margin-right: 4%; - } - - .components-base-control__label { - margin-bottom: 0; - } - } - } - } - - .business-hours__remove { - align-self: flex-end; - margin-bottom: 8px; - text-align: center; - width: 10%; - } - - .business-hours-row__add button:hover { - box-shadow: none !important; - } - - .business-hours__remove button { - display: block; - margin: 0 auto; - } - - .business-hours-row__add .components-button.is-default:hover, - .business-hours__remove .components-button.is-default:hover, - .business-hours-row__add .components-button.is-default:focus, - .business-hours__remove .components-button.is-default:focus, - .business-hours-row__add .components-button.is-default:active, - .business-hours__remove .components-button.is-default:active { - background: none; - box-shadow: none; - } -} - -/** - * We consider the editor area to be small when the business hours block is: - * - within a column block - * - in a screen < xlarge size with the sidebar open - * - in a screen < small size - * In these cases we'll apply small screen styles. - */ -@mixin editor-area-is-small { - @media ( max-width: $break-xlarge ) { - .is-sidebar-opened { - @content; - } - } - @media ( max-width: $break-small ) { - @content; - } - - .wp-block-columns { - @content; - } -} - -@include editor-area-is-small() { - .wp-block-jetpack-business-hours { - .business-hours__row { - flex-wrap: wrap; - - &.business-hours-row__add { - .business-hours__day, - .business-hours__remove { - display: none; - } - } - - .business-hours__day { - width: 100%; - } - - .business-hours__hours { - width: 78%; - } - .business-hours__remove { - width: 18%; - } - } - } -} diff --git a/extensions/blocks/business-hours/index.js b/extensions/blocks/business-hours/index.js deleted file mode 100644 index c1a2bbf062807..0000000000000 --- a/extensions/blocks/business-hours/index.js +++ /dev/null @@ -1,102 +0,0 @@ -/** - * External dependencies - */ -import { Path } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __, _x } from '../../utils/i18n'; -import renderMaterialIcon from '../../utils/render-material-icon'; - -import './editor.scss'; -import BusinessHours from './edit'; - -/** - * Block Registrations: - */ - -export const name = 'business-hours'; - -export const icon = renderMaterialIcon( - -); - -export const settings = { - title: __( 'Business Hours' ), - description: __( 'Display opening hours for your business.' ), - icon, - category: 'jetpack', - supports: { - html: true, - }, - keywords: [ - _x( 'opening hours', 'block search term' ), - _x( 'closing time', 'block search term' ), - _x( 'schedule', 'block search term' ), - ], - attributes: { - days: { - type: 'array', - default: [ - { - name: 'Sun', - hours: [], // Closed by default - }, - { - name: 'Mon', - hours: [ - { - opening: '09:00', - closing: '17:00', - }, - ], - }, - { - name: 'Tue', - hours: [ - { - opening: '09:00', - closing: '17:00', - }, - ], - }, - { - name: 'Wed', - hours: [ - { - opening: '09:00', - closing: '17:00', - }, - ], - }, - { - name: 'Thu', - hours: [ - { - opening: '09:00', - closing: '17:00', - }, - ], - }, - { - name: 'Fri', - hours: [ - { - opening: '09:00', - closing: '17:00', - }, - ], - }, - { - name: 'Sat', - hours: [], // Closed by default - }, - ], - }, - }, - - edit: props => , - - save: () => null, -}; diff --git a/extensions/blocks/contact-form/components/jetpack-contact-form.js b/extensions/blocks/contact-form/components/jetpack-contact-form.js deleted file mode 100644 index d17f970cf1432..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-contact-form.js +++ /dev/null @@ -1,261 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { Button, PanelBody, Placeholder, TextControl, Path } from '@wordpress/components'; -import { InnerBlocks, InspectorControls } from '@wordpress/editor'; -import { Component, Fragment } from '@wordpress/element'; -import { sprintf } from '@wordpress/i18n'; -import emailValidator from 'email-validator'; -import { compose, withInstanceId } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; -import renderMaterialIcon from '../../../utils/render-material-icon'; -import SubmitButton from '../../../utils/submit-button'; -import HelpMessage from '../../../shared/help-message'; -const ALLOWED_BLOCKS = [ - 'jetpack/markdown', - 'core/paragraph', - 'core/image', - 'core/heading', - 'core/gallery', - 'core/list', - 'core/quote', - 'core/shortcode', - 'core/audio', - 'core/code', - 'core/cover', - 'core/file', - 'core/html', - 'core/separator', - 'core/spacer', - 'core/subhead', - 'core/table', - 'core/verse', - 'core/video', -]; - -class JetpackContactForm extends Component { - constructor( ...args ) { - super( ...args ); - this.onChangeSubject = this.onChangeSubject.bind( this ); - this.onBlurTo = this.onBlurTo.bind( this ); - this.onChangeTo = this.onChangeTo.bind( this ); - this.onChangeSubmit = this.onChangeSubmit.bind( this ); - this.onFormSettingsSet = this.onFormSettingsSet.bind( this ); - this.getToValidationError = this.getToValidationError.bind( this ); - this.renderToAndSubjectFields = this.renderToAndSubjectFields.bind( this ); - this.preventEnterSubmittion = this.preventEnterSubmittion.bind( this ); - this.hasEmailError = this.hasEmailError.bind( this ); - - const to = args[ 0 ].attributes.to ? args[ 0 ].attributes.to : ''; - const error = to - .split( ',' ) - .map( this.getToValidationError ) - .filter( Boolean ); - - this.state = { - toError: error && error.length ? error : null, - }; - } - - getIntroMessage() { - return __( - 'You’ll receive an email notification each time someone fills out the form. Where should it go, and what should the subject line be?' - ); - } - - getEmailHelpMessage() { - return __( 'You can enter multiple email addresses separated by commas.' ); - } - - onChangeSubject( subject ) { - this.props.setAttributes( { subject } ); - } - - getToValidationError( email ) { - email = email.trim(); - if ( email.length === 0 ) { - return false; // ignore the empty emails - } - if ( ! emailValidator.validate( email ) ) { - return { email }; - } - return false; - } - - onBlurTo( event ) { - const error = event.target.value - .split( ',' ) - .map( this.getToValidationError ) - .filter( Boolean ); - if ( error && error.length ) { - this.setState( { toError: error } ); - return; - } - } - - onChangeTo( to ) { - const emails = to.trim(); - if ( emails.length === 0 ) { - this.setState( { toError: null } ); - this.props.setAttributes( { to } ); - return; - } - - this.setState( { toError: null } ); - this.props.setAttributes( { to } ); - } - - onChangeSubmit( submitButtonText ) { - this.props.setAttributes( { submitButtonText } ); - } - - onFormSettingsSet( event ) { - event.preventDefault(); - if ( this.state.toError ) { - // don't submit the form if there are errors. - return; - } - this.props.setAttributes( { hasFormSettingsSet: 'yes' } ); - } - - getfieldEmailError( errors ) { - if ( errors ) { - if ( errors.length === 1 ) { - if ( errors[ 0 ] && errors[ 0 ].email ) { - return sprintf( __( '%s is not a valid email address.' ), errors[ 0 ].email ); - } - return errors[ 0 ]; - } - - if ( errors.length === 2 ) { - return sprintf( - __( '%s and %s are not a valid email address.' ), - errors[ 0 ].email, - errors[ 1 ].email - ); - } - const inValidEmails = errors.map( error => error.email ); - return sprintf( __( '%s are not a valid email address.' ), inValidEmails.join( ', ' ) ); - } - return null; - } - - preventEnterSubmittion( event ) { - if ( event.key === 'Enter' ) { - event.preventDefault(); - event.stopPropagation(); - } - } - - renderToAndSubjectFields() { - const fieldEmailError = this.state.toError; - const { instanceId, attributes } = this.props; - const { subject, to } = attributes; - return ( - - - - { this.getfieldEmailError( fieldEmailError ) } - - - { this.getEmailHelpMessage() } - - - - - ); - } - - hasEmailError() { - const fieldEmailError = this.state.toError; - return fieldEmailError && fieldEmailError.length > 0; - } - - render() { - const { className, attributes } = this.props; - const { hasFormSettingsSet } = attributes; - const formClassnames = classnames( className, 'jetpack-contact-form', { - 'has-intro': ! hasFormSettingsSet, - } ); - - return ( - - - - { this.renderToAndSubjectFields() } - - -
- { ! hasFormSettingsSet && ( - - ) } - > -
-

{ this.getIntroMessage() }

- { this.renderToAndSubjectFields() } -

- { __( - '(If you leave these blank, notifications will go to the author with the post or page title as the subject line.)' - ) } -

-
- -
-
-
- ) } - { hasFormSettingsSet && ( - - ) } - { hasFormSettingsSet && } -
-
- ); - } -} - -export default compose( [ withInstanceId ] )( JetpackContactForm ); diff --git a/extensions/blocks/contact-form/components/jetpack-field-checkbox.js b/extensions/blocks/contact-form/components/jetpack-field-checkbox.js deleted file mode 100644 index d378c58936c97..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-field-checkbox.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * External dependencies - */ -import { BaseControl, PanelBody, TextControl, ToggleControl } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; -import { withInstanceId } from '@wordpress/compose'; -import { InspectorControls } from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import JetpackFieldLabel from './jetpack-field-label'; -import { __ } from '../../../utils/i18n'; - -const JetpackFieldCheckbox = ( { - instanceId, - required, - label, - setAttributes, - isSelected, - defaultValue, - id, -} ) => { - return ( - - - - - - setAttributes( { defaultValue: value } ) } - /> - setAttributes( { id: value } ) } - /> - - - - } - /> - ); -}; - -export default withInstanceId( JetpackFieldCheckbox ); diff --git a/extensions/blocks/contact-form/components/jetpack-field-label.js b/extensions/blocks/contact-form/components/jetpack-field-label.js deleted file mode 100644 index 3493cab951f32..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-field-label.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * External dependencies - */ -import { PlainText } from '@wordpress/editor'; -import { ToggleControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; - -const JetpackFieldLabel = ( { setAttributes, label, resetFocus, isSelected, required } ) => { - return ( -
- { - resetFocus && resetFocus(); - setAttributes( { label: value } ); - } } - placeholder={ __( 'Write label…' ) } - /> - { isSelected && ( - <ToggleControl - label={ __( 'Required' ) } - className="jetpack-field-label__required" - checked={ required } - onChange={ value => setAttributes( { required: value } ) } - /> - ) } - { ! isSelected && required && <span className="required">{ __( '(required)' ) }</span> } - </div> - ); -}; - -export default JetpackFieldLabel; diff --git a/extensions/blocks/contact-form/components/jetpack-field-multiple.js b/extensions/blocks/contact-form/components/jetpack-field-multiple.js deleted file mode 100644 index 791bc9c62c819..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-field-multiple.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * External dependencies - */ -import { BaseControl, IconButton, TextControl, PanelBody } from '@wordpress/components'; -import { withInstanceId } from '@wordpress/compose'; -import { Component, Fragment } from '@wordpress/element'; -import { InspectorControls } from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import JetpackFieldLabel from './jetpack-field-label'; -import JetpackOption from './jetpack-option'; -import { __ } from '../../../utils/i18n'; - -class JetpackFieldMultiple extends Component { - constructor( ...args ) { - super( ...args ); - this.onChangeOption = this.onChangeOption.bind( this ); - this.addNewOption = this.addNewOption.bind( this ); - this.state = { inFocus: null }; - } - - onChangeOption( key = null, option = null ) { - const newOptions = this.props.options.slice( 0 ); - if ( null === option ) { - // Remove a key - newOptions.splice( key, 1 ); - if ( key > 0 ) { - this.setState( { inFocus: key - 1 } ); - } - } else { - // update a key - newOptions.splice( key, 1, option ); - this.setState( { inFocus: key } ); // set the focus. - } - this.props.setAttributes( { options: newOptions } ); - } - - addNewOption( key = null ) { - const newOptions = this.props.options.slice( 0 ); - let inFocus = 0; - if ( 'object' === typeof key ) { - newOptions.push( '' ); - inFocus = newOptions.length - 1; - } else { - newOptions.splice( key + 1, 0, '' ); - inFocus = key + 1; - } - - this.setState( { inFocus: inFocus } ); - this.props.setAttributes( { options: newOptions } ); - } - - render() { - const { type, instanceId, required, label, setAttributes, isSelected, id } = this.props; - let { options } = this.props; - let { inFocus } = this.state; - if ( ! options.length ) { - options = [ '' ]; - inFocus = 0; - } - - return ( - <Fragment> - <BaseControl - id={ `jetpack-field-multiple-${ instanceId }` } - className="jetpack-field jetpack-field-multiple" - label={ - <JetpackFieldLabel - required={ required } - label={ label } - setAttributes={ setAttributes } - isSelected={ isSelected } - resetFocus={ () => this.setState( { inFocus: null } ) } - /> - } - > - <ol - className="jetpack-field-multiple__list" - id={ `jetpack-field-multiple-${ instanceId }` } - > - { options.map( ( option, index ) => ( - <JetpackOption - type={ type } - key={ index } - option={ option } - index={ index } - onChangeOption={ this.onChangeOption } - onAddOption={ this.addNewOption } - isInFocus={ index === inFocus && isSelected } - isSelected={ isSelected } - /> - ) ) } - </ol> - { isSelected && ( - <IconButton - className="jetpack-field-multiple__add-option" - icon="insert" - label={ __( 'Insert option' ) } - onClick={ this.addNewOption } - > - { __( 'Add option' ) } - </IconButton> - ) } - </BaseControl> - - <InspectorControls> - <PanelBody title={ __( 'Field Settings' ) }> - <TextControl - label={ __( 'ID' ) } - value={ id } - onChange={ value => setAttributes( { id: value } ) } - /> - </PanelBody> - </InspectorControls> - </Fragment> - ); - } -} - -export default withInstanceId( JetpackFieldMultiple ); diff --git a/extensions/blocks/contact-form/components/jetpack-field-required-toggle.js b/extensions/blocks/contact-form/components/jetpack-field-required-toggle.js deleted file mode 100644 index bab73001bd812..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-field-required-toggle.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * External dependencies - */ -import { ToggleControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; - -const JetpackFieldRequiredToggle = ( { required, onChange } ) => { - return <ToggleControl label={ __( 'Required' ) } checked={ required } onChange={ onChange } />; -}; - -export default JetpackFieldRequiredToggle; diff --git a/extensions/blocks/contact-form/components/jetpack-field-textarea.js b/extensions/blocks/contact-form/components/jetpack-field-textarea.js deleted file mode 100644 index 186fc1e80953b..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-field-textarea.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * External dependencies - */ -import { TextareaControl, TextControl, PanelBody } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; -import { InspectorControls } from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import JetpackFieldLabel from './jetpack-field-label'; -import { __ } from '../../../utils/i18n'; - -function JetpackFieldTextarea( { - required, - label, - setAttributes, - isSelected, - defaultValue, - placeholder, - id, -} ) { - return ( - <Fragment> - <div className="jetpack-field"> - <TextareaControl - label={ - <JetpackFieldLabel - required={ required } - label={ label } - setAttributes={ setAttributes } - isSelected={ isSelected } - /> - } - placeholder={ placeholder } - value={ placeholder } - onChange={ value => setAttributes( { placeholder: value } ) } - title={ __( 'Set the placeholder text' ) } - /> - </div> - <InspectorControls> - <PanelBody title={ __( 'Field Settings' ) }> - <TextControl - label={ __( 'Default Value' ) } - value={ defaultValue } - onChange={ value => setAttributes( { defaultValue: value } ) } - /> - <TextControl - label={ __( 'ID' ) } - value={ id } - onChange={ value => setAttributes( { id: value } ) } - /> - </PanelBody> - </InspectorControls> - </Fragment> - ); -} - -export default JetpackFieldTextarea; diff --git a/extensions/blocks/contact-form/components/jetpack-field.js b/extensions/blocks/contact-form/components/jetpack-field.js deleted file mode 100644 index 77c3ffaa87213..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-field.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * External dependencies - */ -import { TextControl, PanelBody } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; -import classNames from 'classnames'; -import { InspectorControls } from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import JetpackFieldLabel from './jetpack-field-label'; -import { __ } from '../../../utils/i18n'; - -function JetpackField( { - isSelected, - type, - required, - label, - setAttributes, - defaultValue, - placeholder, - id, -} ) { - return ( - <Fragment> - <div className={ classNames( 'jetpack-field', { 'is-selected': isSelected } ) }> - <TextControl - type={ type } - label={ - <JetpackFieldLabel - required={ required } - label={ label } - setAttributes={ setAttributes } - isSelected={ isSelected } - /> - } - placeholder={ placeholder } - value={ placeholder } - onChange={ value => setAttributes( { placeholder: value } ) } - title={ __( 'Set the placeholder text' ) } - /> - </div> - <InspectorControls> - <PanelBody title={ __( 'Field Settings' ) }> - <TextControl - label={ __( 'Default Value' ) } - value={ defaultValue } - onChange={ value => setAttributes( { defaultValue: value } ) } - /> - <TextControl - label={ __( 'ID' ) } - value={ id } - onChange={ value => setAttributes( { id: value } ) } - /> - </PanelBody> - </InspectorControls> - </Fragment> - ); -} - -export default JetpackField; diff --git a/extensions/blocks/contact-form/components/jetpack-option.js b/extensions/blocks/contact-form/components/jetpack-option.js deleted file mode 100644 index 5d3052b8ab356..0000000000000 --- a/extensions/blocks/contact-form/components/jetpack-option.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * External dependencies - */ -import { IconButton } from '@wordpress/components'; -import { Component, createRef } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; - -class JetpackOption extends Component { - constructor( ...args ) { - super( ...args ); - this.onChangeOption = this.onChangeOption.bind( this ); - this.onKeyPress = this.onKeyPress.bind( this ); - this.onDeleteOption = this.onDeleteOption.bind( this ); - this.textInput = createRef(); - } - - componentDidMount() { - if ( this.props.isInFocus ) { - this.textInput.current.focus(); - } - } - - componentDidUpdate() { - if ( this.props.isInFocus ) { - this.textInput.current.focus(); - } - } - - onChangeOption( event ) { - this.props.onChangeOption( this.props.index, event.target.value ); - } - - onKeyPress( event ) { - if ( event.key === 'Enter' ) { - this.props.onAddOption( this.props.index ); - event.preventDefault(); - return; - } - - if ( event.key === 'Backspace' && event.target.value === '' ) { - this.props.onChangeOption( this.props.index ); - event.preventDefault(); - return; - } - } - - onDeleteOption() { - this.props.onChangeOption( this.props.index ); - } - - render() { - const { isSelected, option, type } = this.props; - return ( - <li className="jetpack-option"> - { type && type !== 'select' && ( - <input className="jetpack-option__type" type={ type } disabled /> - ) } - <input - type="text" - className="jetpack-option__input" - value={ option } - placeholder={ __( 'Write option…' ) } - onChange={ this.onChangeOption } - onKeyDown={ this.onKeyPress } - ref={ this.textInput } - /> - { isSelected && ( - <IconButton - className="jetpack-option__remove" - icon="trash" - label={ __( 'Remove option' ) } - onClick={ this.onDeleteOption } - /> - ) } - </li> - ); - } -} - -export default JetpackOption; diff --git a/extensions/blocks/contact-form/editor.js b/extensions/blocks/contact-form/editor.js deleted file mode 100644 index 5b6bd3cdd88a0..0000000000000 --- a/extensions/blocks/contact-form/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { childBlocks, name, settings } from '.'; - -registerJetpackBlock( name, settings, childBlocks ); diff --git a/extensions/blocks/contact-form/editor.scss b/extensions/blocks/contact-form/editor.scss deleted file mode 100644 index b988f7f40ef6f..0000000000000 --- a/extensions/blocks/contact-form/editor.scss +++ /dev/null @@ -1,694 +0,0 @@ - -.jetpack-contact-form { - padding: 10px 18px; - - &.has-intro { - padding: 0; - } -} - -.jetpack-contact-form .components-placeholder { - padding: 24px; - - input[type='text'] { - width: 100%; - outline-width: 0; - outline-style: none; - line-height: 16px; - } - - .components-placeholder__label svg { - margin-right: 1ch; - } - - .help-message, - .components-placeholder__fieldset { - text-align: left; - } - - .help-message { - width: 100%; - margin: -18px 0 28px; - } - - .components-base-control { - margin-bottom: 16px; - width: 100%; - } -} - -.jetpack-contact-form__intro-message { - margin: 0 0 16px; -} - -.jetpack-contact-form__create { - width: 100%; -} - -.jetpack-field-label { - display: flex; - flex-direction: row; - - .components-base-control { - margin-top:-1px; - margin-bottom: -3px; - - .components-form-toggle { - margin: 2px 8px 0 0; - } - } - - .required { - color: #dc3232; - } - - .components-toggle-control .components-base-control__field { - margin-bottom: 0; - } -} - -.jetpack-field-label__input { - flex-grow: 1; - min-height: unset; - padding: 0; -} - -// Duplicated to elevate specificity in order to overwrite core styles -.jetpack-field-label__input.jetpack-field-label__input.jetpack-field-label__input { - border-color: #fff; - border-radius: 0; - font-weight: 600; - margin: 0; - margin-bottom: 2px; - padding: 0; - - &:focus { - border-color: #fff; - box-shadow: none; - } -} - -input.components-text-control__input { - line-height: 16px; -} - -.jetpack-field { - // done to increase elevate specificity in order to overwrite calypso styles - .components-text-control__input.components-text-control__input { - width: 100%; - } - .components-text-control__input, - .components-textarea-control__input { - color: #72777c; - padding: 10px 8px; - } -} - -.jetpack-field-checkbox__checkbox.jetpack-field-checkbox__checkbox.jetpack-field-checkbox__checkbox { - float: left; -} - -// Duplicated to elevate specificity in order to overwrite core styles -.jetpack-field-multiple__list.jetpack-field-multiple__list { - list-style-type: none; - margin: 0; - - &:empty { - display: none; - } - - // TODO: make this a class, @enej - [data-type='jetpack/field-select'] & { - border: 1px solid #8d96a0; - border-radius: 4px; - padding: 4px; - } -} - -.jetpack-option { - display: flex; - align-items: center; - margin: 0; -} - -.jetpack-option__type.jetpack-option__type { - margin-top: 0; -} - -// Duplicated to elevate specificity in order to overwrite core styles -.jetpack-option__input.jetpack-option__input.jetpack-option__input { - border-color: #fff; - border-radius: 0; - flex-grow: 1; - - &:hover { - border-color: #357cb5; - } - - &:focus { - border-color: #e3e5e8; - box-shadow: none; - } -} -// Duplicated to elevate specificity in order to overwrite calypso styles -.jetpack-option__remove.jetpack-option__remove { - padding: 6px; - vertical-align: bottom; -} - -.jetpack-field-multiple__add-option { - margin-left: -6px; - padding: 4px; - padding-right: 8px; - - svg { - margin-right: 12px; - } -} - -.jetpack-field-checkbox .components-base-control__label { - display: flex; - align-items: center; - - .jetpack-field-label { - flex-grow:1; - } - - .jetpack-field-label__input { - font-size: 13px; - font-weight: 400; - padding-left: 10px; - } -} - -/* ========================================================================== -** Shortcode Classic Block Styles -** ======================================================================= */ - -@media ( min-width: 481px ) { - .jetpack-contact-form-shortcode-preview { - padding: 24px; - } -} - -.jetpack-contact-form-shortcode-preview { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; - font-size: 16px; - line-height: 1.4em; - display: block; - position: relative; - margin: 0 auto; - padding: 16px; - box-sizing: border-box; - background: white; - box-shadow: 0 0 0 1px rgba( 200, 215, 225, 0.5 ), 0 1px 2px #e9eff3; - - &::after { - content: '.'; - display: block; - height: 0; - clear: both; - visibility: hidden; - } - - > div { - margin-top: 24px; - } - - > div:first-child { - margin-top: 0; - } - /* ========================================================================== - ** Labels - ** ======================================================================= */ - - label { - display: block; - font-size: 14px; - font-weight: 600; - margin-bottom: 5px; - } - - - /* ========================================================================== - ** Text Inputs - ** ======================================================================= */ - - input[type='text'], - input[type='tel'], - input[type='email'], - input[type='url'] { - border-radius: 0; - appearance: none; - box-sizing: border-box; - margin: 0; - padding: 7px 14px; - width: 100%; - color: #2e4453; - font-size: 16px; - line-height: 1.5; - border: 1px solid #c8d7e1; - background-color: #fff; - transition: all 0.15s ease-in-out; - box-shadow: none; - } - - input[type='text']::placeholder, - input[type='tel']::placeholder, - input[type='email']::placeholder, - input[type='url']::placeholder { - color: #87a6bc; - } - - input[type='text']:hover, - input[type='tel']:hover, - input[type='email']:hover, - input[type='url']:hover { - border-color: #a8bece; - } - - input[type='text']:focus, - input[type='tel']:focus, - input[type='email']:focus, - input[type='url']:focus { - border-color: #0087be; - outline: none; - box-shadow: 0 0 0 2px #78dcfa; - } - - input[type='text']:focus::-ms-clear, - input[type='tel']:focus::-ms-clear, - input[type='email']:focus::-ms-clear, - input[type='url']:focus::-ms-clear { - display: none; - } - - input[type='text']:disabled, - input[type='tel']:disabled, - input[type='email']:disabled, - input[type='url']:disabled { - background: #f3f6f8; - border-color: #e9eff3; - color: #a8bece; - -webkit-text-fill-color: #a8bece; - } - - input[type='text']:disabled:hover, - input[type='tel']:disabled:hover, - input[type='email']:disabled:hover, - input[type='url']:disabled:hover { - cursor: default; - } - - input[type='text']:disabled::placeholder, - input[type='tel']:disabled::placeholder, - input[type='email']:disabled::placeholder, - input[type='url']:disabled::placeholder { - color: #a8bece; - } - - - /* ========================================================================== - ** Textareas - ** ======================================================================= */ - - textarea { - border-radius: 0; - appearance: none; - box-sizing: border-box; - margin: 0; - padding: 7px 14px; - height: 92px; - width: 100%; - color: #2e4453; - font-size: 16px; - line-height: 1.5; - border: 1px solid #c8d7e1; - background-color: #fff; - transition: all 0.15s ease-in-out; - box-shadow: none; - } - - textarea::placeholder { - color: #87a6bc; - } - - textarea:hover { - border-color: #a8bece; - } - - textarea:focus { - border-color: #0087be; - outline: none; - box-shadow: 0 0 0 2px #78dcfa; - } - - textarea:focus::-ms-clear { - display: none; - } - - textarea:disabled { - background: #f3f6f8; - border-color: #e9eff3; - color: #a8bece; - -webkit-text-fill-color: #a8bece; - } - - textarea:disabled:hover { - cursor: default; - } - - textarea:disabled::placeholder { - color: #a8bece; - } - - - /* ========================================================================== - ** Checkboxes - ** ======================================================================= */ - - input[type='checkbox'] { - -webkit-appearance: none; - display: inline-block; - box-sizing: border-box; - margin: 2px 0 0; - padding: 7px 14px; - width: 16px; - height: 16px; - float: left; - outline: 0; - padding: 0; - box-shadow: none; - background-color: #fff; - border: 1px solid #c8d7e1; - color: #2e4453; - font-size: 16px; - line-height: 0; - text-align: center; - vertical-align: middle; - appearance: none; - transition: all 0.15s ease-in-out; - clear: none; - cursor: pointer; - } - - input[type='checkbox']:checked::before { - content: '\f147'; - font-family: Dashicons; - margin: -3px 0 0 -4px; - float: left; - display: inline-block; - vertical-align: middle; - width: 16px; - font-size: 20px; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - speak: none; - color: #00aadc; - } - - input[type='checkbox']:disabled:checked::before { - color: #a8bece; - } - - input[type='checkbox']:hover { - border-color: #a8bece; - } - - input[type='checkbox']:focus { - border-color: #0087be; - outline: none; - box-shadow: 0 0 0 2px #78dcfa; - } - - input[type='checkbox']:disabled { - background: #f3f6f8; - border-color: #e9eff3; - color: #a8bece; - opacity: 1; - } - - input[type='checkbox']:disabled:hover { - cursor: default; - } - - input[type='checkbox'] + span { - display: block; - font-weight: normal; - margin-left: 24px; - } - - - /* ========================================================================== - ** Radio buttons - ** ======================================================================== */ - - input[type=radio] { - color: #2e4453; - font-size: 16px; - border: 1px solid #c8d7e1; - background-color: #fff; - transition: all 0.15s ease-in-out; - box-sizing: border-box; - -webkit-appearance: none; - clear: none; - cursor: pointer; - display: inline-block; - line-height: 0; - height: 16px; - margin: 2px 4px 0 0; - float: left; - outline: 0; - padding: 0; - text-align: center; - vertical-align: middle; - width: 16px; - min-width: 16px; - appearance: none; - border-radius: 50%; - line-height: 10px; - } - - input[type='radio']:hover { - border-color: #a8bece; - } - - input[type='radio']:focus { - border-color: #0087be; - outline: none; - box-shadow: 0 0 0 2px #78dcfa; - } - - input[type='radio']:focus::-ms-clear { - display: none; - } - - input[type='radio']:checked::before { - float: left; - display: inline-block; - content: '\2022'; - margin: 3px; - width: 8px; - height: 8px; - text-indent: -9999px; - background: #00aadc; - vertical-align: middle; - border-radius: 50%; - animation: grow 0.2s ease-in-out; - } - - input[type='radio']:disabled { - background: #f3f6f8; - border-color: #e9eff3; - color: #a8bece; - opacity: 1; - -webkit-text-fill-color: #a8bece; - } - - input[type='radio']:disabled:hover { - cursor: default; - } - - input[type='radio']:disabled::placeholder { - color: #a8bece; - } - - input[type='radio']:disabled:checked::before { - background: #e9eff3; - } - - input[type='radio'] + span { - display: block; - font-weight: normal; - margin-left: 24px; - } - - @keyframes grow { - 0% { - transform: scale( 0.3 ); - } - - 60% { - transform: scale( 1.15 ); - } - - 100% { - transform: scale( 1 ); - } - } - - @keyframes grow { - 0% { - transform: scale( 0.3 ); - } - - 60% { - transform: scale( 1.15 ); - } - - 100% { - transform: scale( 1 ); - } - } - - - /* ========================================================================== - ** Selects - ** ======================================================================== */ - - select { - background: #fff url( data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPiAgICAgICAgPHRpdGxlPmFycm93LWRvd248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBza2V0Y2g6dHlwZT0iTVNQYWdlIj4gICAgICAgIDxnIGlkPSJhcnJvdy1kb3duIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIiBmaWxsPSIjQzhEN0UxIj4gICAgICAgICAgICA8cGF0aCBkPSJNMTUuNSw2IEwxNyw3LjUgTDEwLjI1LDE0LjI1IEwzLjUsNy41IEw1LDYgTDEwLjI1LDExLjI1IEwxNS41LDYgWiIgaWQ9IkRvd24tQXJyb3ciIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4gICAgICAgIDwvZz4gICAgPC9nPjwvc3ZnPg== ) no-repeat right 10px center; - border-color: #c8d7e1; - border-style: solid; - border-radius: 4px; - border-width: 1px 1px 2px; - color: #2e4453; - cursor: pointer; - display: inline-block; - margin: 0; - outline: 0; - overflow: hidden; - font-size: 14px; - line-height: 21px; - font-weight: 600; - text-overflow: ellipsis; - text-decoration: none; - vertical-align: top; - white-space: nowrap; - box-sizing: border-box; - padding: 2px 32px 2px 14px; // Aligns the text to the 8px baseline grid and adds padding on right to allow for the arrow. - appearance: none; - font-family: sans-serif; - } - - select:hover { - background-image: url( data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPiAgICAgICAgPHRpdGxlPmFycm93LWRvd248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBza2V0Y2g6dHlwZT0iTVNQYWdlIj4gICAgICAgIDxnIGlkPSJhcnJvdy1kb3duIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIiBmaWxsPSIjYThiZWNlIj4gICAgICAgICAgICA8cGF0aCBkPSJNMTUuNSw2IEwxNyw3LjUgTDEwLjI1LDE0LjI1IEwzLjUsNy41IEw1LDYgTDEwLjI1LDExLjI1IEwxNS41LDYgWiIgaWQ9IkRvd24tQXJyb3ciIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4gICAgICAgIDwvZz4gICAgPC9nPjwvc3ZnPg== ); - } - - select:focus { - background-image: url( data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPiA8dGl0bGU+YXJyb3ctZG93bjwvdGl0bGU+IDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPiA8ZGVmcz48L2RlZnM+IDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPiA8ZyBpZD0iYXJyb3ctZG93biIgc2tldGNoOnR5cGU9Ik1TQXJ0Ym9hcmRHcm91cCIgZmlsbD0iIzJlNDQ1MyI+IDxwYXRoIGQ9Ik0xNS41LDYgTDE3LDcuNSBMMTAuMjUsMTQuMjUgTDMuNSw3LjUgTDUsNiBMMTAuMjUsMTEuMjUgTDE1LjUsNiBaIiBpZD0iRG93bi1BcnJvdyIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPiA8L2c+IDwvZz48L3N2Zz4= ); - border-color: #00aadc; - box-shadow: 0 0 0 2px #78dcfa; - outline: 0; - -moz-outline:none; - -moz-user-focus:ignore; - } - - select:disabled, - select:hover:disabled { - background: url( data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPiAgICAgICAgPHRpdGxlPmFycm93LWRvd248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBza2V0Y2g6dHlwZT0iTVNQYWdlIj4gICAgICAgIDxnIGlkPSJhcnJvdy1kb3duIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIiBmaWxsPSIjZTllZmYzIj4gICAgICAgICAgICA8cGF0aCBkPSJNMTUuNSw2IEwxNyw3LjUgTDEwLjI1LDE0LjI1IEwzLjUsNy41IEw1LDYgTDEwLjI1LDExLjI1IEwxNS41LDYgWiIgaWQ9IkRvd24tQXJyb3ciIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4gICAgICAgIDwvZz4gICAgPC9nPjwvc3ZnPg== ) no-repeat right 10px center;; - } - - select.is-compact { - min-width: 0; - padding: 0 20px 2px 6px; - margin: 0 4px; - background-position: right 5px center; - background-size: 12px 12px; - } - - /* Make it display:block when it follows a label */ - label select, - label + select { - display: block; - min-width: 200px; - } - - label select.is-compact, - label + select.is-compact { - display: inline-block; - min-width: 0; - } - - /* IE: Remove the default arrow */ - select::-ms-expand { - display: none; - } - - /* IE: Remove default background and color styles on focus */ - select::-ms-value { - background: none; - color: #2e4453; - } - - /* Firefox: Remove the focus outline, see http://stackoverflow.com/questions/3773430/remove-outline-from-select-box-in-ff/18853002#18853002 */ - select:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 #2e4453; - } - - - /* ========================================================================== - ** Buttons - ** ======================================================================== */ - - input[type='submit'] { - padding: 0; - font-size: 14px; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - vertical-align: baseline; - background: white; - border-color: #c8d7e1; - border-style: solid; - border-width: 1px 1px 2px; - color: #2e4453; - cursor: pointer; - display: inline-block; - margin: 24px 0 0; - outline: 0; - overflow: hidden; - font-weight: 500; - text-overflow: ellipsis; - text-decoration: none; - vertical-align: top; - box-sizing: border-box; - font-size: 14px; - line-height: 21px; - border-radius: 4px; - padding: 7px 14px 9px; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - } - - input[type='submit']:hover { - border-color: #a8bece; - color: #2e4453; - } - - input[type='submit']:active { - border-width: 2px 1px 1px; - } - - input[type='submit']:visited { - color: #2e4453; - } - - input[type='submit']:focus { - border-color: #00aadc; - box-shadow: 0 0 0 2px #78dcfa; - } -} diff --git a/extensions/blocks/contact-form/index.js b/extensions/blocks/contact-form/index.js deleted file mode 100644 index c3e75cbd21495..0000000000000 --- a/extensions/blocks/contact-form/index.js +++ /dev/null @@ -1,440 +0,0 @@ -/** - * External dependencies - */ -import { getBlockType, createBlock } from '@wordpress/blocks'; -import { Path, Circle } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; -import { InnerBlocks } from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import './editor.scss'; -import JetpackContactForm from './components/jetpack-contact-form'; -import JetpackField from './components/jetpack-field'; -import JetpackFieldTextarea from './components/jetpack-field-textarea'; -import JetpackFieldCheckbox from './components/jetpack-field-checkbox'; -import JetpackFieldMultiple from './components/jetpack-field-multiple'; -import { __ } from '../../utils/i18n'; -import renderMaterialIcon from '../../utils/render-material-icon'; - -export const name = 'contact-form'; - -export const settings = { - title: __( 'Form' ), - description: __( 'A simple way to get feedback from folks visiting your site.' ), - icon: renderMaterialIcon( - <Path d="M13 7.5h5v2h-5zm0 7h5v2h-5zM19 3H5c-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 16H5V5h14v14zM11 6H6v5h5V6zm-1 4H7V7h3v3zm1 3H6v5h5v-5zm-1 4H7v-3h3v3z" /> - ), - keywords: [ __( 'email' ), __( 'feedback' ), __( 'contact' ) ], - category: 'jetpack', - supports: { - reusable: false, - html: false, - }, - attributes: { - subject: { - type: 'string', - default: '', - }, - to: { - type: 'string', - default: '', - }, - submitButtonText: { - type: 'string', - default: __( 'Submit' ), - }, - customBackgroundButtonColor: { type: 'string' }, - customTextButtonColor: { type: 'string' }, - submitButtonClasses: { type: 'string' }, - hasFormSettingsSet: { - type: 'string', - default: null, - }, - - // Deprecated - has_form_settings_set: { - type: 'string', - default: null, - }, - submit_button_text: { - type: 'string', - default: __( 'Submit' ), - }, - }, - - edit: JetpackContactForm, - save: InnerBlocks.Content, - deprecated: [ - { - attributes: { - subject: { - type: 'string', - default: '', - }, - to: { - type: 'string', - default: '', - }, - submit_button_text: { - type: 'string', - default: __( 'Submit' ), - }, - has_form_settings_set: { - type: 'string', - default: null, - }, - }, - migrate: attr => { - return { - submitButtonText: attr.submit_button_text, - hasFormSettingsSet: attr.has_form_settings_set, - to: attr.to, - subject: attr.subject, - }; - }, - - isEligible: attr => { - // when the deprecated, snake_case values are default, no need to migrate - if ( ! attr.has_form_settings_set && attr.submit_button_text === 'Submit' ) { - return false; - } - return true; - }, - - save: InnerBlocks.Content, - }, - ], -}; - -const FieldDefaults = { - category: 'jetpack', - parent: [ 'jetpack/contact-form' ], - supports: { - reusable: false, - html: false, - }, - attributes: { - label: { - type: 'string', - default: null, - }, - required: { - type: 'boolean', - default: false, - }, - options: { - type: 'array', - default: [], - }, - defaultValue: { - type: 'string', - default: '', - }, - placeholder: { - type: 'string', - default: '', - }, - id: { - type: 'string', - default: '', - }, - }, - transforms: { - to: [ - { - type: 'block', - blocks: [ 'jetpack/field-text' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-text', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-name' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-name', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-email' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-email', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-url' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-url', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-date' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-date', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-telephone' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-telephone', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-textarea' ], - isMatch: ( { options } ) => ! options.length, - transform: attributes => createBlock( 'jetpack/field-textarea', attributes ), - }, - /* // not yet ready for prime time. - { - type: 'block', - blocks: [ 'jetpack/field-checkbox' ], - isMatch: ( { options } ) => 1 === options.length, - transform: ( attributes )=>createBlock( 'jetpack/field-checkbox', attributes ) - }, - */ - { - type: 'block', - blocks: [ 'jetpack/field-checkbox-multiple' ], - isMatch: ( { options } ) => 1 <= options.length, - transform: attributes => createBlock( 'jetpack/field-checkbox-multiple', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-radio' ], - isMatch: ( { options } ) => 1 <= options.length, - transform: attributes => createBlock( 'jetpack/field-radio', attributes ), - }, - { - type: 'block', - blocks: [ 'jetpack/field-select' ], - isMatch: ( { options } ) => 1 <= options.length, - transform: attributes => createBlock( 'jetpack/field-select', attributes ), - }, - ], - }, - save: () => null, -}; - -const getFieldLabel = ( { attributes, name: blockName } ) => { - return null === attributes.label ? getBlockType( blockName ).title : attributes.label; -}; - -const editField = type => props => ( - <JetpackField - type={ type } - label={ getFieldLabel( props ) } - required={ props.attributes.required } - setAttributes={ props.setAttributes } - isSelected={ props.isSelected } - defaultValue={ props.attributes.defaultValue } - placeholder={ props.attributes.placeholder } - id={ props.attributes.id } - /> -); - -const editMultiField = type => props => ( - <JetpackFieldMultiple - label={ getFieldLabel( props ) } - required={ props.attributes.required } - options={ props.attributes.options } - setAttributes={ props.setAttributes } - type={ type } - isSelected={ props.isSelected } - id={ props.attributes.id } - /> -); - -export const childBlocks = [ - { - name: 'field-text', - settings: { - ...FieldDefaults, - title: __( 'Text' ), - description: __( 'When you need just a small amount of text, add a text input.' ), - icon: renderMaterialIcon( <Path d="M4 9h16v2H4V9zm0 4h10v2H4v-2z" /> ), - edit: editField( 'text' ), - }, - }, - { - name: 'field-name', - settings: { - ...FieldDefaults, - title: __( 'Name' ), - description: __( 'Introductions are important. Add an input for folks to add their name.' ), - icon: renderMaterialIcon( - <Path d="M12 6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2m0 10c2.7 0 5.8 1.29 6 2H6c.23-.72 3.31-2 6-2m0-12C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" /> - ), - edit: editField( 'text' ), - }, - }, - { - name: 'field-email', - settings: { - ...FieldDefaults, - title: __( 'Email' ), - keywords: [ __( 'e-mail' ), __( 'mail' ), 'email' ], - description: __( 'Want to reply to folks? Add an email address input.' ), - icon: renderMaterialIcon( - <Path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z" /> - ), - edit: editField( 'email' ), - }, - }, - - { - name: 'field-url', - settings: { - ...FieldDefaults, - title: __( 'Website' ), - keywords: [ 'url', __( 'internet page' ), 'link' ], - description: __( 'Add an address input for a website.' ), - icon: renderMaterialIcon( - <Path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z" /> - ), - edit: editField( 'url' ), - }, - }, - - { - name: 'field-date', - settings: { - ...FieldDefaults, - title: __( 'Date Picker' ), - keywords: [ __( 'Calendar' ), __( 'day month year', 'block search term' ) ], - description: __( 'The best way to set a date. Add a date picker.' ), - icon: renderMaterialIcon( - <Path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V9h14v10zm0-12H5V5h14v2zM7 11h5v5H7z" /> - ), - edit: editField( 'text' ), - }, - }, - { - name: 'field-telephone', - settings: { - ...FieldDefaults, - title: __( 'Telephone' ), - keywords: [ __( 'Phone' ), __( 'Cellular phone' ), __( 'Mobile' ) ], - description: __( 'Add a phone number input.' ), - icon: renderMaterialIcon( - <Path d="M6.54 5c.06.89.21 1.76.45 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79h1.51m9.86 12.02c.85.24 1.72.39 2.6.45v1.49c-1.32-.09-2.59-.35-3.8-.75l1.2-1.19M7.5 3H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.49c0-.55-.45-1-1-1-1.24 0-2.45-.2-3.57-.57-.1-.04-.21-.05-.31-.05-.26 0-.51.1-.71.29l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c.28-.28.36-.67.25-1.02C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1z" /> - ), - edit: editField( 'tel' ), - }, - }, - { - name: 'field-textarea', - settings: { - ...FieldDefaults, - title: __( 'Message' ), - keywords: [ __( 'Textarea' ), 'textarea', __( 'Multiline text' ) ], - description: __( 'Let folks speak their mind. This text box is great for longer responses.' ), - icon: renderMaterialIcon( <Path d="M21 11.01L3 11v2h18zM3 16h12v2H3zM21 6H3v2.01L21 8z" /> ), - edit: props => ( - <JetpackFieldTextarea - label={ getFieldLabel( props ) } - required={ props.attributes.required } - setAttributes={ props.setAttributes } - isSelected={ props.isSelected } - defaultValue={ props.attributes.defaultValue } - placeholder={ props.attributes.placeholder } - id={ props.attributes.id } - /> - ), - }, - }, - { - name: 'field-checkbox', - settings: { - ...FieldDefaults, - title: __( 'Checkbox' ), - keywords: [ __( 'Confirm' ), __( 'Accept' ) ], - description: __( 'Add a single checkbox.' ), - icon: renderMaterialIcon( - <Path d="M19 3H5c-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 16H5V5h14v14zM17.99 9l-1.41-1.42-6.59 6.59-2.58-2.57-1.42 1.41 4 3.99z" /> - ), - edit: props => ( - <JetpackFieldCheckbox - label={ props.attributes.label } // label intentinally left blank - required={ props.attributes.required } - setAttributes={ props.setAttributes } - isSelected={ props.isSelected } - defaultValue={ props.attributes.defaultValue } - id={ props.attributes.id } - /> - ), - attributes: { - ...FieldDefaults.attributes, - label: { - type: 'string', - default: '', - }, - }, - }, - }, - { - name: 'field-checkbox-multiple', - settings: { - ...FieldDefaults, - title: __( 'Checkbox Group' ), - keywords: [ __( 'Choose Multiple' ), __( 'Option' ) ], - description: __( 'People love options. Add several checkbox items.' ), - icon: renderMaterialIcon( - <Path d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z" /> - ), - edit: editMultiField( 'checkbox' ), - attributes: { - ...FieldDefaults.attributes, - label: { - type: 'string', - default: 'Choose several', - }, - }, - }, - }, - { - name: 'field-radio', - settings: { - ...FieldDefaults, - title: __( 'Radio' ), - keywords: [ __( 'Choose' ), __( 'Select' ), __( 'Option' ) ], - description: __( - 'Inspired by radios, only one radio item can be selected at a time. Add several radio button items.' - ), - icon: renderMaterialIcon( - <Fragment> - <Path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" /> - <Circle cx="12" cy="12" r="5" /> - </Fragment> - ), - edit: editMultiField( 'radio' ), - attributes: { - ...FieldDefaults.attributes, - label: { - type: 'string', - default: 'Choose one', - }, - }, - }, - }, - { - name: 'field-select', - settings: { - ...FieldDefaults, - title: __( 'Select' ), - keywords: [ __( 'Choose' ), __( 'Dropdown' ), __( 'Option' ) ], - description: __( 'Compact, but powerful. Add a select box with several items.' ), - icon: renderMaterialIcon( - <Path d="M3 17h18v2H3zm16-5v1H5v-1h14m2-2H3v5h18v-5zM3 6h18v2H3z" /> - ), - edit: editMultiField( 'select' ), - attributes: { - ...FieldDefaults.attributes, - label: { - type: 'string', - default: 'Select one', - }, - }, - }, - }, -]; diff --git a/extensions/blocks/contact-info/address/edit.js b/extensions/blocks/contact-info/address/edit.js deleted file mode 100644 index 5ba64592556fa..0000000000000 --- a/extensions/blocks/contact-info/address/edit.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { PlainText } from '@wordpress/editor'; -import { Component, Fragment } from '@wordpress/element'; -import { ToggleControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; -import { default as save } from './save'; - -class AddressEdit extends Component { - constructor( ...args ) { - super( ...args ); - - this.preventEnterKey = this.preventEnterKey.bind( this ); - } - - preventEnterKey( event ) { - if ( event.key === 'Enter' ) { - event.preventDefault(); - return; - } - } - - render() { - const { - attributes: { - address, - addressLine2, - addressLine3, - city, - region, - postal, - country, - linkToGoogleMaps, - }, - isSelected, - setAttributes, - } = this.props; - - const hasContent = [ address, addressLine2, addressLine3, city, region, postal, country ].some( - value => value !== '' - ); - const classNames = classnames( { - 'jetpack-address-block': true, - 'is-selected': isSelected, - } ); - - const externalLink = ( - <ToggleControl - label={ __( 'Link address to Google Maps' ) } - checked={ linkToGoogleMaps } - onChange={ newlinkToGoogleMaps => - setAttributes( { linkToGoogleMaps: newlinkToGoogleMaps } ) - } - /> - ); - - return ( - <div className={ classNames }> - { ! isSelected && hasContent && save( this.props ) } - { ( isSelected || ! hasContent ) && ( - <Fragment> - <PlainText - value={ address } - placeholder={ __( 'Street Address' ) } - aria-label={ __( 'Street Address' ) } - onChange={ newAddress => setAttributes( { address: newAddress } ) } - onKeyDown={ this.preventEnterKey } - /> - <PlainText - value={ addressLine2 } - placeholder={ __( 'Address Line 2' ) } - aria-label={ __( 'Address Line 2' ) } - onChange={ newAddressLine2 => setAttributes( { addressLine2: newAddressLine2 } ) } - onKeyDown={ this.preventEnterKey } - /> - <PlainText - value={ addressLine3 } - placeholder={ __( 'Address Line 3' ) } - aria-label={ __( 'Address Line 3' ) } - onChange={ newAddressLine3 => setAttributes( { addressLine3: newAddressLine3 } ) } - onKeyDown={ this.preventEnterKey } - /> - <PlainText - value={ city } - placeholder={ __( 'City' ) } - aria-label={ __( 'City' ) } - onChange={ newCity => setAttributes( { city: newCity } ) } - onKeyDown={ this.preventEnterKey } - /> - <PlainText - value={ region } - placeholder={ __( 'State/Province/Region' ) } - aria-label={ __( 'State/Province/Region' ) } - onChange={ newRegion => setAttributes( { region: newRegion } ) } - onKeyDown={ this.preventEnterKey } - /> - <PlainText - value={ postal } - placeholder={ __( 'Postal/Zip Code' ) } - aria-label={ __( 'Postal/Zip Code' ) } - onChange={ newPostal => setAttributes( { postal: newPostal } ) } - onKeyDown={ this.preventEnterKey } - /> - <PlainText - value={ country } - placeholder={ __( 'Country' ) } - aria-label={ __( 'Country' ) } - onChange={ newCountry => setAttributes( { country: newCountry } ) } - onKeyDown={ this.preventEnterKey } - /> - { externalLink } - </Fragment> - ) } - </div> - ); - } -} - -export default AddressEdit; diff --git a/extensions/blocks/contact-info/address/editor.js b/extensions/blocks/contact-info/address/editor.js deleted file mode 100644 index 4f9f21a0fde07..0000000000000 --- a/extensions/blocks/contact-info/address/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/contact-info/address/index.js b/extensions/blocks/contact-info/address/index.js deleted file mode 100644 index 7e0daca65cbe4..0000000000000 --- a/extensions/blocks/contact-info/address/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * External dependencies - */ -import { Path, Circle } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import save from './save'; -import renderMaterialIcon from '../../../utils/render-material-icon'; -import { __, _x } from '../../../utils/i18n'; - -const attributes = { - address: { - type: 'string', - default: '', - }, - addressLine2: { - type: 'string', - default: '', - }, - addressLine3: { - type: 'string', - default: '', - }, - city: { - type: 'string', - default: '', - }, - region: { - type: 'string', - default: '', - }, - postal: { - type: 'string', - default: '', - }, - country: { - type: 'string', - default: '', - }, - linkToGoogleMaps: { - type: 'boolean', - default: false, - }, -}; - -export const name = 'address'; - -export const settings = { - title: __( 'Address' ), - description: __( 'Lets you add a physical address with Schema markup.' ), - keywords: [ - _x( 'location', 'block search term' ), - _x( 'direction', 'block search term' ), - _x( 'place', 'block search term' ), - ], - icon: renderMaterialIcon( - <Fragment> - <Path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zM7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 2.88-2.88 7.19-5 9.88C9.92 16.21 7 11.85 7 9z" /> - <Circle cx="12" cy="9" r="2.5" /> - </Fragment> - ), - category: 'jetpack', - attributes, - parent: [ 'jetpack/contact-info' ], - edit, - save, -}; diff --git a/extensions/blocks/contact-info/address/save.js b/extensions/blocks/contact-info/address/save.js deleted file mode 100644 index 9369b12d8e43b..0000000000000 --- a/extensions/blocks/contact-info/address/save.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * External dependencies - */ -import { Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; - -const hasAddress = ( { address, addressLine2, addressLine3, city, region, postal, country } ) => { - return [ address, addressLine2, addressLine3, city, region, postal, country ].some( - value => value !== '' - ); -}; - -const Address = ( { - attributes: { address, addressLine2, addressLine3, city, region, postal, country }, -} ) => ( - <Fragment> - { address && ( - <div className="jetpack-address__address jetpack-address__address1">{ address }</div> - ) } - { addressLine2 && ( - <div className="jetpack-address__address jetpack-address__address2">{ addressLine2 }</div> - ) } - { addressLine3 && ( - <div className="jetpack-address__address jetpack-address__address3">{ addressLine3 }</div> - ) } - { city && ! ( region || postal ) && <div className="jetpack-address__city">{ city }</div> } - { city && ( region || postal ) && ( - <div> - { [ - <span className="jetpack-address__city">{ city }</span>, - ', ', - <span className="jetpack-address__region">{ region }</span>, - ' ', - <span className="jetpack-address__postal">{ postal }</span>, - ] } - </div> - ) } - { ! city && ( region || postal ) && ( - <div> - { [ - <span className="jetpack-address__region">{ region }</span>, - ' ', - <span className="jetpack-address__postal">{ postal }</span>, - ] } - </div> - ) } - { country && <div className="jetpack-address__country">{ country }</div> } - </Fragment> -); - -export const googleMapsUrl = ( { - attributes: { address, addressLine2, addressLine3, city, region, postal, country }, -} ) => { - const addressUrl = address ? `${ address },` : ''; - const addressLine2Url = addressLine2 ? `${ addressLine2 },` : ''; - const addressLine3Url = addressLine3 ? `${ addressLine3 },` : ''; - const cityUrl = city ? `+${ city },` : ''; - let regionUrl = region ? `+${ region },` : ''; - regionUrl = postal ? `${ regionUrl }+${ postal }` : regionUrl; - const countryUrl = country ? `+${ country }` : ''; - - return `https://www.google.com/maps/search/${ addressUrl }${ addressLine2Url }${ addressLine3Url }${ cityUrl }${ regionUrl }${ countryUrl }`.replace( - ' ', - '+' - ); -}; - -const save = props => - hasAddress( props.attributes ) && ( - <div className={ props.className }> - { props.attributes.linkToGoogleMaps && ( - <a - href={ googleMapsUrl( props ) } - target="_blank" - rel="noopener noreferrer" - title={ __( 'Open address in Google Maps' ) } - > - <Address { ...props } /> - </a> - ) } - { ! props.attributes.linkToGoogleMaps && <Address { ...props } /> } - </div> - ); - -export default save; diff --git a/extensions/blocks/contact-info/edit.js b/extensions/blocks/contact-info/edit.js deleted file mode 100644 index b3ba63a66536b..0000000000000 --- a/extensions/blocks/contact-info/edit.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * External dependencies - */ -import { InnerBlocks } from '@wordpress/editor'; -import classnames from 'classnames'; - -/** - * Internal dependencies - */ -const ALLOWED_BLOCKS = [ - 'jetpack/markdown', - 'jetpack/address', - 'jetpack/email', - 'jetpack/phone', - 'jetpack/map', - 'jetpack/business-hours', - 'core/paragraph', - 'core/image', - 'core/heading', - 'core/gallery', - 'core/list', - 'core/quote', - 'core/shortcode', - 'core/audio', - 'core/code', - 'core/cover', - 'core/html', - 'core/separator', - 'core/spacer', - 'core/subhead', - 'core/video', -]; - -const TEMPLATE = [ [ 'jetpack/email' ], [ 'jetpack/phone' ], [ 'jetpack/address' ] ]; - -const ContactInfoEdit = props => { - const { isSelected } = props; - - return ( - <div - className={ classnames( { - 'jetpack-contact-info-block': true, - 'is-selected': isSelected, - } ) } - > - <InnerBlocks allowedBlocks={ ALLOWED_BLOCKS } templateLock={ false } template={ TEMPLATE } /> - </div> - ); -}; - -export default ContactInfoEdit; diff --git a/extensions/blocks/contact-info/editor.js b/extensions/blocks/contact-info/editor.js deleted file mode 100644 index 5b6bd3cdd88a0..0000000000000 --- a/extensions/blocks/contact-info/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { childBlocks, name, settings } from '.'; - -registerJetpackBlock( name, settings, childBlocks ); diff --git a/extensions/blocks/contact-info/editor.scss b/extensions/blocks/contact-info/editor.scss deleted file mode 100644 index 2e1e08a1cec89..0000000000000 --- a/extensions/blocks/contact-info/editor.scss +++ /dev/null @@ -1,19 +0,0 @@ -.jetpack-contact-info-block { - padding: 10px 18px; - /* css class added to increase specificity */ - .editor-plain-text.editor-plain-text:focus { - box-shadow: none; - } - - .editor-plain-text { - flex-grow: 1; - min-height: unset; - padding: 0; - box-shadow: none; - font-family: inherit; - font-size: inherit; - color: inherit; - line-height: inherit; - border: none; - } -} diff --git a/extensions/blocks/contact-info/email/edit.js b/extensions/blocks/contact-info/email/edit.js deleted file mode 100644 index 164b57eb206fa..0000000000000 --- a/extensions/blocks/contact-info/email/edit.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Internal dependencies - */ -import save from './save'; -import { __ } from '../../../utils/i18n'; -import simpleInput from '../../../utils/simple-input'; - -const EmailEdit = props => { - const { setAttributes } = props; - return simpleInput( 'email', props, __( 'Email' ), save, nextValue => - setAttributes( { email: nextValue } ) - ); -}; - -export default EmailEdit; diff --git a/extensions/blocks/contact-info/email/editor.js b/extensions/blocks/contact-info/email/editor.js deleted file mode 100644 index 4f9f21a0fde07..0000000000000 --- a/extensions/blocks/contact-info/email/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/contact-info/email/index.js b/extensions/blocks/contact-info/email/index.js deleted file mode 100644 index 8b87f75b7f5a6..0000000000000 --- a/extensions/blocks/contact-info/email/index.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * External dependencies - */ -import { Path } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import save from './save'; -import renderMaterialIcon from '../../../utils/render-material-icon'; -import { __, _x } from '../../../utils/i18n'; - -const attributes = { - email: { - type: 'string', - default: '', - }, -}; - -export const name = 'email'; - -export const settings = { - title: __( 'Email Address' ), - description: __( - 'Lets you add an email address with an automatically generated click-to-email link.' - ), - keywords: [ - 'e-mail', // not translatable on purpose - 'email', // not translatable on purpose - _x( 'message', 'block search term' ), - ], - icon: renderMaterialIcon( - <Path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z" /> - ), - category: 'jetpack', - attributes, - edit, - save, - parent: [ 'jetpack/contact-info' ], -}; diff --git a/extensions/blocks/contact-info/email/save.js b/extensions/blocks/contact-info/email/save.js deleted file mode 100644 index e0eb0204d1545..0000000000000 --- a/extensions/blocks/contact-info/email/save.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * External dependencies - */ -import emailValidator from 'email-validator'; -import { Fragment } from '@wordpress/element'; - -const renderEmail = inputText => { - const explodedInput = inputText.split( /(\s+)/ ).map( ( email, i ) => { - // Remove and punctuation from the end of the email address. - const emailToValidate = email.replace( /([.,/#!$%^&*;:{}=\-_`~()\][])+$/g, '' ); - if ( email.indexOf( '@' ) && emailValidator.validate( emailToValidate ) ) { - return email === emailToValidate ? ( - // Email. - <a href={ `mailto:${ email }` } key={ i }> - { email } - </a> - ) : ( - // Email with punctionation. - <Fragment key={ i }> - <a href={ `mailto:${ email }` } key={ i }> - { emailToValidate } - </a> - <Fragment>{ email.slice( -( email.length - emailToValidate.length ) ) }</Fragment> - </Fragment> - ); - } - // Just a plain string. - return <Fragment key={ i }>{ email }</Fragment>; - } ); - return explodedInput; -}; - -const save = ( { attributes: { email }, className } ) => - email && <div className={ className }>{ renderEmail( email ) }</div>; - -export default save; diff --git a/extensions/blocks/contact-info/index.js b/extensions/blocks/contact-info/index.js deleted file mode 100644 index da9b43977ac8d..0000000000000 --- a/extensions/blocks/contact-info/index.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * External dependencies - */ -import { InnerBlocks } from '@wordpress/editor'; -import { Path } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import renderMaterialIcon from '../../utils/render-material-icon'; -import { __, _x } from '../../utils/i18n'; -import './editor.scss'; -import './style.scss'; -import { name as addressName, settings as addressSettings } from './address/'; -import { name as emailName, settings as emailSettings } from './email/'; -import { name as phoneName, settings as phoneSettings } from './phone/'; - -const attributes = {}; - -const save = ( { className } ) => ( - <div className={ className }> - <InnerBlocks.Content /> - </div> -); - -export const name = 'contact-info'; - -export const settings = { - title: __( 'Contact Info' ), - description: __( - 'Lets you add an email address, phone number, and physical address with improved markup for better SEO results.' - ), - keywords: [ - _x( 'email', 'block search term' ), - _x( 'phone', 'block search term' ), - _x( 'address', 'block search term' ), - ], - icon: renderMaterialIcon( - <Path d="M19 5v14H5V5h14m0-2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 9c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm6 10H6v-1.53c0-2.5 3.97-3.58 6-3.58s6 1.08 6 3.58V18zm-9.69-2h7.38c-.69-.56-2.38-1.12-3.69-1.12s-3.01.56-3.69 1.12z" /> - ), - category: 'jetpack', - supports: { - align: [ 'wide', 'full' ], - html: false, - }, - attributes, - edit, - save, -}; - -export const childBlocks = [ - { name: addressName, settings: addressSettings }, - { name: emailName, settings: emailSettings }, - { name: phoneName, settings: phoneSettings }, -]; diff --git a/extensions/blocks/contact-info/phone/edit.js b/extensions/blocks/contact-info/phone/edit.js deleted file mode 100644 index 9be393ff1475f..0000000000000 --- a/extensions/blocks/contact-info/phone/edit.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Internal dependencies - */ -import save from './save'; -import { __ } from '../../../utils/i18n'; -import simpleInput from '../../../utils/simple-input'; - -const PhoneEdit = props => { - const { setAttributes } = props; - return simpleInput( 'phone', props, __( 'Phone number' ), save, nextValue => - setAttributes( { phone: nextValue } ) - ); -}; - -export default PhoneEdit; diff --git a/extensions/blocks/contact-info/phone/editor.js b/extensions/blocks/contact-info/phone/editor.js deleted file mode 100644 index 4f9f21a0fde07..0000000000000 --- a/extensions/blocks/contact-info/phone/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/contact-info/phone/index.js b/extensions/blocks/contact-info/phone/index.js deleted file mode 100644 index 5a52f99493cff..0000000000000 --- a/extensions/blocks/contact-info/phone/index.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * External dependencies - */ -import { Path } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import save from './save'; -import renderMaterialIcon from '../../../utils/render-material-icon'; -import { __, _x } from '../../../utils/i18n'; - -const attributes = { - phone: { - type: 'string', - default: '', - }, -}; - -export const name = 'phone'; - -export const settings = { - title: __( 'Phone Number' ), - description: __( - 'Lets you add a phone number with an automatically generated click-to-call link.' - ), - keywords: [ - _x( 'mobile', 'block search term' ), - _x( 'telephone', 'block search term' ), - _x( 'cell', 'block search term' ), - ], - icon: renderMaterialIcon( - <Path d="M6.54 5c.06.89.21 1.76.45 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79h1.51m9.86 12.02c.85.24 1.72.39 2.6.45v1.49c-1.32-.09-2.59-.35-3.8-.75l1.2-1.19M7.5 3H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.49c0-.55-.45-1-1-1-1.24 0-2.45-.2-3.57-.57-.1-.04-.21-.05-.31-.05-.26 0-.51.1-.71.29l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c.28-.28.36-.67.25-1.02C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1z" /> - ), - category: 'jetpack', - attributes, - parent: [ 'jetpack/contact-info' ], - edit, - save, -}; diff --git a/extensions/blocks/contact-info/phone/save.js b/extensions/blocks/contact-info/phone/save.js deleted file mode 100644 index 50f6791464a06..0000000000000 --- a/extensions/blocks/contact-info/phone/save.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Internal dependencies - */ - -export function renderPhone( inputText ) { - const arrayOfNumbers = inputText.match( /\d+\.\d+|\d+\b|\d+(?=\w)/g ); - if ( ! arrayOfNumbers ) { - // No numbers found - return inputText; - } - const indexOfFirstNumber = inputText.indexOf( arrayOfNumbers[ 0 ] ); - - // Assume that eveything after the first number should be part of the phone number. - // care about the first prefix character. - let phoneNumber = indexOfFirstNumber ? inputText.substring( indexOfFirstNumber - 1 ) : inputText; - let prefix = indexOfFirstNumber ? inputText.substring( 0, indexOfFirstNumber ) : ''; - - let justNumber = phoneNumber.replace( /\D/g, '' ); - // Phone numbers starting with + should be part of the number. - if ( /[0-9/+/(]/.test( phoneNumber[ 0 ] ) ) { - // Remove the special character from the prefix so they don't appear twice. - prefix = prefix.slice( 0, -1 ); - // Phone numbers starting with + shoud be part of the number. - if ( phoneNumber[ 0 ] === '+' ) { - justNumber = '+' + justNumber; - } - } else { - // Remove the first character. - phoneNumber = phoneNumber.substring( 1 ); - } - const prefixSpan = prefix.trim() ? ( - <span key="phonePrefix" className="phone-prefix"> - { prefix } - </span> - ) : null; - return [ - prefixSpan, - <a key="phoneNumber" href={ `tel:${ justNumber }` }> - { phoneNumber } - </a>, - ]; -} - -const save = ( { attributes: { phone }, className } ) => - phone && <div className={ className }>{ renderPhone( phone ) }</div>; - -export default save; diff --git a/extensions/blocks/contact-info/style.scss b/extensions/blocks/contact-info/style.scss deleted file mode 100644 index 8f81ca897b1d9..0000000000000 --- a/extensions/blocks/contact-info/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.wp-block-jetpack-contact-info { - margin-bottom: 1.5em; -} diff --git a/extensions/blocks/contact-info/view.js b/extensions/blocks/contact-info/view.js deleted file mode 100644 index fd92905ca5dd0..0000000000000 --- a/extensions/blocks/contact-info/view.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Internal dependencies - */ - -import './style.scss'; diff --git a/extensions/blocks/gif/edit.js b/extensions/blocks/gif/edit.js deleted file mode 100644 index 96c7c2a914598..0000000000000 --- a/extensions/blocks/gif/edit.js +++ /dev/null @@ -1,214 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '../../utils/i18n'; -import classNames from 'classnames'; -import { Component, createRef } from '@wordpress/element'; -import { Button, PanelBody, Path, Placeholder, SVG, TextControl } from '@wordpress/components'; -import { InspectorControls, RichText } from '@wordpress/editor'; - -import { icon, title } from './'; - -const GIPHY_API_KEY = 't1PkR1Vq0mzHueIFBvZSZErgFs9NBmYW'; -const INPUT_PROMPT = __( 'Search for a term or paste a Giphy URL' ); - -class GifEdit extends Component { - textControlRef = createRef(); - - state = { - captionFocus: false, - results: null, - }; - - onFormSubmit = event => { - event.preventDefault(); - this.onSubmit(); - }; - - onSubmit = () => { - const { attributes } = this.props; - const { searchText } = attributes; - this.parseSearch( searchText ); - }; - - parseSearch = searchText => { - let giphyID = null; - // If search is hardcoded Giphy URL following this pattern: https://giphy.com/embed/4ZFekt94LMhNK - if ( searchText.indexOf( '//giphy.com/gifs' ) !== -1 ) { - giphyID = this.splitAndLast( this.splitAndLast( searchText, '/' ), '-' ); - } - // If search is hardcoded Giphy URL following this patterh: http://i.giphy.com/4ZFekt94LMhNK.gif - if ( searchText.indexOf( '//i.giphy.com' ) !== -1 ) { - giphyID = this.splitAndLast( searchText, '/' ).replace( '.gif', '' ); - } - // https://media.giphy.com/media/gt0hYzKlMpfOg/giphy.gif - const match = searchText.match( - /http[s]?:\/\/media.giphy.com\/media\/([A-Za-z0-9\-.]+)\/giphy.gif/ - ); - if ( match ) { - giphyID = match[ 1 ]; - } - if ( giphyID ) { - return this.fetch( this.urlForId( giphyID ) ); - } - - return this.fetch( this.urlForSearch( searchText ) ); - }; - - urlForSearch = searchText => { - return `https://api.giphy.com/v1/gifs/search?q=${ encodeURIComponent( - searchText - ) }&api_key=${ encodeURIComponent( GIPHY_API_KEY ) }&limit=10`; - }; - - urlForId = giphyId => { - return `https://api.giphy.com/v1/gifs/${ encodeURIComponent( - giphyId - ) }?api_key=${ encodeURIComponent( GIPHY_API_KEY ) }`; - }; - - splitAndLast = ( array, delimiter ) => { - const split = array.split( delimiter ); - return split[ split.length - 1 ]; - }; - - fetch = url => { - const xhr = new XMLHttpRequest(); - xhr.open( 'GET', url ); - xhr.onload = () => { - if ( xhr.status === 200 ) { - const res = JSON.parse( xhr.responseText ); - // If there is only one result, Giphy's API does not return an array. - // The following statement normalizes the data into an array with one member in this case. - const results = typeof res.data.images !== 'undefined' ? [ res.data ] : res.data; - const giphyData = results[ 0 ]; - // No results - if ( ! giphyData.images ) { - return; - } - this.setState( { results }, () => { - this.selectGiphy( giphyData ); - } ); - } else { - // Error handling TK - } - }; - xhr.send(); - }; - - selectGiphy = giphy => { - const { setAttributes } = this.props; - const calculatedPaddingTop = Math.floor( - ( giphy.images.original.height / giphy.images.original.width ) * 100 - ); - const paddingTop = `${ calculatedPaddingTop }%`; - const giphyUrl = giphy.embed_url; - setAttributes( { giphyUrl, paddingTop } ); - }; - - setFocus = () => { - this.textControlRef.current.querySelector( 'input' ).focus(); - this.setState( { captionFocus: false } ); - }; - - hasSearchText = () => { - const { attributes } = this.props; - const { searchText } = attributes; - return searchText && searchText.length > 0; - }; - - thumbnailClicked = thumbnail => { - this.selectGiphy( thumbnail ); - }; - - render() { - const { attributes, className, isSelected, setAttributes } = this.props; - const { align, caption, giphyUrl, searchText, paddingTop } = attributes; - const { captionFocus, results } = this.state; - const style = { paddingTop }; - const classes = classNames( className, `align${ align }` ); - const inputFields = ( - <form - className="wp-block-jetpack-gif_input-container" - onSubmit={ this.onFormSubmit } - ref={ this.textControlRef } - > - <TextControl - className="wp-block-jetpack-gif_input" - label={ INPUT_PROMPT } - placeholder={ INPUT_PROMPT } - onChange={ value => setAttributes( { searchText: value } ) } - value={ searchText } - /> - <Button isLarge onClick={ this.onSubmit }> - { __( 'Search' ) } - </Button> - </form> - ); - return ( - <div className={ classes }> - <InspectorControls> - <PanelBody className="components-panel__body-gif-branding"> - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 202 22"> - <Path d="M4.6 5.9H0v10h1.6v-3.1h3c4.8 0 4.8-6.9 0-6.9zm0 5.4h-3v-4h3c2.6.1 2.6 4 0 4zM51.2 12.3c2-.3 2.7-1.7 2.7-3.1 0-1.7-1.2-3.3-3.5-3.3h-4.6v10h1.6v-3.4h2.1l3 3.4h1.9l-.2-.3-3-3.3zM47.4 11V7.4h3c1.3 0 1.9.9 1.9 1.8s-.6 1.8-1.9 1.8h-3zM30.6 13.6L28 5.9h-1.1l-2.5 7.7-2.6-7.7H20l3.7 10H25l1.4-3.5L27.5 9l1.1 3.4 1.3 3.5h1.4l3.5-10h-1.7z" /> - <Path d="M14.4 5.7c-3 0-5.1 2.2-5.1 5.2 0 2.6 1.6 5.1 5.1 5.1 3.5 0 5.1-2.5 5.1-5.2-.1-2.6-1.7-5.1-5.1-5.1zm-.1 8.9c-2.5 0-3.5-1.9-3.5-3.7 0-2.2 1.2-3.8 3.5-3.8 2.4 0 3.5 2 3.5 3.8.1 2-1 3.7-3.5 3.7zM57.7 11.6h5.5v-1.5h-5.5V7.4h5.7V5.9h-7.3v10h7.3v-1.6h-5.7zM38 14.3v-2.7h5.5v-1.5H38V7.4h5.7V5.9h-7.3v10h7.3v-1.6zM93 10.3l-2.7-4.4h-1.9V6l3.8 5.8v4.1h1.6v-4.1l4-5.8v-.1h-2zM69.3 5.9h-3.8v10h3.8c3.5 0 5.1-2.5 5-5.1-.1-2.5-1.6-4.9-5-4.9zm0 8.4h-2.2V7.4h2.2c2.3 0 3.4 1.7 3.4 3.4s-1 3.5-3.4 3.5zM86.3 10.7c.9-.4 1.4-1.1 1.4-2 0-2-1.5-2.8-3.4-2.8h-4.6v10h4.6c2 0 3.7-.7 3.7-2.8 0-.8-.5-2-1.7-2.4zm-5-3.4h3c1.2 0 1.8.7 1.8 1.4 0 .8-.6 1.3-1.8 1.3h-3V7.3zm3 7.1h-3v-2.9h3c.9 0 2.1.5 2.1 1.6 0 1-1.2 1.3-2.1 1.3zM113.9 13.3h5.3V16c-1.2.9-2.9 1.1-4 1.1-4.2 0-5.6-3.3-5.6-6 0-4.1 2.2-6.1 5.6-6.1 1.4 0 3.2.4 4.8 1.8l3.4-3.4C120.7.6 118.1 0 115.2 0c-7.8 0-11.4 5.6-11.4 11s3.1 10.9 11.4 10.9c4 0 7.6-1.4 8.9-4.1V8.6h-10.2v4.7zM171.9 8.5h-7.4V.6h-5.9v20.8h5.9v-7.8h7.4v7.8h5.9V.6h-5.9zM195.1.6l-4.5 7.1-4.3-7.1h-6.6v.2l7.9 12.3v8.3h5.9v-8.3L201.8.9V.6zM127.4.6h5.9v20.8h-5.9zM147.6.6h-10.1v20.8h5.9v-5.6h4.2c5.6-.1 8.3-3.4 8.3-7.6.1-4.1-2.7-7.6-8.3-7.6zm0 10.2h-4.2V5.6h4.2c1.6 0 2.5 1.2 2.5 2.6 0 1.4-.9 2.6-2.5 2.6z" /> - </SVG> - </PanelBody> - </InspectorControls> - { ! giphyUrl ? ( - <Placeholder className="wp-block-jetpack-gif_placeholder" icon={ icon } label={ title }> - { inputFields } - </Placeholder> - ) : ( - <figure> - { isSelected && inputFields } - { isSelected && results && results.length > 1 && ( - <div className="wp-block-jetpack-gif_thumbnails-container"> - { results.map( thumbnail => { - const thumbnailStyle = { - backgroundImage: `url(${ thumbnail.images.downsized_still.url })`, - }; - return ( - <button - className="wp-block-jetpack-gif_thumbnail-container" - key={ thumbnail.id } - onClick={ () => { - this.thumbnailClicked( thumbnail ); - } } - style={ thumbnailStyle } - /> - ); - } ) } - </div> - ) } - <div className="wp-block-jetpack-gif-wrapper" style={ style }> - <div - className="wp-block-jetpack-gif_cover" - onClick={ this.setFocus } - onKeyDown={ this.setFocus } - role="button" - tabIndex="0" - /> - <iframe src={ giphyUrl } title={ searchText } /> - </div> - { ( ! RichText.isEmpty( caption ) || isSelected ) && !! giphyUrl && ( - <RichText - className="wp-block-jetpack-gif-caption gallery-caption" - inlineToolbar - isSelected={ captionFocus } - unstableOnFocus={ () => { - this.setState( { captionFocus: true } ); - } } - onChange={ value => setAttributes( { caption: value } ) } - placeholder={ __( 'Write caption…' ) } - tagName="figcaption" - value={ caption } - /> - ) } - </figure> - ) } - </div> - ); - } -} -export default GifEdit; diff --git a/extensions/blocks/gif/editor.js b/extensions/blocks/gif/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/gif/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/gif/editor.scss b/extensions/blocks/gif/editor.scss deleted file mode 100644 index 6505083f85efc..0000000000000 --- a/extensions/blocks/gif/editor.scss +++ /dev/null @@ -1,84 +0,0 @@ -.wp-block-jetpack-gif { - figure { - transition: padding-top 125ms ease-in-out; - } - .components-base-control__field { - text-align: center; - } - .wp-block-jetpack-gif_cover { - background: none; - border: none; - height: 100%; - left: 0; - margin: 0; - padding: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1; - &:focus { - outline: none; - } - } - .wp-block-jetpack-gif_input-container { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - margin: 0 auto; - max-width: 400px; - width: 100%; - z-index: 1; - .components-base-control__label { - height: 0; - margin: 0; - text-indent: -9999px; - } - } - .wp-block-jetpack-gif_input { - flex-grow: 1; - margin-right: 0.5em; - } - .wp-block-jetpack-gif_thumbnails-container { - display: flex; - margin: -2px 0 2px 0; - margin-left: calc( -4px / 2 ); - overflow-x: auto; - width: calc( 100% + 4px ); - &::-webkit-scrollbar { - display: none; - } - } - .wp-block-jetpack-gif_thumbnail-container { - align-items: center; - background-size: cover; - background-repeat: no-repeat; - background-position: 50% 50%; - border: none; - border-radius: 3px; - cursor: pointer; - display: flex; - justify-content: center; - margin: 2px; - padding: 0; - padding-bottom: calc( 100% / 10 - 4px ); - width: calc( 100% / 10 - 4px ); - &:hover { - box-shadow: 0 0 0 1px #555d66; - } - &:focus { - box-shadow: 0 0 0 2px #00a0d2; - outline: 0; - } - } -} -.components-panel__body-gif-branding { - svg { - display: block; - margin: 0 auto; - max-width: 200px; - } - svg path { - fill: #ccc; - } -} diff --git a/extensions/blocks/gif/index.js b/extensions/blocks/gif/index.js deleted file mode 100644 index 045d1618173f9..0000000000000 --- a/extensions/blocks/gif/index.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * External dependencies - */ -import { Path, SVG } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import { __ } from '../../utils/i18n'; - -// Ordering is important! Editor overrides style! -import './style.scss'; -import './editor.scss'; - -export const name = 'gif'; -export const title = __( 'GIF' ); - -export const icon = ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M18 13v7H4V6h5.02c.05-.71.22-1.38.48-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-5l-2-2zm-1.5 5h-11l2.75-3.53 1.96 2.36 2.75-3.54L16.5 18zm2.8-9.11c.44-.7.7-1.51.7-2.39C20 4.01 17.99 2 15.5 2S11 4.01 11 6.5s2.01 4.5 4.49 4.5c.88 0 1.7-.26 2.39-.7L21 13.42 22.42 12 19.3 8.89zM15.5 9C14.12 9 13 7.88 13 6.5S14.12 4 15.5 4 18 5.12 18 6.5 16.88 9 15.5 9z" /> - </SVG> -); - -export const settings = { - title, - icon, - category: 'jetpack', - keywords: [ __( 'animated' ), __( 'giphy' ), __( 'image' ) ], - description: __( 'Search for and insert an animated image.' ), - attributes: { - align: { - type: 'string', - default: 'center', - }, - caption: { - type: 'string', - }, - giphyUrl: { - type: 'string', - }, - searchText: { - type: 'string', - }, - paddingTop: { - type: 'string', - default: '56.2%', - }, - }, - supports: { - html: false, - align: true, - }, - edit, - save: () => null, -}; diff --git a/extensions/blocks/gif/style.scss b/extensions/blocks/gif/style.scss deleted file mode 100644 index 2e7b057a79ac4..0000000000000 --- a/extensions/blocks/gif/style.scss +++ /dev/null @@ -1,38 +0,0 @@ -.wp-block-jetpack-gif { - clear: both; - margin: 0 0 20px; - figure { - margin: 0; - position: relative; - width: 100%; - } - iframe { - border: 0; - left: 0; - height: 100%; - position: absolute; - top: 0; - width: 100%; - } - &.aligncenter { - text-align: center; - } - &.alignright, - &.alignleft { - min-width: 300px; - } - // Mirroring Gutenberg caption-style mixin: https://github.com/WordPress/gutenberg/blob/master/assets/stylesheets/_mixins.scss#L312-L318 - .wp-block-jetpack-gif-caption { - margin-top: 0.5em; - margin-bottom: 1em; - color: #555d66; - text-align: center; - } - .wp-block-jetpack-gif-wrapper { - height: 0; - margin: 0; - padding: calc( 56.2% + 12px ) 0 0 0; - position: relative; - width: 100%; - } -} diff --git a/extensions/blocks/gif/view.js b/extensions/blocks/gif/view.js deleted file mode 100644 index 6a6dda31712c5..0000000000000 --- a/extensions/blocks/gif/view.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Internal dependencies - */ -import './style.scss'; diff --git a/extensions/blocks/mailchimp/edit.js b/extensions/blocks/mailchimp/edit.js deleted file mode 100644 index 8941092eb831f..0000000000000 --- a/extensions/blocks/mailchimp/edit.js +++ /dev/null @@ -1,234 +0,0 @@ -/** - * External dependencies - */ -import apiFetch from '@wordpress/api-fetch'; -import { __ } from '../../utils/i18n'; -import classnames from 'classnames'; -import SubmitButton from '../../utils/submit-button'; -import { - Button, - ExternalLink, - PanelBody, - Placeholder, - Spinner, - TextControl, - withNotices, -} from '@wordpress/components'; -import { InspectorControls, RichText } from '@wordpress/editor'; -import { Fragment, Component } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { icon } from '.'; - -const API_STATE_LOADING = 0; -const API_STATE_CONNECTED = 1; -const API_STATE_NOTCONNECTED = 2; - -const NOTIFICATION_PROCESSING = 'processing'; -const NOTIFICATION_SUCCESS = 'success'; -const NOTIFICATION_ERROR = 'error'; - -class MailchimpSubscribeEdit extends Component { - constructor() { - super( ...arguments ); - this.state = { - audition: null, - connected: API_STATE_LOADING, - connectURL: null, - }; - this.timeout = null; - } - - componentDidMount = () => { - this.apiCall(); - }; - - onError = message => { - const { noticeOperations } = this.props; - noticeOperations.removeAllNotices(); - noticeOperations.createErrorNotice( message ); - }; - - apiCall = () => { - const path = '/wpcom/v2/mailchimp'; - const method = 'GET'; - const fetch = { path, method }; - apiFetch( fetch ).then( - result => { - const connectURL = result.connect_url; - const connected = - result.code === 'connected' ? API_STATE_CONNECTED : API_STATE_NOTCONNECTED; - this.setState( { connected, connectURL } ); - }, - result => { - const connectURL = null; - const connected = API_STATE_NOTCONNECTED; - this.setState( { connected, connectURL } ); - this.onError( result.message ); - } - ); - }; - - auditionNotification = notification => { - this.setState( { audition: notification } ); - if ( this.timeout ) { - clearTimeout( this.timeout ); - } - this.timeout = setTimeout( this.clearAudition, 3000 ); - }; - - clearAudition = () => { - this.setState( { audition: null } ); - }; - - updateProcessingText = processingLabel => { - const { setAttributes } = this.props; - setAttributes( { processingLabel } ); - this.auditionNotification( NOTIFICATION_PROCESSING ); - }; - - updateSuccessText = successLabel => { - const { setAttributes } = this.props; - setAttributes( { successLabel } ); - this.auditionNotification( NOTIFICATION_SUCCESS ); - }; - - updateErrorText = errorLabel => { - const { setAttributes } = this.props; - setAttributes( { errorLabel } ); - this.auditionNotification( NOTIFICATION_ERROR ); - }; - - updateEmailPlaceholder = emailPlaceholder => { - const { setAttributes } = this.props; - setAttributes( { emailPlaceholder } ); - this.clearAudition(); - }; - - labelForAuditionType = audition => { - const { attributes } = this.props; - const { processingLabel, successLabel, errorLabel } = attributes; - if ( audition === NOTIFICATION_PROCESSING ) { - return processingLabel; - } else if ( audition === NOTIFICATION_SUCCESS ) { - return successLabel; - } else if ( audition === NOTIFICATION_ERROR ) { - return errorLabel; - } - return null; - }; - - roleForAuditionType = audition => { - if ( audition === NOTIFICATION_ERROR ) { - return 'alert'; - } - return 'status'; - }; - - render = () => { - const { attributes, className, notices, noticeUI, setAttributes } = this.props; - const { audition, connected, connectURL } = this.state; - const { emailPlaceholder, consentText, processingLabel, successLabel, errorLabel } = attributes; - const classPrefix = 'wp-block-jetpack-mailchimp_'; - const waiting = ( - <Placeholder icon={ icon } notices={ notices }> - <Spinner /> - </Placeholder> - ); - const placeholder = ( - <Placeholder icon={ icon } label={ __( 'Mailchimp' ) } notices={ notices }> - <div className="components-placeholder__instructions"> - { __( - 'You need to connect your Mailchimp account and choose a list in order to start collecting Email subscribers.' - ) } - <br /> - <br /> - <Button isDefault isLarge href={ connectURL } target="_blank"> - { __( 'Set up Mailchimp form' ) } - </Button> - <br /> - <br /> - <Button isLink onClick={ this.apiCall }> - { __( 'Re-check Connection' ) } - </Button> - </div> - </Placeholder> - ); - const inspectorControls = ( - <InspectorControls> - <PanelBody title={ __( 'Text Elements' ) }> - <TextControl - label={ __( 'Email Placeholder' ) } - value={ emailPlaceholder } - onChange={ this.updateEmailPlaceholder } - /> - </PanelBody> - <PanelBody title={ __( 'Notifications' ) }> - <TextControl - label={ __( 'Processing text' ) } - value={ processingLabel } - onChange={ this.updateProcessingText } - /> - <TextControl - label={ __( 'Success text' ) } - value={ successLabel } - onChange={ this.updateSuccessText } - /> - <TextControl - label={ __( 'Error text' ) } - value={ errorLabel } - onChange={ this.updateErrorText } - /> - </PanelBody> - <PanelBody title={ __( 'Mailchimp Connection' ) }> - <ExternalLink href={ connectURL }>{ __( 'Manage Connection' ) }</ExternalLink> - </PanelBody> - </InspectorControls> - ); - const blockClasses = classnames( className, { - [ `${ classPrefix }notication-audition` ]: audition, - } ); - const blockContent = ( - <div className={ blockClasses }> - <TextControl - aria-label={ emailPlaceholder } - className="wp-block-jetpack-mailchimp_text-input" - disabled - onChange={ () => false } - placeholder={ emailPlaceholder } - title={ __( 'You can edit the email placeholder in the sidebar.' ) } - type="email" - /> - <SubmitButton { ...this.props } /> - <RichText - tagName="p" - placeholder={ __( 'Write consent text' ) } - value={ consentText } - onChange={ value => setAttributes( { consentText: value } ) } - inlineToolbar - /> - { audition && ( - <div - className={ `${ classPrefix }notification ${ classPrefix }${ audition }` } - role={ this.roleForAuditionType( audition ) } - > - { this.labelForAuditionType( audition ) } - </div> - ) } - </div> - ); - return ( - <Fragment> - { noticeUI } - { connected === API_STATE_LOADING && waiting } - { connected === API_STATE_NOTCONNECTED && placeholder } - { connected === API_STATE_CONNECTED && inspectorControls } - { connected === API_STATE_CONNECTED && blockContent } - </Fragment> - ); - }; -} - -export default withNotices( MailchimpSubscribeEdit ); diff --git a/extensions/blocks/mailchimp/editor.js b/extensions/blocks/mailchimp/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/mailchimp/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/mailchimp/editor.scss b/extensions/blocks/mailchimp/editor.scss deleted file mode 100644 index 84de75481ad0a..0000000000000 --- a/extensions/blocks/mailchimp/editor.scss +++ /dev/null @@ -1,29 +0,0 @@ -@import './view.scss'; - -.wp-block-jetpack-mailchimp { - - .wp-block-jetpack-mailchimp_notification { - display: block; - } - - .editor-rich-text__inline-toolbar { - pointer-events: none; - .components-toolbar { - pointer-events: all; - } - } - - // Hide everything else except notification when modifying notification labels - &.wp-block-jetpack-mailchimp_notication-audition > *:not( .wp-block-jetpack-mailchimp_notification ) { - display: none; - } - - .wp-block-jetpack-mailchimp_text-input, .jetpack-submit-button { - margin-bottom: 1.5rem; - } - - .wp-block-button .wp-block-button__link { - margin-top: 0; - } - -} diff --git a/extensions/blocks/mailchimp/index.js b/extensions/blocks/mailchimp/index.js deleted file mode 100644 index a14f2f5d4cbbb..0000000000000 --- a/extensions/blocks/mailchimp/index.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * External dependencies - */ -import { Path, SVG } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __, _x } from '../../utils/i18n'; -import edit from './edit'; -import './editor.scss'; - -export const name = 'mailchimp'; - -export const icon = ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z" /> - </SVG> -); - -export const settings = { - title: __( 'Mailchimp' ), - icon, - description: __( 'A form enabling readers to join a Mailchimp list.' ), - category: 'jetpack', - keywords: [ - _x( 'email', 'block search term' ), - _x( 'subscription', 'block search term' ), - _x( 'newsletter', 'block search term' ), - ], - attributes: { - emailPlaceholder: { - type: 'string', - default: __( 'Enter your email' ), - }, - submitButtonText: { - type: 'string', - default: __( 'Join my email list' ), - }, - customBackgroundButtonColor: { - type: 'string', - }, - customTextButtonColor: { - type: 'string', - }, - consentText: { - type: 'string', - default: __( - 'By clicking submit, you agree to share your email address with the site owner and Mailchimp to receive marketing, updates, and other emails from the site owner. Use the unsubscribe link in those emails to opt out at any time.' - ), - }, - processingLabel: { - type: 'string', - default: __( 'Processing…' ), - }, - successLabel: { - type: 'string', - default: __( "Success! You're on the list." ), - }, - errorLabel: { - type: 'string', - default: __( - "Whoops! There was an error and we couldn't process your subscription. Please reload the page and try again." - ), - }, - }, - edit, - save: () => null, -}; diff --git a/extensions/blocks/mailchimp/view.js b/extensions/blocks/mailchimp/view.js deleted file mode 100644 index a2ec768045560..0000000000000 --- a/extensions/blocks/mailchimp/view.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Internal dependencies - */ -import emailValidator from 'email-validator'; - -/** - * Internal dependencies - */ -import './view.scss'; - -const blockClassName = 'wp-block-jetpack-mailchimp'; - -function fetchSubscription( blogId, email ) { - const url = - 'https://public-api.wordpress.com/rest/v1.1/sites/' + - encodeURIComponent( blogId ) + - '/email_follow/subscribe?email=' + - encodeURIComponent( email ); - return new Promise( function( resolve, reject ) { - const xhr = new XMLHttpRequest(); - xhr.open( 'GET', url ); - xhr.onload = function() { - if ( xhr.status === 200 ) { - const res = JSON.parse( xhr.responseText ); - resolve( res ); - } else { - const res = JSON.parse( xhr.responseText ); - reject( res ); - } - }; - xhr.send(); - } ); -} - -function activateSubscription( block, blogId ) { - const form = block.querySelector( 'form' ); - const errorClass = 'error'; - const processingEl = block.querySelector( '.' + blockClassName + '_processing' ); - const errorEl = block.querySelector( '.' + blockClassName + '_error' ); - const successEl = block.querySelector( '.' + blockClassName + '_success' ); - form.addEventListener( 'submit', e => { - e.preventDefault(); - const emailField = form.querySelector( 'input' ); - emailField.classList.remove( errorClass ); - const email = emailField.value; - if ( ! emailValidator.validate( email ) ) { - emailField.classList.add( errorClass ); - return; - } - block.classList.add( 'is-processing' ); - processingEl.classList.add( 'is-visible' ); - fetchSubscription( blogId, email ).then( - response => { - processingEl.classList.remove( 'is-visible' ); - if ( response.error && response.error !== 'member_exists' ) { - errorEl.classList.add( 'is-visible' ); - } else { - successEl.classList.add( 'is-visible' ); - } - }, - () => { - processingEl.classList.remove( 'is-visible' ); - errorEl.classList.add( 'is-visible' ); - } - ); - } ); -} - -const initializeMailchimpBlocks = () => { - const mailchimpBlocks = Array.from( document.querySelectorAll( '.' + blockClassName ) ); - mailchimpBlocks.forEach( block => { - const blog_id = block.getAttribute( 'data-blog-id' ); - try { - activateSubscription( block, blog_id ); - } catch ( err ) { - if ( 'production' !== process.env.NODE_ENV ) { - // eslint-disable-next-line no-console - console.error( err ); - } - } - } ); -}; - -if ( typeof window !== 'undefined' && typeof document !== 'undefined' ) { - // `DOMContentLoaded` may fire before the script has a chance to run - if ( document.readyState === 'loading' ) { - document.addEventListener( 'DOMContentLoaded', initializeMailchimpBlocks ); - } else { - initializeMailchimpBlocks(); - } -} diff --git a/extensions/blocks/mailchimp/view.scss b/extensions/blocks/mailchimp/view.scss deleted file mode 100644 index 56b852e28ffaa..0000000000000 --- a/extensions/blocks/mailchimp/view.scss +++ /dev/null @@ -1,32 +0,0 @@ -.wp-block-jetpack-mailchimp { - - &.is-processing { - form { - display: none; - } - } - - .wp-block-jetpack-mailchimp_notification { - display: none; - margin-bottom: 1.5em; - padding: 0.75em; - &.is-visible { - display: block; - } - - &.wp-block-jetpack-mailchimp_error { - background-color: $muriel-hot-red-500; - color: #fff; - } - - &.wp-block-jetpack-mailchimp_processing { - background-color: rgba( 0, 0, 0, 0.025 ); - } - - &.wp-block-jetpack-mailchimp_success { - background-color: $muriel-hot-green-500; - color: #fff; - } - } - -} diff --git a/extensions/blocks/map/add-point/index.js b/extensions/blocks/map/add-point/index.js deleted file mode 100644 index dc08337d98afc..0000000000000 --- a/extensions/blocks/map/add-point/index.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { Button, Dashicon, Popover } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import LocationSearch from '../location-search'; -import { __ } from '../../../utils/i18n'; -import './style.scss'; - -export class AddPoint extends Component { - render() { - const { onClose, onAddPoint, onError, apiKey } = this.props; - return ( - <Button className="component__add-point"> - { __( 'Add marker' ) } - <Popover className="component__add-point__popover"> - <Button className="component__add-point__close" onClick={ onClose }> - <Dashicon icon="no" /> - </Button> - <LocationSearch - onAddPoint={ onAddPoint } - label={ __( 'Add a location' ) } - apiKey={ apiKey } - onError={ onError } - /> - </Popover> - </Button> - ); - } -} - -AddPoint.defaultProps = { - onAddPoint: () => {}, - onClose: () => {}, - onError: () => {}, -}; - -export default AddPoint; diff --git a/extensions/blocks/map/add-point/oval.svg b/extensions/blocks/map/add-point/oval.svg deleted file mode 100644 index cb149ec47cc3d..0000000000000 --- a/extensions/blocks/map/add-point/oval.svg +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg width="32px" height="38px" viewBox="0 0 32 38" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 52.1 (67048) - http://www.bohemiancoding.com/sketch --> - <title>Oval Copy</title> - <desc>Created with Sketch.</desc> - <defs> - <path d="M119,136 C119,136 135,124.692424 135,114 C135,103.307576 127.836556,98 119,98 C110.163444,98 103,103.307576 103,114 C103,124.692424 119,136 119,136 Z" id="path-1"></path> - <mask id="mask-2" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="32" height="38" fill="white"> - <use xlink:href="#path-1"></use> - </mask> - </defs> - <g id="Map-Block" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-dasharray="4"> - <g id="Revised-01-Placeholder-Copy" transform="translate(-496.000000, -376.000000)" stroke="#444444" stroke-width="4"> - <g id="Group" transform="translate(393.000000, 278.000000)"> - <use id="Oval-Copy" mask="url(#mask-2)" xlink:href="#path-1"></use> - </g> - </g> - </g> -</svg> \ No newline at end of file diff --git a/extensions/blocks/map/add-point/style.scss b/extensions/blocks/map/add-point/style.scss deleted file mode 100644 index 9cce943eb3831..0000000000000 --- a/extensions/blocks/map/add-point/style.scss +++ /dev/null @@ -1,46 +0,0 @@ - -.component__add-point { - position: absolute; - left: 50%; - top: 50%; - width: 32px; - height: 38px; - margin-top: -19px; - margin-left: -16px; - background-image: url( ./oval.svg ); - background-repeat: no-repeat; - text-indent: -9999px; - box-shadow: none; - background-color: transparent; - &.components-button:not( :disabled ):not( [aria-disabled='true'] ):focus { - background-color: transparent; - box-shadow: none; - } - &:focus, - &:active { - background-color: transparent; - box-shadow: none; - } -} -.component__add-point__popover { - .components-button:not( :disabled ):not( [aria-disabled='true'] ):focus { - background-color: transparent; - box-shadow: none; - } - .components-popover__content { - padding: 0.1rem; - } - .components-location-search { - margin: 0.5rem; - } -} -.component__add-point__close { - margin: 0; - padding: 0; - border: none; - box-shadow: none; - float: right; - path { - color: #aaa; - } -} diff --git a/extensions/blocks/map/component.js b/extensions/blocks/map/component.js deleted file mode 100644 index efd19c3a0d411..0000000000000 --- a/extensions/blocks/map/component.js +++ /dev/null @@ -1,332 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '../../utils/i18n'; -import { assign, debounce, get } from 'lodash'; -import { Button, Dashicon, TextareaControl, TextControl } from '@wordpress/components'; -import { Children, Component, createRef, Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import MapMarker from './map-marker/'; -import InfoWindow from './info-window/'; -import { mapboxMapFormatter } from './mapbox-map-formatter/'; - -export class Map extends Component { - // Lifecycle - constructor() { - super( ...arguments ); - - this.state = { - map: null, - fit_to_bounds: false, - loaded: false, - mapboxgl: null, - }; - - // Refs - this.mapRef = createRef(); - - // Debouncers - this.debouncedSizeMap = debounce( this.sizeMap, 250 ); - } - render() { - const { points, admin, children, markerColor } = this.props; - const { map, activeMarker, mapboxgl } = this.state; - const { onMarkerClick, deleteActiveMarker, updateActiveMarker } = this; - const currentPoint = get( activeMarker, 'props.point' ) || {}; - const { title, caption } = currentPoint; - const addPoint = Children.map( children, child => { - const tagName = get( child, 'props.tagName' ); - if ( 'AddPoint' === tagName ) { - return child; - } - } ); - const mapMarkers = - map && - mapboxgl && - points.map( ( point, index ) => { - return ( - <MapMarker - key={ index } - point={ point } - index={ index } - map={ map } - mapboxgl={ mapboxgl } - markerColor={ markerColor } - onClick={ onMarkerClick } - /> - ); - } ); - const infoWindow = mapboxgl && ( - <InfoWindow - activeMarker={ activeMarker } - map={ map } - mapboxgl={ mapboxgl } - unsetActiveMarker={ () => this.setState( { activeMarker: null } ) } - > - { activeMarker && admin && ( - <Fragment> - <TextControl - label={ __( 'Marker Title' ) } - value={ title } - onChange={ value => updateActiveMarker( { title: value } ) } - /> - <TextareaControl - className="wp-block-jetpack-map__marker-caption" - label={ __( 'Marker Caption' ) } - value={ caption } - rows="2" - tag="textarea" - onChange={ value => updateActiveMarker( { caption: value } ) } - /> - <Button onClick={ deleteActiveMarker } className="wp-block-jetpack-map__delete-btn"> - <Dashicon icon="trash" size="15" /> { __( 'Delete Marker' ) } - </Button> - </Fragment> - ) } - - { activeMarker && ! admin && ( - <Fragment> - <h3>{ title }</h3> - <p>{ caption }</p> - </Fragment> - ) } - </InfoWindow> - ); - return ( - <Fragment> - <div className="wp-block-jetpack-map__gm-container" ref={ this.mapRef }> - { mapMarkers } - </div> - { infoWindow } - { addPoint } - </Fragment> - ); - } - componentDidMount() { - const { apiKey } = this.props; - if ( apiKey ) { - this.loadMapLibraries(); - } - } - componentWillUnmount() { - this.debouncedSizeMap.cancel(); - } - componentDidUpdate( prevProps ) { - const { apiKey, children, points, mapStyle, mapDetails } = this.props; - const { map } = this.state; - if ( apiKey && apiKey.length > 0 && apiKey !== prevProps.apiKey ) { - this.loadMapLibraries(); - } - // If the user has just clicked to show the Add Point component, hide info window. - // AddPoint is the only possible child. - if ( children !== prevProps.children && children !== false ) { - this.clearCurrentMarker(); - } - if ( points !== prevProps.points ) { - this.setBoundsByMarkers(); - } - if ( points.length !== prevProps.points.length ) { - this.clearCurrentMarker(); - } - if ( mapStyle !== prevProps.mapStyle || mapDetails !== prevProps.mapDetails ) { - map.setStyle( this.getMapStyle() ); - } - } - /* Event handling */ - onMarkerClick = marker => { - const { onMarkerClick } = this.props; - this.setState( { activeMarker: marker } ); - onMarkerClick(); - }; - onMapClick = () => { - this.setState( { activeMarker: null } ); - }; - clearCurrentMarker = () => { - this.setState( { activeMarker: null } ); - }; - updateActiveMarker = updates => { - const { points } = this.props; - const { activeMarker } = this.state; - const { index } = activeMarker.props; - const newPoints = points.slice( 0 ); - - assign( newPoints[ index ], updates ); - this.props.onSetPoints( newPoints ); - }; - deleteActiveMarker = () => { - const { points } = this.props; - const { activeMarker } = this.state; - const { index } = activeMarker.props; - const newPoints = points.slice( 0 ); - - newPoints.splice( index, 1 ); - this.props.onSetPoints( newPoints ); - this.setState( { activeMarker: null } ); - }; - // Various map functions - sizeMap = () => { - const { map } = this.state; - const mapEl = this.mapRef.current; - const blockWidth = mapEl.offsetWidth; - const maxHeight = window.innerHeight * 0.8; - const blockHeight = Math.min( blockWidth * ( 3 / 4 ), maxHeight ); - mapEl.style.height = blockHeight + 'px'; - map.resize(); - this.setBoundsByMarkers(); - }; - setBoundsByMarkers = () => { - const { zoom, points, onSetZoom } = this.props; - const { map, activeMarker, mapboxgl, zoomControl, boundsSetProgrammatically } = this.state; - if ( ! map ) { - return; - } - // If there are no points at all, there is no data to set bounds to. Abort the function. - if ( ! points.length ) { - return; - } - // If there is an open info window, resizing will probably move the info window which complicates interaction. - if ( activeMarker ) { - return; - } - const bounds = new mapboxgl.LngLatBounds(); - points.forEach( point => { - bounds.extend( [ point.coordinates.longitude, point.coordinates.latitude ] ); - } ); - - // If there are multiple points, zoom is determined by the area they cover, and zoom control is removed. - if ( points.length > 1 ) { - map.fitBounds( bounds, { - padding: { - top: 40, - bottom: 40, - left: 20, - right: 20, - }, - } ); - this.setState( { boundsSetProgrammatically: true } ); - map.removeControl( zoomControl ); - return; - } - // If there is only one point, center map around it. - map.setCenter( bounds.getCenter() ); - - // If the number of markers has just changed from > 1 to 1, set an arbitrary tight zoom, which feels like the original default. - if ( boundsSetProgrammatically ) { - const newZoom = 12; - map.setZoom( newZoom ); - onSetZoom( newZoom ); - } else { - // If there are one (or zero) points, and this is not a recent change, respect user's chosen zoom. - map.setZoom( parseInt( zoom, 10 ) ); - } - map.addControl( zoomControl ); - this.setState( { boundsSetProgrammatically: false } ); - }; - getMapStyle() { - const { mapStyle, mapDetails } = this.props; - return mapboxMapFormatter( mapStyle, mapDetails ); - } - getMapType() { - const { mapStyle } = this.props; - switch ( mapStyle ) { - case 'satellite': - return 'HYBRID'; - case 'terrain': - return 'TERRAIN'; - case 'black_and_white': - default: - return 'ROADMAP'; - } - } - // Script loading, browser geolocation - scriptsLoaded = () => { - const { mapCenter, points } = this.props; - this.setState( { loaded: true } ); - - // If the map has any points, skip geolocation and use what we have. - if ( points.length > 0 ) { - this.initMap( mapCenter ); - return; - } - this.initMap( mapCenter ); - }; - loadMapLibraries() { - const { apiKey } = this.props; - Promise.all( [ - import( /* webpackChunkName: "map/mapbox-gl" */ 'mapbox-gl' ), - import( /* webpackChunkName: "map/mapbox-gl" */ 'mapbox-gl/dist/mapbox-gl.css' ), - ] ).then( ( [ { default: mapboxgl } ] ) => { - mapboxgl.accessToken = apiKey; - this.setState( { mapboxgl: mapboxgl }, this.scriptsLoaded ); - } ); - } - initMap( mapCenter ) { - const { mapboxgl } = this.state; - const { zoom, onMapLoaded, onError, admin } = this.props; - let map = null; - try { - map = new mapboxgl.Map( { - container: this.mapRef.current, - style: this.getMapStyle(), - center: this.googlePoint2Mapbox( mapCenter ), - zoom: parseInt( zoom, 10 ), - pitchWithRotate: false, - attributionControl: false, - dragRotate: false, - } ); - } catch ( e ) { - onError( 'mapbox_error', e.message ); - return; - } - map.on( 'error', e => { - onError( 'mapbox_error', e.error.message ); - } ); - const zoomControl = new mapboxgl.NavigationControl( { - showCompass: false, - showZoom: true, - } ); - map.on( 'zoomend', () => { - this.props.onSetZoom( map.getZoom() ); - } ); - - /* Listen for clicks on the Map background, which hides the current popup. */ - map.getCanvas().addEventListener( 'click', this.onMapClick ); - this.setState( { map, zoomControl }, () => { - this.debouncedSizeMap(); - map.addControl( zoomControl ); - if ( ! admin ) { - map.addControl( new mapboxgl.FullscreenControl() ); - } - this.mapRef.current.addEventListener( 'alignmentChanged', this.debouncedSizeMap ); - map.resize(); - onMapLoaded(); - this.setState( { loaded: true } ); - window.addEventListener( 'resize', this.debouncedSizeMap ); - } ); - } - googlePoint2Mapbox( google_point ) { - const mapCenter = [ - google_point.longitude ? google_point.longitude : 0, - google_point.latitude ? google_point.latitude : 0, - ]; - return mapCenter; - } -} - -Map.defaultProps = { - points: [], - mapStyle: 'default', - zoom: 13, - onSetZoom: () => {}, - onMapLoaded: () => {}, - onMarkerClick: () => {}, - onError: () => {}, - markerColor: 'red', - apiKey: null, - mapCenter: {}, -}; - -export default Map; diff --git a/extensions/blocks/map/edit.js b/extensions/blocks/map/edit.js deleted file mode 100644 index 54168e1dbd0e3..0000000000000 --- a/extensions/blocks/map/edit.js +++ /dev/null @@ -1,282 +0,0 @@ -/** - * External dependencies - */ -import apiFetch from '@wordpress/api-fetch'; -import { Component, createRef, Fragment } from '@wordpress/element'; -import { - Button, - ButtonGroup, - ExternalLink, - IconButton, - PanelBody, - Placeholder, - Spinner, - TextControl, - ToggleControl, - Toolbar, - withNotices, -} from '@wordpress/components'; -import { - BlockAlignmentToolbar, - BlockControls, - InspectorControls, - PanelColorSettings, -} from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import AddPoint from './add-point'; -import Locations from './locations'; -import Map from './component.js'; -import MapThemePicker from './map-theme-picker'; -import { __ } from '../../utils/i18n'; -import { settings } from './settings.js'; - -const API_STATE_LOADING = 0; -const API_STATE_FAILURE = 1; -const API_STATE_SUCCESS = 2; - -class MapEdit extends Component { - constructor() { - super( ...arguments ); - this.state = { - addPointVisibility: false, - apiState: API_STATE_LOADING, - }; - this.mapRef = createRef(); - } - addPoint = point => { - const { attributes, setAttributes } = this.props; - const { points } = attributes; - const newPoints = points.slice( 0 ); - let duplicateFound = false; - points.map( existingPoint => { - if ( existingPoint.id === point.id ) { - duplicateFound = true; - } - } ); - if ( duplicateFound ) { - return; - } - newPoints.push( point ); - setAttributes( { points: newPoints } ); - this.setState( { addPointVisibility: false } ); - }; - updateAlignment = value => { - this.props.setAttributes( { align: value } ); - // Allow one cycle for alignment change to take effect - setTimeout( this.mapRef.current.sizeMap, 0 ); - }; - updateAPIKeyControl = value => { - this.setState( { - apiKeyControl: value, - } ); - }; - updateAPIKey = () => { - const { noticeOperations } = this.props; - const { apiKeyControl } = this.state; - noticeOperations.removeAllNotices(); - apiKeyControl && this.apiCall( apiKeyControl, 'POST' ); - }; - removeAPIKey = () => { - this.apiCall( null, 'DELETE' ); - }; - apiCall( serviceApiKey = null, method = 'GET' ) { - const { noticeOperations } = this.props; - const { apiKey } = this.state; - const path = '/wpcom/v2/service-api-keys/mapbox'; - const fetch = serviceApiKey - ? { path, method, data: { service_api_key: serviceApiKey } } - : { path, method }; - this.setState( { apiRequestOutstanding: true }, () => { - apiFetch( fetch ).then( - result => { - noticeOperations.removeAllNotices(); - this.setState( { - apiState: result.service_api_key ? API_STATE_SUCCESS : API_STATE_FAILURE, - apiKey: result.service_api_key, - apiKeyControl: result.service_api_key, - apiRequestOutstanding: false, - } ); - }, - result => { - this.onError( null, result.message ); - this.setState( { - apiRequestOutstanding: false, - apiKeyControl: apiKey, - } ); - } - ); - } ); - } - componentDidMount() { - this.apiCall(); - } - onError = ( code, message ) => { - const { noticeOperations } = this.props; - noticeOperations.removeAllNotices(); - noticeOperations.createErrorNotice( message ); - }; - render() { - const { className, setAttributes, attributes, noticeUI, notices } = this.props; - const { mapStyle, mapDetails, points, zoom, mapCenter, markerColor, align } = attributes; - const { - addPointVisibility, - apiKey, - apiKeyControl, - apiState, - apiRequestOutstanding, - } = this.state; - const inspectorControls = ( - <Fragment> - <BlockControls> - <BlockAlignmentToolbar - value={ align } - onChange={ this.updateAlignment } - controls={ [ 'center', 'wide', 'full' ] } - /> - <Toolbar> - <IconButton - icon={ settings.markerIcon } - label="Add a marker" - onClick={ () => this.setState( { addPointVisibility: true } ) } - /> - </Toolbar> - </BlockControls> - <InspectorControls> - <PanelBody title={ __( 'Map Theme' ) }> - <MapThemePicker - value={ mapStyle } - onChange={ value => setAttributes( { mapStyle: value } ) } - options={ settings.mapStyleOptions } - /> - <ToggleControl - label={ __( 'Show street names' ) } - checked={ mapDetails } - onChange={ value => setAttributes( { mapDetails: value } ) } - /> - </PanelBody> - <PanelColorSettings - title={ __( 'Colors' ) } - initialOpen={ true } - colorSettings={ [ - { - value: markerColor, - onChange: value => setAttributes( { markerColor: value } ), - label: 'Marker Color', - }, - ] } - /> - { points.length ? ( - <PanelBody title={ __( 'Markers' ) } initialOpen={ false }> - <Locations - points={ points } - onChange={ value => { - setAttributes( { points: value } ); - } } - /> - </PanelBody> - ) : null } - <PanelBody title={ __( 'Mapbox Access Token' ) } initialOpen={ false }> - <TextControl - label={ __( 'Mapbox Access Token' ) } - value={ apiKeyControl } - onChange={ value => this.setState( { apiKeyControl: value } ) } - /> - <ButtonGroup> - <Button type="button" onClick={ this.updateAPIKey } isDefault> - { __( 'Update Token' ) } - </Button> - <Button type="button" onClick={ this.removeAPIKey } isDefault> - { __( 'Remove Token' ) } - </Button> - </ButtonGroup> - </PanelBody> - </InspectorControls> - </Fragment> - ); - const placholderAPIStateLoading = ( - <Placeholder icon={ settings.icon }> - <Spinner /> - </Placeholder> - ); - const placeholderAPIStateFailure = ( - <Placeholder icon={ settings.icon } label={ __( 'Map' ) } notices={ notices }> - <Fragment> - <div className="components-placeholder__instructions"> - { __( 'To use the map block, you need an Access Token.' ) } - <br /> - <ExternalLink href="https://www.mapbox.com"> - { __( 'Create an account or log in to Mapbox.' ) } - </ExternalLink> - <br /> - { __( - 'Locate and copy the default access token. Then, paste it into the field below.' - ) } - </div> - <TextControl - className="wp-block-jetpack-map-components-text-control-api-key" - disabled={ apiRequestOutstanding } - placeholder={ __( 'Paste Token Here' ) } - value={ apiKeyControl } - onChange={ this.updateAPIKeyControl } - /> - <Button - className="wp-block-jetpack-map-components-text-control-api-key-submit" - isLarge - disabled={ apiRequestOutstanding || ! apiKeyControl || apiKeyControl.length < 1 } - onClick={ this.updateAPIKey } - > - { __( 'Set Token' ) } - </Button> - </Fragment> - </Placeholder> - ); - const placeholderAPIStateSuccess = ( - <Fragment> - { inspectorControls } - <div className={ className }> - <Map - ref={ this.mapRef } - mapStyle={ mapStyle } - mapDetails={ mapDetails } - points={ points } - zoom={ zoom } - mapCenter={ mapCenter } - markerColor={ markerColor } - onSetZoom={ value => { - setAttributes( { zoom: value } ); - } } - admin={ true } - apiKey={ apiKey } - onSetPoints={ value => setAttributes( { points: value } ) } - onMapLoaded={ () => this.setState( { addPointVisibility: true } ) } - onMarkerClick={ () => this.setState( { addPointVisibility: false } ) } - onError={ this.onError } - > - { addPointVisibility && ( - <AddPoint - onAddPoint={ this.addPoint } - onClose={ () => this.setState( { addPointVisibility: false } ) } - apiKey={ apiKey } - onError={ this.onError } - tagName="AddPoint" - /> - ) } - </Map> - </div> - </Fragment> - ); - return ( - <Fragment> - { noticeUI } - { apiState === API_STATE_LOADING && placholderAPIStateLoading } - { apiState === API_STATE_FAILURE && placeholderAPIStateFailure } - { apiState === API_STATE_SUCCESS && placeholderAPIStateSuccess } - </Fragment> - ); - } -} - -export default withNotices( MapEdit ); diff --git a/extensions/blocks/map/editor.js b/extensions/blocks/map/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/map/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/map/editor.scss b/extensions/blocks/map/editor.scss deleted file mode 100644 index ab66d12b53279..0000000000000 --- a/extensions/blocks/map/editor.scss +++ /dev/null @@ -1,28 +0,0 @@ - -.wp-block-jetpack-map__delete-btn { - padding: 0; - svg { - margin-right: 0.4em; - } -} -.wp-block-jetpack-map-components-text-control-api-key { - margin-right: 4px; - &.components-base-control .components-base-control__field { - margin-bottom: 0; - } -} -.wp-block-jetpack-map-components-text-control-api-key-submit.is-large { - height: 31px; -} -.wp-block-jetpack-map-components-text-control-api-key-submit:disabled { - opacity: 1; -} -.wp-block[data-type='jetpack/map'] { - .components-placeholder__label { - svg { - fill: currentColor; - margin-right: 6px; - margin-right: 1ch; - } - } -} diff --git a/extensions/blocks/map/index.js b/extensions/blocks/map/index.js deleted file mode 100644 index 2e66caaebd597..0000000000000 --- a/extensions/blocks/map/index.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Internal dependencies - */ -import { settings as mapSettings } from './settings.js'; -import edit from './edit'; -import save from './save'; -import './style.scss'; -import './editor.scss'; - -export const { name } = mapSettings; - -export const settings = { - title: mapSettings.title, - icon: mapSettings.icon, - category: mapSettings.category, - keywords: mapSettings.keywords, - description: mapSettings.description, - attributes: mapSettings.attributes, - supports: mapSettings.supports, - getEditWrapperProps( attributes ) { - const { align } = attributes; - if ( -1 !== mapSettings.validAlignments.indexOf( align ) ) { - return { 'data-align': align }; - } - }, - edit, - save, -}; diff --git a/extensions/blocks/map/info-window/index.js b/extensions/blocks/map/info-window/index.js deleted file mode 100644 index f469efad91c8f..0000000000000 --- a/extensions/blocks/map/info-window/index.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * External dependencies - */ - -import { Component, createPortal } from '@wordpress/element'; - -export class InfoWindow extends Component { - componentDidMount() { - const { mapboxgl } = this.props; - this.el = document.createElement( 'DIV' ); - this.infowindow = new mapboxgl.Popup( { - closeButton: true, - closeOnClick: false, - offset: { - left: [ 0, 0 ], - top: [ 0, 5 ], - right: [ 0, 0 ], - bottom: [ 0, -40 ], - }, - } ); - this.infowindow.setDOMContent( this.el ); - this.infowindow.on( 'close', this.closeClick ); - } - componentDidUpdate( prevProps ) { - if ( this.props.activeMarker !== prevProps.activeMarker ) { - this.props.activeMarker ? this.openWindow() : this.closeWindow(); - } - } - render() { - // Use React portal to render components directly into the Mapbox info window. - return this.el ? createPortal( this.props.children, this.el ) : null; - } - closeClick = () => { - this.props.unsetActiveMarker(); - }; - openWindow() { - const { map, activeMarker } = this.props; - this.infowindow.setLngLat( activeMarker.getPoint() ).addTo( map ); - } - closeWindow() { - this.infowindow.remove(); - } -} - -InfoWindow.defaultProps = { - unsetActiveMarker: () => {}, - activeMarker: null, - map: null, - mapboxgl: null, -}; - -export default InfoWindow; diff --git a/extensions/blocks/map/location-search/index.js b/extensions/blocks/map/location-search/index.js deleted file mode 100644 index 43c2d9edb0d2e..0000000000000 --- a/extensions/blocks/map/location-search/index.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * External dependencies - */ -import { Component, createRef } from '@wordpress/element'; -import { BaseControl, TextControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import Lookup from '../lookup'; -import { __ } from '../../../utils/i18n'; - -const placeholderText = __( 'Add a marker…' ); - -export class LocationSearch extends Component { - constructor() { - super( ...arguments ); - - this.textRef = createRef(); - this.containerRef = createRef(); - this.state = { - isEmpty: true, - }; - this.autocompleter = { - name: 'placeSearch', - options: this.search, - isDebounced: true, - getOptionLabel: option => <span>{ option.place_name }</span>, - getOptionKeywords: option => [ option.place_name ], - getOptionCompletion: this.getOptionCompletion, - }; - } - componentDidMount() { - setTimeout( () => { - this.containerRef.current.querySelector( 'input' ).focus(); - }, 50 ); - } - getOptionCompletion = option => { - const { value } = option; - const point = { - placeTitle: value.text, - title: value.text, - caption: value.place_name, - id: value.id, - coordinates: { - longitude: value.geometry.coordinates[ 0 ], - latitude: value.geometry.coordinates[ 1 ], - }, - }; - this.props.onAddPoint( point ); - return value.text; - }; - - search = value => { - const { apiKey, onError } = this.props; - const url = - 'https://api.mapbox.com/geocoding/v5/mapbox.places/' + - encodeURI( value ) + - '.json?access_token=' + - apiKey; - return new Promise( function( resolve, reject ) { - const xhr = new XMLHttpRequest(); - xhr.open( 'GET', url ); - xhr.onload = function() { - if ( xhr.status === 200 ) { - const res = JSON.parse( xhr.responseText ); - resolve( res.features ); - } else { - const res = JSON.parse( xhr.responseText ); - onError( res.statusText, res.responseJSON.message ); - reject( new Error( 'Mapbox Places Error' ) ); - } - }; - xhr.send(); - } ); - }; - onReset = () => { - this.textRef.current.value = null; - }; - render() { - const { label } = this.props; - return ( - <div ref={ this.containerRef }> - <BaseControl label={ label } className="components-location-search"> - <Lookup completer={ this.autocompleter } onReset={ this.onReset }> - { ( { isExpanded, listBoxId, activeId, onChange, onKeyDown } ) => ( - <TextControl - placeholder={ placeholderText } - ref={ this.textRef } - onChange={ onChange } - aria-expanded={ isExpanded } - aria-owns={ listBoxId } - aria-activedescendant={ activeId } - onKeyDown={ onKeyDown } - /> - ) } - </Lookup> - </BaseControl> - </div> - ); - } -} - -LocationSearch.defaultProps = { - onError: () => {}, -}; - -export default LocationSearch; diff --git a/extensions/blocks/map/locations/index.js b/extensions/blocks/map/locations/index.js deleted file mode 100644 index 80385891a2656..0000000000000 --- a/extensions/blocks/map/locations/index.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * External dependencies - */ -import { - Button, - Dashicon, - Panel, - PanelBody, - TextareaControl, - TextControl, -} from '@wordpress/components'; -import { Component } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import './style.scss'; - -export class Locations extends Component { - constructor() { - super( ...arguments ); - this.state = { - selectedCell: null, - }; - } - - onDeletePoint = e => { - const index = parseInt( e.target.getAttribute( 'data-id' ) ); - const { points, onChange } = this.props; - - const newPoints = points.slice( 0 ); - newPoints.splice( index, 1 ); - onChange( newPoints ); - }; - - setMarkerField( field, value, index ) { - const { points, onChange } = this.props; - - const newPoints = points.slice( 0 ); - newPoints[ index ][ field ] = value; - onChange( newPoints ); - } - - render() { - const { points } = this.props; - const rows = points.map( ( point, index ) => ( - <PanelBody title={ point.placeTitle } key={ point.id } initialOpen={ false }> - <TextControl - label="Marker Title" - value={ point.title } - onChange={ title => this.setMarkerField( 'title', title, index ) } - /> - <TextareaControl - label="Marker Caption" - value={ point.caption } - rows="3" - onChange={ caption => this.setMarkerField( 'caption', caption, index ) } - /> - <Button - data-id={ index } - onClick={ this.onDeletePoint } - className="component__locations__delete-btn" - > - <Dashicon icon="trash" size="15" /> Delete Marker - </Button> - </PanelBody> - ) ); - return ( - <div className="component__locations"> - <Panel className="component__locations__panel">{ rows }</Panel> - </div> - ); - } -} - -Locations.defaultProps = { - points: Object.freeze( [] ), - onChange: () => {}, -}; - -export default Locations; diff --git a/extensions/blocks/map/locations/style.scss b/extensions/blocks/map/locations/style.scss deleted file mode 100644 index 73f5e8b51d2e1..0000000000000 --- a/extensions/blocks/map/locations/style.scss +++ /dev/null @@ -1,27 +0,0 @@ - -.component__locations__panel { - .edit-post-settings-sidebar__panel-block & { - margin-bottom: 1em; - &:empty { - display: none; - } - .components-panel__body:first-child { - border-top: none; - } - .components-panel__body, - .components-panel__body:first-child, - .components-panel__body:last-child { - max-width: 100%; - margin: 0; - } - .components-panel__body button { - padding-right: 40px; - } - } -} -.component__locations__delete-btn { - padding: 0; - svg { - margin-right: 0.4em; - } -} diff --git a/extensions/blocks/map/lookup/index.js b/extensions/blocks/map/lookup/index.js deleted file mode 100644 index e4ded4d691fee..0000000000000 --- a/extensions/blocks/map/lookup/index.js +++ /dev/null @@ -1,234 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { Button, Popover, withFocusOutside, withSpokenMessages } from '@wordpress/components'; -import { Component } from '@wordpress/element'; -import { debounce, map } from 'lodash'; -import { ENTER, ESCAPE, UP, DOWN, LEFT, RIGHT } from '@wordpress/keycodes'; -import { sprintf } from '@wordpress/i18n'; -import { withInstanceId, compose } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import { __, _n } from '../../../utils/i18n'; - -function filterOptions( options = [], maxResults = 10 ) { - const filtered = []; - for ( let i = 0; i < options.length; i++ ) { - const option = options[ i ]; - - // Merge label into keywords - let { keywords = [] } = option; - if ( 'string' === typeof option.label ) { - keywords = [ ...keywords, option.label ]; - } - - filtered.push( option ); - - // Abort early if max reached - if ( filtered.length === maxResults ) { - break; - } - } - - return filtered; -} - -export class Lookup extends Component { - static getInitialState() { - return { - selectedIndex: 0, - query: undefined, - filteredOptions: [], - isOpen: false, - }; - } - - constructor() { - super( ...arguments ); - this.debouncedLoadOptions = debounce( this.loadOptions, 250 ); - this.state = this.constructor.getInitialState(); - } - - componentWillUnmount() { - this.debouncedLoadOptions.cancel(); - } - - select = option => { - const { completer } = this.props; - const getOptionCompletion = completer.getOptionCompletion || {}; - getOptionCompletion( option ); - this.reset(); - }; - - reset = () => { - this.setState( this.constructor.getInitialState() ); - }; - - handleFocusOutside() { - this.reset(); - } - - loadOptions( completer, query ) { - const { options } = completer; - const promise = ( this.activePromise = Promise.resolve( - typeof options === 'function' ? options( query ) : options - ).then( optionsData => { - if ( promise !== this.activePromise ) { - // Another promise has become active since this one was asked to resolve, so do nothing, - // or else we might end triggering a race condition updating the state. - return; - } - const keyedOptions = optionsData.map( ( optionData, optionIndex ) => ( { - key: `${ optionIndex }`, - value: optionData, - label: completer.getOptionLabel( optionData ), - keywords: completer.getOptionKeywords ? completer.getOptionKeywords( optionData ) : [], - } ) ); - - const filteredOptions = filterOptions( keyedOptions ); - const selectedIndex = - filteredOptions.length === this.state.filteredOptions.length ? this.state.selectedIndex : 0; - this.setState( { - [ 'options' ]: keyedOptions, - filteredOptions, - selectedIndex, - isOpen: filteredOptions.length > 0, - } ); - this.announce( filteredOptions ); - } ) ); - } - - onChange = query => { - const { completer } = this.props; - const { options } = this.state; - - if ( ! query ) { - this.reset(); - return; - } - - if ( completer ) { - if ( completer.isDebounced ) { - this.debouncedLoadOptions( completer, query ); - } else { - this.loadOptions( completer, query ); - } - } - - const filteredOptions = completer ? filterOptions( options ) : []; - if ( completer ) { - this.setState( { selectedIndex: 0, filteredOptions, query } ); - } - }; - - onKeyDown = event => { - const { isOpen, selectedIndex, filteredOptions } = this.state; - if ( ! isOpen ) { - return; - } - let nextSelectedIndex; - switch ( event.keyCode ) { - case UP: - nextSelectedIndex = ( selectedIndex === 0 ? filteredOptions.length : selectedIndex ) - 1; - this.setState( { selectedIndex: nextSelectedIndex } ); - break; - - case DOWN: - nextSelectedIndex = ( selectedIndex + 1 ) % filteredOptions.length; - this.setState( { selectedIndex: nextSelectedIndex } ); - break; - - case ENTER: - this.select( filteredOptions[ selectedIndex ] ); - break; - - case LEFT: - case RIGHT: - case ESCAPE: - this.reset(); - return; - - default: - return; - } - - // Any handled keycode should prevent original behavior. This relies on - // the early return in the default case. - event.preventDefault(); - event.stopPropagation(); - }; - announce( filteredOptions ) { - const { debouncedSpeak } = this.props; - if ( ! debouncedSpeak ) { - return; - } - if ( filteredOptions.length ) { - debouncedSpeak( - sprintf( - _n( - '%d result found, use up and down arrow keys to navigate.', - '%d results found, use up and down arrow keys to navigate.', - filteredOptions.length, - 'jetpack' - ), - filteredOptions.length - ), - 'assertive' - ); - } else { - debouncedSpeak( __( 'No results.' ), 'assertive' ); - } - } - render() { - const { onChange, onKeyDown } = this; - const { children, instanceId, completer } = this.props; - const { selectedIndex, filteredOptions } = this.state; - const { key: selectedKey = '' } = filteredOptions[ selectedIndex ] || {}; - const { className } = completer; - const isExpanded = filteredOptions.length > 0; - const listBoxId = isExpanded ? `components-autocomplete-listbox-${ instanceId }` : null; - const activeId = isExpanded - ? `components-autocomplete-item-${ instanceId }-${ selectedKey }` - : null; - return ( - <div className="components-autocomplete"> - { children( { isExpanded, listBoxId, activeId, onChange, onKeyDown } ) } - { isExpanded && ( - <Popover - focusOnMount={ false } - onClose={ this.reset } - position="top center" - className="components-autocomplete__popover" - noArrow - > - <div id={ listBoxId } role="listbox" className="components-autocomplete__results"> - { map( filteredOptions, ( option, index ) => ( - <Button - key={ option.key } - id={ `components-autocomplete-item-${ instanceId }-${ option.key }` } - role="option" - aria-selected={ index === selectedIndex } - disabled={ option.isDisabled } - className={ classnames( 'components-autocomplete__result', className, { - 'is-selected': index === selectedIndex, - } ) } - onClick={ () => this.select( option ) } - > - { option.label } - </Button> - ) ) } - </div> - </Popover> - ) } - </div> - ); - } -} -export default compose( [ - withSpokenMessages, - withInstanceId, - withFocusOutside, // this MUST be the innermost HOC as it calls handleFocusOutside -] )( Lookup ); diff --git a/extensions/blocks/map/map-marker/index.js b/extensions/blocks/map/map-marker/index.js deleted file mode 100644 index e8db9934a76e4..0000000000000 --- a/extensions/blocks/map/map-marker/index.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import './style.scss'; - -export class MapMarker extends Component { - componentDidMount() { - this.renderMarker(); - } - componentWillUnmount() { - if ( this.marker ) { - this.marker.remove(); - } - } - componentDidUpdate() { - this.renderMarker(); - } - handleClick = () => { - const { onClick } = this.props; - onClick( this ); - }; - getPoint = () => { - const { point } = this.props; - return [ point.coordinates.longitude, point.coordinates.latitude ]; - }; - renderMarker() { - const { map, point, mapboxgl, markerColor } = this.props; - const { handleClick } = this; - const mapboxPoint = [ point.coordinates.longitude, point.coordinates.latitude ]; - const el = this.marker ? this.marker.getElement() : document.createElement( 'div' ); - if ( this.marker ) { - this.marker.setLngLat( mapboxPoint ); - } else { - el.className = 'wp-block-jetpack-map-marker'; - this.marker = new mapboxgl.Marker( el ) - .setLngLat( mapboxPoint ) - .setOffset( [ 0, -19 ] ) - .addTo( map ); - - this.marker.getElement().addEventListener( 'click', handleClick ); - } - el.innerHTML = - '<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 32 38" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill-rule="evenodd"><path id="d" d="m16 38s16-11.308 16-22-7.1634-16-16-16-16 5.3076-16 16 16 22 16 22z" fill="' + - markerColor + - '" mask="url(#c)"/></g></svg>'; - } - render() { - return null; - } -} - -MapMarker.defaultProps = { - point: {}, - map: null, - markerColor: '#000000', - mapboxgl: null, - onClick: () => {}, -}; - -export default MapMarker; diff --git a/extensions/blocks/map/map-marker/style.scss b/extensions/blocks/map/map-marker/style.scss deleted file mode 100644 index 6c5a2a65a1cc8..0000000000000 --- a/extensions/blocks/map/map-marker/style.scss +++ /dev/null @@ -1,6 +0,0 @@ - -.wp-block-jetpack-map-marker { - width: 32px; - height: 38px; - opacity: 0.9; -} diff --git a/extensions/blocks/map/map-theme-picker/index.js b/extensions/blocks/map/map-theme-picker/index.js deleted file mode 100644 index a4286f1f496c7..0000000000000 --- a/extensions/blocks/map/map-theme-picker/index.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { Button, ButtonGroup } from '@wordpress/components'; -import classnames from 'classnames'; - -/** - * Internal dependencies - */ -import './style.scss'; - -export class MapThemePicker extends Component { - render() { - const { options, value, onChange, label } = this.props; - const buttons = options.map( ( option, index ) => { - const classes = classnames( - 'component__map-theme-picker__button', - 'is-theme-' + option.value, - option.value === value ? 'is-selected' : '' - ); - return ( - <Button - className={ classes } - title={ option.label } - key={ index } - onClick={ () => onChange( option.value ) } - > - { option.label } - </Button> - ); - } ); - return ( - <div className="component__map-theme-picker components-base-control"> - <label className="components-base-control__label">{ label }</label> - <ButtonGroup>{ buttons }</ButtonGroup> - </div> - ); - } -} - -MapThemePicker.defaultProps = { - label: '', - options: [], - value: null, - onChange: () => {}, -}; - -export default MapThemePicker; diff --git a/extensions/blocks/map/map-theme-picker/map-theme_black_and_white.jpg b/extensions/blocks/map/map-theme-picker/map-theme_black_and_white.jpg deleted file mode 100644 index 34cc14120423d..0000000000000 Binary files a/extensions/blocks/map/map-theme-picker/map-theme_black_and_white.jpg and /dev/null differ diff --git a/extensions/blocks/map/map-theme-picker/map-theme_default.jpg b/extensions/blocks/map/map-theme-picker/map-theme_default.jpg deleted file mode 100644 index 35505eb164be3..0000000000000 Binary files a/extensions/blocks/map/map-theme-picker/map-theme_default.jpg and /dev/null differ diff --git a/extensions/blocks/map/map-theme-picker/map-theme_satellite.jpg b/extensions/blocks/map/map-theme-picker/map-theme_satellite.jpg deleted file mode 100644 index ef6ae41708ada..0000000000000 Binary files a/extensions/blocks/map/map-theme-picker/map-theme_satellite.jpg and /dev/null differ diff --git a/extensions/blocks/map/map-theme-picker/map-theme_terrain.jpg b/extensions/blocks/map/map-theme-picker/map-theme_terrain.jpg deleted file mode 100644 index eee1a2dae1c3d..0000000000000 Binary files a/extensions/blocks/map/map-theme-picker/map-theme_terrain.jpg and /dev/null differ diff --git a/extensions/blocks/map/map-theme-picker/style.scss b/extensions/blocks/map/map-theme-picker/style.scss deleted file mode 100644 index b970fa502c818..0000000000000 --- a/extensions/blocks/map/map-theme-picker/style.scss +++ /dev/null @@ -1,35 +0,0 @@ - -.component__map-theme-picker__button { - .edit-post-settings-sidebar__panel-block & { - border: 1px solid lightgray; - border-radius: 100%; - width: 56px; - height: 56px; - margin: 2px; - text-indent: -9999px; - background-color: lightgray; - background-position: center center; - background-repeat: no-repeat; - background-size: contain; - transform: scale( 1 ); - transition: transform 0.2s ease; - &:hover { - transform: scale( 1.1 ); - } - &.is-selected { - border-color: black; - } - &.is-theme-default { - background-image: url( './map-theme_default.jpg' ); - } - &.is-theme-black_and_white { - background-image: url( './map-theme_black_and_white.jpg' ); - } - &.is-theme-satellite { - background-image: url( './map-theme_satellite.jpg' ); - } - &.is-theme-terrain { - background-image: url( './map-theme_terrain.jpg' ); - } - } -} diff --git a/extensions/blocks/map/mapbox-map-formatter/index.js b/extensions/blocks/map/mapbox-map-formatter/index.js deleted file mode 100644 index 6ec21ad855f60..0000000000000 --- a/extensions/blocks/map/mapbox-map-formatter/index.js +++ /dev/null @@ -1,22 +0,0 @@ -export function mapboxMapFormatter( mapStyle, mapDetails ) { - const style_urls = { - default: { - details: 'mapbox://styles/automattic/cjolkhmez0qdd2ro82dwog1in', - no_details: 'mapbox://styles/automattic/cjolkci3905d82soef4zlmkdo', - }, - black_and_white: { - details: 'mapbox://styles/automattic/cjolkixvv0ty42spgt2k4j434', - no_details: 'mapbox://styles/automattic/cjolkgc540tvj2spgzzoq37k4', - }, - satellite: { - details: 'mapbox://styles/mapbox/satellite-streets-v10', - no_details: 'mapbox://styles/mapbox/satellite-v9', - }, - terrain: { - details: 'mapbox://styles/automattic/cjolkf8p405fh2soet2rdt96b', - no_details: 'mapbox://styles/automattic/cjolke6fz12ys2rpbpvgl12ha', - }, - }; - const style_url = style_urls[ mapStyle ][ mapDetails ? 'details' : 'no_details' ]; - return style_url; -} diff --git a/extensions/blocks/map/save.js b/extensions/blocks/map/save.js deleted file mode 100644 index ffa82641dcd83..0000000000000 --- a/extensions/blocks/map/save.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * External dependencies - */ - -import { Component } from '@wordpress/element'; - -class MapSave extends Component { - render() { - const { attributes } = this.props; - const { align, mapStyle, mapDetails, points, zoom, mapCenter, markerColor } = attributes; - const pointsList = points.map( ( point, index ) => { - const { longitude, latitude } = point.coordinates; - const url = 'https://www.google.com/maps/search/?api=1&query=' + latitude + ',' + longitude; - return ( - <li key={ index }> - <a href={ url }>{ point.title }</a> - </li> - ); - } ); - const alignClassName = align ? `align${ align }` : null; - // All camelCase attribute names converted to snake_case data attributes - return ( - <div - className={ alignClassName } - data-map-style={ mapStyle } - data-map-details={ mapDetails } - data-points={ JSON.stringify( points ) } - data-zoom={ zoom } - data-map-center={ JSON.stringify( mapCenter ) } - data-marker-color={ markerColor } - > - { points.length > 0 && <ul>{ pointsList }</ul> } - </div> - ); - } -} - -export default MapSave; diff --git a/extensions/blocks/map/settings.js b/extensions/blocks/map/settings.js deleted file mode 100644 index ce0ceaeee2ba5..0000000000000 --- a/extensions/blocks/map/settings.js +++ /dev/null @@ -1,100 +0,0 @@ -// Disable forbidden <svg> etc. so that frontend component does not depend on @wordpress/component -/* eslint-disable react/forbid-elements */ -/** - * External dependencies - */ -import { __ } from '../../utils/i18n'; - -export const settings = { - name: 'map', - prefix: 'jetpack', - title: __( 'Map' ), - icon: ( - /* Do not use SVG components from @wordpress/component to avoid frontend bloat */ - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - role="img" - aria-hidden="true" - focusable="false" - > - <path fill="none" d="M0 0h24v24H0V0z" /> - <path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM10 5.47l4 1.4v11.66l-4-1.4V5.47zm-5 .99l3-1.01v11.7l-3 1.16V6.46zm14 11.08l-3 1.01V6.86l3-1.16v11.84z" /> - </svg> - ), - category: 'jetpack', - keywords: [ __( 'map' ), __( 'location' ) ], - description: __( 'Add an interactive map showing one or more locations.' ), - attributes: { - align: { - type: 'string', - }, - points: { - type: 'array', - default: [], - }, - mapStyle: { - type: 'string', - default: 'default', - }, - mapDetails: { - type: 'boolean', - default: true, - }, - zoom: { - type: 'integer', - default: 13, - }, - mapCenter: { - type: 'object', - default: { - longitude: -122.41941550000001, - latitude: 37.7749295, - }, - }, - markerColor: { - type: 'string', - default: 'red', - }, - }, - supports: { - html: false, - }, - mapStyleOptions: [ - { - value: 'default', - label: __( 'Basic' ), - }, - { - value: 'black_and_white', - label: __( 'Black and white' ), - }, - { - value: 'satellite', - label: __( 'Satellite' ), - }, - { - value: 'terrain', - label: __( 'Terrain' ), - }, - ], - validAlignments: [ 'center', 'wide', 'full' ], - markerIcon: ( - /* Do not use SVG components from @wordpress/component to avoid frontend bloat */ - <svg width="14" height="20" viewBox="0 0 14 20" xmlns="http://www.w3.org/2000/svg"> - <g id="Page-1" fill="none" fillRule="evenodd"> - <g id="outline-add_location-24px" transform="translate(-5 -2)"> - <polygon id="Shape" points="0 0 24 0 24 24 0 24" /> - <path - d="M12,2 C8.14,2 5,5.14 5,9 C5,14.25 12,22 12,22 C12,22 19,14.25 19,9 C19,5.14 15.86,2 12,2 Z M7,9 C7,6.24 9.24,4 12,4 C14.76,4 17,6.24 17,9 C17,11.88 14.12,16.19 12,18.88 C9.92,16.21 7,11.85 7,9 Z M13,6 L11,6 L11,8 L9,8 L9,10 L11,10 L11,12 L13,12 L13,10 L15,10 L15,8 L13,8 L13,6 Z" - id="Shape" - fill="#000" - fillRule="nonzero" - /> - </g> - </g> - </svg> - ), -}; diff --git a/extensions/blocks/map/style.scss b/extensions/blocks/map/style.scss deleted file mode 100644 index 72fc4a4abbc75..0000000000000 --- a/extensions/blocks/map/style.scss +++ /dev/null @@ -1,21 +0,0 @@ - -.wp-block-jetpack-map { - .wp-block-jetpack-map__gm-container { - width: 100%; - overflow: hidden; - background: lightgray; - min-height: 400px; - text-align: left; - } - .mapboxgl-popup { - h3 { - font-size: 1.3125em; - font-weight: 400; - margin-bottom: 0.5rem; - } - p { - margin-bottom: 0; - } - max-width: 300px; - } -} diff --git a/extensions/blocks/map/view.js b/extensions/blocks/map/view.js deleted file mode 100644 index fc825d6704185..0000000000000 --- a/extensions/blocks/map/view.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Internal dependencies - */ -import './style.scss'; -import component from './component.js'; -import { settings } from './settings.js'; -import FrontendManagement from '../../shared/frontend-management.js'; - -typeof window !== 'undefined' && - window.addEventListener( 'load', function() { - const frontendManagement = new FrontendManagement(); - // Add apiKey to attibutes so FrontendManagement knows about it. - // It is dynamically being added on the php side. - // So that it can be updated accross all the map blocks at the same time. - const apiKey = { - type: 'string', - default: '', - }; - frontendManagement.blockIterator( document, [ - { - component: component, - options: { - settings: { - ...settings, - attributes: { - ...settings.attributes, - apiKey, - }, - }, - }, - }, - ] ); - } ); diff --git a/extensions/blocks/markdown/edit.js b/extensions/blocks/markdown/edit.js deleted file mode 100644 index da938f30527d0..0000000000000 --- a/extensions/blocks/markdown/edit.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * External dependencies - */ -import { BlockControls, PlainText } from '@wordpress/editor'; -import { Component } from '@wordpress/element'; -import { compose } from '@wordpress/compose'; -import { withDispatch, withSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import MarkdownRenderer from './renderer'; -import { __ } from '../../utils/i18n'; - -/** - * Module variables - */ -const PANEL_EDITOR = 'editor'; -const PANEL_PREVIEW = 'preview'; - -class MarkdownEdit extends Component { - input = null; - - state = { - activePanel: PANEL_EDITOR, - }; - - bindInput = ref => void ( this.input = ref ); - - componentDidUpdate( prevProps ) { - if ( - prevProps.isSelected && - ! this.props.isSelected && - this.state.activePanel === PANEL_PREVIEW - ) { - this.toggleMode( PANEL_EDITOR )(); - } - if ( - ! prevProps.isSelected && - this.props.isSelected && - this.state.activePanel === PANEL_EDITOR && - this.input - ) { - this.input.focus(); - } - } - - isEmpty() { - const source = this.props.attributes.source; - return ! source || source.trim() === ''; - } - - updateSource = source => this.props.setAttributes( { source } ); - - handleKeyDown = e => { - const { attributes, removeBlock } = this.props; - const { source } = attributes; - - // Remove the block if source is empty and we're pressing the Backspace key - if ( e.keyCode === 8 && source === '' ) { - removeBlock(); - e.preventDefault(); - } - }; - - toggleMode = mode => () => this.setState( { activePanel: mode } ); - - renderToolbarButton( mode, label ) { - const { activePanel } = this.state; - - return ( - <button - className={ `components-tab-button ${ activePanel === mode ? 'is-active' : '' }` } - onClick={ this.toggleMode( mode ) } - > - <span>{ label }</span> - </button> - ); - } - - render() { - const { attributes, className, isSelected } = this.props; - const { source } = attributes; - const { activePanel } = this.state; - - if ( ! isSelected && this.isEmpty() ) { - return ( - <p className={ `${ className }__placeholder` }> - { __( 'Write your _Markdown_ **here**…' ) } - </p> - ); - } - - return ( - <div className={ className }> - <BlockControls> - <div className="components-toolbar"> - { this.renderToolbarButton( PANEL_EDITOR, __( 'Markdown' ) ) } - { this.renderToolbarButton( PANEL_PREVIEW, __( 'Preview' ) ) } - </div> - </BlockControls> - - { activePanel === PANEL_PREVIEW || ! isSelected ? ( - <MarkdownRenderer className={ `${ className }__preview` } source={ source } /> - ) : ( - <PlainText - className={ `${ className }__editor` } - onChange={ this.updateSource } - onKeyDown={ this.handleKeyDown } - aria-label={ __( 'Markdown' ) } - innerRef={ this.bindInput } - value={ source } - /> - ) } - </div> - ); - } -} - -export default compose( [ - withSelect( select => ( { - currentBlockId: select( 'core/editor' ).getSelectedBlockClientId(), - } ) ), - withDispatch( ( dispatch, { currentBlockId } ) => ( { - removeBlock: () => dispatch( 'core/editor' ).removeBlocks( currentBlockId ), - } ) ), -] )( MarkdownEdit ); diff --git a/extensions/blocks/markdown/editor.js b/extensions/blocks/markdown/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/markdown/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/markdown/editor.scss b/extensions/blocks/markdown/editor.scss deleted file mode 100644 index 66fe610e4949c..0000000000000 --- a/extensions/blocks/markdown/editor.scss +++ /dev/null @@ -1,148 +0,0 @@ -// @TODO: Replace with Gutenberg variables -$black: #000; -$dark-gray-100: #8f98a1; -$dark-gray-800: #23282d; -$editor-html-font: Menlo, Consolas, monaco, monospace; -$light-gray-200: #f3f4f5; -$light-gray-500: #e2e4e7; -$text-editor-font-size: inherit; - -.wp-block-jetpack-markdown__placeholder { - opacity: 0.62; // See https://github.com/WordPress/gutenberg/blob/c0f87a212b0ad25c18ac5bf8c2e9b1cb780f1a14/packages/editor/src/components/rich-text/style.scss#L110 - pointer-events: none; -} - -// @TODO: Remove all these specific styles when related Gutenberg core styles become more generic -.editor-block-list__block { - .wp-block-jetpack-markdown__preview { - min-height: 1.8em; - line-height: 1.8; - - & > * { - margin-top: 32px; - margin-bottom: 32px; - } - - h1, - h2, - h3 { - line-height: 1.4; - } - - h1 { - font-size: 2.44em; - } - - h2 { - font-size: 1.95em; - } - - h3 { - font-size: 1.56em; - } - - h4 { - font-size: 1.25em; - line-height: 1.5; - } - - h5 { - font-size: 1em; - } - - h6 { - font-size: 0.8em; - } - - hr { - border: none; - border-bottom: 2px solid $dark-gray-100; - margin: 2em auto; - max-width: 100px; - } - - p { - line-height: 1.8; - } - - blockquote { - border-left: 4px solid $black; - margin-left: 0; - margin-right: 0; - padding-left: 1em; - - p { - line-height: 1.5; - margin: 1em 0; - } - } - - ul, - ol { - margin-left: 1.3em; - padding-left: 1.3em; - } - - li { - p { - margin: 0; - } - } - - code, - pre { - color: $dark-gray-800; - font-family: $editor-html-font; - } - - code { - background: $light-gray-200; - border-radius: 2px; - font-size: $text-editor-font-size; - padding: 2px; - } - - pre { - border-radius: 4px; - border: 1px solid $light-gray-500; - font-size: 14px; - padding: 0.8em 1em; - - code { - background: transparent; - padding: 0; - } - } - - table { - overflow-x: auto; - border-collapse: collapse; - width: 100%; - } - - thead, - tbody, - tfoot { - width: 100%; - min-width: 240px; - } - - td, - th { - padding: 0.5em; - border: 1px solid currentColor; - } - } -} - -.wp-block-jetpack-markdown { - .wp-block-jetpack-markdown__editor { - font-family: $editor-html-font; - font-size: 14px; - - &:focus { - border-color: transparent; - box-shadow: 0 0 0 transparent; - } - } -} diff --git a/extensions/blocks/markdown/index.js b/extensions/blocks/markdown/index.js deleted file mode 100644 index be38d73e2d584..0000000000000 --- a/extensions/blocks/markdown/index.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * External dependencies - */ -import { ExternalLink, Path, Rect, SVG } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import './editor.scss'; -import edit from './edit'; -import save from './save'; -import { __ } from '../../utils/i18n'; - -export const name = 'markdown'; - -export const settings = { - title: __( 'Markdown' ), - - description: ( - <Fragment> - <p>{ __( 'Use regular characters and punctuation to style text, links, and lists.' ) }</p> - <ExternalLink href="https://en.support.wordpress.com/markdown-quick-reference/"> - { __( 'Support reference' ) } - </ExternalLink> - </Fragment> - ), - - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 208 128"> - <Rect - width="198" - height="118" - x="5" - y="5" - ry="10" - stroke="currentColor" - strokeWidth="10" - fill="none" - /> - <Path d="M30 98v-68h20l20 25 20-25h20v68h-20v-39l-20 25-20-25v39zM155 98l-30-33h20v-35h20v35h20z" /> - </SVG> - ), - - category: 'jetpack', - - keywords: [ __( 'formatting' ), __( 'syntax' ), __( 'markup' ) ], - - attributes: { - //The Markdown source is saved in the block content comments delimiter - source: { type: 'string' }, - }, - - supports: { - html: false, - }, - - edit, - - save, -}; diff --git a/extensions/blocks/markdown/renderer.js b/extensions/blocks/markdown/renderer.js deleted file mode 100644 index a77d24f24ebf5..0000000000000 --- a/extensions/blocks/markdown/renderer.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * External dependencies - */ -import MarkdownIt from 'markdown-it'; -import { RawHTML } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; - -/** - * Module variables - */ -const markdownConverter = new MarkdownIt(); -const handleLinkClick = event => { - if ( event.target.nodeName === 'A' ) { - const hasConfirmed = window.confirm( __( 'Are you sure you wish to leave this page?' ) ); - - if ( ! hasConfirmed ) { - event.preventDefault(); - } - } -}; - -export default ( { className, source = '' } ) => ( - <RawHTML className={ className } onClick={ handleLinkClick }> - { source.length ? markdownConverter.render( source ) : '' } - </RawHTML> -); diff --git a/extensions/blocks/markdown/save.js b/extensions/blocks/markdown/save.js deleted file mode 100644 index 06d08138f7604..0000000000000 --- a/extensions/blocks/markdown/save.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Internal dependencies - */ -import MarkdownRenderer from './renderer'; - -export default ( { attributes, className } ) => ( - <MarkdownRenderer className={ className } source={ attributes.source } /> -); diff --git a/extensions/blocks/publicize/connection-verify.js b/extensions/blocks/publicize/connection-verify.js deleted file mode 100644 index 92cd1f62ff583..0000000000000 --- a/extensions/blocks/publicize/connection-verify.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Publicize connections verification component. - * - * Component to create Ajax request to check - * all connections. If any connection tests failed, - * a refresh link may be provided to the user. If - * no connection tests fail, this component will - * not render anything. - */ - -/** - * External dependencies - */ -import { Button, Notice } from '@wordpress/components'; -import { Component, Fragment } from '@wordpress/element'; -import { compose } from '@wordpress/compose'; -import { withDispatch, withSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; - -class PublicizeConnectionVerify extends Component { - componentDidMount() { - this.props.refreshConnections(); - } - - /** - * Opens up popup so user can refresh connection - * - * Displays pop up with to specified URL where user - * can refresh a specific connection. - * - * @param {object} event Event instance for onClick. - */ - refreshConnectionClick = event => { - const { href, title } = event.target; - event.preventDefault(); - // open a popup window - // when it is closed, kick off the tests again - const popupWin = window.open( href, title, '' ); - const popupTimer = window.setInterval( () => { - if ( false !== popupWin.closed ) { - window.clearInterval( popupTimer ); - this.props.refreshConnections(); - } - }, 500 ); - }; - - renderRefreshableConnections() { - const { failedConnections } = this.props; - const refreshableConnections = failedConnections.filter( connection => connection.can_refresh ); - - if ( refreshableConnections.length ) { - return ( - <Notice className="jetpack-publicize-notice" isDismissible={ false } status="error"> - <p> - { __( - 'Before you hit Publish, please refresh the following connection(s) to make sure we can Publicize your post:' - ) } - </p> - { refreshableConnections.map( connection => ( - <Button - href={ connection.refresh_url } - isSmall - key={ connection.id } - onClick={ this.refreshConnectionClick } - title={ connection.refresh_text } - > - { connection.refresh_text } - </Button> - ) ) } - </Notice> - ); - } - - return null; - } - - renderNonRefreshableConnections() { - const { failedConnections } = this.props; - const nonRefreshableConnections = failedConnections.filter( - connection => ! connection.can_refresh - ); - - if ( nonRefreshableConnections.length ) { - return nonRefreshableConnections.map( connection => ( - <Notice className="jetpack-publicize-notice" isDismissible={ false } status="error"> - <p>{ connection.test_message }</p> - </Notice> - ) ); - } - - return null; - } - - render() { - return ( - <Fragment> - { this.renderRefreshableConnections() } - { this.renderNonRefreshableConnections() } - </Fragment> - ); - } -} - -export default compose( [ - withSelect( select => ( { - failedConnections: select( 'jetpack/publicize' ).getFailedConnections(), - } ) ), - withDispatch( dispatch => ( { - refreshConnections: dispatch( 'jetpack/publicize' ).refreshConnectionTestResults, - } ) ), -] )( PublicizeConnectionVerify ); diff --git a/extensions/blocks/publicize/connection.js b/extensions/blocks/publicize/connection.js deleted file mode 100644 index 3bbb810c55321..0000000000000 --- a/extensions/blocks/publicize/connection.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Publicize connection form component. - * - * Component to display connection label and a - * checkbox to enable/disable the connection for sharing. - */ - -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { Disabled, FormToggle, Notice, ExternalLink } from '@wordpress/components'; -import { withSelect } from '@wordpress/data'; -import { includes } from 'lodash'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; -import PublicizeServiceIcon from './service-icon'; -import getSiteFragment from '../../shared/get-site-fragment'; - -class PublicizeConnection extends Component { - state = { - showGooglePlusNotice: true, - }; - - /** - * Hide notice when it's removed - */ - onRemoveGooglePlusNotice = () => { - this.setState( { - showGooglePlusNotice: false, - } ); - }; - - /** - * If this is the Google+ connection, display a notice. - * - * @param {string} serviceName Name of the connnected social network. - * @returns {object} Message warning users of Google+ shutting down. - */ - maybeDisplayGooglePlusNotice = serviceName => - 'google-plus' === serviceName && - this.state.showGooglePlusNotice && ( - <Notice status="error" onRemove={ this.onRemoveGooglePlusNotice }> - { __( - 'Google+ will shut down in April 2019. You can keep posting with your existing Google+ connection through March.' - ) } - <ExternalLink - target="_blank" - href="https://www.blog.google/technology/safety-security/expediting-changes-google-plus/" - > - { __( ' Learn more' ) }. - </ExternalLink> - </Notice> - ); - - /** - * Displays a message when a connection requires reauthentication. We used this when migrating LinkedIn API usage from v1 to v2, - * since the prevous OAuth1 tokens were incompatible with OAuth2. - * - * @returns {object|?null} Notice about reauthentication - */ - maybeDisplayLinkedInNotice = () => - this.connectionNeedsReauth() && ( - <Notice className="jetpack-publicize-notice" isDismissible={ false } status="error"> - <p> - { __( - 'Your LinkedIn connection needs to be reauthenticated ' + - 'to continue working – head to Sharing to take care of it.' - ) } - </p> - <ExternalLink href={ `https://wordpress.com/sharing/${ getSiteFragment() }` }> - { __( 'Go to Sharing settings' ) } - </ExternalLink> - </Notice> - ); - - /** - * Check whether the connection needs to be reauthenticated. - * - * @returns {boolean} True if connection must be reauthenticated. - */ - connectionNeedsReauth = () => includes( this.props.mustReauthConnections, this.props.name ); - - onConnectionChange = () => { - const { id } = this.props; - this.props.toggleConnection( id ); - }; - - connectionIsFailing() { - const { failedConnections, name } = this.props; - return failedConnections.some( connection => connection.service_name === name ); - } - - render() { - const { disabled, enabled, id, label, name } = this.props; - const fieldId = 'connection-' + name + '-' + id; - // Genericon names are dash separated - const serviceName = name.replace( '_', '-' ); - - let toggle = ( - <FormToggle - id={ fieldId } - className="jetpack-publicize-connection-toggle" - checked={ enabled } - onChange={ this.onConnectionChange } - /> - ); - - if ( disabled || this.connectionIsFailing() || this.connectionNeedsReauth() ) { - toggle = <Disabled>{ toggle }</Disabled>; - } - - return ( - <li> - { this.maybeDisplayGooglePlusNotice( serviceName ) } - { this.maybeDisplayLinkedInNotice() } - <div className="publicize-jetpack-connection-container"> - <label htmlFor={ fieldId } className="jetpack-publicize-connection-label"> - <PublicizeServiceIcon serviceName={ serviceName } /> - <span className="jetpack-publicize-connection-label-copy">{ label }</span> - </label> - { toggle } - </div> - </li> - ); - } -} - -export default withSelect( select => ( { - failedConnections: select( 'jetpack/publicize' ).getFailedConnections(), - mustReauthConnections: select( 'jetpack/publicize' ).getMustReauthConnections(), -} ) )( PublicizeConnection ); diff --git a/extensions/blocks/publicize/editor.js b/extensions/blocks/publicize/editor.js deleted file mode 100644 index e2811b0c0d125..0000000000000 --- a/extensions/blocks/publicize/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import { name, settings } from '.'; -import registerJetpackPlugin from '../../utils/register-jetpack-plugin'; - -registerJetpackPlugin( name, settings ); diff --git a/extensions/blocks/publicize/editor.scss b/extensions/blocks/publicize/editor.scss deleted file mode 100644 index ba40bef74b909..0000000000000 --- a/extensions/blocks/publicize/editor.scss +++ /dev/null @@ -1,105 +0,0 @@ - -// @TODO: Replace with Gutenberg variables -$dark-gray-500: #555d66; - -.jetpack-publicize-message-box { - background-color: #edeff0; - border-radius: 4px; -} - -.jetpack-publicize-message-box textarea { - width: 100%; -} - -.jetpack-publicize-character-count { - padding-bottom: 5px; - padding-left: 5px; -} - -.jetpack-publicize__connections-list { - list-style-type: none; - margin: 13px 0; -} - -.publicize-jetpack-connection-container { - display: flex; -} - -.jetpack-publicize-gutenberg-social-icon { - fill: $dark-gray-500; - margin-right: 5px; - - &.is-facebook { - fill: var( --color-facebook ); - } - &.is-twitter { - fill: var( --color-twitter ); - } - &.is-linkedin { - fill: var( --color-linkedin ); - } - &.is-tumblr { - fill: var( --color-tumblr ); - } - &.is-google-plus { - fill: var( --color-gplus ); - } -} - -.jetpack-publicize-connection-label { - flex: 1; - margin-right: 5px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - .jetpack-publicize-gutenberg-social-icon, - .jetpack-publicize-connection-label-copy { - display: inline-block; - vertical-align: middle; - } -} - -.jetpack-publicize-connection-toggle { - margin-top: 3px; -} - -.jetpack-publicize-notice { - &.components-notice { - margin-left: 0; - margin-right: 0; - margin-bottom: 13px; - } - - .components-button + .components-button { - margin-top: 5px; - } -} - -.jetpack-publicize-message-note { - display: inline-block; - margin-bottom: 4px; - margin-top: 13px; -} - -.jetpack-publicize-add-connection-wrapper { - margin: 15px 0; -} - -.jetpack-publicize-add-connection-container { - display: flex; - - a { - cursor: pointer; - } - - span { - vertical-align: middle; - } -} - -.jetpack-publicize__connections-list { - .components-notice { - margin: 5px 0 10px; - } -} diff --git a/extensions/blocks/publicize/form-unwrapped.js b/extensions/blocks/publicize/form-unwrapped.js deleted file mode 100644 index d9640bab79102..0000000000000 --- a/extensions/blocks/publicize/form-unwrapped.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Publicize sharing form component. - * - * Displays text area and connection list to allow user - * to select connections to share to and write a custom - * sharing message. - */ - -/** - * External dependencies - */ -import classnames from 'classnames'; -import { sprintf } from '@wordpress/i18n'; -import { Component, Fragment } from '@wordpress/element'; -import { uniqueId } from 'lodash'; - -/** - * Internal dependencies - */ -import PublicizeConnection from './connection'; -import PublicizeSettingsButton from './settings-button'; -import { __, _n } from '../../utils/i18n'; - -export const MAXIMUM_MESSAGE_LENGTH = 256; - -class PublicizeFormUnwrapped extends Component { - state = { - hasEditedShareMessage: false, - }; - - fieldId = uniqueId( 'jetpack-publicize-message-field-' ); - - /** - * Check to see if form should be disabled. - * - * Checks full connection list to determine if all are disabled. - * If they all are, it returns true to disable whole form. - * - * @return {boolean} True if whole form should be disabled. - */ - isDisabled() { - return this.props.connections.every( connection => ! connection.toggleable ); - } - - getShareMessage() { - const { shareMessage, defaultShareMessage } = this.props; - return ! this.state.hasEditedShareMessage && shareMessage === '' - ? defaultShareMessage - : shareMessage; - } - - onMessageChange = event => { - const { messageChange } = this.props; - this.setState( { hasEditedShareMessage: true } ); - messageChange( event ); - }; - - render() { - const { connections, toggleConnection, refreshCallback } = this.props; - const shareMessage = this.getShareMessage(); - const charactersRemaining = MAXIMUM_MESSAGE_LENGTH - shareMessage.length; - const characterCountClass = classnames( 'jetpack-publicize-character-count', { - 'wpas-twitter-length-limit': charactersRemaining <= 0, - } ); - - return ( - <div id="publicize-form"> - <ul className="jetpack-publicize__connections-list"> - { connections.map( ( { display_name, enabled, id, service_name, toggleable } ) => ( - <PublicizeConnection - disabled={ ! toggleable } - enabled={ enabled } - key={ id } - id={ id } - label={ display_name } - name={ service_name } - toggleConnection={ toggleConnection } - /> - ) ) } - </ul> - <PublicizeSettingsButton refreshCallback={ refreshCallback } /> - { connections.some( connection => connection.enabled ) && ( - <Fragment> - <label className="jetpack-publicize-message-note" htmlFor={ this.fieldId }> - { __( 'Customize your message' ) } - </label> - <div className="jetpack-publicize-message-box"> - <textarea - id={ this.fieldId } - value={ shareMessage } - onChange={ this.onMessageChange } - disabled={ this.isDisabled() } - maxLength={ MAXIMUM_MESSAGE_LENGTH } - placeholder={ __( - "Write a message for your audience here. If you leave this blank, we'll use the post title as the message." - ) } - rows={ 4 } - /> - <div className={ characterCountClass }> - { sprintf( - _n( '%d character remaining', '%d characters remaining', charactersRemaining ), - charactersRemaining - ) } - </div> - </div> - </Fragment> - ) } - </div> - ); - } -} - -export default PublicizeFormUnwrapped; diff --git a/extensions/blocks/publicize/form.js b/extensions/blocks/publicize/form.js deleted file mode 100644 index 7785d6d8b685d..0000000000000 --- a/extensions/blocks/publicize/form.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Higher Order Publicize sharing form composition. - * - * Uses Gutenberg data API to dispatch publicize form data to - * editor post data in format to match 'publicize' field schema. - */ - -/** - * External dependencies - */ -import get from 'lodash/get'; -import { compose } from '@wordpress/compose'; -import { withSelect, withDispatch } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import PublicizeFormUnwrapped, { MAXIMUM_MESSAGE_LENGTH } from './form-unwrapped'; - -const PublicizeForm = compose( [ - withSelect( select => { - const meta = select( 'core/editor' ).getEditedPostAttribute( 'meta' ); - const postTitle = select( 'core/editor' ).getEditedPostAttribute( 'title' ); - const message = get( meta, [ 'jetpack_publicize_message' ], '' ); - - return { - connections: select( 'core/editor' ).getEditedPostAttribute( - 'jetpack_publicize_connections' - ), - defaultShareMessage: postTitle.substr( 0, MAXIMUM_MESSAGE_LENGTH ), - shareMessage: message.substr( 0, MAXIMUM_MESSAGE_LENGTH ), - }; - } ), - withDispatch( ( dispatch, { connections } ) => ( { - /** - * Toggle connection enable/disable state based on checkbox. - * - * Saves enable/disable value to connections property in editor - * in field 'jetpack_publicize_connections'. - * - * @param {number} id ID of the connection being enabled/disabled - */ - toggleConnection( id ) { - const newConnections = connections.map( connection => ( { - ...connection, - enabled: connection.id === id ? ! connection.enabled : connection.enabled, - } ) ); - - dispatch( 'core/editor' ).editPost( { - jetpack_publicize_connections: newConnections, - } ); - }, - - /** - * Handler for when sharing message is edited. - * - * Saves edited message to state and to the editor - * in field 'jetpack_publicize_message'. - * - * @param {object} event Change event data from textarea element. - */ - messageChange( event ) { - dispatch( 'core/editor' ).editPost( { - meta: { - jetpack_publicize_message: event.target.value, - }, - } ); - }, - } ) ), -] )( PublicizeFormUnwrapped ); - -export default PublicizeForm; diff --git a/extensions/blocks/publicize/index.js b/extensions/blocks/publicize/index.js deleted file mode 100644 index 8f29e933cf42b..0000000000000 --- a/extensions/blocks/publicize/index.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Top-level Publicize plugin for Gutenberg editor. - * - * Hooks into Gutenberg's PluginPrePublishPanel - * to display Jetpack's Publicize UI in the pre-publish flow. - * - * It also hooks into our dedicated Jetpack plugin sidebar and - * displays the Publicize UI there. - */ - -/** - * External dependencies - */ -import { PanelBody } from '@wordpress/components'; -import { PluginPrePublishPanel } from '@wordpress/edit-post'; -import { PostTypeSupportCheck } from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import './editor.scss'; -import './store'; -import JetpackPluginSidebar from '../../shared/jetpack-plugin-sidebar'; -import PublicizePanel from './panel'; -import { __ } from '../../utils/i18n'; - -export const name = 'publicize'; - -export const settings = { - render: () => ( - <PostTypeSupportCheck supportKeys="publicize"> - <JetpackPluginSidebar> - <PanelBody title={ __( 'Share this post' ) }> - <PublicizePanel /> - </PanelBody> - </JetpackPluginSidebar> - <PluginPrePublishPanel - initialOpen - id="publicize-title" - title={ - <span id="publicize-defaults" key="publicize-title-span"> - { __( 'Share this post' ) } - </span> - } - > - <PublicizePanel /> - </PluginPrePublishPanel> - </PostTypeSupportCheck> - ), -}; diff --git a/extensions/blocks/publicize/panel.js b/extensions/blocks/publicize/panel.js deleted file mode 100644 index 55cb3989599f0..0000000000000 --- a/extensions/blocks/publicize/panel.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Publicize sharing panel component. - * - * Displays Publicize notifications if no - * services are connected or displays form if - * services are connected. - */ - -/** - * External dependencies - */ -import { compose } from '@wordpress/compose'; -import { Fragment } from '@wordpress/element'; -import { withDispatch, withSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import PublicizeConnectionVerify from './connection-verify'; -import PublicizeForm from './form'; -import PublicizeSettingsButton from './settings-button'; -import { __ } from '../../utils/i18n'; - -const PublicizePanel = ( { connections, refreshConnections } ) => ( - <Fragment> - { connections && connections.some( connection => connection.enabled ) && ( - <PublicizeConnectionVerify /> - ) } - <div>{ __( "Connect and select the accounts where you'd like to share your post." ) }</div> - { connections && connections.length > 0 && ( - <PublicizeForm refreshCallback={ refreshConnections } /> - ) } - { connections && 0 === connections.length && ( - <PublicizeSettingsButton - className="jetpack-publicize-add-connection-wrapper" - refreshCallback={ refreshConnections } - /> - ) } - </Fragment> -); - -export default compose( [ - withSelect( select => ( { - connections: select( 'core/editor' ).getEditedPostAttribute( 'jetpack_publicize_connections' ), - } ) ), - withDispatch( dispatch => ( { - refreshConnections: dispatch( 'core/editor' ).refreshPost, - } ) ), -] )( PublicizePanel ); diff --git a/extensions/blocks/publicize/service-icon.js b/extensions/blocks/publicize/service-icon.js deleted file mode 100644 index ff15c7cacd711..0000000000000 --- a/extensions/blocks/publicize/service-icon.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * External dependencies - */ -import { G, Icon, Path, Rect, SVG } from '@wordpress/components'; - -/** - * Module variables - */ -// @TODO: Import those from https://github.com/Automattic/social-logos when that's possible. -// Currently we can't directly import icons from there, because all icons are bundled in a single file. -// This means that to import an icon from there, we'll need to add the entire bundle with all icons to our build. -// In the future we'd want to export each icon in that repo separately, and then import them separately here. -const FacebookIcon = ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Rect x="0" fill="none" width="24" height="24" /> - <G> - <Path d="M20.007 3H3.993C3.445 3 3 3.445 3 3.993v16.013c0 .55.445.994.993.994h8.62v-6.97H10.27V11.31h2.346V9.31c0-2.325 1.42-3.59 3.494-3.59.993 0 1.847.073 2.096.106v2.43h-1.438c-1.128 0-1.346.537-1.346 1.324v1.734h2.69l-.35 2.717h-2.34V21h4.587c.548 0 .993-.445.993-.993V3.993c0-.548-.445-.993-.993-.993z" /> - </G> - </SVG> -); -const TwitterIcon = ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Rect x="0" fill="none" width="24" height="24" /> - <G> - <Path d="M22.23 5.924c-.736.326-1.527.547-2.357.646.847-.508 1.498-1.312 1.804-2.27-.793.47-1.67.812-2.606.996C18.325 4.498 17.258 4 16.078 4c-2.266 0-4.103 1.837-4.103 4.103 0 .322.036.635.106.935-3.41-.17-6.433-1.804-8.457-4.287-.353.607-.556 1.312-.556 2.064 0 1.424.724 2.68 1.825 3.415-.673-.022-1.305-.207-1.86-.514v.052c0 1.988 1.415 3.647 3.293 4.023-.344.095-.707.145-1.08.145-.265 0-.522-.026-.773-.074.522 1.63 2.038 2.817 3.833 2.85-1.404 1.1-3.174 1.757-5.096 1.757-.332 0-.66-.02-.98-.057 1.816 1.164 3.973 1.843 6.29 1.843 7.547 0 11.675-6.252 11.675-11.675 0-.178-.004-.355-.012-.53.802-.578 1.497-1.3 2.047-2.124z" /> - </G> - </SVG> -); -const LinkedinIcon = ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Rect x="0" fill="none" width="24" height="24" /> - <G> - <Path d="M19.7 3H4.3C3.582 3 3 3.582 3 4.3v15.4c0 .718.582 1.3 1.3 1.3h15.4c.718 0 1.3-.582 1.3-1.3V4.3c0-.718-.582-1.3-1.3-1.3zM8.34 18.338H5.666v-8.59H8.34v8.59zM7.003 8.574c-.857 0-1.55-.694-1.55-1.548 0-.855.692-1.548 1.55-1.548.854 0 1.547.694 1.547 1.548 0 .855-.692 1.548-1.546 1.548zm11.335 9.764h-2.67V14.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.6 1.086-1.6 2.206v4.248h-2.668v-8.59h2.56v1.174h.036c.357-.675 1.228-1.387 2.527-1.387 2.703 0 3.203 1.78 3.203 4.092v4.71z" /> - </G> - </SVG> -); -const TumblrIcon = ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Rect x="0" fill="none" width="24" height="24" /> - <G> - <Path d="M19 3H5c-1.105 0-2 .895-2 2v14c0 1.105.895 2 2 2h14c1.105 0 2-.895 2-2V5c0-1.105-.895-2-2-2zm-5.57 14.265c-2.445.042-3.37-1.742-3.37-2.998V10.6H8.922V9.15c1.703-.615 2.113-2.15 2.21-3.026.006-.06.053-.084.08-.084h1.645V8.9h2.246v1.7H12.85v3.495c.008.476.182 1.13 1.08 1.107.3-.008.698-.094.907-.194l.54 1.6c-.205.297-1.12.642-1.946.657z" /> - </G> - </SVG> -); -const GooglePlusIcon = ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Rect x="0" fill="none" width="24" height="24" /> - <G> - <Path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm-1.92 14.05c-2.235 0-4.05-1.814-4.05-4.05s1.815-4.05 4.05-4.05c1.095 0 2.01.4 2.71 1.057l-1.15 1.118c-.292-.275-.802-.6-1.56-.6-1.34 0-2.433 1.115-2.433 2.48s1.094 2.48 2.434 2.48c1.552 0 2.123-1.074 2.228-1.71h-2.232v-1.51h3.79c.058.255.102.494.102.83 0 2.312-1.55 3.956-3.887 3.956zm8.92-3.3h-1.25V14h-1.5v-1.25H15v-1.5h1.25V10h1.5v1.25H19v1.5z" /> - </G> - </SVG> -); - -export default ( { serviceName } ) => { - const defaultProps = { - className: `jetpack-publicize-gutenberg-social-icon is-${ serviceName }`, - size: 24, - }; - - switch ( serviceName ) { - case 'facebook': - return <Icon icon={ FacebookIcon } { ...defaultProps } />; - case 'twitter': - return <Icon icon={ TwitterIcon } { ...defaultProps } />; - case 'linkedin': - return <Icon icon={ LinkedinIcon } { ...defaultProps } />; - case 'tumblr': - return <Icon icon={ TumblrIcon } { ...defaultProps } />; - case 'google-plus': - return <Icon icon={ GooglePlusIcon } { ...defaultProps } />; - } - - return null; -}; diff --git a/extensions/blocks/publicize/settings-button.js b/extensions/blocks/publicize/settings-button.js deleted file mode 100644 index 7f97d457c00d6..0000000000000 --- a/extensions/blocks/publicize/settings-button.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Publicize settings button component. - * - * Component which allows user to click to open settings - * in a new window/tab. If window/tab is closed, then - * connections will be automatically refreshed. - */ - -/** - * External dependencies - */ -import classnames from 'classnames'; -import { Component } from '@wordpress/element'; -import { ExternalLink } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; -import getSiteFragment from '../../shared/get-site-fragment'; - -class PublicizeSettingsButton extends Component { - getButtonLink() { - const siteFragment = getSiteFragment(); - - // If running in WP.com wp-admin or in Calypso, we redirect to Calypso sharing settings. - if ( siteFragment ) { - return `https://wordpress.com/sharing/${ siteFragment }`; - } - - // If running in WordPress.org wp-admin we redirect to Sharing settings in wp-admin. - return 'options-general.php?page=sharing&publicize_popup=true'; - } - - /** - * Opens up popup so user can view/modify connections - * - * @param {object} event Event instance for onClick. - */ - settingsClick = event => { - const href = this.getButtonLink(); - const { refreshCallback } = this.props; - event.preventDefault(); - /** - * Open a popup window, and - * when it is closed, refresh connections - */ - const popupWin = window.open( href, '', '' ); - const popupTimer = window.setInterval( () => { - if ( false !== popupWin.closed ) { - window.clearInterval( popupTimer ); - refreshCallback(); - } - }, 500 ); - }; - - render() { - const className = classnames( - 'jetpack-publicize-add-connection-container', - this.props.className - ); - - return ( - <div className={ className }> - <ExternalLink onClick={ this.settingsClick }>{ __( 'Connect an account' ) }</ExternalLink> - </div> - ); - } -} - -export default PublicizeSettingsButton; diff --git a/extensions/blocks/publicize/store/actions.js b/extensions/blocks/publicize/store/actions.js deleted file mode 100644 index e5b7169428a85..0000000000000 --- a/extensions/blocks/publicize/store/actions.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Returns an action object used in signalling that - * we're setting the Publicize connection test results. - * - * @param {Array} results Connection test results. - * - * @return {Object} Action object. - */ -export function setConnectionTestResults( results ) { - return { - type: 'SET_CONNECTION_TEST_RESULTS', - results, - }; -} - -/** - * Returns an action object used in signalling that - * we're refreshing the Publicize connection test results. - * - * @return {Object} Action object. - */ -export function refreshConnectionTestResults() { - return { - type: 'REFRESH_CONNECTION_TEST_RESULTS', - }; -} - -/** - * Returns an action object used in signalling that - * we're initiating a fetch request to the REST API. - * - * @param {String} path API endpoint path. - * - * @return {Object} Action object. - */ -export function fetchFromAPI( path ) { - return { - type: 'FETCH_FROM_API', - path, - }; -} diff --git a/extensions/blocks/publicize/store/controls.js b/extensions/blocks/publicize/store/controls.js deleted file mode 100644 index afe6eccdf59e4..0000000000000 --- a/extensions/blocks/publicize/store/controls.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * External dependencies - */ -import apiFetch from '@wordpress/api-fetch'; - -/** - * Trigger an API Fetch request. - * - * @param {Object} action Action Object. - * - * @return {Promise} Fetch request promise. - */ -const fetchFromApi = ( { path } ) => { - return apiFetch( { path } ); -}; - -export default { - FETCH_FROM_API: fetchFromApi, -}; diff --git a/extensions/blocks/publicize/store/effects.js b/extensions/blocks/publicize/store/effects.js deleted file mode 100644 index 594c8b72a6d09..0000000000000 --- a/extensions/blocks/publicize/store/effects.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * External dependencies - */ -import apiFetch from '@wordpress/api-fetch'; - -/** - * Internal dependencies - */ -import { setConnectionTestResults } from './actions'; - -/** - * Effect handler which will refresh the connection test results. - * - * @param {Object} action Action which had initiated the effect handler. - * @param {Object} store Store instance. - * - * @return {Object} Refresh connection test results action. - */ -export async function refreshConnectionTestResults( action, store ) { - const { dispatch } = store; - - try { - const results = await apiFetch( { path: '/wpcom/v2/publicize/connection-test-results' } ); - return dispatch( setConnectionTestResults( results ) ); - } catch ( error ) { - // Refreshing connections failed - } -} - -export default { - REFRESH_CONNECTION_TEST_RESULTS: refreshConnectionTestResults, -}; diff --git a/extensions/blocks/publicize/store/index.js b/extensions/blocks/publicize/store/index.js deleted file mode 100644 index 337167bc97caf..0000000000000 --- a/extensions/blocks/publicize/store/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * External dependencies - */ -import { registerStore } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import * as actions from './actions'; -import * as selectors from './selectors'; -import applyMiddlewares from './middlewares'; -import controls from './controls'; -import reducer from './reducer'; - -const store = registerStore( 'jetpack/publicize', { - actions, - controls, - reducer, - selectors, -} ); - -applyMiddlewares( store ); - -export default store; diff --git a/extensions/blocks/publicize/store/middlewares.js b/extensions/blocks/publicize/store/middlewares.js deleted file mode 100644 index 1403b8084f1dc..0000000000000 --- a/extensions/blocks/publicize/store/middlewares.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * External dependencies - */ -import refx from 'refx'; -import { flowRight } from 'lodash'; - -/** - * Internal dependencies - */ -import effects from './effects'; - -/** - * Applies the custom middlewares used specifically in the Publicize extension. - * - * @param {Object} store Store Object. - * - * @return {Object} Update Store Object. - */ -export default function applyMiddlewares( store ) { - const middlewares = [ refx( effects ) ]; - - let enhancedDispatch = () => { - throw new Error( - 'Dispatching while constructing your middleware is not allowed. ' + - 'Other middleware would not be applied to this dispatch.' - ); - }; - let chain = []; - - const middlewareAPI = { - getState: store.getState, - dispatch: ( ...args ) => enhancedDispatch( ...args ), - }; - chain = middlewares.map( middleware => middleware( middlewareAPI ) ); - enhancedDispatch = flowRight( ...chain )( store.dispatch ); - - store.dispatch = enhancedDispatch; - - return store; -} diff --git a/extensions/blocks/publicize/store/reducer.js b/extensions/blocks/publicize/store/reducer.js deleted file mode 100644 index 80af0701001a3..0000000000000 --- a/extensions/blocks/publicize/store/reducer.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Reducer managing Publicize connection test results. - * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. - * - * @return {Object} Updated state. - */ -export default function( state = [], action ) { - switch ( action.type ) { - case 'SET_CONNECTION_TEST_RESULTS': - return action.results; - case 'REFRESH_CONNECTION_TEST_RESULTS': - return []; - } - - return state; -} diff --git a/extensions/blocks/publicize/store/selectors.js b/extensions/blocks/publicize/store/selectors.js deleted file mode 100644 index db86a4fec0d5c..0000000000000 --- a/extensions/blocks/publicize/store/selectors.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Returns the failed Publicize connections. - * - * @param {Object} state State object. - * - * @return {Array} List of connections. - */ -export function getFailedConnections( state ) { - return state.filter( connection => false === connection.test_success ); -} - -/** - * Returns a list of Publicize connection service names that require reauthentication from users. - * iFor example, when LinkedIn switched its API from v1 to v2. - * - * @param {Object} state State object. - * - * @return {Array} List of service names that need reauthentication. - */ -export function getMustReauthConnections( state ) { - return state - .filter( connection => 'must_reauth' === connection.test_success ) - .map( connection => connection.service_name ); -} diff --git a/extensions/blocks/related-posts/edit.js b/extensions/blocks/related-posts/edit.js deleted file mode 100644 index f6816e822e909..0000000000000 --- a/extensions/blocks/related-posts/edit.js +++ /dev/null @@ -1,253 +0,0 @@ -/** - * External dependencies - */ -import { BlockControls, InspectorControls } from '@wordpress/editor'; -import { PanelBody, RangeControl, ToggleControl, Toolbar, Path, SVG } from '@wordpress/components'; -import { Component, Fragment } from '@wordpress/element'; -import { get } from 'lodash'; -import { withSelect } from '@wordpress/data'; -import { compose, withInstanceId } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; - -export const MAX_POSTS_TO_SHOW = 6; - -function PlaceholderPostEdit( props ) { - return ( - <div - className="jp-related-posts-i2__post" - id={ props.id } - aria-labelledby={ props.id + '-heading' } - > - <strong id={ props.id + '-heading' } className="jp-related-posts-i2__post-link"> - { __( "Preview unavailable: you haven't published enough posts with similar content." ) } - </strong> - { props.displayThumbnails && ( - <figure - className="jp-related-posts-i2__post-image-placeholder" - aria-label={ __( 'Placeholder image' ) } - > - <SVG - className="jp-related-posts-i2__post-image-placeholder-square" - xmlns="http://www.w3.org/2000/svg" - width="100%" - height="100%" - viewBox="0 0 350 200" - > - <title>{ __( 'Grey square' ) }</title> - <Path d="M0 0h350v200H0z" fill="#8B8B96" fill-opacity=".1" /> - </SVG> - <SVG - className="jp-related-posts-i2__post-image-placeholder-icon" - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - > - <title>{ __( 'Icon for image' ) }</title> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4.86 8.86l-3 3.87L9 13.14 6 17h12l-3.86-5.14z" /> - </SVG> - </figure> - ) } - - { props.displayDate && ( - <div className="jp-related-posts-i2__post-date has-small-font-size"> - { __( 'August 3, 2018' ) } - </div> - ) } - { props.displayContext && ( - <div className="jp-related-posts-i2__post-context has-small-font-size"> - { __( 'In “Uncategorized”' ) } - </div> - ) } - </div> - ); -} - -function RelatedPostsEditItem( props ) { - return ( - <div - className="jp-related-posts-i2__post" - id={ props.id } - aria-labelledby={ props.id + '-heading' } - > - <a - className="jp-related-posts-i2__post-link" - id={ props.id + '-heading' } - href={ props.post.url } - rel="nofollow noopener noreferrer" - target="_blank" - > - { props.post.title } - </a> - { props.displayThumbnails && props.post.img && props.post.img.src && ( - <a className="jp-related-posts-i2__post-img-link" href={ props.post.url }> - <img - className="jp-related-posts-i2__post-img" - src={ props.post.img.src } - alt={ props.post.title } - rel="nofollow noopener noreferrer" - target="_blank" - /> - </a> - ) } - { props.displayDate && ( - <div className="jp-related-posts-i2__post-date has-small-font-size"> - { props.post.date } - </div> - ) } - { props.displayContext && ( - <div className="jp-related-posts-i2__post-context has-small-font-size"> - { props.post.context } - </div> - ) } - </div> - ); -} - -function RelatedPostsPreviewRows( props ) { - const className = 'jp-related-posts-i2__row'; - - let topRowEnd = 0; - const displayLowerRow = props.posts.length > 3; - - switch ( props.posts.length ) { - case 2: - case 4: - case 5: - topRowEnd = 2; - break; - default: - topRowEnd = 3; - break; - } - - return ( - <div> - <div className={ className } data-post-count={ props.posts.slice( 0, topRowEnd ).length }> - { props.posts.slice( 0, topRowEnd ) } - </div> - { displayLowerRow && ( - <div className={ className } data-post-count={ props.posts.slice( topRowEnd ).length }> - { props.posts.slice( topRowEnd ) } - </div> - ) } - </div> - ); -} - -class RelatedPostsEdit extends Component { - render() { - const { attributes, className, posts, setAttributes, instanceId } = this.props; - const { displayContext, displayDate, displayThumbnails, postLayout, postsToShow } = attributes; - - const layoutControls = [ - { - icon: 'grid-view', - title: __( 'Grid View' ), - onClick: () => setAttributes( { postLayout: 'grid' } ), - isActive: postLayout === 'grid', - }, - { - icon: 'list-view', - title: __( 'List View' ), - onClick: () => setAttributes( { postLayout: 'list' } ), - isActive: postLayout === 'list', - }, - ]; - - // To prevent the block from crashing, we need to limit ourselves to the - // posts returned by the backend - so if we want 6 posts, but only 3 are - // returned, we need to limit ourselves to those 3 and fill in the rest - // with placeholders. - // - // Also, if the site does not have sufficient posts to display related ones - // (minimum 10 posts), we also use this code block to fill in the - // placeholders. - const previewClassName = 'jp-relatedposts-i2'; - const displayPosts = []; - for ( let i = 0; i < postsToShow; i++ ) { - if ( posts[ i ] ) { - displayPosts.push( - <RelatedPostsEditItem - id={ `related-posts-${ instanceId }-post-${ i }` } - key={ previewClassName + '-' + i } - post={ posts[ i ] } - displayThumbnails={ displayThumbnails } - displayDate={ displayDate } - displayContext={ displayContext } - /> - ); - } else { - displayPosts.push( - <PlaceholderPostEdit - id={ `related-posts-${ instanceId }-post-${ i }` } - key={ 'related-post-placeholder-' + i } - displayThumbnails={ displayThumbnails } - displayDate={ displayDate } - displayContext={ displayContext } - /> - ); - } - } - - return ( - <Fragment> - <InspectorControls> - <PanelBody title={ __( 'Related Posts Settings' ) }> - <ToggleControl - label={ __( 'Display thumbnails' ) } - checked={ displayThumbnails } - onChange={ value => setAttributes( { displayThumbnails: value } ) } - /> - <ToggleControl - label={ __( 'Display date' ) } - checked={ displayDate } - onChange={ value => setAttributes( { displayDate: value } ) } - /> - <ToggleControl - label={ __( 'Display context (category or tag)' ) } - checked={ displayContext } - onChange={ value => setAttributes( { displayContext: value } ) } - /> - <RangeControl - label={ __( 'Number of posts' ) } - value={ postsToShow } - onChange={ value => - setAttributes( { postsToShow: Math.min( value, MAX_POSTS_TO_SHOW ) } ) - } - min={ 1 } - max={ MAX_POSTS_TO_SHOW } - /> - </PanelBody> - </InspectorControls> - - <BlockControls> - <Toolbar controls={ layoutControls } /> - </BlockControls> - - <div className={ className } id={ `related-posts-${ instanceId }` }> - <div className={ previewClassName } data-layout={ postLayout }> - <RelatedPostsPreviewRows posts={ displayPosts } /> - </div> - </div> - </Fragment> - ); - } -} - -export default compose( - withInstanceId, - withSelect( select => { - const { getCurrentPost } = select( 'core/editor' ); - const posts = get( getCurrentPost(), 'jetpack-related-posts', [] ); - - return { - posts, - }; - } ) -)( RelatedPostsEdit ); diff --git a/extensions/blocks/related-posts/editor.js b/extensions/blocks/related-posts/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/related-posts/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/related-posts/index.js b/extensions/blocks/related-posts/index.js deleted file mode 100644 index 486cad0f75f3e..0000000000000 --- a/extensions/blocks/related-posts/index.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * External dependencies - */ -import { G, Path, SVG } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import './style.scss'; -import edit from './edit'; -import { __, _x } from '../../utils/i18n'; - -export const name = 'related-posts'; - -export const settings = { - title: __( 'Related Posts' ), - - icon: ( - <SVG viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <G stroke="currentColor" strokeWidth="2" strokeLinecap="square"> - <Path d="M4,4 L4,19 M4,4 L19,4 M4,9 L19,9 M4,14 L19,14 M4,19 L19,19 M9,4 L9,19 M19,4 L19,19" /> - </G> - </SVG> - ), - - category: 'jetpack', - - keywords: [ - _x( 'Similar content', 'block search term' ), - _x( 'Linked', 'block search term' ), - _x( 'Connected', 'block search term' ), - ], - - attributes: { - postLayout: { - type: 'string', - default: 'grid', - }, - displayDate: { - type: 'boolean', - default: true, - }, - displayThumbnails: { - type: 'boolean', - default: false, - }, - displayContext: { - type: 'boolean', - default: false, - }, - postsToShow: { - type: 'number', - default: 3, - }, - }, - - supports: { - html: false, - multiple: false, - reusable: false, - }, - - transforms: { - from: [ - { - type: 'shortcode', - tag: 'jetpack-related-posts', - }, - ], - }, - - edit, - - save: () => null, -}; diff --git a/extensions/blocks/related-posts/style.scss b/extensions/blocks/related-posts/style.scss deleted file mode 100644 index 8008c57aad8dc..0000000000000 --- a/extensions/blocks/related-posts/style.scss +++ /dev/null @@ -1,87 +0,0 @@ -// @TODO: Replace with Gutenberg variables - -.jp-related-posts-i2 { - &__row { - display: flex; - margin-top: 1.5rem; - - &:first-child { - margin-top: 0; - } - - &[data-post-count='3'] .jp-related-posts-i2__post { - max-width: calc( 33% - 20px ); - } - - &[data-post-count='2'] .jp-related-posts-i2__post, - &[data-post-count='1'] .jp-related-posts-i2__post { - max-width: calc( 50% - 20px ); - } - } - - &__post { - flex-grow: 1; - flex-basis: 0; - margin: 0 10px; - display: flex; - flex-direction: column; - } - - &__post-heading, &__post-img-link, &__post-date, &__post-context { - flex-direction: row; - } - - &__post-img-link, &__post-image-placeholder { - order: -1; - } - - &__post-heading { - margin: 0.5rem 0; - font-size: 1rem; - line-height: 1.2em; - } - - &__post-link { - display: block; - width: 100%; - line-height: 1.2em; - margin: 0.2em 0; - } - - &__post-img { - width: 100%; - } - - &__post-image-placeholder { - display: block; - position: relative; - margin: 0 auto; - max-width: 350px; - &-icon { - position: absolute; - top: calc( 50% - 12px ); - left: calc( 50% - 12px ); - } - } -} - -/* List view */ - -.jp-relatedposts-i2[data-layout='list'] { - .jp-related-posts-i2__row { - margin-top: 0; - display: block; - } - .jp-related-posts-i2__post { - max-width: none; - margin: 0; - margin-top: 1rem; - } - .jp-related-posts-i2__post-image-placeholder { - max-width: 350px; - margin: 0; - } - .jp-related-posts-i2__post-img-link { - margin-top: 1rem; - } -} diff --git a/extensions/blocks/repeat-visitor/components/edit.js b/extensions/blocks/repeat-visitor/components/edit.js deleted file mode 100644 index 2ad72b4e00930..0000000000000 --- a/extensions/blocks/repeat-visitor/components/edit.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * External dependencies - */ -import { Notice, TextControl, RadioControl, Placeholder } from '@wordpress/components'; -import { Component } from '@wordpress/element'; -import { InnerBlocks } from '@wordpress/editor'; -import { withSelect } from '@wordpress/data'; -import classNames from 'classnames'; - -/** - * Internal dependencies - */ -import { sprintf } from '@wordpress/i18n'; -import { __, _n } from '../../../utils/i18n'; -import { CRITERIA_AFTER, CRITERIA_BEFORE } from '../constants'; -import { icon } from '../index'; - -const RADIO_OPTIONS = [ - { - value: CRITERIA_AFTER, - label: __( 'Show after threshold' ), - }, - { - value: CRITERIA_BEFORE, - label: __( 'Show before threshold' ), - }, -]; - -class RepeatVisitorEdit extends Component { - componentDidMount() { - this.props.setAttributes( { isThresholdValid: true } ); - } - - setCriteria = criteria => this.props.setAttributes( { criteria } ); - setThreshold = threshold => { - if ( /^\d+$/.test( threshold ) && +threshold > 0 ) { - this.props.setAttributes( { threshold, isThresholdValid: true } ); - return; - } - this.props.setAttributes( { isThresholdValid: false } ); - }; - - getNoticeLabel() { - if ( this.props.attributes.criteria === CRITERIA_AFTER ) { - return sprintf( - _n( - 'This block will only appear to people who have visited this page at least once.', - 'This block will only appear to people who have visited this page at least %d times.', - +this.props.attributes.threshold - ), - this.props.attributes.threshold - ); - } - - return sprintf( - _n( - 'This block will only appear to people who have never visited this page before.', - 'This block will only appear to people who have visited this page less than %d times.', - +this.props.attributes.threshold - ), - this.props.attributes.threshold - ); - } - - render() { - return ( - <div - className={ classNames( this.props.className, { - 'wp-block-jetpack-repeat-visitor--is-unselected': ! this.props.isSelected, - } ) } - > - <Placeholder - icon={ icon } - label={ __( 'Repeat Visitor' ) } - className="wp-block-jetpack-repeat-visitor-placeholder" - > - <TextControl - className="wp-block-jetpack-repeat-visitor-threshold" - defaultValue={ this.props.attributes.threshold } - help={ - this.props.attributes.isThresholdValid ? '' : __( 'Please enter a valid number.' ) - } - label={ __( 'Visit count threshold' ) } - min="1" - onChange={ this.setThreshold } - pattern="[0-9]" - type="number" - /> - - <RadioControl - label={ __( 'Visibility' ) } - selected={ this.props.attributes.criteria } - options={ RADIO_OPTIONS } - onChange={ this.setCriteria } - /> - </Placeholder> - - <Notice status="info" isDismissible={ false }> - { this.getNoticeLabel() } - </Notice> - <InnerBlocks /> - </div> - ); - } -} - -export default withSelect( ( select, ownProps ) => { - const { isBlockSelected, hasSelectedInnerBlock } = select( 'core/editor' ); - return { - isSelected: isBlockSelected( ownProps.clientId ) || hasSelectedInnerBlock( ownProps.clientId ), - }; -} )( RepeatVisitorEdit ); diff --git a/extensions/blocks/repeat-visitor/components/save.js b/extensions/blocks/repeat-visitor/components/save.js deleted file mode 100644 index 7484c06d9e5f9..0000000000000 --- a/extensions/blocks/repeat-visitor/components/save.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * External dependencies - */ -import { InnerBlocks } from '@wordpress/editor'; - -export default ( { className } ) => { - return ( - <div className={ className }> - <InnerBlocks.Content /> - </div> - ); -}; diff --git a/extensions/blocks/repeat-visitor/constants.js b/extensions/blocks/repeat-visitor/constants.js deleted file mode 100644 index 09f459d2947d7..0000000000000 --- a/extensions/blocks/repeat-visitor/constants.js +++ /dev/null @@ -1,5 +0,0 @@ -export const CRITERIA_AFTER = 'after-visits'; -export const CRITERIA_BEFORE = 'before-visits'; -export const DEFAULT_THRESHOLD = 3; -export const COOKIE_NAME = 'jp-visit-counter'; -export const MAX_COOKIE_AGE = 6 * 30 * 24 * 60 * 60; // 6 months diff --git a/extensions/blocks/repeat-visitor/editor.js b/extensions/blocks/repeat-visitor/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/repeat-visitor/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/repeat-visitor/editor.scss b/extensions/blocks/repeat-visitor/editor.scss deleted file mode 100644 index 72b90c39d3d3c..0000000000000 --- a/extensions/blocks/repeat-visitor/editor.scss +++ /dev/null @@ -1,54 +0,0 @@ -.wp-block-jetpack-repeat-visitor { - .components-notice { - margin: 1em 0 0; - } - .components-radio-control__option { - text-align: left; - } - .components-notice__content { - margin: 0.5em 0; - font-size: 0.8em; - - .components-base-control { - display: inline-block; - max-width: 8em; - vertical-align: middle; - - .components-base-control__field { - margin-bottom: 0; - } - } - } -} - -.wp-block-jetpack-repeat-visitor-placeholder { - min-height: inherit; - - .components-placeholder__label svg { - margin-right: 0.5ch; - } - - .components-placeholder__fieldset { - flex-wrap: nowrap; - .components-base-control { - flex-basis: 100%; - } - } - - .components-base-control__help { - color: $muriel-hot-red-500; - } -} - -.wp-block-jetpack-repeat-visitor--is-unselected .wp-block-jetpack-repeat-visitor-placeholder { - display: none; -} - -.wp-block-jetpack-repeat-visitor-threshold { - margin-right: 20px; - - .components-text-control__input { - width: 5em; - text-align: center; - } -} diff --git a/extensions/blocks/repeat-visitor/index.js b/extensions/blocks/repeat-visitor/index.js deleted file mode 100644 index 52246fa2a5b60..0000000000000 --- a/extensions/blocks/repeat-visitor/index.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * External dependencies - */ -import { Path } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __, _x } from '../../utils/i18n'; -import renderMaterialIcon from '../../utils/render-material-icon'; -import edit from './components/edit'; -import save from './components/save'; -import { CRITERIA_AFTER, DEFAULT_THRESHOLD } from './constants'; -import './editor.scss'; - -export const name = 'repeat-visitor'; -export const icon = renderMaterialIcon( - <Path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z" /> -); -export const settings = { - attributes: { - criteria: { - type: 'string', - default: CRITERIA_AFTER, - }, - threshold: { - type: 'number', - default: DEFAULT_THRESHOLD, - }, - isThresholdValid: { - type: 'boolean', - default: true, - }, - }, - category: 'jetpack', - description: __( 'Control block visibility based on how often a visitor has viewed the page.' ), - icon, - keywords: [ - _x( 'traffic', 'block search term' ), - _x( 'visitors', 'block search term' ), - _x( 'visibility', 'block search term' ), - ], - supports: { html: false }, - title: __( 'Repeat Visitor' ), - edit, - save, -}; diff --git a/extensions/blocks/repeat-visitor/view.js b/extensions/blocks/repeat-visitor/view.js deleted file mode 100644 index 0273932ca04e1..0000000000000 --- a/extensions/blocks/repeat-visitor/view.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * External dependencies - */ -import cookie from 'cookie'; - -/** - * Internal dependencies - */ -import { COOKIE_NAME, MAX_COOKIE_AGE } from './constants'; - -function getViewCount() { - const cookies = cookie.parse( document.cookie ); - const value = cookies[ COOKIE_NAME ] || 0; - return +value; -} - -function setViewCount( value ) { - document.cookie = cookie.serialize( COOKIE_NAME, value, { - path: window.location.pathname, - maxAge: MAX_COOKIE_AGE, - } ); -} - -function incrementCookieValue() { - const repeatVisitorBlocks = Array.from( - document.querySelectorAll( '.wp-block-jetpack-repeat-visitor' ) - ); - if ( repeatVisitorBlocks.length === 0 ) { - return; - } - - setViewCount( getViewCount() + 1 ); -} - -window && window.addEventListener( 'load', incrementCookieValue ); diff --git a/extensions/blocks/seo/editor.js b/extensions/blocks/seo/editor.js deleted file mode 100644 index e2811b0c0d125..0000000000000 --- a/extensions/blocks/seo/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import { name, settings } from '.'; -import registerJetpackPlugin from '../../utils/register-jetpack-plugin'; - -registerJetpackPlugin( name, settings ); diff --git a/extensions/blocks/seo/editor.scss b/extensions/blocks/seo/editor.scss deleted file mode 100644 index ea00e7faf8f50..0000000000000 --- a/extensions/blocks/seo/editor.scss +++ /dev/null @@ -1,16 +0,0 @@ -// @TODO: Replace with Gutenberg variables -$light-gray-300: #edeff0; - -.jetpack-seo-message-box { - background-color: $light-gray-300; - border-radius: 4px; -} - -.jetpack-seo-message-box textarea { - width: 100%; -} - -.jetpack-seo-character-count { - padding-bottom: 5px; - padding-left: 5px; -} diff --git a/extensions/blocks/seo/index.js b/extensions/blocks/seo/index.js deleted file mode 100644 index 12747d5d79b17..0000000000000 --- a/extensions/blocks/seo/index.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * External dependencies - */ -import { Fragment } from '@wordpress/element'; -import { PanelBody } from '@wordpress/components'; -import { PluginPrePublishPanel } from '@wordpress/edit-post'; - -/** - * Internal dependencies - */ -import './editor.scss'; -import JetpackPluginSidebar from '../../shared/jetpack-plugin-sidebar'; -import SeoPanel from './panel'; -import { __ } from '../../utils/i18n'; - -export const name = 'seo'; - -export const settings = { - render: () => ( - <Fragment> - <JetpackPluginSidebar> - <PanelBody title={ __( 'SEO Description' ) }> - <SeoPanel /> - </PanelBody> - </JetpackPluginSidebar> - <PluginPrePublishPanel - initialOpen - id="seo-title" - title={ - <span id="seo-defaults" key="seo-title-span"> - { __( 'SEO Description' ) } - </span> - } - > - <SeoPanel /> - </PluginPrePublishPanel> - </Fragment> - ), -}; diff --git a/extensions/blocks/seo/panel.js b/extensions/blocks/seo/panel.js deleted file mode 100644 index 7c408791910d7..0000000000000 --- a/extensions/blocks/seo/panel.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { compose } from '@wordpress/compose'; -import { get } from 'lodash'; -import { sprintf } from '@wordpress/i18n'; -import { withDispatch, withSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { __, _n } from '../../utils/i18n'; - -class SeoPanel extends Component { - onMessageChange = event => { - this.props.updateSeoDescription( event.target.value ); - }; - - render() { - const { seoDescription } = this.props; - - return ( - <div className="jetpack-seo-message-box"> - <textarea - value={ seoDescription } - onChange={ this.onMessageChange } - placeholder={ __( 'Write a description…' ) } - rows={ 4 } - /> - <div className="jetpack-seo-character-count"> - { sprintf( - _n( '%d character', '%d characters', seoDescription.length ), - seoDescription.length - ) } - </div> - </div> - ); - } -} - -export default compose( [ - withSelect( select => ( { - seoDescription: get( - select( 'core/editor' ).getEditedPostAttribute( 'meta' ), - [ 'advanced_seo_description' ], - '' - ), - } ) ), - withDispatch( dispatch => ( { - updateSeoDescription( seoDescription ) { - dispatch( 'core/editor' ).editPost( { - meta: { - advanced_seo_description: seoDescription, - }, - } ); - }, - } ) ), -] )( SeoPanel ); diff --git a/extensions/blocks/shortlinks/editor.js b/extensions/blocks/shortlinks/editor.js deleted file mode 100644 index e2811b0c0d125..0000000000000 --- a/extensions/blocks/shortlinks/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import { name, settings } from '.'; -import registerJetpackPlugin from '../../utils/register-jetpack-plugin'; - -registerJetpackPlugin( name, settings ); diff --git a/extensions/blocks/shortlinks/index.js b/extensions/blocks/shortlinks/index.js deleted file mode 100644 index 11effa9ccf114..0000000000000 --- a/extensions/blocks/shortlinks/index.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { get } from 'lodash'; -import { PanelBody } from '@wordpress/components'; -import { withSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import ClipboardInput from '../../utils/clipboard-input'; -import JetpackPluginSidebar from '../../shared/jetpack-plugin-sidebar'; -import { __ } from '../../utils/i18n'; - -export const name = 'shortlinks'; - -export const settings = { - render: () => <Shortlinks />, -}; - -class ShortlinksPanel extends Component { - render() { - const { shortlink } = this.props; - - if ( ! shortlink ) { - return null; - } - - return ( - <JetpackPluginSidebar> - <PanelBody title={ __( 'Shortlink' ) } className="jetpack-shortlinks__panel"> - <ClipboardInput link={ shortlink } /> - </PanelBody> - </JetpackPluginSidebar> - ); - } -} - -const Shortlinks = withSelect( select => { - const currentPost = select( 'core/editor' ).getCurrentPost(); - return { - shortlink: get( currentPost, 'jetpack_shortlink', '' ), - }; -} )( ShortlinksPanel ); diff --git a/extensions/blocks/simple-payments/constants.js b/extensions/blocks/simple-payments/constants.js deleted file mode 100644 index e593f9476210a..0000000000000 --- a/extensions/blocks/simple-payments/constants.js +++ /dev/null @@ -1,39 +0,0 @@ -export const SIMPLE_PAYMENTS_PRODUCT_POST_TYPE = 'jp_pay_product'; - -export const DEFAULT_CURRENCY = 'USD'; - -// https://developer.paypal.com/docs/integration/direct/rest/currency-codes/ -// If this list changes, Simple Payments in Jetpack must be updated as well. -// See https://github.com/Automattic/jetpack/blob/master/modules/simple-payments/simple-payments.php - -/** - * Indian Rupee not supported because at the time of the creation of this file - * because it's limited to in-country PayPal India accounts only. - * Discussion: https://github.com/Automattic/wp-calypso/pull/28236 - */ -export const SUPPORTED_CURRENCY_LIST = [ - DEFAULT_CURRENCY, - 'EUR', - 'AUD', - 'BRL', - 'CAD', - 'CZK', - 'DKK', - 'HKD', - 'HUF', - 'ILS', - 'JPY', - 'MYR', - 'MXN', - 'TWD', - 'NZD', - 'NOK', - 'PHP', - 'PLN', - 'GBP', - 'RUB', - 'SGD', - 'SEK', - 'CHF', - 'THB', -]; diff --git a/extensions/blocks/simple-payments/edit.js b/extensions/blocks/simple-payments/edit.js deleted file mode 100644 index 9f71c6e1a1a12..0000000000000 --- a/extensions/blocks/simple-payments/edit.js +++ /dev/null @@ -1,578 +0,0 @@ -/** - * External dependencies - */ -import classNames from 'classnames'; -import emailValidator from 'email-validator'; -import { Component } from '@wordpress/element'; -import { compose, withInstanceId } from '@wordpress/compose'; -import { dispatch, withSelect } from '@wordpress/data'; -import { get, isEqual, pick, trimEnd } from 'lodash'; -import { sprintf } from '@wordpress/i18n'; -import { - Disabled, - ExternalLink, - SelectControl, - TextareaControl, - TextControl, - ToggleControl, -} from '@wordpress/components'; -import { getCurrencyDefaults } from '@automattic/format-currency'; - -/** - * Internal dependencies - */ -import HelpMessage from './help-message'; -import ProductPlaceholder from './product-placeholder'; -import FeaturedMedia from './featured-media'; -import { __, _n } from '../../utils/i18n'; -import { decimalPlaces, formatPrice } from './utils'; -import { SIMPLE_PAYMENTS_PRODUCT_POST_TYPE, SUPPORTED_CURRENCY_LIST } from './constants'; - -class SimplePaymentsEdit extends Component { - state = { - fieldEmailError: null, - fieldPriceError: null, - fieldTitleError: null, - isSavingProduct: false, - }; - - /** - * We'll use this flag to inject attributes one time when the product entity is loaded. - * - * It is based on the presence of a `productId` attribute. - * - * If present, initially we are waiting for attributes to be injected. - * If absent, we may save the product in the future but do not need to inject attributes based - * on the response as they will have come from our product submission. - */ - shouldInjectPaymentAttributes = !! this.props.attributes.productId; - - componentDidMount() { - // Try to get the simplePayment loaded into attributes if possible. - this.injectPaymentAttributes(); - - const { attributes, hasPublishAction } = this.props; - const { productId } = attributes; - - // If the user can publish save an empty product so that we have an ID and can save - // concurrently with the post that contains the Simple Payment. - if ( ! productId && hasPublishAction ) { - this.saveProduct(); - } - } - - componentDidUpdate( prevProps ) { - const { hasPublishAction, isSelected } = this.props; - - if ( ! isEqual( prevProps.simplePayment, this.props.simplePayment ) ) { - this.injectPaymentAttributes(); - } - - if ( - ! prevProps.isSaving && - this.props.isSaving && - hasPublishAction && - this.validateAttributes() - ) { - // Validate and save product on post save - this.saveProduct(); - } else if ( prevProps.isSelected && ! isSelected ) { - // Validate on block deselect - this.validateAttributes(); - } - } - - injectPaymentAttributes() { - /** - * Prevent injecting the product attributes when not desired. - * - * When we first load a product, we should inject its attributes as our initial form state. - * When subsequent saves occur, we should avoid injecting attributes so that we do not - * overwrite changes that the user has made with stale state from the previous save. - */ - if ( ! this.shouldInjectPaymentAttributes ) { - return; - } - - const { attributes, setAttributes, simplePayment } = this.props; - const { - content, - currency, - email, - featuredMediaId, - multiple, - price, - productId, - title, - } = attributes; - - if ( productId && simplePayment ) { - setAttributes( { - content: get( simplePayment, [ 'content', 'raw' ], content ), - currency: get( simplePayment, [ 'meta', 'spay_currency' ], currency ), - email: get( simplePayment, [ 'meta', 'spay_email' ], email ), - featuredMediaId: get( simplePayment, [ 'featured_media' ], featuredMediaId ), - multiple: Boolean( get( simplePayment, [ 'meta', 'spay_multiple' ], Boolean( multiple ) ) ), - price: get( simplePayment, [ 'meta', 'spay_price' ], price || undefined ), - title: get( simplePayment, [ 'title', 'raw' ], title ), - } ); - this.shouldInjectPaymentAttributes = ! this.shouldInjectPaymentAttributes; - } - } - - toApi() { - const { attributes } = this.props; - const { - content, - currency, - email, - featuredMediaId, - multiple, - price, - productId, - title, - } = attributes; - - return { - id: productId, - content, - featured_media: featuredMediaId, - meta: { - spay_currency: currency, - spay_email: email, - spay_multiple: multiple, - spay_price: price, - }, - status: productId ? 'publish' : 'draft', - title, - }; - } - - saveProduct() { - if ( this.state.isSavingProduct ) { - return; - } - - const { attributes, setAttributes } = this.props; - const { email } = attributes; - const { saveEntityRecord } = dispatch( 'core' ); - - this.setState( { isSavingProduct: true }, () => { - saveEntityRecord( 'postType', SIMPLE_PAYMENTS_PRODUCT_POST_TYPE, this.toApi() ) - .then( record => { - if ( record ) { - setAttributes( { productId: record.id } ); - } - - return record; - } ) - .catch( error => { - // Nothing we can do about errors without details at the moment - if ( ! error || ! error.data ) { - return; - } - - const { - data: { key: apiErrorKey }, - } = error; - - // @TODO errors in other fields - this.setState( { - fieldEmailError: - apiErrorKey === 'spay_email' - ? sprintf( __( '%s is not a valid email address.' ), email ) - : null, - fieldPriceError: apiErrorKey === 'spay_price' ? __( 'Invalid price.' ) : null, - } ); - } ) - .finally( () => { - this.setState( { - isSavingProduct: false, - } ); - } ); - } ); - } - - validateAttributes = () => { - const isPriceValid = this.validatePrice(); - const isTitleValid = this.validateTitle(); - const isEmailValid = this.validateEmail(); - const isCurrencyValid = this.validateCurrency(); - - return isPriceValid && isTitleValid && isEmailValid && isCurrencyValid; - }; - - /** - * Validate currency - * - * This method does not include validation UI. Currency selection should not allow for invalid - * values. It is primarily to ensure that the currency is valid to save. - * - * @return {boolean} True if currency is valid - */ - validateCurrency = () => { - const { currency } = this.props.attributes; - return SUPPORTED_CURRENCY_LIST.includes( currency ); - }; - - /** - * Validate price - * - * Stores error message in state.fieldPriceError - * - * @returns {Boolean} True when valid, false when invalid - */ - validatePrice = () => { - const { currency, price } = this.props.attributes; - const { precision } = getCurrencyDefaults( currency ); - - if ( ! price || parseFloat( price ) === 0 ) { - this.setState( { - fieldPriceError: __( 'If you’re selling something, you need a price tag. Add yours here.' ), - } ); - return false; - } - - if ( Number.isNaN( parseFloat( price ) ) ) { - this.setState( { - fieldPriceError: __( 'Invalid price' ), - } ); - return false; - } - - if ( parseFloat( price ) < 0 ) { - this.setState( { - fieldPriceError: __( - 'Your price is negative — enter a positive number so people can pay the right amount.' - ), - } ); - return false; - } - - if ( decimalPlaces( price ) > precision ) { - if ( precision === 0 ) { - this.setState( { - fieldPriceError: __( - 'We know every penny counts, but prices in this currency can’t contain decimal values.' - ), - } ); - return false; - } - - this.setState( { - fieldPriceError: sprintf( - _n( - 'The price cannot have more than %d decimal place.', - 'The price cannot have more than %d decimal places.', - precision - ), - precision - ), - } ); - return false; - } - - if ( this.state.fieldPriceError ) { - this.setState( { fieldPriceError: null } ); - } - - return true; - }; - - /** - * Validate email - * - * Stores error message in state.fieldEmailError - * - * @returns {Boolean} True when valid, false when invalid - */ - validateEmail = () => { - const { email } = this.props.attributes; - if ( ! email ) { - this.setState( { - fieldEmailError: __( - 'We want to make sure payments reach you, so please add an email address.' - ), - } ); - return false; - } - - if ( ! emailValidator.validate( email ) ) { - this.setState( { - fieldEmailError: sprintf( __( '%s is not a valid email address.' ), email ), - } ); - return false; - } - - if ( this.state.fieldEmailError ) { - this.setState( { fieldEmailError: null } ); - } - - return true; - }; - - /** - * Validate title - * - * Stores error message in state.fieldTitleError - * - * @returns {Boolean} True when valid, false when invalid - */ - validateTitle = () => { - const { title } = this.props.attributes; - if ( ! title ) { - this.setState( { - fieldTitleError: __( - 'Please add a brief title so that people know what they’re paying for.' - ), - } ); - return false; - } - - if ( this.state.fieldTitleError ) { - this.setState( { fieldTitleError: null } ); - } - - return true; - }; - - handleEmailChange = email => { - this.props.setAttributes( { email } ); - this.setState( { fieldEmailError: null } ); - }; - - handleFeaturedMediaSelect = media => { - this.props.setAttributes( { featuredMediaId: get( media, 'id', 0 ) } ); - }; - - handleContentChange = content => { - this.props.setAttributes( { content } ); - }; - - handlePriceChange = price => { - price = parseFloat( price ); - if ( ! isNaN( price ) ) { - this.props.setAttributes( { price } ); - } else { - this.props.setAttributes( { price: undefined } ); - } - this.setState( { fieldPriceError: null } ); - }; - - handleCurrencyChange = currency => { - this.props.setAttributes( { currency } ); - }; - - handleMultipleChange = multiple => { - this.props.setAttributes( { multiple: !! multiple } ); - }; - - handleTitleChange = title => { - this.props.setAttributes( { title } ); - this.setState( { fieldTitleError: null } ); - }; - - getCurrencyList = SUPPORTED_CURRENCY_LIST.map( value => { - const { symbol } = getCurrencyDefaults( value ); - // if symbol is equal to the code (e.g., 'CHF' === 'CHF'), don't duplicate it. - // trim the dot at the end, e.g., 'kr.' becomes 'kr' - const label = symbol === value ? value : `${ value } ${ trimEnd( symbol, '.' ) }`; - return { value, label }; - } ); - - render() { - const { fieldEmailError, fieldPriceError, fieldTitleError } = this.state; - const { - attributes, - featuredMedia, - instanceId, - isSelected, - setAttributes, - simplePayment, - } = this.props; - const { - content, - currency, - email, - featuredMediaId, - featuredMediaUrl: featuredMediaUrlAttribute, - featuredMediaTitle: featuredMediaTitleAttribute, - multiple, - price, - productId, - title, - } = attributes; - - const featuredMediaUrl = - featuredMediaUrlAttribute || ( featuredMedia && featuredMedia.source_url ); - const featuredMediaTitle = - featuredMediaTitleAttribute || ( featuredMedia && featuredMedia.alt_text ); - - /** - * The only disabled state that concerns us is when we expect a product but don't have it in - * local state. - */ - const isDisabled = productId && ! simplePayment; - - if ( ! isSelected && isDisabled ) { - return ( - <div className="simple-payments__loading"> - <ProductPlaceholder - aria-busy="true" - content="█████" - formattedPrice="█████" - title="█████" - /> - </div> - ); - } - - if ( - ! isSelected && - email && - price && - title && - ! fieldEmailError && - ! fieldPriceError && - ! fieldTitleError - ) { - return ( - <ProductPlaceholder - aria-busy="false" - content={ content } - featuredMediaUrl={ featuredMediaUrl } - featuredMediaTitle={ featuredMediaTitle } - formattedPrice={ formatPrice( price, currency ) } - multiple={ multiple } - title={ title } - /> - ); - } - - const Wrapper = isDisabled ? Disabled : 'div'; - - return ( - <Wrapper className="wp-block-jetpack-simple-payments"> - <FeaturedMedia - { ...{ featuredMediaId, featuredMediaUrl, featuredMediaTitle, setAttributes } } - /> - <div> - <TextControl - aria-describedby={ `${ instanceId }-title-error` } - className={ classNames( 'simple-payments__field', 'simple-payments__field-title', { - 'simple-payments__field-has-error': fieldTitleError, - } ) } - label={ __( 'Item name' ) } - onChange={ this.handleTitleChange } - placeholder={ __( 'Item name' ) } - required - type="text" - value={ title } - /> - <HelpMessage id={ `${ instanceId }-title-error` } isError> - { fieldTitleError } - </HelpMessage> - - <TextareaControl - className="simple-payments__field simple-payments__field-content" - label={ __( 'Describe your item in a few words' ) } - onChange={ this.handleContentChange } - placeholder={ __( 'Describe your item in a few words' ) } - value={ content } - /> - - <div className="simple-payments__price-container"> - <SelectControl - className="simple-payments__field simple-payments__field-currency" - label={ __( 'Currency' ) } - onChange={ this.handleCurrencyChange } - options={ this.getCurrencyList } - value={ currency } - /> - <TextControl - aria-describedby={ `${ instanceId }-price-error` } - className={ classNames( 'simple-payments__field', 'simple-payments__field-price', { - 'simple-payments__field-has-error': fieldPriceError, - } ) } - label={ __( 'Price' ) } - onChange={ this.handlePriceChange } - placeholder={ formatPrice( 0, currency, false ) } - required - step="1" - type="number" - value={ price || '' } - /> - <HelpMessage id={ `${ instanceId }-price-error` } isError> - { fieldPriceError } - </HelpMessage> - </div> - - <div className="simple-payments__field-multiple"> - <ToggleControl - checked={ Boolean( multiple ) } - label={ __( 'Allow people to buy more than one item at a time' ) } - onChange={ this.handleMultipleChange } - /> - </div> - - <TextControl - aria-describedby={ `${ instanceId }-email-${ fieldEmailError ? 'error' : 'help' }` } - className={ classNames( 'simple-payments__field', 'simple-payments__field-email', { - 'simple-payments__field-has-error': fieldEmailError, - } ) } - label={ __( 'Email' ) } - onChange={ this.handleEmailChange } - placeholder={ __( 'Email' ) } - required - type="email" - value={ email } - /> - <HelpMessage id={ `${ instanceId }-email-error` } isError> - { fieldEmailError } - </HelpMessage> - <HelpMessage id={ `${ instanceId }-email-help` }> - { __( - 'Enter the email address associated with your PayPal account. Don’t have an account?' - ) + ' ' } - <ExternalLink href="https://www.paypal.com/"> - { __( 'Create one on PayPal' ) } - </ExternalLink> - </HelpMessage> - </div> - </Wrapper> - ); - } -} - -const mapSelectToProps = withSelect( ( select, props ) => { - const { getEntityRecord, getMedia } = select( 'core' ); - const { isSavingPost, getCurrentPost } = select( 'core/editor' ); - - const { productId, featuredMediaId } = props.attributes; - - const fields = [ - [ 'content' ], - [ 'meta', 'spay_currency' ], - [ 'meta', 'spay_email' ], - [ 'meta', 'spay_multiple' ], - [ 'meta', 'spay_price' ], - [ 'title', 'raw' ], - [ 'featured_media' ], - ]; - - const simplePayment = productId - ? pick( getEntityRecord( 'postType', SIMPLE_PAYMENTS_PRODUCT_POST_TYPE, productId ), fields ) - : undefined; - - return { - hasPublishAction: !! get( getCurrentPost(), [ '_links', 'wp:action-publish' ] ), - isSaving: !! isSavingPost(), - simplePayment, - featuredMedia: featuredMediaId ? getMedia( featuredMediaId ) : null, - }; -} ); - -export default compose( - mapSelectToProps, - withInstanceId -)( SimplePaymentsEdit ); diff --git a/extensions/blocks/simple-payments/editor.js b/extensions/blocks/simple-payments/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/simple-payments/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/simple-payments/editor.scss b/extensions/blocks/simple-payments/editor.scss deleted file mode 100644 index 76d6fd9d065fb..0000000000000 --- a/extensions/blocks/simple-payments/editor.scss +++ /dev/null @@ -1,62 +0,0 @@ - -.wp-block-jetpack-simple-payments { - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, - Helvetica Neue, sans-serif; - display: grid; - grid-template-columns: 200px auto; - grid-column-gap: 10px; - - .simple-payments__field { - .components-base-control__label { - display: none; - } - .components-base-control__field { - margin-bottom: 1em; - } - // Reset empty space under textarea on Chrome - textarea { - display: block; - } - } - - .simple-payments__field-has-error { - .components-text-control__input, - .components-textarea-control__input { - border-color: var( --color-error ); - } - } - - .simple-payments__price-container { - display: flex; - flex-wrap: wrap; - .simple-payments__field { - margin-right: 10px; - } - .simple-payments__help-message { - flex: 1 1 100%; - margin-top: 0; - } - } - - .simple-payments__field-price { - .components-text-control__input { - max-width: 90px; - } - } - - .simple-payments__field-email { - .components-text-control__input { - max-width: 400px; - } - } - - .simple-payments__field-multiple { - .components-toggle-control__label { - line-height: 1.4em; - } - } - - .simple-payments__field-content .components-textarea-control__input { - min-height: 32px; - } -} diff --git a/extensions/blocks/simple-payments/featured-media.js b/extensions/blocks/simple-payments/featured-media.js deleted file mode 100644 index 4737b85100047..0000000000000 --- a/extensions/blocks/simple-payments/featured-media.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * External dependencies - */ -import { Fragment } from '@wordpress/element'; -import { IconButton, Toolbar, ToolbarButton } from '@wordpress/components'; -import { BlockControls, MediaPlaceholder, MediaUpload } from '@wordpress/editor'; -import { get } from 'lodash'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; - -const onSelectMedia = setAttributes => media => - setAttributes( { - featuredMediaId: get( media, 'id', 0 ), - featuredMediaUrl: get( media, 'url', null ), - featuredMediaTitle: get( media, 'title', null ), - } ); - -export default ( { featuredMediaId, featuredMediaUrl, featuredMediaTitle, setAttributes } ) => { - if ( ! featuredMediaId ) { - return ( - <MediaPlaceholder - icon="format-image" - labels={ { - title: __( 'Product Image' ), - } } - accept="image/*" - allowedTypes={ [ 'image' ] } - onSelect={ onSelectMedia( setAttributes ) } - /> - ); - } - - return ( - <div> - <Fragment> - <BlockControls> - <Toolbar> - <MediaUpload - onSelect={ onSelectMedia( setAttributes ) } - allowedTypes={ [ 'image' ] } - value={ featuredMediaId } - render={ ( { open } ) => ( - <IconButton - className="components-toolbar__control" - label={ __( 'Edit Image' ) } - icon="edit" - onClick={ open } - /> - ) } - /> - <ToolbarButton - icon={ 'trash' } - title={ __( 'Remove Image' ) } - onClick={ () => - setAttributes( { - featuredMediaId: null, - featuredMediaUrl: null, - featuredMediaTitle: null, - } ) - } - /> - </Toolbar> - </BlockControls> - <figure> - <img src={ featuredMediaUrl } alt={ featuredMediaTitle } /> - </figure> - </Fragment> - </div> - ); -}; diff --git a/extensions/blocks/simple-payments/help-message.js b/extensions/blocks/simple-payments/help-message.js deleted file mode 100644 index 57a6e6819ebb1..0000000000000 --- a/extensions/blocks/simple-payments/help-message.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * External dependencies - */ -import classNames from 'classnames'; - -/** - * Internal dependencies - */ -import GridiconNoticeOutline from 'gridicons/dist/notice-outline'; -import './help-message.scss'; - -export default ( { children = null, isError = false, ...props } ) => { - const classes = classNames( 'simple-payments__help-message', { - 'simple-payments__help-message-is-error': isError, - } ); - - return ( - children && ( - <div className={ classes } { ...props }> - { isError && <GridiconNoticeOutline size="24" /> } - <span>{ children }</span> - </div> - ) - ); -}; diff --git a/extensions/blocks/simple-payments/help-message.scss b/extensions/blocks/simple-payments/help-message.scss deleted file mode 100644 index 86f50f9e3b9b7..0000000000000 --- a/extensions/blocks/simple-payments/help-message.scss +++ /dev/null @@ -1,23 +0,0 @@ - -.wp-block-jetpack-simple-payments { - .simple-payments__help-message { - display: flex; - font-size: 13px; - line-height: 1.4em; - margin-bottom: 1em; - margin-top: -0.5em; - svg { - margin-right: 5px; - min-width: 24px; - } - > span { - margin-top: 2px; - } - &.simple-payments__help-message-is-error { - color: var( --color-error ); - svg { - fill: var( --color-error ); - } - } - } -} diff --git a/extensions/blocks/simple-payments/index.js b/extensions/blocks/simple-payments/index.js deleted file mode 100644 index 223c20c823e19..0000000000000 --- a/extensions/blocks/simple-payments/index.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * External dependencies - */ -import { ExternalLink, Path, SVG } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import save from './save'; -import { DEFAULT_CURRENCY } from './constants'; -import { __, _x } from '../../utils/i18n'; - -/** - * Styles - */ -import './editor.scss'; - -export const name = 'simple-payments'; - -export const settings = { - title: __( 'Simple Payments button' ), - - description: ( - <Fragment> - <p> - { __( - 'Lets you create and embed credit and debit card payment buttons with minimal setup.' - ) } - </p> - <ExternalLink href="https://support.wordpress.com/simple-payments/"> - { __( 'Support reference' ) } - </ExternalLink> - </Fragment> - ), - - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z" /> - </SVG> - ), - - category: 'jetpack', - - keywords: [ _x( 'shop', 'block search term' ), _x( 'sell', 'block search term' ), 'PayPal' ], - - attributes: { - currency: { - type: 'string', - default: DEFAULT_CURRENCY, - }, - content: { - type: 'string', - default: '', - }, - email: { - type: 'string', - default: '', - }, - featuredMediaId: { - type: 'number', - default: 0, - }, - featuredMediaUrl: { - type: 'string', - default: null, - }, - featuredMediaTitle: { - type: 'string', - default: null, - }, - multiple: { - type: 'boolean', - default: false, - }, - price: { - type: 'number', - }, - productId: { - type: 'number', - }, - title: { - type: 'string', - default: '', - }, - }, - - transforms: { - from: [ - { - type: 'shortcode', - tag: 'simple-payment', - attributes: { - productId: { - type: 'number', - shortcode: ( { named: { id } } ) => { - if ( ! id ) { - return; - } - - const result = parseInt( id, 10 ); - if ( result ) { - return result; - } - }, - }, - }, - }, - ], - }, - - edit, - - save, - - supports: { - className: false, - customClassName: false, - html: false, - reusable: false, - }, -}; diff --git a/extensions/blocks/simple-payments/paypal-button-2x.png b/extensions/blocks/simple-payments/paypal-button-2x.png deleted file mode 100644 index ceea141d3ae93..0000000000000 Binary files a/extensions/blocks/simple-payments/paypal-button-2x.png and /dev/null differ diff --git a/extensions/blocks/simple-payments/paypal-button.png b/extensions/blocks/simple-payments/paypal-button.png deleted file mode 100644 index 13bbad02e25fa..0000000000000 Binary files a/extensions/blocks/simple-payments/paypal-button.png and /dev/null differ diff --git a/extensions/blocks/simple-payments/product-placeholder.js b/extensions/blocks/simple-payments/product-placeholder.js deleted file mode 100644 index c8f82870ed03e..0000000000000 --- a/extensions/blocks/simple-payments/product-placeholder.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '../../utils/i18n'; - -/** - * Internal dependencies - */ -import './product-placeholder.scss'; -import paypalImage from './paypal-button.png'; -import paypalImage2x from './paypal-button-2x.png'; - -export default ( { - title = '', - content = '', - formattedPrice = '', - multiple = false, - featuredMediaUrl = null, - featuredMediaTitle = null, -} ) => ( - <div className="jetpack-simple-payments-wrapper"> - <div className="jetpack-simple-payments-product"> - { featuredMediaUrl && ( - <div className="jetpack-simple-payments-product-image"> - <figure className="jetpack-simple-payments-image"> - <img src={ featuredMediaUrl } alt={ featuredMediaTitle } /> - </figure> - </div> - ) } - <div className="jetpack-simple-payments-details"> - { title && ( - <div className="jetpack-simple-payments-title"> - <p>{ title }</p> - </div> - ) } - { content && ( - <div className="jetpack-simple-payments-description"> - <p>{ content }</p> - </div> - ) } - { formattedPrice && ( - <div className="jetpack-simple-payments-price"> - <p>{ formattedPrice }</p> - </div> - ) } - <div className="jetpack-simple-payments-purchase-box"> - { multiple && ( - <div className="jetpack-simple-payments-items"> - <input - className="jetpack-simple-payments-items-number" - readOnly - type="number" - value="1" - /> - </div> - ) } - <div className="jetpack-simple-payments-button"> - <img - alt={ __( 'Pay with PayPal' ) } - src={ paypalImage } - srcSet={ `${ paypalImage2x } 2x` } - /> - </div> - </div> - </div> - </div> - </div> -); diff --git a/extensions/blocks/simple-payments/product-placeholder.scss b/extensions/blocks/simple-payments/product-placeholder.scss deleted file mode 100644 index ca99e1e0b3aee..0000000000000 --- a/extensions/blocks/simple-payments/product-placeholder.scss +++ /dev/null @@ -1,92 +0,0 @@ - -.simple-payments__loading { - animation: simple-payments-loading 1600ms ease-in-out infinite; -} - -@keyframes simple-payments-loading { - 0% { - opacity: 0.5; - } - 50% { - opacity: 0.7; - } - 100% { - opacity: 0.5; - } -} - -.jetpack-simple-payments-wrapper { - margin-bottom: 1.5em; -} - -/* Higher specificity in order to reset paragraph style */ -body .jetpack-simple-payments-wrapper .jetpack-simple-payments-details p { - margin: 0 0 1.5em; - padding: 0; -} - -.jetpack-simple-payments-product { - display: flex; - flex-direction: column; -} - -.jetpack-simple-payments-product-image { - flex: 0 0 30%; - margin-bottom: 1.5em; -} - -.jetpack-simple-payments-image { - box-sizing: border-box; - min-width: 70px; - padding-top: 100%; - position: relative; -} - -.jetpack-simple-payments-image img { - border: 0; - border-radius: 0; - height: auto; - left: 50%; - margin: 0; - max-height: 100%; - max-width: 100%; - padding: 0; - position: absolute; - top: 50%; - transform: translate( -50%, -50% ); - width: auto; -} - -.jetpack-simple-payments-title p, -.jetpack-simple-payments-price p { - font-weight: bold; -} - -.jetpack-simple-payments-purchase-box { - align-items: flex-start; - display: flex; -} - -.jetpack-simple-payments-items { - flex: 0 0 auto; - margin-right: 10px; -} - -input[type='number'].jetpack-simple-payments-items-number { - background: var( --color-white ); - font-size: 16px; - line-height: 1; - max-width: 60px; - padding: 4px 8px; -} - -@media screen and ( min-width: 400px ) { - .jetpack-simple-payments-product { - flex-direction: row; - } - - .jetpack-simple-payments-product-image + .jetpack-simple-payments-details { - flex-basis: 70%; - padding-left: 1em; - } -} diff --git a/extensions/blocks/simple-payments/save.js b/extensions/blocks/simple-payments/save.js deleted file mode 100644 index ed81e7a8f1b6d..0000000000000 --- a/extensions/blocks/simple-payments/save.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * External dependencies - */ -import { RawHTML } from '@wordpress/element'; - -export default function Save( { attributes } ) { - const { productId } = attributes; - return productId ? <RawHTML>{ `[simple-payment id="${ productId }"]` }</RawHTML> : null; -} diff --git a/extensions/blocks/simple-payments/utils.js b/extensions/blocks/simple-payments/utils.js deleted file mode 100644 index c29e367baad3e..0000000000000 --- a/extensions/blocks/simple-payments/utils.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * External dependencies - */ -import { getCurrencyDefaults } from '@automattic/format-currency'; -import { trimEnd } from 'lodash'; - -/** - * Internal dependencies - */ -import { SIMPLE_PAYMENTS_PRODUCT_POST_TYPE } from './constants'; - -export const isValidSimplePaymentsProduct = product => - product.type === SIMPLE_PAYMENTS_PRODUCT_POST_TYPE && product.status === 'publish'; - -// based on https://stackoverflow.com/a/10454560/59752 -export const decimalPlaces = number => { - const match = ( '' + number ).match( /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/ ); - if ( ! match ) { - return 0; - } - return Math.max( 0, ( match[ 1 ] ? match[ 1 ].length : 0 ) - ( match[ 2 ] ? +match[ 2 ] : 0 ) ); -}; - -export const formatPrice = ( price, currency, withSymbol = true ) => { - const { precision, symbol } = getCurrencyDefaults( currency ); - const value = price.toFixed( precision ); - // Trim the dot at the end of symbol, e.g., 'kr.' becomes 'kr' - return withSymbol ? `${ value } ${ trimEnd( symbol, '.' ) }` : value; -}; diff --git a/extensions/blocks/slideshow/create-swiper.js b/extensions/blocks/slideshow/create-swiper.js deleted file mode 100644 index 72a54f56a93fe..0000000000000 --- a/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/extensions/blocks/slideshow/edit.js b/extensions/blocks/slideshow/edit.js deleted file mode 100644 index 0934cb7b5cfcc..0000000000000 --- a/extensions/blocks/slideshow/edit.js +++ /dev/null @@ -1,229 +0,0 @@ -/** - * External dependencies - */ -import { __, _x } from '../../utils/i18n'; -import { isBlobURL } from '@wordpress/blob'; -import { compose } from '@wordpress/compose'; -import { withDispatch } from '@wordpress/data'; -import { Component, Fragment } from '@wordpress/element'; -import { - BlockControls, - MediaUpload, - MediaPlaceholder, - InspectorControls, - mediaUpload, -} from '@wordpress/editor'; - -import { - DropZone, - FormFileUpload, - IconButton, - PanelBody, - RangeControl, - SelectControl, - ToggleControl, - Toolbar, - withNotices, -} from '@wordpress/components'; -import { filter, pick } from 'lodash'; - -/** - * Internal dependencies - */ -import Slideshow from './slideshow'; -import './editor.scss'; - -const ALLOWED_MEDIA_TYPES = [ 'image' ]; - -const effectOptions = [ - { label: _x( 'Slide', 'Slideshow transition effect' ), value: 'slide' }, - { label: _x( 'Fade', 'Slideshow transition effect' ), value: 'fade' }, -]; - -export const pickRelevantMediaFiles = image => - pick( image, [ 'alt', 'id', 'link', 'url', 'caption' ] ); - -class SlideshowEdit extends Component { - constructor() { - super( ...arguments ); - this.state = { - selectedImage: null, - }; - } - onSelectImages = images => { - const { setAttributes } = this.props; - const mapped = images.map( image => pickRelevantMediaFiles( image ) ); - setAttributes( { - images: mapped, - } ); - }; - onRemoveImage = index => { - return () => { - const images = filter( this.props.attributes.images, ( img, i ) => index !== i ); - this.setState( { selectedImage: null } ); - this.props.setAttributes( { images } ); - }; - }; - addFiles = files => { - const currentImages = this.props.attributes.images || []; - const { lockPostSaving, unlockPostSaving, noticeOperations, setAttributes } = this.props; - const lockName = 'slideshowBlockLock'; - lockPostSaving( lockName ); - mediaUpload( { - allowedTypes: ALLOWED_MEDIA_TYPES, - filesList: files, - onFileChange: images => { - const imagesNormalized = images.map( image => pickRelevantMediaFiles( image ) ); - 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' ) }> - <ToggleControl - label={ __( 'Autoplay' ) } - help={ __( 'Autoplay between slides' ) } - checked={ autoplay } - onChange={ value => { - setAttributes( { autoplay: value } ); - } } - /> - { autoplay && ( - <RangeControl - label={ __( 'Delay between transitions (in seconds)' ) } - 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.' - ) } - </span> - ) } - </PanelBody> - <PanelBody title={ __( 'Effects' ) }> - <SelectControl - label={ __( 'Transition effect' ) } - 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' ) } - icon="edit" - onClick={ open } - /> - ) } - /> - </Toolbar> - ) } - </BlockControls> - </Fragment> - ); - - if ( images.length === 0 ) { - return ( - <Fragment> - { controls } - <MediaPlaceholder - icon="format-gallery" - className={ className } - labels={ { - title: __( 'Slideshow' ), - instructions: __( 'Drag images, upload new ones or select files from your library.' ), - } } - 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' ) } - </FormFileUpload> - </div> - ) } - </Fragment> - ); - } -} -export default compose( - withDispatch( dispatch => { - const { lockPostSaving, unlockPostSaving } = dispatch( 'core/editor' ); - return { - lockPostSaving, - unlockPostSaving, - }; - } ), - withNotices -)( SlideshowEdit ); diff --git a/extensions/blocks/slideshow/editor.js b/extensions/blocks/slideshow/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/slideshow/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/slideshow/editor.scss b/extensions/blocks/slideshow/editor.scss deleted file mode 100644 index b04e53eec9655..0000000000000 --- a/extensions/blocks/slideshow/editor.scss +++ /dev/null @@ -1,43 +0,0 @@ -.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 #555d66; - } - } - -} - -.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/extensions/blocks/slideshow/index.js b/extensions/blocks/slideshow/index.js deleted file mode 100644 index 1844a9e5f07fb..0000000000000 --- a/extensions/blocks/slideshow/index.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * External dependencies - */ -import { Path, SVG } from '@wordpress/components'; -import { __ } from '../../utils/i18n'; - -/** - * Internal dependencies - */ -import edit from './edit'; -import save from './save'; -import transforms from './transforms'; - -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, - }, - 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' ), - category: 'jetpack', - keywords: [ __( 'image' ), __( 'gallery' ), __( 'slider' ) ], - description: __( 'Add an interactive slideshow.' ), - attributes, - supports: { - align: [ 'center', 'wide', 'full' ], - html: false, - }, - icon, - edit, - save, - transforms, -}; diff --git a/extensions/blocks/slideshow/save.js b/extensions/blocks/slideshow/save.js deleted file mode 100644 index 59879ded67abd..0000000000000 --- a/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/extensions/blocks/slideshow/slideshow.js b/extensions/blocks/slideshow/slideshow.js deleted file mode 100644 index 9d395df75c5c0..0000000000000 --- a/extensions/blocks/slideshow/slideshow.js +++ /dev/null @@ -1,228 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '../../utils/i18n'; -import ResizeObserver from 'resize-observer-polyfill'; -import classnames from 'classnames'; -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.' ) ); - } ); - } - - 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 - ) { - const realIndex = - images.length === prevProps.images.length - ? this.swiperInstance.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.' ) ); - } ); - } - } - - 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/extensions/blocks/slideshow/slideshow.php b/extensions/blocks/slideshow/slideshow.php index 4f53ef8f10b32..3004594137290 100644 --- a/extensions/blocks/slideshow/slideshow.php +++ b/extensions/blocks/slideshow/slideshow.php @@ -25,7 +25,6 @@ function jetpack_slideshow_block_load_assets( $attr, $content ) { $dependencies = array( 'lodash', - 'wp-escape-html', 'wp-polyfill', ); diff --git a/extensions/blocks/slideshow/style.scss b/extensions/blocks/slideshow/style.scss deleted file mode 100644 index 701658fbb9ba1..0000000000000 --- a/extensions/blocks/slideshow/style.scss +++ /dev/null @@ -1,162 +0,0 @@ -.wp-block-jetpack-slideshow { - margin-bottom: 1.5em; - 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 #fff; - 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: #fff; - 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/extensions/blocks/slideshow/swiper-callbacks.js b/extensions/blocks/slideshow/swiper-callbacks.js deleted file mode 100644 index 6765f3b2270e7..0000000000000 --- a/extensions/blocks/slideshow/swiper-callbacks.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * External dependencies - */ -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/extensions/blocks/slideshow/transforms.js b/extensions/blocks/slideshow/transforms.js deleted file mode 100644 index f0fba8cb1335e..0000000000000 --- a/extensions/blocks/slideshow/transforms.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * External dependencies - */ -import { createBlock } from '@wordpress/blocks'; -import { filter } from 'lodash'; - -const transforms = { - from: [ - { - type: 'block', - blocks: [ 'core/gallery', 'jetpack/tiled-gallery' ], - transform: attributes => { - const validImages = filter( attributes.images, ( { id, url } ) => id && url ); - if ( validImages.length > 0 ) { - return createBlock( 'jetpack/slideshow', { - images: validImages.map( ( { id, url, alt, caption } ) => ( { - id, - url, - alt, - caption, - } ) ), - } ); - } - return createBlock( 'jetpack/slideshow' ); - }, - }, - ], - to: [ - { - type: 'block', - blocks: [ 'core/gallery' ], - transform: ( { images } ) => createBlock( 'core/gallery', { images } ), - }, - { - type: 'block', - blocks: [ 'jetpack/tiled-gallery' ], - transform: ( { images } ) => createBlock( 'jetpack/tiled-gallery', { images }, [] ), - }, - { - 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/extensions/blocks/slideshow/view.js b/extensions/blocks/slideshow/view.js deleted file mode 100644 index 6d8078972525f..0000000000000 --- a/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' ); - } ); - } ); - } ); diff --git a/extensions/blocks/subscriptions/edit.js b/extensions/blocks/subscriptions/edit.js deleted file mode 100644 index 787a49a1d411e..0000000000000 --- a/extensions/blocks/subscriptions/edit.js +++ /dev/null @@ -1,82 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { TextControl, ToggleControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; -import SubmitButton from '../../utils/submit-button'; -import apiFetch from '@wordpress/api-fetch'; -import { sprintf, _n } from '@wordpress/i18n'; - -class SubscriptionEdit extends Component { - state = { - subscriberCountString: '', - }; - - componentDidMount() { - // Get the subscriber count so it is available right away if the user toggles the setting - this.get_subscriber_count(); - } - - render() { - const { attributes, className, isSelected, setAttributes } = this.props; - const { subscribePlaceholder, showSubscribersTotal } = attributes; - - if ( isSelected ) { - return ( - <div className={ className } role="form"> - <ToggleControl - label={ __( 'Show total subscribers' ) } - checked={ showSubscribersTotal } - onChange={ () => { - setAttributes( { showSubscribersTotal: ! showSubscribersTotal } ); - } } - /> - <TextControl - placeholder={ subscribePlaceholder } - disabled={ true } - onChange={ () => {} } - /> - <SubmitButton { ...this.props } /> - </div> - ); - } - - return ( - <div className={ className } role="form"> - { showSubscribersTotal && <p role="heading">{ this.state.subscriberCountString }</p> } - <TextControl placeholder={ subscribePlaceholder } /> - - <SubmitButton { ...this.props } /> - </div> - ); - } - - get_subscriber_count() { - apiFetch( { path: '/wpcom/v2/subscribers/count' } ).then( count => { - // Handle error condition - if ( ! count.hasOwnProperty( 'count' ) ) { - this.setState( { - subscriberCountString: __( 'Subscriber count unavailable' ), - } ); - } else { - this.setState( { - subscriberCountString: sprintf( - _n( 'Join %s other subscriber', 'Join %s other subscribers', count.count ), - count.count - ), - } ); - } - } ); - } - - onChangeSubmit( submitButtonText ) { - this.props.setAttributes( { submitButtonText } ); - } -} - -export default SubscriptionEdit; diff --git a/extensions/blocks/subscriptions/editor.js b/extensions/blocks/subscriptions/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/subscriptions/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/subscriptions/index.js b/extensions/blocks/subscriptions/index.js deleted file mode 100644 index fbd27697d5756..0000000000000 --- a/extensions/blocks/subscriptions/index.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Internal dependencies - */ -import edit from './edit'; -import save from './save'; -import { __ } from '../../utils/i18n'; -import renderMaterialIcon from '../../utils/render-material-icon'; -import { Path } from '@wordpress/components'; -import { isEmpty } from 'lodash'; -import { RawHTML } from '@wordpress/element'; - -export const name = 'subscriptions'; -export const settings = { - title: __( 'Subscription Form' ), - - description: ( - <p> - { __( - 'A form enabling readers to get notifications when new posts are published from this site.' - ) } - </p> - ), - icon: renderMaterialIcon( - <Path d="M23 16v2h-3v3h-2v-3h-3v-2h3v-3h2v3h3zM20 2v9h-4v3h-3v4H4c-1.1 0-2-.9-2-2V2h18zM8 13v-1H4v1h4zm3-3H4v1h7v-1zm0-2H4v1h7V8zm7-4H4v2h14V4z" /> - ), - category: 'jetpack', - - keywords: [ __( 'subscribe' ), __( 'join' ), __( 'follow' ) ], - - attributes: { - subscribePlaceholder: { type: 'string', default: __( 'Email Address' ) }, - subscribeButton: { type: 'string', default: __( 'Subscribe' ) }, - showSubscribersTotal: { type: 'boolean', default: false }, - submitButtonText: { - type: 'string', - default: __( 'Subscribe' ), - }, - customBackgroundButtonColor: { type: 'string' }, - customTextButtonColor: { type: 'string' }, - submitButtonClasses: { type: 'string' }, - }, - edit, - save, - deprecated: [ - { - attributes: { - subscribeButton: { type: 'string', default: __( 'Subscribe' ) }, - showSubscribersTotal: { type: 'boolean', default: false }, - }, - migrate: attr => { - return { - subscribeButton: '', - submitButtonText: attr.subscribeButton, - showSubscribersTotal: attr.showSubscribersTotal, - customBackgroundButtonColor: '', - customTextButtonColor: '', - submitButtonClasses: '', - }; - }, - - isEligible: attr => { - if ( ! isEmpty( attr.subscribeButton ) ) { - return false; - } - return true; - }, - save: function( { attributes } ) { - return ( - <RawHTML>{ `[jetpack_subscription_form show_subscribers_total="${ - attributes.showSubscribersTotal - }" show_only_email_and_button="true"]` }</RawHTML> - ); - }, - }, - ], -}; diff --git a/extensions/blocks/subscriptions/save.js b/extensions/blocks/subscriptions/save.js deleted file mode 100644 index a7db7fe6288a8..0000000000000 --- a/extensions/blocks/subscriptions/save.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * External dependencies - */ -import { RawHTML } from '@wordpress/element'; - -export default function Save( { attributes } ) { - const { - showSubscribersTotal, - submitButtonClasses, - customBackgroundButtonColor, - customTextButtonColor, - submitButtonText, - } = attributes; - return ( - <RawHTML>{ `[jetpack_subscription_form show_only_email_and_button="true" custom_background_button_color="${ customBackgroundButtonColor }" custom_text_button_color="${ customTextButtonColor }" submit_button_text="${ submitButtonText }" submit_button_classes="${ submitButtonClasses }" show_subscribers_total="${ showSubscribersTotal }" ]` }</RawHTML> - ); -} diff --git a/extensions/blocks/tiled-gallery/constants.js b/extensions/blocks/tiled-gallery/constants.js deleted file mode 100644 index 0df9073729066..0000000000000 --- a/extensions/blocks/tiled-gallery/constants.js +++ /dev/null @@ -1,28 +0,0 @@ -export const ALLOWED_MEDIA_TYPES = [ 'image' ]; -export const DEFAULT_GALLERY_WIDTH = 580; -export const GUTTER_WIDTH = 4; -export const MAX_COLUMNS = 20; -export const PHOTON_MAX_RESIZE = 2000; - -/** - * Layouts - */ -export const LAYOUT_CIRCLE = 'circle'; -export const LAYOUT_COLUMN = 'columns'; -export const LAYOUT_DEFAULT = 'rectangular'; -export const LAYOUT_SQUARE = 'square'; -export const LAYOUT_STYLES = [ - { - isDefault: true, - name: LAYOUT_DEFAULT, - }, - { - name: LAYOUT_CIRCLE, - }, - { - name: LAYOUT_SQUARE, - }, - { - name: LAYOUT_COLUMN, - }, -]; diff --git a/extensions/blocks/tiled-gallery/css-gram.scss b/extensions/blocks/tiled-gallery/css-gram.scss deleted file mode 100644 index 9fd2f49c52aa1..0000000000000 --- a/extensions/blocks/tiled-gallery/css-gram.scss +++ /dev/null @@ -1,86 +0,0 @@ -/** - * This code is based on CSS gram: - * https://github.com/una/CSSgram/tree/master - * - * Due to the packaging options available, the source has been duplicated and adapted here - * to best fit our specific needs. - */ - -/* From https://github.com/una/CSSgram/blob/0.1.12/source/scss/_shared.scss */ -@mixin pseudo-elem { - content: ''; - display: block; - height: 100%; - width: 100%; - top: 0; - left: 0; - position: absolute; - pointer-events: none; -} - -@mixin filter-base { - position: relative; - - img { - width: 100%; - z-index: 1; - } - - &::before { - @include pseudo-elem; - z-index: 2; - } - - &::after { - @include pseudo-elem; - z-index: 3; - } -} - -/** - * 1977 - * From https://github.com/una/CSSgram/blob/0.1.12/source/scss/1977.scss - */ -@mixin _1977( $filters... ) { - @include filter-base; - filter: contrast( 1.1 ) brightness( 1.1 ) saturate( 1.3 ) $filters; - - &::after { - background: rgba( 243, 106, 188, 0.3 ); - mix-blend-mode: screen; - } - - @content; -} - -/* - * Clarendon - * From https://github.com/una/CSSgram/blob/0.1.12/source/scss/clarendon.scss - */ -@mixin clarendon( $filters... ) { - @include filter-base; - filter: contrast( 1.2 ) saturate( 1.35 ) $filters; - - &::before { - background: rgba( 127, 187, 227, 0.2 ); - mix-blend-mode: overlay; - } - - @content; -} - -/** - * Gingham - * From https://github.com/una/CSSgram/blob/0.1.12/source/scss/gingham.scss - */ -@mixin gingham( $filters... ) { - @include filter-base; - filter: brightness( 1.05 ) hue-rotate( -10deg ) $filters; - - &::after { - background: rgb( 230, 230, 250 ); - mix-blend-mode: soft-light; - } - - @content; -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/constants.js b/extensions/blocks/tiled-gallery/deprecated/v1/constants.js deleted file mode 100644 index 55a451fccf618..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/constants.js +++ /dev/null @@ -1,27 +0,0 @@ -export const ALLOWED_MEDIA_TYPES = [ 'image' ]; -export const GUTTER_WIDTH = 4; -export const MAX_COLUMNS = 20; -export const PHOTON_MAX_RESIZE = 2000; - -/** - * Layouts - */ -export const LAYOUT_CIRCLE = 'circle'; -export const LAYOUT_COLUMN = 'columns'; -export const LAYOUT_DEFAULT = 'rectangular'; -export const LAYOUT_SQUARE = 'square'; -export const LAYOUT_STYLES = [ - { - isDefault: true, - name: LAYOUT_DEFAULT, - }, - { - name: LAYOUT_CIRCLE, - }, - { - name: LAYOUT_SQUARE, - }, - { - name: LAYOUT_COLUMN, - }, -]; diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/image.js b/extensions/blocks/tiled-gallery/deprecated/v1/image.js deleted file mode 100644 index 61d4a2cd05cef..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/image.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * External Dependencies - */ -import { isBlobURL } from '@wordpress/blob'; - -export default function GalleryImageSave( props ) { - const { - 'aria-label': ariaLabel, - alt, - // caption, - height, - id, - link, - linkTo, - origUrl, - url, - width, - } = props; - - if ( isBlobURL( origUrl ) ) { - return null; - } - - let href; - - switch ( linkTo ) { - case 'media': - href = url; - break; - case 'attachment': - href = link; - break; - } - - const img = ( - <img - alt={ alt } - aria-label={ ariaLabel } - data-height={ height } - data-id={ id } - data-link={ link } - data-url={ origUrl } - data-width={ width } - src={ url } - /> - ); - - return ( - <figure className="tiled-gallery__item">{ href ? <a href={ href }>{ img }</a> : img }</figure> - ); -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/index.js b/extensions/blocks/tiled-gallery/deprecated/v1/index.js deleted file mode 100644 index 69539d007cdef..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/index.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Internal dependencies - */ -export { default as save } from './save'; -import { LAYOUT_DEFAULT } from './constants'; - -export const attributes = { - // Set default align - align: { - default: 'center', - type: 'string', - }, - // Set default className (used with block styles) - className: { - default: `is-style-${ LAYOUT_DEFAULT }`, - type: 'string', - }, - columns: { - type: 'number', - }, - ids: { - default: [], - type: 'array', - }, - images: { - type: 'array', - default: [], - source: 'query', - selector: '.tiled-gallery__item', - query: { - alt: { - attribute: 'alt', - default: '', - selector: 'img', - source: 'attribute', - }, - caption: { - selector: 'figcaption', - source: 'html', - type: 'string', - }, - height: { - attribute: 'data-height', - selector: 'img', - source: 'attribute', - type: 'number', - }, - id: { - attribute: 'data-id', - selector: 'img', - source: 'attribute', - }, - link: { - attribute: 'data-link', - selector: 'img', - source: 'attribute', - }, - url: { - attribute: 'data-url', - selector: 'img', - source: 'attribute', - }, - width: { - attribute: 'data-width', - selector: 'img', - source: 'attribute', - type: 'number', - }, - }, - }, - linkTo: { - default: 'none', - type: 'string', - }, -}; - -export const support = { - align: [ 'center', 'wide', 'full' ], - customClassName: false, - html: false, -}; diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/column.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/column.js deleted file mode 100644 index a3ed5cdf04cbb..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/column.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Column( { children } ) { - return <div className="tiled-gallery__col">{ children }</div>; -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/gallery.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/gallery.js deleted file mode 100644 index 94fc61e4be980..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/gallery.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function Gallery( { children, galleryRef } ) { - return ( - <div className="tiled-gallery__gallery" ref={ galleryRef }> - { children } - </div> - ); -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/index.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/index.js deleted file mode 100644 index 0ae3f744860b0..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/index.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * External dependencies - */ -import photon from 'photon'; -import { Component } from '@wordpress/element'; -import { format as formatUrl, parse as parseUrl } from 'url'; -import { isBlobURL } from '@wordpress/blob'; -import { sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import Image from '../image'; -import Mosaic from './mosaic'; -import Square from './square'; -import { __ } from '../../../../../utils/i18n'; -import { PHOTON_MAX_RESIZE } from '../constants'; - -export default class Layout extends Component { - photonize( { height, width, url } ) { - if ( ! url ) { - return; - } - - // Do not Photonize images that are still uploading or from localhost - if ( isBlobURL( url ) || /^https?:\/\/localhost/.test( url ) ) { - return url; - } - - // Drop query args, photon URLs can't handle them - // This should be the "raw" url, we'll add dimensions later - const cleanUrl = url.split( '?', 1 )[ 0 ]; - - const photonImplementation = isWpcomFilesUrl( url ) ? photonWpcomImage : photon; - - const { layoutStyle } = this.props; - - if ( isSquareishLayout( layoutStyle ) && width && height ) { - const size = Math.min( PHOTON_MAX_RESIZE, width, height ); - return photonImplementation( cleanUrl, { resize: `${ size },${ size }` } ); - } - return photonImplementation( cleanUrl ); - } - - // This is tricky: - // - We need to "photonize" to resize the images at appropriate dimensions - // - The resize will depend on the image size and the layout in some cases - // - Handlers need to be created by index so that the image changes can be applied correctly. - // This is because the images are stored in an array in the block attributes. - renderImage( img, i ) { - const { images, linkTo, selectedImage } = this.props; - - /* translators: %1$d is the order number of the image, %2$d is the total number of images. */ - const ariaLabel = sprintf( __( 'image %1$d of %2$d in gallery' ), i + 1, images.length ); - return ( - <Image - alt={ img.alt } - aria-label={ ariaLabel } - height={ img.height } - id={ img.id } - origUrl={ img.url } - isSelected={ selectedImage === i } - key={ i } - link={ img.link } - linkTo={ linkTo } - url={ this.photonize( img ) } - width={ img.width } - /> - ); - } - - render() { - const { align, children, className, columns, images, layoutStyle } = this.props; - - const LayoutRenderer = isSquareishLayout( layoutStyle ) ? Square : Mosaic; - - const renderedImages = this.props.images.map( this.renderImage, this ); - - return ( - <div className={ className }> - <LayoutRenderer - align={ align } - columns={ columns } - images={ images } - layoutStyle={ layoutStyle } - renderedImages={ renderedImages } - /> - { children } - </div> - ); - } -} - -function isSquareishLayout( layout ) { - return [ 'circle', 'square' ].includes( layout ); -} - -function isWpcomFilesUrl( url ) { - const { host } = parseUrl( url ); - return /\.files\.wordpress\.com$/.test( host ); -} - -/** - * Apply photon arguments to *.files.wordpress.com images - * - * This function largely duplicates the functionlity of the photon.js lib. - * This is necessary because we want to serve images from *.files.wordpress.com so that private - * WordPress.com sites can use this block which depends on a Photon-like image service. - * - * If we pass all images through Photon servers, some images are unreachable. *.files.wordpress.com - * is already photon-like so we can pass it the same parameters for image resizing. - * - * @param {string} url Image url - * @param {Object} opts Options to pass to photon - * - * @return {string} Url string with options applied - */ -function photonWpcomImage( url, opts = {} ) { - // Adhere to the same options API as the photon.js lib - const photonLibMappings = { - width: 'w', - height: 'h', - letterboxing: 'lb', - removeLetterboxing: 'ulb', - }; - - // Discard some param parts - const { auth, hash, port, query, search, ...urlParts } = parseUrl( url ); - - // Build query - // This reduction intentionally mutates the query as it is built internally. - urlParts.query = Object.keys( opts ).reduce( - ( q, key ) => - Object.assign( q, { - [ photonLibMappings.hasOwnProperty( key ) ? photonLibMappings[ key ] : key ]: opts[ key ], - } ), - {} - ); - - return formatUrl( urlParts ); -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/index.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/index.js deleted file mode 100644 index 8c56b1641dd1e..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/index.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * External dependencies - */ -import { Component, createRef } from '@wordpress/element'; -import ResizeObserver from 'resize-observer-polyfill'; - -/** - * Internal dependencies - */ -import Column from '../column'; -import Gallery from '../gallery'; -import Row from '../row'; -import { getGalleryRows, handleRowResize } from './resize'; -import { imagesToRatios, ratiosToColumns, ratiosToMosaicRows } from './ratios'; - -export default class Mosaic extends Component { - gallery = createRef(); - pendingRaf = null; - ro = null; // resizeObserver instance - - componentDidMount() { - this.observeResize(); - } - - componentWillUnmount() { - this.unobserveResize(); - } - - componentDidUpdate( prevProps ) { - if ( prevProps.images !== this.props.images || prevProps.align !== this.props.align ) { - this.triggerResize(); - } else if ( 'columns' === this.props.layoutStyle && prevProps.columns !== this.props.columns ) { - this.triggerResize(); - } - } - - handleGalleryResize = entries => { - if ( this.pendingRaf ) { - cancelAnimationFrame( this.pendingRaf ); - this.pendingRaf = null; - } - this.pendingRaf = requestAnimationFrame( () => { - for ( const { contentRect, target } of entries ) { - const { width } = contentRect; - getGalleryRows( target ).forEach( row => handleRowResize( row, width ) ); - } - } ); - }; - - triggerResize() { - if ( this.gallery.current ) { - this.handleGalleryResize( [ - { - target: this.gallery.current, - contentRect: { width: this.gallery.current.clientWidth }, - }, - ] ); - } - } - - observeResize() { - this.triggerResize(); - this.ro = new ResizeObserver( this.handleGalleryResize ); - if ( this.gallery.current ) { - this.ro.observe( this.gallery.current ); - } - } - - unobserveResize() { - if ( this.ro ) { - this.ro.disconnect(); - this.ro = null; - } - if ( this.pendingRaf ) { - cancelAnimationFrame( this.pendingRaf ); - this.pendingRaf = null; - } - } - - render() { - const { align, columns, images, layoutStyle, renderedImages } = this.props; - - const ratios = imagesToRatios( images ); - const rows = - 'columns' === layoutStyle - ? ratiosToColumns( ratios, columns ) - : ratiosToMosaicRows( ratios, { isWide: [ 'full', 'wide' ].includes( align ) } ); - - let cursor = 0; - return ( - <Gallery galleryRef={ this.gallery }> - { rows.map( ( row, rowIndex ) => ( - <Row key={ rowIndex }> - { row.map( ( colSize, colIndex ) => { - const columnImages = renderedImages.slice( cursor, cursor + colSize ); - cursor += colSize; - return <Column key={ colIndex }>{ columnImages }</Column>; - } ) } - </Row> - ) ) } - </Gallery> - ); - } -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/ratios.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/ratios.js deleted file mode 100644 index 8accd552b710a..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/ratios.js +++ /dev/null @@ -1,280 +0,0 @@ -/** - * External dependencies - */ -import { - drop, - every, - isEqual, - map, - overEvery, - some, - sum, - take, - takeRight, - takeWhile, - zipWith, -} from 'lodash'; - -export function imagesToRatios( images ) { - return map( images, ratioFromImage ); -} - -export function ratioFromImage( { height, width } ) { - return height && width ? width / height : 1; -} - -/** - * Build three columns, each of which should contain approximately 1/3 of the total ratio - * - * @param {Array.<number>} ratios Ratios of images put into shape - * @param {number} columnCount Number of columns - * - * @return {Array.<Array.<number>>} Shape of rows and columns - */ -export function ratiosToColumns( ratios, columnCount ) { - // If we don't have more than 1 per column, just return a simple 1 ratio per column shape - if ( ratios.length <= columnCount ) { - return [ Array( ratios.length ).fill( 1 ) ]; - } - - const total = sum( ratios ); - const targetColRatio = total / columnCount; - - const row = []; - let toProcess = ratios; - let accumulatedRatio = 0; - - // We skip the last column in the loop and add rest later - for ( let i = 0; i < columnCount - 1; i++ ) { - const colSize = takeWhile( toProcess, ratio => { - const shouldTake = accumulatedRatio <= ( i + 1 ) * targetColRatio; - if ( shouldTake ) { - accumulatedRatio += ratio; - } - return shouldTake; - } ).length; - row.push( colSize ); - toProcess = drop( toProcess, colSize ); - } - - // Don't calculate last column, just add what's left - row.push( toProcess.length ); - - // A shape is an array of rows. Wrap our row in an array. - return [ row ]; -} - -/** - * These are partially applied functions. - * They rely on helper function (defined below) to create a function that expects to be passed ratios - * during processing. - * - * …FitsNextImages() functions should be passed ratios to be processed - * …IsNotRecent() functions should be passed the processed shapes - */ - -const reverseSymmetricRowIsNotRecent = isNotRecentShape( [ 2, 1, 2 ], 5 ); -const reverseSymmetricFitsNextImages = checkNextRatios( [ - isLandscape, - isLandscape, - isPortrait, - isLandscape, - isLandscape, -] ); -const longSymmetricRowFitsNextImages = checkNextRatios( [ - isLandscape, - isLandscape, - isLandscape, - isPortrait, - isLandscape, - isLandscape, - isLandscape, -] ); -const longSymmetricRowIsNotRecent = isNotRecentShape( [ 3, 1, 3 ], 5 ); -const symmetricRowFitsNextImages = checkNextRatios( [ - isPortrait, - isLandscape, - isLandscape, - isPortrait, -] ); -const symmetricRowIsNotRecent = isNotRecentShape( [ 1, 2, 1 ], 5 ); -const oneThreeFitsNextImages = checkNextRatios( [ - isPortrait, - isLandscape, - isLandscape, - isLandscape, -] ); -const oneThreeIsNotRecent = isNotRecentShape( [ 1, 3 ], 3 ); -const threeOneIsFitsNextImages = checkNextRatios( [ - isLandscape, - isLandscape, - isLandscape, - isPortrait, -] ); -const threeOneIsNotRecent = isNotRecentShape( [ 3, 1 ], 3 ); -const oneTwoFitsNextImages = checkNextRatios( [ - lt( 1.6 ), - overEvery( gte( 0.9 ), lt( 2 ) ), - overEvery( gte( 0.9 ), lt( 2 ) ), -] ); -const oneTwoIsNotRecent = isNotRecentShape( [ 1, 2 ], 3 ); -const fiveIsNotRecent = isNotRecentShape( [ 1, 1, 1, 1, 1 ], 1 ); -const fourIsNotRecent = isNotRecentShape( [ 1, 1, 1, 1 ], 1 ); -const threeIsNotRecent = isNotRecentShape( [ 1, 1, 1 ], 3 ); -const twoOneFitsNextImages = checkNextRatios( [ - overEvery( gte( 0.9 ), lt( 2 ) ), - overEvery( gte( 0.9 ), lt( 2 ) ), - lt( 1.6 ), -] ); -const twoOneIsNotRecent = isNotRecentShape( [ 2, 1 ], 3 ); -const panoramicFitsNextImages = checkNextRatios( [ isPanoramic ] ); - -export function ratiosToMosaicRows( ratios, { isWide } = {} ) { - // This function will recursively process the input until it is consumed - const go = ( processed, toProcess ) => { - if ( ! toProcess.length ) { - return processed; - } - - let next; - - if ( - /* Reverse_Symmetric_Row */ - toProcess.length > 15 && - reverseSymmetricFitsNextImages( toProcess ) && - reverseSymmetricRowIsNotRecent( processed ) - ) { - next = [ 2, 1, 2 ]; - } else if ( - /* Long_Symmetric_Row */ - toProcess.length > 15 && - longSymmetricRowFitsNextImages( toProcess ) && - longSymmetricRowIsNotRecent( processed ) - ) { - next = [ 3, 1, 3 ]; - } else if ( - /* Symmetric_Row */ - toProcess.length !== 5 && - symmetricRowFitsNextImages( toProcess ) && - symmetricRowIsNotRecent( processed ) - ) { - next = [ 1, 2, 1 ]; - } else if ( - /* One_Three */ - oneThreeFitsNextImages( toProcess ) && - oneThreeIsNotRecent( processed ) - ) { - next = [ 1, 3 ]; - } else if ( - /* Three_One */ - threeOneIsFitsNextImages( toProcess ) && - threeOneIsNotRecent( processed ) - ) { - next = [ 3, 1 ]; - } else if ( - /* One_Two */ - oneTwoFitsNextImages( toProcess ) && - oneTwoIsNotRecent( processed ) - ) { - next = [ 1, 2 ]; - } else if ( - /* Five */ - isWide && - ( toProcess.length === 5 || ( toProcess.length !== 10 && toProcess.length > 6 ) ) && - fiveIsNotRecent( processed ) && - sum( take( toProcess, 5 ) ) < 5 - ) { - next = [ 1, 1, 1, 1, 1 ]; - } else if ( - /* Four */ - isFourValidCandidate( processed, toProcess ) - ) { - next = [ 1, 1, 1, 1 ]; - } else if ( - /* Three */ - isThreeValidCandidate( processed, toProcess, isWide ) - ) { - next = [ 1, 1, 1 ]; - } else if ( - /* Two_One */ - twoOneFitsNextImages( toProcess ) && - twoOneIsNotRecent( processed ) - ) { - next = [ 2, 1 ]; - } else if ( /* Panoramic */ panoramicFitsNextImages( toProcess ) ) { - next = [ 1 ]; - } else if ( /* One_One */ toProcess.length > 3 ) { - next = [ 1, 1 ]; - } else { - // Everything left - next = Array( toProcess.length ).fill( 1 ); - } - - // Add row - const nextProcessed = processed.concat( [ next ] ); - - // Trim consumed images from next processing step - const consumedImages = sum( next ); - const nextToProcess = toProcess.slice( consumedImages ); - - return go( nextProcessed, nextToProcess ); - }; - return go( [], ratios ); -} - -function isThreeValidCandidate( processed, toProcess, isWide ) { - const ratio = sum( take( toProcess, 3 ) ); - return ( - toProcess.length >= 3 && - toProcess.length !== 4 && - toProcess.length !== 6 && - threeIsNotRecent( processed ) && - ( ratio < 2.5 || - ( ratio < 5 && - /* nextAreSymettric */ - ( toProcess.length >= 3 && - /* @FIXME floating point equality?? */ toProcess[ 0 ] === toProcess[ 2 ] ) ) || - isWide ) - ); -} - -function isFourValidCandidate( processed, toProcess ) { - const ratio = sum( take( toProcess, 4 ) ); - return ( - ( fourIsNotRecent( processed ) && ( ratio < 3.5 && toProcess.length > 5 ) ) || - ( ratio < 7 && toProcess.length === 4 ) - ); -} - -function isNotRecentShape( shape, numRecents ) { - return recents => - ! some( takeRight( recents, numRecents ), recentShape => isEqual( recentShape, shape ) ); -} - -function checkNextRatios( shape ) { - return ratios => - ratios.length >= shape.length && - every( zipWith( shape, ratios.slice( 0, shape.length ), ( f, r ) => f( r ) ) ); -} - -function isLandscape( ratio ) { - return ratio >= 1 && ratio < 2; -} - -function isPortrait( ratio ) { - return ratio < 1; -} - -function isPanoramic( ratio ) { - return ratio >= 2; -} - -// >= -function gte( n ) { - return m => m >= n; -} - -// < -function lt( n ) { - return m => m < n; -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/resize.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/resize.js deleted file mode 100644 index 022729c8bac72..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/mosaic/resize.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Internal dependencies - */ -import { GUTTER_WIDTH } from '../../constants'; - -/** - * Distribute a difference across ns so that their sum matches the target - * - * @param {Array<number>} parts Array of numbers to fit - * @param {number} target Number that sum should match - * @return {Array<number>} Adjusted parts - */ -function adjustFit( parts, target ) { - const diff = target - parts.reduce( ( sum, n ) => sum + n, 0 ); - const partialDiff = diff / parts.length; - return parts.map( p => p + partialDiff ); -} - -export function handleRowResize( row, width ) { - applyRowRatio( row, getRowRatio( row ), width ); -} - -function getRowRatio( row ) { - const result = getRowCols( row ) - .map( getColumnRatio ) - .reduce( - ( [ ratioA, weightedRatioA ], [ ratioB, weightedRatioB ] ) => { - return [ ratioA + ratioB, weightedRatioA + weightedRatioB ]; - }, - [ 0, 0 ] - ); - return result; -} - -export function getGalleryRows( gallery ) { - return Array.from( gallery.querySelectorAll( '.tiled-gallery__row' ) ); -} - -function getRowCols( row ) { - return Array.from( row.querySelectorAll( '.tiled-gallery__col' ) ); -} - -function getColImgs( col ) { - return Array.from( - col.querySelectorAll( '.tiled-gallery__item > img, .tiled-gallery__item > a > img' ) - ); -} - -function getColumnRatio( col ) { - const imgs = getColImgs( col ); - const imgCount = imgs.length; - const ratio = - 1 / - imgs.map( getImageRatio ).reduce( ( partialColRatio, imgRatio ) => { - return partialColRatio + 1 / imgRatio; - }, 0 ); - const result = [ ratio, ratio * imgCount || 1 ]; - return result; -} - -function getImageRatio( img ) { - const w = parseInt( img.dataset.width, 10 ); - const h = parseInt( img.dataset.height, 10 ); - const result = w && ! Number.isNaN( w ) && h && ! Number.isNaN( h ) ? w / h : 1; - return result; -} - -function applyRowRatio( row, [ ratio, weightedRatio ], width ) { - const rawHeight = - ( 1 / ratio ) * ( width - GUTTER_WIDTH * ( row.childElementCount - 1 ) - weightedRatio ); - - applyColRatio( row, { - rawHeight, - rowWidth: width - GUTTER_WIDTH * ( row.childElementCount - 1 ), - } ); -} - -function applyColRatio( row, { rawHeight, rowWidth } ) { - const cols = getRowCols( row ); - - const colWidths = cols.map( - col => ( rawHeight - GUTTER_WIDTH * ( col.childElementCount - 1 ) ) * getColumnRatio( col )[ 0 ] - ); - - const adjustedWidths = adjustFit( colWidths, rowWidth ); - - cols.forEach( ( col, i ) => { - const rawWidth = colWidths[ i ]; - const width = adjustedWidths[ i ]; - applyImgRatio( col, { - colHeight: rawHeight - GUTTER_WIDTH * ( col.childElementCount - 1 ), - width, - rawWidth, - } ); - } ); -} - -function applyImgRatio( col, { colHeight, width, rawWidth } ) { - const imgHeights = getColImgs( col ).map( img => rawWidth / getImageRatio( img ) ); - const adjustedHeights = adjustFit( imgHeights, colHeight ); - - // Set size of col children, not the <img /> element - Array.from( col.children ).forEach( ( item, i ) => { - const height = adjustedHeights[ i ]; - item.setAttribute( 'style', `height:${ height }px;width:${ width }px;` ); - } ); -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/row.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/row.js deleted file mode 100644 index 200a58c2e3acf..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/row.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -export default function Row( { children, className } ) { - return <div className={ classnames( 'tiled-gallery__row', className ) }>{ children }</div>; -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/layout/square.js b/extensions/blocks/tiled-gallery/deprecated/v1/layout/square.js deleted file mode 100644 index 2a1ab888b1916..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/layout/square.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * External dependencies - */ -import { chunk, drop, take } from 'lodash'; - -/** - * Internal dependencies - */ -import Row from './row'; -import Column from './column'; -import Gallery from './gallery'; -import { MAX_COLUMNS } from '../constants'; - -export default function Square( { columns, renderedImages } ) { - const columnCount = Math.min( MAX_COLUMNS, columns ); - - const remainder = renderedImages.length % columnCount; - - return ( - <Gallery> - { [ - ...( remainder ? [ take( renderedImages, remainder ) ] : [] ), - ...chunk( drop( renderedImages, remainder ), columnCount ), - ].map( ( imagesInRow, rowIndex ) => ( - <Row key={ rowIndex } className={ `columns-${ imagesInRow.length }` }> - { imagesInRow.map( ( image, colIndex ) => ( - <Column key={ colIndex }>{ image }</Column> - ) ) } - </Row> - ) ) } - </Gallery> - ); -} diff --git a/extensions/blocks/tiled-gallery/deprecated/v1/save.js b/extensions/blocks/tiled-gallery/deprecated/v1/save.js deleted file mode 100644 index 1a5b0b66eff79..0000000000000 --- a/extensions/blocks/tiled-gallery/deprecated/v1/save.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Internal dependencies - */ -import Layout from './layout'; -import { getActiveStyleName } from '../../../../utils'; -import { LAYOUT_STYLES } from './constants'; - -export function defaultColumnsNumber( attributes ) { - return Math.min( 3, attributes.images.length ); -} - -export default function TiledGallerySave( { attributes } ) { - const { images } = attributes; - - if ( ! images.length ) { - return null; - } - - const { align, className, columns = defaultColumnsNumber( attributes ), linkTo } = attributes; - - return ( - <Layout - align={ align } - className={ className } - columns={ columns } - images={ images } - layoutStyle={ getActiveStyleName( LAYOUT_STYLES, className ) } - linkTo={ linkTo } - /> - ); -} diff --git a/extensions/blocks/tiled-gallery/edit.js b/extensions/blocks/tiled-gallery/edit.js deleted file mode 100644 index 17524c6d20235..0000000000000 --- a/extensions/blocks/tiled-gallery/edit.js +++ /dev/null @@ -1,293 +0,0 @@ -/** - * External Dependencies - */ -import { Component, Fragment } from '@wordpress/element'; -import { filter, get, pick } from 'lodash'; -import { - BlockControls, - InspectorControls, - MediaPlaceholder, - MediaUpload, - mediaUpload, -} from '@wordpress/editor'; -import { - DropZone, - FormFileUpload, - IconButton, - PanelBody, - RangeControl, - SelectControl, - Toolbar, - withNotices, -} from '@wordpress/components'; - -/** - * Internal dependencies - */ -import FilterToolbar from './filter-toolbar'; -import Layout from './layout'; -import { __ } from '../../utils/i18n'; -import { ALLOWED_MEDIA_TYPES, LAYOUT_STYLES, MAX_COLUMNS } from './constants'; -import { getActiveStyleName } from '../../utils'; -import { icon } from '.'; - -const linkOptions = [ - { value: 'attachment', label: __( 'Attachment Page' ) }, - { value: 'media', label: __( 'Media File' ) }, - { value: 'none', label: __( 'None' ) }, -]; - -// @TODO keep here or move to ./layout ? -function layoutSupportsColumns( layout ) { - return [ 'columns', 'circle', 'square' ].includes( layout ); -} - -export function defaultColumnsNumber( attributes ) { - return Math.min( 3, attributes.images.length ); -} - -export const pickRelevantMediaFiles = image => { - const imageProps = pick( image, [ - [ 'alt' ], - [ 'id' ], - [ 'link' ], - /* @TODO Captions disabled [ 'caption' ], */ - ] ); - imageProps.url = - get( image, [ 'sizes', 'large', 'url' ] ) || - get( image, [ 'media_details', 'sizes', 'large', 'source_url' ] ) || - image.url; - return imageProps; -}; - -class TiledGalleryEdit extends Component { - state = { - selectedImage: null, - }; - - static getDerivedStateFromProps( props, state ) { - // Deselect images when deselecting the block - if ( ! props.isSelected && null !== state.selectedImage ) { - return { selectedImage: null }; - } - return 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 ); - } - - addFiles = files => { - const currentImages = this.props.attributes.images || []; - const { noticeOperations } = this.props; - mediaUpload( { - allowedTypes: ALLOWED_MEDIA_TYPES, - filesList: files, - onFileChange: images => { - const imagesNormalized = images.map( image => pickRelevantMediaFiles( image ) ); - this.setAttributes( { images: currentImages.concat( imagesNormalized ) } ); - }, - onError: noticeOperations.createErrorNotice, - } ); - }; - - onRemoveImage = index => () => { - const images = filter( this.props.attributes.images, ( img, i ) => index !== i ); - const { columns } = this.props.attributes; - this.setState( { - selectedImage: null, - } ); - this.setAttributes( { - images, - columns: columns ? Math.min( images.length, columns ) : columns, - } ); - }; - - onSelectImage = index => () => { - if ( this.state.selectedImage !== index ) { - this.setState( { - selectedImage: index, - } ); - } - }; - - onSelectImages = images => { - const { columns } = this.props.attributes; - this.setAttributes( { - columns: columns ? Math.min( images.length, columns ) : columns, - images: images.map( image => pickRelevantMediaFiles( image ) ), - } ); - }; - - setColumnsNumber = value => this.setAttributes( { columns: value } ); - - setImageAttributes = index => attributes => { - const { - attributes: { images }, - } = this.props; - if ( ! images[ index ] ) { - return; - } - this.setAttributes( { - images: [ - ...images.slice( 0, index ), - { ...images[ index ], ...attributes }, - ...images.slice( index + 1 ), - ], - } ); - }; - - setLinkTo = value => this.setAttributes( { linkTo: value } ); - - uploadFromFiles = event => this.addFiles( event.target.files ); - - render() { - const { selectedImage } = this.state; - const { - attributes, - isSelected, - className, - noticeOperations, - noticeUI, - setAttributes, - } = this.props; - const { - align, - columns = defaultColumnsNumber( attributes ), - imageFilter, - images, - linkTo, - } = attributes; - - const dropZone = <DropZone onFilesDrop={ this.addFiles } />; - - const controls = ( - <BlockControls> - { !! images.length && ( - <Fragment> - <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 Gallery' ) } - icon="edit" - onClick={ open } - /> - ) } - /> - </Toolbar> - <FilterToolbar - value={ imageFilter } - onChange={ value => { - setAttributes( { imageFilter: value } ); - this.setState( { selectedImage: null } ); - } } - /> - </Fragment> - ) } - </BlockControls> - ); - - if ( images.length === 0 ) { - return ( - <Fragment> - { controls } - <MediaPlaceholder - icon={ <div className="tiled-gallery__media-placeholder-icon">{ icon }</div> } - className={ className } - labels={ { - title: __( 'Tiled Gallery' ), - name: __( 'images' ), - } } - onSelect={ this.onSelectImages } - accept="image/*" - allowedTypes={ ALLOWED_MEDIA_TYPES } - multiple - notices={ noticeUI } - onError={ noticeOperations.createErrorNotice } - /> - </Fragment> - ); - } - - const layoutStyle = getActiveStyleName( LAYOUT_STYLES, attributes.className ); - - return ( - <Fragment> - { controls } - <InspectorControls> - <PanelBody title={ __( 'Tiled Gallery settings' ) }> - { layoutSupportsColumns( layoutStyle ) && images.length > 1 && ( - <RangeControl - label={ __( 'Columns' ) } - value={ columns } - onChange={ this.setColumnsNumber } - min={ 1 } - max={ Math.min( MAX_COLUMNS, images.length ) } - /> - ) } - <SelectControl - label={ __( 'Link To' ) } - value={ linkTo } - onChange={ this.setLinkTo } - options={ linkOptions } - /> - </PanelBody> - </InspectorControls> - - { noticeUI } - - <Layout - align={ align } - className={ className } - columns={ columns } - imageFilter={ imageFilter } - images={ images } - layoutStyle={ layoutStyle } - linkTo={ linkTo } - onRemoveImage={ this.onRemoveImage } - onSelectImage={ this.onSelectImage } - selectedImage={ isSelected ? selectedImage : null } - setImageAttributes={ this.setImageAttributes } - > - { dropZone } - { isSelected && ( - <div className="tiled-gallery__add-item"> - <FormFileUpload - multiple - isLarge - className="tiled-gallery__add-item-button" - onChange={ this.uploadFromFiles } - accept="image/*" - icon="insert" - > - { __( 'Upload an image' ) } - </FormFileUpload> - </div> - ) } - </Layout> - </Fragment> - ); - } -} - -export default withNotices( TiledGalleryEdit ); diff --git a/extensions/blocks/tiled-gallery/editor.js b/extensions/blocks/tiled-gallery/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/tiled-gallery/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/tiled-gallery/editor.scss b/extensions/blocks/tiled-gallery/editor.scss deleted file mode 100644 index 4d7c9f5597dd7..0000000000000 --- a/extensions/blocks/tiled-gallery/editor.scss +++ /dev/null @@ -1,183 +0,0 @@ -@import './view.scss'; -@import './variables.scss'; - -// inspired by from assets/shared/_animations loading-fade -@keyframes tiled-gallery-img-placeholder { - 0% { - background-color: var( --color-neutral-0 ); - } - 50% { - background-color: rgba( var( --color-neutral-0-rgb ), 0.5 ); - } - 100% { - background-color: var( --color-neutral-0 ); - } -} - -.wp-block-jetpack-tiled-gallery { - // Ensure that selected image outlines are visibile - padding-left: 4px; - padding-right: 4px; - - .tiled-gallery__item { - // Hide the focus outline that otherwise briefly appears when selecting a block. - > img:focus { - outline: none; - } - - > img { - // Inspired by Calypso's placeholder mixin - animation: tiled-gallery-img-placeholder 1.6s ease-in-out infinite; - } - - &.is-selected { - outline: 4px solid $tiled-gallery-selection; - - // Disable filters when selected - filter: none; - &::before, - &::after { - content: none; - } - } - - &.is-transient img { - opacity: 0.3; - } - - /* @TODO Caption has been commented out */ - // .editor-rich-text { - // position: absolute; - // bottom: 0; - // width: 100%; - // max-height: 100%; - // overflow-y: auto; - // } - - // .editor-rich-text figcaption:not( [data-is-placeholder-visible='true'] ) { - // position: relative; - // overflow: hidden; - // color: var( --color-white ); - // } - - // &.is-selected .editor-rich-text { - // // IE calculates this incorrectly, so leave it to modern browsers. - // @supports ( position: sticky ) { - // right: 0; - // left: 0; - // margin-top: -4px; - // } - - // // Override negative margins so this toolbar isn't hidden by overflow. Overflow is needed for long captions. - // .editor-rich-text__inline-toolbar { - // top: 0; - // } - - // // Make extra space for the inline toolbar. - // .editor-rich-text__tinymce { - // padding-top: 48px; - // } - // } - } - - // Circle layout doesn't support captions - // @TODO handle this in the component - /* @TODO Caption has been commented out */ - // &.is-style-circle .tiled-gallery__item .editor-rich-text { - // display: none; - // } - - .tiled-gallery__add-item { - margin-top: $tiled-gallery-gutter; - width: 100%; - - .components-form-file-upload, - .components-button.tiled-gallery__add-item-button { - width: 100%; - height: 100%; - } - - .components-button.tiled-gallery__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: $tiled-gallery-add-item-border-width solid $tiled-gallery-add-item-border-color; - } - } - } - - .tiled-gallery__item__inline-menu { - background-color: $tiled-gallery-selection; - display: inline-flex; - padding: 0 0 2px 2px; - position: absolute; - right: 0; - top: 0; - - .components-button { - color: var( --color-white ); - &:hover, - &:focus { - color: var( --color-white ); - } - } - } - - .tiled-gallery__item__remove { - padding: 0; - } - - .tiled-gallery__item .components-spinner { - position: absolute; - top: 50%; - left: 50%; - transform: translate( -50%, -50% ); - } - - // Hide captions and upload buttons in style picker preview - .editor-block-preview__content & { - /* @TODO Caption has been commented out */ - // figcaption, - .editor-media-placeholder { - display: none; - } - } - - // Matches with `.dashicon` in `MediaPlaceholder` component - .tiled-gallery__media-placeholder-icon { - height: 20px; - margin-right: 1ch; // stylelint-disable-line unit-whitelist - width: 20px; - } -} - -.tiled-gallery__filter-picker-menu { - $active-item-outline-width: 2px; - - // @TODO replace with Gutenberg variables - $dark-gray-500: #555d66; - $dark-gray-900: #191e23; - - padding: 7px; - - // Leave space between elements for active state styling - .components-menu-item__button + .components-menu-item__button { - margin-top: $active-item-outline-width; - } - - .components-menu-item__button.is-active { - color: $dark-gray-900; - box-shadow: 0 0 0 $active-item-outline-width $dark-gray-500 !important; - } -} diff --git a/extensions/blocks/tiled-gallery/filter-toolbar.js b/extensions/blocks/tiled-gallery/filter-toolbar.js deleted file mode 100644 index 7938cf997e8fd..0000000000000 --- a/extensions/blocks/tiled-gallery/filter-toolbar.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * External Dependencies - */ -import { Dropdown, MenuItem, NavigableMenu, Path, SVG, Toolbar } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __, _x } from '../../utils/i18n'; - -const availableFilters = [ - { - icon: ( - /* No filter */ - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z" /> - </SVG> - ), - title: _x( 'Original', 'image style' ), - value: undefined, - }, - { - icon: ( - /* 1 */ - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 10h2V5h-4v2h2v8zm7-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z" /> - </SVG> - ), - title: _x( 'Black and White', 'image style' ), - value: 'black-and-white', - }, - { - icon: ( - /* 2 */ - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-4-4h-4v-2h2c1.1 0 2-.89 2-2V7c0-1.11-.9-2-2-2h-4v2h4v2h-2c-1.1 0-2 .89-2 2v4h6v-2z" /> - </SVG> - ), - title: _x( 'Sepia', 'image style' ), - value: 'sepia', - }, - { - icon: ( - /* 3 */ - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7c0-1.11-.9-2-2-2h-4v2h4v2h-2v2h2v2h-4v2h4c1.1 0 2-.89 2-2z" /> - </SVG> - ), - title: '1977', - value: '1977', - }, - { - icon: ( - /* 4 */ - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm12 10h2V5h-2v4h-2V5h-2v6h4v4zm6-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z" /> - </SVG> - ), - title: _x( 'Clarendon', 'image style' ), - value: 'clarendon', - }, - { - icon: ( - /* 5 */ - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0z" /> - <Path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-2c0-1.11-.9-2-2-2h-2V7h4V5h-6v6h4v2h-4v2h4c1.1 0 2-.89 2-2z" /> - </SVG> - ), - title: _x( 'Gingham', 'image style' ), - value: 'gingham', - }, -]; - -const label = __( 'Pick an image filter' ); - -export default function FilterToolbar( { value, onChange } ) { - return ( - <Dropdown - position="bottom right" - className="editor-block-switcher" - contentClassName="editor-block-switcher__popover" - renderToggle={ ( { onToggle, isOpen } ) => { - return ( - <Toolbar - controls={ [ - { - onClick: onToggle, - extraProps: { - 'aria-haspopup': 'true', - 'aria-expanded': isOpen, - }, - title: label, - tooltip: label, - icon: ( - <SVG - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - > - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M19 10v9H4.98V5h9V3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-9h-2zm-2.94-2.06L17 10l.94-2.06L20 7l-2.06-.94L17 4l-.94 2.06L14 7zM12 8l-1.25 2.75L8 12l2.75 1.25L12 16l1.25-2.75L16 12l-2.75-1.25z" /> - </SVG> - ), - }, - ] } - /> - ); - } } - renderContent={ ( { onClose } ) => { - const applyOrUnset = nextValue => () => { - onChange( value === nextValue ? undefined : nextValue ); - onClose(); - }; - return ( - <NavigableMenu className="tiled-gallery__filter-picker-menu"> - { availableFilters.map( ( { icon, title, value: filterValue } ) => ( - <MenuItem - className={ value === filterValue ? 'is-active' : undefined } - icon={ icon } - isSelected={ value === filterValue } - key={ filterValue || 'original' } - onClick={ applyOrUnset( filterValue ) } - role="menuitemcheckbox" - > - { title } - </MenuItem> - ) ) } - </NavigableMenu> - ); - } } - /> - ); -} diff --git a/extensions/blocks/tiled-gallery/gallery-image/edit.js b/extensions/blocks/tiled-gallery/gallery-image/edit.js deleted file mode 100644 index 2aa86544dd9e2..0000000000000 --- a/extensions/blocks/tiled-gallery/gallery-image/edit.js +++ /dev/null @@ -1,196 +0,0 @@ -/** - * External Dependencies - */ -import classnames from 'classnames'; -import { BACKSPACE, DELETE } from '@wordpress/keycodes'; -import { Component, createRef, Fragment } from '@wordpress/element'; -import { IconButton, Spinner } from '@wordpress/components'; -import { isBlobURL } from '@wordpress/blob'; -/* @TODO Caption has been commented out */ -// import { RichText } from '@wordpress/editor'; -import { withSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { __ } from '../../../utils/i18n'; - -class GalleryImageEdit extends Component { - img = createRef(); - - /* @TODO Caption has been commented out */ - // state = { - // captionSelected: false, - // }; - - // onSelectCaption = () => { - // if ( ! this.state.captionSelected ) { - // this.setState( { - // captionSelected: true, - // } ); - // } - - // if ( ! this.props.isSelected ) { - // this.props.onSelect(); - // } - // }; - - onImageClick = () => { - if ( ! this.props.isSelected ) { - this.props.onSelect(); - } - - // if ( this.state.captionSelected ) { - // this.setState( { - // captionSelected: false, - // } ); - // } - }; - - onImageKeyDown = event => { - if ( - this.img.current === document.activeElement && - this.props.isSelected && - [ BACKSPACE, DELETE ].includes( event.keyCode ) - ) { - this.props.onRemove(); - } - }; - - /* @TODO Caption has been commented out */ - // static getDerivedStateFromProps( props, state ) { - // // unselect the caption so when the user selects other image and comeback - // // the caption is not immediately selected - // if ( ! props.isSelected && state.captionSelected ) { - // return { captionSelected: false }; - // } - // return null; - // } - - componentDidUpdate() { - const { alt, height, image, link, url, width } = this.props; - - if ( image ) { - const nextAtts = {}; - - if ( ! alt && image.alt_text ) { - nextAtts.alt = image.alt_text; - } - if ( ! height && image.media_details && image.media_details.height ) { - nextAtts.height = +image.media_details.height; - } - if ( ! link && image.link ) { - nextAtts.link = image.link; - } - if ( ! url && image.source_url ) { - nextAtts.url = image.source_url; - } - if ( ! width && image.media_details && image.media_details.width ) { - nextAtts.width = +image.media_details.width; - } - - if ( Object.keys( nextAtts ).length ) { - this.props.setAttributes( nextAtts ); - } - } - } - - render() { - const { - 'aria-label': ariaLabel, - alt, - // caption, - height, - id, - imageFilter, - isSelected, - link, - linkTo, - onRemove, - origUrl, - // setAttributes, - url, - width, - } = this.props; - - let href; - - switch ( linkTo ) { - case 'media': - href = url; - break; - case 'attachment': - href = link; - break; - } - - const img = ( - // Disable reason: Image itself is not meant to be interactive, but should - // direct image selection and unfocus caption fields. - /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */ - <Fragment> - <img - alt={ alt } - aria-label={ ariaLabel } - data-height={ height } - data-id={ id } - data-link={ link } - data-url={ origUrl } - data-width={ width } - onClick={ this.onImageClick } - onKeyDown={ this.onImageKeyDown } - ref={ this.img } - src={ url } - tabIndex="0" - /> - { isBlobURL( origUrl ) && <Spinner /> } - </Fragment> - /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */ - ); - - // Disable reason: Each block can be selected by clicking on it and we should keep the same saved markup - return ( - <figure - className={ classnames( 'tiled-gallery__item', { - 'is-selected': isSelected, - 'is-transient': isBlobURL( origUrl ), - [ `filter__${ imageFilter }` ]: !! imageFilter, - } ) } - > - { isSelected && ( - <div className="tiled-gallery__item__inline-menu"> - <IconButton - icon="no-alt" - onClick={ onRemove } - className="tiled-gallery__item__remove" - label={ __( 'Remove Image' ) } - /> - </div> - ) } - { /* Keep the <a> HTML structure, but ensure there is no navigation from edit */ - /* eslint-disable-next-line jsx-a11y/anchor-is-valid */ } - { href ? <a>{ img }</a> : img } - { /* ( ! RichText.isEmpty( caption ) || isSelected ) && ( - <RichText - tagName="figcaption" - placeholder={ __( 'Write caption…' ) } - value={ caption } - isSelected={ this.state.captionSelected } - onChange={ newCaption => setAttributes( { caption: newCaption } ) } - unstableOnFocus={ this.onSelectCaption } - inlineToolbar - /> - ) */ } - </figure> - ); - } -} - -export default withSelect( ( select, ownProps ) => { - const { getMedia } = select( 'core' ); - const { id } = ownProps; - - return { - image: id ? getMedia( id ) : null, - }; -} )( GalleryImageEdit ); diff --git a/extensions/blocks/tiled-gallery/gallery-image/save.js b/extensions/blocks/tiled-gallery/gallery-image/save.js deleted file mode 100644 index ac57133da7229..0000000000000 --- a/extensions/blocks/tiled-gallery/gallery-image/save.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * External Dependencies - */ -import classnames from 'classnames'; -import { isBlobURL } from '@wordpress/blob'; - -/* @TODO Caption has been commented out */ -// import { RichText } from '@wordpress/editor'; - -export default function GalleryImageSave( props ) { - const { - alt, - // caption, - imageFilter, - height, - id, - link, - linkTo, - origUrl, - url, - width, - } = props; - - if ( isBlobURL( origUrl ) ) { - return null; - } - - let href; - - switch ( linkTo ) { - case 'media': - href = url; - break; - case 'attachment': - href = link; - break; - } - - const img = ( - <img - alt={ alt } - data-height={ height } - data-id={ id } - data-link={ link } - data-url={ origUrl } - data-width={ width } - src={ url } - /> - ); - - return ( - <figure - className={ classnames( 'tiled-gallery__item', { - [ `filter__${ imageFilter }` ]: !! imageFilter, - } ) } - > - { href ? <a href={ href }>{ img }</a> : img } - { /* ! RichText.isEmpty( caption ) && ( - <RichText.Content tagName="figcaption" value={ caption } /> - ) */ } - </figure> - ); -} diff --git a/extensions/blocks/tiled-gallery/index.js b/extensions/blocks/tiled-gallery/index.js deleted file mode 100644 index 2a6d979a57019..0000000000000 --- a/extensions/blocks/tiled-gallery/index.js +++ /dev/null @@ -1,190 +0,0 @@ -/** - * External dependencies - */ -import { createBlock } from '@wordpress/blocks'; -import { filter } from 'lodash'; -import { Path, SVG } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __, _x } from '../../utils/i18n'; -import edit from './edit'; -import save from './save'; -import { - LAYOUT_CIRCLE, - LAYOUT_COLUMN, - LAYOUT_DEFAULT, - LAYOUT_SQUARE, - LAYOUT_STYLES, -} from './constants'; - -/** - * Style dependencies - */ -import './editor.scss'; - -import * as deprecatedV1 from './deprecated/v1'; - -// Style names are translated. Avoid introducing an i18n dependency elsewhere (view) -// by only including the labels here, the only place they're needed. -// -// Map style names to labels and merge them together. -const styleNames = { - [ LAYOUT_DEFAULT ]: _x( 'Tiled mosaic', 'Tiled gallery layout' ), - [ LAYOUT_CIRCLE ]: _x( 'Circles', 'Tiled gallery layout' ), - [ LAYOUT_COLUMN ]: _x( 'Tiled columns', 'Tiled gallery layout' ), - [ LAYOUT_SQUARE ]: _x( 'Square tiles', 'Tiled gallery layout' ), -}; -const layoutStylesWithLabels = LAYOUT_STYLES.map( style => ( { - ...style, - label: styleNames[ style.name ], -} ) ); - -const blockAttributes = { - // Set default align - align: { - default: 'center', - type: 'string', - }, - // Set default className (used with block styles) - className: { - default: `is-style-${ LAYOUT_DEFAULT }`, - type: 'string', - }, - columns: { - type: 'number', - }, - ids: { - default: [], - type: 'array', - }, - imageFilter: { - type: 'string', - }, - images: { - type: 'array', - default: [], - source: 'query', - selector: '.tiled-gallery__item', - query: { - alt: { - attribute: 'alt', - default: '', - selector: 'img', - source: 'attribute', - }, - caption: { - selector: 'figcaption', - source: 'html', - type: 'string', - }, - height: { - attribute: 'data-height', - selector: 'img', - source: 'attribute', - type: 'number', - }, - id: { - attribute: 'data-id', - selector: 'img', - source: 'attribute', - }, - link: { - attribute: 'data-link', - selector: 'img', - source: 'attribute', - }, - url: { - attribute: 'data-url', - selector: 'img', - source: 'attribute', - }, - width: { - attribute: 'data-width', - selector: 'img', - source: 'attribute', - type: 'number', - }, - }, - }, - linkTo: { - default: 'none', - type: 'string', - }, -}; - -export const name = 'tiled-gallery'; - -export const icon = ( - <SVG viewBox="0 0 24 24" width={ 24 } height={ 24 }> - <Path - fill="currentColor" - d="M19 5v2h-4V5h4M9 5v6H5V5h4m10 8v6h-4v-6h4M9 17v2H5v-2h4M21 3h-8v6h8V3zM11 3H3v10h8V3zm10 8h-8v10h8V11zm-10 4H3v6h8v-6z" - /> - </SVG> -); - -export const settings = { - attributes: blockAttributes, - category: 'jetpack', - description: __( 'Display multiple images in an elegantly organized tiled layout.' ), - icon, - keywords: [ - _x( 'images', 'block search term' ), - _x( 'photos', 'block search term' ), - _x( 'masonry', 'block search term' ), - ], - styles: layoutStylesWithLabels, - supports: { - align: [ 'center', 'wide', 'full' ], - customClassName: false, - html: false, - }, - title: __( 'Tiled Gallery' ), - transforms: { - from: [ - { - type: 'block', - blocks: [ 'core/gallery' ], - transform: attributes => { - const validImages = filter( attributes.images, ( { id, url } ) => id && url ); - if ( validImages.length > 0 ) { - return createBlock( `jetpack/${ name }`, { - images: validImages.map( ( { id, url, alt, caption } ) => ( { - id, - url, - alt, - caption, - } ) ), - } ); - } - return createBlock( `jetpack/${ name }` ); - }, - }, - ], - to: [ - { - type: 'block', - blocks: [ 'core/gallery' ], - transform: ( { images, columns, linkTo } ) => - createBlock( 'core/gallery', { images, columns, imageCrop: true, linkTo } ), - }, - { - 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' ); - }, - }, - ], - }, - edit, - save, - deprecated: [ deprecatedV1 ], -}; diff --git a/extensions/blocks/tiled-gallery/layout/column.js b/extensions/blocks/tiled-gallery/layout/column.js deleted file mode 100644 index a3ed5cdf04cbb..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/column.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Column( { children } ) { - return <div className="tiled-gallery__col">{ children }</div>; -} diff --git a/extensions/blocks/tiled-gallery/layout/gallery.js b/extensions/blocks/tiled-gallery/layout/gallery.js deleted file mode 100644 index 94fc61e4be980..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/gallery.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function Gallery( { children, galleryRef } ) { - return ( - <div className="tiled-gallery__gallery" ref={ galleryRef }> - { children } - </div> - ); -} diff --git a/extensions/blocks/tiled-gallery/layout/index.js b/extensions/blocks/tiled-gallery/layout/index.js deleted file mode 100644 index e375ffe3063b9..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/index.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - * External dependencies - */ -import photon from 'photon'; -import { Component } from '@wordpress/element'; -import { format as formatUrl, parse as parseUrl } from 'url'; -import { isBlobURL } from '@wordpress/blob'; -import { sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import GalleryImageEdit from '../gallery-image/edit'; -import GalleryImageSave from '../gallery-image/save'; -import Mosaic from './mosaic'; -import Square from './square'; -import { PHOTON_MAX_RESIZE } from '../constants'; -import { __ } from '../../../utils/i18n'; - -export default class Layout extends Component { - photonize( { height, width, url } ) { - if ( ! url ) { - return; - } - - // Do not Photonize images that are still uploading or from localhost - if ( isBlobURL( url ) || /^https?:\/\/localhost/.test( url ) ) { - return url; - } - - // Drop query args, photon URLs can't handle them - // This should be the "raw" url, we'll add dimensions later - const cleanUrl = url.split( '?', 1 )[ 0 ]; - - const photonImplementation = isWpcomFilesUrl( url ) ? photonWpcomImage : photon; - - const { layoutStyle } = this.props; - - if ( isSquareishLayout( layoutStyle ) && width && height ) { - const size = Math.min( PHOTON_MAX_RESIZE, width, height ); - return photonImplementation( cleanUrl, { resize: `${ size },${ size }` } ); - } - return photonImplementation( cleanUrl ); - } - - // This is tricky: - // - We need to "photonize" to resize the images at appropriate dimensions - // - The resize will depend on the image size and the layout in some cases - // - Handlers need to be created by index so that the image changes can be applied correctly. - // This is because the images are stored in an array in the block attributes. - renderImage( img, i ) { - const { - imageFilter, - images, - isSave, - linkTo, - onRemoveImage, - onSelectImage, - selectedImage, - setImageAttributes, - } = this.props; - - /* translators: %1$d is the order number of the image, %2$d is the total number of images. */ - const ariaLabel = sprintf( __( 'image %1$d of %2$d in gallery' ), i + 1, images.length ); - const Image = isSave ? GalleryImageSave : GalleryImageEdit; - - return ( - <Image - alt={ img.alt } - aria-label={ ariaLabel } - // @TODO Caption has been commented out - // caption={ img.caption } - height={ img.height } - id={ img.id } - imageFilter={ imageFilter } - isSelected={ selectedImage === i } - key={ i } - link={ img.link } - linkTo={ linkTo } - onRemove={ isSave ? undefined : onRemoveImage( i ) } - onSelect={ isSave ? undefined : onSelectImage( i ) } - origUrl={ img.url } - setAttributes={ isSave ? undefined : setImageAttributes( i ) } - url={ this.photonize( img ) } - width={ img.width } - /> - ); - } - - render() { - const { align, children, className, columns, images, layoutStyle } = this.props; - - const LayoutRenderer = isSquareishLayout( layoutStyle ) ? Square : Mosaic; - - const renderedImages = this.props.images.map( this.renderImage, this ); - - return ( - <div className={ className }> - <LayoutRenderer - align={ align } - columns={ columns } - images={ images } - layoutStyle={ layoutStyle } - renderedImages={ renderedImages } - /> - { children } - </div> - ); - } -} - -function isSquareishLayout( layout ) { - return [ 'circle', 'square' ].includes( layout ); -} - -function isWpcomFilesUrl( url ) { - const { host } = parseUrl( url ); - return /\.files\.wordpress\.com$/.test( host ); -} - -/** - * Apply photon arguments to *.files.wordpress.com images - * - * This function largely duplicates the functionlity of the photon.js lib. - * This is necessary because we want to serve images from *.files.wordpress.com so that private - * WordPress.com sites can use this block which depends on a Photon-like image service. - * - * If we pass all images through Photon servers, some images are unreachable. *.files.wordpress.com - * is already photon-like so we can pass it the same parameters for image resizing. - * - * @param {string} url Image url - * @param {Object} opts Options to pass to photon - * - * @return {string} Url string with options applied - */ -function photonWpcomImage( url, opts = {} ) { - // Adhere to the same options API as the photon.js lib - const photonLibMappings = { - width: 'w', - height: 'h', - letterboxing: 'lb', - removeLetterboxing: 'ulb', - }; - - // Discard some param parts - const { auth, hash, port, query, search, ...urlParts } = parseUrl( url ); - - // Build query - // This reduction intentionally mutates the query as it is built internally. - urlParts.query = Object.keys( opts ).reduce( - ( q, key ) => - Object.assign( q, { - [ photonLibMappings.hasOwnProperty( key ) ? photonLibMappings[ key ] : key ]: opts[ key ], - } ), - {} - ); - - return formatUrl( urlParts ); -} diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/index.js b/extensions/blocks/tiled-gallery/layout/mosaic/index.js deleted file mode 100644 index 8c56b1641dd1e..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/index.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * External dependencies - */ -import { Component, createRef } from '@wordpress/element'; -import ResizeObserver from 'resize-observer-polyfill'; - -/** - * Internal dependencies - */ -import Column from '../column'; -import Gallery from '../gallery'; -import Row from '../row'; -import { getGalleryRows, handleRowResize } from './resize'; -import { imagesToRatios, ratiosToColumns, ratiosToMosaicRows } from './ratios'; - -export default class Mosaic extends Component { - gallery = createRef(); - pendingRaf = null; - ro = null; // resizeObserver instance - - componentDidMount() { - this.observeResize(); - } - - componentWillUnmount() { - this.unobserveResize(); - } - - componentDidUpdate( prevProps ) { - if ( prevProps.images !== this.props.images || prevProps.align !== this.props.align ) { - this.triggerResize(); - } else if ( 'columns' === this.props.layoutStyle && prevProps.columns !== this.props.columns ) { - this.triggerResize(); - } - } - - handleGalleryResize = entries => { - if ( this.pendingRaf ) { - cancelAnimationFrame( this.pendingRaf ); - this.pendingRaf = null; - } - this.pendingRaf = requestAnimationFrame( () => { - for ( const { contentRect, target } of entries ) { - const { width } = contentRect; - getGalleryRows( target ).forEach( row => handleRowResize( row, width ) ); - } - } ); - }; - - triggerResize() { - if ( this.gallery.current ) { - this.handleGalleryResize( [ - { - target: this.gallery.current, - contentRect: { width: this.gallery.current.clientWidth }, - }, - ] ); - } - } - - observeResize() { - this.triggerResize(); - this.ro = new ResizeObserver( this.handleGalleryResize ); - if ( this.gallery.current ) { - this.ro.observe( this.gallery.current ); - } - } - - unobserveResize() { - if ( this.ro ) { - this.ro.disconnect(); - this.ro = null; - } - if ( this.pendingRaf ) { - cancelAnimationFrame( this.pendingRaf ); - this.pendingRaf = null; - } - } - - render() { - const { align, columns, images, layoutStyle, renderedImages } = this.props; - - const ratios = imagesToRatios( images ); - const rows = - 'columns' === layoutStyle - ? ratiosToColumns( ratios, columns ) - : ratiosToMosaicRows( ratios, { isWide: [ 'full', 'wide' ].includes( align ) } ); - - let cursor = 0; - return ( - <Gallery galleryRef={ this.gallery }> - { rows.map( ( row, rowIndex ) => ( - <Row key={ rowIndex }> - { row.map( ( colSize, colIndex ) => { - const columnImages = renderedImages.slice( cursor, cursor + colSize ); - cursor += colSize; - return <Column key={ colIndex }>{ columnImages }</Column>; - } ) } - </Row> - ) ) } - </Gallery> - ); - } -} diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/ratios.js b/extensions/blocks/tiled-gallery/layout/mosaic/ratios.js deleted file mode 100644 index 8accd552b710a..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/ratios.js +++ /dev/null @@ -1,280 +0,0 @@ -/** - * External dependencies - */ -import { - drop, - every, - isEqual, - map, - overEvery, - some, - sum, - take, - takeRight, - takeWhile, - zipWith, -} from 'lodash'; - -export function imagesToRatios( images ) { - return map( images, ratioFromImage ); -} - -export function ratioFromImage( { height, width } ) { - return height && width ? width / height : 1; -} - -/** - * Build three columns, each of which should contain approximately 1/3 of the total ratio - * - * @param {Array.<number>} ratios Ratios of images put into shape - * @param {number} columnCount Number of columns - * - * @return {Array.<Array.<number>>} Shape of rows and columns - */ -export function ratiosToColumns( ratios, columnCount ) { - // If we don't have more than 1 per column, just return a simple 1 ratio per column shape - if ( ratios.length <= columnCount ) { - return [ Array( ratios.length ).fill( 1 ) ]; - } - - const total = sum( ratios ); - const targetColRatio = total / columnCount; - - const row = []; - let toProcess = ratios; - let accumulatedRatio = 0; - - // We skip the last column in the loop and add rest later - for ( let i = 0; i < columnCount - 1; i++ ) { - const colSize = takeWhile( toProcess, ratio => { - const shouldTake = accumulatedRatio <= ( i + 1 ) * targetColRatio; - if ( shouldTake ) { - accumulatedRatio += ratio; - } - return shouldTake; - } ).length; - row.push( colSize ); - toProcess = drop( toProcess, colSize ); - } - - // Don't calculate last column, just add what's left - row.push( toProcess.length ); - - // A shape is an array of rows. Wrap our row in an array. - return [ row ]; -} - -/** - * These are partially applied functions. - * They rely on helper function (defined below) to create a function that expects to be passed ratios - * during processing. - * - * …FitsNextImages() functions should be passed ratios to be processed - * …IsNotRecent() functions should be passed the processed shapes - */ - -const reverseSymmetricRowIsNotRecent = isNotRecentShape( [ 2, 1, 2 ], 5 ); -const reverseSymmetricFitsNextImages = checkNextRatios( [ - isLandscape, - isLandscape, - isPortrait, - isLandscape, - isLandscape, -] ); -const longSymmetricRowFitsNextImages = checkNextRatios( [ - isLandscape, - isLandscape, - isLandscape, - isPortrait, - isLandscape, - isLandscape, - isLandscape, -] ); -const longSymmetricRowIsNotRecent = isNotRecentShape( [ 3, 1, 3 ], 5 ); -const symmetricRowFitsNextImages = checkNextRatios( [ - isPortrait, - isLandscape, - isLandscape, - isPortrait, -] ); -const symmetricRowIsNotRecent = isNotRecentShape( [ 1, 2, 1 ], 5 ); -const oneThreeFitsNextImages = checkNextRatios( [ - isPortrait, - isLandscape, - isLandscape, - isLandscape, -] ); -const oneThreeIsNotRecent = isNotRecentShape( [ 1, 3 ], 3 ); -const threeOneIsFitsNextImages = checkNextRatios( [ - isLandscape, - isLandscape, - isLandscape, - isPortrait, -] ); -const threeOneIsNotRecent = isNotRecentShape( [ 3, 1 ], 3 ); -const oneTwoFitsNextImages = checkNextRatios( [ - lt( 1.6 ), - overEvery( gte( 0.9 ), lt( 2 ) ), - overEvery( gte( 0.9 ), lt( 2 ) ), -] ); -const oneTwoIsNotRecent = isNotRecentShape( [ 1, 2 ], 3 ); -const fiveIsNotRecent = isNotRecentShape( [ 1, 1, 1, 1, 1 ], 1 ); -const fourIsNotRecent = isNotRecentShape( [ 1, 1, 1, 1 ], 1 ); -const threeIsNotRecent = isNotRecentShape( [ 1, 1, 1 ], 3 ); -const twoOneFitsNextImages = checkNextRatios( [ - overEvery( gte( 0.9 ), lt( 2 ) ), - overEvery( gte( 0.9 ), lt( 2 ) ), - lt( 1.6 ), -] ); -const twoOneIsNotRecent = isNotRecentShape( [ 2, 1 ], 3 ); -const panoramicFitsNextImages = checkNextRatios( [ isPanoramic ] ); - -export function ratiosToMosaicRows( ratios, { isWide } = {} ) { - // This function will recursively process the input until it is consumed - const go = ( processed, toProcess ) => { - if ( ! toProcess.length ) { - return processed; - } - - let next; - - if ( - /* Reverse_Symmetric_Row */ - toProcess.length > 15 && - reverseSymmetricFitsNextImages( toProcess ) && - reverseSymmetricRowIsNotRecent( processed ) - ) { - next = [ 2, 1, 2 ]; - } else if ( - /* Long_Symmetric_Row */ - toProcess.length > 15 && - longSymmetricRowFitsNextImages( toProcess ) && - longSymmetricRowIsNotRecent( processed ) - ) { - next = [ 3, 1, 3 ]; - } else if ( - /* Symmetric_Row */ - toProcess.length !== 5 && - symmetricRowFitsNextImages( toProcess ) && - symmetricRowIsNotRecent( processed ) - ) { - next = [ 1, 2, 1 ]; - } else if ( - /* One_Three */ - oneThreeFitsNextImages( toProcess ) && - oneThreeIsNotRecent( processed ) - ) { - next = [ 1, 3 ]; - } else if ( - /* Three_One */ - threeOneIsFitsNextImages( toProcess ) && - threeOneIsNotRecent( processed ) - ) { - next = [ 3, 1 ]; - } else if ( - /* One_Two */ - oneTwoFitsNextImages( toProcess ) && - oneTwoIsNotRecent( processed ) - ) { - next = [ 1, 2 ]; - } else if ( - /* Five */ - isWide && - ( toProcess.length === 5 || ( toProcess.length !== 10 && toProcess.length > 6 ) ) && - fiveIsNotRecent( processed ) && - sum( take( toProcess, 5 ) ) < 5 - ) { - next = [ 1, 1, 1, 1, 1 ]; - } else if ( - /* Four */ - isFourValidCandidate( processed, toProcess ) - ) { - next = [ 1, 1, 1, 1 ]; - } else if ( - /* Three */ - isThreeValidCandidate( processed, toProcess, isWide ) - ) { - next = [ 1, 1, 1 ]; - } else if ( - /* Two_One */ - twoOneFitsNextImages( toProcess ) && - twoOneIsNotRecent( processed ) - ) { - next = [ 2, 1 ]; - } else if ( /* Panoramic */ panoramicFitsNextImages( toProcess ) ) { - next = [ 1 ]; - } else if ( /* One_One */ toProcess.length > 3 ) { - next = [ 1, 1 ]; - } else { - // Everything left - next = Array( toProcess.length ).fill( 1 ); - } - - // Add row - const nextProcessed = processed.concat( [ next ] ); - - // Trim consumed images from next processing step - const consumedImages = sum( next ); - const nextToProcess = toProcess.slice( consumedImages ); - - return go( nextProcessed, nextToProcess ); - }; - return go( [], ratios ); -} - -function isThreeValidCandidate( processed, toProcess, isWide ) { - const ratio = sum( take( toProcess, 3 ) ); - return ( - toProcess.length >= 3 && - toProcess.length !== 4 && - toProcess.length !== 6 && - threeIsNotRecent( processed ) && - ( ratio < 2.5 || - ( ratio < 5 && - /* nextAreSymettric */ - ( toProcess.length >= 3 && - /* @FIXME floating point equality?? */ toProcess[ 0 ] === toProcess[ 2 ] ) ) || - isWide ) - ); -} - -function isFourValidCandidate( processed, toProcess ) { - const ratio = sum( take( toProcess, 4 ) ); - return ( - ( fourIsNotRecent( processed ) && ( ratio < 3.5 && toProcess.length > 5 ) ) || - ( ratio < 7 && toProcess.length === 4 ) - ); -} - -function isNotRecentShape( shape, numRecents ) { - return recents => - ! some( takeRight( recents, numRecents ), recentShape => isEqual( recentShape, shape ) ); -} - -function checkNextRatios( shape ) { - return ratios => - ratios.length >= shape.length && - every( zipWith( shape, ratios.slice( 0, shape.length ), ( f, r ) => f( r ) ) ); -} - -function isLandscape( ratio ) { - return ratio >= 1 && ratio < 2; -} - -function isPortrait( ratio ) { - return ratio < 1; -} - -function isPanoramic( ratio ) { - return ratio >= 2; -} - -// >= -function gte( n ) { - return m => m >= n; -} - -// < -function lt( n ) { - return m => m < n; -} diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/resize.js b/extensions/blocks/tiled-gallery/layout/mosaic/resize.js deleted file mode 100644 index 022729c8bac72..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/resize.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Internal dependencies - */ -import { GUTTER_WIDTH } from '../../constants'; - -/** - * Distribute a difference across ns so that their sum matches the target - * - * @param {Array<number>} parts Array of numbers to fit - * @param {number} target Number that sum should match - * @return {Array<number>} Adjusted parts - */ -function adjustFit( parts, target ) { - const diff = target - parts.reduce( ( sum, n ) => sum + n, 0 ); - const partialDiff = diff / parts.length; - return parts.map( p => p + partialDiff ); -} - -export function handleRowResize( row, width ) { - applyRowRatio( row, getRowRatio( row ), width ); -} - -function getRowRatio( row ) { - const result = getRowCols( row ) - .map( getColumnRatio ) - .reduce( - ( [ ratioA, weightedRatioA ], [ ratioB, weightedRatioB ] ) => { - return [ ratioA + ratioB, weightedRatioA + weightedRatioB ]; - }, - [ 0, 0 ] - ); - return result; -} - -export function getGalleryRows( gallery ) { - return Array.from( gallery.querySelectorAll( '.tiled-gallery__row' ) ); -} - -function getRowCols( row ) { - return Array.from( row.querySelectorAll( '.tiled-gallery__col' ) ); -} - -function getColImgs( col ) { - return Array.from( - col.querySelectorAll( '.tiled-gallery__item > img, .tiled-gallery__item > a > img' ) - ); -} - -function getColumnRatio( col ) { - const imgs = getColImgs( col ); - const imgCount = imgs.length; - const ratio = - 1 / - imgs.map( getImageRatio ).reduce( ( partialColRatio, imgRatio ) => { - return partialColRatio + 1 / imgRatio; - }, 0 ); - const result = [ ratio, ratio * imgCount || 1 ]; - return result; -} - -function getImageRatio( img ) { - const w = parseInt( img.dataset.width, 10 ); - const h = parseInt( img.dataset.height, 10 ); - const result = w && ! Number.isNaN( w ) && h && ! Number.isNaN( h ) ? w / h : 1; - return result; -} - -function applyRowRatio( row, [ ratio, weightedRatio ], width ) { - const rawHeight = - ( 1 / ratio ) * ( width - GUTTER_WIDTH * ( row.childElementCount - 1 ) - weightedRatio ); - - applyColRatio( row, { - rawHeight, - rowWidth: width - GUTTER_WIDTH * ( row.childElementCount - 1 ), - } ); -} - -function applyColRatio( row, { rawHeight, rowWidth } ) { - const cols = getRowCols( row ); - - const colWidths = cols.map( - col => ( rawHeight - GUTTER_WIDTH * ( col.childElementCount - 1 ) ) * getColumnRatio( col )[ 0 ] - ); - - const adjustedWidths = adjustFit( colWidths, rowWidth ); - - cols.forEach( ( col, i ) => { - const rawWidth = colWidths[ i ]; - const width = adjustedWidths[ i ]; - applyImgRatio( col, { - colHeight: rawHeight - GUTTER_WIDTH * ( col.childElementCount - 1 ), - width, - rawWidth, - } ); - } ); -} - -function applyImgRatio( col, { colHeight, width, rawWidth } ) { - const imgHeights = getColImgs( col ).map( img => rawWidth / getImageRatio( img ) ); - const adjustedHeights = adjustFit( imgHeights, colHeight ); - - // Set size of col children, not the <img /> element - Array.from( col.children ).forEach( ( item, i ) => { - const height = adjustedHeights[ i ]; - item.setAttribute( 'style', `height:${ height }px;width:${ width }px;` ); - } ); -} diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/test/__snapshots__/index.js.snap b/extensions/blocks/tiled-gallery/layout/mosaic/test/__snapshots__/index.js.snap deleted file mode 100644 index e726fa521fa5b..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,98 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders as expected 1`] = ` -<Gallery - galleryRef={ - Object { - "current": null, - } - } -> - <Row - key="0" - > - <Column - key="0" - > - 0 - </Column> - </Row> - <Row - key="1" - > - <Column - key="0" - > - 1 - </Column> - </Row> - <Row - key="2" - > - <Column - key="0" - > - 2 - </Column> - <Column - key="1" - > - 3 - </Column> - <Column - key="2" - > - 4 - </Column> - <Column - key="3" - > - 5 - </Column> - </Row> - <Row - key="3" - > - <Column - key="0" - > - 6 - </Column> - <Column - key="1" - > - 7 - </Column> - </Row> - <Row - key="4" - > - <Column - key="0" - > - 8 - </Column> - <Column - key="1" - > - 9 - 10 - </Column> - </Row> - <Row - key="5" - > - <Column - key="0" - > - 11 - 12 - </Column> - <Column - key="1" - > - 13 - </Column> - </Row> -</Gallery> -`; diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/test/__snapshots__/ratios.js.snap b/extensions/blocks/tiled-gallery/layout/mosaic/test/__snapshots__/ratios.js.snap deleted file mode 100644 index df02118c594b9..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/test/__snapshots__/ratios.js.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ratiosToMosaicRows transforms as expected 1`] = ` -Array [ - Array [ - 1, - ], - Array [ - 1, - ], - Array [ - 1, - 1, - 1, - 1, - ], - Array [ - 1, - 1, - ], - Array [ - 1, - 2, - ], - Array [ - 2, - 1, - ], -] -`; diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/test/fixtures/ratios.js b/extensions/blocks/tiled-gallery/layout/mosaic/test/fixtures/ratios.js deleted file mode 100644 index 77db288c5b0f8..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/test/fixtures/ratios.js +++ /dev/null @@ -1,16 +0,0 @@ -export const ratios = [ - 4, - 2.26056338028169, - 0.6676143094053542, - 0.75, - 0.7444409646100846, - 0.6666666666666666, - 0.8000588062334607, - 3.6392174704276616, - 1.335559265442404, - 1.509433962264151, - 1.6, - 1.3208430913348945, - 1.3553937789543349, - 1.499531396438613, -]; diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/test/index.js b/extensions/blocks/tiled-gallery/layout/mosaic/test/index.js deleted file mode 100644 index 57e991d23b463..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/test/index.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * External dependencies - */ -import Adapter from 'enzyme-adapter-react-16'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; -import { createSerializer } from 'enzyme-to-json'; -import { range } from 'lodash'; - -/** - * Internal dependencies - */ -import Mosaic from '..'; -import * as imageSets from '../../test/fixtures/image-sets'; - -Enzyme.configure( { adapter: new Adapter() } ); -expect.addSnapshotSerializer( createSerializer( { mode: 'deep' } ) ); - -test( 'renders as expected', () => { - Object.keys( imageSets ).forEach( k => { - const images = imageSets[ k ]; - expect( - shallow( <Mosaic images={ images } renderedImages={ range( images.length ) } /> ) - ).toMatchSnapshot(); - } ); -} ); diff --git a/extensions/blocks/tiled-gallery/layout/mosaic/test/ratios.js b/extensions/blocks/tiled-gallery/layout/mosaic/test/ratios.js deleted file mode 100644 index 3756b971e03ce..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/mosaic/test/ratios.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Internal dependencies - */ -import { ratiosToMosaicRows } from '../ratios'; -import { ratios } from './fixtures/ratios'; - -describe( 'ratiosToMosaicRows', () => { - test( 'transforms as expected', () => { - expect( ratiosToMosaicRows( ratios ) ).toMatchSnapshot(); - } ); -} ); diff --git a/extensions/blocks/tiled-gallery/layout/row.js b/extensions/blocks/tiled-gallery/layout/row.js deleted file mode 100644 index 200a58c2e3acf..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/row.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -export default function Row( { children, className } ) { - return <div className={ classnames( 'tiled-gallery__row', className ) }>{ children }</div>; -} diff --git a/extensions/blocks/tiled-gallery/layout/square.js b/extensions/blocks/tiled-gallery/layout/square.js deleted file mode 100644 index 2a1ab888b1916..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/square.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * External dependencies - */ -import { chunk, drop, take } from 'lodash'; - -/** - * Internal dependencies - */ -import Row from './row'; -import Column from './column'; -import Gallery from './gallery'; -import { MAX_COLUMNS } from '../constants'; - -export default function Square( { columns, renderedImages } ) { - const columnCount = Math.min( MAX_COLUMNS, columns ); - - const remainder = renderedImages.length % columnCount; - - return ( - <Gallery> - { [ - ...( remainder ? [ take( renderedImages, remainder ) ] : [] ), - ...chunk( drop( renderedImages, remainder ), columnCount ), - ].map( ( imagesInRow, rowIndex ) => ( - <Row key={ rowIndex } className={ `columns-${ imagesInRow.length }` }> - { imagesInRow.map( ( image, colIndex ) => ( - <Column key={ colIndex }>{ image }</Column> - ) ) } - </Row> - ) ) } - </Gallery> - ); -} diff --git a/extensions/blocks/tiled-gallery/layout/test/fixtures/image-sets.js b/extensions/blocks/tiled-gallery/layout/test/fixtures/image-sets.js deleted file mode 100644 index 07c6bef746a9a..0000000000000 --- a/extensions/blocks/tiled-gallery/layout/test/fixtures/image-sets.js +++ /dev/null @@ -1,117 +0,0 @@ -export const imageSet1 = [ - { - alt: '', - id: 163, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/architecture-bay-bridge-356830.jpg', - height: 2048, - width: 8192, - }, - { - alt: '', - id: 162, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/bloom-blossom-flora-40797-1.jpg', - height: 1562, - width: 3531, - }, - { - alt: '', - id: 161, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/architecture-building-city-597049.jpg', - height: 4221, - width: 2818, - }, - { - alt: '', - id: 160, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/architecture-art-blue-699466.jpg', - height: 4032, - width: 3024, - }, - { - alt: '', - id: 159, - caption: '', - url: - 'https://example.files.wordpress.com/2018/12/black-and-white-construction-ladder-54335.jpg', - height: 3193, - width: 2377, - }, - { - alt: '', - id: 158, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/architecture-buildings-city-1672110.jpg', - height: 6000, - width: 4000, - }, - { - alt: '', - id: 157, - caption: '', - url: - 'https://example.files.wordpress.com/2018/12/architectural-design-architecture-black-and-white-1672122-1.jpg', - height: 3401, - width: 2721, - }, - { - alt: '', - id: 156, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/grass-hd-wallpaper-lake-127753.jpg', - height: 2198, - width: 7999, - }, - { - alt: '', - id: 122, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/texaco-car-1.jpg', - height: 599, - width: 800, - }, - { - alt: '', - id: 92, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/43824553435_ea38cbc92a_m.jpg', - height: 159, - width: 240, - }, - { - alt: '', - id: 90, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/42924685680_7b5632e58e_m.jpg', - height: 150, - width: 240, - }, - { - alt: '', - id: 89, - caption: '', - url: - 'https://example.files.wordpress.com/2018/12/31962299833_1e106f7f7a_z-1-e1545262352979.jpg', - height: 427, - width: 564, - }, - { - alt: '', - id: 88, - caption: '', - url: 'https://example.files.wordpress.com/2018/12/29797558147_3c72afa8f4_k.jpg', - height: 1511, - width: 2048, - }, - { - alt: '', - id: 8, - caption: '', - url: 'https://example.files.wordpress.com/2018/11/person-smartphone-office-table.jpeg', - height: 1067, - width: 1600, - }, -]; diff --git a/extensions/blocks/tiled-gallery/save.js b/extensions/blocks/tiled-gallery/save.js deleted file mode 100644 index 8d69444ff5309..0000000000000 --- a/extensions/blocks/tiled-gallery/save.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Internal dependencies - */ -import Layout from './layout'; -import { defaultColumnsNumber } from './edit'; -import { getActiveStyleName } from '../../utils'; -import { LAYOUT_STYLES } from './constants'; - -export default function TiledGallerySave( { attributes } ) { - const { imageFilter, images } = attributes; - - if ( ! images.length ) { - return null; - } - - const { align, className, columns = defaultColumnsNumber( attributes ), linkTo } = attributes; - - return ( - <Layout - align={ align } - className={ className } - columns={ columns } - imageFilter={ imageFilter } - images={ images } - isSave - layoutStyle={ getActiveStyleName( LAYOUT_STYLES, className ) } - linkTo={ linkTo } - /> - ); -} diff --git a/extensions/blocks/tiled-gallery/variables.scss b/extensions/blocks/tiled-gallery/variables.scss deleted file mode 100644 index 6472bae167b0b..0000000000000 --- a/extensions/blocks/tiled-gallery/variables.scss +++ /dev/null @@ -1,5 +0,0 @@ -$tiled-gallery-add-item-border-color: #555d66; // Gutenberg $dark-gray-500 -$tiled-gallery-add-item-border-width: 1px; // Gutenberg $border-width -$tiled-gallery-caption-background-color: #000; -$tiled-gallery-gutter: 4px; // Fixed in JS, see `LayoutStyles` from `edit.jsx` -$tiled-gallery-selection: #0085ba; // Gutenberg primary theme color (https://github.com/WordPress/gutenberg/blob/6928e41c8afd7daa3a709afdda7eee48218473b7/bin/packages/post-css-config.js#L4) diff --git a/extensions/blocks/tiled-gallery/view.js b/extensions/blocks/tiled-gallery/view.js deleted file mode 100644 index 1f45b13ddc347..0000000000000 --- a/extensions/blocks/tiled-gallery/view.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Internal dependencies - */ -import './view.scss'; -import ResizeObserver from 'resize-observer-polyfill'; -import { handleRowResize } from './layout/mosaic/resize'; - -/** - * Handler for Gallery ResizeObserver - * - * @param {Array<ResizeObserverEntry>} galleries Resized galleries - */ -function handleObservedResize( galleries ) { - if ( handleObservedResize.pendingRaf ) { - cancelAnimationFrame( handleObservedResize.pendingRaf ); - } - handleObservedResize.pendingRaf = requestAnimationFrame( () => { - handleObservedResize.pendingRaf = null; - for ( const gallery of galleries ) { - const { width: galleryWidth } = gallery.contentRect; - // We can't use childNodes becuase post content may contain unexpected text nodes - const rows = Array.from( gallery.target.querySelectorAll( '.tiled-gallery__row' ) ); - rows.forEach( row => handleRowResize( row, galleryWidth ) ); - } - } ); -} - -/** - * Get all the galleries on the document - * - * @return {Array} List of gallery nodes - */ -function getGalleries() { - return Array.from( - document.querySelectorAll( - '.wp-block-jetpack-tiled-gallery.is-style-rectangular > .tiled-gallery__gallery,' + - '.wp-block-jetpack-tiled-gallery.is-style-columns > .tiled-gallery__gallery' - ) - ); -} - -/** - * Setup ResizeObserver to follow each gallery on the page - */ -const observeGalleries = () => { - const galleries = getGalleries(); - - if ( galleries.length === 0 ) { - return; - } - - const observer = new ResizeObserver( handleObservedResize ); - - galleries.forEach( gallery => observer.observe( gallery ) ); -}; - -if ( typeof window !== 'undefined' && typeof document !== 'undefined' ) { - // `DOMContentLoaded` may fire before the script has a chance to run - if ( document.readyState === 'loading' ) { - document.addEventListener( 'DOMContentLoaded', observeGalleries ); - } else { - observeGalleries(); - } -} diff --git a/extensions/blocks/tiled-gallery/view.scss b/extensions/blocks/tiled-gallery/view.scss deleted file mode 100644 index d7e475c5d6244..0000000000000 --- a/extensions/blocks/tiled-gallery/view.scss +++ /dev/null @@ -1,135 +0,0 @@ -@import './variables.scss'; -@import './css-gram.scss'; - -$tiled-gallery-max-column-count: 20; - -.wp-block-jetpack-tiled-gallery { - margin: 0 auto 1.5em; - - &.is-style-circle .tiled-gallery__item img { - border-radius: 50%; - } - - &.is-style-square, - &.is-style-circle { - .tiled-gallery__row { - flex-grow: 1; - width: 100%; - - @for $cols from 1 through $tiled-gallery-max-column-count { - &.columns-#{$cols} { - .tiled-gallery__col { - width: calc( ( 100% - ( #{$tiled-gallery-gutter} * ( #{$cols} - 1 ) ) ) / #{$cols} ); - } - } - } - } - } - - &.is-style-columns, - &.is-style-rectangular { - .tiled-gallery__item { - display: flex; - } - } -} - -.tiled-gallery__gallery { - width: 100%; - display: flex; - padding: 0; - flex-wrap: wrap; -} - -.tiled-gallery__row { - width: 100%; - display: flex; - flex-direction: row; - justify-content: center; - margin: 0; - - & + & { - margin-top: $tiled-gallery-gutter; - } -} - -.tiled-gallery__col { - display: flex; - flex-direction: column; - justify-content: center; - margin: 0; - - & + & { - margin-left: $tiled-gallery-gutter; - } -} - -.tiled-gallery__item { - justify-content: center; - margin: 0; - overflow: hidden; - padding: 0; - position: relative; - - &.filter__black-and-white { - filter: grayscale( 100% ); - } - - &.filter__sepia { - filter: sepia( 100% ); - } - - &.filter__1977 { - @include _1977; - } - - &.filter__clarendon { - @include clarendon; - } - - &.filter__gingham { - @include gingham; - } - - & + & { - margin-top: $tiled-gallery-gutter; - } - - > img { - background-color: rgba( 0, 0, 0, 0.1 ); - } - - > a, - > a > img, - > img { - display: block; - height: auto; - margin: 0; - max-width: 100%; - object-fit: cover; - object-position: center; - padding: 0; - width: 100%; - } - - /* @TODO Caption has been commented out */ - // figcaption { - // position: absolute; - // bottom: 0; - // width: 100%; - // max-height: 100%; - // overflow: auto; - // padding: 40px 10px 5px; - // color: var( --color-white ); - // text-align: center; - // font-size: $root-font-size; - // // stylelint-disable function-parentheses-space-inside - // background: linear-gradient( - // 0deg, - // rgba( $color: $tiled-gallery-caption-background-color, $alpha: 0.7 ) 0, - // rgba( $color: $tiled-gallery-caption-background-color, $alpha: 0.3 ) 60%, - // transparent - // ); - // // stylelint-enable function-parentheses-space-inside - // } -} diff --git a/extensions/blocks/videopress/edit.js b/extensions/blocks/videopress/edit.js deleted file mode 100644 index ab3e487a99e1d..0000000000000 --- a/extensions/blocks/videopress/edit.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * External dependencies - */ -import apiFetch from '@wordpress/api-fetch'; -import { isBlobURL } from '@wordpress/blob'; -import { compose, createHigherOrderComponent } from '@wordpress/compose'; -import { withSelect } from '@wordpress/data'; -import { Disabled, IconButton, SandBox, Toolbar } from '@wordpress/components'; -import { BlockControls, RichText } from '@wordpress/editor'; -import { Component, createRef, Fragment } from '@wordpress/element'; -import classnames from 'classnames'; -import { get } from 'lodash'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; -import Loading from './loading'; - -const VideoPressEdit = CoreVideoEdit => - class extends Component { - constructor() { - super( ...arguments ); - this.state = { - media: null, - isFetchingMedia: false, - fallback: false, - }; - this.posterImageButton = createRef(); - } - - componentDidMount() { - const { guid } = this.props.attributes; - if ( ! guid ) { - this.setGuid(); - } - } - - componentDidUpdate( prevProps ) { - const { attributes } = this.props; - - if ( attributes.id !== prevProps.attributes.id ) { - this.setGuid(); - } - } - - fallbackToCore = () => { - this.props.setAttributes( { guid: undefined } ); - this.setState( { fallback: true } ); - }; - - setGuid = async () => { - const { attributes, setAttributes } = this.props; - const { id } = attributes; - - if ( ! id ) { - setAttributes( { guid: undefined } ); - return; - } - - try { - this.setState( { isFetchingMedia: true } ); - const media = await apiFetch( { path: `/wp/v2/media/${ id }` } ); - this.setState( { isFetchingMedia: false } ); - - const { id: currentId } = this.props.attributes; - if ( id !== currentId ) { - // Video was changed in the editor while fetching data for the previous video; - return; - } - - this.setState( { media } ); - const guid = get( media, 'jetpack_videopress_guid' ); - if ( guid ) { - setAttributes( { guid } ); - } else { - this.fallbackToCore(); - } - } catch ( e ) { - this.setState( { isFetchingMedia: false } ); - this.fallbackToCore(); - } - }; - - switchToEditing = () => { - this.props.setAttributes( { - id: undefined, - guid: undefined, - src: undefined, - } ); - }; - - onRemovePoster = () => { - this.props.setAttributes( { poster: '' } ); - - // Move focus back to the Media Upload button. - this.posterImageButton.current.focus(); - }; - - render() { - const { - attributes, - className, - isFetchingPreview, - isSelected, - isUploading, - preview, - setAttributes, - } = this.props; - const { fallback, isFetchingMedia } = this.state; - - if ( isUploading ) { - return <Loading text={ __( 'Uploading…' ) } />; - } - - if ( isFetchingMedia || isFetchingPreview ) { - return <Loading text={ __( 'Embedding…' ) } />; - } - - if ( fallback || ! preview ) { - return <CoreVideoEdit { ...this.props } />; - } - - const { html, scripts } = preview; - const { caption } = attributes; - - return ( - <Fragment> - <BlockControls> - <Toolbar> - <IconButton - className="components-icon-button components-toolbar__control" - label={ __( 'Edit video' ) } - onClick={ this.switchToEditing } - icon="edit" - /> - </Toolbar> - </BlockControls> - <figure className={ classnames( className, 'wp-block-embed', 'is-type-video' ) }> - { /* - Disable the video player so the user clicking on it won't play the - video when the controls are enabled. - */ } - <Disabled> - <div className="wp-block-embed__wrapper"> - <SandBox html={ html } scripts={ scripts } /> - </div> - </Disabled> - { ( ! RichText.isEmpty( caption ) || isSelected ) && ( - <RichText - tagName="figcaption" - placeholder={ __( 'Write caption…' ) } - value={ caption } - onChange={ value => setAttributes( { caption: value } ) } - inlineToolbar - /> - ) } - </figure> - </Fragment> - ); - } - }; - -export default createHigherOrderComponent( - compose( [ - withSelect( ( select, ownProps ) => { - const { guid, src } = ownProps.attributes; - const { getEmbedPreview, isRequestingEmbedPreview } = select( 'core' ); - - const url = !! guid && `https://videopress.com/v/${ guid }`; - const preview = !! url && getEmbedPreview( url ); - - const isFetchingEmbedPreview = !! url && isRequestingEmbedPreview( url ); - const isUploading = isBlobURL( src ); - - return { - isFetchingPreview: isFetchingEmbedPreview, - isUploading, - preview, - }; - } ), - VideoPressEdit, - ] ), - 'withVideoPressEdit' -); diff --git a/extensions/blocks/videopress/editor.js b/extensions/blocks/videopress/editor.js deleted file mode 100644 index c8d2e87d82cf1..0000000000000 --- a/extensions/blocks/videopress/editor.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * External dependencies - */ -import { createBlobURL } from '@wordpress/blob'; -import { createBlock } from '@wordpress/blocks'; -import { mediaUpload } from '@wordpress/editor'; -import { addFilter } from '@wordpress/hooks'; -import { every } from 'lodash'; - -/** - * Internal dependencies - */ -import withVideoPressEdit from './edit'; -import withVideoPressSave from './save'; -import getJetpackExtensionAvailability from '../../utils/get-jetpack-extension-availability'; - -const addVideoPressSupport = ( settings, name ) => { - if ( 'core/video' !== name ) { - return settings; - } - - const { available, unavailableReason } = getJetpackExtensionAvailability( 'videopress' ); - - // We customize the video block even if VideoPress it not available so we can support videos that were uploaded to - // VideoPress if it was available in the past (i.e. before a plan downgrade). - if ( available || [ 'missing_plan', 'missing_module' ].includes( unavailableReason ) ) { - return { - ...settings, - - attributes: { - autoplay: { - type: 'boolean', - }, - caption: { - type: 'string', - source: 'html', - selector: 'figcaption', - }, - controls: { - type: 'boolean', - default: true, - }, - guid: { - type: 'string', - }, - id: { - type: 'number', - }, - loop: { - type: 'boolean', - }, - muted: { - type: 'boolean', - }, - poster: { - type: 'string', - }, - preload: { - type: 'string', - default: 'metadata', - }, - src: { - type: 'string', - }, - }, - - transforms: { - ...settings.transforms, - from: [ - { - type: 'files', - isMatch: files => every( files, file => file.type.indexOf( 'video/' ) === 0 ), - // We define a higher priority (lower number) than the default of 10. This ensures that this - // transformation prevails over the core video block default transformations. - priority: 9, - transform: ( files, onChange ) => { - const blocks = []; - files.forEach( file => { - const block = createBlock( 'core/video', { - src: createBlobURL( file ), - } ); - mediaUpload( { - filesList: [ file ], - onFileChange: ( [ { id, url } ] ) => { - onChange( block.clientId, { id, src: url } ); - }, - allowedTypes: [ 'video' ], - } ); - blocks.push( block ); - } ); - return blocks; - }, - }, - ], - }, - - supports: { - ...settings.supports, - reusable: false, - }, - - edit: withVideoPressEdit( settings.edit ), - - save: withVideoPressSave( settings.save ), - - deprecated: [ - { - attributes: settings.attributes, - save: settings.save, - isEligible: attrs => ! attrs.guid, - }, - ], - }; - } - - return settings; -}; - -addFilter( 'blocks.registerBlockType', 'jetpack/videopress', addVideoPressSupport ); diff --git a/extensions/blocks/videopress/index.js b/extensions/blocks/videopress/index.js deleted file mode 100644 index 60d3531f4ddc5..0000000000000 --- a/extensions/blocks/videopress/index.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Internal dependencies - */ -// Register the hook that customize the core video block -import './editor'; - -// This is exporting deliberately an empty object so we don't break `getExtensions` -// at the same time we don't register any new plugin or block -export const settings = {}; diff --git a/extensions/blocks/videopress/loading.js b/extensions/blocks/videopress/loading.js deleted file mode 100644 index 76c25d4cf06a8..0000000000000 --- a/extensions/blocks/videopress/loading.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * External dependencies - */ -import { Spinner } from '@wordpress/components'; - -const Loading = ( { text } ) => ( - <div className="wp-block-embed is-loading"> - <Spinner /> - <p>{ text }</p> - </div> -); - -export default Loading; diff --git a/extensions/blocks/videopress/save.js b/extensions/blocks/videopress/save.js deleted file mode 100644 index 52790480769af..0000000000000 --- a/extensions/blocks/videopress/save.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * External dependencies - */ -import { createHigherOrderComponent } from '@wordpress/compose'; -import { RichText } from '@wordpress/editor'; - -const VideoPressSave = CoreVideoSave => props => { - const { attributes: { caption, guid } = {} } = props; - - if ( ! guid ) { - /** - * We return the element produced by the render so Gutenberg can add the block class when cloning the element. - * This is due to the fact that `React.cloneElement` ignores the class name when we clone a component to be - * rendered (i.e. `React.cloneElement( <CoreVideoSave { ...props } />, { className: 'wp-block-video' } )`). - * - * @see https://github.com/WordPress/gutenberg/blob/3f1324b53cc8bb45d08d12d5321d6f88510bed09/packages/blocks/src/api/serializer.js#L78-L96 - * @see https://github.com/WordPress/gutenberg/blob/c5f9bd88125282a0c35f887cc8d835f065893112/packages/editor/src/hooks/generated-class-name.js#L42 - * @see https://github.com/Automattic/wp-calypso/pull/30546#issuecomment-463637946 - */ - return CoreVideoSave( props ); - } - - const url = `https://videopress.com/v/${ guid }`; - - return ( - <figure className="wp-block-embed is-type-video is-provider-videopress"> - <div className="wp-block-embed__wrapper"> - { `\n${ url }\n` /* URL needs to be on its own line. */ } - </div> - { ! RichText.isEmpty( caption ) && ( - <RichText.Content tagName="figcaption" value={ caption } /> - ) } - </figure> - ); -}; - -export default createHigherOrderComponent( VideoPressSave, 'withVideoPressSave' ); diff --git a/extensions/blocks/vr/edit.js b/extensions/blocks/vr/edit.js deleted file mode 100644 index 9ee1dc35219c3..0000000000000 --- a/extensions/blocks/vr/edit.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { Placeholder, SelectControl, TextControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import VRImageSave from './save'; -import { __ } from '../../utils/i18n'; - -export default class VRImageEdit extends Component { - onChangeUrl = value => void this.props.setAttributes( { url: value.trim() } ); - onChangeView = value => void this.props.setAttributes( { view: value } ); - - render() { - const { attributes, className } = this.props; - - if ( attributes.url && attributes.view ) { - return <VRImageSave attributes={ attributes } className={ className } />; - } - - return ( - <Placeholder - key="placeholder" - icon="format-image" - label={ __( 'VR Image' ) } - className={ className } - > - <TextControl - type="url" - label={ __( 'Enter URL to VR image' ) } - value={ attributes.url } - onChange={ this.onChangeUrl } - /> - <SelectControl - label={ __( 'View Type' ) } - disabled={ ! attributes.url } - value={ attributes.view } - onChange={ this.onChangeView } - options={ [ - { label: '', value: '' }, - { label: __( '360°' ), value: '360' }, - { label: __( 'Cinema' ), value: 'cinema' }, - ] } - /> - </Placeholder> - ); - } -} diff --git a/extensions/blocks/vr/editor.js b/extensions/blocks/vr/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/vr/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/vr/editor.scss b/extensions/blocks/vr/editor.scss deleted file mode 100644 index 3f628c193b59c..0000000000000 --- a/extensions/blocks/vr/editor.scss +++ /dev/null @@ -1,12 +0,0 @@ - -.wp-block-jetpack-vr { - position: relative; - max-width: 525px; - margin-left: auto; - margin-right: auto; - overflow: hidden; - - .components-placeholder__fieldset { - justify-content: space-around; - } -} diff --git a/extensions/blocks/vr/index.js b/extensions/blocks/vr/index.js deleted file mode 100644 index 7e02cfe2fefbf..0000000000000 --- a/extensions/blocks/vr/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Internal dependencies - */ -import './editor.scss'; -import VRImageEdit from './edit'; -import VRImageSave from './save'; -import { __ } from '../../utils/i18n'; - -export const name = 'vr'; - -export const settings = { - title: __( 'VR Image' ), - description: __( 'Embed 360° photos and Virtual Reality (VR) content' ), - icon: 'embed-photo', - category: 'jetpack', - keywords: [ __( 'vr' ), __( 'panorama' ), __( '360' ) ], - supports: { - html: false, - }, - attributes: { - url: { - type: 'string', - }, - view: { - type: 'string', - }, - }, - edit: VRImageEdit, - save: VRImageSave, -}; diff --git a/extensions/blocks/vr/save.js b/extensions/blocks/vr/save.js deleted file mode 100644 index ab341f798ed77..0000000000000 --- a/extensions/blocks/vr/save.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * External dependencies - */ -import { addQueryArgs } from '@wordpress/url'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; - -export default ( { attributes: { url, view }, className } ) => ( - <div className={ className }> - <iframe - title={ __( 'VR Image' ) } - allowFullScreen="true" - frameBorder="0" - width="100%" - height="300" - src={ addQueryArgs( 'https://vr.me.sh/view/', { url, view } ) } - /> - </div> -); diff --git a/extensions/blocks/wordads/constants.js b/extensions/blocks/wordads/constants.js deleted file mode 100644 index 1c56fd7d589f1..0000000000000 --- a/extensions/blocks/wordads/constants.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * External dependencies - */ -import { Path, SVG } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; - -export const DEFAULT_FORMAT = 'mrec'; -export const AD_FORMATS = [ - { - height: 250, - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M19 3H5c-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 16H5V5h14v14zm-7-2h2V7h-4v2h2z" /> - </SVG> - ), - name: __( 'Rectangle 300x250' ), - tag: DEFAULT_FORMAT, - width: 300, - editorPadding: 30, - }, - { - height: 90, - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M19 3H5c-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 16H5V5h14v14zm-4-4h-4v-2h2c1.1 0 2-.89 2-2V9c0-1.11-.9-2-2-2H9v2h4v2h-2c-1.1 0-2 .89-2 2v4h6v-2z" /> - </SVG> - ), - name: __( 'Leaderboard 728x90' ), - tag: 'leaderboard', - width: 728, - editorPadding: 40, - }, - { - height: 50, - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M19 3H5c-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 16H5V5h14v14zm-4-4v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V9c0-1.11-.9-2-2-2H9v2h4v2h-2v2h2v2H9v2h4c1.1 0 2-.89 2-2z" /> - </SVG> - ), - name: __( 'Mobile Leaderboard 320x50' ), - tag: 'mobile_leaderboard', - width: 320, - editorPadding: 60, - }, - { - height: 600, - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M.04 0h24v24h-24V0z" /> - <Path d="M19.04 3h-14c-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 16h-14V5h14v14zm-6-2h2V7h-2v4h-2V7h-2v6h4z" /> - </SVG> - ), - name: __( 'Wide Skyscraper 160x600' ), - tag: 'wideskyscraper', - width: 160, - editorPadding: 30, - }, -]; diff --git a/extensions/blocks/wordads/edit.js b/extensions/blocks/wordads/edit.js deleted file mode 100644 index 720588f25bb48..0000000000000 --- a/extensions/blocks/wordads/edit.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '../../utils/i18n'; -import { BlockControls } from '@wordpress/editor'; -import { Component, Fragment } from '@wordpress/element'; -import { Placeholder } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import FormatPicker from './format-picker'; -import { AD_FORMATS } from './constants'; -import { icon, title } from './'; - -import './editor.scss'; - -class WordAdsEdit extends Component { - render() { - const { attributes, setAttributes } = this.props; - const { format } = attributes; - const selectedFormatObject = AD_FORMATS.filter( ( { tag } ) => tag === format )[ 0 ]; - - return ( - <Fragment> - <BlockControls> - <FormatPicker - value={ format } - onChange={ nextFormat => setAttributes( { format: nextFormat } ) } - /> - </BlockControls> - <div className={ `wp-block-jetpack-wordads jetpack-wordads-${ format }` }> - <div - className="jetpack-wordads__ad" - style={ { - width: selectedFormatObject.width, - height: selectedFormatObject.height + selectedFormatObject.editorPadding, - } } - > - <div className="jetpack-wordads__header">{ __( 'Advertisements' ) }</div> - <Placeholder icon={ icon } label={ title } /> - <div className="jetpack-wordads__footer">{ __( 'Report this Ad' ) }</div> - </div> - </div> - </Fragment> - ); - } -} -export default WordAdsEdit; diff --git a/extensions/blocks/wordads/editor.js b/extensions/blocks/wordads/editor.js deleted file mode 100644 index babee275335fe..0000000000000 --- a/extensions/blocks/wordads/editor.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import registerJetpackBlock from '../../utils/register-jetpack-block'; -import { name, settings } from '.'; - -registerJetpackBlock( name, settings ); diff --git a/extensions/blocks/wordads/editor.scss b/extensions/blocks/wordads/editor.scss deleted file mode 100644 index 5643383ec889f..0000000000000 --- a/extensions/blocks/wordads/editor.scss +++ /dev/null @@ -1,61 +0,0 @@ -.wp-block-jetpack-wordads { - background: var( --color-white ); -} - -[data-type='jetpack/wordads'][data-align='center'] .jetpack-wordads__ad { - margin: 0 auto; -} - -.jetpack-wordads__header, -.jetpack-wordads__footer { - font-size: 10px; - font-family: sans-serif; -} - -.jetpack-wordads__header { - text-align: left; -} - -.jetpack-wordads__footer { - text-transform: uppercase; - text-align: right; -} - -.jetpack-wordads__ad { - display: flex; - overflow: hidden; - flex-direction: column; - max-width: 100%; - - .components-placeholder { - flex-grow: 2; - } -} - -.jetpack-wordads-leaderboard .components-placeholder { - min-height: 90px; -} - -.jetpack-wordads-mobile_leaderboard .components-placeholder { - min-height: 72px; -} - -.wp-block-jetpack-wordads__format-picker { - $active-item-outline-width: 2px; - - // @TODO replace with Gutenberg variables - $dark-gray-500: #555d66; - $dark-gray-900: #191e23; - - padding: 7px; - - // Leave space between elements for active state styling - .components-menu-item__button + .components-menu-item__button { - margin-top: $active-item-outline-width; - } - - .components-menu-item__button.is-active { - color: $dark-gray-900; - box-shadow: 0 0 0 $active-item-outline-width $dark-gray-500 !important; - } -} diff --git a/extensions/blocks/wordads/format-picker.js b/extensions/blocks/wordads/format-picker.js deleted file mode 100644 index 3f113f0e9c299..0000000000000 --- a/extensions/blocks/wordads/format-picker.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * External Dependencies - */ -import { Dropdown, MenuItem, NavigableMenu, Path, SVG, Toolbar } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; -import { AD_FORMATS } from './constants'; - -const label = __( 'Pick an ad format' ); - -export default function FormatPicker( { value, onChange } ) { - return ( - <Dropdown - position="bottom right" - renderToggle={ ( { onToggle, isOpen } ) => { - return ( - <Toolbar - controls={ [ - { - icon: ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M1 9h2V7H1v2zm0 4h2v-2H1v2zm0-8h2V3c-1.1 0-2 .9-2 2zm8 16h2v-2H9v2zm-8-4h2v-2H1v2zm2 4v-2H1c0 1.1.9 2 2 2zM21 3h-8v6h10V5c0-1.1-.9-2-2-2zm0 14h2v-2h-2v2zM9 5h2V3H9v2zM5 21h2v-2H5v2zM5 5h2V3H5v2zm16 16c1.1 0 2-.9 2-2h-2v2zm0-8h2v-2h-2v2zm-8 8h2v-2h-2v2zm4 0h2v-2h-2v2z" /> - </SVG> - ), - title: label, - onClick: onToggle, - extraProps: { 'aria-expanded': isOpen }, - className: 'wp-block-jetpack-wordads__format-picker-icon', - }, - ] } - /> - ); - } } - renderContent={ ( { onClose } ) => ( - <NavigableMenu className="wp-block-jetpack-wordads__format-picker"> - { AD_FORMATS.map( ( { tag, name, icon } ) => ( - <MenuItem - className={ tag === value ? 'is-active' : undefined } - icon={ icon } - isSelected={ tag === value } - key={ tag } - onClick={ () => { - onChange( tag ); - onClose(); - } } - role="menuitemcheckbox" - > - { name } - </MenuItem> - ) ) } - </NavigableMenu> - ) } - /> - ); -} diff --git a/extensions/blocks/wordads/index.js b/extensions/blocks/wordads/index.js deleted file mode 100644 index c42d1bfb29506..0000000000000 --- a/extensions/blocks/wordads/index.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * External dependencies - */ -import { ExternalLink, Path, SVG } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { __ } from '../../utils/i18n'; -import edit from './edit'; -import { DEFAULT_FORMAT } from './constants'; - -export const name = 'wordads'; -export const title = __( 'Ad' ); - -export const icon = ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - <Path d="M12,8H4A2,2 0 0,0 2,10V14A2,2 0 0,0 4,16H5V20A1,1 0 0,0 6,21H8A1,1 0 0,0 9,20V16H12L17,20V4L12,8M15,15.6L13,14H4V10H13L15,8.4V15.6M21.5,12C21.5,13.71 20.54,15.26 19,16V8C20.53,8.75 21.5,10.3 21.5,12Z" /> - </SVG> -); - -export const settings = { - title, - - description: ( - <Fragment> - <p>{ __( 'Earn income by adding high quality ads to your post' ) }</p> - <ExternalLink href="https://wordads.co/">{ __( 'Learn all about WordAds' ) }</ExternalLink> - </Fragment> - ), - - icon, - attributes: { - align: { - type: 'string', - default: 'center', - }, - format: { - type: 'string', - default: DEFAULT_FORMAT, - }, - }, - - category: 'jetpack', - - keywords: [ __( 'ads' ), 'WordAds', __( 'Advertisement' ) ], - - supports: { - align: [ 'left', 'center', 'right' ], - alignWide: false, - className: false, - customClassName: false, - html: false, - reusable: false, - }, - edit, - save: () => null, -}; diff --git a/extensions/shared/README.md b/extensions/shared/README.md deleted file mode 100644 index 25a49ce1ba42c..0000000000000 --- a/extensions/shared/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Jetpack Block Editor Extensions: Shared - -The shared directory is meant to contain components, scripts, and stylesheets that are used in multiple blocks. diff --git a/extensions/shared/frontend-management.js b/extensions/shared/frontend-management.js deleted file mode 100644 index dbb830e6e816e..0000000000000 --- a/extensions/shared/frontend-management.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * External dependencies - */ -import { assign, kebabCase } from 'lodash'; -import { createElement, render } from '@wordpress/element'; - -export class FrontendManagement { - blockIterator( rootNode, blocks ) { - blocks.forEach( block => { - this.initializeFrontendReactBlocks( block.component, block.options, rootNode ); - } ); - } - initializeFrontendReactBlocks( component, options = {}, rootNode ) { - const { attributes, name, prefix } = options.settings; - const { selector } = options; - const fullName = prefix && prefix.length ? `${ prefix }/${ name }` : name; - const blockClass = `.wp-block-${ fullName.replace( '/', '-' ) }`; - - const blockNodeList = rootNode.querySelectorAll( blockClass ); - for ( const node of blockNodeList ) { - const data = this.extractAttributesFromContainer( node, attributes ); - assign( data, options.props ); - const children = this.extractChildrenFromContainer( node ); - const el = createElement( component, data, children ); - render( el, selector ? node.querySelector( selector ) : node ); - } - } - extractAttributesFromContainer( node, attributes ) { - const data = {}; - for ( const name in attributes ) { - const attribute = attributes[ name ]; - const dataAttributeName = 'data-' + kebabCase( name ); - data[ name ] = node.getAttribute( dataAttributeName ); - if ( attribute.type === 'boolean' ) { - data[ name ] = data[ name ] === 'false' ? false : !! data[ name ]; - } - if ( attribute.type === 'array' || attribute.type === 'object' ) { - try { - data[ name ] = JSON.parse( data[ name ] ); - } catch ( e ) { - // console.log( 'Error decoding JSON data for field ' + name, e); - data[ name ] = null; - } - } - } - return data; - } - extractChildrenFromContainer( node ) { - const children = [ ...node.childNodes ]; - return children.map( child => { - const attr = {}; - for ( let i = 0; i < child.attributes.length; i++ ) { - const attribute = child.attributes[ i ]; - attr[ attribute.nodeName ] = attribute.nodeValue; - } - attr.dangerouslySetInnerHTML = { - __html: child.innerHTML, - }; - return createElement( child.tagName.toLowerCase(), attr ); - } ); - } -} - -export default FrontendManagement; diff --git a/extensions/shared/get-site-fragment.js b/extensions/shared/get-site-fragment.js deleted file mode 100644 index 6d141e4e70ce7..0000000000000 --- a/extensions/shared/get-site-fragment.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import { includes } from 'lodash'; - -/** - * Internal dependencies - */ -// FIXME: REMOVE! -import { getSiteFragment as getCalypsoSiteFragment } from '../../../../client/lib/route/path'; - -/** - * Returns the site fragment in the environment we're running Gutenberg in. - * - * @returns {?Number|String} Site fragment (ID or slug); null for WP.org wp-admin. - */ -export default function getSiteFragment() { - // WP.com wp-admin exposes the site ID in window._currentSiteId - if ( window && window._currentSiteId ) { - return window._currentSiteId; - } - - // FIXME: REMOVE THIS BLOCK! vvv - // Calypso will contain a site slug or ID in the site fragment. - // WP.org will contain either `post` or `post-new.php`. - const siteFragment = getCalypsoSiteFragment( window.location.pathname ); - if ( ! includes( [ 'post.php', 'post-new.php' ], siteFragment ) ) { - return siteFragment || null; - } - // FIXME: REMOVE TO HERE ^^^ - - // Gutenberg in Jetpack adds a site fragment in the initial state - if ( - window && - window.Jetpack_Editor_Initial_State && - window.Jetpack_Editor_Initial_State.siteFragment - ) { - return window.Jetpack_Editor_Initial_State.siteFragment; - } - - return null; -} diff --git a/extensions/shared/help-message.js b/extensions/shared/help-message.js deleted file mode 100644 index 4c3b7eb78ab21..0000000000000 --- a/extensions/shared/help-message.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * External dependencies - */ -import classNames from 'classnames'; - -/** - * Internal dependencies - */ -import GridiconNoticeOutline from 'gridicons/dist/notice-outline'; -import './help-message.scss'; - -export default ( { children = null, isError = false, ...props } ) => { - const classes = classNames( 'help-message', { - 'help-message-is-error': isError, - } ); - - return ( - children && ( - <div className={ classes } { ...props }> - { isError && <GridiconNoticeOutline size="24" /> } - <span>{ children }</span> - </div> - ) - ); -}; diff --git a/extensions/shared/help-message.scss b/extensions/shared/help-message.scss deleted file mode 100644 index b8ea026fa4560..0000000000000 --- a/extensions/shared/help-message.scss +++ /dev/null @@ -1,21 +0,0 @@ - -.help-message { - display: flex; - font-size: 13px; - line-height: 1.4em; - margin-bottom: 1em; - margin-top: -0.5em; - svg { - margin-right: 5px; - min-width: 24px; - } - > span { - margin-top: 2px; - } - &.help-message-is-error { - color: var( --color-error ); - svg { - fill: var( --color-error ); - } - } -} diff --git a/extensions/shared/jetpack-logo.js b/extensions/shared/jetpack-logo.js deleted file mode 100644 index 0d13127b9689f..0000000000000 --- a/extensions/shared/jetpack-logo.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * External dependencies - */ -import { Path, Polygon, SVG } from '@wordpress/components'; -import classNames from 'classnames'; - -export default ( { size = 24, className } ) => ( - <SVG - className={ classNames( 'jetpack-logo', className ) } - width={ size } - height={ size } - viewBox="0 0 32 32" - > - <Path - className="jetpack-logo__icon-circle" - fill="#00be28" - d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z" - /> - <Polygon className="jetpack-logo__icon-triangle" fill="#fff" points="15,19 7,19 15,3 " /> - <Polygon className="jetpack-logo__icon-triangle" fill="#fff" points="17,29 17,13 25,13 " /> - </SVG> -); diff --git a/extensions/shared/jetpack-plugin-sidebar.js b/extensions/shared/jetpack-plugin-sidebar.js deleted file mode 100644 index b3cfd3d789a39..0000000000000 --- a/extensions/shared/jetpack-plugin-sidebar.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * External dependencies - */ -import { createSlotFill } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; -import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/edit-post'; -import { registerPlugin } from '@wordpress/plugins'; - -/** - * Internal dependencies - */ -import './jetpack-plugin-sidebar.scss'; -import JetpackLogo from './jetpack-logo'; - -const { Fill, Slot } = createSlotFill( 'JetpackPluginSidebar' ); - -const JetpackPluginSidebar = ( { children } ) => <Fill>{ children }</Fill>; - -JetpackPluginSidebar.Slot = () => ( - <Slot> - { fills => { - if ( ! fills.length ) { - return null; - } - - return ( - <Fragment> - <PluginSidebarMoreMenuItem target="jetpack" icon={ <JetpackLogo /> }> - Jetpack - </PluginSidebarMoreMenuItem> - <PluginSidebar name="jetpack" title="Jetpack" icon={ <JetpackLogo /> }> - { fills } - </PluginSidebar> - </Fragment> - ); - } } - </Slot> -); - -registerPlugin( 'jetpack-sidebar', { - render: () => <JetpackPluginSidebar.Slot />, -} ); - -export default JetpackPluginSidebar; diff --git a/extensions/shared/jetpack-plugin-sidebar.scss b/extensions/shared/jetpack-plugin-sidebar.scss deleted file mode 100644 index 494cba0f8e582..0000000000000 --- a/extensions/shared/jetpack-plugin-sidebar.scss +++ /dev/null @@ -1,42 +0,0 @@ -.edit-post-pinned-plugins, -.edit-post-more-menu__content { - .components-icon-button { - .jetpack-logo { - width: 20px; - height: 20px; - } - } -} - -.edit-post-more-menu__content { - .components-icon-button { - .jetpack-logo { - margin-right: 4px; - } - } -} - -.edit-post-pinned-plugins { - .components-icon-button { - &:not( .is-toggled ), - &:hover, - &.is-toggled, - &.is-toggled:hover { - .jetpack-logo, - .jetpack-logo__icon-circle, - .jetpack-logo__icon-triangle { - stroke: none !important; - } - - .jetpack-logo__icon-circle { - // CSS colour variables are not available in Gutenberg extensions when built by SDK - fill: $green-jetpack !important; - } - - .jetpack-logo__icon-triangle { - // CSS colour variables are not available in Gutenberg extensions when built by SDK - fill: var( --color-white ) !important; - } - } - } -} diff --git a/extensions/shared/test/get-site-fragment.js b/extensions/shared/test/get-site-fragment.js deleted file mode 100644 index ec51761bde343..0000000000000 --- a/extensions/shared/test/get-site-fragment.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Internal dependencies - */ -import getSiteFragment from '../get-site-fragment'; - -describe( 'getSiteFragment()', () => { - let beforeWindow; - - beforeAll( () => { - beforeWindow = global.window; - global.window = { - location: {}, - }; - } ); - - afterAll( () => { - global.window = beforeWindow; - } ); - - test( 'should return null by default', () => { - window.location.pathname = '/'; - expect( getSiteFragment() ).toBeNull(); - } ); - - test( 'should return null when editing a post in wp-admin', () => { - window.location.pathname = '/wp-admin/post.php'; - expect( getSiteFragment() ).toBeNull(); - } ); - - test( 'should return null when starting a new post in wp-admin', () => { - window.location.pathname = '/wp-admin/post-new.php'; - expect( getSiteFragment() ).toBeNull(); - } ); - - test( 'should return site fragment when starting a post in calypso', () => { - window.location.pathname = '/block-editor/post/yourjetpack.blog'; - expect( getSiteFragment() ).toBe( 'yourjetpack.blog' ); - } ); - - test( 'should return site fragment when editing a post in calypso', () => { - window.location.pathname = '/block-editor/post/yourjetpack.blog/123'; - expect( getSiteFragment() ).toBe( 'yourjetpack.blog' ); - } ); - - test( 'should return site ID when _currentSiteId is exposed', () => { - window.location.pathname = '/block-editor/post/yourjetpack.blog/123'; - window._currentSiteId = 12345678; - expect( getSiteFragment() ).toBe( 12345678 ); - } ); - - test( 'should return site slug when editing a post in Gutenberg in WP-Admin', () => { - delete window._currentSiteId; - window.Jetpack_Editor_Initial_State = { - siteFragment: 'yourjetpack.blog', - }; - expect( getSiteFragment() ).toBe( 'yourjetpack.blog' ); - } ); -} ); diff --git a/extensions/utils/README.md b/extensions/utils/README.md deleted file mode 100644 index 54faf077c1658..0000000000000 --- a/extensions/utils/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Jetpack Block Editor Extensions: Utils - -This folder contains files that are not automatically bundled, -but included on an individual basis instead. - -## getJetpackData() - -Gets a list of available blocks, and jetpack's connection status. -On a Jetpack site, there are special cases when a block is considered unavailable. -For example, the site may not have the required module, they may not have the required plan, -or the site admin may have filtered out the use of a certain block. - -## registerJetpackBlock() - -This util will only register a block if it meets the availability requirements described above. - - -## registerJetpackPlugin() - -This util will only register a Gutenberg plugin if it meets the availability requirements described above. diff --git a/extensions/utils/block-styles.js b/extensions/utils/block-styles.js deleted file mode 100644 index 5a278356fc6d3..0000000000000 --- a/extensions/utils/block-styles.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * External dependencies - */ -import TokenList from '@wordpress/token-list'; -import { find } from 'lodash'; - -/** - * Returns the active style from the given className. - * - * From @link https://github.com/WordPress/gutenberg/blob/ddac4f3cf8fd311169c7e125411343a437bdbb5a/packages/editor/src/components/block-styles/index.js#L20-L42 - * - * @param {Array} styles Block style variations. - * @param {string} className Class name - * - * @return {Object?} The active style. - */ -function getActiveStyle( styles, className ) { - for ( const style of new TokenList( className ).values() ) { - if ( style.indexOf( 'is-style-' ) === -1 ) { - continue; - } - - const potentialStyleName = style.substring( 9 ); - const activeStyle = find( styles, { name: potentialStyleName } ); - if ( activeStyle ) { - return activeStyle; - } - } - - return find( styles, 'isDefault' ); -} - -export function getActiveStyleName( styles, className ) { - const style = getActiveStyle( styles, className ); - return style ? style.name : null; -} - -export function getDefaultStyleClass( styles ) { - const defaultStyle = find( styles, 'isDefault' ); - return defaultStyle ? `is-style-${ defaultStyle.name }` : null; -} - -/** - * Checks if className has a class selector starting with `is-style-` - * Does not check validity of found style. - * - * @param {String} classNames Optional. Space separated classNames. Defaults to ''. - * @return {Boolean} true if `classNames` has a Gutenberg style class - */ -export function hasStyleClass( classNames = '' ) { - return classNames.split( ' ' ).some( className => className.startsWith( 'is-style-' ) ); -} diff --git a/extensions/utils/clipboard-input.js b/extensions/utils/clipboard-input.js deleted file mode 100644 index 44250540f6c9a..0000000000000 --- a/extensions/utils/clipboard-input.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * External dependencies - */ -import { Component } from '@wordpress/element'; -import { ClipboardButton, TextControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { __, _x } from './i18n'; -import './clipboard-input.scss'; - -class ClipboardInput extends Component { - state = { - hasCopied: false, - }; - - onCopy = () => this.setState( { hasCopied: true } ); - - onFinishCopy = () => this.setState( { hasCopied: false } ); - - onFocus = event => event.target.select(); - - render() { - const { link } = this.props; - const { hasCopied } = this.state; - - if ( ! link ) { - return null; - } - - return ( - <div className="jetpack-clipboard-input"> - <TextControl readOnly onFocus={ this.onFocus } value={ link } /> - <ClipboardButton - isDefault - onCopy={ this.onCopy } - onFinishCopy={ this.onFinishCopy } - text={ link } - > - { hasCopied ? __( 'Copied!' ) : _x( 'Copy', 'verb' ) } - </ClipboardButton> - </div> - ); - } -} - -export default ClipboardInput; diff --git a/extensions/utils/clipboard-input.scss b/extensions/utils/clipboard-input.scss deleted file mode 100644 index d0118a3efc72c..0000000000000 --- a/extensions/utils/clipboard-input.scss +++ /dev/null @@ -1,7 +0,0 @@ -.jetpack-clipboard-input { - display: flex; - - .components-clipboard-button { - margin: 2px 0 0 6px; - } -} diff --git a/extensions/utils/get-jetpack-data.js b/extensions/utils/get-jetpack-data.js deleted file mode 100644 index 6871383a6dc0e..0000000000000 --- a/extensions/utils/get-jetpack-data.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * External Dependencies - */ -import { get } from 'lodash'; - -export const JETPACK_DATA_PATH = 'Jetpack_Editor_Initial_State'; - -export default function getJetpackData() { - return get( 'object' === typeof window ? window : null, [ JETPACK_DATA_PATH ], null ); -} diff --git a/extensions/utils/get-jetpack-extension-availability.js b/extensions/utils/get-jetpack-extension-availability.js deleted file mode 100644 index a3636f1c91289..0000000000000 --- a/extensions/utils/get-jetpack-extension-availability.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * External dependencies - */ -import { get, includes } from 'lodash'; - -/** - * Internal dependencies - */ -import extensionSlugsJson from '../preset/index.json'; -import getJetpackData from './get-jetpack-data'; - -/** - * Return whether a Jetpack Gutenberg extension is available or not. - * - * Defaults to `false` for production blocks, and to `true` for beta blocks. - * This is to make it easier for folks to write their first block without needing - * to touch the server side. - * - * @param {string} name The extension's name (without the `jetpack/` prefix) - * @returns {object} Object indicating if the extension is available (property `available`) and the reason why it is - * unavailable (property `unavailable_reason`). - */ -export default function getJetpackExtensionAvailability( name ) { - const data = getJetpackData(); - const defaultAvailability = includes( extensionSlugsJson.beta, name ); - const available = get( data, [ 'available_blocks', name, 'available' ], defaultAvailability ); - const unavailableReason = get( - data, - [ 'available_blocks', name, 'unavailable_reason' ], - 'unknown' - ); - - return { - available, - ...( ! available && { unavailableReason } ), - }; -} diff --git a/extensions/utils/i18n.js b/extensions/utils/i18n.js deleted file mode 100644 index 0759252b02b17..0000000000000 --- a/extensions/utils/i18n.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * This contains a set of wrappers for all the @wordpress/i18n localization functions. - * Each of the wrappers has the same signature like the original corresponding function, - * but without the textdomain at the end. - * - * The wrappers are necessary because we'd like to reuse i18n-calypso provided translations - * for our Gutenberg blocks, but we'd also like to not include i18n-calypso in block bundles. - * Instead, we use @wordpress/i18n, but it requires a textdomain in order to know where - * to look for the translations. - * - * In the same time, we use those blocks in Jetpack, and in order to be able to index the - * translations there without issues, the localization function calls in the source code - * must not contain a textdomain. - */ - -/** - * External dependencies - */ -import { __ as wpI18n__, _n as wpI18n_n, _x as wpI18n_x, _nx as wpI18n_nx } from '@wordpress/i18n'; - -/** - * Module variables - */ -const TEXTDOMAIN = 'jetpack'; - -/** - * Add a textdomain to arguments of a localization function call. - * - * @param {Array} originalArgs Arguments that the localization function was called with. - * - * @return {Array} Arguments, with textdomain added as the last one. - */ -const addTextdomain = originalArgs => { - const args = [ ...originalArgs ]; - args.push( TEXTDOMAIN ); - return args; -}; - -/** - * Retrieve the translation of text. - * - * @see https://developer.wordpress.org/reference/functions/__/ - * - * @param {string} text Text to translate. - * @param {?string} domain Domain to retrieve the translated text. - * - * @return {string} Translated text. - */ -export function __() { - return wpI18n__( ...addTextdomain( arguments ) ); -} - -/** - * Translates and retrieves the singular or plural form based on the supplied - * number. - * - * @see https://developer.wordpress.org/reference/functions/_n/ - * - * @param {string} single The text to be used if the number is singular. - * @param {string} plural The text to be used if the number is plural. - * @param {number} number The number to compare against to use either the - * singular or plural form. - * @param {?string} domain Domain to retrieve the translated text. - * - * @return {string} The translated singular or plural form. - */ -export function _n() { - return wpI18n_n( ...addTextdomain( arguments ) ); -} - -/** - * Retrieve translated string with gettext context. - * - * @see https://developer.wordpress.org/reference/functions/_x/ - * - * @param {string} text Text to translate. - * @param {string} context Context information for the translators. - * @param {?string} domain Domain to retrieve the translated text. - * - * @return {string} Translated context string without pipe. - */ -export function _x() { - return wpI18n_x( ...addTextdomain( arguments ) ); -} - -/** - * Translates and retrieves the singular or plural form based on the supplied - * number, with gettext context. - * - * @see https://developer.wordpress.org/reference/functions/_nx/ - * - * @param {string} single The text to be used if the number is singular. - * @param {string} plural The text to be used if the number is plural. - * @param {number} number The number to compare against to use either the - * singular or plural form. - * @param {string} context Context information for the translators. - * @param {?string} domain Domain to retrieve the translated text. - * - * @return {string} The translated singular or plural form. - */ -export function _nx() { - return wpI18n_nx( ...addTextdomain( arguments ) ); -} diff --git a/extensions/utils/index.js b/extensions/utils/index.js deleted file mode 100644 index 13e2e308b4974..0000000000000 --- a/extensions/utils/index.js +++ /dev/null @@ -1 +0,0 @@ -export { getActiveStyleName, getDefaultStyleClass, hasStyleClass } from './block-styles'; diff --git a/extensions/utils/register-jetpack-block.js b/extensions/utils/register-jetpack-block.js deleted file mode 100644 index be72fe373ab9e..0000000000000 --- a/extensions/utils/register-jetpack-block.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import { registerBlockType } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import getJetpackExtensionAvailability from './get-jetpack-extension-availability'; - -/** - * Registers a gutenberg block if the availability requirements are met. - * - * @param {string} name The block's name. - * @param {object} settings The block's settings. - * @param {object} childBlocks The block's child blocks. - * @returns {object|false} Either false if the block is not available, or the results of `registerBlockType` - */ -export default function registerJetpackBlock( name, settings, childBlocks = [] ) { - const { available, unavailableReason } = getJetpackExtensionAvailability( name ); - const unavailable = ! available; - - if ( unavailable ) { - if ( 'production' !== process.env.NODE_ENV ) { - // eslint-disable-next-line no-console - console.warn( - `Block ${ name } couldn't be registered because it is unavailable (${ unavailableReason }).` - ); - } - return false; - } - - const result = registerBlockType( `jetpack/${ name }`, settings ); - - // Register child blocks. Using `registerBlockType()` directly avoids availability checks -- if - // their parent is available, we register them all, without checking for their individual availability. - childBlocks.forEach( childBlock => - registerBlockType( `jetpack/${ childBlock.name }`, childBlock.settings ) - ); - - return result; -} diff --git a/extensions/utils/register-jetpack-plugin.js b/extensions/utils/register-jetpack-plugin.js deleted file mode 100644 index 8d891462b9ef5..0000000000000 --- a/extensions/utils/register-jetpack-plugin.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * External dependencies - */ -import { registerPlugin } from '@wordpress/plugins'; - -/** - * Internal dependencies - */ -import getJetpackExtensionAvailability from './get-jetpack-extension-availability'; - -/** - * Registers a Gutenberg block if the availability requirements are met. - * - * @param {string} name The plugin's name - * @param {object} settings The plugin's settings. - * @returns {object|false} Either false if the plugin is not available, or the results of `registerPlugin` - */ -export default function registerJetpackPlugin( name, settings ) { - const { available, unavailableReason } = getJetpackExtensionAvailability( name ); - const unavailable = ! available; - - if ( unavailable ) { - if ( 'production' !== process.env.NODE_ENV ) { - // eslint-disable-next-line no-console - console.warn( - `Plugin ${ name } couldn't be registered because it is unavailable (${ unavailableReason }).` - ); - } - return false; - } - - return registerPlugin( `jetpack-${ name }`, settings ); -} diff --git a/extensions/utils/render-material-icon.js b/extensions/utils/render-material-icon.js deleted file mode 100644 index 82d245915631a..0000000000000 --- a/extensions/utils/render-material-icon.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * External dependencies - */ -import { Path, SVG } from '@wordpress/components'; - -const renderMaterialIcon = svg => ( - <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <Path fill="none" d="M0 0h24v24H0V0z" /> - { svg } - </SVG> -); - -export default renderMaterialIcon; diff --git a/extensions/utils/simple-input.js b/extensions/utils/simple-input.js deleted file mode 100644 index c55a5a6f6670b..0000000000000 --- a/extensions/utils/simple-input.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * External dependencies - */ -import { PlainText } from '@wordpress/editor'; - -const simpleInput = ( type, props, label, view, onChange ) => { - const { isSelected } = props; - const value = props.attributes[ type ]; - return ( - <div - className={ isSelected ? `jetpack-${ type }-block is-selected` : `jetpack-${ type }-block` } - > - { ! isSelected && value !== '' && view( props ) } - { ( isSelected || value === '' ) && ( - <PlainText - value={ value } - placeholder={ label } - aria-label={ label } - onChange={ onChange } - /> - ) } - </div> - ); -}; - -export default simpleInput; diff --git a/extensions/utils/submit-button.js b/extensions/utils/submit-button.js deleted file mode 100644 index bc3d090328482..0000000000000 --- a/extensions/utils/submit-button.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { Component, Fragment } from '@wordpress/element'; -import { compose } from '@wordpress/compose'; -import { withFallbackStyles } from '@wordpress/components'; -import { - InspectorControls, - PanelColorSettings, - ContrastChecker, - RichText, - withColors, -} from '@wordpress/editor'; -import { isEqual, get } from 'lodash'; - -/** - * Internal dependencies - */ -import { __ } from './i18n'; - -const { getComputedStyle } = window; - -const applyFallbackStyles = withFallbackStyles( ( node, ownProps ) => { - const { textButtonColor, backgroundButtonColor } = ownProps; - const backgroundColorValue = backgroundButtonColor && backgroundButtonColor.color; - const textColorValue = textButtonColor && textButtonColor.color; - //avoid the use of querySelector if textColor color is known and verify if node is available. - - let textNode; - let button; - - if ( ! textColorValue && node ) { - textNode = node.querySelector( '[contenteditable="true"]' ); - } - - if ( node.querySelector( '.wp-block-button__link' ) ) { - button = node.querySelector( '.wp-block-button__link' ); - } else { - button = node; - } - - let fallbackBackgroundColor; - let fallbackTextColor; - - if ( node ) { - fallbackBackgroundColor = getComputedStyle( button ).backgroundColor; - } - - if ( textNode ) { - fallbackTextColor = getComputedStyle( textNode ).color; - } - - return { - fallbackBackgroundColor: backgroundColorValue || fallbackBackgroundColor, - fallbackTextColor: textColorValue || fallbackTextColor, - }; -} ); - -class SubmitButton extends Component { - componentDidUpdate( prevProps ) { - if ( - ! isEqual( this.props.textButtonColor, prevProps.textButtonColor ) || - ! isEqual( this.props.backgroundButtonColor, prevProps.backgroundButtonColor ) - ) { - const buttonClasses = this.getButtonClasses(); - this.props.setAttributes( { submitButtonClasses: buttonClasses } ); - } - } - getButtonClasses() { - const { textButtonColor, backgroundButtonColor } = this.props; - const textClass = get( textButtonColor, 'class' ); - const backgroundClass = get( backgroundButtonColor, 'class' ); - return classnames( 'wp-block-button__link', { - 'has-text-color': textButtonColor, - [ textClass ]: textClass, - 'has-background': backgroundButtonColor, - [ backgroundClass ]: backgroundClass, - } ); - } - render() { - const { - attributes, - fallbackBackgroundColor, - fallbackTextColor, - setAttributes, - setBackgroundButtonColor, - setTextButtonColor, - } = this.props; - - const backgroundColor = attributes.customBackgroundButtonColor || fallbackBackgroundColor; - const color = attributes.customTextButtonColor || fallbackTextColor; - const buttonStyle = { border: 'none', backgroundColor, color }; - const buttonClasses = this.getButtonClasses(); - - return ( - <Fragment> - <div className="wp-block-button jetpack-submit-button"> - <RichText - placeholder={ __( 'Add text…' ) } - value={ attributes.submitButtonText } - onChange={ nextValue => setAttributes( { submitButtonText: nextValue } ) } - className={ buttonClasses } - style={ buttonStyle } - keepPlaceholderOnFocus - formattingControls={ [] } - /> - </div> - <InspectorControls> - <PanelColorSettings - title={ __( 'Button Color Settings' ) } - colorSettings={ [ - { - value: backgroundColor, - onChange: nextColour => { - setBackgroundButtonColor( nextColour ); - setAttributes( { customBackgroundButtonColor: nextColour } ); - }, - label: __( 'Background Color' ), - }, - { - value: color, - onChange: nextColour => { - setTextButtonColor( nextColour ); - setAttributes( { customTextButtonColor: nextColour } ); - }, - label: __( 'Text Color' ), - }, - ] } - /> - <ContrastChecker textColor={ color } backgroundColor={ backgroundColor } /> - </InspectorControls> - </Fragment> - ); - } -} - -export default compose( [ - withColors( 'backgroundButtonColor', { textButtonColor: 'color' } ), - applyFallbackStyles, -] )( SubmitButton ); diff --git a/extensions/utils/text-match-replace.js b/extensions/utils/text-match-replace.js deleted file mode 100644 index b339719f5e1b7..0000000000000 --- a/extensions/utils/text-match-replace.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * External dependencies - */ -import { isRegExp, escapeRegExp, isString, flatten } from 'lodash'; - -/** - * Given a string, replace every substring that is matched by the `match` regex - * with the result of calling `fn` on matched substring. The result will be an - * array with all odd indexed elements containing the replacements. The primary - * use case is similar to using String.prototype.replace except for React. - * - * React will happily render an array as children of a react element, which - * makes this approach very useful for tasks like surrounding certain text - * within a string with react elements. - * - * Example: - * matchReplace( - * 'Emphasize all phone numbers like 884-555-4443.', - * /([\d|-]+)/g, - * (number, i) => <strong key={i}>{number}</strong> - * ); - * // => ['Emphasize all phone numbers like ', <strong>884-555-4443</strong>, '.' - * - * @param {string} text - The text that you want to replace - * @param {regexp|str} match Must contain a matching group - * @param {function} fn function that helps replace the matched text - * @return {array} An array of string or react components - */ -function replaceString( text, match, fn ) { - let curCharStart = 0; - let curCharLen = 0; - - if ( text === '' ) { - return text; - } else if ( ! text || ! isString( text ) ) { - throw new TypeError( 'First argument must be a string' ); - } - - let re = match; - - if ( ! isRegExp( re ) ) { - re = new RegExp( '(' + escapeRegExp( re ) + ')', 'gi' ); - } - - const result = text.split( re ); - // Apply fn to all odd elements - for ( let i = 1, length = result.length; i < length; i += 2 ) { - curCharLen = result[ i ].length; - curCharStart += result[ i - 1 ].length; - if ( result[ i ] ) { - result[ i ] = fn( result[ i ], i, curCharStart ); - } - curCharStart += curCharLen; - } - - return result; -} - -const textMatchReplace = ( source, match, fn ) => { - if ( ! Array.isArray( source ) ) source = [ source ]; - - return flatten( - source.map( x => { - return isString( x ) ? replaceString( x, match, fn ) : x; - } ) - ); -}; - -export default textMatchReplace; diff --git a/tools/build-release-branch.sh b/tools/build-release-branch.sh index 801a37e292c3a..b225559448fff 100755 --- a/tools/build-release-branch.sh +++ b/tools/build-release-branch.sh @@ -1,5 +1,4 @@ #!/bin/bash - # This script can build a new set of release branches, or update an existing release branch. # It doesn't care which branch you're currently standing on. # @@ -17,68 +16,54 @@ # Exit the build in scary red text if error function exit_build { - echo -e "${RED}Something went wrong and the build has stopped. See error above for more details." - exit 1 + echo -e "${RED}Something went wrong and the build has stopped. See error above for more details." + exit 1 } trap 'exit_build' ERR # Instructions function usage { - echo "usage: $0 [-n new] [-u update <branchname>]" - echo " -n Create new release branches" - echo " -u Update existing release built branch" - echo " Can take an extra param that refers to an existing branch." - echo " Example: $0 -u master-stable" - echo " -h help" - exit 1 + echo "usage: $0 [-n new] [-u update <branchname>]" + echo " -n Create new release branches" + echo " -u Update existing release built branch" + echo " Can take an extra param that refers to an existing branch." + echo " Example: $0 -u master-stable" + echo " -h help" + exit 1 } # This creates a new .gitignore file based on master, but removes the items we need for release builds function create_release_gitignore { - # Copy .gitignore to temp file - mv .gitignore .gitignore-tmp - - # Create empty .gitignore - touch .gitignore - - # Add things to the new .gitignore file, stopping at the things we want to keep. - while IFS='' read -r line || [[ -n "$line" ]]; do - if [ "$line" == "## Things we will need in release branches" ]; then - break - fi - echo "$line" >> .gitignore - done < ".gitignore-tmp" - - # Add custom stuff to .gitignore release - echo "/_inc/client" >> .gitignore - echo "/docker/" >> .gitignore - - # Needs to stay in sync with .svnignore and `create_new_release_branches` in this file. - echo "__snapshots__/" >> .gitignore - echo "/extensions/**/*.css" >> .gitignore - echo "/extensions/**/*.gif" >> .gitignore - echo "/extensions/**/*.jpeg" >> .gitignore - echo "/extensions/**/*.jpg" >> .gitignore - echo "/extensions/**/*.js" >> .gitignore - echo "/extensions/**/*.json" >> .gitignore - echo "/extensions/**/*.jsx" >> .gitignore - echo "/extensions/**/*.md" >> .gitignore - echo "/extensions/**/*.png" >> .gitignore - echo "/extensions/**/*.sass" >> .gitignore - echo "/extensions/**/*.scss" >> .gitignore - echo "/extensions/**/*.svg" >> .gitignore - - # Remove old .gitignore - rm .gitignore-tmp - - git commit .gitignore -m "updated .gitignore" + # Copy .gitignore to temp file + mv .gitignore .gitignore-tmp + + # Create empty .gitignore + touch .gitignore + + # Add things to the new .gitignore file, stopping at the things we want to keep. + while IFS='' read -r line || [[ -n "$line" ]] + do + if [ "$line" == "## Things we will need in release branches" ] + then + break + fi + echo "$line" >> .gitignore + done < ".gitignore-tmp" + + # Add custom stuff to .gitignore release + echo "/_inc/client" >> .gitignore + + # Remove old .gitignore + rm .gitignore-tmp + + git commit .gitignore -m "updated .gitignore" } # Remove stuff from .svnignore for releases function modify_svnignore { - awk '!/.eslintrc.js/' .svnignore > temp && mv temp .svnignore - awk '!/.eslintignore/' .svnignore > temp && mv temp .svnignore - git commit .svnignore -m "Updated .svnignore" + awk '!/.eslintrc.js/' .svnignore > temp && mv temp .svnignore + awk '!/.eslintignore/' .svnignore > temp && mv temp .svnignore + git commit .svnignore -m "Updated .svnignore" } # This function will create a new set of release branches. @@ -86,60 +71,62 @@ function modify_svnignore { # These branches will be created off of master. function create_new_release_branches { - # Prompt for version number. - read -p "What version are you releasing? Please write in x.x syntax. Example: 4.9 - " version - - # Declare the new branch names. - NEW_UNBUILT_BRANCH="branch-$version" - NEW_BUILT_BRANCH="branch-$version-built" - - # Check if branch already exists, if not, create new branch named "branch-x.x" - if [[ -n $( git branch -r | grep "$NEW_UNBUILT_BRANCH" ) ]]; then - echo "$NEW_UNBUILT_BRANCH already exists. Exiting..." - exit 1 - else - echo "" - echo "Creating new unbuilt branch $NEW_UNBUILT_BRANCH from current master branch..." - echo "" - # reset --hard to remote master in case they have local commits in their repo - git checkout master && git pull && git reset --hard origin/master - - # Create new branch, push to repo - git checkout -b $NEW_UNBUILT_BRANCH - - git push -u origin $NEW_UNBUILT_BRANCH - echo "" - echo "$NEW_UNBUILT_BRANCH created." - echo "" - # Verify you want a built version - read -n1 -p "Would you like to create a built version of $NEW_UNBUILT_BRANCH as new $NEW_BUILT_BRANCH? [y/N]" reply - if [[ 'y' == $reply || 'Y' == $reply ]]; then - # make sure we're still checked out on the right branch - git checkout $NEW_UNBUILT_BRANCH - - git checkout -b $NEW_BUILT_BRANCH - - # New .gitignore for release branches - echo "" - echo "Creating new .gitignore" - echo "" - create_release_gitignore - - # Remove stuff from svnignore - modify_svnignore - - git checkout $NEW_UNBUILT_BRANCH - - git push -u origin $NEW_BUILT_BRANCH - - # Script will continue on to actually build the plugin onto this new branch... - else - # Nothing left to do... - echo "" - echo "Ok, all done then." - exit 1 - fi - fi + # Prompt for version number. + read -p "What version are you releasing? Please write in x.x syntax. Example: 4.9 - " version + + # Declare the new branch names. + NEW_UNBUILT_BRANCH="branch-$version" + NEW_BUILT_BRANCH="branch-$version-built" + + # Check if branch already exists, if not, create new branch named "branch-x.x" + if [[ -n $( git branch -r | grep "$NEW_UNBUILT_BRANCH" ) ]]; + then + echo "$NEW_UNBUILT_BRANCH already exists. Exiting..." + exit 1 + else + echo "" + echo "Creating new unbuilt branch $NEW_UNBUILT_BRANCH from current master branch..." + echo "" + # reset --hard to remote master in case they have local commits in their repo + git checkout master && git pull && git reset --hard origin/master + + # Create new branch, push to repo + git checkout -b $NEW_UNBUILT_BRANCH + + git push -u origin $NEW_UNBUILT_BRANCH + echo "" + echo "$NEW_UNBUILT_BRANCH created." + echo "" + # Verify you want a built version + read -n1 -p "Would you like to create a built version of $NEW_UNBUILT_BRANCH as new $NEW_BUILT_BRANCH? [y/N]" reply + if [[ 'y' == $reply || 'Y' == $reply ]] + then + # make sure we're still checked out on the right branch + git checkout $NEW_UNBUILT_BRANCH + + git checkout -b $NEW_BUILT_BRANCH + + # New .gitignore for release branches + echo "" + echo "Creating new .gitignore" + echo "" + create_release_gitignore + + # Remove stuff from svnignore + modify_svnignore + + git checkout $NEW_UNBUILT_BRANCH + + git push -u origin $NEW_BUILT_BRANCH + + # Script will continue on to actually build the plugin onto this new branch... + else + # Nothing left to do... + echo "" + echo "Ok, all done then." + exit 1 + fi + fi } # Script parameter, what do you want to do? @@ -155,50 +142,56 @@ TMP_LOCAL_BUILT_VERSION="/tmp/jetpack2" # Make sure we don't have uncommitted changes. if [[ -n $( git status -s --porcelain ) ]]; then - echo "Uncommitted changes found." - echo "Please deal with them and try again clean." - exit 1 + echo "Uncommitted changes found." + echo "Please deal with them and try again clean." + exit 1 fi # Check the command -if [[ 'new' == $COMMAND || '-n' == $COMMAND ]]; then - create_new_release_branches -elif [[ 'update' = $COMMAND || '-u' = $COMMAND ]]; then - # It's possible they passed the branch name directly to the script - if [[ -z $2 ]]; then - read -p "What branch are you updating? (enter full branch name): " branch - UPDATE_BUILT_BRANCH=$branch - else - UPDATE_BUILT_BRANCH=$2 - fi +if [[ 'new' == $COMMAND || '-n' == $COMMAND ]] +then + create_new_release_branches +elif [[ 'update' = $COMMAND || '-u' = $COMMAND ]] +then + # It's possible they passed the branch name directly to the script + if [[ -z $2 ]] + then + read -p "What branch are you updating? (enter full branch name): " branch + UPDATE_BUILT_BRANCH=$branch + else + UPDATE_BUILT_BRANCH=$2 + fi else - usage + usage fi # Cast the branch name that we'll be building to a single var. -if [[ -n $NEW_BUILT_BRANCH ]]; then - BUILD_TARGET=$NEW_BUILT_BRANCH -elif [[ -n $UPDATE_BUILT_BRANCH ]]; then - BUILD_TARGET=$UPDATE_BUILT_BRANCH +if [[ -n $NEW_BUILT_BRANCH ]] +then + BUILD_TARGET=$NEW_BUILT_BRANCH +elif [[ -n $UPDATE_BUILT_BRANCH ]] +then + BUILD_TARGET=$UPDATE_BUILT_BRANCH else - echo "" - echo "No target branch specified. How did you make it this far?" - exit 1 + echo "" + echo "No target branch specified. How did you make it this far?" + exit 1 fi ### This bit is the engine that will build a branch and push to another one #### # Make sure we're trying to deploy something that exists. if [[ -z $( git branch -r | grep "$BUILD_TARGET" ) ]]; then - echo "Branch $BUILD_TARGET not found in git repository." - echo "" - exit 1 + echo "Branch $BUILD_TARGET not found in git repository." + echo "" + exit 1 fi read -p "You are about to deploy a new production build to the $BUILD_TARGET branch from the $CURRENT_BRANCH branch. Are you sure? [y/N]" -n 1 -r -if [[ $REPLY != "y" && $REPLY != "Y" ]]; then - exit 1 +if [[ $REPLY != "y" && $REPLY != "Y" ]] +then + exit 1 fi echo "" @@ -206,9 +199,9 @@ echo "Building Jetpack" # Checking for yarn hash yarn 2>/dev/null || { - echo >&2 "This script requires you to have yarn package manager installed." - echo >&2 "Please install it following the instructions on https://yarnpkg.com. Aborting."; - exit 1; + echo >&2 "This script requires you to have yarn package manager installed." + echo >&2 "Please install it following the instructions on https://yarnpkg.com. Aborting."; + exit 1; } # Start clean by removing previously installed dependencies and built files @@ -234,7 +227,8 @@ echo "Purging paths included in .svnignore" # check .svnignore for file in $( cat "$DIR/.svnignore" 2>/dev/null ); do # We want to commit changes to to-test.md as well as the testing tips. - if [[ $file == "to-test.md" || $file == "docs/testing/testing-tips.md" ]]; then + if [[ $file == "to-test.md" || $file == "docs/testing/testing-tips.md" ]] + then continue; fi rm -rf TMP_LOCAL_BUILT_VERSION/$file