Browse Source

Merge pull request #14653 from vvlladd28/improvement/audit-log-style

Improvements for Audit log and Event details dialog
pull/14655/head
Vladyslav Prykhodko 9 months ago
committed by GitHub
parent
commit
52bb245b41
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 13
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html
  2. 26
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss
  3. 99
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts
  4. 4
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html
  5. 23
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss
  6. 103
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts
  7. 88
      ui-ngx/src/app/shared/components/json-object-view.component.ts
  8. 16
      ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts
  9. 41
      ui-ngx/src/app/shared/models/ace/ace.models.ts

13
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html

@ -24,13 +24,14 @@
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>
</mat-toolbar> </mat-toolbar>
<div mat-dialog-content class="flex flex-col"> <div mat-dialog-content class="flex flex-col gap-8" style="padding:16px;">
<label translate class="tb-title no-padding">audit-log.action-data</label> <div class="flex max-h-fit min-h-0 flex-1 flex-col">
<div #actionDataEditor class="tb-audit-log-action-data"> <span translate class="tb-form-panel-hint">audit-log.action-data</span>
<div #actionDataEditor class="tb-audit-log size-full min-h-12 max-w-full"></div>
</div> </div>
<span style="min-height: 30px;"></span> <div class="flex max-h-fit min-h-0 flex-1 flex-col" [class.!hidden]="!displayFailureDetails">
<label [class.!hidden]="!displayFailureDetails" translate class="tb-title no-padding">audit-log.failure-details</label> <span translate class="tb-form-panel-hint">audit-log.failure-details</span>
<div #failureDetailsEditor [class.!hidden]="!displayFailureDetails" class="tb-audit-log-failure-details"> <div #failureDetailsEditor class="tb-audit-log size-full min-h-12 max-w-full"></div>
</div> </div>
</div> </div>
<div mat-dialog-actions class="flex items-center justify-end"> <div mat-dialog-actions class="flex items-center justify-end">

26
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss

@ -13,13 +13,27 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
@import '../../../../../scss/constants';
:host { :host {
.tb-audit-log-action-data, display: grid;
.tb-audit-log-failure-details { height: 100%;
width: 100%; max-width: 100%;
min-width: 400px; max-height: 100vh;
height: 100%; grid-template-rows: min-content minmax(auto, 1fr) min-content;
min-height: 50px;
@media #{$mat-gt-xs} {
.mat-mdc-dialog-content {
max-height: 80vh;
}
}
.tb-audit-log {
min-width: max(400px, 100%);
border: 1px solid #c0c0c0; border: 1px solid #c0c0c0;
@media #{$mat-xs} {
min-width: 100%;
}
} }
} }

99
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts

