New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

bit-docs-html-toc

Package Overview
Dependencies
Maintainers
4
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bit-docs-html-toc - npm Package Compare versions

Comparing version 1.0.0 to 1.0.1

attribute-connect.js

3

make-tree.js

@@ -66,3 +66,4 @@ module.exports = function makeTree(elements) {

var text = element.textContent;
var id = makeHeadingId(text);
var id = element.id || makeHeadingId(text);
element.id = id;
var level = getElementLevel(element);

@@ -69,0 +70,0 @@

{
"name": "bit-docs-html-toc",
"version": "1.0.0",
"version": "1.0.1",
"description": "table of contents bit-docs plugin",

@@ -30,2 +30,5 @@ "main": "toc.js",

"can-assign": "^1.3.1",
"can-define-lazy-value": "^1.1.0",
"can-dom-mutate": "^1.3.6",
"can-string": "^1.0.0",
"can-view-target": "^4.1.2"

@@ -32,0 +35,0 @@ },

@@ -32,9 +32,15 @@ # bit-docs-html-toc

In your template add a class **on-this-page-container**:
In your template add a `<bit-toc>` element:
```html
<div class="on-this-page-container"></div>
<bit-toc></bit-toc>
```
By default, all heading tags children of the first `article` tag on the page will
## Attributes
`<bit-toc>` supports the following attributes:
### heading-container-selector
By default, all heading tags children of the first `article` tag on the page will
be collected to create the table of contents; if you want to use a different element

@@ -44,7 +50,6 @@ just do:

```html
<div
class="on-this-page-container"
data-heading-container-selector="#my-custom-selector"
<bit-toc
heading-container-selector="#my-custom-selector"
>
</div>
</bit-toc>
```

@@ -54,4 +59,12 @@

### depth
To control the number of child headers that will be included in the TOC, use the
By default, only `h2` elements are collected. You
can change to include `<h3>` elements by setting depth like:
```html
<bit-toc depth="2"></bit-toc>
```
Alternatively, the number of child headers that will be included in the TOC, use the
`@outline` tag like so:

@@ -63,2 +76,21 @@

This will include `<h2>` and `<h3>` elements, rather than the normal `<h2>` only.
### child-tag
If you want `<li>`'s to be within an `<ol>` instead of a `<ul>`, this
can be configured like:
```html
<bit-toc child-tag="ol"></bit-toc>
```
## Methods
Call `.highlight()` to force an update of the `active` or `completed`
class names on the `<li>` elements:
```js
document.querySelector("bit-toc").highlight()
```
This happens automatically when the `heading-container-selector`
element is scrolled.

@@ -36,2 +36,3 @@ var $ = require("jquery");

var $items = $("#toc-container ul");
$("#toc-container li").removeAttr("class");
assert.equal(

@@ -46,3 +47,8 @@ $items.html(),

);
var ids = $.makeArray($("article h2").map(function(i, element) {
return element.id;
}));
assert.deepEqual(ids, ["usage","install","configure"]);
// remove headings container from the DOM

@@ -52,37 +58,2 @@ $("article").remove();

it("reads the headings container selector from the element", function() {
// set the container selector as a data-* attribute
$el.attr(
"data-headings-container-selector",
".my-custom-container"
);
var headings = [
'<div class="my-custom-container">',
"<h2>Usage</h2>",
"<h2>Install</h2>",
"<h2>Configure</h2>",
"<h2>Configure</h2>",
"</div>"
];
// append the headings to the DOM and then instantiate the control
$("body").append(headings.join(""));
new TOCContainer($el.get(0));
var $items = $("#toc-container ul");
assert.equal(
$items.html(),
[
'<li><a href="#usage">Usage</a></li>',
'<li><a href="#install">Install</a></li>',
'<li><a href="#configure">Configure</a></li>',
'<li><a href="#configure-1">Configure</a></li>'
].join(""),
"should create table of contents from the headings inside container"
);
// remove headings container from the DOM
$(".my-custom-container").remove();
});
});
var $ = require("jquery");
var assert = require("chai/chai").assert;
var TableOfContents = require("../toc-control");
var safeCustomElement = require("../safe-custom-element");

@@ -8,14 +9,15 @@ require("steal-mocha");

