Enhance plugin initialization with name and description; remove minWidth setting and update form labels for clarity
This commit is contained in:
1 parent
0defa8a577
commit
85db2cdbcb
1 file changed
+45
-28
+45
-28
@@ -2,10 +2,11 @@
|
|||||||
class PluginBluditToc extends Plugin {
|
class PluginBluditToc extends Plugin {
|
||||||
|
|
||||||
public function init() {
|
public function init() {
|
||||||
$this->dbFields = array(
|
$this->name = 'Table of Contents';
|
||||||
|
$this->description = 'Sticky ToC sidebar (desktop) and floating drawer (mobile) from article headings.';
|
||||||
|
$this->dbFields = array(
|
||||||
'title' => 'On this page',
|
'title' => 'On this page',
|
||||||
'navbarHeight' => 80,
|
'navbarHeight' => 80,
|
||||||
'minWidth' => 1280,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ class PluginBluditToc extends Plugin {
|
|||||||
|
|
||||||
$title = htmlspecialchars($this->getValue('title'), ENT_QUOTES, 'UTF-8');
|
$title = htmlspecialchars($this->getValue('title'), ENT_QUOTES, 'UTF-8');
|
||||||
$navbarHeight = (int) $this->getValue('navbarHeight');
|
$navbarHeight = (int) $this->getValue('navbarHeight');
|
||||||
$minWidth = (int) $this->getValue('minWidth');
|
|
||||||
|
|
||||||
$h = '';
|
$h = '';
|
||||||
|
|
||||||
@@ -26,13 +26,7 @@ class PluginBluditToc extends Plugin {
|
|||||||
$h .= '<div class="form-group">';
|
$h .= '<div class="form-group">';
|
||||||
$h .= '<label>' . $L->get('Navbar height (px)') . '</label>';
|
$h .= '<label>' . $L->get('Navbar height (px)') . '</label>';
|
||||||
$h .= '<input class="form-control" type="number" name="navbarHeight" min="0" max="300" value="' . $navbarHeight . '">';
|
$h .= '<input class="form-control" type="number" name="navbarHeight" min="0" max="300" value="' . $navbarHeight . '">';
|
||||||
$h .= '<small class="form-text text-muted">' . $L->get('Pixel offset applied to heading anchors to clear a fixed navbar.') . '</small>';
|
$h .= '<small class="form-text text-muted">' . $L->get('Pixel offset so heading anchors clear a fixed navbar.') . '</small>';
|
||||||
$h .= '</div>';
|
|
||||||
|
|
||||||
$h .= '<div class="form-group">';
|
|
||||||
$h .= '<label>' . $L->get('Sidebar min viewport width (px)') . '</label>';
|
|
||||||
$h .= '<input class="form-control" type="number" name="minWidth" min="768" max="2560" value="' . $minWidth . '">';
|
|
||||||
$h .= '<small class="form-text text-muted">' . $L->get('Below this width the mobile floating button is shown instead.') . '</small>';
|
|
||||||
$h .= '</div>';
|
$h .= '</div>';
|
||||||
|
|
||||||
return $h;
|
return $h;
|
||||||
@@ -40,11 +34,10 @@ class PluginBluditToc extends Plugin {
|
|||||||
|
|
||||||
public function siteHead() {
|
public function siteHead() {
|
||||||
return '<style>
|
return '<style>
|
||||||
#bltoc-sidebar{--bltoc-text:#777;--bltoc-hover:#111;--bltoc-active:#0066cc;--bltoc-label:#999;--bltoc-fab-bg:#0066cc;--bltoc-fab-color:#fff}
|
.bltoc-sidebar{display:none;position:fixed;top:5rem;width:210px;max-height:calc(100vh - 6rem);overflow-y:auto;overflow-x:hidden;z-index:200;font-size:.8125rem;line-height:1.5;scrollbar-width:thin;--bltoc-text:#777;--bltoc-hover:#111;--bltoc-active:#0066cc;--bltoc-label:#999}
|
||||||
@media(prefers-color-scheme:dark){#bltoc-sidebar{--bltoc-text:#999;--bltoc-hover:#eee;--bltoc-active:#4d9fff;--bltoc-label:#666}}
|
@media(prefers-color-scheme:dark){.bltoc-sidebar{--bltoc-text:#999;--bltoc-hover:#eee;--bltoc-active:#4d9fff;--bltoc-label:#666}}
|
||||||
@supports(color:var(--accent)){#bltoc-sidebar{--bltoc-active:var(--accent)}}
|
@supports(color:var(--accent)){.bltoc-sidebar{--bltoc-active:var(--accent)}}
|
||||||
@supports(color:var(--text-muted)){#bltoc-sidebar{--bltoc-text:var(--text-muted)}}
|
@supports(color:var(--text-muted)){.bltoc-sidebar{--bltoc-text:var(--text-muted)}}
|
||||||
.bltoc-sidebar{display:none;position:fixed;left:1rem;top:5rem;width:210px;max-height:calc(100vh - 6rem);overflow-y:auto;overflow-x:hidden;z-index:200;font-size:.8125rem;line-height:1.5;scrollbar-width:thin}
|
|
||||||
.bltoc-header{margin-bottom:.65rem}
|
.bltoc-header{margin-bottom:.65rem}
|
||||||
.bltoc-title{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--bltoc-label,#999)}
|
.bltoc-title{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--bltoc-label,#999)}
|
||||||
.bltoc-list{list-style:none;padding:0;margin:0}
|
.bltoc-list{list-style:none;padding:0;margin:0}
|
||||||
@@ -54,7 +47,7 @@ class PluginBluditToc extends Plugin {
|
|||||||
.bltoc-link{display:block;color:var(--bltoc-text,#777);text-decoration:none;line-height:1.5;transition:color .15s ease}
|
.bltoc-link{display:block;color:var(--bltoc-text,#777);text-decoration:none;line-height:1.5;transition:color .15s ease}
|
||||||
.bltoc-link:hover{color:var(--bltoc-hover,#111);text-decoration:none}
|
.bltoc-link:hover{color:var(--bltoc-hover,#111);text-decoration:none}
|
||||||
.bltoc-link.active{color:var(--bltoc-active,#0066cc);font-weight:600}
|
.bltoc-link.active{color:var(--bltoc-active,#0066cc);font-weight:600}
|
||||||
.bltoc-fab{display:none;position:fixed;left:1.5rem;bottom:1.5rem;z-index:1500;width:46px;height:46px;border-radius:50%;border:none;background:var(--bltoc-fab-bg,#0066cc);color:var(--bltoc-fab-color,#fff);align-items:center;justify-content:center;cursor:pointer;box-shadow:0 3px 10px rgba(0,0,0,.25);transition:transform .15s ease,box-shadow .15s ease}
|
.bltoc-fab{display:none;position:fixed;left:1.5rem;bottom:1.5rem;z-index:1500;width:46px;height:46px;border-radius:50%;border:none;background:#0066cc;color:#fff;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 3px 10px rgba(0,0,0,.25);transition:transform .15s ease,box-shadow .15s ease}
|
||||||
.bltoc-fab:hover{transform:scale(1.08);box-shadow:0 4px 14px rgba(0,0,0,.3)}
|
.bltoc-fab:hover{transform:scale(1.08);box-shadow:0 4px 14px rgba(0,0,0,.3)}
|
||||||
.bltoc-fab svg{display:block}
|
.bltoc-fab svg{display:block}
|
||||||
.bltoc-drawer{position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.45);opacity:0;visibility:hidden;transition:opacity .22s ease,visibility 0s linear .22s}
|
.bltoc-drawer{position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.45);opacity:0;visibility:hidden;transition:opacity .22s ease,visibility 0s linear .22s}
|
||||||
@@ -83,41 +76,44 @@ class PluginBluditToc extends Plugin {
|
|||||||
public function siteBodyEnd() {
|
public function siteBodyEnd() {
|
||||||
$title = json_encode($this->getValue('title'));
|
$title = json_encode($this->getValue('title'));
|
||||||
$navbarHeight = (int) $this->getValue('navbarHeight');
|
$navbarHeight = (int) $this->getValue('navbarHeight');
|
||||||
$minWidth = (int) $this->getValue('minWidth');
|
|
||||||
|
|
||||||
return '<script>(function(){' .
|
return '<script>(function(){' .
|
||||||
'var TITLE=' . $title . ',' .
|
'var TITLE=' . $title . ',' .
|
||||||
'NAVBAR_HEIGHT=' . $navbarHeight . ',' .
|
'NAVBAR_HEIGHT=' . $navbarHeight . ';' .
|
||||||
'MIN_WIDTH=' . $minWidth . ';' .
|
|
||||||
'var s=document.createElement("style");' .
|
/* ---- Find content area ---- */
|
||||||
's.textContent="@media(min-width:"+MIN_WIDTH+"px){.bltoc-sidebar.bltoc-ready{display:block}}"' .
|
|
||||||
'+"@media(max-width:"+(MIN_WIDTH-1)+"px){.bltoc-fab.bltoc-ready{display:flex}}";' .
|
|
||||||
'document.head.appendChild(s);' .
|
|
||||||
'var content=document.querySelector(".content")||document.querySelector("article .entry-content")||document.querySelector(".entry-content")||document.querySelector(".post-content");' .
|
'var content=document.querySelector(".content")||document.querySelector("article .entry-content")||document.querySelector(".entry-content")||document.querySelector(".post-content");' .
|
||||||
'if(!content)return;' .
|
'if(!content)return;' .
|
||||||
|
|
||||||
|
/* ---- Collect headings ---- */
|
||||||
'var headings=content.querySelectorAll("h2,h3,h4");' .
|
'var headings=content.querySelectorAll("h2,h3,h4");' .
|
||||||
'if(!headings.length)return;' .
|
'if(!headings.length)return;' .
|
||||||
|
|
||||||
|
/* ---- Assign IDs ---- */
|
||||||
'var usedIds={};' .
|
'var usedIds={};' .
|
||||||
'Array.prototype.forEach.call(headings,function(h){' .
|
'Array.prototype.forEach.call(headings,function(h){' .
|
||||||
'if(h.id){usedIds[h.id]=true;}' .
|
'if(h.id){usedIds[h.id]=true;}' .
|
||||||
'else{' .
|
'else{' .
|
||||||
'var base=h.textContent.trim().toLowerCase().replace(/[^a-z0-9\s-]/g,"").replace(/\s+/g,"-").replace(/^-+|-+$/g,"")||"heading";' .
|
'var base=h.textContent.trim().toLowerCase().replace(/[^a-z0-9\s-]/g,"").replace(/\s+/g,"-").replace(/^-+|-+$/g,"")||"heading";' .
|
||||||
'var id=base,n=2;' .
|
'var id=base,n=2;while(usedIds[id]){id=base+"-"+(n++);}' .
|
||||||
'while(usedIds[id]){id=base+"-"+(n++);}' .
|
|
||||||
'usedIds[id]=true;h.id=id;' .
|
'usedIds[id]=true;h.id=id;' .
|
||||||
'}' .
|
'}' .
|
||||||
'h.style.scrollMarginTop=NAVBAR_HEIGHT+"px";' .
|
'h.style.scrollMarginTop=NAVBAR_HEIGHT+"px";' .
|
||||||
'});' .
|
'});' .
|
||||||
|
|
||||||
|
/* ---- Build ToC list ---- */
|
||||||
'var ul=document.createElement("ul");ul.className="bltoc-list";' .
|
'var ul=document.createElement("ul");ul.className="bltoc-list";' .
|
||||||
'Array.prototype.forEach.call(headings,function(h){' .
|
'Array.prototype.forEach.call(headings,function(h){' .
|
||||||
'var li=document.createElement("li");li.className="bltoc-item bltoc-"+h.tagName.toLowerCase();' .
|
'var li=document.createElement("li");li.className="bltoc-item bltoc-"+h.tagName.toLowerCase();' .
|
||||||
'var a=document.createElement("a");a.href="#"+h.id;a.textContent=h.textContent;a.className="bltoc-link";' .
|
'var a=document.createElement("a");a.href="#"+h.id;a.textContent=h.textContent;a.className="bltoc-link";' .
|
||||||
'li.appendChild(a);ul.appendChild(li);' .
|
'li.appendChild(a);ul.appendChild(li);' .
|
||||||
'});' .
|
'});' .
|
||||||
|
|
||||||
|
/* ---- Populate nav ---- */
|
||||||
'var nav=document.getElementById("bltoc-nav");' .
|
'var nav=document.getElementById("bltoc-nav");' .
|
||||||
'if(nav)nav.appendChild(ul);' .
|
'if(nav)nav.appendChild(ul);' .
|
||||||
'var sb=document.getElementById("bltoc-sidebar");' .
|
|
||||||
'if(sb)sb.classList.add("bltoc-ready");' .
|
/* ---- Scroll-spy ---- */
|
||||||
'var arr=Array.prototype.slice.call(headings);' .
|
'var arr=Array.prototype.slice.call(headings);' .
|
||||||
'function updateActive(){' .
|
'function updateActive(){' .
|
||||||
'var sy=window.scrollY||window.pageYOffset,thresh=sy+NAVBAR_HEIGHT+16,active=null;' .
|
'var sy=window.scrollY||window.pageYOffset,thresh=sy+NAVBAR_HEIGHT+16,active=null;' .
|
||||||
@@ -127,7 +123,9 @@ class PluginBluditToc extends Plugin {
|
|||||||
'if(active){var lnk=nav.querySelector("a[href=\'#"+active.id+"\']");if(lnk)lnk.classList.add("active");}' .
|
'if(active){var lnk=nav.querySelector("a[href=\'#"+active.id+"\']");if(lnk)lnk.classList.add("active");}' .
|
||||||
'}' .
|
'}' .
|
||||||
'window.addEventListener("scroll",updateActive,{passive:true});updateActive();' .
|
'window.addEventListener("scroll",updateActive,{passive:true});updateActive();' .
|
||||||
'var fab=document.createElement("button");fab.type="button";fab.className="bltoc-fab bltoc-ready";' .
|
|
||||||
|
/* ---- Mobile FAB + drawer ---- */
|
||||||
|
'var fab=document.createElement("button");fab.type="button";fab.className="bltoc-fab";' .
|
||||||
'fab.setAttribute("aria-label",TITLE);fab.setAttribute("aria-expanded","false");' .
|
'fab.setAttribute("aria-label",TITLE);fab.setAttribute("aria-expanded","false");' .
|
||||||
'fab.innerHTML=\'<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></svg>\';' .
|
'fab.innerHTML=\'<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></svg>\';' .
|
||||||
'var drawer=document.createElement("div");drawer.className="bltoc-drawer";' .
|
'var drawer=document.createElement("div");drawer.className="bltoc-drawer";' .
|
||||||
@@ -137,12 +135,31 @@ class PluginBluditToc extends Plugin {
|
|||||||
'var clone=ul.cloneNode(true);' .
|
'var clone=ul.cloneNode(true);' .
|
||||||
'panel.appendChild(dt);panel.appendChild(clone);drawer.appendChild(panel);' .
|
'panel.appendChild(dt);panel.appendChild(clone);drawer.appendChild(panel);' .
|
||||||
'document.body.appendChild(fab);document.body.appendChild(drawer);' .
|
'document.body.appendChild(fab);document.body.appendChild(drawer);' .
|
||||||
|
|
||||||
'function openDrawer(){drawer.classList.add("is-open");document.body.style.overflow="hidden";fab.setAttribute("aria-expanded","true");}' .
|
'function openDrawer(){drawer.classList.add("is-open");document.body.style.overflow="hidden";fab.setAttribute("aria-expanded","true");}' .
|
||||||
'function closeDrawer(){drawer.classList.remove("is-open");document.body.style.overflow="";fab.setAttribute("aria-expanded","false");}' .
|
'function closeDrawer(){drawer.classList.remove("is-open");document.body.style.overflow="";fab.setAttribute("aria-expanded","false");}' .
|
||||||
'fab.addEventListener("click",openDrawer);' .
|
'fab.addEventListener("click",openDrawer);' .
|
||||||
'drawer.addEventListener("click",function(e){if(e.target===drawer)closeDrawer();});' .
|
'drawer.addEventListener("click",function(e){if(e.target===drawer)closeDrawer();});' .
|
||||||
'Array.prototype.forEach.call(clone.querySelectorAll("a"),function(a){a.addEventListener("click",closeDrawer);});' .
|
'Array.prototype.forEach.call(clone.querySelectorAll("a"),function(a){a.addEventListener("click",closeDrawer);});' .
|
||||||
'document.addEventListener("keydown",function(e){if((e.key==="Escape"||e.key==="Esc")&&drawer.classList.contains("is-open")){closeDrawer();fab.focus();}});' .
|
'document.addEventListener("keydown",function(e){if((e.key==="Escape"||e.key==="Esc")&&drawer.classList.contains("is-open")){closeDrawer();fab.focus();}});' .
|
||||||
|
|
||||||
|
/* ---- Dynamic positioning: place sidebar to the left of .content ---- */
|
||||||
|
'var sb=document.getElementById("bltoc-sidebar");' .
|
||||||
|
'var TOC_W=220,GAP=16;' .
|
||||||
|
'function positionSidebar(){' .
|
||||||
|
'if(!sb)return;' .
|
||||||
|
'var rect=content.getBoundingClientRect();' .
|
||||||
|
'if(rect.left>=TOC_W+GAP){' .
|
||||||
|
'sb.style.left=Math.max(8,rect.left-TOC_W-GAP)+"px";' .
|
||||||
|
'sb.style.display="block";' .
|
||||||
|
'fab.style.display="none";' .
|
||||||
|
'}else{' .
|
||||||
|
'sb.style.display="none";' .
|
||||||
|
'fab.style.display="flex";' .
|
||||||
|
'}' .
|
||||||
|
'}' .
|
||||||
|
'positionSidebar();' .
|
||||||
|
'window.addEventListener("resize",positionSidebar);' .
|
||||||
'})();</script>' . PHP_EOL;
|
'})();</script>' . PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user