@ -22,7 +22,11 @@ import { ActionStatus, AuditLog } from '@shared/models/audit-log.models';
import { Ace } from 'ace-builds'; import { Ace } from 'ace-builds';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { getAce } from '@shared/models/ace/ace.models'; import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
import { Observable, of } from 'rxjs';
import { isObject } from '@core/utils';
import { ContentType, contentTypesMap } from '@shared/models/constants';
import { beautifyJs } from '@shared/models/beautify.models';
export interface AuditLogDetailsDialogData { export interface AuditLogDetailsDialogData {
auditLog: AuditLog; auditLog: AuditLog;
@ -41,11 +45,10 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
@ViewChild('failureDetailsEditor', {static: true}) @ViewChild('failureDetailsEditor', {static: true})
failureDetailsEditorElmRef: ElementRef; failureDetailsEditorElmRef: ElementRef;
auditLog: AuditLog;
displayFailureDetails: boolean; displayFailureDetails: boolean;
actionData: string;
actionFailureDetails: string; private auditLog: AuditLog;
aceEditors: Ace.Editor[] = []; private aceEditors: Ace.Editor[] = [];
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@ -58,12 +61,10 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
ngOnInit(): void { ngOnInit(): void {
this.auditLog = this.data.auditLog; this.auditLog = this.data.auditLog;
this.displayFailureDetails = this.auditLog.actionStatus === ActionStatus.FAILURE; this.displayFailureDetails = this.auditLog.actionStatus === ActionStatus.FAILURE;
this.actionData = this.auditLog.actionData ? JSON.stringify(this.auditLog.actionData, null, 2) : '';
this.actionFailureDetails = this.auditLog.actionFailureDetails;
this.createEditor(this.actionDataEditorElmRef, this.actionData); this.createEditor(this.actionDataEditorElmRef, this.auditLog.actionData);
if (this.displayFailureDetails) { if (this.displayFailureDetails) {
this.createEditor(this.failureDetailsEditorElmRef, this.actionFailureDetails); this.createEditor(this.failureDetailsEditorElmRef, this.auditLog.actionFailureDetails);
} }
} }
@ -72,53 +73,43 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
super.ngOnDestroy(); super.ngOnDestroy();
} }
createEditor(editorElementRef: ElementRef, content: string): void { createEditor(editorElementRef: ElementRef, content: string | object): void {
const editorElement = editorElementRef.nativeElement; const editorElement = editorElementRef.nativeElement;
let editorOptions: Partial<Ace.EditorOptions> = { let mode = 'java';
mode: 'ace/mode/java', let content$: Observable<string> = null;
theme: 'ace/theme/github', let contentType = ContentType.TEXT;
showGutter: false, if (content && isObject(content)) {
showPrintMargin: false, contentType = ContentType.JSON;
readOnly: true mode = contentTypesMap.get(contentType).code;
}; content$ = beautifyJs(JSON.stringify(content), {indent_size: 2});
}
const advancedOptions = { if (!content$) {
enableSnippets: false, content$ = of(content as string);
enableBasicAutocompletion: false,
enableLiveAutocompletion: false
};
editorOptions = {...editorOptions, ...advancedOptions};
getAce().subscribe(
(ace) => {
const editor = ace.edit(editorElement, editorOptions);
this.aceEditors.push(editor);
editor.session.setUseWrapMode(false);
editor.setValue(content, -1);
this.updateEditorSize(editorElement, content, editor);
}
);
}
updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 200;
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 18 * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
newWidth = 9 * maxLineLength + 16;
} }
// newHeight = Math.min(400, newHeight); content$.subscribe((processedContent) => {
this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px'); const isJSON = contentType === ContentType.JSON
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); const editorOptions: Partial<Ace.EditorOptions> = {
this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px'); mode: `ace/mode/${mode}`,
editor.resize(); theme: 'ace/theme/github',
showGutter: isJSON,
showFoldWidgets: true,
foldStyle: 'markbeginend',
showPrintMargin: false,
readOnly: true,
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false,
};
getAce().subscribe(
(ace) => {
const editor = ace.edit(editorElement, editorOptions);
this.aceEditors.push(editor);
editor.session.setUseWrapMode(false);
editor.setValue(processedContent, -1);
updateEditorSize(editorElement, processedContent, editor, this.renderer, {showGutter: isJSON});
}
)
});
} }
} }

4
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html

@ -24,8 +24,8 @@
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>
</mat-toolbar> </mat-toolbar>
<div mat-dialog-content class="flex flex-col"> <div mat-dialog-content class="flex flex-col" style="padding:16px;">
<div #eventContentEditor class="tb-event-content"> <div #eventContentEditor class="tb-event-content size-full min-h-12 max-w-full" [class.border]="showBorder">
</div> </div>
</div> </div>
<div mat-dialog-actions class="flex flex-row"> <div mat-dialog-actions class="flex flex-row">

23
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss

@ -13,11 +13,28 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
@import '../../../../../scss/constants';
:host { :host {
display: grid;
height: 100%;
max-width: 100%;
max-height: 100vh;
grid-template-rows: min-content minmax(auto, 1fr) min-content;
@media #{$mat-gt-xs} {
.mat-mdc-dialog-content {
max-height: 80vh;
}
}
.tb-event-content { .tb-event-content {
width: 100%;
min-width: 400px; min-width: 400px;
height: 100%; &.border {
min-height: 50px; border: 1px solid #c0c0c0;
}
@media #{$mat-xs} {
min-width: 100%;
}
} }
} }

103
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts

