@blox/material
Version:
Material Components for Angular
1,268 lines (1,257 loc) • 374 kB
JavaScript
import { ɵɵdefineInjectable, Injectable, ElementRef, Directive, Renderer2, HostListener, HostBinding, forwardRef, Inject, Input, ContentChildren, EventEmitter, Optional, Self, Output, ChangeDetectorRef, ContentChild, Host, SkipSelf, SimpleChange, ɵɵinject, QueryList, NgZone, NgModule } from '@angular/core';
import { DOCUMENT, CommonModule } from '@angular/common';
import { util, MDCRippleFoundation } from '@material/ripple';
import { events, ponyfill, focusTrap } from '@material/dom';
import { NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject, ReplaySubject, merge } from 'rxjs';
import { takeUntil, debounceTime } from 'rxjs/operators';
import { MDCCheckboxFoundation } from '@material/checkbox';
import { MDCChipFoundation, MDCChipSetFoundation } from '@material/chips';
import { util as util$1, cssClasses, MDCDialogFoundation } from '@material/dialog';
import { MDCDismissibleDrawerFoundation, MDCModalDrawerFoundation } from '@material/drawer';
import { strings, cssClasses as cssClasses$1, MDCListFoundation } from '@material/list';
import { MDCRadioFoundation } from '@material/radio';
import { MDCFloatingLabelFoundation } from '@material/floating-label';
import { MDCFormFieldFoundation } from '@material/form-field';
import { MDCIconButtonToggleFoundation } from '@material/icon-button';
import { MDCLinearProgressFoundation } from '@material/linear-progress';
import { strings as strings$1, cssClasses as cssClasses$3, DefaultFocusState, MDCMenuFoundation } from '@material/menu';
import { cssClasses as cssClasses$2, util as util$2, MDCMenuSurfaceFoundation, Corner } from '@material/menu-surface';
import { MDCNotchedOutlineFoundation } from '@material/notched-outline';
import { MDCLineRippleFoundation } from '@material/line-ripple';
import { strings as strings$2, cssClasses as cssClasses$4, MDCSelectFoundation } from '@material/select';
import { MDCSliderFoundation } from '@material/slider';
import { util as util$3, MDCSnackbarFoundation, numbers } from '@material/snackbar';
import { MDCSwitchFoundation } from '@material/switch';
import { MDCTabBarFoundation } from '@material/tab-bar';
import { util as util$4, MDCTabScrollerFoundation } from '@material/tab-scroller';
import { MDCTabFoundation } from '@material/tab';
import { MDCFadingTabIndicatorFoundation, MDCSlidingTabIndicatorFoundation } from '@material/tab-indicator';
import { NavigationEnd, Router, RouterLink, RouterLinkWithHref } from '@angular/router';
import { MDCTextFieldIconFoundation, MDCTextFieldHelperTextFoundation, MDCTextFieldFoundation } from '@material/textfield';
import { MDCShortTopAppBarFoundation, MDCFixedTopAppBarFoundation, MDCTopAppBarFoundation } from '@material/top-app-bar';
function asBoolean(value) {
return value != null && `${value}` !== 'false';
}
function asBooleanOrNull(value) {
if (value == null)
return value;
return `${value}` !== 'false';
}
function asNumberOrNull(value) {
if (value == null)
return value;
return +value;
}
const unlisteners = new Map();
class MdcEventRegistry {
constructor() { }
listen(renderer, type, listener, ref, options) {
this.listenElm(renderer, type, listener, ref.nativeElement, options);
}
listenElm(renderer, type, listener, el, options) {
el.addEventListener(type, listener, options);
const unlistener = function () {
el.removeEventListener(type, listener, options);
};
this.registerUnlisten(type, listener, unlistener);
}
registerUnlisten(type, listener, unlistener) {
if (!unlisteners.has(type))
unlisteners.set(type, new WeakMap());
unlisteners.get(type).set(listener, unlistener);
}
unlisten(type, listener) {
if (!unlisteners.has(type))
return;
const unlistenerMap = unlisteners.get(type);
if (!unlistenerMap.has(listener))
return;
unlistenerMap.get(listener)();
unlistenerMap.delete(listener);
}
}
MdcEventRegistry.ɵprov = ɵɵdefineInjectable({ factory: function MdcEventRegistry_Factory() { return new MdcEventRegistry(); }, token: MdcEventRegistry, providedIn: "root" });
MdcEventRegistry.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
MdcEventRegistry.ctorParameters = () => [];
/** @docs-private */
class AbstractMdcRipple {
constructor(_rippleElm, _renderer, _registry, doc) {
this._rippleElm = _rippleElm;
this._renderer = _renderer;
this._registry = _registry;
this.mdcRippleAdapter = {
browserSupportsCssVars: () => util.supportsCssVariables(this.document.defaultView),
isUnbounded: () => this._unbounded,
isSurfaceActive: () => this.isRippleSurfaceActive(),
isSurfaceDisabled: () => this.isRippleSurfaceDisabled(),
addClass: (className) => this.addClassToRipple(className),
removeClass: (className) => this.removeClassFromRipple(className),
containsEventTarget: (target) => this._rippleElm.nativeElement.contains(target),
registerInteractionHandler: (type, handler) => {
if (this.getRippleInteractionElement())
this._registry.listenElm(this._renderer, type, handler, this.getRippleInteractionElement().nativeElement, events.applyPassive());
},
deregisterInteractionHandler: (type, handler) => {
this._registry.unlisten(type, handler);
},
registerDocumentInteractionHandler: (type, handler) => this._registry.listenElm(this._renderer, type, handler, this.document, events.applyPassive()),
deregisterDocumentInteractionHandler: (type, handler) => this._registry.unlisten(type, handler),
registerResizeHandler: (handler) => {
this._registry.listenElm(this._renderer, 'resize', handler, this.document.defaultView);
},
deregisterResizeHandler: (handler) => {
this._registry.unlisten('resize', handler);
},
updateCssVariable: (name, value) => { this.getRippleStylingElement().nativeElement.style.setProperty(name, value); },
computeBoundingRect: () => this.computeRippleBoundingRect(),
getWindowPageOffset: () => ({ x: this.document.defaultView.pageXOffset, y: this.document.defaultView.pageYOffset })
};
/** @internal */
this._rippleFoundation = null;
this._unbounded = false;
this._rippleSurface = null;
// workaround compiler bug when using ViewEngine. Type Document fails compilation
this.document = doc;
}
/** @internal */
initRipple(unbounded = false) {
if (this._rippleFoundation)
throw new Error('initRipple() is called multiple times');
this._unbounded = unbounded;
this._rippleFoundation = new MDCRippleFoundation(this.mdcRippleAdapter);
this._rippleFoundation.init();
}
/** @internal */
destroyRipple() {
if (this._rippleFoundation) {
this._rippleFoundation.destroy();
this._rippleFoundation = null;
}
}
/** @internal */
reinitRipple() {
if (this._rippleFoundation) {
this.destroyRipple();
this.initRipple(this._unbounded);
}
}
/** @internal */
isRippleInitialized() {
return this._rippleFoundation != null;
}
/** @internal */
addRippleSurface(clazz, firstElement = false) {
this.destroyRippleSurface();
this._rippleSurface = this._renderer.createElement('div');
this._renderer.addClass(this._rippleSurface, clazz);
if (firstElement && this._rippleElm.nativeElement.children.length > 0) {
const firstChild = this._rippleElm.nativeElement.children.item(0);
this._renderer.insertBefore(this._rippleElm.nativeElement, this._rippleSurface, firstChild);
}
else
this._renderer.appendChild(this._rippleElm.nativeElement, this._rippleSurface);
return this._rippleSurface;
}
/** @internal */
destroyRippleSurface() {
if (this._rippleSurface) {
this._renderer.removeChild(this._rippleElm.nativeElement, this._rippleSurface);
this._rippleSurface = null;
}
}
activateRipple() {
if (this._rippleFoundation)
this._rippleFoundation.activate();
}
deactivateRipple() {
if (this._rippleFoundation)
this._rippleFoundation.deactivate();
}
layout() {
if (this._rippleFoundation)
this._rippleFoundation.layout();
}
get rippleSurface() {
return new ElementRef(this._rippleSurface);
}
getRippleInteractionElement() {
return this._rippleElm;
}
getRippleStylingElement() {
return this._rippleElm;
}
isRippleUnbounded() {
return this._unbounded;
}
/** @internal */
setRippleUnbounded(value) {
if (!!value !== this._unbounded) {
this._unbounded = !!value;
// despite what the documentation seems to indicate, you can't
// just change the unbounded property of an already initialized
// ripple. The initialization registers different handlers, and won't
// change those registrations when you change the unbounded property.
// Hence we destroy and re-init the whole thing:
this.reinitRipple();
}
}
isRippleSurfaceActive() {
let interactionElm = this.getRippleInteractionElement();
return !!interactionElm && this.isActiveElement(interactionElm.nativeElement);
}
isActiveElement(element) {
return element == null ? false : ponyfill.matches(element, ':active');
}
isRippleSurfaceDisabled() {
let interactionElm = this.getRippleInteractionElement();
return !!interactionElm && !!interactionElm.nativeElement.attributes.getNamedItem('disabled');
}
/** @internal */
addClassToRipple(name) {
this._renderer.addClass(this.getRippleStylingElement().nativeElement, name);
}
/** @internal */
removeClassFromRipple(name) {
this._renderer.removeClass(this.getRippleStylingElement().nativeElement, name);
}
computeRippleBoundingRect() {
return this._rippleElm.nativeElement.getBoundingClientRect();
}
/** @internal */
onFocus() {
if (this._rippleFoundation)
this._rippleFoundation.handleFocus();
}
/** @internal */
onBlur() {
if (this._rippleFoundation)
this._rippleFoundation.handleBlur();
}
}
AbstractMdcRipple.decorators = [
{ type: Directive }
];
AbstractMdcRipple.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: MdcEventRegistry },
{ type: undefined }
];
AbstractMdcRipple.propDecorators = {
onFocus: [{ type: HostListener, args: ['focusin',] }],
onBlur: [{ type: HostListener, args: ['focusout',] }]
};
/**
* Use this directive for adding an icon to an <code>mdcButton</code>. This directive can be
* added to font-style icons (such as <a href="https://material.io/icons/" target="_blank">material icons</a>
* from Google fonts), or with <code>svg</code> elements for svg based icons.
*/
class MdcButtonIconDirective {
constructor() {
/** @internal */
this._cls = true;
/** @internal */
this._ariaHidden = true;
}
}
MdcButtonIconDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcButtonIcon]'
},] }
];
MdcButtonIconDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-button__icon',] }],
_ariaHidden: [{ type: HostBinding, args: ['attr.aria-hidden',] }]
};
/**
* Directive for the label of an <code>mdcButton</code>. Must be a direct child
* of <code>mdcButton</code>.
*/
class MdcButtonLabelDirective {
constructor() {
/** @internal */
this._cls = true;
}
}
MdcButtonLabelDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcButtonLabel]'
},] }
];
MdcButtonLabelDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-button__label',] }]
};
/**
* Material design button. Anchors can also be styled as buttons with this directive.
* Defaults to a button that is flushed with the surface.
* Use the input modifiers to alter the styling, or create your own style
* based on the provided sass-mixins.
*
* For buttons with a trailing icon, you must put the label inside an `mdcButtonLabel`
* directive. For all other buttons it is also recommnded to put the label inside
* an `mdcButtonLabel`, because future version of material-components-web may make
* it's use mandatory.
*
* A ripple (and the required DOM elements for the ripple) will be added automatically.
*/
class MdcButtonDirective extends AbstractMdcRipple {
constructor(_elm, renderer, registry, doc) {
super(_elm, renderer, registry, doc);
this._elm = _elm;
/** @internal */
this._cls = true;
this._raised = false;
this._unelevated = false;
this._outlined = false;
this.addRippleSurface('mdc-button__ripple');
}
ngAfterContentInit() {
this.initRipple();
}
ngOnDestroy() {
this.destroyRipple();
}
/**
* When this input is defined and does not have value false, the button will be elevated
* upon the surface.
*/
get raised() {
return this._raised;
}
set raised(val) {
this._raised = asBoolean(val);
}
/**
* When this input is defined and does not have value false, the button will be styled
* flush with the surface and have a visible border.
*/
get outlined() {
return this._outlined;
}
set outlined(val) {
this._outlined = asBoolean(val);
}
/**
* Set this property to a non false value for a contained button
* flush with the surface.
*/
get unelevated() {
return this._unelevated;
}
set unelevated(val) {
this._unelevated = asBoolean(val);
}
}
MdcButtonDirective.decorators = [
{ type: Directive, args: [{
selector: 'button[mdcButton],a[mdcButton]',
providers: [{ provide: AbstractMdcRipple, useExisting: forwardRef(() => MdcButtonDirective) }]
},] }
];
MdcButtonDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: MdcEventRegistry },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
MdcButtonDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-button',] }],
raised: [{ type: HostBinding, args: ['class.mdc-button--raised',] }, { type: Input }],
outlined: [{ type: HostBinding, args: ['class.mdc-button--outlined',] }, { type: Input }],
unelevated: [{ type: HostBinding, args: ['class.mdc-button--unelevated',] }, { type: Input }]
};
const BUTTON_DIRECTIVES = [
MdcButtonIconDirective,
MdcButtonLabelDirective,
MdcButtonDirective
];
/** @docs-private */
class AbstractMdcIcon extends AbstractMdcRipple {
constructor(_elm, renderer, registry, doc) {
super(_elm, renderer, registry, doc);
this._elm = _elm;
}
}
AbstractMdcIcon.decorators = [
{ type: Directive }
];
AbstractMdcIcon.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: MdcEventRegistry },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
/**
* Directive for an area that displays a custom background-image. See the <code>size</code>
* property for the sizing of the image.
* If used, this directive should be put inside the card itself (<code>MdcCardDirective</code>).
* Add an <code>mdcCardMediaContent</code> as sub-element for displaying a title, text,
* or icon on top of the background image.
*/
class MdcCardMediaDirective {
constructor() {
/** @internal */
this._cls = true;
this._size = 'cover';
}
/** @internal */
get _square() {
return this._size === 'square';
}
/** @internal */
get _size2() {
return this._size === '16:9';
}
/**
* Directive to select size to which this element's background-image should
* be scaled. Can be one of 'cover', '16:9', or 'square'. The default value
* is 'cover'.
*/
get size() {
return this._size;
}
set size(val) {
this._size = val;
}
}
MdcCardMediaDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCardMedia]',
},] }
];
MdcCardMediaDirective.ctorParameters = () => [];
MdcCardMediaDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card__media',] }],
_square: [{ type: HostBinding, args: ['class.mdc-card__media--square',] }],
_size2: [{ type: HostBinding, args: ['class.mdc-card__media--16-9',] }],
size: [{ type: Input }]
};
/**
* Directive for displaying text on top of a <code>mdcCardMedia</code> element.
* This directive should be used as child element of the <code>mdcCardMedia</code>, and
* creates an absolutely positioned box the same size as the media area.
*/
class MdcCardMediaContentDirective {
constructor() {
/** @internal */
this._cls = true;
}
}
MdcCardMediaContentDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCardMediaContent]'
},] }
];
MdcCardMediaContentDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card__media-content',] }]
};
/**
* Directive for displaying the button card actions. Composed of one or more
* card actions, which must be buttons that have the <code>MdcButtonDirective</code>.
* This directive should be placed inside an <code>MdcCardActionsDirective</code>.
*/
class MdcCardActionButtonsDirective {
constructor() {
/** @internal */
this._cls = true;
}
}
MdcCardActionButtonsDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCardActionButtons]'
},] }
];
MdcCardActionButtonsDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card__action-buttons',] }]
};
/**
* Directive for displaying the icon card actions. Composed of one or more
* card actions, which must be icons (for instance <code>mdcIconButton</code>.
* This directive should be placed inside an <code>MdcCardActionsDirective</code>.
*/
class MdcCardActionIconsDirective {
constructor() {
/** @internal */
this._cls = true;
}
}
MdcCardActionIconsDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCardActionIcons]'
},] }
];
MdcCardActionIconsDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card__action-icons',] }]
};
/**
* Directive for showing the different actions a user can take. Use
* <code>mdcButton</code>, or <code>mdcIconButton</code> as child elements.
* If you want to use both buttons and icons in the same row, wrap them in
* <code>mdcCardActionButtons</code>, and <code>mdcCardActionIcons</code> directives.
*/
class MdcCardActionsDirective {
constructor(renderer) {
this.renderer = renderer;
/** @internal */
this._cls = true;
this._initialized = false;
this._fullBleed = false;
}
ngAfterContentInit() {
this._initialized = true;
this._initButtons();
this._initIcons();
this._buttons.changes.subscribe(() => {
this._initButtons();
});
this._icons.changes.subscribe(() => {
this._initIcons();
});
}
_initButtons() {
if (this._initialized)
this._buttons.forEach(btn => {
this.renderer.addClass(btn._elm.nativeElement, 'mdc-card__action');
this.renderer.addClass(btn._elm.nativeElement, 'mdc-card__action--button');
});
}
_initIcons() {
if (this._initialized)
this._icons.forEach(icon => {
this.renderer.addClass(icon._elm.nativeElement, 'mdc-card__action');
this.renderer.addClass(icon._elm.nativeElement, 'mdc-card__action--icon');
});
}
/**
* When this input is defined and does not have value false, the contained
* button takes up the entire width of the action row. This should be used only when
* there is a single button contained in the directive.
*/
get fullBleed() {
return this._fullBleed;
}
set fullBleed(val) {
this._fullBleed = asBoolean(val);
}
}
MdcCardActionsDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCardActions]',
},] }
];
MdcCardActionsDirective.ctorParameters = () => [
{ type: Renderer2 }
];
MdcCardActionsDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card__actions',] }],
_buttons: [{ type: ContentChildren, args: [MdcButtonDirective, { descendants: true },] }],
_icons: [{ type: ContentChildren, args: [AbstractMdcIcon, { descendants: true },] }],
fullBleed: [{ type: HostBinding, args: ['class.mdc-card__actions--full-bleed',] }, { type: Input }]
};
/**
* Directive for the main tappable area of the card (so should be a child of <code>mdcCard</code>).
* Typically contains most (or all) card content except <code>mdcCardActions</code>.
* Only applicable to cards that have a primary action that the main surface should trigger.
*/
class MdcCardPrimaryActionDirective extends AbstractMdcRipple {
constructor(elm, renderer, registry, doc) {
super(elm, renderer, registry, doc);
this.elm = elm;
/** @internal */
this._cls = true;
}
ngAfterContentInit() {
if (!this.elm.nativeElement.hasAttribute('tabindex'))
// unless overridden, make the action tabbable:
this.elm.nativeElement.tabIndex = 0;
this.initRipple();
}
ngOnDestroy() {
this.destroyRipple();
}
}
MdcCardPrimaryActionDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCardPrimaryAction]',
},] }
];
MdcCardPrimaryActionDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: MdcEventRegistry },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
MdcCardPrimaryActionDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card__primary-action',] }]
};
/**
* Directive for a material designed card. The card can be composed with the following directives:
* <code>MdcCardMediaDirective</code>, <code>MdcCardActionsDirective</code>
*/
class MdcCardDirective {
constructor() {
/** @internal */
this._cls = true;
this._outlined = false;
}
/**
* When this input is set to a value other than false, the card will have a
* hairline stroke instead of a shadow.
*/
get outlined() {
return this._outlined;
}
set outlined(val) {
this._outlined = asBoolean(val);
}
}
MdcCardDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCard]'
},] }
];
MdcCardDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-card',] }],
outlined: [{ type: HostBinding, args: ['class.mdc-card--outlined',] }, { type: Input }]
};
const CARD_DIRECTIVES = [
MdcCardMediaDirective,
MdcCardMediaContentDirective,
MdcCardActionButtonsDirective,
MdcCardActionIconsDirective,
MdcCardActionsDirective,
MdcCardPrimaryActionDirective,
MdcCardDirective
];
/** @docs-private */
class AbstractMdcInput {
}
/**
* Directive for the input element of an <code>MdcCheckboxDirective</code>.
*/
class MdcCheckboxInputDirective extends AbstractMdcInput {
constructor(_elm, _cntr) {
super();
this._elm = _elm;
this._cntr = _cntr;
/** @internal */
this._cls = true;
this.onDestroy$ = new Subject();
this._id = null;
this._disabled = false;
this._checked = false;
this._indeterminate = false;
/** @internal */
this._checkedChange = new EventEmitter();
/** @internal */
this._indeterminateChange = new EventEmitter();
/** @internal */
this._disabledChange = new EventEmitter();
}
ngOnInit() {
var _a;
(_a = this._cntr) === null || _a === void 0 ? void 0 : _a.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((value) => {
this.updateValue(value, true);
});
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
/** @docs-private */
get id() {
return this._id;
}
set id(value) {
this._id = value;
}
/** @docs-private */
get disabled() {
return this._cntr ? !!this._cntr.disabled : this._disabled;
}
set disabled(value) {
const newVal = asBoolean(value);
if (newVal != this._disabled) {
this._disabled = asBoolean(newVal);
this._disabledChange.emit(newVal);
}
}
/** @docs-private */
get checked() {
return this._checked;
}
set checked(value) {
this.updateValue(value, false);
}
updateValue(value, fromControl) {
// When the 'checked' property is the source of the change, we want to coerce boolean
// values using asBoolean, so that initializing with an attribute with no value works
// as expected.
// When the NgControl is the source of the change we don't want that. The value should
// be interpreted like NgControl/NgForms handles non-boolean values when binding.
const newVal = fromControl ? !!value : asBoolean(value);
if (newVal !== this._checked) {
this._checked = newVal;
this._checkedChange.emit(newVal);
}
if (!fromControl && this._cntr && newVal !== this._cntr.value) {
this._cntr.control.setValue(newVal);
}
}
/** @docs-private */
get indeterminate() {
return this._indeterminate;
}
set indeterminate(value) {
const newVal = asBoolean(value);
if (newVal !== this._indeterminate) {
this._indeterminate = newVal;
Promise.resolve().then(() => this._indeterminateChange.emit(newVal));
}
}
// We listen to click-event instead of change-event, because IE doesn't fire the
// change-event when an indeterminate checkbox is clicked. There's no need to
// also listen to change-events.
_onChange() {
// only update the checked state from click if there is no control for which we already
// listen to value changes:
if (!this._cntr)
this.checked = this._elm.nativeElement.checked;
this.indeterminate = this._elm.nativeElement.indeterminate;
}
}
MdcCheckboxInputDirective.decorators = [
{ type: Directive, args: [{
selector: 'input[mdcCheckboxInput][type=checkbox]',
providers: [{ provide: AbstractMdcInput, useExisting: forwardRef(() => MdcCheckboxInputDirective) }]
},] }
];
MdcCheckboxInputDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: NgControl, decorators: [{ type: Optional }, { type: Self }] }
];
MdcCheckboxInputDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-checkbox__native-control',] }],
_checkedChange: [{ type: Output }],
_indeterminateChange: [{ type: Output }],
_disabledChange: [{ type: Output }],
id: [{ type: HostBinding }, { type: Input }],
disabled: [{ type: HostBinding }, { type: Input }],
checked: [{ type: HostBinding }, { type: Input }],
indeterminate: [{ type: HostBinding }, { type: Input }],
_onChange: [{ type: HostListener, args: ['click',] }]
};
/**
* Directive for creating a Material Design checkbox. The checkbox is driven by an
* underlying native checkbox input, which must use the <code>MdcCheckboxInputDirective</code>
* directive.
* The current implementation will add all other required DOM elements (such as the
* background and ripple).
* Future implementations will also support supplying (customized) background
* elements.
*
* This directive can be used together with an <code>mdcFormField</code> to
* easily position checkboxes and their labels, see
* <a href="/components/form-field">mdcFormField</a>.
*/
class MdcCheckboxDirective extends AbstractMdcRipple {
constructor(renderer, root, registry, doc) {
super(root, renderer, registry, doc);
this.root = root;
/** @internal */
this._cls = true;
this.onDestroy$ = new Subject();
this.onInputChange$ = new Subject();
this.mdcAdapter = {
addClass: (className) => this._renderer.addClass(this.root.nativeElement, className),
removeClass: (className) => this._renderer.removeClass(this.root.nativeElement, className),
setNativeControlAttr: (attr, value) => this._renderer.setAttribute(this._input._elm.nativeElement, attr, value),
removeNativeControlAttr: (attr) => this._renderer.removeAttribute(this._input._elm.nativeElement, attr),
forceLayout: () => this.root.nativeElement.offsetWidth,
isAttachedToDOM: () => !!this._input,
hasNativeControl: () => !!this._input,
isChecked: () => this._input._elm.nativeElement.checked,
isIndeterminate: () => this._input._elm.nativeElement.indeterminate,
setNativeControlDisabled: (disabled) => this._input.disabled = disabled
};
/** @internal */
this._foundation = null;
this.addRippleSurface('mdc-checkbox__ripple');
}
ngAfterContentInit() {
MdcCheckboxDirective.addBackground(this._rippleElm, this._renderer);
this.initRipple(true);
if (this._input) {
this._foundation = new MDCCheckboxFoundation(this.mdcAdapter);
this._foundation.init();
}
this._inputs.changes.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
this.reinitRipple();
if (this._foundation)
this._foundation.destroy();
if (this._input) {
this._foundation = new MDCCheckboxFoundation(this.mdcAdapter);
this._foundation.init();
}
else
this._foundation = null;
this.subscribeInputChanges();
});
this.subscribeInputChanges();
}
ngOnDestroy() {
this.onInputChange$.next();
this.onInputChange$.complete();
this.onDestroy$.next();
this.onDestroy$.complete();
if (this._foundation) {
this._foundation.destroy();
this._foundation = null;
}
this.destroyRipple();
}
subscribeInputChanges() {
var _a, _b, _c;
this.onInputChange$.next();
(_a = this._input) === null || _a === void 0 ? void 0 : _a._indeterminateChange.asObservable().pipe(takeUntil(this.onInputChange$)).subscribe(() => { var _a; return (_a = this._foundation) === null || _a === void 0 ? void 0 : _a.handleChange(); });
(_b = this._input) === null || _b === void 0 ? void 0 : _b._checkedChange.asObservable().pipe(takeUntil(this.onInputChange$)).subscribe(() => { var _a; return (_a = this._foundation) === null || _a === void 0 ? void 0 : _a.handleChange(); });
(_c = this._input) === null || _c === void 0 ? void 0 : _c._disabledChange.asObservable().pipe(takeUntil(this.onInputChange$)).subscribe(val => { var _a; return (_a = this._foundation) === null || _a === void 0 ? void 0 : _a.setDisabled(val); });
}
static addBackground(elm, renderer) {
let path = renderer.createElement('path', 'svg');
renderer.addClass(path, 'mdc-checkbox__checkmark-path');
renderer.setAttribute(path, 'fill', 'none');
renderer.setAttribute(path, 'd', 'M1.73,12.91 8.1,19.28 22.79,4.59');
let svg = renderer.createElement('svg', 'svg');
renderer.appendChild(svg, path);
renderer.addClass(svg, 'mdc-checkbox__checkmark');
renderer.setAttribute(svg, 'viewBox', '0 0 24 24');
let mixedmark = renderer.createElement('div');
renderer.addClass(mixedmark, 'mdc-checkbox__mixedmark');
let bg = renderer.createElement('div');
renderer.appendChild(bg, svg);
renderer.appendChild(bg, mixedmark);
renderer.addClass(bg, 'mdc-checkbox__background');
renderer.appendChild(elm.nativeElement, bg);
}
/** @internal */
getRippleInteractionElement() {
var _a;
return (_a = this._input) === null || _a === void 0 ? void 0 : _a._elm;
}
/** @internal */
onAnimationEnd() {
var _a;
(_a = this._foundation) === null || _a === void 0 ? void 0 : _a.handleAnimationEnd();
}
/** @internal */
get _input() {
return this._inputs && this._inputs.length > 0 ? this._inputs.first : null;
}
}
MdcCheckboxDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcCheckbox]'
},] }
];
MdcCheckboxDirective.ctorParameters = () => [
{ type: Renderer2 },
{ type: ElementRef },
{ type: MdcEventRegistry },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
MdcCheckboxDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-checkbox',] }],
_inputs: [{ type: ContentChildren, args: [MdcCheckboxInputDirective,] }],
onAnimationEnd: [{ type: HostListener, args: ['animationend',] }]
};
const CHECKBOX_DIRECTIVES = [
MdcCheckboxInputDirective,
MdcCheckboxDirective
];
/**
* @license
* Copyright 2020 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Priorities for the announce function
*/
var AnnouncerPriority;
(function (AnnouncerPriority) {
AnnouncerPriority["POLITE"] = "polite";
AnnouncerPriority["ASSERTIVE"] = "assertive";
})(AnnouncerPriority || (AnnouncerPriority = {}));
/**
* Data attribute added to live region element.
*/
const DATA_MDC_DOM_ANNOUNCE = 'data-mdc-dom-announce';
/**
* Announces the given message with optional priority, defaulting to "polite"
*/
function announce(message, priority) {
Announcer.getInstance().say(message, priority);
}
class Announcer {
// Constructor made private to ensure only the singleton is used
constructor() {
this.liveRegions = new Map();
}
static getInstance() {
if (!Announcer.instance) {
Announcer.instance = new Announcer();
}
return Announcer.instance;
}
say(message, priority = AnnouncerPriority.POLITE) {
const liveRegion = this.getLiveRegion(priority);
// Reset the region to pick up the message, even if the message is the
// exact same as before.
liveRegion.textContent = '';
// Timeout is necessary for screen readers like NVDA and VoiceOver.
setTimeout(() => {
liveRegion.textContent = message;
document.addEventListener('click', clearLiveRegion);
}, 1);
function clearLiveRegion() {
liveRegion.textContent = '';
document.removeEventListener('click', clearLiveRegion);
}
}
getLiveRegion(priority) {
const existingLiveRegion = this.liveRegions.get(priority);
if (existingLiveRegion &&
document.body.contains(existingLiveRegion)) {
return existingLiveRegion;
}
const liveRegion = this.createLiveRegion(priority);
this.liveRegions.set(priority, liveRegion);
return liveRegion;
}
createLiveRegion(priority) {
const el = document.createElement('div');
el.style.position = 'absolute';
el.style.top = '-9999px';
el.style.left = '-9999px';
el.style.height = '1px';
el.style.overflow = 'hidden';
el.setAttribute('aria-atomic', 'true');
el.setAttribute('aria-live', priority);
el.setAttribute(DATA_MDC_DOM_ANNOUNCE, 'true');
document.body.appendChild(el);
return el;
}
}
var ChipEventSource;
(function (ChipEventSource) {
ChipEventSource[ChipEventSource["primary"] = 0] = "primary";
ChipEventSource[ChipEventSource["trailing"] = 1] = "trailing";
ChipEventSource[ChipEventSource["none"] = 2] = "none";
})(ChipEventSource || (ChipEventSource = {}));
;
/**
* Directive for the (optional) leading or trailing icon of an `mdcChip`.
* Depending on the position within the `mdcChip` the icon will determine
* whether it is a leading or trailing icon.
* Trailing icons must implement the functionality to remove the chip from the set, and
* must only be added to input chips (`mdcChipSet="input"`). Chips with a trailing
* icon must implement the `remove` event. Trailing icons should be wrapped
* inside an `mdcChipCell`.
*/
class MdcChipIconDirective {
constructor(_elm, _rndr, _cdRef) {
this._elm = _elm;
this._rndr = _rndr;
this._cdRef = _cdRef;
/** @internal */
this._cls = true;
/** @internal */
this._leading = false;
/**
* Event emitted for trailing icon user interactions. Please note that chip removal should be
* handled by the `remove` output of the chip, *not* by a handler for this output.
*/
this.interact = new EventEmitter();
/** @internal */
this._trailing = false;
this._originalTabindex = null;
this.__tabindex = -1;
this.__role = null;
/** @internal */
this._chip = null;
this.__role = _elm.nativeElement.getAttribute('role');
let tabIndex = _elm.nativeElement.getAttribute('tabindex');
if (tabIndex) {
this._originalTabindex = +tabIndex;
this.__tabindex = +tabIndex;
}
}
ngOnDestroy() {
this._chip = null;
}
/** @internal */
get _tabindex() {
return this._trailing ? this.__tabindex : this._originalTabindex;
}
/** @internal */
set _tabindex(val) {
this.__tabindex = val == null ? -1 : val;
}
/** @internal */
get _role() {
if (this.__role)
return this.__role;
return this._trailing ? 'button' : null;
}
/** @internal */
_handleClick(event) {
var _a;
if (this._chip && this._trailing)
(_a = this._chip._foundation) === null || _a === void 0 ? void 0 : _a.handleTrailingIconInteraction(event);
}
/** @internal */
_handleInteraction(event) {
var _a;
if (this._chip && this._trailing)
(_a = this._chip._foundation) === null || _a === void 0 ? void 0 : _a.handleTrailingIconInteraction(event);
}
}
MdcChipIconDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcChipIcon]'
},] }
];
MdcChipIconDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: ChangeDetectorRef }
];
MdcChipIconDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-chip__icon',] }],
_leading: [{ type: HostBinding, args: ['class.mdc-chip__icon--leading',] }],
interact: [{ type: Output }],
_trailing: [{ type: HostBinding, args: ['class.mdc-chip__icon--trailing',] }],
_tabindex: [{ type: HostBinding, args: ['attr.tabindex',] }],
_role: [{ type: HostBinding, args: ['attr.role',] }],
_handleClick: [{ type: HostListener, args: ['click', ['$event'],] }],
_handleInteraction: [{ type: HostListener, args: ['keydown', ['$event'],] }]
};
/**
* Directive for the text of an `mdcChip`. Must be contained in an `mdcChipPrimaryAction`
* directive.
*/
class MdcChipTextDirective {
constructor(_elm) {
this._elm = _elm;
this._cls = true;
}
}
MdcChipTextDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcChipText]'
},] }
];
MdcChipTextDirective.ctorParameters = () => [
{ type: ElementRef }
];
MdcChipTextDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-chip__text',] }]
};
/**
* Directive for the primary action element of a chip. The `mdcChipPrimaryAction` must
* contain the `mdcChipText` directive, and be contained by an `mdcChipCell` directive.
*/
class MdcChipPrimaryActionDirective {
constructor(_elm) {
this._elm = _elm;
/** @internal */
this._cls = true;
this.__tabindex = -1;
/** @internal */
this.__role = 'button';
this.__tabindex = +(this._elm.nativeElement.getAttribute('tabindex') || -1);
}
/** @internal */
get _role() {
return this.__role;
}
/** @internal */
get _tabindex() {
return this.__tabindex;
}
/** @internal */
set _tabindex(val) {
this.__tabindex = val;
}
}
MdcChipPrimaryActionDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcChipPrimaryAction]'
},] }
];
MdcChipPrimaryActionDirective.ctorParameters = () => [
{ type: ElementRef }
];
MdcChipPrimaryActionDirective.propDecorators = {
_cls: [{ type: HostBinding, args: ['class.mdc-chip__primary-action',] }],
_role: [{ type: HostBinding, args: ['attr.role',] }],
_tabindex: [{ type: HostBinding, args: ['attr.tabindex',] }]
};
/**
* Directive for the main content of a chip, or for an optional trailing
* action on `input` chips. This directive must contain an
* `mdcChipPrimaryActione` or an `mdcChipIcon` (when used for the trailing action).
* An `mdcChipCell` must always be the direct child of an `mdcChip`.
*/
class MdcChipCellDirective {
constructor(_elm) {
this._elm = _elm;
/** @internal */
this._role = 'gridcell';
}
}
MdcChipCellDirective.decorators = [
{ type: Directive, args: [{
selector: '[mdcChipCell]'
},] }
];
MdcChipCellDirective.ctorParameters = () => [
{ type: ElementRef }
];
MdcChipCellDirective.propDecorators = {
_role: [{ type: HostBinding, args: ['attr.role',] }]
};
/**
* Directive for a chip. Chips must be child elements of an `mdcChipSet`,
* and must contain an `mdcChipCell`, and may additionally contain an `mdcChipIcon` for
* the leading icon. An optional trailing icon must be wrapped in a second `mdcChipCell`.
*/
class MdcChipDirective extends AbstractMdcRipple {
constructor(_elm, rndr, registry, doc) {
super(_elm, rndr, registry, doc);
this._elm = _elm;
/** @internal */
this._cls = true;
/** @internal */
this._role = 'row';
this.initialized = false;
this.selectedMem = false;
this.removableMem = true;
this._value = null;
/**
* Event emitted for user interaction with the chip.
*/
this.interact = new EventEmitter();
/**
* Event emitted when the user has removed (by clicking the trailing icon) the chip.
* This event must be implemented when the `removable` property is set, and the chip
* has a trailing icon. The implementation must remove the chip from the set.
* Without such implementation the directive will animate the chip out of vision,
* but will not remove the chip from the DOM.
*/
this.remove = new EventEmitter();
/**
* Event emitted when a navigation event has occured.
*/
this.navigation = new EventEmitter();
/**
* Event emitted when the chip changes from not-selected to selected state or vice versa
* (for filter and choice chips).
*/
this.selectedChange = new EventEmitter();
// Like selectedChange, but only the events that should go to the chipset (i.e. not including the ones initiated by the chipset)
/** @internal */
this._selectedForChipSet = new EventEmitter();
/** @internal */
this._notifyRemoval = new EventEmitter();
/** @internal */
this._set = null;
this._checkmark = null;
this._leadingIcon = null;
this._trailingIcon = null;
this._adapter = {
addClass: (className) => {
const hasClass = this._elm.nativeElement.classList.contains(className);
this._renderer.addClass(this._elm.nativeElement, className);
if (!hasClass && className === 'mdc-chip--selected') {
this.selectedMem = true;
this.selectedChange.emit(true);
}
},
removeClass: (className) => {
const hasClass = this._elm.nativeElement.classList.contains(className);
this._renderer.removeClass(this._elm.nativeElement, className);
if (hasClass && className === 'mdc-chip--selected') {
this.selectedMem = false;
this.selectedChange.emit(false);
}
},
hasClass: (className) => this._elm.nativeElement.classList.contains(className),
addClassToLeadingIcon: (className) => this._leadingIcon && this._renderer.addClass(this._leadingIcon._elm.nativeElement, className),
removeClassFromLeadingIcon: (className) => this._leadingIcon && this._renderer.removeClass(this._leadingIcon._elm.nativeElement, className),
eventTargetHasClass: (target, className) => !!target && target.classList.contains(className),
getAttribute: (attr) => this._elm.nativeElement.getAttribute(attr),
notifyInteraction: () => this.interact.emit(),
notifySelection: (selected, chipSetShouldIgnore) => {
if (!chipSetShouldIgnore)
this._selectedForChipSet.emit(selected);
},
notifyTrailingIconInteraction: () => this._trailingIcon.interact.emit(),
notifyRemoval: (removedAnnouncement) => this._notifyRemoval.emit({ removedAnnouncement }),
notifyNavigation: (key, source) => this.navigation.emit({ key, source: source }),
getComputedStyleValue: (propertyName) => getComputedStyle(this._elm.nativeElement).getPropertyValue(propertyName),
setStyleProperty: (style, value) => this._renderer.setStyle(this._elm.nativeElement, style, value),
hasLeadingIcon: () => !!this._leadingIcon,
getRootBoundingClientRect: () => this._elm.nativeElement.getBoundingClientRect(),
getCheckmarkBoundingClientRect: () => { var _a; return ((_a = this._checkmark) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || null; },
setPrimaryActionAttr: (attr, value) => this._primaryAction && this._renderer.setAttribute(this._primaryAction._elm.nativeElement, attr, value),
focusPrimaryAction: () => { var _a; return (_a = this._primaryAction) === null || _a === void 0 ? void 0 : _a._elm.nativeElement.focus(); },
hasTrailingAction: () => !!this._trailingIcon,
setTrailingActionAttr: (attr, value) => this._trailingIcon && this._renderer.setAttribute(this._trailingIcon._elm.nativeElement, attr, value),
focusTrailingAction: () => { var _a; return (_a = this._trailingIcon) === null || _a === void 0 ? void 0 : _a._elm.nativeElement.focus(); },
isRTL: () => getComputedStyle(this._elm.nativeElement).getPropertyValue('direction') === 'rtl'
};
/** @internal */
this._foundation = new MDCChipFoundation(this._adapter);
this._uniqueValue = `mdc-chip-${MdcChipDirective.nextValue++}`;
}
ngAfterContentInit() {
this.initActions();
this.initIcons();
this._icons.changes.subscribe(() => this._reInit());
this._texts.changes.subscribe(() => this._reInit());
this._primaryActions.changes.subscribe(() => this._reInit());
this._chipCells.changes.subscribe(() => this._reInit());
this.addRippleSurface('mdc-chip__ripple');
this.initCheckMark();
this.initRipple();
this._foundation.init();
if (this.selectedMem)
// triggers setting the foundation selected state (and possibly for other [choice] chips too):
this.selected = this.selectedMem;
if (!this.removableMem)
this._foundation.setShouldRemoveOnTrailingIconClick(this.removableMem);
this.initialized = true;
}