Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File Block: Add support for embedding PDFs #24233

Closed
wants to merge 14 commits into from
Closed
2 changes: 1 addition & 1 deletion lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ function gutenberg_reregister_core_block_types() {
'code',
'column',
'columns',
'file',
'gallery',
'group',
'heading',
Expand Down Expand Up @@ -57,6 +56,7 @@ function gutenberg_reregister_core_block_types() {
'calendar.php' => 'core/calendar',
'categories.php' => 'core/categories',
'cover.php' => 'core/cover',
'file.php' => 'core/file',
'latest-comments.php' => 'core/latest-comments',
'latest-posts.php' => 'core/latest-posts',
'legacy-widget.php' => 'core/legacy-widget',
Expand Down
9 changes: 8 additions & 1 deletion packages/block-library/src/file/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@
"type": "string",
"source": "html",
"selector": "a[download]"
},
"showInlineEmbed": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call it "embed" or "preview"? I mean it's not that important but we could the same for like images, text .... any file that can be previewed. Something like "quick view" in MacOS

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showPreview or displayPreview perhaps?

"type": "boolean"
},
"embedHeight": {
"type": "number"
}
},
"supports": {
"anchor": true,
"align": true
}
},
"script": "utils.js"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is doing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it's a way to tell pack to generate a separate script? Why not just a package because that's our way of doing this in Gutenberg?

