-
Notifications
You must be signed in to change notification settings - Fork 772
rfc: new layout system #1185
base: master
Are you sure you want to change the base?
rfc: new layout system #1185
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
The `uni` entrypoint is a completely new approach to the Layout library. The emphasis here | ||
is on simplicity, with as little defined in the library as possible. The goal is to create | ||
a utility around the hardest part of layout management: creating and defining interchangeable | ||
builders and coordinating that with the media query system in the browser. | ||
|
||
This is a backwards-incompatible approach to the current version of Angular Flex Layout, but | ||
we believe that this approach is more ergonomic in nature, and is the right way of doing things | ||
moving forward. That being said, this is completely opt-in, with no actual plans to transition | ||
anywhere in the near- or long-term. | ||
|
||
To use this is quite simple: | ||
|
||
### Step 1: Import the new module | ||
```ts | ||
// app.module.ts | ||
|
||
@NgModule({ | ||
imports: [UnifiedModule.withDefaults()] // brings in default tags and breakpoints | ||
}) | ||
export class AppModule {} | ||
``` | ||
|
||
### Step 2: Use the new syntax | ||
```html | ||
<div ngl flexAlign="start"> | ||
<bp tag="xs" flexAlign="end"></bp> | ||
<bp tag="md" flexAlign="center"></bp> | ||
</div> | ||
``` | ||
|
||
Here, `ngl` stands for "Angular (ng) Layout (l)". The `bp` elements are optional if | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was |
||
you don't plan to use breakpoints in your template. Conceptually, it's cleaner than | ||
cramming all of the breakpoints on one root element, and the `bp` elements don't | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is <div ngLayout flexAlign="start">
<breakpoint tag="xs" flexAlign="end"></breakpoint>
<breakpoint tag="md" flexAlign="center"></breakpoint>
</div> |
||
actually render in the template anyway. | ||
|
||
We've removed the `fx` and `gd` prefixes, since now we can rely on attributes that | ||
are only applicable on the `bp` and root `ngl` elements. | ||
|
||
Again, our goal here is simplicity. The most basic usage could be construed as the | ||
following: | ||
|
||
```html | ||
<div ngl flex></div> | ||
``` | ||
|
||
This roughly corresponds to the old syntax for accomplishing the same: | ||
|
||
```html | ||
<div fxFlex></div> | ||
``` | ||
|
||
While users who don't use many attributes per directive may not see a difference, | ||
it will become quite apparent to those who use many, or as you scale. | ||
|
||
This is only a proposal, and all feedback is welcome. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* 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 | ||
*/ | ||
|
||
export * from './public-api'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* 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 {ModuleWithProviders, NgModule} from '@angular/core'; | ||
|
||
import {BreakpointDirective, UnifiedDirective} from './src/unified'; | ||
import {FLEX_PROVIDER, GRID_PROVIDER, TAGS_PROVIDER} from './src/tags/tags'; | ||
import {BREAKPOINTS_PROVIDER} from './src/breakpoint'; | ||
|
||
|
||
@NgModule({ | ||
declarations: [UnifiedDirective, BreakpointDirective], | ||
exports: [UnifiedDirective, BreakpointDirective] | ||
}) | ||
export class UnifiedModule { | ||
static withDefaults(withDefaultBp: boolean = true): ModuleWithProviders<UnifiedModule> { | ||
return { | ||
ngModule: UnifiedModule, | ||
providers: withDefaultBp ? [ | ||
TAGS_PROVIDER, | ||
BREAKPOINTS_PROVIDER, | ||
] : [TAGS_PROVIDER], | ||
}; | ||
} | ||
|
||
static withFlex(withDefaultBp: boolean = true): ModuleWithProviders<UnifiedModule> { | ||
return { | ||
ngModule: UnifiedModule, | ||
providers: withDefaultBp ? [ | ||
FLEX_PROVIDER, | ||
BREAKPOINTS_PROVIDER | ||
] : [FLEX_PROVIDER] | ||
}; | ||
} | ||
|
||
static withGrid(withDefaultBp: boolean = true): ModuleWithProviders<UnifiedModule> { | ||
return { | ||
ngModule: UnifiedModule, | ||
providers: withDefaultBp ? [ | ||
GRID_PROVIDER, | ||
BREAKPOINTS_PROVIDER | ||
] : [GRID_PROVIDER] | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* 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 | ||
*/ | ||
|
||
export * from './src/uni'; | ||
export * from './module'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* 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 {inject, InjectionToken} from '@angular/core'; | ||
|
||
|
||
/** | ||
* A breakpoint is a wrapper interface around the browser's mediaQuery, | ||
* which is a condition string used for matching based on browser window | ||
* parameters. Here, a breakpoint has a shortcut name, e.g. 'xs', the | ||
* corresponding mediaQuery, and a priority in case the mediaQuery overlaps | ||
* with other registered breakpoints. | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries | ||
*/ | ||
export interface Breakpoint { | ||
/** The shortcut name for the breakpoint, e.g. 'xs' */ | ||
name: string; | ||
/** The mediaQuery for the breakpoint, e.g. 'screen and (max-width: 500px)' */ | ||
media: string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that there's a good case for calling this |
||
/** The priority of the breakpoint compared to other breakpoints */ | ||
priority: number; | ||
} | ||
|
||
export const FALLBACK_BREAKPOINT_KEY: string = '__FALLBACK__'; | ||
|
||
/** | ||
* The fallback breakpoint, which has no real name and is | ||
* superseded by any other breakpoint value | ||
*/ | ||
export const FALLBACK_BREAKPOINT: Breakpoint = { | ||
name: FALLBACK_BREAKPOINT_KEY, | ||
media: 'all', | ||
priority: -Number.MAX_SAFE_INTEGER, | ||
}; | ||
|
||
/** | ||
* The default breakpoints as provided by Google's Material Design. | ||
* These do not include orientation breakpoints or device breakpoints. | ||
*/ | ||
export const DEFAULT_BREAKPOINTS: Breakpoint[] = [ | ||
{ | ||
name: 'xs', | ||
media: 'screen and (min-width: 0px) and (max-width: 599.9px)', | ||
priority: 1000, | ||
}, | ||
{ | ||
name: 'sm', | ||
media: 'screen and (min-width: 600px) and (max-width: 959.9px)', | ||
priority: 900, | ||
}, | ||
{ | ||
name: 'md', | ||
media: 'screen and (min-width: 960px) and (max-width: 1279.9px)', | ||
priority: 800, | ||
}, | ||
{ | ||
name: 'lg', | ||
media: 'screen and (min-width: 1280px) and (max-width: 1919.9px)', | ||
priority: 700, | ||
}, | ||
{ | ||
name: 'xl', | ||
media: 'screen and (min-width: 1920px) and (max-width: 4999.9px)', | ||
priority: 600, | ||
}, | ||
{ | ||
name: 'lt-sm', | ||
media: 'screen and (max-width: 599.9px)', | ||
priority: 950, | ||
}, | ||
{ | ||
name: 'lt-md', | ||
media: 'screen and (max-width: 959.9px)', | ||
priority: 850, | ||
}, | ||
{ | ||
name: 'lt-lg', | ||
media: 'screen and (max-width: 1279.9px)', | ||
priority: 750, | ||
}, | ||
{ | ||
name: 'lt-xl', | ||
priority: 650, | ||
media: 'screen and (max-width: 1919.9px)', | ||
}, | ||
{ | ||
name: 'gt-xs', | ||
media: 'screen and (min-width: 600px)', | ||
priority: -950, | ||
}, | ||
{ | ||
name: 'gt-sm', | ||
media: 'screen and (min-width: 960px)', | ||
priority: -850, | ||
}, { | ||
name: 'gt-md', | ||
media: 'screen and (min-width: 1280px)', | ||
priority: -750, | ||
}, | ||
{ | ||
name: 'gt-lg', | ||
media: 'screen and (min-width: 1920px)', | ||
priority: -650, | ||
} | ||
]; | ||
|
||
/** | ||
* The user-facing injection token for providing breakpoints, | ||
* this is meant to be provided as a multi-provider, and | ||
* consolidated later. | ||
*/ | ||
export const BREAKPOINTS = | ||
new InjectionToken<Array<Array<Breakpoint>>>('Angular Layout Breakpoints'); | ||
|
||
/** An internal-facing provider for the default breakpoints */ | ||
export const BREAKPOINTS_PROVIDER = { | ||
provide: BREAKPOINTS, | ||
useValue: DEFAULT_BREAKPOINTS, | ||
multi: true, | ||
}; | ||
|
||
/** | ||
* An internal-facing injection token to consolidate all registered | ||
* breakpoints for use in the application. | ||
*/ | ||
export const BPS = new InjectionToken<Breakpoint[]>('Angular Layout Condensed Breakpoints', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a short token, but it's not very expressive. |
||
providedIn: 'root', | ||
factory: () => { | ||
const providedBps = inject(BREAKPOINTS); | ||
const bpMap: Map<string, Breakpoint> = new Map(); | ||
|
||
providedBps.forEach(bps => { | ||
bps.forEach(bp => { | ||
bpMap.set(bp.name, bp); | ||
}); | ||
}); | ||
|
||
return [...Array.from(bpMap.values()), FALLBACK_BREAKPOINT]; | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description uses
name="xs"
here. It looks like the rest of the code usestag="xs"
.I'm not sure which one is clearer, probably
name
?I guess the formal name for "the stuff inside the media query CSS at-rule" is a "media query list". But I don't think that we support multiple queries per
<bp>
. Perhaps it should just be"query="xs"
?