Notion Sticky TOC (2022 Available)

Set Notion TOC Sticky.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==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 } );
})();