11<script setup lang="ts">
2- import { ref , computed , onMounted } from ' vue'
2+ import { ref , computed , onMounted , onUnmounted } from ' vue'
33import CodeEditor from ' ./components/CodeEditor.vue'
44import FileUpload from ' ./components/FileUpload.vue'
55import ExampleSelector from ' ./components/ExampleSelector.vue'
@@ -19,6 +19,21 @@ const code = ref(examples[0].code)
1919const lastExecutedCode = ref (' ' )
2020const hasChanges = computed (() => code .value !== lastExecutedCode .value )
2121
22+ // Mobile state
23+ const activePanel = ref <' code' | ' chart' >(' code' )
24+ const windowWidth = ref (window .innerWidth )
25+ const isMobile = computed (() => windowWidth .value < 768 )
26+
27+ // Update width on resize
28+ const updateWidth = (): void => {
29+ windowWidth .value = window .innerWidth
30+ }
31+
32+ // Lifecycle hooks
33+ onUnmounted (() => {
34+ window .removeEventListener (' resize' , updateWidth )
35+ })
36+
2237// Current CSV data state
2338const currentCsvData = ref <CsvData | null >(null )
2439
@@ -133,6 +148,9 @@ const handleExampleSelect = async (example: RExample): Promise<void> => {
133148}
134149
135150onMounted (async () => {
151+ // Add resize listener
152+ window .addEventListener (' resize' , updateWidth )
153+
136154 // Initialize WebR first
137155 await initializeWebR (' ' )
138156
@@ -148,7 +166,30 @@ onMounted(async () => {
148166 <AppHeader />
149167
150168 <main class =" main" >
151- <div class =" toolbar" >
169+ <!-- Mobile toolbar -->
170+ <div
171+ v-if =" isMobile"
172+ class =" mobile-toolbar"
173+ >
174+ <FileUpload
175+ :uploaded-file =" currentCsvData"
176+ @file-uploaded =" handleFileUpload"
177+ @file-removed =" handleFileRemoved"
178+ />
179+ <ExampleSelector @example-selected =" handleExampleSelect" />
180+ <LibrarySelector
181+ :installed-libraries =" installedLibraries"
182+ :is-loading =" isInitializing"
183+ :package-versions =" packageVersions"
184+ @toggle-library =" toggleLibrary"
185+ />
186+ </div >
187+
188+ <!-- Desktop toolbar -->
189+ <div
190+ v-else
191+ class =" toolbar"
192+ >
152193 <div class =" toolbar-left" >
153194 <FileUpload
154195 :uploaded-file =" currentCsvData"
@@ -173,7 +214,57 @@ onMounted(async () => {
173214 </div >
174215 </div >
175216
176- <div class =" container" >
217+ <!-- Mobile split view with glimpse -->
218+ <div
219+ v-if =" isMobile"
220+ class =" mobile-container"
221+ >
222+ <div
223+ class =" panel-wrapper code-panel"
224+ :class =" { active: activePanel === 'code', collapsed: activePanel === 'chart' }"
225+ @click =" activePanel === 'chart' ? activePanel = 'code' : null"
226+ >
227+ <div
228+ v-show =" activePanel === 'chart'"
229+ class =" panel-label"
230+ >
231+ CODE
232+ </div >
233+ <div class =" panel-content-wrapper" >
234+ <CodeEditor v-model =" code" />
235+ </div >
236+ </div >
237+
238+ <div
239+ class =" panel-wrapper chart-panel"
240+ :class =" { active: activePanel === 'chart', collapsed: activePanel === 'code' }"
241+ @click =" activePanel === 'code' ? activePanel = 'chart' : null"
242+ >
243+ <div
244+ v-show =" activePanel === 'code'"
245+ class =" panel-label"
246+ >
247+ CHART
248+ </div >
249+ <div class =" panel-content-wrapper" >
250+ <OutputDisplay
251+ :messages =" messages"
252+ :is-loading =" isLoading"
253+ :is-executing =" isExecuting"
254+ />
255+ <ConsoleOutput
256+ ref =" consoleRef"
257+ :messages =" messages"
258+ />
259+ </div >
260+ </div >
261+ </div >
262+
263+ <!-- Desktop view -->
264+ <div
265+ v-else
266+ class =" container"
267+ >
177268 <div class =" editor-section" >
178269 <CodeEditor v-model =" code" />
179270 </div >
@@ -233,33 +324,117 @@ onMounted(async () => {
233324 min-height : 0 ;
234325}
235326
327+ /* Mobile toolbar */
328+ .mobile-toolbar {
329+ background : white ;
330+ border-bottom : 1px solid #e5e7eb ;
331+ padding : 0.25rem ;
332+ display : flex ;
333+ align-items : center ;
334+ gap : 0.25rem ;
335+ flex-shrink : 0 ;
336+ height : 32px ;
337+ }
338+
339+
340+ /* Mobile split view */
341+ .mobile-container {
342+ flex : 1 ;
343+ display : flex ;
344+ overflow : hidden ;
345+ position : relative ;
346+ width : 100% ;
347+ }
348+
349+ .panel-wrapper {
350+ position : relative ;
351+ height : 100% ;
352+ overflow : hidden ;
353+ }
354+
355+ .panel-wrapper.active {
356+ flex : 1 ;
357+ }
358+
359+ .panel-wrapper.collapsed {
360+ width : 80px ;
361+ flex-shrink : 0 ;
362+ cursor : pointer ;
363+ position : relative ;
364+ overflow : hidden ;
365+ }
366+
367+ .panel-wrapper.collapsed :first-child {
368+ border-right : 1px solid #e5e7eb ;
369+ }
370+
371+ .panel-wrapper.collapsed :last-child {
372+ border-left : 1px solid #e5e7eb ;
373+ }
374+
375+ .panel-label {
376+ position : absolute ;
377+ top : 50% ;
378+ left : 50% ;
379+ transform : translate (-50% , -50% ) rotate (-90deg );
380+ font-size : 0.75rem ;
381+ font-weight : 700 ;
382+ color : #374151 ;
383+ text-transform : uppercase ;
384+ letter-spacing : 0.1em ;
385+ white-space : nowrap ;
386+ z-index : 10 ;
387+ background : rgba (255 , 255 , 255 , 0.95 );
388+ padding : 0.375rem 0.75rem ;
389+ border-radius : 4px ;
390+ box-shadow : 0 1px 3px rgba (0 , 0 , 0 , 0.1 );
391+ }
392+
393+ .panel-content-wrapper {
394+ position : absolute ;
395+ top : 0 ;
396+ left : 0 ;
397+ height : 100% ;
398+ width : calc (100vw - 80px );
399+ overflow : hidden ;
400+ }
401+
402+ .panel-wrapper.active .panel-content-wrapper {
403+ opacity : 1 ;
404+ overflow : auto ;
405+ }
406+
407+ .panel-wrapper.collapsed .panel-content-wrapper {
408+ opacity : 0.3 ;
409+ pointer-events : none ;
410+ }
411+
412+ /* Desktop toolbar */
236413.toolbar {
237414 background : white ;
238415 border-bottom : 1px solid #e5e7eb ;
239416 padding : 0.75rem 1rem ;
240417 display : flex ;
241418 justify-content : space-between ;
242419 align-items : center ;
243- box-shadow : 0 1px 3px rgba (0 , 0 , 0 , 0.1 );
244420 flex-shrink : 0 ;
245421 gap : 1rem ;
246- flex-wrap : wrap ;
247422}
248423
249424.toolbar-left {
250425 display : flex ;
251426 gap : 0.75rem ;
252427 align-items : center ;
253- flex-wrap : wrap ;
254428}
255429
256430.toolbar-right {
257431 display : flex ;
258432 gap : 0.75rem ;
259433 align-items : center ;
260- flex-wrap : wrap ;
261434}
262435
436+
437+
263438.container {
264439 flex : 1 ;
265440 display : grid ;
@@ -291,12 +466,12 @@ onMounted(async () => {
291466.bottom-bar {
292467 background : white ;
293468 border-top : 1px solid #e5e7eb ;
294- padding : 0.75 rem 1rem ;
469+ padding : 0.5 rem 1rem ;
295470 display : flex ;
296471 justify-content : space-between ;
297472 align-items : center ;
298- box-shadow : 0 -1px 3 px rgba (0 , 0 , 0 , 0.1 );
299- min-height : 52 px ;
473+ box-shadow : 0 -1px 2 px rgba (0 , 0 , 0 , 0.05 );
474+ min-height : 48 px ;
300475 flex-shrink : 0 ;
301476 gap : 1rem ;
302477}
@@ -324,36 +499,44 @@ onMounted(async () => {
324499
325500/* Mobile styles */
326501@media (max-width : 768px ) {
327- .toolbar {
328- padding : 0.5rem ;
329- gap : 0.5rem ;
502+ .mobile-toolbar {
503+ display : flex ;
504+ }
505+
506+ .mobile-container {
507+ display : flex ;
508+ flex : 1 ;
509+ min-height : 0 ;
510+ height : 100% ;
330511 }
331512
332- .toolbar-left ,
333- .toolbar-right {
334- width : 100% ;
335- justify-content : space-between ;
336- gap : 0.5rem ;
513+ .toolbar {
514+ display : none ;
337515 }
338516
339517 .container {
340- grid-template-columns : 1fr ;
341- grid-template-rows : 1fr 1fr ;
518+ display : none ;
342519 }
343520
344- .editor-section {
345- border-right : none ;
346- border-bottom : 1 px solid #e5e7eb ;
347- padding : 0.5 rem ;
521+ .bottom-bar {
522+ padding : 0.25 rem 0.5 rem ;
523+ min-height : 36 px ;
524+ box-shadow : 0 -1 px 1 px rgba ( 0 , 0 , 0 , 0.05 ) ;
348525 }
349526
350- .output-section {
351- min-height : 40vh ;
527+ .panel-wrapper {
528+ min-height : 0 ;
529+ height : 100% ;
352530 }
353531
354- .bottom-bar {
355- padding : 0.5rem ;
356- min-height : 48px ;
532+ .panel-wrapper.active {
533+ width : calc (100% - 80px );
534+ flex : none ;
535+ }
536+
537+ .code-panel .panel-content-wrapper ,
538+ .chart-panel .panel-content-wrapper {
539+ height : 100% ;
357540 }
358541}
359542
@@ -363,15 +546,21 @@ onMounted(async () => {
363546 height : 100vh ;
364547 }
365548
366- .container {
367- display : flex ;
368- flex-direction : column ;
549+ .panel-wrapper.collapsed {
550+ width : 50px ; /* Even smaller on very narrow screens */
369551 }
370552
371- .editor-section ,
372- .output-section {
373- flex : 1 ;
374- min-height : 0 ;
553+ .panel-wrapper.active {
554+ width : calc (100% - 50px );
555+ }
556+
557+ .panel-content-wrapper {
558+ width : calc (100vw - 50px ); /* Adjust for smaller collapsed width */
559+ }
560+
561+ .panel-label {
562+ font-size : 0.625rem ;
563+ padding : 0.25rem 0.375rem ;
375564 }
376565}
377566 </style >
0 commit comments