feat(docs): setup Gridsome for the website

This commit is contained in:
Luca Spezzano
2021-11-02 18:35:41 +01:00
committed by Dario Tranchitella
parent 14f9686bbb
commit 0acc2d2ef1
111 changed files with 25054 additions and 149 deletions

View File

@@ -0,0 +1,49 @@
<template>
<div>
<button @click="toggleIsOpened()" class="flex items-center space-x-3 text-left">
<slot name="title" />
<icon-arrow
class="w-2 transition-all duration-200 transform"
:class="{
'rotate-180': isOpened,
'rotate-0': !isOpened,
}"
/>
</button>
<div
class="transition-all duration-400 overflow-hidden"
:class="{
'max-h-99': isOpened,
'max-h-0': !isOpened,
}"
>
<slot name="content" />
</div>
</div>
</template>
<script>
import IconArrow from "~/assets/icon/arrow.svg?inline";
export default {
components: {
IconArrow,
},
data() {
return {
isOpened: false,
};
},
methods: {
toggleIsOpened() {
this.isOpened = !this.isOpened;
},
},
};
</script>
<style scoped>
.max-h-99 {
max-height: 99rem;
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<component
:is="link ? 'g-link' : 'button'"
:to="link"
class="
inline-block
focus:outline-none
font-medium
rounded-md
relative
bg-primary
py-3 px-8
"
>
<slot />
</component>
</template>
<script>
export default {
props: {
link: {
type: String,
required: false,
default: () => undefined,
},
},
};
</script>
<style>
</style>

View File

@@ -0,0 +1,71 @@
<template>
<footer class="py-8 bg-gray-900 bg-opacity-50">
<div
class="
container
lg:flex lg:items-center lg:justify-between
space-y-4
lg:space-y-0
"
>
<div class="space-y-2">
<h1 class="font-bold text-2xl inline-block">Capsule</h1>
<small class="block">
©{{ new Date().getFullYear() }} All rights reserved
</small>
</div>
<ul class="lg:flex lg:items-center lg:space-x-5 space-y-2 lg:space-y-0">
<li class="block lg:hidden">
<g-link to="/">About Capsule</g-link>
</li>
<li class="block lg:hidden pb-4">
<g-link to="/docs">Documentation</g-link>
</li>
<li>
<a
href="https://github.com/clastix/capsule"
target="_blank"
rel="noopener noreferrer"
class="flex space-x-2 hover:text-blue-400"
><icon-github class="w-5 h-5" /> <span> Github </span></a
>
</li>
<li>
<a
href="https://twitter.com/clastixio"
target="_blank"
rel="noopener noreferrer"
class="flex space-x-2 hover:text-blue-400"
><icon-twitter class="w-5 h-5" /> <span>Twitter</span></a
>
</li>
<li>
<a
href="https://www.linkedin.com/company/clastix/"
target="_blank"
rel="noopener noreferrer"
class="flex space-x-2 hover:text-blue-400"
><icon-linkedin class="w-5 h-5" /> <span>Linkedin</span></a
>
</li>
</ul>
</div>
</footer>
</template>
<script>
import IconGithub from "~/assets/icon/github.svg?inline";
import IconTwitter from "~/assets/icon/twitter.svg?inline";
import IconLinkedin from "~/assets/icon/linkedin.svg?inline";
export default {
components: {
IconGithub,
IconLinkedin,
IconTwitter,
},
};
</script>
<style>
</style>

View File

@@ -0,0 +1,404 @@
<template>
<header class="py-3 lg:py-6 relative bg-gray-900 bg-opacity-50">
<div class="container flex items-center lg:justify-between">
<div class="w-full flex items-center space-x-4 lg:space-x-0">
<div class="w-2/12 lg:hidden" v-if="type === 'doc'">
<button
class="
lg:hidden
relative
z-30
shadow-lg
w-8
h-8
space-y-1
inline-flex
flex-col
justify-center
items-center
outline-none
focus:outline-none
transition-all
duration-200
transform
"
@click="toggleMenu()"
>
<span
class="
inline-block
w-6
h-0.5
rounded-sm
transition-all
duration-200
bg-gray-100
"
:class="{
'transform rotate-45 translate-y-1.5': menuOpened,
}"
></span>
<span
class="inline-block w-6 h-0.5 rounded-sm bg-gray-100"
:class="{
invisible: menuOpened,
}"
></span>
<span
class="
inline-block
w-6
h-0.5
rounded-sm
transition-all
duration-200
bg-gray-100
"
:class="{
'transform -rotate-45 -translate-y-1.5': menuOpened,
}"
></span>
</button>
</div>
<div
class="w-8/12 lg:w-auto text-center flex items-center space-x-16"
:class="{
'justify-center lg:justify-start': type === 'doc',
}"
>
<g-link to="/" class="flex items-center space-x-4">
<logo-capsule class="w-10 lg:w-12" />
<h1 class="font-bold text-2xl lg:text-3xl inline-block">Capsule</h1>
</g-link>
<nav class="hidden lg:inline-block">
<ul class="flex items-center font-medium space-x-5">
<li class="hidden lg:block">
<g-link to="/">About Capsule</g-link>
</li>
<li>
<g-link to="/docs">Documentation</g-link>
</li>
<!-- <li class="group relative">
main
<ul
class="
py-2
px-4
absolute
top-full
hidden
group-hover:block
bg-gray-100
text-gray-800
rounded
text-left
"
>
<li>
<a href="/" target="_blank" rel="noopener noreferrer">
v1.5.0
</a>
</li>
<li>
<a href="/" target="_blank" rel="noopener noreferrer">
v1.6.0
</a>
</li>
<li>
<a href="/" target="_blank" rel="noopener noreferrer">
main
</a>
</li>
</ul>
</li> -->
</ul>
</nav>
</div>
<div class="w-2/12 lg:hidden" v-if="type === 'doc'">
<button @click="toggleSearch()" class="block ml-auto">
<icon-search class="w-8" />
</button>
</div>
</div>
<div
class="relative"
:class="{
'hidden lg:flex items-center space-x-16': type === 'doc',
}"
>
<div v-if="type === 'doc'">
<div class="relative">
<icon-search
class="w-8 absolute left-0 bottom-2 text-gray-100 opacity-80"
/>
<input
type="search"
name="search"
id="search"
class="
rounded-0
pl-10
pr-2
py-2
outline-none
w-96
bg-transparent
border-b border-solid border-gray-100
text-gray-100
"
placeholder="Search"
@focus="focused = true"
@blur="focusOut()"
@input="query = $event.target.value"
@change="query = $event.target.value"
/>
</div>
<ul
v-show="showResult"
class="
absolute
left-0
top-12
z-20
text-left
bg-gray-900
rounded-br rounded-bl
text-sm
min-w-96
"
>
<li v-if="results.length === 0" class="px-4 py-2">
No results for <span class="font-bold">{{ query }}</span
>.
</li>
<li v-else v-for="result in results" :key="result.id">
<g-link
:to="result.item.path + result.item.anchor"
class="block px-4 py-2 hover:bg-gray-700 hover:text-blue-400"
>
<span v-if="result.item.value === result.item.title">
{{ result.item.value }}
</span>
<span v-else class="flex items-center">
{{ result.item.title }}
<icon-arrow class="w-2 mx-2 transform -rotate-90" />
<span class="font-normal opacity-75">
{{ result.item.value }}
</span>
</span>
</g-link>
</li>
</ul>
</div>
<ul
class="items-center"
:class="{
'hidden lg:flex': type === 'doc',
flex: type === 'default',
}"
>
<li>
<a
href="https://github.com/clastix/capsule"
target="_blank"
rel="noopener noreferrer"
>
Github
</a>
</li>
</ul>
</div>
</div>
<div
class="
container
py-4
absolute
bg-gray-900
inset-x-0
top-0
z-30
shadow-xl
rounded-br rounded-bl
"
v-if="searchOpened && type === 'doc'"
>
<div class="flex items-center space-x-4">
<input
type="search"
name="search"
id="search"
class="w-full rounded p-2 outline-none text-gray-800"
placeholder="Search"
@focus="focused = true"
@blur="focusOutMobile()"
@input="query = $event.target.value"
@change="query = $event.target.value"
/>
<button @click="toggleSearch()">Cancel</button>
</div>
<ul class="p-4 space-y-2" v-show="showResult">
<li v-if="results.length === 0">
No results for <span class="font-bold">{{ query }}</span
>.
</li>
<li v-else v-for="result in results" :key="result.id">
<g-link
:to="result.item.path + result.item.anchor"
class="hover:text-blue-400"
>
<span v-if="result.item.value === result.item.title">
{{ result.item.value }}
</span>
<span v-else class="flex items-center">
{{ result.item.title }}
<span class="block">
<icon-arrow class="w-2 mx-2 transform -rotate-90" />
</span>
<span class="font-normal opacity-75">
{{ result.item.value }}
</span>
</span>
</g-link>
</li>
</ul>
</div>
</header>
</template>
<static-query>
query Search {
allMarkdownPage{
edges {
node {
id
path
title
headings {
depth
value
anchor
}
}
}
}
}
</static-query>
<script>
import Fuse from "fuse.js";
import IconSearch from "~/assets/icon/search.svg?inline";
import IconGithub from "~/assets/icon/github.svg?inline";
import IconTwitter from "~/assets/icon/twitter.svg?inline";
import IconSlack from "~/assets/icon/slack.svg?inline";
import IconArrow from "~/assets/icon/arrow.svg?inline";
import LogoCapsule from "~/assets/logo.svg?inline";
export default {
props: {
type: {
type: String,
required: false,
default: () => "default",
validator: (value) => ["default", "doc"].includes(value),
},
},
components: {
IconSearch,
IconGithub,
IconTwitter,
IconSlack,
IconArrow,
LogoCapsule,
},
data() {
return {
menuOpened: false,
searchOpened: false,
query: "",
focusIndex: -1,
focused: false,
};
},
watch: {
$route: function () {
if (this.menuOpened) {
this.menuOpened = false;
this.$emit("onToggleMenu", this.menuOpened);
document.querySelector("body").classList.remove("overflow-hidden");
}
},
},
computed: {
results() {
const fuse = new Fuse(this.headings, {
keys: ["value"],
threshold: 0.25,
});
return fuse.search(this.query).slice(0, 15);
},
headings() {
let result = [];
const allPages = this.$static.allMarkdownPage.edges.map(
(edge) => edge.node
);
// Create the array of all headings of all pages.
allPages.forEach((page) => {
page.headings.forEach((heading) => {
result.push({
...heading,
path: page.path,
title: page.title,
});
});
});
return result;
},
showResult() {
// Show results, if the input is focused and the query is not empty.
return this.focused && this.query.length > 0;
},
},
methods: {
toggleMenu() {
this.menuOpened = !this.menuOpened;
this.$emit("onToggleMenu", this.menuOpened);
if (this.menuOpened) {
document.querySelector("body").classList.add("overflow-hidden");
} else {
document.querySelector("body").classList.remove("overflow-hidden");
}
},
toggleSearch() {
this.searchOpened = !this.searchOpened;
},
focusOut() {
const _this = this;
setTimeout(function () {
_this.focused = false;
}, 200);
},
focusOutMobile() {
const _this = this;
setTimeout(function () {
_this.focused = false;
_this.toggleSearch();
}, 200);
},
},
};
</script>

