Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pages navigation #447

Draft
wants to merge 2 commits into
base: feature/header-searchbar
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/docset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,11 @@ toc:
children:
- file: first-page.md
- file: second-page.md
- folder: deeply-nested
children:
- file: index.md
- file: foo.md
- file: bar.md
- folder: baz
children:
- file: qux.md
1 change: 1 addition & 0 deletions docs/testing/deeply-nested/bar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Bar
1 change: 1 addition & 0 deletions docs/testing/deeply-nested/baz/qux.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Qux
1 change: 1 addition & 0 deletions docs/testing/deeply-nested/foo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Foo
1 change: 1 addition & 0 deletions docs/testing/deeply-nested/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Deeply Nested
23 changes: 21 additions & 2 deletions src/Elastic.Markdown/Assets/fonts.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
@font-face {
font-family: "Inter";
src: url("./fonts/InterVariable.woff2") format("woff2");
font-display: swap;
}

@font-face {
font-family: "Mier B";
src: url("./fonts/MierB-Regular.woff2") format("woff2");
font-weight: normal;
font-display: swap;
}

@font-face {
font-family: "Mier B";
src: url("./fonts/MierB-Bold.woff2") format("woff2");
font-weight: bold;
font-display: swap;
}

@font-face {
font-family: "Mier B";
src: url("./fonts/MierB-Regular.woff2") format("woff2")
src: url("./fonts/MierB-Demi.woff2") format("woff2");
font-weight: 600;
font-display: swap;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
44 changes: 44 additions & 0 deletions src/Elastic.Markdown/Assets/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import hljs from "highlight.js";
import {mergeHTMLPlugin} from "./hljs-merge-html-plugin";
import {$, $$} from 'select-dom/strict.js'

hljs.registerLanguage('apiheader', function() {
return {
Expand All @@ -16,3 +17,46 @@ hljs.registerLanguage('apiheader', function() {

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

type NavState = { [key: string]: boolean };
const PAGE_NAV_STATE_KEY = 'pagesNavState';
const sessionState = JSON.parse(sessionStorage.getItem(PAGE_NAV_STATE_KEY)) as NavState

function keepNavState(element: HTMLElement) {
const inputs = $$('input[type="checkbox"]', element);
if (sessionState) {
inputs.forEach(input => {
const key = input.id;
if (input.dataset['shouldExpand'] === 'true') {
input.checked = true;
} else {
input.checked = sessionState[key];
}
});
}
window.addEventListener('beforeunload', () => {
const inputs = $$('input[type="checkbox"]', element);
const state: NavState = inputs.reduce((state: NavState, input) => {
const key = input.id;
const value = input.checked;
return { ...state, [key]: value};
}, {});
sessionStorage.setItem(PAGE_NAV_STATE_KEY, JSON.stringify(state));
});
}

const PAGE_NAV_SCROLL_POSITION_KEY = 'pagesNavScrollPosition';
const scrollPosition = sessionStorage.getItem(PAGE_NAV_SCROLL_POSITION_KEY);

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

const pagesNav = $('#pages-nav');
keepNavPosition(pagesNav);
keepNavState(pagesNav);
21 changes: 21 additions & 0 deletions src/Elastic.Markdown/Assets/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
@import "highlight.js/styles/atom-one-dark.css";
@import "./markdown/typography.css";

:root {
interpolate-size: allow-keywords;
}

#default-search::-webkit-search-cancel-button {
@apply pr-2;
-webkit-appearance: none;
Expand All @@ -15,3 +19,20 @@
cursor: pointer;
background-repeat: no-repeat;
}

#pages-nav {
scrollbar-gutter: stable;
&::-webkit-scrollbar-track {
background-color: transparent;
}
&:hover::-webkit-scrollbar-thumb {
background-color: var(--color-gray-light);
}
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
}
}
10 changes: 10 additions & 0 deletions src/Elastic.Markdown/Helpers/BoolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Elastic.Markdown.Helpers;

public static class BoolExtensions
{
public static string ToLowerString(this bool @bool) => @bool.ToString().ToLowerInvariant();
}
7 changes: 7 additions & 0 deletions src/Elastic.Markdown/IO/Navigation/DocumentationGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public class DocumentationGroup

public int Depth { get; }

public bool ContainsCurrentPage(MarkdownFile current) => NavigationItems.Any(n => n switch
{
FileNavigation f => f.File == current,
GroupNavigation g => g.Group.ContainsCurrentPage(current),
_ => false
});

public DocumentationGroup(
BuildContext context,
IReadOnlyCollection<ITocItem> toc,
Expand Down
51 changes: 34 additions & 17 deletions src/Elastic.Markdown/Slices/Layout/_TocTree.cshtml
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
@inherits RazorSlice<NavigationViewModel>
<aside id="lside" class="sy-lside md:w-72 md:shrink-0 print:hidden">
<div class="sy-lside-inner md:sticky">
<div class="sy-scrollbar p-6">
<div class="globaltoc" data-expand-depth="0">
<p class="caption" role="heading" aria-level="3">
<span class="caption-text">Elastic Docs Guide</span>
</p>
<ul class="current">@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = Model.Tree.Depth,
SubTree = Model.Tree,
CurrentDocument = Model.CurrentDocument
}))
</ul>

@if (Model.IsRedesign)
{
<div class="pt-6 sticky">
<ul class="block w-full">
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = Model.Tree.Depth,
SubTree = Model.Tree,
CurrentDocument = Model.CurrentDocument
}))
</ul>
</div>
}
else
{
<aside id="lside" class="sy-lside md:w-72 md:shrink-0 print:hidden">
<div class="sy-lside-inner md:sticky">
<div class="sy-scrollbar p-6">
<div class="globaltoc" data-expand-depth="0">
<p class="caption" role="heading" aria-level="3">
<span class="caption-text">Elastic Docs Guide</span>
</p>
<ul class="current">@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = Model.Tree.Depth,
SubTree = Model.Tree,
CurrentDocument = Model.CurrentDocument
}))
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="lside-overlay js-menu" role="button" aria-label="Close left sidebar" aria-controls="lside" aria-expanded="false"></div>
</aside>
<div class="lside-overlay js-menu" role="button" aria-label="Close left sidebar" aria-controls="lside" aria-expanded="false"></div>
}
108 changes: 89 additions & 19 deletions src/Elastic.Markdown/Slices/Layout/_TocTreeNav.cshtml
Original file line number Diff line number Diff line change
@@ -1,27 +1,97 @@
@using Elastic.Markdown.Helpers
@using Elastic.Markdown.IO.Navigation
@inherits RazorSlice<NavigationTreeItem>
@foreach (var item in Model.SubTree.NavigationItems)
@if (Model.IsRedesign)
{
if (item is FileNavigation file)
@foreach (var item in Model.SubTree.NavigationItems)
{
var f = file.File;
var current = f == Model.CurrentDocument ? " current" : string.Empty;
<li class="toctree-l@(Model.SubTree.Depth + 1)@current"><a class="@(current.Trim()) reference internal" href="@f.Url">@f.NavigationTitle</a></li>
if (item is FileNavigation file)
{
var f = file.File;
var isCurrent = f == Model.CurrentDocument;
<li class="block pl-4 w-full @(isCurrent ? "current" : string.Empty)">
<div class="flex">
<div class="w-6">
</div>
<a
class="block py-1 w-full hover:font-bold @(isCurrent ? "font-bold text-blue-elastic" : string.Empty) "
href="@f.Url">
@f.NavigationTitle
</a>
</div>
</li>
}
else if (item is GroupNavigation folder)
{
var g = folder.Group;
var isCurrent = g.Index == Model.CurrentDocument;
var slug = g.Index?.Title.Slugify();
const int initialExpandLevel = 1;
var shouldExpand = g.HoldsCurrent(Model.CurrentDocument) || g.Depth <= initialExpandLevel || g.ContainsCurrentPage(Model.CurrentDocument);
<li class="flex flex-wrap pl-4">
<label for="@slug" class="peer group flex items-center mr-1">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 shrink -rotate-90 group-has-checked:rotate-0 cursor-pointer">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>
</svg>
<input
id="@slug"
type="checkbox"
class="hidden"
aria-hidden="true"
data-should-expand="@shouldExpand.ToLowerString()"
@(shouldExpand ? "checked" : string.Empty)>
</label>
<a
href="@g.Index?.Url"
class="block py-1 grow hover:font-bold @(isCurrent ? "font-bold text-blue-elastic" : string.Empty) ">
@g.Index?.NavigationTitle
</a>
@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)">
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = g.Depth,
CurrentDocument = Model.CurrentDocument,
SubTree = g
}))
</ul>
}
</li>
}
}
else if (item is GroupNavigation folder)
}
else
{
@foreach (var item in Model.SubTree.NavigationItems)
{
var g = folder.Group;
var current = g.HoldsCurrent(Model.CurrentDocument) ? " current" : string.Empty;
var currentFile = g.Index == Model.CurrentDocument ? " current" : string.Empty;
<li class="toctree-l@(g.Depth)@current"><a class="reference internal@(currentFile)" href="@g.Index?.Url">@g.Index?.NavigationTitle</a>@if (@g.NavigationItems.Count > 0) {<ul class="@(current.Trim())">
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = g.Depth,
CurrentDocument = Model.CurrentDocument,
SubTree = g
}))
</ul>
}
</li>
if (item is FileNavigation file)
{
var f = file.File;
var current = f == Model.CurrentDocument ? " current" : string.Empty;
<li class="toctree-l@(Model.SubTree.Depth + 1)@current"><a class="@(current.Trim()) reference internal" href="@f.Url">@f.NavigationTitle</a></li>
}
else if (item is GroupNavigation folder)
{
var g = folder.Group;
var current = g.HoldsCurrent(Model.CurrentDocument) ? " current" : string.Empty;
var currentFile = g.Index == Model.CurrentDocument ? " current" : string.Empty;
<li class="toctree-l@(g.Depth)@current"><a class="reference internal@(currentFile)" href="@g.Index?.Url">@g.Index?.NavigationTitle</a>@if (@g.NavigationItems.Count > 0) {<ul class="@(current.Trim())">
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
{
Level = g.Depth,
CurrentDocument = Model.CurrentDocument,
SubTree = g
}))
</ul>
}
</li>
}
}
}
20 changes: 13 additions & 7 deletions src/Elastic.Markdown/Slices/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
</head>

