// ==UserScript==
// @name notion TOC sider & go to top
// @namespace https://www.notion.so/
// @version 0.0.4
// @description display a fixed sider on the top-right of notion page which has a table of content block inside
// @author nan chen
// @match *://www.notion.so/*
// @icon https://www.google.com/s2/favicons?domain=notion.so
// @grant none
// @run-at document-idle
// @license MIT
// @downloadURL https://update.greasyfork.icu/scripts/446576/notion%20TOC%20sider%20%20go%20to%20top.user.js
// @updateURL https://update.greasyfork.icu/scripts/446576/notion%20TOC%20sider%20%20go%20to%20top.meta.js
// ==/UserScript==
(function () {
setTimeout(function () {
var tocEle = document.querySelector(
'.notion-table_of_contents-block'
);
var parent = document.querySelector('#notion-app');
function refresh() {
var tocMenu = document.querySelector('#notion-toc-menu-sider');
var closeButton = document.querySelector(
'#notion-toc-menu-sider-close-button'
);
if (tocMenu) {
parent.removeChild(tocMenu);
}
if (closeButton) {
parent.removeChild(closeButton);
}
}
function buildSider() {
var headings = [];
var parseLevel = function (outerHtml) {
if (outerHtml.includes('margin-left: 24px')) {
return 2;
}
if (outerHtml.includes('margin-left: 48px')) {
return 3;
}
return 1;
};
var tocEleToBuild = document.querySelector(
'.notion-table_of_contents-block'
);
if (!tocEleToBuild) return;
tocEleToBuild.childNodes[0].childNodes.forEach(function (n) {
if (n.nodeType === 1) {
var an = n.querySelector('a');
headings.push({
title: n.innerText,
level: parseLevel(n.outerHTML),
href: an.href,
});
}
});
var menu = document.createElement('aside');
menu.id = 'notion-toc-menu-sider';
var isExpanded = true;
parent.appendChild(menu);
menu.style.minWidth = '120px';
menu.style.maxHeight = '600px';
menu.style.overflowY = 'auto';
menu.style.position = 'fixed';
menu.style.top = '55px';
menu.style.right = '10px';
menu.style.zIndex = 100000;
menu.style.boxShadow =
'rgb(15 15 15 / 5%) 0px 0px 0px 1px, rgb(15 15 15 / 10%) 0px 3px 6px, rgb(15 15 15 / 20%) 0px 9px 24px';
var menuList = document.createElement('div');
menuList.style.maxWidth = '160px';
menuList.style.backgroundColor = '#fff';
menuList.style.padding = '10px';
menu.appendChild(menuList);
var closeButtonWrapper = document.createElement('div');
closeButtonWrapper.id = 'notion-toc-menu-sider-close-button';
var closeButton = document.createElement('div');
closeButtonWrapper.style.borderRadius = '12px';
closeButtonWrapper.style.backgroundColor = '#fff';
closeButtonWrapper.style.width = '24px';
closeButtonWrapper.style.height = '24px';
closeButtonWrapper.style.cursor = 'pointer';
closeButtonWrapper.style.zIndex = 100000;
closeButtonWrapper.style.position = 'absolute';
closeButtonWrapper.style.top = '30px';
closeButtonWrapper.style.right = '10px';
closeButton.style.width = '10px';
closeButton.style.height = '10px';
closeButton.style.borderLeft = '1px solid black';
closeButton.style.borderBottom = '1px solid black';
closeButton.style.transform = 'rotate(-45deg)';
closeButton.style.position = 'absolute';
closeButton.style.top = '10px';
closeButton.style.left = '7px';
parent.appendChild(closeButtonWrapper);
closeButtonWrapper.appendChild(closeButton);
setTimeout(function () {
closeButton.addEventListener('click', function () {
if (!isExpanded) {
closeButton.style.transform = 'rotate(-45deg)';
menu.style.display = 'block';
} else {
closeButton.style.transform = 'rotate(135deg)';
menu.style.display = 'none';
}
isExpanded = !isExpanded;
});
});
var lists = headings.map(function (h) {
var anchor = document.createElement('a');
anchor.innerText = h.title;
anchor.style.fontSize = '10px';
anchor.href = h.href;
anchor.style.cursor = 'pointer';
anchor.style.display = 'block';
anchor.style.textOverflow = 'ellipsis';
anchor.style.whiteSpace = 'nowrap';
anchor.style.overflow = 'hidden';
anchor.style.color = 'rgb(141,141,141)';
switch (h.level) {
case 1:
break;
case 2:
anchor.style.marginLeft = '16px';
break;
case 3:
anchor.style.marginLeft = '24px';
break;
default:
break;
}
return anchor;
});
lists.forEach(function (l) {
menuList.appendChild(l);
});
/**** to top ******/
var topAnchor = document.createElement('a');
topAnchor.name = 'top';
parent.prepend(topAnchor);
var toTop = document.createElement('div');
toTop.className = 'to-top';
toTop.innerHTML = ``;
parent.appendChild(toTop);
var toTopButton = document.querySelector('.to-top-button');
document.addEventListener('scroll', scrollFunction);
function scrollFunction() {
console.log('scrolling');
if (
document.body.scrollTop > 20 ||
document.documentElement.scrollTop > 20
) {
toTopButton.style.display = 'block';
} else {
toTopButton.style.display = 'none';
}
}
toTopButton.addEventListener('click', function (e) {
document.querySelector('#go-to-top').click();
});
}
buildSider();
var lastUrlWithoutHash = location.origin + location.pathname;
var lastUrl = location.href;
new MutationObserver(() => {
var url = location.href;
const urlWithoutHash = location.origin + location.pathname;
if (lastUrlWithoutHash !== urlWithoutHash) {
lastUrl = url;
lastUrlWithoutHash = urlWithoutHash;
onUrlChange();
}
}).observe(document, { subtree: true, childList: true });
function onUrlChange() {
setTimeout(function () {
var toc = document.querySelector(
'.notion-table_of_contents-block'
);
refresh();
if (toc) {
buildSider();
}
}, 0);
// console.log('change');
}
}, 2000);
})();