Skip to content

Commit

Permalink
add showcase page
Browse files Browse the repository at this point in the history
  • Loading branch information
EwanLyon committed Jul 24, 2024
1 parent 698d43d commit 908fb34
Show file tree
Hide file tree
Showing 16 changed files with 1,220 additions and 2 deletions.
8 changes: 6 additions & 2 deletions packages/speedrun-theme/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,17 @@
--ifm-table-border-width: 0px;

--speeddocs-primary: #030c20;
--speeddocs-primary-rgb-values: 3, 12, 32;
--speeddocs-secondary: #14213d;
--speeddocs-secondary-rgb-values: 20, 33, 61;
--speeddocs-accent: #fca311;
--speeddocs-accent-rgb-values: 252, 163, 17;
--logo-filter: invert(1);

--ifm-navbar-background-color: var(--speeddocs-secondary);
--ifm-navbar-background-color: rgba(var(--speeddocs-secondary-rgb-values), 0.5);
--ifm-background-color: var(--speeddocs-primary) !important;
--ifm-background-surface-color: var(--speeddocs-secondary) !important;
--ifm-background-surface-color-split: 3, 12, 32; /* Used for the gradients on top of images */
/* --ifm-background-surface-color-split: 3, 12, 32; Used for the gradients on top of images */
--ifm-navbar-search-input-background-color: var(--speeddocs-primary);
--ifm-navbar-search-input-icon: url('data:image/svg+xml;utf8,<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16px" width="16px"><path d="M6.02945,10.20327a4.17382,4.17382,0,1,1,4.17382-4.17382A4.15609,4.15609,0,0,1,6.02945,10.20327Zm9.69195,4.2199L10.8989,9.59979A5.88021,5.88021,0,0,0,12.058,6.02856,6.00467,6.00467,0,1,0,9.59979,10.8989l4.82338,4.82338a.89729.89729,0,0,0,1.29912,0,.89749.89749,0,0,0-.00087-1.29909Z" /></svg>');
--ifm-footer-background-color: var(--speeddocs-secondary);
Expand Down Expand Up @@ -89,6 +92,7 @@

.navbar {
border-bottom: thin solid var(--speeddocs-accent);
backdrop-filter: saturate(180%) blur(10px);
}

