1
1
'use client' ;
2
2
3
3
import { usePathname , useRouter , useSearchParams } from 'next/navigation' ;
4
- import { use , useRef } from 'react' ;
4
+ import { use , useState } from 'react' ;
5
5
import { flushSync } from 'react-dom' ;
6
6
import { Toaster } from 'sonner' ;
7
7
import { useDebouncedCallback } from 'use-debounce' ;
8
8
import { Topbar } from '../../../components' ;
9
9
import { CodeContainer } from '../../../components/code-container' ;
10
10
import {
11
- ResizableWarpper ,
11
+ ResizableWrapper ,
12
12
makeIframeDocumentBubbleEvents ,
13
13
} from '../../../components/resizable-wrapper' ;
14
14
import { Send } from '../../../components/send' ;
15
- import { ShellContent } from '../../../components/shell ' ;
15
+ import { useToolbarState } from '../../../components/toolbar ' ;
16
16
import { Tooltip } from '../../../components/tooltip' ;
17
17
import { ActiveViewToggleGroup } from '../../../components/topbar/active-view-toggle-group' ;
18
18
import { ViewSizeControls } from '../../../components/topbar/view-size-controls' ;
19
19
import { PreviewContext } from '../../../contexts/preview' ;
20
20
import { useClampedState } from '../../../hooks/use-clamped-state' ;
21
+ import { cn } from '../../../utils' ;
21
22
import { RenderingError } from './rendering-error' ;
22
23
23
- interface PreviewProps {
24
+ interface PreviewProps extends React . ComponentProps < 'div' > {
24
25
emailTitle : string ;
25
26
}
26
27
27
- const Preview = ( { emailTitle } : PreviewProps ) => {
28
+ const Preview = ( { emailTitle, className , ... props } : PreviewProps ) => {
28
29
const { renderingResult, renderedEmailMetadata } = use ( PreviewContext ) ! ;
29
30
30
31
const router = useRouter ( ) ;
@@ -53,21 +54,21 @@ const Preview = ({ emailTitle }: PreviewProps) => {
53
54
const hasRenderingMetadata = typeof renderedEmailMetadata !== 'undefined' ;
54
55
const hasErrors = 'error' in renderingResult ;
55
56
56
- const maxWidthRef = useRef ( Number . POSITIVE_INFINITY ) ;
57
- const maxHeightRef = useRef ( Number . POSITIVE_INFINITY ) ;
58
- const minWidth = 350 ;
59
- const minHeight = 600 ;
57
+ const [ maxWidth , setMaxWidth ] = useState ( Number . POSITIVE_INFINITY ) ;
58
+ const [ maxHeight , setMaxHeight ] = useState ( Number . POSITIVE_INFINITY ) ;
59
+ const minWidth = 100 ;
60
+ const minHeight = 100 ;
60
61
const storedWidth = searchParams . get ( 'width' ) ;
61
62
const storedHeight = searchParams . get ( 'height' ) ;
62
63
const [ width , setWidth ] = useClampedState (
63
64
storedWidth ? Number . parseInt ( storedWidth ) : 600 ,
64
- 350 ,
65
- maxWidthRef . current ,
65
+ minWidth ,
66
+ maxWidth ,
66
67
) ;
67
68
const [ height , setHeight ] = useClampedState (
68
69
storedHeight ? Number . parseInt ( storedHeight ) : 1024 ,
69
- 600 ,
70
- maxHeightRef . current ,
70
+ minHeight ,
71
+ maxHeight ,
71
72
) ;
72
73
73
74
const handleSaveViewSize = useDebouncedCallback ( ( ) => {
@@ -77,6 +78,8 @@ const Preview = ({ emailTitle }: PreviewProps) => {
77
78
router . push ( `${ pathname } ?${ params . toString ( ) } ${ location . hash } ` ) ;
78
79
} , 300 ) ;
79
80
81
+ const { toggled : toolbarToggled } = useToolbarState ( ) ;
82
+
80
83
return (
81
84
< >
82
85
< Topbar emailTitle = { emailTitle } >
@@ -107,14 +110,20 @@ const Preview = ({ emailTitle }: PreviewProps) => {
107
110
) : null }
108
111
</ Topbar >
109
112
110
- < ShellContent
111
- className = "relative flex bg-gray-200 p-4"
113
+ < div
114
+ { ...props }
115
+ className = { cn (
116
+ 'h-[calc(100%-3.5rem-2.375rem)] will-change-height flex p-4 transition-all duration-300' ,
117
+ activeView === 'preview' && 'bg-gray-200' ,
118
+ toolbarToggled && 'h-[calc(100%-3.5rem-13rem)]' ,
119
+ className ,
120
+ ) }
112
121
ref = { ( element ) => {
113
122
const observer = new ResizeObserver ( ( entry ) => {
114
123
const [ elementEntry ] = entry ;
115
124
if ( elementEntry ) {
116
- maxWidthRef . current = elementEntry . contentRect . width ;
117
- maxHeightRef . current = elementEntry . contentRect . height ;
125
+ setMaxWidth ( elementEntry . contentRect . width ) ;
126
+ setMaxHeight ( elementEntry . contentRect . height ) ;
118
127
}
119
128
} ) ;
120
129
@@ -132,11 +141,11 @@ const Preview = ({ emailTitle }: PreviewProps) => {
132
141
{ hasRenderingMetadata ? (
133
142
< >
134
143
{ activeView === 'preview' && (
135
- < ResizableWarpper
144
+ < ResizableWrapper
136
145
minHeight = { minHeight }
137
146
minWidth = { minWidth }
138
- maxHeight = { maxHeightRef . current }
139
- maxWidth = { maxWidthRef . current }
147
+ maxHeight = { maxHeight }
148
+ maxWidth = { maxWidth }
140
149
height = { height }
141
150
onResizeEnd = { ( ) => {
142
151
handleSaveViewSize ( ) ;
@@ -166,12 +175,12 @@ const Preview = ({ emailTitle }: PreviewProps) => {
166
175
} }
167
176
title = { emailTitle }
168
177
/>
169
- </ ResizableWarpper >
178
+ </ ResizableWrapper >
170
179
) }
171
180
172
181
{ activeView === 'source' && (
173
- < div className = "h-full w-full bg-black " >
174
- < div className = "m-auto flex max-w-3xl p-6" >
182
+ < div className = "h-full w-full" >
183
+ < div className = "m-auto h-full flex max-w-3xl p-6" >
175
184
< Tooltip . Provider >
176
185
< CodeContainer
177
186
activeLang = { activeLang }
@@ -199,7 +208,7 @@ const Preview = ({ emailTitle }: PreviewProps) => {
199
208
) : null }
200
209
201
210
< Toaster />
202
- </ ShellContent >
211
+ </ div >
203
212
</ >
204
213
) ;
205
214
} ;
0 commit comments