Skip to content

Commit

Permalink
feat(fxFlex): compute immediate parent flex-direction (#220)
Browse files Browse the repository at this point in the history
Support use of **fxFlex** directive on HTML element without a **fxLayout**  parent
directive. Previous versions of **fxFlex** required a `flex-direction` to be specific on the immediate parent.
Current version improved:

  * use defined flex-direction or getComputed.
  * auto-inject **fxLayout** styles to parent if parent `flex-direction` not already provided.
  * fix ngOnDestory() with fxLayoutWrap
  • Loading branch information
ThomasBurleson authored and mmalerba committed Mar 16, 2017
1 parent f270078 commit ba0d85d
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 141 deletions.
15 changes: 8 additions & 7 deletions src/demo-app/app/demo-app-module.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {SharedModule} from './shared/shared.module';

import {DemoApp} from './demo-app/demo-app';
import {DemoAppRoutingModule} from "./demo-app/demo-app-routes";
import {DemosStackOverflowModule} from "./stack-overflow/DemosStackOverflow";
import {DemosGithubIssuesModule} from './github-issues/DemosGithubIssues';
import {DemosLayoutAPIModule} from './docs-layout/DemosLayoutAPI';
import {DemosResponsiveLayoutsModule} from './docs-layout-responsive/DemosResponsiveLayouts';
import {SharedModule} from './shared/_module';
import {DemoRoutesModule} from "./demo-app/demo-routes";

import {DemosStackOverflowModule} from "./stack-overflow/_module";
import {DemosGithubIssuesModule} from './github-issues/_module';
import {DemosLayoutAPIModule} from './docs-layout/_module';
import {DemosResponsiveLayoutsModule} from './docs-layout-responsive/_module';

@NgModule({
declarations: [DemoApp],
bootstrap: [DemoApp],
imports: [
BrowserModule,
SharedModule,
DemoAppRoutingModule,
DemoRoutesModule,

/* Internal Demo App Modules */
DemosStackOverflowModule,
Expand Down
6 changes: 3 additions & 3 deletions src/demo-app/app/demo-app/demo-app.css
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ div.colored.box.nopad > div {
min-width: 100px;
}

md-toolbar.bigger {
min-height: 145px;
padding: 10px;
.bigger {
padding: 0 20px;
padding-bottom: 30px;
}

md-toolbar .md-toolbar-layout md-toolbar-row {
Expand Down
13 changes: 5 additions & 8 deletions src/demo-app/app/demo-app/demo-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import {Component, ViewEncapsulation} from '@angular/core';
selector: 'demo-app',
styleUrls : [ 'demo-app.css' ],
template: `
<md-toolbar class="bigger" style="padding:0 20px;padding-bottom:30px;">
<md-toolbar-row>
<div fxLayout="column" class="bigger">
<div fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="20px"
Expand All @@ -16,18 +15,16 @@ import {Component, ViewEncapsulation} from '@angular/core';
<button md-raised-button color="primary" [routerLink]="['issues']"> Github </button>
<button md-raised-button color="primary" [routerLink]="['stackoverflow']"> StackOverflow </button>
</div>
<div fxFlex="15px"></div>
</md-toolbar-row>
<md-toolbar-row fxFlex fxLayout="column"
<div fxFlex fxLayout="column"
fxLayoutAlign="left top"
style="font-size: 0.85em; margin-top: 0px; padding-bottom:20px; white-space:normal">
style="font-size: 0.85em; margin-top: 10px; padding-bottom:20px; white-space:normal">
These Layout demos are curated from the Angular Material v1.x documentation, GitHub Issues, StackOverflow,
and CodePen.
<span class="title" style="font-size: 0.7em; font-weight:normal;">
Hint: Click on any of the samples below to toggle the layout direction(s).
</span>
</md-toolbar-row>
</md-toolbar>
</div>
</div>
<div class="demo-content">
<router-outlet></router-outlet>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HashLocationStrategy, LocationStrategy } from "@angular/common";

import {DemosLayoutAPI} from "../docs-layout/DemosLayoutAPI";
import {DemosResponsiveLayout} from "../docs-layout-responsive/DemosResponsiveLayouts";
import {DemosGithubIssues} from "../github-issues/DemosGithubIssues";
import {DemosStackOverflow} from "../stack-overflow/DemosStackOverflow";
import {DemosLayoutAPI} from "../docs-layout/_module";
import {DemosResponsiveLayout} from "../docs-layout-responsive/_module";
import {DemosGithubIssues} from "../github-issues/_module";
import {DemosStackOverflow} from "../stack-overflow/_module";

const DemoAppRoutes: Routes = [
{path: '' , redirectTo: 'docs', pathMatch: 'full'},
Expand All @@ -24,7 +24,7 @@ const DemoAppRoutes: Routes = [
{ provide: LocationStrategy, useClass: HashLocationStrategy }
]
})
export class DemoAppRoutingModule { }
export class DemoRoutesModule { }

// These components are already exported in their associated Feature modules
// export const DemoAppRoutingComponents = [DemosLayoutAPI, DemosResponsiveLayout, DemosGithubIssues, DemosStackOverflow];
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {DemoResponsiveShowHide} from "./responsiveShowHide.demo";
import {DemoResponsiveFlexDirectives} from "./responsiveFlexDirective.demo";
import {DemoResponsiveFlexOrder} from "./responsiveFlexOrder.demo";
import {DemoResponsiveStyle} from "./responsiveStyle.demo";
import {SharedModule} from '../shared/shared.module';
import {SharedModule} from '../shared/_module';

@NgModule({
declarations : [
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/demo-app/app/docs-layout/flexOtherValues.demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {Component} from '@angular/core';
<md-card-subtitle>Explore impact of non-numerical values for the 'fxFlex' API:</md-card-subtitle>
<md-card-content>
<div class="containerX">
<div fxLayout="row" fxLayoutWrap class="colored box nopad" >
<div fxLayout="row wrap" class="colored box nopad" >
<div fxFlex="none"> [flex="none"] </div>
<div fxFlex> [flex] </div>
<div fxFlex="nogrow"> [flex="nogrow"] </div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {DemoIssue9897} from "./issue.9897.demo";
import {DemoIssue135} from "./issue.135.demo";
import {DemoIssue181} from './issue.181.demo';
import {DemoIssue197} from './issue.197.demo';
import {SharedModule} from '../shared/shared.module';
import {SharedModule} from '../shared/_module';

@NgModule({
declarations: [
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/demo-app/app/stack-overflow/columnOrder.demo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, ViewEncapsulation} from '@angular/core';
import {Component} from '@angular/core';

@Component({
selector: 'demo-complex-column-ordering',
Expand Down
20 changes: 20 additions & 0 deletions src/lib/flexbox/api/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import {ElementRef, Renderer, OnDestroy} from '@angular/core';

import {applyCssPrefixes} from '../../utils/auto-prefixer';
import {buildLayoutCSS} from '../../utils/layout-validator';

import {ResponsiveActivation, KeyOptions} from '../responsive/responsive-activation';
import {MediaMonitor} from '../../media-query/media-monitor';
Expand Down Expand Up @@ -93,6 +94,25 @@ export abstract class BaseFxDirective implements OnDestroy {
return value.trim();
}

protected _getFlowDirection(target: any, addIfMissing = false): string {
let value = "";
if ( target ) {
let directionKeys = Object.keys(applyCssPrefixes({'flex-direction': ''}));
let findDirection = (styles) => directionKeys.reduce((direction, key) => {
return direction || styles[key];
}, null);

let immediateValue = findDirection(target['style']);
value = immediateValue || findDirection(getComputedStyle(target as Element));
if ( !immediateValue && addIfMissing ) {
value = value || 'row';
this._applyStyleToElements(buildLayoutCSS(value), [target]);
}
}

return value ? value.trim() : "row";
}

/**
* Applies styles given via string pair or object map to the directive element.
*/
Expand Down
56 changes: 56 additions & 0 deletions src/lib/flexbox/api/flex.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,62 @@ describe('flex directive', () => {
});
});

it('should work fxLayout parents', () => {
fixture = componentWithTemplate(`
<div fxLayout="column" class="test">
<div fxFlex="30px" fxFlex.gt-sm="50" > </div>
</div>
`);
fixture.detectChanges();
let parent = queryFor(fixture, ".test")[0].nativeElement;
let element = queryFor(fixture, "[fxFlex]")[0].nativeElement;

// parent flex-direction found with 'column' with child height styles
expect(parent).toHaveCssStyle({'flex-direction': 'column', 'display': 'flex'});
expect(element).toHaveCssStyle({'min-height': '30px'});
expect(element).not.toHaveCssStyle({'min-width': '30px'});
});

it('should not work with non-direct-parent fxLayouts', async(() => {
fixture = componentWithTemplate(`
<div fxLayout="column">
<div class="test">
<div fxFlex="40px" fxFlex.gt-sm="50" > </div>
</div>
</div>
`);
fixture.detectChanges();
let element = queryFor(fixture, "[fxFlex]")[0].nativeElement;
let parent = queryFor(fixture, ".test")[0].nativeElement;

setTimeout(() => {
// The parent flex-direction not found;
// A flex-direction should have been auto-injected to the parent...
// fallback to 'row' and set child width styles accordingly
expect(parent).toHaveCssStyle({ 'flex-direction': 'row' });
expect(element).toHaveCssStyle({ 'min-width': '40px' });
expect(element).not.toHaveCssStyle({ 'min-height': '40px' });
});

}));

it('should work with styled-parent flex directions', () => {
fixture = componentWithTemplate(`
<div fxLayout="row">
<div style="flex-direction:column" class="parent">
<div fxFlex="60px" > </div>
</div>
</div>
`);
fixture.detectChanges();
let element = queryFor(fixture, "[fxFlex]")[0].nativeElement;
let parent = queryFor(fixture, ".parent")[0].nativeElement;

// parent flex-direction found with 'column'; set child with height styles
expect(element).toHaveCssStyle({ 'min-height': '60px' });
expect(parent).toHaveCssStyle({ 'flex-direction': 'column' });
});

it('should work with "1 1 auto" values', () => {
fixture = componentWithTemplate(`
<div fxLayout="column">
Expand Down
27 changes: 13 additions & 14 deletions src/lib/flexbox/api/flex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
*/
ngOnChanges(changes: SimpleChanges) {
if (changes['flex'] != null || this._mqActivation) {
this._onLayoutChange();
this._updateStyle();
}
}

Expand All @@ -120,7 +120,7 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
this._listenForMediaQueryChanges('flex', '', (changes: MediaChange) => {
this._updateStyle(changes.value);
});
this._onLayoutChange();
this._updateStyle();
}

ngOnDestroy() {
Expand Down Expand Up @@ -158,18 +158,13 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
protected _validateValue(grow: number|string,
shrink: number|string,
basis: string|number|FlexBasisAlias) {
// The flex-direction of this element's flex container. Defaults to 'row'.
let layout = this._getFlowDirection(this.parentElement, true);
let direction = (layout.indexOf('column') > -1) ? 'column' : 'row';
let css, isValue;
let direction = (this._layout === 'column') || (this._layout == 'column-reverse') ?
'column' :
'row';

if ( grow == "0" ) {
grow = 0;
}

if ( shrink == "0" ) {
shrink = 0;
}
grow = (grow == "0") ? 0 : grow;
shrink = (shrink == "0") ? 0 : shrink;

// flex-basis allows you to specify the initial/starting main-axis size of the element,
// before anything else is computed. It can either be a percentage or an absolute value.
Expand Down Expand Up @@ -256,6 +251,10 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
css[min] = (basis == '0%') ? 0 : isFixed || (isPx && grow) ? basis : null;
css[max] = (basis == '0%') ? 0 : isFixed || (!usingCalc && shrink) ? basis : null;

return extendObject(css, {'box-sizing': 'border-box'});
}
return extendObject(css, {'box-sizing': 'border-box'});
}

protected get parentElement(): any {
return this._elementRef.nativeElement.parentNode;
}
}
3 changes: 2 additions & 1 deletion src/lib/flexbox/api/layout-align.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {BaseFxDirective} from './base';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';

import {LAYOUT_VALUES, LayoutDirective} from './layout';
import {LayoutDirective} from './layout';
import {LAYOUT_VALUES} from '../../utils/layout-validator';


/**
Expand Down
4 changes: 2 additions & 2 deletions src/lib/flexbox/api/layout-gap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import {
import {Subscription} from 'rxjs/Subscription';

import {BaseFxDirective} from './base';
import {LayoutDirective} from './layout';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {LayoutDirective, LAYOUT_VALUES} from './layout';

import {LAYOUT_VALUES} from '../../utils/layout-validator';
/**
* 'layout-padding' styling directive
* Defines padding of child elements in a layout container
Expand Down
51 changes: 19 additions & 32 deletions src/lib/flexbox/api/layout-wrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ import {
SimpleChanges, Self, Optional,
} from '@angular/core';
import {Subscription} from 'rxjs/Subscription';
import {extendObject} from '../../utils/object-extend';

import {BaseFxDirective} from './base';
import {LayoutDirective} from './layout';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {LayoutDirective, LAYOUT_VALUES} from './layout';

import {validateWrapValue, LAYOUT_VALUES} from '../../utils/layout-validator';
/**
* @deprecated
* This functionality is now part of the `fxLayout` API
Expand Down Expand Up @@ -96,6 +95,12 @@ export class LayoutWrapDirective extends BaseFxDirective implements OnInit, OnCh
this._updateWithValue();
}

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

// *********************************************
// Protected methods
Expand All @@ -114,47 +119,29 @@ export class LayoutWrapDirective extends BaseFxDirective implements OnInit, OnCh
}

protected _updateWithValue(value?: string) {
value = value || this._queryInput("wrap") || 'wrap';
value = value || this._queryInput("wrap");
if (this._mqActivation) {
value = this._mqActivation.activatedInput;
}
value = this._validateValue(value);
value = validateWrapValue(value || 'wrap');

this._applyStyleToElement(this._buildCSS(value));
}


/**
* Build the CSS that should be assigned to the element instance
*/
protected _buildCSS(value) {
return extendObject({ 'flex-wrap': value }, {
'display' : 'flex',
'flex-direction' : this._layout || 'row'
});
return {
'display': 'flex',
'flex-wrap': value,
'flex-direction': this.flowDirection
};
}

/**
* Convert layout-wrap="<value>" to expected flex-wrap style
*/
protected _validateValue(value) {
switch (value.toLowerCase()) {
case 'reverse':
case 'wrap-reverse':
value = 'wrap-reverse';
break;

case 'no':
case 'none':
case 'nowrap':
value = 'nowrap';
break;

// All other values fallback to "wrap"
default:
value = 'wrap';
break;
}
return value;
protected get flowDirection(): string {
let computeFlowDirection = () => this._getFlowDirection(this._elementRef.nativeElement);
return this._layoutWatcher ? this._layout : computeFlowDirection();
}

}
Loading

0 comments on commit ba0d85d

Please sign in to comment.