@wordpress/block-library
Version:
Block library for the WordPress editor.
239 lines (222 loc) • 5.53 kB
JavaScript
/**
* External dependencies
*/
import { every } from 'lodash';
/**
* WordPress dependencies
*/
import { createBlobURL } from '@wordpress/blob';
import { createBlock, getBlockAttributes } from '@wordpress/blocks';
import { dispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
import { __ } from '@wordpress/i18n';
export function stripFirstImage( attributes, { shortcode } ) {
const { body } = document.implementation.createHTMLDocument( '' );
body.innerHTML = shortcode.content;
let nodeToRemove = body.querySelector( 'img' );
// If an image has parents, find the topmost node to remove.
while (
nodeToRemove &&
nodeToRemove.parentNode &&
nodeToRemove.parentNode !== body
) {
nodeToRemove = nodeToRemove.parentNode;
}
if ( nodeToRemove ) {
nodeToRemove.parentNode.removeChild( nodeToRemove );
}
return body.innerHTML.trim();
}
function getFirstAnchorAttributeFormHTML( html, attributeName ) {
const { body } = document.implementation.createHTMLDocument( '' );
body.innerHTML = html;
const { firstElementChild } = body;
if ( firstElementChild && firstElementChild.nodeName === 'A' ) {
return firstElementChild.getAttribute( attributeName ) || undefined;
}
}
const imageSchema = {
img: {
attributes: [ 'src', 'alt', 'title' ],
classes: [
'alignleft',
'aligncenter',
'alignright',
'alignnone',
/^wp-image-\d+$/,
],
},
};
const schema = ( { phrasingContentSchema } ) => ( {
figure: {
require: [ 'img' ],
children: {
...imageSchema,
a: {
attributes: [ 'href', 'rel', 'target' ],
children: imageSchema,
},
figcaption: {
children: phrasingContentSchema,
},
},
},
} );
const transforms = {
from: [
{
type: 'raw',
isMatch: ( node ) =>
node.nodeName === 'FIGURE' && !! node.querySelector( 'img' ),
schema,
transform: ( node ) => {
// Search both figure and image classes. Alignment could be
// set on either. ID is set on the image.
const className =
node.className +
' ' +
node.querySelector( 'img' ).className;
const alignMatches =
/(?:^|\s)align(left|center|right)(?:$|\s)/.exec(
className
);
const anchor = node.id === '' ? undefined : node.id;
const align = alignMatches ? alignMatches[ 1 ] : undefined;
const idMatches = /(?:^|\s)wp-image-(\d+)(?:$|\s)/.exec(
className
);
const id = idMatches ? Number( idMatches[ 1 ] ) : undefined;
const anchorElement = node.querySelector( 'a' );
const linkDestination =
anchorElement && anchorElement.href ? 'custom' : undefined;
const href =
anchorElement && anchorElement.href
? anchorElement.href
: undefined;
const rel =
anchorElement && anchorElement.rel
? anchorElement.rel
: undefined;
const linkClass =
anchorElement && anchorElement.className
? anchorElement.className
: undefined;
const attributes = getBlockAttributes(
'core/image',
node.outerHTML,
{
align,
id,
linkDestination,
href,
rel,
linkClass,
anchor,
}
);
return createBlock( 'core/image', attributes );
},
},
{
// Note: when dragging and dropping multiple files onto a gallery this overrides the
// gallery transform in order to add new images to the gallery instead of
// creating a new gallery.
type: 'files',
isMatch( files ) {
// The following check is intended to catch non-image files when dropped together with images.
if (
files.some(
( file ) => file.type.indexOf( 'image/' ) === 0
) &&
files.some(
( file ) => file.type.indexOf( 'image/' ) !== 0
)
) {
const { createErrorNotice } = dispatch( noticesStore );
createErrorNotice(
__(
'If uploading to a gallery all files need to be image formats'
),
{
id: 'gallery-transform-invalid-file',
type: 'snackbar',
}
);
}
return every(
files,
( file ) => file.type.indexOf( 'image/' ) === 0
);
},
transform( files ) {
const blocks = files.map( ( file ) => {
return createBlock( 'core/image', {
url: createBlobURL( file ),
} );
} );
return blocks;
},
},
{
type: 'shortcode',
tag: 'caption',
attributes: {
url: {
type: 'string',
source: 'attribute',
attribute: 'src',
selector: 'img',
},
alt: {
type: 'string',
source: 'attribute',
attribute: 'alt',
selector: 'img',
},
caption: {
shortcode: stripFirstImage,
},
href: {
shortcode: ( attributes, { shortcode } ) => {
return getFirstAnchorAttributeFormHTML(
shortcode.content,
'href'
);
},
},
rel: {
shortcode: ( attributes, { shortcode } ) => {
return getFirstAnchorAttributeFormHTML(
shortcode.content,
'rel'
);
},
},
linkClass: {
shortcode: ( attributes, { shortcode } ) => {
return getFirstAnchorAttributeFormHTML(
shortcode.content,
'class'
);
},
},
id: {
type: 'number',
shortcode: ( { named: { id } } ) => {
if ( ! id ) {
return;
}
return parseInt( id.replace( 'attachment_', '' ), 10 );
},
},
align: {
type: 'string',
shortcode: ( { named: { align = 'alignnone' } } ) => {
return align.replace( 'align', '' );
},
},
},
},
],
};
export default transforms;