Skip to content

Commit

Permalink
feat(fxLayoutGap): add gutter functionality to layout-gap
Browse files Browse the repository at this point in the history
* Apply negative margin and positive padding as browser-agnostic
  hack for grid gutter
* The new feature is activated by appending `grid` to any
  `fxLayoutGap` input, e.g.
  `fxLayoutGap="10px grid"`
  • Loading branch information
CaerusKaru authored and ThomasBurleson committed Mar 20, 2018
1 parent 3cfafd1 commit 84ca5c3
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Component} from '@angular/core';
template: `
<demo-layout-alignment class="small-demo"></demo-layout-alignment>
<demo-layout-fill class="small-demo"></demo-layout-fill>
<demo-layout-gap class="small-demo"></demo-layout-gap>
<demo-flex-row-fill class="small-demo"></demo-flex-row-fill>
<demo-flex-row-fill-wrap class="small-demo"></demo-flex-row-fill-wrap>
<demo-flex-attribute-values class="small-demo"></demo-flex-attribute-values>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LayoutGapComponent } from './layout-gap.component';

describe('LayoutGapComponent', () => {
let component: LayoutGapComponent;
let fixture: ComponentFixture<LayoutGapComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LayoutGapComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(LayoutGapComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {Component} from '@angular/core';

const DIRECTIONS = ['row', 'row-reverse', 'column', 'column-reverse'];

@Component({
selector: 'demo-layout-gap',
template: `
<mat-card class="card-demo">
<mat-card-title><a href="" target="_blank">Layout Gap</a></mat-card-title>

This comment has been minimized.

Copy link
@julianobrasil

julianobrasil Mar 30, 2018