describe("TableOfContents", function() {
if(!safeCustomElement.supported) {
return;
}
var $el, $testArea;
beforeEach(function() {
$("body").append("<ul id=\"toc-test\"></ul>");
$testArea = $("#test-area");
$el = $("#toc-test");
$testArea = $("#test-area");
});
afterEach(function() {
$el.remove();
$testArea.empty();
});

@@ -31,11 +33,12 @@

$testArea.html(headings.join(""));
$testArea.html("<article id='article'>"+headings.join("")+"</article>"+
"<bit-toc child-tag='ul' headings-container-selector='#article' depth='1'>");
new TableOfContents($el.get(0), {
/*new TableOfContents($el.get(0), {
tagName: "ul",
depth: 1,
headingsContainerSelector: "#test-area"
});
assert.equal($el.html(), [
});*/
$('bit-toc li').removeAttr("class"); // remove for nice html
assert.equal(document.querySelector('bit-toc ul').innerHTML, [
'<li><a href="#usage">Usage</a></li>',

@@ -58,10 +61,8 @@ '<li><a href="#install">Install</a></li>',

$testArea.html(headings.join(""));
new TableOfContents($el.get(0), {
tagName: "ul",
depth: 3,
headingsContainerSelector: "#test-area"
});
$testArea.html("<article id='article'>"+headings.join("")+"</article>"+
"<bit-toc child-tag='ul' headings-container-selector='#article' depth='3'>");
var $el = $('bit-toc>ul');
assert.equal($el.find(">li:eq(0) ul").length, 1, "bower has a nested list");

@@ -82,10 +83,9 @@ assert.equal($el.find(">li:eq(1) ul").length, 2, "npm two nested lists");

$testArea.html(headings.join(""));
$testArea.html("<article id='article'>"+headings.join("")+"</article>"+
"<bit-toc child-tag='ul' headings-container-selector='#article' depth='1'>");
new TableOfContents($el.get(0), {
tagName: "ul",
depth: 1,
headingsContainerSelector: "#test-area"
});
var $el = $('bit-toc>ul');
$('bit-toc li').removeAttr("class"); // remove for nice html
assert.equal($el.html(), [

@@ -97,2 +97,64 @@ '<li><a href="#bower">Bower</a></li>',

});
it("highlights what has been completed", function(){
var headings = [
"<h2>Bower</h2>",
"<p>Install</p>",
"<h2>NPM</h2>",
"<p>Install</p>",
"<h2>Configure</h2>",
"<p>xyz</p>",
"<h2>Writing Modules</h2>",
"<p>writing modules</p>",
"<h2>Extra</h2>",
"<p>final</p>"
];
$testArea.html("<article id='article'>"+headings.join("")+"</article>"+
"<bit-toc headings-container-selector='#article'></bit-toc>");
$("article").css({
position: "fixed",
top: 0,
height: 200,
left: 200,
width: 600,
backgroundColor: "gray",
overflowY: "auto"
});
$("article p").css({
height: "500px",
border: "solid 1px red"
});
$("bit-toc")[0].highlight();
function getCompletedAndActive(){
var result = {completed: [], active: []}
$("bit-toc li").each(function(i, node){
if(node.classList.contains("completed")) {
result.completed.push(node.textContent)
}
if(node.classList.contains("active")) {
result.active.push(node.textContent)
}
});
return result;
}
assert.deepEqual(getCompletedAndActive(), {
active: ["Bower"],
completed: []
}, "initialized correctly");
$("#article").scrollTop(600);
$("bit-toc")[0].highlight(); // so we don't have to wait for the throttling
assert.deepEqual(getCompletedAndActive(), {
active: ["NPM"],
completed: ["Bower"]
}, "initialized correctly");
});
});

@@ -1,49 +0,18 @@

var assign = require("can-assign");
var TableOfContents = require("./toc-control");
require("./toc-control");
// this is legacy
var TocControl = function(el){
el.style.display = "none";
var depth = this.getOutlineDepth();
var tagName = this.getOutlineTagName();
var selector = this.getHeadingsContainerSelector(el);
var toc = document.createElement(tagName);
var toc = document.createElement("bit-toc");
toc.className = "on-this-page";
el.appendChild(toc);
new TableOfContents(toc, {
depth: depth,
tagName: tagName,
headingsContainerSelector: selector
});
if(el.append) {
el.append(toc);
} else {
el.appendChild(toc);
}
};
assign(TocControl.prototype, {
getDocObject: function() {
return window.docObject || {};
},
getOutlineTagName: function() {
var docObject = this.getDocObject();
var outline = docObject.outline || {};
return (outline.tag === "ol") ? "ol" : "ul";
},
getOutlineDepth: function() {
var docObject = this.getDocObject();
var depth = docObject.outline && docObject.outline.depth;
return (typeof depth === "number" ? Math.min(depth, 6) : 1);
},
getHeadingsContainerSelector: function(el) {
var selector = el.dataset.headingsContainerSelector;
return selector ? selector : "article";
}
});
module.exports = TocControl;
var viewTarget = require("can-view-target");
var makeTree = require("./make-tree");
var assign = require("can-assign");
var lazy = require("can-define-lazy-value");
var safeCustomElement = require("./safe-custom-element");
var attributeConnect = require("./attribute-connect");
// data { tagName: tagName, node: node }
function debounce(func, wait) {
var timeout;
return function executedFunction() {
var context = this;
var args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (!timeout) {
func.apply(context, args);
}
};
}
// data { childTag: childTag, node: node }
var renderNodeTarget = viewTarget([{

@@ -19,5 +43,5 @@ tag: "li",

function(data){
var container = document.createElement(data.tagName);
var container = document.createElement(data.childTag);
data.node.children.forEach(function(node){
container.appendChild(renderNodeTarget.hydrate({node: node, tagName: data.tagName}));
container.appendChild(renderNodeTarget.hydrate({node: node, childTag: data.childTag}));
});

@@ -31,22 +55,8 @@ if(data.node.children.length) {

}]);
/*
var renderNode = stache(
"<li>" +
"<a href='#{{node.id}}'>{{node.text}}</a>" +
"{{#if node.children.length}}" +
"{{#is tagName 'ul'}}" +
"<ul>{{#each node.children}}{{renderNode tagName .}}{{/each}}</ul>" +
"{{else}}" +
"<ol>{{#each node.children}}{{renderNode tagName .}}{{/each}}</ol>" +
"{{/is}}" +
"{{/if}}" +
"</li>"
);
*/
// data - { nodes: titles, tagName: this.tagName }
// data - { nodes: titles, childTag: this.childTag }
var template = function(data){
var container = document.createDocumentFragment();
var container = document.createElement(data.childTag);
data.nodes.forEach(function(node){
container.appendChild(renderNodeTarget.hydrate({node: node, tagName: data.tagName}));
container.appendChild(renderNodeTarget.hydrate({node: node, childTag: data.childTag}));
});

@@ -56,49 +66,64 @@ return container;

var connectAttribute = attributeConnect();
//var template = stache("{{#each nodes}}{{renderNode tagName .}}{{/each}}");
/*stache.registerSimpleHelper("renderNode", function(tagName, node) {
return renderNode({ tagName: tagName, node: node });
});*/
var BitToc = function(){
console.log("initialized");
connectAttribute.initialize(this);
this.teardowns = [];
var TocControl = function(el, options){
this.depth = options.depth;
this.tagName = options.tagName;
this.headingsContainerSelector = options.headingsContainerSelector;
};
var prototype = {
connectedCallback: function() {
var titles = this.collectTitles();
var titles = this.titleTree;
// If there are no titles, bail
if (!titles.length) {
el.parentNode.removeChild(el);
return;
} else {
el.parentNode.style.display = 'block';
}
// If there are no titles, bail
if (!titles.length) {
this.parentNode.removeChild(this);
return;
} else {
this.parentNode.style.display = 'block';
}
// Append our template
el.appendChild(template({
nodes: titles,
tagName: this.tagName
}));
};
// Append our template
this.appendChild(template({
nodes: titles,
childTag: this.outlineTagName
}));
assign(TocControl.prototype, {
this.setupHighlighting();
},
get docObject() {
return window.docObject || {};
},
get outlineTagName(){
if(this.childTag) {
return this.childTag;
} else {
var docObject = this.docObject;
var outline = docObject.outline || {};
return (outline.tag === "ol") ? "ol" : "ul";
}
},
get outlineDepth(){
if(this.depth) {
return parseInt(this.depth);
} else {
var docObject = this.docObject;
var depth = docObject.outline && docObject.outline.depth;
return (typeof depth === "number" ? Math.min(depth, 6) : 1);
}
},
get containerSelector(){
return this.headingsContainerSelector || "article";
},
makeSelector: function(tagName) {
var container = this.headingsContainerSelector;
var container = this.containerSelector;
return container + " " + tagName;
},
collectTitles: function() {
var selector = this.getHeadings()
.map(this.makeSelector.bind(this))
.join(",");
var titles = selector ? document.querySelectorAll(selector) : [];
return makeTree(titles);
},
getHeadings: function() {
var headings = [];
for(var i = 0; i < this.depth; i++) {
for(var i = 0; i < this.outlineDepth; i++) {
headings.push("h" + (i + 2));

@@ -108,4 +133,92 @@ }

return headings;
}
},
setupHighlighting: function(){
this.article = document.querySelector(this.containerSelector);
if(this.article) {
var highlight = debounce(this.highlight.bind(this),50);
this.article.addEventListener("scroll",highlight);
this.teardowns.push(function(){
this.article.removeEventListener("scroll", highlight);
}.bind(this));
this.highlight();
}
},
// completed only once the one after it is in the page and 1/2 way visible.
//
highlight: function(){
var articleRect = this.article.getBoundingClientRect();
var buttons = this.buttons;
var positions = this.titles.map(function(header, i){
return {
header: header,
rect: header.getBoundingClientRect(),
button: buttons[i]
};
});
// this simulates a header at the end of the page
positions.push({
rect: {
top: articleRect.top + this.article.scrollHeight - this.article.scrollTop
}
});
// loop through the actual headers and their positions
positions.slice(0, positions.length - 1).forEach(function(position, index){
position.button.classList.remove("completed","active");
var curRect = position.rect;
var curDistance = curRect.top - articleRect.top;
var nextRect = positions[index+1].rect;
var nextDistance = nextRect.top - articleRect.top;
// =====[CUR=NEXT]===
if( nextDistance >= 0 && nextDistance <= articleRect.height && curDistance >= 0 && curDistance <= articleRect.height ) {
position.button.classList.add("active");
}
// =======CUR=====[=NEXT=|===]======
// =======CUR=======NEXT=====[===|===]===
else if( nextDistance < (articleRect.height / 2) ) {
position.button.classList.add("completed");
}
// ======[=CUR=|=NEXT=]======
// ======[=CUR=|===]=NEXT====
// ====CUR=[===|=NEXT]=======
// ====CUR=[===|===]=NEXT====
else if( nextDistance >= (articleRect.height / 2) && curDistance < (articleRect.height / 2) ) {
position.button.classList.add("active");
}
});
},
disconnectedCallback: function(){
this.teardowns.forEach(function(teardown){
teardown();
});
},
attributeChangedCallback: connectAttribute.attributeChangedCallback
};
lazy(prototype,"titles", function(){
var selector = this.getHeadings()
.map(this.makeSelector.bind(this))
.join(",");
// we should save this ...
return selector ? Array.from( document.querySelectorAll(selector) ) : [];
});
module.exports = TocControl;
lazy(prototype,"titleTree", function(){
return makeTree(this.titles);
});
lazy(prototype,"buttons", function(){
return this.querySelectorAll("li");
});
BitToc = safeCustomElement("bit-toc",BitToc, prototype);
module.exports = BitToc;
/**
* @parent bit-docs-html-toc/static
* @module {function} bit-docs-html-toc/toc.js
*
*
* Main front end JavaScript file for static portion of this plugin.
*
*
* @signature `TOCContainer(el)`
*
*
* Hydrates the container element with class `on-this-page-container` with the
* headers from the page.
*
*
* If `DEPTH` was specified to the [bit-docs-html-toc/tags/outline] tag, then
* only headers less than and including that depth will be hydrated.
*
*
* @param {HTMLElement} el The HTML element to hydrate.
*
*
* @body
*/
var TOCContainer = require("./toc-container-control");
var el = document.getElementsByClassName("on-this-page-container");
if (el.length) {
new TOCContainer(el.item(0));
} else {
console.log("An element with class 'on-this-page-container' is required");
}
require("./toc");

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc