mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-12 00:27:21 +02:00
feat(v2): list blog tags on posts (#1456)
* feat(v2): list blog tags on posts * fix date handling on blog header * fix console log error due to non unique key
This commit is contained in:
parent
55d7920825
commit
f47059b5bd
5 changed files with 64 additions and 29 deletions
|
@ -135,29 +135,44 @@ class DocusaurusPluginContentBlog {
|
||||||
}
|
}
|
||||||
|
|
||||||
const blogTags = {};
|
const blogTags = {};
|
||||||
|
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
||||||
blogPosts.forEach(blogPost => {
|
blogPosts.forEach(blogPost => {
|
||||||
const {tags} = blogPost.metadata;
|
const {tags} = blogPost.metadata;
|
||||||
if (!tags || tags.length === 0) {
|
if (!tags || tags.length === 0) {
|
||||||
|
// TODO: Extract tags out into a separate plugin.
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
blogPost.metadata.tags = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tags.forEach(tag => {
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
blogPost.metadata.tags = tags.map(tag => {
|
||||||
const normalizedTag = _.kebabCase(tag);
|
const normalizedTag = _.kebabCase(tag);
|
||||||
|
const permalink = normalizeUrl([tagsPath, normalizedTag]);
|
||||||
if (!blogTags[normalizedTag]) {
|
if (!blogTags[normalizedTag]) {
|
||||||
blogTags[normalizedTag] = {
|
blogTags[normalizedTag] = {
|
||||||
name: tag.toLowerCase(), // Will only use the name of the first occurrence of the tag.
|
name: tag.toLowerCase(), // Will only use the name of the first occurrence of the tag.
|
||||||
items: [],
|
items: [],
|
||||||
|
permalink,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
blogTags[normalizedTag].items.push(blogPost.id);
|
blogTags[normalizedTag].items.push(blogPost.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: tag,
|
||||||
|
permalink,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const blogTagsListPath = Object.keys(blogTags).length > 0 ? tagsPath : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
blogPosts,
|
blogPosts,
|
||||||
blogListPaginated,
|
blogListPaginated,
|
||||||
blogTags,
|
blogTags,
|
||||||
|
blogTagsListPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +189,12 @@ class DocusaurusPluginContentBlog {
|
||||||
} = this.options;
|
} = this.options;
|
||||||
|
|
||||||
const {addRoute, createData} = actions;
|
const {addRoute, createData} = actions;
|
||||||
const {blogPosts, blogListPaginated, blogTags} = blogContents;
|
const {
|
||||||
|
blogPosts,
|
||||||
|
blogListPaginated,
|
||||||
|
blogTags,
|
||||||
|
blogTagsListPath,
|
||||||
|
} = blogContents;
|
||||||
|
|
||||||
const blogItemsToModules = {};
|
const blogItemsToModules = {};
|
||||||
// Create routes for blog entries.
|
// Create routes for blog entries.
|
||||||
|
@ -254,21 +274,14 @@ class DocusaurusPluginContentBlog {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tags.
|
// Tags.
|
||||||
const {routeBasePath} = this.options;
|
|
||||||
const {
|
|
||||||
siteConfig: {baseUrl},
|
|
||||||
} = this.context;
|
|
||||||
|
|
||||||
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
||||||
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
|
||||||
const tagsModule = {};
|
const tagsModule = {};
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.keys(blogTags).map(async tag => {
|
Object.keys(blogTags).map(async tag => {
|
||||||
const permalink = normalizeUrl([tagsPath, tag]);
|
const {name, items, permalink} = blogTags[tag];
|
||||||
const {name, items} = blogTags[tag];
|
|
||||||
|
|
||||||
tagsModule[tag] = {
|
tagsModule[tag] = {
|
||||||
|
allTagsPath: blogTagsListPath,
|
||||||
slug: tag,
|
slug: tag,
|
||||||
name,
|
name,
|
||||||
count: items.length,
|
count: items.length,
|
||||||
|
@ -309,12 +322,12 @@ class DocusaurusPluginContentBlog {
|
||||||
// Only create /tags page if there are tags.
|
// Only create /tags page if there are tags.
|
||||||
if (Object.keys(blogTags).length > 0) {
|
if (Object.keys(blogTags).length > 0) {
|
||||||
const tagsListPath = await createData(
|
const tagsListPath = await createData(
|
||||||
`${docuHash(`${tagsPath}-tags`)}.json`,
|
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
|
||||||
JSON.stringify(tagsModule, null, 2),
|
JSON.stringify(tagsModule, null, 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
addRoute({
|
addRoute({
|
||||||
path: tagsPath,
|
path: blogTagsListPath,
|
||||||
component: blogTagsListComponent,
|
component: blogTagsListComponent,
|
||||||
exact: true,
|
exact: true,
|
||||||
modules: {
|
modules: {
|
||||||
|
|
|
@ -10,12 +10,12 @@ import Link from '@docusaurus/Link';
|
||||||
|
|
||||||
function BlogPostItem(props) {
|
function BlogPostItem(props) {
|
||||||
const {children, frontMatter, metadata, truncated} = props;
|
const {children, frontMatter, metadata, truncated} = props;
|
||||||
|
const {date, permalink, tags} = metadata;
|
||||||
|
const {author, authorURL, authorTitle, authorFBID, title} = frontMatter;
|
||||||
|
|
||||||
const renderPostHeader = () => {
|
const renderPostHeader = () => {
|
||||||
const {author, authorURL, authorTitle, authorFBID, title} = frontMatter;
|
const match = date.substring(0, 10).split('-');
|
||||||
const {date, permalink} = metadata;
|
const year = match[0];
|
||||||
|
|
||||||
const blogPostDate = new Date(date);
|
|
||||||
const month = [
|
const month = [
|
||||||
'January',
|
'January',
|
||||||
'February',
|
'February',
|
||||||
|
@ -29,7 +29,8 @@ function BlogPostItem(props) {
|
||||||
'October',
|
'October',
|
||||||
'November',
|
'November',
|
||||||
'December',
|
'December',
|
||||||
];
|
][parseInt(match[1], 10) - 1];
|
||||||
|
const day = parseInt(match[2], 10);
|
||||||
|
|
||||||
const authorImageURL = authorFBID
|
const authorImageURL = authorFBID
|
||||||
? `https://graph.facebook.com/${authorFBID}/picture/?height=200&width=200`
|
? `https://graph.facebook.com/${authorFBID}/picture/?height=200&width=200`
|
||||||
|
@ -37,12 +38,13 @@ function BlogPostItem(props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1 className="margin-bottom--xs">
|
||||||
<Link to={permalink}>{title}</Link>
|
<Link to={permalink}>{title}</Link>
|
||||||
</h1>
|
</h1>
|
||||||
<div className="margin-bottom--sm">
|
<div className="margin-bottom--sm">
|
||||||
{month[blogPostDate.getMonth()]} {blogPostDate.getDay()},{' '}
|
<small>
|
||||||
{blogPostDate.getFullYear()}
|
{month} {day}, {year}
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div className="avatar margin-bottom--md">
|
<div className="avatar margin-bottom--md">
|
||||||
{authorImageURL && (
|
{authorImageURL && (
|
||||||
|
@ -79,11 +81,30 @@ function BlogPostItem(props) {
|
||||||
<div>
|
<div>
|
||||||
{renderPostHeader()}
|
{renderPostHeader()}
|
||||||
<article className="markdown">{children}</article>
|
<article className="markdown">{children}</article>
|
||||||
{truncated && (
|
<div className="row margin-vert--lg">
|
||||||
<div className="text--right margin-vert--md">
|
<div className="col col-6">
|
||||||
<Link to={metadata.permalink}>Read More</Link>
|
{tags.length > 0 && (
|
||||||
|
<>
|
||||||
|
<strong>Tags:</strong>
|
||||||
|
{tags.map(({label, permalink: tagPermalink}) => (
|
||||||
|
<Link
|
||||||
|
key={tagPermalink}
|
||||||
|
className="margin-horiz--sm"
|
||||||
|
to={tagPermalink}>
|
||||||
|
{label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="col col-6 text--right">
|
||||||
|
{truncated && (
|
||||||
|
<Link to={metadata.permalink}>
|
||||||
|
<strong>Read More</strong>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ function BlogPostPage(props) {
|
||||||
<BlogPostItem frontMatter={frontMatter} metadata={metadata}>
|
<BlogPostItem frontMatter={frontMatter} metadata={metadata}>
|
||||||
<BlogPostContents />
|
<BlogPostContents />
|
||||||
</BlogPostItem>
|
</BlogPostItem>
|
||||||
<div className="margin-vert--lg">
|
<div className="margin-vert--xl">
|
||||||
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
|
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,10 +9,11 @@ import React from 'react';
|
||||||
|
|
||||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
import BlogPostItem from '@theme/BlogPostItem';
|
import BlogPostItem from '@theme/BlogPostItem';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
|
||||||
function BlogTagsPostPage(props) {
|
function BlogTagsPostPage(props) {
|
||||||
const {metadata, items} = props;
|
const {metadata, items} = props;
|
||||||
const {name: tagName, count} = metadata;
|
const {allTagsPath, name: tagName, count} = metadata;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
|
@ -24,7 +25,8 @@ function BlogTagsPostPage(props) {
|
||||||
<h1>
|
<h1>
|
||||||
{count} post(s) tagged with "{tagName}"
|
{count} post(s) tagged with "{tagName}"
|
||||||
</h1>
|
</h1>
|
||||||
<div className="margin-vert--lg">
|
<Link href={allTagsPath}>View All Tags</Link>
|
||||||
|
<div className="margin-vert--xl">
|
||||||
{items.map(
|
{items.map(
|
||||||
({content: BlogPostContent, metadata: blogPostMetadata}) => (
|
({content: BlogPostContent, metadata: blogPostMetadata}) => (
|
||||||
<div key={blogPostMetadata.permalink}>
|
<div key={blogPostMetadata.permalink}>
|
||||||
|
|
|
@ -4,7 +4,6 @@ author: Joel Marcey
|
||||||
authorURL: http://twitter.com/JoelMarcey
|
authorURL: http://twitter.com/JoelMarcey
|
||||||
authorFBID: 611217057
|
authorFBID: 611217057
|
||||||
authorTwitter: JoelMarcey
|
authorTwitter: JoelMarcey
|
||||||
tags: [birth]
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue