This commit is contained in:
2020-11-08 09:12:17 +01:00
parent 598ea0cc25
commit 50527d0370
93 changed files with 14163 additions and 19 deletions

194
src/assets/scss/main.scss Normal file
View File

@@ -0,0 +1,194 @@
@tailwind base;
blockquote {
@apply border-l;
@apply border-l-4;
@apply border-l-blue-500;
@apply pl-4;
@apply italic;
@apply my-8;
p {
padding: 0 !important;
}
}
pre {
@apply border-l;
@apply border-l-2;
@apply border-l-gray-500;
@apply pl-4;
@apply mt-4;
@apply text-base;
@apply break-words;
@apply overflow-x-auto;
}
ol {
@apply list-decimal;
@apply ml-5;
}
ul {
@apply list-disc;
@apply ml-5;
}
:not(pre)>code {
@apply text-red-600;
@apply text-base;
}
@tailwind components;
@tailwind utilities;
@responsive {
.pxi-0 {
@apply px-0 #{!important};
}
.pyi-0 {
@apply py-0 #{!important};
}
.pi-0 {
@apply p-0 #{!important};
}
}
.animated-link:after {
content: '';
width: 0px;
height: 1px;
display: block;
transition: 300ms;
@apply bg-gray-500;
}
.animated-link:hover:after {
width: 100%;
}
.height-30px {
height:30px;
}
.no-border {
border-width: 0px !important;
}
.flex-post {
@apply border-b;
@apply border-b-gray-300;
@apply flex-col;
@apply w-full;
.post-card-image {
@apply h-56;
@apply w-full;
@apply object-cover;
@apply rounded;
@apply relative;
}
.post-card-title {
@apply leading-none;
@apply text-2xl;
@apply font-medium;
}
.post-card-excerpt {
@apply font-serif;
}
}
.author-list-item:nth-child(n+2) {
@apply -ml-3
}
@media (min-width: 768px) {
.with-large>.flex-post:nth-child(6n+1) {
@apply flex-100;
@apply flex-row;
.post-card-image-link {
@apply flex-auto;
@apply relative;
min-height: 380px;
@apply mr-6;
}
.post-card-image {
@apply absolute;
@apply h-full;
@apply w-full;
@apply object-cover;
@apply rounded-lg;
}
.post-card-content {
@apply flex-post-large-content;
@apply self-center
}
.post-card-title {
@apply text-4xl;
}
.post-card-excerpt {
@apply text-xl;
}
}
}
body[data-theme="dark"] {
background-color: #191b1f;
@apply text-gray-300;
.post-content-text {
@apply text-gray-300;
}
.post-card-excerpt {
@apply text-gray-500;
}
.pagination li {
@apply bg-gray-700;
@apply text-gray-200;
@apply border-gray-600;
&:hover {
@apply bg-gray-600;
@apply text-gray-900;
}
}
.pagination li.border-l-black {
@apply border-l-gray-300;
}
pre {
@apply text-gray-500;
}
.flex-post {
@apply border-b-gray-800;
}
.author-list-item img {
border-color: #191b1f;
}
.author-social {
a {
&:hover {
@apply text-white
}
}
}
}

View File

