Add subscribe and community components, adjust blog width, update readme (#272)

- Updated readme
- Used double quotes instead of single quotes
- Increase blog content width

  Before

  ![Screen Shot 2023-07-14 at 13.45.45](/attachments/9c669c42-2efd-4ae5-bbf5-24f252c26fa0)

  After

  ![Screen Shot 2023-07-14 at 13.45.31](/attachments/6f72912c-2fd9-4a63-8839-583864faa474)

- Added subscribe and community components to buttom of the blog post page.

 ![Screen Shot 2023-07-14 at 15.55.12](/attachments/3808fa5a-e951-487b-8ed2-dc693cfec9e2)

  ![Screen Shot 2023-07-14 at 15.55.30](/attachments/864ff357-a116-47ce-abf0-8278e24ede7d)

  Loading:

  ![Screen Shot 2023-07-14 at 15.57.00](/attachments/2f0ace67-d83e-4415-a778-8e7913fb6505)

   Post sent:

  ![Screen Shot 2023-07-14 at 13.48.44](/attachments/ceac4970-a914-49d5-ad42-febb3d259d89)

   Mobile:

  ![Screen Shot 2023-07-14 at 15.58.19](/attachments/7e01f575-7e4d-4d99-9888-7fb246ceb313)

  ![Screen Shot 2023-07-14 at 15.58.27](/attachments/879128d1-f115-41a7-8e8e-574f0b3de208)

Reviewed-on: https://gitea.com/gitea/blog/pulls/272
Co-authored-by: HesterG <hestergong@gmail.com>
Co-committed-by: HesterG <hestergong@gmail.com>
This commit is contained in:
HesterG 2023-07-14 09:35:26 +00:00 committed by Lunny Xiao
parent 42fd823b6e
commit c3d361092e
25 changed files with 738 additions and 64 deletions

View File

@ -13,13 +13,13 @@ If you want to host this page on your own you can take our docker image
## Install
This website uses the [Hugo](https://github.com/spf13/hugo) static site
generator. If you are planning to contribute you'll want to download and install
Hugo on your local machine.
This website uses the [Docusaurus](https://docusaurus.io/) static site
generator. If you are planning to contribute you'll want to install
Docusaurus on your local machine.
The installation of Hugo is out of the scope of this document, so please take
the [official install instructions](https://gohugo.io/overview/installing/) to
get Hugo up and running.
The installation of Docusaurus is out of the scope of this document, so please take
the [official install instructions](https://docusaurus.io/docs/installation) to
get Docusaurus up and running.
## Development
@ -27,7 +27,7 @@ To generate the website and serve it on [localhost:1313](http://localhost:1313)
just execute this command and stop it with `Ctrl+C`:
```
make server
npm run start
```
When you are done with your changes just create a pull request, after merging

View File

@ -4,12 +4,12 @@
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"start": "docusaurus start --port 1313",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"serve": "docusaurus serve --port 1313",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},

View File

@ -0,0 +1,22 @@
import clsx from "clsx";
import React from "react";
import style from "./styles.module.css";
// skin?: "default" | "primary"
export const ActionCard = ({ skin = "default", icon, title, description, svgBackgroundColor, children, className }) => {
const styles = { background: svgBackgroundColor};
return (
<div
className={clsx(style.root, className, {
[style.skinPrimary]: skin === "primary",
})}
>
<div className={style.icon} style={styles}>{icon}</div>
<h3 className={style.title}>{title}</h3>
<p className={style.description}>{description}</p>
<div className={style.content}>{children}</div>
</div>
)
}
export default ActionCard

View File

@ -0,0 +1,53 @@
.root {
padding: 2rem;
border-radius: 16px;
background: var(--theme-attention-card-bg-color);
}
.title {
margin-top: 1rem;
margin-bottom: 1rem;
font-size: var(--font-size-big-1);
font-weight: var(--ifm-font-weight-bold);
}
.description {
font-size: var(--font-size-large);
}
@media screen and (min-width: 880px) {
.root {
padding: 2rem;
}
.title {
font-size: 1.2rem;
}
}
.content {
display: flex;
flex: 1;
}
.skinPrimary {
background: var(--ifm-color-primary-lighter);
}
[data-theme='dark'] .skinPrimary {
background: var(--ifm-color-primary-darker);
}
.skinPrimary .title,
.skinPrimary .description {
color: var(--theme-attention-card-text-color);
}
.icon {
display: flex;
align-items: center;
justify-content: center;
height: 76px;
width: 76px;
border-radius: 50%;
}

View File

@ -0,0 +1,19 @@
.cards {
display: grid;
gap: 1.5rem;
}
.card__link {
text-transform: uppercase;
font-size: var(--font-size-normal);
}
.card__link:not(:last-child) {
margin-right: 1.5rem;
}
@media screen and (min-width: 768px) {
.cards {
grid-template-columns: 1fr 1fr;
}
}

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="45" height="45" viewBox="0 0 576 512">
<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path d="M80.3 44C69.8 69.9 64 98.2 64 128s5.8 58.1 16.3 84c6.6 16.4-1.3 35-17.7 41.7s-35-1.3-41.7-17.7C7.4 202.6 0 166.1 0 128S7.4 53.4 20.9 20C27.6 3.6 46.2-4.3 62.6 2.3S86.9 27.6 80.3 44zM555.1 20C568.6 53.4 576 89.9 576 128s-7.4 74.6-20.9 108c-6.6 16.4-25.3 24.3-41.7 17.7S489.1 228.4 495.7 212c10.5-25.9 16.3-54.2 16.3-84s-5.8-58.1-16.3-84C489.1 27.6 497 9 513.4 2.3s35 1.3 41.7 17.7zM352 128c0 23.7-12.9 44.4-32 55.4V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V183.4c-19.1-11.1-32-31.7-32-55.4c0-35.3 28.7-64 64-64s64 28.7 64 64zM170.6 76.8C163.8 92.4 160 109.7 160 128s3.8 35.6 10.6 51.2c7.1 16.2-.3 35.1-16.5 42.1s-35.1-.3-42.1-16.5c-10.3-23.6-16-49.6-16-76.8s5.7-53.2 16-76.8c7.1-16.2 25.9-23.6 42.1-16.5s23.6 25.9 16.5 42.1zM464 51.2c10.3 23.6 16 49.6 16 76.8s-5.7 53.2-16 76.8c-7.1 16.2-25.9 23.6-42.1 16.5s-23.6-25.9-16.5-42.1c6.8-15.6 10.6-32.9 10.6-51.2s-3.8-35.6-10.6-51.2c-7.1-16.2 .3-35.1 16.5-42.1s35.1 .3 42.1 16.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,53 @@
import footerCss from "./footer.module.css";
import ActionCard from "../ActionCard";
import FossIcon from "./foss.svg";
import SubscribeIcon from "./subscribeIcon.svg";
import Subscribe from "../Subscribe";
import React from "react";
import SvgImage from "../SvgImage";
export const ActionFooter = () => (
<div className={footerCss.cards}>
<ActionCard
icon={
<SvgImage
image={<FossIcon />}
title="An icon showing wave propagation"
/>
}
svgBackgroundColor="#ffffff"
title="Join our community"
description="Gitea is open source. Star our GitHub repo, and join our community on Discord!"
>
<a
className={footerCss.card__link}
href={'https://github.com/go-gitea/gitea'}
rel="noopener noreferrer"
target="_blank"
>
Go to GitHub&nbsp;&nbsp;&gt;
</a>
<a className={footerCss.card__link} href={'https://discord.com/invite/gitea'}>
Join Discord&nbsp;&nbsp;&gt;
</a>
</ActionCard>
<ActionCard
title="Subscribe to our newsletter"
description="Stay up to date with all things Gitea"
svgBackgroundColor="#1E1F27"
icon={
<SvgImage
image={<SubscribeIcon />}
title="An icon showing a paper plane"
/>
}
skin="primary"
>
<Subscribe
placeholder="Email address"
submitButtonText = "Subscribe"
/>
</ActionCard>
</div>
)

View File

@ -0,0 +1,27 @@
.loader {
position: absolute;
width: 20px;
height: 20px;
}
.loader:after {
content: " ";
display: block;
width: 14px;
height: 14px;
margin: 0;
border-radius: 50%;
border: 3px solid transparent;
border-color: var(--ifm-color-white) transparent var(--ifm-color-white)
transparent;
animation: loader 1.2s linear infinite;
}
@keyframes loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="45" height="45" viewBox="0 0 512 512">
<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<style>svg{fill:#ffffff}</style>
<path d="M16.1 260.2c-22.6 12.9-20.5 47.3 3.6 57.3L160 376V479.3c0 18.1 14.6 32.7 32.7 32.7c9.7 0 18.9-4.3 25.1-11.8l62-74.3 123.9 51.6c18.9 7.9 40.8-4.5 43.9-24.7l64-416c1.9-12.1-3.4-24.3-13.5-31.2s-23.3-7.5-34-1.4l-448 256zm52.1 25.5L409.7 90.6 190.1 336l1.2 1L68.2 285.7zM403.3 425.4L236.7 355.9 450.8 116.6 403.3 425.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 623 B

View File

@ -0,0 +1,66 @@
import clsx from "clsx";
import React from "react";
import styles from "./styles.module.css";
const Button = (props) => {
const { icon, variant, size, uppercase, className, to, href, children } = props;
const classes = clsx(className, styles.button, {
[styles["button--icon"]]: icon != null,
[styles["button--primary"]]: variant === "primary",
[styles["button--secondary"]]: variant === "secondary",
[styles["button--small"]]: size === "small",
[styles["button--tertiary"]]: variant === "tertiary",
[styles["button--plain"]]: variant === "plain",
[styles["button--uppercase"]]: uppercase === "true",
[styles["button--xsmall"]]: size === "xsmall",
[styles["button--xxsmall"]]: size === "xxsmall",
})
if (href != null) {
const { disabled, onClick, newtab} = props;
return (
<a
className={classes}
{...(disabled ?? false ? {} : {
href,
onClick,
})}
{...(newtab === "true" ? {
rel: "noopener noreferrer",
target: "_blank",
} : {})}
>
{icon}
{children}
</a>
)
}
if (to != null) {
return (
<a className={classes} href={to} onClick={onClick}>
{icon}
{children}
</a>
)
}
return (
<button
{...props}
className={classes}
>
{icon}
{children}
</button>
)
}
Button.defaultProps = {
newtab: "true",
size: "normal",
uppercase: "true",
variant: "primary",
}
export default Button

View File

@ -0,0 +1,95 @@
.button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 55px;
padding: 0 2rem;
border: none;
border-radius: calc(var(--ifm-global-border-radius) / 2);
font-weight: var(--ifm-font-weight-bold);
font-size: var(--font-size-normal);
transition: background-color 100ms cubic-bezier(0.17, 0.67, 0.83, 0.67);
}
.button:hover {
text-decoration: none;
cursor: pointer;
}
.button--plain {
height: auto;
padding: 0;
font-weight: unset;
font-size: unset;
}
.button--primary {
background-color: var(--theme-button-primary-background-color);
color: var(--theme-button-primary-text-color); }
.button--primary:hover {
background-color: var(--theme-button-primary-hover-background-color);
color: var(--theme-button-primary-text-color);
}
.button--icon img, .button--icon svg {
margin-right: 0.5rem;
}
.button--secondary {
background-color: var(--theme-button-secondary-background-color);
color: var(--theme-button-secondary-text-color);
}
.button--secondary:hover {
background-color: var(--theme-button-secondary-hover-background-color);
color: var(--theme-button-secondary-text-color);
}
.button--tertiary {
background-color: var(--theme-button-tertiary-background-color);
color: var(--theme-button-tertiary-text-color);
}
.button--tertiary:hover {
color: var(--theme-button-tertiary-text-color);
background-color: var(--theme-button-tertiary-hover-background-color);
}
.button--small {
height: 3.5rem;
}
.button--xsmall {
height: 2.6rem;
padding: 0 1rem;
}
.button--xxsmall {
height: 2rem;
font-weight: normal;
font-size: 0.9rem;
}
.button--uppercase {
text-transform: uppercase;
}
@media (max-width: 996px) {
.button {
padding: 0 1.75rem;
}
.button--xsmall {
padding: 0 1rem;
}
.button--xxsmall {
padding: 0 0.9rem;
}
.button--plain {
padding: 0;
}
}

View File

@ -0,0 +1,21 @@
import clsx from "clsx";
import React from "react";
import styles from "./styles.module.css";
const Input = (props) => {
const classes = clsx(props.className, styles.input)
return (
<input
{...props}
className={classes}
/>
)
}
Input.defaultProps = {
type: "text",
}
export default Input

View File

@ -0,0 +1,23 @@
.input {
display: flex;
height: 55px;
padding: 0 2rem;
align-items: center;
border-radius: calc(var(--ifm-global-border-radius) / 2);
border: none;
background: var(--palette-rock);
font-size: var(--font-size-normal);
color: var(--ifm-color-white);
border: 2px solid transparent;
}
.input:focus {
outline: none;
border-color: var(--ifm-color-white);
}
.input:placeholder {
color: var(--palette-pale-blue);
font-size: var(--font-size-normal);
font-weight: var(--ifm-font-weight-bold);
}

View File

@ -0,0 +1,31 @@
import React from "react";
import style from "./styles.module.css";
import clsx from "clsx";
export const Section = ({
fullWidth,
children,
odd,
accent,
row,
noGap,
center,
className = "",
}) => (
<div
className={clsx(
style.root,
{
[style.odd]: odd,
[style.accent]: accent,
[style.row]: row,
[style.fullWidth]: fullWidth,
[style.noGap]: noGap,
[style.center]: center,
},
className,
)}
>
{children}
</div>
)

View File

@ -0,0 +1,41 @@
.root {
display: flex;
flex-direction: column;
max-width: var(--ifm-container-width);
width: 100%;
padding: 2rem 1rem;
margin: 0 auto;
}
@media screen and (min-width: 900px) {
.root {
padding: 4.5rem 2rem;
}
}
.row {
flex-direction: row;
}
.odd {
background-color: var(--theme-section-odd-bg-color);
}
.accent {
--ifm-link-hover-color: var(--palette-pink);
--ifm-link-color: var(--palette-pink);
padding-top: 7rem;
padding-bottom: 7.5rem;
}
.fullWidth {
max-width: 100%;
}
.noGap {
padding: 0;
}
.center {
align-items: center;
}

View File

@ -0,0 +1,39 @@
import React, { useState } from "react"
import Input from "../Input"
import Button from "../Button"
import style from "./style.module.css"
import clsx from "clsx"
const Spinner = () => <span className={style.loader} />
const Subscribe = ({placeholder, submitButtonText, className, classNameInputs}) => {
const [loading, setLoading] = useState(false)
return (
<form method="post" action="https://list.gitea.com/subscription/form" className={clsx(style.root, className)} onSubmit={() => {setLoading(true)}}>
<div className={clsx(style.inputs, classNameInputs)}>
<Input type="hidden" name="nonce" />
<Input className={style.checkbox} id="2aae7" type="checkbox" name="l" value="2aae7a49-b6b9-4aa3-b35a-32c9aeace57b" checked readOnly />
<Input
className={style.input}
name="email"
type="email"
title="Email address should be valid"
placeholder={placeholder}
required
autoComplete="off"
/>
<Button
variant={"tertiary"}
type="submit"
className={style.subscribeSubmit}
>
{loading ? <Spinner /> : submitButtonText}
</Button>
</div>
</form>
)
}
export default Subscribe

View File

@ -0,0 +1,75 @@
.root {
width: 100%;
}
.inputs {
display: grid;
gap: 1rem;
}
:global(html[data-theme="light"]) .subscribeSubmit {
background: #dde0e9;
}
:global(html[data-theme="light"]) .subscribeSubmit:hover {
opacity: 0.8;
}
@media screen and (min-width: 600px) {
.inputs {
grid-template-columns: 4fr 2fr;
}
}
.input {
color: var(--theme-input-text-color);
padding: 1rem;
font-size: var(--font-size-small);
width: 100%;
background-color: var(--theme-input-bg-color);
}
.checkbox {
display: none;
}
.input::placeholder {
color: var(--theme-input-text-color);
}
.submit {
white-space: nowrap;
}
.loader {
position: absolute;
width: 20px;
height: 20px;
}
.loader:after {
content: " ";
display: block;
width: 14px;
height: 14px;
margin: 0;
border-radius: 50%;
border: 3px solid transparent;
border-color: var(--ifm-color-white) transparent var(--ifm-color-white)
transparent;
animation: loader 1.2s linear infinite;
}
.success {
font-size: var(--font-size-large);
font-weight: var(--ifm-font-weight-bold);
}
@keyframes loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,9 @@
import { cloneElement } from "react";
const SvgImage = ({ image, title = "" }) =>
cloneElement(image, {
...image.props,
title,
})
export default SvgImage

View File

@ -6,6 +6,25 @@
/* You can override the default Infima variables here. */
:root {
--font-size-small: 15px;
--font-size-normal: 16px;
--font-size-large: 17px;
--font-size-big-1: 22px;
--font-size-big-2: 24px;
--font-size-big-3: 32px;
--font-size-big-4: 46px;
--font-size-big-5: 64px;
--palette-dark-10: rgba(0, 0, 0, 0.1);
--palette-dark-20: rgba(0, 0, 0, 0.2);
--palette-dark-30: rgba(0, 0, 0, 0.3);
--palette-dark-40: rgba(0, 0, 0, 0.4);
--palette-dark-60: rgba(0, 0, 0, 0.6);
--palette-dark-80: rgba(0, 0, 0, 0.8);
--palette-white-10: rgba(255, 255, 255, 0.1);
--palette-white-20: rgba(255, 255, 255, 0.2);
--palette-charade: #21222c;
--palette-rock: #262833;
--palette-pale-blue: #b1b5d3;
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
@ -14,16 +33,32 @@
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
--ifm-global-border-radius: 8px;
--docusaurus-highlighted-code-line-bg: var(--palette-dark-10);
--theme-card-bg-color: #f0f1f5;
--theme-card-bg-color-hover: rgb(225, 227, 235);
--theme-card-border-color: #b7b9c3;
--theme-card-text-color: #21222c;
--theme-card-title-color: #21222c;
--theme-card-text-color: var(--palette-charade);
--theme-card-title-color: var(--palette-charade);
--theme-attention-card-bg-color: #f0f1f5;
--theme-attention-card-text-color: var(--ifm-color-white);
--theme-input-text-color: #555b88;
--theme-card-secondary-bg-color: #dde0e9;
--theme-button-primary-background-color: var(--ifm-color-primary);
--theme-button-primary-text-color: var(--ifm-color-white);
--theme-button-primary-hover-background-color: var(--ifm-color-primary-darker);
--theme-button-secondary-background-color: var(--palette-dark-10);
--theme-button-secondary-text-color: var(--palette-charade);
--theme-button-secondary-hover-background-color: var(--palette-dark-30);
--theme-button-tertiary-background-color: var(--palette-dark-30);
--theme-button-tertiary-text-color: var(--palette-dark-80);
--theme-button-tertiary-hover-background-color: var(--palette-dark-40);
--theme-input-bg-color: #f0f1f5;
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--palette-gray: #4b4e5d;
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
@ -31,13 +66,26 @@
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
--docusaurus-highlighted-code-line-bg: var(--palette-dark-30);
--theme-card-text-color: var(--ifm-color-white);
--theme-card-title-color: var(--ifm-color-white);
--theme-card-bg-color: #262833;
--theme-card-bg-color: var(--palette-rock);
--theme-card-bg-color-hover: rgb(47, 49, 63);
--theme-card-border-color: #51535f;
--theme-card-border-radius: 8px;
--theme-attention-card-bg-color: var(--palette-gray);
--theme-input-bg-color: #44475a;
--theme-input-text-color: #b1b5d3;
--theme-card-secondary-bg-color: var(--palette-charade);
--theme-button-primary-background-color: var(--ifm-color-primary);
--theme-button-primary-text-color: var(--ifm-color-white);
--theme-button-primary-hover-background-color: var(--ifm-color-primary-darker);
--theme-button-secondary-background-color: var(--ifm-color-white);
--theme-button-secondary-text-color: var(--palette-charade);
--theme-button-secondary-hover-background-color: #d9d9d9;
--theme-button-tertiary-background-color: var(--palette-white-10);
--theme-button-tertiary-text-color: var(--ifm-color-white);
--theme-button-tertiary-hover-background-color: var(--palette-white-20);
}
.internal-href [class*='iconExternalLink'] {

View File

@ -0,0 +1,39 @@
// Ejected unsafe, need to check if this changes and maintain this component
// https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx
import React from "react";
import clsx from "clsx";
import Layout from "@theme/Layout";
import BlogSidebar from "@theme/BlogSidebar";
import { ActionFooter } from "@site/src/components/ActionFooter";
import { Section } from "@site/src/components/Section";
// customized:
// - Use "col--9" for `main` if there is no toc
// - Addded subscribe and community card to the bottom
export default function BlogLayout(props) {
const {sidebar, toc, children, ...layoutProps} = props;
const hasSidebar = sidebar && sidebar.items.length > 0;
return (
<Layout {...layoutProps}>
<div className="container margin-vert--lg">
<div className="row">
<BlogSidebar sidebar={sidebar} />
<main
className={clsx("col", {
"col--7": hasSidebar,
"col--9 col--offset-1": !hasSidebar,
"col--9": !toc,
})}
itemScope
itemType="http://schema.org/Blog">
{children}
</main>
{toc && <div className="col col--2">{toc}</div>}
</div>
<Section>
<ActionFooter />
</Section>
</div>
</Layout>
);
}

View File

@ -1,7 +1,7 @@
import clsx from "clsx"
import React from "react"
import styles from "./styles.module.css"
import Link from "@docusaurus/Link"
import clsx from "clsx";
import React from "react";
import styles from "./styles.module.css";
import Link from "@docusaurus/Link";
// type Skin = "primary" | "secondary"
// type Size = "small" | "medium" | "large"

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Chip } from '../Chip';
import styles from './styles.module.css';
import { ensureTrailingSlash } from '@site/src/utils/ensureTrailingSlash.js';
import React from "react";
import { Chip } from "../Chip";
import styles from "./styles.module.css";
import { ensureTrailingSlash } from "@site/src/utils/ensureTrailingSlash.js";
export const Chips = ({ items, activeChipLabel }) => (
<div className={styles.root}>

View File

@ -1,23 +1,23 @@
import React from 'react';
import styles from './styles.module.css';
import Link from '@docusaurus/Link';
import { Chip } from '../Chip';
import { ensureTrailingSlash } from '@site/src/utils/ensureTrailingSlash.js';
import React from "react";
import styles from "./styles.module.css";
import Link from "@docusaurus/Link";
import { Chip } from "../Chip";
import { ensureTrailingSlash } from "@site/src/utils/ensureTrailingSlash.js";
export const ListItem = ({ content, belowFold }) => {
const {metadata, frontMatter} = content;
const {tags, permalink, authors, title, formattedDate} = metadata;
const tag = tags[0] ?? {};
const imageUrl = frontMatter.coverImage ?? '/img/blog_placeholder.png';
const imageUrl = frontMatter.coverImage ?? "/img/blog_placeholder.png";
const postUrl = ensureTrailingSlash(permalink);
return (
<div className={styles.root}>
<div className={styles.imageBox}>
<Link to={postUrl} className={styles.image}>
<img
loading={belowFold ? 'lazy' : 'eager'}
loading={belowFold ? "lazy" : "eager"}
src={imageUrl}
alt={`Banner for blog post with title '${title}'`}
alt={`Banner for blog post with title "${title}"`}
/>
</Link>
</div>
@ -27,8 +27,8 @@ export const ListItem = ({ content, belowFold }) => {
<Chip
label={tag.label}
permalink={tag.permalink}
skin='secondary'
size='small'
skin="secondary"
size="small"
/>
</div>

View File

@ -1,23 +1,26 @@
// Ejected unsafe, need to check if this changes and maintain this component
// https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx
import React from 'react';
import clsx from 'clsx';
import Layout from '@theme/Layout';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import React from "react";
import clsx from "clsx";
import Layout from "@theme/Layout";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import {
PageMetadata,
HtmlClassNameProvider,
ThemeClassNames,
} from '@docusaurus/theme-common';
// import BlogLayout from '@theme/BlogLayout';
import BlogListPaginator from '@theme/BlogListPaginator';
import SearchMetadata from '@theme/SearchMetadata';
// import BlogPostItems from '@theme/BlogPostItems';
import { ListItem } from './ListItem';
import { Chips } from './Chips';
import styles from './styles.module.css';
import useGlobalData from '@docusaurus/useGlobalData';
} from "@docusaurus/theme-common";
// import BlogLayout from "@theme/BlogLayout";
import BlogListPaginator from "@theme/BlogListPaginator";
import SearchMetadata from "@theme/SearchMetadata";
// import BlogPostItems from "@theme/BlogPostItems";
import { ListItem } from "./ListItem";
import { Chips } from "./Chips";
import styles from "./styles.module.css";
import useGlobalData from "@docusaurus/useGlobalData";
// customized:
// - Layout
// - Tags list page
function BlogListPageMetadata(props) {
const isTagsPage = props.tag !== undefined;
const metadata = isTagsPage ? props.listMetadata: props.metadata;
@ -25,12 +28,12 @@ function BlogListPageMetadata(props) {
siteConfig: {title: siteTitle},
} = useDocusaurusContext();
const {blogDescription, blogTitle, permalink} = metadata;
const isBlogOnlyMode = permalink === '/';
const isBlogOnlyMode = permalink === "/";
const title = isBlogOnlyMode ? siteTitle : blogTitle;
return (
<>
<PageMetadata title={title} description={blogDescription} />
<SearchMetadata tag='blog_posts_list' />
<SearchMetadata tag="blog_posts_list" />
</>
);
}
@ -41,7 +44,7 @@ function BlogListPageContent(props) {
// No official api to get all tags right now: https://github.com/facebook/docusaurus/discussions/5856
// So used customized blog plugin which extends original blog plugin
const globalData = useGlobalData();
const myPluginData = globalData['docusaurus-plugin-content-blog']['default'];
const myPluginData = globalData["docusaurus-plugin-content-blog"]["default"];
const tags = [];
for (const {items, label, permalink} of Object.values(myPluginData.tags)) {
tags.push({label, permalink});
@ -56,7 +59,7 @@ function BlogListPageContent(props) {
// <BlogPostItems items={items} />
// <BlogListPaginator metadata={metadata} />
// </BlogLayout>
<Layout title='Blog'>
<Layout title="Blog">
<main className={styles.root}>
<h2 className={styles.title}>Filter by Tag</h2>
<div className={styles.categories}>
@ -65,8 +68,8 @@ function BlogListPageContent(props) {
items={tags}
/>
</div>
<a className={styles.link} href='/'>Show all posts</a>
<h2 className={styles.title}>{isTagsPage? `${tagPageCount !== -1 ? `${tagPageCount} posts`: 'Posts'} tagged with "${props.tag.label}"`: "Blog Posts"}</h2>
<a className={styles.link} href="/">Show all posts</a>
<h2 className={styles.title}>{isTagsPage? `${tagPageCount !== -1 ? `${tagPageCount} posts`: "Posts"} tagged with "${props.tag.label}"`: "Blog Posts"}</h2>
<div className={styles.posts}>
{items.map(({ content }, i) => (
<ListItem

View File

@ -1,19 +1,19 @@
// Ejected unsafe, need to check if this changes and maintain this component
// https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx
import React from 'react';
import clsx from 'clsx';
import {HtmlClassNameProvider, ThemeClassNames} from '@docusaurus/theme-common';
import {BlogPostProvider, useBlogPost} from '@docusaurus/theme-common/internal';
import BlogLayout from '@theme/BlogLayout';
import BlogPostItem from '@theme/BlogPostItem';
import BlogPostPaginator from '@theme/BlogPostPaginator';
import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata';
import TOC from '@theme/TOC';
import styles from './styles.module.css';
import React from "react";
import clsx from "clsx";
import {HtmlClassNameProvider, ThemeClassNames} from "@docusaurus/theme-common";
import {BlogPostProvider, useBlogPost} from "@docusaurus/theme-common/internal";
import BlogLayout from "@theme/BlogLayout";
import BlogPostItem from "@theme/BlogPostItem";
import BlogPostPaginator from "@theme/BlogPostPaginator";
import BlogPostPageMetadata from "@theme/BlogPostPage/Metadata";
import TOC from "@theme/TOC";
import styles from "./styles.module.css";
// customized:
// Hide toc
// Added Cover Image to the post
// - Hide toc
// - Added Cover Image to the post
function BlogPostPageContent({sidebar, children}) {
const {metadata, toc} = useBlogPost();
const {nextItem, prevItem, frontMatter, title} = metadata;
@ -25,11 +25,12 @@ function BlogPostPageContent({sidebar, children}) {
return (
<BlogLayout
sidebar={sidebar}
toc={undefined}
>
<BlogPostItem>
{frontMatter.coverImage && <figure>
<img
alt={`Banner for blog post with title '${title}'`}
alt={`Banner for blog post with title "${title}"`}
className={clsx(styles.image, {
[styles["image--title"]]: children != null,
})}