Skip to content

Commit

Permalink
feat: add home page feature (#108)
Browse files Browse the repository at this point in the history
Co-authored-by: Kia Ishii <[email protected]>
  • Loading branch information
kazupon and kiaking authored Nov 2, 2020
1 parent 2183c67 commit 3a0af0b
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/client/app/composables/siteData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { SiteData } from '../../../../types/shared'

const parse = (data: string) => readonly(JSON.parse(data)) as SiteData

export type SiteDataRef<T = any> = Ref<SiteData<T>>

export const siteDataRef: Ref<SiteData> = ref(parse(serialized))

export function useSiteData<T = any>() {
Expand Down
86 changes: 72 additions & 14 deletions src/client/theme-default/Layout.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<template>
<div class="theme">
<header>
<div class="theme" :class="pageClasses">
<header class="navbar" v-if="showNavbar">
<NavBar>
<template #search>
<slot name="navbar-search" />
</template>
</NavBar>
<ToggleSideBarButton @toggle="toggleSidebar" />
</header>
<aside :class="{ open }">
<aside :class="{ open: openSideBar }">
<SideBar>
<template #top>
<slot name="sidebar-top" />
Expand All @@ -19,12 +19,21 @@
</SideBar>
</aside>
<!-- TODO: make this button accessible -->
<div
class="sidebar-mask"
:class="{ 'sidebar-open': open }"
@click="toggleSidebar(false)"
/>
<main>
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<main class="home" aria-labelledby="main-title" v-if="enableHome">
<Home>
<template #hero>
<slot name="home-hero" />
</template>
<template #features>
<slot name="home-features" />
</template>
<template #footer>
<slot name="home-footer" />
</template>
</Home>
</main>
<main v-else>
<Page>
<template #top>
<slot name="page-top" />
Expand All @@ -39,27 +48,69 @@
</template>

<script>
import { ref, watch } from 'vue'
import { ref, computed, watch } from 'vue'
import NavBar from './components/NavBar.vue'
import Home from './components/Home.vue'
import ToggleSideBarButton from './components/ToggleSideBarButton.vue'
import SideBar from './components/SideBar.vue'
import Page from './components/Page.vue'
import { useRoute } from 'vitepress'
import { useRoute, usePageData, useSiteData, useSiteDataByRoute } from 'vitepress'
export default {
components: {
Home,
NavBar,
ToggleSideBarButton,
SideBar,
Page
},
setup() {
const open = ref(false)
const route = useRoute()
const pageData = usePageData()
const siteData = useSiteData()
const siteRouteData = useSiteDataByRoute()
const openSideBar = ref(false)
const enableHome = computed(() => !!pageData.value.frontmatter.home)
const showNavbar = computed(() => {
const { themeConfig } = siteRouteData.value
const { frontmatter } = pageData.value
if (
frontmatter.navbar === false
|| themeConfig.navbar === false) {
return false
}
return (
siteData.value.title
|| themeConfig.logo
|| themeConfig.repo
|| themeConfig.nav
)
})
const showSidebar = computed(() => {
const { frontmatter } = pageData.value
const { themeConfig } = siteRouteData.value
return (
!frontmatter.home
&& frontmatter.sidebar !== false
&& ((typeof themeConfig.sidebar === 'object') && (Object.keys(themeConfig.sidebar).length != 0)
|| (Array.isArray(themeConfig.sidebar) && themeConfig.sidebar.length != 0))
)
})
const pageClasses = computed(() => {
return [{
'no-navbar': !showNavbar.value,
'sidebar-open': openSideBar.value,
'no-sidebar': !showSidebar.value
}]
})
const toggleSidebar = (to) => {
open.value = typeof to === 'boolean' ? to : !open.value
openSideBar.value = typeof to === 'boolean' ? to : !openSideBar.value
}
const hideSidebar = toggleSidebar.bind(null, false)
Expand All @@ -68,7 +119,14 @@ export default {
// TODO: route only changes when the pathname changes
// listening to hashchange does nothing because it's prevented in router
return { open, toggleSidebar }
return {
showNavbar,
showSidebar,
openSideBar,
pageClasses,
enableHome,
toggleSidebar
}
}
}
</script>
219 changes: 219 additions & 0 deletions src/client/theme-default/components/Home.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
<template>
<header class="hero">
<img
v-if="data.heroImage"
:src="heroImageSrc"
:alt="data.heroAlt || 'hero'"
>

<h1
v-if="data.heroText !== null"
id="main-title"
>
{{ data.heroText || siteTitle || 'Hello' }}
</h1>

<p
v-if="data.tagline !== null"
class="description"
>
{{ data.tagline || siteDescription || 'Welcome to your VitePress site' }}
</p>

<p
v-if="data.actionText && data.actionLink"
class="action"
>
<NavBarLink :item="actionLink" />
</p>
<slot name="hero" />
</header>

<div
v-if="data.features && data.features.length"
class="features"
>
<div
v-for="(feature, index) in data.features"
:key="index"
class="feature"
>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
<slot name="features" />
</div>

<div
v-if="data.footer"
class="footer"
>
{{ data.footer }}
<slot name="footer" />
</div>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue'
import NavBarLink from './NavBarLink.vue'
import { withBase } from '../utils'
import { usePageData, useSiteData } from 'vitepress'
export default defineComponent({
components: {
NavBarLink
},
setup() {
const pageData = usePageData()
const siteData = useSiteData()
const data = computed(() => pageData.value.frontmatter)
const actionLink = computed(() => ({
link: data.value.actionLink,
text: data.value.actionText
}))
const heroImageSrc = computed(() => withBase(data.value.heroImage))
const siteTitle = computed(() => siteData.value.title)
const siteDescription = computed(() => siteData.value.description)
return {
data,
actionLink,
heroImageSrc,
siteTitle,
siteDescription
}
}
})
</script>

<style scoped>
.hero {
text-align: center;
}
.hero img {
max-width: 100%;
max-height: 280px;
display: block;
margin: 3rem auto 1.5rem;
}
.hero h1 {
font-size: 3rem;
}
.hero h1,
.hero .description,
.hero .action {
margin: 1.8rem auto;
}
.hero .description {
max-width: 35rem;
font-size: 1.6rem;
line-height: 1.3;
/* TODO: calculating lighten 40% color with using style :vars from `--text-color` */
color: #6a8bad;
}
::v-deep(.nav-link) {
display: inline-block;
font-size: 1.2rem;
color: #fff;
background-color: var(--accent-color);
margin-left: 0;
padding: 0.8rem 1.6rem;
border-radius: 4px;
transition: background-color .1s ease;
box-sizing: border-box;
/* TODO: calculating darken 10% color with using style vars from `--accent-color` */
border-bottom: 1px solid #389d70;
}
::v-deep(.nav-link:hover) {
/* TODO: calculating lighten 10% color with using style vars from `--accent-color` */
background-color: #4abf8a;
}
.features {
border-top: 1px solid var(--border-color);
padding: 1.2rem 0;
margin-top: 2.5rem;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
align-content: stretch;
justify-content: space-between;
}
.feature {
flex-grow: 1;
flex-basis: 30%;
max-width: 30%;
}
.feature h2 {
font-size: 1.4rem;
font-weight: 500;
border-bottom: none;
padding-bottom: 0;
/* TODO: calculating lighten 10% color with using style :vars from `--text-color` */
color: #3a5169;
}
.feature p {
/* TODO: calculating lighten 25% color with using style :vars from `--text-color` */
color: #4e6e8e;
}
.footer {
padding: 2.5rem;
border-top: 1px solid var(--border-color);
text-align: center;
/* TODO: calculating lighten 25% color with using style :vars from `--text-color` */
color: #4e6e8e;
}
@media screen and (max-width: 719px) {
.features {
flex-direction: column;
}
.feature {
max-width: 100%;
padding: 0 2.5rem;
}
}
@media screen and (max-width: 429px) {
.hero img {
max-height: 210px;
margin: 2rem auto 1.2rem;
}
.hero h1 {
font-size: 2rem;
}
.hero h1,
.hero .description,
.hero .action {
margin: 1.2rem auto;
}
.hero .description {
font-size: 1.2rem;
}
.hero .action-button {
font-size: 1rem;
padding: 0.6rem 1.2rem;
}
.feature h2 {
font-size: 1.25rem;
}
}
</style>
Loading

0 comments on commit 3a0af0b

Please sign in to comment.