Skip to content

Commit

Permalink
feature(media-queries): service & directives (#22)
Browse files Browse the repository at this point in the history
* first draft on media service/directive

* modified comments and little fixes

* media docs/demo (first draft)

* fixed unit test

* update(cards): href for -prod routes

- for link styles

* update(media): change name to Media Queries

- update description
  • Loading branch information
emoralesb05 authored and kyleledbetter committed Jul 19, 2016
1 parent 9712754 commit 341f56e
Show file tree
Hide file tree
Showing 15 changed files with 542 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/app/components/components/components.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export class ComponentsComponent {
icon: 'chrome_reader_mode',
route: 'markdown',
title: 'Markdown',
}, {
description: 'Responsive service & directive',
icon: 'devices',
route: 'media',
title: 'Media Queries',
}, {
description: 'Custom Angular pipes (filters)',
icon: 'filter_list',
Expand Down
4 changes: 4 additions & 0 deletions src/app/components/components/components.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ExpansionPanelDemoComponent } from './expansion-panel';
import { FileUploadDemoComponent } from './file-upload';
import { LoadingDemoComponent } from './loading';
import { MarkdownDemoComponent } from './markdown';
import { MediaDemoComponent } from './media';
import { PipesComponent } from './pipes';

export const componentsRoutes: RouterConfig = [{
Expand All @@ -32,6 +33,9 @@ export const componentsRoutes: RouterConfig = [{
}, {
component: MarkdownDemoComponent,
path: 'markdown',
}, {
component: MediaDemoComponent,
path: 'media',
}, {
component: PipesComponent,
path: 'pipes',
Expand Down
1 change: 1 addition & 0 deletions src/app/components/components/media/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MediaDemoComponent } from './media.component';
134 changes: 134 additions & 0 deletions src/app/components/components/media/media.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<md-card>
<md-card-title>Media Queries</md-card-title>
<md-card-subtitle>Responsive service & directive (for attributes)</md-card-subtitle>
<md-divider></md-divider>
<md-card-content>
<p>Media Queries:</p>
<md-list>
<template let-demo let-last="demo" ngFor [ngForOf]="mediaDemo">
<a md-list-item layout-align="row">
<h3 [tdMediaToggle]="demo.query" [mediaStyles]="{color: 'red'}" [mediaAttributes]="{title: 'It matches!!!'}" md-line> {{demo.query}}</h3>
<p md-line> matches: {{demo.value}} </p>
</a>
<md-divider *ngIf="!last"></md-divider>
</template>
</md-list>
</md-card-content>
</md-card>
<md-card>
<md-card-title>TdMediaService</md-card-title>
<md-card-subtitle>How to use this service</md-card-subtitle>
<md-divider></md-divider>
<md-card-content>
<h2><code>TdMediaService</code></h2>
<p>This service is designed to provide basic media query evaluation for responsive applications.</p>
<p>It has pre-programmed support for media queries that match the layout
<a href="https://material.google.com/layout/responsive-ui.html" target="_blank">breakpoints</a>:
</p>
<md-list>
<template let-bpoint let-last="bpoint" ngFor [ngForOf]="mediaBreakpoints">
<a md-list-item layout-align="row">
<h3 md-line> {{bpoint.breakpoint}}</h3>
<p md-line> {{bpoint.query}} </p>
</a>
<md-divider *ngIf="!last"></md-divider>
</template>
</md-list>
<h3>Methods:</h3>
<p>The <code>TdMediaService</code> service has {{mediaServiceMethods.length}} properties:</p>
<md-list>
<template let-attr let-last="attr" ngFor [ngForOf]="mediaServiceMethods">
<a md-list-item layout-align="row">
<h3 md-line> {{attr.name}}: <span>{{attr.type}}</span></h3>
<p md-line> {{attr.description}} </p>
</a>
<md-divider *ngIf="!last"></md-divider>
</template>
</md-list>
<h3>Example:</h3>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
import { Component, NgZone, OnInit, OnDestroy } from '@angular/core';
import { TdMediaService } from '@covalent/core';
import { Subscription } from 'rxjs/Subscription';
...
providers: [ TdMediaService ]
})
export class Demo implements OnInit, OnDestroy {
isSmallScreen: boolean = false;
private _querySubscription: Subscription;

constructor(private _mediaService: TdMediaService, private _ngZone: NgZone) {
}

checkScreen(): void {
this._ngZone.run(() => {
this.isSmallScreen = this._mediaService.query('sm'); // or '(min-width: 960px) and (max-width: 1279px)'
});
}

watchScreen(): void {
this._querySubscription = this._mediaService.registerQuery('sm').subscribe((matches: boolean) => {
this._ngZone.run(() => {
this.isSmallScreen = matches;
});
});
}

ngOnInit(): void {
this.watchScreen();
}

ngOnDestroy(): void {
this._querySubscription.unsubscribe();
}
}
]]>
</td-highlight>
<p>Note: Always unsubscribe from [Observable] objects when not using them anymore.</p>
<p>A good way of doing it is in the <code>ngOnDestroy</code> component life-cycle hook provided by the [OnDestroy] interface.</p>
</md-card-content>
</md-card>
<md-card>
<md-card-title>TdMediaToggleDirective</md-card-title>
<md-card-subtitle>How to use this directive</md-card-subtitle>
<md-divider></md-divider>
<md-card-content>
<h2><code>tdMediaToggle</code></h2>
<p>Simply add the <code>tdMediaToggle</code> attibute with a "media query" value to the element you want to modify depending on screen size.</p>
<h3>Properties:</h3>
<p>The <code>tdMediaToggle</code> directive has {{mediaAttrs.length}} properties:</p>
<md-list>
<template let-attr let-last="attr" ngFor [ngForOf]="mediaAttrs">
<a md-list-item layout-align="row">
<h3 md-line> {{attr.name}}: <span>{{attr.type}}</span></h3>
<p md-line> {{attr.description}} </p>
</a>
<md-divider *ngIf="!last"></md-divider>
</template>
</md-list>
<h3>Example:</h3>
<p>HTML:</p>
<td-highlight lang="html">
<![CDATA[
<div tdMediaToggle="sm" [mediaClasses]="['classOne', 'classTwo']"
[mediaAttributes]="{title: 'tooltip'}" [mediaStyles]="{color: 'red'}">
...
</div>
]]>
</td-highlight>
<p>Typescript:</p>
<td-highlight lang="typescript">
<![CDATA[
import { TdMediaToggleDirective, TdMediaService } from '@covalent/core';
...
directives: [ TdMediaToggleDirective ],
providers: [ TdMediaService ]
})
export class Demo {
}
]]>
</td-highlight>
</md-card-content>
</md-card>
Empty file.
51 changes: 51 additions & 0 deletions src/app/components/components/media/media.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
beforeEach,
addProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { MediaDemoComponent } from './media.component';
import { TdMediaService } from '../../../../platform/core';

