Browse Source

feat(widget-select): add icons to top-level Installed / IoT Hub toggle

- Add a `template?: TemplateRef<any>` slot to ToggleHeaderOption +
  ToggleOption so callers can render arbitrary content inside a
  toggle (the existing string label / error icon path stays the
  default).
- Use it in the dashboard widget-select header to render a tb-icon
  next to each label: now_widgets for Installed, hub for IoT Hub.
- Style the top-level toggle with a 42px pill (8px radius, white-15
  border, 40px buttons, 36px line-height, 500 weight) so the icon +
  label sit comfortably and contrast against the primary toolbar.
pull/15615/head
Igor Kulikov 5 months ago
parent
commit
382de37b16
  1. 19
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html
  2. 35
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss
  3. 20
      ui-ngx/src/app/shared/components/toggle-header.component.html
  4. 6
      ui-ngx/src/app/shared/components/toggle-header.component.ts

19
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html

@ -19,13 +19,26 @@
<mat-toolbar color="primary" class="tb-widget-select-header flex flex-col"> <mat-toolbar color="primary" class="tb-widget-select-header flex flex-col">
<div class="tb-widget-select-title-row flex flex-row items-center"> <div class="tb-widget-select-title-row flex flex-row items-center">
@if (showTopLevelWidgetModeToggle()) { @if (showTopLevelWidgetModeToggle()) {
<tb-toggle-select appearance="fill-invert" <tb-toggle-select class="tb-top-level-widget-mode-toggle"
appearance="fill-invert"
[disablePagination]="true" [disablePagination]="true"
selectMediaBreakpoint="xs" selectMediaBreakpoint="xs"
[(ngModel)]="selectWidgetMode"> [(ngModel)]="selectWidgetMode">
<tb-toggle-option value="installed">{{ 'widget.installed' | translate }}</tb-toggle-option> <tb-toggle-option value="installed" [template]="installedWidgetsOption"></tb-toggle-option>
<tb-toggle-option value="iotHub">{{ 'iot-hub.iot-hub' | translate }}</tb-toggle-option> <tb-toggle-option value="iotHub" [template]="iotHubWidgetsOption"></tb-toggle-option>
</tb-toggle-select> </tb-toggle-select>
<ng-template #installedWidgetsOption>
<div class="flex items-center gap-2">
<tb-icon>now_widgets</tb-icon>
<div>{{ 'widget.installed' | translate }}</div>
</div>
</ng-template>
<ng-template #iotHubWidgetsOption>
<div class="flex items-center gap-2">
<tb-icon>hub</tb-icon>
<div>{{ 'iot-hub.iot-hub' | translate }}</div>
</div>
</ng-template>
} @else if (showBackButton()) { } @else if (showBackButton()) {
<button mat-icon-button type="button" (click)="onBackClick()"> <button mat-icon-button type="button" (click)="onBackClick()">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>

35
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss

@ -64,6 +64,41 @@ tb-dashboard-widget-select {
} }
} }
.tb-top-level-widget-mode-toggle {
.tb-toggle-container {
.mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header {
height: 42px;
border-radius: 8px;
overflow: hidden;
padding: 0;
border: 1px solid rgba(255, 255, 255, 0.15);
.mat-button-toggle.mat-button-toggle-appearance-standard {
.mat-button-toggle-focus-overlay, .mat-button-toggle-ripple {
border-radius: 0;
}
.mat-button-toggle-button {
height: 40px;
.mat-button-toggle-label-content {
line-height: 36px;
font-weight: 500;
}
}
&.mat-button-toggle-checked {
.mat-button-toggle-button {
border: none;
border-radius: 0;
}
}
}
&.tb-fill.tb-invert {
.mat-button-toggle.mat-button-toggle-appearance-standard {
color: #FFF;
}
}
}
}
}
.tb-widget-select-search-row { .tb-widget-select-search-row {
width: 100%; width: 100%;
gap: 8px; gap: 8px;

20
ui-ngx/src/app/shared/components/toggle-header.component.html

@ -42,14 +42,18 @@
[(ngModel)]="value" [(ngModel)]="value"
(ngModelChange)="valueChange.emit(value)"> (ngModelChange)="valueChange.emit(value)">
<mat-button-toggle *ngFor="let option of options; trackBy: trackByHeaderOption" [value]="option.value" [disabled]="disabled" [class.tb-toggle-button-error]="(option.error$ | async) && value !== option.value"> <mat-button-toggle *ngFor="let option of options; trackBy: trackByHeaderOption" [value]="option.value" [disabled]="disabled" [class.tb-toggle-button-error]="(option.error$ | async) && value !== option.value">
{{ option.name }} @if (option.template) {
<mat-icon matTooltipPosition="above" <ng-container *ngTemplateOutlet="option.template"></ng-container>
matTooltipClass="tb-error-tooltip" } @else {
[matTooltip]="(option.error$ | async)" {{ option.name }}
*ngIf="(option.error$ | async) && value !== option.value" <mat-icon matTooltipPosition="above"
class="tb-error tb-error-icon"> matTooltipClass="tb-error-tooltip"
warning [matTooltip]="(option.error$ | async)"
</mat-icon> *ngIf="(option.error$ | async) && value !== option.value"
class="tb-error tb-error-icon">
warning
</mat-icon>
}
</mat-button-toggle> </mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>

6
ui-ngx/src/app/shared/components/toggle-header.component.ts

@ -33,7 +33,7 @@ import {
OnInit, OnInit,
Output, Output,
QueryList, QueryList,
SimpleChanges, SimpleChanges, TemplateRef,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -50,6 +50,7 @@ import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-
export interface ToggleHeaderOption { export interface ToggleHeaderOption {
name: string; name: string;
value: any; value: any;
template?: TemplateRef<any>;
error$?: Observable<string>; error$?: Observable<string>;
} }
@ -69,6 +70,8 @@ export class ToggleOption implements OnChanges, OnDestroy {
@Input() value: any; @Input() value: any;
@Input() template: TemplateRef<any>;
@Input() error: string; @Input() error: string;
currentError = new ReplaySubject<string>(1); currentError = new ReplaySubject<string>(1);
@ -127,6 +130,7 @@ export abstract class _ToggleBase extends PageComponent implements AfterContentI
{ {
name: option.viewValue, name: option.viewValue,
value: option.value, value: option.value,
template: option.template,
error$: option.currentError.asObservable() error$: option.currentError.asObservable()
} }
); );

Loading…
Cancel
Save