Skip to content

Commit

Permalink
feat(tokens): add configuration for breakpoints and flex styles
Browse files Browse the repository at this point in the history
* add token to add individual breakpoints that can be merged with
  the defaults seamlessly
* add token that optionally adds flex stylings to parents without
  inline flex styles (set to false by default)
* add token to optionally disable adding vendor prefixes to inline
  styles

BREAKING CHANGE:

* `fxFlex` no longer adds `display: flex; flex-direction: row` by
  default
  • Loading branch information
CaerusKaru authored and ThomasBurleson committed Mar 19, 2018
1 parent bc1954c commit 605f4d1
Show file tree
Hide file tree
Showing 30 changed files with 517 additions and 359 deletions.
52 changes: 45 additions & 7 deletions docs/documentation/BreakPoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,21 @@ export class CoreModule {
}
```

This provider is used to return a list to *all* known BreakPoint(s)... and, in turn, this list is used internally to
This provider is used to return a list to *all* known BreakPoint(s) and, in turn, this list is used internally to
register mediaQueries and announce mediaQuery activations.


### Custom BreakPoints

Using the **BREAKPOINTS** InjectionToken, developers can add custom breakpoints or easily override existing breakpoints.
Using the **BREAKPOINT** (note: singular) `InjectionToken`, developers can add custom breakpoints or easily override
existing breakpoints.

For example to add mediaQueries that activate when printing:

##### `custom-breakpoints.ts`

```typescript
import {BREAKPOINTS, DEFAULT_BREAKPOINTS} from '@angular/flex-layout';
import {BREAKPOINT} from '@angular/flex-layout';

