import { __ } from '@wordpress/i18n';
|
import {
|
useBlockProps,
|
InspectorControls,
|
MediaUpload,
|
MediaUploadCheck,
|
InnerBlocks,
|
useInnerBlocksProps
|
} from '@wordpress/block-editor';
|
import {
|
PanelBody,
|
Button,
|
ToggleControl,
|
BaseControl,
|
RangeControl,
|
SelectControl
|
} from '@wordpress/components';
|
import './editor.scss';
|
|
const ALLOWED_VIDEO_TYPES = ['video/mp4', 'video/webm', 'video/ogg', 'video/ogv'];
|
|
const INNER_BLOCKS_TEMPLATE = [
|
['core/heading', {
|
level: 1,
|
placeholder: 'Add heading...',
|
textAlign: 'center'
|
}],
|
['core/paragraph', {
|
placeholder: 'Add description...',
|
align: 'center'
|
}],
|
['core/buttons', {
|
layout: { type: 'flex', justifyContent: 'center' }
|
}]
|
];
|
|
export default function Edit({ attributes, setAttributes }) {
|
const {
|
posterId,
|
posterUrl,
|
videoSources,
|
mobileSources,
|
fadeEffect,
|
overlayOpacity,
|
contentAlignment,
|
minHeight
|
} = attributes;
|
|
const blockProps = useBlockProps({
|
className: 'video-cover-editor',
|
style: {
|
minHeight: minHeight ? `${minHeight}px` : undefined
|
}
|
});
|
|
const innerBlocksProps = useInnerBlocksProps(
|
{ className: 'video-cover-content' },
|
{
|
template: INNER_BLOCKS_TEMPLATE,
|
templateLock: false
|
}
|
);
|
|
const onSelectPoster = (media) => {
|
setAttributes({
|
posterId: media.id,
|
posterUrl: media.url
|
});
|
};
|
|
const onSelectVideo = (media, isMobile = false) => {
|
const newSource = {
|
id: media.id,
|
url: media.url,
|
mime: media.mime
|
};
|
|
if (isMobile) {
|
// Check if this format already exists in mobile sources
|
const exists = mobileSources.some(s => s.mime === media.mime);
|
if (!exists) {
|
setAttributes({
|
mobileSources: [...mobileSources, newSource]
|
});
|
}
|
} else {
|
// Check if this format already exists in desktop sources
|
const exists = videoSources.some(s => s.mime === media.mime);
|
if (!exists) {
|
setAttributes({
|
videoSources: [...videoSources, newSource]
|
});
|
}
|
}
|
};
|
|
const removeVideoSource = (index, isMobile = false) => {
|
if (isMobile) {
|
const updated = [...mobileSources];
|
updated.splice(index, 1);
|
setAttributes({ mobileSources: updated });
|
} else {
|
const updated = [...videoSources];
|
updated.splice(index, 1);
|
setAttributes({ videoSources: updated });
|
}
|
};
|
|
const renderVideoSourceList = (sources, isMobile = false) => {
|
if (sources.length === 0) return null;
|
|
return (
|
<ul className="video-source-list">
|
{sources.map((source, index) => (
|
<li key={index} className="video-source-item">
|
<span className="video-source-mime">{source.mime}</span>
|
<Button
|
isDestructive
|
isSmall
|
onClick={() => removeVideoSource(index, isMobile)}
|
>
|
{__('Remove', 'jvb')}
|
</Button>
|
</li>
|
))}
|
</ul>
|
);
|
};
|
|
return (
|
<>
|
<InspectorControls>
|
<PanelBody title={__('Video Settings', 'jvb')} initialOpen={true}>
|
<BaseControl
|
label={__('Poster Image', 'jvb')}
|
help={__('Image shown while video loads', 'jvb')}
|
>
|
<MediaUploadCheck>
|
<MediaUpload
|
onSelect={onSelectPoster}
|
allowedTypes={['image']}
|
value={posterId}
|
render={({ open }) => (
|
<>
|
{posterUrl && (
|
<img
|
src={posterUrl}
|
alt={__('Poster preview', 'jvb')}
|
style={{ maxWidth: '100%', marginBottom: '10px' }}
|
/>
|
)}
|
<Button
|
onClick={open}
|
variant={posterUrl ? 'secondary' : 'primary'}
|
>
|
{posterUrl
|
? __('Change Poster', 'jvb')
|
: __('Select Poster', 'jvb')}
|
</Button>
|
{posterUrl && (
|
<Button
|
isDestructive
|
onClick={() => setAttributes({ posterId: 0, posterUrl: '' })}
|
style={{ marginLeft: '10px' }}
|
>
|
{__('Remove', 'jvb')}
|
</Button>
|
)}
|
</>
|
)}
|
/>
|
</MediaUploadCheck>
|
</BaseControl>
|
|
<BaseControl
|
label={__('Desktop Video Sources', 'jvb')}
|
help={__('Add multiple formats for better browser support', 'jvb')}
|
>
|
{renderVideoSourceList(videoSources, false)}
|
<MediaUploadCheck>
|
<MediaUpload
|
multiple={ true }
|
onSelect={(media) => onSelectVideo(media, false)}
|
allowedTypes={ALLOWED_VIDEO_TYPES}
|
render={({ open }) => (
|
<Button
|
onClick={open}
|
variant="secondary"
|
>
|
{__('Add Desktop Video', 'jvb')}
|
</Button>
|
)}
|
/>
|
</MediaUploadCheck>
|
</BaseControl>
|
|
<BaseControl
|
label={__('Mobile Video Sources (Optional)', 'jvb')}
|
help={__('Smaller videos for mobile devices', 'jvb')}
|
>
|
{renderVideoSourceList(mobileSources, true)}
|
<MediaUploadCheck>
|
<MediaUpload
|
multiple={ true }
|
onSelect={(media) => onSelectVideo(media, true)}
|
allowedTypes={ALLOWED_VIDEO_TYPES}
|
render={({ open }) => (
|
<Button
|
onClick={open}
|
variant="secondary"
|
>
|
{__('Add Mobile Video', 'jvb')}
|
</Button>
|
)}
|
/>
|
</MediaUploadCheck>
|
</BaseControl>
|
|
<ToggleControl
|
label={__('Fade Effect', 'jvb')}
|
help={__('Add fade class to video element', 'jvb')}
|
checked={fadeEffect}
|
onChange={(value) => setAttributes({ fadeEffect: value })}
|
/>
|
</PanelBody>
|
|
<PanelBody title={__('Overlay Settings', 'jvb')} initialOpen={true}>
|
<RangeControl
|
label={__('Overlay Opacity', 'jvb')}
|
help={__('Darken video for better text readability', 'jvb')}
|
value={overlayOpacity}
|
onChange={(value) => setAttributes({ overlayOpacity: value })}
|
min={0}
|
max={100}
|
step={5}
|
/>
|
|
<SelectControl
|
label={__('Content Alignment', 'jvb')}
|
value={contentAlignment}
|
options={[
|
{ label: __('Top Left', 'jvb'), value: 'top-left' },
|
{ label: __('Top Center', 'jvb'), value: 'top-center' },
|
{ label: __('Top Right', 'jvb'), value: 'top-right' },
|
{ label: __('Center Left', 'jvb'), value: 'center-left' },
|
{ label: __('Center', 'jvb'), value: 'center' },
|
{ label: __('Center Right', 'jvb'), value: 'center-right' },
|
{ label: __('Bottom Left', 'jvb'), value: 'bottom-left' },
|
{ label: __('Bottom Center', 'jvb'), value: 'bottom-center' },
|
{ label: __('Bottom Right', 'jvb'), value: 'bottom-right' }
|
]}
|
onChange={(value) => setAttributes({ contentAlignment: value })}
|
/>
|
|
<RangeControl
|
label={__('Minimum Height', 'jvb')}
|
help={__('Minimum height in pixels (leave 0 for auto)', 'jvb')}
|
value={minHeight}
|
onChange={(value) => setAttributes({ minHeight: value })}
|
min={0}
|
max={1000}
|
step={50}
|
/>
|
</PanelBody>
|
</InspectorControls>
|
|
<div {...blockProps}>
|
{posterUrl || videoSources.length > 0 ? (
|
<div className="video-cover-preview">
|
{posterUrl && (
|
<>
|
<img src={posterUrl} alt={__('Video poster', 'jvb')} />
|
{overlayOpacity > 0 && (
|
<div
|
className="video-overlay-preview"
|
style={{ opacity: overlayOpacity / 100 }}
|
/>
|
)}
|
</>
|
)}
|
<div className={`video-cover-content-preview align-${contentAlignment}`}>
|
<div {...innerBlocksProps} />
|
</div>
|
<div className="video-info">
|
<p>
|
{videoSources.length} {__('desktop source(s)', 'jvb')}
|
{mobileSources.length > 0 &&
|
`, ${mobileSources.length} ${__('mobile source(s)', 'jvb')}`
|
}
|
</p>
|
</div>
|
</div>
|
) : (
|
<div className="video-cover-placeholder">
|
<p>{__('Configure video sources in the sidebar →', 'jvb')}</p>
|
</div>
|
)}
|
</div>
|
</>
|
);
|
}
|