@@ -0,0 +1,86 @@
<template>
<ul class="flex pl-0 list-none rounded my-2">
<li class="w-10 relative block text-center py-2 leading-tight bg-white border border-gray-300 text-black ml-0 mr-1 rounded hover:bg-gray-300" v-if="!isFirstPage(currentPage, totalPages)">
<g-link :to="previousPage(currentPage,totalPages)" class="page-link" tabindex="-1" >&laquo;</g-link>
</li>
<li
v-for="page in pages" :key="page.name"
v-bind:class="[isCurrentPage(currentPage, page.name) ? 'border-l-2 border-l-black' : '']"
class="w-10 relative block py-2 text-center leading-tight bg-white border border-gray-300 text-black rounded hover:bg-gray-300 ml-1 mr-1">
<g-link
:to="page.link"
class="page-link"
:aria-label="page.name"
:aria-current="page.name"
>{{page.name}}</g-link>
</li>
<li class="w-10 relative block py-2 text-center leading-tight bg-white border border-gray-300 text-black ml-1 rounded hover:bg-gray-300" v-if="!isLastPage(currentPage, totalPages)">
<g-link :to="nextPage(currentPage,totalPages)" class="page-link" tabindex="-1" >&raquo;</g-link>
</li>
</ul>
</template>
<script>
export default {
props: {
baseUrl: String,
currentPage: Number,
totalPages: Number,
maxVisibleButtons: {
type: Number,
required: false,
default: 3
}
},
methods: {
isFirstPage(currentPage, totalPages) {
return currentPage == 1;
},
isLastPage(currentPage, totalPages) {
return currentPage == totalPages;
},
isCurrentPage(currentPage, pageElement) {
return currentPage == pageElement;
},
nextPage(currentPage, totalPages) {
return `${this.baseUrl}/${currentPage + 1}`;
},
previousPage(currentPage, totalPages) {
return currentPage === 2
? `${this.baseUrl}/`
: `${this.baseUrl}/${currentPage - 1}`;
}
},
computed: {
startPage() {
if (this.currentPage === 1) {
return 1;
}
if (this.currentPage === this.totalPages) {
return this.currentPage - 1;
}
return this.currentPage - 1;
},
pages() {
const range = [];
for (
let i = this.startPage;
i <=
Math.min(this.startPage + this.maxVisibleButtons - 1, this.totalPages);
i += 1
) {
range.push({
name: i,
isDisabled: i === this.currentPage,
link: i === 1 ? `${this.baseUrl}/` : `${this.baseUrl}/${i}`
});
}
return range;
}
}
};
</script>

View File

@@ -0,0 +1,62 @@
<template>
<div class="flex flex-post px-0 sm:px-4 pb-8 mb-8" v-bind:class="{'no-border': !border}">
<g-link :to="record.path" class="post-card-image-link">
<g-image :src="record.image" :alt="record.title" class="post-card-image"></g-image>
</g-link>
<div class="post-card-content">
<g-link :to="record.path">
<p class="uppercase font-medium text-xs text-blue-700 mt-3">{{ record.category.title }}</p>
<h2 class="post-card-title mt-0">
{{ record.title }}
</h2>
<p class="post-card-excerpt">{{ record.excerpt }}</p>
</g-link>
<div class="w-full post-card-meta pt-4">
<div class="avatars">
<div class="flex items-center">
<div class="flex justify-between items-center">
<ul class="list-none flex author-list">
<li v-for="author in record.author" :key="author.id" class="author-list-item">
<g-link :to="author.path" v-tooltip="author.name">
<g-image
:src="author.image"
:alt="author.name"
class="w-8 h-8 rounded-full bg-gray-200 border-2 border-white"
/>
</g-link>
</li>
</ul>
</div>
<div class="ml-3 pl-3 border-l flex flex-col text-xs leading-none uppercase">
<p>
<time :datetime="record.datetime" >{{ record.humanTime }}</time>
</p>
<time :datetime="record.datetime" >{{ record.startDate }}</time>
{{ record.status }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
record: {},
border: {
type: Boolean,
default: true
}
},
};
</script>
<style>
</style>

View File

