Bài viết này sẽ hướng dẫn các bạn tạo 1 block có thể upload hoặc thêm hình ảnh để sử dụng. Ví dụ như để làm background-image cho block, gallery, custom image.
Trong block này sẽ bao gồm các attributes
sau:
- bgColor: Set background color cho block
- bgUrl: Background image của block
- bgID: Đây là id của image select từ media
- focalPoint: Attribute này mình dùng để làm background position cho block
attributes: { focalPoint: { type: 'object', default: {x: 0.5, y: 0.5} }, bgID: { type: 'number' }, bgUrl: { type: 'string', default: '' }, bgColor: { type: 'string', default: '#948c8c' }, }
Để add, upload image trong gutenberg block thì chúng ta sử dụng component MediaUpload
. Để đảm bảo người dùng hiện tại có quyền Upload, bạn cần phải bọc component MediaUpload
vào MediaUploadCheck
.
import { Button } from '@wordpress/components';
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
const ALLOWED_MEDIA_TYPES = ['audio'];
function MyMediaUploader() {
return (
<MediaUploadCheck>
<MediaUpload
onSelect={(media) =>
console.log('selected ' + media.length)
}
allowedTypes={ALLOWED_MEDIA_TYPES}
value={mediaId}
render={({open}) => (
<Button onClick={open}>Open Media Library</Button>
)}
/>
</MediaUploadCheck>
);
}
Props
Component này chấp nhận các props sau:
- allowedTypes: Mảng với các loại phương tiện để upload/select từ thư viện phương tiện. Mỗi loại là một chuỗi có thể chứa mime type, ví dụ: ‘hình ảnh’, ‘âm thanh’, ‘văn bản’ hoặc mime type hoàn chỉnh, ví dụ: ‘audio/mpeg’, ‘image/gif’. Nếu allowedTypes không được đặt, tất cả các mime type phải được cho phép.
- multiple: Có cho phép nhiều lựa chọn hay không.
- value: Media ID (hoặc media IDs nếu multiple là true) được chọn theo mặc định khi mở media library.
- onClose: Callback function được gọi khi media modal bị đóng.
- onSelect: Callback function được gọi khi media modal bị đóng sau khi media được chọn.
- title: Tiêu đề được hiển thị trong media modal.
- modalClass: Class CSS được thêm vào media modal.
- addToGallery: Nếu true, gallery media modal sẽ mở trực tiếp trong thư viện phương tiện nơi người dùng có thể thêm hình ảnh bổ sung. Nếu false, gallery media modal sẽ mở trong chế độ chỉnh sửa, nơi người dùng có thể chỉnh sửa các hình ảnh hiện có, bằng cách sắp xếp lại chúng, xóa chúng hoặc thay đổi thuộc tính của chúng. Chỉ áp dụng nếu gallery === true.
- autoOpen: Nếu true, bộ chọn của nền tảng tương ứng sẽ tự động mở.
- gallery: Nếu true, thành phần sẽ khởi tạo tất cả các trạng thái cần thiết để đại diện cho một thư viện. Theo mặc định, media modal sẽ mở trong khung chỉnh sửa thư viện, nhưng điều đó có thể được thay đổi bằng cách sử dụng addToGallery.
- render: Một callback function được gọi để hiển thị Button mở thư viện phương tiện.
Demo Block
Code
Dưới đây là toàn bộ code của block demo.
index.js
// Import CSS.
import './editor.scss';
import './style.scss';
import { __ } from '@wordpress/i18n'
import { registerBlockType } from '@wordpress/blocks'
import { Fragment } from '@wordpress/element'
import {
MediaUpload,
MediaUploadCheck,
BlockControls,
InspectorControls,
InnerBlocks,
} from '@wordpress/block-editor'
import {
PanelBody,
Toolbar,
ToolbarGroup,
ToolbarButton,
FocalPointPicker,
Spinner,
ResponsiveWrapper,
Button,
ColorPalette
} from '@wordpress/components'
import { withSelect } from '@wordpress/data';
const ALLOWED_MEDIA_TYPES = ['image'];
const TEMPLATE = [
[
'core/heading',
{
level: 1,
placeholder: __('Heading...'),
className: 'heading',
content: __('Heading...')
},
],
[
'core/paragraph',
{
placeholder: __('Content...'),
content: __('Content...')
},
],
[
'core/paragraph',
{
placeholder: __('Content...'),
content: __('Content...')
},
],
]
const Edit = (props) => {
const {attributes, setAttributes, className, mediaBG} = props;
const {bgID, bgUrl, bgColor, focalPoint} = attributes;
const instructions = <p>{__('To edit the image, you need permission to upload media.')}</p>;
console.log(props)
return (
<Fragment>
<BlockControls>
<ToolbarGroup>
<MediaUploadCheck>
<MediaUpload
title={__('Background image')}
onSelect={(media) => (
setAttributes({
bgID: media.id,
bgUrl: media.url,
})
)}
allowedTypes={ALLOWED_MEDIA_TYPES}
value={bgID}
render={({open}) => (
<ToolbarButton
icon={<span className="dashicons dashicons-format-image" />}
label={__('Add background')}
onClick={open}
/>
)}
/>
</MediaUploadCheck>
</ToolbarGroup>
</BlockControls>
<InspectorControls>
<PanelBody title={__('Background Color')}>
<ColorPalette
colors={[
{name: 'red', color: '#f00'},
{name: 'white', color: '#fff'},
{name: 'blue', color: '#00f'},
]}
value={bgColor}
onChange={(bgColor) => setAttributes({bgColor})}
/>
</PanelBody>
<PanelBody title={__('Background Image')}>
<div className="components-placeholder__fieldset">
<MediaUploadCheck fallback={instructions}>
<MediaUpload
title={__('Background Image')}
onSelect={(media) => (
setAttributes({
bgID: media.id,
bgUrl: media.url,
})
)}
allowedTypes={ALLOWED_MEDIA_TYPES}
value={bgID}
render={({open}) => (
<Button
className={!bgID ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview'}
onClick={open}>
{!bgID && (__('Change Background Image'))}
{!!bgID && !mediaBG && <Spinner />}
{!!bgID && mediaBG &&
<img src={mediaBG.source_url} alt={__('Background image')} />
}
</Button>
)}
/>
</MediaUploadCheck>
{!!bgID && mediaBG &&
<MediaUploadCheck>
<MediaUpload
title={__('Icon')}
onSelect={(media) => (
setAttributes({
bgID: media.id,
bgUrl: media.url,
})
)}
allowedTypes={ALLOWED_MEDIA_TYPES}
value={bgID}
render={({open}) => (
<Button onClick={open} isSecondary>
{__('Replace Image')}
</Button>
)}
/>
</MediaUploadCheck>
}
{!!bgID &&
<MediaUploadCheck>
<Button onClick={() => (
setAttributes({
bgID: undefined,
bgUrl: '',
focalPoint: {x: 0.5, y: 0.5}
})
)} isDestructive>
{__('Remove Image')}
</Button>
</MediaUploadCheck>
}
</div>
</PanelBody>
{bgUrl && (<PanelBody title={__('Background Position')}>
<FocalPointPicker
label={__('Focal Point Picker')}
url={bgUrl}
value={focalPoint}
onChange={(focalPoint) => setAttributes({focalPoint})}
/>
</PanelBody>)}
</InspectorControls>
<div style={{
backgroundImage: `url(${bgUrl})`,
backgroundPosition: focalPoint ? `${focalPoint.x * 100}% ${focalPoint.y * 100}%` : 'auto',
backgroundColor: bgColor,
}} className={['my-block', 'block-hero-section block-editor', className].join(' ')}>
<div className={["inner-wrap"].join(' ')}>
<div className="content-wrap">
<InnerBlocks
templateLock="inser"
template={TEMPLATE}
/>
</div>
</div>
</div>
</Fragment>
);
}
const Save = (props) => {
const {attributes, className} = props;
const {bgID, bgUrl, bgColor, focalPoint,} = attributes;
return (
<div style={{
backgroundImage: `url(${bgUrl})`,
backgroundPosition: focalPoint ? `${focalPoint.x * 100}% ${focalPoint.y * 100}%` : 'auto',
backgroundColor: bgColor,
}} className={['my-block', 'block-hero-section', className].join(' ')}>
<div className={["inner-wrap"].join(' ')}>
<div className="content-wrap">
<InnerBlocks.Content />
</div>
</div>
</div>
)
}
registerBlockType('cgb/my-block', {
title: __('my-block - CGB Block'),
icon: 'shield',
category: 'common',
keywords: [
__('my-block — CGB Block'),
__('CGB Example'),
__('create-guten-block'),
],
attributes: {
focalPoint: {
type: 'object',
default: {x: 0.5, y: 0.5}
},
bgID: {
type: 'number'
},
bgUrl: {
type: 'string',
default: ''
},
bgColor: {
type: 'string',
default: '#948c8c'
},
},
supports: {
align: true,
anchor: true
},
example: {
attributes: {
focalPoint: {x: 0.5, y: 0.5},
bgUrl: 'https://picsum.photos/800',
align: 'full',
bgColor: '#948c8c',
},
},
edit: withSelect((select, props) => {
const {getMedia} = select('core');
const {bgID} = props.attributes;
return {
mediaBG: bgID ? getMedia(bgID) : null,
};
})(Edit),
save: Save,
});
style.scss
.block-hero-section {
padding-top: 80px;
padding-bottom: 80px;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
text-align: center;
}
Tài liệu tham khảo
https://github.com/WordPress/gutenberg/tree/trunk/packages/block-editor/src/components/media-upload
https://github.com/WordPress/gutenberg/tree/trunk/packages/components/src/focal-point-picker
https://github.com/WordPress/gutenberg/tree/trunk/packages/components/src/color-palette