screenshot api

This commit is contained in:
ozakione 2024-04-20 19:25:11 +02:00
parent 510539fc96
commit c8b7e6d5df
8 changed files with 108 additions and 32 deletions

View file

@ -31,7 +31,15 @@ export default async function pluginContentShowcase(
): Promise<Plugin<ShowcaseItems>> { ): Promise<Plugin<ShowcaseItems>> {
const {siteDir, localizationDir} = context; const {siteDir, localizationDir} = context;
// todo check for better naming of path: sitePath // todo check for better naming of path: sitePath
const {include, exclude, tags, routeBasePath, path: sitePath, id} = options; const {
include,
exclude,
tags,
routeBasePath,
path: sitePath,
id,
screenshotApi,
} = options;
const contentPaths: ShowcaseContentPaths = { const contentPaths: ShowcaseContentPaths = {
contentPath: path.resolve(siteDir, sitePath), contentPath: path.resolve(siteDir, sitePath),
@ -81,6 +89,7 @@ export default async function pluginContentShowcase(
await processContentLoaded({ await processContentLoaded({
content, content,
tags: validatedTags, tags: validatedTags,
screenshotApi,
routeBasePath, routeBasePath,
addRoute, addRoute,
}); });

View file

@ -15,11 +15,13 @@ export async function processContentLoaded({
content, content,
tags, tags,
routeBasePath, routeBasePath,
screenshotApi,
addRoute, addRoute,
}: { }: {
content: ShowcaseItems; content: ShowcaseItems;
routeBasePath: string; routeBasePath: string;
tags: TagsOption; tags: TagsOption;
screenshotApi: string;
addRoute: PluginContentLoadedActions['addRoute']; addRoute: PluginContentLoadedActions['addRoute'];
}): Promise<void> { }): Promise<void> {
addRoute({ addRoute({
@ -28,6 +30,7 @@ export async function processContentLoaded({
props: { props: {
items: content.items, items: content.items,
tags, tags,
screenshotApi,
}, },
exact: true, exact: true,
}); });

View file

@ -256,6 +256,7 @@ declare module '@theme/Showcase' {
export type Props = { export type Props = {
items: ShowcaseItem[]; items: ShowcaseItem[];
tags: TagsOption; tags: TagsOption;
screenshotApi: string;
}; };
export default function Showcase(props: Props): JSX.Element; export default function Showcase(props: Props): JSX.Element;

View file

@ -10,7 +10,10 @@ import clsx from 'clsx';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';
import Translate from '@docusaurus/Translate'; import Translate from '@docusaurus/Translate';
import {sortBy} from '@docusaurus/plugin-content-showcase/client'; import {sortBy} from '@docusaurus/plugin-content-showcase/client';
import {useShowcaseTags} from '@docusaurus/theme-common/internal'; import {
useShowcaseTags,
useShowcaseApiScreenshot,
} from '@docusaurus/theme-common/internal';
import Heading from '@theme/Heading'; import Heading from '@theme/Heading';
import FavoriteIcon from '@theme/Showcase/FavoriteIcon'; import FavoriteIcon from '@theme/Showcase/FavoriteIcon';
import type {ShowcaseItem, TagType} from '@docusaurus/plugin-content-showcase'; import type {ShowcaseItem, TagType} from '@docusaurus/plugin-content-showcase';
@ -36,6 +39,7 @@ function TagItem({
); );
} }
// TODO move tag reorder logic into hook
function ShowcaseCardTag({tags}: {tags: TagType[]}) { function ShowcaseCardTag({tags}: {tags: TagType[]}) {
const Tags = useShowcaseTags(); const Tags = useShowcaseTags();
const TagList = Object.keys(Tags) as TagType[]; const TagList = Object.keys(Tags) as TagType[];
@ -56,18 +60,13 @@ function ShowcaseCardTag({tags}: {tags: TagType[]}) {
); );
} }
function getCardImage(item: ShowcaseItem): string { function getCardImage(item: ShowcaseItem, api: string): string {
return ( return item.preview ?? `${api}/${encodeURIComponent(item.website)}/showcase`;
item.preview ??
// TODO make it configurable
`https://slorber-api-screenshot.netlify.app/${encodeURIComponent(
item.website,
)}/showcase`
);
} }
function ShowcaseCard({item}: {item: ShowcaseItem}) { function ShowcaseCard({item}: {item: ShowcaseItem}) {
const image = getCardImage(item); const api = useShowcaseApiScreenshot();
const image = getCardImage(item, api);
return ( return (
<li key={item.title} className="card shadow--md"> <li key={item.title} className="card shadow--md">
<div className={clsx('card__image', styles.showcaseCardImage)}> <div className={clsx('card__image', styles.showcaseCardImage)}>

View file

@ -7,7 +7,7 @@
import Translate, {translate} from '@docusaurus/Translate'; import Translate, {translate} from '@docusaurus/Translate';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';
import {TagsProvider, ItemsProvider} from '@docusaurus/theme-common/internal'; import {ShowcaseProvider} from '@docusaurus/theme-common/internal';
import Layout from '@theme/Layout'; import Layout from '@theme/Layout';
import Heading from '@theme/Heading'; import Heading from '@theme/Heading';
import ShowcaseSearchBar from '@theme/Showcase/ShowcaseSearchBar'; import ShowcaseSearchBar from '@theme/Showcase/ShowcaseSearchBar';
@ -37,21 +37,22 @@ function ShowcaseHeader() {
export default function Showcase(props: Props): JSX.Element { export default function Showcase(props: Props): JSX.Element {
return ( return (
<ItemsProvider items={props.items}> <ShowcaseProvider
<TagsProvider tags={props.tags}> items={props.items}
<Layout title={TITLE} description={DESCRIPTION}> tags={props.tags}
<main className="margin-vert--lg"> screenshotApi={props.screenshotApi}>
<ShowcaseHeader /> <Layout title={TITLE} description={DESCRIPTION}>
<ShowcaseFilters /> <main className="margin-vert--lg">
<div <ShowcaseHeader />
style={{display: 'flex', marginLeft: 'auto'}} <ShowcaseFilters />
className="container"> <div
<ShowcaseSearchBar /> style={{display: 'flex', marginLeft: 'auto'}}
</div> className="container">
<ShowcaseCards /> <ShowcaseSearchBar />
</main> </div>
</Layout> <ShowcaseCards />
</TagsProvider> </main>
</ItemsProvider> </Layout>
</ShowcaseProvider>
); );
} }

View file

@ -12,18 +12,31 @@ import type {
TagsOption, TagsOption,
} from '@docusaurus/plugin-content-showcase'; } from '@docusaurus/plugin-content-showcase';
// duplicated from theme classic showcase
type Props = {
items: ShowcaseItem[];
tags: TagsOption;
screenshotApi: string;
children: ReactNode;
};
const ItemsContext = React.createContext<ShowcaseItem[] | null>(null); const ItemsContext = React.createContext<ShowcaseItem[] | null>(null);
const ApiContext = React.createContext<string | null>(null);
const TagsContext = React.createContext<TagsOption | null>(null); const TagsContext = React.createContext<TagsOption | null>(null);
function useItemsContextValue(content: ShowcaseItem[]): ShowcaseItem[] { function useItemsContextValue(content: ShowcaseItem[]): ShowcaseItem[] {
return useMemo(() => content, [content]); return useMemo(() => content, [content]);
} }
function useApiScreenshotContextValue(content: string): string {
return useMemo(() => content, [content]);
}
function useTagsContextValue(tags: TagsOption): TagsOption { function useTagsContextValue(tags: TagsOption): TagsOption {
return useMemo(() => tags, [tags]); return useMemo(() => tags, [tags]);
} }
export function ItemsProvider({ function ItemsProvider({
children, children,
items, items,
}: { }: {
@ -38,7 +51,20 @@ export function ItemsProvider({
); );
} }
export function TagsProvider({ function ApiScreenshotProvider({
children,
api,
}: {
children: ReactNode;
api: string;
}): JSX.Element {
const contextValue = useApiScreenshotContextValue(api);
return (
<ApiContext.Provider value={contextValue}>{children}</ApiContext.Provider>
);
}
function TagsProvider({
children, children,
tags, tags,
}: { }: {
@ -51,6 +77,23 @@ export function TagsProvider({
); );
} }
export function ShowcaseProvider({
items,
tags,
screenshotApi,
children,
}: Props): JSX.Element {
return (
<ItemsProvider items={items}>
<TagsProvider tags={tags}>
<ApiScreenshotProvider api={screenshotApi}>
{children}
</ApiScreenshotProvider>
</TagsProvider>
</ItemsProvider>
);
}
export function useShowcaseItems(): ShowcaseItem[] { export function useShowcaseItems(): ShowcaseItem[] {
const showcaseItems = useContext(ItemsContext); const showcaseItems = useContext(ItemsContext);
if (showcaseItems === null) { if (showcaseItems === null) {
@ -59,6 +102,14 @@ export function useShowcaseItems(): ShowcaseItem[] {
return showcaseItems; return showcaseItems;
} }
export function useShowcaseApiScreenshot(): string {
const showcaseItems = useContext(ApiContext);
if (showcaseItems === null) {
throw new ReactContextError('ItemsProvider');
}
return showcaseItems;
}
export function useShowcaseTags(): TagsOption { export function useShowcaseTags(): TagsOption {
const tags = useContext(TagsContext); const tags = useContext(TagsContext);
if (tags === null) { if (tags === null) {

View file

@ -27,10 +27,10 @@ export {DocsSidebarProvider, useDocsSidebar} from './contexts/docsSidebar';
export {DocProvider, useDoc, type DocContextValue} from './contexts/doc'; export {DocProvider, useDoc, type DocContextValue} from './contexts/doc';
export { export {
ItemsProvider,
TagsProvider,
useShowcaseItems, useShowcaseItems,
useShowcaseTags, useShowcaseTags,
useShowcaseApiScreenshot,
ShowcaseProvider,
} from './contexts/showcase'; } from './contexts/showcase';
export { export {
BlogPostProvider, BlogPostProvider,

12
website/showcase/tags.yml Normal file
View file

@ -0,0 +1,12 @@
favorite:
label: 'Favorite'
description:
message: 'Our favorite Docusaurus sites that you must absolutely check out!'
id: 'showcase.tag.favorite.description'
color: '#e9669e'
opensource:
label: 'Open Source'
description:
message: 'These sites are open source, so you can learn from them!'
id: 'showcase.tag.opensource.description'
color: '#f6993f'