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

fix(lib, media-query): support angular/universal #353

Merged
merged 1 commit into from
Aug 4, 2017
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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
include:
- env: "MODE=lint"
- env: "MODE=aot"
- env: "MODE=prerender"
- env: "MODE=closure-compiler"
- env: "MODE=saucelabs_required"
- env: "MODE=browserstack_required"
Expand Down
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@
"url": "git+https://github.com/angular/flex-layout.git"
},
"scripts": {
"api": "gulp api-docs",
"build": "gulp :publish:build-releases",
"closure": "./scripts/closure-compiler/build-devapp-bundle.sh",
"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",
"universal:test": "gulp ci:prerender"
},
"version": "2.0.0-beta.8",
"license": "MIT",
Expand Down
2 changes: 2 additions & 0 deletions scripts/ci/travis-testing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ elif is_aot; then
$(npm bin)/gulp ci:aot
elif is_unit; then
$(npm bin)/gulp ci:test
elif is_prerender; then
$(npm bin)/gulp ci:prerender
elif is_closure_compiler; then
./scripts/closure-compiler/build-devapp-bundle.sh
fi
Expand Down
2 changes: 1 addition & 1 deletion scripts/closure-compiler/build-devapp-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set -e -o pipefail
cd $(dirname $0)/../..


# Build a release of material and of the CDK package.
# Build a release of Flex-Layout library
$(npm bin)/gulp flex-layout:build-release:clean

# Build demo-app with ES2015 modules. Closure compiler is then able to parse imports.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {Directive, ElementRef, Output} from '@angular/core';
import {Directive, ElementRef, Inject, Output} from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';

import {Observable} from 'rxjs/Observable';

import 'rxjs/add/observable/fromEvent';
Expand All @@ -17,14 +19,13 @@ export class SplitHandleDirective {

@Output() drag: Observable<{ x: number, y: number }>;

constructor(ref: ElementRef) {
constructor(ref: ElementRef, @Inject(DOCUMENT) _document: any) {
const fromEvent = Observable.fromEvent;
const getMouseEventPosition = (event: MouseEvent) => ({x: event.movementX, y: event.movementY});

const mouseup$ = Observable.fromEvent(document, 'mouseup');
const mousemove$ = Observable.fromEvent(document, 'mousemove')
.map(getMouseEventPosition);
const mousedown$ = Observable.fromEvent(ref.nativeElement, 'mousedown')
.map(getMouseEventPosition);
const mousedown$ = fromEvent(ref.nativeElement, 'mousedown').map(getMouseEventPosition);
const mousemove$ = fromEvent(_document, 'mousemove').map(getMouseEventPosition);
const mouseup$ = fromEvent(_document, 'mouseup').map(getMouseEventPosition);

this.drag = mousedown$.switchMap(_ => mousemove$.takeUntil(mouseup$));
}
Expand Down
44 changes: 29 additions & 15 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,27 +118,40 @@ 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 = this._lookupStyle(element, 'display');

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

protected _getFlowDirection(target: any, addIfMissing = false): string {
let value = '';
let value = 'row';

if (target) {
let directionKeys = Object.keys(applyCssPrefixes({'flex-direction': ''}));
let findDirection = (styles) => directionKeys.reduce((direction, key) => {
return direction || styles[key];
}, null);

let immediateValue = findDirection(target.style);
value = immediateValue || findDirection(getComputedStyle(target as Element));
if (!immediateValue && addIfMissing) {
value = value || 'row';
value = this._lookupStyle(target, 'flex-direction') || 'row';

let hasInlineValue = getDom().getStyle(target, 'flex-direction');
if (!hasInlineValue && addIfMissing) {
this._applyStyleToElements(buildLayoutCSS(value), [target]);
}
}

return value ? value.trim() : 'row';
return value.trim();
}

/**
* Determine the inline or inherited CSS style
*/
protected _lookupStyle(element: HTMLElement, styleName: string): any {
let value = '';
try {
if (element) {
let immediateValue = getDom().getStyle(element, styleName);
value = immediateValue || getDom().getComputedStyle(element).display;
}
} catch (e) {
// TODO: platform-server throws an exception for getComputedStyle
}
return value;
}

/**
Expand Down Expand Up @@ -220,11 +234,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
37 changes: 24 additions & 13 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,8 +116,9 @@ 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;
protected _buildMQL(query: string): MediaQueryList {
let canListen = isBrowser() && !!(<any>window).matchMedia('all').addListener;

return canListen ? (<any>window).matchMedia(query) : <MediaQueryList>{
matches: query === 'all' || query === '',
media: query,
Expand All @@ -127,6 +130,13 @@ export class MatchMedia {
}
}

/**
* Determine if SSR or Browser rendering.
*/
export function isBrowser() {
return getDom().supportsDOMEvents();
}

/**
* Private global registry for all dynamically-created, injected style tags
* @see prepare(query)
Expand All @@ -140,27 +150,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