1<script lang="ts">
 2	import { Dialog as DialogPrimitive } from 'bits-ui';
 3	import XIcon from '@lucide/svelte/icons/x';
 4
 5	interface Props {
 6		open: boolean;
 7		code: string;
 8		language: string;
 9		onOpenChange?: (open: boolean) => void;
10	}
11
12	let { open = $bindable(), code, language, onOpenChange }: Props = $props();
13
14	let iframeRef = $state<HTMLIFrameElement | null>(null);
15
16	$effect(() => {
17		if (!iframeRef) return;
18
19		if (open) {
20			iframeRef.srcdoc = code;
21		} else {
22			iframeRef.srcdoc = '';
23		}
24	});
25
26	function handleOpenChange(nextOpen: boolean) {
27		open = nextOpen;
28		onOpenChange?.(nextOpen);
29	}
30</script>
31
32<DialogPrimitive.Root {open} onOpenChange={handleOpenChange}>
33	<DialogPrimitive.Portal>
34		<DialogPrimitive.Overlay class="code-preview-overlay" />
35
36		<DialogPrimitive.Content class="code-preview-content">
37			<iframe
38				bind:this={iframeRef}
39				title="Preview {language}"
40				sandbox="allow-scripts"
41				class="code-preview-iframe"
42			></iframe>
43
44			<DialogPrimitive.Close
45				class="code-preview-close absolute top-4 right-4 border-none bg-transparent text-white opacity-70 mix-blend-difference transition-opacity hover:opacity-100 focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-8"
46				aria-label="Close preview"
47			>
48				<XIcon />
49				<span class="sr-only">Close preview</span>
50			</DialogPrimitive.Close>
51		</DialogPrimitive.Content>
52	</DialogPrimitive.Portal>
53</DialogPrimitive.Root>
54
55<style lang="postcss">
56	:global(.code-preview-overlay) {
57		position: fixed;
58		inset: 0;
59		background-color: transparent;
60		z-index: 100000;
61	}
62
63	:global(.code-preview-content) {
64		position: fixed;
65		inset: 0;
66		top: 0 !important;
67		left: 0 !important;
68		width: 100dvw;
69		height: 100dvh;
70		margin: 0;
71		padding: 0;
72		border: none;
73		border-radius: 0;
74		background-color: transparent;
75		box-shadow: none;
76		display: block;
77		overflow: hidden;
78		transform: none !important;
79		z-index: 100001;
80	}
81
82	:global(.code-preview-iframe) {
83		display: block;
84		width: 100dvw;
85		height: 100dvh;
86		border: 0;
87	}
88
89	:global(.code-preview-close) {
90		position: absolute;
91		z-index: 100002;
92	}
93</style>