Browse Source
- Top-level toggle now Installed / IoT Hub; Installed default shows bundles + 'All widgets' logical card; IoT Hub default shows 'All widgets' + 'Installed from IoT Hub' logical cards + widget categories - Logical cards rendered inline in the scroll-grid via a fetch-function wrapper that prepends __logical discriminators - 120px 2-row mat-toolbar header built into the component (no more tb-details-panel wrapper) - Inline search input replaces tb-widgets-bundle-search (component deleted); search preserved across state transitions - Filter popover: unified style for both modes with mat-expansion-panel, Clear all + close in header, immediate-apply (no Apply/Cancel); badge count on filter button - Install-mode filter: widget types (empty = all) + Include Deprecated (visible for bundle or all-widgets) - Sort mat-menu (Most installed / Newest / Name), visible in iotHub sub-modes - scadaFirst input propagated to MpItemVersionQuery, getWidgetCategories, and installed-widget sort - Remove obsolete widget-types-panel component and editWidgetsTypesToDisplay flow - Add WidgetCategory model and getWidgetCategories API, add scadaFirst parampull/15508/head
21 changed files with 1072 additions and 754 deletions
@ -1,23 +0,0 @@ |
|||||
<!-- |
|
||||
|
|
||||
Copyright © 2016-2026 The Thingsboard Authors |
|
||||
|
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
you may not use this file except in compliance with the License. |
|
||||
You may obtain a copy of the License at |
|
||||
|
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
|
|
||||
Unless required by applicable law or agreed to in writing, software |
|
||||
distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
See the License for the specific language governing permissions and |
|
||||
limitations under the License. |
|
||||
|
|
||||
--> |
|
||||
<div class="mat-content mat-padding flex flex-col"> |
|
||||
<mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="type.display" *ngFor="let type of types" |
|
||||
(ngModelChange)="update()"> |
|
||||
{{ 'widget.' + type.type | translate }} |
|
||||
</mat-checkbox> |
|
||||
</div> |
|
||||
@ -1,36 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2026 The Thingsboard Authors |
|
||||
* |
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
* you may not use this file except in compliance with the License. |
|
||||
* You may obtain a copy of the License at |
|
||||
* |
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
* |
|
||||
* Unless required by applicable law or agreed to in writing, software |
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
* See the License for the specific language governing permissions and |
|
||||
* limitations under the License. |
|
||||
*/ |
|
||||
:host { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
min-width: 200px; |
|
||||
overflow: hidden; |
|
||||
background: #fff; |
|
||||
border-radius: 4px; |
|
||||
box-shadow: |
|
||||
0 7px 8px -4px rgba(0, 0, 0, .2), |
|
||||
0 13px 19px 2px rgba(0, 0, 0, .14), |
|
||||
0 5px 24px 4px rgba(0, 0, 0, .12); |
|
||||
|
|
||||
.mat-content { |
|
||||
overflow: hidden; |
|
||||
background-color: #fff; |
|
||||
} |
|
||||
|
|
||||
.mat-padding { |
|
||||
padding: 16px; |
|
||||
} |
|
||||
} |
|
||||
@ -1,49 +0,0 @@ |
|||||
///
|
|
||||
/// Copyright © 2016-2026 The Thingsboard Authors
|
|
||||
///
|
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||
/// you may not use this file except in compliance with the License.
|
|
||||
/// You may obtain a copy of the License at
|
|
||||
///
|
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||
///
|
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||
/// See the License for the specific language governing permissions and
|
|
||||
/// limitations under the License.
|
|
||||
///
|
|
||||
|
|
||||
import { Component, Inject, InjectionToken } from '@angular/core'; |
|
||||
import { widgetType } from '@shared/models/widget.models'; |
|
||||
|
|
||||
export const DISPLAY_WIDGET_TYPES_PANEL_DATA = new InjectionToken<any>('DisplayWidgetTypesPanelData'); |
|
||||
|
|
||||
export interface WidgetTypes { |
|
||||
type: widgetType; |
|
||||
display: boolean; |
|
||||
} |
|
||||
|
|
||||
export interface DisplayWidgetTypesPanelData { |
|
||||
types: WidgetTypes[]; |
|
||||
typesUpdated: (columns: WidgetTypes[]) => void; |
|
||||
} |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'tb-widget-types-panel', |
|
||||
templateUrl: './widget-types-panel.component.html', |
|
||||
styleUrls: ['./widget-types-panel.component.scss'], |
|
||||
standalone: false |
|
||||
}) |
|
||||
export class DisplayWidgetTypesPanelComponent { |
|
||||
|
|
||||
types: WidgetTypes[]; |
|
||||
|
|
||||
constructor(@Inject(DISPLAY_WIDGET_TYPES_PANEL_DATA) public data: DisplayWidgetTypesPanelData) { |
|
||||
this.types = this.data.types; |
|
||||
} |
|
||||
|
|
||||
public update() { |
|
||||
this.data.typesUpdated(this.types); |
|
||||
} |
|
||||
} |
|
||||
@ -1,29 +0,0 @@ |
|||||
<!-- |
|
||||
|
|
||||
Copyright © 2016-2026 The Thingsboard Authors |
|
||||
|
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
you may not use this file except in compliance with the License. |
|
||||
You may obtain a copy of the License at |
|
||||
|
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
|
|
||||
Unless required by applicable law or agreed to in writing, software |
|
||||
distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
See the License for the specific language governing permissions and |
|
||||
limitations under the License. |
|
||||
|
|
||||
--> |
|
||||
<div class="input-wrapper flex items-center justify-start gap-4" [class.focus]="focus"> |
|
||||
<mat-icon>search</mat-icon> |
|
||||
<input type="text" |
|
||||
[(ngModel)]="searchText" |
|
||||
(ngModelChange)="updateSearchText()" |
|
||||
(focus)="toggleFocus()" |
|
||||
(blur)="toggleFocus()" |
|
||||
[placeholder]=placeholder> |
|
||||
<button type="button" *ngIf="searchText" mat-icon-button (click)="clear($event)"> |
|
||||
<mat-icon class="close">close</mat-icon> |
|
||||
</button> |
|
||||
</div> |
|
||||
@ -1,55 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2026 The Thingsboard Authors |
|
||||
* |
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
* you may not use this file except in compliance with the License. |
|
||||
* You may obtain a copy of the License at |
|
||||
* |
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
* |
|
||||
* Unless required by applicable law or agreed to in writing, software |
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
* See the License for the specific language governing permissions and |
|
||||
* limitations under the License. |
|
||||
*/ |
|
||||
.input-wrapper { |
|
||||
background: hsla(0, 0%, 100%, .2); |
|
||||
border-radius: 8px; |
|
||||
color: rgb(255, 255, 255, .84); |
|
||||
padding: 5px 8px 5px 16px; |
|
||||
height: 46px; |
|
||||
|
|
||||
input { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
padding: 0; |
|
||||
font-size: 20px; |
|
||||
outline: none; |
|
||||
border: none; |
|
||||
background-color: transparent; |
|
||||
color: #fff; |
|
||||
|
|
||||
&::placeholder { |
|
||||
color: #fff; |
|
||||
opacity: .6; |
|
||||
line-height: 26px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
&.focus { |
|
||||
background: #fff; |
|
||||
color: rgba(0,0,0,.54); |
|
||||
|
|
||||
input { |
|
||||
color: #000; |
|
||||
opacity: .60; |
|
||||
} |
|
||||
|
|
||||
.close { |
|
||||
color: #000 !important; |
|
||||
opacity: .54; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@ -1,75 +0,0 @@ |
|||||
///
|
|
||||
/// Copyright © 2016-2026 The Thingsboard Authors
|
|
||||
///
|
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||
/// you may not use this file except in compliance with the License.
|
|
||||
/// You may obtain a copy of the License at
|
|
||||
///
|
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||
///
|
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||
/// See the License for the specific language governing permissions and
|
|
||||
/// limitations under the License.
|
|
||||
///
|
|
||||
|
|
||||
import { Component, ElementRef, forwardRef, Input, ViewChild, ViewEncapsulation } from '@angular/core'; |
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'tb-widgets-bundle-search', |
|
||||
templateUrl: './widgets-bundle-search.component.html', |
|
||||
styleUrls: ['./widgets-bundle-search.component.scss'], |
|
||||
providers: [{ |
|
||||
provide: NG_VALUE_ACCESSOR, |
|
||||
useExisting: forwardRef(() => WidgetsBundleSearchComponent), |
|
||||
multi: true |
|
||||
}], |
|
||||
encapsulation: ViewEncapsulation.None, |
|
||||
standalone: false |
|
||||
}) |
|
||||
export class WidgetsBundleSearchComponent implements ControlValueAccessor { |
|
||||
|
|
||||
searchText: string; |
|
||||
focus = false; |
|
||||
|
|
||||
@Input() placeholder: string; |
|
||||
|
|
||||
@ViewChild('searchInput') searchInput: ElementRef<HTMLInputElement>; |
|
||||
|
|
||||
private propagateChange = (v: any) => { }; |
|
||||
|
|
||||
constructor() { |
|
||||
} |
|
||||
|
|
||||
registerOnChange(fn: any): void { |
|
||||
this.propagateChange = fn; |
|
||||
} |
|
||||
|
|
||||
registerOnTouched(fn: any): void { |
|
||||
} |
|
||||
|
|
||||
writeValue(value: string | null): void { |
|
||||
this.searchText = value; |
|
||||
} |
|
||||
|
|
||||
updateSearchText(): void { |
|
||||
this.updateView(); |
|
||||
} |
|
||||
|
|
||||
private updateView() { |
|
||||
this.propagateChange(this.searchText); |
|
||||
} |
|
||||
|
|
||||
clear($event: Event): void { |
|
||||
$event.preventDefault(); |
|
||||
$event.stopPropagation(); |
|
||||
this.searchText = ''; |
|
||||
this.updateView(); |
|
||||
} |
|
||||
|
|
||||
toggleFocus() { |
|
||||
this.focus = !this.focus; |
|
||||
} |
|
||||
} |
|
||||
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 647 B |
Loading…
Reference in new issue