Browse Source

UI: Add widget action tooltip reference hint and new action replaceReferenceWithWidgetCopy

pull/11430/head
Vladyslav_Prykhodko 2 years ago
parent
commit
07a45573b7
  1. 67
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  2. 44
      ui-ngx/src/app/core/services/item-buffer.service.ts
  3. 33
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  4. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts
  5. 4
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.ts
  6. 2
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html
  7. 12
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts
  8. 30
      ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss
  9. 74
      ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts
  10. 2
      ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
  11. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  12. 6
      ui-ngx/src/styles.scss

67
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -29,7 +29,7 @@ import {
GridSettings, GridSettings,
WidgetLayout WidgetLayout
} from '@shared/models/dashboard.models'; } from '@shared/models/dashboard.models';
import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined, isEqual } from '@core/utils'; import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined } from '@core/utils';
import { import {
Datasource, Datasource,
datasourcesHasOnlyComparisonAggregation, datasourcesHasOnlyComparisonAggregation,
@ -40,7 +40,7 @@ import {
TargetDeviceType, TargetDeviceType,
Widget, Widget,
WidgetConfig, WidgetConfig,
WidgetConfigMode, WidgetConfigMode, WidgetSize,
widgetType, widgetType,
WidgetTypeDescriptor WidgetTypeDescriptor
} from '@app/shared/models/widget.models'; } from '@app/shared/models/widget.models';
@ -933,4 +933,67 @@ export class DashboardUtilsService {
} }
} }
replaceReferenceWithWidgetCopy(widget: Widget,
dashboard: Dashboard,
targetState: string,
targetLayout: DashboardLayoutId,
breakpointId: string,
isRemoveWidget: boolean): Widget {
const newWidget = deepClone(widget);
newWidget.id = this.utils.guid();
const originalColumns = this.getOriginalColumns(dashboard, targetState, targetLayout, breakpointId);
const originalSize = this.getOriginalSize(dashboard, targetState, targetLayout, widget, breakpointId);
const layout = this.getDashboardLayoutConfig(dashboard.configuration.states[targetState].layouts[targetLayout], breakpointId);
const widgetLayout = layout.widgets[widget.id];
const targetRow = widgetLayout.row;
const targetColumn = widgetLayout.col;
if (isRemoveWidget) {
this.removeWidgetFromLayout(dashboard, targetState, targetLayout, widget.id, breakpointId);
}
this.addWidgetToLayout(dashboard, targetState, targetLayout, newWidget, originalColumns, originalSize,
targetRow, targetColumn, breakpointId);
return newWidget;
}
getDashboardLayoutConfig(layout: DashboardLayout, breakpointId: string): DashboardLayout {
if (breakpointId !== 'default') {
return layout.breakpoints[breakpointId];
}
return layout;
}
getOriginalColumns(dashboard: Dashboard, sourceState: string, sourceLayout: DashboardLayoutId, breakpointId: string): number {
let originalColumns = 24;
let gridSettings = null;
const state = dashboard.configuration.states[sourceState];
const layoutCount = Object.keys(state.layouts).length;
if (state) {
const layout = this.getDashboardLayoutConfig(state.layouts[sourceLayout], breakpointId);
if (layout) {
gridSettings = layout.gridSettings;
}
}
if (gridSettings && gridSettings.columns) {
originalColumns = gridSettings.columns;
}
originalColumns = originalColumns * layoutCount;
return originalColumns;
}
getOriginalSize(dashboard: Dashboard, sourceState: string, sourceLayout: DashboardLayoutId,
widget: Widget, breakpointId: string): WidgetSize {
const layout = this.getDashboardLayoutConfig(dashboard.configuration.states[sourceState].layouts[sourceLayout], breakpointId);
const widgetLayout = layout.widgets[widget.id];
return {
sizeX: widgetLayout.sizeX,
sizeY: widgetLayout.sizeY
};
}
} }

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

