chore: expand and polish cdk docs (#7675)

* Add JsDocs where missing
* Adds and polishes overviews for main cdk subpackages
* Tweaks dgeni to disambiguate between material and cdk output (e.g. with "table").
* Tweaks markdown process to disambiguate between material and cdk output
* Fix an issue where method return types were missing from _all_ api docs
* Correct some `any` property types in cdk/stepper
* Correct table trackBy JsDoc being omitted because it was on the setter
This commit is contained in:
Jeremy Elbourn
2017-10-27 09:57:50 -07:00
committed by mmalerba
parent dcef604506
commit 0f2c2721e5
38 changed files with 417 additions and 102 deletions
+92
View File
@@ -0,0 +1,92 @@
The `a11y` package provides a number of tools to improve accessibility, described below.
### ListKeyManager
`ListKeyManager` manages the active option in a list of items based on keyboard interaction.
Intended to be used with components that correspond to a `role="menu"` or `role="listbox"` pattern.
#### Basic usage
Any component that uses a `ListKeyManager` will generally do three things:
* Create a `@ViewChildren` query for the options being managed.
* Initialize the `ListKeyManager`, passing in the options.
* Forward keyboard events from the managed component to the `ListKeyManager`.
Each option should implement the `ListKeyManagerOption` interface:
```ts
interface ListKeyManagerOption {
disabled?: boolean;
getLabel?(): string;
}
```
#### Wrapping
Navigation through options can be made to wrap via the `withWrap` method
```ts
this.keyManager = new FocusKeyManager(...).withWrap();
```
#### Types of key managers
There are two varieties of `ListKeyManager`, `FocusKeyManager` and `ActiveDescendantKeyManager`.
##### FocusKeyManager
Used when options will directly receive browser focus. Each item managed must implement the
`FocusableOption` interface:
```ts
interface FocusableOption extends ListKeyManagerOption {
focus(): void;
}
```
##### ActiveDescendantKeyManager
Used when options will be marked as active via `aria-activedescendant`.
Each item managed must implement the
`Highlightable` interface:
```ts
interface Highlightable extends ListKeyManagerOption {
setActiveStyles(): void;
setInactiveStyles(): void;
}
```
Each item must also have an ID bound to the listbox's or menu's `aria-activedescendant`.
### FocusTrap
The `cdkTrapFocus` directive traps <kbd>Tab</kbd> key focus within an element. This is intended to
be used to create accessible experience for components like
[modal dialogs](https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal), where focus must be
constrained.
This directive is declared in `A11yModule`.
#### Example
```html
<div class="my-inner-dialog-content" cdkTrapFocus>
<!-- Tab and Shift + Tab will not leave this element. -->
</div>
```
This directive will not prevent focus from moving out of the trapped region due to mouse
interaction.
### InteractivityChecker
`InteractivityChecker` is used to check the interactivity of an element, capturing disabled,
visible, tabbable, and focusable states for accessibility purposes. See the API docs for more
details.
### LiveAnnouncer
`LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region.
See [the W3C's WAI-ARIA](https://www.w3.org/TR/wai-aria/states_and_properties#aria-live)
for more information on aria-live regions.
#### Example
```ts
@Component({...})
export class MyComponent {
constructor(liveAnnouncer: LiveAnnouncer) {
liveAnnouncer.announce("Hey Google");
}
}
```
+16 -7
View File
@@ -24,10 +24,9 @@ import {InteractivityChecker} from './interactivity-checker';
/**
* Class that allows for trapping focus within a DOM element.
*
* NOTE: This class currently uses a very simple (naive) approach to focus trapping.
* This class currently uses a relatively simple approach to focus trapping.
* It assumes that the tab order is the same as DOM order, which is not necessarily true.
* Things like tabIndex > 0, flex `order`, and shadow roots can cause to two to misalign.
* This will be replaced with a more intelligent solution before the library is considered stable.
*/
export class FocusTrap {
private _startAnchor: HTMLElement | null;
@@ -169,7 +168,7 @@ export class FocusTrap {
/**
* Focuses the element that should be focused when the focus trap is initialized.
* @returns Returns whether focus was moved successfuly.
* @returns Whether focus was moved successfuly.
*/
focusInitialElement(): boolean {
if (!this._platform.isBrowser) {
@@ -188,7 +187,7 @@ export class FocusTrap {
/**
* Focuses the first tabbable element within the focus trap region.
* @returns Returns whether focus was moved successfuly.
* @returns Whether focus was moved successfuly.
*/
focusFirstTabbableElement(): boolean {
const redirectToElement = this._getRegionBoundary('start');
@@ -202,7 +201,7 @@ export class FocusTrap {
/**
* Focuses the last tabbable element within the focus trap region.
* @returns Returns whether focus was moved successfuly.
* @returns Whether focus was moved successfuly.
*/
focusLastTabbableElement(): boolean {
const redirectToElement = this._getRegionBoundary('end');
@@ -287,14 +286,23 @@ export class FocusTrapFactory {
private _platform: Platform,
private _ngZone: NgZone) { }
create(element: HTMLElement, deferAnchors: boolean = false): FocusTrap {
return new FocusTrap(element, this._platform, this._checker, this._ngZone, deferAnchors);
/**
* Creates a focus-trapped region around the given element.
* @param element The element around which focus will be trapped.
* @param deferCaptureElements Defers the creation of focus-capturing elements to be done
* manually by the user.
* @returns The created focus trap instance.
*/
create(element: HTMLElement, deferCaptureElements: boolean = false): FocusTrap {
return new FocusTrap(
element, this._platform, this._checker, this._ngZone, deferCaptureElements);
}
}
/**
* Directive for trapping focus within a region.
* @docs-private
* @deprecated
*/
@Directive({
@@ -330,6 +338,7 @@ export class FocusTrapDeprecatedDirective implements OnDestroy, AfterContentInit
exportAs: 'cdkTrapFocus',
})
export class FocusTrapDirective implements OnDestroy, AfterContentInit {
/** Underlying FocusTrap instance. */
focusTrap: FocusTrap;
/** Whether the focus trap is active. */
+4 -5
View File
@@ -9,11 +9,10 @@
import {Injectable} from '@angular/core';
import {Platform} from '@angular/cdk/platform';
/**
* The InteractivityChecker leans heavily on the ally.js accessibility utilities.
* Methods like `isTabbable` are only covering specific edge-cases for the browsers which are
* supported.
*/
// The InteractivityChecker leans heavily on the ally.js accessibility utilities.
// Methods like `isTabbable` are only covering specific edge-cases for the browsers which are
// supported.
/**
* Utility for checking the interactivity of an element, such as whether is is focusable or
+1 -3
View File
@@ -12,9 +12,7 @@ import {Subscription} from 'rxjs/Subscription';
import {UP_ARROW, DOWN_ARROW, TAB, A, Z, ZERO, NINE} from '@angular/cdk/keycodes';
import {RxChain, debounceTime, filter, map, doOperator} from '@angular/cdk/rxjs';
/**
* This interface is for items that can be passed to a ListKeyManager.
*/
/** This interface is for items that can be passed to a ListKeyManager. */
export interface ListKeyManagerOption {
disabled?: boolean;
getLabel?(): string;
+9 -1
View File
@@ -1,4 +1,7 @@
### BidiModule
The `bidi` package provides a common system for components to get and respond to change in the
application's LTR/RTL layout direction.
### Directionality
When including the CDK's `BidiModule`, components can inject `Directionality` to get the current
text direction (RTL or LTR);
@@ -28,3 +31,8 @@ export class MyWidget implements OnDestroy {
}
```
### The `Dir` directive
The `BidiModule` also includes a directive that matches any elements with a `dir` attribute. This
directive has the same API as Directionality and provides itself _as_ `Directionality`. By doing
this, any component that injects `Directionality` will get the closest ancestor layout direction
context.
+3 -6
View File
@@ -18,7 +18,8 @@ import {Direction, Directionality} from './directionality';
/**
* Directive to listen for changes of direction of part of the DOM.
*
* Would provide itself in case a component looks for the Directionality service
* Provides itself as Directionality such that descendant directives only need to ever inject
* Directionality to get the closest direction.
*/
@Directive({
selector: '[dir]',
@@ -27,7 +28,6 @@ import {Direction, Directionality} from './directionality';
exportAs: 'dir',
})
export class Dir implements Directionality {
/** Layout direction of the element. */
_dir: Direction = 'ltr';
/** Whether the `value` has been set to its initial value. */
@@ -38,10 +38,7 @@ export class Dir implements Directionality {
/** @docs-private */
@Input('dir')
get dir(): Direction {
return this._dir;
}
get dir(): Direction { return this._dir; }
set dir(v: Direction) {
let old = this._dir;
this._dir = v;
+3
View File
@@ -37,7 +37,10 @@ export const DIR_DOCUMENT = new InjectionToken<Document>('mat-dir-doc');
*/
@Injectable()
export class Directionality {
/** The current 'ltr' or 'rtl' value. */
readonly value: Direction = 'ltr';
/** Stream that emits whenever the 'ltr' / 'rtl' state changes. */
readonly change = new EventEmitter<void>();
constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) {
+7 -4
View File
@@ -24,9 +24,7 @@ interface Query {
mql: MediaQueryList;
}
/**
* Utility for checking the matching state of @media queries.
*/
/** Utility for checking the matching state of @media queries. */
@Injectable()
export class BreakpointObserver implements OnDestroy {
/** A map of all media queries currently being listened for. */
@@ -42,7 +40,11 @@ export class BreakpointObserver implements OnDestroy {
this._destroySubject.complete();
}
/** Whether the query currently is matched. */
/**
* Whether one or more media queries match the current viewport size.
* @param value One or more media queries to check.
* @returns Whether any of the media queries match.
*/
isMatched(value: string | string[]): boolean {
let queries = coerceArray(value);
return queries.some(mediaQuery => this._registerQuery(mediaQuery).mql.matches);
@@ -51,6 +53,7 @@ export class BreakpointObserver implements OnDestroy {
/**
* Gets an observable of results for the given queries that will emit new results for any changes
* in matching of the given queries.
* @returns A stream of matches for the given queries.
*/
observe(value: string | string[]): Observable<BreakpointState> {
let queries = coerceArray(value);
+77
View File
@@ -0,0 +1,77 @@
The `layout` package provides utilities to build responsive UIs that react to screen-size changes.
### BreakpointObserver
`BreakpointObserver` is a utility for evaluating media queries and reacting to their changing.
#### Evaluate against the current viewport
The `isMatched` method is used to evaluate one or more media queries against the current viewport
size.
```ts
const isSmallScreen = breakpointObserver.isMatched('(max-width: 599px)');
```
#### React to changes to the viewport
The `observe` method is used to get an observable stream that will emit whenever one of the given
media queries would have a different result.
```ts
const layoutChanges = breakpointObserver.observe([
'(orientation: portrait)',
'(orientation: landscape)',
]);
layoutChanges.subscribe(result => {
updateMyLayoutForOrientationChange();
});
```
#### Default breakpoints
A set of default media queries are available corresponding to breakpoints for different device
types.
```ts
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
@Component({...})
class MyComponent {
constructor(breakpointObserver: BreakpointObserver) {
breakpointObserver.observe([
Breakpoints.HandsetLandscape,
Breakpoints.HandsetPortrait
]).subscribe(result => {
if (result.matches) {
this.activateHandsetLayout();
}
});
}
}
```
The built-in breakpoints based on [Google's Material Design
specification](https://material.io/guidelines/layout/responsive-ui.html#responsive-ui-breakpoints).
The available values are:
* Handset
* Tablet
* Web
* HandsetPortrait
* TabletPortrait
* WebPortrait
* HandsetLandscape
* TabletLandscape
* WebLandscape
### MediaMatcher
`MediaMatcher` is a lower-level utility that wraps the native `matchMedia`. This service normalizes
browser differences and serves as a convenient API that can be replaces with a fake in unit tests.
The `matchMedia` method can be used to get a native
[`MediaQueryList`](https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList).
```ts
@Component({...})
class MyComponent {
constructor(mediaMatcher: MediaMatcher) {
const mediaQueryList = mediaMatcher.matchMedia('(min-width: 1px)');
}
}
```
+3 -3
View File
@@ -13,9 +13,7 @@ import {Platform} from '@angular/cdk/platform';
*/
const styleElementForWebkitCompatibility: Map<string, HTMLStyleElement> = new Map();
/**
* A utility for calling matchMedia queries.
*/
/** A utility for calling matchMedia queries. */
@Injectable()
export class MediaMatcher {
/** The internal matchMedia method to return back a MediaQueryList like object. */
@@ -30,6 +28,8 @@ export class MediaMatcher {
}
/**
* Evaluates the given media query and returns the native MediaQueryList from which results
* can be retrieved.
* Confirms the layout engine will trigger for the selector query provided and returns the
* MediaQueryList for the query provided.
*/
+5 -3
View File
@@ -1,10 +1,12 @@
### Observers
The `observers` package provides convenience directives built on top of native web platform
observers, such as MutationObserver.
### cdkObserveContent
A directive for observing when the content of the host element changes. An event is emitted when a
mutation to the content is observed.
#### Example
```html
<div class="projected-content-wrapper" (cdkObserveContent)="projectContentChanged()">
<ng-content></ng-content>
@@ -10,13 +10,11 @@ import {Injectable} from '@angular/core';
import {OverlayContainer} from './overlay-container';
/**
* The FullscreenOverlayContainer is the alternative to OverlayContainer
* that supports correct displaying of overlay elements in Fullscreen mode
* Alternative to OverlayContainer that supports correct displaying of overlay elements in
* Fullscreen mode
* https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen
* It should be provided in the root component that way:
* providers: [
* {provide: OverlayContainer, useClass: FullscreenOverlayContainer}
* ],
*
* Should be provided in the root component.
*/
@Injectable()
export class FullscreenOverlayContainer extends OverlayContainer {
+1 -1
View File
@@ -12,7 +12,7 @@ import {ScrollStrategy} from './scroll/scroll-strategy';
import {NoopScrollStrategy} from './scroll/noop-scroll-strategy';
/** OverlayConfig captures the initial configuration used when opening an overlay. */
/** Initial configuration used when creating an overlay. */
export class OverlayConfig {
/** Strategy with which to position the overlay. */
positionStrategy?: PositionStrategy;
+1 -4
View File
@@ -9,10 +9,7 @@
import {Injectable, Optional, SkipSelf, OnDestroy} from '@angular/core';
/**
* The OverlayContainer is the container in which all overlays will load.
* It should be provided in the root component to ensure it is properly shared.
*/
/** Container inside which all overlays will render. */
@Injectable()
export class OverlayContainer implements OnDestroy {
protected _containerElement: HTMLElement;
+3 -1
View File
@@ -79,7 +79,9 @@ export const MAT_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = {
exportAs: 'cdkOverlayOrigin',
})
export class OverlayOrigin {
constructor(public elementRef: ElementRef) { }
constructor(
/** Reference to the element on which the directive is applied. */
public elementRef: ElementRef) { }
}
+1 -3
View File
@@ -1,6 +1,4 @@
### Overlay
The `Overlay` service provides a way to open floating panels on the screen.
The `overlay` package provides a way to open floating panels on the screen.
### Creating overlays
Calling `overlay.create()` will return an `OverlayRef` instance. This instance is a handle for
+6 -3
View File
@@ -38,7 +38,9 @@ let defaultConfig = new OverlayConfig();
*/
@Injectable()
export class Overlay {
constructor(public scrollStrategies: ScrollStrategyOptions,
constructor(
/** Scrolling strategies that can be used when creating an overlay. */
public scrollStrategies: ScrollStrategyOptions,
private _overlayContainer: OverlayContainer,
private _componentFactoryResolver: ComponentFactoryResolver,
private _positionBuilder: OverlayPositionBuilder,
@@ -48,7 +50,7 @@ export class Overlay {
/**
* Creates an overlay.
* @param config Config to apply to the overlay.
* @param config Configuration applied to the overlay.
* @returns Reference to the created overlay.
*/
create(config: OverlayConfig = defaultConfig): OverlayRef {
@@ -58,8 +60,9 @@ export class Overlay {
}
/**
* Returns a position builder that can be used, via fluent API,
* Gets a position builder that can be used, via fluent API,
* to construct and configure a position strategy.
* @returns An overlay position builder.
*/
position(): OverlayPositionBuilder {
return this._positionBuilder;
@@ -90,6 +90,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
return this._preferredPositions;
}
/** Attach this position strategy to an overlay. */
attach(overlayRef: OverlayRef): void {
this._overlayRef = overlayRef;
this._pane = overlayRef.overlayElement;
@@ -97,7 +98,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
this._resizeSubscription = this._viewportRuler.change().subscribe(() => this.apply());
}
/** Performs any cleanup after the element is destroyed. */
/** Disposes all resources used by the position strategy. */
dispose() {
this._resizeSubscription.unsubscribe();
}
@@ -156,7 +157,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
}
/**
* This re-aligns the overlay element with the trigger in its last calculated position,
* Re-positions the overlay element with the trigger in its last calculated position,
* even if a position higher in the "preferred positions" list would now fit. This
* allows one to re-align the panel without changing the orientation of the panel.
*/
+11 -2
View File
@@ -28,9 +28,13 @@ export interface OverlayConnectionPosition {
/** The points of the origin element and the overlay element to connect. */
export class ConnectionPositionPair {
/** X-axis attachment point for connected overlay origin. Can be 'start', 'end', or 'center'. */
originX: HorizontalConnectionPos;
/** Y-axis attachment point for connected overlay origin. Can be 'top', 'bottom', or 'center'. */
originY: VerticalConnectionPos;
/** X-axis attachment point for connected overlay. Can be 'start', 'end', or 'center'. */
overlayX: HorizontalConnectionPos;
/** Y-axis attachment point for connected overlay. Can be 'top', 'bottom', or 'center'. */
overlayY: VerticalConnectionPos;
constructor(
@@ -68,6 +72,8 @@ export class ConnectionPositionPair {
* | Scrollable |
* | |
* --------------------------
*
* @docs-private
*/
export class ScrollingVisibility {
isOriginClipped: boolean;
@@ -78,6 +84,9 @@ export class ScrollingVisibility {
/** The change event emitted by the strategy when a fallback position is used. */
export class ConnectedOverlayPositionChange {
constructor(public connectionPair: ConnectionPositionPair,
@Optional() public scrollableViewProperties: ScrollingVisibility) {}
constructor(
/** The position used as a result of this change. */
public connectionPair: ConnectionPositionPair,
/** @docs-private */
@Optional() public scrollableViewProperties: ScrollingVisibility) {}
}
@@ -19,8 +19,10 @@ export class BlockScrollStrategy implements ScrollStrategy {
constructor(private _viewportRuler: ViewportRuler) { }
/** Attaches this scroll strategy to an overlay. */
attach() { }
/** Blocks page-level scroll while the attached overlay is open. */
enable() {
if (this._canBeEnabled()) {
const root = document.documentElement;
@@ -40,6 +42,7 @@ export class BlockScrollStrategy implements ScrollStrategy {
}
}
/** Unblocks page-level scroll while the attached overlay is open. */
disable() {
if (this._isEnabled) {
this._isEnabled = false;
@@ -21,6 +21,7 @@ export class CloseScrollStrategy implements ScrollStrategy {
constructor(private _scrollDispatcher: ScrollDispatcher) { }
/** Attaches this scroll strategy to an overlay. */
attach(overlayRef: OverlayRef) {
if (this._overlayRef) {
throw getMatScrollStrategyAlreadyAttachedError();
@@ -29,6 +30,7 @@ export class CloseScrollStrategy implements ScrollStrategy {
this._overlayRef = overlayRef;
}
/** Enables the closing of the attached on scroll. */
enable() {
if (!this._scrollSubscription) {
this._scrollSubscription = this._scrollDispatcher.scrolled(0).subscribe(() => {
@@ -41,6 +43,7 @@ export class CloseScrollStrategy implements ScrollStrategy {
}
}
/** Disables the closing the attached overlay on scroll. */
disable() {
if (this._scrollSubscription) {
this._scrollSubscription.unsubscribe();
@@ -8,11 +8,12 @@
import {ScrollStrategy} from './scroll-strategy';
/**
* Scroll strategy that doesn't do anything.
*/
/** Scroll strategy that doesn't do anything. */
export class NoopScrollStrategy implements ScrollStrategy {
/** Does nothing, as this scroll strategy is a no-op. */
enable() { }
/** Does nothing, as this scroll strategy is a no-op. */
disable() { }
/** Does nothing, as this scroll strategy is a no-op. */
attach() { }
}
@@ -29,6 +29,7 @@ export class RepositionScrollStrategy implements ScrollStrategy {
private _scrollDispatcher: ScrollDispatcher,
private _config?: RepositionScrollStrategyConfig) { }
/** Attaches this scroll strategy to an overlay. */
attach(overlayRef: OverlayRef) {
if (this._overlayRef) {
throw getMatScrollStrategyAlreadyAttachedError();
@@ -37,6 +38,7 @@ export class RepositionScrollStrategy implements ScrollStrategy {
this._overlayRef = overlayRef;
}
/** Enables repositioning of the attached overlay on scroll. */
enable() {
if (!this._scrollSubscription) {
let throttle = this._config ? this._config.scrollThrottle : 0;
@@ -47,6 +49,7 @@ export class RepositionScrollStrategy implements ScrollStrategy {
}
}
/** Disables repositioning of the attached overlay on scroll. */
disable() {
if (this._scrollSubscription) {
this._scrollSubscription.unsubscribe();
+2 -2
View File
@@ -19,8 +19,6 @@ import {BasePortalHost, ComponentPortal, TemplatePortal} from './portal';
/**
* A PortalHost for attaching portals to an arbitrary DOM element outside of the Angular
* application context.
*
* This is the only part of the portal core that directly touches the DOM.
*/
export class DomPortalHost extends BasePortalHost {
constructor(
@@ -34,6 +32,7 @@ export class DomPortalHost extends BasePortalHost {
/**
* Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver.
* @param portal Portal to be attached
* @returns Reference to the created component.
*/
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component);
@@ -68,6 +67,7 @@ export class DomPortalHost extends BasePortalHost {
/**
* Attaches a template portal to the DOM as an embedded view.
* @param portal Portal to be attached.
* @returns Reference to the created embedded view.
*/
attachTemplatePortal<C>(portal: TemplatePortal<C>): EmbeddedViewRef<C> {
let viewContainer = portal.viewContainerRef;
+2 -5
View File
@@ -23,11 +23,6 @@ import {Portal, TemplatePortal, ComponentPortal, BasePortalHost} from './portal'
/**
* Directive version of a `TemplatePortal`. Because the directive *is* a TemplatePortal,
* the directive instance itself can be attached to a host, enabling declarative use of portals.
*
* Usage:
* <ng-template portal #greeting>
* <p> Hello {{name}} </p>
* </ng-template>
*/
@Directive({
selector: '[cdk-portal], [cdkPortal], [portal]',
@@ -93,6 +88,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy {
* Attach the given ComponentPortal to this PortalHost using the ComponentFactoryResolver.
*
* @param portal Portal to be attached to the portal host.
* @returns Reference to the created component.
*/
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
portal.setAttachedHost(this);
@@ -118,6 +114,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy {
/**
* Attach the given TemplatePortal to this PortlHost as an embedded View.
* @param portal Portal to be attached.
* @returns Reference to the created embedded view.
*/
attachTemplatePortal<C>(portal: TemplatePortal<C>): EmbeddedViewRef<C> {
portal.setAttachedHost(this);
+3 -1
View File
@@ -1,3 +1,5 @@
The `portals` package provides a flexible system for rendering dynamic content into an application.
### Portals
A `Portal `is a piece of UI that can be dynamically rendered to an open slot on the page.
@@ -30,7 +32,7 @@ Used to get a portal from an `<ng-template>`. `TemplatePortalDirectives` *is* a
Usage:
```html
<ng-template cdk-portal>
<ng-template cdkPortal>
<p>The content of this template is captured by the portal.</p>
</ng-template>
+7 -3
View File
@@ -161,8 +161,8 @@ export interface PortalHost {
/**
* Partial implementation of PortalHost that only deals with attaching either a
* ComponentPortal or a TemplatePortal.
* Partial implementation of PortalHost that handles attaching
* ComponentPortal and TemplatePortal.
*/
export abstract class BasePortalHost implements PortalHost {
/** The portal currently attached to the host. */
@@ -179,6 +179,7 @@ export abstract class BasePortalHost implements PortalHost {
return !!this._attachedPortal;
}
/** Attaches a portal. */
attach(portal: Portal<any>): any {
if (!portal) {
throwNullPortalError();
@@ -207,6 +208,7 @@ export abstract class BasePortalHost implements PortalHost {
abstract attachTemplatePortal<C>(portal: TemplatePortal<C>): EmbeddedViewRef<C>;
/** Detaches a previously attached portal. */
detach(): void {
if (this._attachedPortal) {
this._attachedPortal.setAttachedHost(null);
@@ -216,7 +218,8 @@ export abstract class BasePortalHost implements PortalHost {
this._invokeDisposeFn();
}
dispose() {
/** Permanently dispose of this portal host. */
dispose(): void {
if (this.hasAttached()) {
this.detach();
}
@@ -225,6 +228,7 @@ export abstract class BasePortalHost implements PortalHost {
this._isDisposed = true;
}
/** @docs-private */
setDisposeFn(fn: () => void) {
this._disposeFn = fn;
}
+9 -2
View File
@@ -1,3 +1,10 @@
### Scrolling
The `scrolling` package provides helpers for directives that react to scroll events.
Some things to help with scrollling.
### cdkScrollable and ScrollDispatcher
The `cdkScrollable` directive and the `ScrollDispatcher` service to together to allow components to
react to scrolling in any of its ancestor scrolling containers.
The `cdkScrollable` directive should be applied to any element that acts as a scrolling container.
This marks the element as a `Scrollable` and registers it with the `ScrollDispatcher`. The
dispatcher, then, allows components to share both event listeners and knowledge of all of the
scrollable containers in the application.
+1 -1
View File
@@ -12,5 +12,5 @@ import {Directive, TemplateRef} from '@angular/core';
selector: '[cdkStepLabel]',
})
export class CdkStepLabel {
constructor(public template: TemplateRef<any>) { }
constructor(/** @docs-private */ public template: TemplateRef<any>) { }
}
+59
View File
@@ -0,0 +1,59 @@
CDK stepper provides a foundation upon which more concrete stepper varities can be built. A
stepper is a wizard-like workflow that divides content into logical steps
### Behavior captured by CdkStepper
The base CDK version of the stepper primarily manages which step is active. This includes handling
keyboard interactions and exposing an API for advancing or rewinding through the workflow.
#### Linear stepper
A stepper marked as `linear` requires the user to complete previous steps before proceeding.
For each step, the `stepControl` attribute can be set to the top level
`AbstractControl` that is used to check the validity of the step.
There are two possible approaches. One is using a single form for stepper, and the other is
using a different form for each step.
#### Using a single form for the entire stepper
When using a single form for the stepper, any intermediate next/previous buttons within the steps
must be set to `type="button"` in order to prevent submission of the form before all steps are
complete.
#### Using a form for each individual step
When using a form for each step, the workflow is advanced whenever one of the forms is submitted.
### Types of steps
#### Optional step
If completion of a step in linear stepper is not required, then the `optional` attribute can be set
on `CdkStep` in a `linear` stepper.
#### Editable step
By default, steps are editable, which means users can return to previously completed steps and
edit their responses. `editable="true"` can be set on `CdkStep` to change the default.
#### Completed step
By default, the `completed` attribute of a step returns `true` if the step is valid (in case of
linear stepper) and the user has interacted with the step. The user, however, can also override
this default `completed` behavior by setting the `completed` attribute as needed.
### Stepper buttons
There are two button directives to support navigation between different steps:
`CdkStepperNext` and `CdkStepperPrevious`. When placed inside of a step, these will automatically
add click handlers to advance or rewind the workflow, respectively.
### Keyboard interaction
- <kbd>LEFT_ARROW</kbd>: Focuses the previous step header
- <kbd>RIGHT_ARROW</kbd>: Focuses the next step header
- <kbd>ENTER</kbd>, <kbd>SPACE</kbd>: Selects the step that the focus is currently on
- <kbd>TAB</kbd>: Focuses the next tabbable element
- <kbd>TAB</kbd>+<kbd>SHIFT</kbd>: Focuses the previous tabbable element
### Accessibility
The CDK stepper is treated as a tabbed view for accessibility purposes, so it is given
`role="tablist"` by default. The header of step that can be clicked to select the step
is given `role="tab"`, and the content that can be expanded upon selection is given
`role="tabpanel"`. `aria-selected` attribute of step header and `aria-expanded` attribute of
step content is automatically set based on step selection change.
The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`.
+11 -10
View File
@@ -84,27 +84,28 @@ export class CdkStep implements OnChanges {
/** Label of the step. */
@Input() label: string;
/** Whether the user can return to this step once it has been marked as complted. */
@Input()
get editable() { return this._editable; }
set editable(value: any) {
get editable(): boolean { return this._editable; }
set editable(value: boolean) {
this._editable = coerceBooleanProperty(value);
}
private _editable = true;
/** Whether the completion of step is optional or not. */
/** Whether the completion of step is optional. */
@Input()
get optional() { return this._optional; }
set optional(value: any) {
get optional(): boolean { return this._optional; }
set optional(value: boolean) {
this._optional = coerceBooleanProperty(value);
}
private _optional = false;
/** Return whether step is completed or not. */
/** Whether step is marked as completed. */
@Input()
get completed() {
get completed(): boolean {
return this._customCompleted == null ? this._defaultCompleted : this._customCompleted;
}
set completed(value: any) {
set completed(value: boolean) {
this._customCompleted = coerceBooleanProperty(value);
}
private _customCompleted: boolean | null = null;
@@ -140,8 +141,8 @@ export class CdkStepper {
/** Whether the validity of previous steps should be checked or not. */
@Input()
get linear() { return this._linear; }
set linear(value: any) { this._linear = coerceBooleanProperty(value); }
get linear(): boolean { return this._linear; }
set linear(value: boolean) { this._linear = coerceBooleanProperty(value); }
private _linear = false;
/** The index of the selected step. */
+2 -2
View File
@@ -14,7 +14,7 @@ import {ContentChild, Directive, ElementRef, Input, Renderer2, TemplateRef} from
*/
@Directive({selector: '[cdkCellDef]'})
export class CdkCellDef {
constructor(public template: TemplateRef<any>) { }
constructor(/** @docs-private */ public template: TemplateRef<any>) { }
}
/**
@@ -23,7 +23,7 @@ export class CdkCellDef {
*/
@Directive({selector: '[cdkHeaderCellDef]'})
export class CdkHeaderCellDef {
constructor(public template: TemplateRef<any>) { }
constructor(/** @docs-private */ public template: TemplateRef<any>) { }
}
/**
+1 -1
View File
@@ -37,7 +37,7 @@ export abstract class BaseRowDef {
/** Differ used to check if any changes were made to the columns. */
protected _columnsDiffer: IterableDiffer<any>;
constructor(public template: TemplateRef<any>,
constructor(/** @docs-private */ public template: TemplateRef<any>,
protected _differs: IterableDiffers) { }
ngOnChanges(changes: SimpleChanges): void {
+1 -1
View File
@@ -117,6 +117,7 @@ export class CdkTable<T> implements CollectionViewer {
* Accepts a function that takes two parameters, `index` and `item`.
*/
@Input()
get trackBy(): TrackByFunction<T> { return this._trackByFn; }
set trackBy(fn: TrackByFunction<T>) {
if (isDevMode() &&
fn != null && typeof fn !== 'function' &&
@@ -125,7 +126,6 @@ export class CdkTable<T> implements CollectionViewer {
}
this._trackByFn = fn;
}
get trackBy(): TrackByFunction<T> { return this._trackByFn; }
private _trackByFn: TrackByFunction<T>;
/**
+24 -1
View File
@@ -8,6 +8,15 @@ export class ComponentGroup {
/** Name of the component group. */
name: string;
/** Display name of the component group */
displayName: string;
/** Name of the package, either material or cdk */
packageName: string;
/** Display name of the package. */
packageDisplayName: string;
/** Unique id for the component group. */
id: string;
@@ -60,7 +69,17 @@ export class ComponentGrouper implements Processor {
// All of the component documentation is under either `src/lib` or `src/cdk`.
// We group the docs up by the directory immediately under that root.
const groupName = path.relative(basePath, filePath).split(path.sep)[1];
let packageName, packageDisplayName;
if (filePath.includes('cdk')) {
packageName = 'cdk';
packageDisplayName = 'CDK';
} else {
packageName = 'material';
packageDisplayName = 'Material';
}
const displayName = path.relative(basePath, filePath).split(path.sep)[1];
const groupName = packageName + '-' + displayName;
// Get the group for this doc, or, if one does not exist, create it.
let group;
@@ -71,6 +90,10 @@ export class ComponentGrouper implements Processor {
groups.set(groupName, group);
}
group.packageName = packageName;
group.packageDisplayName = packageDisplayName;
group.displayName = displayName;
// Put this doc into the appropriate list in this group.
if (doc.isDirective) {
group.directives.push(doc);
@@ -21,12 +21,12 @@
<div class="docs-api">
<h2>
API reference for Angular Material {$ doc.name $}
API reference for Angular {$ doc.packageDisplayName $} {$ doc.displayName $}
</h2>
<p class="docs-api-module-import">
<code>
import {{$ doc.ngModule.name $}} from '@angular/material';
import {{$ doc.ngModule.name $}} from '@angular/{$ doc.packageName $}';
</code>
</p>
+1 -1
View File
@@ -49,7 +49,7 @@
</thead>
<tr>
<td class="docs-api-method-returns-type-cell">
<code class="docs-api-method-returns-type">{$ method.returnType $}</code>
<code class="docs-api-method-returns-type">{$ method.type $}</code>
</td>
<td class="docs-api-method-returns-description-cell">
<p class="docs-api-method-returns-description">
+29 -13
View File
@@ -59,9 +59,23 @@ const htmlMinifierOptions = {
removeAttributeQuotes: false
};
const markdownOptions = {
// Add syntax highlight using highlight.js
highlight: (code: string, language: string): string => {
if (language) {
// highlight.js expects "typescript" written out, while Github supports "ts".
let lang = language.toLowerCase() === 'ts' ? 'typescript' : language;
return hljs.highlight(lang, code).value;
}
return code;
}
};
/** Generate all docs content. */
task('docs', [
'markdown-docs',
'markdown-docs-cdk',
'highlight-examples',
'api-docs',
'minified-api-docs',
@@ -69,7 +83,7 @@ task('docs', [
'plunker-example-assets',
]);
/** Generates html files from the markdown overviews and guides. */
/** Generates html files from the markdown overviews and guides for material. */
task('markdown-docs', () => {
// Extend the renderer for custom heading anchor rendering
markdown.marked.Renderer.prototype.heading = (text: string, level: number): string => {
@@ -86,19 +100,21 @@ task('markdown-docs', () => {
}
};
return src(['src/lib/**/*.md', 'src/cdk/**/*.md', 'guides/*.md'])
.pipe(markdown({
// Add syntax highlight using highlight.js
highlight: (code: string, language: string): string => {
if (language) {
// highlight.js expects "typescript" written out, while Github supports "ts".
let lang = language.toLowerCase() === 'ts' ? 'typescript' : language;
return hljs.highlight(lang, code).value;
}
return src(['src/lib/**/*.md', 'guides/*.md'])
.pipe(rename({prefix: 'material-'}))
.pipe(markdown(markdownOptions))
.pipe(transform(transformMarkdownFiles))
.pipe(dom(createTagNameAliaser('docs-markdown')))
.pipe(dest('dist/docs/markdown'));
});
return code;
}
}))
// TODO(jelbourn): figure out how to avoid duplicating this task w/ material while still
// disambiguating the output.
/** Generates html files from the markdown overviews and guides for the cdk. */
task('markdown-docs-cdk', () => {
return src(['src/cdk/**/*.md'])
.pipe(rename({prefix: 'cdk-'}))
.pipe(markdown(markdownOptions))
.pipe(transform(transformMarkdownFiles))
.pipe(dom(createTagNameAliaser('docs-markdown')))
.pipe(dest('dist/docs/markdown'));