novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
176 lines (151 loc) • 5.29 kB
text/typescript
// NG2
import { Component, Input, Output, ViewChild, EventEmitter, NgZone, forwardRef, AfterViewInit, OnDestroy } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
// Value accessor for the component (supports ngModel)
const CKEDITOR_CONTROL_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NovoCKEditorElement),
multi: true
};
declare var CKEDITOR: any;
/**
* CKEditor component
* Usage :
* <novo-editor [(ngModel)]="data" [config]="{...}" debounce="500"></novo-editor>
*/
export class NovoCKEditorElement implements OnDestroy, AfterViewInit {
config;
debounce;
name;
change = new EventEmitter();
ready = new EventEmitter();
blur = new EventEmitter();
focus = new EventEmitter();
paste = new EventEmitter();
loaded = new EventEmitter();
host;
_value: string = '';
instance;
debounceTimeout;
constructor(private zone: NgZone) { }
get value() {
return this._value;
}
set value(v) {
if (v !== this._value) {
this._value = v;
this.onChange(v);
}
}
ngOnDestroy() {
if (this.instance) {
this.instance.focusManager.blur(true); // Remove focus from editor
setTimeout(() => {
this.instance.removeAllListeners();
CKEDITOR.instances[this.instance.name].destroy();
this.instance.destroy();
this.instance = null;
});
}
}
ngAfterViewInit() {
let config = this.config || this.getBaseConfig();
this.ckeditorInit(config);
}
updateValue(value) {
this.zone.run(() => {
this.value = value;
this.onChange(value);
this.onTouched();
this.change.emit(value);
});
}
ckeditorInit(config) {
if (!CKEDITOR) {
console.error('Make sure to include CKEditor sources in your dependencies!');
return;
}
// CKEditor replace textarea
this.instance = CKEDITOR.replace(this.host.nativeElement, config);
// Set initial value
this.instance.setData(this.value);
// listen for instanceReady event
this.instance.on('instanceReady', (evt) => {
// send the evt to the EventEmitter
this.ready.emit(evt);
});
// CKEditor change event
this.instance.on('change', () => {
this.onTouched();
let value = this.instance.getData();
// Debounce update
if (this.debounce) {
if (this.debounceTimeout) {
clearTimeout(this.debounceTimeout);
}
this.debounceTimeout = setTimeout(() => {
this.updateValue(value);
this.debounceTimeout = null;
}, parseInt(this.debounce));
} else {
this.updateValue(value);
}
});
this.instance.on('blur', (event) => {
this.blur.emit(event);
});
this.instance.on('focus', (event) => {
this.focus.emit(event);
});
this.instance.on('paste', (event) => {
this.paste.emit(event);
});
this.instance.on('loaded', (event) => {
this.loaded.emit(event);
});
}
getBaseConfig() {
return {
enterMode : CKEDITOR.ENTER_BR,
shiftEnterMode: CKEDITOR.ENTER_P,
disableNativeSpellChecker: false,
removePlugins: 'liststyle,tabletools,contextmenu', // allows browser based spell checking
toolbar: [
{ name: 'clipboard', items: ['Paste', 'PasteText', 'PasteFromWord', 'Undo', 'Redo'] },
{ name: 'paragraph', items: ['NumberedList', 'BulletedList', 'Outdent', 'Indent', 'Blockquote', 'CreateDiv', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', 'BidiLtr', 'BidiRtl'] },
{ name: 'links', items: ['Link'] },
{ name: 'insert', items: ['Image', 'Table', 'HorizontalRule'] },
{ name: 'tools', items: ['Maximize', 'Source'] },
'/',
{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript'] },
{ name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize'] },
{ name: 'colors', items: ['TextColor', 'BGColor'] }
]
};
}
writeValue(value) {
this._value = value;
if (this.instance) {
this.instance.setData(value);
}
}
onChange(value?: any) {
}
onTouched(event?) {
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
insertText(text) {
let trimmedText = text.trim();
this.instance.insertText(trimmedText);
}
}