diff --git a/package-lock.json b/package-lock.json index 915c0ff..70c5219 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index a646a21..a8230b0 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index 89a0bbc..91ad8bf 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -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, diff --git a/src/app/components/nodes/EditorNode.contract.ts b/src/app/components/nodes/EditorNode.contract.ts index 7aeb9c0..b854218 100644 --- a/src/app/components/nodes/EditorNode.contract.ts +++ b/src/app/components/nodes/EditorNode.contract.ts @@ -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; diff --git a/src/app/components/nodes/norm/norm.component.html b/src/app/components/nodes/norm/norm.component.html index 17ad38c..a0e939c 100644 --- a/src/app/components/nodes/norm/norm.component.html +++ b/src/app/components/nodes/norm/norm.component.html @@ -1,66 +1,68 @@
+ (focusout)="onFocusIn($event)">
-
+
-
+ +
-
+ +
-
+ +
+
-
- -
-
- -
-
+
+ + +
-
+
-
+ +
+
-
+
-
+ +
-
+ +
+
-
+
+
- Content editable! -
+ appDomChange + [innerHTML]="initialValue" + #editable + (domChange)="onContentsChanged($event)" + >
\ No newline at end of file diff --git a/src/app/components/nodes/norm/norm.component.ts b/src/app/components/nodes/norm/norm.component.ts index 218c564..b6491a1 100644 --- a/src/app/components/nodes/norm/norm.component.ts +++ b/src/app/components/nodes/norm/norm.component.ts @@ -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 { - 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; + } + } } diff --git a/src/app/directives/directives.module.ts b/src/app/directives/directives.module.ts new file mode 100644 index 0000000..37c21dd --- /dev/null +++ b/src/app/directives/directives.module.ts @@ -0,0 +1,9 @@ +import {NgModule} from '@angular/core'; +import {DomChangeDirective} from './dom-change.directive'; + +@NgModule({ + imports: [], + exports: [DomChangeDirective], + declarations: [DomChangeDirective], +}) +export class DirectivesModule {} diff --git a/src/app/directives/dom-change.directive.ts b/src/app/directives/dom-change.directive.ts new file mode 100644 index 0000000..5cb2b0d --- /dev/null +++ b/src/app/directives/dom-change.directive.ts @@ -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(); + } +}