Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues 159 and 160 #161

Merged
merged 3 commits into from
Jul 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions projects/scion/dimension/src/lib/dimension.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import { SciDimensionDirective } from './dimension.directive';
* Web Performance Working Group is working on a W3C recommendation for natively observing changes to Element’s size.
* The Web API draft is still work in progress and support limited to Google Chrome and Opera.
*
* You can control if to use the native {ResizeObserver} by default with {USE_NATIVE_RESIZE_OBSERVER} DI injection token.
* If not provided, the native resize observable is used, unless explicitly set via options object when creating the resize observable.
*
* @see https://wicg.github.io/ResizeObserver/
* @see https://caniuse.com/#feat=resizeobserver
*/
Expand Down
23 changes: 20 additions & 3 deletions projects/scion/dimension/src/lib/dimension.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@

import { concat, fromEvent, Observable, Observer, of, ReplaySubject, Subject, TeardownLogic } from 'rxjs';
import { map, multicast, refCount, switchMap, takeUntil, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Injectable, InjectionToken, Injector } from '@angular/core';

/**
* CSS class added to the HTML <object> element emitting resize events if not using the native {ResizeObserver}.
*/
const SYNTH_RESIZE_OBSERVABLE_OBJECT_MARKER = 'synth-resize-observable';

/**
* DI injection token to control if to use the native {ResizeObserver} by default.
* If not provided, the native resize observable is used, unless explicitly set via options object when creating the resize observable.
*
* This flag is only evaluated if the user agent supports {ResizeObserver}.
*/
export const USE_NATIVE_RESIZE_OBSERVER = new InjectionToken<boolean>('USE_NATIVE_RESIZE_OBSERVER');

/**
* Allows observing the dimension of an element.
*
Expand All @@ -37,6 +48,9 @@ export class SciDimensionService {
*/
public _objectObservableRegistry = new Map<HTMLElement, Observable<SciDimension>>();

constructor(private _injector: Injector) {
}

