Notion Sticky TOC (2022 Available)

Set Notion TOC Sticky.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Notion Sticky TOC (2022 Available)
// @name:zh-CN   Notion 固定左侧 TOC (2022 亲测可用)
// @namespace    https://github.com/soraliu
// @version      0.6.0
// @description  Set Notion TOC Sticky.
// @description:zh-cn TOC 左侧固定
// @author       Sora Liu<[email protected]>
// @match        https://www.notion.so/*
// @grant        none
// @license MIT
// ==/UserScript==

/* jshint esversion:6 */
(function() {
    'use strict';
    // selectors
    const SELECTOR_NOTION_APP = 'notion-app';
    const SELECTOR_NOTION_SCROLLER = '.notion-scroller';
    const SELECTOR_NOTION_TOC = '.notion-table_of_contents-block';
    const SELECTOR_MODAL_PAGE = '.notion-peek-renderer'; // the selector which used to check if the page is in any modal

    // toc config
    const TOC_CONFIG_WIDTH = '168px';
    const TOC_CONFIG_LEFT = '256px';

    /* Helper function to wait for the element ready */
    const waitFor = (...selectors) => new Promise(resolve => {
        const delay = 500;
        const f = () => {
            const elements = selectors.map(selector => document.querySelector(selector));
            if (elements.every(element => element != null)) {
                resolve(elements);
            } else {
                setTimeout(f, delay);
            }
        };
        f();
    });

    // for performance
    const LISTENED_SELECTORS = new WeakMap();
    const addScrollListener = (selectors, fn) => {
        let lastKnownScrollPosition = 0;
        let ticking = false;

        fn(lastKnownScrollPosition); // init once

        selectors.forEach(selector => {
            if (LISTENED_SELECTORS.has(selector)) {
                return;
            }
            // set listened
            LISTENED_SELECTORS.set(selector, true);

            selector.addEventListener('scroll', function(e) {
                lastKnownScrollPosition = window.scrollY;

                if (!ticking) {
                    window.requestAnimationFrame(function() {
                        fn(lastKnownScrollPosition);
                        ticking = false;
                    });

                    ticking = true;
                }
            }, false);
        });
    };

    const callback = function(mutations) {
        waitFor(SELECTOR_NOTION_TOC).then(([el]) => {
            const toc = document.querySelector(SELECTOR_NOTION_TOC);
            const modal = document.querySelector(SELECTOR_MODAL_PAGE);
            if (!modal && toc) {
                toc.style.position = 'fixed';
                toc.style.top = '50%';
                toc.style.transform= 'translateY(-50%)';
                toc.style.zIndex = 999
                toc.style.maxHeight = 'calc(100vh - 168px)'
                toc.style.overflowY = 'auto'

                const sidebarWidth = window.innerWidth - toc.closest(SELECTOR_NOTION_SCROLLER).clientWidth
                toc.style.left = `${sidebarWidth + 16}px`;
                toc.style.width = `${(toc.closest(SELECTOR_NOTION_SCROLLER).clientWidth - 900) / 2}px`
            }
        });
    };

    const observer = new MutationObserver(callback);
    observer.observe(document.getElementById(SELECTOR_NOTION_APP), { childList: true, subtree: true } );
})();