Feedback Widget Layout

User
<style>
.feedback-slider-track {
            position: relative;
            height: auto;
        }

        .feedback-section .elementor-widget {
            position: unset !important;
        }

        .feedback-slide-group {
            display: flex;
            gap: 30px;
            position: absolute;
            width: 100%;
            top: 0;
            left: 0;
            opacity: 0;
            transition: opacity 0.5s ease-in-out;
        }

        .feedback-box {
            flex: 1;
            background: #fff;
            padding: 30px 30px 0 30px;
            border-radius: 5px;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            box-sizing: border-box;
        }

        .feedback-user {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .feedback-user-img {
            width: 56px;
            height: 56px !important;
            border-radius: 50% !important;
            margin-right: 10px;
            border: 2px solid #E9204F !important;
        }

        .feedback-pagination {
            position: absolute;
            right: 105px;
            top: 50%;
            transform: translateY(-50%);
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 32px;
        }

        .feedback-pagination::before,
        .feedback-pagination::after {
            content: '';
            display: block;
            width: 1px;
            height: 85px;
            background-color: #fff;
            opacity: 0.5;
        }

        .feedback-pagination .dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #fff;
            cursor: pointer;
            position: relative;
            z-index: 1;
        }

        .feedback-pagination .dot.active {
            background: #fff;
            border: 2px solid #fff;
            box-shadow: 0 0 0 2px #000, 0 0 0 4px #fff;
        }

        .feedback-slide-group.active {
            opacity: 1;
            position: relative;
        }

        .user-inner-box {
            display: flex;
            align-items: center;
        }

        .user-inner-text {
            display: flex;
            flex-direction: column;
        }

        .user-name {
            font-size: 18px;
            font-weight: 600;
            color: #000;
        }

        .feedback-text,
        span.user-title {
            color: #444;
        }
</style>
<script>
jQuery(document).ready(function ($) {
            var $groups = $('.feedback-slide-group');
            var current = 0;

            function showSlide(index) {
                $groups.removeClass('active').eq(index).addClass('active');
                $('.feedback-pagination .dot').removeClass('active').eq(index).addClass('active');
            }

            $('.feedback-pagination .dot').click(function () {
                var index = $(this).data('index');
                current = index;
                showSlide(index);
            });

            showSlide(0); // Initialize

            // ✅ Equal height logic
            function equalizeHeights() {
                var maxGroupHeight = 0;

                $groups.each(function () {
                    var $boxes = $(this).find('.feedback-box');
                    var maxBoxHeight = 0;

                    $boxes.css('height', 'auto'); // reset first

                    $boxes.each(function () {
                        var h = $(this).outerHeight();
                        if (h > maxBoxHeight) maxBoxHeight = h;
                    });

                    $boxes.css('height', maxBoxHeight + 'px');
                    $(this).css('height', maxBoxHeight + 40 + 'px'); // group height + padding
                    if (maxBoxHeight + 40 > maxGroupHeight) maxGroupHeight = maxBoxHeight + 40;
                });

                // Apply same height to all groups
                $groups.css('height', maxGroupHeight + 'px');
            }

            equalizeHeights();

            $(window).on('resize', function () {
                //equalizeHeights();
            });
        });
</script>
Assistant
To make the `FeedbackWidget` display **1 feedback per slide on mobile (width ≤ 767px)** and **2 feedbacks per slide on larger screens**, you can dynamically detect screen size via JavaScript and adjust the layout accordingly.

Since `PHP` runs server-side and can't detect the screen width, we must render all items individually and let JavaScript group them for desktop if needed. Here's the **correct and complete solution**:

### ✅ Changes Summary:
1. Render all feedbacks as separate `.feedback-box` divs.
2. Let JavaScript group into slide sets (`.feedback-slide-group`) depending on screen width.
3. Dynamically reinitialize the groups on window resize.

### ✅ Updated `render()` Code (Just the relevant part):

Replace this PHP part:

$groups = array_chunk($settings['feedback_list'], 2);
foreach ($groups as $group) {
    echo '<div class="feedback-slide-group">';
    foreach ($group as $item) {
        ...
    }
    echo '</div>';
}

With this:

foreach ($settings['feedback_list'] as $item) {
    ?>
    <div class="feedback-box" data-feedback-box>
        <div class="feedback-text"><?php echo esc_html($item['feedback_text']); ?></div>
        <div class="feedback-user">
            <div class="user-inner-box">
                <div><img class="feedback-user-img" src="<?php echo esc_url($item['user_image']['url']); ?>" /></div>
                <div class="user-inner-text">
                    <span class="user-name"><?php echo esc_html($item['user_name']); ?></span>
                    <span class="user-title"><?php echo esc_html($item['user_title']); ?></span>
                </div>
            </div>
            <div>
                <img src="<?php echo esc_url($item['quote_icon']['url']); ?>" class="quote-icon" />
            </div>
        </div>
    </div>
    <?php
}

### ✅ JavaScript Update (Replace “ section):

<script>
jQuery(document).ready(function ($) {
    var $track = $('.feedback-slider-track');
    var $pagination = $('.feedback-pagination');

    function groupSlides() {
        var perSlide = $(window).width() <= 767 ? 1 : 2;

        // Clear existing groups
        $track.empty();
        $pagination.empty();

        var $boxes = $('[data-feedback-box]');
        var groups = [];

        for (var i = 0; i < $boxes.length; i += perSlide) {
            var $group = $('<div class="feedback-slide-group"></div>');
            $boxes.slice(i, i + perSlide).appendTo($group);
            groups.push($group);
        }

        groups.forEach(function ($group, index) {
            $track.append($group);
            if (index === 0) $group.addClass('active');
            var dot = $('<div class="dot" data-index="' + index + '"></div>');
            if (index === 0) dot.addClass('active');
            $pagination.append(dot);
        });

        bindDots();
        equalizeHeights();
    }

    function bindDots() {
        $('.feedback-pagination .dot').off('click').on('click', function () {
            var index = $(this).data('index');
            $('.feedback-slide-group').removeClass('active').eq(index).addClass('active');
            $('.feedback-pagination .dot').removeClass('active').eq(index).addClass('active');
        });
    }

    function equalizeHeights() {
        var maxGroupHeight = 0;
        $('.feedback-slide-group').each(function () {
            var $boxes = $(this).find('.feedback-box');
            var maxBoxHeight = 0;
            $boxes.css('height', 'auto');
            $boxes.each(function () {
                var h = $(this).outerHeight();
                if (h > maxBoxHeight) maxBoxHeight = h;
            });
            $boxes.css('height', maxBoxHeight + 'px');
            $(this).css('height', maxBoxHeight + 40 + 'px');
            if (maxBoxHeight + 40 > maxGroupHeight) maxGroupHeight = maxBoxHeight + 40;
        });

        $('.feedback-slide-group').css('height', maxGroupHeight + 'px');
    }

    // Initial grouping
    groupSlides();

    $(window).on('resize', function () {
        groupSlides();
    });
});
</script>

Would you like me to generate a downloadable PHP file with this full updated widget code?

User
give me full code
Assistant
Here is the **full updated code** of your `FeedbackWidget` Elementor widget with mobile responsiveness (1 item per slide on mobile, 2 items per slide on desktop):

### ✅ `FeedbackWidget.php` (Full)

<?php
if ( ! defined( 'ABSPATH' ) ) exit;

class FeedbackWidget extends ElementorWidget_Base {

    public function get_name() {
        return 'feedbackWidget';
    }

    public function get_title() {
        return __('Feedback Widget', 'gentium');
    }

    public function get_icon() {
        return 'eicon-testimonial-carousel';
    }

    public function get_categories() {
        return ['general'];
    }

    protected function _register_controls() {
        $this->start_controls_section('content_section', [
            'label' => __('Feedbacks', 'gentium'),
        ]);

        $repeater = new ElementorRepeater();

        $repeater->add_control('feedback_text', [
            'label' => __('Feedback Text'),
            'type' => ElementorControls_Manager::TEXTAREA,
            'default' => 'Sample feedback text...',
        ]);

        $repeater->add_control('user_image', [
            'label' => __('User Image'),
            'type' => ElementorControls_Manager::MEDIA,
        ]);

        $repeater->add_control('user_name', [
            'label' => __('User Name'),
            'type' => ElementorControls_Manager::TEXT,
            'default' => 'John Doe',
        ]);

        $repeater->add_control('user_title', [
            'label' => __('Job Title'),
            'type' => ElementorControls_Manager::TEXT,
            'default' => 'CEO, Company',
        ]);

        $repeater->add_control('quote_icon', [
            'label' => __('Quote Icon'),
            'type' => ElementorControls_Manager::MEDIA,
        ]);

        $this->add_control('feedback_list', [
            'label' => __('Feedback List'),
            'type' => ElementorControls_Manager::REPEATER,
            'fields' => $repeater->get_controls(),
            'default' => [],
        ]);

        $this->end_controls_section();
    }

