From 7690205376705a9131903ebd416d904eef71edc9 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 6 Feb 2020 14:54:38 +0100 Subject: [PATCH] Feature/reactive ui (#485) * Reactive UI * Disabled improved. * Resized service. --- .../shared/forms/assets-editor.component.html | 17 +++++- .../framework/angular/resized.directive.ts | 45 ++++++-------- .../framework/angular/sync-width.directive.ts | 36 +++++++----- frontend/app/framework/internal.ts | 1 + frontend/app/framework/module.ts | 2 + .../app/framework/services/resize.service.ts | 58 +++++++++++++++++++ .../components/assets/asset.component.html | 28 +++++---- 7 files changed, 132 insertions(+), 55 deletions(-) create mode 100644 frontend/app/framework/services/resize.service.ts diff --git a/frontend/app/features/content/shared/forms/assets-editor.component.html b/frontend/app/features/content/shared/forms/assets-editor.component.html index 905ce1ac7..690f122e5 100644 --- a/frontend/app/features/content/shared/forms/assets-editor.component.html +++ b/frontend/app/features/content/shared/forms/assets-editor.component.html @@ -1,8 +1,13 @@ -
+
-
-
+
+
Drop files or click
@@ -26,11 +31,15 @@
@@ -42,6 +51,7 @@
@@ -59,6 +69,7 @@ { - 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(); - 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); + }); } } } \ No newline at end of file diff --git a/frontend/app/framework/angular/sync-width.directive.ts b/frontend/app/framework/angular/sync-width.directive.ts index 709abf0e1..013165a4c 100644 --- a/frontend/app/framework/angular/sync-width.directive.ts +++ b/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, - 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`); + } } } \ No newline at end of file diff --git a/frontend/app/framework/internal.ts b/frontend/app/framework/internal.ts index 950a0684d..75cfcb18d 100644 --- a/frontend/app/framework/internal.ts +++ b/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'; diff --git a/frontend/app/framework/module.ts b/frontend/app/framework/module.ts index 7f07ea98c..76953d953 100644 --- a/frontend/app/framework/module.ts +++ b/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, diff --git a/frontend/app/framework/services/resize.service.ts b/frontend/app/framework/services/resize.service.ts new file mode 100644 index 000000000..84fd7164b --- /dev/null +++ b/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(); + 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); + } +} \ No newline at end of file diff --git a/frontend/app/shared/components/assets/asset.component.html b/frontend/app/shared/components/assets/asset.component.html index d18f90fe0..a64c8afec 100644 --- a/frontend/app/shared/components/assets/asset.component.html +++ b/frontend/app/shared/components/assets/asset.component.html @@ -30,18 +30,23 @@ + - - - - - - + + + + + + + + + +
@@ -133,13 +138,14 @@ - - +