Initial commit with Casper starter template
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
*.log
|
||||
.cache
|
||||
.DS_Store
|
||||
src/.temp
|
||||
node_modules
|
||||
dist
|
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Yashu Mittal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
15
README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
![Gridsome starter casper preview](https://i.imgur.com/TmJcF77.png?1)
|
||||
|
||||
This starter is a [casper](https://demo.ghost.io/) based theme built using [Gridsome](https://gridsome.org/).
|
||||
|
||||
## Deploy
|
||||
|
||||
To deploy the website, click the deploy button.
|
||||
|
||||
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://gitlab.com/mittalyashu/gridsome-starter-casper)
|
||||
|
||||
## Donate
|
||||
|
||||
I've put a lot of time and effort into making **Gridsome Starter Casper** project. If you love it, you can Become a Patron. I promise it will be a good investment 😉.
|
||||
|
||||
[![Become a Patron](https://i.imgur.com/wYOr44L.png)](https://www.patreon.com/bePatron?u=8494594)
|
31
blog/2018-12-08-welcome.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: Welcome to Gridsome
|
||||
date: 2018-12-08 20:08:08 -0700
|
||||
slug: welcome
|
||||
image: '/images/welcome-to-gridsome.jpg'
|
||||
tags: getting-started
|
||||
author: ['mittalyashu']
|
||||
---
|
||||
|
||||
|
||||
|
||||
👋 Welcome, it's great to have you here.
|
||||
|
||||
We know that first impressions are important, so we've populated your new site with some initial **getting started** posts that will help you get familiar with everything in no time. This is the first one!
|
||||
|
||||
**A few things you should know upfront**:
|
||||
|
||||
1. Ghost is designed for ambitious, professional publishers who want to actively build a business around their content. That's who it works best for.
|
||||
2. The entire platform can be modified and customised to suit your needs. It's very powerful, but does require some knowledge of code. Ghost is not necessarily a good platform for beginners or people who just want a simple personal blog.
|
||||
3. For the best experience we recommend downloading the Ghost Desktop App for your computer, which is the best way to access your Ghost site on a desktop device.
|
||||
|
||||
Ghost is made by an independent non-profit organisation called the Ghost Foundation. We are 100% self funded by revenue from our Ghost(Pro) service, and every penny we make is re-invested into funding further development of free, open source technology for modern publishing.
|
||||
|
||||
The version of Ghost you are looking at right now would not have been made possible without generous contributions from the open source [community](https://github.com/TryGhost).
|
||||
|
||||
## Next up, the editor
|
||||
|
||||
The main thing you'll want to read about next is probably: [the Ghost editor](/the-editor). This is where the good stuff happens.
|
||||
|
||||
> By the way, once you're done reading, you can simply delete the default **Ghost** user from your team to remove all of these introductory posts!
|
||||
|
18
blog/2018-12-09-the-editor.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Writing posts with Text editor ✍️
|
||||
date: 2018-12-09 20:08:08 -0700
|
||||
slug: the-editor
|
||||
image: '/images/writing-posts-with-gridsome.jpg'
|
||||
tags: editing
|
||||
author: ['gridsome', 'mittalyashu']
|
||||
---
|
||||
|
||||
Ghost has a powerful visual editor with familiar formatting options, as well as the ability to seamlessly add dynamic content.
|
||||
|
||||
Select the text to add formatting, headers or create links, or use Markdown shortcuts to do the work for you - if that's your thing.
|
||||
|
||||
## Rich editing at your fingertips
|
||||
|
||||
The editor can also handle rich media objects, called cards.
|
||||
|
||||
You can insert a card either by clicking the + button on a new line, or typing / on a new line to search for a particular card. This allows you to efficiently insert images, markdown, html and embeds.
|
12
blog/2018-12-10-publishing-options.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: Publishing options
|
||||
date: 2018-12-10 20:08:08 -0700
|
||||
slug: publishing-options
|
||||
image: ''
|
||||
tags: publish
|
||||
author: ['mittalyashu', 'john']
|
||||
---
|
||||
|
||||
Customise your social media sharing cards for Facebook and Twitter, enabling you to add custom images, titles and descriptions for social media.
|
||||
|
||||
There’s no need to hard code your meta data. You can set your meta title and description using the post settings tool, which has a handy character guide and SERP preview.
|
11
blog/2018-12-11-admin-settings.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: Managing admin settings
|
||||
date: 2018-12-11 20:08:08 -0700
|
||||
slug: admin-settings
|
||||
image: '/images/admin-settings.jpg'
|
||||
tags: getting-started
|
||||
author: ['gridsome']
|
||||
---
|
||||
|
||||
Make your site private
|
||||
If you've got a publication that you don't want the world to see yet because it's not ready to launch, you can hide your Ghost site behind a basic shared pass-phrase.
|
23
data/admin.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
site:
|
||||
title: Gridsome Casper
|
||||
cover_image: '/images/blog-cover.jpg'
|
||||
logo: '/images/gridsome-logo.png'
|
||||
url: '/'
|
||||
description: 'The professional publishing platform'
|
||||
subscribers: true
|
||||
navigation: true
|
||||
|
||||
social_media:
|
||||
facebook: 'gridsome'
|
||||
twitter: 'gridsome'
|
||||
patreon: 'mittalyashu'
|
||||
|
||||
nav_home:
|
||||
- title: Home
|
||||
link: /
|
||||
- title: About
|
||||
link: /about
|
||||
- title: Getting Started
|
||||
link: /tag/getting-started/
|
||||
- title: Try Gridsome
|
||||
link: https://www.gridsome.org/
|
10
data/author/gridsome.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
id: gridsome
|
||||
name: Gridsome
|
||||
image: /images/gridsome-logo.png
|
||||
tagline: 'Static Site Generator'
|
||||
bio: "We are a SSG a.k.a. Static site generator which use Vue.js at it's core."
|
||||
location: World
|
||||
website: https://gridsome.org/
|
||||
twitter: gridsome
|
||||
---
|
8
data/author/john.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
id: john
|
||||
name: John O' Nolan
|
||||
bio: 'I am the founder of Ghost foundation and we are doing a great job.'
|
||||
location: Earth
|
||||
twitter: john
|
||||
facebook: john
|
||||
---
|
10
data/author/mittalyashu.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
id: mittalyashu
|
||||
name: Yashu Mittal
|
||||
image: /images/mittalyashu.jpg
|
||||
tagline: 'Open Source Developer'
|
||||
bio: 'I am the Founder and CEO at CodeCarrot and open source developer.'
|
||||
location: India
|
||||
website: https://mittalyashu.now.sh/
|
||||
twitter: mittalyashu77
|
||||
---
|
40
gridsome.config.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
module.exports = {
|
||||
siteName: 'Gridsome Casper',
|
||||
siteUrl: 'https://www.gridsome.org',
|
||||
siteDescription: 'The professional publishing platform',
|
||||
titleTemplate: `%s - Gridsome`,
|
||||
|
||||
plugins: [
|
||||
{
|
||||
use: '@gridsome/plugin-google-analytics',
|
||||
options: {
|
||||
id: 'UA-XXXXXXXXX-X'
|
||||
}
|
||||
},
|
||||
{
|
||||
use: '@gridsome/source-filesystem',
|
||||
options: {
|
||||
path: 'blog/*.md',
|
||||
typeName: 'Post',
|
||||
route: '/:slug',
|
||||
refs: {
|
||||
author: 'Author',
|
||||
tags: {
|
||||
typeName: 'Tag',
|
||||
route: '/tag/:title',
|
||||
create: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
use: '@gridsome/source-filesystem',
|
||||
options: {
|
||||
// TODO Use yaml file as data source
|
||||
path: 'data/author/*.md',
|
||||
typeName: 'Author',
|
||||
route: '/author/:id'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
3
netlify.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[build]
|
||||
publish = "dist"
|
||||
command = "gridsome build"
|
18
package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "snipette-gridsome",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "gridsome build",
|
||||
"develop": "gridsome develop",
|
||||
"explore": "gridsome explore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gridsome/plugin-google-analytics": "^0.1.0",
|
||||
"@gridsome/source-filesystem": "^0.3.0",
|
||||
"@gridsome/transformer-remark": "^0.2.0",
|
||||
"gridsome": "^0.5.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"vue-moment": "^4.0.0"
|
||||
}
|
||||
}
|
2
src/assets/css/style.scss
Normal file
69
src/components/Card.vue
Normal file
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<article :class="articleClass">
|
||||
<a v-if="cardData.image" class="post-card-image-link" :href="cardData.path">
|
||||
<!-- FIXME Background size cover -->
|
||||
<div class="post-card-image" :style="'background-image: url(' + cardData.image + ')'"></div>
|
||||
</a>
|
||||
<div class="post-card-content">
|
||||
<a class="post-card-content-link" :href="cardData.path">
|
||||
<header class="post-card-header">
|
||||
<span
|
||||
v-if="cardData.tags"
|
||||
class="post-card-tags"
|
||||
>{{ cardData.tags.title.replace('-', ' ') }}</span>
|
||||
<h2 class="post-card-title">{{ cardData.title }}</h2>
|
||||
</header>
|
||||
<section class="post-card-excerpt">
|
||||
<p>{{ cardData.content | stripHTML | truncate(190, '...') }}</p>
|
||||
</section>
|
||||
</a>
|
||||
<footer class="post-card-meta">
|
||||
<ul class="author-list">
|
||||
<li v-for="author in cardData.author" class="author-list-item" :key="author.name">
|
||||
<div class="author-name-tooltip">{{ author.name }}</div>
|
||||
<a v-if="author.image" :href="'/author/' + author.id" class="static-avatar">
|
||||
<img class="author-profile-image" :src="author.image" :alt="author.name" />
|
||||
</a>
|
||||
<a v-else :href="'/author/' + author.id" class="static-avatar author-profile-image">
|
||||
<Avatar/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<span class="reading-time">{{ cardData.timeToRead }} MIN READ</span>
|
||||
</footer>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from "./icons/Avatar";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Avatar
|
||||
},
|
||||
props: {
|
||||
cardData: Object
|
||||
},
|
||||
computed: {
|
||||
articleClass() {
|
||||
let classes = ["post-card", "post"];
|
||||
if (this.cardData.fields === null) {
|
||||
classes.push("no-image");
|
||||
}
|
||||
const cardTagClass = "post-" + this.cardData.tags;
|
||||
classes.push(cardTagClass);
|
||||
return classes;
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
truncate: (text, length, suffix) => {
|
||||
return text.substring(0, length) + suffix;
|
||||
},
|
||||
stripHTML: text => {
|
||||
return text.replace(/<[^>]+>/g, '')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
48
src/components/FloatingHeader.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="floating-header">
|
||||
<div class="floating-header-logo">
|
||||
<a href="/">
|
||||
<img v-if="Admin.site.logo" :src="Admin.site.logo" :alt="Admin.site.title + ' icon'" />
|
||||
<span>{{ Admin.site.title }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<span class="floating-header-divider">—</span>
|
||||
<div class="floating-header-title">{{ title }}</div>
|
||||
<div class="floating-header-share">
|
||||
<div class="floating-header-share-label">Share this <PointerIcon/></div>
|
||||
<!-- TODO Add Twitter share link -->
|
||||
<a class="floating-header-share-tw">
|
||||
<TwitterIcon/>
|
||||
</a>
|
||||
<!-- TODO Add Facebook share link -->
|
||||
<a class="floating-header-share-fb">
|
||||
<FacebookIcon/>
|
||||
</a>
|
||||
</div>
|
||||
<progress id="reading-progress" class="progress" value="0">
|
||||
<div class="progress-container">
|
||||
<span class="progress-bar"></span>
|
||||
</div>
|
||||
</progress>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from "../../data/admin.yml"
|
||||
import PointerIcon from './icons/Pointer';
|
||||
import FacebookIcon from './icons/Facebook'
|
||||
import TwitterIcon from './icons/Twitter'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PointerIcon,
|
||||
FacebookIcon,
|
||||
TwitterIcon
|
||||
},
|
||||
comments: {
|
||||
Admin() {
|
||||
return Admin
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
29
src/components/Footer.vue
Normal file
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<!-- The footer at the very bottom of the screen -->
|
||||
<footer class="site-footer outer">
|
||||
<div class="site-footer-content inner">
|
||||
<section class="copyright">
|
||||
<a href="/">{{ Admin.site.title }}</a> © {{ new Date().getFullYear() }}
|
||||
</section>
|
||||
<nav class="site-footer-nav">
|
||||
<g-link to="/">Latest Posts</g-link>
|
||||
<a v-if="Admin.social_media.facebook" :href="'https://facebook.com/' + Admin.social_media.facebook" target="_blank" rel="noopener">Facebook</a>
|
||||
<a v-if="Admin.social_media.twitter" :href="'https://twitter.com/' + Admin.social_media.twitter" target="_blank" rel="noopener">Twitter</a>
|
||||
<a v-if="Admin.social_media.patreon" :href="'https://www.patreon.com/' + Admin.social_media.patreon" target="_blank" rel="noopener">Become My Patron</a>
|
||||
<a href="https://gridsome.org" target="_blank" rel="noopener">Gridsome</a>
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from '../../data/admin.yml'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
90
src/components/Navbar.vue
Normal file
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<nav class="site-nav">
|
||||
<div class="site-nav-left">
|
||||
<div v-if="logo">
|
||||
<a v-if="Admin.site.logo" class="site-nav-logo" :href="Admin.site.url">
|
||||
<img :src="Admin.site.logo" :alt="Admin.site.title">
|
||||
</a>
|
||||
<a v-else class="site-nav-logo" :href="Admin.site.url">{{ Admin.site.title }}</a>
|
||||
</div>
|
||||
<Navigation v-if="Admin.site.navigation"/>
|
||||
</div>
|
||||
<div class="site-nav-right">
|
||||
<div class="social-links">
|
||||
<a
|
||||
v-if="Admin.social_media.patreon"
|
||||
class="social-link social-link-p"
|
||||
:href="'https://www.patreon.com/' + Admin.social_media.patreon"
|
||||
title="Become My Patron"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Patreon/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-if="Admin.social_media.facebook"
|
||||
class="social-link social-link-fb"
|
||||
:href="'https://facebook.com/' + Admin.social_media.facebook"
|
||||
title="Facebook"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Facebook/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-if="Admin.social_media.twitter"
|
||||
class="social-link social-link-tw"
|
||||
:href="'https://twitter.com/' + Admin.social_media.twitter"
|
||||
title="Twitter"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Twitter/>
|
||||
</a>
|
||||
</div>
|
||||
<a v-if="Admin.site.subscribers" class="subscribe-button" href="#subscribe">Subscribe</a>
|
||||
<a v-else class="rss-button" href="/feed.xml" title="RSS" target="_blank" rel="noopener">
|
||||
<RSS/>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from "../../data/admin.yml";
|
||||
import Navigation from "./Navigation";
|
||||
|
||||
// Icons
|
||||
import Facebook from "./icons/Facebook";
|
||||
import Twitter from "./icons/Twitter";
|
||||
import Patreon from "./icons/Patreon";
|
||||
import RSS from "./icons/RSS";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
logo: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Navigation,
|
||||
Patreon,
|
||||
Facebook,
|
||||
Twitter,
|
||||
RSS
|
||||
},
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.social-link > svg
|
||||
width: 1.8rem
|
||||
</style>
|
19
src/components/Navigation.vue
Normal file
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<ul class="nav" role="menu">
|
||||
<li v-for="item in Admin.nav_home" :key="item.title" role="menuitem">
|
||||
<a :href="item.link">{{ item.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from '../../data/admin.yml'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
126
src/components/PreviousNext.vue
Normal file
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<!-- Links to Previous/Next posts -->
|
||||
<aside class="read-next outer">
|
||||
<div class="inner">
|
||||
<div class="read-next-feed">
|
||||
<article
|
||||
class="read-next-card"
|
||||
:style="'background-image: url(' + Admin.site.cover_image + ')'"
|
||||
>
|
||||
<header class="read-next-card-header">
|
||||
<small class="read-next-card-header-sitetitle">— {{ Admin.site.title }} —</small>
|
||||
<h3 class="read-next-card-header-title">
|
||||
<a :href="'/tag/' + tag">{{ tag.replace('-', ' ') | capitalizeFilter }}</a>
|
||||
</h3>
|
||||
</header>
|
||||
<div class="read-next-divider">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="read-next-card-content">
|
||||
<ul>
|
||||
<li v-for="post in posts" :key="post.node.id">
|
||||
<a :href="post.node.path">{{ post.node.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="read-next-card-footer">
|
||||
<a :href="'/tag/' + this.tag">See all {{ posts.length }} posts →</a>
|
||||
</footer>
|
||||
</article>
|
||||
<Card
|
||||
v-for="PreviousNexts in this.PreviousNexts"
|
||||
:key="PreviousNexts.id"
|
||||
:cardData="PreviousNexts"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Card from "./Card";
|
||||
import Admin from "../../data/admin.yml";
|
||||
import capitalizeFilter from "../filters/capitalize";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentPostId: this.id,
|
||||
PreviousNexts: []
|
||||
};
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String
|
||||
},
|
||||
tag: {
|
||||
type: String
|
||||
},
|
||||
posts: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Card
|
||||
},
|
||||
filters: {
|
||||
capitalizeFilter
|
||||
},
|
||||
mounted() {
|
||||
this.getPreviousNext();
|
||||
},
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin;
|
||||
},
|
||||
tagPosts() {
|
||||
const allTagPosts = this.$static.allTag.edges;
|
||||
console.log(allTagPosts);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPreviousNext() {
|
||||
const allBlogs = this.$static.allPost.edges;
|
||||
for (let i = 0; i < allBlogs.length; i++) {
|
||||
if (allBlogs[i].node.id === this.currentPostId) {
|
||||
if (i > 0) {
|
||||
this.PreviousNexts.push(allBlogs[i - 1].node);
|
||||
}
|
||||
if (allBlogs.length > i) {
|
||||
this.PreviousNexts.push(allBlogs[i + 1].node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<static-query>
|
||||
query Blog {
|
||||
allPost(order: ASC) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
path
|
||||
tags {
|
||||
title
|
||||
}
|
||||
image
|
||||
author {
|
||||
id
|
||||
name
|
||||
image
|
||||
}
|
||||
content
|
||||
timeToRead
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</static-query>
|
89
src/components/bylineMultiple.vue
Normal file
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<footer class="post-full-footer">
|
||||
<section class="post-full-authors">
|
||||
<div class="post-full-authors-content">
|
||||
<p>This post was a collaboration between</p>
|
||||
<p>
|
||||
<!-- FIXME Add comma after first author -->
|
||||
<a
|
||||
v-for="author in author"
|
||||
:key="author.name"
|
||||
:href="'/author/' + author.id"
|
||||
>{{ author.name }}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul class="author-list" v-on:mouseleave="hideAuthorCard">
|
||||
<!-- FIXME Appear only single card on hover -->
|
||||
<li
|
||||
v-on:mouseover="authorCardHovered = authorUsername"
|
||||
v-for="author in author"
|
||||
:key="author.name"
|
||||
class="author-list-item"
|
||||
>
|
||||
<div :class="{'author-card': true, hovered: authorCardHovered === authorUsername }">
|
||||
<div class="basic-info">
|
||||
<img
|
||||
v-if="author.image"
|
||||
class="author-profile-image"
|
||||
:src="author.image"
|
||||
:alt="author.name"
|
||||
>
|
||||
<div v-else class="author-profile-image">
|
||||
<Avatar/>
|
||||
</div>
|
||||
|
||||
<h2>{{ author.name }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="bio">
|
||||
<div v-if="author.bio">
|
||||
<p>{{ author.bio }}</p>
|
||||
<p>
|
||||
<a :href="'/author/' + author.id">More posts</a>
|
||||
by {{ author.name }}.
|
||||
</p>
|
||||
</div>
|
||||
<p v-else>
|
||||
Read
|
||||
<a :href="'/author/' + author.id">more posts</a> by this author.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a v-if="author.image" :href="'/author/' + author.id" class="moving-avatar">
|
||||
<img class="author-profile-image" :src="author.image" :alt="author.name">
|
||||
</a>
|
||||
<a v-else :href="'/author/' + author.id" class="moving-avatar author-profile-image">
|
||||
<Avatar/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from "./icons/Avatar";
|
||||
|
||||
export default {
|
||||
data: () => {
|
||||
return {
|
||||
authorCardHovered: ""
|
||||
};
|
||||
},
|
||||
props: {
|
||||
author: Array
|
||||
},
|
||||
components: {
|
||||
Avatar
|
||||
},
|
||||
methods: {
|
||||
hideAuthorCard() {
|
||||
setTimeout(() => {
|
||||
this.authorCardHovered = "";
|
||||
}, 800);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
41
src/components/bylineSingle.vue
Normal file
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<footer class="post-full-footer">
|
||||
<section class="author-card">
|
||||
<g-image v-if="authorData.image" class="author-profile-image" :src="authorData.image" :alt="authorData.name" />
|
||||
<span v-else class="avatar-wrapper">
|
||||
<Avatar />
|
||||
</span>
|
||||
|
||||
<section class="author-card-content">
|
||||
<h4 class="author-card-name">
|
||||
<a :href="'/author/' + authorData.id">
|
||||
{{ authorData.name }}
|
||||
</a>
|
||||
</h4>
|
||||
<p v-if="authorData.tagline">{{ authorData.tagline }}</p>
|
||||
<p v-else>Read <a :href="'/author/' + authorData.id">more posts</a> by this author.</p>
|
||||
</section>
|
||||
</section>
|
||||
<div class="post-full-footer-right">
|
||||
<a class="author-card-button" :href="'/author/' + authorData.id">Read More</a>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from './icons/Avatar';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
author: Array
|
||||
},
|
||||
components: {
|
||||
Avatar
|
||||
},
|
||||
computed: {
|
||||
authorData() {
|
||||
return this.author[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
3
src/components/icons/Avatar.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M3.513 18.998C4.749 15.504 8.082 13 12 13s7.251 2.504 8.487 5.998C18.47 21.442 15.417 23 12 23s-6.47-1.558-8.487-4.002zM12 12c2.21 0 4-2.79 4-5s-1.79-4-4-4-4 1.79-4 4 1.79 5 4 5z" fill="#FFF"/></g></svg>
|
||||
</template>
|
3
src/components/icons/Facebook.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M19 6h5V0h-5c-3.86 0-7 3.14-7 7v3H8v6h4v16h6V16h5l1-6h-6V7c0-.542.458-1 1-1z"/></svg>
|
||||
</template>
|
3
src/components/icons/Patreon.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="96" viewBox="0 0 100 96"> <g fill-rule="evenodd"> <path d="M64.1102,0.1004 C44.259,0.1004 28.1086,16.2486 28.1086,36.0986 C28.1086,55.8884 44.259,71.989 64.1102,71.989 C83.9,71.989 100,55.8884 100,36.0986 C100,16.2486 83.9,0.1004 64.1102,0.1004"/> <polygon points=".012 95.988 17.59 95.988 17.59 .1 .012 .1"/> </g> </svg>
|
||||
</template>
|
3
src/components/icons/Pointer.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M7.5 15.5V4a1.5 1.5 0 1 1 3 0v4.5h2a1 1 0 0 1 1 1h2a1 1 0 0 1 1 1H18a1.5 1.5 0 0 1 1.5 1.5v3.099c0 .929-.13 1.854-.385 2.748L17.5 23.5h-9c-1.5-2-5.417-8.673-5.417-8.673a1.2 1.2 0 0 1 1.76-1.605L7.5 15.5zm6-6v2m-3-3.5v3.5m6-1v2"/> </svg>
|
||||
</template>
|
3
src/components/icons/RSS.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="6.18" cy="17.82" r="2.18"/><path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/></svg>
|
||||
</template>
|
3
src/components/icons/Twitter.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>
|
||||
</template>
|
3
src/components/icons/Website.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.5 11.957c0 6.375-5.163 11.544-11.532 11.544C5.599 23.5.5 18.125.5 11.75.5 5.542 5.37.758 11.505.511l.5-.011C18.374.5 23.5 5.582 23.5 11.957zM11.505.511c-6 6.5-6 14.98 0 22.98m1-22.98c6 6.5 6 14.977 0 22.977M2 17.479h20.063m-19.657-12h19.062m-20.968 6h22.938" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></svg>
|
||||
</template>
|
22
src/components/subscribeForm.vue
Normal file
|
@ -0,0 +1,22 @@
|
|||
<template>
|
||||
<form method="post" action="/subscribe/" id="" class="">
|
||||
<div class="form-group">
|
||||
<input class="subscribe-email" type="email" name="email" :placeholder=placeholder>
|
||||
</div>
|
||||
|
||||
<button id="" class="" type="submit">
|
||||
<span>Subscribe</span>
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "youremail@example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
BIN
src/favicon.png
Normal file
After Width: | Height: | Size: 60 KiB |
4
src/filters/camelCase.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default value => {
|
||||
console.log(value);
|
||||
return value.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase());
|
||||
}
|
8
src/filters/capitalize.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
export default string => {
|
||||
let array = string.split(' ')
|
||||
let capitalizeString = '';
|
||||
array.map(value => {
|
||||
capitalizeString += value[0].toUpperCase() + value.substr(1) + ' '
|
||||
})
|
||||
return capitalizeString.trim();
|
||||
}
|
41
src/layouts/Default.vue
Normal file
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div class="site-wrapper">
|
||||
<!-- All the main content gets inserted here, index.vue, blogPost.vue, etc -->
|
||||
<slot/>
|
||||
|
||||
<!-- The footer at the very bottom of the screen -->
|
||||
<Footer/>
|
||||
|
||||
<!-- TODO Showing upon clicking the button -->
|
||||
<!-- The big email subscribe modal content -->
|
||||
<div v-if="Admin.site.subscribers" id="subscribe" class="subscribe-overlay">
|
||||
<a class="subscribe-overlay-close" href="#"></a>
|
||||
<div class="subscribe-overlay-content">
|
||||
<img v-if="!Admin.site.logo" class="subscribe-overlay-logo" :src="Admin.site.logo" :alt="Admin.site.title" />
|
||||
<h1 class="subscribe-overlay-title">Subscribe to {{ Admin.site.title }}</h1>
|
||||
<p class="subscribe-overlay-description">Stay up to date! Get all the latest & greatest posts delivered straight to your inbox</p>
|
||||
<subscribeForm placeholder="youremail@example.com" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO Add pagination -->
|
||||
<!-- <script v-if="Admin.site.pagination" src=""></script> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Footer from '../components/Footer';
|
||||
import Admin from '../../data/admin.yml';
|
||||
import subscribeForm from '../components/subscribeForm'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Footer, subscribeForm
|
||||
},
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
32
src/layouts/Page.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<Layout>
|
||||
<header class="site-header outer">
|
||||
<div class="inner">
|
||||
<Navbar :logo="true"/>
|
||||
</div>
|
||||
</header>
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
<slot/>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Navbar from "../components/Navbar";
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
bodyAttrs: {
|
||||
class: `page-template`
|
||||
}
|
||||
};
|
||||
},
|
||||
components: {
|
||||
Navbar
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
8
src/main.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import '~/assets/css/style.scss'
|
||||
import DefaultLayout from '~/layouts/Default.vue'
|
||||
import moment from "vue-moment"
|
||||
|
||||
export default Vue => {
|
||||
Vue.component('Layout', DefaultLayout)
|
||||
Vue.use(moment)
|
||||
}
|
51
src/pages/About.vue
Normal file
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<Page>
|
||||
<article class="post-full post page" :class="pageClass">
|
||||
|
||||
<header class="post-full-header">
|
||||
<h1 class="post-full-title">About us</h1>
|
||||
</header>
|
||||
|
||||
<figure v-if="image" class="post-full-image">
|
||||
<g-image v-bind="image" src="" alt="" />
|
||||
</figure>
|
||||
|
||||
<section class="post-full-content">
|
||||
<div class="post-content">
|
||||
<p>This is a Gridsome starter casper theme cloned from <a target="_blank" href="https://demo.ghost.io/">Casper</a>. I've put a lot of time and effort into making <strong>Gridsome Starter Casper</strong> project. If you love it, you can <a target="_blank" href="https://www.patreon.com/bePatron?u=8494594">Become a Patron</a>. I promise it will be a good investment 😉.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Page from "../layouts/Page";
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: 'About',
|
||||
bodyAttrs: {
|
||||
class: `page-about`
|
||||
}
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
image: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageClass() {
|
||||
if (this.image === '') {
|
||||
return 'no-image'
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Page
|
||||
}
|
||||
};
|
||||
</script>
|
88
src/pages/Index.vue
Normal file
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<Layout>
|
||||
<header :class=HeroBgClass :style=HeroBgImage>
|
||||
<div class="inner">
|
||||
<div class="site-header-content">
|
||||
<h1 class="site-title">
|
||||
<img v-if="Admin.site.logo != ''" class="site-logo" :src="Admin.site.logo" :alt="Admin.site.title" />
|
||||
<p v-if="Admin.site.logo === ''">
|
||||
{{ Admin.site.title }}
|
||||
</p>
|
||||
</h1>
|
||||
<h2 class="site-description">{{ Admin.site.description}}</h2>
|
||||
</div>
|
||||
<Navbar :logo=false />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- The main content area -->
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
<div class="post-feed">
|
||||
<Card v-for="{ node } in $page.allPost.edges" :key="node.id" :cardData="node" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from '../../data/admin.yml';
|
||||
import Navbar from '../components/Navbar'
|
||||
import Card from '../components/Card';
|
||||
|
||||
export default {
|
||||
metaInfo: {
|
||||
bodyAttrs: {
|
||||
class: 'home-template'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Navbar, Card
|
||||
},
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin
|
||||
},
|
||||
HeroBgImage() {
|
||||
if (Admin.site.cover_image) {
|
||||
return {
|
||||
backgroundImage: 'url(' + Admin.site.cover_image + ')'
|
||||
}
|
||||
}
|
||||
},
|
||||
HeroBgClass() {
|
||||
if (Admin.site.cover_image) {
|
||||
return 'site-header outer'
|
||||
} else {
|
||||
return 'site-header outer no-cover'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<page-query>
|
||||
query Home ($page: Int) {
|
||||
allPost (page: $page, order: ASC) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
path
|
||||
tags {
|
||||
title
|
||||
}
|
||||
image
|
||||
content
|
||||
author {
|
||||
id
|
||||
name
|
||||
image
|
||||
}
|
||||
timeToRead
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</page-query>
|
135
src/templates/Author.vue
Normal file
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<Layout>
|
||||
<header class="site-header outer no-image">
|
||||
<div class="inner">
|
||||
<Navbar :logo=true />
|
||||
<div class="site-header-content">
|
||||
<img v-if="$page.author.image" class="author-profile-image" :src="$page.author.image" :alt="$page.author.name" />
|
||||
<h1 class="site-title"> {{ $page.author.name }} </h1>
|
||||
<h2 v-if="bio = true" class="author-bio">{{ $page.author.bio }}</h2>
|
||||
<div class="author-meta">
|
||||
|
||||
<div v-if="location = true" class="author-location">{{ $page.author.location }}
|
||||
<span class="bull">•</span>
|
||||
</div>
|
||||
|
||||
<div class="author-stats">
|
||||
{{ numberofPosts }}
|
||||
<span class="bull">•</span>
|
||||
</div>
|
||||
|
||||
<a v-if="$page.author.website" class="social-link social-link-wb" :href="$page.author.website" target="_blank" rel="noopener">
|
||||
<WebsiteIcon />
|
||||
</a>
|
||||
|
||||
<a v-if="$page.author.twitter" class="social-link social-link-tw" :href="'https://www.twitter.com/' + $page.author.twitter" target="_blank" rel="noopener">
|
||||
<TwitterIcon />
|
||||
</a>
|
||||
|
||||
<a v-if="$page.author.facebook" class="social-link social-link-fb" :href="'https://www.facebook.com/' + $page.author.facebook" target="_blank" rel="noopener">
|
||||
<FacebookIcon />
|
||||
</a>
|
||||
|
||||
<!-- NOTE Gridsome doesn't support RSS feed yet -->
|
||||
<!-- <a class="social-link social-link-rss" href="" target="_blank" rel="noopener">
|
||||
<RSSIcon />
|
||||
</a> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- The main content area -->
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
|
||||
<div class="post-feed">
|
||||
<Card v-for="{ node } in $page.author.belongsTo.edges" :key="node.id" :cardData="node" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import Navbar from '../components/Navbar';
|
||||
import Card from '../components/Card';
|
||||
|
||||
// Icons
|
||||
import WebsiteIcon from '../components/icons/Website';
|
||||
import TwitterIcon from '../components/icons/Twitter';
|
||||
import FacebookIcon from '../components/icons/Facebook';
|
||||
import RSSIcon from '../components/icons/RSS';
|
||||
|
||||
import { Pager } from 'gridsome'
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.$page.author.name,
|
||||
bodyAttrs: {
|
||||
class: `author-template`
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
// Components
|
||||
Navbar, Card,
|
||||
// Icons
|
||||
WebsiteIcon, TwitterIcon, FacebookIcon, RSSIcon, Pager
|
||||
},
|
||||
computed: {
|
||||
numberofPosts() {
|
||||
let count = this.$page.author.belongsTo.edges.length
|
||||
if (count == 1) {
|
||||
return `${count} post`
|
||||
} else if (count >= 2) {
|
||||
return `${count} posts`
|
||||
} else {
|
||||
return 'No posts'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<page-query>
|
||||
query Author ($id: String!) {
|
||||
author (id: $id) {
|
||||
name
|
||||
image
|
||||
bio
|
||||
location
|
||||
website
|
||||
twitter
|
||||
facebook
|
||||
belongsTo {
|
||||
edges {
|
||||
node {
|
||||
...on Post {
|
||||
id
|
||||
title
|
||||
author {
|
||||
id
|
||||
name
|
||||
image
|
||||
}
|
||||
path
|
||||
image
|
||||
content
|
||||
timeToRead
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</page-query>
|
128
src/templates/Post.vue
Normal file
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<Layout>
|
||||
<header class="site-header outer">
|
||||
<div class="inner">
|
||||
<Navbar :logo="true"/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- <FloatingHeader/> -->
|
||||
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
<article :class="postClass">
|
||||
<header class="post-full-header">
|
||||
<section class="post-full-meta">
|
||||
<time class="post-full-meta-date" :datetime="$page.post.date | moment('d, MMMM YYYY')">{{ $page.post.date | moment("d, MMMM YYYY") }}</time>
|
||||
<span class="date-divider">/</span>
|
||||
<a
|
||||
:href="'/tag/' + $page.post.tags.title"
|
||||
>{{ $page.post.tags.title.replace('-', ' ') }}</a>
|
||||
</section>
|
||||
<h1 class="post-full-title">{{ $page.post.title }}</h1>
|
||||
</header>
|
||||
|
||||
<figure v-if="$page.post.image" class="post-full-image">
|
||||
<g-image :src="$page.post.image" :alt="$page.post.title"/>
|
||||
</figure>
|
||||
|
||||
<section class="post-full-content">
|
||||
<div class="post-content" v-html="$page.post.content"></div>
|
||||
</section>
|
||||
|
||||
<!-- Email subscribe form at the bottom of the page -->
|
||||
<section v-if="Admin.site.subscribers" class="subscribe-form">
|
||||
<h3 class="subscribe-form-title">Subscribe to {{ Admin.site.title }}</h3>
|
||||
<p>Get the latest posts delivered right to your inbox</p>
|
||||
<subscribeForm placeholder="youremail@example.com"/>
|
||||
</section>
|
||||
|
||||
<bylineMultiple :author="$page.post.author" v-if="$page.post.author.length > 1"/>
|
||||
<bylineSingle :author="$page.post.author" v-else/>
|
||||
|
||||
<!-- NOTE Comment section -->
|
||||
<!-- <section class="post-full-comments">
|
||||
If you want to embed comments, this is a good place to do it!
|
||||
</section>-->
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<PreviousNext :id="$page.post.id" :tag="$page.post.tags.title" :posts="$page.post.tags.belongsTo.edges"/>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from "../../data/admin.yml";
|
||||
import Navbar from "../components/Navbar";
|
||||
import FloatingHeader from "../components/FloatingHeader";
|
||||
import subscribeForm from "../components/subscribeForm";
|
||||
import bylineMultiple from "../components/bylineMultiple";
|
||||
import bylineSingle from "../components/bylineSingle";
|
||||
import PreviousNext from "../components/PreviousNext";
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.$page.post.title,
|
||||
bodyAttrs: {
|
||||
class: `post-template tag-${this.$page.post.tags.title}`
|
||||
}
|
||||
};
|
||||
},
|
||||
components: {
|
||||
Navbar,
|
||||
FloatingHeader,
|
||||
subscribeForm,
|
||||
bylineMultiple,
|
||||
bylineSingle,
|
||||
PreviousNext
|
||||
},
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin;
|
||||
},
|
||||
postClass() {
|
||||
let classes = ["post-full", "post"];
|
||||
if (!this.$page.post.image) {
|
||||
classes.push("no-image");
|
||||
}
|
||||
const postTagClass = "tag-" + this.$page.post.tags.title;
|
||||
classes.push(postTagClass);
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<page-query>
|
||||
query BlogPost ($path: String!) {
|
||||
post (path: $path) {
|
||||
id
|
||||
title
|
||||
date
|
||||
tags {
|
||||
title
|
||||
belongsTo {
|
||||
edges {
|
||||
node {
|
||||
... on Post {
|
||||
id
|
||||
title
|
||||
path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
image
|
||||
author {
|
||||
id
|
||||
name
|
||||
image
|
||||
tagline
|
||||
bio
|
||||
}
|
||||
content
|
||||
}
|
||||
}
|
||||
</page-query>
|
88
src/templates/Tag.vue
Normal file
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<Layout>
|
||||
<header class="site-header outer no-image">
|
||||
<div class="inner">
|
||||
<Navbar :logo="true"/>
|
||||
<div class="site-header-content">
|
||||
<h1 class="site-title">{{ capitalize }}</h1>
|
||||
<h2 class="site-description">A collection of {{ $page.tag.belongsTo.edges.length }} posts</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="site-main" class="site-main outer">
|
||||
<div class="inner">
|
||||
<div class="post-feed">
|
||||
<Card v-for="{ node } in $page.tag.belongsTo.edges" :key="node.id" :cardData="node"/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Admin from "../../data/admin.yml";
|
||||
import Navbar from "../components/Navbar";
|
||||
import Card from "../components/Card";
|
||||
import capitalizeFilter from "../filters/capitalize";
|
||||
import capitalize from '../filters/capitalize';
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.capitalize,
|
||||
bodyAttrs: {
|
||||
class: `tag-template tag-${this.$page.tag.title}`
|
||||
}
|
||||
};
|
||||
},
|
||||
components: {
|
||||
Navbar,
|
||||
Card
|
||||
},
|
||||
computed: {
|
||||
Admin() {
|
||||
return Admin;
|
||||
},
|
||||
postClass() {
|
||||
let classes = ["post-full", "post"];
|
||||
if (!this.$page.post.image) {
|
||||
classes.push("no-image");
|
||||
}
|
||||
const postTagClass = "tag-" + this.$page.post.tags.title;
|
||||
classes.push(postTagClass);
|
||||
return classes;
|
||||
},
|
||||
capitalize() {
|
||||
return capitalizeFilter(this.$page.tag.title.replace('-', ' '))
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<page-query>
|
||||
query Tags ($id: String!) {
|
||||
tag (id: $id) {
|
||||
title
|
||||
belongsTo {
|
||||
edges {
|
||||
node {
|
||||
...on Post {
|
||||
id
|
||||
title
|
||||
path
|
||||
image
|
||||
author {
|
||||
id
|
||||
name
|
||||
image
|
||||
}
|
||||
content
|
||||
timeToRead
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</page-query>
|
BIN
static/images/admin-settings.jpg
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
static/images/blog-cover.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
static/images/gridsome-logo.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
static/images/mittalyashu.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
static/images/publishing-options.jpg
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
static/images/welcome-to-gridsome.jpg
Normal file
After Width: | Height: | Size: 210 KiB |
BIN
static/images/writing-posts-with-gridsome.jpg
Normal file
After Width: | Height: | Size: 152 KiB |