Bài 13: Cách tạo Slide block với thư viện Splide.js

Nếu bạn đã đồng hành với Nam Digital trong series từ những ngày đầu, hẳn bạn sẽ thấy Nam rất tập trung vào việc phát triển các block dựa theo đúng chuẩn của Block Editor Handbook, bởi điều này sẽ khiến quá trình xây dựng Block trở nên dễ dàng hơn rất nhiều, Block sau khi được hoàn thiện có thể được build và ghép trực tiếp vào themes của bạn để triển khai ngay.

Để thực hành quá trình đó thì hãy cùng Nam phát triển Slide Block dạng repeater (tạo mới các bản ghi) và sử dụng thư viện Splide.js – đây là thư viện Slide vô cùng nhẹ, hình như chỉ bằng 1/5 swiper thì phải.

Chúng ta sẽ cùng đi xuyên suốt vào từng bước xây dựng, cấu trúc của từng file liên quan như:

  • index.js (File thực hiện khởi tạo, đăng ký block, đồng thời nối với 2 file edit.js và save.js – bởi slide block bản chất là một static block, nên đầu save.js sẽ được định nghĩa để in vào CSDL)
  • block.json (File khai báo tên, đường dẫn, mô tả để block được đăng ký với Wordress)
  • edit.js – File JXS (React) chứa những tinh chỉnh ở đầu editor (tức là trình soạn thảo người dùng)
  • save.js – File JXS chứa giá trị block markup để in vào CSDL (Lưu file đó như thế nào?)
  • plugin-name.php – File PHP khai báo plugins (khi ném nó vào web WordPress xài), cũng như gọi các thư viện tương ứng
  • assets/… – Folder chứa thư viện splide.js
  • view.js – File Javascript khởi chạy khi tạo block

Lưu ý là Nam sử dụng hoàn toàn copilot làm người giúp việc trong trường hợp này, bởi Nam cũng không có background về developers, chỉ đơn giản Nam lắp ghép đúng thành phần mà thôi.

Index.js

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
import Edit from './edit';
import save from './save';
import metadata from './block.json';

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,

	/**
	 * @see ./save.js
	 */
	save,
} );

File này thì vẫn theo cấu trúc cơ bản thôi, index.js sẽ dùng hàm registerBlockType đọc block.json (metadata.name) và trỏ tới 2 file làm nhiệm vụ khác nhau: edit.js và save.js

Block.json

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/slide-block",
	"version": "0.1.0",
	"title": "Slide Block",
	"category": "widgets",
	"icon": "slides",
	"description": "Example block scaffolded with Create Block tool.",
	"example": {},
	"supports": {
		"html": true,
		"align": true
	},
	"attributes": {
		"slides": {
			"type": "array",
			"default": [],
			"items": {
				"type": "object",
				"properties": {
					"image": {
						"type": "string",
						"default": ""
					},
					"link": {
						"type": "string",
						"default": ""
					}
				}
			}
		}
	},
	"textdomain": "slide-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"viewScript": "file:./view.js"
}

Lưu ý là file block.json cần đăng ký view.js, bởi đây là file sẽ chứa trình điều khiển slider, thuộc tính mà Nam lựa chọn đó là đối tượng slides với 2 thuộc tính là image (string) và link (string) để làm phần chọn ảnh và gắn link cho ảnh – ví dụ như một banner thì cần có trang đích trỏ tới.

Edit.js

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';


const { MediaUpload, InspectorControls } = wp.blockEditor;
const { Button } = wp.components;
/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './editor.scss';

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {Element} Element to render.
 */
export default function Edit({ attributes, setAttributes }) {
	const { slides } = attributes;

	return (
		<div>
			<InspectorControls>
				{/* Add controls for slide settings */}
			</InspectorControls>
			{slides.map((slide, index) => (
				<div key={index}>
					<MediaUpload
						onSelect={(media) => {
							const newSlides = [...slides];
							newSlides[index].image = media.url;
							setAttributes({ slides: newSlides });
						}}
						type="image"
						value={slide.image}
						render={({ open }) => (
							<>
								<Button onClick={open}>Choose Image</Button>
								{slide.image && (
									<img
										src={slide.image}
										alt="Selected Thumbnail"
										style={{ maxWidth: '100px', marginTop: '10px' }}
									/>
								)}
							</>
						)}
					/>

					{/* Add a link input for each slide */}
					<input
						type="text"
						value={slide.link}
						onChange={(e) => {
							const newSlides = [...slides];
							newSlides[index].link = e.target.value;
							setAttributes({ slides: newSlides });
						}}
						placeholder="Slide Link"
					/>
				</div>
			))}
			<Button
				onClick={() => {
					setAttributes({ slides: [...slides, { image: '', title: '', link: '' }] });
				}}
			>
				Add New Slide
			</Button>
		</div>
	);
}