@CaerusKaru, I know this has already been merged, but... why do we have an anchor tag here? Was the building process supposed to add any useful link? Or is it a placeholder for future use? At the online demo app (https://tburleson-layouts-demos.firebaseapp.com) it opens a new browser tab with the same content of the original one because it points to https://tburleson-layouts-demos.firebaseapp.com.

This comment has been minimized.

Copy link
@CaerusKaru

CaerusKaru Mar 30, 2018

Author Member

Honestly, I just copied existing demo code which also had an empty anchor tag. In reality, all of the demo code will be replaced in the near future with a new demo app, so this isn’t a long-term solution

<mat-card-subtitle>Using 'fxLayoutGap' to create a grid-like layout
</mat-card-subtitle>
<mat-card-content class="large">
<div fxFlexFill>
<div fxFlex
[fxLayout]="direction + ' wrap'"
fxLayoutGap="10px grid"
style="cursor: pointer;"
(click)="toggleDirection()">
<div fxFlex="25">
<div class="one" fxFlexFill fxLayoutAlign="center center">
A
</div>
</div>
<div fxFlex="25">
<div class="two" fxFlexFill fxLayoutAlign="center center">
B
</div>
</div>
<div fxFlex="25">
<div class="three" fxFlexFill fxLayoutAlign="center center">
C
</div>
</div>
<div fxFlex="25">
<div class="four" fxFlexFill fxLayoutAlign="center center">
D
</div>
</div>
<div fxFlex="25">
<div class="five" fxFlexFill fxLayoutAlign="center center">
E
</div>
</div>
<div fxFlex="25">
<div class="six" fxFlexFill fxLayoutAlign="center center">
F
</div>
</div>
<div fxFlex="25">
<div class="seven" fxFlexFill fxLayoutAlign="center center">
G
</div>
</div>
</div>
</div>
</mat-card-content>
<mat-card-footer class="bottomPad">
<div class="hint"></div>
</mat-card-footer>
</mat-card>
`
})
export class LayoutGapComponent {
direction = 'row';

toggleDirection() {
const next = (DIRECTIONS.indexOf(this.direction) + 1 ) % DIRECTIONS.length;
this.direction = DIRECTIONS[next];
}
}
2 changes: 2 additions & 0 deletions src/apps/demo-app/src/app/layout/layout.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {RoutingModule} from './routing.module';
import {
LayoutWithDirectionComponent
} from './layout-with-direction/layout-with-direction.component';
import {LayoutGapComponent} from './layout-gap/layout-gap.component';

@NgModule({
imports: [
Expand All @@ -38,6 +39,7 @@ import {
FlexOffsetValuesComponent,
FlexAlignSelfComponent,
LayoutWithDirectionComponent,
LayoutGapComponent,
]
})
export class DocsLayoutModule {}
56 changes: 51 additions & 5 deletions src/lib/flex/layout-gap/layout-gap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import {SERVER_TOKEN, StyleUtils} from '@angular/flex-layout/core';

import {FlexLayoutModule} from '../../module';
import {customMatchers, expect} from '../../utils/testing/custom-matchers';
import {expectEl, makeCreateTestComponent, queryFor} from '../../utils/testing/helpers';
import {
expectEl,
expectNativeEl,
makeCreateTestComponent,
queryFor,
} from '../../utils/testing/helpers';

describe('layout-gap directive', () => {
let fixture: ComponentFixture<any>;
Expand Down Expand Up @@ -151,7 +156,6 @@ describe('layout-gap directive', () => {
// Since the layoutGap directive detects the *ngFor changes by using a MutationObserver, the
// browser will take up some time, to actually announce the changes to the directive.
// (Kudos to @DevVersion)

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

Expand Down Expand Up @@ -341,13 +345,57 @@ describe('layout-gap directive', () => {
});
});

describe('grid option', () => {
it('should add gap styles correctly', () => {
let template = `
<div fxLayoutGap='13px grid'>
<div fxFlex></div>
<div fxFlex></div>
<div fxFlex></div>
</div>
`;
createTestComponent(template);
fixture.detectChanges();

let nodes = queryFor(fixture, '[fxFlex]');
let expectedMargin = {'margin': '0px -13px -13px 0px'};
let expectedPadding = {'padding': '0px 13px 13px 0px'};
expect(nodes.length).toEqual(3);
expectEl(nodes[0]).toHaveStyle(expectedPadding, styler);
expectEl(nodes[1]).toHaveStyle(expectedPadding, styler);
expectEl(nodes[2]).toHaveStyle(expectedPadding, styler);
expectNativeEl(fixture).toHaveStyle(expectedMargin, styler);
});

it('should add gap styles correctly for rtl', () => {
fakeDocument.body.dir = 'rtl';
let template = `
<div fxLayoutGap='13px grid'>
<div fxFlex></div>
<div fxFlex></div>
<div fxFlex></div>
</div>
`;
createTestComponent(template);
fixture.detectChanges();

let nodes = queryFor(fixture, '[fxFlex]');
let expectedMargin = {'margin': '0px 0px -13px -13px'};
let expectedPadding = {'padding': '0px 0px 13px 13px'};
expect(nodes.length).toEqual(3);
expectEl(nodes[0]).toHaveStyle(expectedPadding, styler);
expectEl(nodes[1]).toHaveStyle(expectedPadding, styler);
expectEl(nodes[2]).toHaveStyle(expectedPadding, styler);
expectNativeEl(fixture).toHaveStyle(expectedMargin, styler);
});
});

});


// *****************************************************************
// Template Component
// *****************************************************************

@Component({
selector: 'test-layout',
template: `<span>PlaceHolder Template HTML</span>`
Expand All @@ -363,5 +411,3 @@ class TestLayoutGapComponent implements OnInit {
ngOnInit() {
}
}


68 changes: 58 additions & 10 deletions src/lib/flex/layout-gap/layout-gap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ import {
NgZone,
} from '@angular/core';
import {Directionality} from '@angular/cdk/bidi';
import {BaseFxDirective, MediaChange, MediaMonitor, StyleUtils} from '@angular/flex-layout/core';
import {
BaseFxDirective,
MediaChange,
MediaMonitor,
StyleDefinition,
StyleUtils
} from '@angular/flex-layout/core';
import {Subscription} from 'rxjs/Subscription';

import {Layout, LayoutDirective} from '../layout/layout';
Expand Down Expand Up @@ -155,9 +161,9 @@ export class LayoutGapDirective extends BaseFxDirective
*
*/
protected _updateWithValue(value?: string) {
value = value || this._queryInput('gap') || '0';
let gapValue = value || this._queryInput('gap') || '0';
if (this._mqActivation) {
value = this._mqActivation.activatedInput;
gapValue = this._mqActivation.activatedInput;
}

// Gather all non-hidden Element nodes
Expand All @@ -174,15 +180,56 @@ export class LayoutGapDirective extends BaseFxDirective
});

if (items.length > 0) {
const lastItem = items.pop();
if (gapValue.endsWith(GRID_SPECIFIER)) {
gapValue = gapValue.substring(0, gapValue.indexOf(GRID_SPECIFIER));
// For each `element` children, set the padding
this._applyStyleToElements(this._buildGridPadding(gapValue), items);

// Add the margin to the host element
this._applyStyleToElement(this._buildGridMargin(gapValue));
} else {
const lastItem = items.pop();

// For each `element` children EXCEPT the last,
// set the margin right/bottom styles...
this._applyStyleToElements(this._buildCSS(gapValue), items);

// Clear all gaps for all visible elements
this._applyStyleToElements(this._buildCSS(), [lastItem]);
}
}
}

/**
*
*/
private _buildGridPadding(value: string): StyleDefinition {
let paddingTop = '0px', paddingRight = '0px', paddingBottom = value, paddingLeft = '0px';

// For each `element` children EXCEPT the last,
// set the margin right/bottom styles...
this._applyStyleToElements(this._buildCSS(value), items);
if (this._directionality.value === 'rtl') {
paddingLeft = value;
} else {
paddingRight = value;
}

return {'padding': `${paddingTop} ${paddingRight} ${paddingBottom} ${paddingLeft}`};
}

// Clear all gaps for all visible elements
this._applyStyleToElements(this._buildCSS(), [lastItem]);
/**
* Prepare margin CSS, remove any previous explicitly
* assigned margin assignments
* Note: this will not work with calc values (negative calc values are invalid)
*/
private _buildGridMargin(value: string): StyleDefinition {
let marginTop = '0px', marginRight = '0px', marginBottom = '-' + value, marginLeft = '0px';

if (this._directionality.value === 'rtl') {
marginLeft = '-' + value;
} else {
marginRight = '-' + value;
}

return {'margin': `${marginTop} ${marginRight} ${marginBottom} ${marginLeft}`};
}

/**
Expand Down Expand Up @@ -212,5 +259,6 @@ export class LayoutGapDirective extends BaseFxDirective

return margins;
}

}

const GRID_SPECIFIER = ' grid';

0 comments on commit 84ca5c3

Please sign in to comment.