@wordpress/format-library
Version:
Format library for the WordPress editor.
196 lines (186 loc) • 4.54 kB
JavaScript
/**
* WordPress dependencies
*/
import {
Path,
SVG,
Popover,
Button,
ExternalLink,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
__experimentalNumberControl as NumberControl,
TextareaControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { insertObject, useAnchor } from '@wordpress/rich-text';
import {
MediaUpload,
RichTextToolbarButton,
MediaUploadCheck,
} from '@wordpress/block-editor';
const ALLOWED_MEDIA_TYPES = [ 'image' ];
const name = 'core/image';
const title = __( 'Inline image' );
export const image = {
name,
title,
keywords: [ __( 'photo' ), __( 'media' ) ],
object: true,
tagName: 'img',
className: null,
attributes: {
className: 'class',
style: 'style',
url: 'src',
alt: 'alt',
},
edit: Edit,
};
function InlineUI( { value, onChange, activeObjectAttributes, contentRef } ) {
const { style, alt } = activeObjectAttributes;
const width = style?.replace( /\D/g, '' );
const [ editedWidth, setEditedWidth ] = useState( width );
const [ editedAlt, setEditedAlt ] = useState( alt );
const hasChanged = editedWidth !== width || editedAlt !== alt;
const popoverAnchor = useAnchor( {
editableContentElement: contentRef.current,
settings: image,
} );
return (
<Popover
placement="bottom"
focusOnMount={ false }
anchor={ popoverAnchor }
className="block-editor-format-toolbar__image-popover"
>
<form
className="block-editor-format-toolbar__image-container-content"
onSubmit={ ( event ) => {
const newReplacements = value.replacements.slice();
newReplacements[ value.start ] = {
type: name,
attributes: {
...activeObjectAttributes,
style: editedWidth
? `width: ${ editedWidth }px;`
: '',
alt: editedAlt,
},
};
onChange( {
...value,
replacements: newReplacements,
} );
event.preventDefault();
} }
>
<VStack spacing={ 4 }>
<NumberControl
__next40pxDefaultSize
label={ __( 'Width' ) }
value={ editedWidth }
min={ 1 }
onChange={ ( newWidth ) => {
setEditedWidth( newWidth );
} }
/>
<TextareaControl
label={ __( 'Alternative text' ) }
__nextHasNoMarginBottom
value={ editedAlt }
onChange={ ( newAlt ) => {
setEditedAlt( newAlt );
} }
help={
<>
<ExternalLink
href={
// translators: Localized tutorial, if one exists. W3C Web Accessibility Initiative link has list of existing translations.
__(
'https://www.w3.org/WAI/tutorials/images/decision-tree/'
)
}
>
{ __(
'Describe the purpose of the image.'
) }
</ExternalLink>
<br />
{ __( 'Leave empty if decorative.' ) }
</>
}
/>
<HStack justify="right">
<Button
disabled={ ! hasChanged }
accessibleWhenDisabled
variant="primary"
type="submit"
size="compact"
>
{ __( 'Apply' ) }
</Button>
</HStack>
</VStack>
</form>
</Popover>
);
}
function Edit( {
value,
onChange,
onFocus,
isObjectActive,
activeObjectAttributes,
contentRef,
} ) {
return (
<MediaUploadCheck>
<MediaUpload
allowedTypes={ ALLOWED_MEDIA_TYPES }
onSelect={ ( { id, url, alt, width: imgWidth } ) => {
onChange(
insertObject( value, {
type: name,
attributes: {
className: `wp-image-${ id }`,
style: `width: ${ Math.min(
imgWidth,
150
) }px;`,
url,
alt,
},
} )
);
onFocus();
} }
render={ ( { open } ) => (
<RichTextToolbarButton
icon={
<SVG
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<Path d="M4 18.5h16V17H4v1.5zM16 13v1.5h4V13h-4zM5.1 15h7.8c.6 0 1.1-.5 1.1-1.1V6.1c0-.6-.5-1.1-1.1-1.1H5.1C4.5 5 4 5.5 4 6.1v7.8c0 .6.5 1.1 1.1 1.1zm.4-8.5h7V10l-1-1c-.3-.3-.8-.3-1 0l-1.6 1.5-1.2-.7c-.3-.2-.6-.2-.9 0l-1.3 1V6.5zm0 6.1l1.8-1.3 1.3.8c.3.2.7.2.9-.1l1.5-1.4 1.5 1.4v1.5h-7v-.9z" />
</SVG>
}
title={ title }
onClick={ open }
isActive={ isObjectActive }
/>
) }
/>
{ isObjectActive && (
<InlineUI
value={ value }
onChange={ onChange }
activeObjectAttributes={ activeObjectAttributes }
contentRef={ contentRef }
/>
) }
</MediaUploadCheck>
);
}