From f4c86a06e21c81c79802eccd5a2fa0141ca87ead Mon Sep 17 00:00:00 2001 From: garrettmills Date: Sun, 9 Feb 2020 02:07:31 -0600 Subject: [PATCH] Add VSCode editor component --- angular.json | 5 + package-lock.json | 11 +- package.json | 2 + src/app/app.module.ts | 2 + src/app/components/components.module.ts | 10 +- .../editor/code/code.component.html | 24 +++ .../editor/code/code.component.scss | 4 + .../components/editor/code/code.component.ts | 198 ++++++++++++++++++ .../editor/database/database.component.ts | 3 + .../editor/host/host.component.html | 8 + .../components/editor/host/host.component.ts | 1 - .../node-picker/node-picker.component.html | 6 +- src/app/structures/HostRecord.ts | 2 +- 13 files changed, 267 insertions(+), 9 deletions(-) create mode 100644 src/app/components/editor/code/code.component.html create mode 100644 src/app/components/editor/code/code.component.scss create mode 100644 src/app/components/editor/code/code.component.ts diff --git a/angular.json b/angular.json index 7a173c6..1094c5b 100644 --- a/angular.json +++ b/angular.json @@ -29,6 +29,11 @@ "glob": "**/*.svg", "input": "node_modules/ionicons/dist/ionicons/svg", "output": "./svg" + }, + { + "glob": "**/*", + "input": "node_modules/ngx-monaco-editor/assets/monaco", + "output": "./assets/monaco/" } ], "styles": [ diff --git a/package-lock.json b/package-lock.json index 2f99926..ff39d68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7099,6 +7099,14 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, + "ngx-monaco-editor": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/ngx-monaco-editor/-/ngx-monaco-editor-8.1.1.tgz", + "integrity": "sha512-SmUGFG5m2HHWybHD6qXdB1FrtC/XySSHm5O/E1cDGW7moIWzJGqiitCKLJdSh9D2hsoe8oBNEg74vYF1UGznsQ==", + "requires": { + "tslib": "^1.9.0" + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -10369,8 +10377,7 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "validate-npm-package-license": { "version": "3.0.4", diff --git a/package.json b/package.json index fa6850f..5fabd49 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,10 @@ "ag-grid-community": "^22.1.1", "angular-tree-component": "^8.5.2", "core-js": "^2.5.4", + "ngx-monaco-editor": "^8.1.1", "rxjs": "~6.5.1", "tslib": "^1.9.0", + "uuid": "^3.4.0", "zone.js": "~0.9.1" }, "devDependencies": { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 05f2a93..3ee0f61 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -12,6 +12,7 @@ import { HttpClientModule } from '@angular/common/http'; import { ComponentsModule } from './components/components.module'; import { TreeModule } from 'angular-tree-component'; import {AgGridModule} from 'ag-grid-angular'; +import {MonacoEditorModule} from 'ngx-monaco-editor'; @NgModule({ declarations: [AppComponent], @@ -24,6 +25,7 @@ import {AgGridModule} from 'ag-grid-angular'; ComponentsModule, TreeModule.forRoot(), AgGridModule.withComponents([]), + MonacoEditorModule.forRoot(), ], providers: [ StatusBar, diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index c75dc64..a2ae438 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -7,11 +7,13 @@ import {DatabaseComponent} from './editor/database/database.component'; import {AgGridModule} from 'ag-grid-angular'; import {ColumnsComponent} from './editor/database/columns/columns.component'; import {FormsModule} from '@angular/forms'; +import {CodeComponent} from './editor/code/code.component'; +import {MonacoEditorModule} from 'ngx-monaco-editor'; @NgModule({ - declarations: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent], - imports: [CommonModule, IonicModule, AgGridModule, FormsModule], - entryComponents: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent], - exports: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent] + declarations: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent, CodeComponent], + imports: [CommonModule, IonicModule, AgGridModule, FormsModule, MonacoEditorModule], + entryComponents: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent, CodeComponent], + exports: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent, CodeComponent] }) export class ComponentsModule {} diff --git a/src/app/components/editor/code/code.component.html b/src/app/components/editor/code/code.component.html new file mode 100644 index 0000000..f9d5a43 --- /dev/null +++ b/src/app/components/editor/code/code.component.html @@ -0,0 +1,24 @@ +
+ + + Language + + {{lang}} + + + +
+ +
+ + +  Drop Editor +  Save Changes + + +
diff --git a/src/app/components/editor/code/code.component.scss b/src/app/components/editor/code/code.component.scss new file mode 100644 index 0000000..ef5db85 --- /dev/null +++ b/src/app/components/editor/code/code.component.scss @@ -0,0 +1,4 @@ +div.code-wrapper { + border: 2px solid #8c8c8c; + border-radius: 3px; +} diff --git a/src/app/components/editor/code/code.component.ts b/src/app/components/editor/code/code.component.ts new file mode 100644 index 0000000..cda9b7b --- /dev/null +++ b/src/app/components/editor/code/code.component.ts @@ -0,0 +1,198 @@ +import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; +import {v4} from 'uuid'; +import HostRecord from '../../../structures/HostRecord'; +import {Observable} from 'rxjs'; +import {ApiService} from '../../../service/api.service'; +import {AlertController, LoadingController} from '@ionic/angular'; + +@Component({ + selector: 'editor-code', + templateUrl: './code.component.html', + styleUrls: ['./code.component.scss'], +}) +export class CodeComponent implements OnInit { + @Input() hostRecord: HostRecord; + @Output() hostRecordChange = new EventEmitter(); + @Output() requestParentSave = new EventEmitter(); + @Output() requestParentDelete = new EventEmitter(); + @ViewChild('theEditor', {static: false}) theEditor; + + public dirty = false; + public pendingSetup = true; + protected dbRecord: any = {}; + + public languageOptions: Array = [ + 'ABAP', + 'AES', + 'Apex', + 'AZCLI', + 'Bat', + 'C', + 'Cameligo', + 'Clojure', + 'CoffeeScript', + 'Cpp', + 'Csharp', + 'CSP', + 'CSS', + 'Dockerfile', + 'Fsharp', + 'Go', + 'GraphQL', + 'Handlebars', + 'HTML', + 'INI', + 'Java', + 'JavaScript', + 'JSON', + 'Kotlin', + 'LeSS', + 'Lua', + 'Markdown', + 'MiPS', + 'MSDAX', + 'MySQL', + 'Objective-C', + 'Pascal', + 'Pascaligo', + 'Perl', + 'pgSQL', + 'PHP', + 'Plaintext', + 'Postiats', + 'PowerQuery', + 'PowerShell', + 'Pug', + 'Python', + 'R', + 'Razor', + 'Redis', + 'RedShift', + 'RestructuredText', + 'Ruby', + 'Rust', + 'SB', + 'Scheme', + 'SCSS', + 'Shell', + 'SOL', + 'SQL', + 'St', + 'Swift', + 'TCL', + 'Twig', + 'TypeScript', + 'VB', + 'XML', + 'YAML', + ]; + + public editorOptions = { + language: 'javascript', + uri: v4(), + }; + public editorValue = ''; + + constructor( + protected api: ApiService, + protected loader: LoadingController, + protected alerts: AlertController, + ) { } + + ngOnInit() { + this.loader.create({message: 'Loading code...'}).then(loader => { + loader.present().then(() => { + this.getInitObservable().subscribe(() => { + this.editorOptions.language = this.dbRecord.language; + this.onSelectChange(false); + loader.dismiss(); + }); + }); + }); + } + + getInitObservable(): Observable { + return new Observable(sub => { + if ( this.hostRecord && this.pendingSetup ) { + if ( !this.hostRecord.Value ) { + this.hostRecord.Value = {}; + } + + if ( !this.hostRecord.Value.Value ) { + this.api.post(`/code/${this.hostRecord.PageId}/${this.hostRecord.UUID}/create`).subscribe(res => { + this.dbRecord = res.data; + this.hostRecord.Value.Mode = 'code'; + this.hostRecord.Value.Value = res.data.UUID; + this.hostRecord.value = res.data.UUID; + this.hostRecordChange.emit(this.hostRecord); + this.pendingSetup = false; + sub.next(true); + sub.complete(); + }); + } else { + this.api.get(`/code/${this.hostRecord.PageId}/${this.hostRecord.UUID}/get/${this.hostRecord.Value.Value}`).subscribe(res => { + this.dbRecord = res.data; + this.editorValue = this.dbRecord.code; + this.editorOptions.language = this.dbRecord.language; + this.pendingSetup = false; + sub.next(true); + sub.complete(); + }); + } + } else { + this.pendingSetup = true; + } + }); + } + + onSaveClick() { + this.dbRecord.code = this.editorValue; + this.dbRecord.language = this.editorOptions.language; + this.api.post(`/code/${this.hostRecord.PageId}/${this.hostRecord.UUID}/set/${this.hostRecord.Value.Value}`, this.dbRecord) + .subscribe(res => { + this.dbRecord = res.data; + this.editorOptions.language = this.dbRecord.language; + this.editorValue = this.dbRecord.code; + this.dirty = false; + }); + } + + async onDropClick() { + const alert = await this.alerts.create({ + header: 'Are you sure?', + message: `You are about to delete this code. This action cannot be undone.`, + buttons: [ + { + text: 'Keep It', + role: 'cancel', + }, + { + text: 'Delete It', + handler: async () => { + this.api.post(`/code/${this.hostRecord.PageId}/${this.hostRecord.UUID}/delete/${this.hostRecord.Value.Value}`) + .subscribe(res => { + this.requestParentDelete.emit(this); + }); + }, + }, + ], + }); + + await alert.present(); + } + + public onEditorModelChange($event) { + if ( this.editorValue !== this.dbRecord.code ) { + this.dirty = true; + } + } + + onSelectChange(updateDbRecord = true) { + if ( updateDbRecord ) { + this.dbRecord.language = this.editorOptions.language; + } + + this.editorOptions = {...this.editorOptions}; + } + +} diff --git a/src/app/components/editor/database/database.component.ts b/src/app/components/editor/database/database.component.ts index e0261c3..ce9596f 100644 --- a/src/app/components/editor/database/database.component.ts +++ b/src/app/components/editor/database/database.component.ts @@ -203,6 +203,9 @@ export class DatabaseComponent implements OnInit { getInitObservable(): Observable { return new Observable(sub => { if ( this.hostRecord && this.pendingSetup ) { + if ( !this.hostRecord.Value ) { + this.hostRecord.Value = {}; + } if ( !this.hostRecord.Value.Value ) { this.api.post(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/create`).subscribe(res => { this.dbRecord = res.data; diff --git a/src/app/components/editor/host/host.component.html b/src/app/components/editor/host/host.component.html index e7cc0f8..d265088 100644 --- a/src/app/components/editor/host/host.component.html +++ b/src/app/components/editor/host/host.component.html @@ -34,4 +34,12 @@ (requestParentDelete)="onRequestDelete($event)" > +
+ +
diff --git a/src/app/components/editor/host/host.component.ts b/src/app/components/editor/host/host.component.ts index 9aeb144..80168ab 100644 --- a/src/app/components/editor/host/host.component.ts +++ b/src/app/components/editor/host/host.component.ts @@ -22,7 +22,6 @@ export class HostComponent implements OnInit { ngOnInit() {} onRecordChange($event) { - console.log({$event}); this.recordChange.emit($event); } diff --git a/src/app/components/editor/node-picker/node-picker.component.html b/src/app/components/editor/node-picker/node-picker.component.html index 5a8d183..466ad27 100644 --- a/src/app/components/editor/node-picker/node-picker.component.html +++ b/src/app/components/editor/node-picker/node-picker.component.html @@ -20,7 +20,7 @@ Heading 4 - + Monospace Block @@ -31,4 +31,8 @@ Database + + + Code Editor + diff --git a/src/app/structures/HostRecord.ts b/src/app/structures/HostRecord.ts index 3613f60..9bebeb3 100644 --- a/src/app/structures/HostRecord.ts +++ b/src/app/structures/HostRecord.ts @@ -1,6 +1,6 @@ export default class HostRecord { public value = ''; - public type: 'paragraph'|'header1'|'header2'|'header3'|'header4'|'block_code'|'click_link'|'database_ref'|'ul' = 'paragraph'; + public type: 'paragraph'|'header1'|'header2'|'header3'|'header4'|'block_code'|'click_link'|'database_ref'|'ul'|'code_ref' = 'paragraph'; public CreatedAt: string; public PageId: string;