diff --git a/src/components/grid-list/grid-list.scss b/src/components/grid-list/grid-list.scss index d2372096a135..f0024520cad3 100644 --- a/src/components/grid-list/grid-list.scss +++ b/src/components/grid-list/grid-list.scss @@ -1,4 +1,15 @@ -// TODO(kara): Review this to see if MD spec updates are needed +@import "list-shared"; + +/* height of tile header or footer if it has one line */ +$md-grid-list-one-line-height: 48px; +/* height of tile header or footer if it has two lines */ +$md-grid-list-two-line-height: 68px; +/* side padding for text in tile headers and footers */ +$md-grid-list-text-padding: 16px; + +/* font styles for text in tile headers and footers */ +$md-grid-list-font-size: 16px; +$md-grid-list-secondary-font: 12px; md-grid-list { display: block; @@ -29,32 +40,30 @@ md-grid-tile { // Headers & footers md-grid-tile-header, md-grid-tile-footer { + @include md-line-base($md-grid-list-secondary-font); + @include md-normalize-text(); + display: flex; - flex-direction: row; align-items: center; - height: 48px; + height: $md-grid-list-one-line-height; color: #fff; background: rgba(0, 0, 0, 0.18); overflow: hidden; + padding: 0 $md-grid-list-text-padding; + font-size: $md-grid-list-font-size; // Positioning position: absolute; left: 0; right: 0; - h3, - h4 { - font-weight: 400; - margin: 0 0 0 16px; - } - - h3 { - font-size: 14px; + &.md-2-line { + height: $md-grid-list-two-line-height; } + } - h4 { - font-size: 12px; - } + .md-grid-list-text { + @include md-line-wrapper-base(); } md-grid-tile-header { @@ -64,4 +73,17 @@ md-grid-tile { md-grid-tile-footer { bottom: 0; } + + [md-grid-avatar] { + padding-right: $md-grid-list-text-padding; + + [dir='rtl'] & { + padding-right: 0; + padding-left: $md-grid-list-text-padding; + } + + &:empty { + display:none; + } + } } diff --git a/src/components/grid-list/grid-list.spec.ts b/src/components/grid-list/grid-list.spec.ts index 55fe5936478c..481369193388 100644 --- a/src/components/grid-list/grid-list.spec.ts +++ b/src/components/grid-list/grid-list.spec.ts @@ -12,7 +12,7 @@ import {Component} from '@angular/core'; import {By} from '@angular/platform-browser'; import {MD_GRID_LIST_DIRECTIVES, MdGridList} from './grid-list'; -import {MdGridTile} from './grid-tile'; +import {MdGridTile, MdGridTileText} from './grid-tile'; describe('MdGridList', () => { let builder: TestComponentBuilder; @@ -374,6 +374,48 @@ describe('MdGridList', () => { }); }); }); + + it('should add not add any classes to footers without lines', () => { + var template = ` + + + + I'm a footer! + + + + `; + + return builder.overrideTemplate(TestGridList, template) + .createAsync(TestGridList).then((fixture: ComponentFixture) => { + fixture.detectChanges(); + + let footer = fixture.debugElement.query(By.directive(MdGridTileText)); + expect(footer.nativeElement.classList.contains('md-2-line')).toBe(false); + }); + }); + + it('should add class to footers with two lines', () => { + var template = ` + + + +

First line

