Skip to content

Add storage finder page and relevant components #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@
"dependencies": {
"@docusaurus/core": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
"@docusaurus/theme-mermaid": "^3.7.0",
"@mdx-js/react": "^3.1.0",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-tooltip": "^1.1.8",
"clsx": "^2.1.1",
"docusaurus-lunr-search": "^3.6.0",
"lucide-react": "^0.487.0",
"lunr": "^2.3.9",
"prism-react-renderer": "^2.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"react-use": "^17.6.0"
},
"devDependencies": {
"@docusaurus/eslint-plugin": "^3.7.0",
Expand All @@ -51,7 +56,9 @@
"prettier": "^3.5.2",
"prettier-plugin-jsdoc": "^1.3.2",
"stylelint": "^16.14.1",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^37.0.0",
"stylelint-order": "^6.0.4",
"typescript": "~5.7.3",
"typescript-eslint": "^8.25.0"
},
Expand Down
14,457 changes: 0 additions & 14,457 deletions pnpm-lock.yaml

This file was deleted.

103 changes: 103 additions & 0 deletions src/components/Dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/** @author Ka Pui (August) Cheung */

import * as RadixDialog from "@radix-ui/react-dialog";
import { X } from "lucide-react";

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

export interface DialogProps {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
className?: string;
}

export function Dialog({
isOpen,
onClose,
children,
className = "",
}: DialogProps) {
return (
<RadixDialog.Root open={isOpen} onOpenChange={(open) => !open && onClose()}>
<RadixDialog.Portal>
<RadixDialog.Overlay className={styles.dialogOverlay} />
<RadixDialog.Content className={`${styles.dialog} ${className}`}>
{children}
</RadixDialog.Content>
</RadixDialog.Portal>
</RadixDialog.Root>
);
}

interface DialogContentProps {
className?: string;
children: React.ReactNode;
}

export function DialogContent({
className = "",
children,
}: DialogContentProps) {
return (
<div className={`${styles.dialogContent} ${className}`}>{children}</div>
);
}

interface DialogHeaderProps {
className?: string;
children: React.ReactNode;
onClose?: () => void;
}

export function DialogHeader({
className = "",
children,
onClose,
}: DialogHeaderProps) {
return (
<div className={`${styles.dialogHeader} ${className}`}>
<RadixDialog.Title className={`${styles.dialogTitle} margin-bottom--0`}>
{children}
</RadixDialog.Title>
{onClose && (
<RadixDialog.Close asChild>
<button
aria-label="Close"
className={`${styles.dialogCloseButton} clean-btn`}
type="button"
onClick={onClose}
>
<X size={18} />
</button>
</RadixDialog.Close>
)}
</div>
);
}

interface DialogBodyProps {
className?: string;
children: React.ReactNode;
}

export function DialogBody({ className = "", children }: DialogBodyProps) {
return (
<RadixDialog.Description className={`${styles.dialogBody} ${className}`}>
{children}
</RadixDialog.Description>
);
}

interface DialogFooterProps {
className?: string;
children: React.ReactNode;
}

export function DialogFooter({ className = "", children }: DialogFooterProps) {
return (
<div className={`${styles.dialogFooter} ${className} margin-top--md`}>
{children}
</div>
);
}
108 changes: 108 additions & 0 deletions src/components/Dialog/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/** @author Ka Pui (August) Cheung */

.dialogOverlay {
position: fixed;
z-index: var(--ifm-z-index-fixed);
display: flex;
align-items: center;
justify-content: center;
background-color: rgb(0 0 0 / 50%);
backdrop-filter: blur(2px);
animation: overlayShow var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
inset: 0;
}

.dialog {
position: fixed;
top: 50%;
left: 50%;
z-index: var(--ifm-z-index-overlay);
width: 90vw;
max-width: 500px;
max-height: 85vh;
background-color: var(--ifm-background-surface-color);
border-radius: var(--ifm-global-radius);
box-shadow: var(--ifm-global-shadow-tl);
transform: translate(-50%, -50%);
animation-name: dialog-fade-in;
animation-duration: var(--ifm-transition-fast);
animation-timing-function: var(--ifm-transition-timing-default);
animation-fill-mode: forwards;
}

.dialogContent {
display: flex;
flex-direction: column;
width: 100%;
max-height: inherit;
}

.dialogHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal);
}

.dialogTitle {
flex-grow: 1;
margin: 0;
font-weight: var(--ifm-heading-font-weight);
font-size: var(--ifm-h4-font-size);
line-height: var(--ifm-heading-line-height);
}

