You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by li...@apache.org on 2021/09/05 10:03:17 UTC
[apisix-website] branch master updated: feat: optimize the blog
page display effect (#553)
This is an automated email from the ASF dual-hosted git repository.
liuxiran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-website.git
The following commit(s) were added to refs/heads/master by this push:
new e6cbdbb feat: optimize the blog page display effect (#553)
e6cbdbb is described below
commit e6cbdbb2638979bcdccb43ec4d74f30af73e7950
Author: Baoyuan <ba...@gmail.com>
AuthorDate: Sun Sep 5 05:03:11 2021 -0500
feat: optimize the blog page display effect (#553)
---
website/docusaurus.config.js | 3 +-
website/src/assets/icons/blog-date.svg | 1 +
website/src/assets/icons/blog-tags.svg | 1 +
website/src/theme/BlogListPage/index.js | 75 +++++++++++
website/src/theme/BlogPostItem/index.js | 153 +++++++++++++++++++++++
website/src/theme/BlogPostItem/styles.module.css | 49 ++++++++
website/src/theme/BlogPostPage/index.js | 57 +++++++++
website/src/theme/BlogSidebar/index.js | 67 ++++++++++
website/src/theme/BlogSidebar/styles.module.css | 68 ++++++++++
website/src/theme/BlogTagsPostsPage/index.js | 69 ++++++++++
10 files changed, 542 insertions(+), 1 deletion(-)
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index 1050c8c..7ae1910 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -215,7 +215,8 @@ module.exports = {
},
blog: {
path: "blog",
- blogSidebarCount: 0,
+ // see https://github.com/facebook/docusaurus/issues/5353
+ postsPerPage: 10000,
},
theme: {
customCss: "../src/css/customTheme.css",
diff --git a/website/src/assets/icons/blog-date.svg b/website/src/assets/icons/blog-date.svg
new file mode 100644
index 0000000..cd21dbd
--- /dev/null
+++ b/website/src/assets/icons/blog-date.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="48" height="48" fill="white" fill-opacity="0.01"/><path d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/><path d="M24.0083 12L24.0071 24.0088L32.4865 32.4882" stroke="#333" stroke-width="4" stroke-linecap="rou [...]
\ No newline at end of file
diff --git a/website/src/assets/icons/blog-tags.svg b/website/src/assets/icons/blog-tags.svg
new file mode 100644
index 0000000..5072247
--- /dev/null
+++ b/website/src/assets/icons/blog-tags.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="48" height="48" fill="white" fill-opacity="0.01"/><path d="M42.1691 29.2451L29.2631 42.1511C28.5879 42.8271 27.6716 43.2069 26.7161 43.2069C25.7606 43.2069 24.8444 42.8271 24.1691 42.1511L8 26V8H26L42.1691 24.1691C43.5649 25.5732 43.5649 27.841 42.1691 29.2451Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/><path fill-rule [...]
\ No newline at end of file
diff --git a/website/src/theme/BlogListPage/index.js b/website/src/theme/BlogListPage/index.js
new file mode 100644
index 0000000..134ccc7
--- /dev/null
+++ b/website/src/theme/BlogListPage/index.js
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import React, { useEffect, useState } from 'react';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import Layout from '@theme/Layout';
+import BlogPostItem from '@theme/BlogPostItem';
+import BlogListPaginator from '@theme/BlogListPaginator';
+import BlogSidebar from '@theme/BlogSidebar';
+import {ThemeClassNames} from '@docusaurus/theme-common';
+
+function BlogListPage(props) {
+ const {metadata, items, sidebar} = props;
+ const {
+ siteConfig: {title: siteTitle},
+ } = useDocusaurusContext();
+ const {blogDescription, blogTitle, permalink} = metadata;
+ const isBlogOnlyMode = permalink === '/';
+ const title = isBlogOnlyMode ? siteTitle : blogTitle;
+ const [tagsCount, setTagsCount] = useState();
+
+ useEffect(() => {
+ let totalTags = [];
+ items.forEach(item => {
+ const tags = item.content.frontMatter.tags;
+ if (tags) {
+ totalTags = totalTags.concat(tags);
+ }
+ });
+ const tagsCount = {
+ All: items.length,
+ };
+ totalTags.forEach(item => {
+ tagsCount[item] = (tagsCount[item] || 0) + 1;
+ })
+ setTagsCount(tagsCount);
+ }, []);
+
+ return (
+ <Layout
+ title={title}
+ description={blogDescription}
+ wrapperClassName={ThemeClassNames.wrapper.blogPages}
+ pageClassName={ThemeClassNames.page.blogListPage}
+ searchMetadatas={{
+ // assign unique search tag to exclude this page from search results!
+ tag: 'blog_posts_list',
+ }}>
+ <div className="container margin-vert--lg">
+ <div className="row">
+ <div className="col col--3">
+ <BlogSidebar count={tagsCount} />
+ </div>
+ <main className="col col--9">
+ {items.map(({content: BlogPostContent}) => (
+ <BlogPostItem
+ key={BlogPostContent.metadata.permalink}
+ frontMatter={BlogPostContent.frontMatter}
+ metadata={BlogPostContent.metadata}
+ truncated={BlogPostContent.metadata.truncated}>
+ <BlogPostContent />
+ </BlogPostItem>
+ ))}
+ <BlogListPaginator metadata={metadata} />
+ </main>
+ </div>
+ </div>
+ </Layout>
+ );
+}
+
+export default BlogListPage;
diff --git a/website/src/theme/BlogPostItem/index.js b/website/src/theme/BlogPostItem/index.js
new file mode 100644
index 0000000..4ded666
--- /dev/null
+++ b/website/src/theme/BlogPostItem/index.js
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * 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 clsx from 'clsx';
+import {MDXProvider} from '@mdx-js/react';
+import Translate, {translate} from '@docusaurus/Translate';
+import Link from '@docusaurus/Link';
+import MDXComponents from '@theme/MDXComponents';
+import Seo from '@theme/Seo';
+import styles from './styles.module.css';
+import {usePluralForm} from '@docusaurus/theme-common'; // Very simple pluralization: probably good enough for now
+import TagsLogo from "../../assets/icons/blog-tags.svg";
+import DateLogo from "../../assets/icons/blog-date.svg";
+
+function useReadingTimePlural() {
+ const {selectMessage} = usePluralForm();
+ return (readingTimeFloat) => {
+ const readingTime = Math.ceil(readingTimeFloat);
+ return selectMessage(
+ readingTime,
+ translate(
+ {
+ id: 'theme.blog.post.readingTime.plurals',
+ description:
+ 'Pluralized label for "{readingTime} min read". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',
+ message: 'One min read|{readingTime} min read',
+ },
+ {
+ readingTime,
+ },
+ ),
+ );
+ };
+}
+
+function BlogPostItem(props) {
+ const readingTimePlural = useReadingTimePlural();
+ const {
+ children,
+ frontMatter,
+ metadata,
+ truncated,
+ isBlogPostPage = false,
+ } = props;
+ const {date, formattedDate, permalink, tags, readingTime} = metadata;
+ const {author, title, image, keywords} = frontMatter;
+ const authorURL = frontMatter.author_url || frontMatter.authorURL;
+ const authorTitle = frontMatter.author_title || frontMatter.authorTitle;
+ const authorImageURL =
+ frontMatter.author_image_url || frontMatter.authorImageURL;
+
+ const renderPostHeader = () => {
+ const TitleHeading = isBlogPostPage ? 'h1' : 'h2';
+ return (
+ <header>
+ <TitleHeading
+ className={clsx('margin-bottom--sm', styles.blogPostTitle)}>
+ {isBlogPostPage ? title : <Link to={permalink}>{title}</Link>}
+ </TitleHeading>
+ <div className={styles.postHeader}>
+ <div className="avatar margin-bottom--md">
+ <div className="avatar__intro">
+ {author && (
+ <>
+ <h4 className="avatar__name">
+ <Link href={authorURL} className={styles.authorName}>{`Author: ${author}`}</Link>
+ </h4>
+ </>
+ )}
+ </div>
+ </div>
+ <div className={`margin-bottom--md ${styles.line}`}>
+ <div></div>
+ </div>
+ <div className={`margin-bottom--md ${styles.headerDate}`}>
+ <DateLogo />
+ <time dateTime={date} className={styles.blogPostDate}>
+ {formattedDate}
+ {readingTime && (
+ <>
+ {' ยท '}
+ {readingTimePlural(readingTime)}
+ </>
+ )}
+ </time>
+ </div>
+ <div className={`margin-bottom--md ${styles.line}`}>
+ <div></div>
+ </div>
+ <div className={`margin-bottom--md`}>
+ {tags.length > 0 && (
+ <div className={`col ${styles.headerTags}`}>
+ <TagsLogo />
+ {tags.map(({label, permalink: tagPermalink}) => (
+ <Link
+ key={tagPermalink}
+ className="margin-horiz--sm"
+ to={tagPermalink}>
+ {label}
+ </Link>
+ ))}
+ </div>
+ )}
+ </div>
+ </div>
+
+ </header>
+ );
+ };
+
+ return (
+ <>
+ <Seo
+ {...{
+ keywords,
+ image,
+ }}
+ />
+
+ <article className={!isBlogPostPage ? 'margin-bottom--xl' : undefined}>
+ {renderPostHeader()}
+ <div className="markdown">
+ <MDXProvider components={MDXComponents}>{children}</MDXProvider>
+ </div>
+ {(tags.length > 0 || truncated) && (
+ <footer className="row margin-vert--lg">
+ {truncated && (
+ <div className="col text--right">
+ <Link
+ to={metadata.permalink}
+ aria-label={`Read more about ${title}`}>
+ <strong>
+ <Translate
+ id="theme.blog.post.readMore"
+ description="The label used in blog post item excerpts to link to full blog posts">
+ Read More
+ </Translate>
+ </strong>
+ </Link>
+ </div>
+ )}
+ </footer>
+ )}
+ </article>
+ </>
+ );
+}
+
+export default BlogPostItem;
diff --git a/website/src/theme/BlogPostItem/styles.module.css b/website/src/theme/BlogPostItem/styles.module.css
new file mode 100644
index 0000000..cb58acc
--- /dev/null
+++ b/website/src/theme/BlogPostItem/styles.module.css
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+.blogPostTitle {
+ font-size: 2.25rem;
+}
+
+.headerDate {
+ display: flex;
+ align-items: center;
+ margin-left: 16px;
+}
+
+.blogPostDate {
+ font-size: 0.9rem;
+ margin-left: 5px;
+}
+
+.postHeader {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ color: #6A737D;
+}
+
+.authorName {
+ font-weight: normal;
+}
+
+.headerTags {
+ display: flex;
+ align-items: center;
+}
+
+.line {
+ margin-left: 16px;
+ display: flex;
+ align-items: center;
+}
+
+.line div {
+ width: 1px;
+ height: 12px;
+ background-color: #6A737D;
+}
diff --git a/website/src/theme/BlogPostPage/index.js b/website/src/theme/BlogPostPage/index.js
new file mode 100644
index 0000000..247ba1f
--- /dev/null
+++ b/website/src/theme/BlogPostPage/index.js
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * 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';
+import BlogPostItem from '@theme/BlogPostItem';
+import BlogPostPaginator from '@theme/BlogPostPaginator';
+import BlogSidebar from '@theme/BlogSidebar';
+import TOC from '@theme/TOC';
+import EditThisPage from '@theme/EditThisPage';
+import {ThemeClassNames} from '@docusaurus/theme-common';
+
+function BlogPostPage(props) {
+ const {content: BlogPostContents, sidebar} = props;
+ const {frontMatter, metadata} = BlogPostContents;
+ const {title, description, nextItem, prevItem, editUrl} = metadata;
+ const {hide_table_of_contents: hideTableOfContents} = frontMatter;
+ return (
+ <Layout
+ title={title}
+ description={description}
+ wrapperClassName={ThemeClassNames.wrapper.blogPages}
+ pageClassName={ThemeClassNames.page.blogPostPage}>
+ {BlogPostContents && (
+ <div className="container margin-vert--lg">
+ <div className="row">
+ <div className="col col--2"></div>
+ <main className="col col--8">
+ <BlogPostItem
+ frontMatter={frontMatter}
+ metadata={metadata}
+ isBlogPostPage>
+ <BlogPostContents />
+ </BlogPostItem>
+ <div>{editUrl && <EditThisPage editUrl={editUrl} />}</div>
+ {(nextItem || prevItem) && (
+ <div className="margin-vert--xl">
+ <BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
+ </div>
+ )}
+ </main>
+ {!hideTableOfContents && BlogPostContents.toc && (
+ <div className="col col--2">
+ <TOC toc={BlogPostContents.toc} />
+ </div>
+ )}
+ </div>
+ </div>
+ )}
+ </Layout>
+ );
+}
+
+export default BlogPostPage;
diff --git a/website/src/theme/BlogSidebar/index.js b/website/src/theme/BlogSidebar/index.js
new file mode 100644
index 0000000..e873afe
--- /dev/null
+++ b/website/src/theme/BlogSidebar/index.js
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import React, { useEffect, useState } from 'react';
+import clsx from 'clsx';
+import Link from '@docusaurus/Link';
+import styles from './styles.module.css';
+import { useHistory } from '@docusaurus/router';
+
+export default function BlogSidebar({count}) {
+ const [ selected, setSelected ] = useState();
+ const history = useHistory();
+ const path = history.location.pathname.split('/');
+
+ useEffect(() => {
+ if (path.length === 2) {
+ setSelected('All');
+ } else if (path.length === 4) {
+ if (path[3].indexOf('-') !== -1) {
+ setSelected(path[3].replace('-', ' '));
+ } else {
+ setSelected(path[3]);
+ }
+ } else {
+ setSelected('All');
+ }
+ }, [path]);
+
+ if (!count) {
+ return null;
+ }
+
+ const handleTagClick = (tag) => {
+ setSelected(tag);
+ if (tag === "All") {
+ history.push('/blog');
+ } else {
+ if (tag.indexOf(' ') !== -1) {
+ tag = tag.replace(' ', '-');
+ }
+ history.push(`/blog/tags/${tag}`);
+ }
+ };
+
+ return (
+ <div className={clsx(styles.sidebar, 'thin-scrollbar')}>
+ <h3 className={styles.sidebarItemTitle}>Tags</h3>
+ <div className={styles.sidebarItemList}>
+ {Object.entries(count).map(([tag, num]) => (
+ <div
+ key={tag}
+ className={`${styles.sidebarItem} ${selected === tag ? styles.selected : ''}`}
+ onClick={() => handleTagClick(tag)}
+ >
+ <div className={styles.sidebarItemLink}>
+ {tag}
+ </div>
+ <p>{num}</p>
+ </div>
+ ))}
+ </div>
+ </div>
+ );
+}
diff --git a/website/src/theme/BlogSidebar/styles.module.css b/website/src/theme/BlogSidebar/styles.module.css
new file mode 100644
index 0000000..185b090
--- /dev/null
+++ b/website/src/theme/BlogSidebar/styles.module.css
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+.sidebar {
+ display: inherit;
+ max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
+ overflow-y: auto;
+ position: sticky;
+ top: calc(var(--ifm-navbar-height) + 2rem);
+}
+
+.sidebarItemTitle {
+ margin-bottom: 0.5rem;
+}
+
+.sidebarItemList {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+ align-items: flex-start;
+ flex-wrap: wrap;
+}
+
+.sidebarItem {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ align-items: center;
+ border: 1px solid var(--ifm-color-primary-dark);
+ border-radius: 4px;
+ height: 2rem;
+ line-height: 2rem;
+ margin-bottom: 0.5rem;
+ margin-right: 0.5rem;
+ padding: 0 1rem;
+}
+
+.selected {
+ background-color: var(--ifm-color-primary);
+ border: 1px solid var(--color-border);
+ color: var(--ifm-color-primary-light);
+}
+
+.sidebarItem:hover {
+ background-color: var(--ifm-color-primary);
+ border: 1px solid var(--color-border);
+ color: var(--ifm-color-primary-light);
+ cursor: pointer;
+}
+
+.sidebarItem p {
+ margin: 0 0 0 0.5rem;
+ line-height: 2rem;
+}
+
+.sidebarItemLinkActive {
+ color: var(--ifm-color-primary);
+}
+
+@media only screen and (max-width: 996px) {
+ .sidebar {
+ display: none;
+ }
+}
diff --git a/website/src/theme/BlogTagsPostsPage/index.js b/website/src/theme/BlogTagsPostsPage/index.js
new file mode 100644
index 0000000..8ff68c4
--- /dev/null
+++ b/website/src/theme/BlogTagsPostsPage/index.js
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import React, { useEffect, useState } from 'react';
+import Layout from '@theme/Layout';
+import BlogPostItem from '@theme/BlogPostItem';
+import Link from '@docusaurus/Link';
+import BlogSidebar from '@theme/BlogSidebar';
+import Translate, {translate} from '@docusaurus/Translate';
+import {ThemeClassNames, usePluralForm} from '@docusaurus/theme-common'; // Very simple pluralization: probably good enough for now
+
+function BlogTagsPostPage(props) {
+ const {metadata, items, sidebar} = props;
+ const {allTagsPath, name: tagName, count} = metadata;
+ const [tagsCount, setTagsCount] = useState();
+
+ useEffect(() => {
+ let totalTags = [];
+ items.forEach(item => {
+ const tags = item.content.frontMatter.tags;
+ if (tags) {
+ totalTags = totalTags.concat(tags);
+ }
+ });
+ const tagsCount = {
+ All: items.length,
+ };
+ totalTags.forEach(item => {
+ tagsCount[item] = (tagsCount[item] || 0) + 1;
+ })
+ setTagsCount(tagsCount);
+ }, []);
+
+ return (
+ <Layout
+ title={`Posts tagged "${tagName}"`}
+ description={`Blog | Tagged "${tagName}"`}
+ wrapperClassName={ThemeClassNames.wrapper.blogPages}
+ pageClassName={ThemeClassNames.page.blogTagsPostPage}
+ searchMetadatas={{
+ // assign unique search tag to exclude this page from search results!
+ tag: 'blog_tags_posts',
+ }}>
+ <div className="container margin-vert--lg">
+ <div className="row">
+ <div className="col col--3">
+ <BlogSidebar count={tagsCount} />
+ </div>
+ <main className="col col--9">
+ {items.map(({content: BlogPostContent}) => (
+ <BlogPostItem
+ key={BlogPostContent.metadata.permalink}
+ frontMatter={BlogPostContent.frontMatter}
+ metadata={BlogPostContent.metadata}
+ truncated>
+ <BlogPostContent />
+ </BlogPostItem>
+ ))}
+ </main>
+ </div>
+ </div>
+ </Layout>
+ );
+}
+
+export default BlogTagsPostPage;