Tworzymy statyczny i dynamiczny blok WordPressa pozwalający na umieszczenie w nim obrazka przy pomocy m. in komponentu MediaUpload. Poznamy kilka nowych komponentów oraz kilka nowych sposobów na tworzenie bloków WordPress.

Tworzymy blok statyczny – pierwsze kroki

Standardowo w folderze plugins wewnątrz wp-content w naszej instalacji WordPressa tworzymy plugin z blokiem statycznym przy użyciu pewnej komendy tworzącej nam wszystkie potrzebne do pracy pliki, w tym block.json, edit.js, save.js, pliki stylowania scss oraz plik pluginu.

npx @wordpress/create-block@latest img-block-try1

Teraz przechodzimy do tego folderu:

cd img-block-try1

Teraz możemy aktywować nasz plugin w panelu admina. Gdy już to zrobimy przechodzimy do folderu src, gdzie w block.json dodajemy nasz atrybut:

"attributes" : {
        "imageUrl": {
            "type": "string",
            "default": "" 
        }
	}

Atrybut typu napisowego, domyślnie pusty napis. Gdy to zrobimy, przechodzimy do pliku save.js – zrobienie poprawnego renderowania jest akurat banalnie proste:

export default function save({ attributes }) {
	const { imageUrl} = attributes;
	return (
		<div { ...useBlockProps.save() }>
			<img src={imageUrl}/>
		</div>
	);
}

Sprowadza się to jedynie do wyciągnięcia atrybutu imageUrl i wyświetlenia tagu <img> o atrybucie src równym naszemu urlowi. Robimy to w sposób Reactowo-JSXowy, a zatem zamiast jakichś cudzysłowów mamy nawiasy klamrowe ze zmienną interpolowaną tam.

Teraz możemy otworzyć nasz plik edit.js i zapewnić sobie wszelkie potrzebne importy:

import { useBlockProps } from '@wordpress/block-editor';
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button} from '@wordpress/components';

useBlockProps już znamy – dodaje nam klasę CSS z plików SCSS do naszego głównego komponentu. Jeżeli nie chcemy, aby nam tę klasę dodawał – nie potrzebujemy go. Ja bym jednak tego nie robił, co najwyżej usunął/wykomentował z plików scss ten css, którego nie chcemy.

Element MediaUpload służy do uploadowania mediów, jak sama nazwa wskazuje, zaś element MediaUploadCheck to wrapper, który sprawdza, czy mamy takie uprawnienia, aby cokolwiek przesyłać. Komponent Button to guzik.

Teraz pozostaje nam jedynie przygotować funkcję edit do pracy:

export default function Edit({ attributes, setAttributes }) {
	const { imageUrl } = attributes;
	return (
		<div { ...useBlockProps() }>
			{ __(
				'Img Block Try1 – hello from the editor!',
				'img-block-try1'
			) }
		</div>
	);
}

MediaUpload, MediaUploadCheck – omówienie

Bez zbędnego przedłużania – tak wygląda nasza funkcja edit.js:

export default function Edit({ attributes, setAttributes }) {
	const { imageUrl} = attributes;
	return (
			<div { ...useBlockProps() }>
			<MediaUploadCheck>
				<MediaUpload
					onSelect={(media) => setAttributes({ imageUrl: media.url })}
					allowedTypes={['image']}
					render={({ open }) => (
						<>  {imageUrl ? <img src={imageUrl}/> : null }
							<Button onClick={open}>
								{imageUrl ? 'Zmień obraz' : 'Wybierz obraz'}
							</Button>
						   
						</>
					)}
				/>
			</MediaUploadCheck>
		</div>
		
	);
}

MediaUploadCheck – wrapper sprawdzający, czy mamy uprawnienia do przesyłania mediów. MediaUpload – narzędzie do przesyłania takowych. Atrybut onSelect – funkcja ustawiająca imageUrl (nasz atrybut z pliku block.json) na url, jaki podamy wybierając obraz z listy dostępnych w naszym WordPressie mediów. Atrybut allowedTypes – co można przesyłać, tutaj akurat wszystkie typy obrazkowe.

Ciekawa jest funkcja render. Ona musi wyglądać tak jak wygląda, natomiast we fragmencie, jaki zwróciliśmy (znaki <> oraz </> bowiem w React zawsze zwracamy jeden element nadrzędny, a zatem jeżeli chcemy zwrócić kilka, oplatamy je w takie coś) – tam mamy do czynienia z renderowaniem warunkowym.

Jeżeli mamy ustawiony atrybut imageUrl zobaczymy podgląd ostatniego obrazka. Jeżeli nie – nie.

I dalej, w guziku, który po kliknięciu otwiera nam modal do wybierania obrazków, między tagiem otwierającym a zamykającym mamy renderowanie warunkowe – albo napis „Wybierz obraz” albo „zmień obraz”.

Teraz trzeba tylko skorzystać z naszej ulubionej komendy, czyli:

npm run build

Możemy przy tym zauważyć, że obraz inaczej wygląda w podglądzie, a inaczej w rzeczywistości, na stronie, jakoś tak rozlewa się poza swój blok (nie musi tak być, ale może).

