mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-04 20:57:17 +02:00
feat(v2): blog tags (#1453)
* feat(v2): blog tags * feat(v2): blog tags
This commit is contained in:
parent
ffdd3e60fc
commit
fd270bdceb
7 changed files with 218 additions and 3 deletions
|
@ -26,6 +26,8 @@ const DEFAULT_OPTIONS = {
|
||||||
postsPerPage: 10, // How many posts per page.
|
postsPerPage: 10, // How many posts per page.
|
||||||
blogListComponent: '@theme/BlogListPage',
|
blogListComponent: '@theme/BlogListPage',
|
||||||
blogPostComponent: '@theme/BlogPostPage',
|
blogPostComponent: '@theme/BlogPostPage',
|
||||||
|
blogTagsListComponent: '@theme/BlogTagsListPage',
|
||||||
|
blogTagsPostsComponent: '@theme/BlogTagsPostsPage',
|
||||||
};
|
};
|
||||||
|
|
||||||
class DocusaurusPluginContentBlog {
|
class DocusaurusPluginContentBlog {
|
||||||
|
@ -87,6 +89,7 @@ class DocusaurusPluginContentBlog {
|
||||||
source,
|
source,
|
||||||
description: frontMatter.description || excerpt,
|
description: frontMatter.description || excerpt,
|
||||||
date,
|
date,
|
||||||
|
tags: frontMatter.tags,
|
||||||
title: frontMatter.title || blogFileName,
|
title: frontMatter.title || blogFileName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -126,16 +129,39 @@ class DocusaurusPluginContentBlog {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blogTags = {};
|
||||||
|
blogPosts.forEach(blogPost => {
|
||||||
|
const {tags} = blogPost.metadata;
|
||||||
|
if (!tags || tags.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.forEach(tag => {
|
||||||
|
const normalizedTag = tag.toLowerCase();
|
||||||
|
if (!blogTags[normalizedTag]) {
|
||||||
|
blogTags[normalizedTag] = [];
|
||||||
|
}
|
||||||
|
blogTags[normalizedTag].push(blogPost.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
blogPosts,
|
blogPosts,
|
||||||
blogListPaginated,
|
blogListPaginated,
|
||||||
|
blogTags,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async contentLoaded({content: blogContents, actions}) {
|
async contentLoaded({content: blogContents, actions}) {
|
||||||
const {blogListComponent, blogPostComponent} = this.options;
|
const {
|
||||||
|
blogListComponent,
|
||||||
|
blogPostComponent,
|
||||||
|
blogTagsListComponent,
|
||||||
|
blogTagsPostsComponent,
|
||||||
|
} = this.options;
|
||||||
|
|
||||||
const {addRoute, createData} = actions;
|
const {addRoute, createData} = actions;
|
||||||
const {blogPosts, blogListPaginated} = blogContents;
|
const {blogPosts, blogListPaginated, blogTags} = blogContents;
|
||||||
|
|
||||||
const blogItemsToModules = {};
|
const blogItemsToModules = {};
|
||||||
// Create routes for blog entries.
|
// Create routes for blog entries.
|
||||||
|
@ -213,6 +239,76 @@ class DocusaurusPluginContentBlog {
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Tags.
|
||||||
|
const {routeBasePath} = this.options;
|
||||||
|
const {
|
||||||
|
siteConfig: {baseUrl},
|
||||||
|
} = this.context;
|
||||||
|
|
||||||
|
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
||||||
|
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
||||||
|
const tagsModule = {};
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.keys(blogTags).map(async tag => {
|
||||||
|
const permalink = normalizeUrl([tagsPath, tag]);
|
||||||
|
const postIDs = blogTags[tag];
|
||||||
|
tagsModule[tag] = {
|
||||||
|
count: postIDs.length,
|
||||||
|
permalink,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagsMetadataPath = await createData(
|
||||||
|
`${docuHash(permalink)}.json`,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
addRoute({
|
||||||
|
path: permalink,
|
||||||
|
component: blogTagsPostsComponent,
|
||||||
|
exact: true,
|
||||||
|
modules: {
|
||||||
|
items: postIDs.map(postID => {
|
||||||
|
const {metadata: postMetadata, metadataPath} = blogItemsToModules[
|
||||||
|
postID
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
content: {
|
||||||
|
__import: true,
|
||||||
|
path: postMetadata.source,
|
||||||
|
query: {
|
||||||
|
truncated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata: metadataPath,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
metadata: tagsMetadataPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const tagsListPath = await createData(
|
||||||
|
`${docuHash(`${tagsPath}-tags`)}.json`,
|
||||||
|
JSON.stringify(tagsModule, null, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
addRoute({
|
||||||
|
path: tagsPath,
|
||||||
|
component: blogTagsListComponent,
|
||||||
|
exact: true,
|
||||||
|
modules: {
|
||||||
|
tags: tagsListPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getThemePath() {
|
getThemePath() {
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
|
||||||
|
const CHARS_IN_ALPHABET = 26;
|
||||||
|
const ASCII_LOWERCASE_A = 97;
|
||||||
|
|
||||||
|
function BlogTagsListPage(props) {
|
||||||
|
const {tags} = props;
|
||||||
|
|
||||||
|
const tagsList = Array(CHARS_IN_ALPHABET)
|
||||||
|
.fill(null)
|
||||||
|
.map(() => []);
|
||||||
|
const allTags = Object.keys(tags).sort();
|
||||||
|
|
||||||
|
allTags.forEach(tag => {
|
||||||
|
const firstLetter = tag.charCodeAt(0);
|
||||||
|
tagsList[firstLetter - ASCII_LOWERCASE_A].push(tag);
|
||||||
|
});
|
||||||
|
|
||||||
|
const tagsSection = tagsList
|
||||||
|
.map((tagsForLetter, index) => {
|
||||||
|
if (tagsForLetter.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const letter = String.fromCharCode(
|
||||||
|
ASCII_LOWERCASE_A + index,
|
||||||
|
).toUpperCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={letter}>
|
||||||
|
<h3>{letter}</h3>
|
||||||
|
{tagsForLetter.map(tag => (
|
||||||
|
<Link
|
||||||
|
className="padding-right--md"
|
||||||
|
href={tags[tag].permalink}
|
||||||
|
key="tag">
|
||||||
|
{tag} ({tags[tag].count})
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter(item => item != null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout title="Blog Tags" description="Blog Tags">
|
||||||
|
<div className="container margin-vert--xl">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col col--8 col--offset-2">
|
||||||
|
<h1>Tags</h1>
|
||||||
|
<div className="margin-vert--lg">{tagsSection}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlogTagsListPage;
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
|
import BlogPostItem from '@theme/BlogPostItem';
|
||||||
|
|
||||||
|
function BlogTagsPostPage(props) {
|
||||||
|
const {metadata, items} = props;
|
||||||
|
const {tag} = metadata;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout title={`Blog | Tagged ${tag}`} description={`Blog | Tagged ${tag}`}>
|
||||||
|
<div className="container margin-vert--xl">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col col--8 col--offset-2">
|
||||||
|
<h1>
|
||||||
|
{items.length} post(s) tagged with "{tag}"
|
||||||
|
</h1>
|
||||||
|
<div className="margin-vert--lg">
|
||||||
|
{items.map(
|
||||||
|
({content: BlogPostContent, metadata: blogPostMetadata}) => (
|
||||||
|
<div key={blogPostMetadata.permalink}>
|
||||||
|
<BlogPostItem
|
||||||
|
frontMatter={BlogPostContent.frontMatter}
|
||||||
|
metadata={blogPostMetadata}
|
||||||
|
truncated>
|
||||||
|
<BlogPostContent />
|
||||||
|
</BlogPostItem>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlogTagsPostPage;
|
|
@ -4,6 +4,7 @@ author: Joel Marcey
|
||||||
authorURL: http://twitter.com/JoelMarcey
|
authorURL: http://twitter.com/JoelMarcey
|
||||||
authorFBID: 611217057
|
authorFBID: 611217057
|
||||||
authorTwitter: JoelMarcey
|
authorTwitter: JoelMarcey
|
||||||
|
tags: [birth]
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
|
@ -4,6 +4,7 @@ author: Christine Abernathy
|
||||||
authorURL: http://twitter.com/abernathyca
|
authorURL: http://twitter.com/abernathyca
|
||||||
authorFBID: 1424840234
|
authorFBID: 1424840234
|
||||||
authorTwitter: abernathyca
|
authorTwitter: abernathyca
|
||||||
|
tags: [profilo, adoption]
|
||||||
---
|
---
|
||||||
|
|
||||||
> *“Joel and I were discussing having a website and how it would have been great to launch with it. So I challenged myself to add Docusaurus support. It took just over an hour and a half. I'm going to send you a PR with the addition so you can take a look and see if you like it. Your workflow for adding docs wouldn't be much different from editing those markdown files.”*
|
> *“Joel and I were discussing having a website and how it would have been great to launch with it. So I challenged myself to add Docusaurus support. It took just over an hour and a half. I'm going to send you a PR with the addition so you can take a look and see if you like it. Your workflow for adding docs wouldn't be much different from editing those markdown files.”*
|
||||||
|
|
|
@ -5,6 +5,7 @@ authorTitle: Maintainer of Docusaurus
|
||||||
authorURL: https://github.com/endiliey
|
authorURL: https://github.com/endiliey
|
||||||
authorImageURL: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4
|
authorImageURL: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4
|
||||||
authorTwitter: endiliey
|
authorTwitter: endiliey
|
||||||
|
tags: [new, adoption]
|
||||||
---
|
---
|
||||||
|
|
||||||
Docusaurus was [officially announced](https://docusaurus.io/blog/2017/12/14/introducing-docusaurus) over nine months ago as a way to easily build open source documentation websites. Since then, it has amassed over 8,600 GitHub Stars, and is used by many popular open source projects such as [React Native](https://facebook.github.io/react-native/), [Babel](https://babeljs.io/), [Jest](https://jestjs.io/), [Reason](https://reasonml.github.io/) and [Prettier](https://prettier.io/).
|
Docusaurus was [officially announced](https://docusaurus.io/blog/2017/12/14/introducing-docusaurus) over nine months ago as a way to easily build open source documentation websites. Since then, it has amassed over 8,600 GitHub Stars, and is used by many popular open source projects such as [React Native](https://facebook.github.io/react-native/), [Babel](https://babeljs.io/), [Jest](https://jestjs.io/), [Reason](https://reasonml.github.io/) and [Prettier](https://prettier.io/).
|
||||||
|
|
|
@ -5,6 +5,7 @@ authorTitle: Co-creator of Docusaurus
|
||||||
authorURL: https://github.com/JoelMarcey
|
authorURL: https://github.com/JoelMarcey
|
||||||
authorFBID: 611217057
|
authorFBID: 611217057
|
||||||
authorTwitter: JoelMarcey
|
authorTwitter: JoelMarcey
|
||||||
|
tags: [birth]
|
||||||
---
|
---
|
||||||
|
|
||||||

|

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