Website: Add sidebar nav to article template. (#22992)

Closes: #22037
Closes: #21281

Changes:
- Added a sidebar nav to article pages with 
   - links to headings (h2) in the article
   - links to /guides, /docs, and the REST API documentation
   - links to share the article on Twitter, LinkedIn, and Hacker News.

---------

Co-authored-by: Mike Thomas <78363703+mike-j-thomas@users.noreply.github.com>
This commit is contained in:
Eric 2024-10-18 01:48:07 -05:00 committed by GitHub
parent 726889bed1
commit 0ec10c4e71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 176 additions and 41 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -5,6 +5,7 @@ parasails.registerPage('basic-article', {
data: {
articleHasSubtitle: false,
articleSubtitle: undefined,
subtopics: [],
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
@ -14,7 +15,16 @@ parasails.registerPage('basic-article', {
//…
},
mounted: async function() {
//…
this.subtopics = (() => {
let subtopics = $('[purpose="article-content"]').find('h2.markdown-heading').map((_, el) => el);
subtopics = $.makeArray(subtopics).map((subheading) => {
return {
title: subheading.innerText,
url: $(subheading).find('a.markdown-link').attr('href'),
};
});
return subtopics;
})();
// If the article has a subtitle (an H2 immediatly after an H1), we'll set articleSubtitle to be the text of that heading
this.articleHasSubtitle = $('[purpose="article-content"]').find('h1 + h2');
if(this.articleHasSubtitle.length > 0 && this.articleHasSubtitle[0].innerText) {
@ -25,6 +35,9 @@ parasails.registerPage('basic-article', {
let startValue = parseInt(ol.getAttribute('start'), 10) - 1;
ol.style.counterReset = 'custom-counter ' + startValue;
});
// Add an event listener to add a class to the right sidebar when the header is hidden.
window.addEventListener('scroll', this.handleScrollingInArticle);
if(this.algoliaPublicKey) {// Note: Docsearch will only be enabled if sails.config.custom.algoliaPublicKey is set. If the value is undefined, the handbook search will be disabled.
docsearch({
appId: 'NZXAYZXDGH',
@ -60,5 +73,19 @@ parasails.registerPage('basic-article', {
window.open('https://fleetdm.com/rss/'+articleCategory, '_blank');
}
},
handleScrollingInArticle: function () {
let rightNavBar = document.querySelector('div[purpose="right-sidebar"]');
let scrollTop = window.pageYOffset;
let windowHeight = window.innerHeight;
// Add/remove the 'header-hidden' class to the right sidebar to scroll it upwards with the website's header.
if (rightNavBar) {
if (scrollTop > this.scrollDistance && scrollTop > windowHeight * 1.5) {
rightNavBar.classList.add('header-hidden');
} else {
rightNavBar.classList.remove('header-hidden');
}
}
this.scrollDistance = scrollTop;
},
}
});

View file

@ -1,5 +1,4 @@
#basic-article {
padding: 0px 24px 0px 24px;
h1 {
font-size: 36px;
}
@ -9,9 +8,12 @@
border-top: 2px solid @core-vibrant-blue-15;
width: 100%;
}
[purpose='page-container'] {
max-width: 1200px;
padding: 64px;
}
[purpose='breadcrumbs-and-search'] {
padding-top: 64px;
// padding-top: 64px;
max-width: 1072px;
margin: auto;
font-size: 14px;
@ -119,14 +121,56 @@
[purpose='breadcrumbs-title'] {
margin-left: 8px;
}
}
[purpose='right-sidebar'] {
position: sticky;
margin-top: 64px;
top: 144px;
width: 256px;
font-size: 14px;
transition-property: transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 500ms;
a:not([purpose='edit-button']) {
margin-bottom: 8px;
display: block;
color: #515774;
&:hover {
text-decoration: none;
color: @core-fleet-black;
}
}
}
[purpose='docs-links'] {
a {
display: block;
}
}
[purpose='social-share-buttons'] {
padding-bottom: 24px;
margin-bottom: 24px;
border-bottom: 1px solid #E2E4EA;
a {
margin-right: 16px;
}
img {
height: 20px;
width: 20px;
}
}
[purpose='subtopics'] {
padding-bottom: 24px;
margin-bottom: 24px;
border-bottom: 1px solid #E2E4EA;
}
.header-hidden { // For scrolling the sidebar with the sticky header
transform: translateY(-80px);
}
[purpose='article-container'] {
max-width: 800px;
margin: auto;
padding-right: 64px;
display: flex;
flex-direction: column;
}
[purpose='article-title'] {
padding-top: 64px;
@ -141,7 +185,6 @@
line-height: 32px;
}
}
[purpose='rss-button'] {
padding: 4px 8px;
display: inline;
@ -190,22 +233,24 @@
}
[purpose='edit-button'] {
margin-left: 24px;
margin-top: 16px;
img {
width: 16px;
height: 16px;
display: inline;
margin-right: 8px;
}
padding: 4px 8px;
padding: 6px 8px;
display: block;
color: @core-fleet-black-75;
text-decoration: none;
font-size: 14px;
line-height: 21px;
border-radius: 6px;
width: 102px;
background: rgba(25, 33, 71, 0.05);
&:hover {
background-color: rgba(25, 33, 71, 0.05);
background-color: rgba(25, 33, 71, 0.1);
}
&:active {
background-color: rgba(25, 33, 71, 0.1);
@ -546,6 +591,24 @@
}
@media (max-width: 991px) {
[purpose='page-container'] {
max-width: 1200px;
padding: 64px 32px;
}
[purpose='right-sidebar'] {
margin-top: 48px;
width: 100%;
}
.header-hidden {
transform: translateY(0);
}
[purpose='article-container'] {
max-width: 100%;
padding-right: 0px;
display: flex;
flex-direction: column;
}
[purpose='article-title'] {
padding-top: 60px;
}
@ -575,6 +638,9 @@
line-height: 38px;
}
}
[purpose='right-sidebar'] {
margin-top: 40px;
}
[purpose='article-content'] {
img {
padding-bottom: 0px;
@ -584,13 +650,12 @@
margin-bottom: 16px;
}
}
[purpose='breadcrumbs-and-search'] {
padding-top: 32px;
}
}
@media (max-width: 576px) {
@media (max-width: 575px) {
[purpose='page-container'] {
padding: 32px 24px;
}
[purpose='bottom-cta'] {
[purpose='next-steps-button'] {
width: 100%;

View file

@ -1,5 +1,5 @@
<div id="basic-article" v-cloak>
<div class="container-fluid px-0">
<div purpose="page-container" class="container-fluid">
<div purpose="breadcrumbs-and-search" class="d-flex flex-lg-row flex-column justify-content-between align-items-lg-center align-items-start">
<div purpose="breadcrumbs" class="d-flex flex-row align-items-start">
<div>
@ -26,34 +26,77 @@
</div>
</div>
</div>
<div purpose="article-container">
<div purpose="article-title">
<h1><%=thisPage.meta.articleTitle %></h1>
<h2 v-if="articleHasSubtitle && articleSubtitle !== undefined">{{articleSubtitle}}</h2>
</div>
<div class="d-flex flex-sm-row flex-column justify-content-between align-items-sm-center">
<div purpose="article-details" class="d-flex flex-row align-items-center">
<span><js-timestamp format="billing" :at="thisPage.meta.publishedOn"></js-timestamp></span>
<span class="px-2">|</span>
<img style="height: 28px; width: 28px; border-radius: 100%;" alt="The author's GitHub profile picture" :src="'https://github.com/'+thisPage.meta.authorGitHubUsername+'.png?size=200'">
<p class="pl-2 font-weight-bold"><%=thisPage.meta.authorFullName %></p>
<div purpose="article-and-sidebar" class="d-flex flex-lg-row-reverse flex-column justify-content-lg-between">
<div purpose="article-title-and-author" class="d-lg-none d-block">
<div purpose="article-title">
<h1><%=thisPage.meta.articleTitle %></h1>
<h2 v-if="articleHasSubtitle && articleSubtitle !== undefined">{{articleSubtitle}}</h2>
</div>
<div class="d-flex flex-row align-items-center justify-content-sm-end pt-3 pt-sm-1">
<a purpose="rss-button" @click="clickCopyRssLink(articleCategorySlug)"><span>Subscribe</span></a>
<a purpose="edit-button" class="d-flex flex-row align-items-center text-nowrap" target="_blank" :href="'https://github.com/fleetdm/fleet/edit/main/articles/'+thisPage.sectionRelativeRepoPath"><img alt="A pencil icon" src="/images/pencil-16x16@2x.png">Edit page</a>
<div class="d-flex flex-sm-row flex-column justify-content-between align-items-sm-center">
<div purpose="article-details" class="d-flex flex-row align-items-center">
<span><js-timestamp format="billing" :at="thisPage.meta.publishedOn"></js-timestamp></span>
<span class="px-2">|</span>
<img style="height: 28px; width: 28px; border-radius: 100%;" alt="The author's GitHub profile picture" :src="'https://github.com/'+thisPage.meta.authorGitHubUsername+'.png?size=200'">
<p class="pl-2 font-weight-bold"><%=thisPage.meta.authorFullName %></p>
</div>
</div>
</div>
<div purpose="article-content" class="d-flex flex-column" parasails-has-no-page-script>
<%- partial(path.relative(path.dirname(__filename), path.resolve( sails.config.appPath, path.join(sails.config.builtStaticContent.compiledPagePartialsAppPath, thisPage.htmlId)))) %>
<div purpose="sidebar-container">
<div purpose="right-sidebar" class="d-flex flex-column">
<div purpose="social-share-buttons" class="d-flex flex-column order-lg-2 order-1">
<p><strong>Share</strong></p>
<div class="d-flex flex-row">
<a :href="`https://news.ycombinator.com/submitlink?u=https://fleetdm.com${encodeURIComponent(thisPage.url)}&t=${encodeURIComponent(thisPage.meta.articleTitle)}`"><img src="/images/social-share-icon-hacker-news-20x20@2x.png" alt="Share this article on Hacker News"></a>
<a :href="`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent('https://fleetdm.com'+thisPage.url)}`"><img src="/images/social-share-icon-linkedin-20x20@2x.png" alt="Share this article on LinkedIn"></a>
<a :href="`https://twitter.com/intent/tweet?url=${encodeURIComponent('https://fleetdm.com'+thisPage.url)}`"><img src="/images/social-share-icon-twitter-20x20@2x.png" alt="Share this article on Twitter"></a>
</div>
</div>
<div purpose="subtopics" id="subtopics" class="d-flex nav flex-column order-lg-1 order-2">
<p><strong>On this page</strong></p>
<a v-for="topic in subtopics" :href="topic.url" class="nav-link p-0">{{topic.title}}</a>
</div>
<div purpose="docs-links" class="order-3">
<a href="/docs">Docs</a>
<a href="/docs/rest-api">REST API</a>
<a href="/guides">Guides</a>
<a purpose="edit-button" class="d-inline-block text-nowrap" target="_blank" :href="'https://github.com/fleetdm/fleet/edit/main/articles/'+thisPage.sectionRelativeRepoPath"><img alt="A pencil icon" src="/images/pencil-16x16@2x.png">Edit page</a>
</div>
<!-- <div class="d-flex flex-row align-items-center justify-content-start pt-3 pt-sm-1">
<a purpose="rss-button" @click="clickCopyRssLink(articleCategorySlug)"><span>Subscribe</span></a>
</div> -->
</div>
</div>
<hr>
<div purpose="bottom-cta" class="d-block">
<h3 style="font-size: 24px; line-height: 32px;">Get started</h3>
<div class="d-flex flex-sm-row flex-column align-items-center">
<a href="/register" class="d-flex btn btn-primary justify-content-center align-items-center" purpose="next-steps-button">
Start now
</a>
<animated-arrow-button class="ml-sm-4" href="/contact">Talk to us</animated-arrow-button>
<div purpose="article-container">
<div purpose="article-title-and-author" class="d-none d-lg-block">
<div purpose="article-title">
<h1><%=thisPage.meta.articleTitle %></h1>
<h2 v-if="articleHasSubtitle && articleSubtitle !== undefined">{{articleSubtitle}}</h2>
</div>
<div class="d-flex flex-sm-row flex-column justify-content-between align-items-sm-center">
<div purpose="article-details" class="d-flex flex-row align-items-center">
<span><js-timestamp format="billing" :at="thisPage.meta.publishedOn"></js-timestamp></span>
<span class="px-2">|</span>
<img style="height: 28px; width: 28px; border-radius: 100%;" alt="The author's GitHub profile picture" :src="'https://github.com/'+thisPage.meta.authorGitHubUsername+'.png?size=200'">
<p class="pl-2 font-weight-bold"><%=thisPage.meta.authorFullName %></p>
</div>
</div>
</div>
<div purpose="article-content" class="d-flex flex-column" parasails-has-no-page-script>
<%- partial(path.relative(path.dirname(__filename), path.resolve( sails.config.appPath, path.join(sails.config.builtStaticContent.compiledPagePartialsAppPath, thisPage.htmlId)))) %>
</div>
<hr>
<div purpose="bottom-cta" class="d-block">
<h3 style="font-size: 24px; line-height: 32px;">Get started</h3>
<div class="d-flex flex-sm-row flex-column align-items-center">
<a href="/register" class="d-flex btn btn-primary justify-content-center align-items-center" purpose="next-steps-button">
Start now
</a>
<animated-arrow-button class="ml-sm-4" href="/contact">Talk to us</animated-arrow-button>
</div>
</div>
</div>
</div>