Browse Source

UI: Refactor rule chain note and fixed minor bugs

pull/15121/head
Vladyslav_Prykhodko 6 months ago
parent
commit
496bc44d9c
  1. 2
      ui-ngx/package.json
  2. 18
      ui-ngx/src/app/core/services/item-buffer.service.ts
  3. 2
      ui-ngx/src/app/modules/home/pages/rulechain/add-note-dialog.component.html
  4. 3
      ui-ngx/src/app/modules/home/pages/rulechain/rule-note-editor.component.ts
  5. 37
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html
  6. 9
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.scss
  7. 38
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts
  8. 2
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.models.ts
  9. 6
      ui-ngx/src/app/modules/home/pages/rulechain/rulenote.component.html
  10. 6
      ui-ngx/src/app/modules/home/pages/rulechain/rulenote.component.scss
  11. 20
      ui-ngx/src/app/modules/home/pages/rulechain/rulenote.component.ts
  12. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  13. 5
      ui-ngx/yarn.lock

2
ui-ngx/package.json

@ -71,7 +71,7 @@
"ngx-clipboard": "^16.0.0",
"ngx-daterangepicker-material": "^6.0.4",
"ngx-drag-drop": "^20.0.1",
"ngx-flowchart": "file:../../ngx-flowchart/dist/ngx-flowchart",
"ngx-flowchart": "https://github.com/thingsboard/ngx-flowchart.git#release/4.1.0",
"ngx-hm-carousel": "^19.0.0",
"ngx-markdown": "^20.1.0",
"ngx-sharebuttons": "^17.0.0",

18
ui-ngx/src/app/core/services/item-buffer.service.ts

