77 SandpackLayout ,
88 useSandpack ,
99} from "@codesandbox/sandpack-react" ;
10- import { useEffect , useState } from "react" ;
10+ import { useEffect , useState , useCallback } from "react" ;
1111
1212function ResetButton ( ) {
1313 const { sandpack } = useSandpack ( ) ;
@@ -36,6 +36,7 @@ export function PlaygroundCell({
3636 title ?: string ;
3737} ) {
3838 const [ isDark , setIsDark ] = useState ( false ) ;
39+ const [ isFullscreen , setIsFullscreen ] = useState ( false ) ;
3940
4041 useEffect ( ( ) => {
4142 const check = ( ) =>
@@ -49,6 +50,23 @@ export function PlaygroundCell({
4950 return ( ) => observer . disconnect ( ) ;
5051 } , [ ] ) ;
5152
53+ // Close fullscreen on Escape
54+ useEffect ( ( ) => {
55+ if ( ! isFullscreen ) return ;
56+ const handler = ( e : KeyboardEvent ) => {
57+ if ( e . key === "Escape" ) setIsFullscreen ( false ) ;
58+ } ;
59+ document . addEventListener ( "keydown" , handler ) ;
60+ // Prevent body scroll while fullscreen
61+ document . body . style . overflow = "hidden" ;
62+ return ( ) => {
63+ document . removeEventListener ( "keydown" , handler ) ;
64+ document . body . style . overflow = "" ;
65+ } ;
66+ } , [ isFullscreen ] ) ;
67+
68+ const toggleFullscreen = useCallback ( ( ) => setIsFullscreen ( ( v ) => ! v ) , [ ] ) ;
69+
5270 const sandpackTheme = {
5371 colors : {
5472 surface1 : isDark ? "#1a1a2e" : "#ffffff" ,
@@ -70,26 +88,89 @@ export function PlaygroundCell({
7088 } ,
7189 } ;
7290
91+ const wrapperClass = isFullscreen
92+ ? "fixed inset-0 z-50 flex flex-col"
93+ : "notebook-cell overflow-hidden" ;
94+
95+ const wrapperStyle = isFullscreen
96+ ? {
97+ background : isDark ? "#0a0a0a" : "#ffffff" ,
98+ borderColor : "var(--color-mint)" ,
99+ }
100+ : { borderColor : "var(--color-mint)" } ;
101+
73102 return (
74- < div className = "notebook-cell overflow-hidden" style = { { borderColor : "var(--color-mint)" } } >
103+ < div className = { wrapperClass } style = { wrapperStyle } >
104+ { /* Header bar */ }
75105 { title && (
76106 < div
77- className = "px-4 py-2.5 text-sm font-semibold flex items-center gap-2 border-b"
107+ className = "px-4 py-2.5 text-sm font-semibold flex items-center gap-2 border-b shrink-0 "
78108 style = { {
79109 borderColor : "var(--color-border-glass)" ,
80- background : "var(--color-glass-subtle)" ,
110+ background : isFullscreen
111+ ? isDark
112+ ? "#1a1a2e"
113+ : "#f7f7f9"
114+ : "var(--color-glass-subtle)" ,
81115 } }
82116 >
83117 < span
84118 className = "inline-flex items-center justify-center w-5 h-5 rounded text-xs"
85119 style = { {
86- background : "linear-gradient(135deg, var(--color-lilac), var(--color-mint))" ,
120+ background :
121+ "linear-gradient(135deg, var(--color-lilac), var(--color-mint))" ,
87122 color : "#fff" ,
88123 } }
89124 >
90125 ▶
91126 </ span >
92- < span style = { { color : "var(--text-primary)" } } > { title } </ span >
127+ < span className = "flex-1" style = { { color : "var(--text-primary)" } } >
128+ { title }
129+ </ span >
130+ < button
131+ onClick = { toggleFullscreen }
132+ className = "flex items-center justify-center w-7 h-7 rounded-md transition-colors cursor-pointer"
133+ style = { {
134+ background : "var(--color-glass-subtle)" ,
135+ border : "1px solid var(--color-border-glass)" ,
136+ color : "var(--text-secondary)" ,
137+ } }
138+ title = { isFullscreen ? "Exit fullscreen (Esc)" : "Fullscreen" }
139+ >
140+ { isFullscreen ? (
141+ < svg
142+ width = "14"
143+ height = "14"
144+ viewBox = "0 0 24 24"
145+ fill = "none"
146+ stroke = "currentColor"
147+ strokeWidth = "2"
148+ strokeLinecap = "round"
149+ strokeLinejoin = "round"
150+ >
151+ < polyline points = "4 14 10 14 10 20" />
152+ < polyline points = "20 10 14 10 14 4" />
153+ < line x1 = "14" y1 = "10" x2 = "21" y2 = "3" />
154+ < line x1 = "3" y1 = "21" x2 = "10" y2 = "14" />
155+ </ svg >
156+ ) : (
157+ < svg
158+ width = "14"
159+ height = "14"
160+ viewBox = "0 0 24 24"
161+ fill = "none"
162+ stroke = "currentColor"
163+ strokeWidth = "2"
164+ strokeLinecap = "round"
165+ strokeLinejoin = "round"
166+ >
167+ < polyline points = "15 3 21 3 21 9" />
168+ < polyline points = "9 21 3 21 3 15" />
169+ < line x1 = "21" y1 = "3" x2 = "14" y2 = "10" />
170+ < line x1 = "3" y1 = "21" x2 = "10" y2 = "14" />
171+ </ svg >
172+ ) }
173+ </ button >
93174 </ div >
94175 ) }
95176
@@ -111,25 +192,27 @@ export function PlaygroundCell({
111192 } }
112193 >
113194 < SandpackLayout
195+ className = { isFullscreen ? "flex-1" : "" }
114196 style = { {
115197 border : "none" ,
116198 borderRadius : 0 ,
117199 background : "transparent" ,
200+ ...( isFullscreen ? { height : "100%" } : { } ) ,
118201 } }
119202 >
120203 < SandpackCodeEditor
121204 showLineNumbers
122205 showTabs
123- style = { { minHeight : 280 } }
206+ style = { { minHeight : isFullscreen ? undefined : 280 } }
124207 />
125208 < SandpackPreview
126209 showOpenInCodeSandbox = { false }
127210 showRefreshButton
128- style = { { minHeight : 280 } }
211+ style = { { minHeight : isFullscreen ? undefined : 280 } }
129212 />
130213 </ SandpackLayout >
131214 < div
132- className = "flex items-center gap-2 px-4 py-2 border-t"
215+ className = "flex items-center gap-2 px-4 py-2 border-t shrink-0 "
133216 style = { { borderColor : "var(--color-border-glass)" } }
134217 >
135218 < ResetButton />
0 commit comments