|
1 | 1 | <script lang="ts"> |
2 | | - import * as Dialog from '$lib/components/ui/dialog'; |
| 2 | + import { Dialog as DialogPrimitive } from 'bits-ui'; |
| 3 | + import XIcon from '@lucide/svelte/icons/x'; |
3 | 4 |
|
4 | 5 | interface Props { |
5 | 6 | open: boolean; |
|
13 | 14 | let iframeRef = $state<HTMLIFrameElement | null>(null); |
14 | 15 |
|
15 | 16 | $effect(() => { |
16 | | - if (iframeRef) { |
17 | | - if (open) { |
18 | | - iframeRef.srcdoc = code; |
19 | | - } else { |
20 | | - iframeRef.srcdoc = ''; |
21 | | - } |
| 17 | + if (!iframeRef) return; |
| 18 | +
|
| 19 | + if (open) { |
| 20 | + iframeRef.srcdoc = code; |
| 21 | + } else { |
| 22 | + iframeRef.srcdoc = ''; |
22 | 23 | } |
23 | 24 | }); |
24 | 25 |
|
|
28 | 29 | } |
29 | 30 | </script> |
30 | 31 |
|
31 | | -<Dialog.Root {open} onOpenChange={handleOpenChange}> |
32 | | - <Dialog.Content class="max-w-[calc(100%-1rem)] sm:max-w-4xl md:max-w-5xl"> |
33 | | - <Dialog.Header> |
34 | | - <Dialog.Title>HTML Preview</Dialog.Title> |
35 | | - </Dialog.Header> |
| 32 | +<DialogPrimitive.Root {open} onOpenChange={handleOpenChange}> |
| 33 | + <DialogPrimitive.Portal> |
| 34 | + <DialogPrimitive.Overlay class="code-preview-overlay" /> |
36 | 35 |
|
37 | | - <div class="preview-container mt-2"> |
| 36 | + <DialogPrimitive.Content class="code-preview-content"> |
38 | 37 | <iframe |
39 | 38 | bind:this={iframeRef} |
40 | 39 | title={`Preview ${language}`} |
41 | 40 | sandbox="allow-scripts" |
42 | | - class="h-[70vh] w-full rounded-md border border-border/40 bg-background" |
| 41 | + class="code-preview-iframe" |
43 | 42 | ></iframe> |
44 | | - </div> |
45 | | - </Dialog.Content> |
46 | | -</Dialog.Root> |
47 | | - |
48 | | -<style> |
49 | | - .preview-container { |
50 | | - display: flex; |
51 | | - flex-direction: column; |
52 | | - gap: 0.75rem; |
| 43 | + |
| 44 | + <DialogPrimitive.Close |
| 45 | + class="code-preview-close absolute top-4 right-6 rounded-xs text-white opacity-70 mix-blend-difference ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden 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; |
53 | 92 | } |
54 | 93 | </style> |
0 commit comments