Files
onspring-api-docs/src/app/components/CodeSnippet.tsx
T
Stevan Freeborn 1c5d57e4ed Fix: Pass Copied Content as prop
Add prop to fence component called content which will
hold the content contained within the fence component.

Update code snippet component so that it filters the fence components
passed to it as children to only display the fence component that
is same language as the selected language and pass the content prop
of the fence component to the copy code button component on each
render of the snippet component.
2023-04-26 19:31:15 -05:00

189 lines
4.6 KiB
TypeScript

'use client';
import React, {
Children,
MouseEvent,
ReactElement,
ReactNode,
useState,
} from 'react';
import styles from './CodeSnippet.module.css';
function CodeSnippetCopyButton({
copyText,
}: {
copyText: string;
}) {
const [copyButtonTooltip, setCopyButtonTooltip] =
useState<boolean>(false);
const [showTooltip, setShowTooltip] =
useState<boolean>(false);
const handleCopy = (e: MouseEvent<HTMLButtonElement>) => {
setCopyButtonTooltip(true);
setTimeout(() => {
setCopyButtonTooltip(false);
}, 1000);
navigator.clipboard.writeText(copyText ?? '');
};
return (
<div className={styles.copyButtonContainer}>
<div>
<div
className={
showTooltip
? styles.tooltip
: styles.tooltipHidden
}
>
{copyButtonTooltip ? 'Copied!' : 'Click to Copy'}
</div>
</div>
<button
className={styles.copyButton}
onClick={handleCopy}
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16"
>
<path
fillRule="evenodd"
d="M10 1.5a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-1Zm-5 0A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5v1A1.5 1.5 0 0 1 9.5 4h-3A1.5 1.5 0 0 1 5 2.5v-1Zm-2 0h1v1A2.5 2.5 0 0 0 6.5 5h3A2.5 2.5 0 0 0 12 2.5v-1h1a2 2 0 0 1 2 2V14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3.5a2 2 0 0 1 2-2Z"
/>
</svg>
</button>
</div>
);
}
function SnippetLanguageDropdown({
selectedLanguage,
snippetLanguages,
handleSelectChange,
}: {
selectedLanguage: string;
snippetLanguages: string[];
handleSelectChange: (
e: React.ChangeEvent<HTMLSelectElement>
) => void;
}) {
return (
<div className={styles.dropdownContainer}>
<div className={styles.dropdown}>
<select
title="Select a language"
className={styles.dropdownSelect}
value={selectedLanguage}
onChange={handleSelectChange}
>
{snippetLanguages.map(language => {
return (
<option
key={language}
value={language}
className={styles.selectItem}
>
{language}
</option>
);
})}
</select>
</div>
</div>
);
}
export default function CodeSnippet({
children,
method,
heading,
defaultLanguage,
}: {
children: ReactNode;
method?: string;
heading: string;
defaultLanguage: string;
}) {
const methodStyles: { [key: string]: string } = {
GET: styles.get,
POST: styles.post,
PUT: styles.put,
DELETE: styles.delete,
};
const [selectedLanguage, setSelectedLanguage] =
useState<string>(defaultLanguage);
const handleSelectChange = (
e: React.ChangeEvent<HTMLSelectElement>
) => {
setSelectedLanguage(e.target.value);
};
const code = Children.toArray(children)
.filter(
child =>
React.isValidElement(child) &&
child.props.className ===
`language-${selectedLanguage}`
)
.map(child => {
return child as ReactElement;
})[0];
const snippetLanguages = Children.toArray(children).map(
child => {
if (React.isValidElement(child)) {
const languageClassParts =
child.props.className.split('-');
if (languageClassParts.length > 1) {
const language = languageClassParts[1];
return language;
}
}
}
);
return (
<div className={styles.snippetContainer}>
<div className={styles.snippetHeader}>
<div className={styles.snippetHeading}>
{method ? (
<span className={methodStyles[method]}>
{method}
</span>
) : null}{' '}
{heading}
</div>
<div className={styles.controlsContainer}>
{snippetLanguages.length > 1 ? (
<>
<SnippetLanguageDropdown
selectedLanguage={selectedLanguage}
snippetLanguages={snippetLanguages}
handleSelectChange={handleSelectChange}
/>
<div className={styles.spacer} />
</>
) : null}
<CodeSnippetCopyButton
copyText={code.props.content}
/>
</div>
</div>
<div className={styles.snippetContent}>
<pre>{code}</pre>
</div>
</div>
);
}