feat(gallery): tambah CPT Galeri, template page-galeri, filter interaktif, dan native lightbox zoom

This commit is contained in:
Mas Budi Kusuma
2026-09-16 07:52:18 +07:00
parent d5f2d3bbdd
commit 2dae435dff
4 changed files with 421 additions and 0 deletions
+8
View File
@@ -904,3 +904,11 @@ function edukarsa_about_page_html() {
</div>
<?php
}
// Menyuntikkan sub-menu Galeri ke dalam menu utama EduKarsa
add_action('admin_menu', function() {
// Deteksi parent slug (edukarsa_main_menu atau edukarsa_settings)
$parent = menu_page_url('edukarsa_main_menu', false) ? 'edukarsa_main_menu' : 'edukarsa_settings';
add_submenu_page( $parent, 'Semua Galeri', '📸 Galeri Fasilitas', 'manage_options', 'edit.php?post_type=galeri' );
add_submenu_page( $parent, 'Kategori Galeri', '📂 Kategori Galeri', 'manage_options', 'edit-tags.php?taxonomy=kategori_galeri&post_type=galeri' );
}, 100);
+27
View File
@@ -46,3 +46,30 @@ function edukarsa_slider_save_meta( $post_id ) {
if ( isset( $_POST['slider_link'] ) ) { update_post_meta( $post_id, '_slider_link', sanitize_text_field( $_POST['slider_link'] ) ); }
}
add_action( 'save_post', 'edukarsa_slider_save_meta' );
// --- REGISTRASI CPT GALERI & TAXONOMY ---
function edukarsa_register_galeri_cpt() {
register_post_type( 'galeri', array(
'labels' => array(
'name' => 'Galeri Fasilitas',
'singular_name' => 'Galeri',
'add_new_item' => 'Tambah Foto Galeri',
'all_items' => 'Semua Foto Galeri'
),
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
'public' => true,
'show_ui' => true,
'show_in_menu' => false,
'menu_icon' => 'dashicons-format-gallery'
));
register_taxonomy( 'kategori_galeri', 'galeri', array(
'labels' => array('name' => 'Kategori Galeri', 'singular_name' => 'Kategori', 'add_new_item' => 'Tambah Kategori'),
'hierarchical' => true,
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'show_in_menu' => false
));
}
add_action( 'init', 'edukarsa_register_galeri_cpt', 0 );
+234
View File
@@ -0,0 +1,234 @@
<?php
/**
* Template Name: Galeri & Fasilitas
* Deskripsi: Menampilkan grid foto galeri dengan filter kategori dan efek Zoom/Lightbox interaktif.
*/
get_header();
?>
<main id="primary" class="site-main edukarsa-gallery-page" style="padding: 60px 0 80px; background-color: var(--bg-light-blue); min-height: 75vh;">
<div class="container">
<!-- Header Seksi Galeri -->
<div class="section-header text-center" style="margin-bottom: 40px;">
<span class="badge-title">FASILITAS & DOKUMENTASI KAMI</span>
<h1 class="section-title">Galeri Kampus</h1>
<hr class="title-underline">
<p style="color: #64748b; max-width: 600px; margin: 0 auto; line-height: 1.7; font-size: 15px;">
Jelajahi berbagai fasilitas pendukung belajar, laboratorium, sarana olahraga, dan lingkungan inspiratif di institusi kami.
</p>
</div>
<!-- Tombol Filter Kategori -->
<div class="gallery-filters text-center" style="margin-bottom: 40px;">
<button class="gallery-filter-btn active" data-filter="all">Semua Foto</button>
<?php
$terms = get_terms( array( 'taxonomy' => 'kategori_galeri', 'hide_empty' => true ) );
foreach ( $terms as $term ) {
echo '<button class="gallery-filter-btn" data-filter="' . esc_attr( $term->slug ) . '">' . esc_html( $term->name ) . '</button>';
}
?>
</div>
<!-- Grid Foto Galeri -->
<div class="gallery-grid" id="edukarsa-gallery-grid">
<?php
$query_galeri = new WP_Query( array( 'post_type' => 'galeri', 'posts_per_page' => -1 ) );
if ( $query_galeri->have_posts() ) :
$idx = 0;
while ( $query_galeri->have_posts() ) : $query_galeri->the_post();
// Ambil kategori untuk filter class
$terms = get_the_terms( get_the_ID(), 'kategori_galeri' );
$term_slugs = '';
$term_names = '';
if ( $terms && ! is_wp_error( $terms ) ) {
$slugs = array();
$names = array();
foreach ( $terms as $term ) {
$slugs[] = $term->slug;
$names[] = $term->name;
}
$term_slugs = implode( ' ', $slugs );
$term_names = implode( ', ', $names );
}
// Ambil URL gambar dan deskripsi
$img_full = has_post_thumbnail() ? get_the_post_thumbnail_url( get_the_ID(), 'full' ) : '';
$img_thumb = has_post_thumbnail() ? get_the_post_thumbnail_url( get_the_ID(), 'large' ) : '';
// Prioritaskan Excerpt, jika kosong ambil paragraf pertama dari Content
$desc = wp_strip_all_tags( get_the_excerpt() );
if ( empty($desc) ) {
$desc = wp_trim_words( wp_strip_all_tags( get_the_content() ), 25, '...' );
}
?>
<div class="gallery-item-card <?php echo esc_attr( $term_slugs ); ?>"
data-src="<?php echo esc_url( $img_full ); ?>"
data-title="<?php echo esc_attr( get_the_title() ); ?>"
data-desc="<?php echo esc_attr( $desc ); ?>"
data-cat="<?php echo esc_attr( $term_names ); ?>">
<div class="gallery-img-wrapper">
<?php if ( $img_thumb ) : ?>
<img src="<?php echo esc_url( $img_thumb ); ?>" alt="<?php the_title(); ?>" loading="lazy">
<?php else: ?>
<div class="img-placeholder" style="height: 100%;"><i>📸</i></div>
<?php endif; ?>
<div class="gallery-overlay">
<span class="gallery-zoom-icon">🔍</span>
</div>
</div>
<div class="gallery-item-info">
<h4><?php the_title(); ?></h4>
<?php if($term_names): ?>
<span class="gallery-cat-badge"><?php echo esc_html( $term_names ); ?></span>
<?php endif; ?>
</div>
</div>
<?php
$idx++;
endwhile;
wp_reset_postdata();
else :
echo '<div style="grid-column: 1 / -1; text-align: center; padding: 40px; background: #fff; border-radius: 12px; border: 1px dashed #cbd5e1; color: #64748b;">Belum ada foto galeri yang dipublikasikan.</div>';
endif;
?>
</div>
</div>
</main>
<!-- VANILLA JS LIGHTBOX (ZOOM EFFECT OVERLAY) -->
<div id="ek-lightbox" class="ek-lightbox">
<span class="ek-lb-close" id="ek-lb-close" title="Tutup">&times;</span>
<div class="ek-lb-content">
<div class="ek-lb-img-container">
<img id="ek-lb-img" src="" alt="">
</div>
<div class="ek-lb-caption">
<span id="ek-lb-cat" class="ek-lb-cat"></span>
<h3 id="ek-lb-title"></h3>
<p id="ek-lb-desc"></p>
</div>
</div>
<button class="ek-lb-nav ek-lb-prev" id="ek-lb-prev" title="Sebelumnya">&#10094;</button>
<button class="ek-lb-nav ek-lb-next" id="ek-lb-next" title="Selanjutnya">&#10095;</button>
</div>
<!-- Logika Interaktif Native JS -->
<script>
document.addEventListener("DOMContentLoaded", function() {
// 1. LOGIKA FILTER KATEGORI (SMOOTH FADE)
const filterBtns = document.querySelectorAll('.gallery-filter-btn');
const galleryItems = document.querySelectorAll('.gallery-item-card');
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
// Hapus status aktif dari semua tombol
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const filterValue = btn.getAttribute('data-filter');
galleryItems.forEach(item => {
// Sembunyikan dengan fade out
item.style.opacity = '0';
item.style.transform = 'scale(0.95)';
setTimeout(() => {
if (filterValue === 'all' || item.classList.contains(filterValue)) {
item.style.display = 'block';
// Beri jeda sedikit agar display block dirender sebelum mengubah opacity
setTimeout(() => {
item.style.opacity = '1';
item.style.transform = 'scale(1)';
}, 50);
} else {
item.style.display = 'none';
}
}, 300); // 300ms sesuaikan dengan durasi CSS transition
});
});
});
// 2. LOGIKA LIGHTBOX / ZOOM SLIDER
const lightbox = document.getElementById('ek-lightbox');
const lbImg = document.getElementById('ek-lb-img');
const lbTitle = document.getElementById('ek-lb-title');
const lbDesc = document.getElementById('ek-lb-desc');
const lbCat = document.getElementById('ek-lb-cat');
const closeBtn = document.getElementById('ek-lb-close');
const prevBtn = document.getElementById('ek-lb-prev');
const nextBtn = document.getElementById('ek-lb-next');
let currentIndex = 0;
let visibleItems = [];
// Fungsi untuk memperbarui daftar item yang sedang tidak disembunyikan oleh filter
function updateVisibleItems() {
visibleItems = Array.from(galleryItems).filter(item => item.style.display !== 'none');
}
function openLightbox(index) {
if(visibleItems.length === 0) return;
currentIndex = index;
const item = visibleItems[currentIndex];
lbImg.src = item.getAttribute('data-src');
lbTitle.textContent = item.getAttribute('data-title');
lbDesc.textContent = item.getAttribute('data-desc');
lbCat.textContent = item.getAttribute('data-cat');
lightbox.classList.add('show');
document.body.style.overflow = 'hidden'; // Kunci scroll halaman belakang
}
function closeLightbox() {
lightbox.classList.remove('show');
document.body.style.overflow = ''; // Kembalikan scroll
setTimeout(() => lbImg.src = '', 300); // Bersihkan gambar setelah animasi tutup
}
function showNext() {
currentIndex = (currentIndex + 1) % visibleItems.length;
openLightbox(currentIndex);
}
function showPrev() {
currentIndex = (currentIndex - 1 + visibleItems.length) % visibleItems.length;
openLightbox(currentIndex);
}
// Pasang Event Klik pada setiap foto di Grid
galleryItems.forEach(item => {
item.addEventListener('click', function() {
updateVisibleItems();
const idx = visibleItems.indexOf(this);
if(idx !== -1) openLightbox(idx);
});
});
// Event Navigasi Lightbox
closeBtn.addEventListener('click', closeLightbox);
nextBtn.addEventListener('click', showNext);
prevBtn.addEventListener('click', showPrev);
// Tutup jika mengklik area kosong di luar konten
lightbox.addEventListener('click', function(e) {
if (e.target === lightbox) closeLightbox();
});
// Dukungan Navigasi Keyboard
document.addEventListener('keydown', function(e) {
if (!lightbox.classList.contains('show')) return;
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowRight') showNext();
if (e.key === 'ArrowLeft') showPrev();
});
});
</script>
<?php get_footer(); ?>
+152
View File
@@ -717,3 +717,155 @@ body.a11y-underline-links a { text-decoration: underline !important; text-underl
.sidebar-html-rendered-content { font-size: 14px; line-height: 1.7; color: var(--text-color); }
.sidebar-html-rendered-content iframe { width: 100% !important; border-radius: 8px; border: none; }
.sidebar-html-rendered-content img { border-radius: 8px; margin: 10px 0; }
/* ---------------------------------------------------------
MODUL GALERI & FASILITAS (GRID + LIGHTBOX)
--------------------------------------------------------- */
.gallery-filters { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.gallery-filter-btn {
background: #ffffff;
border: 2px solid #e2e8f0;
color: var(--text-muted);
padding: 8px 22px;
border-radius: 50px;
font-weight: 700;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
}
.gallery-filter-btn:hover, .gallery-filter-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: #ffffff;
box-shadow: 0 4px 12px rgba(11, 74, 142, 0.2);
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 25px;
}
.gallery-item-card {
background: #ffffff;
border-radius: 14px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.04);
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid #f1f5f9;
}
.gallery-item-card:hover {
transform: translateY(-6px);
box-shadow: 0 15px 35px rgba(11, 74, 142, 0.12);
border-color: var(--secondary-color);
}
.gallery-img-wrapper {
position: relative;
aspect-ratio: 4/3;
overflow: hidden;
background: #f8fafc;
}
.gallery-img-wrapper img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.gallery-item-card:hover .gallery-img-wrapper img {
transform: scale(1.08);
}
.gallery-overlay {
position: absolute; top: 0; left: 0; right: 0; bottom: 0;
background: linear-gradient(to bottom, rgba(11,74,142,0.2) 0%, rgba(11,74,142,0.8) 100%);
display: flex; align-items: center; justify-content: center;
opacity: 0; transition: opacity 0.3s ease;
}
.gallery-item-card:hover .gallery-overlay { opacity: 1; }
.gallery-zoom-icon {
font-size: 32px; color: #fff;
transform: translateY(20px);
transition: transform 0.3s ease;
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.3));
}
.gallery-item-card:hover .gallery-zoom-icon { transform: translateY(0); }
.gallery-item-info { padding: 22px 20px; }
.gallery-item-info h4 { margin: 0 0 10px; font-size: 17px; font-weight: 800; color: var(--heading-color); line-height: 1.4; }
.gallery-cat-badge {
display: inline-block;
font-size: 11px; font-weight: 800;
color: var(--secondary-color);
text-transform: uppercase;
letter-spacing: 0.5px;
background: #e0f2fe; padding: 4px 12px; border-radius: 20px;
}
/* NATIVE LIGHTBOX (ZOOM EFEK) */
.ek-lightbox {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(15, 23, 42, 0.96); z-index: 9999;
display: flex; align-items: center; justify-content: center;
opacity: 0; visibility: hidden; transition: all 0.3s ease;
backdrop-filter: blur(8px);
}
.ek-lightbox.show { opacity: 1; visibility: visible; }
.ek-lb-close {
position: absolute; top: 25px; right: 35px; font-size: 45px;
color: rgba(255,255,255,0.6); cursor: pointer; line-height: 1;
transition: color 0.2s, transform 0.2s; z-index: 10;
}
.ek-lb-close:hover { color: #f59e0b; transform: scale(1.1); }
.ek-lb-content {
max-width: 900px; width: 92%; max-height: 90vh;
display: flex; flex-direction: column;
background: #ffffff; border-radius: 16px; overflow: hidden;
transform: scale(0.95); transition: transform 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
}
.ek-lightbox.show .ek-lb-content { transform: scale(1); }
.ek-lb-img-container {
width: 100%; height: 60vh; background: #000;
display: flex; align-items: center; justify-content: center;
}
.ek-lb-img-container img {
width: 100%; height: 100%; object-fit: contain;
}
.ek-lb-caption { padding: 25px 35px; text-align: center; }
.ek-lb-cat {
font-size: 12px; font-weight: 800; color: var(--secondary-color);
text-transform: uppercase; letter-spacing: 1px; display: inline-block;
margin-bottom: 8px; background: #f1f5f9; padding: 4px 14px; border-radius: 20px;
}
.ek-lb-caption h3 { margin: 0 0 12px; font-size: 24px; font-weight: 800; color: var(--heading-color); }
.ek-lb-caption p { margin: 0; font-size: 15px; color: var(--text-color); line-height: 1.7; }
/* Tombol Navigasi Lightbox */
.ek-lb-nav {
position: absolute; top: 50%; transform: translateY(-50%);
background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2);
color: #ffffff; width: 56px; height: 56px; border-radius: 50%;
font-size: 20px; cursor: pointer; transition: all 0.2s ease;
display: flex; align-items: center; justify-content: center;
}
.ek-lb-nav:hover { background: var(--primary-color); border-color: var(--primary-color); transform: translateY(-50%) scale(1.1); }
.ek-lb-prev { left: 40px; }
.ek-lb-next { right: 40px; }
@media (max-width: 768px) {
.ek-lb-nav { width: 44px; height: 44px; font-size: 16px; }
.ek-lb-prev { left: 15px; }
.ek-lb-next { right: 15px; }
.ek-lb-close { top: 15px; right: 20px; font-size: 35px; }
.ek-lb-img-container { height: 45vh; }
.ek-lb-caption { padding: 20px; }
.ek-lb-caption h3 { font-size: 20px; }
.gallery-grid { grid-template-columns: 1fr; }
}