Skip to content

Commit

Permalink
Refactor and fix
Browse files Browse the repository at this point in the history
  • Loading branch information
reakaleek committed Feb 10, 2025
1 parent 6dce031 commit f9c1389
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 88 deletions.
21 changes: 21 additions & 0 deletions src/Elastic.Markdown/Assets/hljs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {mergeHTMLPlugin} from "./hljs-merge-html-plugin";
import hljs from "highlight.js";

hljs.registerLanguage('apiheader', function() {
return {
case_insensitive: true, // language is case-insensitive
keywords: 'GET POST PUT DELETE HEAD OPTIONS PATCH',
contains: [
hljs.HASH_COMMENT_MODE,
{
className: "subst", // (pathname: path1/path2/dothis) color #ab5656
begin: /(?<=(?:\/|GET |POST |PUT |DELETE |HEAD |OPTIONS |PATH))[^?\n\r\/]+/,
}
], }
})

hljs.addPlugin(mergeHTMLPlugin);

export function initHighlight() {
hljs.highlightAll();
}
89 changes: 4 additions & 85 deletions src/Elastic.Markdown/Assets/main.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,5 @@
import hljs from "highlight.js";
import {mergeHTMLPlugin} from "./hljs-merge-html-plugin";
import {$, $$} from 'select-dom/strict.js'
import {initNav} from "./pages-nav";
import {initHighlight} from "./hljs";

hljs.registerLanguage('apiheader', function() {
return {
case_insensitive: true, // language is case-insensitive
keywords: 'GET POST PUT DELETE HEAD OPTIONS PATCH',
contains: [
hljs.HASH_COMMENT_MODE,
{
className: "subst", // (pathname: path1/path2/dothis) color #ab5656
begin: /(?<=(?:\/|GET |POST |PUT |DELETE |HEAD |OPTIONS |PATH))[^?\n\r\/]+/,
}
], }
})

hljs.addPlugin(mergeHTMLPlugin);
hljs.highlightAll();

type NavExpandState = { [key: string]: boolean };
const PAGE_NAV_EXPAND_STATE_KEY = 'pagesNavState';
const navState = JSON.parse(sessionStorage.getItem(PAGE_NAV_EXPAND_STATE_KEY)) as NavExpandState

function keepNavState(nav: HTMLElement) {
const inputs = $$('input[type="checkbox"]', nav);
if (navState) {
inputs.forEach(input => {
const key = input.id;
if (input.dataset['shouldExpand'] === 'true') {
input.checked = true;
} else {
input.checked = navState[key];
}
});
}
window.addEventListener('beforeunload', () => {
const inputs = $$('input[type="checkbox"]', nav);
const state: NavExpandState = inputs.reduce((state: NavExpandState, input) => {
const key = input.id;
const value = input.checked;
return { ...state, [key]: value};
}, {});
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)
);

function keepNavPosition(nav: HTMLElement) {
if (pagesNavScrollPosition) {
nav.scrollTop = pagesNavScrollPosition;
}
window.addEventListener('beforeunload', () => {
sessionStorage.setItem(PAGE_NAV_SCROLL_POSITION_KEY, nav.scrollTop.toString());
});
}

function scrollCurrentNaviItemIntoView(nav: HTMLElement, delay: number) {
setTimeout(() => {
const currentNavItem = $('.current');
if (currentNavItem && !isElementInViewport(currentNavItem)) {
currentNavItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}, delay);
}
function isElementInViewport(el: HTMLElement): boolean {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}

const pagesNav = $('#pages-nav');
keepNavState(pagesNav);
keepNavPosition(pagesNav);
pagesNav.style.opacity = '1';
scrollCurrentNaviItemIntoView(pagesNav, 100);
// $('.current a', pagesNav).focus();
initNav();
initHighlight();
78 changes: 78 additions & 0 deletions src/Elastic.Markdown/Assets/pages-nav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {$, $$} from "select-dom/strict";

type NavExpandState = { [key: string]: boolean };
const PAGE_NAV_EXPAND_STATE_KEY = 'pagesNavState';
const navState = JSON.parse(sessionStorage.getItem(PAGE_NAV_EXPAND_STATE_KEY)) as NavExpandState

// 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 inputs = $$('input[type="checkbox"]', nav);
if (navState) {
inputs.forEach(input => {
const key = input.id;
if ('shouldExpand' in input.dataset && input.dataset['shouldExpand'] === 'true') {
input.checked = true;
} else {
input.checked = navState[key];
}
});
}

return () => {
const inputs = $$('input[type="checkbox"]', nav);
const state: NavExpandState = inputs.reduce((state: NavExpandState, input) => {
const key = input.id;
const value = input.checked;
return { ...state, [key]: value};
}, {});
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());
}
}

function scrollCurrentNaviItemIntoView(nav: HTMLElement, delay: number) {
setTimeout(() => {
const currentNavItem = $('.current', nav);
if (currentNavItem && !isElementInViewport(currentNavItem)) {
currentNavItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}, delay);
}
function isElementInViewport(el: HTMLElement): boolean {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}

export function initNav() {
const pagesNav = $('#pages-nav');
const keepNavStateCallback = keepNavState(pagesNav);
const keepNavPositionCallback = keepNavPosition(pagesNav);
scrollCurrentNaviItemIntoView(pagesNav, 100);
window.addEventListener('beforeunload', () => {
keepNavStateCallback();
keepNavPositionCallback();
}, true);
}
6 changes: 3 additions & 3 deletions src/Elastic.Markdown/Slices/Layout/_TocTreeNav.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
var f = file.File;
var isCurrent = f == Model.CurrentDocument;
<li class="block ml-2 pl-2 w-full border-l-1 border-l-gray-200 group/li @(isCurrent ? "current" : string.Empty)">
<li class="block ml-2 pl-2 border-l-1 border-l-gray-200 group/li @(isCurrent ? "current" : string.Empty)">
<div class="flex">
<div class="w-5">
</div>
Expand All @@ -32,7 +32,7 @@
var containsCurrent = g.HoldsCurrent(Model.CurrentDocument) || g.ContainsCurrentPage(Model.CurrentDocument);
var shouldInitiallyExpand = containsCurrent || g.Depth <= initialExpandLevel;
<li class="flex flex-wrap ml-2 pl-2 @(g.Depth > 1 ? "border-l-1 border-l-gray-200" : string.Empty)">
<label for="@slug" class="peer group/label flex items-center mr-1">
<label for="@slug" class="peer group/label flex items-center mr-1 overflow-hidden">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
Expand All @@ -58,7 +58,7 @@
</label>
@if (g.NavigationItems.Count > 0)
{
<ul class="h-0 grow overflow-hidden peer-has-checked:h-auto w-full overflow-hidden" data-has-current="@g.ContainsCurrentPage(Model.CurrentDocument)">
<ul class="h-0 w-full overflow-y-hidden peer-has-checked:h-auto" data-has-current="@g.ContainsCurrentPage(Model.CurrentDocument)">
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = g.Depth,
Expand Down

0 comments on commit f9c1389

Please sign in to comment.