View File

@@ -0,0 +1,135 @@
<template>
<aside>
<nav>
<ul class="space-y-3 pl-4 lg:pl-0">
<li
v-for="section in $static.allSidebar.edges[0].node.sections"
:key="section.id"
>
<h5
class="text-2xl lg:text-xl font-bold mb-1"
v-if="section.title !== ''"
>
{{ section.title }}
</h5>
<ul
class="space-y-1.5"
:class="{
'pl-2': section.title !== '',
}"
>
<template v-for="(item, index) in section.items">
<li :key="item.id" v-if="item.title === ''">
<g-link
:to="item.path"
class="
block
transition-transform
duration-100
transform
hover:translate-x-1
text-lg
lg:text-base
hover:text-blue-400
"
:class="{
'js-index-link': item.path === '/docs/' && index === 0,
}"
>
{{ item.label }}</g-link
>
</li>
<template v-else>
<li :key="item.id">
<app-accordion>
<template v-slot:title>
<h6 class="font-semibold text-xl lg:text-lg mb-1">
{{ item.title }}
</h6>
</template>
<template v-slot:content>
<ul class="space-y-1.5 pl-2">
<li v-for="subItem in item.subItems" :key="subItem.id">
<g-link
:to="subItem.path"
class="
block
transition-transform
duration-100
transform
hover:translate-x-1
text-lg
lg:text-base
hover:text-blue-400
"
>
{{ subItem.label }}</g-link
>
</li>
</ul>
</template>
</app-accordion>
</li>
</template>
</template>
</ul>
</li>
</ul>
</nav>
</aside>
</template>
<static-query>
{
allSidebar {
edges{
node{
id
sections {
title
items{
title
label
path
subItems {
label
path
}
}
}
}
}
}
}
</static-query>
<script>
import AppAccordion from "~/components/AppAccordion.vue";
export default {
components: {
AppAccordion,
},
watch: {
$route: function () {
if (process.isClient && this.$route.fullPath !== "/docs/") {
setTimeout(function () {
document.querySelector(".js-index-link").classList.remove("active");
}, 80);
}
},
},
mounted() {
if (this.$route.fullPath !== "/docs/") {
document.querySelector(".js-index-link").classList.remove("active");
}
},
};
</script>
<style lang="scss" scoped>
.active {
@apply text-blue-400 font-semibold;
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<ul class="space-y-2">
<template v-for="(heading, index) in headings">
<li v-if="heading.depth > 1" :key="index" class="hover:underline">
<g-link
:to="`${pagePath}${heading.anchor}`"
class="text-sm"
:class="{
'text-white underline font-semibold':
activeAnchor === heading.anchor,
'text-gray-300': activeAnchor !== heading.anchor,
}"
>
{{ heading.value }}
</g-link>
</li>
</template>
</ul>
</template>
<script>
export default {
props: {
headings: {
type: Array,
required: true,
},
pagePath: {
type: String,
required: true,
},
},
data() {
return {
activeAnchor: "",
observer: null,
};
},
watch: {
$route: function () {
if (process.isClient && window.location.hash) {
this.activeAnchor = window.location.hash;
}
if (this.observer) {
// Clear the current observer.
this.observer.disconnect();
this.$nextTick(this.initObserver);
}
},
},
mounted() {
if (process.isClient) {
if (window.location.hash) {
this.activeAnchor = window.location.hash;
}
this.$nextTick(this.initObserver);
}
},
methods: {
observerCallback(entries, observer) {
// This early return fixes the jumping
// of the bubble active state when we click on a link.
// There should be only one intersecting element anyways.
if (entries.length > 1) {
return;
}
const id = entries[0].target.id;
// We want to give the link of the intersecting
// headline active and add the hash to the url.
if (id) {
this.activeAnchor = "#" + id;
if (history.replaceState) {
history.replaceState(null, null, "#" + id);
}
}
},
initObserver() {
this.observer = new IntersectionObserver(this.observerCallback, {
// This rootMargin should allow intersections at the top of the page.
// root: document.querySelector('.content'),
rootMargin: "0px 0px 99999px",
threshold: 1,
});
const elements = document.querySelectorAll(
".content h2, .content h3, .content h4, .content h5, .content h6"
);
for (let i = 0; i < elements.length; i++) {
this.observer.observe(elements[i]);
}
},
},
};
</script>
<style>
</style>

View File

@@ -0,0 +1,4 @@
Add components that will be imported to Pages and Layouts to this folder.
Learn more about components here: https://gridsome.org/docs/components/
You can delete this file.