<?php
    namespace Pixebuilder\Element;
    
    // prevent direct file access
	if( !defined( 'ABSPATH' ) ) exit;

    class Table_Of_Contents extends Item{

        static function get_name(){
            return 'table-of-contents';
        }

        static function get_title(){
            return esc_html__('Table Of Contents', 'pixebuilder');
        }

        static function get_icon(){
            return 'toc';
        }
        
        static function render_content($settings){

            $atts = array(
                'tag' => empty($settings['content-layout']['style'])? '': $settings['content-layout']['style'],
                'sublevel' => empty($settings['content-layout']['collapse'])? '': '0',
                'hierarchy' => empty($settings['content-layout']['hierarchy'])? '': '0',
                'selector' => empty($settings['content-layout']['tags'])? '': implode(',', $settings['content-layout']['tags']),
                'includes' => empty($settings['content-layout']['included-class'])? '': $settings['content-layout']['included-class'],
                'excludes' => empty($settings['content-layout']['excluded-class'])? '': $settings['content-layout']['excluded-class'],
            );
            $icon = '';
            $extra_class = '';

            // title layout
            if( empty($settings['title-layout']['toggle']) ){
                $extra_class .= ' pixe-toggle';

                if( empty($settings['title-layout']['initial']) ){
                    $extra_class .= ' pixe-active';
                }

                // icon
                if( empty($settings['title-icon']['icon']) ){
                    $icon = pixe_icon_preset('down');
                }else if( $settings['title-icon']['icon'] != 'none' ){
                    $icon = pixe_get_icon($settings['title-icon']['icon']);
                    $extra_class .= ' pixe-custom-icon';
                }
            }else{
                $extra_class .= ' pixe-active';
            }

            // typo
            $title = empty($settings['title-layout']['title'])? esc_html__('Table Of Contents', 'pixebuilder'): $settings['title-layout']['title'];
            $head_atts = array(
                'typo-preset' => empty($settings['title-typo']['preset'])? '': $settings['title-typo']['preset']
            );
            $content_atts = array(
                'typo-preset' => empty($settings['content-typo']['preset'])? '': $settings['content-typo']['preset']
            );
        
            $ret  = '<div class="pixe-table-of-contents ' . esc_attr(trim($extra_class)) . '" ';
            $ret .= pixe_get_data_atts($atts);
            $ret .= ' >';
            $ret .= '<div class="pixe-head" ' . pixe_get_data_atts($head_atts) . ' >' . $title . $icon . '</div>';
            $ret .= '<div class="pixe-content" ' . pixe_get_data_atts($content_atts) . ' ></div>';
            $ret .= '</div>';
            return $ret;
        }

    }

    \Pixe_Builder::register_element('Pixebuilder\Element\Table_Of_Contents');
   
    // register tab script
    function table_of_contents_style(){
?>
.pixe-table-of-contents{ font-size: 17px;  }
.pixe-table-of-contents .pixe-head{ display: flex; align-items: center; justify-content: space-between; position: relative; font-weight: 500; padding: 20px 22px; 
    color: #313131; background: #ececec; transition: color 300ms, background 300ms, border 300ms, shadow 300ms; }
.pixe-table-of-contents .pixe-content{ display: none; position: relative; padding: 22px 22px; background: #fafafa; }
.pixe-table-of-contents .pixe-content.pixe-list-style-none ul{ margin-left: 0; list-style: none; }
.pixe-table-of-contents .pixe-content.pixe-list-style-none ul ul{ margin-left: 20px; }
.pixe-table-of-contents.pixe-active .pixe-content{ display: block; }
.pixe-table-of-contents.pixe-toggle .pixe-head{ cursor: pointer; color: var(--pixe--color--heading); }
.pixe-table-of-contents ul{ margin-bottom: 0; }
.pixe-table-of-contents ol{ counter-reset: item; margin-left: 0; margin-bottom: 0; }
.pixe-table-of-contents ol ol{ margin-left: 20px; }
.pixe-table-of-contents ol li{ display: block }
.pixe-table-of-contents ol li:before{ content: counters(item, ".") ". "; counter-increment: item }
.pixe-table-of-contents li{ margin-bottom: 5px; }
.pixe-table-of-contents li ul,
.pixe-table-of-contents li ol{ margin-top: 5px; }
<?php
    }
    \Pixe_Dependency::get()->register_style('table-of-contents', 'Pixebuilder\Element\table_of_contents_style');

    // register tab script
    function table_of_contents_core_script(){
?>
$.fn.pixeTableOfContents = function(){

    function getLevel(tagName){
        if( tagName == 'div' ){
            return 7;
        }else if( tagName == 'span' ){
            return 8;
        }else{
            return parseInt(tagName.substring(1));
        }
    }

    $(this).find('.pixe-table-of-contents').each(function(){
        var toc = $(this);
        var head = toc.children('.pixe-head');
        var content = toc.children('.pixe-content');

        // header toggle
        if( $(this).hasClass('pixe-toggle') ){
            head.on('click', function(e){
                if( $(e.target).closest('.pixe-tab-settings').length ) return;

                if( toc.hasClass('pixe-active') ){
                    toc.removeClass('pixe-active');
                    content.css('display', 'block').slideUp(500);
                }else{
                    toc.addClass('pixe-active');
                    content.css('display', 'none').slideDown(500);
                }
            });
        }

        // generate content
        var items = $(this).closest('.pixe-editor').find('.pixe-item-title');
        var selector = toc.data('selector')? toc.data('selector'): '';
        
        items = items.filter(function(){
            var header = $(this).find('.pixe-inline');
            if( selector && !header.is(selector) ){ return false; }
            return true;
        });
        if( $.pixeEditor && !items.length ){
            var sampleHtml  = '<div><h3 class="pixe-inline">Heading Text</h3></div>';
                sampleHtml += '<div><h3 class="pixe-inline">Heading Text</h3></div>';
                sampleHtml += '<div><h4 class="pixe-inline">Heading Text</h4></div>';
                sampleHtml += '<div><h4 class="pixe-inline">Heading Text</h4></div>';
                sampleHtml += '<div><h4 class="pixe-inline">Heading Text</h4></div>';
                sampleHtml += '<div><h3 class="pixe-inline">Heading Text</h3></div>';
                sampleHtml += '<div><h3 class="pixe-inline">Heading Text</h3></div>';
            items = $(sampleHtml);
        }

        var listTag = toc.data('tag');
        if( listTag == 'none' ){
            listTag = 'ul';
            content.addClass('pixe-list-style-none');
        }else{
            if( !listTag ){
                listTag = 'ol';
            }
            content.removeClass('pixe-list-style-none');
        }
        
        var hierarchy = toc.data('hierarchy') == '0'? false: true;
        var currentLevel = -1;
        var topList = $(document.createElement(listTag));
        var stack = [topList];

        content.empty();
        topList.appendTo(content);
        topList.data('level', 0);

        // remove item for non sub level
        var targetTag = '';
        var subLevel = toc.data('sublevel') == '0'? false: true;
        if( !subLevel ){
            var targetTag = '';
            var lowestLevel = 99;
            items.each(function(){
                var header = $(this).find('.pixe-inline');
                if( header.length ){
                    var level = getLevel(header.prop('tagName'));
                    if( level < lowestLevel ){
                        targetTag = header.prop('tagName');
                        lowestLevel = level;
                    }
                }
                
            });
        }

        // create a list
        if( !items.length ){
            toc.remove();
        }else{
            items.each(function(){
                var item = $(this);
                var header = $(this).find(targetTag + '.pixe-inline');
                var targetList = stack[stack.length - 1];
                var targetLevel = targetList.data('level');
                if( header.length ){
                    var level = getLevel(header.prop('tagName'));
                    if( currentLevel == -1 ){ currentLevel = level; }

                    var text = header.text().trim(); // Extract header text
                    var listItem = $('<li></li>').attr('data-level', level);
                    var listLink = $('<a href="#" ></a>').text(text);
                    listLink.appendTo(listItem);
                    listLink.on('click', function(){
                        $('html, body').animate({scrollTop: item.offset().top}, {duration: 500, queue: false});
                        return false;
                    });

                    if( hierarchy && level > currentLevel ){
                        // Create a new nested <ul> for deeper levels
                        var nestedList = $(document.createElement(listTag));
                        nestedList.data('level', level);

                        var lastListItem = stack[stack.length - 1].children('li').last();
                        if( lastListItem.length ){
                            lastListItem.append(nestedList);
                        }else{
                            stack[stack.length - 1].append(nestedList);
                        }
                        stack.push(nestedList);
                    
                    }else if( level < targetLevel ){
                        // Move back up the stack for shallower levels
                        while( level < targetLevel && stack.length > 1 ){
                            stack.pop();
                            targetList = stack[stack.length - 1];
                            targetLevel = targetList.data('level');
                        }
                    }

                    // Append the current list item to the correct level
                    stack[stack.length - 1].append(listItem);
                    currentLevel = level;
                }
            });
        }
        
    });
}
<?php
    }
    function table_of_contents_script(){
?>
$(document).ready(function(){ $('.pixe-item-table-of-contents').pixeTableOfContents(); });
<?php
    }
    \Pixe_Dependency::get()->register_core_script('table-of-contents', 'Pixebuilder\Element\table_of_contents_core_script');
    \Pixe_Dependency::get()->register_script('table-of-contents', 'Pixebuilder\Element\table_of_contents_script');