carbon-components-angular
Version:
Next generation components
915 lines • 93.4 kB
JavaScript
import { Component, Input, Output, EventEmitter, ContentChild, ViewChild, HostListener, HostBinding, TemplateRef } from "@angular/core";
import { NG_VALUE_ACCESSOR } from "@angular/forms";
// Observable import is required here so typescript can compile correctly
import { of, Subscription } from "rxjs";
import { AbstractDropdownView } from "./abstract-dropdown-view.class";
import { getScrollableParents } from "carbon-components-angular/utils";
import { hasScrollableParents } from "carbon-components-angular/utils";
import * as i0 from "@angular/core";
import * as i1 from "carbon-components-angular/i18n";
import * as i2 from "./dropdown.service";
import * as i3 from "carbon-components-angular/utils";
import * as i4 from "@angular/common";
import * as i5 from "carbon-components-angular/icon";
/**
* Drop-down lists enable users to select one or more items from a list.
*
* #### Opening behavior/List DOM placement
* By default the dropdown will try to figure out the best placement for the dropdown list.
*
* If it's not contained within any scrolling elements, it will open inline, if it _is_
* contained within a scrolling container it will try to open in the body, or an `cds-placeholder`.
*
* To control this behavior you can use the `appendInline` input:
* - `[appendInline]="null"` is the default (auto detection)
* - `[appendInline]="false"` will always append to the body/`cds-placeholder`
* - `[appendInline]="true"` will always append inline (next to the dropdown button)
*
* Get started with importing the module:
*
* ```typescript
* import { DropdownModule } from 'carbon-components-angular';
* ```
*
* [See demo](../../?path=/story/components-dropdown--basic)
*/
export class Dropdown {
/**
* Creates an instance of Dropdown.
*/
constructor(elementRef, i18n, dropdownService, elementService) {
this.elementRef = elementRef;
this.i18n = i18n;
this.dropdownService = dropdownService;
this.elementService = elementService;
this.id = `dropdown-${Dropdown.dropdownCount++}`;
/**
* Hide label while keeping it accessible for screen readers
*/
this.hideLabel = false;
/**
* Value displayed if no item is selected.
*/
this.placeholder = "";
/**
* The selected value from the `Dropdown`. Can be a string or template.
*/
this.displayValue = "";
/**
* Sets the optional clear button tooltip text.
*/
this.clearText = this.i18n.get().DROPDOWN.CLEAR;
/**
* Size to render the dropdown field.
*/
this.size = "md";
/**
* Defines whether or not the `Dropdown` supports selecting multiple items as opposed to single
* item selection.
*/
this.type = "single";
/**
* @deprecated since v5 - Use `cdsLayer` directive instead
* `light` or `dark` dropdown theme
*/
this.theme = "dark";
/**
* Set to `true` to disable the dropdown.
*/
this.disabled = false;
/**
* Set to `true` for a loading dropdown.
*/
this.skeleton = false;
/**
* Set to `true` for an inline dropdown.
*/
this.inline = false;
/**
* Set to `true` for a dropdown without arrow key activation.
*/
this.disableArrowKeys = false;
/**
* Set to `true` for invalid state.
*/
this.invalid = false;
/**
* Set to `true` to show a warning (contents set by warningText)
*/
this.warn = false;
/**
* set to `true` to place the dropdown view inline with the component
*/
this.appendInline = null;
/**
* Specify feedback (mode) of the selection.
* `top`: selected item jumps to top
* `fixed`: selected item stays at it's position
* `top-after-reopen`: selected item jump to top after reopen dropdown
*/
this.selectionFeedback = "top-after-reopen";
/**
* Accessible label for the button that opens the dropdown list.
* Defaults to the `DROPDOWN.OPEN` value from the i18n service.
*/
this.menuButtonLabel = this.i18n.get().DROPDOWN.OPEN;
/**
* Provides the label for the "# selected" text.
* Defaults to the `DROPDOWN.SELECTED` value from the i18n service.
*/
this.selectedLabel = this.i18n.get().DROPDOWN.SELECTED;
/**
* Emits selection events.
*/
this.selected = new EventEmitter();
/**
* Emits event notifying to other classes that the `Dropdown` has been closed (collapsed).
*/
this.onClose = new EventEmitter();
/**
* Emits event notifying to other classes that the `Dropdown` has been closed (collapsed).
*/
this.close = new EventEmitter();
this.hostClass = true;
/**
* Set to `true` if the dropdown is closed (not expanded).
*/
this.menuIsClosed = true;
/**
* controls whether the `drop-up` class is applied
*/
this._dropUp = false;
// .bind creates a new function, so we declare the methods below
// but .bind them up here
this.noop = this._noop.bind(this);
this.outsideClick = this._outsideClick.bind(this);
this.outsideKey = this._outsideKey.bind(this);
this.keyboardNav = this._keyboardNav.bind(this);
this.visibilitySubscription = new Subscription();
this.onTouchedCallback = this._noop;
// primarily used to capture and propagate input to `writeValue` before the content is available
this._writtenValue = [];
/**
* function passed in by `registerOnChange`
*/
this.propagateChange = (_) => { };
}
get writtenValue() {
return this._writtenValue;
}
set writtenValue(val) {
if (val && val.length === 0) {
this.clearSelected();
}
this._writtenValue = val;
}
/**
* Updates the `type` property in the `@ContentChild`.
* The `type` property specifies whether the `Dropdown` allows single selection or multi selection.
*/
ngOnInit() {
if (this.view) {
this.view.type = this.type;
}
}
/**
* Initializes classes and subscribes to events for single or multi selection.
*/
ngAfterContentInit() {
if (!this.view) {
return;
}
if ((this.writtenValue && this.writtenValue.length) || typeof this.writtenValue === "number") {
this.writeValue(this.writtenValue);
}
this.view.type = this.type;
this.view.size = this.size;
// function to check if the event is organic (isUpdate === false) or programmatic
const isUpdate = event => event && event.isUpdate;
this.view.select.subscribe(event => {
if (this.type === "single" && !isUpdate(event) && !Array.isArray(event)) {
this.closeMenu();
if (event.item && event.item.selected) {
if (this.itemValueKey) {
this.propagateChange(event.item[this.itemValueKey]);
}
else {
this.propagateChange(event.item);
}
}
else {
this.propagateChange(null);
}
}
if (this.type === "multi" && !isUpdate(event)) {
// if we have a `value` selector and selected items map them appropriately
if (this.itemValueKey && this.view.getSelected()) {
const values = this.view.getSelected().map(item => item[this.itemValueKey]);
this.propagateChange(values);
// otherwise just pass up the values from `getSelected`
}
else {
this.propagateChange(this.view.getSelected());
}
}
// only emit selected for "organic" selections
if (!isUpdate(event)) {
this.checkForReorder();
this.selected.emit(event);
}
});
}
ngAfterViewInit() {
// if appendInline is default valued (null) we should:
// 1. if there are scrollable parents (not including body) don't append inline
// this should also cover the case where the dropdown is in a modal
// (where we _do_ want to append to the placeholder)
if (this.appendInline === null && hasScrollableParents(this.elementRef.nativeElement)) {
this.appendInline = false;
// 2. otherwise we should append inline
}
else if (this.appendInline === null) {
this.appendInline = true;
}
this.checkForReorder();
}
/**
* Removing the `Dropdown` from the body if it is appended to the body.
*/
ngOnDestroy() {
if (!this.appendInline) {
this._appendToDropdown();
}
}
/**
* Propagates the injected `value`.
*/
writeValue(value) {
// cache the written value so we can use it in `AfterContentInit`
this.writtenValue = value;
this.view.onItemsReady(() => {
// propagate null/falsey as an array (deselect everything)
if (!value) {
this.view.propagateSelected([value]);
}
else if (this.type === "single") {
if (this.itemValueKey) {
// clone the specified item and update its state
const newValue = Object.assign({}, this.view.getListItems().find(item => item[this.itemValueKey] === value));
newValue.selected = true;
this.view.propagateSelected([newValue]);
}
else {
// pass the singular value as an array of ListItem
this.view.propagateSelected([value]);
}
}
else {
if (this.itemValueKey) {
// clone the items and update their state based on the received value array
// this way we don't lose any additional metadata that may be passed in via the `items` Input
let newValues = [];
for (const v of value) {
for (const item of this.view.getListItems()) {
if (item[this.itemValueKey] === v) {
newValues.push(Object.assign({}, item, { selected: true }));
}
}
}
this.view.propagateSelected(newValues);
}
else {
// we can safely assume we're passing an array of `ListItem`s
this.view.propagateSelected(value);
}
}
this.checkForReorder();
});
}
onBlur() {
this.onTouchedCallback();
}
registerOnChange(fn) {
this.propagateChange = fn;
}
/**
* Registering the function injected to control the touch use of the `Dropdown`.
*/
registerOnTouched(fn) {
this.onTouchedCallback = fn;
}
/**
* `ControlValueAccessor` method to programmatically disable the dropdown.
*
* ex: `this.formGroup.get("myDropdown").disable();`
*
* @param isDisabled `true` to disable the input
*/
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
/**
* Adds keyboard functionality for navigation, selection and closing of the `Dropdown`.
*/
onKeyDown(event) {
if ((event.key === "Escape") && !this.menuIsClosed) {
event.stopImmediatePropagation(); // don't unintentionally close other widgets that listen for Escape
}
if (event.key === "Escape") {
event.preventDefault();
this.closeMenu();
this.dropdownButton.nativeElement.focus();
}
else if (this.menuIsClosed && (event.key === " " || event.key === "ArrowDown" || event.key === "ArrowUp")) {
if (this.disableArrowKeys && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
return;
}
event.preventDefault();
this.openMenu();
}
if (!this.menuIsClosed && event.key === "Tab" && this.dropdownMenu.nativeElement.contains(event.target)) {
this.closeMenu();
}
if (!this.menuIsClosed && event.key === "Tab" && event.shiftKey) {
this.closeMenu();
}
if (this.type === "multi") {
return;
}
if (this.menuIsClosed) {
this.closedDropdownNavigation(event);
}
}
closedDropdownNavigation(event) {
if (event.key === "ArrowDown") {
event.preventDefault();
this.view.getCurrentItem().selected = false;
let item = this.view.getNextItem();
if (item) {
item.selected = true;
}
}
else if (event.key === "ArrowUp") {
event.preventDefault();
this.view.getCurrentItem().selected = false;
let item = this.view.getPrevItem();
if (item) {
item.selected = true;
}
}
}
/**
* Returns the display value if there is a selection and displayValue is set,
* if there is just a selection the ListItem content property will be returned,
* otherwise the placeholder will be returned.
*/
getDisplayStringValue() {
if (!this.view || this.skeleton) {
return;
}
let selected = this.view.getSelected();
if (selected.length && (!this.displayValue || !this.isRenderString())) {
if (this.type === "multi") {
return of(this.placeholder);
}
else {
return of(selected[0].content);
}
}
else if (selected.length && this.isRenderString()) {
return of(this.displayValue);
}
return of(this.placeholder);
}
isRenderString() {
return typeof this.displayValue === "string";
}
getRenderTemplateContext() {
if (!this.view) {
return;
}
let selected = this.view.getSelected();
if (this.type === "multi") {
return { items: selected };
}
else if (selected && selected.length > 0) {
return { item: selected[0] }; // this is to be compatible with the dropdown-list template
}
else {
return {};
}
}
getSelectedCount() {
if (this.view.getSelected()) {
return this.view.getSelected().length;
}
}
clearSelected() {
if (this.disabled || this.getSelectedCount() === 0) {
return;
}
for (const item of this.view.getListItems()) {
item.selected = false;
}
this.selected.emit([]);
this.propagateChange([]);
}
/**
* Returns `true` if there is a value selected.
*/
valueSelected() {
if (this.view.getSelected()) {
return true;
}
return false;
}
_noop() { }
/**
* Handles clicks outside of the `Dropdown`.
*/
_outsideClick(event) {
if (!this.elementRef.nativeElement.contains(event.target) &&
// if we're appendToBody the list isn't within the _elementRef,
// so we've got to check if our target is possibly in there too.
!this.dropdownMenu.nativeElement.contains(event.target)) {
this.closeMenu();
}
}
_outsideKey(event) {
if (!this.menuIsClosed && event.key === "Tab" && this.dropdownMenu.nativeElement.contains(event.target)) {
this.closeMenu();
}
}
/**
* Handles keyboard events so users are controlling the `Dropdown` instead of unintentionally controlling outside elements.
*/
_keyboardNav(event) {
if (event.key === "Escape" && !this.menuIsClosed) {
event.stopImmediatePropagation(); // don't unintentionally close modal if inside of it
}
if (event.key === "Escape") {
event.preventDefault();
this.closeMenu();
this.dropdownButton.nativeElement.focus();
}
else if (!this.menuIsClosed && event.key === "Tab") {
// this way focus will start on the next focusable item from the dropdown
// not the top of the body!
this.dropdownButton.nativeElement.focus();
this.dropdownButton.nativeElement.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: "Tab" }));
this.closeMenu();
}
}
/**
* Creates the `Dropdown` list appending it to the dropdown parent object instead of the body.
*/
_appendToDropdown() {
this.dropdownService.appendToDropdown(this.elementRef.nativeElement);
this.dropdownMenu.nativeElement.removeEventListener("keydown", this.keyboardNav, true);
}
/**
* Creates the `Dropdown` list as an element that is appended to the DOM body.
*/
_appendToBody() {
const lightClass = this.theme === "light" ? " cds--list-box--light" : "";
const expandedClass = !this.menuIsClosed ? " cds--list-box--expanded" : "";
this.dropdownService.appendToBody(this.dropdownButton.nativeElement, this.dropdownMenu.nativeElement, `${this.elementRef.nativeElement.className}${lightClass}${expandedClass}`);
this.dropdownMenu.nativeElement.addEventListener("keydown", this.keyboardNav, true);
}
/**
* Detects whether or not the `Dropdown` list is visible within all scrollable parents.
* This can be overridden by passing in a value to the `dropUp` input.
*/
_shouldDropUp() {
// check if dropdownMenu exists first.
const menu = this.dropdownMenu && this.dropdownMenu.nativeElement.querySelector(".cds--list-box__menu");
// check if menu exists first.
const menuRect = menu && menu.getBoundingClientRect();
if (menu && menuRect) {
const scrollableParents = getScrollableParents(menu);
return scrollableParents.reduce((shouldDropUp, parent) => {
const parentRect = parent.getBoundingClientRect();
const isBelowParent = !(menuRect.bottom <= parentRect.bottom);
return shouldDropUp || isBelowParent;
}, false);
}
return false;
}
/**
* Expands the dropdown menu in the view.
*/
openMenu() {
// prevents the dropdown from opening when list of items is empty
if (this.view.getListItems().length === 0) {
return;
}
this._dropUp = false;
this.menuIsClosed = false;
// move the dropdown list to the body if we're not appending inline
// and position it relative to the dropdown wrapper
if (!this.appendInline) {
const target = this.dropdownButton.nativeElement;
const parent = this.elementRef.nativeElement;
this.visibilitySubscription = this.elementService
.visibility(target, parent)
.subscribe(value => {
if (!value.visible) {
this.closeMenu();
}
});
this._appendToBody();
}
// set the dropdown menu to drop up if it's near the bottom of the screen
// setTimeout lets us measure after it's visible in the DOM
setTimeout(() => {
if (this.dropUp === null || this.dropUp === undefined) {
this._dropUp = this._shouldDropUp();
}
}, 0);
// we bind noop to document.body.firstElementChild to allow safari to fire events
// from document. Then we unbind everything later to keep things light.
document.body.firstElementChild.addEventListener("click", this.noop, true);
document.body.firstElementChild.addEventListener("keydown", this.noop, true);
document.addEventListener("click", this.outsideClick, true);
document.addEventListener("keydown", this.outsideKey, true);
setTimeout(() => this.view.initFocus(), 0);
}
/**
* Collapsing the dropdown menu and removing unnecessary `EventListeners`.
*/
closeMenu() {
// return early if the menu is already closed
if (this.menuIsClosed) {
return;
}
this.menuIsClosed = true;
this.checkForReorder();
this.onClose.emit();
this.close.emit();
// focus the trigger button when we close ...
this.dropdownButton.nativeElement.focus();
// remove the conditional once this api is settled and part of abstract-dropdown-view.class
if (this.view["disableScroll"]) {
this.view["disableScroll"]();
}
// move the list back in the component on close
if (!this.appendInline) {
this.visibilitySubscription.unsubscribe();
this._appendToDropdown();
}
document.body.firstElementChild.removeEventListener("click", this.noop, true);
document.body.firstElementChild.removeEventListener("keydown", this.noop, true);
document.removeEventListener("click", this.outsideClick, true);
document.removeEventListener("keydown", this.outsideKey, true);
}
/**
* Controls toggling menu states between open/expanded and closed/collapsed.
*/
toggleMenu() {
if (this.menuIsClosed) {
this.openMenu();
}
else {
this.closeMenu();
}
}
isTemplate(value) {
return value instanceof TemplateRef;
}
/**
* Controls when it's needed to apply the selection feedback
*/
checkForReorder() {
const topAfterReopen = this.menuIsClosed && this.selectionFeedback === "top-after-reopen";
if ((this.type === "multi") && (topAfterReopen || this.selectionFeedback === "top")) {
this.view.reorderSelected();
}
}
}
Dropdown.dropdownCount = 0;
Dropdown.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Dropdown, deps: [{ token: i0.ElementRef }, { token: i1.I18n }, { token: i2.DropdownService }, { token: i3.ElementService }], target: i0.ɵɵFactoryTarget.Component });
Dropdown.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Dropdown, selector: "cds-dropdown, ibm-dropdown", inputs: { id: "id", label: "label", hideLabel: "hideLabel", helperText: "helperText", placeholder: "placeholder", displayValue: "displayValue", clearText: "clearText", size: "size", type: "type", theme: "theme", disabled: "disabled", skeleton: "skeleton", inline: "inline", disableArrowKeys: "disableArrowKeys", invalid: "invalid", invalidText: "invalidText", warn: "warn", warnText: "warnText", appendInline: "appendInline", scrollableContainer: "scrollableContainer", itemValueKey: "itemValueKey", selectionFeedback: "selectionFeedback", menuButtonLabel: "menuButtonLabel", selectedLabel: "selectedLabel", dropUp: "dropUp" }, outputs: { selected: "selected", onClose: "onClose", close: "close" }, host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class.cds--dropdown__wrapper": "this.hostClass" } }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: Dropdown,
multi: true
}
], queries: [{ propertyName: "view", first: true, predicate: AbstractDropdownView, descendants: true, static: true }], viewQueries: [{ propertyName: "dropdownButton", first: true, predicate: ["dropdownButton"], descendants: true, static: true }, { propertyName: "dropdownMenu", first: true, predicate: ["dropdownMenu"], descendants: true, static: true }], ngImport: i0, template: `
<label
*ngIf="label && !skeleton"
[for]="id"
class="cds--label"
[ngClass]="{
'cds--label--disabled': disabled,
'cds--visually-hidden': hideLabel
}">
<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
<ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template>
</label>
<div
class="cds--list-box"
[ngClass]="{
'cds--dropdown': type !== 'multi',
'cds--multiselect': type === 'multi',
'cds--multi-select--selected': type === 'multi' && getSelectedCount() > 0,
'cds--dropdown--light': theme === 'light',
'cds--list-box--light': theme === 'light',
'cds--list-box--inline': inline,
'cds--skeleton': skeleton,
'cds--dropdown--disabled cds--list-box--disabled': disabled,
'cds--dropdown--invalid': invalid,
'cds--dropdown--warning cds--list-box--warning': warn,
'cds--dropdown--sm cds--list-box--sm': size === 'sm',
'cds--dropdown--md cds--list-box--md': size === 'md',
'cds--dropdown--lg cds--list-box--lg': size === 'lg',
'cds--list-box--expanded': !menuIsClosed
}">
<button
#dropdownButton
[id]="id"
type="button"
class="cds--list-box__field"
[ngClass]="{'a': !menuIsClosed}"
[attr.aria-expanded]="!menuIsClosed"
[attr.aria-disabled]="disabled"
aria-haspopup="listbox"
(click)="disabled ? $event.stopPropagation() : toggleMenu()"
(blur)="onBlur()"
[attr.disabled]="disabled ? true : null">
<div
(click)="clearSelected()"
(keydown.enter)="clearSelected()"
*ngIf="type === 'multi' && getSelectedCount() > 0"
class="cds--list-box__selection cds--tag--filter cds--list-box__selection--multi"
tabindex="0"
[title]="clearText">
{{getSelectedCount()}}
<svg
focusable="false"
preserveAspectRatio="xMidYMid meet"
style="will-change: transform;"
role="img"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
aria-hidden="true">
<path d="M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8z"></path>
</svg>
</div>
<span *ngIf="isRenderString()" class="cds--list-box__label">{{getDisplayStringValue() | async}}</span>
<ng-template
*ngIf="!isRenderString()"
[ngTemplateOutletContext]="getRenderTemplateContext()"
[ngTemplateOutlet]="displayValue">
</ng-template>
<svg
*ngIf="invalid"
class="cds--dropdown__invalid-icon"
cdsIcon="warning--filled"
size="16">
</svg>
<svg
*ngIf="!invalid && warn"
cdsIcon="warning--alt--filled"
size="16"
class="cds--list-box__invalid-icon cds--list-box__invalid-icon--warning">
</svg>
<span class="cds--list-box__menu-icon">
<svg
*ngIf="!skeleton"
cdsIcon="chevron--down"
size="16"
[attr.aria-label]="menuButtonLabel"
[ngClass]="{'cds--list-box__menu-icon--open': !menuIsClosed }">
</svg>
</span>
</button>
<div
#dropdownMenu
[ngClass]="{
'cds--list-box--up': this.dropUp !== null && this.dropUp !== undefined ? dropUp : _dropUp
}">
<ng-content *ngIf="!menuIsClosed"></ng-content>
</div>
</div>
<div
*ngIf="helperText && !invalid && !warn && !skeleton"
class="cds--form__helper-text"
[ngClass]="{
'cds--form__helper-text--disabled': disabled
}">
<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
</div>
<div *ngIf="invalid" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(invalidText)">{{ invalidText }}</ng-container>
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
</div>
<div *ngIf="!invalid && warn" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Dropdown, decorators: [{
type: Component,
args: [{
selector: "cds-dropdown, ibm-dropdown",
template: `
<label
*ngIf="label && !skeleton"
[for]="id"
class="cds--label"
[ngClass]="{
'cds--label--disabled': disabled,
'cds--visually-hidden': hideLabel
}">
<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
<ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template>
</label>
<div
class="cds--list-box"
[ngClass]="{
'cds--dropdown': type !== 'multi',
'cds--multiselect': type === 'multi',
'cds--multi-select--selected': type === 'multi' && getSelectedCount() > 0,
'cds--dropdown--light': theme === 'light',
'cds--list-box--light': theme === 'light',
'cds--list-box--inline': inline,
'cds--skeleton': skeleton,
'cds--dropdown--disabled cds--list-box--disabled': disabled,
'cds--dropdown--invalid': invalid,
'cds--dropdown--warning cds--list-box--warning': warn,
'cds--dropdown--sm cds--list-box--sm': size === 'sm',
'cds--dropdown--md cds--list-box--md': size === 'md',
'cds--dropdown--lg cds--list-box--lg': size === 'lg',
'cds--list-box--expanded': !menuIsClosed
}">
<button
#dropdownButton
[id]="id"
type="button"
class="cds--list-box__field"
[ngClass]="{'a': !menuIsClosed}"
[attr.aria-expanded]="!menuIsClosed"
[attr.aria-disabled]="disabled"
aria-haspopup="listbox"
(click)="disabled ? $event.stopPropagation() : toggleMenu()"
(blur)="onBlur()"
[attr.disabled]="disabled ? true : null">
<div
(click)="clearSelected()"
(keydown.enter)="clearSelected()"
*ngIf="type === 'multi' && getSelectedCount() > 0"
class="cds--list-box__selection cds--tag--filter cds--list-box__selection--multi"
tabindex="0"
[title]="clearText">
{{getSelectedCount()}}
<svg
focusable="false"
preserveAspectRatio="xMidYMid meet"
style="will-change: transform;"
role="img"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
aria-hidden="true">
<path d="M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8z"></path>
</svg>
</div>
<span *ngIf="isRenderString()" class="cds--list-box__label">{{getDisplayStringValue() | async}}</span>
<ng-template
*ngIf="!isRenderString()"
[ngTemplateOutletContext]="getRenderTemplateContext()"
[ngTemplateOutlet]="displayValue">
</ng-template>
<svg
*ngIf="invalid"
class="cds--dropdown__invalid-icon"
cdsIcon="warning--filled"
size="16">
</svg>
<svg
*ngIf="!invalid && warn"
cdsIcon="warning--alt--filled"
size="16"
class="cds--list-box__invalid-icon cds--list-box__invalid-icon--warning">
</svg>
<span class="cds--list-box__menu-icon">
<svg
*ngIf="!skeleton"
cdsIcon="chevron--down"
size="16"
[attr.aria-label]="menuButtonLabel"
[ngClass]="{'cds--list-box__menu-icon--open': !menuIsClosed }">
</svg>
</span>
</button>
<div
#dropdownMenu
[ngClass]="{
'cds--list-box--up': this.dropUp !== null && this.dropUp !== undefined ? dropUp : _dropUp
}">
<ng-content *ngIf="!menuIsClosed"></ng-content>
</div>
</div>
<div
*ngIf="helperText && !invalid && !warn && !skeleton"
class="cds--form__helper-text"
[ngClass]="{
'cds--form__helper-text--disabled': disabled
}">
<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
</div>
<div *ngIf="invalid" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(invalidText)">{{ invalidText }}</ng-container>
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
</div>
<div *ngIf="!invalid && warn" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: Dropdown,
multi: true
}
]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.I18n }, { type: i2.DropdownService }, { type: i3.ElementService }]; }, propDecorators: { id: [{
type: Input
}], label: [{
type: Input
}], hideLabel: [{
type: Input
}], helperText: [{
type: Input
}], placeholder: [{
type: Input
}], displayValue: [{
type: Input
}], clearText: [{
type: Input
}], size: [{
type: Input
}], type: [{
type: Input
}], theme: [{
type: Input
}], disabled: [{
type: Input
}], skeleton: [{
type: Input
}], inline: [{
type: Input
}], disableArrowKeys: [{
type: Input
}], invalid: [{
type: Input
}], invalidText: [{
type: Input
}], warn: [{
type: Input
}], warnText: [{
type: Input
}], appendInline: [{
type: Input
}], scrollableContainer: [{
type: Input
}], itemValueKey: [{
type: Input
}], selectionFeedback: [{
type: Input
}], menuButtonLabel: [{
type: Input
}], selectedLabel: [{
type: Input
}], dropUp: [{
type: Input
}], selected: [{
type: Output
}], onClose: [{
type: Output
}], close: [{
type: Output
}], view: [{
type: ContentChild,
args: [AbstractDropdownView, { static: true }]
}], dropdownButton: [{
type: ViewChild,
args: ["dropdownButton", { static: true }]
}], dropdownMenu: [{
type: ViewChild,
args: ["dropdownMenu", { static: true }]
}], hostClass: [{
type: HostBinding,
args: ["class.cds--dropdown__wrapper"]
}], onKeyDown: [{
type: HostListener,
args: ["keydown", ["$event"]]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcGRvd24uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2Ryb3Bkb3duL2Ryb3Bkb3duLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBQ04sWUFBWSxFQUVaLFlBQVksRUFFWixTQUFTLEVBRVQsWUFBWSxFQUVaLFdBQVcsRUFDWCxXQUFXLEVBRVgsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLGlCQUFpQixFQUF3QixNQUFNLGdCQUFnQixDQUFDO0FBRXpFLHlFQUF5RTtBQUN6RSxPQUFPLEVBRU4sRUFBRSxFQUNGLFlBQVksRUFDWixNQUFNLE1BQU0sQ0FBQztBQUVkLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBSXRFLE9BQU8sRUFBa0Isb0JBQW9CLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN2RixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQzs7Ozs7OztBQUV2RTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUJHO0FBZ0lILE1BQU0sT0FBTyxRQUFRO0lBdUtwQjs7T0FFRztJQUNILFlBQ1csVUFBc0IsRUFDdEIsSUFBVSxFQUNWLGVBQWdDLEVBQ2hDLGNBQThCO1FBSDlCLGVBQVUsR0FBVixVQUFVLENBQVk7UUFDdEIsU0FBSSxHQUFKLElBQUksQ0FBTTtRQUNWLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQUNoQyxtQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUE1S2hDLE9BQUUsR0FBRyxZQUFZLFFBQVEsQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDO1FBS3JEOztXQUVHO1FBQ00sY0FBUyxHQUFHLEtBQUssQ0FBQztRQUszQjs7V0FFRztRQUNNLGdCQUFXLEdBQUcsRUFBRSxDQUFDO1FBQzFCOztXQUVHO1FBQ00saUJBQVksR0FBOEIsRUFBRSxDQUFDO1FBQ3REOztXQUVHO1FBQ00sY0FBUyxHQUFXLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQztRQUM1RDs7V0FFRztRQUNNLFNBQUksR0FBdUIsSUFBSSxDQUFDO1FBQ3pDOzs7V0FHRztRQUNNLFNBQUksR0FBdUIsUUFBUSxDQUFDO1FBQzdDOzs7V0FHRztRQUNNLFVBQUssR0FBcUIsTUFBTSxDQUFDO1FBQzFDOztXQUVHO1FBQ00sYUFBUSxHQUFHLEtBQUssQ0FBQztRQUMxQjs7V0FFRztRQUNNLGFBQVEsR0FBRyxLQUFLLENBQUM7UUFDMUI7O1dBRUc7UUFDTSxXQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3hCOztXQUVHO1FBQ00scUJBQWdCLEdBQUcsS0FBSyxDQUFDO1FBQ2xDOztXQUVHO1FBQ00sWUFBTyxHQUFHLEtBQUssQ0FBQztRQUt6Qjs7WUFFSTtRQUNLLFNBQUksR0FBRyxLQUFLLENBQUM7UUFLdEI7O1dBRUc7UUFDTSxpQkFBWSxHQUFZLElBQUksQ0FBQztRQVV0Qzs7Ozs7V0FLRztRQUNNLHNCQUFpQixHQUF5QyxrQkFBa0IsQ0FBQztRQUN0Rjs7O1dBR0c7UUFDTSxvQkFBZSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQztRQUN6RDs7O1dBR0c7UUFDTSxrQkFBYSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQztRQUszRDs7V0FFRztRQUNPLGFBQVEsR0FBeUIsSUFBSSxZQUFZLEVBQVUsQ0FBQztRQUN0RTs7V0FFRztRQUNPLFlBQU8sR0FBc0IsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUMvRDs7V0FFRztRQUNPLFVBQUssR0FBc0IsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQWVoQixjQUFTLEdBQUcsSUFBSSxDQUFDO1FBQzlEOztXQUVHO1FBQ0gsaUJBQVksR0FBRyxJQUFJLENBQUM7UUFFcEI7O1dBRUc7UUFDSCxZQUFPLEdBQUcsS0FBSyxDQUFDO1FBRWhCLGdFQUFnRTtRQUNoRSx5QkFBeUI7UUFDekIsU0FBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdCLGlCQUFZLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDN0MsZUFBVSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pDLGdCQUFXLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFakMsMkJBQXNCLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUU1QyxzQkFBaUIsR0FBZSxJQUFJLENBQUMsS0FBSyxDQUFDO1FBRXJELGdHQUFnRztRQUN4RixrQkFBYSxHQUFRLEVBQUUsQ0FBQztRQThKaEM7O1dBRUc7UUFDSCxvQkFBZSxHQUFHLENBQUMsQ0FBTSxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7SUEvSVUsQ0FBQztJQWpCN0MsSUFBYyxZQUFZO1FBQ3pCLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQztJQUMzQixDQUFDO0lBQ0QsSUFBYyxZQUFZLENBQUMsR0FBVTtRQUNwQyxJQUFJLEdBQUcsSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtZQUM1QixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7U0FDckI7UUFDRCxJQUFJLENBQUMsYUFBYSxHQUFHLEdBQUcsQ0FBQztJQUMxQixDQUFDO0lBV0Q7OztPQUdHO0lBQ0gsUUFBUTtRQUNQLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtZQUNkLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7U0FDM0I7SUFDRixDQUFDO0lBRUQ7O09BRUc7SUFDSCxrQkFBa0I7UUFDakIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDZixPQUFPO1NBQ1A7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxJQUFJLE9BQU8sSUFBSSxDQUFDLFlBQVksS0FBSyxRQUFRLEVBQUU7WUFDN0YsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7U0FDbkM7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQzNCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7UUFFM0IsaUZBQWlGO1FBQ2pGLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUM7UUFFbEQsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxFQUFFO1lBQ2xDLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFO2dCQUN4RSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ2pCLElBQUksS0FBSyxDQUFDLElBQUksSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRTtvQkFDdEMsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFO3dCQUN0QixJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7cUJBQ3BEO3lCQUFNO3dCQUNOLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO3FCQUNqQztpQkFDRDtxQkFBTTtvQkFDTixJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFDO2lCQUMzQjthQUNEO1lBRUQsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFDOUMsMEVBQTBFO2dCQUMxRSxJQUFJLElBQUksQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBRTtvQkFDakQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7b0JBQzVFLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBQzdCLHVEQUF1RDtpQkFDdkQ7cUJBQU07b0JBQ04sSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7aUJBQzlDO2FBQ0Q7WUFDRCw4Q0FBOEM7WUFDOUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFDckIsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO2dCQUN2QixJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQzthQUMxQjtRQUNGLENBQUMsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVELGVBQWU7UUFDZCxzREFBc0Q7UUFDdEQsOEVBQThFO1FBQzlFLHNFQUFzRTtRQUN0RSx1REFBdUQ7UUFDdkQsSUFBSSxJQUFJLENBQUMsWUFBWSxLQUFLLElBQUksSUFBSSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxFQUFFO1lBQ3RGLElBQUksQ0FBQyxZQUFZLEdBQUcsS0FBSyxDQUFDO1lBQzFCLHVDQUF1QztTQUN2QzthQUFNLElBQUksSUFBSSxDQUFDLFlBQVksS0FBSyxJQUFJLEVBQUU7WUFDdEMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUM7U0FDekI7UUFDRCxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsV0FBVztRQUNWLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFO1lBQ3ZCLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1NBQ3pCO0lBQ0YsQ0FBQztJQUVEOztPQUVHO0lBQ0gsVUFBVSxDQUFDLEtBQVU7UUFDcEIsaUVBQWlFO1FBQ2pFLElBQUksQ0FBQyxZQUFZLEdBQUcsS0FBSyxDQUFDO1FBQzFCLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsRUFBRTtZQUMzQiwwREFBMEQ7WUFDMUQsSUFBSSxDQUFDLEtBQUssRUFBRTtnQkFDWCxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQzthQUNyQztpQkFBTSxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFO2dCQUNsQyxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7b0JBQ3RCLGdEQUFnRDtvQkFDaEQsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUM7b0JBQzdHLFFBQVEsQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO29CQUN6QixJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztpQkFDeEM7cUJBQU07b0JBQ04sa0RBQWtEO29CQUNsRCxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztpQkFDckM7YUFDRDtpQkFBTTtnQkFDTixJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7b0JBQ3RCLDJFQUEyRTtvQkFDM0UsNkZBQTZGO29CQUM3RixJQUFJLFNBQVMsR0FBRyxFQUFFLENBQUM7b0JBQ25CLEtBQUssTUFBTSxDQUFDLElBQUksS0FBSyxFQUFFO3dCQUN0QixLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUU7NEJBQzVDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0NBQ2xDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQzs2QkFDNUQ7eUJBQ0Q7cUJBQ0Q7b0JBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztpQkFDdkM7cUJBQU07b0JBQ04sNkRBQTZEO29CQUM3RCxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxDQUFDO2lCQUNuQzthQUNEO1lBQ0QsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3hCLENBQUMsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU07UUFDTCxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUMxQixDQUFDO0lBRUQsZ0JBQWdCLENBQUMsRUFBTztRQUN2QixJQUFJLENBQUMsZUFBZSxHQUFHLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxpQkFBaUIsQ0FBQyxFQUFPO1FBQ3hCLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxFQUFFLENBQUM7SUFDN0IsQ0FBQztJQU9EOzs7Ozs7T0FNRztJQUNILGdCQUFnQixDQUFDLFVBQW1CO1FBQ25DLElBQUksQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFDO0lBQzVCLENBQUM7SUFFRDs7T0FFRztJQUVILFNBQVMsQ0FBQyxLQUFvQjtRQUM3QixJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUU7WUFDbkQsS0FBSyxDQUFDLHdCQUF3QixFQUFFLENBQUMsQ0FBRSxtRUFBbUU7U0FDdEc7UUFDRCxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssUUFBUSxFQUFFO1lBQzNCLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDakIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7U0FDMUM7YUFBTSxJQUFJLElBQUksQ0FBQyxZQUFZLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFdBQVcsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFNBQVMsQ0FBQyxFQUFFO1lBQzVHLElBQUksSUFBSSxDQUFDLGdCQUFnQixJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxXQUFXLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxTQUFTLENBQUMsRUFBRTtnQkFDcEYsT0FBTzthQUNQO1lBQ0QsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztTQUNoQjtRQUVELElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBYyxDQUFDLEVBQUU7WUFDaEgsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1NBQ2pCO1FBRUQsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxLQUFLLElBQUksS0FBSyxDQUFDLFFBQVEsRUFBRTtZQUNoRSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7U0FDakI7UUFFRCxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssT0FBTyxFQUFFO1lBQUUsT0FBTztTQUFFO1FBRXRDLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRTtZQUN0QixJQUFJLENBQUMsd0JBQXdCLENBQUMsS0FBSyxDQUFDLENBQUM7U0FDckM7SUFDRixDQUFDO0lBRUQsd0JBQXdCLENBQUMsS0FBSztRQUM3QixJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssV0FBVyxFQUFFO1lBQzlCLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUM7WUFDNUMsSUFBSSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNuQyxJQUFJLElBQUksRUFBRTtnQkFBRSxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQzthQUFFO1NBQ25DO2FBQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFNBQVMsRUFBRTtZQUNuQyxLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDdkIsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQyxRQUFRLEdBQUcsS0FBSyxDQUFDO1lBQzVDLElBQUksSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDbkMsSUFBSSxJQUFJLEVBQUU7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7YUFBRTtTQUNuQztJQUNGLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gscUJBQXFCO1FBQ3BCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUU7WUFDaEMsT0FBTztTQUNQO1FBQ0QsSUFBSSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN2QyxJQUFJLFFBQVEsQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUMsRUFBRTtZQUN0RSxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssT0FBTyxFQUFFO2dCQUMxQixPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7YUFDNUI7aUJBQU07Z0JBQ04sT0FBTyxFQUFFLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2FBQy9CO1NBQ0Q7YUFBTSxJQUFJLFFBQVEsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRSxFQUFFO1lBQ3BELE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQyxZQUFzQixDQUFDLENBQUM7U0FDdkM7UUFDRCxPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVELGNBQWM7UUFDYixPQUFPLE9BQU8sSUFBSSxDQUFDLFlBQVksS0FBSyxRQUFRLENBQUM7SUFDOUMsQ0FBQztJQUVELHdCQUF3QjtRQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRTtZQUNmLE9BQU87U0FDUDtRQUNELElBQUksUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDdkMsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLE9BQU8sRUFBRTtZQUMxQixPQUFPLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxDQUFDO1NBQzNCO2FBQU0sSUFBSSxRQUFRLElBQUksUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDM0MsT0FBTyxFQUFFLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLDJEQUEyRDtTQUN6RjthQUFNO1lBQ04sT0FBTyxFQUFFLENBQUM7U0FDVjtJQUNGLENBQUM7SUFFRCxnQkFBZ0I7UUFDZixJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUU7WUFDNUIsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLE1BQU0sQ0FBQztTQUN0QztJQUNGLENBQUM7SUFFRCxhQUFhO1FBQ1osSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxLQUFLLENBQUMsRUFBRTtZQUNuRCxPQUFPO1NBQ1A7UUFDRCxLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUU7WUFDNUMsSUFBSSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUM7U0FDdEI7UUFDRCxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN2QixJQUFJLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzFCLENBQUM7SUFFRDs7T0FFRztJQUNILGFBQWE7UUFDWixJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUU7WUFBRSxPQUFPLElBQUksQ0FBQztTQUFFO1FBQzdDLE9BQU8sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQUVELEtBQUssS0FBSyxDQUFDO0lBQ1g7O09BRUc7SUFDSCxhQUFhLENBQUMsS0FBSztRQUNsQixJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFDeEQsK0RBQStEO1lBQy9ELGdFQUFnRTtZQUNoRSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUU7WUFDekQsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1NBQ2pCO0lBQ0YsQ0FBQztJQUNELFdBQVcsQ0FBQyxLQUFLO1FBQ2hCLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBYyxDQUFDLEVBQUU7WUFDaEgsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1NBQ2pCO0lBQ0YsQ0FBQztJQUNEOztPQUVHO0lBQ0gsWUFBWSxDQUFDLEtBQW9CO1FBQ2hDLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxRQUFRLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFO1lBQ2pELEtBQUssQ0FBQyx3QkFBd0IsRUFBRSxDQUFDLENBQUUsb0RBQW9EO1NBQ3ZGO1FBQ0QsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFFBQVEsRUFBRTtZQUMzQixLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDdkIsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ2pCLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1NBQzFDO2FBQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxLQUFLLEVBQUU7WUFDckQseUVBQXlFO1lBQ3pFLDJCQUEyQjtZQUMzQixJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUMxQyxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsSUFBSSxhQUFhLENBQUMsU0FBUyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDL0gsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1NBQ2pCO0lBQ0YsQ0FBQztJQUVEOztPQUVHO0lBQ0gsaUJBQWlCO1FBQ2hCLElBQUksQ0FBQyxlQUFlLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUNyRSxJQUFJLENBQUMsWUFBWSxDQUFDLGFBQWEsQ0FBQyx