Commit 7e54c673 authored by Ayush's avatar Ayush

Add compilation and file save components and optimize code

parent 0c541874
<app-header></app-header> <app-header></app-header>
<app-l-d-mode></app-l-d-mode>
<div class="container"> <div class="container">
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>
.container { .container {
padding: 10px 20px; padding: 10px 20px;
} }
app-l-d-mode {
position: fixed;
top: 10px;
right: 10px;
z-index: 1000;
}
...@@ -8,16 +8,23 @@ import { HttpClientModule } from '@angular/common/http'; ...@@ -8,16 +8,23 @@ import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component'; import { HeaderComponent } from './header/header.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { LDModeComponent } from './l-d-mode/l-d-mode.component';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
import { IdeCompileComponent } from './ide-compile/ide-compile.component';
import { InputComponent } from './input/input.component';
import { TestcaseStatusComponent } from './testcase-status/testcase-status.component';
import { SaveFileComponent } from './save-file/save-file.component';
import { FileComponent } from './file/file.component'; import { FileComponent } from './file/file.component';
import { FileService } from './file.service';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
HeaderComponent, HeaderComponent,
routerComponents, routerComponents,
LDModeComponent, IdeCompileComponent,
InputComponent,
TestcaseStatusComponent,
SaveFileComponent,
FileComponent FileComponent
], ],
imports: [ imports: [
...@@ -27,7 +34,7 @@ import { FileComponent } from './file/file.component'; ...@@ -27,7 +34,7 @@ import { FileComponent } from './file/file.component';
ReactiveFormsModule, ReactiveFormsModule,
HttpClientModule HttpClientModule
], ],
providers: [ApiService], providers: [ApiService, FileService],
bootstrap: [AppComponent], bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
}) })
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
background: transparent; background: transparent;
color: #ddf; color: #ddf;
border: 2px solid #ddf; border: 2px solid #ddf;
padding: 5px 10px;
margin: 2px; margin: 2px;
cursor: pointer; cursor: pointer;
transition: 0.25s all; transition: 0.25s all;
......
...@@ -9,7 +9,7 @@ import { HttpClient } from '@angular/common/http'; ...@@ -9,7 +9,7 @@ import { HttpClient } from '@angular/common/http';
}) })
export class FileService { export class FileService {
baseApiUrl = "https://file.io"; baseApiUrl = 'https://file.io';
constructor(private http: HttpClient) { } constructor(private http: HttpClient) { }
...@@ -22,20 +22,19 @@ export class FileService { ...@@ -22,20 +22,19 @@ export class FileService {
for (const item of arr) { for (const item of arr) {
ret.push({ ret.push({
id: item, id: item,
filename: "lambda", filename: 'lambda',
language: "cpp/py/java", language: '.cpp',
text: "Random Shit", text: 'Random Shit',
}); });
} }
return of(ret); return of(ret);
} }
upload(file):Observable<any> { upload(file): Observable<any> {
const formData = new FormData(); const formData = new FormData();
formData.append("file", file, file.name); formData.append('file', new Blob([new TextEncoder().encode(file)], {type: 'application/json'}), file.name);
return this.http.post(this.baseApiUrl, formData) return this.http.post(this.baseApiUrl, formData);
} }
} }
...@@ -4,4 +4,3 @@ export interface File { ...@@ -4,4 +4,3 @@ export interface File {
language: string; language: string;
text: string; text: string;
} }
\ No newline at end of file
<div id="compile-cover" [class.open]="isActive"></div>
<div id="compile-popup" [class.open]="isActive">
<p id="compile-status" [innerHTML]="statusVal"></p>
<button id="compile-done" [class.disabled]="resultVal === 0" (click)="resultVal !== 0 && setState(false)">Done</button>
</div>
#compile-popup {
position: fixed;
top: calc(40vh - 100px);
width: 30vw;
left: calc(35vw - 20px);
border: 2px solid #ddf;
padding: 20px;
background: #224;
transform: scale(0);
opacity: 0;
transition: all 0.25s;
z-index: 1000;
&.open {
transform: none;
opacity: 1;
}
}
#compile-done {
float: right;
}
#compile-cover {
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: none;
&.open {
display: block;
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IdeCompileComponent } from './ide-compile.component';
describe('IdeCompileComponent', () => {
let component: IdeCompileComponent;
let fixture: ComponentFixture<IdeCompileComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ IdeCompileComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(IdeCompileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-ide-compile',
templateUrl: './ide-compile.component.html',
styleUrls: ['./ide-compile.component.scss']
})
export class IdeCompileComponent implements OnInit {
@Input() statusVal: string;
@Input() resultVal: number;
isActive = false;
constructor() { }
ngOnInit(): void { }
setState(inp: boolean): void {
this.isActive = inp;
}
set status(value: string) {
this.statusVal = value;
}
}
<div class="container"> <div class="container">
<div id="toggle-lang">C++</div> <div id="toggle-lang">C++</div>
<div id="attempt"> <div id="attempt">
<button id="input">Input</button> <span *ngIf="!isUpToDate"></span>
<button id="run">Run Code</button> <span id="ideFileName">{{file.filename + file.language}}</span>
<button id="save">Save File</button> <button id="inputBtn" (click)="inputField.setState(true);">Input</button>
<button id="runBtn" (click)="this.runField.isActive = true; this.runCodeService.run()">Run Code</button>
<button id="saveBtn" (click)="!isSaved ? this.saveField.setState(true) : updateFile();" [class.disabled]="isUploading || isUpToDate">{{isUploading ? "Saving File..." : "Save File"}}</button>
</div> </div>
<label for="editor"></label> <label for="editor"></label>
<textarea name="editor" id="editor"></textarea> <textarea name="editor" id="editor" value="{{inp}}"></textarea>
</div> </div>
<app-input (valueEmit)="updateInput($event)"></app-input>
<app-ide-compile [statusVal]="runCodeService.status" [resultVal]="runCodeService.result"></app-ide-compile>
<app-save-file [file]="file" (savedFile)="isSaved = isUpToDate = true;"></app-save-file>
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
top: 22px; top: 22px;
z-index: 100; z-index: 100;
cursor: pointer; cursor: pointer;
user-select: none;
} }
label { label {
...@@ -23,14 +24,13 @@ ...@@ -23,14 +24,13 @@
bottom: 24px; bottom: 24px;
z-index: 100; z-index: 100;
#run, #runBtn,
#input, #inputBtn,
#save { #saveBtn {
display: inline-block; display: inline-block;
background: transparent; background: transparent;
color: #ddf; color: #ddf;
border: 2px solid #ddf; border: 2px solid #ddf;
padding: 5px 10px;
margin: 2px; margin: 2px;
cursor: pointer; cursor: pointer;
transition: 0.25s all; transition: 0.25s all;
...@@ -40,10 +40,20 @@ ...@@ -40,10 +40,20 @@
background: var(--color); background: var(--color);
color: var(--bgcolor); color: var(--bgcolor);
} }
&.disabled:hover {
cursor: default;
background: transparent;
color: #ddf;
}
} }
} }
} }
#ideFileName {
margin-right: 10px;
}
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.container { .container {
padding: 20px 0px; padding: 20px 0px;
......
import { Component, OnInit } from '@angular/core'; import {Component, OnInit, ViewChild} from '@angular/core';
import { Router } from '@angular/router'; import {Router} from '@angular/router';
import {InputComponent} from '../input/input.component';
import {IdeCompileComponent} from '../ide-compile/ide-compile.component';
import {RunCodeService} from '../run-code.service';
import {SaveFileComponent} from '../save-file/save-file.component';
import {File} from '../file';
import {FileService} from '../file.service';
declare const CodeMirror: any; declare const CodeMirror: any;
@Component({ @Component({
...@@ -9,7 +16,22 @@ declare const CodeMirror: any; ...@@ -9,7 +16,22 @@ declare const CodeMirror: any;
}) })
export class IdeComponent implements OnInit { export class IdeComponent implements OnInit {
constructor(public router: Router) { } inp = '';
extensions = ['.cpp', '.py', '.java'];
isSaved = false;
isUploading = false;
isUpToDate = false;
file: File = {
id: null,
filename: 'Untitled',
language: '.cpp',
text: ''
};
@ViewChild(InputComponent) inputField;
@ViewChild(IdeCompileComponent) runField;
@ViewChild(SaveFileComponent) saveField;
constructor(public router: Router, public runCodeService: RunCodeService, private fileService: FileService) {}
ngOnInit(): void { ngOnInit(): void {
const editorArea = document.getElementById('editor'); const editorArea = document.getElementById('editor');
...@@ -30,7 +52,7 @@ export class IdeComponent implements OnInit { ...@@ -30,7 +52,7 @@ export class IdeComponent implements OnInit {
let activeLang = 0; let activeLang = 0;
const langs = ['C++', 'Python', 'Java']; const langs = ['C++', 'Python', 'Java'];
const langsMime = ['text/x-c++src', 'text/x-python', 'text/x-java']; const langsMime = ['text/x-c++src', 'text/x-python', 'text/x-java'];
const defaultCode = [`#include <iostream> const code = [`#include <iostream>
using namespace std; using namespace std;
int main() { int main() {
...@@ -42,15 +64,41 @@ int main() { ...@@ -42,15 +64,41 @@ int main() {
} }
}`]; }`];
editor.setValue(defaultCode[activeLang]); editor.setValue(code[activeLang]);
document.getElementById('toggle-lang').onclick = function(): void { this.file.text = code[activeLang];
const th = this as HTMLDivElement; const tl = document.getElementById('toggle-lang') as HTMLDivElement;
tl.onclick = () => {
activeLang = (activeLang + 1) % 3; activeLang = (activeLang + 1) % 3;
th.innerHTML = langs[activeLang]; this.file.language = this.extensions[activeLang];
editor.setValue(defaultCode[activeLang]); tl.innerHTML = langs[activeLang];
editor.setValue(code[activeLang]);
editor.setOption('mode', langsMime[activeLang]); editor.setOption('mode', langsMime[activeLang]);
}; };
editor.on('update', () => {
this.isUpToDate = false;
code[activeLang] = editor.getValue();
this.file.text = editor.getValue();
});
} }
updateInput(val: string): void {
this.inp = val;
}
updateFile(): void {
const btn = document.getElementById('saveBtn') as HTMLButtonElement;
if (btn.classList.contains('disabled')) {return; }
this.isUploading = true;
btn.innerHTML = 'Saving...';
this.fileService.upload(this.file)
.subscribe(data => {
btn.innerHTML = 'Saved';
this.isUploading = false;
this.isUpToDate = true;
}, error => {
console.log(error);
});
}
} }
<div id="input-cover" [class.open]="isActive"></div>
<div id="input" [class.open]="isActive">
<label for="inp" id="title">Enter the input:</label>
<br>
<textarea name="inp" id="inp" cols="30" rows="10" spellcheck="false"></textarea>
<br>
<button id="done" (click)="this.setState(false);">Done</button>
</div>
#input {
position: fixed;
top: calc(40vh - 100px);
width: 30vw;
left: calc(35vw - 20px);
border: 2px solid #ddf;
padding: 20px;
background: #224;
transform: scale(0);
opacity: 0;
transition: all 0.25s;
z-index: 1000;
&.open {
transform: none;
opacity: 1;
}
}
#inp {
box-sizing: border-box;
width: 100%;
color: #ddf;
font-size: 1.1em;
background: transparent;
border: none;
font-family: 'Anonymous Pro', monospace;
resize: none;
}
#done {
float: right;
}
#input-cover {
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: none;
&.open {
display: block;
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LDModeComponent } from './l-d-mode.component'; import { InputComponent } from './input.component';
describe('LDModeComponent', () => { describe('InputComponent', () => {
let component: LDModeComponent; let component: InputComponent;
let fixture: ComponentFixture<LDModeComponent>; let fixture: ComponentFixture<InputComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ LDModeComponent ] declarations: [ InputComponent ]
}) })
.compileComponents(); .compileComponents();
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(LDModeComponent); fixture = TestBed.createComponent(InputComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss']
})
export class InputComponent implements OnInit {
isActive = false;
value: string;
@Output() valueEmit = new EventEmitter<string>();
constructor() { }
ngOnInit(): void { }
setState(value: boolean): void {
const text = document.getElementById('inp') as HTMLTextAreaElement;
this.isActive = value;
if (value) {
text.focus();
} else {
this.valueEmit.emit(text.value);
}
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-l-d-mode',
templateUrl: './l-d-mode.component.html',
styleUrls: ['./l-d-mode.component.scss']
})
export class LDModeComponent implements OnInit {
constructor() { }
currentTheme: any;
ngOnInit(): void {
this.currentTheme = 0;
const btn = document.getElementById('button') as HTMLButtonElement;
btn.onclick = () => {
this.currentTheme = 1 - this.currentTheme;
document.querySelector('body').classList.toggle('light');
if (btn.innerHTML === 'Light') { btn.innerHTML = 'Dark'; }
else { btn.innerHTML = 'Light'; }
const str = this.currentTheme === 1 ? '2px solid #112' : '2px solid #ddf';
Object.assign((document.getElementsByClassName('CodeMirror')[0] as HTMLTextAreaElement).style, {
border: str
});
};
}
}
...@@ -47,6 +47,7 @@ form { ...@@ -47,6 +47,7 @@ form {
&:hover { &:hover {
background: transparent; background: transparent;
color: #ddf;
} }
} }
} }
......
import { TestBed } from '@angular/core/testing';
import { RunCodeService } from './run-code.service';
describe('RunCodeService', () => {
let service: RunCodeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(RunCodeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class RunCodeService {
status = 'Compiling...';
result = 0;
run(): void {
const output = `25
abcd
150`;
const statusArray = ['Uploading...', 'Compiling...', 'Running...', `Done!
<div class="code">${output}</div>`];
this.result = 0;
let time = 0;
for (const status of statusArray) {
setTimeout(() => {
this.status = status;
}, time);
time += 2000;
}
setTimeout(() => {
this.result = 1;
}, 6000);
}
constructor() { }
}
<div id="save-cover" [class.open]="isActive"></div>
<div id="save-popup" [class.open]="isActive">
<div *ngIf="!isUploading">
<label for="filenameInput">Enter Filename:</label>
<input id="filenameInput" type="text" name="filenameInput" [(ngModel)]="file.filename">
<span>{{file.language}}</span>
<br>
<button id="save-cancel" (click)="isActive = false">Cancel</button>
<button id="save-done" [class.disabled]="file.filename === ''" (click)="file.filename !== '' && submitFile()">Done</button>
</div>
<div *ngIf="isUploading">Uploading...</div>
</div>
#save-popup {
position: fixed;
top: calc(40vh - 100px);
width: 30vw;
left: calc(35vw - 20px);
border: 2px solid #ddf;
padding: 20px;
background: #224;
transform: scale(0);
opacity: 0;
transition: all 0.25s;
z-index: 1000;
&.open {
transform: none;
opacity: 1;
}
}
#filenameInput {
box-sizing: border-box;
width: 100%;
color: #ddf;
font-size: 1.05em;
background: transparent;
border: none;
font-family: Lato, sans-serif;
}
#save-done, #save-cancel {
float: right;
}
#save-cover {
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: none;
&.open {
display: block;
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SaveFileComponent } from './save-file.component';
describe('SaveFileComponent', () => {
let component: SaveFileComponent;
let fixture: ComponentFixture<SaveFileComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SaveFileComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SaveFileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core';
import {FileService} from '../file.service';
import {File} from '../file';
@Component({
selector: 'app-save-file',
templateUrl: './save-file.component.html',
styleUrls: ['./save-file.component.scss']
})
export class SaveFileComponent implements OnInit {
isActive = false;
@Output() savedFile = new EventEmitter<boolean>();
@Input() file: File;
isUploading = false;
constructor(public fileService: FileService) { }
ngOnInit(): void { }
setState(value: boolean): void {
const text = document.getElementById('filenameInput') as HTMLTextAreaElement;
this.isActive = value;
if (value) {
text.focus();
}
}
submitFile(): void {
this.isUploading = true;
this.fileService.upload(this.file)
.subscribe((response) => {
this.isUploading = false;
this.savedFile.emit(true);
this.isActive = false;
}, (error) => {
console.log(error);
this.isUploading = false;
});
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TestcaseStatusComponent } from './testcase-status.component';
describe('TestcaseStatusComponent', () => {
let component: TestcaseStatusComponent;
let fixture: ComponentFixture<TestcaseStatusComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TestcaseStatusComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(TestcaseStatusComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-testcase-status',
templateUrl: './testcase-status.component.html',
styleUrls: ['./testcase-status.component.scss']
})
export class TestcaseStatusComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
...@@ -19,17 +19,6 @@ ...@@ -19,17 +19,6 @@
--dark: #0004; --dark: #0004;
--mix: #111144; --mix: #111144;
--tbg: #ddf1; --tbg: #ddf1;
.light {
--color: #112;
--bgcolor: #ddf;
--box-shadow: #88a;
--gc1: #ddddff;
--gc2: #ccccff;
--dark: #8884;
--mix: #8888bb;
--tbg: #1121;
}
} }
nav { nav {
...@@ -65,7 +54,7 @@ button { ...@@ -65,7 +54,7 @@ button {
button { button {
background: transparent; background: transparent;
border: 2px solid var(--color); border: 2px solid var(--color);
padding: 10px 20px; padding: 5px 10px;
cursor: pointer; cursor: pointer;
transition: all 0.25s; transition: all 0.25s;
...@@ -73,6 +62,16 @@ button { ...@@ -73,6 +62,16 @@ button {
background: var(--color); background: var(--color);
color: var(--bgcolor); color: var(--bgcolor);
} }
&.disabled {
opacity: 0.2;
cursor: default;
&:hover {
background: transparent;
color: #eef;
}
}
} }
.inline-code, .inline-code,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment