@@ -13,7 +13,8 @@ import {
1313 MapIcon ,
1414 HomeIcon ,
1515 ChevronRightIcon ,
16- ExclamationCircleIcon
16+ ExclamationCircleIcon ,
17+ AcademicCapIcon
1718} from ' @heroicons/vue/24/solid' ;
1819import {TrophyIcon } from ' @heroicons/vue/24/outline'
1920import PackageSearch from ' ./PackageSearch.vue' ;
@@ -48,6 +49,7 @@ export default {
4849 HomeIcon,
4950 ChevronRightIcon,
5051 ExclamationCircleIcon,
52+ AcademicCapIcon,
5153 OutputMismatch,
5254 Confirm,
5355 },
@@ -63,14 +65,7 @@ export default {
6365 links: Object
6466 },
6567 mounted () {
66- const files = this .getSavedFiles ();
67- for (const fileName in files) {
68- const fileContent = files[fileName];
69- const folderParts = fileName .split (" /" );
7068
71- this .createFileInFolderStructure (this .studentFiles , folderParts, fileContent);
72- this .studentFiles = this .toTree (this .studentFiles );
73- }
7469 },
7570 data () {
7671 // sort the initial files so entry point is at the top
@@ -82,7 +77,18 @@ export default {
8277 const initialFileCopy = this .initialFiles .map (file => {
8378 return {... file}
8479 });
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);
8692
8793 return {
8894 firstRunLoaded: false ,
@@ -122,7 +128,7 @@ export default {
122128 methods: {
123129 getSavedFiles () {
124130 const items = { ... localStorage };
125- const key = this .currentExercise . workshop .code + ' .' + this . currentExercise .exercise .slug ;
131+ const key = this .workshop .code + ' .' + this .exercise .slug ;
126132
127133 const files = {};
128134 for (const localStorageKey in items) {
@@ -198,12 +204,12 @@ export default {
198204
199205 return subdirectory;
200206 },
201- saveSolution (file ) {
207+ saveSolution (fileContent , file ) {
202208 const filePath = toFilePath (file);
203209
204210 localStorage .setItem (
205211 this .currentExercise .workshop .code + ' .' + this .currentExercise .exercise .slug + ' .' + filePath,
206- file . content
212+ fileContent
207213 );
208214 },
209215 resetState () {
@@ -244,6 +250,16 @@ export default {
244250 return ;
245251 }
246252
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+
247263 const found = this .openFiles .find (file => file === selectedFile);
248264
249265 if (! found) {
@@ -316,6 +332,10 @@ export default {
316332 this .loadingResults = false ;
317333 },
318334 closeTab (tab ) {
335+ if (this .openFiles .length === 1 ) {
336+ return ;
337+ }
338+
319339 let index = this .openFiles .findIndex (file => file .name === tab);
320340
321341 this .openFiles .splice (index, 1 );
@@ -449,37 +469,37 @@ export default {
449469
450470 <div class =" h-full flex flex-col" >
451471 <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
454474 :files =" studentFiles"
455475 :file-select-function =" studentSelectFile"
456476 :initial-selected-item =" studentFiles[0]"
457477 :delete-function =" deleteFileOrFolder"
458478 show-controls
459479 @reset =" resetFiles" />
460480 </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 ']" >
463483 <Tabs :tabList =" openFiles.map(file => file.name)" @close-tab =" closeTab" :active-tab =" activeTab" >
464484 <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) "
466486 class =" w-full h-full border-0" />
467487 </template >
468488 </Tabs >
469489 </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 >
474494 <div >
475495 <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" >
477497 <XMarkIcon class =" w-5 h-5" />
478498 </button >
479499 </div >
480500 </div >
481501
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 " >
483503 <div class =" flex-1 space-y-6 py-1" >
484504 <div class =" h-2 bg-slate-700 rounded" ></div >
485505 <div class =" space-y-3" >
0 commit comments