Styling for widgets (mostly)

This commit is contained in:
Travis Ralston 2017-12-15 21:22:34 -07:00
parent ebe0048710
commit a09a4d6b51
12 changed files with 131 additions and 88 deletions

View file

@ -13,7 +13,6 @@ import { UiSwitchModule } from "angular2-ui-switch";
import { ScalarService } from "./shared/services/scalar.service"; import { ScalarService } from "./shared/services/scalar.service";
import { ToasterModule } from "angular2-toaster"; import { ToasterModule } from "angular2-toaster";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { IntegrationComponent } from "./integration/integration.component";
import { ScalarCloseComponent } from "./riot/scalar-close/scalar-close.component"; import { ScalarCloseComponent } from "./riot/scalar-close/scalar-close.component";
import { IntegrationService } from "./shared/services/integration.service"; import { IntegrationService } from "./shared/services/integration.service";
import { BootstrapModalModule } from "ngx-modialog/plugins/bootstrap"; import { BootstrapModalModule } from "ngx-modialog/plugins/bootstrap";
@ -29,6 +28,8 @@ import { PageHeaderComponent } from "./page-header/page-header.component";
import { SpinnerComponent } from "./spinner/spinner.component"; import { SpinnerComponent } from "./spinner/spinner.component";
import { BreadcrumbsModule } from "ng2-breadcrumbs"; import { BreadcrumbsModule } from "ng2-breadcrumbs";
import { RiotHomeComponent } from "./riot/riot-home/home.component"; import { RiotHomeComponent } from "./riot/riot-home/home.component";
import { IntegrationBagComponent } from "./integration-bag/integration-bag.component";
import { IntegrationComponent } from "./integration/integration.component";
const WIDGET_CONFIGURATION_COMPONENTS: any[] = IntegrationService.getAllConfigComponents(); const WIDGET_CONFIGURATION_COMPONENTS: any[] = IntegrationService.getAllConfigComponents();
@ -52,6 +53,7 @@ const WIDGET_CONFIGURATION_COMPONENTS: any[] = IntegrationService.getAllConfigCo
HomeComponent, HomeComponent,
RiotComponent, RiotComponent,
IntegrationComponent, IntegrationComponent,
IntegrationBagComponent,
PageHeaderComponent, PageHeaderComponent,
SpinnerComponent, SpinnerComponent,
ScalarCloseComponent, ScalarCloseComponent,

View file

@ -0,0 +1,9 @@
<div class="integration-bag">
<!--<my-integration *ngFor="let integration of integrations"-->
<!--[integration]="integration" (selected)="onClick(integration)"></my-integration>-->
<div class="integration" *ngFor="let integration of integrations">
<img class="integration-avatar" [src]="getSafeUrl(integration.avatar)"/>
<div class="integration-name">{{ integration.name }}</div>
<div class="integration-description">{{ integration.about }}</div>
</div>
</div>

View file

@ -0,0 +1,37 @@
// component styles are encapsulated and only applied to their components
.integration-bag {
display: flex;
flex-direction: row;
align-items: stretch;
flex-wrap: wrap;
justify-content: flex-start;
}
.integration {
border: 1px solid #eee;
border-radius: 5px;
margin: 7px;
padding: 5px;
width: calc(325px - 14px);
position: relative;
}
.integration .integration-avatar {
width: 50px;
height: 50px;
float: left;
}
.integration .integration-name {
display: inline-block;
font-size: 1.1em;
font-weight: 100;
padding-left: 5px;
}
.integration .integration-description {
display: block;
font-size: 0.8em;
margin-left: 55px;
color: #999;
}

View file

