diff --git a/assets/stacked-images/frontblocks-stacked-images-frontend.js b/assets/stacked-images/frontblocks-stacked-images-frontend.js deleted file mode 100644 index 2225436..0000000 --- a/assets/stacked-images/frontblocks-stacked-images-frontend.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * FrontBlocks Stacked Images Frontend Animation - * - * @package FrontBlocks - */ - -(function () { - 'use strict'; - - // Track initialized containers to avoid re-initialization. - const initializedContainers = new WeakSet(); - - /** - * Initialize stacked images animation. - */ - function initStackedImages() { - const containers = document.querySelectorAll('.frbl-stacked-images-wrapper:not(.frbl-initialized)'); - - containers.forEach(container => { - // Skip if already initialized. - if (initializedContainers.has(container)) { - return; - } - - const direction = container.dataset.direction || 'bottom'; - const duration = parseInt(container.dataset.duration) || 1000; - const delay = parseInt(container.dataset.delay) || 500; - const images = container.querySelectorAll('.frbl-stacked-image'); - - if (images.length === 0) { - return; - } - - // Mark as initialized. - container.classList.add('frbl-initialized'); - initializedContainers.add(container); - - // Set initial position for each image based on direction. - images.forEach((image, index) => { - // Set transition FIRST. - image.style.transition = `opacity ${duration}ms ease-out, transform ${duration}ms ease-out`; - image.style.position = 'absolute'; - image.style.top = '0'; - image.style.left = '0'; - image.style.width = '100%'; - image.style.height = '100%'; - - // Generate random rotation between -10 and 10 degrees. - const randomRotation = (Math.random() * 20) - 10; - image.dataset.rotation = randomRotation; - - // Force a reflow to ensure styles are applied. - void image.offsetHeight; - - // Then set initial state (hidden and off-screen). - image.style.opacity = '0'; - - // Set initial transform based on direction with rotation. - let initialTransform = ''; - switch (direction) { - case 'bottom': - initialTransform = `translateY(100%) rotate(${randomRotation}deg)`; - break; - case 'top': - initialTransform = `translateY(-100%) rotate(${randomRotation}deg)`; - break; - case 'left': - initialTransform = `translateX(-100%) rotate(${randomRotation}deg)`; - break; - case 'right': - initialTransform = `translateX(100%) rotate(${randomRotation}deg)`; - break; - } - image.style.transform = initialTransform; - }); - - // Animate images one by one using Intersection Observer. - const observer = new IntersectionObserver( - (entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - animateImages(images, duration, delay); - observer.unobserve(entry.target); - } - }); - }, - { - threshold: 0.2, - rootMargin: '0px', - } - ); - - observer.observe(container); - }); - } - - /** - * Animate images sequentially. - * - * @param {NodeList} images List of image elements. - * @param {number} duration Animation duration. - * @param {number} delay Delay between images. - */ - function animateImages(images, duration, delay) { - images.forEach((image, index) => { - setTimeout(() => { - const rotation = image.dataset.rotation || 0; - image.style.opacity = '1'; - // Final state: no translation, but keep the random rotation. - image.style.transform = `translate(0, 0) rotate(${rotation}deg)`; - }, index * delay); - }); - } - - // Initialize on DOM ready. - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initStackedImages); - } else { - initStackedImages(); - } -})(); diff --git a/assets/stacked-images/frontblocks-stacked-images-option.jsx b/assets/stacked-images/frontblocks-stacked-images-option.jsx deleted file mode 100644 index 6270a6b..0000000 --- a/assets/stacked-images/frontblocks-stacked-images-option.jsx +++ /dev/null @@ -1,203 +0,0 @@ -const { registerBlockType } = wp.blocks; -const { Fragment, useState } = wp.element; -const { InspectorControls, MediaUpload, MediaUploadCheck, useBlockProps } = wp.blockEditor; -const { PanelBody, SelectControl, RangeControl, Button, Placeholder } = wp.components; -const { __ } = wp.i18n; - -/** - * Edit component for Stacked Images block. - * - * @param {Object} props - Block properties. - * @return {JSX.Element} Block edit component. - */ -function StackedImagesEdit(props) { - const { attributes, setAttributes } = props; - const { images, direction, animationDuration, animationDelay, containerHeight } = attributes; - - const blockProps = useBlockProps(); - - const onSelectImages = (newImages) => { - setAttributes({ - images: newImages.map(img => ({ - id: img.id, - url: img.url, - alt: img.alt || '', - })), - }); - }; - - const removeImage = (indexToRemove) => { - const newImages = images.filter((img, index) => index !== indexToRemove); - setAttributes({ images: newImages }); - }; - - return ( - - - - - img.id)} - render={({ open }) => ( - - )} - /> - - {images.length > 0 && ( - - )} - - - - setAttributes({ direction: value })} - /> - setAttributes({ animationDuration: value })} - min={200} - max={3000} - step={100} - /> - setAttributes({ animationDelay: value })} - min={0} - max={2000} - step={100} - /> - setAttributes({ containerHeight: value })} - min={200} - max={1000} - step={50} - /> - - - -
-
- {images.length === 0 ? ( - - ) : ( -
-
- {images.map((image, index) => ( -
- {image.alt} -
- ))} -
-
-

- {images.length} {images.length === 1 ? __('image', 'frontblocks') : __('images', 'frontblocks')} | - {__(' Direction: ', 'frontblocks')} {direction} | - {__(' Duration: ', 'frontblocks')} {animationDuration}ms | - {__(' Delay: ', 'frontblocks')} {animationDelay}ms -

-
-
- )} -
-
-
- ); -} - -// Register the block. -registerBlockType('frontblocks/stacked-images', { - title: __('Stacked Images', 'frontblocks'), - description: __('Display images with stacked animation effect from different directions.', 'frontblocks'), - category: 'media', - icon: 'format-gallery', - keywords: [ - __('images', 'frontblocks'), - __('stacked', 'frontblocks'), - __('animation', 'frontblocks'), - __('gallery', 'frontblocks'), - ], - attributes: { - images: { - type: 'array', - default: [], - }, - direction: { - type: 'string', - default: 'bottom', - }, - animationDuration: { - type: 'number', - default: 1000, - }, - animationDelay: { - type: 'number', - default: 500, - }, - containerHeight: { - type: 'number', - default: 500, - }, - }, - edit: StackedImagesEdit, - save: function () { - return null; // Dynamic block, render on server side. - }, -}); diff --git a/assets/stacked-images/frontblocks-stacked-images.css b/assets/stacked-images/frontblocks-stacked-images.css deleted file mode 100644 index 6ce317b..0000000 --- a/assets/stacked-images/frontblocks-stacked-images.css +++ /dev/null @@ -1,109 +0,0 @@ -/** - * FrontBlocks Stacked Images Styles - * - * @package FrontBlocks - */ - -/* Wrapper */ -.frbl-stacked-images-wrapper { - position: relative; - width: 100%; - overflow: visible; - display: block; - background: transparent; - padding: 50px; - margin: -50px; -} - -/* Container */ -.frbl-stacked-images-container { - position: relative; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - overflow: visible; -} - -/* Individual image */ -.frbl-stacked-image { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - will-change: transform, opacity; - opacity: 0; - visibility: hidden; - overflow: visible; -} - -/* Show when initialized */ -.frbl-initialized .frbl-stacked-image { - visibility: visible; -} - -.frbl-stacked-image img { - max-width: 100%; - max-height: 100%; - width: auto; - height: auto; - object-fit: contain; - display: block; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); -} - -/* Placeholder */ -.frbl-stacked-images-placeholder { - padding: 40px; - text-align: center; - background: #f0f0f0; - border: 2px dashed #ccc; - border-radius: 8px; - color: #666; - font-size: 16px; -} - -/* Editor styles */ -.frbl-stacked-images-editor { - min-height: 200px; -} - -.frbl-stacked-images-preview { - border: 2px dashed #ddd; - border-radius: 8px; - overflow: visible; - background: #fafafa; - position: relative; - padding: 50px; - margin: 20px; -} - -.frbl-stacked-image-preview { - border-radius: 4px; - overflow: visible; - transition: transform 0.3s ease; -} - -.frbl-stacked-image-preview:hover { - transform: scale(1.02) !important; - z-index: 999 !important; -} - -.frbl-stacked-image-preview img { - display: block; - transition: all 0.3s ease; - pointer-events: none; -} - -/* Responsive */ -@media (max-width: 768px) { - .frbl-stacked-images-wrapper { - height: auto !important; - min-height: 300px; - } -} diff --git a/assets/stacked-images/frontblocks-stacked-images.js b/assets/stacked-images/frontblocks-stacked-images.js deleted file mode 100644 index c089f6c..0000000 --- a/assets/stacked-images/frontblocks-stacked-images.js +++ /dev/null @@ -1,227 +0,0 @@ -"use strict"; - -var registerBlockType = wp.blocks.registerBlockType; -var _wp$element = wp.element, - Fragment = _wp$element.Fragment, - useState = _wp$element.useState; -var _wp$blockEditor = wp.blockEditor, - InspectorControls = _wp$blockEditor.InspectorControls, - MediaUpload = _wp$blockEditor.MediaUpload, - MediaUploadCheck = _wp$blockEditor.MediaUploadCheck, - useBlockProps = _wp$blockEditor.useBlockProps; -var _wp$components = wp.components, - PanelBody = _wp$components.PanelBody, - SelectControl = _wp$components.SelectControl, - RangeControl = _wp$components.RangeControl, - Button = _wp$components.Button, - Placeholder = _wp$components.Placeholder; -var __ = wp.i18n.__; - -/** - * Edit component for Stacked Images block. - * - * @param {Object} props - Block properties. - * @return {JSX.Element} Block edit component. - */ -function StackedImagesEdit(props) { - var attributes = props.attributes, - setAttributes = props.setAttributes; - var images = attributes.images, - direction = attributes.direction, - animationDuration = attributes.animationDuration, - animationDelay = attributes.animationDelay, - containerHeight = attributes.containerHeight; - var blockProps = useBlockProps(); - var onSelectImages = function onSelectImages(newImages) { - setAttributes({ - images: newImages.map(function (img) { - return { - id: img.id, - url: img.url, - alt: img.alt || '' - }; - }) - }); - }; - var removeImage = function removeImage(indexToRemove) { - var newImages = images.filter(function (img, index) { - return index !== indexToRemove; - }); - setAttributes({ - images: newImages - }); - }; - return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(InspectorControls, null, /*#__PURE__*/React.createElement(PanelBody, { - title: __('Images', 'frontblocks'), - initialOpen: true - }, /*#__PURE__*/React.createElement(MediaUploadCheck, null, /*#__PURE__*/React.createElement(MediaUpload, { - onSelect: onSelectImages, - allowedTypes: ['image'], - multiple: true, - value: images.map(function (img) { - return img.id; - }), - render: function render(_ref) { - var open = _ref.open; - return /*#__PURE__*/React.createElement(Button, { - isPrimary: true, - onClick: open, - style: { - width: '100%', - marginBottom: '10px' - } - }, images.length === 0 ? __('Add Images', 'frontblocks') : __('Change Images (' + images.length + ')', 'frontblocks')); - } - })), images.length > 0 && /*#__PURE__*/React.createElement(Button, { - isDestructive: true, - onClick: function onClick() { - return setAttributes({ - images: [] - }); - }, - style: { - width: '100%' - } - }, __('Remove All Images', 'frontblocks'))), /*#__PURE__*/React.createElement(PanelBody, { - title: __('Settings', 'frontblocks'), - initialOpen: true - }, /*#__PURE__*/React.createElement(SelectControl, { - label: __('Animation Direction', 'frontblocks'), - value: direction, - options: [{ - label: __('From Bottom', 'frontblocks'), - value: 'bottom' - }, { - label: __('From Top', 'frontblocks'), - value: 'top' - }, { - label: __('From Left', 'frontblocks'), - value: 'left' - }, { - label: __('From Right', 'frontblocks'), - value: 'right' - }], - onChange: function onChange(value) { - return setAttributes({ - direction: value - }); - } - }), /*#__PURE__*/React.createElement(RangeControl, { - label: __('Animation Duration (ms)', 'frontblocks'), - value: animationDuration, - onChange: function onChange(value) { - return setAttributes({ - animationDuration: value - }); - }, - min: 200, - max: 3000, - step: 100 - }), /*#__PURE__*/React.createElement(RangeControl, { - label: __('Delay Between Images (ms)', 'frontblocks'), - value: animationDelay, - onChange: function onChange(value) { - return setAttributes({ - animationDelay: value - }); - }, - min: 0, - max: 2000, - step: 100 - }), /*#__PURE__*/React.createElement(RangeControl, { - label: __('Container Height (px)', 'frontblocks'), - value: containerHeight, - onChange: function onChange(value) { - return setAttributes({ - containerHeight: value - }); - }, - min: 200, - max: 1000, - step: 50 - }))), /*#__PURE__*/React.createElement("div", blockProps, /*#__PURE__*/React.createElement("div", { - className: "frbl-stacked-images-editor" - }, images.length === 0 ? /*#__PURE__*/React.createElement(Placeholder, { - icon: "format-gallery", - label: __('Stacked Images', 'frontblocks'), - instructions: __('Use the settings panel on the right to add images →', 'frontblocks') - }) : /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { - className: "frbl-stacked-images-preview", - style: { - height: containerHeight + 'px', - position: 'relative' - } - }, images.map(function (image, index) { - return /*#__PURE__*/React.createElement("div", { - key: index, - className: "frbl-stacked-image-preview", - style: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - zIndex: index + 1, - transform: "rotate(".concat(Math.random() * 20 - 10, "deg)") - } - }, /*#__PURE__*/React.createElement("img", { - src: image.url, - alt: image.alt, - style: { - width: '100%', - height: '100%', - objectFit: 'contain', - boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)' - } - })); - })), /*#__PURE__*/React.createElement("div", { - style: { - marginTop: '20px', - textAlign: 'center', - padding: '10px', - background: '#f0f0f0', - borderRadius: '4px' - } - }, /*#__PURE__*/React.createElement("p", { - style: { - margin: '0', - fontSize: '13px', - color: '#666' - } - }, /*#__PURE__*/React.createElement("strong", null, images.length), " ", images.length === 1 ? __('image', 'frontblocks') : __('images', 'frontblocks'), " |", __(' Direction: ', 'frontblocks'), " ", /*#__PURE__*/React.createElement("strong", null, direction), " |", __(' Duration: ', 'frontblocks'), " ", /*#__PURE__*/React.createElement("strong", null, animationDuration, "ms"), " |", __(' Delay: ', 'frontblocks'), " ", /*#__PURE__*/React.createElement("strong", null, animationDelay, "ms"))))))); -} - -// Register the block. -registerBlockType('frontblocks/stacked-images', { - title: __('Stacked Images', 'frontblocks'), - description: __('Display images with stacked animation effect from different directions.', 'frontblocks'), - category: 'media', - icon: 'format-gallery', - keywords: [__('images', 'frontblocks'), __('stacked', 'frontblocks'), __('animation', 'frontblocks'), __('gallery', 'frontblocks')], - attributes: { - images: { - type: 'array', - default: [] - }, - direction: { - type: 'string', - default: 'bottom' - }, - animationDuration: { - type: 'number', - default: 1000 - }, - animationDelay: { - type: 'number', - default: 500 - }, - containerHeight: { - type: 'number', - default: 500 - } - }, - edit: StackedImagesEdit, - save: function save() { - return null; // Dynamic block, render on server side. - } -}); diff --git a/includes/Frontend/StackedImages.php b/includes/Frontend/StackedImages.php deleted file mode 100644 index 9089c18..0000000 --- a/includes/Frontend/StackedImages.php +++ /dev/null @@ -1,185 +0,0 @@ - - * @copyright 2025 Closemarketing - * @version 1.0 - */ - -namespace FrontBlocks\Frontend; - -use WP_Block_Type_Registry; - -defined( 'ABSPATH' ) || exit; - -/** - * StackedImages class. - * - * @since 1.0.0 - */ -class StackedImages { - - /** - * Constructor. - */ - public function __construct() { - add_action( 'init', array( $this, 'register_stacked_images_block' ), 20 ); - add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); - } - - /** - * Enqueue frontend scripts and styles. - * - * @return void - */ - public function enqueue_frontend_scripts() { - wp_register_style( - 'frontblocks-stacked-images-style', - FRBL_PLUGIN_URL . 'assets/stacked-images/frontblocks-stacked-images.css', - array(), - FRBL_VERSION - ); - - wp_register_script( - 'frontblocks-stacked-images-frontend', - FRBL_PLUGIN_URL . 'assets/stacked-images/frontblocks-stacked-images-frontend.js', - array(), - FRBL_VERSION, - true - ); - - if ( is_admin() || has_block( 'frontblocks/stacked-images' ) ) { - wp_enqueue_style( 'frontblocks-stacked-images-style' ); - wp_enqueue_script( 'frontblocks-stacked-images-frontend' ); - } - } - - /** - * Enqueue block editor assets. - * - * @return void - */ - public function enqueue_block_editor_assets() { - // Enqueue styles for editor. - wp_enqueue_style( - 'frontblocks-stacked-images-style', - FRBL_PLUGIN_URL . 'assets/stacked-images/frontblocks-stacked-images.css', - array(), - FRBL_VERSION - ); - - wp_enqueue_script( - 'frontblocks-stacked-images-option', - FRBL_PLUGIN_URL . 'assets/stacked-images/frontblocks-stacked-images.js', - array( 'wp-blocks', 'wp-element', 'wp-components', 'wp-data', 'wp-editor', 'wp-block-editor', 'wp-compose', 'wp-i18n' ), - FRBL_VERSION, - true - ); - } - - /** - * Register the Stacked Images block. - * - * @return void - */ - public function register_stacked_images_block() { - $args = array( - 'editor_script' => 'frontblocks-stacked-images-option', - 'render_callback' => array( $this, 'render_stacked_images_block' ), - 'attributes' => array( - 'images' => array( - 'type' => 'array', - 'default' => array(), - ), - 'direction' => array( - 'type' => 'string', - 'default' => 'bottom', - ), - 'animationDuration' => array( - 'type' => 'number', - 'default' => 1000, - ), - 'animationDelay' => array( - 'type' => 'number', - 'default' => 500, - ), - 'containerHeight' => array( - 'type' => 'number', - 'default' => 500, - ), - 'className' => array( - 'type' => 'string', - 'default' => '', - ), - ), - ); - - if ( ! WP_Block_Type_Registry::get_instance()->is_registered( 'frontblocks/stacked-images' ) ) { - register_block_type( - 'frontblocks/stacked-images', - $args - ); - } - } - - /** - * Render the Stacked Images block on frontend. - * - * @param array $attributes Block attributes. - * @return string HTML output. - */ - public function render_stacked_images_block( $attributes ) { - $images = $attributes['images'] ?? array(); - $direction = sanitize_text_field( $attributes['direction'] ?? 'bottom' ); - $animation_duration = absint( $attributes['animationDuration'] ?? 1000 ); - $animation_delay = absint( $attributes['animationDelay'] ?? 500 ); - $container_height = absint( $attributes['containerHeight'] ?? 500 ); - - if ( empty( $images ) ) { - return '
' . esc_html__( 'Please add images to the Stacked Images block.', 'frontblocks' ) . '
'; - } - - $wrapper_class = 'frbl-stacked-images-wrapper'; - if ( ! empty( $attributes['className'] ) ) { - $wrapper_class .= ' ' . esc_attr( $attributes['className'] ); - } - - ob_start(); - ?> -
-
- $image ) : ?> - -
- <?php echo $image_alt; ?> -
- -
-
-