Highly ambitious ATProtocol AppView service and sdks
at main 96 lines 2.8 kB view raw
1import { Copy } from "lucide-preact"; 2import { ComponentChildren, VNode, FunctionComponent } from "preact"; 3 4interface CodeBlockProps { 5 children: ComponentChildren; 6} 7 8interface CodeBlockHeaderProps { 9 children: ComponentChildren; 10} 11 12interface CodeBlockCodeProps { 13 highlightedCode: string; 14} 15 16// No props needed for CopyButton, it finds the code automatically 17 18function CodeBlockRoot({ children }: CodeBlockProps) { 19 // Check if there's a header in the children 20 const childrenArray = Array.isArray(children) ? children : [children]; 21 22 const hasHeader = childrenArray.some((child) => { 23 const vnode = child as VNode; 24 const component = vnode?.type as FunctionComponent; 25 return ( 26 component === CodeBlockHeader || component?.name === "CodeBlockHeader" 27 ); 28 }); 29 30 // Clone children and pass hasHeader context to CodeBlockCode components 31 const enhancedChildren = childrenArray.map((child) => { 32 const vnode = child as VNode; 33 const component = vnode?.type as FunctionComponent; 34 35 if (component === CodeBlockCode || component?.name === "CodeBlockCode") { 36 return { 37 ...vnode, 38 props: { 39 ...vnode.props, 40 _hasHeader: hasHeader, 41 }, 42 }; 43 } 44 return child; 45 }); 46 47 return ( 48 <div class="my-4 border border-zinc-200 dark:border-zinc-700 rounded-md"> 49 {enhancedChildren} 50 </div> 51 ); 52} 53 54function CodeBlockHeader({ children }: CodeBlockHeaderProps) { 55 return ( 56 <div class="bg-zinc-100 dark:bg-zinc-800 border-b border-zinc-200 dark:border-zinc-700 px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 rounded-t-md font-mono flex justify-between items-center"> 57 {children} 58 </div> 59 ); 60} 61 62function CodeBlockCopyButton() { 63 return ( 64 <button 65 type="button" 66 // @ts-ignore Hyperscript attribute 67 _="on click writeText(the textContent of the first <pre/> in the nextElementSibling of my parentElement) to the navigator's clipboard" 68 class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 p-1 rounded transition-colors" 69 title="Copy code" 70 > 71 <Copy size={16} /> 72 </button> 73 ); 74} 75 76function CodeBlockCode({ 77 highlightedCode, 78 _hasHeader, 79}: CodeBlockCodeProps & { _hasHeader?: boolean }) { 80 const className = _hasHeader 81 ? "[&_pre]:pt-4 [&_pre]:pb-4 [&_pre]:px-4 [&_pre]:rounded-none [&_pre]:rounded-b-md [&_pre]:overflow-x-auto [&_pre]:text-sm [&_pre]:border-0" 82 : "my-4 [&_pre]:p-4 [&_pre]:rounded-md [&_pre]:overflow-x-auto [&_pre]:text-sm"; 83 84 return ( 85 <div 86 class={className} 87 dangerouslySetInnerHTML={{ __html: highlightedCode }} 88 /> 89 ); 90} 91 92export const CodeBlock = Object.assign(CodeBlockRoot, { 93 Header: CodeBlockHeader, 94 Code: CodeBlockCode, 95 CopyButton: CodeBlockCopyButton, 96});