RED-6786: faster text editor

This commit is contained in:
Dan Percic 2023-05-26 00:27:53 +03:00
parent 5bbcdac4df
commit 45d6ca6166
4 changed files with 40 additions and 24 deletions

View File

@ -94,6 +94,7 @@
<div *ngIf="withFloatingActions && !!editor?.hasChanges && canEdit && !isLeavingPage" [class.offset]="compare" class="changes-box">
<iqser-icon-button
(action)="saveDictionary.emit()"
[disabled]="!!(_loadingService.isLoading$ | async)"
[label]="'dictionary-overview.save-changes' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:check"

View File

@ -56,7 +56,7 @@ export class DictionaryManagerComponent implements OnChanges {
constructor(
private readonly _dictionaryService: DictionaryService,
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _loadingService: LoadingService,
protected readonly _loadingService: LoadingService,
private readonly _changeRef: ChangeDetectorRef,
readonly activeDossiersService: ActiveDossiersService,
readonly dossierTemplatesService: DossierTemplatesService,

View File

@ -1,10 +1,10 @@
<ngx-monaco-editor
(init)="onCodeEditorInit($event)"
(ngModelChange)="codeEditorTextChanged()"
(ngModelChange)="_editorTextChanged$.next($event)"
(paste)="onPaste()"
*ngIf="!showDiffEditor"
[(ngModel)]="value"
[options]="editorOptions"
(paste)="onPaste()"
></ngx-monaco-editor>
<ngx-monaco-diff-editor

View File

@ -1,6 +1,8 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Debounce, List, LoadingService, OnChange } from '@iqser/common-ui';
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { List, LoadingService, OnChange } from '@iqser/common-ui';
import { EditorThemeService } from '@services/editor-theme.service';
import { debounceTime, filter, tap } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import ICodeEditor = monaco.editor.ICodeEditor;
import IDiffEditor = monaco.editor.IDiffEditor;
@ -25,7 +27,7 @@ const notZero = (lineChange: ILineChange) => lineChange.originalEndLineNumber !=
templateUrl: './editor.component.html',
styleUrls: ['./editor.component.scss'],
})
export class EditorComponent implements OnInit, OnChanges {
export class EditorComponent implements OnInit, OnChanges, OnDestroy {
@Input() showDiffEditor = false;
@Input() diffEditorText: string;
@Input() @OnChange<List, EditorComponent>('revert') initialEntries: List;
@ -39,10 +41,25 @@ export class EditorComponent implements OnInit, OnChanges {
editorOptions: IStandaloneEditorConstructionOptions = {};
codeEditor: ICodeEditor;
value: string;
protected readonly _editorTextChanged$ = new Subject<string>();
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
private readonly _sub$ = new Subscription();
private _initialEntriesSet = new Set<string>();
constructor(private readonly _loadingService: LoadingService, private readonly _editorThemeService: EditorThemeService) {}
constructor(private readonly _loadingService: LoadingService, private readonly _editorThemeService: EditorThemeService) {
const textChanged$ = this._editorTextChanged$.pipe(
debounceTime(300), // prevent race condition with onPaste event
filter(text => text.length > 0),
tap(newText => {
const newDecorations = this._getDecorations(newText);
this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations);
this.diffValue = this.value;
this._loadingService.stop();
}),
);
this._sub$.add(textChanged$.subscribe());
}
get currentEntries(): string[] {
return this.value.split('\n');
@ -56,6 +73,10 @@ export class EditorComponent implements OnInit, OnChanges {
return this.currentEntries.length;
}
ngOnDestroy() {
this._sub$.unsubscribe();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.diffEditorText) {
this._diffEditor?.getOriginalEditor().setValue(this.diffEditorText);
@ -91,21 +112,17 @@ export class EditorComponent implements OnInit, OnChanges {
this._setTheme();
}
@Debounce()
codeEditorTextChanged() {
const newDecorations = this._getDecorations();
this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations);
this.diffValue = this.value;
this._loadingService.stop();
}
_getDecorations() {
const currentEntries = this.currentEntries;
_getDecorations(text: string) {
const currentEntries = text.split('\n');
const newDecorations: IModelDeltaDecoration[] = [];
for (let index = 0; index < currentEntries.length; ++index) {
const entry = currentEntries.at(index);
if (!this._isNew(entry)) {
for (let index = 0; index < currentEntries.length; index++) {
const entry = currentEntries.at(index)?.trim();
if (!entry || entry.length === 0) {
continue;
}
if (this._initialEntriesSet.has(entry)) {
continue;
}
@ -118,8 +135,10 @@ export class EditorComponent implements OnInit, OnChanges {
revert() {
this.value = this.initialEntries.join('\n');
this._initialEntriesSet = new Set<string>(this.initialEntries);
this.diffValue = this.value;
this._diffEditor?.getModifiedEditor().setValue(this.diffValue);
this._editorTextChanged$.next(this.value);
}
onPaste() {
@ -178,10 +197,6 @@ export class EditorComponent implements OnInit, OnChanges {
});
}
private _isNew(entry: string): boolean {
return this.initialEntries.indexOf(entry) < 0 && entry?.trim().length > 0;
}
private _getDecoration(entry: string, line: number): IModelDeltaDecoration {
const cssClass = entry.length < MIN_WORD_LENGTH ? 'too-short-marker' : 'changed-row-marker';
const range = new monaco.Range(line, 1, line, 1);