Skip to content

Commit

Permalink
fix(lib, media-query): use getDom() for platform-server/universal fixes
Browse files Browse the repository at this point in the history
Now gets document object from platform-browser by DI instead of global.

> Thx to @ardatan for PR #346.

Fixes #187. Closes #346.
  • Loading branch information
ThomasBurleson committed Jul 29, 2017
1 parent 21c34f7 commit 94d4c95
Show file tree
Hide file tree
Showing 20 changed files with 594 additions and 65 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
"url": "git+https://github.com/angular/flex-layout.git"
},
"scripts": {
"api": "gulp api-docs",
"build": "gulp :publish:build-releases",
"demo-app": "gulp serve:devapp",
"test": "gulp test",
"tslint": "gulp lint",
"stylelint": "gulp lint",
"e2e": "gulp e2e",
"docs": "gulp docs",
"deploy": "gulp deploy:devapp",
"stage": "gulp stage-deploy:devapp",
"stylelint": "gulp lint",
"test": "gulp test",
"tslint": "gulp lint",
"webdriver-manager": "webdriver-manager",
"docs": "gulp docs",
"api": "gulp api-docs"
"universal": "gulp universal:build"
},
"version": "2.0.0-beta.8",
"license": "MIT",
Expand Down
28 changes: 18 additions & 10 deletions src/lib/flexbox/api/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ElementRef, OnDestroy, SimpleChanges, OnChanges,
SimpleChange, Renderer
} from '@angular/core';
import {ɵgetDOM as getDom} from '@angular/platform-browser';

import {applyCssPrefixes} from '../../utils/auto-prefixer';
import {buildLayoutCSS} from '../../utils/layout-validator';
Expand Down Expand Up @@ -117,20 +118,27 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
*/
protected _getDisplayStyle(source?: HTMLElement): string {
let element: HTMLElement = source || this._elementRef.nativeElement;
let value = (element.style as any)['display'] || getComputedStyle(element)['display'];
return value ? value.trim() : 'block';
let value = '';
try {
let immediateValue = getDom().getStyle(element, 'display');
value = immediateValue || getDom().getComputedStyle(element).display;
} catch (e) {
// TODO: platform-server throws an exception for getComputedStyle
}

return value ? value.trim() : ((element.nodeType === 1) ? 'block' : 'inline-block');
}