<body class="text-ink">
<section class="h-9 bg-black"></section>
@* <section class="h-9 bg-black"></section> *@
<header class="sticky top-0 bg-blue-developer max-w-screen px-6 pb-6 flex items-center justify-center">
<div class="container flex flex-wrap lg:flex-nowrap">
<div class="h-10 mt-6 basis-full lg:basis-auto">
<img src="@Model.Static("logo-elastic-horizontal-color-reverse.svg")" alt="Elastic" class="h-10">
<a href="/">
<img src="@Model.Static("logo-elastic-horizontal-color-reverse.svg")" alt="Elastic" class="h-10">
</a>
</div>
<form role="search" class="hidden lg:block grow basis-full lg:basis-auto shrink-0 mt-6 lg:mx-10 h-10" autocomplete="off">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
Expand All @@ -37,9 +39,13 @@
</div>
</div>
</header>
<main class="container mx-auto">

<div class="markdown-content w-[100ch] max-w-screen mx-auto pt-6 pb-6 px-6">
<div class="container mx-auto flex">
<aside>
<nav id="pages-nav" class="sticky top-22 z-10 max-h-[calc(100vh-var(--spacing)*22)] overflow-y-auto">
@(new HtmlString(Model.NavigationHtml))
</nav>
</aside>
<main class="markdown-content w-[100ch] max-w-screen pt-6 pb-6 px-6">
<ol class="flex-1 mb-6" itemscope="" itemtype="https://schema.org/BreadcrumbList">
<li class="inline text-gray-500" itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<a itemprop="item" href="/">
Expand All @@ -60,8 +66,8 @@
}
</ol>
@await RenderBodyAsync()
</div>
</main>
</main>
</div>
<script src="@Model.Static("main.js")"></script>
</body>
</html>
Expand Down
Loading