@ -23,7 +23,7 @@ import { Ace } from 'ace-builds';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ContentType, contentTypesMap } from '@shared/models/constants'; import { ContentType, contentTypesMap } from '@shared/models/constants';
import { getAce } from '@shared/models/ace/ace.models'; import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { beautifyJs } from '@shared/models/beautify.models'; import { beautifyJs } from '@shared/models/beautify.models';
import { of } from 'rxjs'; import { of } from 'rxjs';
@ -40,40 +40,38 @@ export interface EventContentDialogData {
templateUrl: './event-content-dialog.component.html', templateUrl: './event-content-dialog.component.html',
styleUrls: ['./event-content-dialog.component.scss'] styleUrls: ['./event-content-dialog.component.scss']
}) })
export class EventContentDialogComponent extends DialogComponent<EventContentDialogData> implements OnInit, OnDestroy { export class EventContentDialogComponent extends DialogComponent<EventContentDialogComponent> implements OnInit, OnDestroy {
@ViewChild('eventContentEditor', {static: true}) @ViewChild('eventContentEditor', {static: true})
eventContentEditorElmRef: ElementRef; eventContentEditorElmRef: ElementRef;
content: string;
title: string; title: string;
contentType: ContentType; showBorder = false;
aceEditor: Ace.Editor;
private contentType: ContentType;
private aceEditor: Ace.Editor;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: EventContentDialogData, @Inject(MAT_DIALOG_DATA) public data: EventContentDialogData,
public dialogRef: MatDialogRef<EventContentDialogComponent>, protected dialogRef: MatDialogRef<EventContentDialogComponent>,
private renderer: Renderer2) { private renderer: Renderer2) {
super(store, router, dialogRef); super(store, router, dialogRef);
} }
ngOnInit(): void { ngOnInit(): void {
this.content = this.data.content;
this.title = this.data.title; this.title = this.data.title;
this.contentType = this.data.contentType; this.contentType = this.data.contentType;
this.createEditor(this.eventContentEditorElmRef, this.content); this.createEditor(this.eventContentEditorElmRef, this.data.content);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.aceEditor) { this.aceEditor?.destroy();
this.aceEditor.destroy();
}
super.ngOnDestroy(); super.ngOnDestroy();
} }
isJson(str) { private isJson(str: string) {
try { try {
return isLiteralObject(JSON.parse(str)); return isLiteralObject(JSON.parse(str));
} catch (e) { } catch (e) {
@ -81,78 +79,53 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia
} }
} }
createEditor(editorElementRef: ElementRef, content: string) { private createEditor(editorElementRef: ElementRef, content: string) {
const editorElement = editorElementRef.nativeElement; const editorElement = editorElementRef.nativeElement;
let mode = 'java'; let mode = 'java';
let content$: Observable<string> = null; let content$: Observable<string> = null;
if (this.contentType) { if (this.contentType) {
mode = contentTypesMap.get(this.contentType).code; mode = contentTypesMap.get(this.contentType).code;
if (this.contentType === ContentType.JSON && content) { if (this.contentType === ContentType.JSON && content) {
content$ = beautifyJs(content, {indent_size: 4}); content$ = beautifyJs(content, {indent_size: 2});
} else if (this.contentType === ContentType.BINARY && content) { } else if (this.contentType === ContentType.BINARY && content) {
try { try {
const decodedData = base64toString(content); const decodedData = base64toString(content);
if (this.isJson(decodedData)) { if (this.isJson(decodedData)) {
mode = 'json'; mode = 'json';
content$ = beautifyJs(decodedData, {indent_size: 4}); content$ = beautifyJs(decodedData, {indent_size: 2});
} else { } else {
content$ = of(decodedData); content$ = of(decodedData);
} }
} catch (e) {} } catch (e) {/**/}
} }
} }
if (!content$) { if (!content$) {
content$ = of(content); content$ = of(content);
} }
content$.subscribe( content$.subscribe((processedContent) => {
(processedContent) => { const isJSON = mode === 'json' && processedContent !== '{}';
let editorOptions: Partial<Ace.EditorOptions> = { this.showBorder = isJSON;
mode: `ace/mode/${mode}`, const editorOptions: Partial<Ace.EditorOptions> = {
theme: 'ace/theme/github', mode: `ace/mode/${mode}`,
showGutter: false, theme: 'ace/theme/github',
showPrintMargin: false, showGutter: isJSON,
readOnly: true showFoldWidgets: true,
}; foldStyle: 'markbeginend',
showPrintMargin: false,
const advancedOptions = { readOnly: true,
enableSnippets: false, enableSnippets: false,
enableBasicAutocompletion: false, enableBasicAutocompletion: false,
enableLiveAutocompletion: false enableLiveAutocompletion: false,
}; };
getAce().subscribe(
editorOptions = {...editorOptions, ...advancedOptions}; (ace) => {
getAce().subscribe( this.aceEditor = ace.edit(editorElement, editorOptions);
(ace) => { this.aceEditor.session.setUseWrapMode(false);
this.aceEditor = ace.edit(editorElement, editorOptions); this.aceEditor.setValue(processedContent, -1);
this.aceEditor.session.setUseWrapMode(false); updateEditorSize(editorElement, processedContent, this.aceEditor, this.renderer, {showGutter: isJSON});
this.aceEditor.setValue(processedContent, -1); }
this.updateEditorSize(editorElement, processedContent, this.aceEditor); )
} });
);
}
);
}
updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 400;
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 17 * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
newWidth = 9 * maxLineLength + 16;
}
// newHeight = Math.min(400, newHeight);
this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
editor.resize();
} }
} }

