1import type { ScopedThreadRef } from "@t3tools/contracts";
2import { MinusIcon, PlusIcon, XIcon } from "lucide-react";
3import { useRef, useState } from "react";
4
5import { HtmlRenderDocument } from "../files/BrowserDocumentFrame";
6import { Button } from "../ui/button";
7import { Dialog, DialogPopup, DialogTitle } from "../ui/dialog";
8
9export function HtmlRenderDialog(props: {
10 readonly src: string;
11 readonly title: string;
12 readonly threadRef?: ScopedThreadRef | undefined;
13 readonly onClose: () => void;
14}) {
15 const [zoom, setZoom] = useState(1);
16 const closeButtonRef = useRef<HTMLButtonElement>(null);
17 return (
18 <Dialog open onOpenChange={(open) => !open && props.onClose()}>
19 <DialogPopup
20 variant="media"
21 showCloseButton={false}
22 bottomStickOnMobile={false}
23 className="row-start-1"
24 initialFocus={closeButtonRef}
25 >
26 <DialogTitle className="sr-only">Expanded {props.title} preview</DialogTitle>
27 <div className="relative w-[92vw]">
28 <Button
29 ref={closeButtonRef}
30 aria-label="Close"
31 size="icon-xs"
32 variant="media-close"
33 className="absolute right-0 -top-10 z-20"
34 onClick={props.onClose}
35 >
36 <XIcon />
37 </Button>
38 <div className="h-[min(86vh,calc(100vh-160px))] overflow-auto overscroll-contain rounded-lg bg-background shadow-2xl ring-1 ring-border/70 [container-type:size]">
39 <div className="h-[100cqh] w-[100cqw]" style={{ zoom }}>
40 <HtmlRenderDocument
41 src={props.src}
42 title={props.title}
43 threadRef={props.threadRef}
44 className="block size-full"
45 />
46 </div>
47 </div>
48 <div className="mt-2 flex min-w-0 items-center justify-center gap-3 text-xs text-white/80">
49 <span className="truncate">{props.title}</span>
50 <div className="flex shrink-0 items-center gap-1">
51 <Button
52 aria-label="Zoom out"
53 size="icon-sm"
54 variant="overlay"
55 disabled={zoom === 0.5}
56 onClick={() => setZoom((value) => value - 0.25)}
57 >
58 <MinusIcon />
59 </Button>
60 <Button
61 aria-label="Reset zoom"
62 size="sm"
63 variant="overlay"
64 onClick={() => setZoom(1)}
65 >
66 {Math.round(zoom * 100)}%
67 </Button>
68 <Button
69 aria-label="Zoom in"
70 size="icon-sm"
71 variant="overlay"
72 disabled={zoom === 2}
73 onClick={() => setZoom((value) => value + 0.25)}
74 >
75 <PlusIcon />
76 </Button>
77 </div>
78 </div>
79 </div>
80 </DialogPopup>
81 </Dialog>
82 );
83}