.dialogCloseButton {
position: static;
display: flex;
align-items: center;
justify-content: center;
padding: calc(var(--ifm-spacing-vertical) * 0.25)
calc(var(--ifm-spacing-horizontal) * 0.5);
color: var(--ifm-color-emphasis-700);
background: transparent;
border: none;
border-radius: var(--ifm-global-radius);
cursor: pointer;
}

.dialogCloseButton:hover {
color: var(--ifm-color-emphasis-900);
background-color: var(--ifm-color-emphasis-200);
}

.dialogBody {
flex: 1;
margin-bottom: 0; /** Cancels the margin added to `<p>` */
padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal);
overflow-y: auto;
}

.dialogFooter {
display: flex;
gap: var(--ifm-spacing-horizontal);
align-items: center;
justify-content: flex-end;
padding: var(--ifm-spacing-horizontal);
border-top: var(--ifm-global-border-width) solid var(--ifm-color-emphasis-300);
}

@keyframes dialog-fade-in {
from {
transform: translate(-50%, -45%);
opacity: 0;
}

to {
transform: translate(-50%, -50%);
opacity: 1;
}
}

@media (width <= 576px) {
.dialog {
width: 95%;
max-width: none;
}
}
33 changes: 33 additions & 0 deletions src/components/DocumentFooter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Link from "@docusaurus/Link";

import EditIcon from "../icons/EditIcon";
import classes from "./styles.module.css";

export interface DocumentFooterProps {
editHref: string;
lastUpdatedContent: React.ReactNode;
}

export default function DocumentFooter({
editHref,
lastUpdatedContent,
}: DocumentFooterProps) {
return (
<footer className="theme-doc-footer docusaurus-mt-lg">
<div className="row margin-top--sm theme-doc-footer-edit-meta-row">
<div className="col">
<Link
className="theme-edit-this-page"
href={editHref}
rel="noopener noreferrer"
target="_blank"
>
<EditIcon aria-hidden="true" className={classes.iconEdit} />
Edit this page
</Link>
</div>
<div className={`col ${classes.lastUpdated}`}>{lastUpdatedContent}</div>
</div>
</footer>
);
}
18 changes: 18 additions & 0 deletions src/components/DocumentFooter/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/** Do not replace hardcoded values with CSS variables in this file, as these values are copied from Docusaurus. */

.iconEdit {
margin-right: 0.3em;
vertical-align: sub;
}

.lastUpdated {
margin-top: 0.2rem;
font-size: smaller;
font-style: italic;
}

@media (width >= 997px) {
.lastUpdated {
text-align: right;
}
}
4 changes: 2 additions & 2 deletions src/components/HomepageFeatures/styles.module.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
padding: 2rem 0;
}

.featureSvg {
height: 200px;
width: 200px;
height: 200px;
}

.featureLink {
Expand Down
64 changes: 64 additions & 0 deletions src/components/InfoButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/** @author Ka Pui (August) Cheung */

import { useState } from "react";

import { Dialog, DialogBody, DialogContent, DialogHeader } from "../Dialog";
import Tooltip from "../Tooltip";
import styles from "./styles.module.css";

export interface InfoButtonProps {
/** The content to display in the dialog. */
content: React.ReactNode;
/** Optional title for the dialog. */
title?: string;
/** Additional CSS class for the button. */
className?: string;
/** Additional CSS class for the dialog content. */
dialogClassName?: string;
}

export default function InfoButton({
content,
title = "Details",
className = "",
dialogClassName = "",
}: InfoButtonProps) {
const [isOpen, setIsOpen] = useState(false);

const handleOpen = () => setIsOpen(true);
const handleClose = () => setIsOpen(false);

const sanitizedContent =
typeof content === "string" ? (
<div
dangerouslySetInnerHTML={{ __html: content }}
className={styles.infoContent}
/>
) : (
content
);

return (
<>
<Tooltip content="Learn more">
<button
aria-expanded={isOpen}
aria-haspopup="dialog"
aria-label={`Information about ${title}`}
className={`${styles.infoButton} ${className}`}
type="button"
onClick={handleOpen}
>
i
</button>
</Tooltip>

<Dialog className={dialogClassName} isOpen={isOpen} onClose={handleClose}>
<DialogContent>
<DialogHeader onClose={handleClose}>{title}</DialogHeader>
<DialogBody>{sanitizedContent}</DialogBody>
</DialogContent>
</Dialog>
</>
);
}
Loading
Loading