W tym wypadku przyda się pomoc cssa. W pliku style.scss:

.wp-block-create-block-img-block-try1 {
	// background-color: #21759b;
	color: #fff;
	// padding: 2px;
}
.wp-block-create-block-img-block-try1 img {
	height: auto;
	width: 100%;
}

Swoją drogą to jest zapis CSSowy, a pracujemy w SCSS – możemy to sobie uprościć:

.wp-block-create-block-img-block-try1 {
	// background-color: #21759b;
	color: #fff;
	// padding: 2px;
	img {
		height: auto;
		width: 100%;
	}
}

Teraz tylko nasza ulubiona komenda

npm run build

I nasz blok działa. Pozwala dodawać i zmieniać obrazki. Jest to jakiś krok w stronę naszego małego rozwoju w pisaniu własnych bloków WordPressa.

Piszemy blok dynamiczny – różnice

Blok dynamiczny jest o tyle ciekawy, że będą nam potrzebne aż dwa atrybuty – imageUrl (do wyświetlania w edytorze) oraz imageID (do wyświetlania w render.php przy użyciu funkcji WordPressa wp_get_attachment_image_url).

Najpierw jednak – stwórzmy blok dynamiczny. Komenda, oczywiście w folderze plugins, nie w folderze poprzedniego pluginu:

 npx @wordpress/create-block@latest img-block-dyn  --variant dynamic

Po wykonaniu komendy przechodzimy do folderu:

cd img-block-dyn

Teraz bierzemy się za block.json w folderze src. Dopisujemy dwa atrybuty:

"attributes" : {
        "imageUrl": {
            "type": "string",
            "default": "" 
        },
		"imageID": {
            "type": "integer",
			"default": 0
             
        }
	}

Teraz edit.js. Skorzystamy sobie z innej opcji niż poprzednio, a mianowicie MediaPlaceholder:

import { useBlockProps, MediaPlaceholder } from '@wordpress/block-editor';

Jak już to mamy, to możemy przygotować względnie nasz edit:

export default function Edit({ attributes, setAttributes }) {
	let { imageUrl, imageID } = attributes;
	const mediaPreview = imageUrl && <img src={imageUrl} alt="Wybrany obraz" />;
	return (
		<p { ...useBlockProps() }>
			{ __( 'Img Block Dyn – hello from the editor!', 'img-block-dyn' ) }
		</p>
	);
}

Jak widać wyciągamy imageUrl oraz imageID oraz robimy sobie mediaPreview za pomocą short-circuiting, który sprawdza, czy imageUrl jest ustawiony i tworzy obrazek z podglądem.

Teraz pora rozwinąć ten blok używając MediaPlaceholder:

export default function Edit({ attributes, setAttributes }) {
	let { imageUrl, imageID } = attributes;
	const mediaPreview = imageUrl && <img src={imageUrl} alt="Wybrany obraz" />;
	return (
		<div { ...useBlockProps() }>
			<MediaPlaceholder
			icon="format-image"
			labels={{
				title: 'Wybierz obraz',
				instructions: 'Przeciągnij i upuść obraz lub kliknij, aby wybrać z biblioteki mediów.',
			}}
			onSelect={(media) => setAttributes({ imageID: media.id, imageUrl: media.url })}
			accept="image/*"
			allowedTypes={['image']}
			value={imageID}
			mediaPreview={mediaPreview}
			/>
		</div>
	);
}

Jak widać, zapisujemy zarówno ID obrazka jak i url. Atrybut mediaPreview naszego placeholdera ma wartość stałej mediaPreview, którą utworzyliśmy. Akceptujemy obrazy.

Teraz nasz render.php:

<?php
/**
 * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
 */
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
	<?php 
	$imageID = $attributes['imageID'];
	$imageUrl = wp_get_attachment_image_url($imageID, 'full');
	?>
    <img src="<?php echo esc_url($imageUrl); ?>" alt="Wybrany obraz" width="300" >
</div>

Korzystamy tutaj z atrybutów. Teoretycznie moglibyśmy użyć atrybutu imageUrl zamiast brać ten url z funkcji wp_get_attachmet_image_url, ale chodziło nam o zobrazowanie czegoś. Ta funkcja pozwala nam bowiem wybrać wielkość obrazka. Możemy zamienić 'full’ na 'thumbnail’ na przykład.

Oczywiście moglibyśmy to zrobić po stronie edytora.

<MediaPlaceholder
			onSelect = {
				( image ) => {
					console.log(image);
					setAttributes( { imageUrl: image.sizes.thumbnail.url, imageID: image.id } );
					}
			}

Jak możemy sobie wylogować, obiekt image ma atrybut sizes, który zawiera wszystkie rozmiary i każdy ma atrybut url. Działając w ten sposób możemy pozbyć się w ogóle potrzeby posiadania imageID, skoro mamy już url o takim rozmiarze, jaki chcemy (w render.php dostępny w tablicy asocjacyjnej $attributes).

Ten nasz dynamiczny blok wyszedł może odrobinę pokracznie, ale nie martwmy się – to dopiero początek zabaw z blokami (także dynamicznymi) zawierającymi obrazki i pisaniem bloków WordPressa w ogóle. Jeszcze się rozkręcimy.