🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

toc-generate

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

toc-generate - npm Package Compare versions

Comparing version
1.0.1
to
1.0.2
+13
dist/toc-generate.module.d.ts
type propsTocGenerate = {
contentWrapperSelector?: string;
tocSelector?: string | undefined;
headingLevelFrom?: number;
headingLevelTo?: number;
showsHighLight?: boolean;
showsParentHighlight?: boolean;
viewablePercentToHighlight?: number;
};
declare function tocGenerate(props: propsTocGenerate): HTMLOListElement;
export default tocGenerate;
export { default, default as tocGenerate } from './dist/toc-generate.module';
function createTocElement(selector, heading) {
const li = document.createElement("li");
const a = document.createElement("a");
heading.id ||= `${heading.textContent
.replace(/\s/g, "-")
.toLowerCase()}-${Math.random().toString(36).substring(2, 15)}`;
a.textContent = heading.textContent;
a.href = `#${heading.id}`;
// Add the link to the li item
li.appendChild(a);
selector.appendChild(li);
return li;
}
export default createTocElement;
function findParentScope(tocTree, level) {
for (let i = tocTree.length - 1; i >= 0; i--) {
if (tocTree[i].level === level) {
return tocTree[i].scope;
}
}
return toc;
}
export default findParentScope;
function queryAllHeadings(headingLevelFrom, headingLevelTo, scopeElement) {
let headingSelector = "";
for (let i = headingLevelFrom; i <= headingLevelTo; ++i) {
headingSelector += `h${i},`;
}
return scopeElement.querySelectorAll(headingSelector.slice(0, -1));
}
export default queryAllHeadings;
import setClass from "./setClass";
function scrollHighLight(headings, tocTree, viewablePercentToHighlight, showsParentHighlight) {
for (let i = 0; i < headings.length; i++) {
const heading = headings[i];
const safeNextIndex = (i + 1 < headings.length) ? i + 1 : i; // If the next heading is the last one, use the current heading
const safePrevIndex = (i - 1 >= 0) ? i - 1 : 1; // First heading not have parent
const nextHeading = headings[safeNextIndex];
const parentToc = tocTree[i].parent;
const elementToc = tocTree[i].selector;
const prevElementToc = tocTree[safePrevIndex];
function highlight() {
let { bottom, top } = heading.getBoundingClientRect();
const scope = nextHeading.getBoundingClientRect().top - bottom;
const scopeSplit = (scope * viewablePercentToHighlight) / 100;
const scopeCanView = scope - scopeSplit;
const windowHeight = window.innerHeight;
if (bottom + scopeCanView <= windowHeight && top + scopeSplit >= 0) {
setClass("highlight",'add', [heading, elementToc]);
if (showsParentHighlight && parentToc) {
setClass("parent_highlight", 'add', [parentToc]);
}
} else if (top + scopeSplit <= 0 || bottom > windowHeight) {
setClass("highlight", 'remove', [heading, elementToc]);
if (showsParentHighlight && prevElementToc.parent) {
setClass("parent_highlight", 'remove', [heading, elementToc, prevElementToc.selector]);
}
}
}
// Add event listener to scroll
highlight(); // Run on load
document.addEventListener("scroll", highlight);
}
}
export default scrollHighLight;
/**
* Set the class of the elements
*
* @param {string} className - The class name to add or remove
* @param {add|remove} type - The type of action to perform
* @param {...any} elements - The elements to add or remove the class from
*/
function setClass(className = "highlight", type = "add", elements) {
elements.forEach((element) => {
if (element !== undefined) {
element.classList[type](className);
}
});
}
export default setClass;
import queryAllHeadings from "./queryAllHeadings";
import findParentScope from "./findParentScope";
import createTocElement from "./createTocElement";
import scrollHighLight from "./scrollHighLight";
function tocGenerate(props) {
const contentWrapperSelector = props.contentWrapperSelector || "body";
const tocSelector = props.tocSelector;
const headingLevelFrom = props.headingLevelFrom || 2;
const headingLevelTo = props.headingLevelTo || 6;
const viewablePercentToHighlight = props.viewablePercentToHighlight || 70;
const showsHighLight = props.showsHighLight || false;
const showsParentHighlight = props.showsParentHighlight || false;
const tocElement = tocSelector && document.querySelector(tocSelector);
const scopeElement = document.querySelector(contentWrapperSelector);
// Get all h-from - h-to elements
const headings = queryAllHeadings(headingLevelFrom, headingLevelTo, scopeElement);
// Create a list to hold the headings
const toc = document.createElement("ol");
const tocTree = [{ level: headingLevelFrom, scope: toc }];
headings.forEach((heading) => {
const headingLevel = Number(heading.tagName.slice(1));
const prevToc = tocTree[tocTree.length - 1];
if (prevToc.level < headingLevel) {
const ol = document.createElement("ol");
const a = createTocElement(ol, heading);
prevToc.scope.appendChild(ol);
tocTree.push({ level: headingLevel, scope: ol, selector: a, parent: tocTree.at(-1).selector });
} else if (prevToc.level > headingLevel) {
const parentToc = findParentScope(tocTree, headingLevel);
const a = createTocElement(parentToc, heading);
tocTree.push({ level: headingLevel, scope: parentToc, selector: a });
} else {
const a = createTocElement(prevToc.scope, heading);
tocTree.push({ level: headingLevel, scope: prevToc.scope, selector: a, ...(prevToc.parent && { parent: prevToc.parent })});
}
});
tocTree.shift();
// Append the list to the toc element
if (tocElement) {
tocElement.appendChild(toc);
}
if (showsHighLight) {
scrollHighLight(headings, tocTree, viewablePercentToHighlight, showsParentHighlight);
}
return toc;
}
export default tocGenerate;
+1
-0

@@ -0,1 +1,2 @@

/** @typedef { import('./toc-generate.d')} */
function e(e,t){for(let n=e.length-1;n>=0;n--)if(e[n].level===t)return e[n].scope;return toc}function t(e,t){const n=document.createElement("li"),o=document.createElement("a");return t.id||=`${t.textContent.replace(/\s/g,"-").toLowerCase()}-${Math.random().toString(36).substring(2,15)}`,o.textContent=t.textContent,o.href=`#${t.id}`,n.appendChild(o),e.appendChild(n),n}function n(e="highlight",t="add",n){n.forEach((n=>{void 0!==n&&n.classList[t](e)}))}function o(o){const l=o.contentWrapperSelector||"body",r=o.tocSelector,c=o.headingLevelFrom||2,i=o.headingLevelTo||6,s=o.viewablePercentToHighlight||70,h=o.showsHighLight||!1,a=o.showsParentHighlight||!1,d=r&&document.querySelector(r),p=function(e,t,n){let o="";for(let n=e;n<=t;++n)o+=`h${n},`;return n.querySelectorAll(o.slice(0,-1))}(c,i,document.querySelector(l)),g=document.createElement("ol"),u=[{level:c,scope:g}];return p.forEach((n=>{const o=Number(n.tagName.slice(1)),l=u[u.length-1];if(l.level<o){const e=document.createElement("ol"),r=t(e,n);l.scope.appendChild(e),u.push({level:o,scope:e,selector:r,parent:u.at(-1).selector})}else if(l.level>o){const l=e(u,o),r=t(l,n);u.push({level:o,scope:l,selector:r})}else{const e=t(l.scope,n);u.push({level:o,scope:l.scope,selector:e,...l.parent&&{parent:l.parent}})}})),u.shift(),d&&d.appendChild(g),h&&function(e,t,o,l){for(let c=0;c<e.length;c++){const i=e[c],s=c-1>=0?c-1:1,h=e[c+1<e.length?c+1:c],a=t[c].parent,d=t[c].selector,p=t[s];function r(){let{bottom:e,top:t}=i.getBoundingClientRect();const r=h.getBoundingClientRect().top-e,c=r*o/100,s=r-c,g=window.innerHeight;e+s<=g&&t+c>=0?(n("highlight","add",[i,d]),l&&a&&n("parent_highlight","add",[a])):(t+c<=0||e>g)&&(n("highlight","remove",[i,d]),l&&p.parent&&n("parent_highlight","remove",[i,d,p.selector]))}r(),document.addEventListener("scroll",r)}}(p,u,s,a),g}export{o as default};
+1
-1

@@ -9,3 +9,3 @@ {

},
"version": "1.0.1",
"version": "1.0.2",
"main": "index.js",

@@ -12,0 +12,0 @@ "scripts": {

# TOC Generate
| Create a table of contents for a HTML, ESModule
[![https://nodei.co/npm/toc-generate.png?downloads=true&downloadRank=true&stars=true](https://nodei.co/npm/toc-generate.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/toc-generate)
### Demo

@@ -35,2 +37,7 @@ [Demo Link](https://dunggramer.github.io/toc-generate/public/)

#### ESModule
```bash
npm i toc-generate
# or
yarn add toc-generate
```
You may not use `tocSelector` if the content is not already displayed.

@@ -37,0 +44,0 @@ ```js

import { terser } from "rollup-plugin-terser";
export default {
input: 'toc-generate/tocGenerate.js',
input: 'src/tocGenerate.js',
output: {

@@ -6,0 +6,0 @@ file: 'dist/toc-generate.js',

function createTocElement(selector, heading) {
const li = document.createElement("li");
const a = document.createElement("a");
heading.id ||= `${heading.textContent
.replace(/\s/g, "-")
.toLowerCase()}-${Math.random().toString(36).substring(2, 15)}`;
a.textContent = heading.textContent;
a.href = `#${heading.id}`;
// Add the link to the li item
li.appendChild(a);
selector.appendChild(li);
return li;
}
export default createTocElement;
function findParentScope(tocTree, level) {
for (let i = tocTree.length - 1; i >= 0; i--) {
if (tocTree[i].level === level) {
return tocTree[i].scope;
}
}
return toc;
}
export default findParentScope;
function queryAllHeadings(headingLevelFrom, headingLevelTo, scopeElement) {
let headingSelector = "";
for (let i = headingLevelFrom; i <= headingLevelTo; ++i) {
headingSelector += `h${i},`;
}
return scopeElement.querySelectorAll(headingSelector.slice(0, -1));
}
export default queryAllHeadings;
import setClass from "./setClass";
function scrollHighLight(headings, tocTree, viewablePercentToHighlight, showsParentHighlight) {
for (let i = 0; i < headings.length; i++) {
const heading = headings[i];
const safeNextIndex = (i + 1 < headings.length) ? i + 1 : i; // If the next heading is the last one, use the current heading
const safePrevIndex = (i - 1 >= 0) ? i - 1 : 1; // First heading not have parent
const nextHeading = headings[safeNextIndex];
const parentToc = tocTree[i].parent;
const elementToc = tocTree[i].selector;
const prevElementToc = tocTree[safePrevIndex];
function highlight() {
let { bottom, top } = heading.getBoundingClientRect();
const scope = nextHeading.getBoundingClientRect().top - bottom;
const scopeSplit = (scope * viewablePercentToHighlight) / 100;
const scopeCanView = scope - scopeSplit;
const windowHeight = window.innerHeight;
if (bottom + scopeCanView <= windowHeight && top + scopeSplit >= 0) {
setClass("highlight",'add', [heading, elementToc]);
if (showsParentHighlight && parentToc) {
setClass("parent_highlight", 'add', [parentToc]);
}
} else if (top + scopeSplit <= 0 || bottom > windowHeight) {
setClass("highlight", 'remove', [heading, elementToc]);
if (showsParentHighlight && prevElementToc.parent) {
setClass("parent_highlight", 'remove', [heading, elementToc, prevElementToc.selector]);
}
}
}
// Add event listener to scroll
highlight(); // Run on load
document.addEventListener("scroll", highlight);
}
}
export default scrollHighLight;
/**
* Set the class of the elements
*
* @param {string} className - The class name to add or remove
* @param {add|remove} type - The type of action to perform
* @param {...any} elements - The elements to add or remove the class from
*/
function setClass(className = "highlight", type = "add", elements) {
elements.forEach((element) => {
if (element !== undefined) {
element.classList[type](className);
}
});
}
export default setClass;
import queryAllHeadings from "./queryAllHeadings";
import findParentScope from "./findParentScope";
import createTocElement from "./createTocElement";
import scrollHighLight from "./scrollHighLight";
function tocGenerate(props) {
const contentWrapperSelector = props.contentWrapperSelector || "body";
const tocSelector = props.tocSelector;
const headingLevelFrom = props.headingLevelFrom || 2;
const headingLevelTo = props.headingLevelTo || 6;
const viewablePercentToHighlight = props.viewablePercentToHighlight || 70;
const showsHighLight = props.showsHighLight || false;
const showsParentHighlight = props.showsParentHighlight || false;
const tocElement = tocSelector && document.querySelector(tocSelector);
const scopeElement = document.querySelector(contentWrapperSelector);
// Get all h-from - h-to elements
const headings = queryAllHeadings(headingLevelFrom, headingLevelTo, scopeElement);
// Create a list to hold the headings
const toc = document.createElement("ol");
const tocTree = [{ level: headingLevelFrom, scope: toc }];
headings.forEach((heading) => {
const headingLevel = Number(heading.tagName.slice(1));
const prevToc = tocTree[tocTree.length - 1];
if (prevToc.level < headingLevel) {
const ol = document.createElement("ol");
const a = createTocElement(ol, heading);
prevToc.scope.appendChild(ol);
tocTree.push({ level: headingLevel, scope: ol, selector: a, parent: tocTree.at(-1).selector });
} else if (prevToc.level > headingLevel) {
const parentToc = findParentScope(tocTree, headingLevel);
const a = createTocElement(parentToc, heading);
tocTree.push({ level: headingLevel, scope: parentToc, selector: a });
} else {
const a = createTocElement(prevToc.scope, heading);
tocTree.push({ level: headingLevel, scope: prevToc.scope, selector: a, ...(prevToc.parent && { parent: prevToc.parent })});
}
});
tocTree.shift();
// Append the list to the toc element
if (tocElement) {
tocElement.appendChild(toc);
}
if (showsHighLight) {
scrollHighLight(headings, tocTree, viewablePercentToHighlight, showsParentHighlight);
}
return toc;
}
export default tocGenerate;