const PRINT_BREAKPOINTS = [{
alias: 'xs.print',
Expand All @@ -46,8 +47,9 @@ const PRINT_BREAKPOINTS = [{
}];

export const CustomBreakPointsProvider = {
provide: BREAKPOINTS,
useValue: [...DEFAULT_BREAKPOINTS, ...PRINT_BREAKPOINTS]
provide: BREAKPOINT,
useValue: PRINT_BREAKPOINTS,
multi: true
};
```

Expand All @@ -56,7 +58,7 @@ export const CustomBreakPointsProvider = {
```typescript
import {CommonModule, NgModule} from '@angular/core';
import {FlexLayoutModule} from '@angular/flex-layout';
import {CustomBreakPointsProvider} from 'custom-breakpoints.ts';
import {CustomBreakPointsProvider} from './custom-breakpoints.ts';

@NgModule({
imports : [
Expand All @@ -71,7 +73,43 @@ export class MyAppModule {
}
```

With the above changes, when printing on mobile-sized viewports the **`xs.print`** mediaQuery will activate.
With the above changes, when printing on mobile-sized viewports the **`xs.print`** mediaQuery will activate. Please note
that the provider is a **multi-provider**, meaning it can be provided multiple times and in a variety of
presentations. The type signature of `BREAKPOINT` is the following:

`BREAKPOINT = InjectionToken<BreakPoint|BreakPoint[]>`

Thus, you can use the token to segment which breakpoints you provide and in which order. For instance,
you can provide all print breakpoints in an array called `PRINT_BREAKPOINTS` and then all mobile breakpoints
in another array called `MOBILE_BREAKPOINTS`. You can also simply provide one additional breakpoint if that's
all you need.

### Disabling the default breakpoints

To disable the default breakpoints, you simply provide the new **DISABLE_DEFAULT_BREAKPOINTS** token as follows:

```typescript
import {DISABLE_DEFAULT_BREAKPOINTS} from '@angular/flex-layout';

{provide: DISABLE_DEFAULT_BREAKPOINTS, useValue: true}
```

The default value for this breakpoint is false

### Adding the orientation breakpoints

The orientation breakpoints are a set of breakpoints that detect when a device is in portrait or landscape mode. Flex
Layout has a set of these that conform to the Material Design spec built-in to the library. They can be found in the
`ORIENTATION_BREAKPOINTS` `InjectionToken`. To have these added to the default breakpoints, you can provide the token
`ADD_ORIENTATION_BREAKPOINTS` to your app as follows:

```typescript
import {ADD_ORIENTATION_BREAKPOINTS} from '@angular/flex-layout';

{provide: ADD_ORIENTATION_BREAKPOINTS, useValue: true}
```

The default value for this breakpoint is false

### Custom Breakpoints and Directives

Expand Down
4 changes: 2 additions & 2 deletions src/lib/core/breakpoints/break-point-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {BREAKPOINTS} from './break-points-token';

/**
* Registry of 1..n MediaQuery breakpoint ranges
* This is published as a provider and may be overriden from custom, application-specific ranges
* This is published as a provider and may be overridden from custom, application-specific ranges
*
*/
@Injectable()
Expand All @@ -25,7 +25,7 @@ export class BreakPointRegistry {
/**
* Accessor to raw list
*/
get items(): BreakPoint[ ] {
get items(): BreakPoint[] {
return [...this._registry];
}

Expand Down
57 changes: 51 additions & 6 deletions src/lib/core/breakpoints/break-points-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '@angular/core';
import {InjectionToken, Optional, SkipSelf} from '@angular/core';

import {BreakPoint} from './break-point';
import {BREAKPOINTS} from './break-points-token';
import {DEFAULT_BREAKPOINTS} from './data/break-points';
import {ORIENTATION_BREAKPOINTS} from './data/orientation-break-points';

import {extendObject} from '../../utils/object-extend';
import {mergeByAlias, validateSuffixes} from './breakpoint-tools';
import {
ADD_ORIENTATION_BREAKPOINTS,
BREAKPOINT,
DISABLE_DEFAULT_BREAKPOINTS,
} from '../tokens/breakpoint-token';


/**
* Options to identify which breakpoint types to include as part of
* a BreakPoint provider
* @deprecated
* @deletion-target v6.0.0-beta.15
*/
export interface BreakPointProviderOptions {
/**
Expand All @@ -35,11 +41,13 @@ export interface BreakPointProviderOptions {

/**
* Add new custom items to the default list or override existing default with custom overrides
* @deprecated
* @deletion-target v6.0.0-beta.15
*/
export function buildMergedBreakPoints(_custom?: BreakPoint[],
options?: BreakPointProviderOptions) {
options = extendObject({}, {
defaults: true, // exclude pre-configured, internal default breakpoints
defaults: true, // exclude pre-configured, internal default breakpoints
orientation: false // exclude pre-configured, internal orientations breakpoints
}, options || {});

Expand All @@ -55,6 +63,8 @@ export function buildMergedBreakPoints(_custom?: BreakPoint[],

/**
* Ensure that only a single global BreakPoint list is instantiated...
* @deprecated
* @deletion-target v6.0.0-beta.15
*/
export function DEFAULT_BREAKPOINTS_PROVIDER_FACTORY() {
return validateSuffixes(DEFAULT_BREAKPOINTS);
Expand All @@ -67,18 +77,53 @@ export function DEFAULT_BREAKPOINTS_PROVIDER_FACTORY() {
* custom breakpoints matching existing breakpoints will override the properties
* of the existing (and not be added as an extra breakpoint entry).
* [xs, gt-xs, sm, gt-sm, md, gt-md, lg, gt-lg, xl]
* @deprecated
* @deletion-target v6.0.0-beta.15
*/
export const DEFAULT_BREAKPOINTS_PROVIDER = { // tslint:disable-line:variable-name
export const DEFAULT_BREAKPOINTS_PROVIDER = {
provide: BREAKPOINTS,
useFactory: DEFAULT_BREAKPOINTS_PROVIDER_FACTORY
};

/**
* Factory that combines the configured breakpoints into one array and then merges
* them using a utility function
*/
export function BREAKPOINTS_PROVIDER_FACTORY(parentBreakpoints: BreakPoint[],
breakpoints: (BreakPoint|BreakPoint[])[],
disableDefaults: boolean,
addOrientation: boolean) {
const bpFlattenArray = [].concat.apply([], (breakpoints || [])
.map(v => Array.isArray(v) ? v : [v]));
const builtIns = DEFAULT_BREAKPOINTS.concat(addOrientation ? ORIENTATION_BREAKPOINTS : []);
return parentBreakpoints || disableDefaults ?
mergeByAlias(bpFlattenArray) : mergeByAlias(builtIns, bpFlattenArray);
}

/**
* Provider that combines the provided extra breakpoints with the default and
* orientation breakpoints based on configuration
*/
export const BREAKPOINTS_PROVIDER = {
provide: BREAKPOINTS,
useFactory: BREAKPOINTS_PROVIDER_FACTORY,
deps: [
[new Optional(), new SkipSelf(), BREAKPOINTS],
[new Optional(), BREAKPOINT],
[new Optional(), DISABLE_DEFAULT_BREAKPOINTS],
[new Optional(), ADD_ORIENTATION_BREAKPOINTS],
]
};

/**
* Use with FlexLayoutModule.CUSTOM_BREAKPOINTS_PROVIDER_FACTORY!
* @deprecated
* @deletion-target v6.0.0-beta.15
*/
export function CUSTOM_BREAKPOINTS_PROVIDER_FACTORY(_custom?: BreakPoint[],
export function CUSTOM_BREAKPOINTS_PROVIDER_FACTORY(custom?: BreakPoint[],
options?: BreakPointProviderOptions) {
return {
provide: <InjectionToken<BreakPoint[]>>BREAKPOINTS,
useFactory: buildMergedBreakPoints(_custom, options)
useFactory: buildMergedBreakPoints(custom, options)
};
}
35 changes: 17 additions & 18 deletions src/lib/core/breakpoints/breakpoint-tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {TestBed, inject} from '@angular/core/testing';
import {TestBed, inject, fakeAsync} from '@angular/core/testing';

import {BreakPoint} from './break-point';
import {BREAKPOINTS} from './break-points-token';
import {
DEFAULT_BREAKPOINTS_PROVIDER,
buildMergedBreakPoints
} from './break-points-provider';
import {BREAKPOINTS_PROVIDER, BREAKPOINTS_PROVIDER_FACTORY} from './break-points-provider';
import {validateSuffixes, mergeByAlias} from './breakpoint-tools';

describe('breakpoint-tools', () => {
Expand All @@ -22,7 +19,7 @@ describe('breakpoint-tools', () => {
}, null);

beforeEach(() => {
all = buildMergedBreakPoints([], {orientations: true})();
all = BREAKPOINTS_PROVIDER_FACTORY([], [], false, true);
});

describe('validation', () => {
Expand All @@ -47,18 +44,20 @@ describe('breakpoint-tools', () => {
expect(validated[4].suffix).toEqual('HandsetPortrait');
});
it('should auto-validate the DEFAULT_BREAKPOINTS', () => {
let xsBp: BreakPoint = findByAlias('xs')!;
let gtLgBp: BreakPoint = findByAlias('gt-lg')!;
let xlBp: BreakPoint = findByAlias('xl')!;
fakeAsync(() => {
let xsBp: BreakPoint = findByAlias('xs')!;
let gtLgBp: BreakPoint = findByAlias('gt-lg')!;
let xlBp: BreakPoint = findByAlias('xl')!;

expect(xsBp.alias).toEqual('xs');
expect(xsBp.suffix).toEqual('Xs');
expect(xsBp.alias).toEqual('xs');
expect(xsBp.suffix).toEqual('Xs');

expect(gtLgBp.alias).toEqual('gt-lg');
expect(gtLgBp.suffix).toEqual('GtLg');
expect(gtLgBp.alias).toEqual('gt-lg');
expect(gtLgBp.suffix).toEqual('GtLg');

expect(xlBp.alias).toEqual('xl');
expect(xlBp.suffix).toEqual('Xl');
expect(xlBp.alias).toEqual('xl');
expect(xlBp.suffix).toEqual('Xl');
});
});
});

Expand All @@ -76,7 +75,7 @@ describe('breakpoint-tools', () => {
});
it('should add custom breakpoints with unique aliases', () => {
let defaults = [{alias: 'xs', mediaQuery: 'screen and (max-width: 599px)'}],
custom = [{alias: 'sm', mediaQuery: 'screen'}, {alias: 'md', mediaQuery: 'screen'}];
custom = [{alias: 'sm', mediaQuery: 'screen'}, {alias: 'md', mediaQuery: 'screen'}];

all = mergeByAlias(defaults, custom);

Expand All @@ -97,12 +96,12 @@ describe('breakpoint-tools', () => {
});
});

describe('with DEFAULT_BREAKPOINTS_PROVIDER', () => {
describe('with BREAKPOINTS_PROVIDER', () => {
beforeEach(() => {
// Configure testbed to prepare services
TestBed.configureTestingModule({
providers: [
DEFAULT_BREAKPOINTS_PROVIDER // Supports developer overrides of list of known breakpoints
BREAKPOINTS_PROVIDER // Supports developer overrides of list of known breakpoints
]
});
});
Expand Down
24 changes: 11 additions & 13 deletions src/lib/core/breakpoints/breakpoint-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ function camelCase(name: string): string {
*/
export function validateSuffixes(list: BreakPoint[]): BreakPoint[] {
list.forEach((bp: BreakPoint) => {
if (!bp.suffix || bp.suffix === '') {
bp.suffix = camelCase(bp.alias); // create Suffix value based on alias
bp.overlapping = bp.overlapping || false; // ensure default value
if (!bp.suffix) {
bp.suffix = camelCase(bp.alias); // create Suffix value based on alias
bp.overlapping = !!bp.overlapping; // ensure default value
}
});
return list;
Expand All @@ -48,21 +48,19 @@ export function validateSuffixes(list: BreakPoint[]): BreakPoint[] {
* - Items are merged with the custom override if the alias exists in the default list
*/
export function mergeByAlias(defaults: BreakPoint[], custom: BreakPoint[] = []): BreakPoint[] {
const merged = defaults.map((bp) => extendObject({}, bp));
const findByAlias = (alias) => merged.reduce((result, bp) => {
return result || (( bp.alias === alias) ? bp : null);
}, null);

const dict: {[key: string]: BreakPoint} = {};
defaults.forEach(bp => {
dict[bp.alias] = bp;
});
// Merge custom breakpoints
custom.forEach((bp: BreakPoint) => {
let target = findByAlias(bp.alias);
if (target) {
extendObject(target, bp);
if (dict[bp.alias]) {
extendObject(dict[bp.alias], bp);
} else {
merged.push(bp);
dict[bp.alias] = bp;
}
});

return validateSuffixes(merged);
return validateSuffixes(Object.keys(dict).map(k => dict[k]));
}

Loading

0 comments on commit 605f4d1

Please sign in to comment.