@@ -0,0 +1,44 @@
<template>
<a role="button" @click.prevent="toggleTheme()"
:aria-label="'Toggle ' + nextTheme"
:title="'Toggle ' + nextTheme"
class="toggle-theme"
>
<font-awesome :icon="['fas', 'sun']" class="mr-3" v-if="theme === 'dark'"></font-awesome>
<font-awesome :icon="['fas', 'moon']" class="mr-3" v-if="theme === 'light'"></font-awesome>
</a>
</template>
<script>
let themes = ['light', 'dark']
export default {
props: {
theme: {
type: String,
},
},
computed: {
nextTheme() {
const currentIndex = themes.indexOf(this.theme)
const nextIndex = (currentIndex + 1) % themes.length
return themes[nextIndex]
}
},
methods: {
toggleTheme() {
const currentIndex = themes.indexOf(this.theme);
const nextIndex = (currentIndex + 1) % themes.length;
window.__setPreferredTheme(themes[nextIndex])
this.$emit('setTheme', themes[nextIndex])
}
},
async mounted() {
// set default
if (typeof window.__theme !== 'undefined') this.$emit('setTheme', window.__theme)
}
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div>
<img class="rounded" :src="img" :alt="imgAlt">
<div class="mt-2">
<div>
<div class="text-xs text-gray-600 uppercase font-bold">{{ eyebrow }}</div>
<div class="font-bold text-gray-700 leading-snug">
<a :href="url" class="hover:underline">{{ title }}</a>
</div>
<div class="mt-2 text-sm text-gray-600">{{ pricing }}</div>
</div>
</div>
</div>
<!-- TODO: how do I get markdown in here? -->
</template>
<script>
export default {
props: ['img', 'imgAlt', 'eyebrow', 'title', 'pricing', 'url']
}
</script>
<style>
</style>

BIN
src/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

47
src/index.html Normal file
View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html ${htmlAttrs}>
<head>
${head}
</head>
<body ${bodyAttrs}>
<script>
// Add dark / light detection that runs before Vue.js load. Borrowed from overreacted.io
// for this starter, i used the code from gridsome.org
(function() {
window.__onThemeChange = function() {};
function setTheme(newTheme) {
window.__theme = newTheme;
preferredTheme = newTheme;
document.body.setAttribute('data-theme', newTheme);
window.__onThemeChange(newTheme);
}
var preferredTheme;
try {
preferredTheme = localStorage.getItem('theme');
} catch (err) { }
window.__setPreferredTheme = function(newTheme) {
setTheme(newTheme);
try {
localStorage.setItem('theme', newTheme);
} catch (err) {}
}
var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkQuery.addListener(function(e) {
window.__setPreferredTheme(e.matches ? 'dark' : 'light');
});
setTheme(preferredTheme || (darkQuery.matches ? 'dark' : 'light'));
})();
</script>
${app}
${scripts}
</body>
</html>

56
src/layouts/Default.vue Normal file
View File

@@ -0,0 +1,56 @@
<template>
<div id="app" dark>
<HeaderPartial v-if="hideHeader!=true" @setTheme="setTheme" :theme="this.theme"></HeaderPartial>
<slot/>
<NavbarPartial :disableScroll="disableScroll" @setTheme="setTheme" :theme="this.theme"></NavbarPartial>
<FooterPartial></FooterPartial>
</div>
</template>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<script>
import HeaderPartial from '~/layouts/partials/HeaderWithNavbar.vue'
import NavbarPartial from '~/layouts/partials/Navbar.vue'
import FooterPartial from '~/layouts/partials/Footer.vue'
export default {
props: {
hideHeader: {
type: Boolean,
default: false
},
disableScroll: {
type: Boolean,
default: false
}
},
data: function() {
return {
theme: 'light'
};
},
methods: {
setTheme(mode) {
this.theme = mode
}
},
components: {
HeaderPartial,
NavbarPartial,
FooterPartial
},
metaInfo: {
bodyAttrs: {
class: "m-0"
}
}
}
</script>

View File

@@ -0,0 +1,47 @@
<template>
<nav class="relative bg-black pt-4 pb-16 text-sm text-gray-500">
<div class="container mx-auto flex flex-wrap justify-between h-12 items-center">
<div class="w-full md:w-1/2 text-center md:text-left">
Copyright {{ currentYear }} by {{ $static.metadata.siteName }}
| Design by <a href="https://ghost.org" target="_blank" class="hover:text-white">Ghost</a>
</div>
<div class="w-full md:w-1/2">
<ul class="list-none flex justify-center md:justify-end">
<li
:key="element.name"
v-for="(element,index) in $static.metadata.navigation"
class="hover:text-white"
v-bind:class="{'mr-6' : index != Object.keys($static.metadata.navigation).length - 1}"
>
<a :href="element.link" v-if="element.external" target="_blank" rel="noopener noreferrer">{{ element.name }}</a>
<g-link v-else :to="element.link" >{{element.name}}</g-link>
</li>
</ul>
</div>
</div>
</nav>
</template>
<static-query>
query {
metadata {
siteName
navigation : footerNavigation {
name
link
external
}
}
}
</static-query>
<script>
export default {
computed: {
currentYear() {
return new Date().getFullYear();
}
}
};
</script>

View File

@@ -0,0 +1,92 @@
<template>
<header class="bg-black relative z-1000 bg-radial-t-gray-to-black" id="header">
<nav class="flex items-center justify-between flex-wrap container mx-auto px-4 sm:px-0 py-4">
<div class="block flex-grow flex items-center w-auto height-30px">
<div class="text-sm flex-grow uppercase">
<ul class="list-none flex justify-left text-gray-300 uppercase">
<li
:key="element.name"
v-for="(element,index) in $static.metadata.navigation"
class="hover:text-white"
v-bind:class="{'mr-4' : index != Object.keys($static.metadata.navigation).length - 1}"
>
<a
:href="element.link"
v-if="element.external"
target="_blank"
rel="noopener noreferrer"
class="animated-link"
>{{ element.name }}</a>
<g-link v-else :to="element.link" class="animated-link">{{element.name}}</g-link>
</li>
</ul>
</div>
<div class="inline-block text-gray-400">
<ul class="list-none flex justify-center md:justify-end">
<li class="mr-0 sm:mr-6">
<theme-switcher v-on="$listeners" :theme="theme"/>
</li>
<li
:key="element.name"
v-for="(element,index) in $static.metadata.social"
class="hover:text-white hidden sm:block"
v-bind:class="{'mr-6' : index != Object.keys($static.metadata.social).length - 1}"
>
<span class="text-sm">
<a :href="element.link" target="_blank" rel="noopener noreferrer">
<font-awesome :icon="['fab', element.icon]" />
</a>
</span>
</li>
</ul>
</div>
</div>
</nav>
<div class="logo pt-0 pb-16 md:pb-32 md:pt-16 container mx-auto text-center text-white">
<h2 class="m-0">
<span class="text-4xl">
<font-awesome :icon="['fas', 'ghost']" class="mb-1 mr-3"></font-awesome>
</span>
<span class="text-5xl text-white">{{ $static.metadata.siteName }}</span>
</h2>
<div class="text-gray-400 font-thin text-xl">{{ $static.metadata.siteDescription }}</div>
</div>
</header>
</template>
<script>
import ThemeSwitcher from '~/components/ThemeSwitcher'
export default {
props: {
theme: {
type: String,
},
},
components : {
ThemeSwitcher
}
};
</script>
<static-query>
query {
metadata {
siteName
siteDescription
navigation : headerNavigation {
name
link
external
}
social {
icon
link
}
}
}
</static-query>

View File

@@ -0,0 +1,126 @@
<template>
<div class="fixed inset-0 h-16 bg-black">
<nav
class="flex items-center justify-between flex-wrap container mx-auto px-4 sm:px-0 py-4 transition-all transition-500"
v-bind:class="{
'opacity-100': !disableScroll && scrollPosition > headerHeight,
'opacity-0': !disableScroll && scrollPosition < headerHeight
}">
<div class="block flex-grow flex items-center w-auto">
<div class="flex items-center flex-shrink-0 text-white mr-6">
<font-awesome :icon="['fas', 'ghost']" class="mr-3"></font-awesome>
<span class="font-semibold text-xl tracking-tight">{{ $static.metadata.siteName }}</span>
</div>
<div class="text-sm flex-grow uppercase">
<ul
class="list-none flex justify-left text-gray-300 uppercase transition-all transition-500">
<li
:key="element.name"
v-for="(element,index) in $static.metadata.navigation"
class="hover:text-white"
v-bind:class="{'mr-4' : index != Object.keys($static.metadata.navigation).length - 1}"
>
<a
:href="element.link"
v-if="element.external"
target="_blank"
rel="noopener noreferrer"
class="animated-link"
>{{ element.name }}</a>
<g-link v-else :to="element.link" class="animated-link">{{element.name}}</g-link>
</li>
</ul>
</div>
<div class="inline-block text-gray-400">
<ul class="list-none flex justify-center md:justify-end">
<li class="mr-0 sm:mr-6">
<theme-switcher v-on="$listeners" :theme="theme"/>
</li>
<li
:key="element.name"
v-for="(element,index) in $static.metadata.social"
class="hover:text-white hidden sm:block"
v-bind:class="{'mr-6' : index != Object.keys($static.metadata.social).length - 1}"
>
<span class="text-sm">
<a :href="element.link" target="_blank" rel="noopener noreferrer">
<font-awesome :icon="['fab', element.icon]" />
</a>
</span>
</li>
</ul>
</div>
</div>
</nav>
</div>
</template>
<script>
/*
* I'm a lazy guy, so i used this script
* https://codepen.io/ninaregli/pen/OjeMLP
* to calculate the current scroll position
*
* Will be used to add/remove the additional
* css classes to show the sticky navbar
*/
import ThemeSwitcher from '~/components/ThemeSwitcher'
export default {
components : {
ThemeSwitcher
},
props: {
disableScroll: {
type: Boolean,
default: false
},
theme: {
type: String
}
},
data: function() {
return {
scrollPosition: null,
headerHeight: 0
};
},
methods: {
updateScroll() {
this.scrollPosition = window.scrollY;
},
setHeaderHeight(height) {
this.headerHeight = height;
}
},
mounted() {
if( !this.disableScroll ) {
var height = document.getElementById("header").clientHeight;
this.setHeaderHeight(height);
window.addEventListener("scroll", this.updateScroll);
}
}
};
</script>
<static-query>
query {
metadata {
siteName
navigation : headerNavigation {
name
link
external
}
social {
icon
link
}
}
}
</static-query>

