Skip to content

Commit f7ecd32

Browse files
authored
Merge pull request #885 from Codeinwp/prf/video-player
feat: video player and embed block integration
2 parents acb384e + 45f9398 commit f7ecd32

File tree

12 files changed

+1268
-0
lines changed

12 files changed

+1268
-0
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { BlockControls, InspectorControls } from '@wordpress/block-editor';
2+
import {
3+
Button,
4+
PanelBody,
5+
Placeholder,
6+
SelectControl,
7+
ToolbarButton,
8+
ToolbarGroup,
9+
Notice,
10+
CheckboxControl,
11+
ColorPalette,
12+
BaseControl
13+
} from '@wordpress/components';
14+
import { useDispatch } from '@wordpress/data';
15+
import { useEffect, useMemo, useState } from '@wordpress/element';
16+
import { edit } from '@wordpress/icons';
17+
import { logo } from '../common/icons';
18+
import { ASPECT_RATIO_OPTIONS } from '../common/constants';
19+
import { isOptimoleURL } from '../common/utils';
20+
21+
const Edit = ( props ) => {
22+
const { replaceBlock } = useDispatch( 'core/block-editor' );
23+
const { attributes, setAttributes, isSelected } = props;
24+
25+
const [ loading, setLoading ] = useState( false );
26+
const [ editing, setEditing ] = useState( false );
27+
const [ url, setUrl ] = useState( attributes.url );
28+
const [ error, setError ] = useState( false );
29+
30+
const onUrlChange = ( value ) => {
31+
setUrl( value );
32+
};
33+
34+
35+
const onAttributeUpdate = ( value, name ) => {
36+
setAttributes({ [name]: value });
37+
};
38+
39+
const onEdit = () => {
40+
setEditing( true );
41+
};
42+
43+
const onSave = ( event ) => {
44+
event.preventDefault();
45+
setError( false );
46+
47+
48+
const url = event.target.url.value;
49+
50+
isOptimoleURL( url ).then( ( valid ) => {
51+
if ( valid ) {
52+
setAttributes({ url });
53+
setEditing( false );
54+
return;
55+
}
56+
57+
setError( true );
58+
});
59+
};
60+
61+
useEffect( () => {
62+
isOptimoleURL( attributes.url ).then( ( valid ) => {
63+
if ( ! valid ) {
64+
setEditing( true );
65+
setError( true );
66+
}
67+
});
68+
}, [ attributes.url ]);
69+
70+
const style = useMemo( () => {
71+
const style = {
72+
'--om-primary-color': attributes.primaryColor,
73+
'--om-aspect-ratio': attributes.aspectRatio
74+
};
75+
76+
if ( ! isSelected ) {
77+
style.pointerEvents = 'none';
78+
}
79+
80+
return style;
81+
}, [ attributes.aspectRatio, attributes.primaryColor, isSelected ]);
82+
83+
return (
84+
<>
85+
<BlockControls>
86+
<ToolbarGroup>
87+
<ToolbarButton
88+
icon={edit}
89+
label={OMVideoPlayerBlock.editLabel}
90+
onClick={onEdit}
91+
/>
92+
</ToolbarGroup>
93+
</BlockControls>
94+
95+
<InspectorControls>
96+
<PanelBody title={OMVideoPlayerBlock.blockTitle} initialOpen={true}>
97+
98+
<SelectControl
99+
value={attributes.aspectRatio}
100+
label={OMVideoPlayerBlock.aspectRatioLabel}
101+
options={ASPECT_RATIO_OPTIONS}
102+
onChange={( value ) => onAttributeUpdate( value, 'aspectRatio' )}
103+
/>
104+
105+
<CheckboxControl
106+
label={OMVideoPlayerBlock.loopLabel}
107+
checked={attributes.loop}
108+
onChange={( value ) => onAttributeUpdate( value, 'loop' )}
109+
/>
110+
111+
<CheckboxControl
112+
label={OMVideoPlayerBlock.hideControlsLabel}
113+
checked={attributes.hideControls}
114+
onChange={( value ) => onAttributeUpdate( value, 'hideControls' )}
115+
/>
116+
117+
</PanelBody>
118+
</InspectorControls>
119+
<InspectorControls group="styles">
120+
<PanelBody title={OMVideoPlayerBlock.blockTitle} initialOpen={true}>
121+
<BaseControl label={OMVideoPlayerBlock.primaryColorLabel}>
122+
<ColorPalette
123+
clearable={false}
124+
value={attributes.primaryColor}
125+
onChange={( value ) => onAttributeUpdate( value, 'primaryColor' )}
126+
/>
127+
</BaseControl>
128+
</PanelBody>
129+
</InspectorControls>
130+
<div style={{ display: 'flex', width: '100%' }}>
131+
{! editing && <optimole-video-player video-src={props.attributes.url} style={style} loop={attributes.loop} hide-controls={attributes.hideControls}></optimole-video-player>}
132+
{editing && <Placeholder
133+
label={OMVideoPlayerBlock.urlLabel}
134+
className="wp-block-embed"
135+
>
136+
<form onSubmit={onSave}>
137+
<input type="url" id="url" defaultValue={attributes.url} className="wp-block-embed__placeholder-input" onChange={onUrlChange} />
138+
<Button isPrimary type="submit">{OMVideoPlayerBlock.save}</Button>
139+
</form>
140+
141+
{error && <Notice status="error" isDismissible={false}><span dangerouslySetInnerHTML={{ __html: OMVideoPlayerBlock.invalidUrlError }}/></Notice>}
142+
143+
</Placeholder>
144+
}
145+
</div>
146+
</>
147+
);
148+
};
149+
150+
export default {
151+
icon: logo,
152+
title: OMVideoPlayerBlock.blockTitle,
153+
description: OMVideoPlayerBlock.blockDescription,
154+
155+
supports: {
156+
align: true,
157+
inserter: false,
158+
spacing: {
159+
margin: true
160+
}
161+
},
162+
category: 'embed',
163+
attributes: {
164+
url: {
165+
type: 'string',
166+
default: ''
167+
},
168+
aspectRatio: {
169+
type: 'string',
170+
default: 'auto'
171+
},
172+
primaryColor: {
173+
type: 'string',
174+
default: '#577BF9'
175+
},
176+
loop: {
177+
type: 'boolean',
178+
default: false
179+
},
180+
hideControls: {
181+
type: 'boolean',
182+
default: false
183+
}
184+
},
185+
edit: Edit,
186+
save: ( props ) => {
187+
return null;
188+
}
189+
};

