Skip to content

Commit

Permalink
feat(api): add responsive support for ngClass and ngStyle (#170)
Browse files Browse the repository at this point in the history
* update(flexbox): refactor fxStyle and fxClass code

*  refactor code to `base-adapter.ts`

* update(flexbox): refactor fxStyle and fxClass code

*  refactor code to `base-adapter.ts`

* feat(api): add responsive support for ngClass and ngStyle
  • Loading branch information
joanllenas authored and tinayuangao committed Feb 9, 2017
1 parent e6bc451 commit f57a63d
Show file tree
Hide file tree
Showing 12 changed files with 839 additions and 5 deletions.
32 changes: 32 additions & 0 deletions src/demo-app/app/demo-app/demo-app.css
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,35 @@ md-toolbar .md-toolbar-layout md-toolbar-row {
.demo-content .small-demo:first-child {
margin-top: 40px;
}

md-card-content pre {
font-size: 12px;
white-space: pre-wrap;
}

.fxClass-all {
font-style: italic;
}

.fxClass-xs {
font-size: 10px;
color: blue!important;
}
.fxClass-sm {
font-size: 20px;
color: lightblue!important;
}
.fxClass-md {
font-size: 30px;
color: orange!important;
}
.fxClass-md2 {
font-weight: bold;
}
.fxClass-lg {
font-size: 40px;
color: lightgreen!important;
}
.fxClass-lg2 {
text-shadow: #5c5c5c;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Component } from '@angular/core';
<demo-responsive-flex-directive class="small-demo"> </demo-responsive-flex-directive>
<demo-responsive-flex-order class="small-demo"> </demo-responsive-flex-order>
<demo-responsive-show-hide class="small-demo"> </demo-responsive-show-hide>
<demo-responsive-style class="small-demo"> </demo-responsive-style>
`
})
export class DemosResponsiveLayout { }
Expand All @@ -23,7 +24,7 @@ import {DemoResponsiveLayoutDirection } from "./responsiveLayoutDirections.demo
import {DemoResponsiveShowHide} from "./responsiveShowHide.demo";
import {DemoResponsiveFlexDirectives} from "./responsiveFlexDirective.demo";
import {DemoResponsiveFlexOrder} from "./responsiveFlexOrder.demo";

import {DemoResponsiveStyle} from "./responsiveStyle.demo";

@NgModule({
declarations : [
Expand All @@ -33,7 +34,8 @@ import {DemoResponsiveFlexOrder} from "./responsiveFlexOrder.demo";
DemoResponsiveLayoutDirection,
DemoResponsiveFlexDirectives,
DemoResponsiveFlexOrder,
DemoResponsiveShowHide
DemoResponsiveShowHide,
DemoResponsiveStyle

],
imports : [
Expand Down
110 changes: 110 additions & 0 deletions src/demo-app/app/docs-layout-responsive/responsiveStyle.demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
import { Subscription } from "rxjs/Subscription";
import 'rxjs/add/operator/filter';

import { MediaChange } from "../../../lib/media-query/media-change";
import { ObservableMedia } from "../../../lib/media-query/observable-media-service";

@Component({
selector: 'demo-responsive-style',
styleUrls: [
'../demo-app/material2.css'
],
template: `
<md-card class="card-demo" >
<md-card-title>Responsive Style</md-card-title>
<md-card-subtitle>
Use the fxClass and fxStyle APIs to responsively apply styles to elements:
</md-card-subtitle>
<md-card-content>
<div class="containerX">
<div fxLayout="row" fxFlex class="coloredContainerX box">
<div
fxFlex
class="fxClass-all"
class.xs="fxClass-xs"
[class.sm]="{'fxClass-sm': hasStyle}"
[class.md]="{'fxClass-md': hasStyle, 'fxClass-md2': hasStyle}"
[class.lg]="['fxClass-lg', 'fxClass-lg2']">
{{ activeMediaQueryAlias }}
<md-checkbox
[(ngModel)]="hasStyle"
fxShow="false"
[fxShow.sm]="true"
[fxShow.md]="true">
Activate styles
</md-checkbox>
</div>
</div>
</div>
</md-card-content>
<md-card-content>
<pre>
&lt;div
fxFlex
class="fxClass-all"
class.xs="fxClass-xs"
[class.sm]="&#123;'fxClass-sm': hasStyle&#125;"
[class.md]="&#123;'fxClass-md': hasStyle, 'fxClass-md2': hasStyle&#125;"
[class.lg]="['fxClass-lg', 'fxClass-lg2']"&gt;
&lt;/div&gt;
</pre>
</md-card-content>
<md-card-content>
<div class="containerX">
<div fxLayout="row" fxFlex class="coloredContainerX box">
<div
fxFlex
style="font-style: italic"
[style.xs]="{'font-size.px': 10, color: 'blue'}"
[style.sm]="{'font-size.px': 20, color: 'lightblue'}"
[style.md]="{'font-size.px': 30, color: 'orange'}"
[style.lg]="styleLgExp">
{{ activeMediaQueryAlias }}
</div>
</div>
</div>
</md-card-content>
<md-card-content>
<pre>
&lt;div
style="font-style: italic"
[style.xs]="&#123;'font-size.px': 10, color: 'blue'&#125;"
[style.sm]="&#123;'font-size.px': 20, color: 'lightblue'&#125;"
[style.md]="&#123;'font-size.px': 30, color: 'orange'&#125;"
[style.lg]="styleLgExp"&gt;
&lt;/div&gt;
</pre>
</md-card-content>
<md-card-footer style="width:95%">
<div class="hint" >Active mediaQuery: <span style="padding-left: 20px; color: rgba(0, 0, 0, 0.54)">{{ activeMediaQuery }}</span></div>
</md-card-footer>
</md-card>
`
})
export class DemoResponsiveStyle implements OnDestroy {
private _watcher: Subscription;
public activeMediaQuery = "";
public activeMediaQueryAlias = "";
public hasStyle: boolean = false;
public styleLgExp = {
'font-size': '40px',
color: 'lightgreen'
};

constructor( private _media$:ObservableMedia ) {
this._watcher = this._media$.subscribe((change: MediaChange) => {
this.activeMediaQuery = change ? `'${change.mqAlias}' = ${change.mediaQuery} )` : "";
this.activeMediaQueryAlias = change.mqAlias;
});
}

ngOnDestroy() {
this._watcher.unsubscribe();
}
}
4 changes: 4 additions & 0 deletions src/lib/flexbox/_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {FlexOrderDirective} from './api/flex-order';
import {LayoutAlignDirective} from './api/layout-align';
import {LayoutWrapDirective} from './api/layout-wrap';
import {LayoutGapDirective} from './api/layout-gap';
import {ClassDirective} from './api/class';
import {StyleDirective} from './api/style';

/**
* Since the equivalent results are easily achieved with a css class attached to each
Expand All @@ -44,6 +46,8 @@ const ALL_DIRECTIVES = [
FlexAlignDirective,
ShowDirective,
HideDirective,
ClassDirective,
StyleDirective,
];

/**
Expand Down
49 changes: 49 additions & 0 deletions src/lib/flexbox/api/base-adapter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @license
* Copyright Google Inc. 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} from '@angular/core';
import {BaseFxDirectiveAdapter} from './base-adapter';
import {expect} from '../../utils/testing/custom-matchers';

export class MockElementRef extends ElementRef {
constructor() {
const nEl = document.createElement('DIV');
super(nEl);
this.nativeElement = nEl;
}
}

describe('BaseFxDirectiveAdapter class', () => {
let component;
beforeEach(() => {
component = new BaseFxDirectiveAdapter(null, new MockElementRef(), null);
});
describe('cacheInput', () => {
it('should call _cacheInputArray when source is an array', () => {
spyOn(component, '_cacheInputArray');
component.cacheInput('key', []);
expect(component._cacheInputArray).toHaveBeenCalled();
});
it('should call _cacheInputObject when source is an object', () => {
spyOn(component, '_cacheInputObject');
component.cacheInput('key', {});
expect(component._cacheInputObject).toHaveBeenCalled();
});
it('should call _cacheInputString when source is a string', () => {
spyOn(component, '_cacheInputString');
component.cacheInput('key', '');
expect(component._cacheInputString).toHaveBeenCalled();
});
it('should throw when source is not an object, array or string', () => {
expect(component.cacheInput.bind(null, true)).toThrow();
});
});

});



83 changes: 83 additions & 0 deletions src/lib/flexbox/api/base-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Adapted BaseFxDirective abtract class version so it can be used via composition.
*
* @see BaseFxDirective
*/
import {BaseFxDirective} from './base';
import {ResponsiveActivation} from './../responsive/responsive-activation';
import {MediaQuerySubscriber} from '../../media-query/media-change';

export class BaseFxDirectiveAdapter extends BaseFxDirective {
get inputMap() {
return this._inputMap;
}

/**
* Save the property value.
*/
cacheInput(key?: string, source?: any) {
if (Array.isArray(source)) {
this._cacheInputArray(key, source);
} else if (typeof source === 'object') {
this._cacheInputObject(key, source);
} else if (typeof source === 'string') {
this._cacheInputString(key, source);
} else {
throw new Error('Invalid class value provided');
}
}

_cacheInputRaw(key?: string, source?: any) {
this._inputMap[key] = source;
}

/**
* Save the property value for Array values.
*/
_cacheInputArray(key?: string, source?: boolean[]) {
this._inputMap[key] = source.join(' ');
}

/**
* Save the property value for key/value pair values.
*/
_cacheInputObject(key?: string, source?: {[key: string]: boolean}) {
let classes = [];
for (let prop in source) {
if (!!source[prop]) {
classes.push(prop);
}
}
this._inputMap[key] = classes.join(' ');
}

/**
* Save the property value for string values.
*/
_cacheInputString(key?: string, source?: string) {
this._inputMap[key] = source;
}

/**
* @see BaseFxDirective._listenForMediaQueryChanges
*/
listenForMediaQueryChanges(key: string,
defaultValue: any,
onMediaQueryChange: MediaQuerySubscriber): ResponsiveActivation {
return this._listenForMediaQueryChanges(key, defaultValue, onMediaQueryChange);
}

/**
* @see BaseFxDirective._queryInput
*/
queryInput(key) {
return this._queryInput(key);
}

/**
* @see BaseFxDirective._mqActivation
*/
get mqActivation(): ResponsiveActivation {
return this._mqActivation;
}
}
Loading

0 comments on commit f57a63d

Please sign in to comment.