    protected function render() {
        $settings = $this->get_settings_for_display();
        if (empty($settings['feedback_list'])) return;

        ?>
        <style>
        .feedback-slider-track {
            position: relative;
            height: auto;
        }

        .feedback-section .elementor-widget {
            position: unset !important;
        }

        .feedback-slide-group {
            display: flex;
            gap: 30px;
            position: absolute;
            width: 100%;
            top: 0;
            left: 0;
            opacity: 0;
            transition: opacity 0.5s ease-in-out;
        }

        .feedback-slide-group.active {
            opacity: 1;
            position: relative;
        }

        .feedback-box {
            flex: 1;
            background: #fff;
            padding: 30px 30px 0 30px;
            border-radius: 5px;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            box-sizing: border-box;
        }

        .feedback-user {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .feedback-user-img {
            width: 56px;
            height: 56px !important;
            border-radius: 50% !important;
            margin-right: 10px;
            border: 2px solid #E9204F !important;
        }

        .feedback-pagination {
            position: absolute;
            right: 105px;
            top: 50%;
            transform: translateY(-50%);
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 32px;
        }

        .feedback-pagination::before,
        .feedback-pagination::after {
            content: '';
            display: block;
            width: 1px;
            height: 85px;
            background-color: #fff;
            opacity: 0.5;
        }

        .feedback-pagination .dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #fff;
            cursor: pointer;
            position: relative;
            z-index: 1;
        }

        .feedback-pagination .dot.active {
            background: #fff;
            border: 2px solid #fff;
            box-shadow: 0 0 0 2px #000, 0 0 0 4px #fff;
        }

        .user-inner-box {
            display: flex;
            align-items: center;
        }

        .user-inner-text {
            display: flex;
            flex-direction: column;
        }

        .user-name {
            font-size: 18px;
            font-weight: 600;
            color: #000;
        }

        .feedback-text,
        span.user-title {
            color: #444;
        }
        </style>

        <div class="feedback-slider-wrapper">
            <div class="feedback-slider-track">
                <?php foreach ($settings['feedback_list'] as $item): ?>
                    <div class="feedback-box" data-feedback-box>
                        <div class="feedback-text"><?php echo esc_html($item['feedback_text']); ?></div>
                        <div class="feedback-user">
                            <div class="user-inner-box">
                                <div><img class="feedback-user-img" src="<?php echo esc_url($item['user_image']['url']); ?>" /></div>
                                <div class="user-inner-text">
                                    <span class="user-name"><?php echo esc_html($item['user_name']); ?></span>
                                    <span class="user-title"><?php echo esc_html($item['user_title']); ?></span>
                                </div>
                            </div>
                            <div>
                                <img src="<?php echo esc_url($item['quote_icon']['url']); ?>" class="quote-icon" />
                            </div>
                        </div>
                    </div>
                <?php endforeach; ?>
            </div>
            <div class="feedback-pagination"></div>
        </div>

        <script>
        jQuery(document).ready(function ($) {
            var $track = $('.feedback-slider-track');
            var $pagination = $('.feedback-pagination');

            function groupSlides() {
                var perSlide = $(window).width() <= 767 ? 1 : 2;

                // Reset
                var $boxes = $('[data-feedback-box]');
                $track.empty();
                $pagination.empty();

                var groups = [];
                for (var i = 0; i < $boxes.length; i += perSlide) {
                    var $group = $('<div class="feedback-slide-group"></div>');
                    $boxes.slice(i, i + perSlide).appendTo($group);
                    groups.push($group);
                }

                groups.forEach(function ($group, index) {
                    $track.append($group);
                    if (index === 0) $group.addClass('active');
                    var dot = $('<div class="dot" data-index="' + index + '"></div>');
                    if (index === 0) dot.addClass('active');
                    $pagination.append(dot);
                });

                bindDots();
                equalizeHeights();
            }

            function bindDots() {
                $('.feedback-pagination .dot').off('click').on('click', function () {
                    var index = $(this).data('index');
                    $('.feedback-slide-group').removeClass('active').eq(index).addClass('active');
                    $('.feedback-pagination .dot').removeClass('active').eq(index).addClass('active');
                });
            }

            function equalizeHeights() {
                var maxGroupHeight = 0;
                $('.feedback-slide-group').each(function () {
                    var $boxes = $(this).find('.feedback-box');
                    var maxBoxHeight = 0;
                    $boxes.css('height', 'auto');
                    $boxes.each(function () {
                        var h = $(this).outerHeight();
                        if (h > maxBoxHeight) maxBoxHeight = h;
                    });
                    $boxes.css('height', maxBoxHeight + 'px');
                    $(this).css('height', maxBoxHeight + 40 + 'px');
                    if (maxBoxHeight + 40 > maxGroupHeight) maxGroupHeight = maxBoxHeight + 40;
                });

                $('.feedback-slide-group').css('height', maxGroupHeight + 'px');
            }

            groupSlides();

            $(window).on('resize', function () {
                groupSlides();
            });
        });
        </script>
        <?php
    }
}

Let me know if you want this in a ZIP with a manifest file for direct Elementor import.

Leave a Comment

Your email address will not be published. Required fields are marked *