mirror of
https://github.com/wavetermdev/docusaurus-og.git
synced 2026-08-05 13:46:35 -07:00
feat: blog theme
This commit is contained in:
@@ -2,4 +2,5 @@
|
||||
font-size: var(--lsd-#{$variant}-fontSize) !important;
|
||||
line-height: var(--lsd-#{$variant}-lineHeight) !important;
|
||||
font-family: var(--lsd-typography-generic-font-family) !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
@use './utils';
|
||||
@use './lsd';
|
||||
|
||||
.blog-wrapper {
|
||||
}
|
||||
|
||||
.blog-wrapper > .container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding-inline: 0;
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.blog-wrapper > .container > .row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.blog-wrapper main > header h1 {
|
||||
@include lsd.typography('h3');
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.blog-wrapper > .container > .row > aside {
|
||||
max-width: unset;
|
||||
|
||||
> nav {
|
||||
top: var(--ifm-navbar-height);
|
||||
padding-top: 16px;
|
||||
|
||||
> div:first-child {
|
||||
@include lsd.typography('subtitle2');
|
||||
border-left: 1px solid transparent;
|
||||
padding: 4px 0 4px 15px;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
& > ul {
|
||||
li {
|
||||
margin: 0;
|
||||
|
||||
a {
|
||||
@include lsd.typography('subtitle2');
|
||||
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: 4px 0 4px 2rem;
|
||||
border-left: 1px solid transparent;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
a[class*='sidebarItemLinkActive_'] {
|
||||
border-color: rgb(var(--lsd-border-primary));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blog-wrapper > .container > .row > main {
|
||||
margin-top: 16px;
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.blog-wrapper article {
|
||||
header {
|
||||
h1,
|
||||
h2 {
|
||||
@include lsd.typography('h3');
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-wrapper--index > .container > .row > aside {
|
||||
> nav {
|
||||
> div:first-child {
|
||||
border-color: rgb(var(--lsd-border-primary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blog-wrapper {
|
||||
main {
|
||||
article {
|
||||
margin-bottom: 40px !important;
|
||||
padding-bottom: 39px;
|
||||
|
||||
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// grid
|
||||
@include utils.responsive('lg', 'up') {
|
||||
.blog-wrapper > .container > .row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(24, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.blog-wrapper > .container > .row > aside {
|
||||
grid-column: 1 / 5;
|
||||
}
|
||||
|
||||
.blog-wrapper > .container > .row > main {
|
||||
grid-column: 6 / 20;
|
||||
}
|
||||
|
||||
.blog-wrapper:not(.blog-wrapper--index) > .container > .row {
|
||||
> div:last-child {
|
||||
grid-column: 21 / 25;
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.blog-wrapper {
|
||||
main {
|
||||
padding-inline: var(--content-padding);
|
||||
}
|
||||
}
|
||||
|
||||
.blog-wrapper article footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.read-more-link {
|
||||
margin-top: 2rem;
|
||||
|
||||
&,
|
||||
button {
|
||||
width: 100%;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blog-archive-page {
|
||||
.main-wrapper {
|
||||
padding: 2rem var(--content-padding);
|
||||
|
||||
header {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--ifm-hero-text-color);
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
padding: 0 !important;
|
||||
|
||||
h1 {
|
||||
@include lsd.typography('h3');
|
||||
}
|
||||
|
||||
p {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main > section {
|
||||
margin-top: 0 !important;
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.container > .row {
|
||||
margin-top: 64px;
|
||||
|
||||
> .col {
|
||||
max-width: unset;
|
||||
padding: 0;
|
||||
margin: 0 !important;
|
||||
|
||||
h3 {
|
||||
@include lsd.typography('h6');
|
||||
}
|
||||
|
||||
a {
|
||||
@include lsd.typography('body1');
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('md', 'down') {
|
||||
.main-wrapper {
|
||||
main > section {
|
||||
.container > .row {
|
||||
margin-top: 0px;
|
||||
|
||||
.col {
|
||||
margin-top: 40px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('md', 'up') {
|
||||
.main-wrapper {
|
||||
main > section {
|
||||
.container > .row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(24, 1fr);
|
||||
gap: 64px 24px;
|
||||
|
||||
> .col {
|
||||
max-width: unset;
|
||||
padding: 0;
|
||||
margin: 0 !important;
|
||||
grid-column: span 12;
|
||||
padding-right: calc(100% / 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('xl', 'up') {
|
||||
.main-wrapper {
|
||||
main > section {
|
||||
.container > .row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(24, 1fr);
|
||||
gap: 64px 24px;
|
||||
|
||||
> .col {
|
||||
max-width: unset;
|
||||
padding: 0;
|
||||
margin: 0 !important;
|
||||
grid-column: span 8;
|
||||
padding-right: calc(100% / 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tags page
|
||||
.blog-wrapper main > section {
|
||||
article {
|
||||
h2 {
|
||||
@include lsd.typography('h3');
|
||||
}
|
||||
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
|
||||
ul {
|
||||
margin-top: 1rem;
|
||||
|
||||
li {
|
||||
a::before,
|
||||
a::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin-right: 0.5rem !important;
|
||||
|
||||
.lsd-tag {
|
||||
padding: 3px 11px;
|
||||
height: 28px;
|
||||
|
||||
> span {
|
||||
color: rgb(var(--lsd-text-primary));
|
||||
@include lsd.typography('label1');
|
||||
|
||||
> span {
|
||||
@include lsd.typography('label1');
|
||||
color: rgb(var(--lsd-text-primary));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1166,3 +1166,10 @@ table {
|
||||
height: 28px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.hero--primary {
|
||||
--ifm-hero-background-color: rgb(var(--lsd-surface-primary));
|
||||
--ifm-hero-text-color: rgb(var(--lsd-text-primary));
|
||||
}
|
||||
|
||||
@import './blog.scss';
|
||||
|
||||
@@ -7,7 +7,7 @@ import styles from './AuthorPage.module.scss'
|
||||
|
||||
export type AuthorPageProps = {
|
||||
data: {
|
||||
author: Author
|
||||
authors: Author
|
||||
docs: {
|
||||
id: string
|
||||
title: string
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import BlogArchivePage from '@docusaurus/theme-classic/lib/theme/BlogArchivePage'
|
||||
import type { WrapperProps } from '@docusaurus/types'
|
||||
import type BlogArchivePageType from '@theme/BlogArchivePage'
|
||||
import React from 'react'
|
||||
|
||||
type Props = WrapperProps<typeof BlogArchivePageType>
|
||||
|
||||
export default function BlogArchivePageWrapper(props: Props): JSX.Element {
|
||||
return (
|
||||
<div className="blog-archive-page">
|
||||
<BlogArchivePage {...props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
|
||||
import BlogLayout from '@docusaurus/theme-classic/lib/theme/BlogLayout'
|
||||
import { Props } from '@theme/BlogLayout'
|
||||
import { ensureTrailingSlash } from '../../lib/string.utils'
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
|
||||
|
||||
const useIsIndexPage = (props: Props) => {
|
||||
if (typeof window === 'undefined') return false
|
||||
|
||||
const ctx = useDocusaurusContext()
|
||||
const activePlugin = ctx.siteConfig.plugins.find(
|
||||
(plugin) =>
|
||||
plugin === '"@docusaurus/plugin-content-blog"' ||
|
||||
plugin?.[0] === '@docusaurus/plugin-content-blog',
|
||||
)
|
||||
|
||||
const routeBasePath = activePlugin?.[1]?.routeBasePath ?? '/blog'
|
||||
|
||||
return (
|
||||
ensureTrailingSlash(routeBasePath) ===
|
||||
ensureTrailingSlash(window.location.pathname)
|
||||
)
|
||||
}
|
||||
|
||||
export default function BlogLayoutWrapper(props: Props): JSX.Element {
|
||||
const { sidebar, toc, children, ...layoutProps } = props
|
||||
const isIndexPage = useIsIndexPage(props)
|
||||
|
||||
return (
|
||||
<BlogLayout
|
||||
{...props}
|
||||
wrapperClassName={clsx(
|
||||
'blog-wrapper',
|
||||
isIndexPage && 'blog-wrapper--index',
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import React from 'react'
|
||||
import Translate, { translate } from '@docusaurus/Translate'
|
||||
import Link from '@docusaurus/Link'
|
||||
import type { Props } from '@theme/BlogPostItem/Footer/ReadMoreLink'
|
||||
import { Button, Typography } from '@acid-info/lsd-react'
|
||||
|
||||
function ReadMoreLabel() {
|
||||
return (
|
||||
<Translate
|
||||
id="theme.blog.post.readMore"
|
||||
description="The label used in blog post item excerpts to link to full blog posts"
|
||||
>
|
||||
Read More
|
||||
</Translate>
|
||||
)
|
||||
}
|
||||
|
||||
export default function BlogPostItemFooterReadMoreLink(
|
||||
props: Props,
|
||||
): JSX.Element {
|
||||
const { blogPostTitle, ...linkProps } = props
|
||||
return (
|
||||
<Link
|
||||
aria-label={translate(
|
||||
{
|
||||
message: 'Read more about {title}',
|
||||
id: 'theme.blog.post.readMoreLabel',
|
||||
description:
|
||||
'The ARIA label for the link to full blog posts from excerpts',
|
||||
},
|
||||
{ title: blogPostTitle },
|
||||
)}
|
||||
{...linkProps}
|
||||
>
|
||||
<Button size="medium" variant="filled">
|
||||
<Typography variant="label1" component="span">
|
||||
<ReadMoreLabel />
|
||||
</Typography>
|
||||
</Button>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useBlogPost } from '@docusaurus/theme-common/internal'
|
||||
import EditThisPage from '@theme/EditThisPage'
|
||||
import TagsListInline from '@theme/TagsListInline'
|
||||
import ReadMoreLink from '@theme/BlogPostItem/Footer/ReadMoreLink'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
|
||||
export default function BlogPostItemFooter(): JSX.Element | null {
|
||||
const { metadata, isBlogPostPage } = useBlogPost()
|
||||
const { tags, title, editUrl, hasTruncateMarker } = metadata
|
||||
|
||||
// A post is truncated if it's in the "list view" and it has a truncate marker
|
||||
const truncatedPost = !isBlogPostPage && hasTruncateMarker
|
||||
|
||||
const tagsExists = tags.length > 0
|
||||
|
||||
const renderFooter = tagsExists || truncatedPost || editUrl
|
||||
|
||||
if (!renderFooter) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<footer
|
||||
className={clsx(
|
||||
'row docusaurus-mt-lg',
|
||||
isBlogPostPage && styles.blogPostFooterDetailsFull,
|
||||
)}
|
||||
>
|
||||
<div>{tagsExists && <TagsListInline tags={tags} />}</div>
|
||||
|
||||
{isBlogPostPage && editUrl && (
|
||||
<div>
|
||||
<EditThisPage editUrl={editUrl} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{truncatedPost && (
|
||||
<div className="read-more-link">
|
||||
<ReadMoreLink
|
||||
className={styles.readMoreLink}
|
||||
blogPostTitle={title}
|
||||
to={metadata.permalink}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.blogPostFooterDetailsFull {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.readMoreLink span {
|
||||
color: rgb(var(--lsd-text-secondary)) !important;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import Link, { type Props as LinkProps } from '@docusaurus/Link'
|
||||
|
||||
import type { Props } from '@theme/BlogPostItem/Header/Author'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
|
||||
function MaybeLink(props: LinkProps): JSX.Element {
|
||||
if (props.href) {
|
||||
return <Link {...props} />
|
||||
}
|
||||
return <>{props.children}</>
|
||||
}
|
||||
|
||||
export default function BlogPostItemHeaderAuthor({
|
||||
author,
|
||||
className,
|
||||
}: Props): JSX.Element {
|
||||
const { name, title, url, imageURL, email } = author
|
||||
console.log(author)
|
||||
const link = url || (email && `mailto:${email}`) || undefined
|
||||
return (
|
||||
<div className={clsx('avatar margin-bottom--sm', className)}>
|
||||
{name && (
|
||||
<div
|
||||
className="avatar__intro"
|
||||
itemProp="author"
|
||||
itemScope
|
||||
itemType="https://schema.org/Person"
|
||||
>
|
||||
<div className="avatar__name">
|
||||
<MaybeLink href={link} itemProp="url">
|
||||
<Typography variant="body2" itemProp="name">
|
||||
{name}
|
||||
</Typography>
|
||||
</MaybeLink>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useBlogPost } from '@docusaurus/theme-common/internal'
|
||||
import BlogPostItemHeaderAuthor from '@theme/BlogPostItem/Header/Author'
|
||||
import type { Props } from '@theme/BlogPostItem/Header/Authors'
|
||||
import styles from './styles.module.css'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
|
||||
// Component responsible for the authors layout
|
||||
export default function BlogPostItemHeaderAuthors({
|
||||
className,
|
||||
}: Props): JSX.Element | null {
|
||||
const {
|
||||
metadata: { authors },
|
||||
assets,
|
||||
} = useBlogPost()
|
||||
const authorsCount = authors.length
|
||||
if (authorsCount === 0) {
|
||||
return null
|
||||
}
|
||||
const imageOnly = authors.every(({ name }) => !name)
|
||||
return (
|
||||
<div className={clsx(className)}>
|
||||
<Typography variant="body2">{'by '}</Typography>
|
||||
{authors.map((author, idx) => (
|
||||
<div
|
||||
className={clsx(
|
||||
!imageOnly && 'col col--6',
|
||||
imageOnly ? styles.imageOnlyAuthorCol : styles.authorCol,
|
||||
)}
|
||||
key={idx}
|
||||
>
|
||||
<BlogPostItemHeaderAuthor
|
||||
author={{
|
||||
...author,
|
||||
// Handle author images using relative paths
|
||||
imageURL: assets.authorsImageUrls[idx] ?? author.imageURL,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
.authorCol {
|
||||
max-width: inherit !important;
|
||||
flex-grow: 1 !important;
|
||||
}
|
||||
|
||||
.imageOnlyAuthorRow {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
.imageOnlyAuthorCol {
|
||||
margin-left: 0.3rem;
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { usePluralForm } from '@docusaurus/theme-common'
|
||||
import { useBlogPost } from '@docusaurus/theme-common/internal'
|
||||
import { translate } from '@docusaurus/Translate'
|
||||
import type { Props } from '@theme/BlogPostItem/Header/Info'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import BlogPostItemHeaderAuthors from '@theme/BlogPostItem/Header/Authors'
|
||||
|
||||
import styles from './styles.module.scss'
|
||||
|
||||
// Very simple pluralization: probably good enough for now
|
||||
function useReadingTimePlural() {
|
||||
const { selectMessage } = usePluralForm()
|
||||
return (readingTimeFloat: number) => {
|
||||
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 ReadingTime({ readingTime }: { readingTime: number }) {
|
||||
const readingTimePlural = useReadingTimePlural()
|
||||
return <div>{readingTimePlural(readingTime)}</div>
|
||||
}
|
||||
|
||||
function Date({
|
||||
date,
|
||||
formattedDate,
|
||||
}: {
|
||||
date: string
|
||||
formattedDate: string
|
||||
}) {
|
||||
return (
|
||||
<time dateTime={date} itemProp="datePublished">
|
||||
{formattedDate}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
|
||||
export default function BlogPostItemHeaderInfo({
|
||||
className,
|
||||
}: Props): JSX.Element {
|
||||
const { metadata } = useBlogPost()
|
||||
const { date, formattedDate, readingTime } = metadata
|
||||
|
||||
return (
|
||||
<div className={clsx(styles.container, 'margin-vert--md', className)}>
|
||||
<Date date={date} formattedDate={formattedDate} />
|
||||
<BlogPostItemHeaderAuthors className={styles.authors} />
|
||||
{typeof readingTime !== 'undefined' && (
|
||||
<>
|
||||
<ReadingTime readingTime={readingTime} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
@use '../../../../css/lsd';
|
||||
|
||||
.container {
|
||||
margin-bottom: 24px !important;
|
||||
@include lsd.typography('body2');
|
||||
}
|
||||
|
||||
.container > div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.container > *:not(:last-child)::after {
|
||||
content: '•';
|
||||
display: inline-block;
|
||||
margin-inline: 0.75rem;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.authors {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.authors > div {
|
||||
display: inline-flex;
|
||||
max-width: unset;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.authors > div :global(.avatar) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.authors > div:not(:last-child)::after {
|
||||
content: ',';
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.authors :global(.avatar__name) {
|
||||
line-height: 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import BlogPostItemHeaderInfo from '@theme/BlogPostItem/Header/Info'
|
||||
import BlogPostItemHeaderTitle from '@theme/BlogPostItem/Header/Title'
|
||||
import React from 'react'
|
||||
|
||||
export default function BlogPostItemHeader(): JSX.Element {
|
||||
return (
|
||||
<header>
|
||||
<BlogPostItemHeaderTitle />
|
||||
<BlogPostItemHeaderInfo />
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import Link from '@docusaurus/Link'
|
||||
import type { Props } from '@theme/Tag'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
import { Tag as LsdTag } from '@acid-info/lsd-react'
|
||||
|
||||
export default function Tag({ permalink, label, count }: Props): JSX.Element {
|
||||
return (
|
||||
<Link
|
||||
href={permalink}
|
||||
className={clsx(count ? styles.tagWithCount : styles.tagRegular)}
|
||||
>
|
||||
<LsdTag disabled={false} size="small">
|
||||
{label}
|
||||
{count && <span>{count}</span>}
|
||||
</LsdTag>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
:root {
|
||||
--docusaurus-tag-list-border: var(--ifm-color-emphasis-300);
|
||||
}
|
||||
|
||||
.tag {
|
||||
border: 1px solid var(--docusaurus-tag-list-border);
|
||||
transition: border var(--ifm-transition-fast);
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
--docusaurus-tag-list-border: var(--ifm-link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tagRegular {
|
||||
border-radius: var(--ifm-global-radius);
|
||||
padding: 0.2rem 0.5rem 0.3rem;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.tagWithCount {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 0 0.5rem 0 1rem;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.tagWithCount::before,
|
||||
.tagWithCount::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
border: 1px solid var(--docusaurus-tag-list-border);
|
||||
transition: inherit;
|
||||
}
|
||||
|
||||
.tagWithCount::before {
|
||||
right: 100%;
|
||||
transform: translate(50%, -50%) rotate(-45deg);
|
||||
width: 1.18rem;
|
||||
height: 1.18rem;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.tagWithCount::after {
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.tagWithCount span {
|
||||
background: var(--ifm-color-secondary);
|
||||
color: var(--ifm-color-black);
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.2;
|
||||
border-radius: var(--ifm-global-radius);
|
||||
padding: 0.1rem 0.4rem;
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import Translate from '@docusaurus/Translate'
|
||||
import Tag from '@theme/Tag'
|
||||
import type { Props } from '@theme/TagsListInline'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
|
||||
export default function TagsListInline({ tags }: Props): JSX.Element {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Typography variant="body2" component="div">
|
||||
<Translate
|
||||
id="theme.tags.tagsListLabel"
|
||||
description="The label alongside a tag list"
|
||||
>
|
||||
Tags:
|
||||
</Translate>
|
||||
</Typography>
|
||||
<ul className={clsx(styles.tags, 'padding--none', 'margin-left--sm')}>
|
||||
{tags.map(({ label, permalink: tagPermalink }) => (
|
||||
<li key={tagPermalink} className={styles.tag}>
|
||||
<Tag label={label} permalink={tagPermalink} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: inline;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
margin-left: 1rem !important;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tag:not(:first-child) {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.tag a {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
Reference in New Issue
Block a user