Initial editor functionality and data bindings
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Garrett Mills 2020-10-13 20:19:38 -05:00
parent 8a9f6d508e
commit 2291b99512
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
8 changed files with 121 additions and 36 deletions

15
package-lock.json generated
View File

@ -1935,6 +1935,21 @@
"schema-utils": "^2.7.0"
}
},
"@ng-stack/contenteditable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@ng-stack/contenteditable/-/contenteditable-1.1.0.tgz",
"integrity": "sha512-bmu0PFWNgAw+shTDlQQV6gBiUcbp6VEwl51fGCUci5GAhKtLwYEe/biPA6Q6tsqSP4l1/XV/HUvroKek1+csOg==",
"requires": {
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
}
}
},
"@ngtools/webpack": {
"version": "10.1.6",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-10.1.6.tgz",

View File

@ -25,6 +25,7 @@
"@ionic-native/splash-screen": "^5.0.0",
"@ionic-native/status-bar": "^5.0.0",
"@ionic/angular": "^5.3.5",
"@ng-stack/contenteditable": "^1.1.0",
"ag-grid-angular": "^22.1.1",
"ag-grid-community": "^22.1.1",
"core-js": "^2.5.4",

View File

@ -6,7 +6,8 @@ import {IonicModule} from '@ionic/angular';
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 {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {ContenteditableModule} from '@ng-stack/contenteditable';
import {CodeComponent} from './editor/code/code.component';
import {MonacoEditorModule} from 'ngx-monaco-editor';
import {FilesComponent} from './editor/files/files.component';
@ -27,6 +28,7 @@ import {BooleanRendererComponent} from './editor/database/renderers/boolean-rend
import {SearchComponent} from './search/Search.component';
import {NormComponent} from './nodes/norm/norm.component';
import {DirectivesModule} from '../directives/directives.module';
@NgModule({
declarations: [
@ -59,7 +61,10 @@ import {NormComponent} from './nodes/norm/norm.component';
IonicModule,
AgGridModule,
FormsModule,
MonacoEditorModule
ReactiveFormsModule,
ContenteditableModule,
MonacoEditorModule,
DirectivesModule,
],
entryComponents: [
HostComponent,

View File

@ -3,6 +3,7 @@ import PageRecord from '../../structures/PageRecord';
export abstract class EditorNodeContract {
protected pageRec!: PageRecord;
protected nodeRec!: any; // TODO
protected initialValue: any;
get page() {
return this.pageRec;

View File

@ -1,66 +1,68 @@
<div class="container"
(focusin)="onFocusIn($event)"
(focusout)="onFocusOut($event)">
(focusout)="onFocusIn($event)">
<div class="toolbar-base" *ngIf="isFocused">
<div class="toolbar-button" title="Bold">
<button class="toolbar-button" title="Bold" (click)="documentCommand('bold')">
<i class="icon fa fa-bold"></i>
</div>
<div class="toolbar-button" title="Italic">
</button>
<button class="toolbar-button" title="Italic" (click)="documentCommand('italic')">
<i class="icon fa fa-italic"></i>
</div>
<div class="toolbar-button" title="Underline">
</button>
<button class="toolbar-button" title="Underline" (click)="documentCommand('underline')">
<i class="icon fa fa-underline"></i>
</div>
<div class="toolbar-button" title="Strikethrough">
</button>
<button class="toolbar-button" title="Strikethrough" (click)="documentCommand('strikeThrough')">
<i class="icon fa fa-strikethrough"></i>
</div>
</button>
<div class="toolbar-sep"></div>
<div class="toolbar-button" title="Align Right">
<i class="icon fa fa-align-right"></i>
</div>
<div class="toolbar-button" title="Align Center">
<i class="icon fa fa-align-center"></i>
</div>
<div class="toolbar-button" title="Align Left">
<button class="toolbar-button" title="Align Left" (click)="documentCommand('justifyLeft')">
<i class="icon fa fa-align-left"></i>
</div>
</button>
<button class="toolbar-button" title="Align Center" (click)="documentCommand('justifyCenter')">
<i class="icon fa fa-align-center"></i>
</button>
<button class="toolbar-button" title="Align Right" (click)="documentCommand('justifyRight')">
<i class="icon fa fa-align-right"></i>
</button>
<div class="toolbar-sep"></div>
<div class="toolbar-button" title="Undo">
<button class="toolbar-button" title="Undo" (click)="documentCommand('undo')">
<i class="icon fa fa-undo"></i>
</div>
<div class="toolbar-button" title="Redo">
</button>
<button class="toolbar-button" title="Redo" (click)="documentCommand('redo')">
<i class="icon fa fa-redo"></i>
</div>
</button>
<div class="toolbar-sep"></div>
<div class="toolbar-button" title="Increase Heading Level">
<button class="toolbar-button" title="Increase Heading Level">
<i class="icon fa fa-heading"></i>
<i class="icon fa fa-long-arrow-alt-up"></i>
</div>
<div class="toolbar-button" title="Decrease Heading Level">
</button>
<button class="toolbar-button" title="Decrease Heading Level">
<i class="icon fa fa-heading"></i>
<i class="icon fa fa-long-arrow-alt-down"></i>
</div>
<div class="toolbar-button" title="Format Monospace">
</button>
<button class="toolbar-button" title="Format Monospace">
<i class="icon fa fa-code"></i>
</div>
</button>
<div class="toolbar-sep"></div>
<div class="toolbar-button" title="Begin Bulleted List">
<button class="toolbar-button" title="Begin Bulleted List">
<i class="icon fa fa-list-ul"></i>
</div>
</button>
</div>
<div
class="editable-base"
[ngClass]="isFocused ? 'focused' : ''"
contenteditable
>
Content editable!
</div>
appDomChange
[innerHTML]="initialValue"
#editable
(domChange)="onContentsChanged($event)"
></div>
</div>

View File

@ -1,4 +1,4 @@
import {Component} from '@angular/core';
import {Component, ViewChild} from '@angular/core';
import {EditorNodeContract} from '../EditorNode.contract';
@Component({
@ -7,10 +7,20 @@ import {EditorNodeContract} from '../EditorNode.contract';
styleUrls: ['./norm.component.scss'],
})
export class NormComponent extends EditorNodeContract {
@ViewChild('editable') editable;
public isFocused = false;
public initialValue = 'Content editable now...';
public contents = '';
private dirtyOverride = false;
constructor() {
super();
console.log('norm compt', this);
this.contents = this.initialValue;
}
public isDirty(): boolean | Promise<boolean> {
return false; // TODO implement
return this.dirtyOverride || this.contents !== this.initialValue;
}
onFocusIn(event: MouseEvent) {
@ -20,4 +30,16 @@ export class NormComponent extends EditorNodeContract {
onFocusOut(event: MouseEvent) {
this.isFocused = false;
}
documentCommand(cmd: string) {
// Yes, technically this is deprecated, but it'll be poly-filled if necessary. Bite me.
document.execCommand(cmd, false, '');
}
onContentsChanged(contents: string) {
const innerHTML = this.editable.nativeElement.innerHTML;
if ( this.contents !== innerHTML ) {
this.contents = innerHTML;
}
}
}

View File

@ -0,0 +1,9 @@
import {NgModule} from '@angular/core';
import {DomChangeDirective} from './dom-change.directive';
@NgModule({
imports: [],
exports: [DomChangeDirective],
declarations: [DomChangeDirective],
})
export class DirectivesModule {}

View File

@ -0,0 +1,30 @@
import {Directive, ElementRef, EventEmitter, OnDestroy, Output} from '@angular/core';
@Directive({
selector: '[appDomChange]'
})
export class DomChangeDirective implements OnDestroy {
private changes: MutationObserver;
@Output()
public domChange = new EventEmitter();
constructor(private elementRef: ElementRef) {
const element = this.elementRef.nativeElement;
this.changes = new MutationObserver((mutations) => {
mutations.forEach(mutation => this.domChange.emit(mutation));
});
this.changes.observe(element, {
attributes: true,
childList: true,
characterData: true,
subtree: true,
});
}
ngOnDestroy() {
this.changes.disconnect();
}
}