Trông dài vậy nhưng lý giải như sau

  • Gọi tới công cụ MediaUpload – để up ảnh và InspectorControls từ thư viện wp.blockEditor
  • Gọi tới công cụ tạo nút Button
  • Hàm Edit sẽ thực hiện những công việc sau:
    • Biến slides (nhiều slides) là thuộc tính
    • Chỗ InspectorControls này có vẻ thừa, có thể bỏ đi, vì mình cũng không muốn có tinh chỉnh gì nhiều ở phần này
    • slides sẽ map những slide con và với mỗi slide con sẽ có trình upload ảnh, cũng như có một phần soạn thảo để nhập liên kết
  • Khi bấm vào nút Add New Slide thì sẽ append 1 giá trị slide mới vào mảng slides

Kết quả của editor.js sẽ được một giao diện như thế này ở phía người dùng, hơi xấu tí nhưng với editor thì theo Nam cũng không cần nhìn slide được khởi tạo trông ra sao ^^!

Với file editor.js kể trên thì bạn có 1 kết quả như trên, hơi xấu nhưng khá đủ dùng

Save.js

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

export default function save({ attributes }) {
  const { slides } = attributes;

  // Generate the HTML structure for each slide.
  const slideList = slides.map((slide, index) => (
    <li className="splide__slide" key={index}>
      <a href={slide.link} target="_blank" rel="noopener noreferrer">
        <img src={slide.image} alt="Slide" />
      </a>
    </li>
  ));

  // Create the entire Splide structure.
  return (
    <div className="splide">
      <div className="splide__track">
        <ul className="splide__list">
          {slideList}
        </ul>
      </div>
    </div>
  );
}

Với save.js thì công việc cần làm là in ra cấu trúc html của splide.js

Plugin-name.php

<?php
/**
 * Plugin Name:       Slide Block
 * Description:       Example block scaffolded with Create Block tool.
 * Requires at least: 6.1
 * Requires PHP:      7.0
 * Version:           0.1.0
 * Author:            The WordPress Contributors
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       slide-block
 *
 * @package           create-block
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
// Gọi script splide.js khi gọi slide
function enqueue_splide_assets() {
    // Enqueue Splide script
    wp_enqueue_script( 'splide-script', plugin_dir_url( __FILE__ ) . 'assets/splide.min.js', array('jquery'), '2.4.0', true );

    // Enqueue Splide CSS
    wp_enqueue_style( 'splide-style', plugin_dir_url( __FILE__ ) . 'assets/splide.min.css', array(), '2.4.0' );
}
add_action( 'wp_enqueue_scripts', 'enqueue_splide_assets' );

 function slide_block_slide_block_block_init() {
	register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'slide_block_slide_block_block_init' );

Đoạn này thì mình chạy 2 hàm, 1 hàm để gọi thư viện splide.min.js và splide.min.css vào, thư mục assets cần để cùng cấp với plugin-name.php. Về thư viện thì bạn có thể tải tại trang chủ của splide.js

Assets và slide-block.php sẽ là thư mục cùng cấp

View.js

Sau khi đã có đủ

  • Trình quản lý slide (editor.js)
  • Cách thức slide được lưu vào CSDL (save.js)
  • Thư viện gói phụ thuộc (assets/…)
document.addEventListener("DOMContentLoaded",(function(){new Splide(".splide").mount()}));

Lúc này ta thực hiện gọi tới thư viện splide.js, lắng nghe một sự kiện “Khi tải hết nội dung html” thì tải Splide Slider, nhắm vào các đối tượng có class là splide (Vốn đã được khởi tạo ở save.js)