88
ui-ngx/src/app/shared/components/json-object-view.component.ts

@ -15,14 +15,11 @@
/// ///
import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core'; import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Ace } from 'ace-builds'; import { Ace } from 'ace-builds';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { RafService } from '@core/services/raf.service';
import { isDefinedAndNotNull, isUndefined } from '@core/utils'; import { isDefinedAndNotNull, isUndefined } from '@core/utils';
import { getAce } from '@shared/models/ace/ace.models'; import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-json-object-view', selector: 'tb-json-object-view',
@ -36,7 +33,7 @@ import { getAce } from '@shared/models/ace/ace.models';
} }
] ]
}) })
export class JsonObjectViewComponent implements OnInit, OnDestroy { export class JsonObjectViewComponent implements OnInit, OnDestroy, ControlValueAccessor {
@ViewChild('jsonViewer', {static: true}) @ViewChild('jsonViewer', {static: true})
jsonViewerElmRef: ElementRef; jsonViewerElmRef: ElementRef;
@ -55,96 +52,54 @@ export class JsonObjectViewComponent implements OnInit, OnDestroy {
@Input() sort: (key: string, value: any) => any; @Input() sort: (key: string, value: any) => any;
private widthValue: boolean;
get autoWidth(): boolean {
return this.widthValue;
}
@Input() @Input()
set autoWidth(value: boolean) { @coerceBoolean()
this.widthValue = coerceBooleanProperty(value); autoWidth: boolean
}
private heigthValue: boolean;
get autoHeight(): boolean {
return this.heigthValue;
}
@Input() @Input()
set autoHeight(value: boolean) { @coerceBoolean()
this.heigthValue = coerceBooleanProperty(value); autoHeight: boolean
}
constructor(public elementRef: ElementRef, constructor(private renderer: Renderer2) {
protected store: Store<AppState>,
private raf: RafService,
private renderer: Renderer2) {
} }
ngOnInit(): void { ngOnInit(): void {
this.viewerElement = this.jsonViewerElmRef.nativeElement; this.viewerElement = this.jsonViewerElmRef.nativeElement;
let editorOptions: Partial<Ace.EditorOptions> = { const editorOptions: Partial<Ace.EditorOptions> = {
mode: 'ace/mode/java', mode: 'ace/mode/java',
theme: 'ace/theme/github', theme: 'ace/theme/github',
showGutter: false, showGutter: false,
showPrintMargin: false, showPrintMargin: false,
readOnly: true readOnly: true,
};
const advancedOptions = {
enableSnippets: false, enableSnippets: false,
enableBasicAutocompletion: false, enableBasicAutocompletion: false,
enableLiveAutocompletion: false enableLiveAutocompletion: false
}; };
editorOptions = {...editorOptions, ...advancedOptions};
getAce().subscribe( getAce().subscribe(
(ace) => { (ace) => {
this.jsonViewer = ace.edit(this.viewerElement, editorOptions); this.jsonViewer = ace.edit(this.viewerElement, editorOptions);
this.jsonViewer.session.setUseWrapMode(false); this.jsonViewer.session.setUseWrapMode(false);
this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1);
if (this.contentValue && (this.widthValue || this.heigthValue)) { if (this.contentValue && (this.autoWidth || this.autoHeight)) {
this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer); updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer, this.renderer, {
ignoreHeight: !this.autoHeight,
ignoreWidth: !this.autoWidth
});
} }
} }
); );
} }
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.jsonViewer) { this.jsonViewer?.destroy();
this.jsonViewer.destroy();
}
} }
updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 200;
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 17 * lines.length + 17;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
newWidth = 8 * maxLineLength + 16;
}
if (this.heigthValue) {
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
}
if (this.widthValue) {
this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
}
editor.resize();
}
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
this.propagateChange = fn; this.propagateChange = fn;
} }
registerOnTouched(fn: any): void { registerOnTouched(_fn: any): void {
} }
writeValue(value: any): void { writeValue(value: any): void {
@ -162,8 +117,11 @@ export class JsonObjectViewComponent implements OnInit, OnDestroy {
} }
if (this.jsonViewer) { if (this.jsonViewer) {
this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1);
if (this.contentValue && (this.widthValue || this.heigthValue)) { if (this.contentValue && (this.autoWidth || this.autoHeight)) {
this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer); updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer, this.renderer, {
ignoreHeight: !this.autoHeight,
ignoreWidth: !this.autoWidth
});
} }
} }
} }

