@covalent/code-editor
Version:
Teradata UI Platform Code Editor Module
391 lines (386 loc) • 16 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, PLATFORM_ID, forwardRef, Component, Inject, ViewChild, Output, Input, NgModule } from '@angular/core';
import { isPlatformServer, CommonModule } from '@angular/common';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { of, Subject, merge, fromEvent, timer } from 'rxjs';
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { editor, languages } from 'monaco-editor';
import { mixinControlValueAccessor, mixinDisabled } from '@covalent/core/common';
const noop = () => {
// empty method
};
// counter for ids to allow for multiple editors on one page
let uniqueCounter = 0;
class TdCodeEditorBase {
constructor(_changeDetectorRef) {
this._changeDetectorRef = _changeDetectorRef;
}
}
const _TdCodeEditorMixinBase = mixinControlValueAccessor(mixinDisabled(TdCodeEditorBase), []);
class TdCodeEditorComponent extends _TdCodeEditorMixinBase {
/**
* value?: string
*/
set value(value) {
this._value = value;
if (this._componentInitialized) {
this.applyValue();
}
}
get value() {
return this._value;
}
applyValue() {
if (!this._fromEditor) {
this._editor.setValue(this._value || '');
}
this._fromEditor = false;
this.propagateChange(this._value);
this.editorValueChange.emit();
}
registerOnChange(fn) {
this.propagateChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
/**
* getEditorContent?: function
* Returns the content within the editor
*/
getValue() {
if (!this._componentInitialized) {
return of('');
}
setTimeout(() => {
this._subject.next(this._value);
this._subject.complete();
this._subject = new Subject();
});
return this._subject.asObservable();
}
/**
* language?: string
* language used in editor
*/
set language(language) {
this._language = language;
if (this._componentInitialized) {
this.applyLanguage();
}
}
get language() {
return this._language;
}
applyLanguage() {
if (this._language) {
editor.setModelLanguage(this._editor.getModel(), this._language);
this.editorLanguageChanged.emit();
}
}
/**
* registerLanguage?: function
* Registers a custom Language within the editor
*/
registerLanguage(language) {
if (this._componentInitialized) {
languages.register({ id: language.id });
this._disposables.push(languages.setMonarchTokensProvider(language.id, {
tokenizer: {
root: language.monarchTokensProvider,
},
}));
// Define a new theme that constains only rules that match this language
editor.defineTheme(language.customTheme.id, language.customTheme.theme);
this._theme = language.customTheme.id;
this._disposables.push(languages.registerCompletionItemProvider(language.id, {
provideCompletionItems: () => {
return language.completionItemProvider;
},
}));
const css = document.createElement('style');
css.type = 'text/css';
css.innerHTML = language.monarchTokensProviderCSS;
document.body.appendChild(css);
this.editorConfigurationChanged.emit();
this._registeredLanguagesStyles = [
...this._registeredLanguagesStyles,
css,
];
}
}
/**
* style?: string
* css style of the editor on the page
*/
set editorStyle(editorStyle) {
this._editorStyle = editorStyle;
if (this._componentInitialized) {
this.applyStyle();
}
}
get editorStyle() {
return this._editorStyle;
}
applyStyle() {
if (this._editorStyle) {
const containerDiv = this._editorContainer.nativeElement;
containerDiv.setAttribute('style', this._editorStyle);
}
}
/**
* theme?: string
* Theme to be applied to editor
*/
set theme(theme) {
this._theme = theme;
if (this._componentInitialized) {
this._editor.updateOptions({ theme });
this.editorConfigurationChanged.emit();
}
}
get theme() {
return this._theme;
}
/**
* fullScreenKeyBinding?: number
* See here for key bindings https://microsoft.github.io/monaco-editor/api/enums/keycode.html
* Sets the KeyCode for shortcutting to Fullscreen mode
*/
set fullScreenKeyBinding(keycode) {
this._keycode = keycode;
}
get fullScreenKeyBinding() {
return this._keycode;
}
/**
* editorOptions?: object
* Options used on editor instantiation. Available options listed here:
* https://microsoft.github.io/monaco-editor/api/interfaces/editor.ieditoroptions.html
*/
set editorOptions(editorOptions) {
this._editorOptions = editorOptions;
if (this._componentInitialized) {
this._editor.updateOptions(editorOptions);
this.editorConfigurationChanged.emit();
}
}
get editorOptions() {
return this._editorOptions;
}
/**
* layout method that calls layout method of editor and instructs the editor to remeasure its container
*/
layout() {
if (this._componentInitialized) {
this._editor.layout();
}
}
/**
* Returns if in Full Screen Mode or not
*/
get isFullScreen() {
return this._isFullScreen;
}
// tslint:disable-next-line:member-ordering
constructor(_changeDetectorRef, _elementRef, _ngZone, platformId) {
super(_changeDetectorRef);
this._elementRef = _elementRef;
this._ngZone = _ngZone;
this.platformId = platformId;
this._destroy = new Subject();
this._widthSubject = new Subject();
this._heightSubject = new Subject();
this._editorStyle = 'width:100%;height:100%;';
this._value = '';
this._theme = 'vs';
this._language = 'javascript';
this._subject = new Subject();
this._editorInnerContainer = 'editorInnerContainer' + uniqueCounter++;
this._fromEditor = false;
this._componentInitialized = false;
this._editorOptions = {};
this._isFullScreen = false;
this._registeredLanguagesStyles = [];
this._disposables = [];
/**
* editorInitialized: function($event)
* Event emitted when editor is first initialized
*/
this.editorInitialized = new EventEmitter();
/**
* editorConfigurationChanged: function($event)
* Event emitted when editor's configuration changes
*/
this.editorConfigurationChanged = new EventEmitter();
/**
* editorLanguageChanged: function($event)
* Event emitted when editor's Language changes
*/
this.editorLanguageChanged = new EventEmitter();
/**
* editorValueChange: function($event)
* Event emitted any time something changes the editor value
*/
this.editorValueChange = new EventEmitter();
this.propagateChange = (_) => noop;
}
ngOnInit() {
if (isPlatformServer(this.platformId)) {
return;
}
const containerDiv = this._editorContainer.nativeElement;
containerDiv.id = this._editorInnerContainer;
this._editor = editor.create(containerDiv, Object.assign({
value: this._value,
language: this.language,
theme: this._theme,
}, this.editorOptions));
this._componentInitialized = true;
setTimeout(() => {
this.applyLanguage();
this._fromEditor = true;
this.applyValue();
this.applyStyle();
this.editorInitialized.emit(this._editor);
this.editorConfigurationChanged.emit();
});
// The `onDidChangeContent` returns a disposable object (an object with `dispose()` method) which will cleanup
// the listener. The callback, that we pass to `onDidChangeContent`, captures `this`. This leads to a circular reference
// (`td-code-editor -> monaco -> td-code-editor`) and prevents the `td-code-editor` from being GC'd.
this._disposables.push(this._editor.getModel().onDidChangeContent(() => {
this._fromEditor = true;
this.writeValue(this._editor.getValue());
this.layout();
}));
this.addFullScreenModeCommand();
this._ngZone.runOutsideAngular(() => merge(fromEvent(window, 'resize').pipe(debounceTime(100)), this._widthSubject.asObservable().pipe(distinctUntilChanged()), this._heightSubject.asObservable().pipe(distinctUntilChanged()))
.pipe(debounceTime(100), takeUntil(this._destroy))
.subscribe(() => {
// Note: this is being called outside of the Angular zone since we don't have to
// run change detection whenever the editor resizes itself.
this.layout();
}));
this._ngZone.runOutsideAngular(() => timer(500, 250)
.pipe(takeUntil(this._destroy))
.subscribe(() => {
const { width, height } = this._elementRef.nativeElement.getBoundingClientRect();
this._widthSubject.next(width);
this._heightSubject.next(height);
}));
}
ngOnDestroy() {
this._changeDetectorRef.detach();
this._registeredLanguagesStyles.forEach((style) => style.remove());
while (this._disposables.length) {
this._disposables.pop()?.dispose();
}
if (this._editor) {
this._editor.dispose();
}
this._destroy.next(true);
this._destroy.unsubscribe();
}
/**
* showFullScreenEditor request for full screen of Code Editor based on its browser type.
*/
showFullScreenEditor() {
if (this._componentInitialized) {
const codeEditorElement = this._editorContainer
.nativeElement;
codeEditorElement.requestFullscreen();
}
this._isFullScreen = true;
}
/**
* exitFullScreenEditor request to exit full screen of Code Editor based on its browser type.
*/
exitFullScreenEditor() {
if (this._componentInitialized) {
document.exitFullscreen();
}
this._isFullScreen = false;
}
/**
* addFullScreenModeCommand used to add the fullscreen option to the context menu
*/
addFullScreenModeCommand() {
this._disposables.push(this._editor.addAction({
// An unique identifier of the contributed action.
id: 'fullScreen',
// A label of the action that will be presented to the user.
label: 'Full Screen',
// An optional array of keybindings for the action.
contextMenuGroupId: 'navigation',
keybindings: this._keycode,
contextMenuOrder: 1.5,
// Method that will be executed when the action is triggered.
run: () => {
this.showFullScreenEditor();
},
}));
}
}
TdCodeEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TdCodeEditorComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
TdCodeEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TdCodeEditorComponent, selector: "td-code-editor", inputs: { value: "value", language: "language", editorStyle: "editorStyle", theme: "theme", fullScreenKeyBinding: "fullScreenKeyBinding", editorOptions: "editorOptions" }, outputs: { editorInitialized: "editorInitialized", editorConfigurationChanged: "editorConfigurationChanged", editorLanguageChanged: "editorLanguageChanged", editorValueChange: "editorValueChange" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TdCodeEditorComponent),
multi: true,
},
], viewQueries: [{ propertyName: "_editorContainer", first: true, predicate: ["editorContainer"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"editor-container\" #editorContainer></div>\n", styles: [":host{display:block;position:relative}:host .editor-container{position:absolute;inset:0}::ng-deep .monaco-aria-container{display:none}\n"] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TdCodeEditorComponent, decorators: [{
type: Component,
args: [{ selector: 'td-code-editor', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TdCodeEditorComponent),
multi: true,
},
], template: "<div class=\"editor-container\" #editorContainer></div>\n", styles: [":host{display:block;position:relative}:host .editor-container{position:absolute;inset:0}::ng-deep .monaco-aria-container{display:none}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; }, propDecorators: { _editorContainer: [{
type: ViewChild,
args: ['editorContainer', { static: true }]
}], editorInitialized: [{
type: Output
}], editorConfigurationChanged: [{
type: Output
}], editorLanguageChanged: [{
type: Output
}], editorValueChange: [{
type: Output
}], value: [{
type: Input
}], language: [{
type: Input
}], editorStyle: [{
type: Input
}], theme: [{
type: Input
}], fullScreenKeyBinding: [{
type: Input
}], editorOptions: [{
type: Input
}] } });
class CovalentCodeEditorModule {
}
CovalentCodeEditorModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CovalentCodeEditorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
CovalentCodeEditorModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: CovalentCodeEditorModule, bootstrap: [TdCodeEditorComponent], declarations: [TdCodeEditorComponent], imports: [CommonModule], exports: [TdCodeEditorComponent] });
CovalentCodeEditorModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CovalentCodeEditorModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CovalentCodeEditorModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [TdCodeEditorComponent],
exports: [TdCodeEditorComponent],
bootstrap: [TdCodeEditorComponent],
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { CovalentCodeEditorModule, TdCodeEditorComponent };
//# sourceMappingURL=covalent-code-editor.mjs.map