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

New component: Video #225

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions components/video/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Video } from './video';
export { VideoControl } from './video-control';
122 changes: 122 additions & 0 deletions components/video/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Video

The Video component allows you to easily add videos to your custom blocks without needing to manually worry about loading states etc. It renders a `<MediaPlaceholder />` component in place of the video if the id is not set and shows a spinner when the video is still loading.

## Usage

```js
import { Video } from '@10up/block-components';

function BlockEdit(props) {
const { attributes, setAttributes } = props;
const { videoId } = attributes;

function handlevideoSelect( video ) {
setAttributes({videoId: video.id});
}

return (
<Video
id={videoId}
className="my-video"
size="full"
onSelect={handlevideoSelect}
labels={{
title: 'Select looping background video',
instructions: 'Upload a media file or pick one from your media library.'
}}
autoPlay={true}
loop={true}
muted={true}
controls={false}
/>
)
}
```

If you'd like to make an Inspector control for this video, use `VideoControl` instead.

```js
import { Video } from '@10up/block-components';

function BlockEdit(props) {
const { attributes, setAttributes } = props;
const { videoId } = attributes;

function handlevideoSelect( video ) {
setAttributes({videoId: video.id});
}

return (
<Video
id={videoId}
className="my-video"
size="full"
onSelect={handlevideoSelect}
labels={{
title: 'Select looping background video',
instructions: 'Upload a media file or pick one from your media library.'
}}
/>
)
}
```

While you can set the same `autoPlay`, `loop`, and `muted` properties to the `Video` or `VideoControl` directly, you can also allow the user to control these settings by passing a `onChangeVideoOptions` callback, and a `videoOptions` setting object:

```js
import { Video } from '@10up/block-components';

function BlockEdit(props) {
const { attributes, setAttributes } = props;
const {
videoId,
videoOptions = {
playsInline: true,
controls: true,
muted: true,
autoPlay: false,
loop: false,
}
} = attributes;

function handleVideoSelect( video ) {
setAttributes({videoId: video.id});
}

function handleChangeVideoOptions( options ) {
setAttributes({videoOptions: options});
}

return (
<Video
id={videoId}
className="my-video"
size="full"
onSelect={handleVideoSelect}
labels={{
title: 'Select looping background video',
instructions: 'Upload a media file or pick one from your media library.'
}}
/>
)
}
```



##