16
ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts

@ -39,7 +39,7 @@ import {
import { ImportExportService } from '@shared/import-export/import-export.service'; import { ImportExportService } from '@shared/import-export/import-export.service';
import { TableColumnsAssignmentComponent } from '@shared/import-export/table-columns-assignment.component'; import { TableColumnsAssignmentComponent } from '@shared/import-export/table-columns-assignment.component';
import { Ace } from 'ace-builds'; import { Ace } from 'ace-builds';
import { getAce } from '@shared/models/ace/ace.models'; import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
export interface ImportDialogCsvData { export interface ImportDialogCsvData {
entityType: EntityType; entityType: EntityType;
@ -283,21 +283,9 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom
this.aceEditor = ace.edit(editorElement, editorOptions); this.aceEditor = ace.edit(editorElement, editorOptions);
this.aceEditor.session.setUseWrapMode(false); this.aceEditor.session.setUseWrapMode(false);
this.aceEditor.setValue(content, -1); this.aceEditor.setValue(content, -1);
this.updateEditorSize(editorElement, content, this.aceEditor); updateEditorSize(editorElement, content, this.aceEditor, this.renderer, {setMinHeight: true, ignoreWidth: true});
} }
); );
} }
private updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 200;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 16 * lines.length + 24;
}
const minHeight = Math.min(200, newHeight);
this.renderer.setStyle(editorElement, 'minHeight', minHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
editor.resize();
}
} }

41
ui-ngx/src/app/shared/models/ace/ace.models.ts

@ -19,6 +19,7 @@ import { Observable } from 'rxjs/internal/Observable';
import { forkJoin, from, of } from 'rxjs'; import { forkJoin, from, of } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators'; import { map, mergeMap, tap } from 'rxjs/operators';
import { unwrapModule } from '@core/utils'; import { unwrapModule } from '@core/utils';
import { Renderer2 } from '@angular/core';
let aceDependenciesLoaded = false; let aceDependenciesLoaded = false;
let aceModule: any; let aceModule: any;
@ -98,6 +99,46 @@ export function getAceDiff(): Observable<any> {
} }
} }
export function updateEditorSize(editorElement: any, content: string, editor: Ace.Editor, renderer: Renderer2, options?: {
showGutter?: boolean,
ignoreHeight?: boolean,
ignoreWidth?: boolean,
setMinHeight?: boolean
}): void {
let newHeight = 200;
let newWidth = 600;
if (content && content.length > 0) {
if (editor.renderer.lineHeight <= 0) {
editor.renderer.updateFull(true);
}
const lines = content.split('\n');
newHeight = editor.renderer.lineHeight * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
if (options?.showGutter) {
maxLineLength += lines.length.toString().length;
}
newWidth = 10 * maxLineLength + 16;
if (options?.showGutter) {
newWidth += 32;
}
}
if (!options.ignoreHeight) {
renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
}
if (options.setMinHeight) {
renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px');
}
if (!options.ignoreWidth) {
renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
}
editor.resize();
}
export class Range implements Ace.Range { export class Range implements Ace.Range {
public start: Ace.Point; public start: Ace.Point;

Loading…
Cancel
Save