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

Commit 84ca5c3

Browse files
CaerusKaruThomasBurleson
authored andcommitted
feat(fxLayoutGap): add gutter functionality to layout-gap
* 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"`
1 parent 3cfafd1 commit 84ca5c3

File tree

6 files changed

+207
-15
lines changed

6 files changed

+207
-15
lines changed

src/apps/demo-app/src/app/layout/docs-layout/docs-layout.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {Component} from '@angular/core';
55
template: `
66
<demo-layout-alignment class="small-demo"></demo-layout-alignment>
77
<demo-layout-fill class="small-demo"></demo-layout-fill>
8+
<demo-layout-gap class="small-demo"></demo-layout-gap>
89
<demo-flex-row-fill class="small-demo"></demo-flex-row-fill>
910
<demo-flex-row-fill-wrap class="small-demo"></demo-flex-row-fill-wrap>
1011
<demo-flex-attribute-values class="small-demo"></demo-flex-attribute-values>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { LayoutGapComponent } from './layout-gap.component';
4+
5+
describe('LayoutGapComponent', () => {
6+
let component: LayoutGapComponent;
7+
let fixture: ComponentFixture<LayoutGapComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ LayoutGapComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(LayoutGapComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {Component} from '@angular/core';
2+
3+
const DIRECTIONS = ['row', 'row-reverse', 'column', 'column-reverse'];
4+
5+
@Component({
6+
selector: 'demo-layout-gap',
7+
template: `
8+
<mat-card class="card-demo">
9+
<mat-card-title><a href="" target="_blank">Layout Gap</a></mat-card-title>
10+
<mat-card-subtitle>Using 'fxLayoutGap' to create a grid-like layout
11+
</mat-card-subtitle>
12+
<mat-card-content class="large">
13+
<div fxFlexFill>
14+
<div fxFlex
15+
[fxLayout]="direction + ' wrap'"
16+
fxLayoutGap="10px grid"
17+
style="cursor: pointer;"
18+
(click)="toggleDirection()">
19+
<div fxFlex="25">
20+
<div class="one" fxFlexFill fxLayoutAlign="center center">
21+
A
22+
</div>
23+
</div>
24+
<div fxFlex="25">
25+
<div class="two" fxFlexFill fxLayoutAlign="center center">
26+
B
27+
</div>
28+
</div>
29+
<div fxFlex="25">
30+
<div class="three" fxFlexFill fxLayoutAlign="center center">
31+
C
32+
</div>
33+
</div>
34+
<div fxFlex="25">
35+
<div class="four" fxFlexFill fxLayoutAlign="center center">
36+
D
37+
</div>
38+
</div>
39+
<div fxFlex="25">
40+
<div class="five" fxFlexFill fxLayoutAlign="center center">
41+
E
42+
</div>
43+
</div>
44+
<div fxFlex="25">
45+
<div class="six" fxFlexFill fxLayoutAlign="center center">
46+
F
47+
</div>
48+
</div>
49+
<div fxFlex="25">
50+
<div class="seven" fxFlexFill fxLayoutAlign="center center">
51+
G
52+
</div>
53+
</div>
54+
</div>
55+
</div>
56+
</mat-card-content>
57+
<mat-card-footer class="bottomPad">
58+
<div class="hint"></div>
59+
</mat-card-footer>
60+
</mat-card>
61+
`
62+
})
63+
export class LayoutGapComponent {
64+
direction = 'row';
65+
66+
toggleDirection() {
67+
const next = (DIRECTIONS.indexOf(this.direction) + 1 ) % DIRECTIONS.length;
68+
this.direction = DIRECTIONS[next];
69+
}
70+
}

src/apps/demo-app/src/app/layout/layout.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {RoutingModule} from './routing.module';
1818
import {
1919
LayoutWithDirectionComponent
2020
} from './layout-with-direction/layout-with-direction.component';
21+
import {LayoutGapComponent} from './layout-gap/layout-gap.component';
2122

2223
@NgModule({
2324
imports: [
@@ -38,6 +39,7 @@ import {
3839
FlexOffsetValuesComponent,
3940
FlexAlignSelfComponent,
4041
LayoutWithDirectionComponent,
42+
LayoutGapComponent,
4143
]
4244
})
4345
export class DocsLayoutModule {}

src/lib/flex/layout-gap/layout-gap.spec.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import {SERVER_TOKEN, StyleUtils} from '@angular/flex-layout/core';
1313

1414
import {FlexLayoutModule} from '../../module';
1515
import {customMatchers, expect} from '../../utils/testing/custom-matchers';
16-
import {expectEl, makeCreateTestComponent, queryFor} from '../../utils/testing/helpers';
16+
import {
17+
expectEl,
18+
expectNativeEl,
19+
makeCreateTestComponent,
20+
queryFor,
21+
} from '../../utils/testing/helpers';
1722

1823
describe('layout-gap directive', () => {
1924
let fixture: ComponentFixture<any>;
@@ -151,7 +156,6 @@ describe('layout-gap directive', () => {
151156
// Since the layoutGap directive detects the *ngFor changes by using a MutationObserver, the
152157
// browser will take up some time, to actually announce the changes to the directive.
153158
// (Kudos to @DevVersion)
154-
155159
nodes = queryFor(fixture, '[fxFlex]');
156160
expect(nodes.length).toEqual(3);
157161

@@ -341,13 +345,57 @@ describe('layout-gap directive', () => {
341345
});
342346
});
343347

348+
describe('grid option', () => {
349+
it('should add gap styles correctly', () => {
350+
let template = `
351+
<div fxLayoutGap='13px grid'>
352+
<div fxFlex></div>
353+
<div fxFlex></div>
354+
<div fxFlex></div>
355+
</div>
356+
`;
357+
createTestComponent(template);
358+
fixture.detectChanges();
359+
360+
let nodes = queryFor(fixture, '[fxFlex]');
361+
let expectedMargin = {'margin': '0px -13px -13px 0px'};
362+
let expectedPadding = {'padding': '0px 13px 13px 0px'};
363+
expect(nodes.length).toEqual(3);
364+
expectEl(nodes[0]).toHaveStyle(expectedPadding, styler);
365+
expectEl(nodes[1]).toHaveStyle(expectedPadding, styler);
366+
expectEl(nodes[2]).toHaveStyle(expectedPadding, styler);
367+
expectNativeEl(fixture).toHaveStyle(expectedMargin, styler);
368+
});
369+
370+
it('should add gap styles correctly for rtl', () => {
371+
fakeDocument.body.dir = 'rtl';
372+
let template = `
373+
<div fxLayoutGap='13px grid'>
374+
<div fxFlex></div>
375+
<div fxFlex></div>
376+
<div fxFlex></div>
377+
</div>
378+
`;
379+
createTestComponent(template);
380+
fixture.detectChanges();
381+
382+
let nodes = queryFor(fixture, '[fxFlex]');
383+
let expectedMargin = {'margin': '0px 0px -13px -13px'};
384+
let expectedPadding = {'padding': '0px 0px 13px 13px'};
385+
expect(nodes.length).toEqual(3);
386+
expectEl(nodes[0]).toHaveStyle(expectedPadding, styler);
387+
expectEl(nodes[1]).toHaveStyle(expectedPadding, styler);
388+
expectEl(nodes[2]).toHaveStyle(expectedPadding, styler);
389+
expectNativeEl(fixture).toHaveStyle(expectedMargin, styler);
390+
});
391+
});
392+
344393
});
345394

346395

347396
// *****************************************************************
348397
// Template Component
349398
// *****************************************************************
350-
351399
@Component({
352400
selector: 'test-layout',
353401
template: `<span>PlaceHolder Template HTML</span>`
@@ -363,5 +411,3 @@ class TestLayoutGapComponent implements OnInit {
363411
ngOnInit() {
364412
}
365413
}
366-
367-

src/lib/flex/layout-gap/layout-gap.ts

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ import {
1818
NgZone,
1919
} from '@angular/core';
2020
import {Directionality} from '@angular/cdk/bidi';
21-
import {BaseFxDirective, MediaChange, MediaMonitor, StyleUtils} from '@angular/flex-layout/core';
21+
import {
22+
BaseFxDirective,
23+
MediaChange,
24+
MediaMonitor,
25+
StyleDefinition,
26+
StyleUtils
27+
} from '@angular/flex-layout/core';
2228
import {Subscription} from 'rxjs/Subscription';
2329

2430
import {Layout, LayoutDirective} from '../layout/layout';
@@ -155,9 +161,9 @@ export class LayoutGapDirective extends BaseFxDirective
155161
*
156162
*/
157163
protected _updateWithValue(value?: string) {
158-
value = value || this._queryInput('gap') || '0';
164+
let gapValue = value || this._queryInput('gap') || '0';
159165
if (this._mqActivation) {
160-
value = this._mqActivation.activatedInput;
166+
gapValue = this._mqActivation.activatedInput;
161167
}
162168

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

176182
if (items.length > 0) {
177-
const lastItem = items.pop();
183+
if (gapValue.endsWith(GRID_SPECIFIER)) {
184+
gapValue = gapValue.substring(0, gapValue.indexOf(GRID_SPECIFIER));
185+
// For each `element` children, set the padding
186+
this._applyStyleToElements(this._buildGridPadding(gapValue), items);
187+
188+
// Add the margin to the host element
189+
this._applyStyleToElement(this._buildGridMargin(gapValue));
190+
} else {
191+
const lastItem = items.pop();
192+
193+
// For each `element` children EXCEPT the last,
194+
// set the margin right/bottom styles...
195+
this._applyStyleToElements(this._buildCSS(gapValue), items);
196+
197+
// Clear all gaps for all visible elements
198+
this._applyStyleToElements(this._buildCSS(), [lastItem]);
199+
}
200+
}
201+
}
202+
203+
/**
204+
*
205+
*/
206+
private _buildGridPadding(value: string): StyleDefinition {
207+
let paddingTop = '0px', paddingRight = '0px', paddingBottom = value, paddingLeft = '0px';
178208

179-
// For each `element` children EXCEPT the last,
180-
// set the margin right/bottom styles...
181-
this._applyStyleToElements(this._buildCSS(value), items);
209+
if (this._directionality.value === 'rtl') {
210+
paddingLeft = value;
211+
} else {
212+
paddingRight = value;
213+
}
214+
215+
return {'padding': `${paddingTop} ${paddingRight} ${paddingBottom} ${paddingLeft}`};
216+
}
182217

183-
// Clear all gaps for all visible elements
184-
this._applyStyleToElements(this._buildCSS(), [lastItem]);
218+
/**
219+
* Prepare margin CSS, remove any previous explicitly
220+
* assigned margin assignments
221+
* Note: this will not work with calc values (negative calc values are invalid)
222+
*/
223+
private _buildGridMargin(value: string): StyleDefinition {
224+
let marginTop = '0px', marginRight = '0px', marginBottom = '-' + value, marginLeft = '0px';
225+
226+
if (this._directionality.value === 'rtl') {
227+
marginLeft = '-' + value;
228+
} else {
229+
marginRight = '-' + value;
185230
}
231+
232+
return {'margin': `${marginTop} ${marginRight} ${marginBottom} ${marginLeft}`};
186233
}
187234

188235
/**
@@ -212,5 +259,6 @@ export class LayoutGapDirective extends BaseFxDirective
212259

213260
return margins;
214261
}
215-
216262
}
263+
264+
const GRID_SPECIFIER = ' grid';

0 commit comments

Comments
 (0)