protected _getFlowDirection(target: any, addIfMissing = false): string {
let value = '';
if (target) {
let directionKeys = Object.keys(applyCssPrefixes({'flex-direction': ''}));
let findDirection = (styles) => directionKeys.reduce((direction, key) => {
return direction || styles[key];
}, null);
let immediateValue = getDom().getStyle(target, 'flex-direction');
try {
value = immediateValue || getDom().getComputedStyle(target)['flex-direction'];
} catch (e) {
// TODO: platform-server throws an exception for getComputedStyle
}

let immediateValue = findDirection(target.style);
value = immediateValue || findDirection(getComputedStyle(target as Element));
if (!immediateValue && addIfMissing) {
value = value || 'row';
this._applyStyleToElements(buildLayoutCSS(value), [target]);
Expand Down Expand Up @@ -220,11 +228,11 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
* Special accessor to query for all child 'element' nodes regardless of type, class, etc.
*/
protected get childrenNodes() {
const obj = this._elementRef.nativeElement.childNodes;
const obj = this._elementRef.nativeElement.children;
const buffer = [];

// iterate backwards ensuring that length is an UInt32
for ( let i = obj.length; i--; ) {
for (let i = obj.length; i--; ) {
buffer[i] = obj[i];
}
return buffer;
Expand Down
42 changes: 23 additions & 19 deletions src/lib/flexbox/api/layout-gap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import {LayoutDirective} from './layout';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {LAYOUT_VALUES} from '../../utils/layout-validator';

/**
* 'layout-padding' styling directive
* Defines padding of child elements in a layout container
*/
@Directive({selector: `
@Directive({
selector: `
[fxLayoutGap],
[fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl],
[fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl],
Expand All @@ -42,22 +44,22 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
protected _observer: MutationObserver;

/* tslint:disable */
@Input('fxLayoutGap') set gap(val) { this._cacheInput('gap', val); }
@Input('fxLayoutGap.xs') set gapXs(val) { this._cacheInput('gapXs', val); }
@Input('fxLayoutGap.sm') set gapSm(val) { this._cacheInput('gapSm', val); };
@Input('fxLayoutGap.md') set gapMd(val) { this._cacheInput('gapMd', val); };
@Input('fxLayoutGap.lg') set gapLg(val) { this._cacheInput('gapLg', val); };
@Input('fxLayoutGap.xl') set gapXl(val) { this._cacheInput('gapXl', val); };

@Input('fxLayoutGap.gt-xs') set gapGtXs(val) { this._cacheInput('gapGtXs', val); };
@Input('fxLayoutGap.gt-sm') set gapGtSm(val) { this._cacheInput('gapGtSm', val); };
@Input('fxLayoutGap.gt-md') set gapGtMd(val) { this._cacheInput('gapGtMd', val); };
@Input('fxLayoutGap.gt-lg') set gapGtLg(val) { this._cacheInput('gapGtLg', val); };

@Input('fxLayoutGap.lt-sm') set gapLtSm(val) { this._cacheInput('gapLtSm', val); };
@Input('fxLayoutGap.lt-md') set gapLtMd(val) { this._cacheInput('gapLtMd', val); };
@Input('fxLayoutGap.lt-lg') set gapLtLg(val) { this._cacheInput('gapLtLg', val); };
@Input('fxLayoutGap.lt-xl') set gapLtXl(val) { this._cacheInput('gapLtXl', val); };
@Input('fxLayoutGap') set gap(val) { this._cacheInput('gap', val); }
@Input('fxLayoutGap.xs') set gapXs(val) { this._cacheInput('gapXs', val); }
@Input('fxLayoutGap.sm') set gapSm(val) { this._cacheInput('gapSm', val); };
@Input('fxLayoutGap.md') set gapMd(val) { this._cacheInput('gapMd', val); };
@Input('fxLayoutGap.lg') set gapLg(val) { this._cacheInput('gapLg', val); };
@Input('fxLayoutGap.xl') set gapXl(val) { this._cacheInput('gapXl', val); };

@Input('fxLayoutGap.gt-xs') set gapGtXs(val) { this._cacheInput('gapGtXs', val); };
@Input('fxLayoutGap.gt-sm') set gapGtSm(val) { this._cacheInput('gapGtSm', val); };
@Input('fxLayoutGap.gt-md') set gapGtMd(val) { this._cacheInput('gapGtMd', val); };
@Input('fxLayoutGap.gt-lg') set gapGtLg(val) { this._cacheInput('gapGtLg', val); };

@Input('fxLayoutGap.lt-sm') set gapLtSm(val) { this._cacheInput('gapLtSm', val); };
@Input('fxLayoutGap.lt-md') set gapLtMd(val) { this._cacheInput('gapLtMd', val); };
@Input('fxLayoutGap.lt-lg') set gapLtLg(val) { this._cacheInput('gapLtLg', val); };
@Input('fxLayoutGap.lt-xl') set gapLtXl(val) { this._cacheInput('gapLtXl', val); };

/* tslint:enable */
constructor(monitor: MediaMonitor,
Expand Down Expand Up @@ -124,8 +126,10 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
}
};

this._observer = new MutationObserver(onMutationCallback);
this._observer.observe(this._elementRef.nativeElement, {childList: true});
if (typeof MutationObserver !== 'undefined') {
this._observer = new MutationObserver(onMutationCallback);
this._observer.observe(this._elementRef.nativeElement, {childList: true});
}
}

/**
Expand Down
34 changes: 20 additions & 14 deletions src/lib/media-query/match-media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 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 {Injectable, NgZone} from '@angular/core';

import {Inject, Injectable, NgZone} from '@angular/core';
import {ɵgetDOM as getDom, DOCUMENT} from '@angular/platform-browser';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import {filter} from 'rxjs/operator/filter';
Expand All @@ -27,7 +27,9 @@ export interface MediaQueryListListener {
export interface MediaQueryList {
readonly matches: boolean;
readonly media: string;

addListener(listener: MediaQueryListListener): void;

removeListener(listener: MediaQueryListListener): void;
}

Expand All @@ -45,7 +47,7 @@ export class MatchMedia {
protected _source: BehaviorSubject<MediaChange>;
protected _observable$: Observable<MediaChange>;

constructor(protected _zone: NgZone) {
constructor(protected _zone: NgZone, @Inject(DOCUMENT) protected _document: any) {
this._registry = new Map<string, MediaQueryList>();
this._source = new BehaviorSubject<MediaChange>(new MediaChange(true));
this._observable$ = this._source.asObservable();
Expand Down Expand Up @@ -86,7 +88,7 @@ export class MatchMedia {
let list = normalizeQuery(mediaQuery);

if (list.length > 0) {
prepareQueryCSS(list);
prepareQueryCSS(list, this._document);

list.forEach(query => {
let mql = this._registry.get(query);
Expand Down Expand Up @@ -114,9 +116,12 @@ export class MatchMedia {
* Call window.matchMedia() to build a MediaQueryList; which
* supports 0..n listeners for activation/deactivation
*/
protected _buildMQL(query: string): MediaQueryList {
let canListen = !!(<any>window).matchMedia('all').addListener;
return canListen ? (<any>window).matchMedia(query) : <MediaQueryList>{
protected _buildMQL(query: string): MediaQueryList {
let canListen = (typeof matchMedia != 'undefined');
if (canListen) {
canListen = !!matchMedia('all').addListener;
}
return canListen ? matchMedia(query) : <MediaQueryList>{
matches: query === 'all' || query === '',
media: query,
addListener: () => {
Expand All @@ -140,27 +145,28 @@ const ALL_STYLES = {};
* @param query string The mediaQuery used to create a faux CSS selector
*
*/
function prepareQueryCSS(mediaQueries: string[]) {
function prepareQueryCSS(mediaQueries: string[], _document: any) {
let list = mediaQueries.filter(it => !ALL_STYLES[it]);
if (list.length > 0) {
let query = list.join(', ');

try {
let style = document.createElement('style');
let styleEl = getDom().createElement('style');

style.setAttribute('type', 'text/css');
if (!style['styleSheet']) {
getDom().setAttribute(styleEl, 'type', 'text/css');
if (!styleEl['styleSheet']) {
let cssText = `/*
@angular/flex-layout - workaround for possible browser quirk with mediaQuery listeners
see http://bit.ly/2sd4HMP
*/
@media ${query} {.fx-query-test{ }}`;
style.appendChild(document.createTextNode(cssText));
getDom().appendChild(styleEl, getDom().createTextNode(cssText));
}

document.getElementsByTagName('head')[0].appendChild(style);
getDom().appendChild(_document.head, styleEl);

// Store in private global registry
list.forEach(mq => ALL_STYLES[mq] = style);
list.forEach(mq => ALL_STYLES[mq] = styleEl);

} catch (e) {
console.error(e);
Expand Down
44 changes: 32 additions & 12 deletions src/lib/media-query/mock/mock-match-media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* 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 {Injectable, NgZone} from '@angular/core';
import {Inject, Injectable, NgZone} from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';

import {MatchMedia} from '../match-media';
import {BreakPointRegistry} from '../breakpoints/break-point-registry';

Expand All @@ -28,8 +30,10 @@ export class MockMatchMedia extends MatchMedia {
*/
public useOverlaps = false;

constructor(_zone: NgZone, private _breakpoints: BreakPointRegistry) {
super(_zone);
constructor(_zone: NgZone,
@Inject(DOCUMENT) _document: any,
private _breakpoints: BreakPointRegistry) {
super(_zone, _document);
this._actives = [];
}

Expand Down Expand Up @@ -83,18 +87,34 @@ export class MockMatchMedia extends MatchMedia {

// Simulate activation of overlapping lt-<XXX> ranges
switch (alias) {
case 'lg' : this._activateByAlias('lt-xl'); break;
case 'md' : this._activateByAlias('lt-xl, lt-lg'); break;
case 'sm' : this._activateByAlias('lt-xl, lt-lg, lt-md'); break;
case 'xs' : this._activateByAlias('lt-xl, lt-lg, lt-md, lt-sm'); break;
case 'lg' :
this._activateByAlias('lt-xl');
break;
case 'md' :
this._activateByAlias('lt-xl, lt-lg');
break;
case 'sm' :
this._activateByAlias('lt-xl, lt-lg, lt-md');
break;
case 'xs' :
this._activateByAlias('lt-xl, lt-lg, lt-md, lt-sm');
break;
}

// Simulate activate of overlapping gt-<xxxx> mediaQuery ranges
switch (alias) {
case 'xl' : this._activateByAlias('gt-lg, gt-md, gt-sm, gt-xs'); break;
case 'lg' : this._activateByAlias('gt-md, gt-sm, gt-xs'); break;
case 'md' : this._activateByAlias('gt-sm, gt-xs'); break;
case 'sm' : this._activateByAlias('gt-xs'); break;
case 'xl' :
this._activateByAlias('gt-lg, gt-md, gt-sm, gt-xs');
break;
case 'lg' :
this._activateByAlias('gt-md, gt-sm, gt-xs');
break;
case 'md' :
this._activateByAlias('gt-sm, gt-xs');
break;
case 'sm' :
this._activateByAlias('gt-xs');
break;
}
}
// Activate last since the responsiveActivation is watching *this* mediaQuery
Expand Down Expand Up @@ -195,7 +215,7 @@ export class MockMediaQueryList implements MediaQueryList {
* Notify all listeners that 'matches === TRUE'
*/
activate(): MockMediaQueryList {
if ( !this._isActive ) {
if (!this._isActive) {
this._isActive = true;
this._listeners.forEach((callback) => {
callback(this);
Expand Down
Loading

0 comments on commit 94d4c95

Please sign in to comment.