Skip to content

Slideshow Block (3-Layer Nesting & Glide.js)

Arsitektur 3 Layer

Slideshow menggunakan tiga lapisan block yang saling bersarang:

┌─────────────────────────────────────────┐
│ Slideshow (parent terluar)              │
│  ┌───────────────────────────────────┐  │
│  │ Slide 1 (= banner block)         │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │ Generic Heading             │  │  │
│  │  │ Generic Button              │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │ Slide 2 (= banner block)         │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │ Generic Heading             │  │  │
│  │  │ Generic Button              │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
LayerBlockFungsi
1 (terluar)SlideshowContainer semua slide + navigasi dots
2 (tengah)SlideBackground image per slide (copy dari Banner)
3 (terdalam)Heading + ButtonKonten teks dan link di setiap slide

Setup di functions.php

php
// Slideshow = JSX block dengan PHP render callback
new JSXBlock("slideshow", true);

// Slide = JSX block dengan PHP render callback (copy dari banner)
new JSXBlock("slide", true);

1. Slideshow Block (Parent)

our-blocks/slideshow.js

js
import { InnerBlocks } from "@wordpress/block-editor";

wp.blocks.registerBlockType("ourBlockTheme/slideshow", {
  title: "Slideshow",
  edit: EditComponent,
  save: SaveComponent
});

function EditComponent() {
  return (
    <div style={{
      backgroundColor: "#333",
      padding: "35px"
    }}>
      <p style={{
        textAlign: "center",
        fontSize: "20px",
        color: "#fff"
      }}>
        Slideshow
      </p>
      <InnerBlocks allowedBlocks={["ourBlockTheme/slide"]} />
    </div>
  );
}

function SaveComponent() {
  return <InnerBlocks.Content />;
}

Catatan: Styling di edit component hanya untuk visual di editor — memberi background gelap agar slide terlihat bersarang di dalam slideshow.


2. Slide Block (Anak = Copy dari Banner)

our-blocks/slide.js

File ini adalah duplikat dari banner.js — hanya nama block yang berbeda:

js
// Sama persis dengan banner.js, hanya ubah:
wp.blocks.registerBlockType("ourBlockTheme/slide", {
  title: "Slide",
  // ... attributes sama (imageID, imgURL)
  // ... edit component sama (MediaUpload, InnerBlocks)
  // ... save component sama (InnerBlocks.Content)
});

Block slide sudah diprogram untuk hanya mengizinkan heading dan button di dalamnya (sama seperti banner).


3. PHP Render Callbacks

our-blocks/slideshow.php

HTML wrapper untuk Glide.js slideshow library:

php
<div class="hero-slider">
  <div data-glide-el="track" class="glide__track">
    <div class="glide__slides">
      <?php echo $content; ?>
    </div>
  </div>
  <div class="slider__bullets glide__bullets" data-glide-el="controls[nav]">
  </div>
</div>

echo $content — di sini semua slide akan di-output (nested InnerBlocks).

our-blocks/slide.php

php
<?php
if (!empty($attributes['themeimage'])) {
    $attributes['imgURL'] = get_theme_file_uri('/images/' . $attributes['themeimage']);
}

if (!isset($attributes['imgURL'])) {
    $attributes['imgURL'] = get_theme_file_uri('/images/library-hero.jpg');
}
?>

<div class="hero-slider__slide"
     style="background-image: url('<?php echo esc_url($attributes['imgURL']); ?>');">
  <div class="hero-slider__interior container">
    <div class="hero-slider__overlay t-center">
      <?php echo $content; ?>
    </div>
  </div>
</div>

Update Edit Component untuk Slide

Agar tampilan editor sama dengan frontend:

js
function EditComponent(props) {
  // ... useEffect dan imports sama

  return (
    <>
      <InspectorControls>
        {/* ... MediaUpload */}
      </InspectorControls>

      <div className="hero-slider__slide"
           style={{backgroundImage: `url('${props.attributes.imgURL}')`}}>
        <div className="hero-slider__interior container">
          <div className="hero-slider__overlay t-center">
            <InnerBlocks allowedBlocks={[
              "ourBlockTheme/genericHeading",
              "ourBlockTheme/genericButton"
            ]} />
          </div>
        </div>
      </div>
    </>
  );
}

Fix: Multiple Slideshows per Halaman

Masalah

JavaScript Glide.js awalnya hanya mencari satu instance slideshow. Jika ada lebih dari satu slideshow di satu halaman, terjadi error:

  • Jumlah dots salah (digabung)
  • Slideshow kedua tidak berfungsi

Solusi: querySelectorAll + forEach

File src/modules/HeroSlider.js:

js
class HeroSlider {
  constructor() {
    // ❌ Sebelum (hanya satu instance)
    // const el = document.querySelector('.hero-slider');
    // if (el) { ... new Glide(el).mount(); }

    // ✅ Sesudah (multiple instances)
    const allSlideshows = document.querySelectorAll(".hero-slider");
    
    allSlideshows.forEach(function(currentSlideshow) {
      // Hitung jumlah slides SPESIFIK untuk slideshow ini
      const count = currentSlideshow.querySelectorAll(".hero-slider__slide").length;
      
      if (count) {
        // Buat dots navigation
        const dotContainer = currentSlideshow.querySelector(".slider__bullets");
        for (let i = 0; i < count; i++) {
          dotContainer.insertAdjacentHTML(
            "beforeend",
            `<button class="slider__bullet glide__bullet" data-glide-dir="=${i}"></button>`
          );
        }
        
        // Initialize Glide untuk slideshow INI (bukan global)
        new Glide(currentSlideshow, {
          type: "carousel",
          perView: 1,
          autoplay: 3000
        }).mount();
      }
    });
  }
}

export default HeroSlider;

Kunci Perubahan

SebelumSesudah
document.querySelector('.hero-slider')document.querySelectorAll('.hero-slider')
Satu instanceforEach — loop semua instances
document.querySelector (child)currentSlideshow.querySelector (scoped)
new Glide('.hero-slider')new Glide(currentSlideshow)

Alur Kerja Lengkap

1. Buat JSXBlock("slideshow", true) dan JSXBlock("slide", true)
2. slideshow.js → hanya InnerBlocks (allowedBlocks: slide)
3. slide.js → copy dari banner.js (ganti nama saja)
4. slideshow.php → HTML wrapper Glide.js + echo $content
5. slide.php → div dengan background image + echo $content
6. Fix HeroSlider.js → querySelectorAll + forEach
7. Tambahkan kedua file ke package.json entry points

Package.json Update

json
{
  "scripts": {
    "start": "wp-scripts start src/index.js our-blocks/banner.js our-blocks/genericHeading.js our-blocks/genericButton.js our-blocks/slideshow.js our-blocks/slide.js"
  }
}

Kesimpulan

KonsepPenjelasan
3-layer nestingSlideshow > Slide > Heading + Button
Slide = Banner copySama persis, hanya nama berbeda
allowedBlocksSlideshow hanya izinkan Slide
Glide.jsLibrary JavaScript untuk slideshow
querySelectorAll + forEachFix untuk multiple slideshows per halaman
Scoped querySelectorcurrentSlideshow.querySelector bukan document.querySelector
isset() dan empty()Cek atribut PHP yang mungkin tidak ada