-
Notifications
You must be signed in to change notification settings - Fork 185
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
271 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import React from 'react' | ||
import type { HTMLAttributes } from 'react' | ||
import type { BundledLanguage, BundledTheme } from 'shiki' | ||
import ShikiCode from '@stateless/CodeHighlighter' | ||
import CopyToClipboard from '@stateless/CopyToClipboard' | ||
import clsx from 'clsx' | ||
|
||
type AdvancedBlockProps = { | ||
code: string | ||
fileName?: string | ||
lang?: BundledLanguage | ||
theme?: BundledTheme | ||
className?: string | ||
} | ||
|
||
export const AdvancedCodeBlock = ({ | ||
code, | ||
fileName, | ||
lang = 'typescript', | ||
theme = 'github-light', | ||
className, | ||
...props | ||
}: AdvancedBlockProps & HTMLAttributes<HTMLDivElement>) => { | ||
return ( | ||
<figure | ||
className={clsx( | ||
'-mt-1 relative flex w-full flex-col flex-wrap rounded-2xl bg-neutral-200/70 text-white leading-6 dark:bg-[rgb(33,33,38)]', | ||
className | ||
)} | ||
{...props} | ||
> | ||
<div className="flex items-center justify-between px-5 py-3"> | ||
<figcaption className="mr-[-48px] max-w-full whitespace-nowrap font-medium text-neutral-600 text-xs dark:text-neutral-200"> | ||
<span>{fileName ?? <br />}</span> | ||
</figcaption> | ||
<CopyToClipboard code={code} /> | ||
</div> | ||
<div className="w-full px-1 pb-1"> | ||
<div className="relative overflow-hidden border-t shadow-sm isolate rounded-xl border-white/80 shadow-black/5 dark:border-white/15 dark:shadow-black/10"> | ||
<pre | ||
className="py-4 overflow-x-auto overflow-y-auto text-sm leading-6 text-white bg-white/50 dark:bg-zinc-700/50" | ||
style={{ paddingRight: '10px' }} | ||
> | ||
<ShikiCode code={code} lang={lang} theme={theme} /> | ||
</pre> | ||
</div> | ||
</div> | ||
</figure> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React, { useState, useEffect } from 'react' | ||
import { type BundledLanguage, type BundledTheme, codeToHtml } from 'shiki' | ||
import clsx from 'clsx' | ||
import type { HTMLAttributes } from 'react' | ||
|
||
type ShikiProps = { | ||
code: string | ||
lang: BundledLanguage | ||
theme: BundledTheme | ||
className?: string | ||
} | ||
|
||
export default function ShikiCode({ | ||
code, | ||
lang, | ||
theme, | ||
className, | ||
...props | ||
}: Readonly<ShikiProps> & HTMLAttributes<HTMLDivElement>) { | ||
const [html, setHtml] = useState<string>('') | ||
|
||
useEffect(() => { | ||
const loadHtml = async () => { | ||
const result = await codeToHtml(code, { lang, theme }) | ||
setHtml(result) | ||
} | ||
|
||
loadHtml() | ||
}, [code, lang, theme]) | ||
|
||
return ( | ||
<div | ||
className={clsx('text-sm hue-rotate-0 invert-0 *:bg-transparent dark:hue-rotate-180 dark:invert', className)} | ||
{...props} | ||
dangerouslySetInnerHTML={{ __html: html }} | ||
/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
'use client' | ||
import React from 'react' | ||
import { CheckIcon, CopyIcon } from 'lucide-react' | ||
import { useState } from 'react' | ||
|
||
const CopyToClipboard = ({ code }: Readonly<{ code: string }>) => { | ||
const [isCopied, setIsCopied] = useState(false) | ||
const copyToClipboard = async () => { | ||
try { | ||
await navigator.clipboard.writeText(code) | ||
setIsCopied(true) | ||
} catch (_error) { | ||
setIsCopied(false) | ||
} finally { | ||
setTimeout(() => { | ||
setIsCopied(false) | ||
}, 2000) | ||
} | ||
} | ||
|
||
return ( | ||
<button | ||
aria-label="Copy code to clipboard" | ||
className="relative flex text-base font-normal leading-6 transition-colors duration-150 ease-in-out bg-transparent cursor-pointer size-4 text-zinc-500 dark:text-zinc-400" | ||
onClick={copyToClipboard} | ||
type="button" | ||
> | ||
{isCopied ? ( | ||
<CheckIcon className="block align-middle size-4" /> | ||
) : ( | ||
<CopyIcon className="block align-middle size-4" /> | ||
)} | ||
</button> | ||
) | ||
} | ||
|
||
export default CopyToClipboard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters