Browse Source

feat(html-container): tabbed settings UI, register widget, fill-height layout

- Restructure HTML Container settings into a mat-tab-group (Resources /
  HTML / CSS / JavaScript) instead of a single resources expansion panel
  followed by stacked editor blocks. Each editor tab uses [fillHeight]
  so the editors fill the panel.
- Wire fill-height plumbing: tb-widget-settings host h-full, basic and
  advanced settings @HostBinding('style.height')='100%', advanced panel
  switched from inline height:100% to flex-1, mat-content height:100%.
- Register html_container in widget_bundles/html_widgets.json so the
  widget appears in the HTML Widgets bundle.
- Replace the placeholder html-card image reference with a dedicated
  html-container.png asset and embedded data.
- Add 'JavaScript' translation key for the new tab label.
pull/15556/head
Igor Kulikov 5 months ago
parent
commit
dc479185a4
  1. 3
      application/src/main/data/json/system/widget_bundles/html_widgets.json
  2. 31
      application/src/main/data/json/system/widget_types/html_container.json
  3. 4
      ui-ngx/src/app/modules/home/components/widget/config/basic/html/html-container-basic-config.component.ts
  4. 73
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.html
  5. 28
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.scss
  6. 7
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.ts
  7. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.html
  8. 5
      ui-ngx/src/app/modules/home/components/widget/lib/settings/html/html-container-widget-settings.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
  10. 1
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss
  11. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
application/src/main/data/json/system/widget_bundles/html_widgets.json

@ -11,6 +11,7 @@
"widgetTypeFqns": [
"cards.html_card",
"cards.html_value_card",
"cards.markdown_card"
"cards.markdown_card",
"html_container"
]
}

31
application/src/main/data/json/system/widget_types/html_container.json

File diff suppressed because one or more lines are too long

