spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1'use client';2import { Check, Copy } from 'lucide-react';3import { useState } from 'react';4import { t } from '@/i18n';5import { cn } from '@/lib/cn';67/** Small "Copy" button for code snippets (clipboard API; falls back to a hidden textarea select). */8export function CopyButton({ text, className, label }: { text: string; className?: string; label?: string }) {9 const [done, setDone] = useState(false);10 const copy = async () => {11 try {12 await navigator.clipboard.writeText(text);13 } catch {14 const ta = document.createElement('textarea');15 ta.value = text;16 document.body.appendChild(ta);17 ta.select();18 document.execCommand('copy');19 ta.remove();20 }21 setDone(true);22 setTimeout(() => setDone(false), 1600);23 };24 return (25 <button type="button" onClick={copy} className={cn('inline-flex min-h-[36px] items-center gap-1 rounded-sm border border-rule px-2 text-xs text-ink-2 hover:bg-surface-2 hover:text-ink', className)} aria-live="polite">26 {done ? <Check size={13} aria-hidden /> : <Copy size={13} aria-hidden />}27 {done ? t('indicator.copied') : label ?? t('indicator.copy')}28 </button>29 );30}3132/** Monospace code block with a thin toolbar (language · copy) above the scrollable code — the button never covers code. */33export function CodeBlock({ code, className, lang }: { code: string; className?: string; lang?: string }) {34 return (35 <div className={cn('min-w-0 rounded-sm border border-rule bg-surface', className)}>36 <div className="flex items-center justify-between gap-2 border-b border-rule px-2 py-1">37 <span className="font-mono text-2xs uppercase tracking-wide text-ink-3">{lang ?? ''}</span>38 <CopyButton text={code} className="min-h-[32px] border-0" />39 </div>40 <pre className="overflow-x-auto p-3 font-mono text-xs leading-relaxed text-ink" data-lang={lang}>41 <code>{code}</code>42 </pre>43 </div>44 );45}46