11'use client'
22
3- import React , { useState , useEffect } from 'react'
3+ import React , { useState , useEffect , useMemo } from 'react'
44import { useLocalStorage } from '@/lib/hooks/useLocalStorage'
55import { useCodeRunner } from '@/lib/hooks/useCodeRunner'
66import { useTheme } from '@/lib/theme/ThemeProvider'
7- import CodeEditor from './CodeEditor'
8- import Console from './Console'
9- import NetworkSelector from './NetworkSelector'
10- import ExampleSelector from './ExampleSelector'
7+ import dynamic from 'next/dynamic'
118import { DEFAULT_NETWORK , NETWORKS } from '@/lib/constants/networks'
129import { DEFAULT_EXAMPLE , EXAMPLES } from '@/lib/constants/examples'
1310import ActionButton from './ui/ActionButton'
@@ -21,56 +18,51 @@ import NetworkBadge from './ui/NetworkBadge'
2118import Card from './ui/Card'
2219import Badge from './ui/Badge'
2320
21+
22+ const CodeEditor = dynamic ( ( ) => import ( './CodeEditor' ) , { ssr : false } )
23+ const Console = dynamic ( ( ) => import ( './Console' ) , { ssr : false } )
24+ const NetworkSelector = dynamic ( ( ) => import ( './NetworkSelector' ) , { ssr : false } )
25+ const ExampleSelector = dynamic ( ( ) => import ( './ExampleSelector' ) , { ssr : false } )
26+
2427export default function Playground ( ) {
2528 const { isDarkTheme, isLoaded, currentNetworkId, setCurrentNetworkId } = useTheme ( )
26-
27-
2829 const [ selectedNetworkId , setSelectedNetworkId ] = useLocalStorage < string > ( 'selectedNetwork' , DEFAULT_NETWORK . id )
2930 const [ selectedExampleId , setSelectedExampleId ] = useLocalStorage < string > ( 'selectedExample' , DEFAULT_EXAMPLE . id )
30-
31-
3231 const [ selectedNetwork , setSelectedNetwork ] = useState < Network > ( DEFAULT_NETWORK )
3332 const [ selectedExample , setSelectedExample ] = useState < Example > ( DEFAULT_EXAMPLE )
34-
35-
3633 const [ sidebarCollapsed , setSidebarCollapsed ] = useState ( false )
37-
38-
34+ const [ isMounted , setIsMounted ] = useState ( false )
3935 const { code, outputs, isRunning, updateCode, setExampleCode, runCode, clearOutput } = useCodeRunner ( )
4036
41-
4237 useEffect ( ( ) => {
38+ setIsMounted ( true )
4339 const checkMobile = ( ) => {
4440 setSidebarCollapsed ( window . innerWidth < 1024 )
4541 }
4642
47- checkMobile ( )
43+ checkMobile ( )
4844 window . addEventListener ( 'resize' , checkMobile )
4945
5046 return ( ) => window . removeEventListener ( 'resize' , checkMobile )
5147 } , [ ] )
5248
53-
5449 useEffect ( ( ) => {
5550 const network = NETWORKS [ selectedNetworkId ] || DEFAULT_NETWORK
5651 setSelectedNetwork ( network )
5752 setCurrentNetworkId ( selectedNetworkId )
5853 } , [ selectedNetworkId , setCurrentNetworkId ] )
5954
60-
6155 useEffect ( ( ) => {
6256 const example = EXAMPLES . find ( e => e . id === selectedExampleId ) || DEFAULT_EXAMPLE
6357 setSelectedExample ( example )
6458 } , [ selectedExampleId ] )
6559
66-
6760 useEffect ( ( ) => {
6861 if ( selectedExample && selectedNetwork ) {
6962 setExampleCode ( selectedExample , selectedNetwork )
7063 }
7164 } , [ selectedExample , selectedNetwork , setExampleCode ] )
7265
73-
7466 const getNetworkColor = ( ) => {
7567 const networkId = NETWORKS [ currentNetworkId ] ?. id as keyof typeof colorMap ;
7668
@@ -84,7 +76,6 @@ export default function Playground() {
8476 return colorMap [ networkId ] || colorMap [ 'polkadot' ]
8577 }
8678
87-
8879 const getDifficultyColor = ( level : 'beginner' | 'intermediate' | 'advanced' ) => {
8980 const colorMap = {
9081 'beginner' : '#22C55E' ,
@@ -94,93 +85,77 @@ export default function Playground() {
9485 return colorMap [ level ] || colorMap [ 'beginner' ]
9586 }
9687
97-
98- if ( ! isLoaded ) {
99- return (
100- < div className = "h-screen w-screen flex items-center justify-center" >
101- < div className = "animate-spin mr-2" >
102- < svg width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" >
103- < line x1 = "12" y1 = "2" x2 = "12" y2 = "6" > </ line >
104- < line x1 = "12" y1 = "18" x2 = "12" y2 = "22" > </ line >
105- < line x1 = "4.93" y1 = "4.93" x2 = "7.76" y2 = "7.76" > </ line >
106- < line x1 = "16.24" y1 = "16.24" x2 = "19.07" y2 = "19.07" > </ line >
107- < line x1 = "2" y1 = "12" x2 = "6" y2 = "12" > </ line >
108- < line x1 = "18" y1 = "12" x2 = "22" y2 = "12" > </ line >
109- < line x1 = "4.93" y1 = "19.07" x2 = "7.76" y2 = "16.24" > </ line >
110- < line x1 = "16.24" y1 = "7.76" x2 = "19.07" y2 = "4.93" > </ line >
111- </ svg >
112- </ div >
113- < div className = "flex flex-col items-center" >
114- < span > Loading Polkadot API Playground...</ span >
115- < span className = "text-xs mt-1 opacity-70" > Preparing your development environment</ span >
116- </ div >
117- </ div >
118- ) ;
119- }
120-
121-
122- const SidebarContent = ( ) => (
123- < div className = "flex flex-col gap-4" >
124- < NetworkSelector
125- networks = { Object . values ( NETWORKS ) }
126- selectedNetworkId = { selectedNetwork . id }
127- onNetworkChange = { setSelectedNetworkId }
128- />
88+ const SidebarContent = useMemo ( ( ) => {
89+ return ( ) => (
90+ < div className = "flex flex-col gap-4" >
91+ < NetworkSelector
92+ networks = { Object . values ( NETWORKS ) }
93+ selectedNetworkId = { selectedNetwork . id }
94+ onNetworkChange = { setSelectedNetworkId }
95+ />
12996
130- < ExampleSelector
131- examples = { EXAMPLES }
132- selectedExampleId = { selectedExample . id }
133- onExampleChange = { setSelectedExampleId }
134- />
97+ < ExampleSelector
98+ examples = { EXAMPLES }
99+ selectedExampleId = { selectedExample . id }
100+ onExampleChange = { setSelectedExampleId }
101+ />
135102
136- < InfoPanel network = { selectedNetwork } example = { selectedExample } />
103+ < InfoPanel network = { selectedNetwork } example = { selectedExample } />
137104
138- < Card >
139- < div className = "flex flex-col gap-3" >
140- < ActionButton
141- onClick = { ( ) => runCode ( selectedExample , selectedNetwork ) }
142- disabled = { isRunning }
143- isPrimary = { true }
144- icon = "play"
145- fullWidth
146- size = "lg"
147- >
148- { isRunning ? 'Running...' : 'Run Code' }
149- </ ActionButton >
105+ < Card >
106+ < div className = "flex flex-col gap-3" >
107+ < ActionButton
108+ onClick = { ( ) => runCode ( selectedExample , selectedNetwork ) }
109+ disabled = { isRunning }
110+ isPrimary = { true }
111+ icon = "play"
112+ fullWidth
113+ size = "lg"
114+ >
115+ { isRunning ? 'Running...' : 'Run Code' }
116+ </ ActionButton >
150117
151- < ActionButton
152- onClick = { clearOutput }
153- disabled = { isRunning || outputs . length === 0 }
154- icon = "trash"
155- fullWidth
156- size = "md"
157- >
158- Clear Console
159- </ ActionButton >
118+ < ActionButton
119+ onClick = { clearOutput }
120+ disabled = { isRunning || outputs . length === 0 }
121+ icon = "trash"
122+ fullWidth
123+ size = "md"
124+ >
125+ Clear Console
126+ </ ActionButton >
160127
161- < div className = "flex items-center justify-center mt-2 text-xs" style = { {
162- color : isDarkTheme ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)'
163- } } >
164- < span > Example difficulty: </ span >
165- < span className = "ml-1 font-medium" style = { {
166- color : getDifficultyColor ( selectedExample . level )
128+ < div className = "flex items-center justify-center mt-2 text-xs" style = { {
129+ color : isDarkTheme ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)'
167130 } } >
168- { selectedExample . level . toUpperCase ( ) }
169- </ span >
131+ < span > Example difficulty: </ span >
132+ < span className = "ml-1 font-medium" style = { {
133+ color : getDifficultyColor ( selectedExample . level )
134+ } } >
135+ { selectedExample . level . toUpperCase ( ) }
136+ </ span >
137+ </ div >
170138 </ div >
171- </ div >
172- </ Card >
173- </ div >
174- )
139+ </ Card >
140+ </ div >
141+ ) ;
142+ } , [
143+ selectedNetwork ,
144+ selectedExampleId ,
145+ selectedExample ,
146+ isDarkTheme ,
147+ isRunning ,
148+ outputs . length ,
149+ runCode ,
150+ clearOutput
151+ ] ) ;
175152
176-
177153 const NetworkIndicator = ( ) => (
178154 < div className = "fixed top-0 left-0 right-0 h-1 z-50 opacity-80" style = { {
179155 background : `linear-gradient(90deg, ${ getNetworkColor ( ) } 0%, rgba(255,255,255,0) 100%)`
180156 } } />
181157 )
182158
183-
184159 const SidebarToggle = ( ) => (
185160 < div className = "fixed bottom-6 right-6 lg:hidden z-30" >
186161 < button
@@ -209,46 +184,83 @@ export default function Playground() {
209184 </ div >
210185 )
211186
212-
213- const MainContent = ( ) => (
214- < div className = "flex flex-col gap-4 h-full" >
215- < TutorialPanel example = { selectedExample } network = { selectedNetwork } />
187+ const MainContent = useMemo ( ( ) => {
188+ return ( ) => (
189+ < div className = "flex flex-col gap-4 h-full" >
190+ < TutorialPanel example = { selectedExample } network = { selectedNetwork } />
216191
217- < Card className = "flex-1" header = {
218- < div className = "flex justify-between items-center" >
219- < div className = "font-medium flex items-center" >
220- < span > Code Editor</ span >
221- < Badge variant = "default" className = "ml-2" >
222- TypeScript
223- </ Badge >
192+ < Card className = "flex-1" header = {
193+ < div className = "flex justify-between items-center" >
194+ < div className = "font-medium flex items-center" >
195+ < span > Code Editor</ span >
196+ < Badge variant = "default" className = "ml-2" >
197+ TypeScript
198+ </ Badge >
199+ </ div >
200+ < div className = "text-xs px-2 py-1 rounded flex items-center" style = { {
201+ backgroundColor : isDarkTheme ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
202+ } } >
203+ < NetworkBadge network = { selectedNetwork } size = "sm" showName = { false } className = "mr-2" />
204+ { selectedExample . name }
205+ </ div >
224206 </ div >
225- < div className = "text-xs px-2 py-1 rounded flex items-center" style = { {
226- backgroundColor : isDarkTheme ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
227- } } >
228- < NetworkBadge network = { selectedNetwork } size = "sm" showName = { false } className = "mr-2" />
229- { selectedExample . name }
207+ } >
208+ < div className = "-m-4" >
209+ { isMounted && (
210+ < CodeEditor
211+ code = { code }
212+ onChange = { updateCode }
213+ disabled = { isRunning }
214+ height = "400px"
215+ />
216+ ) }
230217 </ div >
231- </ div >
232- } >
233- < div className = "-m-4" >
234- < CodeEditor
235- code = { code }
236- onChange = { updateCode }
237- disabled = { isRunning }
238- height = "400px"
218+ </ Card >
219+
220+ { isMounted && (
221+ < Console
222+ outputs = { outputs }
223+ onClear = { clearOutput }
239224 />
240- </ div >
241- </ Card >
225+ ) }
226+ </ div >
227+ ) ;
228+ } , [
229+ selectedExample ,
230+ selectedNetwork ,
231+ isDarkTheme ,
232+ code ,
233+ isRunning ,
234+ outputs ,
235+ updateCode ,
236+ clearOutput ,
237+ isMounted
238+ ] ) ;
242239
243- < Console
244- outputs = { outputs }
245- onClear = { clearOutput }
246- />
247- </ div >
248- )
240+ if ( ! isLoaded || ! isMounted ) {
241+ return (
242+ < div className = "h-screen w-screen flex items-center justify-center" >
243+ < div className = "animate-spin mr-2" >
244+ < svg width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" >
245+ < line x1 = "12" y1 = "2" x2 = "12" y2 = "6" > </ line >
246+ < line x1 = "12" y1 = "18" x2 = "12" y2 = "22" > </ line >
247+ < line x1 = "4.93" y1 = "4.93" x2 = "7.76" y2 = "7.76" > </ line >
248+ < line x1 = "16.24" y1 = "16.24" x2 = "19.07" y2 = "19.07" > </ line >
249+ < line x1 = "2" y1 = "12" x2 = "6" y2 = "12" > </ line >
250+ < line x1 = "18" y1 = "12" x2 = "22" y2 = "12" > </ line >
251+ < line x1 = "4.93" y1 = "19.07" x2 = "7.76" y2 = "16.24" > </ line >
252+ < line x1 = "16.24" y1 = "7.76" x2 = "19.07" y2 = "4.93" > </ line >
253+ </ svg >
254+ </ div >
255+ < div className = "flex flex-col items-center" >
256+ < span > Loading Polkadot API Playground...</ span >
257+ < span className = "text-xs mt-1 opacity-70" > Preparing your development environment</ span >
258+ </ div >
259+ </ div >
260+ ) ;
261+ }
249262
250-
251- if ( window . innerWidth < 1024 ) {
263+ if ( typeof window !== 'undefined' && window . innerWidth < 1024 ) {
252264 return (
253265 < >
254266 < NetworkIndicator />
@@ -269,7 +281,6 @@ export default function Playground() {
269281 )
270282 }
271283
272-
273284 return (
274285 < >
275286 < NetworkIndicator />
0 commit comments