UI: Add mobile configure commands and add useSystemSettings in qr code widget
This commit is contained in:
parent
b0e11e130a
commit
c6396e4074
@ -35,6 +35,8 @@ public class QrCodeSettings extends BaseData<QrCodeSettingsId> implements HasTen
|
||||
|
||||
@Schema(description = "JSON object with Tenant Id.", accessMode = Schema.AccessMode.READ_ONLY)
|
||||
private TenantId tenantId;
|
||||
@Schema(description = "Use settings from system level", example = "true")
|
||||
private boolean useSystemSettings;
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of application: true means use default Thingsboard app", example = "true")
|
||||
private boolean useDefaultApp;
|
||||
@Schema(description = "Mobile app bundle.")
|
||||
|
||||
@ -23,10 +23,53 @@
|
||||
<mat-icon class="material-icons">close</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar>
|
||||
<div mat-dialog-content>
|
||||
|
||||
<div mat-dialog-content class="flex flex-col gap-4 xs:gap-2 gt-xs:max-h-[80vh]">
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title" translate>mobile.configuration-step.prepare-environment-title</div>
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1" translate>mobile.configuration-step.prepare-environment-text</div>
|
||||
<a mat-stroked-button
|
||||
color="primary"
|
||||
href="https://docs.flutter.dev/get-started/install"
|
||||
target="_blank">
|
||||
<mat-icon class="tb-mat-24">description</mat-icon>{{ 'common.documentation' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title" translate>mobile.configuration-step.get-source-code-title</div>
|
||||
<div translate>mobile.configuration-step.get-source-code-text</div>
|
||||
<tb-markdown usePlainMarkdown containerClass="tb-command-code"
|
||||
[data]=createMarkDownCommand(gitRepositoryLink)></tb-markdown>
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title" translate>mobile.configuration-step.configure-api-title</div>
|
||||
<div translate>mobile.configuration-step.configure-api-text</div>
|
||||
<tb-markdown usePlainMarkdown containerClass="tb-command-code"
|
||||
[data]=createMarkDownCommand(pathToConstants)></tb-markdown>
|
||||
<div translate>mobile.configuration-step.configure-api-hint</div>
|
||||
<tb-markdown usePlainMarkdown containerClass="tb-command-code"
|
||||
[data]=createMarkDownCommand(configureApi)></tb-markdown>
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title" translate>mobile.configuration-step.run-app-title</div>
|
||||
<div translate>mobile.configuration-step.run-app-text</div>
|
||||
<tb-markdown usePlainMarkdown containerClass="tb-command-code"
|
||||
[data]=createMarkDownCommand(flutterRunCommand)></tb-markdown>
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex-1" translate>mobile.configuration-step.more-information</div>
|
||||
<a mat-stroked-button
|
||||
color="primary"
|
||||
href="https://docs.flutter.dev/get-started/install"
|
||||
target="_blank">
|
||||
<mat-icon class="tb-mat-24">rocket_launch</mat-icon>{{ 'mobile.configuration-step.getting-started' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions class="tb-dialog-actions">
|
||||
<div mat-dialog-actions class="tb-dialog-actions gap-2">
|
||||
<mat-slide-toggle [class.!hidden]="!showDontShowAgain" [(ngModel)]="notShowAgain">{{ 'action.dont-show-again' | translate}}</mat-slide-toggle>
|
||||
<span class="flex-1"></span>
|
||||
<button mat-button
|
||||
|
||||
@ -13,10 +13,84 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@import "./../scss/constants";
|
||||
:host {
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
display: grid;
|
||||
width: 800px;
|
||||
width: 870px;
|
||||
max-width: 100%;
|
||||
grid-template-rows: min-content minmax(auto, 1fr) min-content;
|
||||
}
|
||||
|
||||
:host-context(.mat-mdc-dialog-container) {
|
||||
.tb-dialog-actions {
|
||||
padding: 8px 16px 8px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
:host ::ng-deep {
|
||||
.tb-markdown-view {
|
||||
.tb-command-code {
|
||||
.code-wrapper {
|
||||
padding: 0;
|
||||
pre[class*=language-] {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
background: #F3F6FA;
|
||||
border-color: $tb-primary-color;
|
||||
padding-right: 38px;
|
||||
overflow: scroll;
|
||||
padding-bottom: 4px;
|
||||
min-height: 42px;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
button.clipboard-btn {
|
||||
right: -2px;
|
||||
p {
|
||||
color: $tb-primary-color;
|
||||
}
|
||||
p, div {
|
||||
background-color: #F3F6FA;
|
||||
}
|
||||
div {
|
||||
img {
|
||||
display: none;
|
||||
}
|
||||
&:after {
|
||||
content: "";
|
||||
position: initial;
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: $tb-primary-color;
|
||||
mask-image: url(/assets/copy-code-icon.svg);
|
||||
-webkit-mask-image: url(/assets/copy-code-icon.svg);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.mdc-button__label > span {
|
||||
.mat-icon {
|
||||
vertical-align: text-bottom;
|
||||
box-sizing: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.tb-form-panel.tb-tab-body {
|
||||
padding: 16px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,9 +20,11 @@ import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { Router } from '@angular/router';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ActionPreferencesPutUserSettings } from '@core/auth/auth.actions';
|
||||
|
||||
export interface MobileAppConfigurationDialogData {
|
||||
afterAdd: boolean;
|
||||
appSecret: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -36,6 +38,12 @@ export class MobileAppConfigurationDialogComponent extends DialogComponent<Mobil
|
||||
|
||||
showDontShowAgain: boolean;
|
||||
|
||||
gitRepositoryLink = 'git clone -b master https://github.com/thingsboard/flutter_thingsboard_app.git';
|
||||
pathToConstants = 'lib/constants/app_constants.dart';
|
||||
flutterRunCommand = 'flutter run';
|
||||
|
||||
configureApi: string[] = [];
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
protected router: Router,
|
||||
@Inject(MAT_DIALOG_DATA) private data: MobileAppConfigurationDialogData,
|
||||
@ -45,14 +53,33 @@ export class MobileAppConfigurationDialogComponent extends DialogComponent<Mobil
|
||||
|
||||
this.showDontShowAgain = this.data.afterAdd;
|
||||
|
||||
this.configureApi.push(`static final thingsBoardApiEndpoint = '${window.location.origin}';`);
|
||||
this.configureApi.push(`static const thingsboardOAuth2AppSecret = '${this.data.appSecret}';`);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.notShowAgain && this.showDontShowAgain) {
|
||||
// this.store.dispatch(new ActionPreferencesPutUserSettings({ notDisplayConnectivityAfterAddDevice: true }));
|
||||
this.store.dispatch(new ActionPreferencesPutUserSettings({ notDisplayConfigurationAfterAddMobileApp: true }));
|
||||
this.dialogRef.close(null);
|
||||
} else {
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
}
|
||||
|
||||
createMarkDownCommand(commands: string | string[]): string {
|
||||
if (Array.isArray(commands)) {
|
||||
const formatCommands: Array<string> = [];
|
||||
commands.forEach(command => formatCommands.push(this.createMarkDownSingleCommand(command)));
|
||||
return formatCommands.join(`\n<br />\n\n`);
|
||||
} else {
|
||||
return this.createMarkDownSingleCommand(commands);
|
||||
}
|
||||
}
|
||||
|
||||
private createMarkDownSingleCommand(command: string): string {
|
||||
return '```bash\n' +
|
||||
command +
|
||||
'{:copy-code}\n' +
|
||||
'```';
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +52,10 @@ import {
|
||||
import {
|
||||
MobileAppConfigurationDialogComponent, MobileAppConfigurationDialogData
|
||||
} from '@home/pages/mobile/applications/mobile-app-configuration-dialog.component';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
import { selectUserSettingsProperty } from '@core/auth/auth.selectors';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AppState } from '@core/core.state';
|
||||
|
||||
@Injectable()
|
||||
export class MobileAppTableConfigResolver {
|
||||
@ -62,7 +66,9 @@ export class MobileAppTableConfigResolver {
|
||||
private datePipe: DatePipe,
|
||||
private mobileAppService: MobileAppService,
|
||||
private truncatePipe: TruncatePipe,
|
||||
private dialog: MatDialog,) {
|
||||
private dialog: MatDialog,
|
||||
private store: Store<AppState>,
|
||||
) {
|
||||
this.config.selectionEnabled = false;
|
||||
this.config.entityType = EntityType.MOBILE_APP;
|
||||
this.config.addEnabled = false;
|
||||
@ -124,6 +130,18 @@ export class MobileAppTableConfigResolver {
|
||||
this.config.saveEntity = (mobileApp) => this.mobileAppService.saveMobileApp(mobileApp);
|
||||
this.config.deleteEntity = id => this.mobileAppService.deleteMobileApp(id.id);
|
||||
|
||||
this.config.entityAdded = (mobileApp) => {
|
||||
this.store.pipe(select(selectUserSettingsProperty( 'notDisplayConfigurationAfterAddMobileApp'))).pipe(
|
||||
take(1)
|
||||
).subscribe((settings: boolean) => {
|
||||
if(!settings) {
|
||||
this.configurationApp(null, mobileApp, true);
|
||||
} else {
|
||||
this.config.updateData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.config.cellActionDescriptors = this.configureCellActions();
|
||||
}
|
||||
|
||||
@ -176,7 +194,8 @@ export class MobileAppTableConfigResolver {
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
afterAdd
|
||||
afterAdd,
|
||||
appSecret: entity.appSecret
|
||||
}
|
||||
}).afterClosed()
|
||||
.subscribe(() => {
|
||||
|
||||
@ -26,7 +26,12 @@
|
||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||
<mat-card-content style="padding-top: 16px">
|
||||
<div class="tb-form-panel no-border no-padding" [formGroup]="mobileAppSettingsForm">
|
||||
<div class="tb-form-panel">
|
||||
<div class="tb-form-row no-border no-padding-bottom" *ngIf="isTenantAdmin()">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="useSystemSettings">
|
||||
{{ 'admin.mobile-app.use-system-settings' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div class="tb-form-panel" *ngIf="!mobileAppSettingsForm.get('useSystemSettings').value">
|
||||
<div class="tb-form-row column-xs no-border no-padding space-between">
|
||||
<div class="tb-form-panel-title" translate>admin.mobile-app.applications</div>
|
||||
<tb-toggle-select formControlName="useDefaultApp">
|
||||
@ -151,7 +156,7 @@
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<div class="tb-form-panel" formGroupName="qrCodeConfig">
|
||||
<div class="tb-form-panel" formGroupName="qrCodeConfig" *ngIf="!mobileAppSettingsForm.get('useSystemSettings').value">
|
||||
<div class="tb-form-row column-xs no-border no-padding space-between">
|
||||
<div class="tb-form-panel-title" translate>admin.mobile-app.appearance-on-home-page</div>
|
||||
<tb-toggle-select formControlName="showOnHomePage">
|
||||
|
||||
@ -14,28 +14,26 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { MobileApplicationService } from '@core/http/mobile-application.service';
|
||||
import {
|
||||
BadgePosition,
|
||||
badgePositionTranslationsMap,
|
||||
QrCodeSettings
|
||||
} from '@shared/models/mobile-app.models';
|
||||
import { BadgePosition, badgePositionTranslationsMap, QrCodeSettings } from '@shared/models/mobile-app.models';
|
||||
import { ActionUpdateMobileQrCodeEnabled } from '@core/auth/auth.actions';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
|
||||
import { Authority } from '@shared/models/authority.enum';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-mobile-qr-code-widget',
|
||||
templateUrl: './mobile-qr-code-widget-settings.component.html',
|
||||
styleUrls: ['mobile-qr-code-widget-settings.component.scss', '../../admin/settings-card.scss']
|
||||
})
|
||||
export class MobileQrCodeWidgetSettingsComponent extends PageComponent implements HasConfirmForm, OnDestroy {
|
||||
export class MobileQrCodeWidgetSettingsComponent extends PageComponent implements HasConfirmForm {
|
||||
|
||||
mobileAppSettingsForm: FormGroup;
|
||||
mobileAppSettings: QrCodeSettings;
|
||||
@ -43,7 +41,7 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
readonly badgePositionTranslationsMap = badgePositionTranslationsMap;
|
||||
readonly entityType = EntityType;
|
||||
|
||||
private readonly destroy$ = new Subject<void>();
|
||||
private authUser = getCurrentAuthUser(this.store);
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private mobileAppService: MobileApplicationService,
|
||||
@ -52,8 +50,23 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
this.buildMobileAppSettingsForm();
|
||||
this.mobileAppService.getMobileAppSettings()
|
||||
.subscribe(settings => this.processMobileAppSettings(settings));
|
||||
if (this.isTenantAdmin()) {
|
||||
// this.mobileAppSettingsForm.get('useSystemSettings').valueChanges.pipe(
|
||||
// takeUntilDestroyed()
|
||||
// ).subscribe(value => {
|
||||
// if (value) {
|
||||
// this.mobileAppSettingsForm.get('androidConfig.enabled').disable();
|
||||
// this.mobileAppSettingsForm.get('iosConfig.enabled').disable();
|
||||
// this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').disable();
|
||||
// } else {
|
||||
// this.mobileAppSettingsForm.get('androidConfig.enabled').enable();
|
||||
// this.mobileAppSettingsForm.get('iosConfig.enabled').enable();
|
||||
// this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').enable();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
this.mobileAppSettingsForm.get('useDefaultApp').valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
takeUntilDestroyed()
|
||||
).subscribe(value => {
|
||||
if (value) {
|
||||
this.mobileAppSettingsForm.get('mobileAppBundleId').disable({emitEvent: false});
|
||||
@ -77,17 +90,17 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
}
|
||||
});
|
||||
// this.mobileAppSettingsForm.get('androidConfig.enabled').valueChanges.pipe(
|
||||
// takeUntil(this.destroy$)
|
||||
// takeUntilDestroyed()
|
||||
// ).subscribe(value => {
|
||||
// this.androidEnableChanged(value);
|
||||
// });
|
||||
// this.mobileAppSettingsForm.get('iosConfig.enabled').valueChanges.pipe(
|
||||
// takeUntil(this.destroy$)
|
||||
// takeUntilDestroyed()
|
||||
// ).subscribe(value => {
|
||||
// this.iosEnableChanged(value);
|
||||
// });
|
||||
this.mobileAppSettingsForm.get('qrCodeConfig.showOnHomePage').valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
takeUntilDestroyed()
|
||||
).subscribe(value => {
|
||||
if (value) {
|
||||
this.mobileAppSettingsForm.get('qrCodeConfig').enable({emitEvent: false});
|
||||
@ -99,7 +112,7 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').updateValueAndValidity({onlySelf: true});
|
||||
});
|
||||
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
takeUntilDestroyed()
|
||||
).subscribe(value => {
|
||||
if (value) {
|
||||
// if (this.mobileAppSettingsForm.get('androidConfig.enabled').value || this.mobileAppSettingsForm.get('iosConfig.enabled').value) {
|
||||
@ -114,7 +127,7 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
}
|
||||
});
|
||||
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
takeUntilDestroyed()
|
||||
).subscribe(value => {
|
||||
if (value && this.mobileAppSettingsForm.get('qrCodeConfig.showOnHomePage').value) {
|
||||
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').enable({emitEvent: false});
|
||||
@ -124,14 +137,13 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
public isTenantAdmin(): boolean {
|
||||
return this.authUser.authority === Authority.TENANT_ADMIN;
|
||||
}
|
||||
|
||||
private buildMobileAppSettingsForm() {
|
||||
this.mobileAppSettingsForm = this.fb.group({
|
||||
useSystemSettings: [false],
|
||||
useDefaultApp: [true],
|
||||
mobileAppBundleId: [{value: null, disabled: true}, Validators.required],
|
||||
// androidConfig: this.fb.group({
|
||||
@ -157,6 +169,9 @@ export class MobileQrCodeWidgetSettingsComponent extends PageComponent implement
|
||||
|
||||
private processMobileAppSettings(mobileAppSettings: QrCodeSettings): void {
|
||||
this.mobileAppSettings = {...mobileAppSettings};
|
||||
if (!this.isTenantAdmin()) {
|
||||
this.mobileAppSettings.useSystemSettings = false;
|
||||
}
|
||||
this.mobileAppSettingsForm.reset(this.mobileAppSettings);
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import { MobileAppBundleId } from '@shared/models/id/mobile-app-bundle-id';
|
||||
import { deepClone, isNotEmptyStr } from '@core/utils';
|
||||
|
||||
export interface QrCodeSettings extends HasTenantId {
|
||||
useSystemSettings: boolean;
|
||||
useDefaultApp: boolean;
|
||||
mobileAppBundleId: MobileAppBundleId
|
||||
androidConfig: any; //TODO: need remove
|
||||
|
||||
@ -18,6 +18,7 @@ export interface UserSettings {
|
||||
openedMenuSections?: string[];
|
||||
notDisplayConnectivityAfterAddDevice?: boolean;
|
||||
notDisplayInstructionsAfterAddEdge?: boolean;
|
||||
notDisplayConfigurationAfterAddMobileApp?: boolean;
|
||||
includeBundleWidgetsInExport?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@ -3489,7 +3489,20 @@
|
||||
"qr-code-widget": "QR code widget",
|
||||
"type-here": "Type hero",
|
||||
"configuration-dialog": "Configuration dialog",
|
||||
"configuration-app": "Configuration app"
|
||||
"configuration-app": "Configuration app",
|
||||
"configuration-step": {
|
||||
"prepare-environment-title": "Prepare development environment",
|
||||
"prepare-environment-text": "Flutter ThingsBoard Mobile Application requires Flutter SDK. Follow instructions to set up Flutter SDK.",
|
||||
"get-source-code-title": "Get app source code",
|
||||
"get-source-code-text": "You can get Flutter ThingsBoard Mobile Application source code by cloning it from the GitHub repository:",
|
||||
"configure-api-title": "Configure ThingsBoard API endpoint",
|
||||
"configure-api-text": "Open the flutter_thingsboard_app project in your editor/IDE. Edit:",
|
||||
"configure-api-hint": "Set the value of the thingsBoardApiEndpoint constant to match the API endpoint of your ThingsBoard server instance. Do not use “localhost” or “127.0.0.1” hostnames.",
|
||||
"run-app-title": "Run the app",
|
||||
"run-app-text": "Run the app as described in your IDE.\nIf using the terminal, run the app with the following command:",
|
||||
"more-information": "Detailed information may be found in our Getting Started documentation.",
|
||||
"getting-started": "Getting Started"
|
||||
}
|
||||
},
|
||||
"notification": {
|
||||
"action-button": "Action button",
|
||||
|
||||
@ -907,6 +907,9 @@ pre.tb-highlight {
|
||||
&.tb-mat-20 {
|
||||
@include tb-mat-icon-size(20);
|
||||
}
|
||||
&.tb-mat-24 {
|
||||
@include tb-mat-icon-size(24);
|
||||
}
|
||||
&.tb-mat-28 {
|
||||
@include tb-mat-icon-size(28);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user