footer {
Expand Down
5 changes: 5 additions & 0 deletions website/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const config: Config = {
position: "left",
label: "Resources",
},
{
position: "left",
label: "Showcase",
to: "showcase"
},
{
href: `https://github.com/${speedrunDocsConfig.github.username}/${speedrunDocsConfig.github.repository}`,
position: "right",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, { type ReactNode, type ComponentProps } from "react";
import clsx from "clsx";
import styles from "./styles.module.css";

export type SvgIconProps = ComponentProps<"svg"> & {
viewBox?: string;
size?: "inherit" | "small" | "medium" | "large";
color?: "inherit" | "primary" | "secondary" | "success" | "error" | "warning";
svgClass?: string; // Class attribute on the child
colorAttr?: string; // Applies a color attribute to the SVG element.
};

export default function FavouriteIcon(props: SvgIconProps): JSX.Element {
const { svgClass, colorAttr, children, color = "inherit", size = "medium", viewBox = "0 0 24 24", ...rest } = props;

return (
<svg
viewBox={viewBox}
color={colorAttr}
aria-hidden
className={clsx(styles.svgIcon, styles[color], styles[size], svgClass)}
{...rest}
>
<path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z" />
</svg>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

.svgIcon {
user-select: none;
width: 1em;
height: 1em;
display: inline-block;
fill: currentColor;
flex-shrink: 0;
color: inherit;
}

/* font-size */
.small {
font-size: 1.25rem;
}

.medium {
font-size: 1.5rem;
}

.large {
font-size: 2.185rem;
}

/* colours */
.primary {
color: var(--ifm-color-primary);
}

.secondary {
color: var(--ifm-color-secondary);
}

.success {
color: var(--ifm-color-success);
}

.error {
color: var(--ifm-color-error);
}

.warning {
color: var(--ifm-color-warning);
}

.inherit {
color: inherit;
}
81 changes: 81 additions & 0 deletions website/src/pages/showcase/_components/ShowcaseCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from "react";
import clsx from "clsx";
import Link from "@docusaurus/Link";
import Translate from "@docusaurus/Translate";
// import Image from '@theme/IdealImage';
import FavouriteIcon from "../FavouriteIcon/favourite-icon";
import { Tags, TagList, type TagType, type Website, type Tag } from "../../data";
import Heading from "@theme/Heading";
import Tooltip from "../ShowcaseTooltip";
import styles from "./styles.module.css";

const TagComp = React.forwardRef<HTMLLIElement, Tag>(({ label, color, description }, ref) => (
<li ref={ref} className={styles.tag} title={description}>
<span className={styles.textLabel}>{label.toLowerCase()}</span>
<span className={styles.colorLabel} style={{ backgroundColor: color }} />
</li>
));

function ShowcaseCardTag({ tags }: { tags: TagType[] }) {
const tagObjects = tags.map((tag) => ({ tag, ...Tags[tag] }));

// Keep same order for all tags
const tagObjectsSorted = tagObjects.sort((a, b) => TagList.indexOf(a.tag) - TagList.indexOf(b.tag));

return (
<>
{tagObjectsSorted.map((tagObject, index) => {
const id = `showcase_card_tag_${tagObject.tag}`;

return (
<Tooltip key={index} text={tagObject.description} anchorEl="#__docusaurus" id={id}>
<TagComp key={index} {...tagObject} />
</Tooltip>
);
})}
</>
);
}

function getCardImage(user: Website): string {
return user.preview ?? `https://slorber-api-screenshot.netlify.app/${encodeURIComponent(user.website)}/showcase`;
}

function ShowcaseCard({ user }: { user: Website }) {
const image = getCardImage(user);
return (
<li key={user.title} className="card shadow--md">
<div className={clsx("card__image", styles.showcaseCardImage)}>
<img src={image} alt={user.title} />
</div>
<div className="card__body">
<div className={clsx(styles.showcaseCardHeader)}>
<Heading as="h4" className={styles.showcaseCardTitle}>
<Link href={user.website} className={styles.showcaseCardLink}>
{user.title}
</Link>
</Heading>
{user.tags.includes("favourite") && <FavouriteIcon svgClass={styles.svgIconFavorite} size="small" />}
{user.source && (
<Link href={user.source} className={clsx("button button--secondary button--sm", styles.showcaseCardSrcBtn)}>
<Translate id="showcase.card.sourceLink">source</Translate>
</Link>
)}
</div>
<p className={styles.showcaseCardBody}>{user.description}</p>
</div>
<ul className={clsx("card__footer", styles.cardFooter)}>
<ShowcaseCardTag tags={user.tags} />
</ul>
</li>
);
}

export default React.memo(ShowcaseCard);
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

.showcaseCardImage {
overflow: hidden;
height: 150px;
border-bottom: 2px solid var(--ifm-color-emphasis-200);
}

.showcaseCardHeader {
display: flex;
align-items: center;
margin-bottom: 12px;
}

.showcaseCardTitle {
margin-bottom: 0;
flex: 1 1 auto;
}

.showcaseCardTitle a {
text-decoration: none;
background: linear-gradient(
var(--ifm-color-primary),
var(--ifm-color-primary)
)
0% 100% / 0% 1px no-repeat;
transition: background-size ease-out 200ms;
}

.showcaseCardTitle a:not(:focus):hover {
background-size: 100% 1px;
}

.showcaseCardTitle,
.showcaseCardHeader .svgIconFavorite {
margin-right: 0.25rem;
}

.showcaseCardHeader .svgIconFavorite {
color: var(--site-color-svg-icon-favorite);
}

.showcaseCardSrcBtn {
margin-left: 6px;
padding-left: 12px;
padding-right: 12px;
border: none;
}

.showcaseCardSrcBtn:focus-visible {
background-color: var(--ifm-color-secondary-dark);
}

[data-theme='dark'] .showcaseCardSrcBtn {
background-color: var(--ifm-color-emphasis-200) !important;
color: inherit;
}

[data-theme='dark'] .showcaseCardSrcBtn:hover {
background-color: var(--ifm-color-emphasis-300) !important;
}

.showcaseCardBody {
font-size: smaller;
line-height: 1.66;
}

.cardFooter {
display: flex;
flex-wrap: wrap;
}

.tag {
font-size: 0.675rem;
border: 1px solid var(--ifm-color-secondary-darkest);
cursor: default;
margin-right: 6px;
margin-bottom: 6px !important;
border-radius: 12px;
display: inline-flex;
align-items: center;
}

.tag .textLabel {
margin-left: 8px;
}

.tag .colorLabel {
width: 7px;
height: 7px;
border-radius: 50%;
margin-left: 6px;
margin-right: 6px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {useState, useEffect, useCallback} from 'react';
import clsx from 'clsx';
import {useHistory, useLocation} from '@docusaurus/router';

import {prepareUserState} from '../../index';

import styles from './styles.module.css';

export type Operator = 'OR' | 'AND';

export const OperatorQueryKey = 'operator';

export function readOperator(search: string): Operator {
return (new URLSearchParams(search).get(OperatorQueryKey) ??
'OR') as Operator;
}

export default function ShowcaseFilterToggle(): JSX.Element {
const id = 'showcase_filter_toggle';
const location = useLocation();
const history = useHistory();
const [operator, setOperator] = useState(false);
useEffect(() => {
setOperator(readOperator(location.search) === 'AND');
}, [location]);
const toggleOperator = useCallback(() => {
setOperator((o) => !o);
const searchParams = new URLSearchParams(location.search);
searchParams.delete(OperatorQueryKey);
if (!operator) {
searchParams.append(OperatorQueryKey, 'AND');
}
history.push({
...location,
search: searchParams.toString(),
state: prepareUserState(),
});
}, [operator, location, history]);

const ClearTag = () => {
history.push({
...location,
search: '',
state: prepareUserState(),
});
};

return (
<div className="row" style={{alignItems: 'center'}}>
<input
type="checkbox"
id={id}
className="screen-reader-only"
aria-label="Toggle between or and and for the tags you selected"
onChange={toggleOperator}
onKeyDown={(e) => {
if (e.key === 'Enter') {
toggleOperator();
}
}}
checked={operator}
/>
<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 */}
</label>

<button
className="button button--outline button--primary"
type="button"
onClick={() => ClearTag()}>
Clear All
</button>
</div>
);
}
Loading

0 comments on commit 908fb34

Please sign in to comment.