@ -95,8 +95,8 @@ export class ItemBufferService {
const filtersInfo: FiltersInfo = { const filtersInfo: FiltersInfo = {
datasourceFilters: {} datasourceFilters: {}
}; };
const originalColumns = this.getOriginalColumns(dashboard, sourceState, sourceLayout, breakpoint); const originalColumns = this.dashboardUtils.getOriginalColumns(dashboard, sourceState, sourceLayout, breakpoint);
const originalSize = this.getOriginalSize(dashboard, sourceState, sourceLayout, widget, breakpoint); const originalSize = this.dashboardUtils.getOriginalSize(dashboard, sourceState, sourceLayout, widget, breakpoint);
const datasources: Datasource[] = widget.type === widgetType.alarm ? [widget.config.alarmSource] : widget.config.datasources; const datasources: Datasource[] = widget.type === widgetType.alarm ? [widget.config.alarmSource] : widget.config.datasources;
if (widget.config && dashboard.configuration if (widget.config && dashboard.configuration
&& dashboard.configuration.entityAliases) { && dashboard.configuration.entityAliases) {
@ -396,42 +396,6 @@ export class ItemBufferService {
return ruleChainImport; return ruleChainImport;
} }
private getOriginalColumns(dashboard: Dashboard, sourceState: string, sourceLayout: DashboardLayoutId,
breakpoint: string): number {
let originalColumns = 24;
let gridSettings = null;
const state = dashboard.configuration.states[sourceState];
const layoutCount = Object.keys(state.layouts).length;
if (state) {
let layout = state.layouts[sourceLayout];
if (breakpoint !== 'default') {
layout = layout.breakpoints[breakpoint];
}
if (layout) {
gridSettings = layout.gridSettings;
}
}
if (gridSettings && gridSettings.columns) {
originalColumns = gridSettings.columns;
}
originalColumns = originalColumns * layoutCount;
return originalColumns;
}
private getOriginalSize(dashboard: Dashboard, sourceState: string, sourceLayout: DashboardLayoutId, widget: Widget,
breakpoint: string): WidgetSize {
let layout = dashboard.configuration.states[sourceState].layouts[sourceLayout];
if (breakpoint !== 'default') {
layout = layout.breakpoints[breakpoint];
}
const widgetLayout = layout.widgets[widget.id];
return {
sizeX: widgetLayout.sizeX,
sizeY: widgetLayout.sizeY
};
}
private prepareAliasInfo(entityAlias: EntityAlias): EntityAliasInfo { private prepareAliasInfo(entityAlias: EntityAlias): EntityAliasInfo {
return { return {
alias: entityAlias.alias, alias: entityAlias.alias,
@ -449,8 +413,8 @@ export class ItemBufferService {
private prepareWidgetReference(dashboard: Dashboard, sourceState: string, private prepareWidgetReference(dashboard: Dashboard, sourceState: string,
sourceLayout: DashboardLayoutId, widget: Widget, breakpoint: string): WidgetReference { sourceLayout: DashboardLayoutId, widget: Widget, breakpoint: string): WidgetReference {
const originalColumns = this.getOriginalColumns(dashboard, sourceState, sourceLayout, breakpoint); const originalColumns = this.dashboardUtils.getOriginalColumns(dashboard, sourceState, sourceLayout, breakpoint);
const originalSize = this.getOriginalSize(dashboard, sourceState, sourceLayout, widget, breakpoint); const originalSize = this.dashboardUtils.getOriginalSize(dashboard, sourceState, sourceLayout, widget, breakpoint);
return { return {
dashboardId: dashboard.id.id, dashboardId: dashboard.id.id,
sourceState, sourceState,

33
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -483,7 +483,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
if (!this.widgetEditMode && !this.readonly && this.dashboardUtils.isEmptyDashboard(this.dashboard)) { if (!this.widgetEditMode && !this.readonly && this.dashboardUtils.isEmptyDashboard(this.dashboard)) {
this.setEditMode(true, false); this.setEditMode(true, false);
} }
this.setEditMode(true, false);
} }
private init(data: DashboardPageInitData) { private init(data: DashboardPageInitData) {
@ -1419,31 +1418,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
} }
} }
copyEditWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget) { replaceReferenceWithWidgetCopy($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget) {
$event.stopPropagation(); $event.stopPropagation();
const widgetItem = deepClone(this.itembuffer.prepareWidgetItem(this.dashboard, this.dashboardCtx.state, const isRemove = layoutCtx.widgets.removeWidgetId(widget.id);
layoutCtx.id, widget, layoutCtx.breakpoint));
const targetRow = layoutCtx.widgetLayouts[widget.id].row; const widgetCopy = this.dashboardUtils.replaceReferenceWithWidgetCopy(widget, this.dashboard, this.dashboardCtx.state,
const targetColumn = layoutCtx.widgetLayouts[widget.id].col; layoutCtx.id, layoutCtx.breakpoint, isRemove);
if (layoutCtx.widgets.removeWidgetId(widget.id)) { layoutCtx.widgets.addWidgetId(widgetCopy.id);
this.dashboardUtils.removeWidgetFromLayout(this.dashboard, this.dashboardCtx.state, layoutCtx.id,
widget.id, layoutCtx.breakpoint);
}
widgetItem.widget.id = this.utils.guid();
this.itembuffer.addWidgetToDashboard(this.dashboard, this.dashboardCtx.state, layoutCtx.id, widgetItem.widget, this.runChangeDetection();
widgetItem.aliasesInfo, widgetItem.filtersInfo,
this.entityAliasesUpdated.bind(this), this.filtersUpdated.bind(this),
widgetItem.originalColumns, widgetItem.originalSize, targetRow, targetColumn, layoutCtx.breakpoint
).subscribe(() => {
layoutCtx.widgets.addWidgetId(widgetItem.widget.id);
this.runChangeDetection();
this.editWidget($event,layoutCtx, widgetItem.widget);
});
} }
copyWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget) { copyWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget) {
@ -1591,11 +1576,11 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
widgetContextActions.push( widgetContextActions.push(
{ {
action: (event, currentWidget) => { action: (event, currentWidget) => {
this.copyEditWidget(event, layoutCtx, currentWidget); this.replaceReferenceWithWidgetCopy(event, layoutCtx, currentWidget);
}, },
enabled: true, enabled: true,
value: 'widget.copy-edit', value: 'widget.replace-reference-with-widget-copy',
icon: 'edit' icon: 'mdi:file-replace-outline'
} }
); );
} }

2
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts

@ -57,7 +57,7 @@ export interface IDashboardController {
openDashboardState(stateId: string, openRightLayout: boolean); openDashboardState(stateId: string, openRightLayout: boolean);
addWidget($event: Event, layoutCtx: DashboardPageLayoutContext); addWidget($event: Event, layoutCtx: DashboardPageLayoutContext);
editWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget); editWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget);
copyEditWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget); replaceReferenceWithWidgetCopy($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget);
exportWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget, widgetTitle: string); exportWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget, widgetTitle: string);
removeWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget); removeWidget($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget);
widgetMouseDown($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget); widgetMouseDown($event: Event, layoutCtx: DashboardPageLayoutContext, widget: Widget);

