@@ -13,7 +13,8 @@ import {
13
13
MapIcon ,
14
14
HomeIcon ,
15
15
ChevronRightIcon ,
16
- ExclamationCircleIcon
16
+ ExclamationCircleIcon ,
17
+ AcademicCapIcon
17
18
} from ' @heroicons/vue/24/solid' ;
18
19
import {TrophyIcon } from ' @heroicons/vue/24/outline'
19
20
import PackageSearch from ' ./PackageSearch.vue' ;
@@ -48,6 +49,7 @@ export default {
48
49
HomeIcon,
49
50
ChevronRightIcon,
50
51
ExclamationCircleIcon,
52
+ AcademicCapIcon,
51
53
OutputMismatch,
52
54
Confirm,
53
55
},
@@ -63,14 +65,7 @@ export default {
63
65
links: Object
64
66
},
65
67
mounted () {
66
- const files = this .getSavedFiles ();
67
- for (const fileName in files) {
68
- const fileContent = files[fileName];
69
- const folderParts = fileName .split (" /" );
70
68
71
- this .createFileInFolderStructure (this .studentFiles , folderParts, fileContent);
72
- this .studentFiles = this .toTree (this .studentFiles );
73
- }
74
69
},
75
70
data () {
76
71
// sort the initial files so entry point is at the top
@@ -82,7 +77,18 @@ export default {
82
77
const initialFileCopy = this .initialFiles .map (file => {
83
78
return {... file}
84
79
});
85
- const studentFiles = this .toTree (initialFileCopy);
80
+ let studentFiles = this .toTree (initialFileCopy);
81
+
82
+ const files = this .getSavedFiles ();
83
+ for (const fileName in files) {
84
+ const fileContent = files[fileName];
85
+ const folderParts = fileName .split (" /" );
86
+
87
+ this .createFileInFolderStructure (studentFiles, folderParts, fileContent);
88
+ }
89
+
90
+ // make sure new files added from saved files have two way relationship
91
+ studentFiles = this .toTree (studentFiles);
86
92
87
93
return {
88
94
firstRunLoaded: false ,
@@ -122,7 +128,7 @@ export default {
122
128
methods: {
123
129
getSavedFiles () {
124
130
const items = { ... localStorage };
125
- const key = this .currentExercise . workshop .code + ' .' + this . currentExercise .exercise .slug ;
131
+ const key = this .workshop .code + ' .' + this .exercise .slug ;
126
132
127
133
const files = {};
128
134
for (const localStorageKey in items) {
@@ -198,12 +204,12 @@ export default {
198
204
199
205
return subdirectory;
200
206
},
201
- saveSolution (file ) {
207
+ saveSolution (fileContent , file ) {
202
208
const filePath = toFilePath (file);
203
209
204
210
localStorage .setItem (
205
211
this .currentExercise .workshop .code + ' .' + this .currentExercise .exercise .slug + ' .' + filePath,
206
- file . content
212
+ fileContent
207
213
);
208
214
},
209
215
resetState () {
@@ -244,6 +250,16 @@ export default {
244
250
return ;
245
251
}
246
252
253
+ if (! selectedFile .content ) {
254
+ selectedFile .content = ' ' ;
255
+
256
+ if (selectedFile .name .endsWith (' .php' )) {
257
+ selectedFile .content = ' <?php\n\n ' ;
258
+ }
259
+
260
+ this .saveSolution (selectedFile .content , selectedFile);
261
+ }
262
+
247
263
const found = this .openFiles .find (file => file === selectedFile);
248
264
249
265
if (! found) {
@@ -316,6 +332,10 @@ export default {
316
332
this .loadingResults = false ;
317
333
},
318
334
closeTab (tab ) {
335
+ if (this .openFiles .length === 1 ) {
336
+ return ;
337
+ }
338
+
319
339
let index = this .openFiles .findIndex (file => file .name === tab);
320
340
321
341
this .openFiles .splice (index, 1 );
@@ -449,37 +469,37 @@ export default {
449
469
450
470
<div class =" h-full flex flex-col" >
451
471
<div class =" flex flex-1 h-full relative" >
452
- <div class =" w-1/5 p-4 min-w-[300px] " >
453
- <file-tree
472
+ <div class =" w-3/12 xl:w-2/12 " >
473
+ <FileTree
454
474
:files =" studentFiles"
455
475
:file-select-function =" studentSelectFile"
456
476
:initial-selected-item =" studentFiles[0]"
457
477
:delete-function =" deleteFileOrFolder"
458
478
show-controls
459
479
@reset =" resetFiles" />
460
480
</div >
461
- <div class =" flex border-l border-solid border-gray-600 p-4 h-full"
462
- :class =" [openResults ? 'w-3/5 ' : 'w-4/5 ']" >
481
+ <div class =" flex border-l border-solid border-gray-600 h-full"
482
+ :class =" [openResults ? 'w-6/12 xl:w-7/12 ' : 'w-9/12 xl:w-10/12 ']" >
463
483
<Tabs :tabList =" openFiles.map(file => file.name)" @close-tab =" closeTab" :active-tab =" activeTab" >
464
484
<template v-slot :[` tab-content- ` + index ] v-for =" (file , index ) in openFiles " >
465
- <AceEditor :id =" 'editor-' + (index + 1)" :file =" file" @changeContent = " saveSolution"
485
+ <AceEditor :id =" 'editor-' + (index + 1)" v-model:value =" file.content " @update:value = " (content) => saveSolution(content, file) "
466
486
class =" w-full h-full border-0" />
467
487
</template >
468
488
</Tabs >
469
489
</div >
470
- <div v-if =" openResults" id =" results-col"
471
- class =" w-1/5 flex flex-col border-l border-solid border-gray-600 p-4 h-full absolute right-0 overflow-y-scroll" >
472
- <div class =" ml-8 flex justify-between items-center" >
473
- <h1 class =" text-2xl pt-0 " > Results</h1 >
490
+ <div v-show =" openResults" id =" results-col"
491
+ class =" w-3/12 flex flex-col bg-gray-950 border-l border-solid border-gray-600 h-full absolute right-0 overflow-y-scroll" >
492
+ <div class =" pl-4 pr-4 py-4 flex justify-between items-center border-solid border-b border-gray-600 " >
493
+ <h1 class =" text-2xl pt-0 flex items-center " >< AcademicCapIcon class = " h-5 w-5 mr-2 " /> Results</h1 >
474
494
<div >
475
495
<button @click =" openResults = false" type =" button"
476
- class =" text-gray-400 bg-transparent rounded-lg text-sm p-1.5 ml-auto inline-flex items-center hover:bg-gray-600 hover:text-white" >
496
+ class =" text-gray-400 bg-transparent rounded-lg text-sm p-1.5 ml-auto inline-flex items-center hover:hover:text-white" >
477
497
<XMarkIcon class =" w-5 h-5" />
478
498
</button >
479
499
</div >
480
500
</div >
481
501
482
- <div v-show =" loadingResults" class =" animate-pulse flex space-x-4 mt-4" >
502
+ <div v-show =" loadingResults" class =" animate-pulse flex space-x-4 mt-4 px-4 " >
483
503
<div class =" flex-1 space-y-6 py-1" >
484
504
<div class =" h-2 bg-slate-700 rounded" ></div >
485
505
<div class =" space-y-3" >
0 commit comments