Browse Source

Feature/reactive ui (#485)

* Reactive UI

* Disabled improved.

* Resized service.
pull/486/head
Sebastian Stehle 6 years ago
committed by GitHub
parent
commit
7690205376
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      frontend/app/features/content/shared/forms/assets-editor.component.html
  2. 45
      frontend/app/framework/angular/resized.directive.ts
  3. 36
      frontend/app/framework/angular/sync-width.directive.ts
  4. 1
      frontend/app/framework/internal.ts
  5. 2
      frontend/app/framework/module.ts
  6. 58
      frontend/app/framework/services/resize.service.ts
  7. 28
      frontend/app/shared/components/assets/asset.component.html

17
frontend/app/features/content/shared/forms/assets-editor.component.html

@ -1,8 +1,13 @@
<div class="assets-container" [class.disabled]="snapshot.isDisabled" (sqxDropFile)="addFiles($event)" tabindex="1000">
<div class="assets-container"
(sqxDropFile)="addFiles($event)"
(sqxDropDisabled)="snapshot.isDisabled"
tabindex="1000">
<div class="header list">
<div class="row no-gutters">
<div class="col">
<div class="drop-area align-items-center" (click)="assetsDialog.show()" (sqxDropFile)="addFiles($event)">
<div class="col" [class.disabled]="snapshot.isDisabled">
<div class="drop-area align-items-center" (click)="assetsDialog.show()"
(sqxDropFile)="addFiles($event)"
(sqxDropDisabled)="snapshot.isDisabled">
Drop files or click
</div>
</div>
@ -26,11 +31,15 @@
<ng-container *ngIf="!snapshot.isListView; else listTemplate">
<div class="row no-gutters">
<sqx-asset *ngFor="let file of snapshot.assetFiles" [assetFile]="file"
[isDisabled]="snapshot.isDisabled"
[isCompact]="snapshot.isCompact"
(loadError)="removeLoadingAsset(file)"
(load)="addAsset(file, $event)">
</sqx-asset>
<sqx-asset *ngFor="let asset of snapshot.assets; trackBy: trackByAsset"
[asset]="asset"
[isDisabled]="snapshot.isDisabled"
[isCompact]="snapshot.isCompact"
(update)="notifyOthers(asset)"
[removeMode]="true"
(remove)="removeLoadedAsset(asset)">
@ -42,6 +51,7 @@
<div class="list-view">
<sqx-asset *ngFor="let file of snapshot.assetFiles" [assetFile]="file"
[isListView]="true"
[isDisabled]="snapshot.isDisabled"
[isCompact]="snapshot.isCompact"
(loadError)="removeLoadingAsset(file)"
(load)="addAsset(file, $event)">
@ -59,6 +69,7 @@
<sqx-asset
[asset]="asset"
[isListView]="true"
[isDisabled]="snapshot.isDisabled"
[isCompact]="snapshot.isCompact"
(update)="notifyOthers(asset)"
[removeMode]="true"

45
frontend/app/framework/angular/resized.directive.ts

@ -5,25 +5,18 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Directive, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import ResizeObserver from 'resize-observer-polyfill';
import { Directive, ElementRef, EventEmitter, Input, NgZone, OnChanges, OnDestroy, Output } from '@angular/core';
const entriesMap = new WeakMap();
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
if (entriesMap.has(entry.target)) {
const component = entriesMap.get(entry.target);
component.onResized(entry);
}
}
});
import {
ResizeListener,
ResizeService,
ResourceOwner
} from '@app/framework/internal';
@Directive({
selector: '[sqxResized], [sqxResizeCondition]'
})
export class ResizedDirective implements OnDestroy, OnChanges {
export class ResizedDirective extends ResourceOwner implements OnDestroy, OnChanges, ResizeListener {
private condition: ((rect: ClientRect) => boolean) | undefined;
private conditionValue = false;
@ -39,12 +32,12 @@ export class ResizedDirective implements OnDestroy, OnChanges {
@Output('sqxResized')
public resize = new EventEmitter<ClientRect>();
constructor(
private readonly element: ElementRef
constructor(resizeService: ResizeService, element: ElementRef,
private readonly zone: NgZone
) {
entriesMap.set(element.nativeElement, this);
super();
observer.observe(element.nativeElement);
this.own(resizeService.listen(element.nativeElement, this));
}
public ngOnChanges() {
@ -62,21 +55,21 @@ export class ResizedDirective implements OnDestroy, OnChanges {
}
}
public ngOnDestroy() {
observer.unobserve(this.element.nativeElement);
}
public onResized(entry: ResizeObserverEntry) {
public onResize(rect: ClientRect) {
if (this.condition) {
const value = this.condition(entry.contentRect);
const value = this.condition(rect);
if (this.conditionValue !== value) {
this.resizeCondition.emit(value);
this.zone.run(() => {
this.resizeCondition.emit(value);
});
this.conditionValue = value;
}
} else {
this.resize.emit(entry.contentRect);
this.zone.run(() => {
this.resize.emit(rect);
});
}
}
}

36
frontend/app/framework/angular/sync-width.directive.ts

@ -5,40 +5,46 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core';
import { timer } from 'rxjs';
import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular/core';
import { ResourceOwner } from '@app/framework/internal';
import {
ResizeListener,
ResizeService,
ResourceOwner
} from '@app/framework/internal';
@Directive({
selector: '[sqxSyncWidth]'
})
export class SyncWidthDirective extends ResourceOwner implements OnInit, AfterViewInit {
export class SyncWidthDirective extends ResourceOwner implements AfterViewInit, ResizeListener {
@Input('sqxSyncWidth')
public target: HTMLElement;
constructor(
private readonly element: ElementRef<HTMLElement>,
private readonly renderer: Renderer2
private readonly renderer: Renderer2,
private readonly resizeService: ResizeService
) {
super();
}
public ngOnInit() {
this.own(timer(100, 100).subscribe(() => this.reposition()));
this.own(this.resizeService.listen(this.element.nativeElement, this));
}
public ngAfterViewInit() {
this.reposition();
this.onReposition();
}
private reposition() {
if (!this.target) {
return;
}
public onResize(size: ClientRect) {
this.resize(size.width);
}
const size = this.element.nativeElement.clientWidth;
private onReposition() {
this.resize(this.element.nativeElement.clientWidth);
}
this.renderer.setStyle(this.target, 'width', `${size}px`);
private resize(width: number) {
if (this.target) {
this.renderer.setStyle(this.target, 'width', `${width}px`);
}
}
}

1
frontend/app/framework/internal.ts

@ -16,6 +16,7 @@ export * from './services/loading.service';
export * from './services/local-store.service';
export * from './services/message-bus.service';
export * from './services/onboarding.service';
export * from './services/resize.service';
export * from './services/resource-loader.service';
export * from './services/shortcut.service';
export * from './services/temp.service';

2
frontend/app/framework/module.ts

@ -75,6 +75,7 @@ import {
PopupLinkDirective,
ProgressBarComponent,
ResizedDirective,
ResizeService,
ResourceLoaderService,
RootViewComponent,
SafeHtmlPipe,
@ -269,6 +270,7 @@ export class SqxFrameworkModule {
LoadingService,
MessageBus,
OnboardingService,
ResizeService,
ResourceLoaderService,
ShortcutService,
TempService,

58
frontend/app/framework/services/resize.service.ts

@ -0,0 +1,58 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable, OnDestroy } from '@angular/core';
import ResizeObserver from 'resize-observer-polyfill';
export interface ResizeListener {
onResize(rect: DOMRect, element: Element): void;
}
export const ResizeServiceFactory = () => {
return new ResizeService();
};
@Injectable()
export class ResizeService implements OnDestroy {
private readonly listeners = new WeakMap<Element, ResizeListener>();
private observer: ResizeObserver;
public ngOnDestroy() {
if (this.observer) {
this.observer.disconnect();
}
}
public listen(target: Element, listener: ResizeListener) {
if (!this.observer) {
this.observer = new ResizeObserver(entries => {
for (const entry of entries) {
if (this.listeners.has(entry.target)) {
const component = this.listeners.get(entry.target);
if (component) {
component.onResize(entry.contentRect as any, entry.target);
}
}
}
});
}
this.listeners.set(target, listener);
this.observer.observe(target);
return () => {
this.unlisten(target);
};
}
public unlisten(target: Element) {
this.observer.unobserve(target);
}
}

28
frontend/app/shared/components/assets/asset.component.html

@ -30,18 +30,23 @@
<a class="file-edit ml-2" (click)="edit()" *ngIf="!isDisabled">
<i class="icon-pencil"></i>
</a>
<a class="file-download ml-2" [href]="asset | sqxAssetUrl" sqxStopClick sqxExternalLink="noicon">
<i class="icon-download"></i>
</a>
<a class="file-delete ml-2" *ngIf="!isDisabled && !removeMode && asset.canDelete"
(sqxConfirmClick)="delete.emit()"
confirmTitle="Delete asset"
confirmText="Do you really want to delete the asset?">
<i class="icon-delete"></i>
</a>
<a class="file-delete ml-2" (click)="remove.emit()" *ngIf="removeMode">
<i class="icon-close"></i>
</a>
<ng-container *ngIf="!isDisabled">
<a class="file-delete ml-2" *ngIf="!removeMode && asset.canDelete"
(sqxConfirmClick)="delete.emit()"
confirmTitle="Delete asset"
confirmText="Do you really want to delete the asset?">
<i class="icon-delete"></i>
</a>
<a class="file-delete ml-2" (click)="remove.emit()" *ngIf="removeMode">
<i class="icon-close"></i>
</a>
</ng-container>
</div>
<span class="overlay-type" *ngIf="asset.fileType">
@ -133,13 +138,14 @@
</td>
</ng-container>
<td class="col-actions text-right" *ngIf="!isDisabled || removeMode">
<button type="button" class="btn btn-text-danger" *ngIf="!isDisabled && !removeMode && asset.canDelete"
<td class="col-actions text-right" *ngIf="!isDisabled">
<button type="button" class="btn btn-text-danger" *ngIf="!removeMode && asset.canDelete"
(sqxConfirmClick)="remove.emit()"
confirmTitle="Delete asset"
confirmText="Do you really want to delete the asset?">
<i class="icon-bin2"></i>
</button>
<button type="button" class="btn btn-text-secondary" (click)="remove.emit()" *ngIf="removeMode">
<i class="icon-close"></i>
</button>

Loading…
Cancel
Save