29
src/main.js Normal file
View File

@@ -0,0 +1,29 @@
import DefaultLayout from '~/layouts/Default.vue'
import VTooltip from 'v-tooltip'
import '~/assets/scss/main.scss';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { config, library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { fab } from '@fortawesome/free-brands-svg-icons'
import '@fortawesome/fontawesome-svg-core/styles.css';
import VacationCard from "~/components/VacationCard.vue";
config.autoAddCss = false;
library.add(fas);
library.add(fab);
export default function (Vue, {router, head, isClient}) {
Vue.component('Layout', DefaultLayout)
Vue.component('VacationCard', VacationCard)
Vue.component('font-awesome', FontAwesomeIcon)
Vue.use(VTooltip, {
defaultPlacement: 'top-end',
defaultClass: 'bg-black text-xs px-2 leading-normal py-1 rounded absolute text-gray-400 max-w-xs mb-1'
})
}

33
src/pages/About.vue Normal file
View File

@@ -0,0 +1,33 @@
<template>
<Layout :hideHeader="false" :disableScroll="true">
<div class="container sm:pxi-0 mx-auto overflow-x-hidden">
<VacationCard
img="/img/garden.jpg"
imgAlt="Beach in Cancun"
eyebrow="Private Villa"
title="
- Relaxing All-Inclusive Resort in Cancun
- Test
"
pricing="$299 USD per night"
url="/vacations/cancun"
/>
</div>
</Layout>
</template>
<script>
export default {
metaInfo: {
title: "About us"
}
};
</script>

62
src/pages/Index.vue Normal file
View File

@@ -0,0 +1,62 @@
<template>
<Layout>
<div class="container sm:pxi-0 mx-auto overflow-x-hidden">
<div class="flex flex-wrap with-large pt-8 pb-8 mx-4 sm:-mx-4">
<PostListItem v-for="edge in $page.entries.edges" :key="edge.node.id" :record="edge.node" />
</div>
</div>
</Layout>
</template>
<page-query>
query($page:Int) {
entries: allBlog(perPage: 9, page: $page) @paginate {
totalCount
pageInfo {
totalPages
currentPage
}
edges {
node {
name
title
team
rank
excerpt
image(width:800)
path
timeToRead
humanTime : created(format:"DD MMM YYYY")
datetime : created
category {
id
title
}
author {
id
name
image(width:64, height:64, fit:inside)
path
}
}
}
}
}
</page-query>
<script>
import PostListItem from '~/components/PostListItem.vue';
export default {
metaInfo: {
title: "Hello, world!"
},
components: {
PostListItem
}
};
</script>

143
src/templates/Author.vue Normal file
View File

@@ -0,0 +1,143 @@
<template>
<Layout :hideHeader="true" :disableScroll="true">
<div class="container sm:pxi-0 mx-auto overflow-x-hidden pt-24">
<div class="flex flex-row flex-wrap items-center mx-4 sm:mx-0">
<div class="w-full md:w-1/6 mx-auto sm:mx-0">
<g-image
:src="$page.author.image"
class="rounded-full bg-gray-200 w-32 h-32 border-4 border-gray-400 mx-auto md:mx-0"
></g-image>
</div>
<div class="w-full md:w-5/6 text-center md:text-left md:pl-8 lg:pl-0">
<h1 class="pb-0 mb-0 mt-0 text-4xl font-medium">{{ $page.author.name }}</h1>
<p class="text-gray-700 text-xl" v-if="$page.author.bio">{{ $page.author.bio }}</p>
<div class="author-social">
{{ $page.author.belongsTo.totalCount }} Projects
&nbsp;&middot;&nbsp;
<!-- <a
:href="$page.author.facebook"
target="_blank"
rel="noopener noreferrer"
class="text-gray-400 hover:text-black"
>
<font-awesome :icon="['fab', 'facebook']" />
</a>
&nbsp;
<a
:href="$page.author.twitter"
target="_blank"
rel="noopener noreferrer"
class="text-gray-400 hover:text-black"
>
<font-awesome :icon="['fab', 'twitter']" />
</a>
&nbsp; -->
<a
:href="$page.author.linkedin"
target="_blank"
rel="noopener noreferrer"
class="text-gray-400 hover:text-black"
>
<font-awesome :icon="['fab', 'linkedin']" />
</a>
</div>
</div>
</div>
<div class="pt-8 border-b mx-4 sm:-mx-4"></div>
<section class="post-content container mx-auto relative font-serif text-gray-700">
<div class="post-content-text text-xl" v-html="$page.author.content"></div>
</section>
<div class="pt-8 border-b mx-4 sm:-mx-4"></div>
<div class="flex flex-wrap pt-8 pb-8 mx-4 sm:-mx-4">
<PostListItem
v-for="edge in $page.author.belongsTo.edges"
:key="edge.node.id"
:record="edge.node"
/>
</div>
<div class="pagination flex justify-center mb-8">
<Pagination
:baseUrl="$page.author.path"
:currentPage="$page.author.belongsTo.pageInfo.currentPage"
:totalPages="$page.author.belongsTo.pageInfo.totalPages"
:maxVisibleButtons="5"
v-if="$page.author.belongsTo.pageInfo.totalPages > 1"
/>
</div>
</div>
</Layout>
</template>
<page-query>
query($id: ID!, $page:Int) {
author(id: $id) {
name
path
bio
image(width:150, height:150)
facebook
twitter
linkedin
content
belongsTo(perPage: 5, page: $page) @paginate {
totalCount
pageInfo {
totalPages
currentPage
}
edges {
node {
... on Blog {
title
excerpt
image(width:800)
path
timeToRead
humanTime : created(format:"DD MMM YYYY")
datetime : created
category {
id
title
}
author {
id
name
image(width:64, height:64, fit:inside)
path
}
}
}
}
}
}
}
</page-query>
<script>
import PostListItem from "~/components/PostListItem.vue";
import Pagination from "~/components/Pagination.vue";
export default {
components: {
Pagination,
PostListItem
},
computed: {
postLabel: function() {
var pluralize = require("pluralize");
return pluralize("post", this.$page.author.belongsTo.totalCount);
}
},
metaInfo() {
return {
title: this.$page.author.name
};
}
};
</script>

189
src/templates/BlogPost.vue Normal file
View File

@@ -0,0 +1,189 @@
<template>
<Layout :hideHeader="true" :disableScroll="true">
<div class="container sm:pxi-0 mx-auto overflow-x-hidden pt-20">
<div class="lg:mx-32 md:mx-16 sm:mx-8 mx-4 pt-8">
<section class="post-header container mx-auto px-0 mb-4 border-b">
<span class="text-blue-500 font-medium uppercase tracking-wide text-sm">
<g-link
:to="$page.blog.category.path"
class="hover:underline"
>{{ $page.blog.category.title }}</g-link>
</span>
<h1 class="text-5xl font-medium leading-none mt-0">{{ $page.blog.title}}</h1>
<div class="text-2xl pt-4 pb-10 text-gray-700 font-serif" v-html="$page.blog.excerpt"></div>
</section>
<section class="post-author-list mb-10 mx-0">
<div class="flex items-center">
<div class="flex justify-between items-center">
<ul class="list-none flex author-list">
<li v-for="author in $page.blog.author" :key="author.id" class="author-list-item">
<g-link :to="author.path" v-tooltip="author.name">
<g-image
:src="author.image"
:alt="author.name"
class="h-8 w-8 sm:h-10 sm:w-10 rounded-full bg-gray-200 border-2 border-white"
/>
</g-link>
</li>
</ul>
</div>
<div class="pl-3 flex flex-col text-xs leading-none uppercase">
<p>
<span v-for="(author, index) in $page.blog.author" :key="author.id">
<g-link
:to="author.path"
v-tooltip="author.name"
class="hover:underline"
>{{ author.name }}</g-link>
<span v-if="index < $page.blog.author.length-1">,</span>
</span>
</p>
<p class="text-gray-700">
<time :datetime="$page.blog.datetime">{{ $page.blog.humanTime }}</time>
<!-- &nbsp;&middot;&nbsp; {{ $page.blog.timeToRead }} min read -->
&nbsp;&middot;&nbsp;
<time :datetime="$page.blog.datetime" >{{ $page.blog.startDate }}</time>
</p>
</div>
</div>
</section>
</div>
<section class="post-image mx-auto w-full">
<g-image :src="$page.blog.image"></g-image>
</section>
<div class="lg:mx-32 md:mx-16 px-4 sm:px-0">
<section class="post-content container mx-auto relative font-serif text-gray-700">
<div class="post-content-text text-xl" v-html="$page.blog.content"></div>
</section>
<section class="post-tags container mx-auto relative py-10">
<g-link
v-for="tag in $page.blog.tags"
:key="tag.id"
:to="tag.path"
class="text-xs bg-transparent hover:text-blue-700 py-2 px-4 mr-2 border hover:border-blue-500 border-gray-600 text-gray-700 rounded-full"
>{{ tag.title }}</g-link>
</section>
</div>
</div>
<section class="post-related bg-black text-gray-200 pt-10 border-b border-b-gray-900">
<div class="container mx-auto">
<div class="flex flex-wrap pt-8 pb-8 mx-4 sm:-mx-4">
<PostListItem v-if="$page.previous" :record="$page.previous" :border=false></PostListItem>
<PostListItem v-if="$page.next" :record="$page.next" :border=false></PostListItem>
</div>
</div>
</section>
</Layout>
</template>
<page-query>
query($id: ID!, $previousElement: ID!, $nextElement: ID!) {
blog(id: $id) {
title
name
rank
path
startDate : startdate(format:"MM YYYY")
image(width:1600, height:800)
image_caption
excerpt
content
humanTime : created(format:"DD MMMM YYYY")
datetime : created(format:"ddd MMM DD YYYY hh:mm:ss zZ")
timeToRead
tags {
id
title
path
}
category {
id
title
path
belongsTo(limit:4) {
totalCount
edges {
node {
... on Blog {
title
path
}
}
}
}
}
author {
id
name
image
path
}
tags {
id
title
path
}
}
previous: blog(id: $previousElement) {
title
excerpt
image(width:800)
path
timeToRead
category {
id
title
}
author {
id
name
image(width:64, height:64, fit:inside)
path
}
}
next: blog(id: $nextElement) {
title
excerpt
image(width:800)
path
timeToRead
category {
id
title
}
author {
id
name
image(width:64, height:64, fit:inside)
path
}
}
}
</page-query>
<script>
import PostListItem from "~/components/PostListItem.vue";
export default {
components: {
PostListItem
},
metaInfo() {
return {
title: this.$page.blog.title
};
}
};
</script>

100
src/templates/Category.vue Normal file
View File

@@ -0,0 +1,100 @@
<template>
<Layout :hideHeader="true" :disableScroll="true">
<div class="container sm:pxi-0 mx-auto overflow-x-hidden pt-24">
<div class="mx-4 sm:mx-0">
<h1 class="pb-0 mb-0 text-5xl font-medium">{{ $page.category.title }}</h1>
<p class="text-gray-700 text-xl">
A collection of
<span
class="self-center"
>{{ $page.category.belongsTo.totalCount }} {{ postLabel }}</span>
</p>
<div class="pt-8 border-b"></div>
</div>
<div class="flex flex-wrap pt-8 pb-8 mx-4 sm:-mx-4">
<PostListItem
v-for="edge in $page.category.belongsTo.edges"
:key="edge.node.id"
:record="edge.node"
/>
</div>
<div class="pagination flex justify-center mb-8">
<Pagination
:baseUrl="$page.category.path"
:currentPage="$page.category.belongsTo.pageInfo.currentPage"
:totalPages="$page.category.belongsTo.pageInfo.totalPages"
:maxVisibleButtons="5"
v-if="$page.category.belongsTo.pageInfo.totalPages > 1"
/>
</div>
</div>
</Layout>
</template>
<page-query>
query($id: ID!, $page:Int) {
category(id: $id) {
title
path
belongsTo(perPage: 12, page: $page) @paginate {
totalCount
pageInfo {
totalPages
currentPage
}
edges {
node {
... on Blog {
title
name
status
excerpt
image(width:800)
path
timeToRead
startDate : startdate(format:"MM YYYY")
humanTime : created(format:"DD MMM YYYY")
datetime : created
category {
id
title
}
author {
id
name
image(width:64, height:64, fit:inside)
path
}
}
}
}
}
}
}
</page-query>
<script>
import PostListItem from "~/components/PostListItem.vue";
import Pagination from "~/components/Pagination.vue";
export default {
components: {
Pagination,
PostListItem
},
computed: {
postLabel: function() {
var pluralize = require("pluralize");
return pluralize("post", this.$page.category.belongsTo.totalCount);
}
},
metaInfo() {
return {
title: this.$page.category.title
};
}
};
</script>

97
src/templates/Tag.vue Normal file
View File

@@ -0,0 +1,97 @@
<template>
<Layout :hideHeader="true" :disableScroll="true">
<div class="container sm:pxi-0 mx-auto overflow-x-hidden pt-24">
<div class="mx-4 sm:mx-0">
<h1 class="pb-0 mb-0 text-5xl font-medium">{{ $page.tag.title }}</h1>
<p class="text-gray-700 text-xl">
A collection of
<span
class="self-center"
>{{ $page.tag.belongsTo.totalCount }} {{ postLabel }}</span>
</p>
</div>
<div class="pt-8 border-b"></div>
<div class="flex flex-wrap pt-8 pb-8 mx-4 sm:-mx-4">
<PostListItem
v-for="edge in $page.tag.belongsTo.edges"
:key="edge.node.id"
:record="edge.node"
/>
</div>
<div class="pagination flex justify-center mb-8">
<Pagination
:baseUrl="$page.tag.path"
:currentPage="$page.tag.belongsTo.pageInfo.currentPage"
:totalPages="$page.tag.belongsTo.pageInfo.totalPages"
:maxVisibleButtons="5"
v-if="$page.tag.belongsTo.pageInfo.totalPages > 1"
/>
</div>
</div>
</Layout>
</template>
<page-query>
query($id: ID!, $page:Int) {
tag(id: $id) {
title
path
belongsTo(perPage: 5, page: $page) @paginate {
totalCount
pageInfo {
totalPages
currentPage
}
edges {
node {
... on Blog {
title
excerpt
image(width:800)
path
timeToRead
humanTime : created(format:"DD MMM YYYY")
datetime : created
category {
id
title
}
author {
id
name
image(width:64, height:64, fit:inside)
path
}
}
}
}
}
}
}
</page-query>
<script>
import PostListItem from "~/components/PostListItem.vue";
import Pagination from "~/components/Pagination.vue";
export default {
components: {
Pagination,
PostListItem
},
computed: {
postLabel: function() {
var pluralize = require("pluralize");
return pluralize("post", this.$page.tag.belongsTo.totalCount);
}
},
metaInfo() {
return {
title: this.$page.tag.title
};
}
};
</script>