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

fix(fxLayoutGap): add gaps to dynamic content #124

Merged
merged 1 commit into from
Jan 24, 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
55 changes: 50 additions & 5 deletions src/lib/flexbox/api/layout-gap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '../../utils/testing/helpers';

describe('layout-gap directive', () => {
let fixture: ComponentFixture<any>;
let createTestComponent = makeCreateTestComponent(() => TestLayoutGapComponent);
let expectDomForQuery = makeExpectDOMForQuery(() => TestLayoutGapComponent);

Expand Down Expand Up @@ -61,7 +62,7 @@ describe('layout-gap directive', () => {
<div fxFlex></div>
</div>
`;
let fixture = createTestComponent(template); // tslint:disable-line:no-shadowed-variable
fixture = createTestComponent(template); // tslint:disable-line:no-shadowed-variable
fixture.detectChanges();

let nodes = queryFor(fixture, "[fxFlex]");
Expand All @@ -71,6 +72,49 @@ describe('layout-gap directive', () => {
expect(nodes[2].nativeElement).toHaveCssStyle({'margin-left': '13px'});
});

it('should add gap styles to dynamics rows EXCEPT first', () => {
let template = `
<div fxLayoutAlign="center center" fxLayoutGap="13px">
<div fxFlex *ngFor="let row of rows"></div>
</div>
`;
fixture = createTestComponent(template);
fixture.componentInstance.direction = "row";
fixture.detectChanges();

let nodes = queryFor(fixture, "[fxFlex]");
expect(nodes.length).toEqual(4);
expect(nodes[0].nativeElement).not.toHaveCssStyle({'margin-left': '13px'});
expect(nodes[1].nativeElement).toHaveCssStyle({'margin-left': '13px'});
expect(nodes[2].nativeElement).toHaveCssStyle({'margin-left': '13px'});
expect(nodes[3].nativeElement).toHaveCssStyle({'margin-left': '13px'});
});

it('should add update gap styles when row items are removed', () => {
let nodes,
template = `
<div fxLayoutAlign="center center" fxLayoutGap="13px">
<div fxFlex *ngFor="let row of rows"></div>
</div>
`;
fixture = createTestComponent(template);
fixture.componentInstance.direction = "row";
fixture.detectChanges();

nodes = queryFor(fixture, "[fxFlex]");
expect(nodes.length).toEqual(4);

fixture.componentInstance.rows.shift();
fixture.detectChanges();

nodes = queryFor(fixture, "[fxFlex]");
expect(nodes.length).toEqual(3);

expect(nodes[0].nativeElement).toHaveCssStyle({'margin-left': '0px'});
expect(nodes[1].nativeElement).toHaveCssStyle({'margin-left': '13px'});
expect(nodes[2].nativeElement).toHaveCssStyle({'margin-left': '13px'});
});

it('should apply margin-top for column layouts', () => {
verifyCorrectMargin('column', 'margin-top');
});
Expand All @@ -84,7 +128,7 @@ describe('layout-gap directive', () => {
});

it('should remove obsolete margin and apply valid margin for layout changes', () => {
let fixture: ComponentFixture<any> = createTestComponent(`
fixture = createTestComponent(`
<div [fxLayout]="direction" [fxLayoutGap]="gap">
<span></span>
<span></span>
Expand Down Expand Up @@ -130,10 +174,10 @@ describe('layout-gap directive', () => {
</div>
`;

const fixture = createTestComponent(template);
fixture.detectChanges();
const fixture1 = createTestComponent(template);
fixture1.detectChanges();

const nodes = queryFor(fixture, 'span');
const nodes = queryFor(fixture1, 'span');
expect(nodes[1].nativeElement).toHaveCssStyle({[marginKey]: margin});
}

Expand All @@ -156,6 +200,7 @@ describe('layout-gap directive', () => {
export class TestLayoutGapComponent implements OnInit {
direction = "column";
gap = "8px";
rows = new Array(4);

constructor() {
}
Expand Down
117 changes: 87 additions & 30 deletions src/lib/flexbox/api/layout-gap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ import {LayoutDirective, LAYOUT_VALUES} from './layout';
* 'layout-padding' styling directive
* Defines padding of child elements in a layout container
*/
@Directive({selector: `
@Directive({
selector: `
[fxLayoutGap],
[fxLayoutGap.xs]
[fxLayoutGap.xs],
[fxLayoutGap.gt-xs],
[fxLayoutGap.sm],
[fxLayoutGap.gt-sm]
Expand All @@ -39,28 +40,58 @@ import {LayoutDirective, LAYOUT_VALUES} from './layout';
[fxLayoutGap.lg],
[fxLayoutGap.gt-lg],
[fxLayoutGap.xl]
`})
`
})
export class LayoutGapDirective extends BaseFxDirective implements AfterContentInit, OnChanges,
OnDestroy {
OnDestroy {
private _layout = 'row'; // default flex-direction
private _layoutWatcher: Subscription;
private _observer: MutationObserver;

@Input('fxLayoutGap') set gap(val) { this._cacheInput('gap', val); }
@Input('fxLayoutGap.xs') set gapXs(val) { this._cacheInput('gapXs', val); }
@Input('fxLayoutGap.gt-xs') set gapGtXs(val) { this._cacheInput('gapGtXs', val); };
@Input('fxLayoutGap.sm') set gapSm(val) { this._cacheInput('gapSm', val); };
@Input('fxLayoutGap.gt-sm') set gapGtSm(val) { this._cacheInput('gapGtSm', val); };
@Input('fxLayoutGap.md') set gapMd(val) { this._cacheInput('gapMd', val); };
@Input('fxLayoutGap.gt-md') set gapGtMd(val) { this._cacheInput('gapGtMd', val); };
@Input('fxLayoutGap.lg') set gapLg(val) { this._cacheInput('gapLg', val); };
@Input('fxLayoutGap.gt-lg') set gapGtLg(val) { this._cacheInput('gapGtLg', val); };
@Input('fxLayoutGap.xl') set gapXl(val) { this._cacheInput('gapXl', val); };

constructor(
monitor: MediaMonitor,
elRef: ElementRef,
renderer: Renderer,
@Optional() @Self() container: LayoutDirective) {
@Input('fxLayoutGap') set gap(val) {
this._cacheInput('gap', val);
}

@Input('fxLayoutGap.xs') set gapXs(val) {
this._cacheInput('gapXs', val);
}

@Input('fxLayoutGap.gt-xs') set gapGtXs(val) {
this._cacheInput('gapGtXs', val);
};

@Input('fxLayoutGap.sm') set gapSm(val) {
this._cacheInput('gapSm', val);
};

@Input('fxLayoutGap.gt-sm') set gapGtSm(val) {
this._cacheInput('gapGtSm', val);
};

@Input('fxLayoutGap.md') set gapMd(val) {
this._cacheInput('gapMd', val);
};

@Input('fxLayoutGap.gt-md') set gapGtMd(val) {
this._cacheInput('gapGtMd', val);
};

@Input('fxLayoutGap.lg') set gapLg(val) {
this._cacheInput('gapLg', val);
};

@Input('fxLayoutGap.gt-lg') set gapGtLg(val) {
this._cacheInput('gapGtLg', val);
};

@Input('fxLayoutGap.xl') set gapXl(val) {
this._cacheInput('gapXl', val);
};

constructor(monitor: MediaMonitor,
elRef: ElementRef,
renderer: Renderer,
@Optional() @Self() container: LayoutDirective) {
super(monitor, elRef, renderer);

if (container) { // Subscribe to layout direction changes
Expand All @@ -83,23 +114,43 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
* mql change events to onMediaQueryChange handlers
*/
ngAfterContentInit() {
this._watchContentChanges();
this._listenForMediaQueryChanges('gap', '0', (changes: MediaChange) => {
this._updateWithValue(changes.value);
});
this._updateWithValue();
}

ngOnDestroy() {
super.ngOnDestroy();
if (this._layoutWatcher) {
this._layoutWatcher.unsubscribe();
}
}
super.ngOnDestroy();
if (this._layoutWatcher) {
this._layoutWatcher.unsubscribe();
}
if (this._observer) {
this._observer.disconnect();
}
}

// *********************************************
// Protected methods
// *********************************************

/**
* Watch for child nodes to be added... and apply the layout gap styles to each.
* NOTE: this does NOT! differentiate between viewChildren and contentChildren
*/
private _watchContentChanges() {
let onMutationCallback = (mutations) => {
// update gap styles only for 'addedNodes' events
mutations
.filter((it: MutationRecord) => it.addedNodes && it.addedNodes.length)
.map(() => this._updateWithValue());
};

this._observer = new MutationObserver(onMutationCallback);
this._observer.observe(this._elementRef.nativeElement, {childList: true});
}

/**
* Cache the parent container 'flex-direction' and update the 'margin' styles
*/
Expand All @@ -108,7 +159,6 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
if (!LAYOUT_VALUES.find(x => x === this._layout)) {
this._layout = 'row';
}

this._updateWithValue();
}

Expand All @@ -121,11 +171,18 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
value = this._mqActivation.activatedInput;
}

// For each `element` child, set the padding styles...
// Reset 1st child element to 0px gap
let items = this.childrenNodes
.filter( el => (el.nodeType === 1)) // only Element types
.filter( (el, j) => j > 0 ); // skip first element since gaps are needed
this._applyStyleToElements(this._buildCSS(value), items );
.filter(el => (el.nodeType === 1)) // only Element types
.filter((el, j) => j == 0);
this._applyStyleToElements(this._buildCSS(0), items);

// For each `element` child, set the padding styles...
items = this.childrenNodes
.filter(el => (el.nodeType === 1)) // only Element types
.filter((el, j) => j > 0); // skip first element since gaps are needed
this._applyStyleToElements(this._buildCSS(value), items);

}

/**
Expand Down