From c76fc2e82abcbf22d9a2f3d34f524e613ce5ce1e Mon Sep 17 00:00:00 2001 From: garrettmills Date: Tue, 10 Nov 2020 08:20:22 -0600 Subject: [PATCH] Move WYSIWYG editor to separate component --- src/app/components/components.module.ts | 4 + .../components/nodes/norm/norm.component.html | 88 ++------------- .../components/nodes/norm/norm.component.ts | 79 ++------------ .../components/wysiwyg/wysiwyg.component.html | 83 ++++++++++++++ .../components/wysiwyg/wysiwyg.component.scss | 46 ++++++++ .../components/wysiwyg/wysiwyg.component.ts | 102 ++++++++++++++++++ 6 files changed, 247 insertions(+), 155 deletions(-) create mode 100644 src/app/components/wysiwyg/wysiwyg.component.html create mode 100644 src/app/components/wysiwyg/wysiwyg.component.scss create mode 100644 src/app/components/wysiwyg/wysiwyg.component.ts diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index cd9a930..a9ea8e5 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -33,6 +33,7 @@ import {MarkdownModule} from 'ngx-markdown'; import {VersionModalComponent} from './version-modal/version-modal.component'; import {EditorPageRoutingModule} from '../pages/editor/editor-routing.module'; import {EditorPage} from '../pages/editor/editor.page'; +import {WysiwygComponent} from './wysiwyg/wysiwyg.component'; @NgModule({ declarations: [ @@ -61,6 +62,7 @@ import {EditorPage} from '../pages/editor/editor.page'; MarkdownEditorComponent, VersionModalComponent, EditorPage, + WysiwygComponent, ], imports: [ CommonModule, @@ -100,6 +102,7 @@ import {EditorPage} from '../pages/editor/editor.page'; MarkdownEditorComponent, VersionModalComponent, EditorPage, + WysiwygComponent, ], exports: [ NodePickerComponent, @@ -127,6 +130,7 @@ import {EditorPage} from '../pages/editor/editor.page'; MarkdownEditorComponent, VersionModalComponent, EditorPage, + WysiwygComponent, ] }) export class ComponentsModule {} diff --git a/src/app/components/nodes/norm/norm.component.html b/src/app/components/nodes/norm/norm.component.html index 3964bae..53fdfd8 100644 --- a/src/app/components/nodes/norm/norm.component.html +++ b/src/app/components/nodes/norm/norm.component.html @@ -1,83 +1,7 @@ -
-
- - - - - -
- - - - - -
- - - - -
- - - - - - - -
- - - - -
-
-
+
+
\ 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 d5db4d2..955cced 100644 --- a/src/app/components/nodes/norm/norm.component.ts +++ b/src/app/components/nodes/norm/norm.component.ts @@ -12,16 +12,10 @@ export class NormComponent extends EditorNodeContract implements OnInit { @Input() nodeId: string; @Input() editorUUID?: string; - public isFocused = false; public initialValue = 'Click to edit...'; protected savedValue = 'Click to edit...'; public contents = ''; private dirtyOverride = false; - protected hadOneFocusOut = false; - - public get displayContents() { - return this.contents.replace(/ `${val}`); - } constructor( public editorService: EditorService, @@ -39,6 +33,10 @@ export class NormComponent extends EditorNodeContract implements OnInit { return this.dirtyOverride || this.contents !== this.savedValue; } + public get isReadonly(): boolean { + return !this.editorService.canEdit(); + } + public writeChangesToNode(): void | Promise { this.nodeRec.value = this.contents; this.savedValue = this.contents; @@ -60,75 +58,10 @@ export class NormComponent extends EditorNodeContract implements OnInit { }); } - onFocusIn(event: MouseEvent) { - this.isFocused = this.editorService.canEdit(); - } - - @HostListener('document:keyup.escape', ['$event']) - onFocusOut(event) { - if ( !this.hadOneFocusOut ) { - this.hadOneFocusOut = true; - setTimeout(() => { - this.hadOneFocusOut = false; - }, 500); - } else { - this.isFocused = false; - this.hadOneFocusOut = 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; + if ( this.contents !== contents ) { + this.contents = contents; this.editorService.triggerSave(); } } - - @HostListener('document:keydown.tab', ['$event']) - onIndent(event) { - event.preventDefault(); - this.documentCommand('indent'); - } - - @HostListener('document:keydown.shift.tab', ['$event']) - onOutdent(event) { - event.preventDefault(); - this.documentCommand('outdent'); - } - - @HostListener('document:keydown.control.b', ['$event']) - onBold(event) { - event.preventDefault(); - this.documentCommand('bold'); - } - - @HostListener('document:keydown.control.i', ['$event']) - onItalic(event) { - event.preventDefault(); - this.documentCommand('italic'); - } - - @HostListener('document:keydown.control.u', ['$event']) - onUnderline(event) { - event.preventDefault(); - this.documentCommand('underline'); - } - - @HostListener('document:keydown.control.z', ['$event']) - onUndo(event) { - event.preventDefault(); - this.documentCommand('undo'); - } - - @HostListener('document:keydown.control.shift.z', ['$event']) - onRedo(event) { - event.preventDefault(); - this.documentCommand('redo'); - } } diff --git a/src/app/components/wysiwyg/wysiwyg.component.html b/src/app/components/wysiwyg/wysiwyg.component.html new file mode 100644 index 0000000..9ec7b4f --- /dev/null +++ b/src/app/components/wysiwyg/wysiwyg.component.html @@ -0,0 +1,83 @@ +
+
+ + + + + +
+ + + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+
+
+
\ No newline at end of file diff --git a/src/app/components/wysiwyg/wysiwyg.component.scss b/src/app/components/wysiwyg/wysiwyg.component.scss new file mode 100644 index 0000000..8eb25b8 --- /dev/null +++ b/src/app/components/wysiwyg/wysiwyg.component.scss @@ -0,0 +1,46 @@ +.editable-base { + padding: 20px; + + &.focused { + background: aliceblue; + } +} + +.toolbar-base { + height: 40px; + border: 1px solid lightgray; + border-radius: 5px; + display: flex; + flex-direction: row; + + .toolbar-button { + height: calc(100% - 6px); + min-width: 30px; + display: flex; + justify-content: center; + align-items: center; + margin: 3px; + + &:hover { + background: lightgrey; + cursor: pointer; + } + } + + .toolbar-sep { + height: 100%; + width: 1px; + border: 1px solid lightgrey; + margin: 0 5px; + } +} + +.container.dark { + .editable-base.focused { + background: #404040 !important; + } + + .toolbar-base .toolbar-button:hover { + background: #404040; + } +} diff --git a/src/app/components/wysiwyg/wysiwyg.component.ts b/src/app/components/wysiwyg/wysiwyg.component.ts new file mode 100644 index 0000000..e89b897 --- /dev/null +++ b/src/app/components/wysiwyg/wysiwyg.component.ts @@ -0,0 +1,102 @@ +import {Component, EventEmitter, HostListener, Input, OnInit, Output, ViewChild} from '@angular/core'; + +@Component({ + selector: 'wysiwyg-editor', + templateUrl: './wysiwyg.component.html', + styleUrls: ['./wysiwyg.component.scss'], +}) +export class WysiwygComponent implements OnInit { + @ViewChild('editable') editable; + @Input() readonly = false; + @Input() contents = ''; + @Output() contentsChanged: EventEmitter = new EventEmitter(); + + public isFocused = false; + protected hadOneFocusOut = false; + public initialValue = ''; + + public get displayContents() { + return this.contents.replace(/ `${val}`); + } + + public isDark() { + return document.body.classList.contains('dark'); + } + + ngOnInit() { + this.initialValue = this.contents; + } + + onFocusIn(event: MouseEvent) { + console.log('on focus in', event); + this.isFocused = !this.readonly; + } + + @HostListener('document:keyup.escape', ['$event']) + onFocusOut(event) { + if ( !this.hadOneFocusOut ) { + this.hadOneFocusOut = true; + setTimeout(() => { + this.hadOneFocusOut = false; + }, 500); + } else { + this.isFocused = false; + this.hadOneFocusOut = 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; + this.contentsChanged.emit(innerHTML); + } + } + + @HostListener('document:keydown.tab', ['$event']) + onIndent(event) { + event.preventDefault(); + this.documentCommand('indent'); + } + + @HostListener('document:keydown.shift.tab', ['$event']) + onOutdent(event) { + event.preventDefault(); + this.documentCommand('outdent'); + } + + @HostListener('document:keydown.control.b', ['$event']) + onBold(event) { + event.preventDefault(); + this.documentCommand('bold'); + } + + @HostListener('document:keydown.control.i', ['$event']) + onItalic(event) { + event.preventDefault(); + this.documentCommand('italic'); + } + + @HostListener('document:keydown.control.u', ['$event']) + onUnderline(event) { + event.preventDefault(); + this.documentCommand('underline'); + } + + @HostListener('document:keydown.control.z', ['$event']) + onUndo(event) { + event.preventDefault(); + this.documentCommand('undo'); + } + + @HostListener('document:keydown.control.shift.z', ['$event']) + onRedo(event) { + event.preventDefault(); + this.documentCommand('redo'); + } +}