4
ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.ts

@ -269,8 +269,8 @@ export class DashboardLayoutComponent extends PageComponent implements ILayoutCo
this.layoutCtx.dashboardCtrl.editWidget($event, this.layoutCtx, widget); this.layoutCtx.dashboardCtrl.editWidget($event, this.layoutCtx, widget);
} }
onCopyEditWidget($event: Event, widget: Widget): void { replaceReferenceWithWidgetCopy($event: Event, widget: Widget): void {
this.layoutCtx.dashboardCtrl.copyEditWidget($event, this.layoutCtx, widget); this.layoutCtx.dashboardCtrl.replaceReferenceWithWidgetCopy($event, this.layoutCtx, widget);
} }
onExportWidget($event: Event, widget: Widget, widgetTitle: string): void { onExportWidget($event: Event, widget: Widget, widgetTitle: string): void {

2
ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html

@ -58,7 +58,7 @@
[disabled]="!item.enabled" [disabled]="!item.enabled"
(click)="item.action(widgetContextMenuEvent, widget)"> (click)="item.action(widgetContextMenuEvent, widget)">
<span *ngIf="item.shortcut" class="tb-alt-text"> {{ item.shortcut | keyboardShortcut }}</span> <span *ngIf="item.shortcut" class="tb-alt-text"> {{ item.shortcut | keyboardShortcut }}</span>
<mat-icon *ngIf="item.icon">{{item.icon}}</mat-icon> <tb-icon matMenuItemIcon *ngIf="item.icon">{{item.icon}}</tb-icon>
<span translate>{{item.value}}</span> <span translate>{{item.value}}</span>
</button> </button>
</div> </div>

12
ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts

@ -453,15 +453,15 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
case WidgetComponentActionType.EDIT: case WidgetComponentActionType.EDIT:
this.editWidget($event, widget); this.editWidget($event, widget);
break; break;
case WidgetComponentActionType.COPY_EDIT:
this.editCopyWidget($event, widget);
break;
case WidgetComponentActionType.EXPORT: case WidgetComponentActionType.EXPORT:
this.exportWidget($event, widget); this.exportWidget($event, widget);
break; break;
case WidgetComponentActionType.REMOVE: case WidgetComponentActionType.REMOVE:
this.removeWidget($event, widget); this.removeWidget($event, widget);
break; break;
case WidgetComponentActionType.REPLACE_REFERENCE_WITH_WIDGET_COPY:
this.replaceReferenceWithWidgetCopy($event, widget);
break;
} }
} }
@ -486,12 +486,12 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
} }
} }
private editCopyWidget($event: Event, widget: DashboardWidget) { private replaceReferenceWithWidgetCopy($event: Event, widget: DashboardWidget) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
if (this.isEditActionEnabled && this.callbacks && this.callbacks.onCopyEditWidget) { if (this.isEditActionEnabled && this.callbacks && this.callbacks.replaceReferenceWithWidgetCopy) {
this.callbacks.onCopyEditWidget($event, widget.widget); this.callbacks.replaceReferenceWithWidgetCopy($event, widget.widget);
} }
} }

