@nebular/theme
Version:
@nebular/theme
1,111 lines • 42.3 kB
JavaScript
/*
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, ElementRef, EventEmitter, forwardRef, HostBinding, Inject, Input, Output, QueryList, ViewChild, Renderer2, NgZone, } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { merge, Subject, BehaviorSubject, from } from 'rxjs';
import { startWith, switchMap, takeUntil, filter, map, finalize } from 'rxjs/operators';
import { NbStatusService } from '../../services/status.service';
import { NbAdjustment, NbPosition, NbPositionBuilderService, } from '../cdk/overlay/overlay-position';
import { NbPortalDirective } from '../cdk/overlay/mapping';
import { NbOverlayService } from '../cdk/overlay/overlay-service';
import { NbTrigger, NbTriggerStrategyBuilderService } from '../cdk/overlay/overlay-trigger';
import { NbFocusKeyManagerFactoryService } from '../cdk/a11y/focus-key-manager';
import { ESCAPE } from '../cdk/keycodes/keycodes';
import { NB_DOCUMENT } from '../../theme.options';
import { NbOptionComponent } from '../option/option.component';
import { convertToBoolProperty } from '../helpers';
import { NB_SELECT_INJECTION_TOKEN } from './select-injection-tokens';
import { NbFormFieldControl, NbFormFieldControlConfig } from '../form-field/form-field-control';
import { NbFocusMonitor } from '../cdk/a11y/a11y.module';
export class NbSelectLabelComponent {
}
NbSelectLabelComponent.decorators = [
{ type: Component, args: [{
selector: 'nb-select-label',
template: '<ng-content></ng-content>'
},] }
];
export function nbSelectFormFieldControlConfigFactory() {
const config = new NbFormFieldControlConfig();
config.supportsSuffix = false;
return config;
}
/**
* The `NbSelectComponent` provides a capability to select one of the passed items.
*
* @stacked-example(Showcase, select/select-showcase.component)
*
* ### Installation
*
* Import `NbSelectModule` to your feature module.
* ```ts
* @NgModule({
* imports: [
* // ...
* NbSelectModule,
* ],
* })
* export class PageModule { }
* ```
* ### Usage
*
* If you want to use it as the multi-select control you have to mark it as `multiple`.
* In this case, `nb-select` will work only with arrays - accept arrays and propagate arrays.
*
* @stacked-example(Multiple, select/select-multiple.component)
*
* Items without values will clean the selection. Both `null` and `undefined` values will also clean the selection.
*
* @stacked-example(Clean selection, select/select-clean.component)
*
* Select may be bounded using `selected` input:
*
* ```html
* <nb-select [(selected)]="selected"></nb-selected>
* ```
*
* Or you can bind control with form controls or ngModel:
*
* @stacked-example(Select form binding, select/select-form.component)
*
* Options in the select may be grouped using `nb-option-group` component.
*
* @stacked-example(Grouping, select/select-groups.component)
*
* Select may have a placeholder that will be shown when nothing selected:
*
* @stacked-example(Placeholder, select/select-placeholder.component)
*
* You can disable select, options and whole groups.
*
* @stacked-example(Disabled select, select/select-disabled.component)
*
* Also, the custom label may be provided in select.
* This custom label will be used for instead placeholder when something selected.
*
* @stacked-example(Custom label, select/select-label.component)
*
* Default `nb-select` size is `medium` and status is `basic`.
* Select is available in multiple colors using `status` property:
*
* @stacked-example(Select statuses, select/select-status.component)
*
* There are five select sizes:
*
* @stacked-example(Select sizes, select/select-sizes.component)
*
* And two additional style types - `filled`:
*
* @stacked-example(Filled select, select/select-filled.component)
*
* and `hero`:
*
* @stacked-example(Select colors, select/select-hero.component)
*
* Select is available in different shapes, that could be combined with the other properties:
*
* @stacked-example(Select shapes, select/select-shapes.component)
*
* By default, the component selects options whose values are strictly equal (`===`) with the select value.
* To change such behavior, pass a custom comparator function to the `compareWith` attribute.
*
* @stacked-example(Select custom comparator, select/select-compare-with.component)
*
* @additional-example(Interactive, select/select-interactive.component)
*
* @styles
*
* select-cursor:
* select-disabled-cursor:
* select-min-width:
* select-outline-width:
* select-outline-color:
* select-icon-offset:
* select-text-font-family:
* select-placeholder-text-font-family:
* select-tiny-text-font-size:
* select-tiny-text-font-weight:
* select-tiny-text-line-height:
* select-tiny-placeholder-text-font-size:
* select-tiny-placeholder-text-font-weight:
* select-tiny-max-width:
* select-small-text-font-size:
* select-small-text-font-weight:
* select-small-text-line-height:
* select-small-placeholder-text-font-size:
* select-small-placeholder-text-font-weight:
* select-small-max-width:
* select-medium-text-font-size:
* select-medium-text-font-weight:
* select-medium-text-line-height:
* select-medium-placeholder-text-font-size:
* select-medium-placeholder-text-font-weight:
* select-medium-max-width:
* select-large-text-font-size:
* select-large-text-font-weight:
* select-large-text-line-height:
* select-large-placeholder-text-font-size:
* select-large-placeholder-text-font-weight:
* select-large-max-width:
* select-giant-text-font-size:
* select-giant-text-font-weight:
* select-giant-text-line-height:
* select-giant-placeholder-text-font-size:
* select-giant-placeholder-text-font-weight:
* select-giant-max-width:
* select-rectangle-border-radius:
* select-semi-round-border-radius:
* select-round-border-radius:
* select-outline-border-style:
* select-outline-border-width:
* select-outline-tiny-padding:
* select-outline-small-padding:
* select-outline-medium-padding:
* select-outline-large-padding:
* select-outline-giant-padding:
* select-outline-basic-icon-color:
* select-outline-basic-text-color:
* select-outline-basic-placeholder-text-color:
* select-outline-basic-background-color:
* select-outline-basic-border-color:
* select-outline-basic-focus-background-color:
* select-outline-basic-focus-border-color:
* select-outline-basic-hover-background-color:
* select-outline-basic-hover-border-color:
* select-outline-basic-disabled-background-color:
* select-outline-basic-disabled-border-color:
* select-outline-basic-disabled-icon-color:
* select-outline-basic-disabled-text-color:
* select-outline-primary-icon-color:
* select-outline-primary-text-color:
* select-outline-primary-placeholder-text-color:
* select-outline-primary-background-color:
* select-outline-primary-border-color:
* select-outline-primary-focus-background-color:
* select-outline-primary-focus-border-color:
* select-outline-primary-hover-background-color:
* select-outline-primary-hover-border-color:
* select-outline-primary-disabled-background-color:
* select-outline-primary-disabled-border-color:
* select-outline-primary-disabled-icon-color:
* select-outline-primary-disabled-text-color:
* select-outline-success-icon-color:
* select-outline-success-text-color:
* select-outline-success-placeholder-text-color:
* select-outline-success-background-color:
* select-outline-success-border-color:
* select-outline-success-focus-background-color:
* select-outline-success-focus-border-color:
* select-outline-success-hover-background-color:
* select-outline-success-hover-border-color:
* select-outline-success-disabled-background-color:
* select-outline-success-disabled-border-color:
* select-outline-success-disabled-icon-color:
* select-outline-success-disabled-text-color:
* select-outline-info-icon-color:
* select-outline-info-text-color:
* select-outline-info-placeholder-text-color:
* select-outline-info-background-color:
* select-outline-info-border-color:
* select-outline-info-focus-background-color:
* select-outline-info-focus-border-color:
* select-outline-info-hover-background-color:
* select-outline-info-hover-border-color:
* select-outline-info-disabled-background-color:
* select-outline-info-disabled-border-color:
* select-outline-info-disabled-icon-color:
* select-outline-info-disabled-text-color:
* select-outline-warning-icon-color:
* select-outline-warning-text-color:
* select-outline-warning-placeholder-text-color:
* select-outline-warning-background-color:
* select-outline-warning-border-color:
* select-outline-warning-focus-background-color:
* select-outline-warning-focus-border-color:
* select-outline-warning-hover-background-color:
* select-outline-warning-hover-border-color:
* select-outline-warning-disabled-background-color:
* select-outline-warning-disabled-border-color:
* select-outline-warning-disabled-icon-color:
* select-outline-warning-disabled-text-color:
* select-outline-danger-icon-color:
* select-outline-danger-text-color:
* select-outline-danger-placeholder-text-color:
* select-outline-danger-background-color:
* select-outline-danger-border-color:
* select-outline-danger-focus-background-color:
* select-outline-danger-focus-border-color:
* select-outline-danger-hover-background-color:
* select-outline-danger-hover-border-color:
* select-outline-danger-disabled-background-color:
* select-outline-danger-disabled-border-color:
* select-outline-danger-disabled-icon-color:
* select-outline-danger-disabled-text-color:
* select-outline-control-icon-color:
* select-outline-control-text-color:
* select-outline-control-placeholder-text-color:
* select-outline-control-background-color:
* select-outline-control-border-color:
* select-outline-control-focus-background-color:
* select-outline-control-focus-border-color:
* select-outline-control-hover-background-color:
* select-outline-control-hover-border-color:
* select-outline-control-disabled-background-color:
* select-outline-control-disabled-border-color:
* select-outline-control-disabled-icon-color:
* select-outline-control-disabled-text-color:
* select-outline-adjacent-border-style:
* select-outline-adjacent-border-width:
* select-outline-basic-open-border-color:
* select-outline-basic-adjacent-border-color:
* select-outline-primary-open-border-color:
* select-outline-primary-adjacent-border-color:
* select-outline-success-open-border-color:
* select-outline-success-adjacent-border-color:
* select-outline-info-open-border-color:
* select-outline-info-adjacent-border-color:
* select-outline-warning-open-border-color:
* select-outline-warning-adjacent-border-color:
* select-outline-danger-open-border-color:
* select-outline-danger-adjacent-border-color:
* select-outline-control-open-border-color:
* select-outline-control-adjacent-border-color:
* select-filled-border-style:
* select-filled-border-width:
* select-filled-tiny-padding:
* select-filled-small-padding:
* select-filled-medium-padding:
* select-filled-large-padding:
* select-filled-giant-padding:
* select-filled-basic-background-color:
* select-filled-basic-border-color:
* select-filled-basic-icon-color:
* select-filled-basic-text-color:
* select-filled-basic-placeholder-text-color:
* select-filled-basic-focus-background-color:
* select-filled-basic-focus-border-color:
* select-filled-basic-hover-background-color:
* select-filled-basic-hover-border-color:
* select-filled-basic-disabled-background-color:
* select-filled-basic-disabled-border-color:
* select-filled-basic-disabled-icon-color:
* select-filled-basic-disabled-text-color:
* select-filled-primary-background-color:
* select-filled-primary-border-color:
* select-filled-primary-icon-color:
* select-filled-primary-text-color:
* select-filled-primary-placeholder-text-color:
* select-filled-primary-focus-background-color:
* select-filled-primary-focus-border-color:
* select-filled-primary-hover-background-color:
* select-filled-primary-hover-border-color:
* select-filled-primary-disabled-background-color:
* select-filled-primary-disabled-border-color:
* select-filled-primary-disabled-icon-color:
* select-filled-primary-disabled-text-color:
* select-filled-success-background-color:
* select-filled-success-border-color:
* select-filled-success-icon-color:
* select-filled-success-text-color:
* select-filled-success-placeholder-text-color:
* select-filled-success-focus-background-color:
* select-filled-success-focus-border-color:
* select-filled-success-hover-background-color:
* select-filled-success-hover-border-color:
* select-filled-success-disabled-background-color:
* select-filled-success-disabled-border-color:
* select-filled-success-disabled-icon-color:
* select-filled-success-disabled-text-color:
* select-filled-info-background-color:
* select-filled-info-border-color:
* select-filled-info-icon-color:
* select-filled-info-text-color:
* select-filled-info-placeholder-text-color:
* select-filled-info-focus-background-color:
* select-filled-info-focus-border-color:
* select-filled-info-hover-background-color:
* select-filled-info-hover-border-color:
* select-filled-info-disabled-background-color:
* select-filled-info-disabled-border-color:
* select-filled-info-disabled-icon-color:
* select-filled-info-disabled-text-color:
* select-filled-warning-background-color:
* select-filled-warning-border-color:
* select-filled-warning-icon-color:
* select-filled-warning-text-color:
* select-filled-warning-placeholder-text-color:
* select-filled-warning-focus-background-color:
* select-filled-warning-focus-border-color:
* select-filled-warning-hover-background-color:
* select-filled-warning-hover-border-color:
* select-filled-warning-disabled-background-color:
* select-filled-warning-disabled-border-color:
* select-filled-warning-disabled-icon-color:
* select-filled-warning-disabled-text-color:
* select-filled-danger-background-color:
* select-filled-danger-border-color:
* select-filled-danger-icon-color:
* select-filled-danger-text-color:
* select-filled-danger-placeholder-text-color:
* select-filled-danger-focus-background-color:
* select-filled-danger-focus-border-color:
* select-filled-danger-hover-background-color:
* select-filled-danger-hover-border-color:
* select-filled-danger-disabled-background-color:
* select-filled-danger-disabled-border-color:
* select-filled-danger-disabled-icon-color:
* select-filled-danger-disabled-text-color:
* select-filled-control-background-color:
* select-filled-control-border-color:
* select-filled-control-icon-color:
* select-filled-control-text-color:
* select-filled-control-placeholder-text-color:
* select-filled-control-focus-background-color:
* select-filled-control-focus-border-color:
* select-filled-control-hover-background-color:
* select-filled-control-hover-border-color:
* select-filled-control-disabled-background-color:
* select-filled-control-disabled-border-color:
* select-filled-control-disabled-icon-color:
* select-filled-control-disabled-text-color:
* select-hero-tiny-padding:
* select-hero-small-padding:
* select-hero-medium-padding:
* select-hero-large-padding:
* select-hero-giant-padding:
* select-hero-basic-left-background-color:
* select-hero-basic-right-background-color:
* select-hero-basic-icon-color:
* select-hero-basic-text-color:
* select-hero-basic-placeholder-text-color:
* select-hero-basic-focus-left-background-color:
* select-hero-basic-focus-right-background-color:
* select-hero-basic-hover-left-background-color:
* select-hero-basic-hover-right-background-color:
* select-hero-basic-disabled-background-color:
* select-hero-basic-disabled-icon-color:
* select-hero-basic-disabled-text-color:
* select-hero-primary-left-background-color:
* select-hero-primary-right-background-color:
* select-hero-primary-icon-color:
* select-hero-primary-text-color:
* select-hero-primary-placeholder-text-color:
* select-hero-primary-focus-left-background-color:
* select-hero-primary-focus-right-background-color:
* select-hero-primary-hover-left-background-color:
* select-hero-primary-hover-right-background-color:
* select-hero-primary-disabled-background-color:
* select-hero-primary-disabled-icon-color:
* select-hero-primary-disabled-text-color:
* select-hero-success-left-background-color:
* select-hero-success-right-background-color:
* select-hero-success-icon-color:
* select-hero-success-text-color:
* select-hero-success-placeholder-text-color:
* select-hero-success-focus-left-background-color:
* select-hero-success-focus-right-background-color:
* select-hero-success-hover-left-background-color:
* select-hero-success-hover-right-background-color:
* select-hero-success-disabled-background-color:
* select-hero-success-disabled-icon-color:
* select-hero-success-disabled-text-color:
* select-hero-info-left-background-color:
* select-hero-info-right-background-color:
* select-hero-info-icon-color:
* select-hero-info-text-color:
* select-hero-info-placeholder-text-color:
* select-hero-info-focus-left-background-color:
* select-hero-info-focus-right-background-color:
* select-hero-info-hover-left-background-color:
* select-hero-info-hover-right-background-color:
* select-hero-info-disabled-background-color:
* select-hero-info-disabled-icon-color:
* select-hero-info-disabled-text-color:
* select-hero-warning-left-background-color:
* select-hero-warning-right-background-color:
* select-hero-warning-icon-color:
* select-hero-warning-text-color:
* select-hero-warning-placeholder-text-color:
* select-hero-warning-focus-left-background-color:
* select-hero-warning-focus-right-background-color:
* select-hero-warning-hover-left-background-color:
* select-hero-warning-hover-right-background-color:
* select-hero-warning-disabled-background-color:
* select-hero-warning-disabled-icon-color:
* select-hero-warning-disabled-text-color:
* select-hero-danger-left-background-color:
* select-hero-danger-right-background-color:
* select-hero-danger-icon-color:
* select-hero-danger-text-color:
* select-hero-danger-placeholder-text-color:
* select-hero-danger-focus-left-background-color:
* select-hero-danger-focus-right-background-color:
* select-hero-danger-hover-left-background-color:
* select-hero-danger-hover-right-background-color:
* select-hero-danger-disabled-background-color:
* select-hero-danger-disabled-icon-color:
* select-hero-danger-disabled-text-color:
* select-hero-control-left-background-color:
* select-hero-control-right-background-color:
* select-hero-control-icon-color:
* select-hero-control-text-color:
* select-hero-control-placeholder-text-color:
* select-hero-control-focus-left-background-color:
* select-hero-control-focus-right-background-color:
* select-hero-control-hover-left-background-color:
* select-hero-control-hover-right-background-color:
* select-hero-control-disabled-background-color:
* select-hero-control-disabled-icon-color:
* select-hero-control-disabled-text-color:
* */
export class NbSelectComponent {
constructor(document, overlay, hostRef, positionBuilder, triggerStrategyBuilder, cd, focusKeyManagerFactoryService, focusMonitor, renderer, zone, statusService) {
this.document = document;
this.overlay = overlay;
this.hostRef = hostRef;
this.positionBuilder = positionBuilder;
this.triggerStrategyBuilder = triggerStrategyBuilder;
this.cd = cd;
this.focusKeyManagerFactoryService = focusKeyManagerFactoryService;
this.focusMonitor = focusMonitor;
this.renderer = renderer;
this.zone = zone;
this.statusService = statusService;
/**
* Select size, available sizes:
* `tiny`, `small`, `medium` (default), `large`, `giant`
*/
this.size = 'medium';
/**
* Select status (adds specific styles):
* `basic`, `primary`, `info`, `success`, `warning`, `danger`, `control`
*/
this.status = 'basic';
/**
* Select shapes: `rectangle` (default), `round`, `semi-round`
*/
this.shape = 'rectangle';
/**
* Select appearances: `outline` (default), `filled`, `hero`
*/
this.appearance = 'outline';
this._fullWidth = false;
/**
* Renders select placeholder if nothing selected.
* */
this.placeholder = '';
this._compareWith = (v1, v2) => v1 === v2;
this._multiple = false;
/**
* Determines options overlay offset (in pixels).
**/
this.optionsOverlayOffset = 8;
/**
* Determines options overlay scroll strategy.
**/
this.scrollStrategy = 'block';
/**
* Will be emitted when selected value changes.
* */
this.selectedChange = new EventEmitter();
/**
* List of selected options.
* */
this.selectionModel = [];
/**
* Current overlay position because of we have to toggle overlayPosition
* in [ngClass] direction and this directive can use only string.
*/
this.overlayPosition = '';
this.alive = true;
this.destroy$ = new Subject();
/**
* Function passed through control value accessor to propagate changes.
* */
this.onChange = () => { };
this.onTouched = () => { };
/*
* @docs-private
**/
this.status$ = new BehaviorSubject(this.status);
/*
* @docs-private
**/
this.size$ = new BehaviorSubject(this.size);
/*
* @docs-private
**/
this.focused$ = new BehaviorSubject(false);
/*
* @docs-private
**/
this.disabled$ = new BehaviorSubject(this.disabled);
/*
* @docs-private
**/
this.fullWidth$ = new BehaviorSubject(this.fullWidth);
}
/**
* Adds `outline` styles
*/
get outline() {
return this.appearance === 'outline';
}
set outline(value) {
if (convertToBoolProperty(value)) {
this.appearance = 'outline';
}
}
/**
* Adds `filled` styles
*/
get filled() {
return this.appearance === 'filled';
}
set filled(value) {
if (convertToBoolProperty(value)) {
this.appearance = 'filled';
}
}
/**
* Adds `hero` styles
*/
get hero() {
return this.appearance === 'hero';
}
set hero(value) {
if (convertToBoolProperty(value)) {
this.appearance = 'hero';
}
}
/**
* Disables the select
*/
get disabled() {
return !!this._disabled;
}
set disabled(value) {
this._disabled = convertToBoolProperty(value);
}
/**
* If set element will fill its container
*/
get fullWidth() {
return this._fullWidth;
}
set fullWidth(value) {
this._fullWidth = convertToBoolProperty(value);
}
/**
* A function to compare option value with selected value.
* By default, values are compared with strict equality (`===`).
*/
get compareWith() {
return this._compareWith;
}
set compareWith(fn) {
if (typeof fn !== 'function') {
return;
}
this._compareWith = fn;
if (this.selectionModel.length && this.canSelectValue()) {
this.setSelection(this.selected);
}
}
/**
* Accepts selected item or array of selected items.
* */
set selected(value) {
this.writeValue(value);
}
get selected() {
return this.multiple
? this.selectionModel.map(o => o.value)
: this.selectionModel[0].value;
}
/**
* Gives capability just write `multiple` over the element.
* */
get multiple() {
return this._multiple;
}
set multiple(value) {
this._multiple = convertToBoolProperty(value);
}
get additionalClasses() {
if (this.statusService.isCustomStatus(this.status)) {
return [this.statusService.getStatusClass(this.status)];
}
return [];
}
/**
* Determines is select opened.
* */
get isOpen() {
return this.ref && this.ref.hasAttached();
}
/**
* Determines is select hidden.
* */
get isHidden() {
return !this.isOpen;
}
/**
* Returns width of the select button.
* */
get hostWidth() {
return this.button.nativeElement.getBoundingClientRect().width;
}
get selectButtonClasses() {
const classes = [];
if (!this.selectionModel.length) {
classes.push('placeholder');
}
if (!this.selectionModel.length && !this.placeholder) {
classes.push('empty');
}
if (this.isOpen) {
classes.push(this.overlayPosition);
}
return classes;
}
/**
* Content rendered in the label.
* */
get selectionView() {
if (this.selectionModel.length > 1) {
return this.selectionModel.map((option) => option.content).join(', ');
}
return this.selectionModel[0].content;
}
ngOnChanges({ disabled, status, size, fullWidth }) {
if (disabled) {
this.disabled$.next(disabled.currentValue);
}
if (status) {
this.status$.next(status.currentValue);
}
if (size) {
this.size$.next(size.currentValue);
}
if (fullWidth) {
this.fullWidth$.next(this.fullWidth);
}
}
ngAfterContentInit() {
this.options.changes
.pipe(startWith(this.options), filter(() => this.queue != null && this.canSelectValue()),
// Call 'writeValue' when current change detection run is finished.
// When writing is finished, change detection starts again, since
// microtasks queue is empty.
// Prevents ExpressionChangedAfterItHasBeenCheckedError.
switchMap((options) => from(Promise.resolve(options))), takeUntil(this.destroy$))
.subscribe(() => this.writeValue(this.queue));
}
ngAfterViewInit() {
this.triggerStrategy = this.createTriggerStrategy();
this.subscribeOnButtonFocus();
this.subscribeOnTriggers();
this.subscribeOnOptionClick();
// TODO: #2254
this.zone.runOutsideAngular(() => setTimeout(() => {
this.renderer.addClass(this.hostRef.nativeElement, 'nb-transition');
}));
}
ngOnDestroy() {
this.alive = false;
this.destroy$.next();
this.destroy$.complete();
if (this.ref) {
this.ref.dispose();
}
if (this.triggerStrategy) {
this.triggerStrategy.destroy();
}
}
show() {
if (this.isHidden) {
this.attachToOverlay();
this.setActiveOption();
this.cd.markForCheck();
}
}
hide() {
if (this.isOpen) {
this.ref.detach();
this.cd.markForCheck();
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
this.cd.markForCheck();
}
writeValue(value) {
if (!this.alive) {
return;
}
if (this.canSelectValue()) {
this.setSelection(value);
if (this.selectionModel.length) {
this.queue = null;
}
}
else {
this.queue = value;
}
}
/**
* Selects option or clear all selected options if value is null.
* */
handleOptionClick(option) {
this.queue = null;
if (option.value == null) {
this.reset();
}
else {
this.selectOption(option);
}
this.cd.markForCheck();
}
/**
* Deselect all selected options.
* */
reset() {
this.selectionModel.forEach((option) => option.deselect());
this.selectionModel = [];
this.hide();
this.button.nativeElement.focus();
this.emitSelected(this.multiple ? [] : null);
}
/**
* Determines how to select option as multiple or single.
* */
selectOption(option) {
if (this.multiple) {
this.handleMultipleSelect(option);
}
else {
this.handleSingleSelect(option);
}
}
/**
* Select single option.
* */
handleSingleSelect(option) {
const selected = this.selectionModel.pop();
if (selected && !this._compareWith(selected.value, option.value)) {
selected.deselect();
}
this.selectionModel = [option];
option.select();
this.hide();
this.button.nativeElement.focus();
this.emitSelected(option.value);
}
/**
* Select for multiple options.
* */
handleMultipleSelect(option) {
if (option.selected) {
this.selectionModel = this.selectionModel.filter(s => !this._compareWith(s.value, option.value));
option.deselect();
}
else {
this.selectionModel.push(option);
option.select();
}
this.emitSelected(this.selectionModel.map((opt) => opt.value));
}
attachToOverlay() {
if (!this.ref) {
this.createOverlay();
this.subscribeOnPositionChange();
this.createKeyManager();
this.subscribeOnOverlayKeys();
}
this.ref.attach(this.portal);
}
setActiveOption() {
if (this.selectionModel.length) {
this.keyManager.setActiveItem(this.selectionModel[0]);
}
else {
this.keyManager.setFirstItemActive();
}
}
createOverlay() {
const scrollStrategy = this.createScrollStrategy();
this.positionStrategy = this.createPositionStrategy();
this.ref = this.overlay.create({
positionStrategy: this.positionStrategy,
scrollStrategy,
panelClass: this.optionsPanelClass,
});
}
createKeyManager() {
this.keyManager = this.focusKeyManagerFactoryService.create(this.options).withTypeAhead(200);
}
createPositionStrategy() {
return this.positionBuilder
.connectedTo(this.button)
.position(NbPosition.BOTTOM)
.offset(this.optionsOverlayOffset)
.adjustment(NbAdjustment.VERTICAL);
}
createScrollStrategy() {
return this.overlay.scrollStrategies[this.scrollStrategy]();
}
createTriggerStrategy() {
return this.triggerStrategyBuilder
.trigger(NbTrigger.CLICK)
.host(this.hostRef.nativeElement)
.container(() => this.getContainer())
.build();
}
subscribeOnTriggers() {
this.triggerStrategy.show$.subscribe(() => this.show());
this.triggerStrategy.hide$
.pipe(filter(() => this.isOpen))
.subscribe(($event) => {
this.hide();
if (!this.isClickedWithinComponent($event)) {
this.onTouched();
}
});
}
subscribeOnPositionChange() {
this.positionStrategy.positionChange
.pipe(takeUntil(this.destroy$))
.subscribe((position) => {
this.overlayPosition = position;
this.cd.detectChanges();
});
}
subscribeOnOptionClick() {
/**
* If the user changes provided options list in the runtime we have to handle this
* and resubscribe on options selection changes event.
* Otherwise, the user will not be able to select new options.
* */
this.options.changes
.pipe(startWith(this.options), switchMap((options) => {
return merge(...options.map(option => option.click));
}), takeUntil(this.destroy$))
.subscribe((clickedOption) => this.handleOptionClick(clickedOption));
}
subscribeOnOverlayKeys() {
this.ref.keydownEvents()
.pipe(filter(() => this.isOpen), takeUntil(this.destroy$))
.subscribe((event) => {
if (event.keyCode === ESCAPE) {
this.button.nativeElement.focus();
this.hide();
}
else {
this.keyManager.onKeydown(event);
}
});
this.keyManager.tabOut
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.hide();
this.onTouched();
});
}
subscribeOnButtonFocus() {
this.focusMonitor.monitor(this.button)
.pipe(map(origin => !!origin), finalize(() => this.focusMonitor.stopMonitoring(this.button)), takeUntil(this.destroy$))
.subscribe(this.focused$);
}
getContainer() {
return this.ref && this.ref.hasAttached() && {
location: {
nativeElement: this.ref.overlayElement,
},
};
}
/**
* Propagate selected value.
* */
emitSelected(selected) {
this.onChange(selected);
this.selectedChange.emit(selected);
}
/**
* Set selected value in model.
* */
setSelection(value) {
const isArray = Array.isArray(value);
if (this.multiple && !isArray) {
throw new Error('Can\'t assign single value if select is marked as multiple');
}
if (!this.multiple && isArray) {
throw new Error('Can\'t assign array if select is not marked as multiple');
}
const previouslySelectedOptions = this.selectionModel;
this.selectionModel = [];
if (isArray) {
value.forEach(option => this.selectValue(option));
}
else {
this.selectValue(value);
}
// find options which were selected before and trigger deselect
previouslySelectedOptions
.filter((option) => !this.selectionModel.includes(option))
.forEach((option) => option.deselect());
this.cd.markForCheck();
}
/**
* Selects value.
* */
selectValue(value) {
const corresponding = this.options.find((option) => this._compareWith(option.value, value));
if (corresponding) {
corresponding.select();
this.selectionModel.push(corresponding);
}
}
/**
* Sets touched if focus moved outside of button and overlay,
* ignoring the case when focus moved to options overlay.
*/
trySetTouched() {
if (this.isHidden) {
this.onTouched();
}
}
isClickedWithinComponent($event) {
return this.hostRef.nativeElement === $event.target || this.hostRef.nativeElement.contains($event.target);
}
canSelectValue() {
return !!(this.options && this.options.length);
}
get tiny() {
return this.size === 'tiny';
}
get small() {
return this.size === 'small';
}
get medium() {
return this.size === 'medium';
}
get large() {
return this.size === 'large';
}
get giant() {
return this.size === 'giant';
}
get primary() {
return this.status === 'primary';
}
get info() {
return this.status === 'info';
}
get success() {
return this.status === 'success';
}
get warning() {
return this.status === 'warning';
}
get danger() {
return this.status === 'danger';
}
get basic() {
return this.status === 'basic';
}
get control() {
return this.status === 'control';
}
get rectangle() {
return this.shape === 'rectangle';
}
get round() {
return this.shape === 'round';
}
get semiRound() {
return this.shape === 'semi-round';
}
}
NbSelectComponent.decorators = [
{ type: Component, args: [{
selector: 'nb-select',
template: "<button [disabled]=\"disabled\"\n [ngClass]=\"selectButtonClasses\"\n (blur)=\"trySetTouched()\"\n (keydown.arrowDown)=\"show()\"\n (keydown.arrowUp)=\"show()\"\n class=\"select-button\"\n type=\"button\"\n #selectButton>\n\n <ng-container *ngIf=\"selectionModel.length; else placeholderTemplate\">\n <ng-container *ngIf=\"customLabel; else defaultSelectionTemplate\">\n <ng-content select=\"nb-select-label\"></ng-content>\n </ng-container>\n\n <ng-template #defaultSelectionTemplate>{{ selectionView }}</ng-template>\n </ng-container>\n\n <ng-template #placeholderTemplate>{{ placeholder }}</ng-template>\n\n <nb-icon icon=\"chevron-down-outline\" pack=\"nebular-essentials\" (click)=\"disabled && $event.stopPropagation()\" aria-hidden=\"true\">\n </nb-icon>\n</button>\n\n<nb-option-list *nbPortal [size]=\"size\" [position]=\"overlayPosition\" [style.width.px]=\"hostWidth\" [ngClass]=\"optionsListClass\">\n <ng-content select=\"nb-option, nb-option-group\"></ng-content>\n</nb-option-list>\n",
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NbSelectComponent),
multi: true,
},
{ provide: NB_SELECT_INJECTION_TOKEN, useExisting: NbSelectComponent },
{ provide: NbFormFieldControl, useExisting: NbSelectComponent },
{ provide: NbFormFieldControlConfig, useFactory: nbSelectFormFieldControlConfigFactory },
],
styles: ["/*!\n * @license\n * Copyright Akveo. All Rights Reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n */:host{display:inline-block;max-width:100%}[dir=ltr] :host .select-button{text-align:left}[dir=ltr] :host .select-button nb-icon{right:0.2em}[dir=rtl] :host .select-button{text-align:right}[dir=rtl] :host .select-button nb-icon{left:0.2em}:host(.full-width){width:100%}:host(.nb-transition) .select-button{transition-duration:0.15s;transition-property:background-color,border-color,border-radius,box-shadow,color;transition-timing-function:ease-in}.select-button{position:relative;width:100%;overflow:hidden;text-overflow:ellipsis;text-transform:none;white-space:nowrap}nb-icon{font-size:1.5em;position:absolute;top:50%;transform:translateY(-50%);transition-duration:0.15s;transition-property:transform;transition-timing-function:ease-in}[dir=ltr] nb-icon{right:.5rem}[dir=rtl] nb-icon{left:.5rem}:host(.open) nb-icon{transform:translateY(-50%) rotate(180deg)}\n"]
},] }
];
NbSelectComponent.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [NB_DOCUMENT,] }] },
{ type: NbOverlayService },
{ type: ElementRef },
{ type: NbPositionBuilderService },
{ type: NbTriggerStrategyBuilderService },
{ type: ChangeDetectorRef },
{ type: NbFocusKeyManagerFactoryService },
{ type: NbFocusMonitor },
{ type: Renderer2 },
{ type: NgZone },
{ type: NbStatusService }
];
NbSelectComponent.propDecorators = {
size: [{ type: Input }],
status: [{ type: Input }],
shape: [{ type: Input }],
appearance: [{ type: Input }],
optionsListClass: [{ type: Input }],
optionsPanelClass: [{ type: Input }],
outline: [{ type: Input }, { type: HostBinding, args: ['class.appearance-outline',] }],
filled: [{ type: Input }, { type: HostBinding, args: ['class.appearance-filled',] }],
hero: [{ type: Input }, { type: HostBinding, args: ['class.appearance-hero',] }],
disabled: [{ type: Input }],
fullWidth: [{ type: Input }, { type: HostBinding, args: ['class.full-width',] }],
placeholder: [{ type: Input }],
compareWith: [{ type: Input }],
selected: [{ type: Input }],
multiple: [{ type: Input }],
optionsOverlayOffset: [{ type: Input }],
scrollStrategy: [{ type: Input }],
additionalClasses: [{ type: HostBinding, args: ['class',] }],
selectedChange: [{ type: Output }],
options: [{ type: ContentChildren, args: [NbOptionComponent, { descendants: true },] }],
customLabel: [{ type: ContentChild, args: [NbSelectLabelComponent,] }],
portal: [{ type: ViewChild, args: [NbPortalDirective,] }],
button: [{ type: ViewChild, args: ['selectButton', { read: ElementRef },] }],
isOpen: [{ type: HostBinding, args: ['class.open',] }],
tiny: [{ type: HostBinding, args: ['class.size-tiny',] }],
small: [{ type: HostBinding, args: ['class.size-small',] }],
medium: [{ type: HostBinding, args: ['class.size-medium',] }],
large: [{ type: HostBinding, args: ['class.size-large',] }],
giant: [{ type: HostBinding, args: ['class.size-giant',] }],
primary: [{ type: HostBinding, args: ['class.status-primary',] }],
info: [{ type: HostBinding, args: ['class.status-info',] }],
success: [{ type: HostBinding, args: ['class.status-success',] }],
warning: [{ type: HostBinding, args: ['class.status-warning',] }],
danger: [{ type: HostBinding, args: ['class.status-danger',] }],
basic: [{ type: HostBinding, args: ['class.status-basic',] }],
control: [{ type: HostBinding, args: ['class.status-control',] }],
rectangle: [{ type: HostBinding, args: ['class.shape-rectangle',] }],
round: [{ type: HostBinding, args: ['class.shape-round',] }],
semiRound: [{ type: HostBinding, args: ['class.shape-semi-round',] }]
};
//# sourceMappingURL=select.component.js.map