Skip to content

Commit 3a1c8e2

Browse files
authored
Merge pull request #258 from php-school/cloud-homepage-rory
Cloud homepage rory
2 parents 9a7e64b + 93a4df5 commit 3a1c8e2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4359
-1546
lines changed

.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"singleQuote": true,
3+
"semi": false,
4+
"tabWidth": 2
5+
6+
}

app/config.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@
182182
]
183183
);
184184

185-
$renderer->addJs('jquery', '//code.jquery.com/jquery-1.12.0.min.js');
186-
$renderer->addJs('highlight.js', '//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js');
185+
//$renderer->addJs('jquery', '//code.jquery.com/jquery-1.12.0.min.js');
186+
//$renderer->addJs('highlight.js', '//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js');
187187

188188
$manifest = $c->get(ViteManifest::class);
189189
$renderer->addJs('main-js', $manifest->assetUrl('main.js'));

assets/cloud.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import VueShepherdPlugin from './shepherd-plugin';
1919
import results from "./components/results/results.js";
2020
import StudentDropdown from "./components/StudentDropdown.vue";
2121
import ListWorkshops from "./components/ListWorkshops.vue";
22+
import Home from "./components/Home.vue";
2223

2324
const components = {
2425
AceEditor,
@@ -32,7 +33,8 @@ const components = {
3233
ExerciseVerify,
3334
ExerciseEditor,
3435
StudentDropdown,
35-
ListWorkshops
36+
ListWorkshops,
37+
Home
3638
}
3739

3840
const app = createApp({

assets/components/AceEditor.vue

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import ace from 'ace-builds';
44
import 'ace-builds/src-noconflict/theme-monokai';
55
import 'ace-builds/src-noconflict/mode-php';
6+
import 'ace-builds/src-noconflict/ext-language_tools';
7+
import 'ace-builds/src-noconflict/snippets/php';
68
import {markRaw} from "vue";
79
810
export default {
@@ -11,7 +13,10 @@ export default {
1113
default: false,
1214
type: Boolean
1315
},
14-
file: Object,
16+
value: {
17+
type: String,
18+
required: true,
19+
},
1520
minLines: Number,
1621
maxLines: Number,
1722
},
@@ -25,31 +30,55 @@ export default {
2530
options.maxLines = this.maxLines;
2631
}
2732
33+
ace.require("ace/ext/language_tools");
2834
this._editor = markRaw(ace.edit(this.$el, options));
2935
3036
this._editor.setOption('useWorker', false);
37+
this._editor.setShowPrintMargin(false);
3138
this._editor.setTheme("ace/theme/monokai");
39+
this._editor.container.style.lineHeight = 2
3240
this._editor.session.setMode("ace/mode/php");
33-
this._editor.setValue(this.file.content, 1);
41+
42+
this._editor.setOptions({
43+
enableBasicAutocompletion: true,
44+
enableSnippets: true,
45+
enableLiveAutocompletion: false
46+
});
47+
48+
this._editor.setValue(this.value, 1);
49+
this._contentBackup = this.value;
50+
this._isSettingContent = false;
3451
3552
if (this.readonly) {
3653
this._editor.setReadOnly(true);
3754
} else {
3855
this._editor.session.on('change', this.change);
3956
}
4057
},
41-
watch: {
42-
'file.content'(newValue, oldValue) {
43-
if (newValue !== oldValue) {
44-
this._editor.setValue(newValue, 1);
45-
}
46-
}
47-
},
4858
methods: {
4959
change() {
50-
this.file.content = this._editor.session.getValue();
51-
this.$emit('changeContent', this.file);
60+
// ref: https://github.com/CarterLi/vue3-ace-editor/issues/11
61+
if (this._isSettingContent) {
62+
return;
63+
}
64+
const content = this._editor.session.getValue();
65+
this._contentBackup = content;
66+
this.$emit('update:value', content);
67+
},
68+
},
69+
emits: ['update:value'],
70+
watch: {
71+
value(val) {
72+
if (this._contentBackup !== val) {
73+
try {
74+
this._isSettingContent = true;
75+
this._editor.setValue(val, 1);
76+
} finally {
77+
this._isSettingContent = false;
78+
}
79+
this._contentBackup = val;
5280
}
81+
},
5382
},
5483
beforeUnmount() {
5584
this._editor.destroy();
@@ -58,5 +87,24 @@ export default {
5887
</script>
5988

6089
<template>
61-
<div class="h-full border-0"></div>
62-
</template>
90+
<div class="h-full border-0 bg-gray-900"></div>
91+
</template>
92+
93+
<style>
94+
.ace_gutter {
95+
@apply !bg-gradient-to-b to-30% !from-gray-900 !to-[#0c1220];
96+
}
97+
98+
.ace_marker-layer .ace_selection {
99+
@apply !bg-gray-800;
100+
}
101+
102+
.ace_gutter-active-line {
103+
@apply !bg-gray-900;
104+
}
105+
106+
.ace_active-line {
107+
background-color: #0c1220 !important;
108+
}
109+
110+
</style>

assets/components/ExerciseEditor.vue

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
MapIcon,
1414
HomeIcon,
1515
ChevronRightIcon,
16-
ExclamationCircleIcon
16+
ExclamationCircleIcon,
17+
AcademicCapIcon
1718
} from '@heroicons/vue/24/solid';
1819
import {TrophyIcon} from '@heroicons/vue/24/outline'
1920
import 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">

assets/components/FileTree.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,16 @@ export default {
7171
</script>
7272

7373
<template>
74-
<div class="border border-solid border-gray-600 rounded">
75-
<div class="border-b border-solid border-gray-600 p-3 flex justify-between">
74+
<div class="">
75+
<div class="border-b border-solid border-gray-600 py-5 px-3 flex justify-between">
7676
<span class="text-white text-base font-mono">Files</span>
7777
<div v-if="showControls" class="flex text-white">
7878
<XMarkIcon @click="reset" class="mr-2 h-5 w-5 cursor-pointer hover:text-pink-500" style="fill: none !important;"/>
7979
<FolderPlusIcon @click="addFolder" class="mr-2 h-5 w-5 cursor-pointer hover:text-pink-500" style="fill: none !important;"/>
8080
<PlusIcon @click="addFile" class="mr-2 h-5 w-5 cursor-pointer hover:text-pink-500" style="fill: none !important;"/>
8181
</div>
8282
</div>
83-
<ul class="w-full text-gray-300 font-mono p-1">
83+
<ul class="w-full text-gray-300 font-mono">
8484
<tree-item
8585
v-for="file in files"
8686
:parent="files"

0 commit comments

Comments
 (0)