diff --git a/client/gutenberg/extensions/contact-info/address/edit.js b/client/gutenberg/extensions/contact-info/address/edit.js
new file mode 100644
index 00000000000000..d1223898c6e8ef
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/address/edit.js
@@ -0,0 +1,129 @@
+/** @format */
+
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+import { PlainText, InspectorControls } from '@wordpress/editor';
+import { Component, Fragment } from '@wordpress/element';
+import { ToggleControl, PanelBody, ExternalLink } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import { __ } from 'gutenberg/extensions/presets/jetpack/utils/i18n';
+import ClipboardInput from 'gutenberg/extensions/presets/jetpack/utils/clipboard-input';
+import { default as save, googleMapsUrl } 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,
+ } );
+
+ return (
+
+ { ! isSelected && hasContent && save( this.props ) }
+ { ( isSelected || ! hasContent ) && (
+
+ setAttributes( { address: newAddress } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+ setAttributes( { addressLine2: newAddressLine2 } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+ setAttributes( { addressLine3: newAddressLine3 } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+ setAttributes( { city: newCity } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+ setAttributes( { region: newRegion } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+ setAttributes( { postal: newPostal } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+ setAttributes( { country: newCountry } ) }
+ onKeyDown={ this.preventEnterKey }
+ />
+
+
+
+ setAttributes( { linkToGoogleMaps: newlinkToGoogleMaps } )
+ }
+ />
+ { hasContent && }
+ { hasContent && (
+
+
+ { __( 'Visit Google Maps' ) }
+
+
+ ) }
+
+
+
+ ) }
+
+ );
+ }
+}
+
+export default AddressEdit;
diff --git a/client/gutenberg/extensions/contact-info/address/editor.js b/client/gutenberg/extensions/contact-info/address/editor.js
new file mode 100644
index 00000000000000..b167dc5cca783d
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/address/editor.js
@@ -0,0 +1,9 @@
+/** @format */
+
+/**
+ * Internal dependencies
+ */
+import registerJetpackBlock from 'gutenberg/extensions/presets/jetpack/utils/register-jetpack-block';
+import { name, settings } from '.';
+
+registerJetpackBlock( name, settings );
diff --git a/client/gutenberg/extensions/contact-info/address/index.js b/client/gutenberg/extensions/contact-info/address/index.js
new file mode 100644
index 00000000000000..ad473c56fc20bc
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/address/index.js
@@ -0,0 +1,72 @@
+/** @format */
+/**
+ * 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 'gutenberg/extensions/presets/jetpack/utils/render-material-icon';
+import { __, _x } from 'gutenberg/extensions/presets/jetpack/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(
+
+
+
+
+ ),
+ category: 'jetpack',
+ attributes,
+ parent: [ 'jetpack/contact-info' ],
+ edit,
+ save,
+};
diff --git a/client/gutenberg/extensions/contact-info/address/save.js b/client/gutenberg/extensions/contact-info/address/save.js
new file mode 100644
index 00000000000000..2f7747e94615bd
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/address/save.js
@@ -0,0 +1,112 @@
+/** @format */
+/**
+ * External dependencies
+ */
+import { Fragment } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { __ } from 'gutenberg/extensions/presets/jetpack/utils/i18n';
+
+const Address = ( {
+ attributes: { address, addressLine2, addressLine3, city, region, postal, country },
+} ) => (
+
+ { address && (
+
+ { address }
+
+ ) }
+ { addressLine2 && (
+
+ { addressLine2 }
+
+ ) }
+ { addressLine3 && (
+
+ { addressLine3 }
+
+ ) }
+ { city && ! ( region || postal ) && (
+
+ { city }
+
+ ) }
+ { city && ( region || postal ) && (
+
+ { [
+
+ { city }
+ ,
+ ', ',
+
+ { region }
+ ,
+ ' ',
+
+ { postal }
+ ,
+ ] }
+
+ ) }
+ { ! city && ( region || postal ) && (
+
+ { [
+
+ { region }
+ ,
+ ' ',
+
+ { postal }
+ ,
+ ] }
+
+ ) }
+ { country && (
+
+ { country }
+
+ ) }
+
+);
+
+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 => (
+
+ { props.attributes.linkToGoogleMaps && (
+
+
+
+ ) }
+ { ! props.attributes.linkToGoogleMaps &&
}
+
+);
+
+export default save;
diff --git a/client/gutenberg/extensions/contact-info/edit.js b/client/gutenberg/extensions/contact-info/edit.js
new file mode 100644
index 00000000000000..e156088c08bdff
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/edit.js
@@ -0,0 +1,54 @@
+/** @format */
+
+/**
+ * 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',
+ '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 {
+ attributes: {},
+ isSelected,
+ } = props;
+
+ return (
+
+
+
+ );
+};
+
+export default ContactInfoEdit;
diff --git a/client/gutenberg/extensions/contact-info/editor.js b/client/gutenberg/extensions/contact-info/editor.js
new file mode 100644
index 00000000000000..3ad6240ae41a32
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/editor.js
@@ -0,0 +1,9 @@
+/** @format */
+
+/**
+ * Internal dependencies
+ */
+import registerJetpackBlock from 'gutenberg/extensions/presets/jetpack/utils/register-jetpack-block';
+import { childBlocks, name, settings } from '.';
+
+registerJetpackBlock( name, settings, childBlocks );
diff --git a/client/gutenberg/extensions/contact-info/editor.scss b/client/gutenberg/extensions/contact-info/editor.scss
new file mode 100644
index 00000000000000..3cfeff2c948e44
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/editor.scss
@@ -0,0 +1,7 @@
+.jetpack-contact-info-block {
+ padding: 10px 18px;
+ /* css class added to increase specificity */
+ .editor-plain-text.editor-plain-text:focus {
+ box-shadow: none;
+ }
+}
diff --git a/client/gutenberg/extensions/contact-info/email/edit.js b/client/gutenberg/extensions/contact-info/email/edit.js
new file mode 100644
index 00000000000000..fd64c949fad14e
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/email/edit.js
@@ -0,0 +1,17 @@
+/** @format */
+
+/**
+ * Internal dependencies
+ */
+import save from './save';
+import { __ } from 'gutenberg/extensions/presets/jetpack/utils/i18n';
+import simpleInput from 'gutenberg/extensions/presets/jetpack/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/client/gutenberg/extensions/contact-info/email/editor.js b/client/gutenberg/extensions/contact-info/email/editor.js
new file mode 100644
index 00000000000000..b167dc5cca783d
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/email/editor.js
@@ -0,0 +1,9 @@
+/** @format */
+
+/**
+ * Internal dependencies
+ */
+import registerJetpackBlock from 'gutenberg/extensions/presets/jetpack/utils/register-jetpack-block';
+import { name, settings } from '.';
+
+registerJetpackBlock( name, settings );
diff --git a/client/gutenberg/extensions/contact-info/email/index.js b/client/gutenberg/extensions/contact-info/email/index.js
new file mode 100644
index 00000000000000..60ba963143480e
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/email/index.js
@@ -0,0 +1,42 @@
+/** @format */
+/**
+ * External dependencies
+ */
+import { Path } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import edit from './edit';
+import save from './save';
+import renderMaterialIcon from 'gutenberg/extensions/presets/jetpack/utils/render-material-icon';
+import { __, _x } from 'gutenberg/extensions/presets/jetpack/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(
+
+ ),
+ category: 'jetpack',
+ attributes,
+ edit,
+ save,
+ parent: [ 'jetpack/contact-info' ],
+};
diff --git a/client/gutenberg/extensions/contact-info/email/save.js b/client/gutenberg/extensions/contact-info/email/save.js
new file mode 100644
index 00000000000000..007e4cb8bc0386
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/email/save.js
@@ -0,0 +1,21 @@
+/**
+ * Internal dependencies
+ */
+import textMatchReplace from 'gutenberg/extensions/presets/jetpack/utils/text-match-replace';
+
+const renderEmail = inputText => {
+ return textMatchReplace(
+ inputText,
+ /((?:[a-z|0-9+_](?:\.|_\+)*)+[a-z|0-9]\@(?:[a-z|0-9])+(?:(?:\.){0,1}[a-z|0-9]){2}\.[a-z]{2,22})/gim,
+ ( email, i ) => (
+
+ { email }
+
+ )
+ );
+};
+
+const save = ( { attributes: { email }, className } ) =>
+ email && { renderEmail( email ) }
;
+
+export default save;
diff --git a/client/gutenberg/extensions/contact-info/index.js b/client/gutenberg/extensions/contact-info/index.js
new file mode 100644
index 00000000000000..4212a47d43d023
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/index.js
@@ -0,0 +1,56 @@
+/** @format */
+/**
+ * External dependencies
+ */
+import { InnerBlocks } from '@wordpress/editor';
+import { Path } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import edit from './edit';
+import renderMaterialIcon from 'gutenberg/extensions/presets/jetpack/utils/render-material-icon';
+import { __, _x } from 'gutenberg/extensions/presets/jetpack/utils/i18n';
+import './editor.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 } ) => (
+
+
+
+);
+
+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(
+
+ ),
+ 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/client/gutenberg/extensions/contact-info/phone/edit.js b/client/gutenberg/extensions/contact-info/phone/edit.js
new file mode 100644
index 00000000000000..3b7c001f9f2bc1
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/phone/edit.js
@@ -0,0 +1,17 @@
+/** @format */
+
+/**
+ * Internal dependencies
+ */
+import save from './save';
+import { __ } from 'gutenberg/extensions/presets/jetpack/utils/i18n';
+import simpleInput from 'gutenberg/extensions/presets/jetpack/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/client/gutenberg/extensions/contact-info/phone/editor.js b/client/gutenberg/extensions/contact-info/phone/editor.js
new file mode 100644
index 00000000000000..b167dc5cca783d
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/phone/editor.js
@@ -0,0 +1,9 @@
+/** @format */
+
+/**
+ * Internal dependencies
+ */
+import registerJetpackBlock from 'gutenberg/extensions/presets/jetpack/utils/register-jetpack-block';
+import { name, settings } from '.';
+
+registerJetpackBlock( name, settings );
diff --git a/client/gutenberg/extensions/contact-info/phone/index.js b/client/gutenberg/extensions/contact-info/phone/index.js
new file mode 100644
index 00000000000000..8715906f3f347a
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/phone/index.js
@@ -0,0 +1,42 @@
+/** @format */
+/**
+ * External dependencies
+ */
+import { Path } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import edit from './edit';
+import save from './save';
+import renderMaterialIcon from 'gutenberg/extensions/presets/jetpack/utils/render-material-icon';
+import { __, _x } from 'gutenberg/extensions/presets/jetpack/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(
+
+ ),
+ category: 'jetpack',
+ attributes,
+ parent: [ 'jetpack/contact-info' ],
+ edit,
+ save,
+};
diff --git a/client/gutenberg/extensions/contact-info/phone/save.js b/client/gutenberg/extensions/contact-info/phone/save.js
new file mode 100644
index 00000000000000..9fc1cc3f45f188
--- /dev/null
+++ b/client/gutenberg/extensions/contact-info/phone/save.js
@@ -0,0 +1,28 @@
+/**
+ * Internal dependencies
+ */
+import textMatchReplace from 'gutenberg/extensions/presets/jetpack/utils/text-match-replace';
+
+export function renderPhone( inputText ) {
+ return textMatchReplace(
+ inputText,
+ /([0-9\()+]{1}[\ \-().]?[0-9]{1,6}[\ \-().]?[0-9]{0,6}[\ \-()]?[0-9]{0,6}[\ \-().]?[0-9]{0,6}[\ \-().]?[0-9]{0,6}[\ \-().]?[0-9]{0,6})/g,
+ ( number, i ) => {
+ if ( number.trim() === '' ) {
+ return number;
+ }
+ const just_number = number.replace( /\D/g, '' );
+
+ return (
+
+ { number }
+
+ );
+ }
+ );
+}
+
+const save = ( { attributes: { phone }, className } ) =>
+ phone && { renderPhone( phone ) }
;
+
+export default save;
diff --git a/client/gutenberg/extensions/presets/jetpack/index.json b/client/gutenberg/extensions/presets/jetpack/index.json
index 9dec6bae2e0a51..b2f49794b8cfbb 100644
--- a/client/gutenberg/extensions/presets/jetpack/index.json
+++ b/client/gutenberg/extensions/presets/jetpack/index.json
@@ -13,6 +13,7 @@
],
"beta": [
"mailchimp",
- "vr"
+ "vr",
+ "contact-info"
]
}
diff --git a/client/gutenberg/extensions/presets/jetpack/utils/clipboard-input.js b/client/gutenberg/extensions/presets/jetpack/utils/clipboard-input.js
new file mode 100644
index 00000000000000..8e9700aac292fa
--- /dev/null
+++ b/client/gutenberg/extensions/presets/jetpack/utils/clipboard-input.js
@@ -0,0 +1,48 @@
+/**
+ * External dependencies
+ */
+import { Component } from '@wordpress/element';
+import { ClipboardButton, TextControl } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import { __, _x } from 'gutenberg/extensions/presets/jetpack/utils/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 (
+
+
+
+ { hasCopied ? __( 'Copied!' ) : _x( 'Copy', 'verb' ) }
+
+
+ );
+ }
+}
+
+export default ClipboardInput;
diff --git a/client/gutenberg/extensions/presets/jetpack/utils/clipboard-input.scss b/client/gutenberg/extensions/presets/jetpack/utils/clipboard-input.scss
new file mode 100644
index 00000000000000..d0118a3efc72c2
--- /dev/null
+++ b/client/gutenberg/extensions/presets/jetpack/utils/clipboard-input.scss
@@ -0,0 +1,7 @@
+.jetpack-clipboard-input {
+ display: flex;
+
+ .components-clipboard-button {
+ margin: 2px 0 0 6px;
+ }
+}
diff --git a/client/gutenberg/extensions/presets/jetpack/utils/render-material-icon.jsx b/client/gutenberg/extensions/presets/jetpack/utils/render-material-icon.jsx
index b9d64c3444e383..acdc6b7c32cc63 100644
--- a/client/gutenberg/extensions/presets/jetpack/utils/render-material-icon.jsx
+++ b/client/gutenberg/extensions/presets/jetpack/utils/render-material-icon.jsx
@@ -1,10 +1,14 @@
/** @format */
+/**
+ * External dependencies
+ */
+import { Path, SVG } from '@wordpress/components';
const renderMaterialIcon = svg => (
-
-
+
+
{ svg }
-
+
);
export default renderMaterialIcon;
diff --git a/client/gutenberg/extensions/presets/jetpack/utils/simple-input.js b/client/gutenberg/extensions/presets/jetpack/utils/simple-input.js
new file mode 100644
index 00000000000000..615e29c1a23d62
--- /dev/null
+++ b/client/gutenberg/extensions/presets/jetpack/utils/simple-input.js
@@ -0,0 +1,21 @@
+/**
+ * External dependencies
+ */
+import { PlainText } from '@wordpress/editor';
+
+const simpleInput = ( type, props, placeholder, view, onChange ) => {
+ const { isSelected } = props;
+ const value = props.attributes[ type ];
+ return (
+
+ { ! isSelected && value !== '' && view( props ) }
+ { ( isSelected || value === '' ) && (
+
+ ) }
+
+ );
+};
+
+export default simpleInput;
diff --git a/client/gutenberg/extensions/presets/jetpack/utils/text-match-replace.js b/client/gutenberg/extensions/presets/jetpack/utils/text-match-replace.js
new file mode 100644
index 00000000000000..2cf8ce55005079
--- /dev/null
+++ b/client/gutenberg/extensions/presets/jetpack/utils/text-match-replace.js
@@ -0,0 +1,71 @@
+/** @format */
+
+/**
+ * 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) => {number}
+ * );
+ * // => ['Emphasize all phone numbers like ', 884-555-4443 , '.'
+ *
+ * @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/client/gutenberg/extensions/shortlinks/editor.scss b/client/gutenberg/extensions/shortlinks/editor.scss
deleted file mode 100644
index b7e8e74e3df55f..00000000000000
--- a/client/gutenberg/extensions/shortlinks/editor.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-.jetpack-shortlinks__panel {
- .components-base-control {
- display: inline-block;
- margin-right: 4px;
- }
-
- .components-clipboard-button {
- margin-top: 1px;
- }
-}