Add VSCode editor component

This commit is contained in:
garrettmills 2020-02-09 02:07:31 -06:00
parent b5d2f64fd3
commit f4c86a06e2
13 changed files with 267 additions and 9 deletions

View File

@ -29,6 +29,11 @@
"glob": "**/*.svg", "glob": "**/*.svg",
"input": "node_modules/ionicons/dist/ionicons/svg", "input": "node_modules/ionicons/dist/ionicons/svg",
"output": "./svg" "output": "./svg"
},
{
"glob": "**/*",
"input": "node_modules/ngx-monaco-editor/assets/monaco",
"output": "./assets/monaco/"
} }
], ],
"styles": [ "styles": [

11
package-lock.json generated
View File

@ -7099,6 +7099,14 @@
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
"dev": true "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": { "nice-try": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@ -10369,8 +10377,7 @@
"uuid": { "uuid": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
"dev": true
}, },
"validate-npm-package-license": { "validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",

View File

@ -27,8 +27,10 @@
"ag-grid-community": "^22.1.1", "ag-grid-community": "^22.1.1",
"angular-tree-component": "^8.5.2", "angular-tree-component": "^8.5.2",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"ngx-monaco-editor": "^8.1.1",
"rxjs": "~6.5.1", "rxjs": "~6.5.1",
"tslib": "^1.9.0", "tslib": "^1.9.0",
"uuid": "^3.4.0",
"zone.js": "~0.9.1" "zone.js": "~0.9.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -12,6 +12,7 @@ import { HttpClientModule } from '@angular/common/http';
import { ComponentsModule } from './components/components.module'; import { ComponentsModule } from './components/components.module';
import { TreeModule } from 'angular-tree-component'; import { TreeModule } from 'angular-tree-component';
import {AgGridModule} from 'ag-grid-angular'; import {AgGridModule} from 'ag-grid-angular';
import {MonacoEditorModule} from 'ngx-monaco-editor';
@NgModule({ @NgModule({
declarations: [AppComponent], declarations: [AppComponent],
@ -24,6 +25,7 @@ import {AgGridModule} from 'ag-grid-angular';
ComponentsModule, ComponentsModule,
TreeModule.forRoot(), TreeModule.forRoot(),
AgGridModule.withComponents([]), AgGridModule.withComponents([]),
MonacoEditorModule.forRoot(),
], ],
providers: [ providers: [
StatusBar, StatusBar,

View File

@ -7,11 +7,13 @@ import {DatabaseComponent} from './editor/database/database.component';
import {AgGridModule} from 'ag-grid-angular'; import {AgGridModule} from 'ag-grid-angular';
import {ColumnsComponent} from './editor/database/columns/columns.component'; import {ColumnsComponent} from './editor/database/columns/columns.component';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {CodeComponent} from './editor/code/code.component';
import {MonacoEditorModule} from 'ngx-monaco-editor';
@NgModule({ @NgModule({
declarations: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent], declarations: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent, CodeComponent],
imports: [CommonModule, IonicModule, AgGridModule, FormsModule], imports: [CommonModule, IonicModule, AgGridModule, FormsModule, MonacoEditorModule],
entryComponents: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent], entryComponents: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent, CodeComponent],
exports: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent] exports: [HostComponent, NodePickerComponent, DatabaseComponent, ColumnsComponent, CodeComponent]
}) })
export class ComponentsModule {} export class ComponentsModule {}

View File

@ -0,0 +1,24 @@
<div class="code-wrapper" style="width: 100%; height: 600px; margin-top: 10px;">
<ion-toolbar>
<ion-item>
<ion-label position="floating">Language</ion-label>
<ion-select style="min-width: 40px;" [(ngModel)]="editorOptions.language" (ionChange)="onSelectChange()">
<ion-select-option *ngFor="let lang of languageOptions" [value]="lang.toLowerCase()">{{lang}}</ion-select-option>
</ion-select>
</ion-item>
</ion-toolbar>
<div class="ed-wrapper" style="width: 100%; height: 480px;">
<ngx-monaco-editor style="width: 100%; height: 100%;"
[options]="editorOptions"
[(ngModel)]="editorValue"
(ngModelChange)="onEditorModelChange($event)"
#theEditor
></ngx-monaco-editor>
</div>
<ion-toolbar>
<ion-buttons slot="end">
<ion-button (click)="onDropClick()"><ion-icon name="alert" color="danger"></ion-icon>&nbsp;Drop Editor</ion-button>
<ion-button (click)="onSaveClick()"><ion-icon name="save" [color]="dirty ? 'warning' : 'success'"></ion-icon>&nbsp;Save Changes</ion-button>
</ion-buttons>
</ion-toolbar>
</div>

View File

@ -0,0 +1,4 @@
div.code-wrapper {
border: 2px solid #8c8c8c;
border-radius: 3px;
}

View File

@ -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<HostRecord>();
@Output() requestParentSave = new EventEmitter<CodeComponent>();
@Output() requestParentDelete = new EventEmitter<CodeComponent>();
@ViewChild('theEditor', {static: false}) theEditor;
public dirty = false;
public pendingSetup = true;
protected dbRecord: any = {};
public languageOptions: Array<string> = [
'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<any> {
return new Observable<any>(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};
}
}

View File

@ -203,6 +203,9 @@ export class DatabaseComponent implements OnInit {
getInitObservable(): Observable<any> { getInitObservable(): Observable<any> {
return new Observable<any>(sub => { return new Observable<any>(sub => {
if ( this.hostRecord && this.pendingSetup ) { if ( this.hostRecord && this.pendingSetup ) {
if ( !this.hostRecord.Value ) {
this.hostRecord.Value = {};
}
if ( !this.hostRecord.Value.Value ) { if ( !this.hostRecord.Value.Value ) {
this.api.post(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/create`).subscribe(res => { this.api.post(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/create`).subscribe(res => {
this.dbRecord = res.data; this.dbRecord = res.data;

View File

@ -34,4 +34,12 @@
(requestParentDelete)="onRequestDelete($event)" (requestParentDelete)="onRequestDelete($event)"
></editor-database> ></editor-database>
</div> </div>
<div class="code-wrapper" *ngIf="record.type === 'code_ref'">
<editor-code
[hostRecord]="record"
(hostRecordChange)="onRecordChange($event)"
(requestParentSave)="onRequestParentSave($event)"
(requestParentDelete)="onRequestDelete($event)"
></editor-code>
</div>
</ng-container> </ng-container>

View File

@ -22,7 +22,6 @@ export class HostComponent implements OnInit {
ngOnInit() {} ngOnInit() {}
onRecordChange($event) { onRecordChange($event) {
console.log({$event});
this.recordChange.emit($event); this.recordChange.emit($event);
} }

View File

@ -20,7 +20,7 @@
<ion-label>Heading 4</ion-label> <ion-label>Heading 4</ion-label>
</ion-item> </ion-item>
<ion-item button (click)="onSelect('block_code')"> <ion-item button (click)="onSelect('block_code')">
<ion-icon slot="start" name="code"></ion-icon> <ion-icon slot="start" name="information"></ion-icon>
<ion-label>Monospace Block</ion-label> <ion-label>Monospace Block</ion-label>
</ion-item> </ion-item>
<ion-item button (click)="onSelect('click_link')"> <ion-item button (click)="onSelect('click_link')">
@ -31,4 +31,8 @@
<ion-icon slot="start" name="analytics"></ion-icon> <ion-icon slot="start" name="analytics"></ion-icon>
<ion-label>Database</ion-label> <ion-label>Database</ion-label>
</ion-item> </ion-item>
<ion-item button (click)="onSelect('code_ref')">
<ion-icon slot="start" name="code"></ion-icon>
<ion-label>Code Editor</ion-label>
</ion-item>
</ion-list> </ion-list>

View File

@ -1,6 +1,6 @@
export default class HostRecord { export default class HostRecord {
public value = ''; 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 CreatedAt: string;
public PageId: string; public PageId: string;