describe('Component: MediaDemo', () => {
let builder: TestComponentBuilder;

beforeEach(() => {
addProviders([
MediaDemoComponent,
TdMediaService,
]);
});

beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder): void {
builder = tcb;
}));

it('should inject the component', inject([MediaDemoComponent], (component: MediaDemoComponent) => {
expect(component).toBeTruthy();
}));

it('should create the component', inject([], () => {
return builder.createAsync(MediaDemoTestControllerComponent)
.then((fixture: ComponentFixture<any>) => {
let query: DebugElement = fixture.debugElement.query(By.directive(MediaDemoComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});

@Component({
directives: [MediaDemoComponent],
selector: 'td-test',
template: `
<td-media-demo></td-media-demo>
`,
})
class MediaDemoTestControllerComponent {
}
164 changes: 164 additions & 0 deletions src/app/components/components/media/media.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { Component, OnInit, NgZone, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';

import { MD_CARD_DIRECTIVES } from '@angular2-material/card';
import { MD_LIST_DIRECTIVES } from '@angular2-material/list';
import { MdButton } from '@angular2-material/button';
import { MD_INPUT_DIRECTIVES } from '@angular2-material/input';

import { TdMediaToggleDirective, TdMediaService } from '../../../../platform/core';
import { TdHighlightComponent } from '../../../../platform/highlight';

@Component({
directives: [
MD_CARD_DIRECTIVES,
MD_LIST_DIRECTIVES,
MdButton,
MD_INPUT_DIRECTIVES,
TdMediaToggleDirective,
TdHighlightComponent,
],
moduleId: module.id,
selector: 'td-media-demo',
styleUrls: [ 'media.component.css' ],
templateUrl: 'media.component.html',
})
export class MediaDemoComponent implements OnInit, OnDestroy {

private _subcriptions: Subscription[] = [];

mediaDemo: any[] = [{
query: 'xs',
value: false,
}, {
query: 'gt-xs',
value: false,
}, {
query: 'sm',
value: false,
}, {
query: 'gt-sm',
value: false,
}, {
query: 'md',
value: false,
}, {
query: 'gt-gm',
value: false,
}, {
query: 'lg',
value: false,
}, {
query: 'gt-lg',
value: false,
}, {
query: 'xl',
value: false,
}, {
query: 'landscape',
value: false,
}, {
query: 'portrait',
value: false,
}, {
query: 'print',
value: false,
}, {
query: '(max-width: 800px)',
value: false,
}, {
query: '(min-width: 700px)',
value: false,
}];

mediaServiceMethods: Object[] = [{
description: `Used to evaluate whether a given media query is true or false given the
current device's screen / window size.`,
name: 'query',
type: 'function(query: string)',
}, {
description: `Registers a media query and returns an [Observable] that will re-evaluate and
return if the given media query matches on window resize.`,
name: 'registerQuery',
type: 'function(query: string)',
}];

mediaBreakpoints: Object[] = [{
breakpoint: 'xs',
query: '(max-width: 599px)',
}, {
breakpoint: 'gt-xs',
query: '(min-width: 600px)',
}, {
breakpoint: 'sm',
query: '(min-width: 600px) and (max-width: 959px)',
}, {
breakpoint: 'gt-sm',
query: '(min-width: 960px)',
}, {
breakpoint: 'md',
query: '(min-width: 960px) and (max-width: 1279px)',
}, {
breakpoint: 'gt-gm',
query: '(min-width: 1280px)',
}, {
breakpoint: 'lg',
query: '(min-width: 1280px) and (max-width: 1919px)',
}, {
breakpoint: 'gt-lg',
query: '(min-width: 1920px)',
}, {
breakpoint: 'xl',
query: '(min-width: 1920px)',
}, {
breakpoint: 'landscape',
query: 'landscape',
}, {
breakpoint: 'portrait',
query: 'portrait',
}, {
breakpoint: 'print',
query: 'print',
}];

mediaAttrs: Object[] = [{
description: `Media query used to evaluate screen/window size.
Toggles attributes, classes and styles if media query is matched.`,
name: 'tdMediaToggle',
type: 'string',
}, {
description: 'Attributes to be toggled when media query matches',
name: 'mediaAttributes?',
type: '{[key: string]: string}',
}, {
description: 'CSS Classes to be toggled when media query matches',
name: 'mediaClasses?',
type: 'string[]',
}, {
description: 'CSS Styles to be toggled when media query matches',
name: 'mediaStyles?',
type: '{[key: string]: string}',
}];

constructor(private _mediaService: TdMediaService, private _ngZone: NgZone) { }

ngOnInit(): void {
for (let demoObj of this.mediaDemo) {
this._ngZone.run(() => {
demoObj.value = this._mediaService.query(demoObj.query);
});
this._subcriptions.push(this._mediaService.registerQuery(demoObj.query).subscribe((matches: boolean) => {
this._ngZone.run(() => {
demoObj.value = matches;
});
}));
}
}

ngOnDestroy(): void {
this._subcriptions.forEach((subs: Subscription) => {
subs.unsubscribe();
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div layout="row" layout-wrap hide-xs hide-sm class="push-sm">
<template let-item let-last="last" ngFor [ngForOf]="items">
<div flex-md="50" flex-gt-md="25" layout="column">
<md-card [routerLink]="[item.route]" class="md-card-colored" flex>
<md-card href [routerLink]="[item.route]" class="md-card-colored" flex>
<md-toolbar class="bgc-{{item.color}} " layout="column" layout-align="center center" layout-padding>
<md-icon class="md-icon-font-xl tc-white">{{item.icon}}</md-icon>
</md-toolbar>
Expand Down
Loading

0 comments on commit 341f56e

Please sign in to comment.