mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-22 11:07: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,
|
ShowcaseItem,
|
||||||
} from '@docusaurus/plugin-content-showcase';
|
} from '@docusaurus/plugin-content-showcase';
|
||||||
|
|
||||||
export function filterUsers({
|
export function filterItems({
|
||||||
users,
|
items,
|
||||||
tags,
|
tags,
|
||||||
operator,
|
operator,
|
||||||
searchName,
|
searchName,
|
||||||
}: {
|
}: {
|
||||||
users: ShowcaseItem[];
|
items: ShowcaseItem[];
|
||||||
tags: TagType[];
|
tags: TagType[];
|
||||||
operator: Operator;
|
operator: Operator;
|
||||||
searchName: string | undefined | null;
|
searchName: string | undefined | null;
|
||||||
}): ShowcaseItem[] {
|
}): ShowcaseItem[] {
|
||||||
if (searchName) {
|
if (searchName) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
users = users.filter((user) =>
|
items = items.filter((user) =>
|
||||||
user.title.toLowerCase().includes(searchName.toLowerCase()),
|
user.title.toLowerCase().includes(searchName.toLowerCase()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (tags.length === 0) {
|
if (tags.length === 0) {
|
||||||
return users;
|
return items;
|
||||||
}
|
}
|
||||||
return users.filter((user) => {
|
return items.filter((user) => {
|
||||||
if (user.tags.length === 0) {
|
if (user.tags.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -71,19 +71,19 @@ export function useOperator(): [Operator, () => void] {
|
||||||
return [operator, toggleOperator];
|
return [operator, toggleOperator];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFilteredUsers(users: ShowcaseItem[]): ShowcaseItem[] {
|
export function useFilteredItems(items: ShowcaseItem[]): ShowcaseItem[] {
|
||||||
const [tags] = useTags();
|
const [tags] = useTags();
|
||||||
const [searchName] = useSearchName() ?? [''];
|
const [searchName] = useSearchName() ?? [''];
|
||||||
const [operator] = useOperator();
|
const [operator] = useOperator();
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
filterUsers({
|
filterItems({
|
||||||
users,
|
items,
|
||||||
tags: tags as TagType[],
|
tags: tags as TagType[],
|
||||||
operator,
|
operator,
|
||||||
searchName,
|
searchName,
|
||||||
}),
|
}),
|
||||||
[users, tags, operator, searchName],
|
[items, tags, operator, searchName],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,108 +122,7 @@ export type Tag = {
|
||||||
color: string;
|
color: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Tags: {[type in TagType]: Tag} = {
|
export function sortItems(params: ShowcaseItem[]): ShowcaseItem[] {
|
||||||
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[] {
|
|
||||||
let result = params;
|
let result = params;
|
||||||
// Sort by site name
|
// Sort by site name
|
||||||
result = sortBy(result, (user) => user.title.toLowerCase());
|
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'));
|
result = sortBy(result, (user) => !user.tags.includes('favorite'));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const sortedUsers = sortUsers();
|
|
||||||
|
|
|
@ -37,17 +37,17 @@ export default async function pluginContentShowcase(
|
||||||
contentPath: path.resolve(siteDir, sitePath),
|
contentPath: path.resolve(siteDir, sitePath),
|
||||||
contentPathLocalized: getPluginI18nPath({
|
contentPathLocalized: getPluginI18nPath({
|
||||||
localizationDir,
|
localizationDir,
|
||||||
pluginName: 'docusaurus-plugin-content-pages',
|
pluginName: 'docusaurus-plugin-content-showcase',
|
||||||
pluginId: id,
|
pluginId: id,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagList = await getTagsList({
|
const {tags: validatedTags, tagkeys} = await getTagsList({
|
||||||
configTags: tags,
|
configTags: tags,
|
||||||
configPath: contentPaths.contentPath,
|
configPath: contentPaths.contentPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
const showcaseItemSchema = createShowcaseItemSchema(tagList);
|
const showcaseItemSchema = createShowcaseItemSchema(tagkeys);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'docusaurus-plugin-content-showcase',
|
name: 'docusaurus-plugin-content-showcase',
|
||||||
|
@ -80,6 +80,7 @@ export default async function pluginContentShowcase(
|
||||||
|
|
||||||
await processContentLoaded({
|
await processContentLoaded({
|
||||||
content,
|
content,
|
||||||
|
tags: validatedTags,
|
||||||
routeBasePath,
|
routeBasePath,
|
||||||
addRoute,
|
addRoute,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,16 +5,21 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* 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';
|
import type {PluginContentLoadedActions} from '@docusaurus/types';
|
||||||
|
|
||||||
export async function processContentLoaded({
|
export async function processContentLoaded({
|
||||||
content,
|
content,
|
||||||
|
tags,
|
||||||
routeBasePath,
|
routeBasePath,
|
||||||
addRoute,
|
addRoute,
|
||||||
}: {
|
}: {
|
||||||
content: ShowcaseItems;
|
content: ShowcaseItems;
|
||||||
routeBasePath: string;
|
routeBasePath: string;
|
||||||
|
tags: TagsOption;
|
||||||
addRoute: PluginContentLoadedActions['addRoute'];
|
addRoute: PluginContentLoadedActions['addRoute'];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
addRoute({
|
addRoute({
|
||||||
|
@ -22,6 +27,7 @@ export async function processContentLoaded({
|
||||||
component: '@theme/Showcase',
|
component: '@theme/Showcase',
|
||||||
props: {
|
props: {
|
||||||
items: content.items,
|
items: content.items,
|
||||||
|
tags,
|
||||||
},
|
},
|
||||||
exact: true,
|
exact: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,6 +17,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
||||||
include: ['**/*.{yml,yaml}'],
|
include: ['**/*.{yml,yaml}'],
|
||||||
// TODO exclude won't work if user pass a custom file name
|
// TODO exclude won't work if user pass a custom file name
|
||||||
exclude: [...GlobExcludeDefault, 'tags.*'],
|
exclude: [...GlobExcludeDefault, 'tags.*'],
|
||||||
|
screenshotApi: 'https://slorber-api-screenshot.netlify.app',
|
||||||
tags: 'tags.yml',
|
tags: 'tags.yml',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||||
tags: Joi.alternatives()
|
tags: Joi.alternatives()
|
||||||
.try(Joi.string().default(DEFAULT_OPTIONS.tags), tagSchema)
|
.try(Joi.string().default(DEFAULT_OPTIONS.tags), tagSchema)
|
||||||
.default(DEFAULT_OPTIONS.tags),
|
.default(DEFAULT_OPTIONS.tags),
|
||||||
|
screenshotApi: Joi.string().default(DEFAULT_OPTIONS.screenshotApi),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function validateOptions({
|
export function validateOptions({
|
||||||
|
|
|
@ -32,7 +32,7 @@ declare module '@docusaurus/plugin-content-showcase' {
|
||||||
| 'rtl';
|
| 'rtl';
|
||||||
|
|
||||||
export type TagsOption = {
|
export type TagsOption = {
|
||||||
[tagName: string]: Tag;
|
[type in TagType]: Tag;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginOptions = {
|
export type PluginOptions = {
|
||||||
|
@ -42,6 +42,7 @@ declare module '@docusaurus/plugin-content-showcase' {
|
||||||
include: string[];
|
include: string[];
|
||||||
exclude: string[];
|
exclude: string[];
|
||||||
tags: string | TagsOption;
|
tags: string | TagsOption;
|
||||||
|
screenshotApi: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShowcaseItem = {
|
export type ShowcaseItem = {
|
||||||
|
|
|
@ -9,7 +9,10 @@ import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import Yaml from 'js-yaml';
|
import Yaml from 'js-yaml';
|
||||||
import {Joi} from '@docusaurus/utils-validation';
|
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(
|
export const tagSchema = Joi.object().pattern(
|
||||||
Joi.string(),
|
Joi.string(),
|
||||||
|
@ -35,7 +38,7 @@ export async function getTagsList({
|
||||||
}: {
|
}: {
|
||||||
configTags: PluginOptions['tags'];
|
configTags: PluginOptions['tags'];
|
||||||
configPath: PluginOptions['path'];
|
configPath: PluginOptions['path'];
|
||||||
}): Promise<string[]> {
|
}): Promise<{tagkeys: string[]; tags: TagsOption}> {
|
||||||
if (typeof configTags === 'object') {
|
if (typeof configTags === 'object') {
|
||||||
const tags = tagSchema.validate(configTags);
|
const tags = tagSchema.validate(configTags);
|
||||||
if (tags.error) {
|
if (tags.error) {
|
||||||
|
@ -44,7 +47,10 @@ export async function getTagsList({
|
||||||
{cause: tags},
|
{cause: tags},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Object.keys(tags.value);
|
return {
|
||||||
|
tagkeys: Object.keys(tags.value),
|
||||||
|
tags: tags.value,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagsPath = path.resolve(configPath, configTags);
|
const tagsPath = path.resolve(configPath, configTags);
|
||||||
|
@ -61,8 +67,10 @@ export async function getTagsList({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagLabels = Object.keys(tags.value);
|
return {
|
||||||
return tagLabels;
|
tagkeys: Object.keys(tags.value),
|
||||||
|
tags: tags.value,
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to read tags file for showcase`, {cause: 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' {
|
declare module '@theme/Showcase' {
|
||||||
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
|
import type {
|
||||||
|
ShowcaseItem,
|
||||||
|
TagsOption,
|
||||||
|
} from '@docusaurus/plugin-content-showcase';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
items: ShowcaseItem[];
|
items: ShowcaseItem[];
|
||||||
|
tags: TagsOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Showcase(props: Props): JSX.Element;
|
export default function Showcase(props: Props): JSX.Element;
|
||||||
|
|
|
@ -7,16 +7,20 @@
|
||||||
|
|
||||||
import React, {type ReactNode} from 'react';
|
import React, {type ReactNode} from 'react';
|
||||||
import {useClearQueryString} from '@docusaurus/theme-common';
|
import {useClearQueryString} from '@docusaurus/theme-common';
|
||||||
|
import Translate from '@docusaurus/Translate';
|
||||||
|
|
||||||
export default function ClearAllButton(): ReactNode {
|
export default function ClearAllButton(): ReactNode {
|
||||||
const clearQueryString = useClearQueryString();
|
const clearQueryString = useClearQueryString();
|
||||||
// TODO translate
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="button button--outline button--primary"
|
className="button button--outline button--primary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => clearQueryString()}>
|
onClick={() => clearQueryString()}>
|
||||||
Clear All
|
<Translate
|
||||||
|
id="theme.Showcase.ClearAllButton.label"
|
||||||
|
description="The label for the Clear All button">
|
||||||
|
Clear All
|
||||||
|
</Translate>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import type {Props} from '@theme/Showcase/FavoriteIcon';
|
import type {Props} from '@theme/Showcase/FavoriteIcon';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,12 @@
|
||||||
import React, {useId} from 'react';
|
import React, {useId} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {useOperator} from '@docusaurus/plugin-content-showcase/client';
|
import {useOperator} from '@docusaurus/plugin-content-showcase/client';
|
||||||
|
import Translate from '@docusaurus/Translate';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
export default function OperatorButton(): JSX.Element {
|
export default function OperatorButton(): JSX.Element {
|
||||||
const id = useId();
|
const id = useId();
|
||||||
const [operator, toggleOperator] = useOperator();
|
const [operator, toggleOperator] = useOperator();
|
||||||
// TODO add translations
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<input
|
<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')}>
|
<label htmlFor={id} className={clsx(styles.checkboxLabel, 'shadow--md')}>
|
||||||
{/* eslint-disable @docusaurus/no-untranslated-text */}
|
<span className={styles.checkboxLabelOr}>
|
||||||
<span className={styles.checkboxLabelOr}>OR</span>
|
<Translate
|
||||||
<span className={styles.checkboxLabelAnd}>AND</span>
|
id="theme.Showcase.OrOperatorButton.label"
|
||||||
{/* eslint-enable @docusaurus/no-untranslated-text */}
|
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>
|
</label>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,12 +9,8 @@ import React from 'react';
|
||||||
import clsx from 'clsx';
|
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 Image from '@theme/IdealImage';
|
import {sortBy} from '@docusaurus/plugin-content-showcase/client';
|
||||||
import {
|
import {useShowcase} from '@docusaurus/theme-common/internal';
|
||||||
sortBy,
|
|
||||||
Tags,
|
|
||||||
TagList,
|
|
||||||
} from '@docusaurus/plugin-content-showcase/client';
|
|
||||||
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';
|
||||||
|
@ -26,11 +22,14 @@ function TagItem({
|
||||||
color,
|
color,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
description: string;
|
description: {
|
||||||
|
message: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
color: string;
|
color: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<li className={styles.tag} title={description}>
|
<li className={styles.tag} title={description.message}>
|
||||||
<span className={styles.textLabel}>{label.toLowerCase()}</span>
|
<span className={styles.textLabel}>{label.toLowerCase()}</span>
|
||||||
<span className={styles.colorLabel} style={{backgroundColor: color}} />
|
<span className={styles.colorLabel} style={{backgroundColor: color}} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -38,6 +37,9 @@ function TagItem({
|
||||||
}
|
}
|
||||||
|
|
||||||
function ShowcaseCardTag({tags}: {tags: TagType[]}) {
|
function ShowcaseCardTag({tags}: {tags: TagType[]}) {
|
||||||
|
const {tags: Tags} = useShowcase();
|
||||||
|
const TagList = Object.keys(Tags) as TagType[];
|
||||||
|
|
||||||
const tagObjects = tags.map((tag) => ({tag, ...Tags[tag]}));
|
const tagObjects = tags.map((tag) => ({tag, ...Tags[tag]}));
|
||||||
|
|
||||||
// Keep same order for all tags
|
// 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 (
|
return (
|
||||||
user.preview ??
|
item.preview ??
|
||||||
// TODO make it configurable
|
// TODO make it configurable
|
||||||
`https://slorber-api-screenshot.netlify.app/${encodeURIComponent(
|
`https://slorber-api-screenshot.netlify.app/${encodeURIComponent(
|
||||||
user.website,
|
item.website,
|
||||||
)}/showcase`
|
)}/showcase`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ShowcaseCard({item}: {item: ShowcaseItem}) {
|
function ShowcaseCard({item}: {item: ShowcaseItem}) {
|
||||||
console.log('ShowcaseCard user:', item);
|
|
||||||
const image = getCardImage(item);
|
const image = getCardImage(item);
|
||||||
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)}>
|
||||||
{/* TODO change back to ideal image */}
|
|
||||||
<img src={image} alt={item.title} />
|
<img src={image} alt={item.title} />
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
|
|
|
@ -9,21 +9,20 @@ import type {ReactNode} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Translate from '@docusaurus/Translate';
|
import Translate from '@docusaurus/Translate';
|
||||||
import {
|
import {
|
||||||
useFilteredUsers,
|
useFilteredItems,
|
||||||
sortUsers,
|
sortItems,
|
||||||
} from '@docusaurus/plugin-content-showcase/client';
|
} from '@docusaurus/plugin-content-showcase/client';
|
||||||
import {useShowcase} from '@docusaurus/theme-common/internal';
|
import {useShowcase} 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 ShowcaseCard from '@theme/Showcase/ShowcaseCard';
|
import ShowcaseCard from '@theme/Showcase/ShowcaseCard';
|
||||||
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
|
import type {ShowcaseItem} from '@docusaurus/plugin-content-showcase';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function HeadingNoResult() {
|
function HeadingNoResult() {
|
||||||
return (
|
return (
|
||||||
<Heading as="h2">
|
<Heading as="h2">
|
||||||
<Translate id="showcase.usersList.noResult">No result</Translate>
|
<Translate id="showcase.itemsList.noResult">No result</Translate>
|
||||||
</Heading>
|
</Heading>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +39,7 @@ function HeadingFavorites() {
|
||||||
function HeadingAllSites() {
|
function HeadingAllSites() {
|
||||||
return (
|
return (
|
||||||
<Heading as="h2">
|
<Heading as="h2">
|
||||||
<Translate id="showcase.usersList.allUsers">All sites</Translate>
|
<Translate id="showcase.itemsList.allItems">All sites</Translate>
|
||||||
</Heading>
|
</Heading>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +51,6 @@ function CardList({
|
||||||
heading?: ReactNode;
|
heading?: ReactNode;
|
||||||
items: ShowcaseItem[];
|
items: ShowcaseItem[];
|
||||||
}) {
|
}) {
|
||||||
console.log('CardList items:', items);
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
{heading}
|
{heading}
|
||||||
|
@ -76,40 +74,37 @@ function NoResultSection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ShowcaseCards(): JSX.Element {
|
export default function ShowcaseCards(): JSX.Element {
|
||||||
const users = useShowcase().items;
|
const {showcaseItems: items} = useShowcase();
|
||||||
console.log('ShowcaseCards users:', users);
|
|
||||||
|
|
||||||
const filteredUsers = useFilteredUsers(users);
|
const filteredItems = useFilteredItems(items);
|
||||||
|
|
||||||
if (filteredUsers.length === 0) {
|
if (filteredItems.length === 0) {
|
||||||
return <NoResultSection />;
|
return <NoResultSection />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedUsers = sortUsers(users);
|
const sortedItems = sortItems(items);
|
||||||
|
|
||||||
const favoriteUsers = sortedUsers.filter((user: ShowcaseItem) =>
|
const favoriteItems = sortedItems.filter((item: ShowcaseItem) =>
|
||||||
user.tags.includes('favorite'),
|
item.tags.includes('favorite'),
|
||||||
);
|
);
|
||||||
console.log('favoriteUsers:', favoriteUsers);
|
|
||||||
|
|
||||||
const otherUsers = sortedUsers.filter(
|
const otherItems = sortedItems.filter(
|
||||||
(user: ShowcaseItem) => !user.tags.includes('favorite'),
|
(item: ShowcaseItem) => !item.tags.includes('favorite'),
|
||||||
);
|
);
|
||||||
console.log('otherUsers:', otherUsers);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="margin-top--lg margin-bottom--xl">
|
<section className="margin-top--lg margin-bottom--xl">
|
||||||
{filteredUsers.length === sortedUsers.length ? (
|
{filteredItems.length === sortedItems.length ? (
|
||||||
<>
|
<>
|
||||||
<div className={styles.showcaseFavorite}>
|
<div className={styles.showcaseFavorite}>
|
||||||
<CardList heading={<HeadingFavorites />} items={favoriteUsers} />
|
<CardList heading={<HeadingFavorites />} items={favoriteItems} />
|
||||||
</div>
|
</div>
|
||||||
<div className="margin-top--lg">
|
<div className="margin-top--lg">
|
||||||
<CardList heading={<HeadingAllSites />} items={otherUsers} />
|
<CardList heading={<HeadingAllSites />} items={otherItems} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<CardList items={filteredUsers} />
|
<CardList items={filteredItems} />
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,10 +9,8 @@ import type {ReactNode, CSSProperties} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Translate from '@docusaurus/Translate';
|
import Translate from '@docusaurus/Translate';
|
||||||
import {
|
import {
|
||||||
useFilteredUsers,
|
useFilteredItems,
|
||||||
useSiteCountPlural,
|
useSiteCountPlural,
|
||||||
Tags,
|
|
||||||
TagList,
|
|
||||||
} from '@docusaurus/plugin-content-showcase/client';
|
} from '@docusaurus/plugin-content-showcase/client';
|
||||||
import {useShowcase} from '@docusaurus/theme-common/internal';
|
import {useShowcase} from '@docusaurus/theme-common/internal';
|
||||||
import FavoriteIcon from '@theme/Showcase/FavoriteIcon';
|
import FavoriteIcon from '@theme/Showcase/FavoriteIcon';
|
||||||
|
@ -21,7 +19,6 @@ import ShowcaseTagSelect from '@theme/Showcase/ShowcaseTagSelect';
|
||||||
import OperatorButton from '@theme/Showcase/OperatorButton';
|
import OperatorButton from '@theme/Showcase/OperatorButton';
|
||||||
import ClearAllButton from '@theme/Showcase/ClearAllButton';
|
import ClearAllButton from '@theme/Showcase/ClearAllButton';
|
||||||
import type {TagType} from '@docusaurus/plugin-content-showcase';
|
import type {TagType} from '@docusaurus/plugin-content-showcase';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function TagCircleIcon({color, style}: {color: string; style?: CSSProperties}) {
|
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}) {
|
function ShowcaseTagListItem({tag}: {tag: TagType}) {
|
||||||
const {label, description, color} = Tags[tag];
|
const {tags} = useShowcase();
|
||||||
|
const {label, description, color} = tags[tag];
|
||||||
return (
|
return (
|
||||||
<li className={styles.tagListItem}>
|
<li className={styles.tagListItem}>
|
||||||
<ShowcaseTagSelect
|
<ShowcaseTagSelect
|
||||||
tag={tag}
|
tag={tag}
|
||||||
label={label}
|
label={label}
|
||||||
description={description}
|
description={description.message}
|
||||||
icon={
|
icon={
|
||||||
tag === 'favorite' ? (
|
tag === 'favorite' ? (
|
||||||
<FavoriteIcon size="small" style={{marginLeft: 8}} />
|
<FavoriteIcon size="small" style={{marginLeft: 8}} />
|
||||||
|
@ -65,6 +63,8 @@ function ShowcaseTagListItem({tag}: {tag: TagType}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ShowcaseTagList() {
|
function ShowcaseTagList() {
|
||||||
|
const {tags} = useShowcase();
|
||||||
|
const TagList = Object.keys(tags) as TagType[];
|
||||||
return (
|
return (
|
||||||
<ul className={clsx('clean-list', styles.tagList)}>
|
<ul className={clsx('clean-list', styles.tagList)}>
|
||||||
{TagList.map((tag) => {
|
{TagList.map((tag) => {
|
||||||
|
@ -75,15 +75,15 @@ function ShowcaseTagList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function HeadingText() {
|
function HeadingText() {
|
||||||
const users = useShowcase().items;
|
const {showcaseItems: items} = useShowcase();
|
||||||
const filteredUsers = useFilteredUsers(users);
|
const filteredItems = useFilteredItems(items);
|
||||||
const siteCountPlural = useSiteCountPlural();
|
const siteCountPlural = useSiteCountPlural();
|
||||||
return (
|
return (
|
||||||
<div className={styles.headingText}>
|
<div className={styles.headingText}>
|
||||||
<Heading as="h2">
|
<Heading as="h2">
|
||||||
<Translate id="showcase.filters.title">Filters</Translate>
|
<Translate id="showcase.filters.title">Filters</Translate>
|
||||||
</Heading>
|
</Heading>
|
||||||
<span>{siteCountPlural(filteredUsers.length)}</span>
|
<span>{siteCountPlural(filteredItems.length)}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import React, {useCallback, type ReactNode, useId} from 'react';
|
import React, {useCallback, type ReactNode, useId} from 'react';
|
||||||
import {useTags} from '@docusaurus/plugin-content-showcase/client';
|
import {useTags} from '@docusaurus/plugin-content-showcase/client';
|
||||||
import type {Props} from '@theme/Showcase/ShowcaseTagSelect';
|
import type {Props} from '@theme/Showcase/ShowcaseTagSelect';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function useTagState(tag: string) {
|
function useTagState(tag: string) {
|
||||||
|
|
|
@ -6,12 +6,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Translate, {translate} from '@docusaurus/Translate';
|
import Translate, {translate} from '@docusaurus/Translate';
|
||||||
|
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import {ShowcaseProvider} 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';
|
||||||
import ShowcaseCards from '@theme/Showcase/ShowcaseCards';
|
import ShowcaseCards from '@theme/Showcase/ShowcaseCards';
|
||||||
import ShowcaseFilters from '@theme/Showcase/ShowcaseFilters';
|
import ShowcaseFilters from '@theme/Showcase/ShowcaseFilters';
|
||||||
|
@ -39,7 +37,7 @@ function ShowcaseHeader() {
|
||||||
|
|
||||||
export default function Showcase(props: Props): JSX.Element {
|
export default function Showcase(props: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<ShowcaseProvider content={{items: props.items}}>
|
<ShowcaseProvider content={props.items} tags={props.tags}>
|
||||||
<Layout title={TITLE} description={DESCRIPTION}>
|
<Layout title={TITLE} description={DESCRIPTION}>
|
||||||
<main className="margin-vert--lg">
|
<main className="margin-vert--lg">
|
||||||
<ShowcaseHeader />
|
<ShowcaseHeader />
|
||||||
|
|
|
@ -7,31 +7,40 @@
|
||||||
|
|
||||||
import React, {useMemo, type ReactNode, useContext} from 'react';
|
import React, {useMemo, type ReactNode, useContext} from 'react';
|
||||||
import {ReactContextError} from '../utils/reactUtils';
|
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 {
|
function useContextValue(
|
||||||
return useMemo(
|
content: ShowcaseItem[],
|
||||||
() => ({
|
tags: TagsOption,
|
||||||
items: content.items,
|
): {showcaseItems: ShowcaseItem[]; tags: TagsOption} {
|
||||||
}),
|
return useMemo(() => ({showcaseItems: content, tags}), [content, tags]);
|
||||||
[content],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ShowcaseProvider({
|
export function ShowcaseProvider({
|
||||||
children,
|
children,
|
||||||
content,
|
content,
|
||||||
|
tags,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
content: ShowcaseItems;
|
content: ShowcaseItem[];
|
||||||
|
tags: TagsOption;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const contextValue = useContextValue(content);
|
const contextValue = useContextValue(content, tags);
|
||||||
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useShowcase(): ShowcaseItems {
|
export function useShowcase(): {
|
||||||
|
showcaseItems: ShowcaseItem[];
|
||||||
|
tags: TagsOption;
|
||||||
|
} {
|
||||||
const showcase = useContext(Context);
|
const showcase = useContext(Context);
|
||||||
if (showcase === null) {
|
if (showcase === null) {
|
||||||
throw new ReactContextError('ShowcaseProvider');
|
throw new ReactContextError('ShowcaseProvider');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue