Browse Source

Feature/grid v2 (#540)

* Expert mode.

* More improvements to the grid.

* Allow wider cards.

* Build fixes.
pull/542/head
Sebastian Stehle 6 years ago
committed by GitHub
parent
commit
0398483073
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      frontend/app/features/content/shared/due-time-selector.component.html
  2. 4
      frontend/app/features/content/shared/references/content-selector.component.html
  3. 2
      frontend/app/features/dashboard/declarations.ts
  4. 4
      frontend/app/features/dashboard/module.ts
  5. 9
      frontend/app/features/dashboard/pages/cards/content-summary-card.component.html
  6. 0
      frontend/app/features/dashboard/pages/cards/content-summary-card.component.scss
  7. 58
      frontend/app/features/dashboard/pages/cards/content-summary-card.component.ts
  8. 6
      frontend/app/features/dashboard/pages/cards/iframe-card.component.html
  9. 8
      frontend/app/features/dashboard/pages/cards/iframe-card.component.scss
  10. 33
      frontend/app/features/dashboard/pages/cards/iframe-card.component.ts
  11. 8
      frontend/app/features/dashboard/pages/dashboard-config.component.html
  12. 6
      frontend/app/features/dashboard/pages/dashboard-config.component.scss
  13. 5
      frontend/app/features/dashboard/pages/dashboard-config.component.ts
  14. 16
      frontend/app/features/dashboard/pages/dashboard-page.component.html
  15. 23
      frontend/app/features/dashboard/pages/dashboard-page.component.scss
  16. 33
      frontend/app/features/dashboard/pages/dashboard-page.component.ts
  17. 8
      frontend/app/features/rules/pages/rules/rule-wizard.component.html
  18. 4
      frontend/app/features/schemas/pages/schema/fields/field-wizard.component.html
  19. 5
      frontend/app/features/schemas/pages/schemas/schema-form.component.html
  20. 4
      frontend/app/features/settings/pages/clients/client-connect-form.component.html
  21. 6
      frontend/app/features/settings/pages/contributors/import-contributors-dialog.component.html
  22. 5
      frontend/app/framework/angular/modals/dialog-renderer.component.html
  23. 5
      frontend/app/shared/components/app-form.component.html
  24. 4
      frontend/app/shared/components/assets/asset-folder-dialog.component.html
  25. 5
      frontend/app/shared/components/assets/assets-selector.component.html
  26. 9
      frontend/app/shared/components/search/search-form.component.html

4
frontend/app/features/content/shared/due-time-selector.component.html

@ -23,8 +23,8 @@
</ng-container>
<ng-container footer>
<button type="button" class="float-left btn btn-secondary" (click)="cancelStatusChange()">Cancel</button>
<button type="button" class="float-right btn btn-primary" [disabled]="dueTimeMode === 'Scheduled' && !dueTime" (click)="confirmStatusChange()" sqxFocusOnInit>Confirm</button>
<button type="button" class="btn btn-secondary" (click)="cancelStatusChange()">Cancel</button>
<button type="button" class="btn btn-primary" [disabled]="dueTimeMode === 'Scheduled' && !dueTime" (click)="confirmStatusChange()" sqxFocusOnInit>Confirm</button>
</ng-container>
</sqx-modal-dialog>
</ng-container>

4
frontend/app/features/content/shared/references/content-selector.component.html

@ -87,7 +87,7 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="float-right btn btn-success" (click)="emitSelect()" [disabled]="selectionCount === 0">Link selected contents ({{selectionCount}})</button>
<button type="button" class="btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="btn btn-success" (click)="emitSelect()" [disabled]="selectionCount === 0">Link selected contents ({{selectionCount}})</button>
</ng-container>
</sqx-modal-dialog>

2
frontend/app/features/dashboard/declarations.ts

@ -13,8 +13,10 @@ export * from './pages/cards/api-traffic-card.component';
export * from './pages/cards/asset-uploads-count-card.component';
export * from './pages/cards/asset-uploads-size-card.component';
export * from './pages/cards/asset-uploads-size-summary-card.component';
export * from './pages/cards/content-summary-card.component';
export * from './pages/cards/github-card.component';
export * from './pages/cards/history-card.component';
export * from './pages/cards/iframe-card.component';
export * from './pages/cards/schema-card.component';
export * from './pages/cards/support-card.component';
export * from './pages/dashboard-config.component';

4
frontend/app/features/dashboard/module.ts

@ -12,7 +12,7 @@ import { RouterModule, Routes } from '@angular/router';
import { SqxFrameworkModule, SqxSharedModule } from '@app/shared';
import { GridsterModule } from 'angular-gridster2';
import { ChartModule } from 'angular2-chartjs';
import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, DashboardConfigComponent, DashboardPageComponent, GithubCardComponent, HistoryCardComponent, SchemaCardComponent, SupportCardComponent } from './declarations';
import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, ContentSummaryCardComponent, DashboardConfigComponent, DashboardPageComponent, GithubCardComponent, HistoryCardComponent, IFrameCardComponent, SchemaCardComponent, SupportCardComponent } from './declarations';
const routes: Routes = [
{
@ -38,10 +38,12 @@ const routes: Routes = [
AssetUploadsCountCardComponent,
AssetUploadsSizeCardComponent,
AssetUploadsSizeSummaryCardComponent,
ContentSummaryCardComponent,
DashboardConfigComponent,
DashboardPageComponent,
GithubCardComponent,
HistoryCardComponent,
IFrameCardComponent,
SchemaCardComponent,
SupportCardComponent
]

9
frontend/app/features/dashboard/pages/cards/content-summary-card.component.html

@ -0,0 +1,9 @@
<div class="card card">
<div class="card-header">{{options?.name}}</div>
<div class="card-body">
<div class="aggregation">
<div class="aggregation-label">Number of items</div>
<div class="aggregation-value">{{itemCount}}</div>
</div>
</div>
</div>

0
frontend/app/features/dashboard/pages/cards/content-summary-card.component.scss

58
frontend/app/features/dashboard/pages/cards/content-summary-card.component.ts

@ -0,0 +1,58 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { AppDto, ContentsService, fadeAnimation, Types } from '@app/shared';
@Component({
selector: 'sqx-content-summary-card',
styleUrls: ['./content-summary-card.component.scss'],
templateUrl: './content-summary-card.component.html',
animations: [
fadeAnimation
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ContentSummaryCardComponent implements OnInit {
@Input()
public app: AppDto;
@Input()
public options: any;
public itemCount = 0;
constructor(
private readonly changeDetector: ChangeDetectorRef,
private readonly contentsService: ContentsService
) {
}
public ngOnInit() {
if (!Types.isString(this.options?.schema)) {
return;
}
let query = this.options?.query;
if (!Types.isObject(query)) {
query = {};
}
query.take = 0;
this.contentsService.getContents(this.app.name, this.options.schema, { query })
.subscribe(dto => {
this.itemCount = dto.total;
this.changeDetector.detectChanges();
},
() => {
this.itemCount = 0;
});
}
}

6
frontend/app/features/dashboard/pages/cards/iframe-card.component.html

@ -0,0 +1,6 @@
<div class="card card-lg">
<div class="card-header">{{options?.name}}</div>
<div class="card-body">
<iframe #iframe scrolling="no" width="100%" height="100%"></iframe>
</div>
</div>

8
frontend/app/features/dashboard/pages/cards/iframe-card.component.scss

@ -0,0 +1,8 @@
iframe {
@include absolute(0, 0, 0, 0);
border: 0;
}
.card-body {
position: relative;
}

33
frontend/app/features/dashboard/pages/cards/iframe-card.component.ts

@ -0,0 +1,33 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild } from '@angular/core';
import { AppDto, fadeAnimation } from '@app/shared';
@Component({
selector: 'sqx-iframe-card',
styleUrls: ['./iframe-card.component.scss'],
templateUrl: './iframe-card.component.html',
animations: [
fadeAnimation
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IFrameCardComponent implements AfterViewInit {
@ViewChild('iframe', { static: false })
public iframe: ElementRef<HTMLIFrameElement>;
@Input()
public app: AppDto;
@Input()
public options: any;
public ngAfterViewInit() {
this.iframe.nativeElement.src = this.options?.src;
}
}

8
frontend/app/features/dashboard/pages/dashboard-config.component.html

@ -1,5 +1,5 @@
<ng-container *ngIf="config">
<button type="button" class="btn settings-button" (click)="dropdownModal.toggle()" #buttonSettings>
<button type="button" class="btn settings-button" [class.focused]="needsAttention" [class.btn-primary]="needsAttention" (click)="dropdownModal.toggle()" #buttonSettings>
<i class="icon-settings"></i>
</button>
@ -9,7 +9,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="field_{{item.type}}"
[ngModel]="isSelected(item)"
(ngModelChange)="addOrRemove(item, $event)" />
(ngModelChange)="addOrRemove(item)" />
<label class="form-check-label" for="field_{{item.type}}">
{{item.name}}
@ -20,6 +20,7 @@
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="startExpertMode()">Expert Mode</a>
<a class="dropdown-item" (click)="saveConfig()">Save</a>
<div class="dropdown-divider"></div>
@ -48,7 +49,8 @@
<ng-container footer>
<button type="button" class="btn btn-secondary" (click)="expertDialog.hide()">Cancel</button>
<button type="button" class="btn btn-primary" (click)="completeExpertMode()">Save</button>
<button type="button" class="btn btn-primary" (click)="completeExpertMode()">Update</button>
</ng-container>
</sqx-modal-dialog>
</ng-container>

6
frontend/app/features/dashboard/pages/dashboard-config.component.scss

@ -1,3 +1,9 @@
.settings-button {
border: 0;
border-radius: 30px;
transition: background .4s ease, color .4s ease, border .4s ease;
}
.json-editor ::ng-deep {
.editor {
@include absolute(0, 0, 0, 0);

5
frontend/app/features/dashboard/pages/dashboard-config.component.ts

@ -27,6 +27,9 @@ export class DashboardConfigComponent implements OnChanges {
@Input()
public config: GridsterItem[];
@Input()
public needsAttention = false;
@Output()
public configChange = new EventEmitter<GridsterItem[]>();
@ -74,8 +77,6 @@ export class DashboardConfigComponent implements OnChanges {
this.expertConfig = null!;
this.expertDialog.hide();
this.saveConfig();
}
public resetConfig() {

16
frontend/app/features/dashboard/pages/dashboard-page.component.html

@ -77,12 +77,26 @@
[app]="app">
</sqx-history-card>
</ng-container>
<ng-container *ngSwitchCase="'content-summary'">
<sqx-content-summary-card
[app]="app" [options]="item">
</sqx-content-summary-card>
</ng-container>
<ng-container *ngSwitchCase="'iframe'">
<sqx-iframe-card
[app]="app" [options]="item">
</sqx-iframe-card>
</ng-container>
</ng-container>
</gridster-item>
</gridster>
<div class="dashboard-settings">
<sqx-dashboard-config [app]="app" [config]="gridConfig" (configChange)="changeConfig($event)"></sqx-dashboard-config>
<sqx-dashboard-config [app]="app"
[needsAttention]="isScrolled"
[config]="gridConfig"
(configChange)="changeConfig($event)">
</sqx-dashboard-config>
</div>
</div>
</ng-container>

23
frontend/app/features/dashboard/pages/dashboard-page.component.scss

@ -5,10 +5,12 @@
&-summary {
@include absolute(2rem, null, null, 2rem);
z-index: 0;
}
&-settings {
@include absolute(1rem, 1rem, null, null);
z-index: 1000;
}
&-title {
@ -24,15 +26,17 @@
}
}
gridster-item {
position: absolute;
}
.btn {
z-index: 1000;
}
:host ::ng-deep {
canvas {
height: 12rem !important;
margin-bottom: 0;
margin-top: -1rem;
chart {
@include absolute(0, 1rem, 1rem, 1rem);
}
.subtext {
@ -41,8 +45,7 @@
.card {
& {
@include force-height(16rem);
height: 100%;
@include absolute(0, 0, 0, 0);
}
&-image {
@ -59,6 +62,14 @@
}
}
&-header {
@include truncate;
}
&-body {
position: relative;
}
&-history {
overflow-y: auto;
}

33
frontend/app/features/dashboard/pages/dashboard-page.component.ts

@ -7,9 +7,9 @@
// tslint:disable: readonly-array
import { AfterViewInit, Component, OnInit, Renderer2, ViewChild } from '@angular/core';
import { AfterViewInit, Component, NgZone, OnInit, Renderer2, ViewChild } from '@angular/core';
import { AppsState, AuthService, CallsUsageDto, CurrentStorageDto, DateTime, fadeAnimation, LocalStoreService, ResourceOwner, StorageUsagePerDateDto, UsagesService } from '@app/shared';
import { GridsterComponent, GridsterItem, GridType } from 'angular-gridster2';
import { GridsterComponent, GridsterConfig, GridsterItem, GridType } from 'angular-gridster2';
import { switchMap } from 'rxjs/operators';
@Component({
@ -39,9 +39,10 @@ export class DashboardPageComponent extends ResourceOwner implements AfterViewIn
constructor(
public readonly appsState: AppsState,
public readonly authState: AuthService,
private readonly localStore: LocalStoreService,
private readonly renderer: Renderer2,
private readonly usagesService: UsagesService,
private readonly localStore: LocalStoreService
private readonly zone: NgZone
) {
super();
@ -75,8 +76,18 @@ export class DashboardPageComponent extends ResourceOwner implements AfterViewIn
}
public ngAfterViewInit() {
this.renderer.listen(this.grid.el, 'scroll', () => {
this.isScrolled = this.grid.el.scrollTop > 0;
this.zone.runOutsideAngular(() => {
const gridElement = this.grid.el;
this.renderer.listen(gridElement, 'scroll', () => {
const isScrolled = gridElement.scrollTop > 0;
if (isScrolled !== this.isScrolled) {
this.zone.run(() => {
this.isScrolled = isScrolled;
});
}
});
});
}
@ -93,20 +104,22 @@ export class DashboardPageComponent extends ResourceOwner implements AfterViewIn
}
}
const DEFAULT_OPTIONS = {
const DEFAULT_OPTIONS: GridsterConfig = {
displayGrid: 'onDrag&Resize',
draggable: {
enabled: true
},
fixedColWidth: 254,
fixedRowHeight: 254,
gridType: GridType.Fixed,
maxItemCols: 3,
maxItemRows: 2,
outerMargin: true,
outerMarginBottom: 16,
outerMarginLeft: 16,
outerMarginRight: 16,
outerMarginTop: 120,
draggable: {
enabled: true
},
resizable: {
enabled: false
enabled: true
}
};

8
frontend/app/features/rules/pages/rules/rule-wizard.component.html

@ -127,14 +127,14 @@
<ng-container footer>
<ng-container *ngIf="step === 2 || step === 4">
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="button" class="btn btn-secondary" (click)="emitComplete()">Cancel</button>
</ng-container>
<ng-container *ngIf="isEditable">
<button *ngIf="step === 2 && isWizard" type="submit" class="float-right btn btn-primary" (click)="saveTrigger()">Next</button>
<button *ngIf="step === 2 && !isWizard" type="submit" class="float-right btn btn-primary" (click)="saveTrigger()">Save</button>
<button *ngIf="step === 2 && isWizard" type="submit" class="btn btn-primary" (click)="saveTrigger()">Next</button>
<button *ngIf="step === 2 && !isWizard" type="submit" class="btn btn-primary" (click)="saveTrigger()">Save</button>
<button *ngIf="step === 4" type="submit" class="float-right btn btn-primary" (click)="saveAction()">Save</button>
<button *ngIf="step === 4" type="submit" class="btn btn-primary" (click)="saveAction()">Save</button>
</ng-container>
</ng-container>
</sqx-modal-dialog>

4
frontend/app/features/schemas/pages/schema/fields/field-wizard.component.html

@ -78,13 +78,13 @@
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<div class="float-right" *ngIf="!editing">
<div *ngIf="!editing">
<button type="button" class="btn btn-outline-success" (click)="addField(false)">Create and close</button>
<button type="button" class="btn btn-success ml-1" (click)="addField(true)">Create and add field</button>
<button type="button" class="btn btn-success ml-1" (click)="addField(false, true)">Create and edit field</button>
</div>
<div class="float-right" *ngIf="editing">
<div *ngIf="editing">
<button type="button" class="btn btn-success" (click)="save(true)">Save and add field</button>
<button type="button" class="btn btn-primary ml-1" (click)="save()">Save and close</button>
</div>

5
frontend/app/features/schemas/pages/schemas/schema-form.component.html

@ -82,8 +82,9 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="cancel.emit()">Cancel</button>
<button type="submit" class="float-right btn btn-success">Create</button>
<button type="button" class="btn btn-secondary" (click)="cancel.emit()">Cancel</button>
<button type="submit" class="btn btn-success">Create</button>
</ng-container>
</sqx-modal-dialog>
</form>

4
frontend/app/features/settings/pages/clients/client-connect-form.component.html

@ -163,8 +163,6 @@
</ng-container>
<ng-container footer>
<button class="btn btn-secondary" [disabled]="isStart" (click)="go('Start')">
Back
</button>
<button class="btn btn-secondary" [disabled]="isStart" (click)="go('Start')">Back</button>
</ng-container>
</sqx-modal-dialog>

6
frontend/app/features/settings/pages/contributors/import-contributors-dialog.component.html

@ -50,15 +50,15 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="close.emit()">Cancel</button>
<button type="button" class="btn btn-secondary" (click)="close.emit()">Cancel</button>
<ng-container [ngSwitch]="importStage">
<ng-container *ngSwitchCase="'Start'">
<button type="submit" class="float-right btn btn-success" [disabled]="importForm.hasNoUser | async">Add Contributors</button>
<button type="submit" class="btn btn-success" [disabled]="importForm.hasNoUser | async">Add Contributors</button>
</ng-container>
<ng-container *ngSwitchCase="'Change'">
<button type="button" class="float-right btn btn-success" [disabled]="importStatus.length === 0" (click)="import()">Import</button>
<button type="button" class="btn btn-success" [disabled]="importStatus.length === 0" (click)="import()">Import</button>
</ng-container>
</ng-container>
</ng-container>

5
frontend/app/framework/angular/modals/dialog-renderer.component.html

@ -11,8 +11,9 @@
</ng-container>
<ng-container footer>
<button type="button" class="float-left btn btn-secondary" (click)="cancel()">No</button>
<button type="button" class="float-right btn btn-danger" (click)="confirm()" sqxFocusOnInit>Yes</button>
<button type="button" class="btn btn-secondary" (click)="cancel()">No</button>
<button type="button" class="btn btn-danger" (click)="confirm()" sqxFocusOnInit>Yes</button>
</ng-container>
</sqx-modal-dialog>
</ng-container>

5
frontend/app/shared/components/app-form.component.html

@ -33,8 +33,9 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="float-right btn btn-success">Create</button>
<button type="button" class="btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="btn btn-success">Create</button>
</ng-container>
</sqx-modal-dialog>
</form>

4
frontend/app/shared/components/assets/asset-folder-dialog.component.html

@ -28,9 +28,9 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="button" class="btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="float-right btn btn-success">
<button type="submit" class="btn btn-success">
<ng-container *ngIf="assetFolder; else noAssetFolderButton">
Rename
</ng-container>

5
frontend/app/shared/components/assets/assets-selector.component.html

@ -53,7 +53,8 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="float-right btn btn-success" (click)="emitSelect()" [disabled]="snapshot.selectionCount === 0">Link selected assets ({{snapshot.selectionCount}})</button>
<button type="button" class="btn btn-secondary" (click)="emitComplete()">Cancel</button>
<button type="submit" class="btn btn-success" (click)="emitSelect()" [disabled]="snapshot.selectionCount === 0">Link selected assets ({{snapshot.selectionCount}})</button>
</ng-container>
</sqx-modal-dialog>

9
frontend/app/shared/components/search/search-form.component.html

@ -62,7 +62,9 @@
</ng-container>
<ng-container footer>
<button type="button" class="float-right btn btn-primary" (click)="search(true)">
<span></span>
<button type="button" class="btn btn-primary" (click)="search(true)">
Submit
</button>
</ng-container>
@ -92,8 +94,9 @@
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="saveQueryDialog.hide()" [disabled]="saveQueryForm.submitted | async">Cancel</button>
<button type="submit" class="float-right btn btn-success">Create</button>
<button type="button" class="btn btn-secondary" (click)="saveQueryDialog.hide()" [disabled]="saveQueryForm.submitted | async">Cancel</button>
<button type="submit" class="btn-success">Create</button>
</ng-container>
</sqx-modal-dialog>
</form>

Loading…
Cancel
Save