Alternatively, if we implement the frontend_script proposal, we could introduce a convention like this (I see it more as a file in the block folder with the property here though). Having the property here will persist in the block registration... which I think is confusing.

}
94 changes: 92 additions & 2 deletions packages/block-library/src/file/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob';
import { Animate, ClipboardButton, withNotices } from '@wordpress/components';
import {
Animate,
ClipboardButton,
withNotices,
ResizableBox,
} from '@wordpress/components';
import { compose } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';
import { withSelect, withDispatch } from '@wordpress/data';
import {
BlockControls,
BlockIcon,
Expand All @@ -25,6 +30,10 @@ import { file as icon } from '@wordpress/icons';
* Internal dependencies
*/
import FileBlockInspector from './inspector';
import { browserSupportsPdfs } from './utils';

export const MIN_EMBED_HEIGHT = 200;
export const MAX_EMBED_HEIGHT = 2000;

class FileEdit extends Component {
constructor() {
Expand All @@ -40,12 +49,17 @@ class FileEdit extends Component {
this.changeShowDownloadButton = this.changeShowDownloadButton.bind(
this
);
this.changeShowInlineEmbed = this.changeShowInlineEmbed.bind( this );
this.onUploadError = this.onUploadError.bind( this );
this.handleOnResizeStop = this.handleOnResizeStop.bind( this );
this.changeEmbedHeight = this.changeEmbedHeight.bind( this );

this.state = {
hasError: false,
showCopyConfirmation: false,
};

this.browserSupportsPdfs = browserSupportsPdfs();
}

componentDidMount() {
Expand Down Expand Up @@ -90,11 +104,14 @@ class FileEdit extends Component {
onSelectFile( media ) {
if ( media && media.url ) {
this.setState( { hasError: false } );
const isPdf = media.url.endsWith( '.pdf' );
this.props.setAttributes( {
href: media.url,
fileName: media.title,
textLinkHref: media.url,
id: media.id,
showInlineEmbed: isPdf ? true : undefined,
embedHeight: isPdf ? 800 : undefined,
} );
}
}
Expand Down Expand Up @@ -129,6 +146,28 @@ class FileEdit extends Component {
this.props.setAttributes( { showDownloadButton: newValue } );
}

changeShowInlineEmbed( newValue ) {
this.props.setAttributes( { showInlineEmbed: newValue } );
}

handleOnResizeStop( event, direction, elt, delta ) {
const { attributes, setAttributes, onResizeStop } = this.props;
const { embedHeight } = attributes;

onResizeStop();

const newHeight = parseInt( embedHeight + delta.height, 10 );
setAttributes( { embedHeight: newHeight } );
}

changeEmbedHeight( newValue ) {
const embedHeight = Math.max(
parseInt( newValue, 10 ),
MIN_EMBED_HEIGHT
);
this.props.setAttributes( { embedHeight } );
}

render() {
const {
className,
Expand All @@ -137,6 +176,7 @@ class FileEdit extends Component {
setAttributes,
noticeUI,
media,
onResizeStart,
} = this.props;
const {
id,
Expand All @@ -146,6 +186,8 @@ class FileEdit extends Component {
textLinkTarget,
showDownloadButton,
downloadButtonText,
showInlineEmbed,
embedHeight,
} = attributes;
const { hasError, showCopyConfirmation } = this.state;
const attachmentPage = media && media.link;
Expand All @@ -172,6 +214,8 @@ class FileEdit extends Component {
'is-transient': isBlobURL( href ),
} );

const displayInlineEmbed = this.browserSupportsPdfs && showInlineEmbed;

return (
<>
<FileBlockInspector
Expand All @@ -183,6 +227,10 @@ class FileEdit extends Component {
.changeLinkDestinationOption,
changeOpenInNewWindow: this.changeOpenInNewWindow,
changeShowDownloadButton: this.changeShowDownloadButton,
showInlineEmbed,
changeShowInlineEmbed: this.changeShowInlineEmbed,
embedHeight,
changeEmbedHeight: this.changeEmbedHeight,
} }
/>
<BlockControls>
Expand All @@ -202,6 +250,40 @@ class FileEdit extends Component {
animateClassName
) }
>
{ displayInlineEmbed && (
<ResizableBox
size={ { height: embedHeight } }
minHeight={ MIN_EMBED_HEIGHT }
maxHeight={ MAX_EMBED_HEIGHT }
minWidth="100%"
grid={ [ 10, 10 ] }
enable={ {
top: false,
right: false,
bottom: true,
left: false,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false,
} }
onResizeStart={ onResizeStart }
onResizeStop={ this.handleOnResizeStop }
showHandle={ isSelected }
>
<object
className="wp-block-file__embed"
data={ href }
type="application/pdf"
aria-label={ __(
'Embed of the selected PDF file.'
) }
/>
{ ! isSelected && (
<div className="wp-block-file__embed-overlay" />
) }
</ResizableBox>
) }
<div className={ 'wp-block-file__content-wrapper' }>
<div className="wp-block-file__textlink">
<RichText
Expand Down Expand Up @@ -273,5 +355,13 @@ export default compose( [
mediaUpload,
};
} ),
withDispatch( ( dispatch ) => {
const { toggleSelection } = dispatch( 'core/block-editor' );

return {
onResizeStart: () => toggleSelection( false ),
onResizeStop: () => toggleSelection( true ),
};
} ),
withNotices,
] )( FileEdit );
25 changes: 25 additions & 0 deletions packages/block-library/src/file/editor.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
.wp-block-file {
.wp-block[data-align="left"] > &,
.wp-block[data-align="right"] > & {
// Stop file block from collapsing when floated.
height: auto;
}

display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
margin-bottom: 0;

.components-resizable-box__container {
margin-bottom: 1em;
}

.wp-block-file__embed {
margin-bottom: 1em;
width: 100%;
height: 100%;
}

.wp-block-file__embed-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}

.wp-block-file__content-wrapper {
flex-grow: 1;
}
Expand Down
39 changes: 39 additions & 0 deletions packages/block-library/src/file/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
* Server-side rendering of the `core/file` block.
*
* @package WordPress
*/

/**
* When the `core/file` block is rendering, check if we need to enqueue the `'wp-block-library-file` script.
*
* @param array $attributes The block attributes.
* @param array $content The block content.
*
* @return string Returns the block content.
*/
function render_block_core_file( $attributes, $content ) {
if ( ! empty( $attributes['showInlineEmbed'] ) ) {
// Check if it's already enqueued, so we don't add the inline script multiple times.
if ( ! in_array( 'wp-block-library-file', wp_scripts()->queue, true ) ) {
wp_enqueue_script( 'wp-block-library-file', plugins_url( '/file.js', __FILE__ ) );
wp_add_inline_script( 'wp-block-library-file', 'hidePdfEmbedsOnUnsupportedBrowsers();' );
}
}

return $content;
}

/**
* Registers the `core/file` block on server.
*/
function register_block_core_file() {
register_block_type_from_metadata(
__DIR__ . '/file',
array(
'render_callback' => 'render_block_core_file',
)
);
}
add_action( 'init', 'register_block_core_file' );
39 changes: 38 additions & 1 deletion packages/block-library/src/file/inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,30 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { PanelBody, SelectControl, ToggleControl } from '@wordpress/components';
import {
PanelBody,
RangeControl,
SelectControl,
ToggleControl,
} from '@wordpress/components';
import { InspectorControls } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import { MIN_EMBED_HEIGHT, MAX_EMBED_HEIGHT } from './edit';

export default function FileBlockInspector( {
hrefs,
openInNewWindow,
showDownloadButton,
showInlineEmbed,
changeLinkDestinationOption,
changeOpenInNewWindow,
changeShowDownloadButton,
changeShowInlineEmbed,
embedHeight,
changeEmbedHeight,
} ) {
const { href, textLinkHref, attachmentPage } = hrefs;

Expand All @@ -26,6 +40,29 @@ export default function FileBlockInspector( {
return (
<>
<InspectorControls>
{ href.endsWith( '.pdf' ) && (
<PanelBody title={ __( 'PDF settings' ) }>
<ToggleControl
label={ __( 'Show inline embed' ) }
help={
showInlineEmbed
? __(
"Note: Most phone and tablet browsers won't display embedded PDFs."
)
: null
}
checked={ showInlineEmbed }
onChange={ changeShowInlineEmbed }
/>
<RangeControl
label={ __( 'Height in pixels' ) }
min={ MIN_EMBED_HEIGHT }
max={ Math.max( MAX_EMBED_HEIGHT, embedHeight ) }
value={ embedHeight }
onChange={ changeEmbedHeight }
/>
</PanelBody>
) }
<PanelBody title={ __( 'Text link settings' ) }>
<SelectControl
label={ __( 'Link to' ) }
Expand Down
21 changes: 21 additions & 0 deletions packages/block-library/src/file/save.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/
import { RichText } from '@wordpress/block-editor';
import { __, sprintf } from '@wordpress/i18n';

export default function save( { attributes } ) {
const {
Expand All @@ -11,11 +12,31 @@ export default function save( { attributes } ) {
textLinkTarget,
showDownloadButton,
downloadButtonText,
showInlineEmbed,
embedHeight,
} = attributes;

return (
href && (
<div>
{ showInlineEmbed && (
<>
<object
className="wp-block-file__embed"
data={ href }
type="application/pdf"
style={ {
width: '100%',
height: `${ embedHeight }px`,
} }
aria-label={ sprintf(
/* translators: %s: filename. */
__( 'Embed of %s.' ),
fileName
) }
/>
</>
) }
{ ! RichText.isEmpty( fileName ) && (
<a
href={ textLinkHref }
Expand Down
Loading