/**
* Upon subscription, it emits the element's dimension, and then continuously emits when the dimension of the element changes. It never completes.
*
Expand All @@ -48,9 +62,12 @@ export class SciDimensionService {
* By default, this flag is enabled.
*/
public dimension$(target: HTMLElement, options?: { useNativeResizeObserver: boolean }): Observable<SciDimension> {
const useNativeResizeObserver = !options || options.useNativeResizeObserver === undefined || options.useNativeResizeObserver === true;
options = {
useNativeResizeObserver: this._injector.get(USE_NATIVE_RESIZE_OBSERVER, true),
...options,
};

if (useNativeResizeObserver && supportsNativeResizeObserver()) {
if (options.useNativeResizeObserver && supportsNativeResizeObserver()) {
return createNativeResizeObservable$(target);
}

Expand Down
2 changes: 1 addition & 1 deletion projects/scion/dimension/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
export { SciDimensionModule } from './lib/dimension.module';
export { SciDimensionDirective } from './lib/dimension.directive';
export { SciMutationService } from './lib/mutation.service';
export { SciDimensionService, SciDimension } from './lib/dimension.service';
export { SciDimensionService, SciDimension, USE_NATIVE_RESIZE_OBSERVER } from './lib/dimension.service';
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { map } from 'rxjs/operators';
* Allows issuing an intent to interact with the platform.
*/
export class IntentService implements Service {

/**
* Issues an intent to the application platform and receives a series of replies.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* SPDX-License-Identifier: EPL-2.0
*/

import { fromEvent, Observable, Subject } from 'rxjs';
import { fromEvent, merge, Observable, Observer, Subject, TeardownLogic } from 'rxjs';
import { filter, first, takeUntil } from 'rxjs/operators';
import { UUID } from './uuid.util';
import { Service } from './metadata';
Expand Down Expand Up @@ -102,14 +102,22 @@ export class DefaultMessageBus implements MessageBus {
envelope.replyToUid = replyToUid;
envelope.protocol = PROTOCOL;

const reply$ = this._stream$
.pipe(
filter(env => env.channel === 'reply'),
filter(env => env.replyToUid === replyToUid),
takeUntil(this._destroy$),
);
this.postMessage(envelope);
return reply$;
return new Observable((observer: Observer<MessageEnvelope>): TeardownLogic => {
const destroy$ = new Subject<void>();
this._stream$
.pipe(
filter(env => env.channel === 'reply'),
filter(env => env.replyToUid === replyToUid),
takeUntil(merge(destroy$, this._destroy$)),
)
.subscribe(observer);

this.postMessage(envelope);

return (): void => {
destroy$.next();
};
});
}

public requestReply(envelope: MessageEnvelope): Promise<MessageEnvelope> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable, ModuleWithProviders, NgModule } from '@angular/core';
import { ViewActivationInstantProvider } from '../view-activation-instant-provider.service';
import { WorkbenchModule } from '../workbench.module';
import { WorkbenchConfig } from '../workbench.config';
import { USE_NATIVE_RESIZE_OBSERVER } from '@scion/dimension';

@Injectable()
export class ViewActivationTestingInstantProvider implements ViewActivationInstantProvider {
Expand All @@ -27,6 +28,14 @@ export class WorkbenchTestingModule {
providers: [
WorkbenchModule.forRoot(config).providers,
{provide: ViewActivationInstantProvider, useClass: ViewActivationTestingInstantProvider},
/**
* Disable native `ResizeObserver` in tests because it sometimes throws 'loop limit exceeded' error which can safely be ignored.
*
* Comment from the specification authors:
* This error means that `ResizeObserver` was not able to deliver all observations within a single animation frame. It is benign and the site will not break.
* See https://stackoverflow.com/a/50387233
*/
{provide: USE_NATIVE_RESIZE_OBSERVER, useValue: false},
],
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<sci-viewport [style.width.px]="viewTabsWidthPx"
scrollbarStyle="hidden"
<sci-viewport scrollbarStyle="hidden"
sciDimension (sciDimensionChange)="onViewportChange()"
(scroll)="onScroll()">
<div #viewport_client class="viewport-client" sciDimension (sciDimensionChange)="onViewportClientChange()">
<div class="viewport-client" sciDimension (sciDimensionChange)="onViewportClientChange($event)">
<wb-view-tab *ngFor="let viewRef of viewPartService.viewRefs" [viewRef]="viewRef" [renderingHint]="'tab-item'"></wb-view-tab>
</div>
</sci-viewport>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { VIEW_DRAG_TYPE } from '../../workbench.constants';
import { WorkbenchLayoutService } from '../../workbench-layout.service';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { SciViewportComponent } from '@scion/viewport';
import { SciDimension } from '@scion/dimension';

@Component({
selector: 'wb-view-part-bar',
Expand All @@ -29,10 +31,8 @@ export class ViewPartBarComponent implements OnDestroy {
@ViewChildren(ViewTabComponent)
private _viewTabs: QueryList<ViewTabComponent>;

@ViewChild('viewport_client', {static: true})
private _viewportClient: ElementRef<HTMLElement>;

public viewTabsWidthPx: number;
@ViewChild(SciViewportComponent, {static: true, read: ElementRef})
private _viewport: ElementRef<HTMLElement>;

constructor(private _workbench: InternalWorkbenchService,
private _workbenchLayout: WorkbenchLayoutService,
Expand Down Expand Up @@ -68,20 +68,23 @@ export class ViewPartBarComponent implements OnDestroy {
}

public onViewportChange(): void {
this.layout();
this.computeHiddenViewTabs();
}

public onViewportClientChange(): void {
this.layout();
public onViewportClientChange(dimension: SciDimension): void {
// The viewport is set to 'flex: initial' with its width set equal to the viewport client width. It shrinks if there is not enough space available.
// The viewport is not set to 'flex: auto' to render left-aligned actions right after the view tabs.

// The width is set directly to the viewport DOM element not to waste a change detection cycle when computing the hidden view tabs.
this._viewport.nativeElement.style.width = `${dimension.clientWidth}px`;
this.computeHiddenViewTabs();
}

public onScroll(): void {
this.layout();
this.computeHiddenViewTabs();
}

private layout(): void {
this.viewTabsWidthPx = this._viewportClient.nativeElement.clientWidth;

private computeHiddenViewTabs(): void {
// Compute tabs which are not visible in the viewtabs viewport.
this._viewTabs && this.viewPartService.setHiddenViewTabs(this._viewTabs
.filter(viewTab => !viewTab.isVisibleInViewport())
Expand Down