mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-21 10:37:51 +02:00
wip
This commit is contained in:
parent
5b6626bcf4
commit
8c12b1c619
16 changed files with 126 additions and 193 deletions
|
@ -19,27 +19,27 @@ import type {
|
|||
ShowcaseItem,
|
||||
} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
export function filterUsers({
|
||||
users,
|
||||
export function filterItems({
|
||||
items,
|
||||
tags,
|
||||
operator,
|
||||
searchName,
|
||||
}: {
|
||||
users: ShowcaseItem[];
|
||||
items: ShowcaseItem[];
|
||||
tags: TagType[];
|
||||
operator: Operator;
|
||||
searchName: string | undefined | null;
|
||||
}): ShowcaseItem[] {
|
||||
if (searchName) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
users = users.filter((user) =>
|
||||
items = items.filter((user) =>
|
||||
user.title.toLowerCase().includes(searchName.toLowerCase()),
|
||||
);
|
||||
}
|
||||
if (tags.length === 0) {
|
||||
return users;
|
||||
return items;
|
||||
}
|
||||
return users.filter((user) => {
|
||||
return items.filter((user) => {
|
||||
if (user.tags.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -71,19 +71,19 @@ export function useOperator(): [Operator, () => void] {
|
|||
return [operator, toggleOperator];
|
||||
}
|
||||
|
||||
export function useFilteredUsers(users: ShowcaseItem[]): ShowcaseItem[] {
|
||||
export function useFilteredItems(items: ShowcaseItem[]): ShowcaseItem[] {
|
||||
const [tags] = useTags();
|
||||
const [searchName] = useSearchName() ?? [''];
|
||||
const [operator] = useOperator();
|
||||
return useMemo(
|
||||
() =>
|
||||
filterUsers({
|
||||
users,
|
||||
filterItems({
|
||||
items,
|
||||
tags: tags as TagType[],
|
||||
operator,
|
||||
searchName,
|
||||
}),
|
||||
[users, tags, operator, searchName],
|
||||
[items, tags, operator, searchName],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -122,108 +122,7 @@ export type Tag = {
|
|||
color: string;
|
||||
};
|
||||
|
||||
export const Tags: {[type in TagType]: Tag} = {
|
||||
favorite: {
|
||||
label: translate({message: 'Favorite'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Our favorite Docusaurus sites that you must absolutely check out!',
|
||||
id: 'showcase.tag.favorite.description',
|
||||
}),
|
||||
color: '#e9669e',
|
||||
},
|
||||
|
||||
opensource: {
|
||||
label: translate({message: 'Open-Source'}),
|
||||
description: translate({
|
||||
message: 'Open-Source Docusaurus sites can be useful for inspiration!',
|
||||
id: 'showcase.tag.opensource.description',
|
||||
}),
|
||||
color: '#39ca30',
|
||||
},
|
||||
|
||||
product: {
|
||||
label: translate({message: 'Product'}),
|
||||
description: translate({
|
||||
message: 'Docusaurus sites associated to a commercial product!',
|
||||
id: 'showcase.tag.product.description',
|
||||
}),
|
||||
color: '#dfd545',
|
||||
},
|
||||
|
||||
design: {
|
||||
label: translate({message: 'Design'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Beautiful Docusaurus sites, polished and standing out from the initial template!',
|
||||
id: 'showcase.tag.design.description',
|
||||
}),
|
||||
color: '#a44fb7',
|
||||
},
|
||||
|
||||
i18n: {
|
||||
label: translate({message: 'I18n'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Translated Docusaurus sites using the internationalization support with more than 1 locale.',
|
||||
id: 'showcase.tag.i18n.description',
|
||||
}),
|
||||
color: '#127f82',
|
||||
},
|
||||
|
||||
versioning: {
|
||||
label: translate({message: 'Versioning'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Docusaurus sites using the versioning feature of the docs plugin to manage multiple versions.',
|
||||
id: 'showcase.tag.versioning.description',
|
||||
}),
|
||||
color: '#fe6829',
|
||||
},
|
||||
|
||||
large: {
|
||||
label: translate({message: 'Large'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Very large Docusaurus sites, including many more pages than the average!',
|
||||
id: 'showcase.tag.large.description',
|
||||
}),
|
||||
color: '#8c2f00',
|
||||
},
|
||||
|
||||
meta: {
|
||||
label: translate({message: 'Meta'}),
|
||||
description: translate({
|
||||
message: 'Docusaurus sites of Meta (formerly Facebook) projects',
|
||||
id: 'showcase.tag.meta.description',
|
||||
}),
|
||||
color: '#4267b2', // Facebook blue
|
||||
},
|
||||
|
||||
personal: {
|
||||
label: translate({message: 'Personal'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Personal websites, blogs and digital gardens built with Docusaurus',
|
||||
id: 'showcase.tag.personal.description',
|
||||
}),
|
||||
color: '#14cfc3',
|
||||
},
|
||||
|
||||
rtl: {
|
||||
label: translate({message: 'RTL Direction'}),
|
||||
description: translate({
|
||||
message:
|
||||
'Docusaurus sites using the right-to-left reading direction support.',
|
||||
id: 'showcase.tag.rtl.description',
|
||||
}),
|
||||
color: '#ffcfc3',
|
||||
},
|
||||
};
|
||||
|
||||
export const TagList = Object.keys(Tags) as TagType[];
|
||||
|
||||
export function sortUsers(params: ShowcaseItem[]): ShowcaseItem[] {
|
||||
export function sortItems(params: ShowcaseItem[]): ShowcaseItem[] {
|
||||
let result = params;
|
||||
// Sort by site name
|
||||
result = sortBy(result, (user) => user.title.toLowerCase());
|
||||
|
@ -231,5 +130,3 @@ export function sortUsers(params: ShowcaseItem[]): ShowcaseItem[] {
|
|||
result = sortBy(result, (user) => !user.tags.includes('favorite'));
|
||||
return result;
|
||||
}
|
||||
|
||||
// export const sortedUsers = sortUsers();
|
||||
|
|
|
@ -37,17 +37,17 @@ export default async function pluginContentShowcase(
|
|||
contentPath: path.resolve(siteDir, sitePath),
|
||||
contentPathLocalized: getPluginI18nPath({
|
||||
localizationDir,
|
||||
pluginName: 'docusaurus-plugin-content-pages',
|
||||
pluginName: 'docusaurus-plugin-content-showcase',
|
||||
pluginId: id,
|
||||
}),
|
||||
};
|
||||
|
||||
const tagList = await getTagsList({
|
||||
const {tags: validatedTags, tagkeys} = await getTagsList({
|
||||
configTags: tags,
|
||||
configPath: contentPaths.contentPath,
|
||||
});
|
||||
|
||||
const showcaseItemSchema = createShowcaseItemSchema(tagList);
|
||||
const showcaseItemSchema = createShowcaseItemSchema(tagkeys);
|
||||
|
||||
return {
|
||||
name: 'docusaurus-plugin-content-showcase',
|
||||
|
@ -80,6 +80,7 @@ export default async function pluginContentShowcase(
|
|||
|
||||
await processContentLoaded({
|
||||
content,
|
||||
tags: validatedTags,
|
||||
routeBasePath,
|
||||
addRoute,
|
||||
});
|
||||
|
|
|
@ -5,16 +5,21 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {ShowcaseItems} from '@docusaurus/plugin-content-showcase';
|
||||
import type {
|
||||
ShowcaseItems,
|
||||
TagsOption,
|
||||
} from '@docusaurus/plugin-content-showcase';
|
||||
import type {PluginContentLoadedActions} from '@docusaurus/types';
|
||||
|
||||
export async function processContentLoaded({
|
||||
content,
|
||||
tags,
|
||||
routeBasePath,
|
||||
addRoute,
|
||||
}: {
|
||||
content: ShowcaseItems;
|
||||
routeBasePath: string;
|
||||
tags: TagsOption;
|
||||
addRoute: PluginContentLoadedActions['addRoute'];
|
||||
}): Promise<void> {
|
||||
addRoute({
|
||||
|
@ -22,6 +27,7 @@ export async function processContentLoaded({
|
|||
component: '@theme/Showcase',
|
||||
props: {
|
||||
items: content.items,
|
||||
tags,
|
||||
},
|
||||
exact: true,
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|||
include: ['**/*.{yml,yaml}'],
|
||||
// TODO exclude won't work if user pass a custom file name
|
||||
exclude: [...GlobExcludeDefault, 'tags.*'],
|
||||
screenshotApi: 'https://slorber-api-screenshot.netlify.app',
|
||||
tags: 'tags.yml',
|
||||
};
|
||||
|
||||
|
@ -28,6 +29,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
|||
tags: Joi.alternatives()
|
||||
.try(Joi.string().default(DEFAULT_OPTIONS.tags), tagSchema)
|
||||
.default(DEFAULT_OPTIONS.tags),
|
||||
screenshotApi: Joi.string().default(DEFAULT_OPTIONS.screenshotApi),
|
||||
});
|
||||
|
||||
export function validateOptions({
|
||||
|
|
|
@ -32,7 +32,7 @@ declare module '@docusaurus/plugin-content-showcase' {
|
|||
| 'rtl';
|
||||
|
||||
export type TagsOption = {
|
||||
[tagName: string]: Tag;
|
||||
[type in TagType]: Tag;
|
||||
};
|
||||
|
||||
export type PluginOptions = {
|
||||
|
@ -42,6 +42,7 @@ declare module '@docusaurus/plugin-content-showcase' {
|
|||
include: string[];
|
||||
exclude: string[];
|
||||
tags: string | TagsOption;
|
||||
screenshotApi: string;
|
||||
};
|
||||
|
||||
export type ShowcaseItem = {
|
||||
|
|
|
@ -9,7 +9,10 @@ import fs from 'fs-extra';
|
|||
import path from 'path';
|
||||
import Yaml from 'js-yaml';
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-showcase';
|
||||
import type {
|
||||
PluginOptions,
|
||||
TagsOption,
|
||||
} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
export const tagSchema = Joi.object().pattern(
|
||||
Joi.string(),
|
||||
|
@ -35,7 +38,7 @@ export async function getTagsList({
|
|||
}: {
|
||||
configTags: PluginOptions['tags'];
|
||||
configPath: PluginOptions['path'];
|
||||
}): Promise<string[]> {
|
||||
}): Promise<{tagkeys: string[]; tags: TagsOption}> {
|
||||
if (typeof configTags === 'object') {
|
||||
const tags = tagSchema.validate(configTags);
|
||||
if (tags.error) {
|
||||
|
@ -44,7 +47,10 @@ export async function getTagsList({
|
|||
{cause: tags},
|
||||
);
|
||||
}
|
||||
return Object.keys(tags.value);
|
||||
return {
|
||||
tagkeys: Object.keys(tags.value),
|
||||
tags: tags.value,
|
||||
};
|
||||
}
|
||||
|
||||
const tagsPath = path.resolve(configPath, configTags);
|
||||
|
@ -61,8 +67,10 @@ export async function getTagsList({
|
|||
);
|
||||
}
|
||||
|
||||
const tagLabels = Object.keys(tags.value);
|
||||
return tagLabels;
|
||||
return {
|
||||
tagkeys: Object.keys(tags.value),
|
||||
tags: tags.value,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to read tags file for showcase`, {cause: error});
|
||||
}
|
||||
|
|
|
@ -248,10 +248,14 @@ declare module '@theme/BlogPostItems' {
|
|||
}
|
||||
|
||||
declare module '@theme/Showcase' {
|
||||
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
|
||||
import type {
|
||||
ShowcaseItem,
|
||||
TagsOption,
|
||||
} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
export type Props = {
|
||||
items: ShowcaseItem[];
|
||||
tags: TagsOption;
|
||||
};
|
||||
|
||||
export default function Showcase(props: Props): JSX.Element;
|
||||
|
|
|
@ -7,16 +7,20 @@
|
|||
|
||||
import React, {type ReactNode} from 'react';
|
||||
import {useClearQueryString} from '@docusaurus/theme-common';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
|
||||
export default function ClearAllButton(): ReactNode {
|
||||
const clearQueryString = useClearQueryString();
|
||||
// TODO translate
|
||||
return (
|
||||
<button
|
||||
className="button button--outline button--primary"
|
||||
type="button"
|
||||
onClick={() => clearQueryString()}>
|
||||
<Translate
|
||||
id="theme.Showcase.ClearAllButton.label"
|
||||
description="The label for the Clear All button">
|
||||
Clear All
|
||||
</Translate>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import type {Props} from '@theme/Showcase/FavoriteIcon';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
|
|
|
@ -8,13 +8,12 @@
|
|||
import React, {useId} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {useOperator} from '@docusaurus/plugin-content-showcase/client';
|
||||
|
||||
import Translate from '@docusaurus/Translate';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function OperatorButton(): JSX.Element {
|
||||
const id = useId();
|
||||
const [operator, toggleOperator] = useOperator();
|
||||
// TODO add translations
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
|
@ -30,11 +29,22 @@ export default function OperatorButton(): JSX.Element {
|
|||
}
|
||||
}}
|
||||
/>
|
||||
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
||||
<label htmlFor={id} className={clsx(styles.checkboxLabel, 'shadow--md')}>
|
||||
{/* eslint-disable @docusaurus/no-untranslated-text */}
|
||||
<span className={styles.checkboxLabelOr}>OR</span>
|
||||
<span className={styles.checkboxLabelAnd}>AND</span>
|
||||
{/* eslint-enable @docusaurus/no-untranslated-text */}
|
||||
<span className={styles.checkboxLabelOr}>
|
||||
<Translate
|
||||
id="theme.Showcase.OrOperatorButton.label"
|
||||
description="The label for the OR operator button">
|
||||
OR
|
||||
</Translate>
|
||||
</span>
|
||||
<span className={styles.checkboxLabelAnd}>
|
||||
<Translate
|
||||
id="theme.Showcase.AndOperatorButton.label"
|
||||
description="The label for the AND operator button">
|
||||
AND
|
||||
</Translate>
|
||||
</span>
|
||||
</label>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -9,12 +9,8 @@ import React from 'react';
|
|||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
// import Image from '@theme/IdealImage';
|
||||
import {
|
||||
sortBy,
|
||||
Tags,
|
||||
TagList,
|
||||
} from '@docusaurus/plugin-content-showcase/client';
|
||||
import {sortBy} from '@docusaurus/plugin-content-showcase/client';
|
||||
import {useShowcase} from '@docusaurus/theme-common/internal';
|
||||
import Heading from '@theme/Heading';
|
||||
import FavoriteIcon from '@theme/Showcase/FavoriteIcon';
|
||||
import type {ShowcaseItem, TagType} from '@docusaurus/plugin-content-showcase';
|
||||
|
@ -26,11 +22,14 @@ function TagItem({
|
|||
color,
|
||||
}: {
|
||||
label: string;
|
||||
description: string;
|
||||
description: {
|
||||
message: string;
|
||||
id: string;
|
||||
};
|
||||
color: string;
|
||||
}) {
|
||||
return (
|
||||
<li className={styles.tag} title={description}>
|
||||
<li className={styles.tag} title={description.message}>
|
||||
<span className={styles.textLabel}>{label.toLowerCase()}</span>
|
||||
<span className={styles.colorLabel} style={{backgroundColor: color}} />
|
||||
</li>
|
||||
|
@ -38,6 +37,9 @@ function TagItem({
|
|||
}
|
||||
|
||||
function ShowcaseCardTag({tags}: {tags: TagType[]}) {
|
||||
const {tags: Tags} = useShowcase();
|
||||
const TagList = Object.keys(Tags) as TagType[];
|
||||
|
||||
const tagObjects = tags.map((tag) => ({tag, ...Tags[tag]}));
|
||||
|
||||
// Keep same order for all tags
|
||||
|
@ -54,23 +56,21 @@ function ShowcaseCardTag({tags}: {tags: TagType[]}) {
|
|||
);
|
||||
}
|
||||
|
||||
function getCardImage(user: ShowcaseItem): string {
|
||||
function getCardImage(item: ShowcaseItem): string {
|
||||
return (
|
||||
user.preview ??
|
||||
item.preview ??
|
||||
// TODO make it configurable
|
||||
`https://slorber-api-screenshot.netlify.app/${encodeURIComponent(
|
||||
user.website,
|
||||
item.website,
|
||||
)}/showcase`
|
||||
);
|
||||
}
|
||||
|
||||
function ShowcaseCard({item}: {item: ShowcaseItem}) {
|
||||
console.log('ShowcaseCard user:', item);
|
||||
const image = getCardImage(item);
|
||||
return (
|
||||
<li key={item.title} className="card shadow--md">
|
||||
<div className={clsx('card__image', styles.showcaseCardImage)}>
|
||||
{/* TODO change back to ideal image */}
|
||||
<img src={image} alt={item.title} />
|
||||
</div>
|
||||
<div className="card__body">
|
||||
|
|
|
@ -9,21 +9,20 @@ import type {ReactNode} from 'react';
|
|||
import clsx from 'clsx';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
import {
|
||||
useFilteredUsers,
|
||||
sortUsers,
|
||||
useFilteredItems,
|
||||
sortItems,
|
||||
} from '@docusaurus/plugin-content-showcase/client';
|
||||
import {useShowcase} from '@docusaurus/theme-common/internal';
|
||||
import Heading from '@theme/Heading';
|
||||
import FavoriteIcon from '@theme/Showcase/FavoriteIcon';
|
||||
import ShowcaseCard from '@theme/Showcase/ShowcaseCard';
|
||||
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function HeadingNoResult() {
|
||||
return (
|
||||
<Heading as="h2">
|
||||
<Translate id="showcase.usersList.noResult">No result</Translate>
|
||||
<Translate id="showcase.itemsList.noResult">No result</Translate>
|
||||
</Heading>
|
||||
);
|
||||
}
|
||||
|
@ -40,7 +39,7 @@ function HeadingFavorites() {
|
|||
function HeadingAllSites() {
|
||||
return (
|
||||
<Heading as="h2">
|
||||
<Translate id="showcase.usersList.allUsers">All sites</Translate>
|
||||
<Translate id="showcase.itemsList.allItems">All sites</Translate>
|
||||
</Heading>
|
||||
);
|
||||
}
|
||||
|
@ -52,7 +51,6 @@ function CardList({
|
|||
heading?: ReactNode;
|
||||
items: ShowcaseItem[];
|
||||
}) {
|
||||
console.log('CardList items:', items);
|
||||
return (
|
||||
<div className="container">
|
||||
{heading}
|
||||
|
@ -76,40 +74,37 @@ function NoResultSection() {
|
|||
}
|
||||
|
||||
export default function ShowcaseCards(): JSX.Element {
|
||||
const users = useShowcase().items;
|
||||
console.log('ShowcaseCards users:', users);
|
||||
const {showcaseItems: items} = useShowcase();
|
||||
|
||||
const filteredUsers = useFilteredUsers(users);
|
||||
const filteredItems = useFilteredItems(items);
|
||||
|
||||
if (filteredUsers.length === 0) {
|
||||
if (filteredItems.length === 0) {
|
||||
return <NoResultSection />;
|
||||
}
|
||||
|
||||
const sortedUsers = sortUsers(users);
|
||||
const sortedItems = sortItems(items);
|
||||
|
||||
const favoriteUsers = sortedUsers.filter((user: ShowcaseItem) =>
|
||||
user.tags.includes('favorite'),
|
||||
const favoriteItems = sortedItems.filter((item: ShowcaseItem) =>
|
||||
item.tags.includes('favorite'),
|
||||
);
|
||||
console.log('favoriteUsers:', favoriteUsers);
|
||||
|
||||
const otherUsers = sortedUsers.filter(
|
||||
(user: ShowcaseItem) => !user.tags.includes('favorite'),
|
||||
const otherItems = sortedItems.filter(
|
||||
(item: ShowcaseItem) => !item.tags.includes('favorite'),
|
||||
);
|
||||
console.log('otherUsers:', otherUsers);
|
||||
|
||||
return (
|
||||
<section className="margin-top--lg margin-bottom--xl">
|
||||
{filteredUsers.length === sortedUsers.length ? (
|
||||
{filteredItems.length === sortedItems.length ? (
|
||||
<>
|
||||
<div className={styles.showcaseFavorite}>
|
||||
<CardList heading={<HeadingFavorites />} items={favoriteUsers} />
|
||||
<CardList heading={<HeadingFavorites />} items={favoriteItems} />
|
||||
</div>
|
||||
<div className="margin-top--lg">
|
||||
<CardList heading={<HeadingAllSites />} items={otherUsers} />
|
||||
<CardList heading={<HeadingAllSites />} items={otherItems} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<CardList items={filteredUsers} />
|
||||
<CardList items={filteredItems} />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -9,10 +9,8 @@ import type {ReactNode, CSSProperties} from 'react';
|
|||
import clsx from 'clsx';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
import {
|
||||
useFilteredUsers,
|
||||
useFilteredItems,
|
||||
useSiteCountPlural,
|
||||
Tags,
|
||||
TagList,
|
||||
} from '@docusaurus/plugin-content-showcase/client';
|
||||
import {useShowcase} from '@docusaurus/theme-common/internal';
|
||||
import FavoriteIcon from '@theme/Showcase/FavoriteIcon';
|
||||
|
@ -21,7 +19,6 @@ import ShowcaseTagSelect from '@theme/Showcase/ShowcaseTagSelect';
|
|||
import OperatorButton from '@theme/Showcase/OperatorButton';
|
||||
import ClearAllButton from '@theme/Showcase/ClearAllButton';
|
||||
import type {TagType} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function TagCircleIcon({color, style}: {color: string; style?: CSSProperties}) {
|
||||
|
@ -39,13 +36,14 @@ function TagCircleIcon({color, style}: {color: string; style?: CSSProperties}) {
|
|||
}
|
||||
|
||||
function ShowcaseTagListItem({tag}: {tag: TagType}) {
|
||||
const {label, description, color} = Tags[tag];
|
||||
const {tags} = useShowcase();
|
||||
const {label, description, color} = tags[tag];
|
||||
return (
|
||||
<li className={styles.tagListItem}>
|
||||
<ShowcaseTagSelect
|
||||
tag={tag}
|
||||
label={label}
|
||||
description={description}
|
||||
description={description.message}
|
||||
icon={
|
||||
tag === 'favorite' ? (
|
||||
<FavoriteIcon size="small" style={{marginLeft: 8}} />
|
||||
|
@ -65,6 +63,8 @@ function ShowcaseTagListItem({tag}: {tag: TagType}) {
|
|||
}
|
||||
|
||||
function ShowcaseTagList() {
|
||||
const {tags} = useShowcase();
|
||||
const TagList = Object.keys(tags) as TagType[];
|
||||
return (
|
||||
<ul className={clsx('clean-list', styles.tagList)}>
|
||||
{TagList.map((tag) => {
|
||||
|
@ -75,15 +75,15 @@ function ShowcaseTagList() {
|
|||
}
|
||||
|
||||
function HeadingText() {
|
||||
const users = useShowcase().items;
|
||||
const filteredUsers = useFilteredUsers(users);
|
||||
const {showcaseItems: items} = useShowcase();
|
||||
const filteredItems = useFilteredItems(items);
|
||||
const siteCountPlural = useSiteCountPlural();
|
||||
return (
|
||||
<div className={styles.headingText}>
|
||||
<Heading as="h2">
|
||||
<Translate id="showcase.filters.title">Filters</Translate>
|
||||
</Heading>
|
||||
<span>{siteCountPlural(filteredUsers.length)}</span>
|
||||
<span>{siteCountPlural(filteredItems.length)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import React, {useCallback, type ReactNode, useId} from 'react';
|
||||
import {useTags} from '@docusaurus/plugin-content-showcase/client';
|
||||
import type {Props} from '@theme/Showcase/ShowcaseTagSelect';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function useTagState(tag: string) {
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
*/
|
||||
|
||||
import Translate, {translate} from '@docusaurus/Translate';
|
||||
|
||||
import Link from '@docusaurus/Link';
|
||||
import {ShowcaseProvider} from '@docusaurus/theme-common/internal';
|
||||
import Layout from '@theme/Layout';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
import ShowcaseSearchBar from '@theme/Showcase/ShowcaseSearchBar';
|
||||
import ShowcaseCards from '@theme/Showcase/ShowcaseCards';
|
||||
import ShowcaseFilters from '@theme/Showcase/ShowcaseFilters';
|
||||
|
@ -39,7 +37,7 @@ function ShowcaseHeader() {
|
|||
|
||||
export default function Showcase(props: Props): JSX.Element {
|
||||
return (
|
||||
<ShowcaseProvider content={{items: props.items}}>
|
||||
<ShowcaseProvider content={props.items} tags={props.tags}>
|
||||
<Layout title={TITLE} description={DESCRIPTION}>
|
||||
<main className="margin-vert--lg">
|
||||
<ShowcaseHeader />
|
||||
|
|
|
@ -7,31 +7,40 @@
|
|||
|
||||
import React, {useMemo, type ReactNode, useContext} from 'react';
|
||||
import {ReactContextError} from '../utils/reactUtils';
|
||||
import type {ShowcaseItems} from '@docusaurus/plugin-content-showcase';
|
||||
import type {
|
||||
ShowcaseItem,
|
||||
TagsOption,
|
||||
} from '@docusaurus/plugin-content-showcase';
|
||||
|
||||
const Context = React.createContext<ShowcaseItems | null>(null);
|
||||
const Context = React.createContext<{
|
||||
showcaseItems: ShowcaseItem[];
|
||||
tags: TagsOption;
|
||||
} | null>(null);
|
||||
|
||||
function useContextValue(content: ShowcaseItems): ShowcaseItems {
|
||||
return useMemo(
|
||||
() => ({
|
||||
items: content.items,
|
||||
}),
|
||||
[content],
|
||||
);
|
||||
function useContextValue(
|
||||
content: ShowcaseItem[],
|
||||
tags: TagsOption,
|
||||
): {showcaseItems: ShowcaseItem[]; tags: TagsOption} {
|
||||
return useMemo(() => ({showcaseItems: content, tags}), [content, tags]);
|
||||
}
|
||||
|
||||
export function ShowcaseProvider({
|
||||
children,
|
||||
content,
|
||||
tags,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
content: ShowcaseItems;
|
||||
content: ShowcaseItem[];
|
||||
tags: TagsOption;
|
||||
}): JSX.Element {
|
||||
const contextValue = useContextValue(content);
|
||||
const contextValue = useContextValue(content, tags);
|
||||
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
||||
}
|
||||
|
||||
export function useShowcase(): ShowcaseItems {
|
||||
export function useShowcase(): {
|
||||
showcaseItems: ShowcaseItem[];
|
||||
tags: TagsOption;
|
||||
} {
|
||||
const showcase = useContext(Context);
|
||||
if (showcase === null) {
|
||||
throw new ReactContextError('ShowcaseProvider');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue