Skip to content
This repository has been archived by the owner on Mar 29, 2024. It is now read-only.

Commit

Permalink
Various improvements for smaller screen sizes. Fix network rating
Browse files Browse the repository at this point in the history
  • Loading branch information
ppacher committed Nov 2, 2022
1 parent 62974d4 commit c475aab
Show file tree
Hide file tree
Showing 27 changed files with 455 additions and 902 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
}
}

span {
& > span {
@apply flex-grow text-ellipsis inline-block overflow-hidden;
@apply px-2;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { SfngTabComponent, SfngTabContentDirective } from './tab';
export { SfngTabGroupComponent } from './tab-group';
export { SfngTabContentScrollEvent, SfngTabGroupComponent } from './tab-group';
export { SfngTabModule as TabModule } from './tabs.module';

44 changes: 38 additions & 6 deletions modules/portmaster/projects/safing/ui/src/lib/tabs/tab-group.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { ListKeyManager } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { CdkPortalOutlet, ComponentPortal, TemplatePortal } from "@angular/cdk/portal";
import { AfterContentInit, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, ContentChildren, ElementRef, Injector, Input, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from "@angular/core";
import { CdkPortalOutlet, ComponentPortal } from "@angular/cdk/portal";
import { AfterContentInit, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, ContentChildren, ElementRef, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, QueryList, ViewChild, ViewChildren } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Observable, Subject } from "rxjs";
import { distinctUntilChanged, map, takeUntil } from "rxjs/operators";
import { SfngTabComponent, TabOutletComponent, TAB_ANIMATION_DIRECTION, TAB_PORTAL } from "./tab";
import { SfngTabComponent, TabOutletComponent, TAB_ANIMATION_DIRECTION, TAB_PORTAL, TAB_SCROLL_HANDLER } from "./tab";

export interface SfngTabContentScrollEvent {
event?: Event;
scrollTop: number;
previousScrollTop: number;
}

/**
* Tab group component for rendering a tab-style navigation with support for
Expand Down Expand Up @@ -53,13 +59,18 @@ export class SfngTabGroupComponent implements AfterContentInit, AfterViewInit, O
@ViewChild(CdkPortalOutlet, { static: true })
portalOutlet: CdkPortalOutlet | null = null;

@Output()
tabContentScroll = new EventEmitter<SfngTabContentScrollEvent>();

/** The name of the tab group. Used to update the currently active tab in the route */
@Input()
name = 'tab'

@Input()
outletClass = '';

private scrollTop: number = 0;

/** Whether or not the current tab should be syncronized with the angular router using a query parameter */
@Input()
set linkRouter(v: any) {
Expand Down Expand Up @@ -167,12 +178,18 @@ export class SfngTabGroupComponent implements AfterContentInit, AfterViewInit, O

this.portalOutlet?.detach();

const newOutletPortal = this.createTabOutlet(activeTab.tabContent.portal, animationDirection);
const newOutletPortal = this.createTabOutlet(activeTab, animationDirection);
this.activeTabIndex = change;
this.tabContentScroll.next({
scrollTop: 0,
previousScrollTop: this.scrollTop,
})

this.scrollTop = 0;

this.tabActivate$.next(activeTab.id);
this.portalOutlet?.attach(newOutletPortal);

this.repositionTabBar();

if (this._linkRouter) {
Expand Down Expand Up @@ -276,17 +293,32 @@ export class SfngTabGroupComponent implements AfterContentInit, AfterViewInit, O
})
}

private createTabOutlet(contentPortal: TemplatePortal<any>, animationDir: 'left' | 'right'): ComponentPortal<TabOutletComponent> {
private createTabOutlet(tab: SfngTabComponent, animationDir: 'left' | 'right'): ComponentPortal<TabOutletComponent> {
const injector = Injector.create({
providers: [
{
provide: TAB_PORTAL,
useValue: contentPortal,
useValue: tab.tabContent!.portal,
},
{
provide: TAB_ANIMATION_DIRECTION,
useValue: animationDir,
},
{
provide: TAB_SCROLL_HANDLER,
useValue: (e: Event) => {
const newScrollTop = (e.target as HTMLElement).scrollTop;

tab.tabContentScroll.next(e);
this.tabContentScroll.next({
event: e,
scrollTop: newScrollTop,
previousScrollTop: this.scrollTop,
});

this.scrollTop = newScrollTop;
}
},
],
parent: this.injector,
name: 'TabOutletInjectot',
Expand Down
19 changes: 17 additions & 2 deletions modules/portmaster/projects/safing/ui/src/lib/tabs/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { animate, style, transition, trigger } from "@angular/animations";
import { ListKeyManagerOption } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { CdkPortalOutlet, TemplatePortal } from "@angular/cdk/portal";
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Directive, Inject, InjectionToken, Input, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Directive, EventEmitter, Inject, InjectionToken, Input, Output, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";

/** TAB_PORTAL is the injection token used to inject the TabContentDirective portal into TabOutletComponent */
export const TAB_PORTAL = new InjectionToken<TemplatePortal>('TAB_PORTAL');

/** TAB_ANIMATION_DIRECTION is the injection token used to control the :enter animation origin of TabOutletComponent */
export const TAB_ANIMATION_DIRECTION = new InjectionToken<'left' | 'right'>('TAB_ANIMATION_DIRECTION');

/** TAB_SCROLL_HANDLER is called by the SfngTabOutletComponent when a scroll event occurs. */
export const TAB_SCROLL_HANDLER = new InjectionToken<(_: Event) => void>('TAB_SCROLL_HANDLER')

/**
* Structural directive (*sfngTabContent) to defined lazy-loaded tab content.
*/
Expand Down Expand Up @@ -60,6 +63,10 @@ export class SfngTabComponent implements ListKeyManagerOption {
get warning() { return this._warning }
private _warning = false;

/** Emits when the tab content is scrolled */
@Output()
tabContentScroll = new EventEmitter<Event>();

/** Whether or not the tab is currently disabled. */
@Input()
set disabled(v: any) {
Expand All @@ -74,14 +81,15 @@ export class SfngTabComponent implements ListKeyManagerOption {
getLabel() { return this.title }
}


/**
* A simple wrapper component around CdkPortalOutlet to add nice
* move animations.
*/
@Component({
selector: 'sfng-tab-outlet',
template: `
<div [@moveInOut]="{value: _appAnimate, params: {in: in, out: out}}" class="flex flex-col overflow-auto {{ outletClass }}">
<div [@moveInOut]="{value: _appAnimate, params: {in: in, out: out}}" class="flex flex-col overflow-auto {{ outletClass }}" (scroll)="onTabContentScroll($event)">
<ng-template [cdkPortalOutlet]="portal"></ng-template>
</div>
`,
Expand Down Expand Up @@ -133,12 +141,19 @@ export class TabOutletComponent implements AfterViewInit {
return this._animateDirection == 'left' ? '-100%' : '100%'
}

onTabContentScroll(event: Event) {
if (!!this.scrollHandler) {
this.scrollHandler(event)
}
}

@ViewChild(CdkPortalOutlet, { static: true })
portalOutlet!: CdkPortalOutlet;

constructor(
@Inject(TAB_PORTAL) public portal: TemplatePortal<any>,
@Inject(TAB_ANIMATION_DIRECTION) public _animateDirection: 'left' | 'right',
@Inject(TAB_SCROLL_HANDLER) public scrollHandler: (_: Event) => void,
private cdr: ChangeDetectorRef
) { }

Expand Down
5 changes: 3 additions & 2 deletions modules/portmaster/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

<app-side-dash class="flex-shrink-0" style="z-index: 100"
[ngClass]="{'absolute top-0 left-16 bg-gray-1002 h-full shadow-2xl': sideDashOverlay, 'relative': !sideDashOverlay}"
*ngIf="sideDashStatus === 'expanded'" [@fadeIn] [@fadeOut]>
*ngIf="sideDashStatus === 'expanded'" [@fadeIn] (@fadeIn.done)="windowResizeChange.next()" [@fadeOut]
(@fadeOut.done)="windowResizeChange.next()">
</app-side-dash>

<div class="main">
<div class="main" #mainContent>
<router-outlet></router-outlet>
</div>

Expand Down
61 changes: 51 additions & 10 deletions modules/portmaster/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Overlay } from '@angular/cdk/overlay';
import { ChangeDetectorRef, Component, HostListener, NgZone, OnInit } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnInit, Renderer2, ViewChild } from '@angular/core';
import { Params, Router } from '@angular/router';
import { PortapiService } from '@safing/portmaster-api';
import { OverlayStepper, SfngDialogService, StepperRef } from '@safing/ui';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { debounceTime, filter, map, mergeMap, skip, startWith, take } from 'rxjs/operators';
import { BehaviorSubject, merge, Subject } from 'rxjs';
import { debounceTime, filter, mergeMap, skip, startWith, take } from 'rxjs/operators';
import { IntroModule } from './intro';
import { NotificationsService, UIStateService } from './services';
import { ActionIndicatorService } from './shared/action-indicator';
Expand All @@ -21,7 +21,7 @@ import { SfngNetquerySearchOverlayComponent } from './shared/netquery/search-ove
fadeOutAnimation,
]
})
export class AppComponent implements OnInit {
export class AppComponent implements OnInit, AfterViewInit {
readonly connected = this.portapi.connected$.pipe(
debounceTime(250),
startWith(false)
Expand All @@ -41,22 +41,26 @@ export class AppComponent implements OnInit {
private sideDashOpen = new BehaviorSubject<boolean>(false);

/** Used to emit when the window size changed */
private windowResizeChange = new Subject<void>();
windowResizeChange = new Subject<void>();

get sideDashOpen$() { return this.sideDashOpen.asObservable() }

get showOverlay$() { return this.exitService.showOverlay$ }

get onContentSizeChange$() {
return combineLatest([
return merge(
this.windowResizeChange,
this.sideDashOpen,
]).pipe(
debounceTime(100),
map(([_, sideDashOpen]) => sideDashOpen)
this.sideDashOpen$
)
.pipe(
startWith(undefined),
debounceTime(100),
)
}

@ViewChild('mainContent', { read: ElementRef, static: true })
mainContent!: ElementRef<HTMLDivElement>;

@HostListener('window:resize')
onWindowResize() {
this.windowResizeChange.next();
Expand Down Expand Up @@ -93,6 +97,7 @@ export class AppComponent implements OnInit {
private dialog: SfngDialogService,
private overlay: Overlay,
private stateService: UIStateService,
private renderer2: Renderer2,
) {
(window as any).portapi = portapi;
}
Expand All @@ -115,6 +120,38 @@ export class AppComponent implements OnInit {
}

ngOnInit() {
// default breakpoints used by tailwindcss
const minContentWithBp = [
640, // sfng-sm:
768, // sfng-md:
1024, // sfng-lg:
1280, // sfng-xl:
1536 // sfng-2xl:
]

// prepare our breakpoint listeners and add the classes to our main element
merge(
this.windowResizeChange,
this.sideDashOpen$
)
.pipe(
startWith(undefined),
debounceTime(100),
)
.subscribe(() => {
const rect = (this.mainContent.nativeElement as HTMLElement).getBoundingClientRect();

minContentWithBp.forEach((bp, idx) => {
if (rect.width >= bp) {
this.renderer2.addClass(this.mainContent.nativeElement, `min-width-${bp}px`)
} else {
this.renderer2.removeClass(this.mainContent.nativeElement, `min-width-${bp}px`)
}
})

this.changeDetectorRef.markForCheck();
})

// force a reload of the current route if we reconnected to
// portmaster. This ensures we'll refresh any data that's currently
// displayed.
Expand Down Expand Up @@ -160,6 +197,10 @@ export class AppComponent implements OnInit {
})
}

ngAfterViewInit(): void {
this.sideDashOpen.next(this.sideDashStatus !== 'collapsed')
}

showIntro(): StepperRef {
const stepperRef = this.overlayStepper.create(IntroModule.Stepper)

Expand Down
Loading

0 comments on commit c475aab

Please sign in to comment.