You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

128 lines
3.4 KiB

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() set contents(val: string) {
if ( this.currentContents !== val ) {
this.currentContents = val;
}
}
get contents(): string {
return this.currentContents;
}
@Output() contentsChanged: EventEmitter<string> = new EventEmitter<string>();
public currentContents = '';
public isFocused = false;
protected hadOneFocusOut = false;
protected isEditOnly = false;
public get editonly() {
return this.isEditOnly;
}
@Input()
public set editonly(val: boolean) {
this.isEditOnly = val;
if ( this.isEditOnly && !this.readonly ) {
this.isFocused = true;
}
}
public get displayContents() {
return (this.contents || 'Double-click to edit...').replace(/</g, '\n<')
.replace(/(https?:\/\/[^\s]+)/g, (val) => `<a href="${val}" target="_blank">${val}</a>`);
}
public isDark() {
return document.body.classList.contains('dark');
}
ngOnInit() { }
onFocusIn(event: MouseEvent) {
console.log('on focus in', event);
this.isFocused = !this.readonly;
}
@HostListener('document:keyup.escape', ['$event'])
onFocusOut(event) {
if ( this.isEditOnly ) {
return;
}
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');
}
}