@ -16,7 +16,13 @@
import { Injectable } from '@angular/core';
import { BreakpointId, Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models';
import {
AliasesInfo,
EntityAlias,
EntityAliases,
EntityAliasInfo,
getEntityAliasId
} from '@shared/models/alias.models';
import {
Datasource,
DatasourceType,
@ -75,6 +81,8 @@ export interface RuleNodesReference {
notes?: FcRuleNote[];
originX?: number;
originY?: number;
minX?: number;
minY?: number;
}
@Injectable({
@ -375,6 +383,8 @@ export class ItemBufferService {
height: origNote.height,
content: origNote.content,
backgroundColor: origNote.backgroundColor,
borderWidth: origNote.borderWidth,
borderColor: origNote.borderColor,
applyDefaultMarkdownStyle: origNote.applyDefaultMarkdownStyle,
markdownCss: origNote.markdownCss
});
@ -392,6 +402,8 @@ export class ItemBufferService {
}
ruleNodes.originX = left + (right - left) / 2;
ruleNodes.originY = top + (bottom - top) / 2;
ruleNodes.minX = left;
ruleNodes.minY = top;
connections.forEach(connection => {
ruleNodes.connections.push(connection);
});
@ -405,8 +417,8 @@ export class ItemBufferService {
public pasteRuleChainObjects(x: number, y: number): RuleNodesReference {
const ruleNodes: RuleNodesReference = this.storeGet(RULE_NODES);
if (ruleNodes) {
const deltaX = x - ruleNodes.originX;
const deltaY = y - ruleNodes.originY;
const deltaX = isDefinedAndNotNull(ruleNodes.minX) ? Math.max(x - ruleNodes.originX, -ruleNodes.minX) : x - ruleNodes.originX;
const deltaY = isDefinedAndNotNull(ruleNodes.minY) ? Math.max(y - ruleNodes.originY, -ruleNodes.minY) : y - ruleNodes.originY;
for (const node of ruleNodes.nodes) {
const component = this.ruleChainService.getRuleNodeComponentByClazz(node.ruleChainType, node.componentClazz);
if (component) {

2
ui-ngx/src/app/modules/home/pages/rulechain/add-note-dialog.component.html

@ -27,7 +27,7 @@
</tb-rule-note-editor>
</div>
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary" type="button" (click)="cancel()">
<button mat-button color="primary" type="button" (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button color="primary" type="button" (click)="save()">

3
ui-ngx/src/app/modules/home/pages/rulechain/rule-note-editor.component.ts

@ -70,7 +70,8 @@ export class RuleNoteEditorComponent implements OnChanges, OnInit {
}
private borderColorFrom(bg: string): string {
return tinycolor(bg || DEFAULT_BACKGROUND_COLOR).darken(20).toString();
const color = tinycolor(bg || DEFAULT_BACKGROUND_COLOR);
return color.isDark() ? color.lighten(20).toString() : color.darken(20).toString();
}
private updatedForm(): void {

37
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html

@ -169,7 +169,7 @@
[theForm]="tbRuleNote.noteForm">
<tb-rule-note-editor #tbRuleNote
[note]="editingNote"
class="flex-1">
class="mt-3 flex-1">
</tb-rule-note-editor>
</tb-details-panel>
</mat-drawer>
@ -182,16 +182,25 @@
matTooltipPosition="above">
<mat-icon class="tb-library-node-btn-icon" [class.tb-library-node-btn-icon-toggled]="drawer.opened">chevron_right</mat-icon>
</button>
<button #versionControlButton
*ngIf="!isImport"
color="primary"
type="button"
mat-mini-fab class="version-control-button"
(click)="toggleVersionControl($event, versionControlButton)"
matTooltip="{{'version-control.version-control' | translate}}"
matTooltipPosition="above">
<mat-icon>history</mat-icon>
</button>
@if (!isImport) {
<button color="primary"
type="button"
mat-mini-fab class="add-note-button"
(click)="addNote()"
matTooltip="{{'rulechain.add-note' | translate}}"
matTooltipPosition="above">
<mat-icon>sticky_note_2</mat-icon>
</button>
<button #versionControlButton
color="primary"
type="button"
mat-mini-fab class="version-control-button"
(click)="toggleVersionControl($event, versionControlButton)"
matTooltip="{{'version-control.version-control' | translate}}"
matTooltipPosition="above">
<mat-icon>history</mat-icon>
</button>
}
<button type="button"
mat-icon-button class="tb-fullscreen-button tb-mat-40"
(click)="isFullscreen = !isFullscreen"
@ -212,9 +221,11 @@
<div class="tb-context-menu-header {{contextInfo.headerClass}}">
<mat-icon *ngIf="!contextInfo.iconUrl">{{contextInfo.icon}}</mat-icon>
<img *ngIf="contextInfo.iconUrl" [src]="contextInfo.iconUrl"/>
<div class="flex-1">
<div class="flex-1 self-center">
<div class="tb-context-menu-title">{{contextInfo.title}}</div>
<div class="tb-context-menu-subtitle">{{contextInfo.subtitle}}</div>
@if (contextInfo.subtitle) {
<div class="tb-context-menu-subtitle">{{contextInfo.subtitle}}</div>
}
</div>
</div>
<div *ngFor="let menuItem of contextInfo.menuItems">

9
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.scss

@ -54,6 +54,15 @@
z-index: 2;
}
button.mdc-fab.add-note-button {
position: absolute;
top: 10px;
right: 110px;
opacity: .85;
margin: 0 6px;
z-index: 2;
}
.tb-library-node-btn {
width: 20px;
height: 90px;

38
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

@ -62,6 +62,8 @@ import {
} from '@shared/models/rule-chain.models';
import { FcItemInfo, FlowchartConstants, NgxFlowchartComponent, UserCallbacks } from 'ngx-flowchart';
import {
FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE,
FC_RULE_NOTE_DEFAULT_BACKGROUND_COLOR,
FcRuleEdge,
FcRuleNode,
FcRuleNodeType,
@ -377,12 +379,15 @@ export class RuleChainPageComponent extends PageComponent
this.selectedObjects = [];
this.ruleChainModel.nodes = [];
this.ruleChainModel.edges = [];
this.ruleChainModel.notes = [];
this.ruleNodeTypesModel = {};
if (this.ruleChainCanvas) {
this.ruleChainCanvas.adjustCanvasSize(true);
}
this.isEditingRuleNode = false;
this.isEditingRuleNodeLink = false;
this.isEditingNote = false;
this.editingNote = null;
this.updateRuleNodesHighlight();
}
@ -733,7 +738,8 @@ export class RuleChainPageComponent extends PageComponent
subtitle: this.translate.instant('rulechain.rulechain'),
menuItems: []
};
if (this.ruleChainCanvas.modelService.nodes.getSelectedNodes().length) {
if (this.ruleChainCanvas.modelService.nodes.getSelectedNodes().length ||
this.ruleChainCanvas.modelService.notes.getSelectedNotes().length) {
contextInfo.menuItems.push(
{
action: () => {
@ -964,15 +970,13 @@ export class RuleChainPageComponent extends PageComponent
}
}).afterClosed().subscribe((ruleChain) => {
if (ruleChain) {
const selectedNotes: FcRuleNote[] = this.ruleChainCanvas.modelService.notes.getSelectedNotes();
this.ruleChainCanvas.modelService.deselectAll();
const selectedNotes: FcRuleNote[] = (this.ruleChainModel.notes || []).filter(
note => this.ruleChainCanvas.modelService.notes.isSelected(note)
);
const ruleChainMetaData: RuleChainMetaData = {
ruleChainId: ruleChain.id,
nodes: [],
connections: [],
notes: selectedNotes.map(note => ({ ...note }))
notes: deepClone(selectedNotes)
};
let outputEdges: FcRuleEdge[] = [];
let minX: number = null;
@ -1137,6 +1141,9 @@ export class RuleChainPageComponent extends PageComponent
selectedNodes.forEach((node) => {
this.ruleChainCanvas.modelService.nodes.delete(node);
});
selectedNotes.forEach((note) => {
this.ruleChainCanvas.modelService.notes.delete(note);
});
this.onModelChanged();
this.updateRuleNodesHighlight();
});
@ -1317,15 +1324,15 @@ export class RuleChainPageComponent extends PageComponent
}
}
private addNote(event?: MouseEvent): void {
addNote(event?: MouseEvent): void {
this.dialog.open<AddNoteDialogComponent, FcRuleNote, Partial<FcRuleNote>>(AddNoteDialogComponent, {
disableClose: false,
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
id: '', x: 0, y: 0, width: 200, height: 120,
content: '',
backgroundColor: '#FFF9C4',
applyDefaultMarkdownStyle: true,
backgroundColor: FC_RULE_NOTE_DEFAULT_BACKGROUND_COLOR,
applyDefaultMarkdownStyle: FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE,
markdownCss: ''
}
}).afterClosed().subscribe((noteData) => {
@ -1364,10 +1371,6 @@ export class RuleChainPageComponent extends PageComponent
headerClass: 'tb-rulechain-header',
icon: 'sticky_note_2',
title: this.translate.instant('rulechain.note'),
subtitle: (() => {
const plain = (note.content || '').replace(/[#*_`>[\]!\\~]/g, '').trim();
return plain.length > 24 ? plain.substring(0, 24) + '…' : plain;
})(),
menuItems: []
};
if (!note.readonly) {
@ -1600,7 +1603,8 @@ export class RuleChainPageComponent extends PageComponent
objectsSelected(): boolean {
return this.ruleChainCanvas.modelService.nodes.getSelectedNodes().length > 0 ||
this.ruleChainCanvas.modelService.edges.getSelectedEdges().length > 0;
this.ruleChainCanvas.modelService.edges.getSelectedEdges().length > 0 ||
this.ruleChainCanvas.modelService.notes.getSelectedNotes().length > 0;
}
deleteSelected() {
@ -1658,7 +1662,7 @@ export class RuleChainPageComponent extends PageComponent
ruleChainId: this.ruleChain.id,
nodes: [],
connections: [],
notes: this.ruleChainModel.notes || [],
notes: deepClone(this.ruleChainModel.notes) || [],
version: ruleChain.version
};
const nodes: FcRuleNode[] = [];
@ -2181,6 +2185,8 @@ export class AddNoteDialogComponent extends DialogComponent<AddNoteDialogCompone
}
save(): void {
this.dialogRef.close(this.ruleNoteComponent.noteForm.value);
if (this.ruleNoteComponent.noteForm.valid) {
this.dialogRef.close(this.ruleNoteComponent.noteForm.value);
}
}
}

2
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.models.ts

@ -40,6 +40,6 @@ export interface RuleChainMenuContextInfo {
icon: string;
iconUrl?: string;
title: string;
subtitle: string;
subtitle?: string;
menuItems: RuleChainMenuItem[];
}

6
ui-ngx/src/app/modules/home/pages/rulechain/rulenote.component.html

@ -34,15 +34,11 @@
(dblclick)="userNoteCallbacks?.doubleClick?.($event, note)">
@if (note?.content) {
<tb-markdown [data]="note.content"
[applyDefaultMarkdownStyle]="note.applyDefaultMarkdownStyle ?? defaultApplyMarkdownStyle"
[applyDefaultMarkdownStyle]="applyDefault"
[additionalStyles]="additionalStyles"
[containerClass]="noteClass"
fallbackToPlainMarkdown
usePlainMarkdown>
</tb-markdown>
} @else {
<span class="tb-rule-note-placeholder">
{{ 'rulechain.note-empty' | translate }}
</span>
}
</div>

6
ui-ngx/src/app/modules/home/pages/rulechain/rulenote.component.scss

@ -62,10 +62,4 @@
padding: 8px;
overflow: auto;
}
.tb-rule-note-placeholder {
color: rgba(0, 0, 0, 0.38);
font-style: italic;
font-size: 13px;
}
}

20
ui-ngx/src/app/modules/home/pages/rulechain/rulenote.component.ts

@ -33,25 +33,35 @@ import {
export class RuleNoteComponent extends FcNoteComponent implements OnInit, OnChanges {
readonly defaultBackgroundColor = FC_RULE_NOTE_DEFAULT_BACKGROUND_COLOR;
readonly defaultApplyMarkdownStyle = FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE;
private static readonly HEADING_STYLE_OVERRIDE = '.tb-markdown-view h1 { font-size: 3rem; }';
private static readonly PADDING_STYLE_OVERRIDE = '.tb-markdown-view p, .tb-markdown-view h1, .tb-markdown-view h2, .tb-markdown-view h3, .tb-markdown-view h4, .tb-markdown-view h5, .tb-markdown-view h6 {padding-left: 0 !important;}';
applyDefault = FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE;
additionalStyles: string[];
noteClass: string;
note: FcRuleNote;
ngOnInit(): void {
super.ngOnInit();
this.applyDefault = this.note?.applyDefaultMarkdownStyle ?? FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE;
this.processCss();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.note) {
this.applyDefault = this.note?.applyDefaultMarkdownStyle ?? FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE;
this.processCss();
}
}
private processCss(): void {
let cssString = this.note?.markdownCss;
const styles: string[] = [];
if (this.applyDefault) {
styles.push(RuleNoteComponent.HEADING_STYLE_OVERRIDE);
styles.push(RuleNoteComponent.PADDING_STYLE_OVERRIDE);
}
if (isNotEmptyStr(cssString)) {
const cssParser = new cssjs();
this.noteClass = 'rule-note-' + hashCode(cssString);
@ -59,18 +69,16 @@ export class RuleNoteComponent extends FcNoteComponent implements OnInit, OnChan
cssParser.testMode = false;
const cssObjects = cssParser.applyNamespacing(cssString);
cssString = cssParser.getCSSForEditor(cssObjects);
this.additionalStyles = [cssString];
styles.push(cssString);
} else {
this.noteClass = undefined;
this.additionalStyles = undefined;
}
this.additionalStyles = styles.length ? styles : undefined;
}
noteEdit(event: MouseEvent): void {
event.stopPropagation();
if (this.userNoteCallbacks?.noteEdit) {
this.userNoteCallbacks.noteEdit(event, this.note);
}
this.userNoteCallbacks?.noteEdit?.(event, this.note);
}
noteDelete(event: MouseEvent): void {

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -5172,7 +5172,6 @@
"edit-note": "Edit note",
"note-content": "Markdown/HTML content",
"note-details": "Note details",
"note-empty": "Empty note",
"note-background-color": "Background color",
"note-border": "Border",
"note-apply-default-markdown-style": "Apply default markdown style",

5
ui-ngx/yarn.lock

@ -7989,8 +7989,9 @@ ngx-drag-drop@^20.0.1:
dependencies:
tslib "^2.3.0"
"ngx-flowchart@file:../../ngx-flowchart/dist/ngx-flowchart":
version "4.0.0"
"ngx-flowchart@https://github.com/thingsboard/ngx-flowchart.git#release/4.1.0":
version "4.1.0"
resolved "https://github.com/thingsboard/ngx-flowchart.git#a3dee1761ecaa96c7d0ca22b1c6538b9e27103af"
dependencies:
tslib "^2.3.0"

Loading…
Cancel
Save