assets/src/video-player/block/hoc.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { createHigherOrderComponent } from '@wordpress/compose';
2+
import { useDispatch } from '@wordpress/data';
3+
import { useEffect, useState } from '@wordpress/element';
4+
import { createBlock } from '@wordpress/blocks';
5+
import { isOptimoleURL } from '../common/utils';
6+
const BlockReplacer = ({ clientIDToReplace, url }) => {
7+
const { replaceBlock } = useDispatch( 'core/block-editor' );
8+
9+
useEffect( ()=> {
10+
const block = createBlock( 'optimole/video-player', {
11+
url: url
12+
});
13+
14+
replaceBlock( clientIDToReplace, block );
15+
}, [ clientIDToReplace ]);
16+
17+
return null;
18+
};
19+
20+
/**
21+
* Higher order component to check URLs and auto-detect custom embeds
22+
*/
23+
export const withCustomEmbedURLDetection = createHigherOrderComponent( ( BlockEdit ) => {
24+
return ( props ) => {
25+
const [ validOptimoleURL, setValidOptimoleURL ] = useState( false );
26+
27+
const { attributes } = props;
28+
const { url } = attributes;
29+
30+
useEffect( ()=> {
31+
isOptimoleURL( props.attributes.url ).then( ( valid ) => {
32+
setValidOptimoleURL( valid );
33+
});
34+
}, [ props.attributes.url ]);
35+
36+
if ( 'core/embed' !== props.name ) {
37+
return <BlockEdit {...props} />;
38+
}
39+
40+
41+
if ( url && validOptimoleURL ) {
42+
return <BlockReplacer clientIDToReplace={props.clientId} url={attributes.url} />;
43+
}
44+
45+
return <BlockEdit {...props} />;
46+
};
47+
}, 'withCustomEmbedURLDetection' );
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const ASPECT_RATIO_OPTIONS = [
2+
{
3+
value: 'auto',
4+
label: OMVideoPlayerBlock.auto
5+
},
6+
{
7+
value: '16/9',
8+
label: '16:9'
9+
},
10+
{
11+
value: '4/3',
12+
label: '4:3'
13+
},
14+
{
15+
value: '1/1',
16+
label: '1:1'
17+
},
18+
{
19+
value: '9/16',
20+
label: '9:16'
21+
},
22+
{
23+
value: '1/2',
24+
label: '1:2'
25+
},
26+
{
27+
value: '2/1',
28+
label: '2:1'
29+
}
30+
];
31+
32+
export { ASPECT_RATIO_OPTIONS };

0 commit comments

Comments
 (0)