Skip to content
Snippets Groups Projects
Commit b04842c0 authored by Julian (sysadmin)'s avatar Julian (sysadmin)
Browse files

Removed custom default theme favoring upstream default theme


The theme was “ejected” and customised, however now with VuePress update, trying to use theme from VuePress package, meaning we’ll get more updates to theme, but need to modify some pages slightly to fit theme features. New Footer component to standardise, and moved out Home pages parts to theme provided blocks for hero part.

Signed-off-by: default avatarJulian Gallimore <julian@mactwister.com>
parent c26d8d0f
No related branches found
No related tags found
1 merge request!4Update vuepress from v0 to v1 (latest)
Showing
with 83 additions and 1543 deletions
# This file is a template, and might need editing before it works on your project.
# Full project: https://gitlab.com/pages/hexo
image: node:8
# Full project: https://gitlab.com/pages/vuepress
image: node:14
pages:
script:
......
v8.17.0
v14.15
<template>
<div class="footer">
{{ copy }} -
<a href="/about/people.html">Contact us</a>
<br>
<br>
<small>
Follow us on
<span v-for="(social, key) in socialLinks" :key="social.title">
<a :href="social.url">{{ social.title }}</a>
<span v-if="key < Object.keys(socialLinks).length - 1"> | </span>
</span>
</small>
<Sponsors :sponsors="sponsors" v-if="sponsors" />
</div>
</template>
<script>
import Sponsors from "./Sponsors.vue";
export default {
props: ['copy', 'socialLinks', 'sponsors']
};
</script>
<style scoped lang="stylus">
.footer
padding 2.5rem
border-top 1px solid $borderColor
text-align center
color lighten($textColor, 25%)
</style>
......@@ -18,6 +18,9 @@ export default {
</script>
<style scoped>
#sponsors {
padding-top: 25px;
}
.logos {
display: flex;
flex-wrap: wrap;
......@@ -27,8 +30,12 @@ export default {
}
.logo {
margin: 12px;
width: 20%;
}
.logo img {
max-width: 300px;
width: auto;
height: auto;
max-width: 100%;
max-height: 120px;
}
</style>
......@@ -35,13 +35,22 @@ module.exports = {
href: '/android-icon-192x192.png'
}]
],
/**
* Theme configuration, here is the default theme configuration for VuePress.
*
* ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html
*/
themeConfig: {
repo: 'https://gitlab.fabcloud.org/academany/fabacademy/site',
repoLabel: 'Edit',
lastUpdated: 'Last Updated',
editLinks: false,
logo: '/fab-academy2.jpg',
editLinkText: 'Help us improve this page!',
footerCopy: "© 2020 Fab Academy",
socialLinks: [
{ title: "Twitter", url: "https://twitter.com/fabacademany" },
{ title: "Instagram", url: "https://instagram.com/fabacademany" },
{ title: "Facebook", url: "https://facebook.com/fabacademany" },
{ title: "Linkedin", url: "https://www.linkedin.com/groups/89815" },
{ title: "YouTube", url: "https://www.youtube.com/c/FabAcademyGlobal" }
],
nav: [{
text: 'About',
items: [{
......@@ -299,11 +308,11 @@ module.exports = {
]
}
},
// Not working? TODO: double check
/**
* Apply plugins,ref:https://v1.vuepress.vuejs.org/plugin/
*/
plugins: [
[
'@vuepress/google-analytics',
{ ga: 'UA-46064152-5' }
]
'@vuepress/back-to-top',
['@vuepress/google-analytics', { ga: 'UA-46064152-5' }]
]
}
/**
* Client app enhancement file.
*
* https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements
*/
import 'leaflet/dist/leaflet.css';
export default ({
......@@ -6,5 +11,5 @@ export default ({
router, // the router instance for the app
siteData // site metadata
}) => {
// ...apply enhancements to the app
// ...apply enhancements for the site.
}
File moved
......@@ -3,3 +3,19 @@
*
* ref:https://v1.vuepress.vuejs.org/config/#index-styl
*/
@import url('https://fonts.googleapis.com/css?family=PT+Sans+Narrow');
body {
font-family: Helvetica, 'PT Sans Narrow', sans-serif;
font-size 16px;
}
.site-name.can-hide {
display: none;
}
.home .hero img {
width: 100%;
height: auto;
max-height: none !important;
}
......@@ -4,15 +4,7 @@
* ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl
*/
@import url('https://fonts.googleapis.com/css?family=PT+Sans+Narrow');
$accentColor = #bd2641
$textColor = #2c3e50
$borderColor = #eaecef
$codeBgColor = #282c34
body {
font-size 16px;
}
<template>
<form
id="search-form"
class="algolia-search-wrapper search-box"
>
<input
id="algolia-search-input"
class="search-query"
>
</form>
</template>
<script>
export default {
props: ['options'],
mounted () {
this.initialize(this.options, this.$lang)
},
methods: {
initialize (userOptions, lang) {
Promise.all([
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
]).then(([docsearch]) => {
docsearch = docsearch.default
const { algoliaOptions = {}} = userOptions
docsearch(Object.assign(
{},
userOptions,
{
inputSelector: '#algolia-search-input',
// #697 Make docsearch work well at i18n mode.
algoliaOptions: Object.assign({
'facetFilters': [`lang:${lang}`].concat(algoliaOptions.facetFilters || [])
}, algoliaOptions)
}
))
})
},
update (options, lang) {
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
this.initialize(options, lang)
}
},
watch: {
$lang (newValue) {
this.update(this.options, newValue)
},
options (newValue) {
this.update(newValue, this.$lang)
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.algolia-search-wrapper
& > span
vertical-align middle
.algolia-autocomplete
line-height normal
.ds-dropdown-menu
background-color #fff
border 1px solid #999
border-radius 4px
font-size 16px
margin 6px 0 0
padding 4px
text-align left
&:before
border-color #999
[class*=ds-dataset-]
border none
padding 0
.ds-suggestions
margin-top 0
.ds-suggestion
border-bottom 1px solid $borderColor
.algolia-docsearch-suggestion--highlight
color #2c815b
.algolia-docsearch-suggestion
border-color $borderColor
padding 0
.algolia-docsearch-suggestion--category-header
padding 5px 10px
margin-top 0
background $accentColor
color #fff
font-weight 600
.algolia-docsearch-suggestion--highlight
background rgba(255, 255, 255, 0.6)
.algolia-docsearch-suggestion--wrapper
padding 0
.algolia-docsearch-suggestion--title
font-weight 600
margin-bottom 0
color $textColor
.algolia-docsearch-suggestion--subcategory-column
vertical-align top
padding 5px 7px 5px 5px
border-color $borderColor
background #f1f3f5
&:after
display none
.algolia-docsearch-suggestion--subcategory-column-text
color #555
.algolia-docsearch-footer
border-color $borderColor
.ds-cursor .algolia-docsearch-suggestion--content
background-color #e7edf3 !important
color $textColor
@media (min-width: $MQMobile)
.algolia-search-wrapper
.algolia-autocomplete
.algolia-docsearch-suggestion
.algolia-docsearch-suggestion--subcategory-column
float none
width 150px
min-width 150px
display table-cell
.algolia-docsearch-suggestion--content
float none
display table-cell
width 100%
vertical-align top
.ds-dropdown-menu
min-width 515px !important
@media (max-width: $MQMobile)
.algolia-search-wrapper
.ds-dropdown-menu
min-width calc(100vw - 4rem) !important
max-width calc(100vw - 4rem) !important
.algolia-docsearch-suggestion--wrapper
padding 5px 7px 5px 5px !important
.algolia-docsearch-suggestion--subcategory-column
padding 0 !important
background white !important
.algolia-docsearch-suggestion--subcategory-column-text:after
content " > "
font-size 10px
line-height 14.4px
display inline-block
width 5px
margin -3px 3px 0
vertical-align middle
</style>
<template>
<div
class="dropdown-wrapper"
:class="{ open }"
>
<a
class="dropdown-title"
@click="toggle"
>
<span class="title">{{ item.text }}</span>
<span
class="arrow"
:class="open ? 'down' : 'right'"
></span>
</a>
<DropdownTransition>
<ul
class="nav-dropdown"
v-show="open"
>
<li
class="dropdown-item"
:key="subItem.link || index"
v-for="(subItem, index) in item.items"
>
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
<ul
class="dropdown-subitem-wrapper"
v-if="subItem.type === 'links'"
>
<li
class="dropdown-subitem"
:key="childSubItem.link"
v-for="childSubItem in subItem.items"
>
<NavLink :item="childSubItem"/>
</li>
</ul>
<NavLink
v-else
:item="subItem"
/>
</li>
</ul>
</DropdownTransition>
</div>
</template>
<script>
import NavLink from './NavLink.vue'
import DropdownTransition from './DropdownTransition.vue'
export default {
components: { NavLink, DropdownTransition },
data () {
return {
open: false
}
},
props: {
item: {
required: true
}
},
methods: {
toggle () {
this.open = !this.open
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.dropdown-wrapper
cursor pointer
.dropdown-title
display block
&:hover
border-color transparent
.arrow
vertical-align middle
margin-top -1px
margin-left 0.4rem
.nav-dropdown
.dropdown-item
color inherit
line-height 1.7rem
h4
margin 0.45rem 0 0
border-top 1px solid #eee
padding 0.45rem 1.5rem 0 1.25rem
.dropdown-subitem-wrapper
padding 0
list-style none
.dropdown-subitem
font-size 0.9em
a
display block
line-height 1.7rem
position relative
border-bottom none
font-weight 400
margin-bottom 0
padding 0 1.5rem 0 1.25rem
&:hover
color $accentColor
&.router-link-active
color $accentColor
&::after
content ""
width 0
height 0
border-left 5px solid $accentColor
border-top 3px solid transparent
border-bottom 3px solid transparent
position absolute
top calc(50% - 2px)
left 9px
&:first-child h4
margin-top 0
padding-top 0
border-top 0
@media (max-width: $MQMobile)
.dropdown-wrapper
&.open .dropdown-title
margin-bottom 0.5rem
.nav-dropdown
transition height .1s ease-out
overflow hidden
.dropdown-item
h4
border-top 0
margin-top 0
padding-top 0
h4, & > a
font-size 15px
line-height 2rem
.dropdown-subitem
font-size 14px
padding-left 1rem
@media (min-width: $MQMobile)
.dropdown-wrapper
height 1.8rem
&:hover .nav-dropdown
// override the inline style.
display block !important
.dropdown-title .arrow
// make the arrow always down at desktop
border-left 4px solid transparent
border-right 4px solid transparent
border-top 6px solid $arrowBgColor
border-bottom 0
.nav-dropdown
display none
// Avoid height shaked by clicking
height auto !important
box-sizing border-box;
max-height calc(100vh - 2.7rem)
overflow-y auto
position absolute
top 100%
right 0
background-color #fff
padding 0.6rem 0
border 1px solid #ddd
border-bottom-color #ccc
text-align left
border-radius 0.25rem
white-space nowrap
margin 0
</style>
<template>
<transition
name="dropdown"
@enter="setHeight"
@after-enter="unsetHeight"
@before-leave="setHeight"
>
<slot/>
</transition>
</template>
<script>
export default {
name: 'DropdownTransition',
methods: {
setHeight (items) {
// explicitly set height so that it can be transitioned
items.style.height = items.scrollHeight + 'px'
},
unsetHeight (items) {
items.style.height = ''
}
}
}
</script>
<style lang="stylus">
.dropdown-enter, .dropdown-leave-to
height 0 !important
</style>
<template>
<div class="home">
<div class="padded">
<Content custom/>
</div>
<div class="footer" v-if="data.footer">
{{ data.footer }} -
<a href="/contacts">Contact us</a>
<br>
<br>
<small>
Follow us on
<a href="https://twitter.com/fabacademany">Twitter</a>
|
<a href="https://instagram.com/fabacademany">Instagram</a>
|
<a href="https://facebook.com/fabacademany">Facebook</a>
|
<a href="https://www.youtube.com/channel/UCzjiE8opj7d1uHTLr-3vj1A/featured">YouTube</a>
</small>
<br>
<br>
<Sponsors :sponsors="data.sponsors"/>
</div>
</div>
</template>
<script>
import NavLink from "./NavLink.vue";
import Tweets from "../components/Tweets.vue";
import Testimonial from "../components/Testimonial.vue";
import Sponsors from "../components/Sponsors.vue";
export default {
components: { NavLink, Tweets },
computed: {
data() {
return this.$page.frontmatter;
},
actionLink() {
return {
link: this.data.actionLink,
text: this.data.actionText
};
}
}
};
</script>
<style lang="stylus">
@import './styles/config.styl';
.home {
.padded {
padding-left: 2rem;
padding-right: 2rem;
max-width: 960px;
margin: 0px auto;
}
.hero {
text-align: center;
padding: $navbarHeight 0 0;
background: url('/banner-FAC2019-website_2-1170x500.jpg');
background-repeat: no-repeat;
background-size: cover;
height: 300px;
img {
width: 100%;
// max-height: 280px;
display: block;
margin: 3rem auto 1.5rem;
}
h1 {
font-size: 3rem;
}
h1, .description, .action {
margin: 1.8rem auto;
}
h1 {
margin-top: 1em;
color: white;
}
.description {
max-width: 35rem;
font-size: 1.8rem;
font-weight: bold;
text-transform: uppercase;
line-height: 1.3;
color: #000033;
}
.action-button {
display: inline-block;
font-size: 1.2rem;
color: #fff;
background-color: $accentColor;
padding: 0.8rem 1.6rem;
border-radius: 4px;
transition: background-color 0.1s ease;
box-sizing: border-box;
border-bottom: 1px solid darken($accentColor, 10%);
&:hover {
background-color: lighten($accentColor, 10%);
}
}
}
.socialmedia {
display: flex;
flex-wrap: wrap;
align-items: 'flex-start';
align-content: center;
justify-content: 'space-around';
.header {
width: 100%;
text-align: center;
font-size: 24px;
padding-top: 32px;
padding-bottom: 32px;
}
.tweets {
margin: 0 auto;
flex-grow: 1;
padding-right: 32px;
}
.testimonial {
// border: 1px solid red;
// text-align: right;
flex-grow: 2;
}
}
.features {
//border-bottom: 1px solid $borderColor;
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%;
h2 {
font-size: 1.4rem;
font-weight: 500;
border-bottom: none;
padding-bottom: 0;
color: lighten($textColor, 10%);
}
p {
color: lighten($textColor, 25%);
}
}
.footer {
padding: 2.5rem;
border-top: 1px solid $borderColor;
text-align: center;
color: lighten($textColor, 25%);
}
}
@media (max-width: $MQMobile) {
.home {
.features {
flex-direction: column;
}
.feature {
max-width: 100%;
padding: 0 2.5rem;
}
}
}
@media (max-width: $MQMobileNarrow) {
.home {
.padded {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.hero {
img {
max-height: 210px;
margin: 2rem auto 1.2rem;
}
h1 {
font-size: 2rem;
}
h1, .description, .action {
margin: 1.2rem auto;
}
h1 {
margin-top: 1.8em;
}
.description {
font-size: 1.2rem;
}
.action-button {
font-size: 1rem;
padding: 0.6rem 1.2rem;
}
}
.feature {
h2 {
font-size: 1.25rem;
}
}
}
}
</style>
<template>
<div
class="theme-container"
:class="pageClasses"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
>
<Navbar
v-if="shouldShowNavbar"
@toggle-sidebar="toggleSidebar"
/>
<div
class="sidebar-mask"
@click="toggleSidebar(false)"
></div>
<Sidebar
:items="sidebarItems"
@toggle-sidebar="toggleSidebar"
>
<slot
name="sidebar-top"
slot="top"
/>
<slot
name="sidebar-bottom"
slot="bottom"
/>
</Sidebar>
<div
class="custom-layout"
v-if="$page.frontmatter.layout"
>
<component :is="$page.frontmatter.layout"/>
</div>
<Home v-else-if="$page.frontmatter.home"/>
<Page
v-else
:sidebar-items="sidebarItems"
>
<slot
name="page-top"
slot="top"
/>
<slot
name="page-bottom"
slot="bottom"
/>
</Page>
<SWUpdatePopup :updateEvent="swUpdateEvent"/>
</div>
</template>
<script>
import Vue from 'vue'
import nprogress from 'nprogress'
import Home from './Home.vue'
import Navbar from './Navbar.vue'
import Page from './Page.vue'
import Sidebar from './Sidebar.vue'
import SWUpdatePopup from './SWUpdatePopup.vue'
import { resolveSidebarItems } from './util'
export default {
components: { Home, Page, Sidebar, Navbar, SWUpdatePopup },
data () {
return {
isSidebarOpen: false,
swUpdateEvent: null
}
},
computed: {
shouldShowNavbar () {
const { themeConfig } = this.$site
const { frontmatter } = this.$page
if (
frontmatter.navbar === false ||
themeConfig.navbar === false) {
return false
}
return (
this.$title ||
themeConfig.logo ||
themeConfig.repo ||
themeConfig.nav ||
this.$themeLocaleConfig.nav
)
},
shouldShowSidebar () {
const { frontmatter } = this.$page
return (
!frontmatter.layout &&
!frontmatter.home &&
frontmatter.sidebar !== false &&
this.sidebarItems.length
)
},
sidebarItems () {
return resolveSidebarItems(
this.$page,
this.$route,
this.$site,
this.$localePath
)
},
pageClasses () {
const userPageClass = this.$page.frontmatter.pageClass
return [
{
'no-navbar': !this.shouldShowNavbar,
'sidebar-open': this.isSidebarOpen,
'no-sidebar': !this.shouldShowSidebar
},
userPageClass
]
}
},
mounted () {
window.addEventListener('scroll', this.onScroll)
// configure progress bar
nprogress.configure({ showSpinner: false })
this.$router.beforeEach((to, from, next) => {
if (to.path !== from.path && !Vue.component(to.name)) {
nprogress.start()
}
next()
})
this.$router.afterEach(() => {
nprogress.done()
this.isSidebarOpen = false
})
this.$on('sw-updated', this.onSWUpdated)
},
methods: {
toggleSidebar (to) {
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
},
// side swipe
onTouchStart (e) {
this.touchStart = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY
}
},
onTouchEnd (e) {
const dx = e.changedTouches[0].clientX - this.touchStart.x
const dy = e.changedTouches[0].clientY - this.touchStart.y
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
if (dx > 0 && this.touchStart.x <= 80) {
this.toggleSidebar(true)
} else {
this.toggleSidebar(false)
}
}
},
onSWUpdated (e) {
this.swUpdateEvent = e
}
}
}
</script>
<style src="prismjs/themes/prism-tomorrow.css"></style>
<style src="./styles/theme.styl" lang="stylus"></style>
<template>
<router-link
class="nav-link"
:to="link"
v-if="!isExternal(link)"
:exact="exact"
>{{ item.text }}</router-link>
<a
v-else
:href="link"
class="nav-link external"
:target="isMailto(link) || isTel(link) ? null : '_blank'"
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
>
{{ item.text }}
<OutboundLink/>
</a>
</template>
<script>
import { isExternal, isMailto, isTel, ensureExt } from './util'
export default {
props: {
item: {
required: true
}
},
computed: {
link () {
return ensureExt(this.item.link)
},
exact () {
if (this.$site.locales) {
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
}
return this.link === '/'
}
},
methods: {
isExternal,
isMailto,
isTel
}
}
</script>
<template>
<nav class="nav-links" v-if="userLinks.length || repoLink">
<!-- user links -->
<div class="nav-item" v-for="item in userLinks" :key="item.link">
<DropdownLink v-if="item.type === 'links'" :item="item"/>
<NavLink v-else :item="item"/>
</div>
<!-- repo link -->
<!-- <a
v-if="repoLink"
:href="repoLink"
class="repo-link"
target="_blank"
rel="noopener noreferrer"
>
{{ repoLabel }}
<OutboundLink/>
</a>-->
</nav>
</template>
<script>
import DropdownLink from "./DropdownLink.vue";
import { resolveNavLinkItem } from "./util";
import NavLink from "./NavLink.vue";
export default {
components: { NavLink, DropdownLink },
computed: {
userNav() {
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || [];
},
nav() {
const { locales } = this.$site;
if (locales && Object.keys(locales).length > 1) {
const currentLink = this.$page.path;
const routes = this.$router.options.routes;
const themeLocales = this.$site.themeConfig.locales || {};
const languageDropdown = {
text: this.$themeLocaleConfig.selectText || "Languages",
items: Object.keys(locales).map(path => {
const locale = locales[path];
const text =
(themeLocales[path] && themeLocales[path].label) || locale.lang;
let link;
// Stay on the current page
if (locale.lang === this.$lang) {
link = currentLink;
} else {
// Try to stay on the same page
link = currentLink.replace(this.$localeConfig.path, path);
// fallback to homepage
if (!routes.some(route => route.path === link)) {
link = path;
}
}
return { text, link };
})
};
return [...this.userNav, languageDropdown];
}
return this.userNav;
},
userLinks() {
return (this.nav || []).map(link => {
return Object.assign(resolveNavLinkItem(link), {
items: (link.items || []).map(resolveNavLinkItem)
});
});
},
repoLink() {
const { repo } = this.$site.themeConfig;
if (repo) {
return /^https?:/.test(repo) ? repo : `https://github.com/${repo}`;
}
},
repoLabel() {
if (!this.repoLink) return;
if (this.$site.themeConfig.repoLabel) {
return this.$site.themeConfig.repoLabel;
}
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0];
const platforms = ["GitHub", "GitLab", "Bitbucket"];
for (let i = 0; i < platforms.length; i++) {
const platform = platforms[i];
if (new RegExp(platform, "i").test(repoHost)) {
return platform;
}
}
return "Source";
}
}
};
</script>
<style lang="stylus">
@import './styles/config.styl';
.nav-links {
display: inline-block;
a {
line-height: 1.4rem;
color: inherit;
&:hover, &.router-link-active {
color: $accentColor;
}
}
.nav-item {
position: relative;
display: inline-block;
margin-left: 1.5rem;
line-height: 2rem;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 1.5rem;
}
}
.repo-link {
margin-left: 1.5rem;
}
}
@media (max-width: $MQMobile) {
.nav-links {
.nav-item, .repo-link {
margin-left: 0;
}
}
}
@media (min-width: $MQMobile) {
.nav-links a {
&:hover, &.router-link-active {
color: $textColor;
}
}
.nav-item > a:not(.external) {
&:hover, &.router-link-active {
margin-bottom: -2px;
border-bottom: 2px solid lighten($accentColor, 8%);
}
}
}
</style>
<template>
<header class="navbar">
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
<router-link :to="$localePath" class="home-link">
<img
class="logo"
v-if="$site.themeConfig.logo"
:src="$withBase($site.themeConfig.logo)"
:alt="$siteTitle"
>
<!-- <span
ref="siteName"
class="site-name"
v-if="$siteTitle"
:class="{ 'can-hide': $site.themeConfig.logo }"
>{{ $siteTitle }}</span>-->
</router-link>
<div class="links" :style="{
'max-width': linksWrapMaxWidth + 'px'
}">
<NavLinks class="can-hide"/>
<AlgoliaSearchBox v-if="isAlgoliaSearch" :options="algolia"/>
<SearchBox v-else-if="$site.themeConfig.search !== false"/>
</div>
</header>
</template>
<script>
import SidebarButton from "./SidebarButton.vue";
import AlgoliaSearchBox from "@theme/AlgoliaSearchBox";
import SearchBox from "./SearchBox.vue";
import NavLinks from "./NavLinks.vue";
export default {
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
data() {
return {
linksWrapMaxWidth: null
};
},
mounted() {
const MOBILE_DESKTOP_BREAKPOINT = 719; // refer to config.styl
const NAVBAR_VERTICAL_PADDING =
parseInt(css(this.$el, "paddingLeft")) +
parseInt(css(this.$el, "paddingRight"));
const handleLinksWrapWidth = () => {
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
this.linksWrapMaxWidth = null;
} else {
this.linksWrapMaxWidth =
this.$el.offsetWidth -
NAVBAR_VERTICAL_PADDING -
((this.$refs.siteName && this.$refs.siteName.offsetWidth) || 0);
}
};
handleLinksWrapWidth();
window.addEventListener("resize", handleLinksWrapWidth, false);
},
computed: {
algolia() {
return (
this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
);
},
isAlgoliaSearch() {
return this.algolia && this.algolia.apiKey && this.algolia.indexName;
}
}
};
function css(el, property) {
// NOTE: Known bug, will return 'auto' if style value is 'auto'
const win = el.ownerDocument.defaultView;
// null means not to return pseudo styles
return win.getComputedStyle(el, null)[property];
}
</script>
<style lang="stylus">
@import './styles/config.styl';
$navbar-vertical-padding = 0.7rem;
$navbar-horizontal-padding = 1.5rem;
.navbar {
padding: $navbar-vertical-padding $navbar-horizontal-padding;
line-height: $navbarHeight - 1.4rem;
position: relative;
a, span, img {
display: inline-block;
}
.logo {
height: $navbarHeight - 1.4rem;
// min-width: $navbarHeight - 1.4rem;
margin-right: 0.8rem;
vertical-align: top;
z-index: 99;
}
.site-name {
font-size: 1.3rem;
font-weight: 600;
color: $textColor;
position: relative;
}
.links {
padding-left: 1.5rem;
box-sizing: border-box;
background-color: transparent;
white-space: nowrap;
font-size: 0.9rem;
position: absolute;
right: $navbar-horizontal-padding;
top: $navbar-vertical-padding;
display: flex;
.search-box {
flex: 0 0 auto;
vertical-align: top;
}
.nav-links {
flex: 1;
}
}
}
@media (max-width: $MQMobile) {
.navbar {
padding-left: 4rem;
.can-hide {
display: none;
}
.links {
padding-left: 1.5rem;
}
}
}
</style>
<template>
<div class="theme-container">
<div class="content">
<h1>404</h1>
<blockquote>{{ getMsg() }}</blockquote>
<router-link to="/">Take me home.</router-link>
</div>
</div>
</template>
<script>
const msgs = [
`There's nothing here.`,
`How did we get here?`,
`That's a Four-Oh-Four.`,
`Looks like we've got some broken links.`
]
export default {
methods: {
getMsg () {
return msgs[Math.floor(Math.random() * msgs.length)]
}
}
}
</script>
<template>
<div class="page">
<slot name="top"/>
<Content class="content" />
<div class="page-edit">
<div class="edit-link" v-if="editLink">
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
<OutboundLink/>
</div>
<div class="last-updated" v-if="lastUpdated">
<span class="prefix">{{ lastUpdatedText }}:</span>
<span class="time">{{ lastUpdated }}</span>
</div>
</div>
<div class="page-nav" v-if="prev || next">
<p class="inner">
<span v-if="prev" class="prev">
<router-link v-if="prev" class="prev" :to="prev.path">{{ prev.title || prev.path }}</router-link>
</span>
<span v-if="next" class="next">
<router-link v-if="next" :to="next.path">{{ next.title || next.path }}</router-link>
</span>
</p>
</div>
<slot name="bottom"/>
<div class="footer">
© 2019 Fab Academy -
<a href="/about/people.html">Contact us</a>
<br>
<br>
<small>
Follow us on
<a href="https://twitter.com/fabacademany">Twitter</a>
|
<a href="https://instagram.com/fabacademany">Instagram</a>
|
<a href="https://www.facebook.com/fabacademany">Facebook</a>
|
<a href="https://www.linkedin.com/groups/89815">Linkedin</a>
</small>
</div>
</div>
</template>
<script>
import { resolvePage, normalize, outboundRE, endingSlashRE } from "./util";
export default {
props: ["sidebarItems"],
computed: {
lastUpdated() {
if (this.$page.lastUpdated) {
return new Date(this.$page.lastUpdated).toLocaleString(this.$lang);
}
},
lastUpdatedText() {
if (typeof this.$themeLocaleConfig.lastUpdated === "string") {
return this.$themeLocaleConfig.lastUpdated;
}
if (typeof this.$site.themeConfig.lastUpdated === "string") {
return this.$site.themeConfig.lastUpdated;
}
return "Last Updated";
},
prev() {
const prev = this.$page.frontmatter.prev;
if (prev === false) {
return;
} else if (prev) {
return resolvePage(this.$site.pages, prev, this.$route.path);
} else {
return resolvePrev(this.$page, this.sidebarItems);
}
},
next() {
const next = this.$page.frontmatter.next;
if (next === false) {
return;
} else if (next) {
return resolvePage(this.$site.pages, next, this.$route.path);
} else {
return resolveNext(this.$page, this.sidebarItems);
}
},
editLink() {
if (this.$page.frontmatter.editLink === false) {
return;
}
const {
repo,
editLinks,
docsDir = "",
docsBranch = "master",
docsRepo = repo
} = this.$site.themeConfig;
let path = normalize(this.$page.path);
if (endingSlashRE.test(path)) {
path += "README.md";
} else {
path += ".md";
}
if (docsRepo && editLinks) {
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path);
}
},
editLinkText() {
return (
this.$themeLocaleConfig.editLinkText ||
this.$site.themeConfig.editLinkText ||
`Edit this page`
);
}
},
methods: {
createEditLink(repo, docsRepo, docsDir, docsBranch, path) {
const bitbucket = /bitbucket.org/;
if (bitbucket.test(repo)) {
const base = outboundRE.test(docsRepo) ? docsRepo : repo;
return (
base.replace(endingSlashRE, "") +
`/${docsBranch}` +
(docsDir ? "/" + docsDir.replace(endingSlashRE, "") : "") +
path +
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
);
}
const base = outboundRE.test(docsRepo)
? docsRepo
: `https://github.com/${docsRepo}`;
return (
base.replace(endingSlashRE, "") +
`/edit/${docsBranch}` +
(docsDir ? "/" + docsDir.replace(endingSlashRE, "") : "") +
path
);
}
}
};
function resolvePrev(page, items) {
return find(page, items, -1);
}
function resolveNext(page, items) {
return find(page, items, 1);
}
function find(page, items, offset) {
const res = [];
items.forEach(item => {
if (item.type === "group") {
res.push(...(item.children || []));
} else {
res.push(item);
}
});
for (let i = 0; i < res.length; i++) {
const cur = res[i];
if (cur.type === "page" && cur.path === page.path) {
return res[i + offset];
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl';
@require './styles/wrapper.styl';
.footer {
border-top: 1px solid #fafafa;
text-align: center;
padding-top: 2rem;
}
.page {
padding-bottom: 2rem;
}
.page-edit {
@extend $wrapper;
padding-top: 1rem;
padding-bottom: 1rem;
overflow: auto;
.edit-link {
display: inline-block;
a {
color: lighten($textColor, 25%);
margin-right: 0.25rem;
}
}
.last-updated {
float: right;
font-size: 0.9em;
.prefix {
font-weight: 500;
color: lighten($textColor, 25%);
}
.time {
font-weight: 400;
color: #aaa;
}
}
}
.page-nav {
@extend $wrapper;
padding-top: 1rem;
padding-bottom: 0;
.inner {
min-height: 2rem;
margin-top: 0;
border-top: 1px solid $borderColor;
padding-top: 1rem;
overflow: auto; // clear float
}
.next {
float: right;
}
}
@media (max-width: $MQMobile) {
.page-edit {
.edit-link {
margin-bottom: 0.5rem;
}
.last-updated {
font-size: 0.8em;
float: none;
text-align: left;
}
}
}
</style>
<template>
<transition name="sw-update-popup">
<div
v-if="enabled"
class="sw-update-popup"
>
{{message}}<br>
<button @click="reload">{{buttonText}}</button>
</div>
</transition>
</template>
<script>
export default {
props: {
updateEvent: {
type: Object,
default: null
}
},
computed: {
popupConfig () {
for (const config of [this.$themeLocaleConfig, this.$site.themeConfig]) {
const sw = config.serviceWorker
if (sw && sw.updatePopup) {
return typeof sw.updatePopup === 'object' ? sw.updatePopup : {}
}
}
return null
},
enabled () {
return Boolean(this.popupConfig && this.updateEvent)
},
message () {
const c = this.popupConfig
return (c && c.message) || 'New content is available.'
},
buttonText () {
const c = this.popupConfig
return (c && c.buttonText) || 'Refresh'
}
},
methods: {
reload () {
if (this.updateEvent) {
this.updateEvent.skipWaiting().then(() => {
location.reload(true)
})
this.updateEvent = null
}
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.sw-update-popup
position fixed
right 1em
bottom 1em
padding 1em
border 1px solid $accentColor
border-radius 3px
background #fff
box-shadow 0 4px 16px rgba(0, 0, 0, 0.5)
text-align center
button
margin-top 0.5em
padding 0.25em 2em
.sw-update-popup-enter-active, .sw-update-popup-leave-active
transition opacity 0.3s, transform 0.3s
.sw-update-popup-enter, .sw-update-popup-leave-to
opacity 0
transform translate(0, 50%) scale(0.5)
</style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment