diff --git a/src/Elastic.Markdown/Assets/copybutton.ts b/src/Elastic.Markdown/Assets/copybutton.ts index 35cb8da22..31bbc5f2d 100644 --- a/src/Elastic.Markdown/Assets/copybutton.ts +++ b/src/Elastic.Markdown/Assets/copybutton.ts @@ -95,19 +95,6 @@ if (!iconCopy) { /** * Set up copy/paste for code blocks */ - -const runWhenDOMLoaded = cb => { - if (document.readyState != 'loading') { - cb() - } else if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', cb) - } else { - document.attachEvent('onreadystatechange', function() { - if (document.readyState == 'complete') cb() - }) - } -} - const codeCellId = index => `codecell${index}` // Clears selected text since ClipboardJS will select the text when copying @@ -145,9 +132,14 @@ const addCopyButtonToCodeCells = () => { // happens because we load ClipboardJS asynchronously. // Add copybuttons to all of our code cells - const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const COPYBUTTON_SELECTOR = '.markdown-content div.highlight pre'; const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) codeCells.forEach((codeCell, index) => { + + if (codeCell.id != "") { + return + } + const id = codeCellId(index) codeCell.setAttribute('id', id) @@ -256,6 +248,5 @@ var copyTargetText = (trigger) => { } export function initCopyButton() { - console.log("initCopyButton"); - runWhenDOMLoaded(addCopyButtonToCodeCells) + addCopyButtonToCodeCells() } diff --git a/src/Elastic.Markdown/Assets/hljs.ts b/src/Elastic.Markdown/Assets/hljs.ts index 675963dc4..c1e80e0d2 100644 --- a/src/Elastic.Markdown/Assets/hljs.ts +++ b/src/Elastic.Markdown/Assets/hljs.ts @@ -164,6 +164,6 @@ hljs.registerLanguage('esql', function() { hljs.addPlugin(mergeHTMLPlugin); export function initHighlight() { - - hljs.highlightAll(); + document.querySelectorAll('.markdown-content pre code:not(.hljs)') + .forEach((block) => { hljs.highlightElement(block as HTMLElement); }); } diff --git a/src/Elastic.Markdown/Assets/main.ts b/src/Elastic.Markdown/Assets/main.ts index e3b7a4e78..ff1534cf3 100644 --- a/src/Elastic.Markdown/Assets/main.ts +++ b/src/Elastic.Markdown/Assets/main.ts @@ -1,9 +1,23 @@ import {initTocNav} from "./toc-nav"; -import {initHighlight} from "./hljs"; import {initTabs} from "./tabs"; import {initCopyButton} from "./copybutton"; +import {initNav} from "./pages-nav"; +import {initHighlight} from "./hljs"; + + + +up.link.config.followSelectors.push('a[href]') +up.link.config.preloadSelectors.push('a[href]') + -initTocNav(); -initHighlight(); -initCopyButton(); -initTabs(); +up.compiler('.markdown-content, #toc-nav', () => { + window.scrollTo(0, 0); + const destroyTocNav = initTocNav(); + initNav(); + initHighlight(); + initCopyButton(); + initTabs(); + return () => { + destroyTocNav(); + } +}) diff --git a/src/Elastic.Markdown/Assets/pages-nav.ts b/src/Elastic.Markdown/Assets/pages-nav.ts index ff9df8073..6660cdb94 100644 --- a/src/Elastic.Markdown/Assets/pages-nav.ts +++ b/src/Elastic.Markdown/Assets/pages-nav.ts @@ -1,87 +1,21 @@ -import {$, $$} from "select-dom"; - -type NavExpandState = { - current:string, - selected: Record -}; -const PAGE_NAV_EXPAND_STATE_KEY = 'pagesNavState'; - -// Initialize the nav state from the session storage -// Return a function to keep the nav state in the session storage that should be called before the page is unloaded -function keepNavState(nav: HTMLElement): () => void { - - const currentNavigation = nav.dataset.currentNavigation; - const currentPageId = nav.dataset.currentPageId; - - let navState = JSON.parse(sessionStorage.getItem(PAGE_NAV_EXPAND_STATE_KEY) ?? "{}") as NavExpandState - if (navState.current !== currentNavigation) - { - sessionStorage.removeItem(PAGE_NAV_EXPAND_STATE_KEY); - navState = { current: currentNavigation } as NavExpandState; - } - if (currentPageId) - { - const currentPageLink = $('a[id="page-' + currentPageId + '"]', nav); - currentPageLink.classList.add('current'); - currentPageLink.classList.add('pointer-events-none'); - currentPageLink.classList.add('text-blue-elastic!'); - currentPageLink.classList.add('font-semibold'); - - const parentIds = nav.dataset.currentPageParentIds?.split(',') ?? []; - for (const parentId of parentIds) - { - const input = $('input[type="checkbox"][id=\"'+parentId+'\"]', nav) as HTMLInputElement; - if (input) { - input.checked = true; - const link = input.nextElementSibling as HTMLAnchorElement; - link.classList.add('font-semibold'); - } +import {$} from "select-dom"; + +function expandAllParents(navItem: HTMLElement) { + let parent = navItem.closest('li'); + while (parent) { + const input = parent.querySelector('input'); + if (input) { + (input as HTMLInputElement).checked = true; } - } - - // expand items previously selected - for (const groupId in navState.selected) - { - const input = $('input[type="checkbox"][id=\"'+groupId+'\"]', nav) as HTMLInputElement; - input.checked = navState.selected[groupId]; - } - - return () => { - // store all expanded groups - const inputs = $$('input[type="checkbox"]:checked', nav); - const selectedMap: Record = inputs - .filter(input => input.checked) - .reduce((state: Record, input) => { - const key = input.id; - const value = input.checked; - return { ...state, [key]: value}; - }, {}); - const state = { current: currentNavigation, selected: selectedMap }; - sessionStorage.setItem(PAGE_NAV_EXPAND_STATE_KEY, JSON.stringify(state)); - } -} - -type NavScrollPosition = number; -const PAGE_NAV_SCROLL_POSITION_KEY = 'pagesNavScrollPosition'; -const pagesNavScrollPosition: NavScrollPosition = parseInt( - sessionStorage.getItem(PAGE_NAV_SCROLL_POSITION_KEY) ?? '0' -); - - -// Initialize the nav scroll position from the session storage -// Return a function to keep the nav scroll position in the session storage that should be called before the page is unloaded -function keepNavPosition(nav: HTMLElement): () => void { - if (pagesNavScrollPosition) { - nav.scrollTop = pagesNavScrollPosition; - } - return () => { - sessionStorage.setItem(PAGE_NAV_SCROLL_POSITION_KEY, nav.scrollTop.toString()); + parent = parent.parentElement?.closest('li'); } } function scrollCurrentNaviItemIntoView(nav: HTMLElement, delay: number) { + const currentNavItem = $('.up-current', nav); + expandAllParents(currentNavItem); setTimeout(() => { - const currentNavItem = $('.current', nav); + if (currentNavItem && !isElementInViewport(currentNavItem)) { currentNavItem.scrollIntoView({ behavior: 'smooth', block: 'center' }); } @@ -102,13 +36,5 @@ export function initNav() { if (!pagesNav) { return; } - const keepNavStateCallback = keepNavState(pagesNav); - const keepNavPositionCallback = keepNavPosition(pagesNav); scrollCurrentNaviItemIntoView(pagesNav, 100); - window.addEventListener('beforeunload', () => { - keepNavStateCallback(); - keepNavPositionCallback(); - }, true); } - -initNav(); diff --git a/src/Elastic.Markdown/Assets/styles.css b/src/Elastic.Markdown/Assets/styles.css index e16fa0016..562a2c5a8 100644 --- a/src/Elastic.Markdown/Assets/styles.css +++ b/src/Elastic.Markdown/Assets/styles.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +/*@import "unpoly/unpoly.css";*/ @import "./fonts.css"; @import "./theme.css"; @import "./markdown/typography.css"; @@ -85,7 +86,7 @@ } .content-container { - @apply w-full max-w-[80ch] lg:w-[80ch]; + @apply w-full max-w-[80ch]; } .applies { @@ -119,9 +120,9 @@ :root { --outline-size: max(2px, 0.08em); - --outline-style: auto; + --outline-style: solid; --outline-color: var(--color-blue-elastic); - --outline-offset: 5; + --outline-offset: 10; } :is(a, button, input, textarea, summary):focus { @@ -137,3 +138,13 @@ :is(a, button, input, textarea, summary):focus:not(:focus-visible) { outline: none; } + +up-progress-bar { + @apply bg-pink-90!; +} + +#pages-nav { + .up-current { + @apply text-blue-elastic!; + } +} diff --git a/src/Elastic.Markdown/Assets/tabs.ts b/src/Elastic.Markdown/Assets/tabs.ts index cfc692dd0..dbf961e04 100644 --- a/src/Elastic.Markdown/Assets/tabs.ts +++ b/src/Elastic.Markdown/Assets/tabs.ts @@ -34,7 +34,7 @@ function ready() { /** @type {string[]} */ let groups = []; - document.querySelectorAll(".tabs-label").forEach((label) => { + document.querySelectorAll(".markdown-content .tabs-label").forEach((label) => { if (label instanceof HTMLElement) { let data = create_key(label); if (data) { @@ -57,12 +57,6 @@ function ready() { group ); if (tabParam) { - console.log( - "sphinx-design: Selecting tab id for group '" + - group + - "' from URL parameter: " + - tabParam - ); window.sessionStorage.setItem(storageKeyPrefix + group, tabParam); } } @@ -72,10 +66,6 @@ function ready() { storageKeyPrefix + group ); if (previousId === id) { - // console.log( - // "sphinx-design: Selecting tab from session storage: " + id - // ); - // @ts-ignore label.previousElementSibling.checked = true; } } @@ -101,6 +91,5 @@ function onSDLabelClick() { } export function initTabs() { - console.log("inittabs"); - document.addEventListener("DOMContentLoaded", ready, false); + ready(); } diff --git a/src/Elastic.Markdown/Assets/toc-nav.ts b/src/Elastic.Markdown/Assets/toc-nav.ts index 61bbb950b..394ee0d17 100644 --- a/src/Elastic.Markdown/Assets/toc-nav.ts +++ b/src/Elastic.Markdown/Assets/toc-nav.ts @@ -110,31 +110,45 @@ function updateIndicator(elements: TocElements) { } } -function setupSmoothScrolling(elements: TocElements) { - elements.tocLinks.forEach(link => { - link.addEventListener('click', (e) => { - const href = link.getAttribute('href'); - if (href?.charAt(0) === '#') { - e.preventDefault(); - const target = document.getElementById(href.slice(1)); - if (target) { - target.scrollIntoView({ behavior: 'smooth' }); - history.pushState(null, '', href); - } +function setupSmoothScrolling(elements: TocElements): () => void { + const clickHandler = (e: Event) => { + const link = e.currentTarget as HTMLAnchorElement; + const href = link.getAttribute('href'); + if (href?.charAt(0) === '#') { + e.preventDefault(); + const target = document.getElementById(href.slice(1)); + if (target) { + target.scrollIntoView({ behavior: 'smooth' }); + history.pushState(null, '', href); } - }); + } + }; + + elements.tocLinks.forEach(link => { + link.addEventListener('click', clickHandler); }); + + return () => { + elements.tocLinks.forEach(link => { + link.removeEventListener('click', clickHandler); + }); + }; } -export function initTocNav() { +export function initTocNav(): () => void { const elements = initializeTocElements(); if (elements.progressIndicator != null) { elements.progressIndicator.style.height = '0'; elements.progressIndicator.style.top = '0'; } - const update = () => updateIndicator(elements) + const update = () => updateIndicator(elements); update(); window.addEventListener('scroll', update); window.addEventListener('resize', update); - setupSmoothScrolling(elements); + const removeSmoothScrolling = setupSmoothScrolling(elements); + return () => { + window.removeEventListener('scroll', update); + window.removeEventListener('resize', update); + removeSmoothScrolling(); + }; } diff --git a/src/Elastic.Markdown/Slices/Layout/_PagesNav.cshtml b/src/Elastic.Markdown/Slices/Layout/_PagesNav.cshtml index 52d8ed4db..26ba5bbec 100644 --- a/src/Elastic.Markdown/Slices/Layout/_PagesNav.cshtml +++ b/src/Elastic.Markdown/Slices/Layout/_PagesNav.cshtml @@ -1,8 +1,8 @@ @inherits RazorSlice -