new component for editor and fix compare for template dictionaries

This commit is contained in:
Dan Percic 2021-10-05 22:54:16 +03:00
parent 760a446fd5
commit 43a46d04e8
11 changed files with 223 additions and 134 deletions

View File

@ -42,7 +42,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
}
get hasChanges() {
return this._dictionaryManager.hasChanges;
return this._dictionaryManager.editor.hasChanges;
}
async ngOnInit() {
@ -85,7 +85,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
}
download(): void {
const content = this._dictionaryManager.editorValue;
const content = this._dictionaryManager.editor.value;
const blob = new Blob([content], {
type: 'text/plain;charset=utf-8'
});
@ -98,7 +98,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
if (file) {
fileReader.onload = () => {
this._dictionaryManager.editorValue = fileReader.result as string;
this._dictionaryManager.editor.value = fileReader.result as string;
this._fileInput.nativeElement.value = null;
};
fileReader.readAsText(file);

View File

@ -33,7 +33,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
}
get changed() {
return this._dictionaryManager.hasChanges;
return this._dictionaryManager.editor.hasChanges;
}
get disabled() {
@ -58,7 +58,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
async save() {
await this._dictionaryService
.saveEntries(
this._dictionaryManager.currentEntries,
this._dictionaryManager.editor.currentEntries,
this._dictionaryManager.initialEntries,
this.dossier.dossierTemplateId,
'dossier_redaction',

View File

@ -30,18 +30,31 @@
{{ 'dictionary-overview.compare.compare' | translate }}
</mat-checkbox>
</div>
<div *ngIf="filterByDossierTemplate" class="iqser-input-group w-200 mt-0 mr-8">
<mat-select [(ngModel)]="dossierTemplate" [disabled]="!compare">
<mat-option [value]="selectDossierTemplate">{{ selectDossierTemplate.name | translate }}</mat-option>
<mat-option *ngFor="let dossierTemplate of dossierTemplatesService.all$ | async" [value]="dossierTemplate">
{{ dossierTemplate.name }}
</mat-option>
</mat-select>
</div>
<div class="iqser-input-group w-200 mt-0">
<mat-select [(ngModel)]="dossier" [disabled]="!compare || dossierTemplateIsNotSelected">
<ng-container *ngIf="filterByDossierTemplate">
<div class="iqser-input-group w-200 mt-0 mr-8">
<mat-select [(ngModel)]="dossierTemplate" [disabled]="!compare">
<mat-option [value]="selectDossierTemplate">{{ selectDossierTemplate.name | translate }}</mat-option>
<mat-option *ngFor="let dossierTemplate of dossierTemplatesService.all$ | async" [value]="dossierTemplate">
{{ dossierTemplate.name }}
</mat-option>
</mat-select>
</div>
<div class="iqser-input-group w-200 mt-0">
<mat-select [(ngModel)]="dictionary" [disabled]="!compare || dossierTemplateIsNotSelected">
<mat-option [value]="selectDictionary">{{ selectDictionary.label | translate }}</mat-option>
<mat-option *ngFor="let dictionary of dictionaries" [value]="dictionary">
{{ dictionary.label }}
</mat-option>
</mat-select>
</div>
</ng-container>
<div *ngIf="!filterByDossierTemplate" class="iqser-input-group w-200 mt-0">
<mat-select [(ngModel)]="dossier" [disabled]="!compare">
<mat-option [value]="selectDossier">{{ selectDossier.dossierName | translate }}</mat-option>
<mat-option *ngFor="let dossier of dossiers" [value]="dossier">
<mat-option *ngFor="let dossier of dossiersService.all$ | async" [value]="dossier">
{{ dossier.dossierName }}
</mat-option>
</mat-select>
@ -50,30 +63,22 @@
</div>
<div class="editor-container">
<ngx-monaco-editor
(init)="onCodeEditorInit($event)"
*ngIf="!compare || !showDiffEditor"
[(ngModel)]="editorValue"
[options]="editorOptions"
></ngx-monaco-editor>
<redaction-editor
[canEdit]="canEdit"
[diffEditorText]="diffEditorText"
[initialEntries]="initialEntries"
[showDiffEditor]="compare && showDiffEditor"
></redaction-editor>
<ngx-monaco-diff-editor
(init)="onDiffEditorInit($event)"
*ngIf="compare && showDiffEditor"
[modified]="editorValue"
[options]="editorOptions"
[original]="diffEditorText"
></ngx-monaco-diff-editor>
<div *ngIf="compare && dossier.dossierName === selectDossier.dossierName" class="no-dictionary-selected">
<div *ngIf="compare && optionNotSelected" class="no-dictionary-selected">
<mat-icon svgIcon="red:dictionary"></mat-icon>
<span class="heading-l" translate="dictionary-overview.select-dictionary"></span>
</div>
</div>
<div *ngIf="withFloatingActions && hasChanges && canEdit" [class.offset]="compare" class="changes-box">
<div *ngIf="withFloatingActions && !!editor?.hasChanges && canEdit" [class.offset]="compare" class="changes-box">
<iqser-icon-button
(action)="saveDictionary.emit(currentEntries)"
(action)="saveDictionary.emit(editor?.currentEntries)"
[label]="'dictionary-overview.save-changes' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:check"

View File

@ -36,12 +36,6 @@ form {
}
}
ngx-monaco-diff-editor,
ngx-monaco-editor {
height: 100%;
width: 100%;
}
.content-container {
height: 100%;

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core';
import { Debounce, IconButtonTypes, List } from '@iqser/common-ui';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
@ -8,13 +8,12 @@ import { DictionaryService } from '@shared/services/dictionary.service';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { DossierTemplatesService } from '../../../dossier/services/dossier-templates.service';
import { DossierTemplate } from '@models/file/dossier-template';
import ICodeEditor = monaco.editor.ICodeEditor;
import IDiffEditor = monaco.editor.IDiffEditor;
import { AppStateService } from '@state/app-state.service';
import { EditorComponent } from '@shared/components/editor/editor.component';
import { TypeValue } from '@models/file/type-value';
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import FindMatch = monaco.editor.FindMatch;
const MIN_WORD_LENGTH = 2;
const SMOOTH_SCROLL = 0;
@Component({
@ -22,7 +21,7 @@ const SMOOTH_SCROLL = 0;
templateUrl: './dictionary-manager.component.html',
styleUrls: ['./dictionary-manager.component.scss']
})
export class DictionaryManagerComponent implements OnChanges, OnInit {
export class DictionaryManagerComponent implements OnChanges {
readonly iconButtonTypes = IconButtonTypes;
@Input() withFloatingActions = true;
@ -30,32 +29,30 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
@Input() initialEntries: List;
@Input() canEdit = false;
@Output() readonly saveDictionary = new EventEmitter<string[]>();
@ViewChild(EditorComponent) readonly editor: EditorComponent;
currentMatch = 0;
findMatches: FindMatch[] = [];
currentEntries: string[] = [];
editorOptions: IStandaloneEditorConstructionOptions = {};
diffEditorText = '';
showDiffEditor = false;
searchText = '';
selectDossier = { dossierName: _('dictionary-overview.compare.select-dossier') };
selectDossierTemplate = { name: _('dictionary-overview.compare.select-dossier-template') };
selectDossier = { dossierName: _('dictionary-overview.compare.select-dossier') } as Dossier;
selectDictionary = {
label: _('dictionary-overview.compare.select-dictionary')
} as TypeValue;
selectDossierTemplate = { name: _('dictionary-overview.compare.select-dossier-template') } as DossierTemplate;
compare: false;
dossiers: List<Dossier> = this._dossiers;
private _codeEditor: ICodeEditor;
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
dictionaries: List<TypeValue> = this._dictionaries;
private _searchDecorations: string[] = [];
constructor(
private readonly _dictionaryService: DictionaryService,
readonly dossiersService: DossiersService,
readonly appStateService: AppStateService,
readonly dossierTemplatesService: DossierTemplatesService
) {}
private _dossierTemplate: DossierTemplate = this.selectDossierTemplate as DossierTemplate;
private _dossierTemplate = this.selectDossierTemplate;
get dossierTemplate() {
return this._dossierTemplate;
@ -63,11 +60,12 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
set dossierTemplate(value) {
this._dossierTemplate = value;
this.dossier = this.selectDossier as Dossier;
this.dossiers = this._dossiers;
this.dictionaries = this._dictionaries;
this._dictionary = this.selectDictionary;
this.showDiffEditor = false;
}
private _dossier: Dossier = this.selectDossier as Dossier;
private _dossier = this.selectDossier;
get dossier() {
return this._dossier;
@ -76,77 +74,61 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
set dossier(dossier: Dossier) {
this._dossier = dossier;
if (dossier === this.selectDossier) {
if (dossier.dossierName === this.selectDossier.dossierName) {
this.showDiffEditor = false;
this.diffEditorText = '';
return;
}
this._onDossierChanged(dossier)
this._onDossierChanged(dossier.dossierTemplateId, dossier.dossierId)
.pipe(take(1))
.subscribe(entries => {
this.diffEditorText = entries;
this.showDiffEditor = true;
if (this.showDiffEditor) {
this._diffEditor?.getOriginalEditor().setValue(this.diffEditorText);
}
});
}
get editorValue(): string {
return this.currentEntries.join('\n');
private _dictionary = this.selectDictionary;
get dictionary() {
return this._dictionary;
}
set editorValue(text: string) {
this.currentEntries = text.split('\n');
this.codeEditorTextChanged();
}
set dictionary(dictionary: TypeValue) {
this._dictionary = dictionary;
get hasChanges(): boolean {
return this.currentEntries.toString() !== this.initialEntries.toString();
}
get _dossiers() {
if (this.filterByDossierTemplate) {
return this.dossiersService.all.filter(dossier => dossier.dossierTemplateId === this.dossierTemplate.dossierTemplateId);
if (dictionary.label === this.selectDictionary.label) {
this.showDiffEditor = false;
this.diffEditorText = '';
return;
}
return this.dossiersService.all;
const entries = this.appStateService.dictionaryData[this._dictionary.dossierTemplateId][this._dictionary.type].entries;
this.diffEditorText = this._toString(entries);
this.showDiffEditor = true;
}
get _dictionaries() {
if (!this._dossierTemplate || this._dossierTemplate.name === this.selectDossierTemplate.name) {
return;
}
return Object.values(this.appStateService.dictionaryData[this.dossierTemplate?.dossierTemplateId]);
}
get dossierTemplateIsNotSelected() {
return this.filterByDossierTemplate && this._dossierTemplate.name === this.selectDossierTemplate.name;
}
ngOnInit(): void {
this.currentEntries = [...this.initialEntries];
this.editorOptions = {
theme: 'vs',
language: 'text/plain',
automaticLayout: true,
readOnly: !this.canEdit
};
}
onDiffEditorInit(editor: IDiffEditor): void {
this._diffEditor = editor;
}
onCodeEditorInit(editor: ICodeEditor): void {
this._codeEditor = editor;
(window as any).monaco.editor.defineTheme('redaction', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editor.lineHighlightBackground': '#f4f5f7'
}
});
(window as any).monaco.editor.setTheme('redaction');
get optionNotSelected() {
if (this.filterByDossierTemplate) {
return this.selectDictionary.label === this._dictionary.label;
}
return this.dossier.dossierName === this.selectDossier.dossierName;
}
revert() {
this.currentEntries = [...this.initialEntries];
this.editor?.revert();
this.searchChanged('');
}
@ -160,13 +142,6 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
this.nextSearchMatch();
}
@Debounce()
codeEditorTextChanged() {
const newDecorations = this.currentEntries.filter(entry => this._isNew(entry)).map(entry => this._getDecoration(entry));
this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
}
nextSearchMatch(): void {
if (this.findMatches.length <= 0) {
return;
@ -190,30 +165,18 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
}
private _applySearchDecorations() {
this._searchDecorations = this._codeEditor?.deltaDecorations(this._searchDecorations, []) || [];
this._searchDecorations = this.editor.codeEditor?.deltaDecorations(this._searchDecorations, []) || [];
const decorations = this.findMatches.map(match => this._getSearchDecoration(match));
this._searchDecorations = this._codeEditor?.deltaDecorations(this._searchDecorations, decorations) || [];
this._searchDecorations = this.editor.codeEditor?.deltaDecorations(this._searchDecorations, decorations) || [];
}
private _getMatches(text: string): FindMatch[] {
const model = this._codeEditor?.getModel();
const model = this.editor.codeEditor?.getModel();
return model?.findMatches(text, false, false, false, null, false) || [];
}
private _isNew(entry: string): boolean {
return this.initialEntries.indexOf(entry) < 0 && entry?.trim().length > 0;
}
private _getDecoration(entry: string): IModelDeltaDecoration {
const line = this.currentEntries.indexOf(entry) + 1;
const cssClass = entry.length < MIN_WORD_LENGTH ? 'too-short-marker' : 'changed-row-marker';
const range = new monaco.Range(line, 1, line, 1);
return { range: range, options: { isWholeLine: true, className: cssClass } };
}
private _getSearchDecoration(match: FindMatch): IModelDeltaDecoration {
return { range: match.range, options: { inlineClassName: 'search-marker' } };
}
@ -221,12 +184,12 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
private _scrollToCurrentMatch(): void {
const range = this.findMatches[this.currentMatch - 1].range;
this._codeEditor.setSelection(range);
this._codeEditor.revealLineInCenter(range.startLineNumber, SMOOTH_SCROLL);
this.editor.codeEditor.setSelection(range);
this.editor.codeEditor.revealLineInCenter(range.startLineNumber, SMOOTH_SCROLL);
}
private _onDossierChanged({ id, dossierTemplateId }: Dossier): Observable<string> {
const dictionary$ = this._dictionaryService.getFor(dossierTemplateId, 'dossier_redaction', id);
private _onDossierChanged(dossierTemplateId: string, dossierId?: string, type = 'dossier_redaction'): Observable<string> {
const dictionary$ = this._dictionaryService.getFor(dossierTemplateId, type, dossierId);
return dictionary$.pipe(map(data => this._toString([...data.entries])));
}

View File

@ -0,0 +1,14 @@
<ngx-monaco-editor
(init)="onCodeEditorInit($event)"
*ngIf="!showDiffEditor"
[(ngModel)]="value"
[options]="editorOptions"
></ngx-monaco-editor>
<ngx-monaco-diff-editor
(init)="onDiffEditorInit($event)"
*ngIf="showDiffEditor"
[modified]="value"
[options]="editorOptions"
[original]="diffEditorText"
></ngx-monaco-diff-editor>

View File

@ -0,0 +1,6 @@
:host,
ngx-monaco-diff-editor,
ngx-monaco-editor {
height: 100%;
width: 100%;
}

View File

@ -0,0 +1,104 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Debounce, List } from '@iqser/common-ui';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import ICodeEditor = monaco.editor.ICodeEditor;
import IDiffEditor = monaco.editor.IDiffEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
const MIN_WORD_LENGTH = 2;
@Component({
selector: 'redaction-editor',
templateUrl: './editor.component.html',
styleUrls: ['./editor.component.scss']
})
export class EditorComponent implements OnInit, OnChanges {
editorOptions: IStandaloneEditorConstructionOptions = {};
@Input() showDiffEditor = false;
@Input() initialEntries: List;
@Input() canEdit = false;
currentEntries: string[] = [];
codeEditor: ICodeEditor;
diffEditor: IDiffEditor;
private _decorations: string[] = [];
_diffEditorText = '';
get diffEditorText() {
return this._diffEditorText;
}
@Input()
set diffEditorText(value: string) {
this._diffEditorText = value;
this.diffEditor?.getOriginalEditor().setValue(value);
}
get value(): string {
return this.currentEntries.join('\n');
}
set value(text: string) {
this.currentEntries = text.split('\n');
this.codeEditorTextChanged();
}
get hasChanges(): boolean {
return this.currentEntries.toString() !== this.initialEntries.toString();
}
ngOnInit(): void {
this.currentEntries = [...this.initialEntries];
this.editorOptions = {
theme: 'vs',
language: 'text/plain',
automaticLayout: true,
readOnly: !this.canEdit
};
}
ngOnChanges(changes: SimpleChanges) {
console.log(changes);
this.revert();
}
onDiffEditorInit(editor: IDiffEditor): void {
this.diffEditor = editor;
}
onCodeEditorInit(editor: ICodeEditor): void {
this.codeEditor = editor;
(window as any).monaco.editor.defineTheme('redaction', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editor.lineHighlightBackground': '#f4f5f7'
}
});
(window as any).monaco.editor.setTheme('redaction');
}
@Debounce()
codeEditorTextChanged() {
const newDecorations = this.currentEntries.filter(entry => this._isNew(entry)).map(entry => this._getDecoration(entry));
this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations);
}
revert() {
this.currentEntries = [...this.initialEntries];
}
private _isNew(entry: string): boolean {
return this.initialEntries.indexOf(entry) < 0 && entry?.trim().length > 0;
}
private _getDecoration(entry: string): IModelDeltaDecoration {
const line = this.currentEntries.indexOf(entry) + 1;
const cssClass = entry.length < MIN_WORD_LENGTH ? 'too-short-marker' : 'changed-row-marker';
const range = new monaco.Range(line, 1, line, 1);
return { range: range, options: { isWholeLine: true, className: cssClass } };
}
}

View File

@ -24,6 +24,7 @@ import { LongPressDirective } from './directives/long-press.directive';
import { NamePipe } from './pipes/name.pipe';
import { TypeFilterComponent } from './components/type-filter/type-filter.component';
import { TeamMembersComponent } from './components/team-members/team-members.component';
import { EditorComponent } from './components/editor/editor.component';
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
@ -47,7 +48,7 @@ const utils = [DatePipe, NamePipe, NavigateLastDossiersScreenDirective, LongPres
const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule, CommonUiModule];
@NgModule({
declarations: [...components, ...utils],
declarations: [...components, ...utils, EditorComponent],
imports: [CommonModule, ...modules, MonacoEditorModule],
exports: [...modules, ...components, ...utils],
providers: [

View File

@ -435,7 +435,8 @@
"compare": {
"compare": "Vergleichen Sie",
"select-dossier": "Wählen Sie Dossiervorlage",
"select-dossier-template": ""
"select-dossier-template": "",
"select-dictionary": ""
},
"dictionary-details": {
"description": "Beschreibung"

View File

@ -475,7 +475,8 @@
"compare": {
"compare": "Compare",
"select-dossier": "Select Dossier",
"select-dossier-template": "Select Dossier Template"
"select-dossier-template": "Select Dossier Template",
"select-dictionary": "Select Dictionary"
},
"dictionary-details": {
"description": "Description"