Пример рекурсивного меню
Вот как построена ёлочка слева на данном сайте, думаю по этому примеру можно сделать что угодно, за основу лучше брать функцию menu() в движке..В примере не происходит парсинга описания из шаблона, это немного долго, функция итак не особо быстрая из-за рекурсии всего дерева, даёт проседание в половину времени генерации страницы..
Добавляю данные функции обычно в конец /index.php или в файл шаблона.
PHP
function menul($typ,$p1='',$p2='',$p3=''){
global $menu,$g;
static $next=0,$c1=0,$c2=0,$c3=0,$mtyp='';
if($mtyp!=$typ){$c1=0;$mtyp=$typ;}//Смена типа меню
$p=1;$i=$c1;
if(''!=$p1){
if(''==$p2 && ''==$p3){$p=2;$i=$c2;}
elseif(''!=$p2 && ''==$p3){$p=3;$i=$c3;}
elseif(''!=$p2 && ''!=$p3){$p=4;$i=0;}
}
for($w=$i;$w<MENUSIZE;$w++){
$r=$menu[$w];
if($r['typ']==$typ && 'no'!=$r['active'] && $r['par1']==$p1 && $r['par2']==$p2 && $r['par3']==$p3){
$x=$next;$pl='';$ishav=false;
if($p==4){
$xlink=$r['par1'].'/'.$r['par2'].'/'.$r['par3'].'/'.$r['name'];
$la='a4';$li='i4 ';
} elseif($p==3){
$xlink=$r['par1'].'/'.$r['par2'].'/'.$r['name'];
$la='a3';$li='i3 ';
} elseif($p==2){
if('context'==$r['name'] || 'ip'==$r['name'])$ishav=1;//ручное управление.. ishave($typ,$r['par1'],$r['name']);
$xlink=$r['par1'].'/'.$r['name'];
$la='a2';$li='i2 ';
} elseif($p==1){
if('speed'==$r['name'] || 'cp'==$r['name'] || 'templates'==$r['name'] || 'docs'==$r['name'] || 'modules'==$r['name'] || 'linux'==$r['name'] ||
'changelog'==$r['name'] || 'any'==$r['name'])$ishav=1;//ishave($typ,$r['name']);
$xlink=$r['name'];
$la='';$li='';
} $ot=$r['text'];
$hint=''!=$r['hint']?' title="'.$r['hint'].'"':'';
$act=' class="none"';
if($r['name']==$g->item($p-1)){//открыт, подпункт
$act='';
$pl=$ishav?'square-minus red" role="button" onclick="se(\''.$x.'\')':'arrow-right red';//красная стрелочка - выбран
if(''!=$la)$la.=' ';$la=' class="'.$la.'onl"';
}else{
$pl=$ishav?'square-plus" role="button" onclick="se(\''.$x.'\')':'arrow-right';
if(''!=$la)$la=' class="'.$la.'"';
}
if('reinstall'==$r['name'])echo'<div class="navp"></div>';
echo '<i id="i',$x,'" class="',$li,'fas fa-',$pl,'"></i><a href="/',P,$xlink,'"',$hint,$la,'>',$ot,'</a>';
if('final'==$r['name'])echo'<div class="navp"></div>';
$next++;
if($p==3){
$c3=$i+1;
if($ishav)echo '<div id="p',$x,'"',$act,'>';
menul($typ,$r['par1'],$r['par2'],$r['name']);
if($ishav)echo '</div>';
} elseif($p==2){
$c3=0;$c2=$i+1;
if($ishav)echo '<div id="p',$x,'"',$act,'>';
menul($typ,$r['par1'],$r['name']);
if($ishav)echo '</div>';
} elseif($p==1){
$c2=0;$c1=$i+1;
if($ishav)echo '<div id="p',$x,'"',$act,'>';
menul($typ,$r['name']);
if($ishav)echo '</div>';
}
}
}
}
JavaScript: (/inc/main.js [or older /inc/_.js])
var alist = [];
function seen() {
var a=[], i, c;
alist=ff.sa(ff.c('seen'));
for (i = 0; i < alist.length; i++){
if(ff.is(alist[i]) && ''!=alist[i]){
a.push(alist[i]);
c = 'i' + alist[i];
ff.cr(c,'fa-square-plus');
ff.ca(c,'fa-square-minus');
ff.b('p'+alist[i]);
}
} alist = a;
}
function se(n){
var a=[], c='i' + n, i, t='';
for (i = 0; i < alist.length; i++){
if (alist[i] != n){a.push(alist[i]);t+=alist[i]+',';}
}
if (ff.d('p'+n) == 'none'){
ff.b('p'+n);
ff.cr(c,'fa-square-plus');
ff.ca(c,'fa-square-minus');
a.push(n);t+=n+',';
} else {
ff.n('p'+n);
ff.cr(c,'fa-square-minus');
ff.ca(c,'fa-square-plus');
}
alist = a;
ff.c('seen', t.slice(0, -1), 3600);
}
ff.onload(function(){
ff.init({...});
seen();
...
});