30
ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss

@ -13,6 +13,8 @@
* 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';
.tb-widget-container { .tb-widget-container {
position: absolute; position: absolute;
inset: 0; inset: 0;
@ -182,12 +184,30 @@ gridster-item:hover {
line-height: 12px; line-height: 12px;
font-weight: 500; font-weight: 500;
color: rgba(0, 0, 0, 0.76); color: rgba(0, 0, 0, 0.76);
.tb-widget-actions-panel { .tb-widget-action-container {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
place-content: center flex-start; gap: 6px;
align-items: center;
gap: 8px; .tb-widget-actions-panel {
display: flex;
flex-direction: row;
place-content: center space-between;
align-items: center;
gap: 8px;
}
.tb-widget-reference-panel {
display: flex;
flex-direction: row;
place-content: center space-between;
align-items: center;
gap: 8px;
font-weight: normal;
color: $tb-primary-color;
font-size: 11px;
padding: 4px 4px 4px 6px;
border-radius: 2px;
}
} }
} }
} }

74
ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts

@ -49,7 +49,7 @@ export enum WidgetComponentActionType {
EDIT, EDIT,
EXPORT, EXPORT,
REMOVE, REMOVE,
COPY_EDIT, REPLACE_REFERENCE_WITH_WIDGET_COPY,
} }
export class WidgetComponentAction { export class WidgetComponentAction {
@ -220,10 +220,10 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O
}); });
} }
onCopyEdit(event: MouseEvent) { onReplaceReferenceWithWidgetCopy(event: MouseEvent) {
this.widgetComponentAction.emit({ this.widgetComponentAction.emit({
event, event,
actionType: WidgetComponentActionType.COPY_EDIT actionType: WidgetComponentActionType.REPLACE_REFERENCE_WITH_WIDGET_COPY
}); });
} }
@ -343,37 +343,43 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O
} }
@Component({ @Component({
template: `<div class="tb-widget-actions-panel"> template: `
<button mat-icon-button class="tb-mat-20" <div class="tb-widget-action-container">
*ngIf="container.widget.isReference" <div class="tb-widget-reference-panel tb-primary-fill" *ngIf="container.widget.isReference">
[fxShow]="container.isEditActionEnabled" {{ 'widget.reference' | translate }}
(click)="container.onCopyEdit($event)" <button mat-icon-button class="tb-mat-16"
matTooltip="{{ 'widget.edit-copy' | translate }}" color="primary"
matTooltipPosition="above"> [fxShow]="container.isEditActionEnabled"
<tb-icon>mdi:circle-edit-outline</tb-icon> (click)="container.onReplaceReferenceWithWidgetCopy($event)"
</button> matTooltip="{{ 'widget.replace-reference-with-widget-copy' | translate }}"
<button mat-icon-button class="tb-mat-20" matTooltipPosition="above">
[fxShow]="container.isEditActionEnabled" <tb-icon matButtonIcon>mdi:file-replace-outline</tb-icon>
(click)="container.onEdit($event)" </button>
matTooltip="{{ 'widget.edit' | translate }}" </div>
matTooltipPosition="above"> <div class="tb-widget-actions-panel">
<tb-icon>edit</tb-icon> <button mat-icon-button class="tb-mat-20"
</button> [fxShow]="container.isEditActionEnabled"
<button mat-icon-button class="tb-mat-20" (click)="container.onEdit($event)"
[fxShow]="container.isExportActionEnabled" matTooltip="{{ 'widget.edit' | translate }}"
(click)="container.onExport($event)" matTooltipPosition="above">
matTooltip="{{ 'widget.export' | translate }}" <tb-icon>edit</tb-icon>
matTooltipPosition="above"> </button>
<tb-icon>file_download</tb-icon> <button mat-icon-button class="tb-mat-20"
</button> [fxShow]="container.isExportActionEnabled"
<button mat-icon-button class="tb-mat-20" (click)="container.onExport($event)"
[fxShow]="container.isRemoveActionEnabled" matTooltip="{{ 'widget.export' | translate }}"
(click)="container.onRemove($event);" matTooltipPosition="above">
matTooltip="{{ 'widget.remove' | translate }}" <tb-icon>file_download</tb-icon>
matTooltipPosition="above"> </button>
<tb-icon>close</tb-icon> <button mat-icon-button class="tb-mat-20"
</button> [fxShow]="container.isRemoveActionEnabled"
</div>`, (click)="container.onRemove($event);"
matTooltip="{{ 'widget.remove' | translate }}"
matTooltipPosition="above">
<tb-icon>close</tb-icon>
</button>
</div>
</div>`,
styles: [], styles: [],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })

2
ui-ngx/src/app/modules/home/models/dashboard-component.models.ts

@ -65,7 +65,7 @@ export interface WidgetContextMenuItem extends ContextMenuItem {
export interface DashboardCallbacks { export interface DashboardCallbacks {
onEditWidget?: ($event: Event, widget: Widget) => void; onEditWidget?: ($event: Event, widget: Widget) => void;
onCopyEditWidget?: ($event: Event, widget: Widget) => void; replaceReferenceWithWidgetCopy?: ($event: Event, widget: Widget) => void;
onExportWidget?: ($event: Event, widget: Widget, widgeTitle: string) => void; onExportWidget?: ($event: Event, widget: Widget, widgeTitle: string) => void;
onRemoveWidget?: ($event: Event, widget: Widget) => void; onRemoveWidget?: ($event: Event, widget: Widget) => void;
onWidgetMouseDown?: ($event: Event, widget: Widget) => void; onWidgetMouseDown?: ($event: Event, widget: Widget) => void;

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

@ -5601,10 +5601,9 @@
"remove": "Remove widget", "remove": "Remove widget",
"delete": "Delete widget", "delete": "Delete widget",
"edit": "Edit widget", "edit": "Edit widget",
"edit-copy": "Edit copy widget",
"copy-edit": "Copy and edit",
"remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?", "remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?",
"remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.", "remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.",
"replace-reference-with-widget-copy": "Replace reference with widget copy",
"timeseries": "Time series", "timeseries": "Time series",
"search-data": "Search data", "search-data": "Search data",
"no-data-found": "No data found", "no-data-found": "No data found",
@ -5685,7 +5684,8 @@
"search": "Search widget", "search": "Search widget",
"filter": "Widget filter type", "filter": "Widget filter type",
"loading-widgets": "Loading widgets...", "loading-widgets": "Loading widgets...",
"widget-template-error": "Invalid widget HTML template." "widget-template-error": "Invalid widget HTML template.",
"reference": "Reference"
}, },
"widget-action": { "widget-action": {
"header-button": "Widget header button", "header-button": "Widget header button",

6
ui-ngx/src/styles.scss

@ -921,6 +921,12 @@ mat-icon {
} }
.mat-mdc-icon-button { .mat-mdc-icon-button {
&.tb-mat-16 {
@include tb-mat-icon-button-size(16);
.mat-icon {
@include tb-mat-icon-size(16);
}
}
&.tb-mat-20 { &.tb-mat-20 {
@include tb-mat-icon-button-size(20); @include tb-mat-icon-button-size(20);
.mat-icon { .mat-icon {

Loading…
Cancel
Save