> **Note**
> In order to get the same result as the GIF you also need to use the [`MediaToolbar`](https://github.com/10up/block-components/tree/develop/components/media-toolbar) component. It adds the Replace flow to the Blocks Toolbar.

## Props

| Name | Type | Default | Description |
| ---------- | ----------------- | -------- | -------------------------------------------------------------- |
| `id` | `number` | `null` | video ID |
| `onSelect` | `Function` | `null` | Callback that gets called with the new video when one is selected |
| `size` | `string` | `large` | Name of the video size to be displayed |
| `labels` | `object` | `{}` | Pass in an object of labels to be used by the `MediaPlaceholder` component under the hook. Allows the sub properties `title` and `instructions` |
| `canEditVideo` | `boolean` | `true` | whether or not the video can be edited by in the context its getting viewed. Controls whether a placeholder or upload controls should be shown when no video is present |
| `...rest` | `*` | `null` | any additional attributes you want to pass to the underlying `img` tag |
170 changes: 170 additions & 0 deletions components/video/video-control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* wraps the Video component with controls to allow use for uploads in the sidebar
*/

import PropTypes from 'prop-types';
import { MediaUploadCheck, MediaUpload } from '@wordpress/block-editor';
import {
Placeholder,
BaseControl,
useBaseControlProps,
Button,
ToggleControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { Video } from './video';

const VideoControl = (props) => {
const {
id,
allowedTypes = ['video/mp4'],
onSelect,
labels: { title = __('Video upload'), instructions = __('Choose a video') },
canEditVideo = true,
hideVideo = false,
video,
videoOptions = {
playsInline: true,
controls: true,
muted: true,
autoPlay: false,
loop: false,
},
onChangeVideoOptions,
} = props;
const hasVideo = !!id;

const { playsInline, controls, muted, autoPlay, loop } = videoOptions;

const { baseControlProps, controlProps } = useBaseControlProps(props);

if (!hasVideo && !canEditVideo) {
return <Placeholder className="block-editor-media-placeholder" withIllustration />;
}

if (!hasVideo && canEditVideo) {
return (
<BaseControl label={title} help={instructions} {...baseControlProps}>
<MediaUploadCheck>
<MediaUpload
value={video}
onSelect={onSelect}
allowedTypes={allowedTypes}
mode="upload"
multiple={false}
title={title}
help={instructions}
render={({ open }) => (
<Button variant="primary" onClick={open}>
{__('Upload video (webm, mp4)')}
</Button>
)}
{...controlProps}
/>
</MediaUploadCheck>
</BaseControl>
);
}

return (
<BaseControl title={title} {...baseControlProps}>
{!hideVideo && (
<Video
id={id}
onSelect={onSelect}
labels={{ title, instructions }}
playsInline={playsInline}
controls={controls}
muted={muted}
autoPlay={autoPlay}
loop={loop}
/>
)}
<MediaUploadCheck>
<MediaUpload
value={id}
onSelect={onSelect}
allowedTypes={allowedTypes}
mode="upload"
multiple={false}
render={({ open }) => (
<Button variant="secondary" onClick={open}>
{__('Change video (webm, mp4)')}
</Button>
)}
/>
</MediaUploadCheck>
{onChangeVideoOptions && (
<>
<ToggleControl
label={__('Autoplay video?')}
checked={autoPlay}
help={__('The video will attempt to play automatically on page load. Browser options may prevent this.')}
onChange={(value) => {
console.log(value);
onChangeVideoOptions({ autoPlay: value, loop, muted });
}}
/>
<ToggleControl
label={__('Mute audio at beginning?')}
checked={muted}
help={__('The video will play muted. This may be the default in some browsers.')}
onChange={(value) => {
console.log(value);
onChangeVideoOptions({ autoPlay, muted: value, loop });
}}
/>
<ToggleControl
label={__('Loop video?')}
checked={loop}
help={__('The video will play to its end, then begin again from the start.')}
onChange={(value) => {
console.log(value);
onChangeVideoOptions({ autoPlay, muted, loop: value });
}}
/>
</>
)}
</BaseControl>
);
};

VideoControl.defaultProps = {
labels: {
title: __('Video upload'),
instructions: __('Choose a video'),
},
canEditVideo: true,
hideVideo: false,
allowedTypes: ['video/mp4'],
videoOptions: {
playsInline: true,
controls: true,
muted: true,
autoPlay: false,
loop: false,
},
onChangeVideoOptions: null,
};

VideoControl.propTypes = {
id: PropTypes.number.isRequired,
onSelect: PropTypes.func.isRequired,
labels: PropTypes.shape({
title: PropTypes.string,
instructions: PropTypes.string,
}),
canEditVideo: PropTypes.bool,
hideVideo: PropTypes.bool,
allowedTypes: PropTypes.array,
videoOptions: PropTypes.shape({
playsInline: PropTypes.bool,
controls: PropTypes.bool,
muted: PropTypes.bool,
autoPlay: PropTypes.bool,
loop: PropTypes.bool,
}),
onChangeVideoOptions: PropTypes.func,
};

export { VideoControl };
71 changes: 71 additions & 0 deletions components/video/video.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { MediaPlaceholder } from '@wordpress/block-editor';
import { Spinner, Placeholder } from '@wordpress/components';

import { useMedia, PropTypes } from '@10up/block-components';

const Video = (props) => {
const {
id,
size = 'full',
onSelect,
labels = {},
canEditVideo = true,
className = '',
playsInline = true,
autoPlay = false,
loop = false,
muted = true,
controls = true,
} = props;
const hasVideo = !!id;
const { media, isResolvingMedia } = useMedia(id);

if (!hasVideo && !canEditVideo) {
return <Placeholder className="block-editor-media-placeholder" withIllustration />;
}

if (!hasVideo && canEditVideo) {
return (
<MediaPlaceholder labels={labels} onSelect={onSelect} accept="video" multiple={false} />
);
}

if (isResolvingMedia) {
return <Spinner />;
}

const sourceUrl = media?.media_details?.sizes[size]?.source_url ?? media?.source_url;
const mimeType = media?.media_details?.type ?? '';
const altText = media?.alt_text;

return (
<video
alt={altText}
className={className}
playsInline={playsInline}
autoPlay={autoPlay}
loop={loop}
muted={muted}
controls={controls}
>
<source src={sourceUrl} type={mimeType} />
</video>
);
};

Video.defaultProps = {
labels: {},
canEditVideo: true,
};

Video.propTypes = {
id: PropTypes.number.isRequired,
onSelect: PropTypes.func.isRequired,
labels: PropTypes.shape({
title: PropTypes.string,
instructions: PropTypes.string,
}),
canEditVideo: PropTypes.bool,
};

export { Video };
Loading