Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

feat(core): add centralized media marshal service #900

Merged
merged 1 commit into from
Dec 14, 2018
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Directive, Optional, Self} from '@angular/core';
import {FlexDirective} from '@angular/flex-layout';
import {DefaultFlexDirective} from '@angular/flex-layout';

@Directive({
selector: '[ngxSplitArea]',
Expand All @@ -8,5 +8,5 @@ import {FlexDirective} from '@angular/flex-layout';
}
})
export class SplitAreaDirective {
constructor(@Optional() @Self() public flex: FlexDirective) {}
constructor(@Optional() @Self() public flex: DefaultFlexDirective) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class SplitDirective implements AfterContentInit, OnDestroy {
const currentValue = flex.activatedValue;

// Update Flex-Layout value to build/inject new flexbox CSS
flex.activatedValue = this.calculateSize(currentValue, delta);
flex.activatedValue = `${this.calculateSize(currentValue, delta)}`;
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/apps/universal-app/src/app/split/split-area.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Directive, Optional, Self} from '@angular/core';
import {FlexDirective} from '@angular/flex-layout';
import {DefaultFlexDirective} from '@angular/flex-layout';

@Directive({
selector: '[ngxSplitArea]',
Expand All @@ -8,5 +8,5 @@ import {FlexDirective} from '@angular/flex-layout';
}
})
export class SplitAreaDirective {
constructor(@Optional() @Self() public flex: FlexDirective) { }
constructor(@Optional() @Self() public flex: DefaultFlexDirective) { }
}
2 changes: 1 addition & 1 deletion src/apps/universal-app/src/app/split/split.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class SplitDirective implements AfterContentInit, OnDestroy {
const currentValue = flex.activatedValue;

// Update Flex-Layout value to build/inject new flexbox CSS
flex.activatedValue = this.calculateSize(currentValue, delta);
flex.activatedValue = `${this.calculateSize(currentValue, delta)}`;
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/core/add-alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {extendObject} from '../utils/object-extend';
* For the specified MediaChange, make sure it contains the breakpoint alias
* and suffix (if available).
*/
export function mergeAlias(dest: MediaChange, source: BreakPoint | null) {
export function mergeAlias(dest: MediaChange, source: BreakPoint | null): MediaChange {
return extendObject(dest, source ? {
mqAlias: source.alias,
suffix: source.suffix
Expand Down
2 changes: 2 additions & 0 deletions src/lib/core/base/base-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {StyleUtils} from '../style-utils/style-utils';
/**
* Adapter to the BaseDirective abstract class so it can be used via composition.
* @see BaseDirective
* @deprecated
* @deletion-target v7.0.0-beta.21
*/
export class BaseDirectiveAdapter extends BaseDirective {

Expand Down
6 changes: 5 additions & 1 deletion src/lib/core/base/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import {MediaMonitor} from '../media-monitor/media-monitor';
import {MediaQuerySubscriber} from '../media-change';
import {StyleBuilder} from '../style-builder/style-builder';

/** Abstract base class for the Layout API styling directives. */
/**
* Abstract base class for the Layout API styling directives.
* @deprecated
* @deletion-target v7.0.0-beta.21
*/
export abstract class BaseDirective implements OnDestroy, OnChanges {

/**
Expand Down
122 changes: 122 additions & 0 deletions src/lib/core/base/base2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementRef, OnChanges, OnDestroy, SimpleChanges} from '@angular/core';
import {Subject} from 'rxjs';

import {StyleDefinition, StyleUtils} from '../style-utils/style-utils';
import {StyleBuilder} from '../style-builder/style-builder';
import {MediaMarshaller} from '../media-marshaller/media-marshaller';
import {buildLayoutCSS} from '../../utils/layout-validator';

export abstract class BaseDirective2 implements OnChanges, OnDestroy {

protected DIRECTIVE_KEY = '';
protected inputs: string[] = [];
protected destroySubject: Subject<void> = new Subject();

/** Access to host element's parent DOM node */
protected get parentElement(): HTMLElement | null {
return this.elementRef.nativeElement.parentElement;
}

/** Access to the HTMLElement for the directive */
protected get nativeElement(): HTMLElement {
return this.elementRef.nativeElement;
}

/** Access to the activated value for the directive */
get activatedValue(): string {
return this.marshal.getValue(this.nativeElement, this.DIRECTIVE_KEY);
}
set activatedValue(value: string) {
this.marshal.setValue(this.nativeElement, this.DIRECTIVE_KEY, value,
this.marshal.activatedBreakpoint);
}

/** Cache map for style computation */
protected styleCache: Map<string, StyleDefinition> = new Map();

protected constructor(protected elementRef: ElementRef,
protected styleBuilder: StyleBuilder,
protected styler: StyleUtils,
protected marshal: MediaMarshaller) {
}

/** For @Input changes */
ngOnChanges(changes: SimpleChanges) {
Object.keys(changes).forEach(key => {
if (this.inputs.indexOf(key) !== -1) {
const bp = key.split('.')[1] || '';
const val = changes[key].currentValue;
this.setValue(val, bp);
}
});
}

ngOnDestroy(): void {
this.destroySubject.next();
this.destroySubject.complete();
this.marshal.releaseElement(this.nativeElement);
}

/** Add styles to the element using predefined style builder */
protected addStyles(input: string, parent?: Object) {
const builder = this.styleBuilder;
const useCache = builder.shouldCache;

let genStyles: StyleDefinition | undefined = this.styleCache.get(input);

if (!genStyles || !useCache) {
genStyles = builder.buildStyles(input, parent);
if (useCache) {
this.styleCache.set(input, genStyles);
}
}

this.applyStyleToElement(genStyles);
builder.sideEffect(input, genStyles, parent);
}

protected triggerUpdate() {
const val = this.marshal.getValue(this.nativeElement, this.DIRECTIVE_KEY);
this.marshal.updateElement(this.nativeElement, this.DIRECTIVE_KEY, val);
}

/**
* Determine the DOM element's Flexbox flow (flex-direction).
*
* Check inline style first then check computed (stylesheet) style.
* And optionally add the flow value to element's inline style.
*/
protected getFlexFlowDirection(target: HTMLElement, addIfMissing = false): string {
if (target) {
const [value, hasInlineValue] = this.styler.getFlowDirection(target);

if (!hasInlineValue && addIfMissing) {
const style = buildLayoutCSS(value);
const elements = [target];
this.styler.applyStyleToElements(style, elements);
}

return value.trim();
}

return 'row';
}

/** Applies styles given via string pair or object map to the directive element */
protected applyStyleToElement(style: StyleDefinition,
value?: string | number,
element: HTMLElement = this.nativeElement) {
this.styler.applyStyleToElement(element, style, value);
}

protected setValue(val: any, bp: string): void {
this.marshal.setValue(this.nativeElement, this.DIRECTIVE_KEY, val, bp);
}
}
1 change: 1 addition & 0 deletions src/lib/core/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@

export * from './base';
export * from './base-adapter';
export * from './base2';
2 changes: 2 additions & 0 deletions src/lib/core/breakpoints/break-point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ export interface BreakPoint {
alias: string;
suffix?: string;
overlapping?: boolean;
// The priority of the individual breakpoint when overlapping another breakpoint
priority?: number;
}
6 changes: 6 additions & 0 deletions src/lib/core/breakpoints/breakpoint-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ export function mergeByAlias(defaults: BreakPoint[], custom: BreakPoint[] = []):
return validateSuffixes(Object.keys(dict).map(k => dict[k]));
}

/** HOF to sort the breakpoints by priority */
export function prioritySort(a: BreakPoint, b: BreakPoint): number {
const priorityA = a.priority || 0;
const priorityB = b.priority || 0;
return priorityB - priorityA;
}
39 changes: 26 additions & 13 deletions src/lib/core/breakpoints/data/break-points.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,63 +14,76 @@ export const RESPONSIVE_ALIASES = [
export const DEFAULT_BREAKPOINTS: BreakPoint[] = [
{
alias: 'xs',
mediaQuery: '(min-width: 0px) and (max-width: 599px)'
mediaQuery: '(min-width: 0px) and (max-width: 599px)',
priority: 100,
},
{
alias: 'gt-xs',
overlapping: true,
mediaQuery: '(min-width: 600px)'
mediaQuery: '(min-width: 600px)',
priority: 7,
},
{
alias: 'lt-sm',
overlapping: true,
mediaQuery: '(max-width: 599px)'
mediaQuery: '(max-width: 599px)',
priority: 10,
},
{
alias: 'sm',
mediaQuery: '(min-width: 600px) and (max-width: 959px)'
mediaQuery: '(min-width: 600px) and (max-width: 959px)',
priority: 100,
},
{
alias: 'gt-sm',
overlapping: true,
mediaQuery: '(min-width: 960px)'
mediaQuery: '(min-width: 960px)',
priority: 8,
},
{
alias: 'lt-md',
overlapping: true,
mediaQuery: '(max-width: 959px)'
mediaQuery: '(max-width: 959px)',
priority: 9,
},
{
alias: 'md',
mediaQuery: '(min-width: 960px) and (max-width: 1279px)'
mediaQuery: '(min-width: 960px) and (max-width: 1279px)',
priority: 100,
},
{
alias: 'gt-md',
overlapping: true,
mediaQuery: '(min-width: 1280px)'
mediaQuery: '(min-width: 1280px)',
priority: 9,
},
{
alias: 'lt-lg',
overlapping: true,
mediaQuery: '(max-width: 1279px)'
mediaQuery: '(max-width: 1279px)',
priority: 8,
},
{
alias: 'lg',
mediaQuery: '(min-width: 1280px) and (max-width: 1919px)'
mediaQuery: '(min-width: 1280px) and (max-width: 1919px)',
priority: 100,
},
{
alias: 'gt-lg',
overlapping: true,
mediaQuery: '(min-width: 1920px)'
mediaQuery: '(min-width: 1920px)',
priority: 10,
},
{
alias: 'lt-xl',
overlapping: true,
mediaQuery: '(max-width: 1919px)'
mediaQuery: '(max-width: 1919px)',
priority: 7,
},
{
alias: 'xl',
mediaQuery: '(min-width: 1920px) and (max-width: 5000px)'
mediaQuery: '(min-width: 1920px) and (max-width: 5000px)',
priority: 100,
}
];

1 change: 1 addition & 0 deletions src/lib/core/breakpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './data/orientation-break-points';
export * from './break-point';
export * from './break-point-registry';
export * from './break-points-token';
export {prioritySort} from './breakpoint-tools';
29 changes: 16 additions & 13 deletions src/lib/core/match-media/mock/mock-match-media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,32 @@ export class MockMatchMedia extends MatchMedia {
// Simulate activation of overlapping lt-<XXX> ranges
switch (alias) {
case 'lg' :
this._activateByAlias('lt-xl');
this._activateByAlias('lt-xl', true);
break;
case 'md' :
this._activateByAlias('lt-xl, lt-lg');
this._activateByAlias('lt-xl, lt-lg', true);
break;
case 'sm' :
this._activateByAlias('lt-xl, lt-lg, lt-md');
this._activateByAlias('lt-xl, lt-lg, lt-md', true);
break;
case 'xs' :
this._activateByAlias('lt-xl, lt-lg, lt-md, lt-sm');
this._activateByAlias('lt-xl, lt-lg, lt-md, lt-sm', true);
break;
}

// Simulate activate of overlapping gt-<xxxx> mediaQuery ranges
switch (alias) {
case 'xl' :
this._activateByAlias('gt-lg, gt-md, gt-sm, gt-xs');
this._activateByAlias('gt-lg, gt-md, gt-sm, gt-xs', true);
break;
case 'lg' :
this._activateByAlias('gt-md, gt-sm, gt-xs');
this._activateByAlias('gt-md, gt-sm, gt-xs', true);
break;
case 'md' :
this._activateByAlias('gt-sm, gt-xs');
this._activateByAlias('gt-sm, gt-xs', true);
break;
case 'sm' :
this._activateByAlias('gt-xs');
this._activateByAlias('gt-xs', true);
break;
}
}
Expand All @@ -115,21 +115,24 @@ export class MockMatchMedia extends MatchMedia {
/**
*
*/
private _activateByAlias(aliases: string) {
private _activateByAlias(aliases: string, useOverlaps = false) {
const activate = (alias: string) => {
const bp = this._breakpoints.findByAlias(alias);
this._activateByQuery(bp ? bp.mediaQuery : alias);
this._activateByQuery(bp ? bp.mediaQuery : alias, useOverlaps);
};
aliases.split(',').forEach(alias => activate(alias.trim()));
}

/**
*
*/
private _activateByQuery(mediaQuery: string) {
const mql = this._registry.get(mediaQuery)!;
private _activateByQuery(mediaQuery: string, useOverlaps = false) {
if (useOverlaps) {
this._registerMediaQuery(mediaQuery);
}
const mql = this._registry.get(mediaQuery);
const alreadyAdded = this._actives
.reduce((found, it) => (found || (mql && (it.media === mql.media))), false);
.reduce((found, it) => (found || (mql ? (it.media === mql.media) : false)), false);

if (mql && !alreadyAdded) {
this._actives.push(mql.activate());
Expand Down
Loading