@ -0,0 +1,25 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Integration } from "../shared/models/integration";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
@Component({
selector: "my-integration-bag",
templateUrl: "./integration-bag.component.html",
styleUrls: ["./integration-bag.component.scss"],
})
export class IntegrationBagComponent {
@Input() integrations: Integration[];
@Output() integrationClicked: EventEmitter<Integration> = new EventEmitter<Integration>();
constructor(private sanitizer: DomSanitizer) {
}
public getSafeUrl(url: string): SafeResourceUrl {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
public onClick(integration: Integration) {
this.integrationClicked.emit(integration);
}
}

View file

@ -1,22 +1,5 @@
<div class="integration"> <div class="integration">
<img [src]="integration.avatar" class="avatar"> <img class="integration-avatar" [src]="getSafeUrl(integration.avatar)"/>
<div class="title"> <div class="integration-name">{{ integration.name }}</div>
<b>{{ integration.name }}</b> <div class="integration-description">{{ integration.about }}</div>
<div style="display: flex;">
<div class="switch" *ngIf="integration.type !== 'bridge' && integration.type !== 'widget'">
<ui-switch [checked]="integration.isEnabled" size="small" [disabled]="integration.isBroken"
(change)="update()"></ui-switch>
</div>
<div class="switch" *ngIf="canHaveErrors(integration) && !integration.isEnabled">
<i class="fa fa-warning text-warning" ngbTooltip="{{ integration.bridgeError }}"></i>
</div>
<div class="toolbar">
<i class="fa fa-question-circle text-info" ngbTooltip="{{integration.about}}"
*ngIf="integration.about"></i>
<i class="fa fa-cog text-info config-icon" (click)="configureIntegration()"
*ngIf="integration.isEnabled && integration.hasConfig"></i>
</div>
</div>
</div>
<div style="clear: both;"></div>
</div> </div>

View file

@ -1,38 +1,31 @@
// component styles are encapsulated and only applied to their components // component styles are encapsulated and only applied to their components
.integration { .integration {
flex: auto; border: 1px solid #eee;
margin: 5px; border-radius: 3px;
padding: 10px; margin-left: 7px;
border: 1px solid #ccc; margin-right: 7px;
border-radius: 4px; padding: 5px;
min-width: 150px; width: calc(325px - 14px);
background-color: white; position: relative;
flex: 1;
} }
.avatar { .integration .integration-avatar {
float: left;
margin-right: 7px;
width: 50px; width: 50px;
height: 50px; height: 50px;
border-radius: 50px; float: left;
} }
.title { .integration .integration-name {
float: right;
width: calc(100% - 57px);
}
.switch {
display: inline-block; display: inline-block;
text-align: left; font-size: 1.1em;
font-weight: 100;
padding-left: 5px;
} }
.toolbar { .integration .integration-description {
display: inline-block; display: block;
vertical-align: top; font-size: 0.8em;
margin-left: 5px; margin-left: 55px;
color: #999;
} }
.config-icon {
cursor: pointer;
}

View file

@ -1,8 +1,7 @@
import { Component, EventEmitter, Input, Output } from "@angular/core"; import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Integration } from "../shared/models/integration"; import { Integration } from "../shared/models/integration";
import { overlayConfigFactory } from "ngx-modialog"; import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { BSModalContext, Modal } from "ngx-modialog/plugins/bootstrap"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
import { IntegrationService } from "../shared/services/integration.service";
export class ConfigModalContext extends BSModalContext { export class ConfigModalContext extends BSModalContext {
public integration: Integration; public integration: Integration;
@ -20,30 +19,12 @@ export class ConfigModalContext extends BSModalContext {
export class IntegrationComponent { export class IntegrationComponent {
@Input() integration: Integration; @Input() integration: Integration;
@Input() roomId: string; @Output() selected: EventEmitter<any> = new EventEmitter<any>();
@Input() scalarToken: string;
@Output() updated: EventEmitter<any> = new EventEmitter();
constructor(public modal: Modal) { constructor(private sanitizer: DomSanitizer) {
} }
public update(): void { public getSafeUrl(url: string): SafeResourceUrl {
this.integration.isEnabled = !this.integration.isEnabled; return this.sanitizer.bypassSecurityTrustResourceUrl(url);
this.updated.emit();
}
public configureIntegration(integrationId: string = null): void {
this.modal.open(IntegrationService.getConfigComponent(this.integration), overlayConfigFactory({
integration: this.integration,
roomId: this.roomId,
scalarToken: this.scalarToken,
isBlocking: false,
integrationId: integrationId,
size: "lg"
}, BSModalContext));
}
public canHaveErrors(integration: Integration): boolean {
return integration.type === "bridge" || integration.type === "widget";
} }
} }

View file

@ -35,14 +35,10 @@
<div *ngFor="let category of getCategories()"> <div *ngFor="let category of getCategories()">
<div class="ibox" *ngIf="getIntegrationsIn(category).length > 0"> <div class="ibox" *ngIf="getIntegrationsIn(category).length > 0">
<div class="ibox-title"> <div class="ibox-title">
<h4>{{ category }}</h4> <h5>{{ category }}</h5>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<div class="integration" *ngFor="let integration of getIntegrationsIn(category)"> <my-integration-bag [integrations]="getIntegrationsIn(category)"></my-integration-bag>
<img class="integration-avatar" [src]="getSafeUrl(integration.avatar)"/>
<div class="integration-name">{{ integration.name }}</div>
<div class="integration-description">{{ integration.about }}</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -7,7 +7,6 @@ import { ActivatedRoute } from "@angular/router";
import { ApiService } from "../../shared/services/api.service"; import { ApiService } from "../../shared/services/api.service";
import { ScalarService } from "../../shared/services/scalar.service"; import { ScalarService } from "../../shared/services/scalar.service";
import * as _ from "lodash"; import * as _ from "lodash";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
const CATEGORY_MAP = { const CATEGORY_MAP = {
"Widgets": ["widget"], "Widgets": ["widget"],
@ -33,14 +32,13 @@ export class RiotHomeComponent {
private userId: string; private userId: string;
private requestedScreen: string = null; private requestedScreen: string = null;
private requestedIntegration: string = null; private requestedIntegration: string = null;
private integrationsForCategory: { [category: string]: Integration[] } = {}; public integrationsForCategory: { [category: string]: Integration[] } = {};
private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP; private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP;
constructor(private activatedRoute: ActivatedRoute, constructor(private activatedRoute: ActivatedRoute,
private api: ApiService, private api: ApiService,
private scalar: ScalarService, private scalar: ScalarService,
private toaster: ToasterService, private toaster: ToasterService) {
private sanitizer: DomSanitizer) {
let params: any = this.activatedRoute.snapshot.queryParams; let params: any = this.activatedRoute.snapshot.queryParams;
this.requestedScreen = params.screen; this.requestedScreen = params.screen;
@ -75,10 +73,6 @@ export class RiotHomeComponent {
} }
} }
public getSafeUrl(url: string): SafeResourceUrl {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
public hasIntegrations(): boolean { public hasIntegrations(): boolean {
for (const category of this.getCategories()) { for (const category of this.getCategories()) {
if (this.getIntegrationsIn(category).length > 0) return true; if (this.getIntegrationsIn(category).length > 0) return true;
@ -196,7 +190,8 @@ export class RiotHomeComponent {
if (opened) return; if (opened) return;
if (component.integration.type !== type || component.integration.integrationType !== integrationType) return; if (component.integration.type !== type || component.integration.integrationType !== integrationType) return;
console.log("Configuring integration " + this.requestedIntegration + " type=" + type + " integrationType=" + integrationType); console.log("Configuring integration " + this.requestedIntegration + " type=" + type + " integrationType=" + integrationType);
component.configureIntegration(this.requestedIntegration); //component.configureIntegration(this.requestedIntegration);
// TODO: Support editing integrations
opened = true; opened = true;
}); });
if (!opened) { if (!opened) {

View file

@ -6,5 +6,4 @@ import { Component } from "@angular/core";
styleUrls: ["./riot.component.scss"], styleUrls: ["./riot.component.scss"],
}) })
export class RiotComponent { export class RiotComponent {
} }

View file

@ -1,6 +1,7 @@
// styles in src/style directory are applied to the whole page // styles in src/style directory are applied to the whole page
@import url('https://fonts.googleapis.com/css?family=Open+Sans:100|Roboto:300'); @import url('https://fonts.googleapis.com/css?family=Open+Sans:100|Roboto:300');
@import '../../node_modules/angular2-toaster/toaster'; @import '../../node_modules/angular2-toaster/toaster';
@import "components/ibox.scss";
@import "riot"; @import "riot";
body { body {

View file

@ -0,0 +1,22 @@
.ibox {
background-color: #fff;
padding: 0;
margin-bottom: 30px;
}
.ibox .ibox-title {
padding: 5px;
border-bottom: 1px solid #e7eaec;
border-top: 2px solid #e7eaec;
}
.ibox .ibox-title h5 {
display: inline-block;
font-size: 1.1em;
padding: 5px;
margin: 0;
}
.ibox .ibox-content {
padding: 15px;
}