Initial editor functionality and data bindings
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
8a9f6d508e
commit
2291b99512
15
package-lock.json
generated
15
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
src/app/directives/directives.module.ts
Normal file
9
src/app/directives/directives.module.ts
Normal 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 {}
|
30
src/app/directives/dom-change.directive.ts
Normal file
30
src/app/directives/dom-change.directive.ts
Normal 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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user