You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
1.9 KiB
75 lines
1.9 KiB
|
6 months ago
|
<script lang="ts">
|
||
|
|
import { browser } from '$app/environment';
|
||
|
|
import './code-block.css';
|
||
|
|
import type { CodeBlockProps } from './types';
|
||
|
|
|
||
|
|
let {
|
||
|
|
language,
|
||
|
|
filename,
|
||
|
|
copyable = true,
|
||
|
|
size = 3,
|
||
|
|
style,
|
||
|
|
children,
|
||
|
|
...restProps
|
||
|
|
}: CodeBlockProps = $props();
|
||
|
|
|
||
|
|
let codeRef = $state<HTMLElement | undefined>();
|
||
|
|
let copied = $state(false);
|
||
|
|
let copyTimeout: ReturnType<typeof setTimeout>;
|
||
|
|
|
||
|
|
const showHeader = $derived(!!filename || !!language || copyable);
|
||
|
|
|
||
|
|
const sizeTokens = $derived(
|
||
|
|
`--_air-code-block-size:var(--air-text-${size}-size);` +
|
||
|
|
`--_air-code-block-lh:var(--air-text-${size}-lh);` +
|
||
|
|
`--_air-code-block-ls:var(--air-text-${size}-ls)`
|
||
|
|
);
|
||
|
|
const mergedStyle = $derived(style ? `${sizeTokens};${style}` : sizeTokens);
|
||
|
|
|
||
|
|
async function handleCopy() {
|
||
|
|
if (!browser || !codeRef) return;
|
||
|
|
|
||
|
|
const text = codeRef.textContent ?? '';
|
||
|
|
|
||
|
|
try {
|
||
|
|
await navigator.clipboard.writeText(text);
|
||
|
|
copied = true;
|
||
|
|
clearTimeout(copyTimeout);
|
||
|
|
copyTimeout = setTimeout(() => {
|
||
|
|
copied = false;
|
||
|
|
}, 2000);
|
||
|
|
} catch {
|
||
|
|
// Clipboard no disponible — silencioso
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<div class="air-code-block" style={mergedStyle} {...restProps}>
|
||
|
|
{#if showHeader}
|
||
|
|
<div class="air-code-block-header">
|
||
|
|
{#if filename}
|
||
|
|
<span class="air-code-block-filename">{filename}</span>
|
||
|
|
{/if}
|
||
|
|
{#if language && !filename}
|
||
|
|
<span class="air-code-block-lang">{language}</span>
|
||
|
|
{/if}
|
||
|
|
{#if language && filename}
|
||
|
|
<span class="air-code-block-lang">{language}</span>
|
||
|
|
{/if}
|
||
|
|
{#if copyable}
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
class="air-code-block-copy"
|
||
|
|
data-copied={copied ? '' : undefined}
|
||
|
|
onclick={handleCopy}
|
||
|
|
aria-label={copied ? 'Copied' : 'Copy code'}
|
||
|
|
>
|
||
|
|
{copied ? '✓ Copied' : 'Copy'}
|
||
|
|
</button>
|
||
|
|
{/if}
|
||
|
|
</div>
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
<pre class="air-code-block-pre"><code class="air-code-block-code" bind:this={codeRef}>{@render children?.()}</code></pre>
|
||
|
|
</div>
|