feat: Add responsive layout overrides and slider settings with Swiper integration.

This commit is contained in:
2026-01-25 20:49:05 +01:00
parent b7a5eefa7d
commit 7db066a23b
10 changed files with 680 additions and 177 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
admin/.DS_Store vendored

Binary file not shown.

View File

@@ -6,18 +6,18 @@
* @package Instagram_Gallery_Sync_Pro
*/
(function($) {
(function ($) {
'use strict';
/**
* Admin Controller
*/
const IGSPAdmin = {
/**
* Initialize
*/
init: function() {
init: function () {
this.initColorPickers();
this.initSliders();
this.initLayoutSelector();
@@ -30,9 +30,9 @@
/**
* Initialize color pickers
*/
initColorPickers: function() {
initColorPickers: function () {
$('.igsp-color-picker').wpColorPicker({
change: function(event, ui) {
change: function (event, ui) {
// Update live preview
IGSPAdmin.updatePreview();
}
@@ -42,26 +42,29 @@
/**
* Initialize range sliders
*/
initSliders: function() {
$('input[type="range"]').on('input change', function() {
initSliders: function () {
$('input[type="range"]').on('input change', function () {
const $slider = $(this);
const $value = $slider.siblings('.igsp-slider-value');
let value = $slider.val();
// Handle special cases
const id = $slider.attr('id');
if (id === 'igsp_auto_delete_days') {
value = value == 0 ? igspAdmin.strings.never || 'Never' : value + ' days';
} else if (id === 'igsp_spacing' || id === 'igsp_padding' ||
id === 'igsp_border_radius' || id === 'igsp_caption_font_size') {
} else if (id === 'igsp_spacing' || id === 'igsp_padding' ||
id === 'igsp_border_radius' || id === 'igsp_caption_font_size') {
value += 'px';
} else if (id === 'igsp_request_timeout') {
value += 's';
} else if (id === 'igsp_slider_delay') {
// Convert milliseconds to seconds for display
value = (parseInt(value) / 1000) + 's';
}
$value.text(value);
// Update live preview if applicable
if (id === 'igsp_caption_font_size') {
IGSPAdmin.updatePreview();
@@ -72,8 +75,8 @@
/**
* Initialize layout selector
*/
initLayoutSelector: function() {
$('.igsp-layout-option input').on('change', function() {
initLayoutSelector: function () {
$('.igsp-layout-option input').on('change', function () {
$('.igsp-layout-option').removeClass('selected');
$(this).closest('.igsp-layout-option').addClass('selected');
});
@@ -82,26 +85,26 @@
/**
* Initialize conditional fields
*/
initConditionalFields: function() {
$('[data-depends-on]').each(function() {
initConditionalFields: function () {
$('[data-depends-on]').each(function () {
const $field = $(this);
const dependsOn = $field.data('depends-on');
const dependsValue = $field.data('depends-value');
function checkVisibility() {
const $input = $('[name="' + dependsOn + '"]:checked, [name="' + dependsOn + '"]').filter(':checked, select');
const currentValue = $input.val();
if (currentValue === dependsValue) {
$field.show();
} else {
$field.hide();
}
}
// Initial check
checkVisibility();
// Listen for changes
$('[name="' + dependsOn + '"]').on('change', checkVisibility);
});
@@ -110,29 +113,29 @@
/**
* Initialize sync button
*/
initSyncButton: function() {
$('#igsp-sync-now').on('click', function() {
initSyncButton: function () {
$('#igsp-sync-now').on('click', function () {
const $button = $(this);
const $progress = $('#igsp-sync-progress');
const $result = $('#igsp-sync-result');
// Disable button
$button.prop('disabled', true);
$progress.show();
$result.hide();
// Animate progress bar
const $fill = $progress.find('.igsp-progress-fill');
$fill.css('width', '0%');
// Simulate progress
let progress = 0;
const progressInterval = setInterval(function() {
const progressInterval = setInterval(function () {
progress += Math.random() * 15;
if (progress > 90) progress = 90;
$fill.css('width', progress + '%');
}, 500);
// Make AJAX request
$.ajax({
url: igspAdmin.ajaxUrl,
@@ -141,25 +144,25 @@
action: 'igsp_manual_sync',
nonce: igspAdmin.nonce
},
success: function(response) {
success: function (response) {
clearInterval(progressInterval);
$fill.css('width', '100%');
setTimeout(function() {
setTimeout(function () {
$progress.hide();
$result.removeClass('success error');
if (response.success) {
$result.addClass('success').html(response.data.message).show();
IGSPAdmin.updateSyncStatus();
} else {
$result.addClass('error').html(response.data.message || igspAdmin.strings.syncError).show();
}
$button.prop('disabled', false);
}, 500);
},
error: function() {
error: function () {
clearInterval(progressInterval);
$progress.hide();
$result.addClass('error').html(igspAdmin.strings.syncError).show();
@@ -172,57 +175,57 @@
/**
* Initialize tool buttons
*/
initToolButtons: function() {
initToolButtons: function () {
// Clear cache
$('#igsp-clear-cache').on('click', function() {
$('#igsp-clear-cache').on('click', function () {
const $button = $(this);
$button.prop('disabled', true).text(igspAdmin.strings.processing);
$.post(igspAdmin.ajaxUrl, {
action: 'igsp_clear_cache',
nonce: igspAdmin.nonce
}, function(response) {
}, function (response) {
if (response.success) {
$button.text(igspAdmin.strings.done);
setTimeout(function() {
setTimeout(function () {
$button.prop('disabled', false).html('<span class="dashicons dashicons-trash"></span> Clear Cache');
}, 2000);
}
});
});
// Reset data
$('#igsp-reset-data').on('click', function() {
$('#igsp-reset-data').on('click', function () {
if (!confirm(igspAdmin.strings.confirmReset)) {
return;
}
const $button = $(this);
$button.prop('disabled', true).text(igspAdmin.strings.processing);
$.post(igspAdmin.ajaxUrl, {
action: 'igsp_reset_data',
nonce: igspAdmin.nonce
}, function(response) {
}, function (response) {
if (response.success) {
location.reload();
}
});
});
// Clear logs
$('#igsp-clear-logs').on('click', function() {
$('#igsp-clear-logs').on('click', function () {
if (!confirm(igspAdmin.strings.confirmClearLogs)) {
return;
}
const $button = $(this);
$button.prop('disabled', true);
$.post(igspAdmin.ajaxUrl, {
action: 'igsp_clear_logs',
nonce: igspAdmin.nonce
}, function(response) {
}, function (response) {
if (response.success) {
location.reload();
}
@@ -233,11 +236,11 @@
/**
* Update sync status
*/
updateSyncStatus: function() {
updateSyncStatus: function () {
$.post(igspAdmin.ajaxUrl, {
action: 'igsp_get_sync_status',
nonce: igspAdmin.nonce
}, function(response) {
}, function (response) {
if (response.success) {
$('#igsp-total-posts').text(response.data.total_posts);
$('#igsp-last-sync').text(response.data.last_sync);
@@ -249,27 +252,27 @@
/**
* Update live preview
*/
updatePreview: function() {
updatePreview: function () {
const primaryColor = $('#igsp_primary_color').val() || '#e1306c';
const hoverColor = $('#igsp_hover_color').val() || '#c13584';
const textColor = $('#igsp_text_color').val() || '#ffffff';
const fontSize = $('#igsp_caption_font_size').val() || 14;
$('.igsp-preview-item').css({
'--primary': primaryColor,
'--hover': hoverColor,
'--text': textColor
});
$('.igsp-preview-caption').css('font-size', fontSize + 'px');
},
/**
* Tab persistence
*/
initTabPersistence: function() {
initTabPersistence: function () {
// Store current tab in localStorage
$('.igsp-tab-link').on('click', function() {
$('.igsp-tab-link').on('click', function () {
const tab = $(this).attr('href').split('tab=')[1];
if (tab) {
localStorage.setItem('igsp_active_tab', tab);
@@ -279,7 +282,7 @@
};
// Initialize when document is ready
$(document).ready(function() {
$(document).ready(function () {
IGSPAdmin.init();
});

View File

@@ -116,6 +116,21 @@ if (!defined('ABSPATH')) {
</fieldset>
</td>
</tr>
<tr class="igsp-conditional" data-depends-on="igsp_show_instagram_btn" data-depends-value="yes">
<th scope="row">
<label
for="igsp_instagram_btn_text"><?php esc_html_e('Button Text', 'instagram-gallery-sync-pro'); ?></label>
</th>
<td>
<input type="text" id="igsp_instagram_btn_text" name="igsp_instagram_btn_text"
value="<?php echo esc_attr(get_option('igsp_instagram_btn_text', 'Follow on Instagram')); ?>"
class="regular-text" placeholder="Follow on Instagram">
<p class="description">
<?php esc_html_e('Text displayed on the button below the gallery.', 'instagram-gallery-sync-pro'); ?>
</p>
</td>
</tr>
</table>
</div>

View File

@@ -80,6 +80,132 @@ if (!defined('ABSPATH')) {
</span>
</label>
</div>
<h3 style="margin-top: 30px;">
<?php esc_html_e('Responsive Layout Override', 'instagram-gallery-sync-pro'); ?>
</h3>
<p class="description" style="margin-bottom: 15px;">
<?php esc_html_e('Optionally use a different layout type on smaller screens.', 'instagram-gallery-sync-pro'); ?>
</p>
<table class="form-table">
<tr>
<th scope="row">
<label for="igsp_layout_type_tablet">
<?php esc_html_e('Layout on Tablet', 'instagram-gallery-sync-pro'); ?>
</label>
</th>
<td>
<?php $layout_tablet = get_option('igsp_layout_type_tablet', 'same'); ?>
<select id="igsp_layout_type_tablet" name="igsp_layout_type_tablet">
<option value="same" <?php selected($layout_tablet, 'same'); ?>>
<?php esc_html_e('Same as Desktop', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="grid" <?php selected($layout_tablet, 'grid'); ?>>
<?php esc_html_e('Grid', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="masonry" <?php selected($layout_tablet, 'masonry'); ?>>
<?php esc_html_e('Masonry', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="slider" <?php selected($layout_tablet, 'slider'); ?>>
<?php esc_html_e('Slider', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="list" <?php selected($layout_tablet, 'list'); ?>>
<?php esc_html_e('List', 'instagram-gallery-sync-pro'); ?>
</option>
</select>
<p class="description"><?php esc_html_e('768px - 1023px', 'instagram-gallery-sync-pro'); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igsp_layout_type_mobile">
<?php esc_html_e('Layout on Mobile', 'instagram-gallery-sync-pro'); ?>
</label>
</th>
<td>
<?php $layout_mobile = get_option('igsp_layout_type_mobile', 'same'); ?>
<select id="igsp_layout_type_mobile" name="igsp_layout_type_mobile">
<option value="same" <?php selected($layout_mobile, 'same'); ?>>
<?php esc_html_e('Same as Desktop/Tablet', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="grid" <?php selected($layout_mobile, 'grid'); ?>>
<?php esc_html_e('Grid', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="slider" <?php selected($layout_mobile, 'slider'); ?>>
<?php esc_html_e('Slider', 'instagram-gallery-sync-pro'); ?>
</option>
<option value="list" <?php selected($layout_mobile, 'list'); ?>>
<?php esc_html_e('List', 'instagram-gallery-sync-pro'); ?>
</option>
</select>
<p class="description"><?php esc_html_e('< 767px', 'instagram-gallery-sync-pro'); ?></p>
</td>
</tr>
</table>
</div>
<div class="igsp-section">
<h2>
<?php esc_html_e('Slider Settings', 'instagram-gallery-sync-pro'); ?>
</h2>
<table class="form-table">
<tr>
<th scope="row">
<label for="igsp_slider_autoplay">
<?php esc_html_e('Auto-Rotate', 'instagram-gallery-sync-pro'); ?>
</label>
</th>
<td>
<?php $slider_autoplay = get_option('igsp_slider_autoplay', 'yes'); ?>
<label class="igsp-toggle">
<input type="checkbox" id="igsp_slider_autoplay" name="igsp_slider_autoplay" value="yes" <?php checked($slider_autoplay, 'yes'); ?>>
<span class="igsp-toggle-slider"></span>
</label>
<p class="description">
<?php esc_html_e('Automatically rotate slides', 'instagram-gallery-sync-pro'); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igsp_slider_delay">
<?php esc_html_e('Rotation Delay', 'instagram-gallery-sync-pro'); ?>
</label>
</th>
<td>
<?php $slider_delay = get_option('igsp_slider_delay', 4000); ?>
<div class="igsp-slider-wrapper">
<input type="range" id="igsp_slider_delay" name="igsp_slider_delay" min="1000" max="10000"
step="500" value="<?php echo esc_attr($slider_delay); ?>">
<span class="igsp-slider-value"><?php echo esc_html($slider_delay / 1000); ?>s</span>
</div>
<p class="description">
<?php esc_html_e('Time between slides in seconds (1-10)', 'instagram-gallery-sync-pro'); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igsp_slider_pause_hover">
<?php esc_html_e('Pause on Hover', 'instagram-gallery-sync-pro'); ?>
</label>
</th>
<td>
<?php $pause_hover = get_option('igsp_slider_pause_hover', 'yes'); ?>
<label class="igsp-toggle">
<input type="checkbox" id="igsp_slider_pause_hover" name="igsp_slider_pause_hover" value="yes"
<?php checked($pause_hover, 'yes'); ?>>
<span class="igsp-toggle-slider"></span>
</label>
<p class="description">
<?php esc_html_e('Pause autoplay when mouse hovers over slider', 'instagram-gallery-sync-pro'); ?>
</p>
</td>
</tr>
</table>
</div>
<div class="igsp-section">

View File

@@ -86,6 +86,8 @@ class IGSP_Admin
// Layout settings
register_setting('igsp_layout_settings', 'igsp_layout_type', 'sanitize_text_field');
register_setting('igsp_layout_settings', 'igsp_layout_type_tablet', 'sanitize_text_field');
register_setting('igsp_layout_settings', 'igsp_layout_type_mobile', 'sanitize_text_field');
register_setting('igsp_layout_settings', 'igsp_columns_desktop', 'absint');
register_setting('igsp_layout_settings', 'igsp_columns_tablet', 'absint');
register_setting('igsp_layout_settings', 'igsp_columns_mobile', 'absint');
@@ -96,12 +98,18 @@ class IGSP_Admin
register_setting('igsp_layout_settings', 'igsp_border_radius', 'absint');
register_setting('igsp_layout_settings', 'igsp_hover_effect', 'sanitize_text_field');
// Slider settings
register_setting('igsp_layout_settings', 'igsp_slider_autoplay', 'sanitize_text_field');
register_setting('igsp_layout_settings', 'igsp_slider_delay', 'absint');
register_setting('igsp_layout_settings', 'igsp_slider_pause_hover', 'sanitize_text_field');
// Display settings
register_setting('igsp_display_settings', 'igsp_display_limit', 'absint');
register_setting('igsp_display_settings', 'igsp_order', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_show_caption', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_caption_position', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_show_instagram_btn', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_instagram_btn_text', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_link_behavior', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_lightbox', 'sanitize_text_field');
register_setting('igsp_display_settings', 'igsp_lazy_loading', 'sanitize_text_field');

View File

@@ -95,10 +95,18 @@ class IGSP_Shortcode
wp_register_style(
'igsp-gallery-slider',
IGSP_PLUGIN_URL . 'public/css/gallery-slider.css',
array('igsp-gallery-base'),
array('igsp-gallery-base', 'igsp-swiper-css'),
IGSP_VERSION
);
// Swiper CSS (required for slider)
wp_register_style(
'igsp-swiper-css',
IGSP_PLUGIN_URL . 'public/css/swiper.min.css',
array(),
'11.0.5'
);
// Lightbox
wp_register_style(
'igsp-lightbox',
@@ -162,35 +170,41 @@ class IGSP_Shortcode
// Sanitize attributes
$atts = $this->sanitize_atts($atts);
// Check cache
// Check cache for gallery content (without dynamic elements like button)
$cache_key = 'igsp_gallery_' . md5(serialize($atts));
$cache_duration = (int) get_option('igsp_cache_duration', 3600);
$cached = get_transient($cache_key);
if ($cached !== false && !is_admin()) {
$this->enqueue_assets($atts);
return $cached;
}
$gallery_output = get_transient($cache_key);
// Get posts from database
$database = new IGSP_Database();
$posts = $database->get_posts(array(
'limit' => $atts['limit'],
'order' => $atts['order'],
));
if ($gallery_output === false || is_admin()) {
// Get posts from database
$database = new IGSP_Database();
$posts = $database->get_posts(array(
'limit' => $atts['limit'],
'order' => $atts['order'],
));
if (empty($posts)) {
return $this->render_empty_state();
if (empty($posts)) {
return $this->render_empty_state();
}
// Render gallery (without button - that's rendered separately)
$gallery_output = $this->render_gallery($posts, $atts);
// Cache gallery output
set_transient($cache_key, $gallery_output, $cache_duration);
}
// Enqueue assets
$this->enqueue_assets($atts);
// Render gallery
$output = $this->render_gallery($posts, $atts);
// Build final output with non-cached elements
$output = $gallery_output;
// Cache output
set_transient($cache_key, $output, $cache_duration);
// Add Instagram button (not cached, so it reflects current settings)
if (get_option('igsp_show_instagram_btn', 'yes') === 'yes') {
$output .= $this->render_instagram_button();
}
return $output;
}
@@ -229,10 +243,14 @@ class IGSP_Shortcode
*/
private function enqueue_assets($atts)
{
// Get responsive layout settings
$layout_tablet = get_option('igsp_layout_type_tablet', 'same');
$layout_mobile = get_option('igsp_layout_type_mobile', 'same');
// Base CSS
wp_enqueue_style('igsp-gallery-base');
// Layout-specific CSS
// Main layout-specific CSS
switch ($atts['layout']) {
case 'masonry':
wp_enqueue_style('igsp-gallery-masonry');
@@ -247,6 +265,19 @@ class IGSP_Shortcode
wp_enqueue_style('igsp-gallery-grid');
}
// Also load slider assets if slider is set for tablet or mobile
if ($layout_tablet === 'slider' || $layout_mobile === 'slider') {
wp_enqueue_style('igsp-gallery-slider');
wp_enqueue_script('igsp-swiper');
}
// Also load masonry assets if masonry is set for tablet
if ($layout_tablet === 'masonry') {
wp_enqueue_style('igsp-gallery-masonry');
wp_enqueue_script('igsp-masonry');
wp_enqueue_script('igsp-imagesloaded');
}
// Lightbox
if ($atts['lightbox']) {
wp_enqueue_style('igsp-lightbox');
@@ -257,10 +288,23 @@ class IGSP_Shortcode
wp_enqueue_script('igsp-gallery-frontend');
// Pass settings to JS
$layout_tablet = get_option('igsp_layout_type_tablet', 'same');
$layout_mobile = get_option('igsp_layout_type_mobile', 'same');
wp_localize_script('igsp-gallery-frontend', 'igspFrontend', array(
'lightbox' => $atts['lightbox'],
'layout' => $atts['layout'],
'layoutTablet' => $layout_tablet === 'same' ? $atts['layout'] : $layout_tablet,
'layoutMobile' => $layout_mobile === 'same' ? ($layout_tablet === 'same' ? $atts['layout'] : $layout_tablet) : $layout_mobile,
'autoplay' => $atts['autoplay'],
'columns' => $atts['columns'],
'columnsTablet' => get_option('igsp_columns_tablet', 2),
'columnsMobile' => get_option('igsp_columns_mobile', 1),
'spacing' => $atts['spacing'],
// Slider settings
'sliderAutoplay' => get_option('igsp_slider_autoplay', 'yes') === 'yes',
'sliderDelay' => (int) get_option('igsp_slider_delay', 4000),
'sliderPauseHover' => get_option('igsp_slider_pause_hover', 'yes') === 'yes',
));
// Add inline custom CSS
@@ -356,11 +400,6 @@ class IGSP_Shortcode
<?php endif; ?>
</div>
<?php if (get_option('igsp_show_instagram_btn', 'yes') === 'yes'): ?>
<?php echo $this->render_instagram_button(); ?>
<?php endif; ?>
<?php
return ob_get_clean();
}
@@ -430,20 +469,15 @@ class IGSP_Shortcode
?>
<div class="<?php echo esc_attr($item_class); ?>">
<?php if (!empty($link_url)): ?>
<a href="<?php echo esc_url($link_url); ?>" class="<?php echo esc_attr($prefix . '-link'); ?>" <?php echo $link_target; ?>
<?php echo $lightbox_attr; ?>
title="
<a href="<?php echo esc_url($link_url); ?>" class="<?php echo esc_attr($prefix . '-link'); ?>" <?php echo $link_target; ?> <?php echo $lightbox_attr; ?> title="
<?php echo esc_attr($caption); ?>">
<?php endif; ?>
<div class="<?php echo esc_attr($prefix . '-image-wrapper'); ?>">
<img src="<?php echo $lazy_loading ? '' : esc_url($display_url); ?>" <?php echo $lazy_loading ? 'data-src="' . esc_url($display_url) . '"' : ''; ?>
class="
<img src="<?php echo $lazy_loading ? '' : esc_url($display_url); ?>" <?php echo $lazy_loading ? 'data-src="' . esc_url($display_url) . '"' : ''; ?> class="
<?php echo esc_attr($prefix . '-image'); ?>
<?php echo $lazy_loading ? 'lazyload' : ''; ?>"
alt="
<?php echo esc_attr($caption); ?>"
<?php echo $lazy_loading ? 'loading="lazy"' : ''; ?>>
<?php echo $lazy_loading ? 'lazyload' : ''; ?>" alt="
<?php echo esc_attr($caption); ?>" <?php echo $lazy_loading ? 'loading="lazy"' : ''; ?>>
<?php if ($caption_position === 'overlay' && $atts['captions'] && !empty($caption)): ?>
<div class="<?php echo esc_attr($prefix . '-overlay'); ?>">
@@ -487,6 +521,7 @@ class IGSP_Shortcode
{
$prefix = get_option('igsp_css_prefix', 'igsp');
$username = get_option('igsp_username', '');
$button_text = get_option('igsp_instagram_btn_text', __('Follow on Instagram', 'instagram-gallery-sync-pro'));
if (empty($username)) {
return '';
@@ -501,7 +536,7 @@ class IGSP_Shortcode
<path
d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z" />
</svg>
<?php esc_html_e('Follow on Instagram', 'instagram-gallery-sync-pro'); ?>
<?php echo esc_html($button_text); ?>
</a>
</div>
<?php

View File

@@ -9,23 +9,52 @@
.igsp-slider {
position: relative;
overflow: hidden;
width: 100%;
display: block !important;
/* Override grid display */
}
.igsp-slider .swiper {
padding-bottom: 40px;
padding: 0 0 50px 0;
overflow: visible;
}
.igsp-slider .swiper-wrapper {
align-items: stretch;
}
.igsp-slider .swiper-slide {
width: auto;
height: auto;
display: flex;
box-sizing: border-box;
}
/* When items are wrapped dynamically, ensure proper sizing */
.igsp-slider .swiper-slide.igsp-item {
width: 100% !important;
height: auto !important;
display: block;
}
.igsp-slider .swiper-slide .igsp-item,
.igsp-slider .swiper-slide.igsp-item {
width: 100%;
height: auto;
}
.igsp-slider .swiper-slide .igsp-image-wrapper {
width: 300px;
height: 300px;
.igsp-slider .igsp-image-wrapper {
width: 100%;
padding-bottom: 100%;
/* 1:1 aspect ratio */
position: relative;
overflow: hidden;
min-height: 200px;
}
.igsp-slider .swiper-slide .igsp-image {
.igsp-slider .igsp-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
@@ -36,22 +65,32 @@
.igsp-slider .swiper-button-next {
width: 44px;
height: 44px;
background: rgba(255, 255, 255, 0.9);
background: rgba(255, 255, 255, 0.95);
border-radius: 50%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
top: calc(50% - 25px);
}
.igsp-slider .swiper-button-prev::after,
.igsp-slider .swiper-button-next::after {
font-size: 18px;
font-size: 16px;
font-weight: bold;
color: #333;
}
.igsp-slider .swiper-button-prev {
left: 10px;
}
.igsp-slider .swiper-button-next {
right: 10px;
}
.igsp-slider .swiper-button-prev:hover,
.igsp-slider .swiper-button-next:hover {
background: var(--igsp-primary);
background: var(--igsp-primary, #E1306C);
transform: scale(1.1);
}
.igsp-slider .swiper-button-prev:hover::after,
@@ -61,29 +100,57 @@
.igsp-slider .swiper-button-disabled {
opacity: 0.3;
pointer-events: none;
}
/* Pagination */
.igsp-slider .swiper-pagination {
bottom: 0;
bottom: 10px;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.igsp-slider .swiper-pagination-bullet {
width: 10px;
height: 10px;
background: #ccc;
background: rgba(0, 0, 0, 0.2);
opacity: 1;
transition: all 0.3s ease;
margin: 0 5px;
}
.igsp-slider .swiper-pagination-bullet-active {
background: var(--igsp-primary);
background: var(--igsp-primary, #E1306C);
transform: scale(1.2);
}
/* Dynamic bullets */
.igsp-slider .swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
transform: scale(0.66);
}
.igsp-slider .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main {
transform: scale(1);
}
/* Cards style slides */
.igsp-slider .igsp-item {
background: #fff;
border-radius: var(--igsp-radius, 8px);
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.igsp-slider .igsp-item:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
/* Centered slides option */
.igsp-slider.centered .swiper-slide {
opacity: 0.5;
opacity: 0.6;
transform: scale(0.9);
transition: all 0.3s ease;
}
@@ -95,18 +162,22 @@
/* Responsive */
@media (max-width: 1024px) {
.igsp-slider .swiper-slide .igsp-image-wrapper {
width: 250px;
height: 250px;
.igsp-slider .swiper-button-prev,
.igsp-slider .swiper-button-next {
width: 40px;
height: 40px;
}
.igsp-slider .swiper-button-prev::after,
.igsp-slider .swiper-button-next::after {
font-size: 14px;
}
}
@media (max-width: 767px) {
.igsp-slider .swiper-slide .igsp-image-wrapper {
width: 100%;
max-width: 300px;
height: auto;
padding-bottom: 100%;
.igsp-slider .swiper {
padding-bottom: 40px;
}
.igsp-slider .swiper-button-prev,
@@ -117,15 +188,66 @@
.igsp-slider .swiper-button-prev::after,
.igsp-slider .swiper-button-next::after {
font-size: 14px;
font-size: 12px;
}
.igsp-slider .swiper-button-prev {
left: 5px;
}
.igsp-slider .swiper-button-next {
right: 5px;
}
.igsp-slider .swiper-pagination-bullet {
width: 8px;
height: 8px;
}
}
/* Hide navigation on touch devices */
@media (hover: none) {
/* Hide navigation on touch devices (optional, keeps them visible by default) */
@media (pointer: coarse) {
.igsp-slider .swiper-button-prev,
.igsp-slider .swiper-button-next {
display: none;
opacity: 0.7;
}
.igsp-slider:hover .swiper-button-prev,
.igsp-slider:hover .swiper-button-next {
opacity: 1;
}
}
/* Loading state */
.igsp-slider:not(.loaded) {
min-height: 200px;
}
.igsp-slider:not(.loaded)::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 40px;
height: 40px;
margin: -20px 0 0 -20px;
border: 3px solid #f3f3f3;
border-top: 3px solid var(--igsp-primary, #E1306C);
border-radius: 50%;
animation: igsp-spin 1s linear infinite;
}
@keyframes igsp-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.igsp-slider.loaded::after {
display: none;
}

13
public/css/swiper.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
/**
* Instagram Gallery Sync Pro - Frontend Script
*
* Handles gallery initialization, masonry, lightbox, and lazy loading
* Handles gallery initialization, masonry, lightbox, slider and responsive layouts
*
* @package Instagram_Gallery_Sync_Pro
*/
@@ -14,6 +14,11 @@
*/
const IGSPGallery = {
// Current viewport layout
currentLayout: null,
swiperInstance: null,
masonryInstance: null,
/**
* Initialize all galleries on page
*/
@@ -23,6 +28,95 @@
// Wait for DOM ready
$(document).ready(function () {
self.initGalleries();
self.setupResponsiveHandling();
});
},
/**
* Get current viewport type
*/
getViewport: function () {
const width = window.innerWidth;
if (width < 768) return 'mobile';
if (width < 1024) return 'tablet';
return 'desktop';
},
/**
* Get layout for current viewport
*/
getLayoutForViewport: function () {
if (typeof igspFrontend === 'undefined') return 'grid';
const viewport = this.getViewport();
switch (viewport) {
case 'mobile':
return igspFrontend.layoutMobile || igspFrontend.layout;
case 'tablet':
return igspFrontend.layoutTablet || igspFrontend.layout;
default:
return igspFrontend.layout;
}
},
/**
* Setup responsive handling
*/
setupResponsiveHandling: function () {
const self = this;
let resizeTimeout;
$(window).on('resize', function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function () {
self.handleResize();
}, 250);
});
},
/**
* Handle resize events for responsive layouts
*/
handleResize: function () {
const newLayout = this.getLayoutForViewport();
// Only reinitialize if layout type changed
if (newLayout !== this.currentLayout) {
this.reinitializeGalleries(newLayout);
} else if (this.masonryInstance) {
// Just re-layout masonry if needed
this.masonryInstance.layout();
}
},
/**
* Reinitialize galleries with new layout
*/
reinitializeGalleries: function (newLayout) {
const self = this;
// Destroy existing instances
if (this.swiperInstance) {
this.swiperInstance.destroy(true, true);
this.swiperInstance = null;
}
if (this.masonryInstance) {
this.masonryInstance.destroy();
this.masonryInstance = null;
}
this.currentLayout = newLayout;
// Reinitialize with new layout
$('.igsp-gallery').each(function () {
const $gallery = $(this);
// Update layout classes
$gallery.removeClass('igsp-grid igsp-masonry igsp-slider igsp-list');
$gallery.addClass('igsp-' + newLayout);
$gallery.attr('data-layout', newLayout);
self.initGalleryLayout($gallery, newLayout);
});
},
@@ -34,23 +128,24 @@
$('.igsp-gallery').each(function () {
const $gallery = $(this);
const layout = $gallery.data('layout');
const serverLayout = $gallery.data('layout') || 'grid';
const responsiveLayout = self.getLayoutForViewport();
self.currentLayout = responsiveLayout;
// Set responsive CSS variables
self.setResponsiveVariables($gallery);
// Initialize based on layout
switch (layout) {
case 'masonry':
self.initMasonry($gallery);
break;
case 'slider':
self.initSlider($gallery);
break;
default:
self.initGrid($gallery);
// If responsive layout differs from server layout, update classes
if (responsiveLayout !== serverLayout) {
$gallery.removeClass('igsp-grid igsp-masonry igsp-slider igsp-list igsp-justified');
$gallery.addClass('igsp-' + responsiveLayout);
$gallery.attr('data-layout', responsiveLayout);
}
// Initialize based on viewport layout
self.initGalleryLayout($gallery, responsiveLayout);
// Initialize lightbox if enabled
if (typeof igspFrontend !== 'undefined' && igspFrontend.lightbox) {
self.initLightbox($gallery);
@@ -61,12 +156,32 @@
});
},
/**
* Initialize gallery with specific layout
*/
initGalleryLayout: function ($gallery, layout) {
switch (layout) {
case 'masonry':
this.initMasonry($gallery);
break;
case 'slider':
this.initSlider($gallery);
break;
case 'list':
this.initList($gallery);
break;
default:
this.initGrid($gallery);
}
},
/**
* Set responsive CSS variables
*/
setResponsiveVariables: function ($gallery) {
const columnsTablet = $gallery.data('columns-tablet') || 2;
const columnsMobile = $gallery.data('columns-mobile') || 1;
const settings = typeof igspFrontend !== 'undefined' ? igspFrontend : {};
const columnsTablet = settings.columnsTablet || $gallery.data('columns-tablet') || 2;
const columnsMobile = settings.columnsMobile || $gallery.data('columns-mobile') || 1;
$gallery.css({
'--columns-tablet': columnsTablet,
@@ -81,10 +196,19 @@
$gallery.addClass('loaded');
},
/**
* Initialize list layout
*/
initList: function ($gallery) {
$gallery.addClass('loaded');
},
/**
* Initialize masonry layout
*/
initMasonry: function ($gallery) {
const self = this;
if (typeof Masonry === 'undefined' || typeof imagesLoaded === 'undefined') {
// Fallback to CSS-only masonry
$gallery.addClass('no-js loaded');
@@ -101,7 +225,7 @@
// Calculate item width
const columnWidth = Math.floor(($gallery.width() - (spacing * (columns - 1))) / columns);
const msnry = new Masonry($gallery[0], {
self.masonryInstance = new Masonry($gallery[0], {
itemSelector: '.igsp-item',
columnWidth: columnWidth,
gutter: spacing,
@@ -110,15 +234,6 @@
});
$gallery.removeClass('loading').addClass('masonry-initialized loaded');
// Handle responsive
let resizeTimeout;
$(window).on('resize', function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function () {
msnry.layout();
}, 250);
});
});
},
@@ -126,53 +241,119 @@
* Initialize slider layout
*/
initSlider: function ($gallery) {
const self = this;
if (typeof Swiper === 'undefined') {
console.warn('IGSP: Swiper library not loaded');
$gallery.addClass('loaded');
return;
}
const $swiperContainer = $gallery.find('.swiper');
let $swiperContainer = $gallery.find('.swiper');
if (!$swiperContainer.length) {
// Wrap items in swiper structure if not already
this.wrapItemsInSwiper($gallery);
$swiperContainer = $gallery.find('.swiper');
}
if (!$swiperContainer.length) {
console.warn('IGSP: Could not create swiper container');
$gallery.addClass('loaded');
return;
}
const autoplay = typeof igspFrontend !== 'undefined' && igspFrontend.autoplay;
new Swiper($swiperContainer[0], {
slidesPerView: 'auto',
spaceBetween: parseInt($gallery.data('spacing')) || 10,
loop: true,
centeredSlides: false,
autoplay: autoplay ? {
delay: 4000,
disableOnInteraction: false
} : false,
pagination: {
el: '.swiper-pagination',
clickable: true
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
breakpoints: {
320: {
slidesPerView: 1,
centeredSlides: true
},
640: {
slidesPerView: 2,
centeredSlides: false
},
1024: {
slidesPerView: 3,
centeredSlides: false
}
// Force load any lazy images in the slider
$gallery.find('.igsp-image.lazyload').each(function () {
const $img = $(this);
if ($img.data('src')) {
$img.attr('src', $img.data('src'));
}
$img.removeClass('lazyload').addClass('loaded');
});
$gallery.addClass('loaded');
// Also ensure images without lazyload class are visible
$gallery.find('.igsp-image').addClass('loaded');
const settings = typeof igspFrontend !== 'undefined' ? igspFrontend : {};
const sliderAutoplay = settings.sliderAutoplay !== undefined ? settings.sliderAutoplay : true;
const sliderDelay = parseInt(settings.sliderDelay) || 4000;
const sliderPauseHover = settings.sliderPauseHover !== undefined ? settings.sliderPauseHover : true;
const spacing = parseInt($gallery.data('spacing')) || 10;
const columns = parseInt(settings.columns) || parseInt($gallery.data('columns')) || 3;
const columnsTablet = parseInt(settings.columnsTablet) || 2;
const columnsMobile = parseInt(settings.columnsMobile) || 1;
// Small delay to ensure DOM is ready
setTimeout(function () {
// Initialize Swiper with proper configuration
self.swiperInstance = new Swiper($swiperContainer[0], {
slidesPerView: columnsMobile,
spaceBetween: spacing,
loop: $gallery.find('.swiper-slide').length > columns,
grabCursor: true,
autoplay: sliderAutoplay ? {
delay: sliderDelay,
disableOnInteraction: false,
pauseOnMouseEnter: sliderPauseHover
} : false,
pagination: {
el: $gallery.find('.swiper-pagination')[0],
clickable: true,
dynamicBullets: true
},
navigation: {
nextEl: $gallery.find('.swiper-button-next')[0],
prevEl: $gallery.find('.swiper-button-prev')[0]
},
breakpoints: {
768: {
slidesPerView: columnsTablet,
spaceBetween: spacing
},
1024: {
slidesPerView: columns,
spaceBetween: spacing
}
},
on: {
init: function () {
$gallery.addClass('loaded swiper-initialized');
}
}
});
}, 50);
},
/**
* Wrap gallery items in Swiper structure
*/
wrapItemsInSwiper: function ($gallery) {
const $items = $gallery.find('.igsp-item');
if (!$items.length) return;
// Remove any existing swiper structure first
$gallery.find('.swiper').remove();
// Clone items before moving them
const $wrapper = $('<div class="swiper-wrapper"></div>');
// Move items into wrapper as slides (clone to avoid issues)
$items.each(function () {
const $item = $(this);
$item.addClass('swiper-slide');
$wrapper.append($item);
});
// Create swiper structure
const $swiper = $('<div class="swiper"></div>');
$swiper.append($wrapper);
$swiper.append('<div class="swiper-pagination"></div>');
$swiper.append('<div class="swiper-button-prev"></div>');
$swiper.append('<div class="swiper-button-next"></div>');
$gallery.append($swiper);
},
/**
@@ -184,7 +365,7 @@
return;
}
const lightbox = GLightbox({
GLightbox({
selector: '.igsp-gallery [data-gallery="igsp-lightbox"]',
touchNavigation: true,
loop: true,