4
ui-ngx/src/app/modules/home/components/widget/config/basic/html/html-container-basic-config.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component } from '@angular/core';
import { Component, HostBinding } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@ -34,6 +34,8 @@ import {
})
export class HtmlContainerBasicConfigComponent extends BasicWidgetConfigComponent {
@HostBinding('style.height') height = '100%';
htmlContainerWidgetConfigForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,

73
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.html

@ -16,7 +16,7 @@
-->
<ng-container [formGroup]="htmlContainerSettingsForm">
<div class="tb-form-panel no-padding no-border">
<div class="tb-form-panel no-padding no-border relative h-full">
<div class="flex flex-row items-center gap-4">
<div class="tb-form-panel-title" translate>widgets.html-container.container-type</div>
<tb-toggle-select formControlName="type">
@ -24,15 +24,13 @@
<tb-toggle-option [value]="HtmlContainerWidgetType.ANGULAR">{{ 'widgets.html-container.type-angular' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<div class="tb-form-panel stroked !py-2">
<mat-expansion-panel #resourcesPanel="matExpansionPanel" class="tb-settings" [expanded]="false" >
<mat-expansion-panel-header class="flex flex-row flex-wrap">
<mat-panel-title class="h-10">
<div [matBadge]="resourcesFormArray.length" [matBadgeHidden]="resourcesPanel.expanded || !resourcesFormArray.length"
matBadgeSize="small">{{ 'widgets.html-container.resources' | translate }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-tab-group [mat-stretch-tabs]="false" selectedIndex="3" class="flex-1">
<mat-tab #resourceTab="matTab">
<ng-template mat-tab-label>
<div [matBadge]="resourcesFormArray.length" [matBadgeHidden]="resourceTab.isActive || !resourcesFormArray.length"
matBadgeSize="small">{{ 'widgets.html-container.resources' | translate }}</div>
</ng-template>
<div class="flex flex-col gap-2 pt-4">
@if (resourcesFormArray.length) {
@for (resourceControl of resourcesControls; track resourceControl; let i = $index) {
<div class="tb-form-row no-border no-padding" [formGroup]="resourceControl">
@ -60,7 +58,7 @@
<span translate
class="tb-prompt flex items-center justify-center">widgets.html-container.no-resources</span>
}
<div class="mt-2">
<div>
<button mat-raised-button color="primary"
(click)="addResource()"
matTooltip="{{'widget.add-resource' | translate}}"
@ -68,30 +66,33 @@
<span translate>action.add</span>
</button>
</div>
</ng-template>
</mat-expansion-panel>
</div>
<div class="tb-form-row">
<tb-html class="flex-1"
formControlName="html"
label="{{ (htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR ? 'widgets.html-container.angular-html-template' : 'widgets.html-container.html') | translate }}">
</tb-html>
</div>
<div class="tb-form-row">
<tb-css class="flex-1"
formControlName="css"
label="{{ 'widgets.html-container.css' | translate }}">
</tb-css>
</div>
<div class="tb-form-row">
<tb-js-func class="flex-1"
formControlName="js"
[globalVariables]="functionScopeVariables"
[editorCompleter]="containerFunctionEditorCompleter"
[functionArgs]="htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR ? ['ctx'] : ['ctx', 'container']"
withModules
functionTitle="{{ 'widgets.html-container.js-function' | translate }}">
</tb-js-func>
</div>
</div>
</mat-tab>
<mat-tab label="{{ (htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR ? 'widgets.html-container.angular-html-template' : 'widgets.html-container.html') | translate }}">
<tb-html class="flex-1"
[fillHeight]="true"
formControlName="html"
label="{{ (htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR ? 'widgets.html-container.angular-html-template' : 'widgets.html-container.html') | translate }}">
</tb-html>
</mat-tab>
<mat-tab label="{{ 'widgets.html-container.css' | translate }}">
<tb-css class="flex-1"
[fillHeight]="true"
formControlName="css"
label="{{ 'widgets.html-container.css' | translate }}">
</tb-css>
</mat-tab>
<mat-tab label="{{ 'widgets.html-container.java-script' | translate }}">
<tb-js-func class="flex-1"
[fillHeight]="true"
formControlName="js"
[globalVariables]="functionScopeVariables"
[editorCompleter]="containerFunctionEditorCompleter"
[functionArgs]="htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR ? ['ctx'] : ['ctx', 'container']"
withModules
functionTitle="{{ 'widgets.html-container.js-function' | translate }}">
</tb-js-func>
</mat-tab>
</mat-tab-group>
</div>
</ng-container>

28
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.scss

@ -0,0 +1,28 @@
/**
* 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 {
&.tb-html-container-settings {
height: 100%;
::ng-deep {
.mat-mdc-tab-body-wrapper {
position: relative;
top: 0;
flex: 1;
}
}
}
}

7
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { Component, DestroyRef, forwardRef, HostBinding, Input, OnInit } from '@angular/core';
import { WidgetResource } from '@shared/models/widget.models';
import {
ControlValueAccessor,
@ -39,7 +39,7 @@ import { WidgetService } from '@core/http/widget.service';
@Component({
selector: 'tb-html-container-settings',
templateUrl: './html-container-settings.component.html',
styleUrls: [],
styleUrls: ['./html-container-settings.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -62,6 +62,9 @@ export class HtmlContainerSettingsComponent implements OnInit, ControlValueAcces
containerFunctionEditorCompleter = ContainerFunctionEditorCompleter;
@HostBinding('class')
hostClass = 'tb-html-container-settings';
@Input()
disabled: boolean;

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div [formGroup]="widgetSettingsFormGroup">
<div [formGroup]="widgetSettingsFormGroup" class="h-full">
<ng-container #definedSettingsContent></ng-container>
<div class="tb-settings-directive-error" *ngIf="definedDirectiveError">{{definedDirectiveError}}</div>
<tb-dynamic-form *ngIf="useDynamicForm()"

5
ui-ngx/src/app/modules/home/components/widget/lib/settings/html/html-container-widget-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component } from '@angular/core';
import { Component, HostBinding } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
@ -29,6 +29,9 @@ import { htmlContainerDefaultSettings } from '@home/components/widget/lib/html/h
})
export class HtmlContainerWidgetSettingsComponent extends WidgetSettingsComponent {
@HostBinding('height')
hostHeight = '100%';
htmlContainerWidgetSettingsForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,

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

@ -308,7 +308,7 @@
</mat-form-field>
</div>
</div>
<div *ngIf="displayAdvancedAppearance" style="height: 100%;" [formGroup]="advancedSettings">
<div *ngIf="displayAdvancedAppearance" class="flex-1" [formGroup]="advancedSettings">
<tb-widget-settings
[aliasController]="aliasController"
[callbacks]="widgetConfigCallbacks"

1
ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss

@ -43,6 +43,7 @@
}
.tb-widget-config-content {
& > .mat-content {
height: 100%;
padding-top: 8px;
@media #{$mat-xs} {
padding-left: 8px;

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

@ -9513,6 +9513,7 @@
}
},
"html-container": {
"java-script": "JavaScript",
"js-function": "JavaScript function",
"html": "HTML",
"angular-html-template": "Angular HTML template",

Loading…
Cancel
Save