+ Second line +
+
+
+ `; + + return builder.overrideTemplate(TestGridList, template) + .createAsync(TestGridList).then((fixture: ComponentFixture) => { + fixture.detectChanges(); + + let footer = fixture.debugElement.query(By.directive(MdGridTileText)); + expect(footer.nativeElement.classList.contains('md-2-line')).toBe(true); + }); + }); + }); @Component({ diff --git a/src/components/grid-list/grid-list.ts b/src/components/grid-list/grid-list.ts index cecf34e6a050..e1b7c65804a8 100644 --- a/src/components/grid-list/grid-list.ts +++ b/src/components/grid-list/grid-list.ts @@ -10,7 +10,7 @@ import { ElementRef, Optional } from '@angular/core'; -import {MdGridTile} from './grid-tile'; +import {MdGridTile, MdGridTileText} from './grid-tile'; import {TileCoordinator} from './tile-coordinator'; import { TileStyler, @@ -20,6 +20,7 @@ import { } from './tile-styler'; import {MdGridListColsError} from './grid-list-errors'; import {Dir} from '@angular2-material/core/rtl/dir'; +import {MdLine} from '@angular2-material/core/line/line'; // TODO(kara): Conditional (responsive) column count / row size. // TODO(kara): Re-layout on window resize / media change (debounced). @@ -167,4 +168,4 @@ export function coerceToNumber(value: string | number): number { return typeof value === 'string' ? parseInt(value, 10) : value; } -export const MD_GRID_LIST_DIRECTIVES: any[] = [MdGridList, MdGridTile]; +export const MD_GRID_LIST_DIRECTIVES: any[] = [MdGridList, MdGridTile, MdLine, MdGridTileText]; diff --git a/src/components/grid-list/grid-tile-text.html b/src/components/grid-list/grid-tile-text.html new file mode 100644 index 000000000000..9946619e7f13 --- /dev/null +++ b/src/components/grid-list/grid-tile-text.html @@ -0,0 +1,3 @@ + +
+ \ No newline at end of file diff --git a/src/components/grid-list/grid-tile.ts b/src/components/grid-list/grid-tile.ts index 3e3088f8d2c9..a709d85f8b40 100644 --- a/src/components/grid-list/grid-tile.ts +++ b/src/components/grid-list/grid-tile.ts @@ -4,9 +4,12 @@ import { Renderer, ElementRef, Input, + ContentChildren, + QueryList, + AfterContentInit } from '@angular/core'; - -import {coerceToNumber} from './grid-list'; +import { coerceToNumber } from './grid-list'; +import { MdLine, MdLineSetter } from '@angular2-material/core/line/line'; @Component({ moduleId: module.id, @@ -19,11 +22,8 @@ import {coerceToNumber} from './grid-list'; export class MdGridTile { _rowspan: number = 1; _colspan: number = 1; - _element: HTMLElement; - constructor(private _renderer: Renderer, element: ElementRef) { - this._element = element.nativeElement; - } + constructor(private _renderer: Renderer, private _element: ElementRef) {} @Input() get rowspan() { @@ -48,7 +48,28 @@ export class MdGridTile { * @internal */ setStyle(property: string, value: string): void { - this._renderer.setElementStyle(this._element, property, value); + this._renderer.setElementStyle(this._element.nativeElement, property, value); } } + +@Component({ + moduleId: module.id, + selector: 'md-grid-tile-header, md-grid-tile-footer', + templateUrl: 'grid-tile-text.html' +}) +export class MdGridTileText implements AfterContentInit { + /** + * Helper that watches the number of lines in a text area and sets + * a class on the host element that matches the line count. + */ + _lineSetter: MdLineSetter; + @ContentChildren(MdLine) _lines: QueryList; + + constructor(private _renderer: Renderer, private _element: ElementRef) {} + + ngAfterContentInit() { + this._lineSetter = new MdLineSetter(this._lines, this._renderer, this._element); + } +} + diff --git a/src/components/list/list.scss b/src/components/list/list.scss index 9739e9caf597..c93d7c05968a 100644 --- a/src/components/list/list.scss +++ b/src/components/list/list.scss @@ -1,5 +1,6 @@ @import "variables"; @import "default-theme"; +@import "list-shared"; $md-list-side-padding: 16px; $md-list-avatar-size: 40px; @@ -56,27 +57,12 @@ based on whether the list is in dense mode. } .md-list-text { - display: flex; - flex-direction: column; - width: 100%; + @include md-line-wrapper-base(); padding: 0 $md-list-side-padding; - box-sizing: border-box; - overflow: hidden; &:first-child { padding: 0; } - - &:empty { - display: none; - } - - & > * { - margin: 0; - padding: 0; - font-weight: normal; - font-size: inherit; - } } [md-list-avatar] { @@ -93,26 +79,6 @@ based on whether the list is in dense mode. } } -/* -This mixin provides all md-line styles, changing secondary font size -based on whether the list is in dense mode. -*/ -@mixin md-line-base($secondary-font-size) { - - [md-line] { - display: block; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; - box-sizing: border-box; - - // all lines but the top line should have smaller text - &:nth-child(n+2) { - font-size: $secondary-font-size; - } - } -} - /* This mixin provides all subheader styles, adjusting heights and padding based on whether the list is in dense mode. diff --git a/src/components/list/list.ts b/src/components/list/list.ts index face45060bf8..2e1af7513e60 100644 --- a/src/components/list/list.ts +++ b/src/components/list/list.ts @@ -9,8 +9,7 @@ import { Renderer, AfterContentInit, } from '@angular/core'; - - +import { MdLine, MdLineSetter } from '../../core/line/line'; @Component({ moduleId: module.id, @@ -22,10 +21,6 @@ import { }) export class MdList {} -/* Need directive for a ContentChildren query in list-item */ -@Directive({ selector: '[md-line]' }) -export class MdLine {} - /* Need directive for a ContentChild query in list-item */ @Directive({ selector: '[md-list-avatar]' }) export class MdListAvatar {} @@ -42,27 +37,25 @@ export class MdListAvatar {} encapsulation: ViewEncapsulation.None }) export class MdListItem implements AfterContentInit { - @ContentChildren(MdLine) _lines: QueryList; - /** @internal */ hasFocus: boolean = false; - /** TODO: internal */ - ngAfterContentInit() { - this._setLineClass(this._lines.length); + _lineSetter: MdLineSetter; - this._lines.changes.subscribe(() => { - this._setLineClass(this._lines.length); - }); - } + @ContentChildren(MdLine) _lines: QueryList; @ContentChild(MdListAvatar) private set _hasAvatar(avatar: MdListAvatar) { - this._setClass('md-list-avatar', avatar != null); + this._renderer.setElementClass(this._element.nativeElement, 'md-list-avatar', avatar != null); } constructor(private _renderer: Renderer, private _element: ElementRef) {} + /** TODO: internal */ + ngAfterContentInit() { + this._lineSetter = new MdLineSetter(this._lines, this._renderer, this._element); + } + /** @internal */ handleFocus() { this.hasFocus = true; @@ -72,22 +65,6 @@ export class MdListItem implements AfterContentInit { handleBlur() { this.hasFocus = false; } - - private _setLineClass(count: number): void { - this._resetClasses(); - if (count === 2 || count === 3) { - this._setClass(`md-${count}-line`, true); - } - } - - private _resetClasses(): void { - this._setClass('md-2-line', false); - this._setClass('md-3-line', false); - } - - private _setClass(className: string, bool: boolean): void { - this._renderer.setElementClass(this._element.nativeElement, className, bool); - } } export const MD_LIST_DIRECTIVES = [MdList, MdListItem, MdLine, MdListAvatar]; diff --git a/src/core/line/line.ts b/src/core/line/line.ts new file mode 100644 index 000000000000..90935c125cab --- /dev/null +++ b/src/core/line/line.ts @@ -0,0 +1,44 @@ +import { + Directive, + Renderer, + ElementRef, + QueryList +} from '@angular/core'; + +/** + * Shared directive to count lines inside a text area, such as a list item. + * Line elements can be extracted with a @ContentChildren(MdLine) query, then + * counted by checking the query list's length. + */ +@Directive({ selector: '[md-line]' }) +export class MdLine {} + +/* Helper that takes a query list of lines and sets the correct class on the host */ +export class MdLineSetter { + constructor(private _lines: QueryList, private _renderer: Renderer, + private _element: ElementRef) { + this._setLineClass(this._lines.length); + + this._lines.changes.subscribe(() => { + this._setLineClass(this._lines.length); + }); + } + + + private _setLineClass(count: number): void { + this._resetClasses(); + if (count === 2 || count === 3) { + this._setClass(`md-${count}-line`, true); + } + } + + private _resetClasses(): void { + this._setClass('md-2-line', false); + this._setClass('md-3-line', false); + } + + private _setClass(className: string, bool: boolean): void { + this._renderer.setElementClass(this._element.nativeElement, className, bool); + } + +} diff --git a/src/core/style/_list-shared.scss b/src/core/style/_list-shared.scss new file mode 100644 index 000000000000..4de8b7edfe87 --- /dev/null +++ b/src/core/style/_list-shared.scss @@ -0,0 +1,50 @@ +/** + * This mixin provides all md-line styles, changing secondary font size + * based on whether the list is in dense mode. + */ +@mixin md-line-base($secondary-font-size) { + [md-line] { + display: block; + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + box-sizing: border-box; + + // all lines but the top line should have smaller text + &:nth-child(n+2) { + font-size: $secondary-font-size; + } + } +} + +/** + * This mixin provides base styles for the wrapper around md-line + * elements in a list. + */ +@mixin md-line-wrapper-base() { + @include md-normalize-text(); + + display: flex; + flex-direction: column; + width: 100%; + box-sizing: border-box; + overflow: hidden; + + // Must remove wrapper when lines are empty or it takes up horizontal + // space and pushes other elements to the right. + &:empty { + display: none; + } +} + +/** + * This mixin normalizes default element styles, e.g. font weight for heading text. + */ +@mixin md-normalize-text() { + & > * { + margin: 0; + padding: 0; + font-weight: normal; + font-size: inherit; + } +} \ No newline at end of file diff --git a/src/demo-app/grid-list/grid-list-demo.html b/src/demo-app/grid-list/grid-list-demo.html index 1ddfd2705072..6a915d77f7d1 100644 --- a/src/demo-app/grid-list/grid-list-demo.html +++ b/src/demo-app/grid-list/grid-list-demo.html @@ -57,5 +57,35 @@

Change gutter:

+ + + Grid list with header + + + + + info_outline + {{dog.name}} + + + + + + + + Grid list with footer + + + + + +

{{dog.name}}

+ Human: {{dog.human}} + star_border +
+
+
+
+
diff --git a/src/demo-app/grid-list/grid-list-demo.scss b/src/demo-app/grid-list/grid-list-demo.scss index 4e6856e6a191..607acef66a07 100644 --- a/src/demo-app/grid-list/grid-list-demo.scss +++ b/src/demo-app/grid-list/grid-list-demo.scss @@ -1,5 +1,5 @@ .demo-grid-list { - width: 800px; + width: 1100px; md-card { margin: 16px 0; @@ -12,4 +12,8 @@ .demo-basic-list md-grid-tile { background: rgba(0,0,0,0.32); } + + img { + width: 350px; + } } diff --git a/src/demo-app/grid-list/grid-list-demo.ts b/src/demo-app/grid-list/grid-list-demo.ts index 095d44c3cc9b..bb1ead3ad70b 100644 --- a/src/demo-app/grid-list/grid-list-demo.ts +++ b/src/demo-app/grid-list/grid-list-demo.ts @@ -2,13 +2,15 @@ import {Component} from '@angular/core'; import {MD_GRID_LIST_DIRECTIVES} from '@angular2-material/grid-list/grid-list'; import {MdButton} from '@angular2-material/button/button'; import {MD_CARD_DIRECTIVES} from '@angular2-material/card/card'; +import {MdIcon, MdIconRegistry} from '@angular2-material/icon/icon'; @Component({ moduleId: module.id, selector: 'grid-list-demo', templateUrl: 'grid-list-demo.html', styleUrls: ['grid-list-demo.css'], - directives: [MD_GRID_LIST_DIRECTIVES, MdButton, MD_CARD_DIRECTIVES] + directives: [MD_GRID_LIST_DIRECTIVES, MdButton, MD_CARD_DIRECTIVES, MdIcon], + providers: [MdIconRegistry] }) export class GridListDemo { tiles: any[] = [ @@ -18,6 +20,15 @@ export class GridListDemo { {text: 'Four', cols: 2, rows: 1, color: '#DDBDF1'}, ]; + dogs: Object[] = [ + { name: 'Porter', human: 'Kara' }, + { name: 'Mal', human: 'Jeremy' }, + { name: 'Koby', human: 'Igor' }, + { name: 'Razzle', human: 'Ward' }, + { name: 'Molly', human: 'Rob' }, + { name: 'Husi', human: 'Matias' }, + ]; + fixedCols: number = 4; fixedRowHeight: number = 100; ratioGutter: number = 1;