Skip to content
Open
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
11 changes: 10 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn run lint-staged
if command -v yarn >/dev/null 2>&1; then
yarn run lint-staged
elif command -v corepack >/dev/null 2>&1; then
corepack yarn run lint-staged
elif command -v npm >/dev/null 2>&1; then
npm exec -- lint-staged
else
echo "Error: yarn/corepack/npm not found in PATH"
exit 127
fi
98 changes: 98 additions & 0 deletions src/components/MDXComponents/ExpandableImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as React from "react";
import { useI18next } from "gatsby-plugin-react-i18next";

const IMAGE_LABELS = {
en: {
expand: "Expand image",
collapse: "Collapse image",
},
zh: {
expand: "Expand image",
collapse: "Collapse image",
},
ja: {
expand: "Expand image",
collapse: "Collapse image",
},
} as const;

function getImageLabel(language: string, expanded: boolean) {
const lang = language.startsWith("zh")
? "zh"
: language.startsWith("ja")
? "ja"
: "en";
return expanded ? IMAGE_LABELS[lang].collapse : IMAGE_LABELS[lang].expand;
}
Comment on lines +19 to +26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This getImageLabel function and its language detection logic are duplicated in ExpandableTable.tsx. To improve maintainability and follow the DRY (Don't Repeat Yourself) principle, please consider extracting the language key detection logic into a shared utility function that both components can use.


export function ExpandableImage(
props: React.ImgHTMLAttributes<HTMLImageElement>
) {
const [open, setOpen] = React.useState(false);
const { language } = useI18next();
const expandLabel = getImageLabel(language, false);
const collapseLabel = getImageLabel(language, true);

React.useEffect(() => {
if (!open) return;

const originalOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";

const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setOpen(false);
}
};

document.addEventListener("keydown", onKeyDown);

return () => {
document.body.style.overflow = originalOverflow;
document.removeEventListener("keydown", onKeyDown);
};
}, [open]);

return (
<div className="expandable-image">
<button
type="button"
className="expandable-toggle-button"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
setOpen(true);
}}
aria-expanded={open}
>
{expandLabel}
</button>
<img {...props} />
{open && (
<div
className="expandable-modal-backdrop"
role="presentation"
onClick={() => setOpen(false)}
>
<div
className="expandable-modal-content expandable-image-modal-content"
role="dialog"
aria-modal="true"
onClick={(event) => event.stopPropagation()}
>
<button
type="button"
className="expandable-modal-collapse"
onClick={() => setOpen(false)}
>
{collapseLabel}
</button>
<div className="expandable-modal-scroll">
<img {...props} className="expandable-modal-image" />
</div>
</div>
</div>
)}
</div>
);
}
94 changes: 94 additions & 0 deletions src/components/MDXComponents/ExpandableTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as React from "react";
import { useI18next } from "gatsby-plugin-react-i18next";

const TABLE_LABELS = {
en: {
expand: "Expand table",
collapse: "Collapse table",
},
zh: {
expand: "Expand table",
collapse: "Collapse table",
},
ja: {
expand: "Expand table",
collapse: "Collapse table",
},
} as const;

function getTableLabel(language: string, expanded: boolean) {
const lang = language.startsWith("zh")
? "zh"
: language.startsWith("ja")
? "ja"
: "en";
return expanded ? TABLE_LABELS[lang].collapse : TABLE_LABELS[lang].expand;
}
Comment on lines +19 to +26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This getTableLabel function and its language detection logic are duplicated in ExpandableImage.tsx. To improve maintainability and follow the DRY (Don't Repeat Yourself) principle, please consider extracting the language key detection logic into a shared utility function that both components can use.


export function ExpandableTable(
props: React.TableHTMLAttributes<HTMLTableElement>
) {
const [open, setOpen] = React.useState(false);
const { language } = useI18next();
const expandLabel = getTableLabel(language, false);
const collapseLabel = getTableLabel(language, true);

React.useEffect(() => {
if (!open) return;

const originalOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";

const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setOpen(false);
}
};

document.addEventListener("keydown", onKeyDown);

return () => {
document.body.style.overflow = originalOverflow;
document.removeEventListener("keydown", onKeyDown);
};
}, [open]);

return (
<div className="expandable-table">
<button
type="button"
className="expandable-toggle-button"
onClick={() => setOpen(true)}
aria-expanded={open}
>
{expandLabel}
</button>
<table {...props} />
{open && (
<div
className="expandable-modal-backdrop"
role="presentation"
onClick={() => setOpen(false)}
>
<div
className="expandable-modal-content expandable-table-modal-content"
role="dialog"
aria-modal="true"
onClick={(event) => event.stopPropagation()}
>
<button
type="button"
className="expandable-modal-collapse"
onClick={() => setOpen(false)}
>
{collapseLabel}
</button>
<div className="expandable-modal-scroll">
<table {...props} />
</div>
</div>
</div>
)}
</div>
);
}
2 changes: 2 additions & 0 deletions src/components/MDXComponents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ export {
} from "components/MDXComponents/developer";

export { TargetLink as Link } from "./Link";
export { ExpandableTable as table } from "./ExpandableTable";
export { ExpandableImage as img } from "./ExpandableImage";
86 changes: 86 additions & 0 deletions src/styles/docTemplate.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,92 @@
margin: auto;
}

.expandable-toggle-button {
display: block;
width: fit-content;
margin: 0 0 8px auto;
padding: 0;
border: 0;
background: none;
color: var(--tiui-palette-secondary);
text-decoration: underline;
text-underline-offset: 2px;
font-size: 14px;
line-height: 1.4;
cursor: pointer;
}

.expandable-table {
margin: 16px 0;
}

.expandable-image {
display: block;
margin: 16px 0;
}

.expandable-image > img {
max-width: 100%;
height: auto;
}

.expandable-modal-backdrop {
position: fixed;
inset: 0;
z-index: 1300;
display: flex;
align-items: center;
justify-content: center;
background: rgba(14, 35, 54, 0.6);
padding: 24px;
box-sizing: border-box;
}

.expandable-modal-content {
width: min(1200px, 100%);
max-height: 100%;
background: #fff;
border-radius: var(--tiui-shape-border-radius);
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
padding: 12px 12px 16px;
box-sizing: border-box;
}

.expandable-modal-collapse {
margin-left: auto;
margin-bottom: 8px;
padding: 0;
border: 0;
background: none;
color: var(--tiui-palette-secondary);
text-decoration: underline;
text-underline-offset: 2px;
font-size: 14px;
line-height: 1.4;
cursor: pointer;
display: block;
}

.expandable-modal-scroll {
max-height: calc(100vh - 120px);
overflow: auto;
}

.expandable-table-modal-content table {
margin: 0;
min-width: 100%;
}

.expandable-image-modal-content .expandable-modal-scroll {
text-align: center;
}

.expandable-modal-image {
max-width: none;
height: auto;
margin: 0 auto;
}

code {
background-color: var(--tiui-palette-carbon-200);
border-radius: var(--tiui-shape-border-radius);
Expand Down