import * as i0 from '@angular/core';
import { Directive, NgModule, Optional, Input, Injectable, Component, ChangeDetectionStrategy, EventEmitter, HostBinding, Output, ContentChildren, PLATFORM_ID, Inject, ViewChild, HostListener, SkipSelf, Renderer2, InjectionToken, ElementRef, ContentChild, ComponentFactoryResolver, KeyValueDiffers, Self, Attribute, LOCALE_ID, ViewContainerRef, NgZone, ChangeDetectorRef, TemplateRef } from '@angular/core';
import * as i5 from '@angular/common';
import { CommonModule, isPlatformBrowser, DOCUMENT, NgForOf, getLocaleDayNames, FormStyle, TranslationWidth, getLocaleMonthNames, getLocaleFirstDayOfWeek, getLocaleDateFormat, FormatWidth } from '@angular/common';
import { ClarityIcons, angleIcon, exclamationCircleIcon, checkCircleIcon, ellipsisHorizontalIcon, windowCloseIcon, eventIcon, calendarIcon, eyeHideIcon, eyeIcon, ellipsisVerticalIcon, viewColumnsIcon, arrowIcon, timesIcon, stepForward2Icon, angleDoubleIcon, filterGridCircleIcon, filterGridIcon, infoCircleIcon, exclamationTriangleIcon, circleIcon, dotCircleIcon, errorStandardIcon, successStandardIcon } from '@cds/core/icon';
import { map, tap, startWith, distinctUntilChanged, filter, skipUntil, debounceTime, takeUntil, take, first, switchMap } from 'rxjs/operators';
import * as i3 from 'rxjs';
import { Subject, BehaviorSubject, Observable, fromEvent, isObservable, of, ReplaySubject, combineLatest, merge } from 'rxjs';
import { trigger, transition, style, animate, state, keyframes } from '@angular/animations';
import * as i1 from '@angular/forms';
import { FormControl, FormGroup, FormsModule, NG_VALIDATORS, SelectMultipleControlValueAccessor } from '@angular/forms';
import { CdkTrapFocus } from '@angular/cdk/a11y';
import { CdkDrag } from '@angular/cdk/drag-drop';
import * as i1$1 from '@angular/router';
import '@cds/core/internal-components/close-button/register.js';
import '@cds/core/icon/register';
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrIconCustomTag {
}
ClrIconCustomTag.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIconCustomTag, deps: [], target: i0.ɵɵFactoryTarget.Directive });
ClrIconCustomTag.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrIconCustomTag, selector: "clr-icon", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIconCustomTag, decorators: [{
type: Directive,
args: [{
selector: 'clr-icon',
}]
}] });
class CdsIconCustomTag {
}
CdsIconCustomTag.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdsIconCustomTag, deps: [], target: i0.ɵɵFactoryTarget.Directive });
CdsIconCustomTag.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: CdsIconCustomTag, selector: "cds-icon", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdsIconCustomTag, decorators: [{
type: Directive,
args: [{
selector: 'cds-icon',
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CLR_ICON_DIRECTIVES = [ClrIconCustomTag, CdsIconCustomTag];
class ClrIconModule {
}
ClrIconModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIconModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrIconModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrIconModule, declarations: [ClrIconCustomTag, CdsIconCustomTag], imports: [CommonModule], exports: [ClrIconCustomTag, CdsIconCustomTag] });
ClrIconModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIconModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIconModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [CLR_ICON_DIRECTIVES],
exports: [CLR_ICON_DIRECTIVES],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This is an abstract class because we need it to still be a valid token for dependency injection after transpiling.
* This does not mean you should extend it, simply implementing it is fine.
*/
class LoadingListener {
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ClrLoadingState;
(function (ClrLoadingState) {
ClrLoadingState[ClrLoadingState["DEFAULT"] = 0] = "DEFAULT";
ClrLoadingState[ClrLoadingState["LOADING"] = 1] = "LOADING";
ClrLoadingState[ClrLoadingState["SUCCESS"] = 2] = "SUCCESS";
ClrLoadingState[ClrLoadingState["ERROR"] = 3] = "ERROR";
})(ClrLoadingState || (ClrLoadingState = {}));
class ClrLoading {
// We find the first parent that handles something loading
constructor(listener) {
this.listener = listener;
this._loadingState = ClrLoadingState.DEFAULT;
}
get loadingState() {
return this._loadingState;
}
set loadingState(value) {
if (value === true) {
value = ClrLoadingState.LOADING;
}
else if (!value) {
value = ClrLoadingState.DEFAULT;
}
if (value === this._loadingState) {
return;
}
this._loadingState = value;
if (this.listener) {
this.listener.loadingStateChange(value);
}
}
ngOnDestroy() {
this.loadingState = ClrLoadingState.DEFAULT;
}
}
ClrLoading.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrLoading, deps: [{ token: LoadingListener, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
ClrLoading.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrLoading, selector: "[clrLoading]", inputs: { loadingState: ["clrLoading", "loadingState"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrLoading, decorators: [{
type: Directive,
args: [{
selector: '[clrLoading]',
}]
}], ctorParameters: function () { return [{ type: LoadingListener, decorators: [{
type: Optional
}] }]; }, propDecorators: { loadingState: [{
type: Input,
args: ['clrLoading']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class IfExpandService {
constructor() {
this.expandable = 0;
this._loading = false;
this._expanded = false;
this._expandChange = new Subject();
}
get loading() {
return this._loading;
}
set loading(value) {
value = !!value;
if (value !== this._loading) {
this._loading = value;
}
}
get expanded() {
return this._expanded;
}
set expanded(value) {
value = !!value;
if (value !== this._expanded) {
this._expanded = value;
this._expandChange.next(value);
}
}
get expandChange() {
return this._expandChange.asObservable();
}
toggle() {
this.expanded = !this._expanded;
}
loadingStateChange(state) {
switch (state) {
case ClrLoadingState.LOADING:
this.loading = true;
break;
default:
this.loading = false;
break;
}
}
}
IfExpandService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: IfExpandService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
IfExpandService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: IfExpandService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: IfExpandService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let NB_INSTANCES = 0;
function uniqueIdFactory() {
return 'clr-id-' + NB_INSTANCES++;
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrAccordionDescription {
}
ClrAccordionDescription.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrAccordionDescription, deps: [], target: i0.ɵɵFactoryTarget.Component });
ClrAccordionDescription.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrAccordionDescription, selector: "clr-accordion-description, clr-step-description", host: { properties: { "class.clr-accordion-description": "true" } }, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrAccordionDescription, decorators: [{
type: Component,
args: [{
selector: 'clr-accordion-description, clr-step-description',
template: ``,
host: { '[class.clr-accordion-description]': 'true' },
changeDetection: ChangeDetectionStrategy.OnPush,
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var AccordionStatus;
(function (AccordionStatus) {
AccordionStatus["Inactive"] = "inactive";
AccordionStatus["Error"] = "error";
AccordionStatus["Complete"] = "complete";
})(AccordionStatus || (AccordionStatus = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const defaultAnimationTiming = '0.2s ease-in-out';
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const panelAnimation = [
trigger('skipInitialRender', [transition(':enter', [])]),
trigger('toggle', [
transition('void => *', [
style({ display: 'block', height: 0 }),
animate(defaultAnimationTiming, style({ height: '*' })),
]),
]),
];
const stepAnimation = [
trigger('skipInitialRender', [transition(':enter', [])]),
trigger('toggle', [
transition('void => *', [
style({ display: 'block', height: 0 }),
animate(defaultAnimationTiming, style({ height: '*' })),
]),
transition('* => void', [
style({ display: 'block' }),
animate(defaultAnimationTiming, style({ height: 0, display: 'none' })),
]),
]),
];
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const commonStringsDefault = {
open: 'Open',
close: 'Close',
show: 'Show',
hide: 'Hide',
expand: 'Expand',
collapse: 'Collapse',
more: 'More',
select: 'Select',
selectAll: 'Select All',
previous: 'Previous',
next: 'Next',
current: 'Jump to current',
info: 'Info',
success: 'Success',
warning: 'Warning',
danger: 'Error',
rowActions: 'Available actions',
pickColumns: 'Manage Columns',
showColumns: 'Show Columns',
sortColumn: 'Sort Column',
firstPage: 'First Page',
lastPage: 'Last Page',
nextPage: 'Next Page',
previousPage: 'Previous Page',
currentPage: 'Current Page',
totalPages: 'Total Pages',
filterItems: 'Filter items',
minValue: 'Min value',
maxValue: 'Max value',
modalContentStart: 'Beginning of Modal Content',
modalContentEnd: 'End of Modal Content',
showColumnsMenuDescription: 'Show or hide columns menu',
allColumnsSelected: 'All columns selected',
signpostToggle: 'Signpost Toggle',
signpostClose: 'Signpost Close',
loading: 'Loading',
// Datagrid
detailPaneStart: 'Start of row details',
detailPaneEnd: 'End of row details',
singleSelectionAriaLabel: 'Single selection header',
singleActionableAriaLabel: 'Single actionable header',
detailExpandableAriaLabel: 'Toggle more row content',
datagridFilterAriaLabel: 'Toggle {COLUMN} filter',
datagridFilterDialogAriaLabel: 'Filter dialog',
columnSeparatorAriaLabel: 'Column resize handle',
columnSeparatorDescription: 'Use left or right key to resize the column',
// Alert
alertCloseButtonAriaLabel: 'Close alert',
alertNextAlertAriaLabel: 'Next alert message, {CURRENT} of {COUNT}',
alertPreviousAlertAriaLabel: 'Previous alert message, {CURRENT} of {COUNT}',
// Date Picker
datepickerDialogLabel: 'Choose date',
datepickerToggleChooseDateLabel: 'Choose date',
datepickerToggleChangeDateLabel: 'Change date, {SELECTED_DATE}',
datepickerPreviousMonth: 'Previous month',
datepickerCurrentMonth: 'Current month',
datepickerNextMonth: 'Next month',
datepickerPreviousDecade: 'Previous decade',
datepickerNextDecade: 'Next decade',
datepickerCurrentDecade: 'Current decade',
datepickerSelectMonthText: 'Select month, the current month is {CALENDAR_MONTH}',
datepickerSelectYearText: 'Select year, the current year is {CALENDAR_YEAR}',
datepickerSelectedLabel: '{FULL_DATE} - Selected',
// Stack View
stackViewChanged: 'Value changed.',
// Responsive Nav
responsiveNavToggleOpen: 'Open navigation menu',
responsiveNavToggleClose: 'Close navigation menu',
responsiveNavOverflowOpen: 'Open navigation overflow menu',
responsiveNavOverflowClose: 'Close navigation overflow menu',
//Vertical Nav
verticalNavToggle: 'Toggle vertical navigation',
// Timeline steps
timelineStepNotStarted: 'Not started',
timelineStepCurrent: 'Current',
timelineStepSuccess: 'Completed',
timelineStepError: 'Error',
timelineStepProcessing: 'In progress',
// Combobox
comboboxDelete: 'Delete selected option',
comboboxSearching: 'Searching for matches for "{INPUT}"',
comboboxSelection: 'Selection',
comboboxSelected: 'Selected',
comboboxNoResults: 'No results',
comboboxOpen: 'Show options',
// Datagrid expandable rows
datagridExpandableBeginningOf: 'Beginning of',
datagridExpandableEndOf: 'End of',
datagridExpandableRowContent: 'Expandable row content',
datagridExpandableRowsHelperText: `Screen reader table commands may not work for viewing expanded content, please use your screen reader's browse mode to read the content exposed by this button`,
// Wizard
wizardStepSuccess: 'Completed',
wizardStepError: 'Error',
wizardStepnavAriaLabel: 'Step navigation',
/**
* Password Input
* Screen-reader text for the hide/show password field button
*/
passwordHide: 'Hide password for {LABEL}',
passwordShow: 'Show password for {LABEL}',
/**
* Datagrid footer; sr-only text after the number of selected rows.
*/
selectedRows: 'Selected rows',
};
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrCommonStringsService {
constructor() {
this._strings = commonStringsDefault;
}
/**
* Access to all of the keys as strings
*/
get keys() {
return this._strings;
}
/**
* Allows you to pass in new overrides for localization
*/
localize(overrides) {
this._strings = { ...this._strings, ...overrides };
}
/**
* Parse a string with a set of tokens to replace
*/
parse(source, tokens = {}) {
const names = Object.keys(tokens);
let output = source;
if (names.length) {
names.forEach(name => {
output = output.replace(`{${name}}`, tokens[name]);
});
}
return output;
}
}
ClrCommonStringsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrCommonStringsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ClrCommonStringsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrCommonStringsService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrCommonStringsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var AccordionStrategy;
(function (AccordionStrategy) {
AccordionStrategy["Default"] = "default";
AccordionStrategy["Multi"] = "multi";
})(AccordionStrategy || (AccordionStrategy = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let accordionCount = 0;
class AccordionPanelModel {
constructor(id, accordionId) {
this.id = id;
this.accordionId = accordionId;
this.status = AccordionStatus.Inactive;
this.index = null;
this.disabled = false;
this.open = false;
this.templateId = `${this.id}-${this.accordionId}`;
}
}
class AccordionModel {
constructor() {
this.strategy = AccordionStrategy.Default;
this.accordionCount = accordionCount++;
this._panels = {};
}
get panels() {
return Object.keys(this._panels).map(id => this._panels[id]);
}
setStrategy(strategy) {
this.strategy = strategy;
}
updatePanelOrder(ids) {
ids.forEach((id, index) => (this._panels[id].index = index));
this.removeOldPanels(ids);
}
addPanel(id, open = false) {
this._panels[id] = new AccordionPanelModel(id, this.accordionCount);
this._panels[id].open = open;
}
togglePanel(panelId, open) {
const panelIsOpen = this._panels[panelId].open;
const newOpenState = open !== undefined ? open : !panelIsOpen;
if (newOpenState && this.strategy === AccordionStrategy.Default) {
this.closeAllPanels();
}
this._panels[panelId].open = newOpenState;
}
disablePanel(panelId, disabled) {
this._panels[panelId].disabled = disabled;
}
closeAllPanels() {
this.panels.forEach(panel => (this._panels[panel.id].open = false));
}
removeOldPanels(ids) {
this.panels
.filter(panel => ids.find(id => id === panel.id) === undefined)
.forEach(panel => delete this._panels[panel.id]);
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class AccordionService {
constructor() {
this.accordion = new AccordionModel();
this._panelsChanges = new BehaviorSubject(this.accordion.panels);
}
getPanelChanges(panelId) {
return this._panelsChanges.pipe(map(panels => panels.find(s => s.id === panelId)));
}
setStrategy(strategy) {
this.accordion.setStrategy(strategy);
}
addPanel(panelId, open = false) {
this.accordion.addPanel(panelId, open);
this.emitUpdatedPanels();
}
togglePanel(panelId, open) {
this.accordion.togglePanel(panelId, open);
this.emitUpdatedPanels();
}
disablePanel(panelId, disabled) {
this.accordion.disablePanel(panelId, disabled);
this.emitUpdatedPanels();
}
updatePanelOrder(ids) {
this.accordion.updatePanelOrder(ids);
this.emitUpdatedPanels();
}
emitUpdatedPanels() {
this._panelsChanges.next(this.accordion.panels);
}
}
AccordionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: AccordionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
AccordionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: AccordionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: AccordionService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrAccordionPanel {
constructor(commonStrings, accordionService, ifExpandService, cdr) {
this.commonStrings = commonStrings;
this.accordionService = accordionService;
this.ifExpandService = ifExpandService;
this.cdr = cdr;
this.disabled = false;
this.panelOpen = false;
this.panelOpenChange = new EventEmitter();
this.AccordionStatus = AccordionStatus;
this.isAccordion = true;
this._id = uniqueIdFactory();
}
get id() {
return this._id;
}
set id(value) {
this._id = value;
}
get panelNumber() {
return this._panelIndex + 1;
}
ngOnInit() {
this.panel = this.accordionService.getPanelChanges(this.id).pipe(tap(panel => this.emitPanelChange(panel)));
this.accordionService.addPanel(this.id, this.panelOpen);
this.accordionService.togglePanel(this.id, this.panelOpen);
this.accordionService.disablePanel(this.id, this.disabled);
}
ngOnChanges(changes) {
if (this.panel && changes.panelOpen && changes.panelOpen.currentValue !== changes.panelOpen.previousValue) {
this.accordionService.togglePanel(this.id, changes.panelOpen.currentValue);
}
if (this.panel && changes.disabled && changes.disabled.currentValue !== changes.disabled.previousValue) {
this.accordionService.disablePanel(this.id, changes.disabled.currentValue);
}
}
togglePanel() {
this.accordionService.togglePanel(this.id);
}
collapsePanelOnAnimationDone(panel) {
if (!panel.open) {
this.ifExpandService.expanded = false;
}
}
getPanelStateClasses(panel) {
return `clr-accordion-panel-${panel.status} ${panel.open ? 'clr-accordion-panel-open' : ''}`;
}
getAccordionContentId(id) {
return `clr-accordion-content-${id}'`;
}
getAccordionHeaderId(id) {
return `clr-accordion-header-${id}`;
}
emitPanelChange(panel) {
if (panel.index !== this._panelIndex) {
this._panelIndex = panel.index;
// The whole chain of updates leading to this line starts in a ngAfterViewInit subscription in accordion.ts,
// listening for DOM changes. It seems to only fails in tests, but as this is not a frequently called code,
// I prefer to stay on the safe side and initiate a detection cycle here.
this.cdr.detectChanges();
}
if (panel.open !== this.panelOpen) {
this.panelOpenChange.emit(panel.open);
/**
* @Note: this line below is needed because we don't want to use another value to track
* for changes of the panel. Because we use BehaviorSubject this emit event is trigger on
* init (that is not needed - there is no change of the original value) - in some cases this
* lead to duplicate events.
*
* To prevent this we try to emit only when the value is changed and keep the value in sync
* even that is used only into the Initial Lifecycle (ngOnInit).
*/
this.panelOpen = panel.open;
}
if (panel.open) {
this.ifExpandService.expanded = true;
}
}
}
ClrAccordionPanel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrAccordionPanel, deps: [{ token: ClrCommonStringsService }, { token: AccordionService }, { token: IfExpandService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
ClrAccordionPanel.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrAccordionPanel, selector: "clr-accordion-panel", inputs: { disabled: ["clrAccordionPanelDisabled", "disabled"], panelOpen: ["clrAccordionPanelOpen", "panelOpen"] }, outputs: { panelOpenChange: "clrAccordionPanelOpenChange" }, host: { properties: { "class.clr-accordion-panel": "true", "class.clr-accordion-panel-disabled": "this.disabled" } }, providers: [IfExpandService], queries: [{ propertyName: "accordionDescription", predicate: ClrAccordionDescription }], usesOnChanges: true, ngImport: i0, template: "\n
\n\n" }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: ClrCommonStringsService }, { type: i1.FormGroupName, decorators: [{
type: Optional
}] }, { type: i1.NgModelGroup, decorators: [{
type: Optional
}] }, { type: StepperService }, { type: IfExpandService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { headerButton: [{
type: ViewChild,
args: ['headerButton']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ClrStepButtonType;
(function (ClrStepButtonType) {
ClrStepButtonType["Next"] = "next";
ClrStepButtonType["Submit"] = "submit";
})(ClrStepButtonType || (ClrStepButtonType = {}));
class ClrStepButton {
constructor(clrStep, stepperService) {
this.clrStep = clrStep;
this.stepperService = stepperService;
this.type = ClrStepButtonType.Next;
this.submitButton = false;
}
ngOnInit() {
this.submitButton = this.type === ClrStepButtonType.Submit;
}
navigateToNextPanel() {
this.stepperService.navigateToNextPanel(this.clrStep.id, this.clrStep.formGroup.valid);
}
}
ClrStepButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepButton, deps: [{ token: ClrStepperPanel }, { token: StepperService }], target: i0.ɵɵFactoryTarget.Directive });
ClrStepButton.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrStepButton, selector: "[clrStepButton]", inputs: { type: ["clrStepButton", "type"] }, host: { listeners: { "click": "navigateToNextPanel()" }, properties: { "class.clr-step-button": "true", "class.btn": "true", "type": "'button'", "class.btn-primary": "this.submitButton" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepButton, decorators: [{
type: Directive,
args: [{
selector: '[clrStepButton]',
host: {
'[class.clr-step-button]': 'true',
'[class.btn]': 'true',
'[type]': "'button'",
},
}]
}], ctorParameters: function () { return [{ type: ClrStepperPanel }, { type: StepperService }]; }, propDecorators: { type: [{
type: Input,
args: ['clrStepButton']
}], submitButton: [{
type: HostBinding,
args: ['class.btn-primary']
}], navigateToNextPanel: [{
type: HostListener,
args: ['click']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrStepper {
constructor(formGroup, ngForm, stepperService) {
this.formGroup = formGroup;
this.ngForm = ngForm;
this.stepperService = stepperService;
this.subscriptions = [];
}
ngOnInit() {
if (!this.formGroup && !this.ngForm) {
throw new Error('To use stepper a Reactive or Template Form is required.');
}
this.form = this.formGroup ? this.formGroup : this.ngForm;
this.subscriptions.push(this.listenForPanelsCompleted());
this.subscriptions.push(this.listenForFormResetChanges());
}
ngOnChanges(changes) {
if (changes.initialPanel.currentValue !== changes.initialPanel.previousValue) {
this.stepperService.overrideInitialPanel(this.initialPanel);
}
}
ngAfterViewInit() {
this.subscriptions.push(this.listenForDOMChanges());
}
ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe());
}
listenForFormResetChanges() {
return fromControlReset(this.form.form).subscribe(() => this.stepperService.resetPanels());
}
listenForPanelsCompleted() {
return this.stepperService.panelsCompleted.subscribe(panelsCompleted => {
if (panelsCompleted && this.form.valid) {
this.form.ngSubmit.emit();
}
else if (!this.form.valid && this.form.touched) {
this.setPanelsWithFormErrors();
}
});
}
setPanelsWithFormErrors() {
const panelsWithErrors = this.panels.reduce((panels, p) => (p.formGroup.invalid ? [...panels, p.id] : panels), []);
this.stepperService.setPanelsWithErrors(panelsWithErrors);
}
listenForDOMChanges() {
return this.panels.changes.pipe(startWith(this.panels)).subscribe((panels) => {
this.stepperService.updatePanelOrder(panels.toArray().map(p => p.id));
if (this.initialPanel) {
this.stepperService.overrideInitialPanel(this.initialPanel);
}
});
}
}
ClrStepper.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepper, deps: [{ token: i1.FormGroupDirective, optional: true }, { token: i1.NgForm, optional: true }, { token: StepperService }], target: i0.ɵɵFactoryTarget.Component });
ClrStepper.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrStepper, selector: "form[clrStepper]", inputs: { initialPanel: ["clrInitialStep", "initialPanel"] }, host: { properties: { "class.clr-accordion": "true", "class.clr-stepper-forms": "true" } }, providers: [StepperService, { provide: AccordionService, useExisting: StepperService }], queries: [{ propertyName: "panels", predicate: ClrStepperPanel, descendants: true }], usesOnChanges: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepper, decorators: [{
type: Component,
args: [{
selector: 'form[clrStepper]',
template: ``,
host: {
'[class.clr-accordion]': 'true',
'[class.clr-stepper-forms]': 'true',
},
providers: [StepperService, { provide: AccordionService, useExisting: StepperService }],
changeDetection: ChangeDetectionStrategy.OnPush,
}]
}], ctorParameters: function () { return [{ type: i1.FormGroupDirective, decorators: [{
type: Optional
}] }, { type: i1.NgForm, decorators: [{
type: Optional
}] }, { type: StepperService }]; }, propDecorators: { initialPanel: [{
type: Input,
args: ['clrInitialStep']
}], panels: [{
type: ContentChildren,
args: [ClrStepperPanel, { descendants: true }]
}] } });
function fromControlReset(control) {
return new Observable(observer => {
const unpatchedControlReset = control.reset;
control.reset = () => {
observer.next();
unpatchedControlReset.apply(control);
};
return () => {
control.reset = unpatchedControlReset;
};
});
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class StepperWillyWonka extends WillyWonka {
}
StepperWillyWonka.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StepperWillyWonka, deps: null, target: i0.ɵɵFactoryTarget.Directive });
StepperWillyWonka.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: StepperWillyWonka, selector: "form[clrStepper]", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StepperWillyWonka, decorators: [{
type: Directive,
args: [{
selector: 'form[clrStepper]',
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class StepperOompaLoompa extends OompaLoompa {
constructor(cdr, willyWonka, ifExpandService) {
if (!willyWonka) {
throw new Error('clr-stepper-panel should only be used inside of clrStepper');
}
super(cdr, willyWonka);
this.expand = ifExpandService;
}
get flavor() {
return this.expand.expanded;
}
}
StepperOompaLoompa.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StepperOompaLoompa, deps: [{ token: i0.ChangeDetectorRef }, { token: StepperWillyWonka, optional: true }, { token: IfExpandService }], target: i0.ɵɵFactoryTarget.Directive });
StepperOompaLoompa.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: StepperOompaLoompa, selector: "clr-stepper-panel, [clrStepButton]", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StepperOompaLoompa, decorators: [{
type: Directive,
args: [{
selector: 'clr-stepper-panel, [clrStepButton]',
}]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: StepperWillyWonka, decorators: [{
type: Optional
}] }, { type: IfExpandService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const declarations = [ClrStepper, ClrStepButton, ClrStepperPanel, StepperOompaLoompa, StepperWillyWonka];
class ClrStepperModule {
}
ClrStepperModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepperModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrStepperModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrStepperModule, declarations: [ClrStepper, ClrStepButton, ClrStepperPanel, StepperOompaLoompa, StepperWillyWonka], imports: [CommonModule, ClrIconModule, ClrAccordionModule], exports: [ClrStepper, ClrStepButton, ClrStepperPanel, StepperOompaLoompa, StepperWillyWonka, ClrAccordionModule] });
ClrStepperModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepperModule, imports: [CommonModule, ClrIconModule, ClrAccordionModule, ClrAccordionModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStepperModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, ClrIconModule, ClrAccordionModule],
declarations: [...declarations],
exports: [...declarations, ClrAccordionModule],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var Keys;
(function (Keys) {
Keys["ArrowLeft"] = "ArrowLeft";
Keys["ArrowUp"] = "ArrowUp";
Keys["ArrowRight"] = "ArrowRight";
Keys["ArrowDown"] = "ArrowDown";
Keys["Backspace"] = "Backspace";
Keys["Tab"] = "Tab";
Keys["Enter"] = "Enter";
Keys["Escape"] = "Escape";
Keys["Space"] = "Space";
Keys["Spacebar"] = " ";
Keys["Home"] = "Home";
Keys["End"] = "End";
})(Keys || (Keys = {}));
var IEKeys;
(function (IEKeys) {
IEKeys["ArrowUp"] = "Up";
IEKeys["ArrowDown"] = "Down";
IEKeys["ArrowRight"] = "Right";
IEKeys["ArrowLeft"] = "Left";
IEKeys["Space"] = "Spacebar";
IEKeys["Escape"] = "Esc";
})(IEKeys || (IEKeys = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function normalizeKey(key) {
if (key === Keys.ArrowUp || key === IEKeys.ArrowUp) {
return Keys.ArrowUp;
}
else if (key === Keys.ArrowDown || key === IEKeys.ArrowDown) {
return Keys.ArrowDown;
}
else if (key === Keys.ArrowRight || key === IEKeys.ArrowRight) {
return Keys.ArrowRight;
}
else if (key === Keys.ArrowLeft || key === IEKeys.ArrowLeft) {
return Keys.ArrowLeft;
}
else if (key === Keys.Space || key === IEKeys.Space) {
return Keys.Space;
}
else if (key === Keys.Escape || key === IEKeys.Escape) {
return Keys.Escape;
}
else {
return key;
}
}
function preventArrowKeyScroll(event) {
const key = normalizeKey(event.key);
if (key === Keys.ArrowUp || key === Keys.ArrowDown || key === Keys.ArrowLeft || key === Keys.ArrowRight) {
// prevent element container scroll
// MDN references this is really the only way to prevent native browser interactions
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets
event.preventDefault();
}
}
function isKeyEitherLetterOrNumber(event) {
const char = event.key;
// Only letter characters differ when they switch between lowercase and uppercase, whether it's an English or non-English letter.
return char.toLowerCase() !== char.toUpperCase() || (char >= '0' && char <= '9');
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
// Popovers might need to ignore click events on an element
// (eg: popover opens on focus on an input field. Clicks should be ignored in this case)
class ClrPopoverToggleService {
constructor() {
this._open = false;
this._openChange = new Subject();
this._openEventChange = new Subject();
this._popoverAligned = new Subject();
this._popoverVisible = new Subject();
}
get openChange() {
return this._openChange.asObservable();
}
get popoverVisible() {
return this._popoverVisible.asObservable();
}
get openEvent() {
return this._openEvent;
}
set openEvent(event) {
this._openEvent = event;
this._openEventChange.next(event);
}
get open() {
return this._open;
}
set open(value) {
value = !!value;
if (this._open !== value) {
this._open = value;
this._openChange.next(value);
}
}
// For compatibility with legacy IfOpenService based implementations
get originalEvent() {
return this._openEvent;
}
get popoverAligned() {
return this._popoverAligned.asObservable();
}
getEventChange() {
return this._openEventChange.asObservable();
}
/**
* Sometimes, we need to remember the event that triggered the toggling to avoid loops.
* This is for instance the case of components that open on a click, but close on a click outside.
*/
toggleWithEvent(event) {
preventArrowKeyScroll(event);
this.openEvent = event;
this.open = !this.open;
}
popoverVisibleEmit(visible) {
this._popoverVisible.next(visible);
}
popoverAlignedEmit(popoverNode) {
this._popoverAligned.next(popoverNode);
}
}
ClrPopoverToggleService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverToggleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ClrPopoverToggleService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverToggleService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverToggleService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
// https://github.com/angular/angular/issues/20351#issuecomment-344009887
/** @dynamic */
class ClrPopoverEventsService {
constructor(renderer, smartOpenService, document) {
this.renderer = renderer;
this.smartOpenService = smartOpenService;
this.document = document;
this.outsideClickClose = true;
this.scrollToClose = true;
this.subscriptions = [];
this.subscriptions.push(smartOpenService.openChange.subscribe(open => {
if (open) {
this.addEscapeListener();
this.addClickListener();
this.addScrollListener();
}
else {
this.removeAllEventListeners();
}
}), smartOpenService.getEventChange().subscribe(event => {
// Remember the event that was used to open the content
this.ignoredEvent = event;
}));
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
this.removeAllEventListeners();
}
addScrollListener() {
if (this.scrollToClose) {
this.documentScroller = fromEvent(this.document, 'scroll', { capture: true });
this.scrollSubscription = this.documentScroller
.pipe(filter(this.testForSmartPopoverContentContainer))
.subscribe(() => {
this.smartOpenService.open = false;
this.setAnchorFocus();
});
}
else {
// I think this is where dynamic re-positioning will be added
// Instead of testing like we do in the close pipe below
// we need to switch positioning to use an observable and then
// debounce the scroll events to recalculate content position in a performant way
// For now, ignore scrolling events.
return;
}
}
removeScrollListener() {
if (this.documentScroller) {
this.scrollSubscription.unsubscribe();
delete this.documentScroller;
}
}
addClickListener() {
if (this.outsideClickClose) {
this.documentClickListener = this.renderer.listen(this.document, 'click', (event) => {
if (event === this.ignoredEvent) {
// Here we ignore the opening click event (w/o this content opens and immediately closes.
delete this.ignoredEvent;
}
else {
this.smartOpenService.open = false;
// Rather than a complex change to the focus trap I put focus on the element that was clicked
const clickedElement = event.target;
clickedElement.focus();
}
});
}
}
removeClickListener() {
if (this.outsideClickClose) {
delete this.ignoredEvent;
if (this.documentClickListener) {
this.documentClickListener();
delete this.documentClickListener;
}
}
}
addEscapeListener() {
this.escapeListener = this.renderer.listen(this.document, 'keydown.escape', () => {
this.smartOpenService.open = false;
this.setAnchorFocus();
});
}
removeEscapeListener() {
if (this.escapeListener) {
this.escapeListener();
delete this.escapeListener;
}
}
setCloseFocus() {
this.closeButtonRef.nativeElement.focus();
}
setAnchorFocus() {
this.anchorButtonRef.nativeElement.focus();
}
testForSmartPopoverContentContainer(event) {
// Filter for the documentScroller observable event targets
let target = event.target;
// Walk up the DOM tree until we get to the element that is a direct child of the body.
while (target.classList && target.parentElement.localName !== 'body') {
target = target.parentElement;
}
// Target is the child element of body where the scroll events originated.
// Return false and prevent the popover content container from closing for any scroll events inside a popover
// content container.
if (target.classList) {
// check scroll events to see if they are happening in popover content or elsewhere
return target.classList.contains('clr-popover-content') ? false : true;
}
else {
// prevents it from closing right after first opening
return false;
}
}
removeAllEventListeners() {
this.removeScrollListener();
this.removeClickListener();
this.removeEscapeListener();
}
}
ClrPopoverEventsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverEventsService, deps: [{ token: i0.Renderer2 }, { token: ClrPopoverToggleService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
ClrPopoverEventsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverEventsService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverEventsService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: ClrPopoverToggleService }, { type: HTMLDocument, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverAnchor {
constructor(smartEventService, element) {
smartEventService.anchorButtonRef = element;
}
}
ClrPopoverAnchor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverAnchor, deps: [{ token: ClrPopoverEventsService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrPopoverAnchor.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrPopoverAnchor, selector: "[clrPopoverAnchor]", host: { properties: { "class.clr-anchor": "true" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverAnchor, decorators: [{
type: Directive,
args: [{
selector: '[clrPopoverAnchor]',
host: {
'[class.clr-anchor]': 'true',
},
}]
}], ctorParameters: function () { return [{ type: ClrPopoverEventsService }, { type: i0.ElementRef }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverCloseButton {
constructor(elementRef, smartEventsService, smartOpenService) {
this.elementRef = elementRef;
this.smartEventsService = smartEventsService;
this.smartOpenService = smartOpenService;
this.closeChange = new EventEmitter();
this.subscriptions = [];
this.subscriptions.push(smartOpenService.openChange.pipe(filter(value => !value)).subscribe(() => {
this.closeChange.next();
}));
}
handleClick(event) {
this.smartOpenService.toggleWithEvent(event);
this.smartEventsService.setAnchorFocus();
}
ngAfterViewInit() {
this.smartEventsService.closeButtonRef = this.elementRef;
this.smartEventsService.setCloseFocus();
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
}
ClrPopoverCloseButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverCloseButton, deps: [{ token: i0.ElementRef }, { token: ClrPopoverEventsService }, { token: ClrPopoverToggleService }], target: i0.ɵɵFactoryTarget.Directive });
ClrPopoverCloseButton.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrPopoverCloseButton, selector: "[clrPopoverCloseButton]", outputs: { closeChange: "clrPopoverOnCloseChange" }, host: { listeners: { "click": "handleClick($event)" }, properties: { "class.clr-smart-close-button": "true" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverCloseButton, decorators: [{
type: Directive,
args: [{
selector: '[clrPopoverCloseButton]',
host: {
'[class.clr-smart-close-button]': 'true',
},
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: ClrPopoverEventsService }, { type: ClrPopoverToggleService }]; }, propDecorators: { closeChange: [{
type: Output,
args: ['clrPopoverOnCloseChange']
}], handleClick: [{
type: HostListener,
args: ['click', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ClrAxis;
(function (ClrAxis) {
ClrAxis[ClrAxis["VERTICAL"] = 0] = "VERTICAL";
ClrAxis[ClrAxis["HORIZONTAL"] = 1] = "HORIZONTAL";
})(ClrAxis || (ClrAxis = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ClrAlignment;
(function (ClrAlignment) {
ClrAlignment[ClrAlignment["START"] = 0] = "START";
ClrAlignment[ClrAlignment["CENTER"] = 0.5] = "CENTER";
ClrAlignment[ClrAlignment["END"] = 1] = "END";
})(ClrAlignment || (ClrAlignment = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ClrViewportViolation;
(function (ClrViewportViolation) {
ClrViewportViolation[ClrViewportViolation["BOTTOM"] = 0] = "BOTTOM";
ClrViewportViolation[ClrViewportViolation["LEFT"] = 1] = "LEFT";
ClrViewportViolation[ClrViewportViolation["RIGHT"] = 2] = "RIGHT";
ClrViewportViolation[ClrViewportViolation["TOP"] = 3] = "TOP";
})(ClrViewportViolation || (ClrViewportViolation = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const flipSides = position => {
return {
...position,
side: -1 * position.side,
};
};
// This could be used in more advanced positioning algorithms.
// flipAxisFlipSideAndNudgeContent(flipAxis, flipSide, nudge, nudgeForward?): ClrTransform {...}
// I would like to keep it for now.
const flipAxis = position => {
return {
...position,
axis: position.axis === 0 ? 1 : 0,
};
};
const nudgeContent = (position, forward) => {
const nextAlignment = position.content + (forward ? 0.5 : -0.5);
if (nextAlignment < 0 || nextAlignment > 1) {
return position;
}
else {
return {
...position,
content: nextAlignment,
};
}
};
function flipSidesAndNudgeContent(flip, nudge, nudgeBack) {
return (position) => nudge(flip(position), nudgeBack);
}
function align(position, anchor, content) {
let xDiff = anchor.left;
let yDiff = anchor.top;
// When ClrAxis is VERTICAL BEFORE = left, AFTER = right
// When ClrAxis is HORIZONTAL BEFORE is top, AFTER is bottom
switch (position.axis + position.side) {
case -1: {
// ClrAxis.VERTICAL + ClrSide.BEFORE
xDiff += alignHorizontal(position, anchor, content);
yDiff -= content.height; // pull content up to the top of the anchor
break;
}
case 1: {
// ClrAxis.VERTICAL + ClrSide.AFTER
xDiff += alignHorizontal(position, anchor, content);
yDiff += anchor.height; // push the content down to below the anchor
break;
}
case 0: {
// ClrAxis.HORIZONTAL + ClrSide.BEFORE
xDiff -= content.width; // pull the content left of the anchor
yDiff += alignVertical(position, anchor, content);
break;
}
case 2: {
// ClrAxis.HORIZONTAL + ClrSide.AFTER
xDiff += anchor.width; // push the content right of of the anchor
yDiff += alignVertical(position, anchor, content);
break;
}
default: {
break;
}
}
return { xOffset: xDiff, yOffset: yDiff };
}
function alignHorizontal(position, anchor, content) {
let horizontalOffset = 0;
// horizontal offset for the anchor position
switch (position.anchor /*calculate for the anchor alignment*/) {
case ClrAlignment.START: {
// nothing to calculate here
break;
}
case ClrAlignment.CENTER: {
horizontalOffset += anchor.width / 2; // push content over 1/2 anchor width
break;
}
case ClrAlignment.END: {
horizontalOffset += anchor.width; // push content over width of the anchor
break;
}
default: {
break;
}
}
// horizontal offsets for anchor alignment
switch (position.content // calculate for the content alignment
) {
case ClrAlignment.START: {
// Nothing to calculate here
break;
}
case ClrAlignment.CENTER: {
horizontalOffset -= content.width / 2; // pull content left by a value of 1/2 content width
break;
}
case ClrAlignment.END: {
// subtract the width of currentContent from horizontalOffset to pull it back
horizontalOffset -= content.width;
break;
}
default: {
break;
}
}
return horizontalOffset;
}
function alignVertical(position, anchor, content) {
// y axis offsets for anchor alignment
let verticalOffset = 0;
// Calculate y offset for anchor position
switch (position.anchor) {
case ClrAlignment.START: {
// nothing to calculate here
break;
}
case ClrAlignment.CENTER: {
verticalOffset += anchor.height / 2; // push content down to the middle of the anchor rect
break;
}
case ClrAlignment.END: {
verticalOffset += anchor.height; // push content down to the bottom of the anchor
break;
}
default: {
break;
}
}
// Calculate y offsets for content alignment
switch (position.content) {
case ClrAlignment.START: {
// aligned to the top of the content rect
break;
}
case ClrAlignment.CENTER: {
verticalOffset -= content.height / 2; // pull content back up to the middle of the content rect
break;
}
case ClrAlignment.END: {
verticalOffset -= content.height; // pull content back up to the bottom of the content rect
break;
}
default: {
break;
}
}
return verticalOffset;
}
function testVisibility(offset, content) {
const violations = [];
const mockCoords = {
bottom: offset.yOffset + content.height,
left: offset.xOffset,
right: offset.xOffset + content.width,
top: offset.yOffset,
};
if (!(mockCoords.top >= 0)) {
violations.push(ClrViewportViolation.TOP);
}
if (!(mockCoords.left >= 0)) {
violations.push(ClrViewportViolation.LEFT);
}
if (!(mockCoords.bottom <= (window.innerHeight || document.documentElement.clientHeight))) {
violations.push(ClrViewportViolation.BOTTOM);
}
if (!(mockCoords.right <= (window.innerWidth || document.documentElement.clientWidth))) {
violations.push(ClrViewportViolation.RIGHT);
}
return violations;
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverPositionService {
constructor(eventService, platformId) {
this.eventService = eventService;
this.platformId = platformId;
this._shouldRealign = new Subject();
this.shouldRealign = this._shouldRealign.asObservable();
}
realign() {
this._shouldRealign.next();
}
alignContent(content) {
if (!isPlatformBrowser(this.platformId)) {
// Only position when in a browser.
// Default to the browser origin and prevent getBoundingClientRect from running.
return {
xOffset: 0,
yOffset: 0,
};
}
this.currentAnchorCoords = this.eventService.anchorButtonRef.nativeElement.getBoundingClientRect();
this.currentContentCoords = content.getBoundingClientRect();
this.contentOffsets = align(this.position, this.currentAnchorCoords, this.currentContentCoords);
const visibilityViolations = testVisibility(this.contentOffsets, this.currentContentCoords);
/**
* Calculate the sum of viewport errors. This calculation is used below with the provided Axis in the given
* ClrPopoverPosition. Its worth putting the ClrViewportViolation enum values here:
*
* BOTTOM = 0,
* LEFT = 1,
* RIGHT = 2,
* TOP = 3,
*
* So, this.visibilityViolations.length tells us how many sides of the viewport that the popover content was
* clipped on. We can only help when the content has an issue on one or two sides.
* errorSum is calculated to determine _how_ to change the position. Looking at both the axis and the number
* of violations I can use the errorSum to determine how to transform the position (on the fly) and adjust
* where it can be improved.
*
* Note, more than 3 viewport violations and there isn't anything we can do to help. Also when there are two
* violations, we can't help if the violations are TOP+BOTTOM || LEFT+RIGHT => There is no transformation we
* can make to the position that will help.
*
* Some examples:
* There is only one error and Primary axis is VERTICAL
* - this.handleVerticalAxisOneViolation has a switch that will use the error sum to apply the correct
* transform to the position based on the reduction of visibilityViolations.
*
* There are two errors and Primary axis is HORIZONTAL
* - handleHorizontalAxisTwoViolations has a switch that uses the error sum to apply both transforms needed to
* improve the content position based on the reduction of visibilityViolations.
*/
const errorSum = visibilityViolations.reduce((count, current) => {
return count + current;
}, 0);
if (visibilityViolations.length === 1 && this.position.axis === ClrAxis.VERTICAL) {
// When primary axis is VERTICAL and there is one viewport violation
this.handleVerticalAxisOneViolation(errorSum);
}
else if (visibilityViolations.length === 1 && this.position.axis === ClrAxis.HORIZONTAL) {
// When primary axis is HORIZONTAL and there is one viewport violation
this.handleHorizontalAxisOneViolation(errorSum);
}
else if (visibilityViolations.length === 2 && this.position.axis === ClrAxis.VERTICAL) {
// When primary axis is VERTICAL and there are two viewport violations
this.handleVerticalAxisTwoViolations(errorSum);
}
else if (visibilityViolations.length === 2 && this.position.axis === ClrAxis.HORIZONTAL) {
// When primary axis is HORIZONTAL and there are two viewport violations
this.handleHorizontalAxisTwoViolations(errorSum);
}
/**
* Adjusts popover position based on scroll value by adding the negative 'top' value of currentContentCoords to yOffset for proper alignment.
* - The negative value means that the 'top' of the content is scrolled out of view at the top of the viewport.
*/
if (this.currentContentCoords.top < 0) {
this.contentOffsets.yOffset += Math.abs(this.currentContentCoords.top);
}
/**
* This detects the condition where the popover is flipped because it would violate the bottom of the viewport, but flipping it results in the
* popover rendering above the top of the body (y coordinate outside the body). In that event, it should be rendered within the body
* as much as possible, so this logic sets the top of popover to render touching the top of the body.
*/
if (this.contentOffsets.yOffset + this.currentAnchorCoords.y < 0) {
this.contentOffsets.yOffset = 0 - this.currentContentCoords.top;
}
return this.contentOffsets;
}
handleVerticalAxisOneViolation(errorSum) {
switch (errorSum) {
case 0:
case 3: {
// BOTTOM(0) or TOP(3) are primary violations and we can just flip sides
this.contentOffsets = align(flipSides(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 1: {
// LEFT(1) is secondary and needs to nudge content right
this.contentOffsets = align(this.position, this.currentAnchorCoords, this.currentContentCoords);
/**
* Even with the nudge we still have a problem. We need to check if the content is going to be clipped
*/
if (this.contentOffsets.xOffset < 0) {
this.contentOffsets.xOffset = 10;
}
break;
}
case 2: {
// RIGHT(2) is secondary and needs to nudge content left
this.contentOffsets = align(nudgeContent(this.position, true), this.currentAnchorCoords, this.currentContentCoords);
break;
}
default: {
break;
}
}
}
handleVerticalAxisTwoViolations(errorSum) {
switch (errorSum) {
// We know there are two violations. We can use the errorSum to determine which combination of sides were
// violated and handle appropriately.
case 5: {
// TOP(3)+RIGHT(2) is case 5. We need to flip sides and nudge the content to the left
const flipAndNudgeLeft = flipSidesAndNudgeContent(flipSides, nudgeContent, true);
this.contentOffsets = align(flipAndNudgeLeft(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 4: {
//TOP(3)+LEFT(1) is case 4, we need to flip sides and nudge content to the right
const flipAndNudgeRight = flipSidesAndNudgeContent(flipSides, nudgeContent, false);
this.contentOffsets = align(flipAndNudgeRight(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 3: {
// TOP(3)+BOTTOM(0) || left(1)+RIGHT(2) is case 3. There is nothing we can do position wise to improve the
// placement for this content.
break;
}
case 2: {
// BOTTOM(0)+RIGHT(2) is case 2. We need to flip sides and nudge the content to the left
const flipAndNudgeLeft = flipSidesAndNudgeContent(flipSides, nudgeContent, true);
this.contentOffsets = align(flipAndNudgeLeft(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 1: {
// BOTTOM(0)+LEFT(1) is case 1. We need to flip sides and nudge to the right
const flipAndNudgeRight = flipSidesAndNudgeContent(flipSides, nudgeContent, false);
this.contentOffsets = align(flipAndNudgeRight(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
default: {
break;
}
}
}
handleHorizontalAxisOneViolation(errorSum) {
switch (errorSum) {
case 1:
case 2: {
// LEFT(1) and RIGHT(2) are primary violations so we can flip sides
this.contentOffsets = align(flipSides(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 0: {
// BOTTOM(0) is a secondary violation and we need to nudge content up
this.contentOffsets = align(nudgeContent(this.position, true), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 3: {
// TOP(3) is a secondary violation and we need to nudge content down
this.contentOffsets = align(nudgeContent(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
default: {
break;
}
}
}
handleHorizontalAxisTwoViolations(errorSum) {
switch (errorSum) {
case 5:
case 4: {
// TOP(3)+LEFT(1) is case 4.
// TOP(3)+RIGHT(2) is case 5.
// In both of these cases we need to flip sides and nudge content down
const flipAndNudgeDown = flipSidesAndNudgeContent(flipSides, nudgeContent, false);
this.contentOffsets = align(flipAndNudgeDown(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
case 3: {
// TOP(3)+BOTTOM(0) || left(1)+RIGHT(2) is case 3. There is nothing we can do position wise to improve the
// placement for this content.
break;
}
case 2:
case 1: {
// BOTTOM(0)+RIGHT(2) is case 2.
// BOTTOM(0)+LEFT(1) is case 1.
// In both cases we need to flip sides and nudge content up
const flipAndNudgeUp = flipSidesAndNudgeContent(flipSides, nudgeContent, true);
this.contentOffsets = align(flipAndNudgeUp(this.position), this.currentAnchorCoords, this.currentContentCoords);
break;
}
default: {
break;
}
}
}
}
ClrPopoverPositionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverPositionService, deps: [{ token: ClrPopoverEventsService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
ClrPopoverPositionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverPositionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverPositionService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: ClrPopoverEventsService }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
// https://github.com/angular/angular/issues/20351#issuecomment-344009887
/** @dynamic */
class ClrPopoverContent {
constructor(document, container, template, renderer, smartPositionService, smartEventsService, smartOpenService) {
this.document = document;
this.container = container;
this.template = template;
this.renderer = renderer;
this.smartPositionService = smartPositionService;
this.smartEventsService = smartEventsService;
this.smartOpenService = smartOpenService;
this.subscriptions = [];
this.removeClickListenerFn = null;
this.shouldRealign = false;
// Check-collector pattern:
// In order to get accurate content height/width values, we cannot calculate alignment offsets until
// after the projected content has stabilized.
// As multiple check events may happen in the same rendering cycle, we need to collect all events
// and only act after the content is really stable. Or we may get wrong intermediate positioning values.
// We will channel subsequent content check events through this observable.
this.checkCollector = new EventEmitter();
}
set open(value) {
this.smartOpenService.open = !!value;
}
set contentAt(position) {
this.smartPositionService.position = position;
}
set outsideClickClose(clickToClose) {
this.smartEventsService.outsideClickClose = !!clickToClose;
}
set scrollToClose(scrollToClose) {
this.smartEventsService.scrollToClose = !!scrollToClose;
}
ngAfterContentChecked() {
if (this.smartOpenService.open && this.view && this.shouldRealign) {
// Channel content-check event through the check-collector
this.checkCollector.emit();
}
}
ngAfterViewInit() {
this.subscriptions.push(this.smartOpenService.openChange.subscribe(change => {
if (change) {
this.addContent();
}
else {
this.removeContent();
}
}), this.smartPositionService.shouldRealign.subscribe(() => {
this.shouldRealign = true;
}),
// Here we collect subsequent synchronously received content-check events and only take action
// at the end of the cycle. See below for details on the check-collector pattern.
this.checkCollector.pipe(debounceTime(0)).subscribe(() => {
this.alignContent();
this.shouldRealign = false;
if (this.view) {
this.renderer.setStyle(this.view.rootNodes[0], 'opacity', '1');
this.smartOpenService.popoverVisibleEmit(true);
}
}));
}
ngOnDestroy() {
this.removeContent();
this.subscriptions.forEach(sub => sub.unsubscribe());
}
/**
* TODO(matt): investigate why DebugElement retains a reference to the nodes and causes a memory leak.
* A note about the use of appendChild/removeChild
* The DebugElement is keeping a reference to the detached node and its unclear why.
* This does warrant further investigation. But, since it doesn't happen in production mode
* it is a low priority issue for now.
*/
addContent() {
// Create the view container
this.view = this.container.createEmbeddedView(this.template);
const [rootNode] = this.view.rootNodes;
this.smartEventsService.contentRef = rootNode; // So we know where/what to set close focus on
this.renderer.addClass(rootNode, 'clr-popover-content');
// Reset to the begining of the document to be available for sizing/positioning calculations.
// If we add new content to the bottom it triggers changes in the layout that may lead to false anchor
// coordinates values.
this.renderer.setStyle(rootNode, 'top', '0px');
this.renderer.setStyle(rootNode, 'left', '0px');
// We need to hide it during the calculation phase, while it's not yet finally positioned.
this.renderer.setStyle(rootNode, 'opacity', '0');
this.removeClickListenerFn = this.renderer.listen(rootNode, 'click', event => {
this.smartOpenService.openEvent = event;
});
this.view.rootNodes.forEach(node => {
this.renderer.appendChild(this.document.body, node);
});
// Mark for realingment on the next content-check cycle.
this.shouldRealign = true;
}
removeContent() {
if (!this.view) {
return;
}
if (this.removeClickListenerFn) {
this.removeClickListenerFn();
this.removeClickListenerFn = null;
}
this.view.rootNodes.forEach(node => this.renderer.removeChild(this.document.body, node));
this.container.clear();
delete this.view;
this.smartOpenService.popoverVisibleEmit(false);
}
alignContent() {
if (!this.view) {
return;
}
const positionCoords = this.smartPositionService.alignContent(this.view.rootNodes[0]);
this.renderer.setStyle(this.view.rootNodes[0], 'top', `${positionCoords.yOffset}px`);
this.renderer.setStyle(this.view.rootNodes[0], 'left', `${positionCoords.xOffset}px`);
this.smartOpenService.popoverAlignedEmit(this.view.rootNodes[0]);
}
}
ClrPopoverContent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverContent, deps: [{ token: DOCUMENT }, { token: i0.ViewContainerRef }, { token: i0.TemplateRef }, { token: i0.Renderer2 }, { token: ClrPopoverPositionService }, { token: ClrPopoverEventsService }, { token: ClrPopoverToggleService }], target: i0.ɵɵFactoryTarget.Directive });
ClrPopoverContent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrPopoverContent, selector: "[clrPopoverContent]", inputs: { open: ["clrPopoverContent", "open"], contentAt: ["clrPopoverContentAt", "contentAt"], outsideClickClose: ["clrPopoverContentOutsideClickToClose", "outsideClickClose"], scrollToClose: ["clrPopoverContentScrollToClose", "scrollToClose"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverContent, decorators: [{
type: Directive,
args: [{
selector: '[clrPopoverContent]',
}]
}], ctorParameters: function () { return [{ type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: i0.ViewContainerRef }, { type: i0.TemplateRef }, { type: i0.Renderer2 }, { type: ClrPopoverPositionService }, { type: ClrPopoverEventsService }, { type: ClrPopoverToggleService }]; }, propDecorators: { open: [{
type: Input,
args: ['clrPopoverContent']
}], contentAt: [{
type: Input,
args: ['clrPopoverContentAt']
}], outsideClickClose: [{
type: Input,
args: ['clrPopoverContentOutsideClickToClose']
}], scrollToClose: [{
type: Input,
args: ['clrPopoverContentScrollToClose']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverOpenCloseButton {
constructor(smartOpenService) {
this.smartOpenService = smartOpenService;
this.openCloseChange = new EventEmitter();
this.subscriptions = [];
this.subscriptions.push(this.smartOpenService.openChange.subscribe(change => {
this.openCloseChange.next(change);
}));
}
handleClick(event) {
this.smartOpenService.toggleWithEvent(event);
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
}
ClrPopoverOpenCloseButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverOpenCloseButton, deps: [{ token: ClrPopoverToggleService }], target: i0.ɵɵFactoryTarget.Directive });
ClrPopoverOpenCloseButton.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrPopoverOpenCloseButton, selector: "[clrPopoverOpenCloseButton]", outputs: { openCloseChange: "clrPopoverOpenCloseChange" }, host: { listeners: { "click": "handleClick($event)" }, properties: { "class.clr-smart-open-close": "true" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverOpenCloseButton, decorators: [{
type: Directive,
args: [{
selector: '[clrPopoverOpenCloseButton]',
host: {
'[class.clr-smart-open-close]': 'true',
},
}]
}], ctorParameters: function () { return [{ type: ClrPopoverToggleService }]; }, propDecorators: { openCloseChange: [{
type: Output,
args: ['clrPopoverOpenCloseChange']
}], handleClick: [{
type: HostListener,
args: ['click', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverModuleNext {
}
ClrPopoverModuleNext.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverModuleNext, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrPopoverModuleNext.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverModuleNext, declarations: [ClrPopoverAnchor, ClrPopoverCloseButton, ClrPopoverOpenCloseButton, ClrPopoverContent], exports: [ClrPopoverAnchor, ClrPopoverCloseButton, ClrPopoverOpenCloseButton, ClrPopoverContent] });
ClrPopoverModuleNext.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverModuleNext });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverModuleNext, decorators: [{
type: NgModule,
args: [{
imports: [],
declarations: [ClrPopoverAnchor, ClrPopoverCloseButton, ClrPopoverOpenCloseButton, ClrPopoverContent],
exports: [ClrPopoverAnchor, ClrPopoverCloseButton, ClrPopoverOpenCloseButton, ClrPopoverContent],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ButtonInGroupService {
constructor() {
this._changes = new Subject();
}
get changes() {
return this._changes.asObservable();
}
updateButtonGroup(button) {
this._changes.next(button);
}
}
ButtonInGroupService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonInGroupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ButtonInGroupService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonInGroupService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonInGroupService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrButton {
constructor(buttonInGroupService) {
this.buttonInGroupService = buttonInGroupService;
this._click = new EventEmitter(false);
this._inMenu = false;
this._enableService = false;
this._classNames = 'btn';
this._name = null;
this._type = null;
this._disabled = null;
this._id = uniqueIdFactory();
}
get inMenu() {
return this._inMenu;
}
set inMenu(value) {
value = !!value;
if (this._inMenu !== value) {
this._inMenu = value;
// We check if the service flag is enabled
// and if the service exists because the service is optional
if (this._enableService && this.buttonInGroupService) {
this.buttonInGroupService.updateButtonGroup(this);
}
}
}
get classNames() {
return this._classNames;
}
set classNames(value) {
if (typeof value === 'string') {
const classNames = value.split(' ');
if (classNames.indexOf('btn') === -1) {
classNames.push('btn');
}
this._classNames = classNames.join(' ');
}
}
get name() {
return this._name;
}
set name(value) {
if (typeof value === 'string') {
this._name = value;
}
}
get type() {
return this._type;
}
set type(value) {
if (typeof value === 'string') {
this._type = value;
}
}
get id() {
return this._id;
}
set id(value) {
if (typeof value === 'string') {
this._id = value;
}
}
get disabled() {
return this._disabled;
}
set disabled(value) {
if (value !== null && value !== false) {
this._disabled = '';
}
else {
this._disabled = null;
}
}
get role() {
return this.inMenu ? 'menuitem' : null;
}
ngAfterViewInit() {
this._enableService = true;
}
loadingStateChange(state) {
this.loading = state === ClrLoadingState.LOADING;
}
emitClick() {
this._click.emit(true);
}
}
ClrButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrButton, deps: [{ token: ButtonInGroupService, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Component });
ClrButton.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrButton, selector: "clr-button", inputs: { inMenu: ["clrInMenu", "inMenu"], classNames: ["class", "classNames"], name: "name", type: "type", id: "id", disabled: "disabled" }, outputs: { _click: "click" }, providers: [{ provide: LoadingListener, useExisting: ClrButton }], viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["buttonProjectedRef"], descendants: true, static: true }], ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrButton, decorators: [{
type: Component,
args: [{
selector: 'clr-button',
template: `
`,
providers: [{ provide: LoadingListener, useExisting: ClrButton }],
}]
}], ctorParameters: function () { return [{ type: ButtonInGroupService, decorators: [{
type: SkipSelf
}, {
type: Optional
}] }]; }, propDecorators: { _click: [{
type: Output,
args: ['click']
}], templateRef: [{
type: ViewChild,
args: ['buttonProjectedRef', { static: true }]
}], inMenu: [{
type: Input,
args: ['clrInMenu']
}], classNames: [{
type: Input,
args: ['class']
}], name: [{
type: Input,
args: ['name']
}], type: [{
type: Input,
args: ['type']
}], id: [{
type: Input,
args: ['id']
}], disabled: [{
type: Input,
args: ['disabled']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* @description
*
* Developers should explicitly add this service to providers; it then can be injected
* into a constructor and used as a notifier for the `takeUntil` operator. This eliminates
* the need for boilerplates with subscriptions, and we don't need to implement the `OnDestroy`
* interface and teardown subscriptions there.
*
* This can be used as follows:
* ```ts
* @Component({
* selector: 'clr-button-group',
* templateUrl: 'button-group.html',
* providers: [ClrDestroyService],
* })
* export class ClrButtonGroup {
* constructor(public buttonGroupNewService: ButtonInGroupService, private destroy$: ClrDestroyService) {}
*
* ngAfterContentInit() {
* this.buttonGroupNewService.changes.pipe(takeUntil(this.destroy$)).subscribe(button => this.rearrangeButton(button));
* }
* }
* ```
*/
class ClrDestroyService extends Subject {
ngOnDestroy() {
this.next();
this.complete();
}
}
ClrDestroyService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDestroyService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
ClrDestroyService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDestroyService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDestroyService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ArrowKeyDirection;
(function (ArrowKeyDirection) {
ArrowKeyDirection["UP"] = "up";
ArrowKeyDirection["DOWN"] = "down";
ArrowKeyDirection["LEFT"] = "left";
ArrowKeyDirection["RIGHT"] = "right";
})(ArrowKeyDirection || (ArrowKeyDirection = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let FocusService$1 = class FocusService {
constructor(renderer) {
this.renderer = renderer;
this._unlistenFuncs = [];
}
get current() {
return this._current;
}
reset(first) {
this._current = first;
}
listenToArrowKeys(el) {
// The following listeners return false when there was an action to take for the key pressed,
// in order to prevent the default behavior of that key.
this._unlistenFuncs.push(this.renderer.listen(el, 'keydown.arrowup', () => !this.move(ArrowKeyDirection.UP)));
this._unlistenFuncs.push(this.renderer.listen(el, 'keydown.arrowdown', () => !this.move(ArrowKeyDirection.DOWN)));
this._unlistenFuncs.push(this.renderer.listen(el, 'keydown.arrowleft', () => !this.move(ArrowKeyDirection.LEFT)));
this._unlistenFuncs.push(this.renderer.listen(el, 'keydown.arrowright', () => !this.move(ArrowKeyDirection.RIGHT)));
}
registerContainer(el, tabIndex = '0') {
this.renderer.setAttribute(el, 'tabindex', tabIndex);
this.listenToArrowKeys(el);
// The following listeners return false when there was an action to take for the key pressed,
// in order to prevent the default behavior of that key.
this._unlistenFuncs.push(this.renderer.listen(el, 'keydown.space', () => !this.activateCurrent()));
this._unlistenFuncs.push(this.renderer.listen(el, 'keydown.enter', () => !this.activateCurrent()));
}
moveTo(item) {
/**
* Make sure that item is not undefined,
* This is safety net in the case that someone sometime decide to
* call this method without having FocusableItem.
*/
if (item === undefined) {
return;
}
if (this.current) {
this.current.blur();
}
item.focus();
this._current = item;
}
move(direction) {
let moved = false;
if (this.current) {
const next = this.current[direction];
if (next) {
// Turning the value into an Observable isn't great, but it's the fastest way to avoid code duplication.
// If performance ever matters for this, we can refactor using additional private methods.
const nextObs = isObservable(next) ? next : of(next);
nextObs.subscribe(item => {
if (item) {
this.moveTo(item);
moved = true;
}
});
}
}
return moved;
}
activateCurrent() {
if (this.current && this.current.activate) {
this.current.activate();
return true;
}
return false;
}
detachListeners() {
this._unlistenFuncs.forEach(unlisten => unlisten());
}
};
FocusService$1.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: FocusService$1, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Injectable });
FocusService$1.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: FocusService$1 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: FocusService$1, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.Renderer2 }]; } });
function clrFocusServiceFactory(existing, renderer) {
return existing || new FocusService$1(renderer);
}
const FOCUS_SERVICE_PROVIDER = {
provide: FocusService$1,
useFactory: clrFocusServiceFactory,
deps: [[new Optional(), new SkipSelf(), FocusService$1], Renderer2],
};
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var ClrSide;
(function (ClrSide) {
ClrSide[ClrSide["BEFORE"] = -1] = "BEFORE";
ClrSide[ClrSide["AFTER"] = 1] = "AFTER";
})(ClrSide || (ClrSide = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverPositions {
}
ClrPopoverPositions['top-right'] = {
axis: ClrAxis.VERTICAL,
side: ClrSide.BEFORE,
anchor: ClrAlignment.END,
content: ClrAlignment.END,
};
ClrPopoverPositions['top-left'] = {
axis: ClrAxis.VERTICAL,
side: ClrSide.BEFORE,
anchor: ClrAlignment.START,
content: ClrAlignment.START,
};
ClrPopoverPositions['bottom-right'] = {
axis: ClrAxis.VERTICAL,
side: ClrSide.AFTER,
anchor: ClrAlignment.END,
content: ClrAlignment.END,
};
ClrPopoverPositions['bottom-left'] = {
axis: ClrAxis.VERTICAL,
side: ClrSide.AFTER,
anchor: ClrAlignment.START,
content: ClrAlignment.START,
};
ClrPopoverPositions['right-top'] = {
axis: ClrAxis.HORIZONTAL,
side: ClrSide.AFTER,
anchor: ClrAlignment.CENTER,
content: ClrAlignment.END,
};
ClrPopoverPositions['right-bottom'] = {
axis: ClrAxis.HORIZONTAL,
side: ClrSide.AFTER,
anchor: ClrAlignment.START,
content: ClrAlignment.START,
};
ClrPopoverPositions['left-top'] = {
axis: ClrAxis.HORIZONTAL,
side: ClrSide.BEFORE,
anchor: ClrAlignment.CENTER,
content: ClrAlignment.END,
};
ClrPopoverPositions['left-bottom'] = {
axis: ClrAxis.HORIZONTAL,
side: ClrSide.BEFORE,
anchor: ClrAlignment.START,
content: ClrAlignment.START,
};
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const POPOVER_HOST_ANCHOR = new InjectionToken('POPOVER_HOST_ANCHOR');
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrStopEscapePropagationDirective {
constructor(toggleService) {
this.toggleService = toggleService;
this.lastOpenChange = null;
}
ngOnInit() {
this.subscription = this.toggleService.openChange.subscribe(open => {
this.lastOpenChange = open;
});
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
onEscapeKey(event) {
if (this.lastOpenChange !== null) {
if (this.lastOpenChange === false) {
event.stopPropagation();
}
this.lastOpenChange = null;
}
}
}
ClrStopEscapePropagationDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStopEscapePropagationDirective, deps: [{ token: ClrPopoverToggleService }], target: i0.ɵɵFactoryTarget.Directive });
ClrStopEscapePropagationDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrStopEscapePropagationDirective, isStandalone: true, host: { listeners: { "keyup.escape": "onEscapeKey($event)" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStopEscapePropagationDirective, decorators: [{
type: Directive,
args: [{
standalone: true,
}]
}], ctorParameters: function () { return [{ type: ClrPopoverToggleService }]; }, propDecorators: { onEscapeKey: [{
type: HostListener,
args: ['keyup.escape', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrPopoverHostDirective {
}
ClrPopoverHostDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverHostDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
ClrPopoverHostDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrPopoverHostDirective, isStandalone: true, providers: [
ClrPopoverToggleService,
ClrPopoverEventsService,
ClrPopoverPositionService,
{ provide: POPOVER_HOST_ANCHOR, useExisting: ElementRef },
], hostDirectives: [{ directive: ClrStopEscapePropagationDirective }], ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPopoverHostDirective, decorators: [{
type: Directive,
args: [{
standalone: true,
providers: [
ClrPopoverToggleService,
ClrPopoverEventsService,
ClrPopoverPositionService,
{ provide: POPOVER_HOST_ANCHOR, useExisting: ElementRef },
],
hostDirectives: [ClrStopEscapePropagationDirective],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class Linkers {
/**
* Links a set of focusable items to a parent along one direction
*/
static linkParent(items, parent, direction) {
items.forEach(item => (item[direction] = parent));
}
/**
* Double-links a set of focusable items vertically, possibly looping
*/
static linkVertical(items, loop = true) {
items.forEach((item, index) => {
if (index > 0) {
item.up = items[index - 1];
}
if (index < items.length - 1) {
item.down = items[index + 1];
}
});
if (loop && items.length > 1) {
items[0].up = items[items.length - 1];
items[items.length - 1].down = items[0];
}
}
}
// Right now I only need the two linkers above, but we can easily add more linkers. A couple examples:
// export function linkHorizontal(items: FocusableItem[], loop = true);
// export function linkTable(items: FocusableItem[][]);
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var InitialFocus;
(function (InitialFocus) {
InitialFocus["FIRST_ITEM"] = "first";
InitialFocus["LAST_ITEM"] = "last";
})(InitialFocus || (InitialFocus = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function collapse() {
'use strict';
return [
state('true', style({ height: 0, 'overflow-y': 'hidden' })),
transition('true => false', [animate(defaultAnimationTiming, style({ height: '*', 'overflow-y': 'hidden' }))]),
transition('false => true', [style({ height: '*', 'overflow-y': 'hidden' }), animate(defaultAnimationTiming)]),
];
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DomAdapter {
userDefinedWidth(element) {
element.classList.add('datagrid-cell-width-zero');
const userDefinedWidth = this.clientRect(element).width;
element.classList.remove('datagrid-cell-width-zero');
return userDefinedWidth;
}
scrollBarWidth(element) {
return element.offsetWidth - element.clientWidth;
}
scrollWidth(element) {
return element.scrollWidth || 0;
}
computedHeight(element) {
return parseInt(getComputedStyle(element).getPropertyValue('height'), 10);
}
clientRect(element) {
const elementClientRect = element.getBoundingClientRect();
return {
top: parseInt(elementClientRect.top, 10),
bottom: parseInt(elementClientRect.bottom, 10),
left: parseInt(elementClientRect.left, 10),
right: parseInt(elementClientRect.right, 10),
width: parseInt(elementClientRect.width, 10),
height: parseInt(elementClientRect.height, 10),
};
}
minWidth(element) {
return parseInt(getComputedStyle(element).getPropertyValue('min-width'), 10);
}
focus(element) {
element.focus();
}
}
DomAdapter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DomAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
DomAdapter.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DomAdapter });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DomAdapter, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrExpandableAnimation {
constructor(element, domAdapter) {
this.element = element;
this.domAdapter = domAdapter;
this.startHeight = 0;
}
get expandAnimation() {
return { value: this.clrExpandTrigger, params: { startHeight: this.startHeight } };
}
animationDone() {
// A "safe" auto-update of the height ensuring basic OOTB user experience .
// Prone to small jumps in initial animation height if data was changed in the meantime, window was resized, etc.
// For optimal behavior call manually updateStartHeight() from the parent component before initiating the update.
this.updateStartHeight();
}
updateStartHeight() {
this.startHeight = this.domAdapter.computedHeight(this.element.nativeElement) || 0;
}
}
ClrExpandableAnimation.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrExpandableAnimation, deps: [{ token: i0.ElementRef }, { token: DomAdapter }], target: i0.ɵɵFactoryTarget.Component });
ClrExpandableAnimation.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrExpandableAnimation, selector: "clr-expandable-animation", inputs: { clrExpandTrigger: "clrExpandTrigger" }, host: { listeners: { "@expandAnimation.done": "animationDone()" }, properties: { "@expandAnimation": "this.expandAnimation" } }, providers: [DomAdapter], ngImport: i0, template: ``, isInline: true, styles: [":host{display:block;overflow:hidden}\n"], animations: [
trigger('expandAnimation', [
transition('true <=> false', [
style({ height: '{{startHeight}}px' }),
animate('0.2s ease-in-out', style({ height: '*' })),
]),
]),
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrExpandableAnimation, decorators: [{
type: Component,
args: [{ selector: 'clr-expandable-animation', template: ``, animations: [
trigger('expandAnimation', [
transition('true <=> false', [
style({ height: '{{startHeight}}px' }),
animate('0.2s ease-in-out', style({ height: '*' })),
]),
]),
], providers: [DomAdapter], styles: [":host{display:block;overflow:hidden}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: DomAdapter }]; }, propDecorators: { clrExpandTrigger: [{
type: Input
}], expandAnimation: [{
type: HostBinding,
args: ['@expandAnimation']
}], animationDone: [{
type: HostListener,
args: ['@expandAnimation.done']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const EXPANDABLE_ANIMATION_DIRECTIVES = [ClrExpandableAnimation];
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function fade(opacity = 1) {
return [
transition('void => *', [style({ opacity: 0 }), animate(defaultAnimationTiming, style({ opacity: opacity }))]),
transition('* => void', [animate(defaultAnimationTiming, style({ opacity: 0 }))]),
];
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function fadeSlide(direction) {
let transform = null;
if (direction === 'up') {
transform = 'translate(0, 25%)';
}
else if (direction === 'down') {
transform = 'translate(0, -25%)';
}
else if (direction === 'left') {
transform = 'translate(25%, 0)';
}
else if (direction === 'right') {
transform = 'translate(-25%, 0)';
}
else {
throw new Error('Unknown direction ' + direction + ' for slide animation.');
}
return [
transition('void => *', [style({ opacity: 0, transform: transform }), animate(defaultAnimationTiming)]),
transition('* => void', [animate(defaultAnimationTiming, style({ opacity: 0, transform: transform }))]),
];
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function slide(direction) {
let transform = null;
if (direction === 'up') {
transform = 'translate(0, 25%)';
}
else if (direction === 'down') {
transform = 'translate(0, -25%)';
}
else if (direction === 'left') {
transform = 'translate(25%, 0)';
}
else if (direction === 'right') {
transform = 'translate(-25%, 0)';
}
else {
throw new Error('Unknown direction ' + direction + ' for slide animation.');
}
return [
transition('void => *', [style({ transform: transform }), animate(defaultAnimationTiming)]),
transition('* => void', [animate(defaultAnimationTiming, style({ transform: transform }))]),
];
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CLR_LOADING_DIRECTIVES = [ClrLoading];
class ClrLoadingModule {
}
ClrLoadingModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrLoadingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrLoadingModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrLoadingModule, declarations: [ClrLoading], imports: [CommonModule], exports: [ClrLoading] });
ClrLoadingModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrLoadingModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrLoadingModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [CLR_LOADING_DIRECTIVES],
exports: [CLR_LOADING_DIRECTIVES],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let activeCounter = 0;
const IF_ACTIVE_ID = new InjectionToken('IF_ACTIVE_ID');
function tokenFactory$1() {
return ++activeCounter;
}
const IF_ACTIVE_ID_PROVIDER = {
provide: IF_ACTIVE_ID,
useFactory: tokenFactory$1,
};
/*********
* @class IfActiveService
*
* @description
* An injectable service used by IfActive structural directives and the components that implement IfActive in their
* templates. It holds the value of the current state and provides an Observable that both the directive and the
* implementing component can subscribe to in order to take action on current value changes.
*
*/
class IfActiveService {
constructor() {
/********
* @property _currentChange
*
* @description
* A RXJS Subject that updates and provides subscriptions to for the current current state of a component template
* implemting the IfActive structural directive.
*
*/
this._currentChange = new Subject();
}
/*********
*
* @description
* A getter function that provides an observable for the _current Subject.
*
*/
get currentChange() {
return this._currentChange.asObservable();
}
/*********
*
* @description
* A property that gets/sets the current state of _current for this instance of IfActive structural directive.
* And, broadcasts the new value to all subscribers.
*
*/
get current() {
return this._current;
}
set current(value) {
if (this._current !== value) {
this._current = value;
this._currentChange.next(value);
}
}
}
IfActiveService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: IfActiveService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
IfActiveService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: IfActiveService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: IfActiveService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**********
*
* @class ClrIfActive
*
* @description
* A structural directive that controls whether or not the associated TemplateRef is instantiated or not.
* It makes use of a Component instance level service: IfActiveService to maintain state between itself and
* the component using it in the component template.
*
*/
class ClrIfActive {
constructor(ifActiveService, id, template, container) {
this.ifActiveService = ifActiveService;
this.id = id;
this.template = template;
this.container = container;
/**********
* @property activeChange
*
* @description
* An event emitter that emits when the active property is set to allow for 2way binding when the directive is
* used with de-structured / de-sugared syntax.
*
*/
this.activeChange = new EventEmitter(false);
this.wasActive = false;
this.checkAndUpdateView(ifActiveService.current);
this.subscription = this.ifActiveService.currentChange.subscribe(newCurrentId => {
this.checkAndUpdateView(newCurrentId);
});
}
/**
* @description
* A property that gets/sets IfActiveService.active with value.
*
*/
get active() {
return this.ifActiveService.current === this.id;
}
set active(value) {
if (value) {
this.ifActiveService.current = this.id;
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
/**
* @description
* Function that takes a any value and either created an embedded view for the associated ViewContainerRef or,
* Clears all views from the ViewContainerRef
*/
updateView(value) {
if (value) {
this.container.createEmbeddedView(this.template);
}
else {
this.container.clear();
}
}
checkAndUpdateView(currentId) {
const isNowActive = currentId === this.id;
// only emit if the new active state is changed since last time.
if (isNowActive !== this.wasActive) {
this.updateView(isNowActive);
this.activeChange.emit(isNowActive);
this.wasActive = isNowActive;
}
}
}
ClrIfActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIfActive, deps: [{ token: IfActiveService }, { token: IF_ACTIVE_ID }, { token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrIfActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrIfActive, selector: "[clrIfActive]", inputs: { active: ["clrIfActive", "active"] }, outputs: { activeChange: "clrIfActiveChange" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIfActive, decorators: [{
type: Directive,
args: [{
selector: '[clrIfActive]',
}]
}], ctorParameters: function () { return [{ type: IfActiveService }, { type: undefined, decorators: [{
type: Inject,
args: [IF_ACTIVE_ID]
}] }, { type: i0.TemplateRef }, { type: i0.ViewContainerRef }]; }, propDecorators: { activeChange: [{
type: Output,
args: ['clrIfActiveChange']
}], active: [{
type: Input,
args: ['clrIfActive']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**********
*
* @class ClrIfOpen
*
* @description
* A structural directive that controls whether or not the associated TemplateRef is instantiated or not.
* It makes use of a Component instance level service: ClrPopoverToggleService to maintain state between itself and the component
* using it in the component template.
*
*/
class ClrIfOpen {
constructor(toggleService, template, container) {
this.toggleService = toggleService;
this.template = template;
this.container = container;
/**********
* @property openChange
*
* @description
* An event emitter that emits when the open property is set to allow for 2way binding when the directive is
* used with de-structured / de-sugared syntax.
*/
this.openChange = new EventEmitter(false);
this.subscription = this.toggleService.openChange.subscribe(change => {
this.updateView(change);
this.openChange.emit(change);
});
}
/**
* @description
* A property that gets/sets ClrPopoverToggleService.open with value.
*/
get open() {
return this.toggleService.open;
}
set open(value) {
this.toggleService.open = value;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
/**
* @description
* Function that takes a boolean value and either created an embedded view for the associated ViewContainerRef or,
* Clears all views from the ViewContainerRef
*
* @param value
*/
updateView(value) {
if (value) {
this.container.createEmbeddedView(this.template);
}
else {
this.container.clear();
}
}
}
ClrIfOpen.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIfOpen, deps: [{ token: ClrPopoverToggleService }, { token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrIfOpen.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrIfOpen, selector: "[clrIfOpen]", inputs: { open: ["clrIfOpen", "open"] }, outputs: { openChange: "clrIfOpenChange" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIfOpen, decorators: [{
type: Directive,
args: [{
selector: '[clrIfOpen]',
}]
}], ctorParameters: function () { return [{ type: ClrPopoverToggleService }, { type: i0.TemplateRef }, { type: i0.ViewContainerRef }]; }, propDecorators: { openChange: [{
type: Output,
args: ['clrIfOpenChange']
}], open: [{
type: Input,
args: ['clrIfOpen']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrIfExpanded {
constructor(template, container, el, renderer, expand) {
this.template = template;
this.container = container;
this.el = el;
this.renderer = renderer;
this.expand = expand;
this.expandedChange = new EventEmitter(true);
this._expanded = false;
/**
* Subscriptions to all the services and queries changes
*/
this._subscriptions = [];
this._subscriptions.push(expand.expandChange.subscribe(() => {
this.updateView();
this.expandedChange.emit(this.expand.expanded);
}));
}
get expanded() {
return this._expanded;
}
set expanded(value) {
if (typeof value === 'boolean') {
this.expand.expanded = value;
this._expanded = value;
}
}
ngOnInit() {
this.expand.expandable++;
this.updateView();
}
ngOnDestroy() {
this.expand.expandable--;
this._subscriptions.forEach((sub) => sub.unsubscribe());
}
updateView() {
if (this.expand.expanded && this.container.length !== 0) {
return;
}
if (this.template) {
if (this.expand.expanded) {
// Should we pass a context? I don't see anything useful to pass right now,
// but we can come back to it in the future as a solution for additional features.
this.container.createEmbeddedView(this.template);
}
else {
// TODO: Move when we move the animation logic to Datagrid Row Expand
// We clear before the animation is over. Not ideal, but doing better would involve a much heavier
// process for very little gain. Once Angular animations are dynamic enough, we should be able to
// get the optimal behavior.
this.container.clear();
}
}
else {
try {
// If we don't have a template ref, we fallback to a crude display: none for now.
if (this.expand.expanded) {
this.renderer.setStyle(this.el.nativeElement, 'display', null);
}
else {
this.renderer.setStyle(this.el.nativeElement, 'display', 'none');
}
}
catch (e) {
// We catch the case where clrIfExpanded was put on a non-DOM element, and we just do nothing
}
}
}
}
ClrIfExpanded.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIfExpanded, deps: [{ token: i0.TemplateRef, optional: true }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: IfExpandService }], target: i0.ɵɵFactoryTarget.Directive });
ClrIfExpanded.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrIfExpanded, selector: "[clrIfExpanded]", inputs: { expanded: ["clrIfExpanded", "expanded"] }, outputs: { expandedChange: "clrIfExpandedChange" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrIfExpanded, decorators: [{
type: Directive,
args: [{
selector: '[clrIfExpanded]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }, { type: i0.ViewContainerRef }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: IfExpandService }]; }, propDecorators: { expandedChange: [{
type: Output,
args: ['clrIfExpandedChange']
}], expanded: [{
type: Input,
args: ['clrIfExpanded']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CONDITIONAL_DIRECTIVES = [ClrIfActive, ClrIfOpen, ClrIfExpanded];
class ClrConditionalModule {
}
ClrConditionalModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrConditionalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrConditionalModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrConditionalModule, declarations: [ClrIfActive, ClrIfOpen, ClrIfExpanded], imports: [CommonModule], exports: [ClrIfActive, ClrIfOpen, ClrIfExpanded] });
ClrConditionalModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrConditionalModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrConditionalModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [CONDITIONAL_DIRECTIVES],
exports: [CONDITIONAL_DIRECTIVES],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const FOCUS_ON_VIEW_INIT = new InjectionToken('FOCUS_ON_VIEW_INIT');
// This provider holds the default value for clrFocusOnViewInit directive's isEnabled property.
// So users can interject this provider and set their own value for this provider.
const FOCUS_ON_VIEW_INIT_PROVIDER = {
provide: FOCUS_ON_VIEW_INIT,
useValue: true,
};
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/* This directive is for guiding the document focus to the newly added content when its view is initialized
so that assistive technologies can read its content. */
class ClrFocusOnViewInit {
constructor(el, platformId, focusOnViewInit, document, renderer, ngZone) {
this.el = el;
this.platformId = platformId;
this.focusOnViewInit = focusOnViewInit;
this.renderer = renderer;
this.directFocus = true; // true if the element gets focused without need to set tabindex;
this.destroy$ = new Subject();
this._isEnabled = this.focusOnViewInit;
// Angular compiler doesn't understand the type Document
// when working out the metadata for injectable parameters,
// even though it understands the injection token DOCUMENT
// https://github.com/angular/angular/issues/20351
this.document = document;
ngZone.runOutsideAngular(() => fromEvent(el.nativeElement, 'focusout')
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
if (!this.directFocus) {
// manually set attributes and styles should be removed
this.renderer.removeAttribute(this.el.nativeElement, 'tabindex');
this.renderer.setStyle(this.el.nativeElement, 'outline', null);
}
}));
}
set isEnabled(value) {
if (this.focusOnViewInit && typeof value === 'boolean') {
this._isEnabled = value;
}
}
ngAfterViewInit() {
this.focus();
}
ngOnDestroy() {
this.destroy$.next();
}
focus() {
if (!isPlatformBrowser(this.platformId)) {
return;
}
if (!this._isEnabled) {
return;
}
if (this.document && this.document.activeElement !== this.el.nativeElement) {
this.el.nativeElement.focus();
if (this.document.activeElement !== this.el.nativeElement) {
// if it's not directly focused now, it means it was a non-interactive element
// so we need to give it a tabindex.
this.directFocus = false;
this.renderer.setAttribute(this.el.nativeElement, 'tabindex', '-1');
this.renderer.setStyle(this.el.nativeElement, 'outline', 'none');
this.el.nativeElement.focus();
}
}
}
}
ClrFocusOnViewInit.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFocusOnViewInit, deps: [{ token: i0.ElementRef }, { token: PLATFORM_ID }, { token: FOCUS_ON_VIEW_INIT }, { token: DOCUMENT }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
ClrFocusOnViewInit.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrFocusOnViewInit, selector: "[clrFocusOnViewInit]", inputs: { isEnabled: ["clrFocusOnViewInit", "isEnabled"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFocusOnViewInit, decorators: [{
type: Directive,
args: [{
selector: '[clrFocusOnViewInit]',
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [FOCUS_ON_VIEW_INIT]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: i0.Renderer2 }, { type: i0.NgZone }]; }, propDecorators: { isEnabled: [{
type: Input,
args: ['clrFocusOnViewInit']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const FOCUS_ON_VIEW_INIT_DIRECTIVES = [ClrFocusOnViewInit];
class ClrFocusOnViewInitModule {
}
ClrFocusOnViewInitModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFocusOnViewInitModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrFocusOnViewInitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrFocusOnViewInitModule, declarations: [ClrFocusOnViewInit], imports: [CommonModule], exports: [ClrFocusOnViewInit] });
ClrFocusOnViewInitModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFocusOnViewInitModule, providers: [FOCUS_ON_VIEW_INIT_PROVIDER], imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFocusOnViewInitModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [FOCUS_ON_VIEW_INIT_DIRECTIVES],
providers: [FOCUS_ON_VIEW_INIT_PROVIDER],
exports: [FOCUS_ON_VIEW_INIT_DIRECTIVES],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrStandaloneCdkTrapFocus extends CdkTrapFocus {
}
ClrStandaloneCdkTrapFocus.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStandaloneCdkTrapFocus, deps: null, target: i0.ɵɵFactoryTarget.Directive });
ClrStandaloneCdkTrapFocus.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrStandaloneCdkTrapFocus, isStandalone: true, usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStandaloneCdkTrapFocus, decorators: [{
type: Directive,
args: [{
standalone: true,
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ButtonGroupFocusHandler {
constructor(focusService, toggleService, renderer) {
this.focusService = focusService;
this.toggleService = toggleService;
this.renderer = renderer;
this.initialFocus = InitialFocus.FIRST_ITEM;
this._unlistenFuncs = [];
}
ngOnDestroy() {
this._unlistenFuncs.forEach((unlisten) => unlisten());
this.focusService.detachListeners();
}
initialize({ menu, menuToggle }) {
this.menu = menu;
this.menuToggle = menuToggle;
this.focusService.registerContainer(this.menu, '-1');
this.listenToKeys();
this.linkButtons();
switch (this.initialFocus) {
case InitialFocus.LAST_ITEM:
this.focusLastItem();
break;
default:
this.focusFirstItem();
break;
}
}
resetButtonsFocus() {
this.buttons.forEach(button => {
button.blur();
});
}
listenToKeys() {
this._unlistenFuncs.push(this.renderer.listen(this.menu, 'keydown.shift.tab', event => this.closeMenu(event, false)));
this._unlistenFuncs.push(this.renderer.listen(this.menu, 'keydown.tab', event => this.closeMenu(event, true)));
}
closeMenu(event, focusBackOnToggle) {
this.toggleService.toggleWithEvent(event);
if (focusBackOnToggle) {
this.menuToggle.focus();
}
this.resetButtonsFocus();
}
linkButtons() {
const buttonElements = Array.from(this.menu.children);
this.buttons = buttonElements.map(buttonElement => {
this._unlistenFuncs.push(this.renderer.listen(buttonElement, 'click', event => this.closeMenu(event, true)));
return {
id: buttonElement.id,
value: buttonElement,
focus: () => {
buttonElement.setAttribute('tabindex', '0');
buttonElement.focus();
},
blur: () => {
buttonElement.setAttribute('tabindex', '-1');
buttonElement.blur();
},
};
});
this.resetButtonsFocus();
Linkers.linkVertical(this.buttons);
}
focusFirstItem() {
if (this.buttons.length) {
this.focusService.moveTo(this.buttons[0]);
}
this.initialFocus = InitialFocus.FIRST_ITEM;
}
focusLastItem() {
if (this.buttons.length) {
this.focusService.moveTo(this.buttons[this.buttons.length - 1]);
}
this.initialFocus = InitialFocus.FIRST_ITEM;
}
}
ButtonGroupFocusHandler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonGroupFocusHandler, deps: [{ token: FocusService$1 }, { token: ClrPopoverToggleService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Injectable });
ButtonGroupFocusHandler.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonGroupFocusHandler });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonGroupFocusHandler, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: FocusService$1 }, { type: ClrPopoverToggleService }, { type: i0.Renderer2 }]; } });
const BUTTON_GROUP_FOCUS_HANDLER_PROVIDER = {
provide: ButtonGroupFocusHandler,
};
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrButtonGroup {
constructor(buttonGroupNewService, toggleService, commonStrings, destroy$, focusHandler) {
this.buttonGroupNewService = buttonGroupNewService;
this.toggleService = toggleService;
this.commonStrings = commonStrings;
this.destroy$ = destroy$;
this.focusHandler = focusHandler;
this.clrToggleButtonAriaLabel = this.commonStrings.keys.rowActions;
this.popoverId = uniqueIdFactory();
this.InitialFocus = InitialFocus;
this.popoverPosition = ClrPopoverPositions['bottom-left'];
this.inlineButtons = [];
this.menuButtons = [];
}
get menuPosition() {
return this._menuPosition;
}
set menuPosition(pos) {
if (pos && ClrPopoverPositions[pos]) {
this._menuPosition = pos;
}
else {
this._menuPosition = 'bottom-left';
}
this.popoverPosition = ClrPopoverPositions[this._menuPosition];
}
get open() {
return this.toggleService.open;
}
/**
* 1. Initializes the initial Button Group View
* 2. Subscribes to changes on the ContentChildren
* in case the user content projection changes
*/
ngAfterContentInit() {
this.initializeButtons();
this.buttonGroupNewService.changes.pipe(takeUntil(this.destroy$)).subscribe(button => this.rearrangeButton(button));
this.buttons.changes.subscribe(() => {
this.initializeButtons();
});
}
ngAfterViewInit() {
this.handleFocusOnMenuOpen();
}
/**
* Moves the button into the other ViewContainer
* when an update is received.
*
* @param button
*/
rearrangeButton(button) {
let fromView;
let toView;
if (button.inMenu) {
fromView = this.inlineButtons;
toView = this.menuButtons;
}
else {
fromView = this.menuButtons;
toView = this.inlineButtons;
}
const index = fromView.indexOf(button);
if (index > -1) {
fromView.splice(index, 1);
const moveIndex = this.getMoveIndex(button);
if (moveIndex <= toView.length) {
toView.splice(moveIndex, 0, button);
}
}
}
openMenu(event, initialFocus) {
this.focusHandler.initialFocus = initialFocus;
if (!this.toggleService.open) {
this.toggleService.toggleWithEvent(event);
}
}
/**
* Author: Eudes
*
* Finds the order of a button w.r.t other buttons
*
* @param buttonToMove
* @returns
*/
getMoveIndex(buttonToMove) {
const tempArr = this.buttons.filter(button => button.inMenu === buttonToMove.inMenu);
return tempArr.indexOf(buttonToMove);
}
initializeButtons() {
const tempInlineButtons = [];
const tempInMenuButtons = [];
this.buttons.forEach(button => {
if (button.inMenu) {
tempInMenuButtons.push(button);
}
else {
tempInlineButtons.push(button);
}
});
this.inlineButtons = tempInlineButtons;
this.menuButtons = tempInMenuButtons;
}
handleFocusOnMenuOpen() {
this.toggleService.popoverVisible.pipe(takeUntil(this.destroy$)).subscribe(visible => {
if (visible) {
this.focusHandler.initialize({
menu: this.menu.nativeElement,
menuToggle: this.menuToggle.nativeElement,
});
}
});
}
}
ClrButtonGroup.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrButtonGroup, deps: [{ token: ButtonInGroupService }, { token: ClrPopoverToggleService }, { token: ClrCommonStringsService }, { token: ClrDestroyService }, { token: ButtonGroupFocusHandler }], target: i0.ɵɵFactoryTarget.Component });
ClrButtonGroup.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrButtonGroup, selector: "clr-button-group", inputs: { clrToggleButtonAriaLabel: "clrToggleButtonAriaLabel", menuPosition: ["clrMenuPosition", "menuPosition"] }, host: { properties: { "class.btn-group": "true" } }, providers: [ButtonInGroupService, ClrDestroyService, BUTTON_GROUP_FOCUS_HANDLER_PROVIDER, FOCUS_SERVICE_PROVIDER], queries: [{ propertyName: "buttons", predicate: ClrButton }], viewQueries: [{ propertyName: "menuToggle", first: true, predicate: ["menuToggle"], descendants: true }, { propertyName: "menu", first: true, predicate: ["menu"], descendants: true }], hostDirectives: [{ directive: ClrPopoverHostDirective }], ngImport: i0, template: "\n\n\n\n
`,
host: {
'[class.clr-form-control]': 'true',
'[class.clr-combobox-form-control]': 'true',
'[class.clr-form-control-disabled]': 'control?.disabled',
'[class.clr-row]': 'addGrid()',
},
providers: [IfControlStateService, NgControlService, ControlIdService, ControlClassService, ComboboxContainerService],
}]
}], ctorParameters: function () { return [{ type: IfControlStateService }, { type: LayoutService, decorators: [{
type: Optional
}] }, { type: ControlClassService }, { type: NgControlService }, { type: ComboboxContainerService }, { type: i0.ElementRef }]; }, propDecorators: { controlContainer: [{
type: ViewChild,
args: ['controlContainer']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class MultiSelectComboboxModel {
containsItem(item) {
return this.model ? this.model.includes(item) : false;
}
select(item) {
this.addItem(item);
}
unselect(item) {
this.removeItem(item);
}
isEmpty() {
return !(this.model && this.model.length > 0);
}
pop() {
let item;
if (this.model && this.model.length > 0) {
item = this.model[this.model.length - 1];
this.removeItem(item);
}
return item;
}
toString(displayField, index = -1) {
let displayString = '';
if (this.model) {
// If the model is array, we can use a specific item from it, to retrieve the display value.
if (index > -1) {
if (this.model[index]) {
// If we have a defined display field, we'll use it's value as display value
if (displayField && this.model[index][displayField]) {
displayString += this.model[index][displayField];
}
else {
// If we don't have a defined display field, we'll use the toString representation of the
// item as display value.
displayString += this.model[index].toString();
}
}
}
else {
this.model.forEach((model) => {
// If we have a defined display field, we'll use it's value as display value
if (displayField && model[displayField]) {
displayString += model[displayField];
}
else {
// If we don't have a defined display field, we'll use the toString representation of the
// model as display value.
displayString += model.toString();
}
displayString += ' ';
});
}
}
return displayString.trim();
}
addItem(item) {
if (!this.containsItem(item)) {
this.model = this.model || [];
this.model.push(item);
}
}
removeItem(item) {
if (this.model === null || this.model === undefined) {
return;
}
const index = this.model.indexOf(item);
if (index > -1) {
this.model.splice(index, 1);
}
// we intentionally set the model to null for form validation
if (this.model.length === 0) {
this.model = null;
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class SingleSelectComboboxModel {
containsItem(item) {
return this.model === item;
}
select(item) {
this.model = item;
}
unselect(item) {
if (this.containsItem(item)) {
this.model = null;
}
}
isEmpty() {
return !this.model;
}
pop() {
const item = this.model;
this.model = null;
return item;
}
toString(displayField) {
if (!this.model) {
return '';
}
if (displayField && this.model[displayField]) {
return this.model[displayField];
}
else {
return this.model.toString();
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrOptionSelected {
constructor(template) {
this.template = template;
}
}
ClrOptionSelected.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOptionSelected, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrOptionSelected.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrOptionSelected, selector: "[clrOptionSelected]", inputs: { selected: ["clrOptionSelected", "selected"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOptionSelected, decorators: [{
type: Directive,
args: [{
selector: '[clrOptionSelected]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { selected: [{
type: Input,
args: ['clrOptionSelected']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class FocusableItem {
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function customFocusableItemProvider(implementation) {
return [
implementation,
{
provide: FocusableItem,
useExisting: implementation,
},
];
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class PseudoFocusModel extends SingleSelectComboboxModel {
constructor() {
super(...arguments);
this._focusChanged = new BehaviorSubject(null);
}
get focusChanged() {
return this._focusChanged.asObservable();
}
select(item) {
if (this.model !== item) {
this.model = item;
this._focusChanged.next(item);
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class OptionSelectionService {
constructor() {
this.loading = false;
// Display all options on first open, even if filter text exists.
// https://github.com/vmware-clarity/ng-clarity/issues/386
this.showAllOptions = true;
this._currentInput = '';
this._inputChanged = new BehaviorSubject('');
this._selectionChanged = new ReplaySubject(1);
this.inputChanged = this._inputChanged.asObservable();
}
get currentInput() {
return this._currentInput;
}
set currentInput(input) {
// clear value in single selection model when input is empty
if (input === '' && !this.multiselectable) {
this.setSelectionValue(null);
}
this._currentInput = input;
this._inputChanged.next(input);
}
// This observable is for notifying the ClrOption to update its
// selection by comparing the value
get selectionChanged() {
return this._selectionChanged.asObservable();
}
get multiselectable() {
return this.selectionModel instanceof MultiSelectComboboxModel;
}
select(item) {
if (item === null || item === undefined || this.selectionModel.containsItem(item)) {
return;
}
this.selectionModel.select(item);
this._selectionChanged.next(this.selectionModel);
}
toggle(item) {
if (item === null || item === undefined) {
return;
}
if (this.selectionModel.containsItem(item)) {
this.selectionModel.unselect(item);
}
else {
this.selectionModel.select(item);
}
this._selectionChanged.next(this.selectionModel);
}
unselect(item) {
if (item === null || item === undefined || !this.selectionModel.containsItem(item)) {
return;
}
this.selectionModel.unselect(item);
this._selectionChanged.next(this.selectionModel);
}
// TODO: Add support for trackBy and compareFn
setSelectionValue(value) {
// NOTE: Currently we assume that no 2 options will have the same value
// but Eudes and I discussed that this is a possibility but we will handle
// this later
// if selection is undefined, or its value hasn't changed, or changing from null <-> undefined, that's not really changing so we return
if (!this.selectionModel || this.selectionModel.model === value || (!this.selectionModel.model && !value)) {
return;
}
this.selectionModel.model = value;
this._selectionChanged.next(this.selectionModel);
}
}
OptionSelectionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: OptionSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
OptionSelectionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: OptionSelectionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: OptionSelectionService, decorators: [{
type: Injectable
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ComboboxFocusHandler {
constructor(rendererFactory, toggleService, selectionService, platformId) {
this.toggleService = toggleService;
this.selectionService = selectionService;
this.platformId = platformId;
this.pseudoFocus = new PseudoFocusModel();
this.optionData = [];
this.handleFocusSubscription();
// Direct renderer injection can be problematic and leads to failing tests at least
this.renderer = rendererFactory.createRenderer(null, null);
}
get trigger() {
return this._trigger;
}
set trigger(el) {
this._trigger = el;
this.addFocusOnBlurListener(el);
}
get listbox() {
return this._listbox;
}
set listbox(el) {
this._listbox = el;
this.addFocusOnBlurListener(el);
}
get textInput() {
return this._textInput;
}
set textInput(el) {
this._textInput = el;
this.renderer.listen(el, 'keydown', event => !this.handleTextInput(event));
this.addFocusOnBlurListener(el);
}
focusInput() {
if (this.textInput && isPlatformBrowser(this.platformId)) {
this.textInput.focus();
}
}
focusFirstActive() {
if (this.optionData.length > 0) {
if (this.selectionService.selectionModel.isEmpty()) {
this.pseudoFocus.select(this.optionData[0]);
}
else {
let firstActive;
if (this.selectionService.multiselectable) {
firstActive = this.selectionService.selectionModel.model[0];
}
else {
firstActive = this.selectionService.selectionModel.model;
}
const activeProxy = this.optionData.find(option => option.value === firstActive);
if (activeProxy) {
// active element is visible
this.pseudoFocus.select(activeProxy);
}
else {
// we have active element, but it's filtered out
this.pseudoFocus.select(this.optionData[0]);
}
this.scrollIntoSelectedModel('auto');
}
}
}
addOptionValues(options) {
this.optionData = options;
}
handleFocusSubscription() {
this.toggleService.openChange.subscribe(open => {
if (!open) {
this.pseudoFocus.model = null;
}
});
}
moveFocusTo(direction) {
let index = this.optionData.findIndex(option => option.equals(this.pseudoFocus.model));
if (direction === ArrowKeyDirection.UP) {
if (index === -1 || index === 0) {
index = this.optionData.length - 1;
}
else {
index--;
}
}
else if (direction === ArrowKeyDirection.DOWN) {
if (index === -1 || index === this.optionData.length - 1) {
index = 0;
}
else {
index++;
}
}
this.pseudoFocus.select(this.optionData[index]);
this.scrollIntoSelectedModel();
}
openAndMoveTo(direction) {
if (!this.toggleService.open) {
this.toggleService.openChange.pipe(take(1)).subscribe(open => {
if (open) {
this.moveFocusTo(direction);
}
});
this.toggleService.open = true;
}
else {
this.moveFocusTo(direction);
}
}
// this service is only interested in keys that may move the focus
handleTextInput(event) {
let preventDefault = false;
const key = normalizeKey(event.key);
if (event) {
switch (key) {
case Keys.Enter:
if (this.toggleService.open && this.pseudoFocus.model) {
if (this.selectionService.multiselectable) {
this.selectionService.toggle(this.pseudoFocus.model.value);
}
else {
this.selectionService.select(this.pseudoFocus.model.value);
}
preventDefault = true;
}
break;
case Keys.Space:
if (!this.toggleService.open) {
this.toggleService.open = true;
preventDefault = true;
}
break;
case Keys.ArrowUp:
this.preventViewportScrolling(event);
this.openAndMoveTo(ArrowKeyDirection.UP);
preventDefault = true;
break;
case Keys.ArrowDown:
this.preventViewportScrolling(event);
this.openAndMoveTo(ArrowKeyDirection.DOWN);
preventDefault = true;
break;
default:
// Any other keypress
if (event.key !== Keys.Tab &&
!(this.selectionService.multiselectable && event.key === Keys.Backspace) &&
!(event.key === Keys.Escape) &&
!this.toggleService.open) {
this.toggleService.open = true;
}
break;
}
}
return preventDefault;
}
scrollIntoSelectedModel(behavior = 'smooth') {
if (this.pseudoFocus.model && this.pseudoFocus.model.el) {
this.pseudoFocus.model.el.scrollIntoView({ behavior, block: 'center', inline: 'nearest' });
}
}
preventViewportScrolling(event) {
event.preventDefault();
event.stopImmediatePropagation();
}
addFocusOnBlurListener(el) {
if (isPlatformBrowser(this.platformId)) {
this.renderer.listen(el, 'blur', event => {
if (this.focusOutOfComponent(event)) {
this.toggleService.open = false;
// Workaround for popover close-on-outside-click timing issues in Edge browser
if (this.componentCdRef) {
this.componentCdRef.detectChanges();
}
}
});
}
}
focusOutOfComponent(event) {
// event.relatedTarget is null in IE11. In that case we use document.activeElement
// which points to the element that becomes active as the blur event occurs on the input.
const target = (event.relatedTarget || document.activeElement);
return !(this.textInput.contains(target) || this.trigger.contains(target) || this.listbox.contains(target));
}
}
ComboboxFocusHandler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ComboboxFocusHandler, deps: [{ token: i0.RendererFactory2 }, { token: ClrPopoverToggleService }, { token: OptionSelectionService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
ComboboxFocusHandler.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ComboboxFocusHandler });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ComboboxFocusHandler, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.RendererFactory2 }, { type: ClrPopoverToggleService }, { type: OptionSelectionService }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; } });
const COMBOBOX_FOCUS_HANDLER_PROVIDER = customFocusableItemProvider(ComboboxFocusHandler);
class OptionData {
constructor(id, value) {
this.id = id;
this.value = value;
}
equals(other) {
if (!other) {
return false;
}
return this.id === other.id && this.value === other.value;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrOption {
constructor(elRef, commonStrings, focusHandler, optionSelectionService) {
this.elRef = elRef;
this.commonStrings = commonStrings;
this.focusHandler = focusHandler;
this.optionSelectionService = optionSelectionService;
// A proxy with only the necessary data to be used for a11y and the focus handler service.
this.optionProxy = new OptionData(null, null);
this.optionProxy.el = this.elRef.nativeElement;
}
get optionId() {
return this._id;
}
set optionId(id) {
this._id = id;
this.optionProxy.id = this._id;
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
this.optionProxy.value = value;
}
get selected() {
return (this.optionSelectionService.selectionModel && this.optionSelectionService.selectionModel.containsItem(this.value));
}
get focusClass() {
return this.focusHandler.pseudoFocus.containsItem(this.optionProxy);
}
ngOnInit() {
if (!this._id) {
this._id = 'clr-option-' + uniqueIdFactory();
this.optionProxy.id = this._id;
}
}
onClick() {
if (this.optionSelectionService.multiselectable) {
this.optionSelectionService.toggle(this.value);
}
else {
this.optionSelectionService.select(this.value);
}
// As the popover stays open in multi-select mode now, we have to take focus back to the input
// This way we achieve two things:
// - do not lose focus
// - we're still able to use onBlur for "outside-click" handling
this.focusHandler.focusInput();
}
}
ClrOption.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOption, deps: [{ token: i0.ElementRef }, { token: ClrCommonStringsService }, { token: ComboboxFocusHandler }, { token: OptionSelectionService }], target: i0.ɵɵFactoryTarget.Component });
ClrOption.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrOption, selector: "clr-option", inputs: { optionId: ["id", "optionId"], value: ["clrValue", "value"] }, host: { listeners: { "click": "onClick()" }, properties: { "class.clr-combobox-option": "true", "attr.role": "\"option\"", "attr.tabindex": "-1", "attr.id": "optionId", "class.active": "this.selected", "class.clr-focus": "this.focusClass" } }, ngImport: i0, template: `
{{ commonStrings.keys.comboboxSelected }}
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOption, decorators: [{
type: Component,
args: [{
selector: 'clr-option',
template: `
{{ commonStrings.keys.comboboxSelected }}
`,
host: {
'[class.clr-combobox-option]': 'true',
'[attr.role]': '"option"',
// Do not remove. Or click-selection will not work.
'[attr.tabindex]': '-1',
'[attr.id]': 'optionId',
},
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: OptionSelectionService }]; }, propDecorators: { optionId: [{
type: Input,
args: ['id']
}], value: [{
type: Input,
args: ['clrValue']
}], selected: [{
type: HostBinding,
args: ['class.active']
}], focusClass: [{
type: HostBinding,
args: ['class.clr-focus']
}], onClick: [{
type: HostListener,
args: ['click']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let nbOptionsComponents = 0;
class ClrOptions {
constructor(optionSelectionService, id, el, commonStrings, focusHandler, toggleService, parentHost, document) {
this.optionSelectionService = optionSelectionService;
this.id = id;
this.el = el;
this.commonStrings = commonStrings;
this.focusHandler = focusHandler;
this.toggleService = toggleService;
this.document = document;
this.loading = false;
this.subscriptions = [];
if (!parentHost) {
throw new Error('clr-options should only be used inside of a clr-combobox');
}
if (!this.optionsId) {
this.optionsId = 'clr-options-' + nbOptionsComponents++;
}
}
get items() {
return this._items;
}
set items(items) {
this._items = items;
this.focusHandler.addOptionValues(this._items.map(option => option.optionProxy));
}
/**
* Tests if the list of options is empty, meaning it doesn't contain any items
*/
get emptyOptions() {
return !this.optionSelectionService.loading && this.items.length === 0;
}
ngAfterViewInit() {
this.focusHandler.listbox = this.el.nativeElement;
this.subscriptions.push(fromEvent(this.document, 'scroll', { capture: true }).subscribe(event => {
if (this.toggleService.open &&
event.target !== this.el.nativeElement &&
event.target !== this.focusHandler.textInput) {
this.toggleService.open = false;
}
}), this.items.changes.subscribe(items => {
if (items.length) {
setTimeout(() => {
this.focusHandler.focusFirstActive();
});
}
else {
this.focusHandler.pseudoFocus.pop();
}
}));
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
searchText(input) {
return this.commonStrings.parse(this.commonStrings.keys.comboboxSearching, { INPUT: input });
}
loadingStateChange(state) {
this.loading = state === ClrLoadingState.LOADING;
}
}
ClrOptions.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOptions, deps: [{ token: OptionSelectionService }, { token: IF_ACTIVE_ID }, { token: i0.ElementRef }, { token: ClrCommonStringsService }, { token: ComboboxFocusHandler }, { token: ClrPopoverToggleService }, { token: POPOVER_HOST_ANCHOR, optional: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component });
ClrOptions.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrOptions, selector: "clr-options", inputs: { optionsId: ["id", "optionsId"] }, host: { properties: { "class.clr-combobox-options": "true", "attr.role": "\"listbox\"", "id": "optionsId" } }, providers: [{ provide: LoadingListener, useExisting: ClrOptions }], queries: [{ propertyName: "items", predicate: ClrOption }], ngImport: i0, template: `
`,
host: {
'[class.clr-form-control]': 'true',
'[class.clr-form-control-disabled]': 'control?.disabled',
'[class.clr-row]': 'addGrid()',
},
providers: [
ControlClassService,
LayoutService,
ControlIdService,
FocusService,
NgControlService,
DatalistIdService,
IfControlStateService,
],
}]
}], ctorParameters: function () { return [{ type: ControlClassService }, { type: LayoutService, decorators: [{
type: Optional
}] }, { type: NgControlService }, { type: FocusService }, { type: IfControlStateService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatalistInput extends WrappedFormControl {
constructor(focusService, vcr, injector, control, renderer, el, datalistIdService) {
super(vcr, ClrDatalistContainer, injector, control, renderer, el);
this.focusService = focusService;
this.datalistIdService = datalistIdService;
if (!this.focusService) {
throw new Error('clrDatalist requires being wrapped in ');
}
}
ngAfterContentInit() {
// Subscriptions is inherited from WrappedFormControl, unsubscribe is handled there
this.subscriptions.push(this.datalistIdService.idChange.subscribe(id => (this.listValue = id)));
}
triggerFocus() {
if (this.focusService) {
this.focusService.focused = true;
}
}
triggerValidation() {
super.triggerValidation();
if (this.focusService) {
this.focusService.focused = false;
}
}
}
ClrDatalistInput.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatalistInput, deps: [{ token: FocusService, optional: true }, { token: i0.ViewContainerRef }, { token: i0.Injector }, { token: i1.NgControl, optional: true, self: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: DatalistIdService }], target: i0.ɵɵFactoryTarget.Directive });
ClrDatalistInput.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatalistInput, selector: "[clrDatalistInput]", host: { listeners: { "focus": "triggerFocus()", "blur": "triggerValidation()" }, properties: { "class.clr-input": "true", "attr.list": "listValue" } }, usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatalistInput, decorators: [{
type: Directive,
args: [{
selector: '[clrDatalistInput]',
host: {
'[class.clr-input]': 'true',
'[attr.list]': 'listValue',
},
}]
}], ctorParameters: function () { return [{ type: FocusService, decorators: [{
type: Optional
}] }, { type: i0.ViewContainerRef }, { type: i0.Injector }, { type: i1.NgControl, decorators: [{
type: Self
}, {
type: Optional
}] }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: DatalistIdService }]; }, propDecorators: { triggerFocus: [{
type: HostListener,
args: ['focus']
}], triggerValidation: [{
type: HostListener,
args: ['blur']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatalistModule {
constructor() {
ClarityIcons.addIcons(exclamationCircleIcon, checkCircleIcon);
}
}
ClrDatalistModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatalistModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrDatalistModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrDatalistModule, declarations: [ClrDatalist, ClrDatalistInput, ClrDatalistContainer], imports: [CommonModule, ClrInputModule, ClrIconModule], exports: [ClrDatalist, ClrDatalistInput, ClrDatalistContainer] });
ClrDatalistModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatalistModule, imports: [CommonModule, ClrInputModule, ClrIconModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatalistModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, ClrInputModule, ClrIconModule],
declarations: [ClrDatalist, ClrDatalistInput, ClrDatalistContainer],
exports: [ClrDatalist, ClrDatalistInput, ClrDatalistContainer],
}]
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This is just a copy of CdkTrapFocus so it can be used independent of the rest of the A11yModule.
*/
class CdkTrapFocusModule_CdkTrapFocus extends CdkTrapFocus {
}
CdkTrapFocusModule_CdkTrapFocus.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkTrapFocusModule_CdkTrapFocus, deps: null, target: i0.ɵɵFactoryTarget.Directive });
CdkTrapFocusModule_CdkTrapFocus.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: CdkTrapFocusModule_CdkTrapFocus, selector: "[cdkTrapFocus]", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkTrapFocusModule_CdkTrapFocus, decorators: [{
type: Directive,
args: [{
selector: '[cdkTrapFocus]',
}]
}] });
/**
* This module allows us to avoid importing all of A11yModule which results in a smaller application bundle.
*/
class CdkTrapFocusModule {
}
CdkTrapFocusModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkTrapFocusModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
CdkTrapFocusModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: CdkTrapFocusModule, declarations: [CdkTrapFocusModule_CdkTrapFocus], exports: [CdkTrapFocusModule_CdkTrapFocus] });
CdkTrapFocusModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkTrapFocusModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkTrapFocusModule, decorators: [{
type: NgModule,
args: [{
declarations: [CdkTrapFocusModule_CdkTrapFocus],
exports: [CdkTrapFocusModule_CdkTrapFocus],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This is the en-001 short locale date format. Setting as default.
*/
const DEFAULT_LOCALE_FORMAT = 'dd/MM/y';
// https://en.wikipedia.org/wiki/Date_format_by_country
const LITTLE_ENDIAN_REGEX = /d+.+m+.+y+/i;
const MIDDLE_ENDIAN_REGEX = /m+.+d+.+y+/i;
// No need for BIG_ENDIAN_REGEX because anything that doesn't satisfy the above 2
// is automatically BIG_ENDIAN
const DELIMITER_REGEX = /d+|m+|y+/i;
const USER_INPUT_REGEX = /\d+/g;
const MOBILE_USERAGENT_REGEX = /Mobi/i;
const RTL_REGEX = /\u200f/g;
const YEAR = 'YYYY';
const MONTH = 'MM';
const DATE = 'DD';
const LITTLE_ENDIAN = {
name: 'LITTLE_ENDIAN',
format: [DATE, MONTH, YEAR],
};
const MIDDLE_ENDIAN = {
name: 'MIDDLE_ENDIAN',
format: [MONTH, DATE, YEAR],
};
const BIG_ENDIAN = {
name: 'BIG_ENDIAN',
format: [YEAR, MONTH, DATE],
};
const NO_OF_DAYS_IN_A_WEEK = 7;
const NO_OF_ROWS_IN_CALENDAR_VIEW = 6;
const TOTAL_DAYS_IN_DAYS_VIEW = NO_OF_DAYS_IN_A_WEEK * NO_OF_ROWS_IN_CALENDAR_VIEW;
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Returns the number of days in a month.
*/
function getNumberOfDaysInTheMonth(year, month) {
// If we go to the next month, but use a day of 0, it returns the last day from the previous month
return new Date(year, month + 1, 0).getDate();
}
/**
* Returns the day for the corresponding date where 0 represents Sunday.
*/
function getDay(year, month, date) {
return new Date(year, month, date).getDay();
}
/**
* Takes in a year and if it is a 2 digit year, returns the corresponding 4 digit year.
* Window of 80 years before and 20 years after the present year.
* Credit: https://github.com/globalizejs/globalize/blob/e1b31cd6a4f1cff75b185b68b7a32220aac5196f/src/date/parse.js
*/
function parseToFourDigitYear(year) {
if (year > 9999 || (year > 100 && year < 999) || year < 10) {
return -1;
}
if (year > 999) {
return year;
}
const currYear = new Date().getFullYear();
const century = Math.floor(currYear / 100) * 100;
let result = year + century;
if (result > currYear + 20) {
result = result - 100;
}
return result;
}
function datesAreEqual(date1, date2) {
if (date1 instanceof Date && date2 instanceof Date) {
return (date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate());
}
else {
return false;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DayViewModel {
constructor(dayModel, isTodaysDate = false, isExcluded = false, isDisabled = false, isSelected = false, isFocusable = false) {
this.dayModel = dayModel;
this.isTodaysDate = isTodaysDate;
this.isExcluded = isExcluded;
this.isDisabled = isDisabled;
this.isSelected = isSelected;
this.isFocusable = isFocusable;
}
/**
* Gets the tab index based on the isFocusable flag.
*/
get tabIndex() {
return this.isFocusable ? 0 : -1;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DayModel {
constructor(year, month, date) {
this.year = year;
this.month = month;
this.date = date;
}
/**
* Checks if the passed CalendarDate is equal to itself.
*/
isEqual(day) {
if (day) {
return this.year === day.year && this.month === day.month && this.date === day.date;
}
return false;
}
toDate() {
return new Date(this.year, this.month, this.date);
}
/**
* Returns a new DayModel which is incremented based on the value passed.
*/
incrementBy(value) {
// Creating new Javascript Date object to increment because
// it will automatically take care of switching to next or previous
// months & years without we having to worry about it.
const date = new Date(this.year, this.month, this.date + value);
return new DayModel(date.getFullYear(), date.getMonth(), date.getDate());
}
/**
* Clones the current day model.
*/
clone() {
return new DayModel(this.year, this.month, this.date);
}
toComparisonString() {
return `${this.year}${this.pad(this.month)}${this.pad(this.date)}`;
}
toDateString() {
return this.toDate().toLocaleDateString();
}
pad(num) {
return num < 10 ? `0${num}` : `${num}`;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class CalendarViewModel {
constructor(calendar, selectedDay, focusableDay, today, firstDayOfWeek, excludedDates) {
this.calendar = calendar;
this.selectedDay = selectedDay;
this.focusableDay = focusableDay;
this.today = today;
this.firstDayOfWeek = firstDayOfWeek;
this.excludedDates = excludedDates;
this.currMonthDayViews = [];
this.initializeCalendarView();
}
/**
* DayViewModel matrix. Size 6x7
*/
get calendarView() {
return this._calendarView;
}
/**
* Updates the focusable day in the calendar.
*/
updateFocusableDay(day) {
this.setFocusableFlag(this.focusableDay, false);
this.setFocusableFlag(day, true);
this.focusableDay = day;
}
/**
* Generates a 6x7 matrix of DayViewModel based on the Calendar.
* The 6x7 matrix is structured according to the first day of the week.
* 6 rows to accommodate months which might have dates spanning over 6 weeks.
* 7 columns because there are 7 days in a week :P :D
*/
initializeCalendarView() {
// Generate prev and next month calendar models.
const prevMonthCalendar = this.calendar.previousMonth();
const nextMonthCalendar = this.calendar.nextMonth();
// Get no of days from prev and next months.
const daysFromPrevMonthInCalView = this.numDaysFromPrevMonthInCalView(this.calendar.year, this.calendar.month);
const daysFromNextMonthInCalView = TOTAL_DAYS_IN_DAYS_VIEW - (this.calendar.days.length + daysFromPrevMonthInCalView);
// Generate prev, curr and next day view models
let prevMonthDayViews = [];
let nextMonthDayViews = [];
if (daysFromPrevMonthInCalView > 0) {
prevMonthDayViews = this.generateDayViewModels(prevMonthCalendar.days.slice(-1 * daysFromPrevMonthInCalView), true, false);
}
this.currMonthDayViews = this.generateDayViewModels(this.calendar.days, false, true);
if (daysFromNextMonthInCalView > 0) {
nextMonthDayViews = this.generateDayViewModels(nextMonthCalendar.days.slice(0, daysFromNextMonthInCalView), true, false);
}
// Generate calendar view and initialize flags
this._calendarView = this.generateCalendarView(prevMonthDayViews, this.currMonthDayViews, nextMonthDayViews);
this.initializeSelectedDay();
this.initializeFocusableDay();
}
isDateExcluded(date) {
const { minDate, maxDate } = this.excludedDates;
const from = minDate.toComparisonString();
const to = maxDate.toComparisonString();
const today = date.toComparisonString();
return !(today >= from && today <= to);
}
/**
* Generates a DayViewModel array based on the DayModel passed
*/
generateDayViewModels(days, isExcluded, isCurrentCalendar) {
const dayViews = days.map(day => {
return new DayViewModel(day, false, isExcluded, this.isDateExcluded(day), false, false);
});
if (isCurrentCalendar && this.calendar.isDayInCalendar(this.today)) {
dayViews[this.today.date - 1].isTodaysDate = true;
}
return dayViews;
}
/**
* Gets the first day of the current month to figure out how many dates of previous month
* are needed to complete the Calendar View based on the first day of the week.
* eg: Assuming locale en-US, the first day of the week is Sunday,
* if first day of the current month lands on Wednesday, then
* (this.getDay function would return 3 since
* first day of the week is 0), we need the 3 days from the previous month.
*/
numDaysFromPrevMonthInCalView(currentYear, currentMonth) {
const firstDayOfCurrMonth = getDay(currentYear, currentMonth, 1);
if (firstDayOfCurrMonth >= this.firstDayOfWeek) {
return firstDayOfCurrMonth - this.firstDayOfWeek;
}
else {
return NO_OF_DAYS_IN_A_WEEK + firstDayOfCurrMonth - this.firstDayOfWeek;
}
}
/**
* Checks if the Day passed is in the CalendarView.
*/
isDayInCalendarView(day) {
if (!this.calendar.isDayInCalendar(day)) {
return false;
}
return true;
}
/**
* Using the DayViewModels from the previous, current and next month, this function
* generates the CalendarView.
*/
generateCalendarView(prev, curr, next) {
const combinationArr = [...prev, ...curr, ...next];
const calendarView = [];
for (let i = 0; i < NO_OF_ROWS_IN_CALENDAR_VIEW; i++) {
calendarView[i] = combinationArr.slice(i * NO_OF_DAYS_IN_A_WEEK, (i + 1) * NO_OF_DAYS_IN_A_WEEK);
}
return calendarView;
}
/**
* Initialize the selected day if the day is in the calendar.
*/
initializeSelectedDay() {
if (this.selectedDay && this.isDayInCalendarView(this.selectedDay)) {
this.currMonthDayViews[this.selectedDay.date - 1].isSelected = true;
}
}
/**
* Initializes the focusable day if the day is in the calendar. If focusable day is not set, then
* we check for the selected day. If selected day is not set then check if today is in the current
* calendar. If not then just set the 15th of the current calendar month.
*/
initializeFocusableDay() {
if (this.focusableDay && this.isDayInCalendarView(this.focusableDay)) {
this.setFocusableFlag(this.focusableDay, true);
}
else if (this.selectedDay && this.isDayInCalendarView(this.selectedDay)) {
this.setFocusableFlag(this.selectedDay, true);
this.focusableDay = this.selectedDay.clone();
}
else if (this.isDayInCalendarView(this.today)) {
this.setFocusableFlag(this.today, true);
this.focusableDay = this.today.clone();
}
else {
this.focusableDay = new DayModel(this.calendar.year, this.calendar.month, 15);
this.setFocusableFlag(this.focusableDay, true);
}
}
setFocusableFlag(day, flag) {
if (day) {
this.currMonthDayViews[day.date - 1].isFocusable = flag;
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This service extracts the Angular CLDR data needed by the datepicker.
*/
class LocaleHelperService {
constructor(locale) {
this.locale = locale;
this._firstDayOfWeek = 0;
this.initializeLocaleData();
}
get firstDayOfWeek() {
return this._firstDayOfWeek;
}
get localeDays() {
return this._localeDays;
}
// leave for backward compatibility
get localeDaysNarrow() {
return this._localeDays.map(day => day.narrow);
}
get localeMonthsAbbreviated() {
return this._localeMonthsAbbreviated;
}
get localeMonthsWide() {
return this._localeMonthsWide;
}
get localeDateFormat() {
return this._localeDateFormat;
}
/**
* Initializes the locale data.
*/
initializeLocaleData() {
// Order in which these functions is called is very important.
this.initializeFirstDayOfWeek();
this.initializeLocaleDateFormat();
this.initializeLocaleMonthsAbbreviated();
this.initializeLocaleMonthsWide();
this.initializeLocaleDays();
}
/**
* Initialize day names based on the locale.
* eg: [{day: Sunday, narrow: S}, {day: Monday, narrow: M}...] for en-US.
*/
initializeLocaleDays() {
// Get locale day names starting with Sunday
const tempArr = [];
const tempWideArr = getLocaleDayNames(this.locale, FormStyle.Standalone, TranslationWidth.Wide).slice();
const tempNarrowArr = getLocaleDayNames(this.locale, FormStyle.Standalone, TranslationWidth.Narrow).slice();
// Get first day of the week based on the locale
const firstDayOfWeek = this.firstDayOfWeek;
for (let i = 0; i < 7; i++) {
tempArr.push({ day: tempWideArr[i], narrow: tempNarrowArr[i] });
}
// Rearrange the tempArr to start with the first day of the week based on the locale.
if (firstDayOfWeek > 0) {
const prevDays = tempArr.splice(0, firstDayOfWeek);
tempArr.push(...prevDays);
}
this._localeDays = tempArr;
}
/**
* Initializes the array of month names in the TranslationWidth.Abbreviated format.
* e.g. `[Jan, Feb, ...]` for en-US
*/
initializeLocaleMonthsAbbreviated() {
this._localeMonthsAbbreviated = getLocaleMonthNames(this.locale, FormStyle.Standalone, TranslationWidth.Abbreviated).slice();
}
/**
* Initializes the array of month names in the TranslationWidth.Wide format.
* e.g. `[January, February, ...]` for en-US
*/
initializeLocaleMonthsWide() {
this._localeMonthsWide = getLocaleMonthNames(this.locale, FormStyle.Standalone, TranslationWidth.Wide).slice();
}
/**
* Initializes the first day of the week based on the locale.
*/
initializeFirstDayOfWeek() {
this._firstDayOfWeek = getLocaleFirstDayOfWeek(this.locale);
}
initializeLocaleDateFormat() {
this._localeDateFormat = getLocaleDateFormat(this.locale, FormatWidth.Short);
}
}
LocaleHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: LocaleHelperService, deps: [{ token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Injectable });
LocaleHelperService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: LocaleHelperService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: LocaleHelperService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [LOCALE_ID]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class CalendarModel {
constructor(year, month) {
this.year = year;
this.month = month;
this.initializeDaysInCalendar();
}
/**
* Checks if the calendar passed is equal to the current calendar.
*/
isEqual(calendar) {
if (calendar) {
return this.year === calendar.year && this.month === calendar.month;
}
return false;
}
/**
* Checks if a DayModel is in the Calendar
*/
isDayInCalendar(day) {
if (day) {
return this.year === day.year && this.month === day.month;
}
return false;
}
/**
* Returns CalendarModel of the previous month.
*/
previousMonth() {
if (this.month === 0) {
return new CalendarModel(this.year - 1, 11);
}
else {
return new CalendarModel(this.year, this.month - 1);
}
}
/**
* Returns CalendarModel of the next month.
*/
nextMonth() {
if (this.month === 11) {
return new CalendarModel(this.year + 1, 0);
}
else {
return new CalendarModel(this.year, this.month + 1);
}
}
/**
* Populates the days array with the DayModels in the current Calendar.
*/
initializeDaysInCalendar() {
const noOfDaysInCalendar = getNumberOfDaysInTheMonth(this.year, this.month);
this.days = Array(noOfDaysInCalendar)
.fill(null)
.map((_date, index) => {
return new DayModel(this.year, this.month, index + 1);
});
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This service is responsible for:
* 1. Initializing the displayed calendar.
* 2. Moving the calendar to the next, previous or current months
* 3. Managing the focused and selected day models.
*/
class DateNavigationService {
constructor() {
this._todaysFullDate = new Date();
this._selectedDayChange = new Subject();
this._displayedCalendarChange = new Subject();
this._focusOnCalendarChange = new Subject();
this._focusedDayChange = new Subject();
}
get today() {
return this._today;
}
get displayedCalendar() {
return this._displayedCalendar;
}
get selectedDayChange() {
return this._selectedDayChange.asObservable();
}
/**
* This observable lets the subscriber know that the displayed calendar has changed.
*/
get displayedCalendarChange() {
return this._displayedCalendarChange.asObservable();
}
/**
* This observable lets the subscriber know that the focus should be applied on the calendar.
*/
get focusOnCalendarChange() {
return this._focusOnCalendarChange.asObservable();
}
/**
* This observable lets the subscriber know that the focused day in the displayed calendar has changed.
*/
get focusedDayChange() {
return this._focusedDayChange.asObservable();
}
/**
* Notifies that the selected day has changed so that the date can be emitted to the user.
* Note: Only to be called from day.ts
*/
notifySelectedDayChanged(dayModel) {
this.selectedDay = dayModel;
this._selectedDayChange.next(dayModel);
}
/**
* Initializes the calendar based on the selected day.
*/
initializeCalendar() {
this.focusedDay = null; // Can be removed later on the store focus
this.initializeTodaysDate();
if (this.selectedDay) {
this._displayedCalendar = new CalendarModel(this.selectedDay.year, this.selectedDay.month);
}
else {
this._displayedCalendar = new CalendarModel(this.today.year, this.today.month);
}
}
changeMonth(month) {
this.setDisplayedCalendar(new CalendarModel(this._displayedCalendar.year, month));
}
changeYear(year) {
this.setDisplayedCalendar(new CalendarModel(year, this._displayedCalendar.month));
}
/**
* Moves the displayed calendar to the next month.
*/
moveToNextMonth() {
this.setDisplayedCalendar(this._displayedCalendar.nextMonth());
}
/**
* Moves the displayed calendar to the previous month.
*/
moveToPreviousMonth() {
this.setDisplayedCalendar(this._displayedCalendar.previousMonth());
}
/**
* Moves the displayed calendar to the current month and year.
*/
moveToCurrentMonth() {
if (!this.displayedCalendar.isDayInCalendar(this.today)) {
this.setDisplayedCalendar(new CalendarModel(this.today.year, this.today.month));
}
this._focusOnCalendarChange.next();
}
incrementFocusDay(value) {
this.focusedDay = this.focusedDay.incrementBy(value);
if (this._displayedCalendar.isDayInCalendar(this.focusedDay)) {
this._focusedDayChange.next(this.focusedDay);
}
else {
this.setDisplayedCalendar(new CalendarModel(this.focusedDay.year, this.focusedDay.month));
}
this._focusOnCalendarChange.next();
}
// not a setter because i want this to remain private
setDisplayedCalendar(value) {
if (!this._displayedCalendar.isEqual(value)) {
this._displayedCalendar = value;
this._displayedCalendarChange.next();
}
}
initializeTodaysDate() {
this._todaysFullDate = new Date();
this._today = new DayModel(this._todaysFullDate.getFullYear(), this._todaysFullDate.getMonth(), this._todaysFullDate.getDate());
}
}
DateNavigationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
DateNavigationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateNavigationService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateNavigationService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This service focuses the day that is focusable in the calendar.
*/
class DatepickerFocusService {
constructor(_ngZone, platformId) {
this._ngZone = _ngZone;
this.platformId = platformId;
}
focusCell(elRef) {
this._ngZone.runOutsideAngular(() => {
this.ngZoneIsStableInBrowser().subscribe(() => {
const focusEl = elRef.nativeElement.querySelector('[tabindex="0"]');
if (focusEl) {
focusEl.focus();
}
});
});
}
focusInput(element) {
this._ngZone.runOutsideAngular(() => this.ngZoneIsStableInBrowser().subscribe(() => element.focus()));
}
elementIsFocused(element) {
return isPlatformBrowser(this.platformId) && document.activeElement === element;
}
ngZoneIsStableInBrowser() {
// Credit: Material: https://github.com/angular/material2/blob/master/src/lib/datepicker/calendar.ts
return this._ngZone.onStable.asObservable().pipe(first(), filter(() => isPlatformBrowser(this.platformId)));
}
}
DatepickerFocusService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatepickerFocusService, deps: [{ token: i0.NgZone }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
DatepickerFocusService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatepickerFocusService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatepickerFocusService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DateIOService {
constructor(_localeHelperService) {
this._localeHelperService = _localeHelperService;
this.disabledDates = {
// This is the default range. It approximates the beginning of time to the end of time.
// Unless a minDate or maxDate is set with the native HTML5 api the range is all dates
// TODO: turn this into an Array of min/max ranges that allow configuration of multiple ranges.
minDate: new DayModel(0, 0, 1),
maxDate: new DayModel(9999, 11, 31),
};
this.cldrLocaleDateFormat = DEFAULT_LOCALE_FORMAT;
this.localeDisplayFormat = LITTLE_ENDIAN;
this.delimiters = ['/', '/'];
this.cldrLocaleDateFormat = this._localeHelperService.localeDateFormat;
this.initializeLocaleDisplayFormat();
}
get placeholderText() {
const format = this.localeDisplayFormat.format;
return format[0] + this.delimiters[0] + format[1] + this.delimiters[1] + format[2];
}
setMinDate(date) {
// NOTE: I'm expecting consumers to pass one of four things here:
// A proper date string(2019-11-11), null, undefined or empty string ('')
if (!date) {
// attribute binding was removed, reset back to the beginning of time
this.disabledDates.minDate = new DayModel(0, 0, 1);
}
else {
const [year, month, day] = date.split('-').map(n => parseInt(n, 10));
this.disabledDates.minDate = new DayModel(year, month - 1, day);
}
}
setMaxDate(date) {
// NOTE: I'm expecting consumers to pass one of four things here:
// A proper date string(2019-11-11), null, undefined or empty string ('')
if (!date) {
// attribute binding was removed, reset forward to the end of time
this.disabledDates.maxDate = new DayModel(9999, 11, 31);
}
else {
const [year, month, day] = date.split('-').map(n => parseInt(n, 10));
this.disabledDates.maxDate = new DayModel(year, month - 1, day);
}
}
toLocaleDisplayFormatString(date) {
if (date) {
if (isNaN(date.getTime())) {
return '';
}
const dateNo = date.getDate();
const monthNo = date.getMonth() + 1;
const dateStr = dateNo > 9 ? dateNo.toString() : '0' + dateNo;
const monthStr = monthNo > 9 ? monthNo.toString() : '0' + monthNo;
if (this.localeDisplayFormat === LITTLE_ENDIAN) {
return dateStr + this.delimiters[0] + monthStr + this.delimiters[1] + date.getFullYear();
}
else if (this.localeDisplayFormat === MIDDLE_ENDIAN) {
return monthStr + this.delimiters[0] + dateStr + this.delimiters[1] + date.getFullYear();
}
else {
return date.getFullYear() + this.delimiters[0] + monthStr + this.delimiters[1] + dateStr;
}
}
return '';
}
getDateValueFromDateString(date) {
if (!date || typeof date !== 'string') {
return null;
}
const dateParts = date.match(USER_INPUT_REGEX);
if (!dateParts || dateParts.length !== 3) {
return null;
}
const [firstPart, secondPart, thirdPart] = dateParts;
if (this.localeDisplayFormat === LITTLE_ENDIAN) {
// secondPart is month && firstPart is date
return this.validateAndGetDate(thirdPart, secondPart, firstPart);
}
else if (this.localeDisplayFormat === MIDDLE_ENDIAN) {
// firstPart is month && secondPart is date
return this.validateAndGetDate(thirdPart, firstPart, secondPart);
}
else {
// secondPart is month && thirdPart is date
return this.validateAndGetDate(firstPart, secondPart, thirdPart);
}
}
initializeLocaleDisplayFormat() {
const format = this.cldrLocaleDateFormat.toLocaleLowerCase();
if (LITTLE_ENDIAN_REGEX.test(format)) {
this.localeDisplayFormat = LITTLE_ENDIAN;
}
else if (MIDDLE_ENDIAN_REGEX.test(format)) {
this.localeDisplayFormat = MIDDLE_ENDIAN;
}
else {
// everything else is set to BIG-ENDIAN FORMAT
this.localeDisplayFormat = BIG_ENDIAN;
}
this.extractDelimiters();
}
extractDelimiters() {
if (this.cldrLocaleDateFormat) {
// Sanitize Date Format. Remove RTL characters.
// FIXME: When we support RTL, remove this and handle it correctly.
const localeFormat = this.cldrLocaleDateFormat.replace(RTL_REGEX, '');
const delimiters = localeFormat.split(DELIMITER_REGEX);
// NOTE: The split from the CLDR date format should always result
// in an arary with 4 elements. The 1st and the 2nd values are the delimiters
// we will use in order.
// Eg: "dd/MM/y".split(/d+|m+|y+/i) results in ["", "/", "/", ""]
if (delimiters && delimiters.length === 4) {
this.delimiters = [delimiters[1], delimiters[2]];
}
else {
console.error('Unexpected date format received. Delimiters extracted: ', delimiters);
}
}
}
/**
* Checks if the month entered by the user is valid or not.
* Note: Month is 0 based.
*/
isValidMonth(month) {
return month > -1 && month < 12;
}
/**
* Checks if the date is valid depending on the year and month provided.
*/
isValidDate(year, month, date) {
return date > 0 && date <= getNumberOfDaysInTheMonth(year, month);
}
/**
* Validates the parameters provided and returns the date.
* If the parameters are not
* valid then return null.
* NOTE: (Month here is 1 based since the user has provided that as an input)
*/
validateAndGetDate(year, month, date) {
// I don't know whats wrong with the TS compiler. It throws an error if I write
// the below if statement. The error is:
// Operator '!==' cannot be applied to types '2' and '4'
// More info here: https://github.com/Microsoft/TypeScript/issues/12794#issuecomment-270342936
/*
if (year.length !== 2 || year.length !== 4) {
return null;
}
*/
// Instead I have to write the logic like this x-(
const y = +year;
const m = +month - 1; // month is 0 based
const d = +date;
if (!this.isValidMonth(m) || !this.isValidDate(y, m, d)) {
return null;
}
const result = parseToFourDigitYear(y);
return result !== -1 ? new Date(result, m, d) : null;
}
}
DateIOService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateIOService, deps: [{ token: LocaleHelperService }], target: i0.ɵɵFactoryTarget.Injectable });
DateIOService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateIOService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateIOService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: LocaleHelperService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DateFormControlService {
constructor() {
this._touchedChange = new Subject();
this._dirtyChange = new Subject();
}
get touchedChange() {
return this._touchedChange.asObservable();
}
get dirtyChange() {
return this._dirtyChange.asObservable();
}
markAsTouched() {
this._touchedChange.next();
}
markAsDirty() {
this._dirtyChange.next();
}
// friendly wrapper
setDisabled(state) {
this.disabled = state;
}
}
DateFormControlService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateFormControlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
DateFormControlService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateFormControlService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DateFormControlService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDay {
constructor(_dateNavigationService, _toggleService, dateFormControlService, commonStrings) {
this._dateNavigationService = _dateNavigationService;
this._toggleService = _toggleService;
this.dateFormControlService = dateFormControlService;
this.commonStrings = commonStrings;
}
/**
* DayViewModel input which is used to build the Day View.
*/
get dayView() {
return this._dayView;
}
set dayView(day) {
this._dayView = day;
}
get dayString() {
return this.dayView.isSelected
? this.commonStrings.parse(this.commonStrings.keys.datepickerSelectedLabel, {
FULL_DATE: this._dayView.dayModel.toDateString(),
})
: this._dayView.dayModel.toDateString();
}
/**
* Updates the focusedDay in the DateNavigationService when the ClrDay is focused.
*/
onDayViewFocus() {
this._dateNavigationService.focusedDay = this.dayView.dayModel;
}
/**
* Updates the selectedDay when the ClrDay is selected and closes the datepicker popover.
*/
selectDay() {
const day = this.dayView.dayModel;
this._dateNavigationService.notifySelectedDayChanged(day);
this.dateFormControlService.markAsDirty();
this._toggleService.open = false;
}
}
ClrDay.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDay, deps: [{ token: DateNavigationService }, { token: ClrPopoverToggleService }, { token: DateFormControlService }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrDay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDay, selector: "clr-day", inputs: { dayView: ["clrDayView", "dayView"] }, host: { properties: { "class.day": "true" } }, ngImport: i0, template: `
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDay, decorators: [{
type: Component,
args: [{
selector: 'clr-day',
template: `
`,
host: { '[class.day]': 'true' },
}]
}], ctorParameters: function () { return [{ type: DateNavigationService }, { type: ClrPopoverToggleService }, { type: DateFormControlService }, { type: ClrCommonStringsService }]; }, propDecorators: { dayView: [{
type: Input,
args: ['clrDayView']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrCalendar {
constructor(_localeHelperService, _dateNavigationService, _datepickerFocusService, _dateIOService, _elRef) {
this._localeHelperService = _localeHelperService;
this._dateNavigationService = _dateNavigationService;
this._datepickerFocusService = _datepickerFocusService;
this._dateIOService = _dateIOService;
this._elRef = _elRef;
this._subs = [];
this.generateCalendarView();
this.initializeSubscriptions();
}
/**
* Gets the locale days according to the TranslationWidth.Narrow format.
*/
get localeDays() {
return this._localeHelperService.localeDays;
}
get calendar() {
return this._dateNavigationService.displayedCalendar;
}
get selectedDay() {
return this._dateNavigationService.selectedDay;
}
get focusedDay() {
return this._dateNavigationService.focusedDay;
}
get today() {
return this._dateNavigationService.today;
}
/**
* Focuses on the focusable day when the Calendar View is initialized.
*/
ngAfterViewInit() {
this._datepickerFocusService.focusCell(this._elRef);
}
/**
* Unsubscribe from subscriptions.
*/
ngOnDestroy() {
this._subs.forEach((sub) => sub.unsubscribe());
}
/**
* Delegates Keyboard arrow navigation to the DateNavigationService.
*/
onKeyDown(event) {
if (event && this.focusedDay) {
switch (normalizeKey(event.key)) {
case Keys.ArrowUp:
event.preventDefault();
this._dateNavigationService.incrementFocusDay(-1 * NO_OF_DAYS_IN_A_WEEK);
break;
case Keys.ArrowDown:
event.preventDefault();
this._dateNavigationService.incrementFocusDay(NO_OF_DAYS_IN_A_WEEK);
break;
case Keys.ArrowLeft:
event.preventDefault();
this._dateNavigationService.incrementFocusDay(-1);
break;
case Keys.ArrowRight:
event.preventDefault();
this._dateNavigationService.incrementFocusDay(1);
break;
default:
break; // No default case. ESLint x-(
}
}
}
/**
* Initialize subscriptions to:
* 1. update the calendar view model.
* 2. update the focusable day in the calendar view model.
* 3. focus on the focusable day in the calendar.
*/
initializeSubscriptions() {
this._subs.push(this._dateNavigationService.displayedCalendarChange.subscribe(() => {
this.generateCalendarView();
}));
this._subs.push(this._dateNavigationService.focusedDayChange.subscribe((focusedDay) => {
this.calendarViewModel.updateFocusableDay(focusedDay);
}));
this._subs.push(this._dateNavigationService.focusOnCalendarChange.subscribe(() => {
this._datepickerFocusService.focusCell(this._elRef);
}));
}
/**
* Generates the Calendar View based on the calendar retrieved from the DateNavigationService.
*/
generateCalendarView() {
this.calendarViewModel = new CalendarViewModel(this.calendar, this.selectedDay, this.focusedDay, this.today, this._localeHelperService.firstDayOfWeek, this._dateIOService.disabledDates);
}
}
ClrCalendar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrCalendar, deps: [{ token: LocaleHelperService }, { token: DateNavigationService }, { token: DatepickerFocusService }, { token: DateIOService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
ClrCalendar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrCalendar, selector: "clr-calendar", host: { listeners: { "keydown": "onKeyDown($event)" } }, ngImport: i0, template: "
\n" }]
}], ctorParameters: function () { return [{ type: LocaleHelperService }, { type: DateNavigationService }, { type: DatepickerFocusService }, { type: DateIOService }, { type: i0.ElementRef }]; }, propDecorators: { onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* TODO:
* Using core functions like:
* - pluckPixelValue
* - getCssPropertyValue
*
* to get the value of the design token.
*
* Note: Memoization/Cache usage possible.
*/
// iPad mini screen width
// http://stephen.io/mediaqueries/#iPadMini
const DATEPICKER_ENABLE_BREAKPOINT = 768;
const SMALL_BREAKPOINT = 576;
const MEDIUM_BREAKPOINT = 768;
const LARGE_BREAKPOINT = 992;
const EXTRA_LARGE_BREAKPOINT = 1200;
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatepickerEnabledService {
constructor(_document) {
this._document = _document;
this._isUserAgentMobile = false;
if (this._document) {
this._isUserAgentMobile = MOBILE_USERAGENT_REGEX.test(_document.defaultView.navigator.userAgent);
this._innerWidth = _document.defaultView.innerWidth;
}
}
/**
* Returns if the calendar should be active or not.
* If the user agent is mobile and the screen width is less than DATEPICKER_ACTIVE_BREAKPOINT
* then the calendar is inactive.
*/
get isEnabled() {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
// What they recommend is:
//"In summary, we recommend looking for the string 'Mobi'
// anywhere in the User Agent to detect a mobile device."
if (this._document) {
if (this._innerWidth < DATEPICKER_ENABLE_BREAKPOINT && this._isUserAgentMobile) {
return false;
}
}
return true;
}
}
DatepickerEnabledService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatepickerEnabledService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
DatepickerEnabledService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatepickerEnabledService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatepickerEnabledService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This service manages which view is visible in the datepicker popover.
*/
class ViewManagerService {
constructor() {
this.position = ClrPopoverPositions['bottom-left'];
this._currentView = "DAYVIEW" /* DatepickerViewEnum.DAYVIEW */;
}
get isDayView() {
return this._currentView === "DAYVIEW" /* DatepickerViewEnum.DAYVIEW */;
}
get isYearView() {
return this._currentView === "YEARVIEW" /* DatepickerViewEnum.YEARVIEW */;
}
get isMonthView() {
return this._currentView === "MONTHVIEW" /* DatepickerViewEnum.MONTHVIEW */;
}
changeToMonthView() {
this._currentView = "MONTHVIEW" /* DatepickerViewEnum.MONTHVIEW */;
}
changeToYearView() {
this._currentView = "YEARVIEW" /* DatepickerViewEnum.YEARVIEW */;
}
changeToDayView() {
this._currentView = "DAYVIEW" /* DatepickerViewEnum.DAYVIEW */;
}
}
ViewManagerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ViewManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ViewManagerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ViewManagerService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ViewManagerService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrMonthpicker {
constructor(_viewManagerService, _localeHelperService, _dateNavigationService, _datepickerFocusService, _elRef) {
this._viewManagerService = _viewManagerService;
this._localeHelperService = _localeHelperService;
this._dateNavigationService = _dateNavigationService;
this._datepickerFocusService = _datepickerFocusService;
this._elRef = _elRef;
this._focusedMonthIndex = this.calendarMonthIndex;
}
/**
* Gets the months array which is used to rendered the monthpicker view.
* Months are in the TranslationWidth.Wide format.
*/
get monthNames() {
return this._localeHelperService.localeMonthsWide;
}
/**
* Gets the month value of the Calendar.
*/
get calendarMonthIndex() {
return this._dateNavigationService.displayedCalendar.month;
}
/**
* Focuses on the current calendar month when the View is initialized.
*/
ngAfterViewInit() {
this._datepickerFocusService.focusCell(this._elRef);
}
/**
* Handles the Keyboard arrow navigation for the monthpicker.
*/
onKeyDown(event) {
// NOTE: Didn't move this to the date navigation service because
// the logic is fairly simple and it didn't make sense for me
// to create extra observables just to move this logic to the service.
if (event) {
const key = normalizeKey(event.key);
if (key === Keys.ArrowUp && this._focusedMonthIndex > 0) {
event.preventDefault();
this._focusedMonthIndex--;
this._datepickerFocusService.focusCell(this._elRef);
}
else if (key === Keys.ArrowDown && this._focusedMonthIndex < 11) {
event.preventDefault();
this._focusedMonthIndex++;
this._datepickerFocusService.focusCell(this._elRef);
}
else if (key === Keys.ArrowRight && this._focusedMonthIndex < 6) {
event.preventDefault();
this._focusedMonthIndex = this._focusedMonthIndex + 6;
this._datepickerFocusService.focusCell(this._elRef);
}
else if (key === Keys.ArrowLeft && this._focusedMonthIndex > 5) {
event.preventDefault();
this._focusedMonthIndex = this._focusedMonthIndex - 6;
this._datepickerFocusService.focusCell(this._elRef);
}
}
}
/**
* Calls the DateNavigationService to update the month value of the calendar.
* Also changes the view to the daypicker.
*/
changeMonth(monthIndex) {
this._dateNavigationService.changeMonth(monthIndex);
this._viewManagerService.changeToDayView();
}
/**
* Compares the month passed to the focused month and returns the tab index.
*/
getTabIndex(monthIndex) {
return monthIndex === this._focusedMonthIndex ? 0 : -1;
}
}
ClrMonthpicker.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrMonthpicker, deps: [{ token: ViewManagerService }, { token: LocaleHelperService }, { token: DateNavigationService }, { token: DatepickerFocusService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
ClrMonthpicker.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrMonthpicker, selector: "clr-monthpicker", host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class.monthpicker": "true" } }, ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrMonthpicker, decorators: [{
type: Component,
args: [{
selector: 'clr-monthpicker',
template: `
`,
host: {
'[class.monthpicker]': 'true',
},
}]
}], ctorParameters: function () { return [{ type: ViewManagerService }, { type: LocaleHelperService }, { type: DateNavigationService }, { type: DatepickerFocusService }, { type: i0.ElementRef }]; }, propDecorators: { onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const YEARS_TO_DISPLAY = 10;
class YearRangeModel {
constructor(year) {
this.year = year;
this.yearRange = [];
this.generateYearRange();
}
/**
* Gets the number in the middle of the range.
*/
get middleYear() {
return this.yearRange[Math.floor(this.yearRange.length / 2)];
}
/**
* Generates the YearRangeModel for the next decade.
*/
nextDecade() {
return new YearRangeModel(this.year + 10);
}
/**
* Generates the YearRangeModel for the previous decade.
*/
previousDecade() {
return new YearRangeModel(this.year - 10);
}
/**
* Generates the YearRangeModel for the current decade.
*/
currentDecade() {
return new YearRangeModel(new Date().getFullYear());
}
/**
* Checks if the value is in the YearRangeModel.
*/
inRange(value) {
return this.yearRange.indexOf(value) > -1;
}
/**
* Generates the year range based on the year parameter.
* eg: If 2018 is passed the output will be [2010, 2011, ..., 2019]
*/
generateYearRange() {
const remainder = this.year % YEARS_TO_DISPLAY;
const floor = this.year - remainder;
const ceil = floor + YEARS_TO_DISPLAY;
this.yearRange = this.generateRange(floor, ceil);
}
/**
* Function which generate a range of numbers from floor to ceil.
*/
generateRange(floor, ceil) {
return Array.from({ length: ceil - floor }, (_v, k) => k + floor);
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrYearpicker {
constructor(_dateNavigationService, _viewManagerService, _datepickerFocusService, _elRef, commonStrings) {
this._dateNavigationService = _dateNavigationService;
this._viewManagerService = _viewManagerService;
this._datepickerFocusService = _datepickerFocusService;
this._elRef = _elRef;
this.commonStrings = commonStrings;
this.yearRangeModel = new YearRangeModel(this.calendarYear);
this._focusedYear = this.calendarYear;
}
/**
* Gets the year which the user is currently on.
*/
get calendarYear() {
return this._dateNavigationService.displayedCalendar.year;
}
/**
* Focuses on the current calendar year when the View is initialized.
*/
ngAfterViewInit() {
this._datepickerFocusService.focusCell(this._elRef);
}
/**
* Handles the Keyboard arrow navigation for the yearpicker.
*/
onKeyDown(event) {
// NOTE: Didn't move this to the date navigation service because
// the logic is fairly simple and it didn't make sense for me
// to create extra observables just to move this logic to the service.
if (event) {
const key = normalizeKey(event.key);
if (key === Keys.ArrowUp) {
event.preventDefault();
this.incrementFocusYearBy(-1);
}
else if (key === Keys.ArrowDown) {
event.preventDefault();
this.incrementFocusYearBy(1);
}
else if (key === Keys.ArrowRight) {
event.preventDefault();
this.incrementFocusYearBy(5);
}
else if (key === Keys.ArrowLeft) {
event.preventDefault();
this.incrementFocusYearBy(-5);
}
}
}
/**
* Calls the DateNavigationService to update the year value of the calendar.
* Also changes the view to the daypicker.
*/
changeYear(year) {
this._dateNavigationService.changeYear(year);
this._viewManagerService.changeToDayView();
}
/**
* Updates the YearRangeModel to the previous decade.
*/
previousDecade() {
this.yearRangeModel = this.yearRangeModel.previousDecade();
// Year in the yearpicker is not focused because while navigating to a different decade,
// you want the focus to remain on the decade switcher arrows.
}
/**
* Updates the YearRangeModel to the current decade.
*/
currentDecade() {
if (!this.yearRangeModel.inRange(this._dateNavigationService.today.year)) {
this.yearRangeModel = this.yearRangeModel.currentDecade();
}
this._datepickerFocusService.focusCell(this._elRef);
}
/**
* Updates the YearRangeModel to the next decade.
*/
nextDecade() {
this.yearRangeModel = this.yearRangeModel.nextDecade();
// Year in the yearpicker is not focused because while navigating to a different decade,
// you want the focus to remain on the decade switcher arrows.
}
/**
* Compares the year passed to the focused year and returns the tab index.
*/
getTabIndex(year) {
if (!this.yearRangeModel.inRange(this._focusedYear)) {
if (this.yearRangeModel.inRange(this.calendarYear)) {
this._focusedYear = this.calendarYear;
}
else {
this._focusedYear = this.yearRangeModel.middleYear;
}
}
return this._focusedYear === year ? 0 : -1;
}
/**
* Increments the focus year by the value passed. Updates the YearRangeModel if the
* new value is not in the current decade.
*/
incrementFocusYearBy(value) {
this._focusedYear = this._focusedYear + value;
if (!this.yearRangeModel.inRange(this._focusedYear)) {
if (value > 0) {
this.yearRangeModel = this.yearRangeModel.nextDecade();
}
else {
this.yearRangeModel = this.yearRangeModel.previousDecade();
}
}
this._datepickerFocusService.focusCell(this._elRef);
}
}
ClrYearpicker.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrYearpicker, deps: [{ token: DateNavigationService }, { token: ViewManagerService }, { token: DatepickerFocusService }, { token: i0.ElementRef }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrYearpicker.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrYearpicker, selector: "clr-yearpicker", host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class.yearpicker": "true" } }, ngImport: i0, template: `
`,
host: {
'[class.clr-form-control]': 'true',
'[class.clr-form-control-disabled]': 'control?.disabled',
'[class.clr-row]': 'addGrid()',
},
providers: [IfControlStateService, NgControlService, ControlIdService, ControlClassService],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrTextarea extends WrappedFormControl {
constructor(vcr, injector, control, renderer, el) {
super(vcr, ClrTextareaContainer, injector, control, renderer, el);
this.index = 1;
}
}
ClrTextarea.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTextarea, deps: [{ token: i0.ViewContainerRef }, { token: i0.Injector }, { token: i1.NgControl, optional: true, self: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrTextarea.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrTextarea, selector: "[clrTextarea]", host: { properties: { "class.clr-textarea": "true" } }, usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTextarea, decorators: [{
type: Directive,
args: [{
selector: '[clrTextarea]',
host: { '[class.clr-textarea]': 'true' },
}]
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.Injector }, { type: i1.NgControl, decorators: [{
type: Self
}, {
type: Optional
}] }, { type: i0.Renderer2 }, { type: i0.ElementRef }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrTextareaModule {
constructor() {
ClarityIcons.addIcons(exclamationCircleIcon, checkCircleIcon);
}
}
ClrTextareaModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTextareaModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrTextareaModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrTextareaModule, declarations: [ClrTextarea, ClrTextareaContainer], imports: [CommonModule, FormsModule, ClrIconModule, ClrCommonFormsModule], exports: [ClrCommonFormsModule, ClrTextarea, ClrTextareaContainer] });
ClrTextareaModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTextareaModule, imports: [CommonModule, FormsModule, ClrIconModule, ClrCommonFormsModule, ClrCommonFormsModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTextareaModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, FormsModule, ClrIconModule, ClrCommonFormsModule],
declarations: [ClrTextarea, ClrTextareaContainer],
exports: [ClrCommonFormsModule, ClrTextarea, ClrTextareaContainer],
}]
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrFormsModule {
}
ClrFormsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFormsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrFormsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrFormsModule, imports: [CommonModule], exports: [ClrCommonFormsModule,
ClrCheckboxModule,
ClrComboboxModule,
ClrDatepickerModule,
ClrInputModule,
ClrPasswordModule,
ClrRadioModule,
ClrSelectModule,
ClrTextareaModule,
ClrRangeModule,
ClrDatalistModule] });
ClrFormsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFormsModule, imports: [CommonModule, ClrCommonFormsModule,
ClrCheckboxModule,
ClrComboboxModule,
ClrDatepickerModule,
ClrInputModule,
ClrPasswordModule,
ClrRadioModule,
ClrSelectModule,
ClrTextareaModule,
ClrRangeModule,
ClrDatalistModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrFormsModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
exports: [
ClrCommonFormsModule,
ClrCheckboxModule,
ClrComboboxModule,
ClrDatepickerModule,
ClrInputModule,
ClrPasswordModule,
ClrRadioModule,
ClrSelectModule,
ClrTextareaModule,
ClrRangeModule,
ClrDatalistModule,
],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrExpandableAnimationModule {
}
ClrExpandableAnimationModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrExpandableAnimationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrExpandableAnimationModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrExpandableAnimationModule, declarations: [ClrExpandableAnimation], imports: [CommonModule], exports: [ClrExpandableAnimation] });
ClrExpandableAnimationModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrExpandableAnimationModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrExpandableAnimationModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [EXPANDABLE_ANIMATION_DIRECTIVES],
exports: [EXPANDABLE_ANIMATION_DIRECTIVES],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* This is just a copy of CdkDrag so it can be used independent of the rest of the CdkDragDropModule.
*/
class CdkDragModule_CdkDrag extends CdkDrag {
}
CdkDragModule_CdkDrag.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkDragModule_CdkDrag, deps: null, target: i0.ɵɵFactoryTarget.Directive });
CdkDragModule_CdkDrag.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: CdkDragModule_CdkDrag, selector: "[cdkDrag]", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkDragModule_CdkDrag, decorators: [{
type: Directive,
args: [{
selector: '[cdkDrag]',
}]
}] });
/**
* This module allows us to avoid importing all of CdkDragDropModule which results in a smaller application bundle.
*/
class CdkDragModule {
}
CdkDragModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkDragModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
CdkDragModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: CdkDragModule, declarations: [CdkDragModule_CdkDrag], exports: [CdkDragModule_CdkDrag] });
CdkDragModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkDragModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: CdkDragModule, decorators: [{
type: NgModule,
args: [{
declarations: [CdkDragModule_CdkDrag],
exports: [CdkDragModule_CdkDrag],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class OutsideClick {
constructor(host, renderer, ngZone) {
this.strict = false;
this.outsideClick = new EventEmitter(false);
ngZone.runOutsideAngular(() => {
this.documentClickListener = renderer.listen('document', 'click', (event) => {
// Compare the element in the DOM on which the mouse was clicked
// with the current actionMenu native HTML element.
if (host.nativeElement === event.target) {
return;
}
if (!this.strict && host.nativeElement.contains(event.target)) {
return;
}
// We'll run change detection only if the click event actually happened outside of
// the host element.
ngZone.run(() => {
this.outsideClick.emit(event);
});
});
});
}
ngOnDestroy() {
this.documentClickListener();
}
}
OutsideClick.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: OutsideClick, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
OutsideClick.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: OutsideClick, selector: "[clrOutsideClick]", inputs: { strict: ["clrStrict", "strict"] }, outputs: { outsideClick: "clrOutsideClick" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: OutsideClick, decorators: [{
type: Directive,
args: [{
selector: '[clrOutsideClick]',
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }]; }, propDecorators: { strict: [{
type: Input,
args: ['clrStrict']
}], outsideClick: [{
type: Output,
args: ['clrOutsideClick']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const OUSTIDE_CLICK_DIRECTIVES = [OutsideClick];
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrOutsideClickModule {
}
ClrOutsideClickModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOutsideClickModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrOutsideClickModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrOutsideClickModule, declarations: [OutsideClick], imports: [CommonModule], exports: [OutsideClick] });
ClrOutsideClickModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOutsideClickModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrOutsideClickModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [OUSTIDE_CLICK_DIRECTIVES],
exports: [OUSTIDE_CLICK_DIRECTIVES],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class CustomFilter {
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* This provider implements some form of synchronous debouncing through a lock pattern
* to avoid emitting multiple state changes for a single user action.
*/
class StateDebouncer {
constructor() {
/*
* This is the lock, to only emit once all the changes have finished processing
*/
this.nbChanges = 0;
/**
* The Observable that lets other classes subscribe to global state changes
*/
this._change = new Subject();
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get change() {
return this._change.asObservable();
}
changeStart() {
this.nbChanges++;
}
changeDone() {
if (--this.nbChanges === 0) {
this._change.next();
}
}
}
StateDebouncer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StateDebouncer, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
StateDebouncer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StateDebouncer });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: StateDebouncer, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class Page {
constructor(stateDebouncer) {
this.stateDebouncer = stateDebouncer;
this.activated = false;
/**
* Page size, a value of 0 means no pagination
*/
this._size = 0;
/**
* Current page
*/
this._current = 1;
/**
* The Observable that lets other classes subscribe to page changes
*/
this._change = new Subject();
this.preventEmit = false;
this._sizeChange = new Subject();
}
get size() {
return this._size;
}
set size(size) {
const oldSize = this._size;
if (size !== oldSize) {
if (!this.preventEmit) {
this.stateDebouncer.changeStart();
}
this._size = size;
if (size === 0) {
this._current = 1;
}
else {
// Yeap. That's the formula to keep the first item from the old page still
// displayed in the new one.
this._current = Math.floor((oldSize / size) * (this._current - 1)) + 1;
}
// We always emit an event even if the current page index didn't change, because
// the size changing means the items inside the page are different
if (!this.preventEmit) {
this._change.next(this._current);
this._sizeChange.next(this._size);
this.stateDebouncer.changeDone();
}
}
this.preventEmit = false;
}
get totalItems() {
return this._totalItems || 0; // remains 0 if not set to avoid breaking change
}
set totalItems(total) {
this._totalItems = total;
// If we have less items than before, we might need to change the current page
if (this.current > this.last) {
this.current = this.last;
}
}
get last() {
if (this._last) {
return this._last;
}
// If the last page isn't known, we compute it from the last item's index
if (this.size > 0 && this.totalItems) {
return Math.ceil(this.totalItems / this.size);
}
return 1;
}
set last(page) {
this._last = page;
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get change() {
return this._change.asObservable();
}
get sizeChange() {
return this._sizeChange.asObservable();
}
get current() {
return this._current;
}
set current(page) {
if (page !== this._current) {
this.stateDebouncer.changeStart();
this._current = page;
this._change.next(page);
this.stateDebouncer.changeDone();
}
}
/**
* Index of the first item displayed on the current page, starting at 0, -1 if none displayed
*/
get firstItem() {
if (this._totalItems === 0) {
return -1;
}
if (this.size === 0) {
return 0;
}
return (this.current - 1) * this.size;
}
/**
* Index of the last item displayed on the current page, starting at 0, -1 if none displayed
*/
get lastItem() {
if (this._totalItems === 0) {
return -1;
}
if (this.size === 0) {
return this.totalItems - 1;
}
let lastInPage = this.current * this.size - 1;
if (this.totalItems) {
lastInPage = Math.min(lastInPage, this.totalItems - 1);
}
return lastInPage;
}
/**
* Moves to the previous page if it exists
*/
previous() {
if (this.current > 1) {
this.current--;
}
}
/**
* Moves to the next page if it exists
*/
next() {
if (this.current < this.last) {
this.current++;
}
}
/**
* Resets the page size to 0
*/
resetPageSize(preventEmit = false) {
this.preventEmit = preventEmit;
this.size = 0;
}
}
Page.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Page, deps: [{ token: StateDebouncer }], target: i0.ɵɵFactoryTarget.Injectable });
Page.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Page });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Page, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: StateDebouncer }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class FiltersProvider {
constructor(_page, stateDebouncer) {
this._page = _page;
this.stateDebouncer = stateDebouncer;
/**
* This subject is the list of filters that changed last, not the whole list.
* We emit a list rather than just one filter to allow batch changes to several at once.
*/
this._change = new Subject();
/**
* List of all filters, whether they're active or not
*/
this._all = [];
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get change() {
return this._change.asObservable();
}
/**
* Tests if at least one filter is currently active
*/
hasActiveFilters() {
// We do not use getActiveFilters() because this function will be called much more often
// and stopping the loop early might be relevant.
for (const { filter } of this._all) {
if (filter && filter.isActive()) {
return true;
}
}
return false;
}
/**
* Returns a list of all currently active filters
*/
getActiveFilters() {
const ret = [];
for (const { filter } of this._all) {
if (filter && filter.isActive()) {
ret.push(filter);
}
}
return ret;
}
/**
* Registers a filter, and returns a deregistration function
*/
add(filter) {
const subscription = filter.changes.subscribe(() => this.resetPageAndEmitFilterChange([filter]));
let hasUnregistered = false;
const registered = new RegisteredFilter(filter, () => {
if (hasUnregistered) {
return;
}
subscription.unsubscribe();
const matchIndex = this._all.findIndex(item => item.filter === filter);
if (matchIndex >= 0) {
this._all.splice(matchIndex, 1);
}
if (filter.isActive()) {
this.resetPageAndEmitFilterChange([]);
}
hasUnregistered = true;
});
this._all.push(registered);
if (filter.isActive()) {
this.resetPageAndEmitFilterChange([filter]);
}
return registered;
}
/**
* Accepts an item if it is accepted by all currently active filters
*/
accepts(item) {
for (const { filter } of this._all) {
if (filter && filter.isActive() && !filter.accepts(item)) {
return false;
}
}
return true;
}
resetPageAndEmitFilterChange(filters) {
this.stateDebouncer.changeStart();
// filtering may change the page number such that current page number doesn't exist in the filtered dataset.
// So here we always set the current page to 1 so that it'll fetch first page's data with the given filter.
this._page.current = 1;
this._change.next(filters);
this.stateDebouncer.changeDone();
}
}
FiltersProvider.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: FiltersProvider, deps: [{ token: Page }, { token: StateDebouncer }], target: i0.ɵɵFactoryTarget.Injectable });
FiltersProvider.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: FiltersProvider });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: FiltersProvider, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: Page }, { type: StateDebouncer }]; } });
class RegisteredFilter {
constructor(filter, unregister) {
this.filter = filter;
this.unregister = unregister;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridFilterRegistrar {
constructor(filters) {
this.filters = filters;
}
get filter() {
return this.registered && this.registered.filter;
}
ngOnDestroy() {
this.deleteFilter();
}
setFilter(filter) {
// If we previously had another filter, we unregister it
this.deleteFilter();
if (filter instanceof RegisteredFilter) {
this.registered = filter;
}
else if (filter) {
this.registered = this.filters.add(filter);
}
}
deleteFilter() {
if (this.registered) {
this.registered.unregister();
delete this.registered;
}
}
}
DatagridFilterRegistrar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridFilterRegistrar, deps: [{ token: FiltersProvider }], target: i0.ɵɵFactoryTarget.Directive });
DatagridFilterRegistrar.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: DatagridFilterRegistrar, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridFilterRegistrar, decorators: [{
type: Directive
}], ctorParameters: function () { return [{ type: FiltersProvider }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Custom filter that can be added in any column to override the default object property string filter.
* The reason this is not just an input on DatagridColumn is because we need the filter's template to be projected,
* since it can be anything (not just a text input).
*/
class ClrDatagridFilter extends DatagridFilterRegistrar {
constructor(_filters, commonStrings, smartToggleService, platformId, elementRef) {
super(_filters);
this.commonStrings = commonStrings;
this.smartToggleService = smartToggleService;
this.platformId = platformId;
this.elementRef = elementRef;
this.openChange = new EventEmitter(false);
this.ariaExpanded = false;
this.popoverId = uniqueIdFactory();
// Smart Popover
this.smartPosition = {
axis: ClrAxis.VERTICAL,
side: ClrSide.AFTER,
anchor: ClrAlignment.END,
content: ClrAlignment.END,
};
this._open = false;
this.subs = [];
this.subs.push(smartToggleService.openChange.subscribe(change => {
this.open = change;
this.ariaExpanded = change;
}));
}
get open() {
return this._open;
}
set open(open) {
open = !!open;
if (this.open !== open) {
this.smartToggleService.open = open;
this.openChange.emit(open);
if (!open && isPlatformBrowser(this.platformId)) {
this.anchor.nativeElement.focus();
}
// keep track of the state
this._open = open;
}
}
set customFilter(filter) {
this.setFilter(filter);
}
/**
* Indicates if the filter is currently active
*/
get active() {
return !!this.filter && this.filter.isActive();
}
ngOnChanges() {
this.setToggleButtonAriaLabel();
}
ngOnDestroy() {
super.ngOnDestroy();
this.subs.forEach(sub => sub.unsubscribe());
}
/**
* This is not in a getter to prevent "expression has changed after it was checked" errors.
* And it's more performant this way since it only runs on change.
*/
setToggleButtonAriaLabel() {
const columnElement = this.elementRef.nativeElement?.closest('clr-dg-column');
const columnTitleElement = columnElement?.querySelector('.datagrid-column-title');
const columnTitle = columnTitleElement?.textContent.trim().toLocaleLowerCase();
this.toggleButtonAriaLabel = this.commonStrings.parse(this.commonStrings.keys.datagridFilterAriaLabel, {
COLUMN: columnTitle || '',
});
}
}
ClrDatagridFilter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridFilter, deps: [{ token: FiltersProvider }, { token: ClrCommonStringsService }, { token: ClrPopoverToggleService }, { token: PLATFORM_ID }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridFilter.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: { open: ["clrDgFilterOpen", "open"], customFilter: ["clrDgFilter", "customFilter"] }, outputs: { openChange: "clrDgFilterOpenChange" }, providers: [{ provide: CustomFilter, useExisting: ClrDatagridFilter }], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ElementRef }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
`,
}]
}], ctorParameters: function () { return [{ type: FiltersProvider }, { type: ClrCommonStringsService }, { type: ClrPopoverToggleService }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: i0.ElementRef }]; }, propDecorators: { openChange: [{
type: Output,
args: ['clrDgFilterOpenChange']
}], anchor: [{
type: ViewChild,
args: ['anchor', { read: ElementRef }]
}], open: [{
type: Input,
args: ['clrDgFilterOpen']
}], customFilter: [{
type: Input,
args: ['clrDgFilter']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Generic accessor for deep object properties
* that can be specified as simple dot-separated strings.
*/
class NestedProperty {
constructor(prop) {
this.prop = prop;
if (prop.indexOf('.') >= 0) {
this.splitProp = prop.split('.');
}
}
// Safe getter for a deep object property, will not throw an error but return
// undefined if one of the intermediate properties is null or undefined.
getPropValue(item) {
if (this.splitProp) {
let value = item;
for (const nestedProp of this.splitProp) {
if (value === null ||
typeof value === 'undefined' ||
typeof value[nestedProp] === 'undefined') {
return undefined;
}
value = value[nestedProp];
}
return value;
}
else {
return item[this.prop];
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridPropertyNumericFilter {
constructor(prop, exact = false) {
this.prop = prop;
this.exact = exact;
this.nestedProp = new NestedProperty(prop);
}
accepts(item, low, high) {
const propValue = this.nestedProp.getPropValue(item);
if (propValue === undefined) {
return false;
}
if (low !== null && (typeof propValue !== 'number' || propValue < low)) {
return false;
}
if (high !== null && (typeof propValue !== 'number' || propValue > high)) {
return false;
}
return true;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridNumericFilterImpl {
constructor(filterFn) {
this.filterFn = filterFn;
/**
* The Observable required as part of the Filter interface
*/
this._changes = new Subject();
/**
* Internal values and accessor
*/
this._low = null;
this._high = null;
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get changes() {
return this._changes.asObservable();
}
get value() {
return [this._low, this._high];
}
set value(vals) {
const low = vals[0];
const high = vals[1];
if (low !== this._low || high !== this._high) {
this._low = low;
this._high = high;
this._changes.next([this._low, this._high]);
}
}
get low() {
return this._low;
}
set low(low) {
if (low !== this._low) {
this._low = low;
this._changes.next([this._low, this._high]);
}
}
get high() {
return this._high;
}
set high(high) {
if (high !== this._high) {
this._high = high;
this._changes.next([this._low, this._high]);
}
}
get state() {
if (this.filterFn instanceof DatagridPropertyNumericFilter) {
return {
property: this.filterFn.prop,
low: this._low,
high: this._high,
};
}
return this;
}
/**
* Indicates if the filter is currently active, (at least one input is set)
*/
isActive() {
return this._low !== null || this.high !== null;
}
/**
* Tests if an item matches a search text
*/
accepts(item) {
// We have a filter function in case someone wants to implement a numeric
// filter that always passes nulls or similar
return this.filterFn.accepts(item, this._low, this._high);
}
equals(other) {
if (other instanceof DatagridNumericFilterImpl) {
if (other.filterFn instanceof DatagridPropertyNumericFilter) {
return (this.filterFn instanceof DatagridPropertyNumericFilter &&
other.filterFn.prop === this.filterFn.prop &&
other.low === this._low &&
other.high === this._high);
}
return other === this;
}
return false;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridNumericFilter extends DatagridFilterRegistrar {
constructor(filters, domAdapter, commonStrings, popoverToggleService, ngZone) {
super(filters);
this.domAdapter = domAdapter;
this.commonStrings = commonStrings;
this.popoverToggleService = popoverToggleService;
this.ngZone = ngZone;
this.filterValueChange = new EventEmitter();
/**
* Indicates if the filter dropdown is open
*/
this.open = false;
this.subscriptions = [];
}
/**
* Common setter for the input values
*/
get value() {
return [this.filter.low, this.filter.high];
}
set value(values) {
if (this.filter && Array.isArray(values)) {
if (values && (values[0] !== this.filter.low || values[1] !== this.filter.high)) {
if (typeof values[0] === 'number') {
this.filter.low = values[0];
}
else {
this.filter.low = null;
}
if (typeof values[1] === 'number') {
this.filter.high = values[1];
}
else {
this.filter.high = null;
}
this.filterValueChange.emit(values);
}
}
else {
this.initFilterValues = values;
}
}
/**
* Customizable filter logic based on high and low values
*/
set customNumericFilter(value) {
if (value instanceof RegisteredFilter) {
this.setFilter(value);
}
else {
this.setFilter(new DatagridNumericFilterImpl(value));
}
if (this.initFilterValues) {
this.value = this.initFilterValues;
// This initFilterValues should be used only once after the filter registration
// So deleting this property value to prevent it from being used again
// if this customStringFilter property is set again
delete this.initFilterValues;
}
}
get maxPlaceholderValue() {
return this.maxPlaceholder || this.commonStrings.keys.maxValue;
}
get minPlaceholderValue() {
return this.minPlaceholder || this.commonStrings.keys.minValue;
}
get low() {
if (typeof this.filter.low === 'number' && isFinite(this.filter.low)) {
return this.filter.low;
}
else {
// There's not a low limit
return null;
}
}
set low(low) {
if (typeof low === 'number' && low !== this.filter.low) {
this.filter.low = low;
this.filterValueChange.emit([this.filter.low, this.filter.high]);
}
else if (typeof low !== 'number') {
this.filter.low = null;
this.filterValueChange.emit([this.filter.low, this.filter.high]);
}
}
get high() {
if (typeof this.filter.high === 'number' && isFinite(this.filter.high)) {
return this.filter.high;
}
else {
// There's not a high limit
return null;
}
}
set high(high) {
if (typeof high === 'number' && high !== this.filter.high) {
this.filter.high = high;
this.filterValueChange.emit([this.filter.low, this.filter.high]);
}
else if (typeof high !== 'number') {
this.filter.high = null;
this.filterValueChange.emit([this.filter.low, this.filter.high]);
}
}
ngAfterViewInit() {
this.subscriptions.push(this.popoverToggleService.openChange.subscribe(openChange => {
this.open = openChange;
// Note: this is being run outside of the Angular zone because `element.focus()` doesn't require
// running change detection.
this.ngZone.runOutsideAngular(() => {
// The animation frame in used because when this executes, the input isn't displayed.
// Note: `element.focus()` causes re-layout and this may lead to frame drop on slower devices.
// `setTimeout` is a macrotask and macrotasks are executed within the current rendering frame.
// Animation tasks are executed within the next rendering frame.
requestAnimationFrame(() => {
this.domAdapter.focus(this.input.nativeElement);
});
});
}));
}
ngOnDestroy() {
super.ngOnDestroy();
this.subscriptions.forEach(sub => {
sub.unsubscribe();
});
}
}
DatagridNumericFilter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridNumericFilter, deps: [{ token: FiltersProvider }, { token: DomAdapter }, { token: ClrCommonStringsService }, { token: ClrPopoverToggleService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
DatagridNumericFilter.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: DatagridNumericFilter, selector: "clr-dg-numeric-filter", inputs: { minPlaceholder: ["clrFilterMinPlaceholder", "minPlaceholder"], maxPlaceholder: ["clrFilterMaxPlaceholder", "maxPlaceholder"], value: ["clrFilterValue", "value"], customNumericFilter: ["clrDgNumericFilter", "customNumericFilter"] }, outputs: { filterValueChange: "clrFilterValueChange" }, providers: [{ provide: CustomFilter, useExisting: DatagridNumericFilter }], viewQueries: [{ propertyName: "input", first: true, predicate: ["input_low"], descendants: true }, { propertyName: "filterContainer", first: true, predicate: ClrDatagridFilter, descendants: true }], usesInheritance: true, ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: ["clrDgFilterOpen", "clrDgFilter"], outputs: ["clrDgFilterOpenChange"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridNumericFilter, decorators: [{
type: Component,
args: [{
selector: 'clr-dg-numeric-filter',
providers: [{ provide: CustomFilter, useExisting: DatagridNumericFilter }],
template: `
`,
}]
}], ctorParameters: function () { return [{ type: FiltersProvider }, { type: DomAdapter }, { type: ClrCommonStringsService }, { type: ClrPopoverToggleService }, { type: i0.NgZone }]; }, propDecorators: { minPlaceholder: [{
type: Input,
args: ['clrFilterMinPlaceholder']
}], maxPlaceholder: [{
type: Input,
args: ['clrFilterMaxPlaceholder']
}], filterValueChange: [{
type: Output,
args: ['clrFilterValueChange']
}], input: [{
type: ViewChild,
args: ['input_low']
}], filterContainer: [{
type: ViewChild,
args: [ClrDatagridFilter]
}], value: [{
type: Input,
args: ['clrFilterValue']
}], customNumericFilter: [{
type: Input,
args: ['clrDgNumericFilter']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridPropertyStringFilter {
constructor(prop, exact = false) {
this.prop = prop;
this.exact = exact;
this.nestedProp = new NestedProperty(prop);
}
accepts(item, search) {
const propValue = this.nestedProp.getPropValue(item);
if (typeof propValue === 'undefined') {
return false;
}
else if (this.exact) {
return ('' + propValue).toLowerCase() === search;
}
else {
return ('' + propValue).toLowerCase().indexOf(search) >= 0;
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridStringFilterImpl {
constructor(filterFn) {
this.filterFn = filterFn;
/**
* The Observable required as part of the Filter interface
*/
this._changes = new Subject();
/**
* Input value converted to lowercase
*/
this._lowerCaseValue = '';
/**
* Raw input value
*/
this._rawValue = '';
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get changes() {
return this._changes.asObservable();
}
get lowerCaseValue() {
return this._lowerCaseValue;
}
get state() {
if (this.filterFn instanceof DatagridPropertyStringFilter) {
return {
property: this.filterFn.prop,
value: this.value,
};
}
return this;
}
get value() {
return this._rawValue;
}
/**
* Common setter for the input value
*/
set value(value) {
if (!value) {
value = '';
}
if (value !== this._rawValue) {
this._rawValue = value;
this._lowerCaseValue = value.toLowerCase().trim();
this._changes.next(value);
}
}
/**
* Indicates if the filter is currently active, meaning the input is not empty
*/
isActive() {
return !!this.value;
}
/**
* Tests if an item matches a search text
*/
accepts(item) {
// We always test with the lowercase value of the input, to stay case insensitive
return this.filterFn.accepts(item, this.lowerCaseValue);
}
equals(other) {
if (other instanceof DatagridStringFilterImpl) {
if (other.filterFn instanceof DatagridPropertyStringFilter) {
return (this.filterFn instanceof DatagridPropertyStringFilter &&
other.filterFn.prop === this.filterFn.prop &&
other.value === this.value);
}
return other === this;
}
return false;
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridStringFilter extends DatagridFilterRegistrar {
constructor(filters, domAdapter, commonStrings, smartToggleService, ngZone) {
super(filters);
this.domAdapter = domAdapter;
this.commonStrings = commonStrings;
this.smartToggleService = smartToggleService;
this.ngZone = ngZone;
this.filterValueChange = new EventEmitter();
/**
* Indicates if the filter dropdown is open
*/
this.open = false;
this.subs = [];
}
/**
* Customizable filter logic based on a search text
*/
set customStringFilter(value) {
if (value instanceof RegisteredFilter) {
this.setFilter(value);
}
else {
this.setFilter(new DatagridStringFilterImpl(value));
}
if (this.initFilterValue) {
this.value = this.initFilterValue;
// This initFilterValue should be used only once after the filter registration
// So deleting this property value to prevent it from being used again
// if this customStringFilter property is set again
delete this.initFilterValue;
}
}
/**
* Common setter for the input value
*/
get value() {
return this.filter.value;
}
set value(value) {
if (this.filter && typeof value === 'string') {
if (!value) {
value = '';
}
if (value !== this.filter.value) {
this.filter.value = value;
this.filterValueChange.emit(value);
}
}
else {
this.initFilterValue = value;
}
}
get placeholderValue() {
return this.placeholder || this.commonStrings.keys.filterItems;
}
ngAfterViewInit() {
this.subs.push(this.smartToggleService.openChange.subscribe(openChange => {
this.open = openChange;
// Note: this is being run outside of the Angular zone because `element.focus()` doesn't require
// running change detection.
this.ngZone.runOutsideAngular(() => {
// The animation frame in used because when this executes, the input isn't displayed.
// Note: `element.focus()` causes re-layout and this may lead to frame drop on slower devices.
// `setTimeout` is a macrotask and macrotasks are executed within the current rendering frame.
// Animation tasks are executed within the next rendering frame.
requestAnimationFrame(() => {
this.domAdapter.focus(this.input.nativeElement);
});
});
}));
}
ngOnDestroy() {
super.ngOnDestroy();
this.subs.forEach(sub => sub.unsubscribe());
}
}
DatagridStringFilter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridStringFilter, deps: [{ token: FiltersProvider }, { token: DomAdapter }, { token: ClrCommonStringsService }, { token: ClrPopoverToggleService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
DatagridStringFilter.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: DatagridStringFilter, selector: "clr-dg-string-filter", inputs: { placeholder: ["clrFilterPlaceholder", "placeholder"], customStringFilter: ["clrDgStringFilter", "customStringFilter"], value: ["clrFilterValue", "value"] }, outputs: { filterValueChange: "clrFilterValueChange" }, providers: [{ provide: CustomFilter, useExisting: DatagridStringFilter }], viewQueries: [{ propertyName: "input", first: true, predicate: ["input"], descendants: true }, { propertyName: "filterContainer", first: true, predicate: ClrDatagridFilter, descendants: true }], usesInheritance: true, ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: ["clrDgFilterOpen", "clrDgFilter"], outputs: ["clrDgFilterOpenChange"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridStringFilter, decorators: [{
type: Component,
args: [{
selector: 'clr-dg-string-filter',
providers: [{ provide: CustomFilter, useExisting: DatagridStringFilter }],
template: `
`,
}]
}], ctorParameters: function () { return [{ type: FiltersProvider }, { type: DomAdapter }, { type: ClrCommonStringsService }, { type: ClrPopoverToggleService }, { type: i0.NgZone }]; }, propDecorators: { placeholder: [{
type: Input,
args: ['clrFilterPlaceholder']
}], filterValueChange: [{
type: Output,
args: ['clrFilterValueChange']
}], input: [{
type: ViewChild,
args: ['input']
}], filterContainer: [{
type: ViewChild,
args: [ClrDatagridFilter]
}], customStringFilter: [{
type: Input,
args: ['clrDgStringFilter']
}], value: [{
type: Input,
args: ['clrFilterValue']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridWillyWonka extends WillyWonka {
}
DatagridWillyWonka.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridWillyWonka, deps: null, target: i0.ɵɵFactoryTarget.Directive });
DatagridWillyWonka.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: DatagridWillyWonka, selector: "clr-datagrid", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridWillyWonka, decorators: [{
type: Directive,
args: [{
selector: 'clr-datagrid',
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class RowActionService {
constructor() {
this.actionableCount = 0;
}
/**
* false means no rows with action
*/
get hasActionableRow() {
return this.actionableCount > 0;
}
register() {
this.actionableCount++;
}
unregister() {
this.actionableCount--;
}
}
RowActionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: RowActionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
RowActionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: RowActionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: RowActionService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ActionableOompaLoompa extends OompaLoompa {
constructor(cdr, willyWonka, rowActions) {
if (!willyWonka) {
throw new Error('clr-dg-row should only be used inside of a clr-datagrid');
}
super(cdr, willyWonka);
this.rowActions = rowActions;
}
get flavor() {
return this.rowActions.hasActionableRow;
}
}
ActionableOompaLoompa.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ActionableOompaLoompa, deps: [{ token: i0.ChangeDetectorRef }, { token: DatagridWillyWonka, optional: true }, { token: RowActionService }], target: i0.ɵɵFactoryTarget.Directive });
ActionableOompaLoompa.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ActionableOompaLoompa, selector: "clr-datagrid, clr-dg-row", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ActionableOompaLoompa, decorators: [{
type: Directive,
args: [{
selector: 'clr-datagrid, clr-dg-row',
}]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: DatagridWillyWonka, decorators: [{
type: Optional
}] }, { type: RowActionService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ModalStackService {
constructor(platformId) {
this.platformId = platformId;
this.modalStack = [];
this.keyUpEventListener = this.onKeyUp.bind(this);
}
trackModalOpen(openedModal) {
if (this.modalStack.includes(openedModal) === false) {
this.modalStack.unshift(openedModal);
}
if (isPlatformBrowser(this.platformId)) {
document.body.addEventListener('keyup', this.keyUpEventListener);
}
}
trackModalClose(closedModal) {
const closedModalIndex = this.modalStack.indexOf(closedModal);
if (closedModalIndex > -1) {
this.modalStack.splice(closedModalIndex, 1);
}
if (this.modalStack.length === 0 && isPlatformBrowser(this.platformId)) {
document.body.removeEventListener('keyup', this.keyUpEventListener);
}
}
onKeyUp(event) {
if (this.modalStack.length && normalizeKey(event.key) === Keys.Escape) {
// We blur the active element because escaping with an input element in focus could cause
// an ExpressionChangedAfterItHasBeenCheckedError for the touched state. (CDE-1662)
document.activeElement.blur();
this.modalStack[0].close();
}
}
}
ModalStackService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ModalStackService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
ModalStackService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ModalStackService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ModalStackService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DetailService {
constructor(modalStackService) {
this.modalStackService = modalStackService;
this.toggleState = false;
this._enabled = false;
this._state = new BehaviorSubject(this.toggleState);
}
get enabled() {
return this._enabled;
}
set enabled(state) {
this._enabled = state;
}
get state() {
return this.cache;
}
get stateChange() {
return this._state.asObservable();
}
get isOpen() {
return this.toggleState === true;
}
open(item, button) {
this.cache = item;
this.button = button;
this.toggleState = true;
this._state.next(this.toggleState);
this.modalStackService.trackModalOpen(this);
}
close() {
this.toggleState = false;
this._state.next(this.toggleState);
this.modalStackService.trackModalClose(this);
if (this.button) {
this.button.focus();
this.button = null;
}
}
toggle(item, button) {
if (this.isRowOpen(item) || !item) {
this.close();
}
else {
this.open(item, button);
}
}
isRowOpen(item) {
return !!(this.toggleState && this.cache === item);
}
}
DetailService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DetailService, deps: [{ token: ModalStackService }], target: i0.ɵɵFactoryTarget.Injectable });
DetailService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DetailService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DetailService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: ModalStackService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ExpandableRowsCount {
constructor(detailService) {
this.detailService = detailService;
this.expandableCount = 0;
}
/**
* false means no rows with action
* check if details are on, and disable rows entirely
*/
get hasExpandableRow() {
return !this.detailService.enabled && this.expandableCount > 0;
}
register() {
this.expandableCount++;
}
unregister() {
this.expandableCount--;
}
}
ExpandableRowsCount.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ExpandableRowsCount, deps: [{ token: DetailService }], target: i0.ɵɵFactoryTarget.Injectable });
ExpandableRowsCount.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ExpandableRowsCount });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ExpandableRowsCount, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: DetailService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ExpandableOompaLoompa extends OompaLoompa {
constructor(cdr, willyWonka, expandableCount) {
if (!willyWonka) {
throw new Error('clr-dg-row should only be used inside of a clr-datagrid');
}
super(cdr, willyWonka);
this.expandableCount = expandableCount;
}
get flavor() {
return this.expandableCount.hasExpandableRow;
}
}
ExpandableOompaLoompa.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ExpandableOompaLoompa, deps: [{ token: i0.ChangeDetectorRef }, { token: DatagridWillyWonka, optional: true }, { token: ExpandableRowsCount }], target: i0.ɵɵFactoryTarget.Directive });
ExpandableOompaLoompa.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ExpandableOompaLoompa, selector: "clr-datagrid, clr-dg-row", usesInheritance: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ExpandableOompaLoompa, decorators: [{
type: Directive,
args: [{
selector: 'clr-datagrid, clr-dg-row',
}]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: DatagridWillyWonka, decorators: [{
type: Optional
}] }, { type: ExpandableRowsCount }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridPropertyComparator {
constructor(prop) {
this.prop = prop;
this.nestedProp = new NestedProperty(prop);
}
compare(a, b) {
let propA = this.nestedProp.getPropValue(a);
let propB = this.nestedProp.getPropValue(b);
if (typeof propA === 'string') {
propA = propA.toLowerCase();
}
if (typeof propB === 'string') {
propB = propB.toLowerCase();
}
if (typeof propA === 'undefined' || propA === null) {
if (typeof propB === 'undefined' || propB === null) {
return 0;
}
else {
return 1;
}
}
else {
if (typeof propB === 'undefined' || propB === null) {
return -1;
}
else if (propA < propB) {
return -1;
}
else if (propA > propB) {
return 1;
}
else {
return 0;
}
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Enumeration representing the sorting order of a datagrid column. It is a constant Enum,
* i.e. each value needs to be treated as a `number`, starting at index 0.
*
* @export
* @enum {number}
*/
var ClrDatagridSortOrder;
(function (ClrDatagridSortOrder) {
ClrDatagridSortOrder[ClrDatagridSortOrder["UNSORTED"] = 0] = "UNSORTED";
ClrDatagridSortOrder[ClrDatagridSortOrder["ASC"] = 1] = "ASC";
ClrDatagridSortOrder[ClrDatagridSortOrder["DESC"] = -1] = "DESC";
})(ClrDatagridSortOrder || (ClrDatagridSortOrder = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class WrappedColumn {
constructor() {
this._dynamic = false;
}
ngAfterViewInit() {
// Create the cells view in memory, not the DOM.
this.columnView = this.templateRef.createEmbeddedView(null);
}
ngOnDestroy() {
this.columnView.destroy();
}
}
WrappedColumn.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WrappedColumn, deps: [], target: i0.ɵɵFactoryTarget.Component });
WrappedColumn.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: WrappedColumn, selector: "dg-wrapped-column", viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["columnPortal"], descendants: true }], ngImport: i0, template: `
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WrappedColumn, decorators: [{
type: Component,
args: [{
selector: 'dg-wrapped-column',
template: `
`,
}]
}], propDecorators: { templateRef: [{
type: ViewChild,
args: ['columnPortal']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class Sort {
constructor(stateDebouncer) {
this.stateDebouncer = stateDebouncer;
/**
* Ascending order if false, descending if true
*/
this._reverse = false;
/**
* The Observable that lets other classes subscribe to sort changes
*/
this._change = new Subject();
}
get comparator() {
return this._comparator;
}
set comparator(value) {
this.stateDebouncer.changeStart();
this._comparator = value;
this.emitChange();
this.stateDebouncer.changeDone();
}
get reverse() {
return this._reverse;
}
set reverse(value) {
this.stateDebouncer.changeStart();
this._reverse = value;
this.emitChange();
this.stateDebouncer.changeDone();
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get change() {
return this._change.asObservable();
}
/**
* Sets a comparator as the current one, or toggles reverse if the comparator is already used. The
* optional forceReverse input parameter allows to override that toggling behavior by sorting in
* reverse order if `true`.
*
* @memberof Sort
*/
toggle(sortBy, forceReverse) {
this.stateDebouncer.changeStart();
// We modify private properties directly, to batch the change event
if (this.comparator === sortBy) {
this._reverse = typeof forceReverse !== 'undefined' ? forceReverse || !this._reverse : !this._reverse;
}
else {
this._comparator = sortBy;
this._reverse = typeof forceReverse !== 'undefined' ? forceReverse : false;
}
this.emitChange();
this.stateDebouncer.changeDone();
}
/**
* Clears the current sorting order
*/
clear() {
this.comparator = null;
}
/**
* Compares two objects according to the current comparator
*/
compare(a, b) {
return (this.reverse ? -1 : 1) * this.comparator.compare(a, b);
}
emitChange() {
this._change.next(this);
}
}
Sort.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Sort, deps: [{ token: StateDebouncer }], target: i0.ɵɵFactoryTarget.Injectable });
Sort.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Sort });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Sort, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: StateDebouncer }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var DatagridRenderStep;
(function (DatagridRenderStep) {
DatagridRenderStep[DatagridRenderStep["ALIGN_COLUMNS"] = 0] = "ALIGN_COLUMNS";
DatagridRenderStep[DatagridRenderStep["CALCULATE_MODE_ON"] = 1] = "CALCULATE_MODE_ON";
DatagridRenderStep[DatagridRenderStep["CALCULATE_MODE_OFF"] = 2] = "CALCULATE_MODE_OFF";
DatagridRenderStep[DatagridRenderStep["CLEAR_WIDTHS"] = 3] = "CLEAR_WIDTHS";
DatagridRenderStep[DatagridRenderStep["COMPUTE_COLUMN_WIDTHS"] = 4] = "COMPUTE_COLUMN_WIDTHS";
})(DatagridRenderStep || (DatagridRenderStep = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DatagridRenderOrganizer {
constructor() {
this._renderStep = new Subject();
this.alreadySized = false;
}
get renderStep() {
return this._renderStep.asObservable();
}
filterRenderSteps(step) {
return this.renderStep.pipe(filter(testStep => step === testStep));
}
resize() {
this._renderStep.next(DatagridRenderStep.CALCULATE_MODE_ON);
if (this.alreadySized) {
this._renderStep.next(DatagridRenderStep.CLEAR_WIDTHS);
}
this._renderStep.next(DatagridRenderStep.COMPUTE_COLUMN_WIDTHS);
this._renderStep.next(DatagridRenderStep.ALIGN_COLUMNS);
this.alreadySized = true;
this._renderStep.next(DatagridRenderStep.CALCULATE_MODE_OFF);
}
}
DatagridRenderOrganizer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridRenderOrganizer, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
DatagridRenderOrganizer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridRenderOrganizer });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridRenderOrganizer, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const MIN_COLUMN_WIDTH = 96;
// This service allows DatagridHeaderRenderer and ClrDatagridColumnSeparator
// to share column resize data with each other.
class ColumnResizerService {
constructor(el, domAdapter, organizer) {
this.el = el;
this.domAdapter = domAdapter;
this.organizer = organizer;
this._resizedBy = 0;
}
get resizedBy() {
return this._resizedBy;
}
get minColumnWidth() {
return this.domAdapter.minWidth(this.el.nativeElement) || MIN_COLUMN_WIDTH;
}
get maxResizeRange() {
return this.widthBeforeResize - this.minColumnWidth;
}
get widthAfterResize() {
return this.widthBeforeResize + this._resizedBy;
}
startResize() {
this._resizedBy = 0;
this.isWithinMaxResizeRange = true;
this.widthBeforeResize = this.domAdapter.clientRect(this.el.nativeElement).width;
}
endResize() {
this.organizer.resize();
}
calculateResize(resizedBy) {
// calculates the resize amount within the allowed range
if (resizedBy < -this.maxResizeRange) {
this._resizedBy = -this.maxResizeRange;
this.isWithinMaxResizeRange = false;
}
else {
this._resizedBy = resizedBy;
this.isWithinMaxResizeRange = true;
}
}
}
ColumnResizerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ColumnResizerService, deps: [{ token: i0.ElementRef }, { token: DomAdapter }, { token: DatagridRenderOrganizer }], target: i0.ɵɵFactoryTarget.Injectable });
ColumnResizerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ColumnResizerService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ColumnResizerService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: DomAdapter }, { type: DatagridRenderOrganizer }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* @description
* Internal datagrid service that holds a reference to the clr-dg-table element and exposes a method to get height.
*/
class TableSizeService {
constructor(platformId) {
this.platformId = platformId;
}
get tableRef() {
return this._tableRef;
}
set tableRef(element) {
this._tableRef = element;
}
set table(table) {
if (isPlatformBrowser(this.platformId) && table.nativeElement) {
this.tableRef = table.nativeElement.querySelector('.datagrid-table');
}
}
// Used when resizing columns to show the column border being dragged.
getColumnDragHeight() {
if (!this.tableRef) {
return null;
}
return `${this.tableRef.clientHeight}px`;
}
}
TableSizeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TableSizeService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
TableSizeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TableSizeService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TableSizeService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
// Default resize length on each keyboard move event
const KEYBOARD_RESIZE_LENGTH = 12;
class ClrDatagridColumnSeparator {
constructor(columnResizerService, renderer, ngZone, tableSizeService, commonString, document) {
this.columnResizerService = columnResizerService;
this.renderer = renderer;
this.ngZone = ngZone;
this.tableSizeService = tableSizeService;
this.commonString = commonString;
this.document = document;
this.columnSeparatorId = uniqueIdFactory();
this.resizeStartedOnKeyDown = false;
this.unlisteners = [];
}
get descriptionId() {
return `${this.columnSeparatorId}-aria-describedby`;
}
get resizeTrackerEl() {
return this.resizeTrackerRef.nativeElement;
}
get columnHandleEl() {
return this.columnHandleRef.nativeElement;
}
ngAfterViewInit() {
this.ngZone.runOutsideAngular(() => {
this.unlisteners.push(this.renderer.listen(this.columnHandleEl, 'keydown', event => {
this.showTrackerOnFirstKeyDown(event);
this.moveTrackerOnKeyDown(event);
}));
this.unlisteners.push(this.renderer.listen(this.columnHandleEl, 'keyup', event => {
this.hideTrackerOnKeyUp(event);
}));
});
}
ngOnDestroy() {
this.unlisteners.forEach(unlistener => unlistener());
}
showTracker() {
this.columnResizerService.startResize();
const tableHeight = this.tableSizeService.getColumnDragHeight();
this.renderer.setStyle(this.resizeTrackerEl, 'height', tableHeight);
this.renderer.setStyle(this.resizeTrackerEl, 'display', 'block');
}
moveTracker(movedBy) {
this.columnResizerService.calculateResize(movedBy);
this.renderer.setStyle(this.resizeTrackerEl, 'transform', `translateX(${this.columnResizerService.resizedBy}px)`);
this.renderer.setStyle(this.document.body, 'cursor', 'col-resize');
this.redFlagTracker();
}
hideTracker() {
this.columnResizerService.endResize();
this.renderer.setStyle(this.resizeTrackerEl, 'display', 'none');
this.renderer.setStyle(this.resizeTrackerEl, 'transform', `translateX(0px)`);
this.renderer.setStyle(this.columnHandleEl, 'transform', `translateX(0px)`);
this.renderer.setStyle(this.document.body, 'cursor', 'auto');
}
showTrackerOnFirstKeyDown(event) {
if (!this.resizeStartedOnKeyDown && (this.isArrowLeftKeyEvent(event) || this.isArrowRightKeyEvent(event))) {
this.resizeStartedOnKeyDown = true;
this.renderer.addClass(this.resizeTrackerEl, 'on-arrow-key-resize');
this.showTracker();
}
}
moveTrackerOnKeyDown(event) {
if (this.isArrowLeftKeyEvent(event)) {
event.stopPropagation();
this.moveTracker(this.columnResizerService.resizedBy - KEYBOARD_RESIZE_LENGTH);
}
else if (this.isArrowRightKeyEvent(event)) {
event.stopPropagation();
this.moveTracker(this.columnResizerService.resizedBy + KEYBOARD_RESIZE_LENGTH);
}
}
hideTrackerOnKeyUp(event) {
if (this.resizeStartedOnKeyDown && (this.isArrowLeftKeyEvent(event) || this.isArrowRightKeyEvent(event))) {
this.resizeStartedOnKeyDown = false;
this.renderer.removeClass(this.resizeTrackerEl, 'on-arrow-key-resize');
this.hideTracker();
this.columnHandleEl.focus();
}
}
redFlagTracker() {
if (this.isWithinMaxResizeRange !== this.columnResizerService.isWithinMaxResizeRange) {
this.isWithinMaxResizeRange = this.columnResizerService.isWithinMaxResizeRange;
if (!this.isWithinMaxResizeRange) {
this.renderer.addClass(this.resizeTrackerEl, 'exceeded-max');
}
else {
this.renderer.removeClass(this.resizeTrackerEl, 'exceeded-max');
}
}
}
isArrowLeftKeyEvent(event) {
return normalizeKey(event.key) === Keys.ArrowLeft;
}
isArrowRightKeyEvent(event) {
return normalizeKey(event.key) === Keys.ArrowRight;
}
}
ClrDatagridColumnSeparator.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridColumnSeparator, deps: [{ token: ColumnResizerService }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: TableSizeService }, { token: ClrCommonStringsService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridColumnSeparator.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridColumnSeparator, selector: "clr-dg-column-separator", host: { properties: { "class.datagrid-column-separator": "true" } }, viewQueries: [{ propertyName: "resizeTrackerRef", first: true, predicate: ["resizeTracker"], descendants: true }, { propertyName: "columnHandleRef", first: true, predicate: ["columnHandle"], descendants: true }], ngImport: i0, template: `
{{ commonString.keys.columnSeparatorDescription }}
`, isInline: true, dependencies: [{ kind: "directive", type: CdkDragModule_CdkDrag, selector: "[cdkDrag]" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridColumnSeparator, decorators: [{
type: Component,
args: [{
selector: 'clr-dg-column-separator',
template: `
{{ commonString.keys.columnSeparatorDescription }}
`,
host: {
'[class.datagrid-column-separator]': 'true',
},
}]
}], ctorParameters: function () { return [{ type: ColumnResizerService }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: TableSizeService }, { type: ClrCommonStringsService }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }]; }, propDecorators: { resizeTrackerRef: [{
type: ViewChild,
args: ['resizeTracker']
}], columnHandleRef: [{
type: ViewChild,
args: ['columnHandle']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridColumn extends DatagridFilterRegistrar {
constructor(_sort, filters, vcr, detailService, changeDetectorRef) {
super(filters);
this._sort = _sort;
this.vcr = vcr;
this.detailService = detailService;
this.changeDetectorRef = changeDetectorRef;
this.sortOrderChange = new EventEmitter();
this.filterValueChange = new EventEmitter();
this.showSeparator = true;
/**
* A custom filter for this column that can be provided in the projected content
*/
this.customFilter = false;
/*
* What type is this column? This defaults to STRING, but can also be
* set to NUMBER. Unsupported types default to STRING. Users can set it
* via the [clrDgColType] input by setting it to 'string' or 'number'.
*/
this._colType = 'string';
/**
* Indicates how the column is currently sorted
*/
this._sortOrder = ClrDatagridSortOrder.UNSORTED;
/**
* Subscription to the sort service changes
*/
this.subscriptions = [];
this.subscriptions.push(this.listenForSortingChanges());
this.subscriptions.push(this.listenForDetailPaneChanges());
}
// TODO: We might want to make this an enum in the future
get colType() {
return this._colType;
}
set colType(value) {
this._colType = value;
}
get field() {
return this._field;
}
set field(field) {
if (typeof field === 'string') {
this._field = field;
if (!this._sortBy) {
this._sortBy = new DatagridPropertyComparator(field);
}
}
}
get sortBy() {
return this._sortBy;
}
set sortBy(comparator) {
if (typeof comparator === 'string') {
this._sortBy = new DatagridPropertyComparator(comparator);
}
else {
if (comparator) {
this._sortBy = comparator;
}
else {
if (this.field) {
this._sortBy = new DatagridPropertyComparator(this.field);
}
else {
delete this._sortBy;
}
}
}
}
get sortOrder() {
return this._sortOrder;
}
set sortOrder(value) {
if (typeof value === 'undefined') {
return;
}
// only if the incoming order is different from the current one
if (this._sortOrder === value) {
return;
}
switch (value) {
// the Unsorted case happens when the current state is either Asc or Desc
default:
case ClrDatagridSortOrder.UNSORTED:
this._sort.clear();
break;
case ClrDatagridSortOrder.ASC:
this.sort(false);
break;
case ClrDatagridSortOrder.DESC:
this.sort(true);
break;
}
}
set updateFilterValue(newValue) {
if (this.filter) {
if (this.filter instanceof DatagridStringFilterImpl) {
if (!newValue || typeof newValue !== 'string') {
newValue = '';
}
if (newValue !== this.filter.value) {
this.filter.value = newValue;
}
}
else if (this.filter instanceof DatagridNumericFilterImpl) {
if (!newValue || !(newValue instanceof Array)) {
newValue = [null, null];
}
if (newValue.length === 2 && (newValue[0] !== this.filter.value[0] || newValue[1] !== this.filter.value[1])) {
this.filter.value = newValue;
}
}
}
else {
this.initFilterValue = newValue;
}
}
set projectedFilter(custom) {
if (custom) {
this.deleteFilter();
this.customFilter = true;
}
}
/**
* Indicates if the column is sortable
*/
get sortable() {
return !!this._sortBy;
}
get ariaSort() {
switch (this._sortOrder) {
default:
case ClrDatagridSortOrder.UNSORTED:
return 'none';
case ClrDatagridSortOrder.ASC:
return 'ascending';
case ClrDatagridSortOrder.DESC:
return 'descending';
}
}
get sortDirection() {
return this._sortDirection;
}
/**
* @NOTE type `any` here is to let us pass templateStrictMode, because in our code we try to handle
* two types of filters String and Number with the same variable but both of them work with different
* format we got an error for casting. We could not cast anything inside the template so to not mess the
* casting, the last type is set to `any`
*
* Orignial types: string | [number, number]
*/
get filterValue() {
if (this.filter instanceof DatagridStringFilterImpl || this.filter instanceof DatagridNumericFilterImpl) {
return this.filter.value;
}
return null;
}
set filterValue(newValue) {
if (this.filter instanceof DatagridStringFilterImpl || this.filter instanceof DatagridNumericFilterImpl) {
this.updateFilterValue = newValue;
this.filterValueChange.emit(this.filter.value);
}
}
get _view() {
return this.wrappedInjector.get(WrappedColumn, this.vcr).columnView;
}
ngOnInit() {
this.wrappedInjector = new HostWrapper(WrappedColumn, this.vcr);
}
ngOnChanges(changes) {
if (changes.colType &&
changes.colType.currentValue &&
changes.colType.currentValue !== changes.colType.previousValue) {
if (!this.customFilter && !this.filter && this.colType && this.field) {
this.setupDefaultFilter(this.field, this.colType);
}
}
if (changes.field && changes.field.currentValue && changes.field.currentValue !== changes.field.previousValue) {
if (!this.customFilter && this.colType) {
this.setupDefaultFilter(this.field, this.colType);
}
}
}
ngOnDestroy() {
super.ngOnDestroy();
this.subscriptions.forEach(s => s.unsubscribe());
}
/**
* Sorts the datagrid based on this column
*/
sort(reverse) {
if (!this.sortable) {
return;
}
this._sort.toggle(this._sortBy, reverse);
// setting the private variable to not retrigger the setter logic
this._sortOrder = this._sort.reverse ? ClrDatagridSortOrder.DESC : ClrDatagridSortOrder.ASC;
// Sets the correct icon for current sort order
this._sortDirection = this._sortOrder === ClrDatagridSortOrder.DESC ? 'down' : 'up';
this.sortOrderChange.emit(this._sortOrder);
}
listenForDetailPaneChanges() {
return this.detailService.stateChange.subscribe(state => {
if (this.showSeparator !== !state) {
this.showSeparator = !state;
// Have to manually change because of OnPush
this.changeDetectorRef.markForCheck();
}
});
}
listenForSortingChanges() {
return this._sort.change.subscribe(sort => {
// Need to manually mark the component to be checked
// for both activating and deactivating sorting
this.changeDetectorRef.markForCheck();
// We're only listening to make sure we emit an event when the column goes from sorted to unsorted
if (this.sortOrder !== ClrDatagridSortOrder.UNSORTED && sort.comparator !== this._sortBy) {
this._sortOrder = ClrDatagridSortOrder.UNSORTED;
this.sortOrderChange.emit(this._sortOrder);
this._sortDirection = null;
}
});
}
setupDefaultFilter(field, colType) {
if (colType === 'number') {
this.setFilter(new DatagridNumericFilterImpl(new DatagridPropertyNumericFilter(field)));
}
else if (colType === 'string') {
this.setFilter(new DatagridStringFilterImpl(new DatagridPropertyStringFilter(field)));
}
if (this.filter && this.initFilterValue) {
this.updateFilterValue = this.initFilterValue;
// This initFilterValue should be used only once after the filter registration
// So deleting this property value to prevent it from being used again
// if this field property is set again
delete this.initFilterValue;
}
}
}
ClrDatagridColumn.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridColumn, deps: [{ token: Sort }, { token: FiltersProvider }, { token: i0.ViewContainerRef }, { token: DetailService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridColumn.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridColumn, selector: "clr-dg-column", inputs: { filterStringPlaceholder: ["clrFilterStringPlaceholder", "filterStringPlaceholder"], filterNumberMaxPlaceholder: ["clrFilterNumberMaxPlaceholder", "filterNumberMaxPlaceholder"], filterNumberMinPlaceholder: ["clrFilterNumberMinPlaceholder", "filterNumberMinPlaceholder"], colType: ["clrDgColType", "colType"], field: ["clrDgField", "field"], sortBy: ["clrDgSortBy", "sortBy"], sortOrder: ["clrDgSortOrder", "sortOrder"], updateFilterValue: ["clrFilterValue", "updateFilterValue"] }, outputs: { sortOrderChange: "clrDgSortOrderChange", filterValueChange: "clrFilterValueChange" }, host: { attributes: { "role": "columnheader" }, properties: { "class.datagrid-column": "true", "attr.aria-sort": "ariaSort" } }, queries: [{ propertyName: "projectedFilter", first: true, predicate: CustomFilter, descendants: true }], usesInheritance: true, usesOnChanges: true, hostDirectives: [{ directive: ClrPopoverHostDirective }], ngImport: i0, template: `
`,
hostDirectives: [ClrPopoverHostDirective],
host: {
'[class.datagrid-column]': 'true',
'[attr.aria-sort]': 'ariaSort',
role: 'columnheader',
},
changeDetection: ChangeDetectionStrategy.OnPush,
}]
}], ctorParameters: function () { return [{ type: Sort }, { type: FiltersProvider }, { type: i0.ViewContainerRef }, { type: DetailService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { filterStringPlaceholder: [{
type: Input,
args: ['clrFilterStringPlaceholder']
}], filterNumberMaxPlaceholder: [{
type: Input,
args: ['clrFilterNumberMaxPlaceholder']
}], filterNumberMinPlaceholder: [{
type: Input,
args: ['clrFilterNumberMinPlaceholder']
}], sortOrderChange: [{
type: Output,
args: ['clrDgSortOrderChange']
}], filterValueChange: [{
type: Output,
args: ['clrFilterValueChange']
}], colType: [{
type: Input,
args: ['clrDgColType']
}], field: [{
type: Input,
args: ['clrDgField']
}], sortBy: [{
type: Input,
args: ['clrDgSortBy']
}], sortOrder: [{
type: Input,
args: ['clrDgSortOrder']
}], updateFilterValue: [{
type: Input,
args: ['clrFilterValue']
}], projectedFilter: [{
type: ContentChild,
args: [CustomFilter]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class Items {
constructor(_filters, _sort, _page) {
this._filters = _filters;
this._sort = _sort;
this._page = _page;
/**
* Indicates if the data is currently loading
*/
this.loading = false;
/**
* Whether we should use smart items for this datagrid or let the user handle
* everything.
*/
this._smart = false;
/**
* List of items currently displayed
*/
this._displayed = [];
/**
* The Observable that lets other classes subscribe to items changes
*/
this._change = new Subject();
this._allChanges = new Subject();
/**
* Tracking function to identify objects. Default is reference equality.
*
* @deprecated in v15 and scheduled for removal in v17 (CDE-71)
*/
this.iteratorTrackBy = (_index, item) => item;
}
get smart() {
return this._smart;
}
get all() {
return this._all;
}
set all(items) {
this._all = items;
this.emitAllChanges(items);
if (this.smart) {
this._filterItems();
}
else {
this._displayed = items;
this.emitChange();
}
}
get displayed() {
// Ideally we could return an immutable array, but we don't have it in Clarity yet.
return this._displayed;
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get change() {
return this._change.asObservable();
}
get allChanges() {
return this._allChanges.asObservable();
}
/**
* Checks if we don't have data to process yet, to abort early operations
*/
get uninitialized() {
return !this._all;
}
/**
* Cleans up our subscriptions to other providers
*/
destroy() {
if (this._filtersSub) {
this._filtersSub.unsubscribe();
}
if (this._sortSub) {
this._sortSub.unsubscribe();
}
if (this._pageSub) {
this._pageSub.unsubscribe();
}
}
smartenUp() {
this._smart = true;
/*
* These observers trigger a chain of function: filter -> sort -> paginate
* An observer up the chain re-triggers all the operations that follow it.
*/
this._filtersSub = this._filters.change.subscribe(() => this._filterItems());
this._sortSub = this._sort.change.subscribe(() => {
// Special case, if the datagrid went from sorted to unsorted, we have to re-filter
// to get the original order back
if (!this._sort.comparator) {
this._filterItems();
}
else {
this._sortItems();
}
});
this._pageSub = this._page.change.subscribe(() => this._changePage());
}
/**
* Manually recompute the list of displayed items
*/
refresh() {
if (this.smart) {
this._filterItems();
}
}
canTrackBy() {
// all items are needed unless `datagridTrackBy` is set because `iteratorTrackBy` requires the item's index
return !!this.datagridTrackBy || Array.isArray(this.all);
}
trackBy(item, index) {
if (this.datagridTrackBy) {
return this.datagridTrackBy(item);
}
else if (Array.isArray(this.all)) {
index = index ?? this.all.indexOf(item);
return this.iteratorTrackBy(index, item);
}
else {
throw new Error('improper call to Items#trackBy');
}
}
emitChange() {
this._change.next(this.displayed);
}
emitAllChanges(items) {
this._allChanges.next(items);
}
/**
* FiltersProvider items from the raw list
*/
_filterItems() {
if (this.uninitialized) {
return;
}
if (this._filters.hasActiveFilters()) {
this._filtered = this._all.filter(item => this._filters.accepts(item));
}
else {
// Work on a shallow copy of the array, to not modify the user's model
this._filtered = this._all.slice();
}
this._page.totalItems = this._filtered.length;
this._sortItems();
}
/**
* Sorts items in the filtered list
*/
_sortItems() {
if (this.uninitialized) {
return;
}
if (this._sort.comparator) {
this._filtered.sort((a, b) => this._sort.compare(a, b));
}
this._changePage();
}
/**
* Extracts the current page from the sorted list
*/
_changePage() {
// If we know we have pagination but the page size hasn't been set yet, we wait for it.
if (this.uninitialized || (this._page.activated && this._page.size === 0)) {
return;
}
if (this._page.size > 0) {
this._displayed = this._filtered.slice(this._page.firstItem, this._page.lastItem + 1);
}
else {
this._displayed = this._filtered;
}
this.emitChange();
}
}
Items.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Items, deps: [{ token: FiltersProvider }, { token: Sort }, { token: Page }], target: i0.ɵɵFactoryTarget.Injectable });
Items.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Items });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Items, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: FiltersProvider }, { type: Sort }, { type: Page }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridItems {
constructor(template, differs, items, vcr) {
this.template = template;
this.differs = differs;
this.items = items;
this.vcr = vcr;
this.differ = null;
this.subscriptions = [];
items.smartenUp();
this.iterableProxy = new NgForOf(this.vcr, this.template, this.differs);
this.subscriptions.push(items.change.subscribe(newItems => {
this.iterableProxy.ngForOf = newItems;
this.iterableProxy.ngDoCheck();
}));
}
set rawItems(items) {
this._rawItems = items ? items : []; // local copy for ngOnChange diffing
}
set trackBy(value) {
this.items.iteratorTrackBy = value;
this.iterableProxy.ngForTrackBy = value;
}
/**
* Asserts the correct type of the template context that the directive will render.
* See https://angular.io/guide/structural-directives#typing-the-directives-context
*
* The presence of this method is a signal to the Ivy template type-check compiler that the
* structural directive renders its template with a specific context type.
*/
static ngTemplateContextGuard(_dir, _ctx) {
return true;
}
ngDoCheck() {
if (!this.differ) {
this.differ = this.differs.find(this._rawItems).create(this.iterableProxy.ngForTrackBy);
}
if (this.differ) {
const changes = this.differ.diff(this._rawItems);
if (changes) {
// TODO: not very efficient right now,
// but premature optimization is the root of all evil.
this.items.all = this._rawItems;
}
}
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
}
ClrDatagridItems.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridItems, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }, { token: Items }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrDatagridItems.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridItems, selector: "[clrDgItems][clrDgItemsOf]", inputs: { rawItems: ["clrDgItemsOf", "rawItems"], trackBy: ["clrDgItemsTrackBy", "trackBy"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridItems, decorators: [{
type: Directive,
args: [{
selector: '[clrDgItems][clrDgItemsOf]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.IterableDiffers }, { type: Items }, { type: i0.ViewContainerRef }]; }, propDecorators: { rawItems: [{
type: Input,
args: ['clrDgItemsOf']
}], trackBy: [{
type: Input,
args: ['clrDgItemsTrackBy']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridPlaceholder {
constructor(items) {
this.items = items;
}
/**
* Tests if the datagrid is empty, meaning it doesn't contain any items
*/
get emptyDatagrid() {
return !this.items.loading && (!this.items.displayed || this.items.displayed.length === 0);
}
}
ClrDatagridPlaceholder.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridPlaceholder, deps: [{ token: Items }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridPlaceholder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridPlaceholder, selector: "clr-dg-placeholder", host: { properties: { "class.datagrid-placeholder-container": "true" } }, ngImport: i0, template: `
`,
host: { '[class.datagrid-placeholder-container]': 'true' },
}]
}], ctorParameters: function () { return [{ type: Items }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class SignpostFocusManager {
set triggerEl(value) {
this._triggerEl = value;
}
focusTrigger() {
if (this._triggerEl) {
this._triggerEl.focus();
}
}
}
SignpostFocusManager.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: SignpostFocusManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
SignpostFocusManager.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: SignpostFocusManager });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: SignpostFocusManager, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class SignpostIdService {
constructor() {
this._id = new Subject();
}
get id() {
return this._id.asObservable();
}
setId(id) {
this._id.next(id);
}
}
SignpostIdService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: SignpostIdService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
SignpostIdService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: SignpostIdService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: SignpostIdService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*********
*
* @description
* A Directive added to the ClrSignpost Trigger button that will call the ClrSignpost.toggle() function to hide/show the
* ClrSignpostContent.
*
*/
class ClrSignpostTrigger {
constructor(toggleService, el, commonStrings, signpostIdService, signpostFocusManager, document, platformId) {
this.toggleService = toggleService;
this.el = el;
this.commonStrings = commonStrings;
this.signpostIdService = signpostIdService;
this.signpostFocusManager = signpostFocusManager;
this.platformId = platformId;
this.ariaExpanded = false;
this.subscriptions = [];
this.document = document;
}
ngOnInit() {
this.signpostFocusManager.triggerEl = this.el.nativeElement;
this.subscriptions.push(this.toggleService.openChange.subscribe((isOpen) => {
this.ariaExpanded = isOpen;
const prevIsOpen = this.isOpen;
this.isOpen = isOpen;
// openChange fires false on initialization because signpost starts as closed by default
// but we shouldn't focus on that initial false value
// we should focus back only if it's closed after being opened
if (!this.isOpen && prevIsOpen) {
this.focusOnClose();
}
}), this.signpostIdService.id.subscribe(idChange => (this.ariaControl = idChange)));
this.addDefaultAriaLabel(this.el.nativeElement);
}
ngOnDestroy() {
this.subscriptions.forEach((sub) => sub.unsubscribe());
}
/**********
*
* @description
* click handler for the ClrSignpost trigger button used to hide/show ClrSignpostContent.
*/
onSignpostTriggerClick(event) {
this.toggleService.toggleWithEvent(event);
}
addDefaultAriaLabel(el) {
if (!el.hasAttribute('aria-label')) {
el.setAttribute('aria-label', this.commonStrings.keys.signpostToggle);
}
}
focusOnClose() {
if (!isPlatformBrowser(this.platformId)) {
return;
}
// we have to set the focus back on the trigger only if the focus is reset back to the body element
// if the focus is on another element, we are not allowed to move that focus back to this trigger again.
if (!this.isOpen && this.document.activeElement === this.document.body) {
this.signpostFocusManager.focusTrigger();
}
}
}
ClrSignpostTrigger.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrSignpostTrigger, deps: [{ token: ClrPopoverToggleService }, { token: i0.ElementRef }, { token: ClrCommonStringsService }, { token: SignpostIdService }, { token: SignpostFocusManager }, { token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Directive });
ClrSignpostTrigger.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrSignpostTrigger, selector: "[clrSignpostTrigger]", host: { listeners: { "click": "onSignpostTriggerClick($event)" }, properties: { "attr.aria-expanded": "ariaExpanded", "attr.aria-controls": "ariaControl", "class.active": "isOpen" }, classAttribute: "signpost-trigger" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrSignpostTrigger, decorators: [{
type: Directive,
args: [{
selector: '[clrSignpostTrigger]',
host: {
class: 'signpost-trigger',
'[attr.aria-expanded]': 'ariaExpanded',
'[attr.aria-controls]': 'ariaControl',
'[class.active]': 'isOpen',
},
}]
}], ctorParameters: function () { return [{ type: ClrPopoverToggleService }, { type: i0.ElementRef }, { type: ClrCommonStringsService }, { type: SignpostIdService }, { type: SignpostFocusManager }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; }, propDecorators: { onSignpostTriggerClick: [{
type: HostListener,
args: ['click', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*********
*
* @class ClrSignpost
*
* @description
* Class used to configure and control the state of a ClrSignpost and its associated ClrSignpostContent.
* It supports the clrPosition with a 'right-middle' default.
*
*/
class ClrSignpost {
constructor(commonStrings) {
this.commonStrings = commonStrings;
/**********
* @property useCustomTrigger
*
* @description
* Flag used to determine if we need to use the default trigger or a user supplied trigger element.
*
*/
this.useCustomTrigger = false;
}
/**********
* @property signPostTrigger
*
* @description
* Uses ContentChild to check for a user supplied element with the ClrSignpostTrigger on it.
*
*/
set customTrigger(trigger) {
this.useCustomTrigger = !!trigger;
}
}
ClrSignpost.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrSignpost, deps: [{ token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrSignpost.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrSignpost, selector: "clr-signpost", host: { properties: { "class.signpost": "true" } }, providers: [SignpostFocusManager, SignpostIdService], queries: [{ propertyName: "customTrigger", first: true, predicate: ClrSignpostTrigger, descendants: true }], hostDirectives: [{ directive: ClrPopoverHostDirective }], ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: CdsIconCustomTag, selector: "cds-icon" }, { kind: "directive", type: ClrSignpostTrigger, selector: "[clrSignpostTrigger]" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrSignpost, decorators: [{
type: Component,
args: [{
selector: 'clr-signpost',
template: `
`,
host: { '[class.signpost]': 'true' },
providers: [SignpostFocusManager, SignpostIdService],
hostDirectives: [ClrPopoverHostDirective],
}]
}], ctorParameters: function () { return [{ type: ClrCommonStringsService }]; }, propDecorators: { customTrigger: [{
type: ContentChild,
args: [ClrSignpostTrigger]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class WrappedCell {
constructor() {
this._dynamic = false;
}
ngAfterViewInit() {
this.cellView = this.templateRef.createEmbeddedView(null);
}
ngOnDestroy() {
this.cellView.destroy();
}
}
WrappedCell.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WrappedCell, deps: [], target: i0.ɵɵFactoryTarget.Component });
WrappedCell.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: WrappedCell, selector: "dg-wrapped-cell", viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["cellPortal"], descendants: true }], ngImport: i0, template: `
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WrappedCell, decorators: [{
type: Component,
args: [{
selector: 'dg-wrapped-cell',
template: `
`,
}]
}], propDecorators: { templateRef: [{
type: ViewChild,
args: ['cellPortal']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridCell {
constructor(vcr) {
this.vcr = vcr;
}
get _view() {
return this.wrappedInjector.get(WrappedCell, this.vcr).cellView;
}
ngOnInit() {
this.wrappedInjector = new HostWrapper(WrappedCell, this.vcr);
}
}
ClrDatagridCell.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridCell, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridCell.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridCell, selector: "clr-dg-cell", host: { attributes: { "role": "gridcell" }, properties: { "class.datagrid-cell": "true", "class.datagrid-signpost-trigger": "signpost.length > 0" } }, queries: [{ propertyName: "signpost", predicate: ClrSignpost }], ngImport: i0, template: ``, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridCell, decorators: [{
type: Component,
args: [{
selector: 'clr-dg-cell',
template: ``,
host: {
'[class.datagrid-cell]': 'true',
'[class.datagrid-signpost-trigger]': 'signpost.length > 0',
role: 'gridcell',
},
}]
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }]; }, propDecorators: { signpost: [{
type: ContentChildren,
args: [ClrSignpost]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let nbRow$1 = 0;
class DatagridIfExpandService extends IfExpandService {
constructor() {
super();
this.expandableId = '';
this._replace = new BehaviorSubject(false);
this._animate = new Subject();
nbRow$1++;
this.expandableId = 'clr-dg-expandable-row-' + nbRow$1;
}
// due to the es5 spec if the set is overridden on base class the getter must also be overridden
get expanded() {
return this._expanded;
}
set expanded(value) {
value = !!value;
if (value !== this._expanded) {
this._expanded = value;
this._animate.next();
this._expandChange.next(value);
}
}
get replace() {
return this._replace.asObservable();
}
get animate() {
return this._animate.asObservable();
}
loadingStateChange(state) {
super.loadingStateChange(state);
if (state !== ClrLoadingState.LOADING) {
this._animate.next();
}
}
setReplace(replaceValue) {
this._replace.next(replaceValue);
}
}
DatagridIfExpandService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridIfExpandService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
DatagridIfExpandService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridIfExpandService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridIfExpandService, decorators: [{
type: Injectable
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var DatagridDisplayMode;
(function (DatagridDisplayMode) {
DatagridDisplayMode[DatagridDisplayMode["DISPLAY"] = 0] = "DISPLAY";
DatagridDisplayMode[DatagridDisplayMode["CALCULATE"] = 1] = "CALCULATE";
})(DatagridDisplayMode || (DatagridDisplayMode = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
var SelectionType;
(function (SelectionType) {
SelectionType[SelectionType["None"] = 0] = "None";
SelectionType[SelectionType["Single"] = 1] = "Single";
SelectionType[SelectionType["Multi"] = 2] = "Multi";
})(SelectionType || (SelectionType = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class WrappedRow {
constructor() {
this._dynamic = false;
}
ngAfterViewInit() {
// Create the cells view in memory, not the DOM.
this.rowView = this.templateRef.createEmbeddedView(null);
}
ngOnDestroy() {
this.rowView.destroy();
}
}
WrappedRow.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WrappedRow, deps: [], target: i0.ɵɵFactoryTarget.Component });
WrappedRow.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: WrappedRow, selector: "dg-wrapped-row", viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["rowPortal"], descendants: true }], ngImport: i0, template: `
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WrappedRow, decorators: [{
type: Component,
args: [{
selector: 'dg-wrapped-row',
template: `
`,
}]
}], propDecorators: { templateRef: [{
type: ViewChild,
args: ['rowPortal']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let nbSelection = 0;
class Selection {
constructor(_items, _filters) {
this._items = _items;
this._filters = _filters;
this.preserveSelection = false;
/**
* Shift key state, for use in range selection.
*/
this.shiftPressed = false;
/** @deprecated since 2.0, remove in 3.0 */
this.rowSelectionMode = false;
this.prevSelectionRefs = []; // Refs of selected items
this.lockedRefs = []; // Ref of locked items
this.valueCollector = new Subject();
this._selectionType = SelectionType.None;
/**
* The Observable that lets other classes subscribe to selection changes
*/
this._change = new Subject();
/**
* Subscriptions to the other providers changes.
*/
this.subscriptions = [];
this.id = 'clr-dg-selection' + nbSelection++;
this.subscriptions.push(this._filters.change.subscribe(() => {
if (!this._selectable || this.preserveSelection) {
return;
}
this.clearSelection();
}));
this.subscriptions.push(this._items.allChanges.subscribe(updatedItems => {
// Reset the lockedRefs;
const updateLockedRef = [];
switch (this.selectionType) {
case SelectionType.None: {
break;
}
case SelectionType.Single: {
let newSingle;
let selectionUpdated = false;
// if the currentSingle has been set before data was loaded, we look up and save the ref from current data set
if (this.currentSingle && !this.prevSingleSelectionRef) {
if (this._items.canTrackBy()) {
this.prevSingleSelectionRef = this._items.trackBy(this.currentSingle);
}
}
updatedItems.forEach((item, index) => {
const ref = this._items.trackBy(item, index);
// If one of the updated items is the previously selectedSingle, set it as the new one
if (this.prevSingleSelectionRef === ref) {
newSingle = item;
selectionUpdated = true;
}
if (this.lockedRefs.indexOf(ref) > -1) {
updateLockedRef.push(ref);
}
});
// If we're using smart datagrids, we expect all items to be present in the updatedItems array.
// Therefore, we should delete the currentSingle if it used to be defined but doesn't exist anymore.
// No explicit "delete" is required, since newSingle would be undefined at this point.
// Marking it as selectionUpdated here will set currentSingle to undefined below in the setTimeout.
if (this._items.smart && !newSingle) {
selectionUpdated = true;
}
// TODO: Discussed this with Eudes and this is fine for now.
// But we need to figure out a different pattern for the
// child triggering the parent change detection problem.
// Using setTimeout for now to fix this.
setTimeout(() => {
if (selectionUpdated) {
this.currentSingle = newSingle;
}
}, 0);
break;
}
case SelectionType.Multi: {
let leftOver = this.current.slice();
let selectionUpdated = false;
// if the current has been set before data was loaded, we look up and save the ref from current data set
if (this.current.length > 0 && this.prevSelectionRefs.length !== this.current.length) {
if (this._items.canTrackBy()) {
this.prevSelectionRefs = [];
this.current.forEach(item => {
this.prevSelectionRefs.push(this._items.trackBy(item));
});
}
}
// Duplicate loop, when the issue is issue#2342 is revisited keep in mind that
// we need to go over every updated item and check to see if there are valid to be
// locked or not and update it. When only add items that are found in the lockedRefs back.
//
// The both loops below that goes over updatedItems could be combined into one.
updatedItems.forEach((item, index) => {
const ref = this._items.trackBy(item, index);
if (this.lockedRefs.indexOf(ref) > -1) {
updateLockedRef.push(ref);
}
});
// TODO: revisit this when we work on https://github.com/vmware/clarity/issues/2342
// currently, the selection is cleared when filter is applied, so the logic inside
// the if statement below results in broken behavior.
if (leftOver.length > 0) {
updatedItems.forEach((item, index) => {
const ref = this._items.trackBy(item, index);
// Look in current selected refs array if item is selected, and update actual value
const selectedIndex = this.prevSelectionRefs.indexOf(ref);
if (selectedIndex > -1) {
leftOver[selectedIndex] = item;
selectionUpdated = true;
}
});
// Filter out any unmatched items if we're using smart datagrids where we expect all items to be
// present
if (this._items.smart) {
leftOver = leftOver.filter(selected => updatedItems.indexOf(selected) > -1);
if (this.current.length !== leftOver.length) {
selectionUpdated = true;
}
}
// TODO: Discussed this with Eudes and this is fine for now.
// But we need to figure out a different pattern for the
// child triggering the parent change detection problem.
// Using setTimeout for now to fix this.
setTimeout(() => {
if (selectionUpdated) {
this.current = leftOver;
}
}, 0);
}
break;
}
default: {
break;
}
}
// Sync locked items
this.lockedRefs = updateLockedRef;
}));
this.subscriptions.push(this.valueCollector.pipe(debounceTime(0)).subscribe(() => this.emitChange()));
}
get selectionType() {
return this._selectionType;
}
set selectionType(value) {
if (value === this.selectionType) {
return;
}
this._selectionType = value;
if (value === SelectionType.None) {
delete this.current;
}
else {
this.updateCurrent([], false);
}
}
get current() {
return this._current;
}
set current(value) {
this.updateCurrent(value, true);
}
get currentSingle() {
return this._currentSingle;
}
set currentSingle(value) {
if (value === this._currentSingle) {
return;
}
this._currentSingle = value;
if (this._items.canTrackBy() && value) {
this.prevSingleSelectionRef = this._items.trackBy(value);
}
this.emitChange();
}
// We do not want to expose the Subject itself, but the Observable which is read-only
get change() {
return this._change.asObservable();
}
get _selectable() {
return this._selectionType === SelectionType.Multi || this._selectionType === SelectionType.Single;
}
clearSelection() {
this._current = [];
this.prevSelectionRefs = [];
this.prevSingleSelectionRef = null;
this._currentSingle = null;
this.emitChange();
}
/**
* Cleans up our subscriptions to other providers
*/
destroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
updateCurrent(value, emit) {
this._current = value;
if (emit) {
this.valueCollector.next(value);
}
}
/**
* Checks if an item is currently selected
*/
isSelected(item) {
if (this._selectionType === SelectionType.Single) {
return this.currentSingle === item;
}
else if (this._selectionType === SelectionType.Multi) {
return this.current.indexOf(item) >= 0;
}
return false;
}
/**
* Selects or deselects an item
*/
setSelected(item, selected) {
const index = this.current ? this.current.indexOf(item) : -1;
switch (this._selectionType) {
case SelectionType.None:
break;
case SelectionType.Single:
// in single selection, set currentSingle method should be used
break;
case SelectionType.Multi:
if (index >= 0 && !selected) {
this.deselectItem(index);
}
else if (index < 0 && selected) {
this.selectItem(item);
}
break;
default:
break;
}
}
/**
* Checks if all currently displayed items are selected
*/
isAllSelected() {
if (this._selectionType !== SelectionType.Multi || !this._items.displayed) {
return false;
}
// make sure to exclude the locked items from the list when counting
const displayedItems = this._items.displayed.filter(item => {
return this.isLocked(item) === false;
});
const nbDisplayed = displayedItems.length;
if (nbDisplayed < 1) {
return false;
}
const temp = displayedItems.filter(item => this.current.indexOf(item) > -1);
return temp.length === displayedItems.length;
}
/**
* Lock and unlock item
*/
lockItem(item, lock) {
if (this.canItBeLocked()) {
const ref = this._items.trackBy(item);
if (lock === true) {
// Add to lockedRef
this.lockedRefs.push(ref);
}
else {
// Remove from lockedRef
this.lockedRefs = this.lockedRefs.filter(lockedItem => ref !== lockedItem);
}
}
}
/**
* Check is item locked or not by searching into lockedRefs for entry
*/
isLocked(item) {
/**
* The check for selectionType will boost the performance by NOT searching
* into the array when there is no need for that.
*/
if (this.canItBeLocked()) {
const ref = this._items.trackBy(item);
return this.lockedRefs.indexOf(ref) > -1;
}
return false;
}
/**
* Selects or deselects all currently displayed items
*/
toggleAll() {
if (this._selectionType === SelectionType.None || this._selectionType === SelectionType.Single) {
return;
}
/**
* If every currently displayed item is already selected, we clear them.
* If at least one item isn't selected, we select every currently displayed item.
*/
if (this.isAllSelected()) {
this._items.displayed.forEach(item => {
const currentIndex = this.current.indexOf(item);
if (currentIndex > -1 && this.isLocked(item) === false) {
this.deselectItem(currentIndex);
}
});
}
else {
this._items.displayed.forEach(item => {
if (this.current.indexOf(item) < 0 && this.isLocked(item) === false) {
this.selectItem(item);
}
});
}
}
/**
* Selects an item
*/
selectItem(item) {
this.current = this.current.concat(item);
if (this._items.canTrackBy()) {
// Push selected ref onto array
this.prevSelectionRefs.push(this._items.trackBy(item));
}
}
/**
* Deselects an item
*/
deselectItem(indexOfItem) {
this.current = this.current.slice(0, indexOfItem).concat(this.current.slice(indexOfItem + 1));
if (indexOfItem < this.prevSelectionRefs.length) {
// Keep selected refs array in sync
const removedItems = this.prevSelectionRefs.splice(indexOfItem, 1);
// locked reference is no longer needed (if any)
this.lockedRefs = this.lockedRefs.filter(locked => locked !== removedItems[0]);
}
}
/**
* Make sure that it could be locked
*/
canItBeLocked() {
return this._selectionType !== SelectionType.None && this._items.canTrackBy();
}
emitChange() {
if (this._selectionType === SelectionType.Single) {
this._change.next(this.currentSingle);
}
else if (this._selectionType === SelectionType.Multi) {
this._change.next(this.current);
}
}
}
Selection.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Selection, deps: [{ token: Items }, { token: FiltersProvider }], target: i0.ɵɵFactoryTarget.Injectable });
Selection.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Selection });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: Selection, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: Items }, { type: FiltersProvider }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class DisplayModeService {
constructor(renderOrganizer) {
this._view = new BehaviorSubject(DatagridDisplayMode.DISPLAY);
this.subscriptions = [];
this.subscriptions.push(renderOrganizer
.filterRenderSteps(DatagridRenderStep.CALCULATE_MODE_ON)
.subscribe(() => this._view.next(DatagridDisplayMode.CALCULATE)));
this.subscriptions.push(renderOrganizer
.filterRenderSteps(DatagridRenderStep.CALCULATE_MODE_OFF)
.subscribe(() => this._view.next(DatagridDisplayMode.DISPLAY)));
}
get view() {
return this._view.asObservable();
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
}
DisplayModeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DisplayModeService, deps: [{ token: DatagridRenderOrganizer }], target: i0.ɵɵFactoryTarget.Injectable });
DisplayModeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DisplayModeService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DisplayModeService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: DatagridRenderOrganizer }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridSelectionCellDirective {
constructor(selection) {
this.selection = selection;
}
onSelectionCellClick(event) {
// We want to effectively expand the selection click target to the entire selection cell.
// If row selection is enabled, do nothing because the entire selection cell is already clickable.
if (this.selection.rowSelectionMode) {
return;
}
// If click was outside the label/input, forward the click to the input.
if (event.target.tagName !== 'LABEL' && event.target.tagName !== 'INPUT') {
event.target.querySelector('input').click();
}
}
}
ClrDatagridSelectionCellDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridSelectionCellDirective, deps: [{ token: Selection }], target: i0.ɵɵFactoryTarget.Directive });
ClrDatagridSelectionCellDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridSelectionCellDirective, selector: ".datagrid-select", host: { listeners: { "click": "onSelectionCellClick($event)" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridSelectionCellDirective, decorators: [{
type: Directive,
args: [{
selector: '.datagrid-select',
}]
}], ctorParameters: function () { return [{ type: Selection }]; }, propDecorators: { onSelectionCellClick: [{
type: HostListener,
args: ['click', ['$event']]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let nbRow = 0;
class ClrDatagridRow {
constructor(selection, rowActionService, globalExpandable, expand, detailService, displayMode, vcr, renderer, el, commonStrings, items, document) {
this.selection = selection;
this.rowActionService = rowActionService;
this.globalExpandable = globalExpandable;
this.expand = expand;
this.detailService = detailService;
this.displayMode = displayMode;
this.vcr = vcr;
this.renderer = renderer;
this.el = el;
this.commonStrings = commonStrings;
this.items = items;
this.document = document;
this.selectedChanged = new EventEmitter(false);
this.expandedChange = new EventEmitter(false);
this.displayCells = false;
this.expandAnimationTrigger = false;
/* reference to the enum so that template can access */
this.SELECTION_TYPE = SelectionType;
/**
* @internal
*/
this.itemChanges = new ReplaySubject(1);
this._selected = false;
this._detailOpenLabel = '';
this._detailCloseLabel = '';
this._rowAriaLabel = '';
this.subscriptions = [];
// By default, every item is selectable; it becomes not selectable only if it's explicitly set to false
this._selectable = true;
nbRow++;
this.id = 'clr-dg-row' + nbRow;
this.radioId = 'clr-dg-row-rd' + nbRow;
this.checkboxId = 'clr-dg-row-cb' + nbRow;
this.expandableId = expand.expandableId;
this.subscriptions.push(combineLatest(this.expand.replace, this.expand.expandChange).subscribe(([expandReplaceValue, expandChangeValue]) => {
if (expandReplaceValue && expandChangeValue) {
// replaced and expanding
this.replaced = true;
this.renderer.addClass(this.el.nativeElement, 'datagrid-row-replaced');
}
else {
this.replaced = false;
// Handles these cases: not replaced and collapsing & replaced and
// collapsing and not replaced and expanding.
this.renderer.removeClass(this.el.nativeElement, 'datagrid-row-replaced');
}
}));
}
/**
* Model of the row, to use for selection
*/
get item() {
return this._item;
}
set item(item) {
this._item = item;
this.itemChanges.next(item);
this.clrDgSelectable = this._selectable;
}
get clrDgSelectable() {
return !this.selection.isLocked(this.item);
}
set clrDgSelectable(value) {
if (this.item) {
this.selection.lockItem(this.item, value === 'false' || value === false);
}
// Store this value locally, to be initialized when item is initialized
this._selectable = value;
}
/**
* Indicates if the row is selected
*/
get selected() {
if (this.selection.selectionType === SelectionType.None) {
return this._selected;
}
else {
return this.selection.isSelected(this.item);
}
}
set selected(value) {
if (this.selection.selectionType === SelectionType.None) {
this._selected = value;
}
else {
if (value && this.selection.selectionType === SelectionType.Multi) {
this.rangeSelect();
}
else {
this.selection.rangeStart = null;
}
this.selection.setSelected(this.item, value);
}
}
get expanded() {
return this.expand.expanded;
}
set expanded(value) {
this.expand.expanded = value;
}
get clrDgDetailOpenLabel() {
return this._detailOpenLabel ? this._detailOpenLabel : this.commonStrings.keys.open;
}
set clrDgDetailOpenLabel(label) {
this._detailOpenLabel = label;
}
get clrDgDetailCloseLabel() {
return this._detailCloseLabel ? this._detailCloseLabel : this.commonStrings.keys.close;
}
set clrDgDetailCloseLabel(label) {
this._detailCloseLabel = label;
}
// CDE-151: Rename this field to clrDgRowSelectionLabel in v16
get clrDgRowAriaLabel() {
return this._rowAriaLabel ? this._rowAriaLabel : this.commonStrings.keys.select;
}
set clrDgRowAriaLabel(label) {
this._rowAriaLabel = label;
}
get _view() {
return this.wrappedInjector.get(WrappedRow, this.vcr).rowView;
}
ngOnInit() {
this.wrappedInjector = new HostWrapper(WrappedRow, this.vcr);
this.selection.lockItem(this.item, this.clrDgSelectable === false);
}
ngAfterContentInit() {
this.dgCells.changes.subscribe(() => {
this.dgCells.forEach(cell => {
if (!cell._view.destroyed) {
this._scrollableCells.insert(cell._view);
}
});
});
}
ngAfterViewInit() {
this.subscriptions.push(this.displayMode.view.subscribe(viewChange => {
// Listen for view changes and move cells around depending on the current displayType
// remove cell views from display view
for (let i = this._scrollableCells.length; i > 0; i--) {
this._scrollableCells.detach();
}
// remove cell views from calculated view
for (let i = this._calculatedCells.length; i > 0; i--) {
this._calculatedCells.detach();
}
if (viewChange === DatagridDisplayMode.CALCULATE) {
this.displayCells = false;
this.dgCells.forEach(cell => {
if (!cell._view.destroyed) {
this._calculatedCells.insert(cell._view);
}
});
}
else {
this.displayCells = true;
this.dgCells.forEach(cell => {
if (!cell._view.destroyed) {
this._scrollableCells.insert(cell._view);
}
});
}
}), this.expand.animate.subscribe(() => {
this.expandAnimationTrigger = !this.expandAnimationTrigger;
}));
}
ngOnDestroy() {
this.subscriptions.forEach((sub) => sub.unsubscribe());
}
toggle(selected = !this.selected) {
if (selected !== this.selected) {
this.selected = selected;
this.selectedChanged.emit(selected);
}
}
toggleExpand() {
if (this.expand.expandable) {
this.expandAnimation.updateStartHeight();
this.expanded = !this.expanded;
this.expandedChange.emit(this.expanded);
}
}
/**
* The default behavior in Chrome and Firefox for shift-clicking on a label is to perform text-selection.
* This prevents our intended range-selection, because this text-selection overrides our shift-click event.
* We need to clear the stored selection range when shift-clicking. This will override the mostly unused shift-click
* selection browser functionality, which is inconsistently implemented in browsers anyway.
*/
clearRanges(event) {
if (event.shiftKey) {
this.document.getSelection().removeAllRanges();
// Firefox is too persistent about its text-selection behaviour. So we need to add a preventDefault();
// We should not try to enforce this on the other browsers, though, because their toggle cycle does not get canceled by
// the preventDefault() and they toggle the checkbox second time, effectively retrurning it to not-selected.
if (window.navigator.userAgent.indexOf('Firefox') !== -1) {
event.preventDefault();
this.toggle(true);
}
}
}
/**
* @deprecated related to clrDgRowSelection, which is deprecated
*/
selectRow(selected = !this.selected, $event) {
// The label also captures clicks that bubble up to the row event listener, causing
// this handler to run twice. This exits early to prevent toggling the checkbox twice.
if ($event.target.tagName === 'LABEL') {
return;
}
if (this.selection.selectionType === this.SELECTION_TYPE.Single) {
this.selection.currentSingle = this.item;
}
else {
this.toggle(selected);
}
}
rangeSelect() {
const items = this.items.displayed;
if (!items) {
return;
}
const startIx = items.indexOf(this.selection.rangeStart);
if (this.selection.rangeStart &&
this.selection.current.includes(this.selection.rangeStart) &&
this.selection.shiftPressed &&
startIx !== -1) {
const endIx = items.indexOf(this.item);
// Using Set to remove duplicates
const newSelection = new Set(this.selection.current.concat(items.slice(Math.min(startIx, endIx), Math.max(startIx, endIx) + 1)));
this.selection.clearSelection();
this.selection.current.push(...newSelection);
}
else {
// page number has changed or
// no Shift was pressed or
// rangeStart not yet set
this.selection.rangeStart = this.item;
}
}
}
ClrDatagridRow.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridRow, deps: [{ token: Selection }, { token: RowActionService }, { token: ExpandableRowsCount }, { token: DatagridIfExpandService }, { token: DetailService }, { token: DisplayModeService }, { token: i0.ViewContainerRef }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: ClrCommonStringsService }, { token: Items }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridRow.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridRow, selector: "clr-dg-row", inputs: { item: ["clrDgItem", "item"], clrDgSelectable: "clrDgSelectable", selected: ["clrDgSelected", "selected"], expanded: ["clrDgExpanded", "expanded"], clrDgDetailOpenLabel: "clrDgDetailOpenLabel", clrDgDetailCloseLabel: "clrDgDetailCloseLabel", clrDgRowAriaLabel: "clrDgRowAriaLabel" }, outputs: { selectedChanged: "clrDgSelectedChange", expandedChange: "clrDgExpandedChange" }, host: { attributes: { "role": "rowgroup" }, properties: { "class.datagrid-row": "true", "class.datagrid-selected": "selected", "attr.aria-owns": "id" } }, providers: [
DatagridIfExpandService,
{ provide: IfExpandService, useExisting: DatagridIfExpandService },
{ provide: LoadingListener, useExisting: DatagridIfExpandService },
], queries: [{ propertyName: "dgCells", predicate: ClrDatagridCell }], viewQueries: [{ propertyName: "expandAnimation", first: true, predicate: ClrExpandableAnimation, descendants: true }, { propertyName: "detailButton", first: true, predicate: ["detailButton"], descendants: true }, { propertyName: "_stickyCells", first: true, predicate: ["stickyCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_scrollableCells", first: true, predicate: ["scrollableCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_calculatedCells", first: true, predicate: ["calculatedCells"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "\n
`,
}]
}], ctorParameters: function () { return [{ type: DetailService }, { type: ClrCommonStringsService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridDetail {
constructor(detailService, commonStrings) {
this.detailService = detailService;
this.commonStrings = commonStrings;
}
close() {
this.detailService.close();
}
}
ClrDatagridDetail.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridDetail, deps: [{ token: DetailService }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridDetail.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridDetail, selector: "clr-dg-detail", host: { properties: { "class.datagrid-detail-pane": "true" } }, queries: [{ propertyName: "header", first: true, predicate: ClrDatagridDetailHeader, descendants: true }], ngImport: i0, template: `
{{ commonStrings.keys.detailPaneStart }}
{{ commonStrings.keys.detailPaneEnd }}
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: CdkTrapFocusModule_CdkTrapFocus, selector: "[cdkTrapFocus]" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridDetail, decorators: [{
type: Component,
args: [{
selector: 'clr-dg-detail',
host: {
'[class.datagrid-detail-pane]': 'true',
},
// We put the *ngIf on the cdkTrapFocus so it doesn't always exist on the page
// have to test for presence of header for aria-describedby because it was causing unit tests to crash
template: `
{{ commonStrings.keys.detailPaneStart }}
{{ commonStrings.keys.detailPaneEnd }}
`,
}]
}], ctorParameters: function () { return [{ type: DetailService }, { type: ClrCommonStringsService }]; }, propDecorators: { header: [{
type: ContentChild,
args: [ClrDatagridDetailHeader]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridDetailBody {
}
ClrDatagridDetailBody.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridDetailBody, deps: [], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridDetailBody.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridDetailBody, selector: "clr-dg-detail-body", host: { properties: { "class.datagrid-detail-body": "true" } }, ngImport: i0, template: `
`,
host: {
'[class.datagrid-detail-body]': 'true',
},
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* I don't think this deserves to be in IfExpanded itself,
* so I'm adding a second directive on the same selector for now just for the datagrid
*/
class DatagridDetailRegisterer {
constructor(expandableRowsCount) {
this.expandableRowsCount = expandableRowsCount;
if (this.expandableRowsCount) {
this.expandableRowsCount.register();
}
}
ngOnDestroy() {
if (this.expandableRowsCount) {
this.expandableRowsCount.unregister();
}
}
}
DatagridDetailRegisterer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridDetailRegisterer, deps: [{ token: ExpandableRowsCount, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
DatagridDetailRegisterer.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: DatagridDetailRegisterer, selector: "[clrIfExpanded]", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: DatagridDetailRegisterer, decorators: [{
type: Directive,
args: [{
selector: '[clrIfExpanded]',
}]
}], ctorParameters: function () { return [{ type: ExpandableRowsCount, decorators: [{
type: Optional
}] }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridFooter {
constructor(selection, detailService, columnsService, commonStrings) {
this.selection = selection;
this.detailService = detailService;
this.columnsService = columnsService;
this.commonStrings = commonStrings;
/* reference to the enum so that template can access */
this.SELECTION_TYPE = SelectionType;
}
get hasHideableColumns() {
return this.columnsService.hasHideableColumns;
}
}
ClrDatagridFooter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridFooter, deps: [{ token: Selection }, { token: DetailService }, { token: ColumnsService }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridFooter.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridFooter, selector: "clr-dg-footer", host: { properties: { "class.datagrid-footer": "true" } }, ngImport: i0, template: `
0">
`,
}]
}], ctorParameters: function () { return [{ type: Page }]; }, propDecorators: { pageSizeOptions: [{
type: Input,
args: ['clrPageSizeOptions']
}], pageSizeOptionsId: [{
type: Input,
args: ['clrPageSizeOptionsId']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrDatagridPagination {
constructor(page, commonStrings, detailService) {
this.page = page;
this.commonStrings = commonStrings;
this.detailService = detailService;
this.currentChanged = new EventEmitter(false);
this.page.activated = true;
}
/**
* Page size
*/
get pageSize() {
return this.page.size;
}
set pageSize(size) {
// todo(v16): Remove this check. The property type used to be `string | number`. I kept this check to maintain the no-op if you pass a string.
if (typeof size === 'number') {
this.page.size = size;
}
}
/**
* Total items (needed to guess the last page)
*/
get totalItems() {
return this.page.totalItems;
}
set totalItems(total) {
// todo(v16): Remove this check. The property type used to be `string | number`. I kept this check to maintain the no-op if you pass a string.
if (typeof total === 'number') {
this.page.totalItems = total;
}
}
/**
* Last page
*/
get lastPage() {
return this.page.last;
}
set lastPage(last) {
// todo(v16): Remove this check. The property type used to be `string | number`. I kept this check to maintain the no-op if you pass a string.
if (typeof last === 'number') {
this.page.last = last;
}
}
/**
* Current page
*/
get currentPage() {
return this.page.current;
}
set currentPage(page) {
// todo(v16): Remove this check. The property type used to be `string | number`. I kept this check to maintain the no-op if you pass a string.
if (typeof page === 'number') {
this.page.current = page;
}
}
/**
* Index of the first item displayed on the current page, starting at 0, -1 if none displayed
*/
get firstItem() {
return this.page.firstItem;
}
/**
* Index of the last item displayed on the current page, starting at 0, -1 if none displayed
*/
get lastItem() {
return this.page.lastItem;
}
/**
* Conditionally adds page numbers before and after the current page
*/
get middlePages() {
const middlePages = [];
if (this.page.current > 1) {
middlePages.push(this.page.current - 1);
}
middlePages.push(this.page.current);
if (this.page.current < this.page.last) {
middlePages.push(this.page.current + 1);
}
return middlePages;
}
/**********
* Subscription to the Page service for page changes.
* Note: this only emits after the datagrid is initialized/stabalized and the page changes.
*/
ngOnInit() {
/*
* Default page size is 10.
* The reason we set it here and not in the provider itself is because
* we don't want pagination if this component isn't present in the datagrid.
*/
if (!this.page.size) {
this.page.size = 10;
}
this._pageSubscription = this.page.change.subscribe(current => this.currentChanged.emit(current));
}
ngOnDestroy() {
this.page.resetPageSize(true);
if (this._pageSubscription) {
this._pageSubscription.unsubscribe();
}
}
/**
* Moves to the previous page if it exists
*/
previous() {
this.page.previous();
}
/**
* Moves to the next page if it exists
*/
next() {
this.page.next();
}
verifyCurrentPage(event) {
const parsed = parseInt(event.target.value, 10);
if (parsed !== this.page.current) {
event.target.value = this.page.current;
}
}
/**
* We only update the pagination's current page on enter.
*/
updateCurrentPage(event) {
const parsed = parseInt(event.target.value, 10);
// if the input value, is not a number, we don't update the page
if (!isNaN(parsed)) {
if (parsed < 1) {
this.page.current = 1;
}
else if (parsed > this.page.last) {
this.page.current = this.page.last;
}
else {
this.page.current = parsed;
}
}
/**
* Set the input's value to the new current page. This is needed because the code
* above may have changed the value from what the user entered in.
*/
this.currentPageInputRef.nativeElement.value = this.page.current;
}
}
ClrDatagridPagination.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrDatagridPagination, deps: [{ token: Page }, { token: ClrCommonStringsService }, { token: DetailService }], target: i0.ɵɵFactoryTarget.Component });
ClrDatagridPagination.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrDatagridPagination, selector: "clr-dg-pagination", inputs: { disableCurrentPageInput: ["clrDgPageInputDisabled", "disableCurrentPageInput"], pageSize: ["clrDgPageSize", "pageSize"], totalItems: ["clrDgTotalItems", "totalItems"], lastPage: ["clrDgLastPage", "lastPage"], currentPage: ["clrDgPage", "currentPage"] }, outputs: { currentChanged: "clrDgPageChange" }, host: { properties: { "class.pagination": "true" } }, queries: [{ propertyName: "_pageSizeComponent", first: true, predicate: ClrDatagridPageSize, descendants: true }], viewQueries: [{ propertyName: "currentPageInputRef", first: true, predicate: ["currentPageInput"], descendants: true }], ngImport: i0, template: `
`, styles: [":host{display:block}\n"] }]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrStackHeader {
constructor(stackView) {
this.stackView = stackView;
}
}
ClrStackHeader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStackHeader, deps: [{ token: ClrStackView }], target: i0.ɵɵFactoryTarget.Component });
ClrStackHeader.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrStackHeader, selector: "clr-stack-header", ngImport: i0, template: `
`, styles: [":host{display:block}\n"] }]
}], ctorParameters: function () { return [{ type: ClrStackView }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CLR_STACK_VIEW_DIRECTIVES = [
ClrStackView,
ClrStackHeader,
ClrStackBlock,
ClrStackContentInput,
ClrStackViewLabel,
ClrStackViewCustomTags,
];
class ClrStackViewModule {
constructor() {
ClarityIcons.addIcons(angleIcon);
}
}
ClrStackViewModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStackViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrStackViewModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrStackViewModule, declarations: [ClrStackView,
ClrStackHeader,
ClrStackBlock,
ClrStackContentInput,
ClrStackViewLabel,
ClrStackViewCustomTags], imports: [CommonModule, FormsModule, ClrIconModule, ClrExpandableAnimationModule], exports: [ClrStackView,
ClrStackHeader,
ClrStackBlock,
ClrStackContentInput,
ClrStackViewLabel,
ClrStackViewCustomTags] });
ClrStackViewModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStackViewModule, imports: [CommonModule, FormsModule, ClrIconModule, ClrExpandableAnimationModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrStackViewModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, FormsModule, ClrIconModule, ClrExpandableAnimationModule],
declarations: [CLR_STACK_VIEW_DIRECTIVES],
exports: [CLR_STACK_VIEW_DIRECTIVES],
}]
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class TreeFeaturesService {
constructor() {
this.selectable = false;
this.eager = true;
this.childrenFetched = new Subject();
}
}
TreeFeaturesService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TreeFeaturesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
TreeFeaturesService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TreeFeaturesService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TreeFeaturesService, decorators: [{
type: Injectable
}] });
function treeFeaturesFactory(existing) {
return existing || new TreeFeaturesService();
}
const TREE_FEATURES_PROVIDER = {
provide: TreeFeaturesService,
useFactory: treeFeaturesFactory,
/*
* The Optional + SkipSelf pattern ensures that in case of nested components, only the root one will
* instantiate a new service and all its children will reuse the root's instance.
* If there are several roots (in this case, several independent trees on a page), each root will instantiate
* its own service so they won't interfere with one another.
*
* TL;DR - Optional + SkipSelf = 1 instance of TreeFeaturesService per tree.
*/
deps: [[new Optional(), new SkipSelf(), TreeFeaturesService]],
};
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Internal component, do not export!
* This is part of the hack to get around https://github.com/angular/angular/issues/15998
*/
class RecursiveChildren {
constructor(featuresService, expandService) {
this.featuresService = featuresService;
this.expandService = expandService;
if (expandService) {
this.subscription = this.expandService.expandChange.subscribe(value => {
if (!value && this.parent && !this.featuresService.eager && this.featuresService.recursion) {
// In the case of lazy-loading recursive trees, we clear the children on collapse.
// This is better in case they change between two user interaction, and that way
// the app itself can decide whether to cache them or not.
this.parent.clearChildren();
}
});
}
}
shouldRender() {
return (this.featuresService.recursion &&
// In the smart case, we eagerly render all the recursive children
// to make sure two-way bindings for selection are available.
// They will be hidden with CSS by the parent.
(this.featuresService.eager || !this.expandService || this.expandService.expanded));
}
getContext(node) {
return {
$implicit: node.model,
clrModel: node,
};
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
RecursiveChildren.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: RecursiveChildren, deps: [{ token: TreeFeaturesService }, { token: IfExpandService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
RecursiveChildren.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: RecursiveChildren, selector: "clr-recursive-children", inputs: { parent: "parent", children: "children" }, host: { properties: { "attr.role": "\"group\"" } }, ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: RecursiveChildren, decorators: [{
type: Component,
args: [{
selector: 'clr-recursive-children',
template: `
`,
host: {
'[attr.role]': '"group"', // Safari + VO needs direct relationship between treeitem and group; no element should exist between them
},
}]
}], ctorParameters: function () { return [{ type: TreeFeaturesService }, { type: IfExpandService, decorators: [{
type: Optional
}] }]; }, propDecorators: { parent: [{
type: Input,
args: ['parent']
}], children: [{
type: Input,
args: ['children']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
function isPromise(o) {
// Shamelessly copied from every open-source project out there.
return o && typeof o.then === 'function';
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
// TODO: I'd like this to be a CheckedState enum for the checkboxes in the future.
var ClrSelectedState;
(function (ClrSelectedState) {
// WARNING! Unselected has the value 0,
// so it's actually the only one that will evaluate to false if cast to a boolean.
// Don't mess with the order!
ClrSelectedState[ClrSelectedState["UNSELECTED"] = 0] = "UNSELECTED";
ClrSelectedState[ClrSelectedState["SELECTED"] = 1] = "SELECTED";
ClrSelectedState[ClrSelectedState["INDETERMINATE"] = 2] = "INDETERMINATE";
})(ClrSelectedState || (ClrSelectedState = {}));
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class TreeNodeModel {
constructor() {
this.loading$ = new BehaviorSubject(false);
this.selected = new BehaviorSubject(ClrSelectedState.UNSELECTED);
/*
* Being able to push this down to the RecursiveTreeNodeModel would require too much work on the angular components
* right now for them to know which kind of model they are using. So I'm lifting the public properties to this
* abstract parent class for now and we can revisit it later, when we're not facing such a close deadline.
*/
this._loading = false;
}
get loading() {
return this._loading;
}
set loading(isLoading) {
this._loading = isLoading;
this.loading$.next(isLoading);
}
destroy() {
// Just to be safe
this.selected.complete();
}
// Propagate by default when eager, don't propagate in the lazy-loaded tree.
setSelected(state, propagateUp, propagateDown) {
if (state === this.selected.value) {
return;
}
this.selected.next(state);
if (propagateDown && state !== ClrSelectedState.INDETERMINATE && this.children) {
this.children.forEach(child => child.setSelected(state, false, true));
}
if (propagateUp && this.parent) {
this.parent._updateSelectionFromChildren();
}
}
toggleSelection(propagate) {
// Both unselected and indeterminate toggle to selected
const newState = this.selected.value === ClrSelectedState.SELECTED ? ClrSelectedState.UNSELECTED : ClrSelectedState.SELECTED;
// NOTE: we always propagate selection up in this method because it is only called when the user takes an action.
// It should never be called from lifecycle hooks or app-provided inputs.
this.setSelected(newState, true, propagate);
}
/*
* Internal, but needs to be called by other nodes
*/
_updateSelectionFromChildren() {
const newState = this.computeSelectionStateFromChildren();
if (newState === this.selected.value) {
return;
}
this.selected.next(newState);
if (this.parent) {
this.parent._updateSelectionFromChildren();
}
}
computeSelectionStateFromChildren() {
let oneSelected = false;
let oneUnselected = false;
// Using a good old for loop to exit as soon as we can tell, for better performance on large trees.
for (const child of this.children) {
switch (child.selected.value) {
case ClrSelectedState.INDETERMINATE:
return ClrSelectedState.INDETERMINATE;
case ClrSelectedState.SELECTED:
oneSelected = true;
if (oneUnselected) {
return ClrSelectedState.INDETERMINATE;
}
break;
case ClrSelectedState.UNSELECTED:
default:
// Default is the same as unselected, in case an undefined somehow made it all the way here.
oneUnselected = true;
if (oneSelected) {
return ClrSelectedState.INDETERMINATE;
}
break;
}
}
if (!oneSelected) {
return ClrSelectedState.UNSELECTED;
}
else if (!oneUnselected) {
return ClrSelectedState.SELECTED;
}
else {
return ClrSelectedState.UNSELECTED;
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* A recursive model is built received from the app and traversed to create the corresponding components.
* Recursive = Model dictates the tree node components
*/
class RecursiveTreeNodeModel extends TreeNodeModel {
constructor(model, parent, getChildren, featuresService) {
super();
this.getChildren = getChildren;
this.featuresService = featuresService;
this.childrenFetched = false;
this._children = [];
this.model = model;
this.parent = parent;
}
get children() {
this.fetchChildren();
return this._children;
}
set children(value) {
this._children = value;
}
destroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
super.destroy();
}
clearChildren() {
this._children.forEach(child => child.destroy());
delete this._children;
this.childrenFetched = false;
}
fetchChildren() {
if (this.childrenFetched) {
return;
}
const asyncChildren = this.getChildren(this.model);
if (isPromise(asyncChildren)) {
this.loading = true;
asyncChildren.then(raw => {
this._children = this.wrapChildren(raw);
this.loading = false;
});
}
else if (isObservable(asyncChildren)) {
this.loading = true;
this.subscription = asyncChildren.subscribe(raw => {
this._children = this.wrapChildren(raw);
this.loading = false;
});
}
else if (asyncChildren) {
// Synchronous case
this._children = this.wrapChildren(asyncChildren);
}
else {
this._children = [];
}
this.childrenFetched = true;
if (this.featuresService) {
this.featuresService.childrenFetched.next();
}
}
wrapChildren(rawModels) {
return rawModels.map(m => new RecursiveTreeNodeModel(m, this, this.getChildren, this.featuresService));
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrRecursiveForOf {
constructor(template, featuresService, cdr) {
this.template = template;
this.featuresService = featuresService;
this.cdr = cdr;
}
// I'm using OnChanges instead of OnInit to easily keep up to date with dynamic trees. Maybe optimizable later.
ngOnChanges() {
let wrapped;
if (Array.isArray(this.nodes)) {
wrapped = this.nodes.map(node => new RecursiveTreeNodeModel(node, null, this.getChildren, this.featuresService));
}
else {
wrapped = [new RecursiveTreeNodeModel(this.nodes, null, this.getChildren, this.featuresService)];
}
if (!this.childrenFetchSubscription) {
this.childrenFetchSubscription = this.featuresService.childrenFetched.pipe(debounceTime(0)).subscribe(() => {
this.cdr.detectChanges();
});
}
this.featuresService.recursion = {
template: this.template,
root: wrapped,
};
}
ngOnDestroy() {
if (this.childrenFetchSubscription) {
this.childrenFetchSubscription.unsubscribe();
}
}
}
ClrRecursiveForOf.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrRecursiveForOf, deps: [{ token: i0.TemplateRef }, { token: TreeFeaturesService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrRecursiveForOf.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrRecursiveForOf, selector: "[clrRecursiveFor][clrRecursiveForOf]", inputs: { nodes: ["clrRecursiveForOf", "nodes"], getChildren: ["clrRecursiveForGetChildren", "getChildren"] }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrRecursiveForOf, decorators: [{
type: Directive,
args: [{
selector: '[clrRecursiveFor][clrRecursiveForOf]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: TreeFeaturesService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { nodes: [{
type: Input,
args: ['clrRecursiveForOf']
}], getChildren: [{
type: Input,
args: ['clrRecursiveForGetChildren']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class TreeFocusManagerService {
constructor() {
this._focusRequest = new Subject();
this._focusChange = new Subject();
}
get focusRequest() {
return this._focusRequest.asObservable();
}
get focusChange() {
return this._focusChange.asObservable();
}
focusNode(model) {
if (model) {
this._focusRequest.next(model.nodeId);
}
}
broadcastFocusedNode(nodeId) {
if (this.focusedNodeId !== nodeId) {
this.focusedNodeId = nodeId;
this._focusChange.next(nodeId);
}
}
focusParent(model) {
if (model) {
this.focusNode(model.parent);
}
}
focusFirstVisibleNode() {
const focusModel = this.rootNodeModels && this.rootNodeModels[0];
this.focusNode(focusModel);
}
focusLastVisibleNode() {
this.focusNode(this.findLastVisibleInTree());
}
focusNodeAbove(model) {
this.focusNode(this.findNodeAbove(model));
}
focusNodeBelow(model) {
this.focusNode(this.findNodeBelow(model));
}
focusNodeStartsWith(searchString, model) {
this.focusNode(this.findClosestNodeStartsWith(searchString, model));
}
findSiblings(model) {
// the method will return not only sibling models but also itself among them
if (model.parent) {
return model.parent.children;
}
else {
return this.rootNodeModels;
}
}
findLastVisibleInNode(model) {
// the method will traverse through until it finds the last visible node from the given node
if (!model) {
return null;
}
if (model.expanded && model.children.length > 0) {
const children = model.children;
const lastChild = children[children.length - 1];
return this.findLastVisibleInNode(lastChild);
}
else {
return model;
}
}
findNextFocusable(model) {
if (!model) {
return null;
}
const siblings = this.findSiblings(model);
const selfIndex = siblings.indexOf(model);
if (selfIndex < siblings.length - 1) {
return siblings[selfIndex + 1];
}
else if (selfIndex === siblings.length - 1) {
return this.findNextFocusable(model.parent);
}
return null;
}
findLastVisibleInTree() {
const lastRootNode = this.rootNodeModels && this.rootNodeModels.length && this.rootNodeModels[this.rootNodeModels.length - 1];
return this.findLastVisibleInNode(lastRootNode);
}
findNodeAbove(model) {
if (!model) {
return null;
}
const siblings = this.findSiblings(model);
const selfIndex = siblings.indexOf(model);
if (selfIndex === 0) {
return model.parent;
}
else if (selfIndex > 0) {
return this.findLastVisibleInNode(siblings[selfIndex - 1]);
}
return null;
}
findNodeBelow(model) {
if (!model) {
return null;
}
if (model.expanded && model.children.length > 0) {
return model.children[0];
}
else {
return this.findNextFocusable(model);
}
}
findDescendentNodeStartsWith(searchString, model) {
if (model.expanded && model.children.length > 0) {
for (const childModel of model.children) {
const found = this.findNodeStartsWith(searchString, childModel);
if (found) {
return found;
}
}
}
return null;
}
findSiblingNodeStartsWith(searchString, model) {
const siblings = this.findSiblings(model);
const selfIndex = siblings.indexOf(model);
// Look from sibling nodes
for (let i = selfIndex + 1; i < siblings.length; i++) {
const siblingModel = siblings[i];
const found = this.findNodeStartsWith(searchString, siblingModel);
if (found) {
return found;
}
}
return null;
}
findRootNodeStartsWith(searchString, model) {
for (const rootModel of this.rootNodeModels) {
// Don't look from a parent yet
if (model.parent && model.parent === rootModel) {
continue;
}
const found = this.findNodeStartsWith(searchString, rootModel);
if (found) {
return found;
}
}
return null;
}
findNodeStartsWith(searchString, model) {
if (!model) {
return null;
}
if (model.textContent.startsWith(searchString)) {
return model;
}
return this.findDescendentNodeStartsWith(searchString, model);
}
findClosestNodeStartsWith(searchString, model) {
if (!model) {
return null;
}
const foundFromDescendents = this.findDescendentNodeStartsWith(searchString, model);
if (foundFromDescendents) {
return foundFromDescendents;
}
const foundFromSiblings = this.findSiblingNodeStartsWith(searchString, model);
if (foundFromSiblings) {
return foundFromSiblings;
}
const foundFromRootNodes = this.findRootNodeStartsWith(searchString, model);
if (foundFromRootNodes) {
return foundFromRootNodes;
}
// Now look from its own direct parent
return this.findNodeStartsWith(searchString, model.parent);
}
}
TreeFocusManagerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TreeFocusManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
TreeFocusManagerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TreeFocusManagerService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: TreeFocusManagerService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* A declarative model is built by traversing the Angular component tree.
* Declarative = Tree node components dictate the model
*/
class DeclarativeTreeNodeModel extends TreeNodeModel {
constructor(parent) {
super();
this.parent = parent;
if (parent) {
parent._addChild(this);
}
this.children = [];
}
destroy() {
if (this.parent) {
this.parent._removeChild(this);
}
super.destroy();
}
_addChild(child) {
this.children.push(child);
}
_removeChild(child) {
const index = this.children.indexOf(child);
if (index > -1) {
this.children.splice(index, 1);
}
}
}
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrTreeNodeLink {
constructor(el) {
this.el = el;
}
get active() {
return this.el.nativeElement.classList.contains('active');
}
activate() {
if (this.el.nativeElement && this.el.nativeElement.click) {
this.el.nativeElement.click();
}
}
}
ClrTreeNodeLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTreeNodeLink, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrTreeNodeLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrTreeNodeLink, selector: ".clr-treenode-link", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTreeNodeLink, decorators: [{
type: Directive,
args: [{
selector: '.clr-treenode-link',
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const LVIEW_CONTEXT_INDEX = 8;
// If the user types multiple keys without allowing 200ms to pass between them,
// then those keys are sent together in one request.
const TREE_TYPE_AHEAD_TIMEOUT = 200;
class ClrTreeNode {
constructor(platformId, parent, featuresService, expandService, commonStrings, focusManager, elementRef, injector) {
this.platformId = platformId;
this.featuresService = featuresService;
this.expandService = expandService;
this.commonStrings = commonStrings;
this.focusManager = focusManager;
this.elementRef = elementRef;
this.selectedChange = new EventEmitter(false);
this.expandedChange = new EventEmitter();
this.STATES = ClrSelectedState;
this.isModelLoading = false;
this.nodeId = uniqueIdFactory();
this.contentContainerTabindex = -1;
this.skipEmitChange = false;
this.typeAheadKeyBuffer = '';
this.typeAheadKeyEvent = new Subject();
this.subscriptions = [];
if (this.featuresService.recursion) {
// I'm completely stuck, we have to hack into private properties until either
// https://github.com/angular/angular/issues/14935 or https://github.com/angular/angular/issues/15998
// are fixed
// This is for non-ivy implementations
if (injector.view) {
this._model = injector.view.context.clrModel;
}
else {
// Ivy puts this on a specific index of a _lView property
this._model = injector._lView[LVIEW_CONTEXT_INDEX].clrModel;
}
}
else {
// Force cast for now, not sure how to tie the correct type here to featuresService.recursion
this._model = new DeclarativeTreeNodeModel(parent ? parent._model : null);
}
this._model.nodeId = this.nodeId;
}
get selected() {
return this._model.selected.value;
}
set selected(value) {
this.featuresService.selectable = true;
// Gracefully handle falsy states like null or undefined because it's just easier than answering questions.
// This shouldn't happen with strict typing on the app's side, but it's not up to us.
if (value === null || typeof value === 'undefined') {
value = ClrSelectedState.UNSELECTED;
}
// We match booleans to the corresponding ClrSelectedState
if (typeof value === 'boolean') {
value = value ? ClrSelectedState.SELECTED : ClrSelectedState.UNSELECTED;
}
// We propagate only if the tree is in smart mode, and skip emitting the output when we set the input
// See https://github.com/vmware/clarity/issues/3073
this.skipEmitChange = true;
this._model.setSelected(value, this.featuresService.eager, this.featuresService.eager);
this.skipEmitChange = false;
}
// I'm caving on this, for tree nodes I think we can tolerate having a two-way binding on the component
// rather than enforce the clrIfExpanded structural directive for dynamic cases. Mostly because for the smart
// case, you can't use a structural directive, it would need to go on an ng-container.
get expanded() {
return this.expandService.expanded;
}
set expanded(value) {
this.expandService.expanded = value;
}
set clrForTypeAhead(value) {
this._model.textContent = trimAndLowerCase(value || this.elementRef.nativeElement.textContent);
}
get ariaSelected() {
if (this.isSelectable()) {
return this._model.selected.value === ClrSelectedState.SELECTED;
}
else if (this.treeNodeLink?.active) {
return true;
}
else {
return null;
}
}
get treeNodeLink() {
return this.treeNodeLinkList && this.treeNodeLinkList.first;
}
get isParent() {
return this._model.children && this._model.children.length > 0;
}
ngOnInit() {
this._model.expanded = this.expanded;
this.subscriptions.push(this._model.selected.pipe(filter(() => !this.skipEmitChange)).subscribe(value => {
this.selectedChange.emit(value);
}));
this.subscriptions.push(this.expandService.expandChange.subscribe(value => {
this.expandedChange.emit(value);
this._model.expanded = value;
}));
this.subscriptions.push(this.focusManager.focusRequest.subscribe(nodeId => {
if (this.nodeId === nodeId) {
this.focusTreeNode();
}
}), this.focusManager.focusChange.subscribe(nodeId => {
this.checkTabIndex(nodeId);
}));
this.subscriptions.push(this._model.loading$.pipe(debounceTime(0)).subscribe(isLoading => (this.isModelLoading = isLoading)));
}
ngAfterContentInit() {
this.subscriptions.push(this.typeAheadKeyEvent.pipe(debounceTime(TREE_TYPE_AHEAD_TIMEOUT)).subscribe((bufferedKeys) => {
this.focusManager.focusNodeStartsWith(bufferedKeys, this._model);
// reset once bufferedKeys are used
this.typeAheadKeyBuffer = '';
}));
}
ngAfterViewInit() {
if (!this._model.textContent) {
this._model.textContent = trimAndLowerCase(this.elementRef.nativeElement.textContent);
}
}
ngOnDestroy() {
this._model.destroy();
this.subscriptions.forEach(sub => sub.unsubscribe());
}
isExpandable() {
if (typeof this.expandable !== 'undefined') {
return this.expandable;
}
return !!this.expandService.expandable || this.isParent;
}
isSelectable() {
return this.featuresService.selectable;
}
focusTreeNode() {
const containerEl = this.contentContainer.nativeElement;
if (isPlatformBrowser(this.platformId) && document.activeElement !== containerEl) {
this.setTabIndex(0);
containerEl.focus();
containerEl.scrollIntoView({ block: 'nearest', inline: 'nearest' });
}
}
broadcastFocusOnContainer() {
this.focusManager.broadcastFocusedNode(this.nodeId);
}
onKeyDown(event) {
// Two reasons to prevent default behavior:
// 1. to prevent scrolling on arrow keys
// 2. Assistive Technology focus differs from Keyboard focus behavior.
// By default, pressing arrow key makes AT focus go into the nested content of the item.
preventArrowKeyScroll(event);
// https://www.w3.org/TR/wai-aria-practices-1.1/#keyboard-interaction-22
switch (normalizeKey(event.key)) {
case Keys.ArrowUp:
this.focusManager.focusNodeAbove(this._model);
break;
case Keys.ArrowDown:
this.focusManager.focusNodeBelow(this._model);
break;
case Keys.ArrowRight:
this.expandOrFocusFirstChild();
break;
case Keys.ArrowLeft:
this.collapseOrFocusParent();
break;
case Keys.Home:
event.preventDefault();
this.focusManager.focusFirstVisibleNode();
break;
case Keys.End:
event.preventDefault();
this.focusManager.focusLastVisibleNode();
break;
case Keys.Enter:
this.toggleExpandOrTriggerDefault();
break;
case Keys.Space:
case Keys.Spacebar:
// to prevent scrolling on space key in this specific case
event.preventDefault();
this.toggleExpandOrTriggerDefault();
break;
default:
if (this._model.textContent && isKeyEitherLetterOrNumber(event)) {
this.typeAheadKeyBuffer += event.key;
this.typeAheadKeyEvent.next(this.typeAheadKeyBuffer);
return;
}
break;
}
// if non-letter keys are pressed, do reset.
this.typeAheadKeyBuffer = '';
}
setTabIndex(value) {
this.contentContainerTabindex = value;
this.contentContainer.nativeElement.setAttribute('tabindex', value);
}
checkTabIndex(nodeId) {
if (isPlatformBrowser(this.platformId) && this.nodeId !== nodeId && this.contentContainerTabindex !== -1) {
this.setTabIndex(-1);
}
}
toggleExpandOrTriggerDefault() {
if (this.isExpandable() && !this.isSelectable()) {
this.expandService.expanded = !this.expanded;
}
else {
this.triggerDefaultAction();
}
}
expandOrFocusFirstChild() {
if (this.expanded) {
// if the node is already expanded and has children, focus its very first child
if (this.isParent) {
this.focusManager.focusNodeBelow(this._model);
}
}
else {
// we must check if the node is expandable, in order to set .expanded to true from false
// because we shouldn't set .expanded to true if it's not expandable node
if (this.isExpandable()) {
this.expandService.expanded = true;
}
}
}
collapseOrFocusParent() {
if (this.expanded) {
this.expandService.expanded = false;
}
else {
this.focusManager.focusParent(this._model);
}
}
triggerDefaultAction() {
if (this.treeNodeLink) {
this.treeNodeLink.activate();
}
else {
if (this.isSelectable()) {
this._model.toggleSelection(this.featuresService.eager);
}
}
}
}
ClrTreeNode.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTreeNode, deps: [{ token: PLATFORM_ID }, { token: ClrTreeNode, optional: true, skipSelf: true }, { token: TreeFeaturesService }, { token: IfExpandService }, { token: ClrCommonStringsService }, { token: TreeFocusManagerService }, { token: i0.ElementRef }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
ClrTreeNode.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrTreeNode, selector: "clr-tree-node", inputs: { expandable: ["clrExpandable", "expandable"], selected: ["clrSelected", "selected"], expanded: ["clrExpanded", "expanded"], clrForTypeAhead: "clrForTypeAhead" }, outputs: { selectedChange: "clrSelectedChange", expandedChange: "clrExpandedChange" }, host: { properties: { "class.clr-tree-node": "true" } }, providers: [TREE_FEATURES_PROVIDER, IfExpandService, { provide: LoadingListener, useExisting: IfExpandService }], queries: [{ propertyName: "treeNodeLinkList", predicate: ClrTreeNodeLink }], viewQueries: [{ propertyName: "contentContainer", first: true, predicate: ["contentContainer"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "\n\n
\n" }]
}], ctorParameters: function () { return [{ type: VerticalNavService }, { type: VerticalNavIconService }, { type: VerticalNavGroupRegistrationService }, { type: ClrCommonStringsService }]; }, propDecorators: { _collapsedChanged: [{
type: Output,
args: ['clrVerticalNavCollapsedChange']
}], collapsible: [{
type: Input,
args: ['clrVerticalNavCollapsible']
}], collapsed: [{
type: Input,
args: ['clrVerticalNavCollapsed']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class VerticalNavGroupService {
constructor() {
this._expandChange = new Subject();
}
get expandChange() {
return this._expandChange.asObservable();
}
expand() {
this._expandChange.next(true);
}
}
VerticalNavGroupService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: VerticalNavGroupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
VerticalNavGroupService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: VerticalNavGroupService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: VerticalNavGroupService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const EXPANDED_STATE = 'expanded';
const COLLAPSED_STATE = 'collapsed';
class ClrVerticalNavGroup {
constructor(_itemExpand, _navGroupRegistrationService, _navGroupService, _navService, commonStrings) {
this._itemExpand = _itemExpand;
this._navGroupRegistrationService = _navGroupRegistrationService;
this._navGroupService = _navGroupService;
this._navService = _navService;
this.commonStrings = commonStrings;
this.expandedChange = new EventEmitter(true);
this.wasExpanded = false;
this._subscriptions = [];
this._expandAnimationState = COLLAPSED_STATE;
this._navGroupRegistrationService.registerNavGroup();
// FIXME: This subscription handles a corner case
// Vertical Nav collapse requires the animation to run first and then
// remove the nodes from the DOM. If the user directly sets the input
// on the clrIfExpanded directive, we have no chance to run the animation
// and wait for it to complete. This subscription makes sure that the
// animation states are correct for that edge case.
this._subscriptions.push(this._itemExpand.expandChange.subscribe(value => {
if (value && this.expandAnimationState === COLLAPSED_STATE) {
if (this._navService.collapsed) {
this._navService.collapsed = false;
}
this.expandAnimationState = EXPANDED_STATE;
}
else if (!value && this.expandAnimationState === EXPANDED_STATE) {
this.expandAnimationState = COLLAPSED_STATE;
}
}));
// 1. If the nav is collapsing, close the open nav group + save its state
// 2. If the nav is expanding, expand the nav group if the previous state was expanded
this._subscriptions.push(this._navService.animateOnCollapsed.subscribe((goingToCollapse) => {
if (goingToCollapse && this.expanded) {
this.wasExpanded = true;
this.expandAnimationState = COLLAPSED_STATE;
}
else if (!goingToCollapse && this.wasExpanded) {
this.expandGroup();
this.wasExpanded = false;
}
}));
// If a link is clicked, expand the nav group
this._subscriptions.push(this._navGroupService.expandChange.subscribe((expand) => {
if (expand && !this.expanded) {
this.expandGroup();
}
}));
}
get expanded() {
return this._itemExpand.expanded;
}
set expanded(value) {
if (this._itemExpand.expanded !== value) {
this._itemExpand.expanded = value;
this.expandedChange.emit(value);
}
}
set userExpandedInput(value) {
value = !!value;
if (this.expanded !== value) {
// We have to call toggleExpand because some cases require animations to occur first
// Directly setting the Expand service value skips the animation and can result in
// nodes in the DOM but the nav group still being collapsed
this.toggleExpand();
}
}
get expandAnimationState() {
return this._expandAnimationState;
}
set expandAnimationState(value) {
if (value !== this._expandAnimationState) {
this._expandAnimationState = value;
}
}
ngAfterContentInit() {
// This makes sure that if someone marks a nav group expanded in a collapsed nav
// the expanded property is switched back to collapsed state.
if (this._navService.collapsed && this.expanded) {
this.wasExpanded = true;
this.expandAnimationState = COLLAPSED_STATE;
}
}
ngOnDestroy() {
this._subscriptions.forEach((sub) => sub.unsubscribe());
this._navGroupRegistrationService.unregisterNavGroup();
}
expandGroup() {
this.expanded = true;
// Expanded animation occurs after Expand.expand is set to true
this.expandAnimationState = EXPANDED_STATE;
}
collapseGroup() {
// If a Vertical Nav Group toggle button is clicked while the Vertical Nav is in Collapsed state,
// the Vertical Nav should be expanded first.
this.expandAnimationState = COLLAPSED_STATE;
}
// closes a group after the collapse animation
expandAnimationDone($event) {
if ($event.toState === COLLAPSED_STATE) {
this.expanded = false;
}
}
toggleExpand() {
if (this.expanded) {
this.collapseGroup();
}
else {
// If nav is collasped, first open the nav
if (this._navService.collapsed) {
this._navService.collapsed = false;
}
// then expand the nav group
this.expandGroup();
}
}
}
ClrVerticalNavGroup.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrVerticalNavGroup, deps: [{ token: IfExpandService }, { token: VerticalNavGroupRegistrationService }, { token: VerticalNavGroupService }, { token: VerticalNavService }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrVerticalNavGroup.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrVerticalNavGroup, selector: "clr-vertical-nav-group", inputs: { userExpandedInput: ["clrVerticalNavGroupExpanded", "userExpandedInput"] }, outputs: { expandedChange: "clrVerticalNavGroupExpandedChange" }, host: { properties: { "class.is-expanded": "this.expanded" }, classAttribute: "nav-group" }, providers: [IfExpandService, VerticalNavGroupService], ngImport: i0, template: "\n\n
`,
host: { '[class.clr-timeline-step]': 'true' },
}]
}], ctorParameters: function () { return [{ type: TimelineIconAttributeService }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; }, propDecorators: { state: [{
type: Input,
args: ['clrState']
}], stepTitle: [{
type: ContentChild,
args: [ClrTimelineStepTitle, { read: ElementRef }]
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrTimelineStepDescription {
}
ClrTimelineStepDescription.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineStepDescription, deps: [], target: i0.ɵɵFactoryTarget.Component });
ClrTimelineStepDescription.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrTimelineStepDescription, selector: "clr-timeline-step-description", host: { properties: { "class.clr-timeline-step-description": "true" } }, ngImport: i0, template: ``, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineStepDescription, decorators: [{
type: Component,
args: [{
selector: 'clr-timeline-step-description',
template: ``,
host: { '[class.clr-timeline-step-description]': 'true' },
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrTimelineStepHeader {
}
ClrTimelineStepHeader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineStepHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
ClrTimelineStepHeader.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrTimelineStepHeader, selector: "clr-timeline-step-header", host: { properties: { "class.clr-timeline-step-header": "true" } }, ngImport: i0, template: ``, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineStepHeader, decorators: [{
type: Component,
args: [{
selector: 'clr-timeline-step-header',
template: ``,
host: { '[class.clr-timeline-step-header]': 'true' },
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CLR_TIMELINE_DIRECTIVES = [
ClrTimeline,
ClrTimelineStep,
ClrTimelineStepDescription,
ClrTimelineStepHeader,
ClrTimelineStepTitle,
];
class ClrTimelineModule {
constructor() {
ClarityIcons.addIcons(circleIcon, dotCircleIcon, errorStandardIcon, successStandardIcon);
}
}
ClrTimelineModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrTimelineModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineModule, declarations: [ClrTimeline,
ClrTimelineStep,
ClrTimelineStepDescription,
ClrTimelineStepHeader,
ClrTimelineStepTitle], imports: [CommonModule, ClrIconModule, ClrSpinnerModule], exports: [ClrTimeline,
ClrTimelineStep,
ClrTimelineStepDescription,
ClrTimelineStepHeader,
ClrTimelineStepTitle, ClrIconModule, ClrSpinnerModule] });
ClrTimelineModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineModule, imports: [CommonModule, ClrIconModule, ClrSpinnerModule, ClrIconModule, ClrSpinnerModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrTimelineModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, ClrIconModule, ClrSpinnerModule],
exports: [...CLR_TIMELINE_DIRECTIVES, ClrIconModule, ClrSpinnerModule],
declarations: [CLR_TIMELINE_DIRECTIVES],
}]
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ButtonHubService {
constructor() {
this.buttonsReady = false;
this._previousBtnClicked = new Subject();
this._nextBtnClicked = new Subject();
this._dangerBtnClicked = new Subject();
this._cancelBtnClicked = new Subject();
this._finishBtnClicked = new Subject();
this._customBtnClicked = new Subject();
}
get previousBtnClicked() {
return this._previousBtnClicked.asObservable();
}
get nextBtnClicked() {
return this._nextBtnClicked.asObservable();
}
get dangerBtnClicked() {
return this._dangerBtnClicked.asObservable();
}
get cancelBtnClicked() {
return this._cancelBtnClicked.asObservable();
}
get finishBtnClicked() {
return this._finishBtnClicked.asObservable();
}
get customBtnClicked() {
return this._customBtnClicked.asObservable();
}
buttonClicked(buttonType) {
if ('previous' === buttonType) {
this._previousBtnClicked.next();
}
else if ('next' === buttonType) {
this._nextBtnClicked.next();
}
else if ('finish' === buttonType) {
this._finishBtnClicked.next();
}
else if ('danger' === buttonType) {
this._dangerBtnClicked.next();
}
else if ('cancel' === buttonType) {
this._cancelBtnClicked.next();
}
else {
this._customBtnClicked.next(buttonType);
}
}
}
ButtonHubService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonHubService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ButtonHubService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonHubService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ButtonHubService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* PageCollectionService manages the collection of pages assigned to the wizard and offers
* a number of functions useful across the wizards providers and subcomponents -- all related
* to essentially lookups on the collection of pages.
*
* The easiest way to access PageCollectionService is via the wizard. The
* following example would allow you to access your instance of the wizard from your host
* component and thereby access the page collection via YourHostComponent.wizard.pageCollection.
*
* @example
*
*
* @example
* export class YourHostComponent {
* @ViewChild("wizard") wizard: Wizard;
* ...
* }
*
* The heart of the page collection is the query list of pages, which it is assigned as a
* reference to the Wizard.pages QueryList when the wizard is created.
*
*/
class PageCollectionService {
constructor() {
/**
*
* @memberof PageCollectionService
*/
this._pagesReset = new Subject();
}
/**
* Converts the PageCollectionService.pages QueryList to an array and returns it.
*
* Useful for many instances when you would prefer a QueryList to act like an array.
*
* @memberof PageCollectionService
*/
get pagesAsArray() {
return this.pages ? this.pages.toArray() : [];
}
/**
* Returns the length of the pages query list.
*
* @memberof PageCollectionService
*/
get pagesCount() {
return this.pages ? this.pages.length : 0;
}
/**
* Returns the next-to-last page in the query list of pages. Operates as a getter
* so that it isn't working with stale data.
*
* @memberof PageCollectionService
*/
get penultimatePage() {
const pageCount = this.pagesCount;
if (pageCount < 2) {
return null;
}
return this.pagesAsArray[pageCount - 2];
}
/**
* Returns the last page in the query list of pages. Operates as a getter
* so that it isn't working with stale data.
*
* @memberof PageCollectionService
*/
get lastPage() {
const pageCount = this.pagesCount;
if (pageCount < 1) {
return null;
}
return this.pagesAsArray[pageCount - 1];
}
/**
* Returns the first page in the query list of pages. Operates as a getter
* so that it isn't working with stale data.
*
* @memberof PageCollectionService
*/
get firstPage() {
if (!this.pagesCount) {
return null;
}
return this.pagesAsArray[0];
}
/**
* An observable that the navigation service listens to in order to know when
* the page collection completed states have been reset to false so that way it
* can also reset the navigation to make the first page in the page collection
* current/active.
*
* @memberof PageCollectionService
*/
get pagesReset() {
return this._pagesReset.asObservable();
}
/**
* Used mostly internally, but accepts a string ID and returns a ClrWizardPage
* object that matches the ID passed. Note that IDs here should include the prefix
* "clr-wizard-page-".
*
* Returns the next-to-last page in the query list of pages. Operates as a getter
* so that it isn't working with stale data.
*
* @memberof PageCollectionService
*/
getPageById(id) {
const foundPages = this.pages.filter((page) => id === page.id);
return this.checkResults(foundPages, id);
}
/**
* Accepts s number as a parameter and treats that number as the index of the page
* you're looking for in the collection of pages. Returns a wizard page object.
*
* @memberof PageCollectionService
*/
getPageByIndex(index) {
const pageCount = this.pagesCount;
const pagesLastIndex = pageCount > 1 ? pageCount - 1 : 0;
if (index < 0) {
throw new Error('Cannot retrieve page with index of ' + index);
}
if (index > pagesLastIndex) {
throw new Error('Page index is greater than length of pages array.');
}
return this.pagesAsArray[index];
}
/**
* Takes a wizard page object as a parameter and returns its index in the
* collection of pages.
*
* @memberof PageCollectionService
*/
getPageIndex(page) {
const index = this.pagesAsArray.indexOf(page);
if (index < 0) {
throw new Error('Requested page cannot be found in collection of pages.');
}
return index;
}
/**
* Accepts two numeric indexes and returns an array of wizard page objects that include
* all wizard pages in the page collection from the first index to the second.
*
* @memberof PageCollectionService
*/
pageRange(start, end) {
let pages = [];
if (start < 0 || end < 0) {
return [];
}
if (start === null || typeof start === 'undefined' || isNaN(start)) {
return [];
}
if (end === null || typeof end === 'undefined' || isNaN(end)) {
return [];
}
if (end > this.pagesCount) {
end = this.pagesCount;
}
pages = this.pagesAsArray;
if (end - start === 0) {
// just return the one page they want
return [this.getPageByIndex(start)];
}
// slice end does not include item referenced by end index, which is weird for users
// incrementing end index here to correct that so users and other methods
// don't have to think about it
end = end + 1;
// slice does not return the last one in the range but it does include the first one
// does not modify original array
return pages.slice(start, end);
}
/**
* Accepts two wizard page objects and returns those page objects with all other page
* objects between them in the page collection. It doesn't care which page is ahead of the
* other in the parameters. It will be smart enough to figure that out on its own.
*
* @memberof PageCollectionService
*/
getPageRangeFromPages(page, otherPage) {
const pageIndex = this.getPageIndex(page);
const otherPageIndex = this.getPageIndex(otherPage);
let startIndex;
let endIndex;
if (pageIndex <= otherPageIndex) {
startIndex = pageIndex;
endIndex = otherPageIndex;
}
else {
startIndex = otherPageIndex;
endIndex = pageIndex;
}
return this.pageRange(startIndex, endIndex);
}
/**
* Takes a wizard page object as a parameter and returns the wizard page object of
* the page immediately before it in the page collection. Returns null if there is
* no page before the page it is passed.
*
* @memberof PageCollectionService
*/
getPreviousPage(page) {
const myPageIndex = this.getPageIndex(page);
const previousPageIndex = myPageIndex - 1;
if (previousPageIndex < 0) {
return null;
}
return this.getPageByIndex(previousPageIndex);
}
/**
* Accepts a wizard page object as a parameter and returns a Boolean that says if
* the page you sent it is complete.
*
* @memberof PageCollectionService
*/
previousPageIsCompleted(page) {
if (!page) {
return false;
}
const previousPage = this.getPreviousPage(page);
if (null === previousPage) {
// page is the first page. no previous page.
return true;
}
return previousPage.completed;
}
/**
* Takes a wizard page object as a parameter and returns the wizard page object of
* the page immediately after it in the page collection. Returns null if there is
* no page after the page it is passed.
*
* @memberof PageCollectionService
*/
getNextPage(page) {
const myPageIndex = this.getPageIndex(page);
const nextPageIndex = myPageIndex + 1;
if (nextPageIndex >= this.pagesAsArray.length) {
return null;
}
return this.getPageByIndex(nextPageIndex);
}
/**
* Takes a wizard page object as a parameter and generates a step item id from the
* page ID. Returns the generated step item ID as a string.
*
* @memberof PageCollectionService
*/
getStepItemIdForPage(page) {
const pageId = page.id;
const pageIdParts = pageId.split('-').reverse();
pageIdParts[1] = 'step';
return pageIdParts.reverse().join('-');
}
/**
* Generally only used internally to mark that a specific page has been "committed".
* This involves marking the page complete and firing the ClrWizardPage.onCommit
* (clrWizardPageOnCommit) output. Takes the wizard page object that you intend to
* mark completed as a parameter.
*
* @memberof PageCollectionService
*/
commitPage(page) {
const pageHasOverrides = page.stopNext || page.preventDefault;
page.completed = true;
if (!pageHasOverrides) {
// prevent loop of event emission; alternate flows work off
// of event emitters this is how they break that cycle.
page.onCommit.emit(page.id);
}
}
/**
* Sets all completed states of the pages in the page collection to false and
* notifies the navigation service to likewise reset the navigation.
*
* @memberof PageCollectionService
*/
reset() {
this.pagesAsArray.forEach((page) => {
page.completed = false;
});
this._pagesReset.next(true);
}
/**
* Rolls through all the pages in the page collection to make sure there are no
* incomplete pages sandwiched between completed pages in the workflow. Identifies
* the first incomplete page index and sets all pages behind it to a completed
* state of false.
*
* @memberof PageCollectionService
*/
updateCompletedStates() {
const firstIncompleteIndex = this.findFirstIncompletePageIndex();
if (firstIncompleteIndex === this.pagesAsArray.length - 1) {
// all complete no need to do anything
return;
}
this.pagesAsArray.forEach((page, index) => {
if (index > firstIncompleteIndex) {
page.completed = false;
}
});
}
/**
* Retrieves the index of the first incomplete page in the page collection.
*
* @memberof PageCollectionService
*/
findFirstIncompletePageIndex() {
let returnIndex = null;
this.pagesAsArray.forEach((page, index) => {
if (null === returnIndex && false === page.completed) {
returnIndex = index;
}
});
// fallthrough, all completed, return last page
if (null === returnIndex) {
returnIndex = this.pagesCount - 1;
}
return returnIndex;
}
findFirstIncompletePage() {
const myIncompleteIndex = this.findFirstIncompletePageIndex();
return this.pagesAsArray[myIncompleteIndex];
}
/**
* Consolidates guard logic that prevents a couple of unfortunate edge cases with
* look ups on the collection of pages.
*
* @memberof PageCollectionService
*/
checkResults(results, requestedPageId) {
const foundPagesCount = results.length || 0;
if (foundPagesCount > 1) {
throw new Error('More than one page has the requested id ' + requestedPageId + '.');
}
else if (foundPagesCount < 1) {
throw new Error('No page can be found with the id ' + requestedPageId + '.');
}
else {
return results[0];
}
}
}
PageCollectionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: PageCollectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
PageCollectionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: PageCollectionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: PageCollectionService, decorators: [{
type: Injectable
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Performs navigation functions for a wizard and manages the current page. Presented as a
* separate service to encapsulate the behavior of navigating and completing the wizard so
* that it can be shared across the wizard and its sub-components.
*
* The easiest way to access the navigation service is there a reference on your wizard. The
* Following example would allow you to access your instance of the wizard from your host
* component and thereby access the navigation service via YourHostComponent.wizard.navService.
*
* @example
*
*
* @example
* export class YourHostComponent {
* @ViewChild("wizard") wizard: Wizard;
* ...
* }
*
*/
class WizardNavigationService {
/**
* Creates an instance of WizardNavigationService. Also sets up subscriptions
* that listen to the button service to determine when a button has been clicked
* in the wizard. Is also responsible for taking action when the page collection
* requests that navigation be reset to its pristine state.
*
* @memberof WizardNavigationService
*/
constructor(pageCollection, buttonService) {
this.pageCollection = pageCollection;
this.buttonService = buttonService;
/**
* A Boolean flag used by the ClrWizardPage to avoid a race condition when pages are
* loading and there is no current page defined.
*
* @memberof WizardNavigationService
*/
this.navServiceLoaded = false;
/**
* A boolean flag shared across the Wizard subcomponents that follows the value
* of the Wizard.forceForward (clrWizardForceForwardNavigation) input. When true,
* navigating backwards in the stepnav menu will reset any skipped pages' completed
* state to false.
*
* This is useful when a wizard executes validation on a page-by-page basis when
* the next button is clicked.
*
* @memberof WizardNavigationService
*/
this.forceForwardNavigation = false;
/**
* A boolean flag shared across the Wizard subcomponents that follows the value
* of the Wizard.stopCancel (clrWizardPreventDefaultCancel) input. When true, the cancel
* routine is subverted and must be reinstated in the host component calling Wizard.close()
* at some point.
*
* @memberof WizardNavigationService
*/
this.wizardHasAltCancel = false;
/**
* A boolean flag shared across the Wizard subcomponents that follows the value
* of the Wizard.stopNext (clrWizardPreventDefaultNext) input. When true, the next and finish
* routines are subverted and must be reinstated in the host component calling Wizard.next(),
* Wizard.forceNext(), Wizard.finish(), or Wizard.forceFinish().
*
* @memberof WizardNavigationService
*/
this.wizardHasAltNext = false;
/**
* A boolean flag shared across the Wizard subcomponents that follows the value
* of the Wizard.stopNavigation (clrWizardPreventNavigation) input. When true, all
* navigational elements in the wizard are disabled.
*
* This is intended to freeze the wizard in place. Events are not fired so this is
* not a way to implement alternate functionality for navigation.
*
* @memberof WizardNavigationService
*/
this.wizardStopNavigation = false;
/**
* A boolean flag shared with the stepnav items that prevents user clicks on
* stepnav items from navigating the wizard.
*
* @memberof WizardNavigationService
*/
this.wizardDisableStepnav = false;
/**
*
* @memberof WizardNavigationService
*/
this._currentChanged = new Subject();
/**
* @memberof WizardNavigationService
*/
this._movedToNextPage = new Subject();
/**
* @memberof WizardNavigationService
*/
this._wizardFinished = new Subject();
/**
* @memberof WizardNavigationService
*/
this._movedToPreviousPage = new Subject();
/**
* @memberof WizardNavigationService
*/
this._cancelWizard = new Subject();
this.previousButtonSubscription = this.buttonService.previousBtnClicked.subscribe(() => {
const currentPage = this.currentPage;
if (this.currentPageIsFirst || currentPage.previousStepDisabled) {
return;
}
currentPage.previousButtonClicked.emit(currentPage);
if (!currentPage.preventDefault) {
this.previous();
}
});
this.nextButtonSubscription = this.buttonService.nextBtnClicked.subscribe(() => {
this.checkAndCommitCurrentPage('next');
});
this.dangerButtonSubscription = this.buttonService.dangerBtnClicked.subscribe(() => {
this.checkAndCommitCurrentPage('danger');
});
this.finishButtonSubscription = this.buttonService.finishBtnClicked.subscribe(() => {
this.checkAndCommitCurrentPage('finish');
});
this.customButtonSubscription = this.buttonService.customBtnClicked.subscribe((type) => {
if (!this.wizardStopNavigation) {
this.currentPage.customButtonClicked.emit(type);
}
});
this.cancelButtonSubscription = this.buttonService.cancelBtnClicked.subscribe(() => {
if (this.wizardStopNavigation) {
return;
}
if (this.currentPage.preventDefault) {
this.currentPage.pageOnCancel.emit(this.currentPage);
}
else {
this.cancel();
}
});
this.pagesResetSubscription = this.pageCollection.pagesReset.subscribe(() => {
this.setFirstPageCurrent();
});
}
/**
* An Observable that is predominantly used amongst the subcomponents and services
* of the wizard. It is recommended that users listen to the ClrWizardPage.onLoad
* (clrWizardPageOnLoad) output instead of this Observable.
*
* @memberof WizardNavigationService
*/
get currentPageChanged() {
// TODO: MAKE SURE EXTERNAL OUTPUTS SAY 'CHANGE' NOT 'CHANGED'
// A BREAKING CHANGE SO AWAITING MINOR RELEASE
return this._currentChanged.asObservable();
}
/**
* @memberof WizardNavigationService
*/
get currentPageTitle() {
// when the querylist of pages is empty. this is the first place it fails...
if (!this.currentPage) {
return null;
}
return this.currentPage.title;
}
/**
* Returns a Boolean that tells you whether or not the current page is the first
* page in the Wizard.
*
* This is helpful for determining whether a page is navigable.
*
* @memberof WizardNavigationService
*/
get currentPageIsFirst() {
return this.pageCollection.firstPage === this.currentPage;
}
/**
* Returns a Boolean that tells you whether or not the current page is the
* last page in the Wizard.
*
* This is used to determine which buttons should display in the wizard footer.
*
* @memberof WizardNavigationService
*/
get currentPageIsLast() {
return this.pageCollection.lastPage === this.currentPage;
}
/**
* Returns the ClrWizardPage object of the current page or null.
*
* @memberof WizardNavigationService
*/
get currentPage() {
if (!this._currentPage) {
return null;
}
return this._currentPage;
}
/**
* Accepts a ClrWizardPage object, since that object to be the current/active
* page in the wizard, and emits the ClrWizardPage.onLoad (clrWizardPageOnLoad)
* event for that page.
*
* Note that all of this work is bypassed if the ClrWizardPage object is already
* the current page.
*
* @memberof WizardNavigationService
*/
set currentPage(page) {
if (this._currentPage !== page && !this.wizardStopNavigation) {
this._currentPage = page;
page.onLoad.emit(page.id);
this._currentChanged.next(page);
}
}
/**
* An observable used internally to alert the wizard that forward navigation
* has occurred. It is recommended that you use the Wizard.onMoveNext
* (clrWizardOnNext) output instead of this one.
*
* @memberof WizardNavigationService
*/
get movedToNextPage() {
return this._movedToNextPage.asObservable();
}
/**
* An observable used internally to alert the wizard that the nav service
* has approved completion of the wizard.
*
* It is recommended that you use the Wizard.wizardFinished (clrWizardOnFinish)
* output instead of this one.
*
* @memberof WizardNavigationService
*/
get wizardFinished() {
return this._wizardFinished.asObservable();
}
/**
* Notifies the wizard when backwards navigation has occurred via the
* previous button.
*
* @memberof WizardNavigationService
*/
get movedToPreviousPage() {
return this._movedToPreviousPage.asObservable();
}
/**
* Notifies the wizard that a user is trying to cancel it.
*
* @memberof WizardNavigationService
*/
get notifyWizardCancel() {
return this._cancelWizard.asObservable();
}
/**
*
* @memberof WizardNavigationService
*/
ngOnDestroy() {
this.previousButtonSubscription.unsubscribe();
this.nextButtonSubscription.unsubscribe();
this.dangerButtonSubscription.unsubscribe();
this.finishButtonSubscription.unsubscribe();
this.customButtonSubscription.unsubscribe();
this.cancelButtonSubscription.unsubscribe();
this.pagesResetSubscription.unsubscribe();
}
/**
* This is a public function that can be used to programmatically advance
* the user to the next page.
*
* When invoked, this method will move the wizard to the next page after
* successful validation. Note that this method goes through all checks
* and event emissions as if Wizard.next(false) had been called.
*
* In most cases, it makes more sense to use Wizard.next(false).
*
* @memberof WizardNavigationService
*/
next() {
if (this.currentPageIsLast) {
this.checkAndCommitCurrentPage('finish');
}
else {
this.checkAndCommitCurrentPage('next');
}
}
/**
* Bypasses checks and most event emissions to force a page to navigate forward.
*
* Comparable to calling Wizard.next() or Wizard.forceNext().
*
* @memberof WizardNavigationService
*/
forceNext() {
const currentPage = this.currentPage;
const nextPage = this.pageCollection.getNextPage(currentPage);
// catch errant null or undefineds that creep in
if (!nextPage) {
throw new Error('The wizard has no next page to go to.');
}
if (this.wizardStopNavigation) {
return;
}
if (!currentPage.completed) {
// this is a state that alt next flows can get themselves in...
this.pageCollection.commitPage(currentPage);
}
this.currentPage = nextPage;
}
/**
* Accepts a button/action type as a parameter. Encapsulates all logic for
* event emissions, state of the current page, and wizard and page level overrides.
*
* Avoid calling this function directly unless you really know what you're doing.
*
* @memberof WizardNavigationService
*/
checkAndCommitCurrentPage(buttonType) {
const currentPage = this.currentPage;
if (!currentPage.readyToComplete || this.wizardStopNavigation) {
return;
}
const iAmTheLastPage = this.currentPageIsLast;
const isNext = buttonType === 'next';
const isDanger = buttonType === 'danger';
const isDangerNext = isDanger && !iAmTheLastPage;
const isDangerFinish = isDanger && iAmTheLastPage;
const isFinish = buttonType === 'finish' || isDangerFinish;
if (isFinish && !iAmTheLastPage) {
return;
}
currentPage.primaryButtonClicked.emit(buttonType);
if (isFinish) {
currentPage.finishButtonClicked.emit(currentPage);
}
else if (isDanger) {
currentPage.dangerButtonClicked.emit();
}
else if (isNext) {
currentPage.nextButtonClicked.emit();
}
if (currentPage.stopNext || currentPage.preventDefault) {
currentPage.onCommit.emit(currentPage.id);
return;
}
// order is very important with these emitters!
if (isFinish) {
// mark page as complete
if (!this.wizardHasAltNext) {
this.pageCollection.commitPage(currentPage);
}
this._wizardFinished.next();
}
if (this.wizardHasAltNext) {
this.pageCollection.commitPage(currentPage);
if (isNext || isDangerNext) {
this._movedToNextPage.next(true);
}
// jump out here, no matter what type we're looking at
return;
}
if (isNext || isDangerNext) {
this.forceNext();
}
if (!this.wizardHasAltNext && !this.wizardStopNavigation) {
this._movedToNextPage.next(true);
}
}
/**
* This is a public function that can be used to programmatically conclude
* the wizard.
*
* When invoked, this method will initiate the work involved with finalizing
* and finishing the wizard workflow. Note that this method goes through all
* checks and event emissions as if Wizard.finish(false) had been called.
*
* In most cases, it makes more sense to use Wizard.finish(false).
*
* @memberof WizardNavigationService
*/
finish() {
this.checkAndCommitCurrentPage('finish');
}
/**
* Programmatically moves the wizard to the page before the current page.
*
* In most instances, it makes more sense to call Wizard.previous()
* which does the same thing.
*
* @memberof WizardNavigationService
*/
previous() {
if (this.currentPageIsFirst || this.wizardStopNavigation) {
return;
}
const previousPage = this.pageCollection.getPreviousPage(this.currentPage);
if (!previousPage) {
return;
}
this._movedToPreviousPage.next(true);
if (this.forceForwardNavigation) {
this.currentPage.completed = false;
}
this.currentPage = previousPage;
}
/**
* Allows a hook into the cancel workflow of the wizard from the nav service. Note that
* this route goes through all checks and event emissions as if a cancel button had
* been clicked.
*
* In most cases, users looking for a hook into the cancel routine are actually looking
* for a way to close the wizard from their host component because they have prevented
* the default cancel action.
*
* In this instance, it is recommended that you use Wizard.close() to avoid any event
* emission loop resulting from an event handler calling back into routine that will
* again evoke the events it handles.
*
* @memberof WizardNavigationService
*/
cancel() {
this._cancelWizard.next();
}
/**
* Performs all required checks to determine if a user can navigate to a page. Checking at each
* point if a page is navigable -- completed where the page immediately after the last completed
* page.
*
* Takes two parameters. The first one must be either the ClrWizardPage object or the ID of the
* ClrWizardPage object that you want to make the current page.
*
* The second parameter is optional and is a Boolean flag for "lazy completion". What this means
* is the Wizard will mark all pages between the current page and the page you want to navigate
* to as completed. This is useful for informational wizards that do not require user action,
* allowing an easy means for users to jump ahead.
*
* To avoid checks on navigation, use ClrWizardPage.makeCurrent() instead.
*
* @memberof WizardNavigationService
*/
goTo(pageToGoToOrId, lazyComplete = false) {
const myPages = this.pageCollection;
const pageToGoTo = typeof pageToGoToOrId === 'string' ? myPages.getPageById(pageToGoToOrId) : pageToGoToOrId;
const currentPage = this.currentPage;
// no point in going to the current page. you're there already!
// also hard block on any navigation when stopNavigation is true
if (pageToGoTo === currentPage || this.wizardStopNavigation) {
return;
}
const currentPageIndex = myPages.getPageIndex(currentPage);
const goToPageIndex = myPages.getPageIndex(pageToGoTo);
const goingForward = goToPageIndex > currentPageIndex;
const pagesToCheck = myPages.getPageRangeFromPages(this.currentPage, pageToGoTo);
const okayToMove = lazyComplete || this.canGoTo(pagesToCheck);
if (!okayToMove) {
return;
}
if (goingForward && lazyComplete) {
pagesToCheck.forEach((page) => {
if (page !== pageToGoTo) {
page.completed = true;
}
});
}
else if (!goingForward && this.forceForwardNavigation) {
pagesToCheck.forEach((page) => {
page.completed = false;
});
}
this.currentPage = pageToGoTo;
}
/**
* Accepts a range of ClrWizardPage objects as a parameter. Performs the work of checking
* those objects to determine if navigation can be accomplished.
*
* @memberof WizardNavigationService
*/
canGoTo(pagesToCheck) {
let okayToMove = true;
const myPages = this.pageCollection;
// previous page can be important when moving because if it's completed it
// allows us to move to the page even if it's incomplete...
let previousPagePasses;
if (!pagesToCheck || pagesToCheck.length < 1) {
return false;
}
pagesToCheck.forEach((page) => {
if (!okayToMove) {
return;
}
if (page.completed) {
// default is true. just jump out instead of complicating it.
return;
}
// so we know our page is not completed...
const previousPage = myPages.getPageIndex(page) > 0 ? myPages.getPreviousPage(page) : null;
previousPagePasses = previousPage === null || previousPage.completed === true;
// we are false if not the current page AND previous page is not completed
// (but must have a previous page)
if (!page.current && !previousPagePasses) {
okayToMove = false;
}
// falls through to true as default
});
return okayToMove;
}
/**
* Looks through the collection of pages to find the first one that is incomplete
* and makes that page the current/active page.
*
* @memberof WizardNavigationService
*/
setLastEnabledPageCurrent() {
const allPages = this.pageCollection.pagesAsArray;
let lastCompletedPageIndex = null;
allPages.forEach((page, index) => {
if (page.completed) {
lastCompletedPageIndex = index;
}
});
if (lastCompletedPageIndex === null) {
// always is at least the first item...
lastCompletedPageIndex = 0;
}
else if (lastCompletedPageIndex + 1 < allPages.length) {
lastCompletedPageIndex = lastCompletedPageIndex + 1;
}
this.currentPage = allPages[lastCompletedPageIndex];
}
/**
* Finds the first page in the collection of pages and makes that page the
* current/active page.
*
* @memberof WizardNavigationService
*/
setFirstPageCurrent() {
this.currentPage = this.pageCollection.pagesAsArray[0];
}
/**
* Updates the stepnav on the left side of the wizard when pages are dynamically
* added or removed from the collection of pages.
*
* @memberof WizardNavigationService
*/
updateNavigation() {
let toSetCurrent;
this.pageCollection.updateCompletedStates();
const currentPageRemoved = this.pageCollection.pagesAsArray.indexOf(this.currentPage) < 0;
if (currentPageRemoved) {
toSetCurrent = this.pageCollection.findFirstIncompletePage();
this.currentPage = toSetCurrent;
}
}
}
WizardNavigationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WizardNavigationService, deps: [{ token: PageCollectionService }, { token: ButtonHubService }], target: i0.ɵɵFactoryTarget.Injectable });
WizardNavigationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WizardNavigationService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: WizardNavigationService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: PageCollectionService }, { type: ButtonHubService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class HeaderActionService {
constructor(navService) {
this.navService = navService;
}
get wizardHasHeaderActions() {
const wizardHdrActions = this.wizardHeaderActions;
if (!wizardHdrActions) {
return false;
}
return wizardHdrActions.toArray().length > 0;
}
get currentPageHasHeaderActions() {
return this.navService.currentPage ? this.navService.currentPage.hasHeaderActions : false;
}
get showWizardHeaderActions() {
return !this.currentPageHasHeaderActions && this.wizardHasHeaderActions;
}
get displayHeaderActionsWrapper() {
return this.currentPageHasHeaderActions || this.wizardHasHeaderActions;
}
}
HeaderActionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: HeaderActionService, deps: [{ token: WizardNavigationService }], target: i0.ɵɵFactoryTarget.Injectable });
HeaderActionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: HeaderActionService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: HeaderActionService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: WizardNavigationService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let wizardHeaderActionIndex = 0;
class ClrWizardHeaderAction {
constructor() {
// title is explanatory text added to the header action
this.title = '';
// If our host has an ID attribute, we use this instead of our index.
this._id = (wizardHeaderActionIndex++).toString();
this.disabled = false;
this.headerActionClicked = new EventEmitter(false);
}
get id() {
return `clr-wizard-header-action-${this._id}`;
}
click() {
if (this.disabled) {
return;
}
// passing the header action id allows users to have one method that
// routes to many different actions based on the type of header action
// clicked. this is further aided by users being able to specify ids
// for their header actions.
this.headerActionClicked.emit(this._id);
}
}
ClrWizardHeaderAction.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardHeaderAction, deps: [], target: i0.ɵɵFactoryTarget.Component });
ClrWizardHeaderAction.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardHeaderAction, selector: "clr-wizard-header-action", inputs: { title: "title", _id: ["id", "_id"], disabled: ["clrWizardHeaderActionDisabled", "disabled"] }, outputs: { headerActionClicked: "actionClicked" }, host: { classAttribute: "clr-wizard-header-action-wrapper" }, ngImport: i0, template: `
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardHeaderAction, decorators: [{
type: Component,
args: [{
selector: 'clr-wizard-header-action',
template: `
`,
host: { class: 'clr-wizard-header-action-wrapper' },
}]
}], propDecorators: { title: [{
type: Input,
args: ['title']
}], _id: [{
type: Input,
args: ['id']
}], disabled: [{
type: Input,
args: ['clrWizardHeaderActionDisabled']
}], headerActionClicked: [{
type: Output,
args: ['actionClicked']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardPageButtons {
constructor(pageButtonsTemplateRef) {
this.pageButtonsTemplateRef = pageButtonsTemplateRef;
}
}
ClrWizardPageButtons.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageButtons, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrWizardPageButtons.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardPageButtons, selector: "[clrPageButtons]", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageButtons, decorators: [{
type: Directive,
args: [{
selector: '[clrPageButtons]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardPageHeaderActions {
constructor(pageHeaderActionsTemplateRef) {
this.pageHeaderActionsTemplateRef = pageHeaderActionsTemplateRef;
}
}
ClrWizardPageHeaderActions.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageHeaderActions, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrWizardPageHeaderActions.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardPageHeaderActions, selector: "[clrPageHeaderActions]", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageHeaderActions, decorators: [{
type: Directive,
args: [{
selector: '[clrPageHeaderActions]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardPageNavTitle {
constructor(pageNavTitleTemplateRef) {
this.pageNavTitleTemplateRef = pageNavTitleTemplateRef;
}
}
ClrWizardPageNavTitle.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageNavTitle, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrWizardPageNavTitle.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardPageNavTitle, selector: "[clrPageNavTitle]", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageNavTitle, decorators: [{
type: Directive,
args: [{
selector: '[clrPageNavTitle]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardPageTitle {
constructor(pageTitleTemplateRef) {
this.pageTitleTemplateRef = pageTitleTemplateRef;
}
}
ClrWizardPageTitle.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageTitle, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
ClrWizardPageTitle.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardPageTitle, selector: "[clrPageTitle]", inputs: { headingLevel: ["clrHeadingLevel", "headingLevel"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPageTitle, decorators: [{
type: Directive,
args: [{
selector: '[clrPageTitle]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { headingLevel: [{
type: Input,
args: ['clrHeadingLevel']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
let wizardPageIndex = 0;
/**
* The ClrWizardPage component is responsible for displaying the content of each step
* in the wizard workflow.
*
* ClrWizardPage component has hooks into the navigation service (ClrWizardPage.navService),
* page collection (ClrWizardPage.pageCollection), and button service
* (ClrWizardPage.buttonService). These three providers are shared across the components
* within each instance of a Wizard.
*
*/
class ClrWizardPage {
/**
* Creates an instance of ClrWizardPage.
*
* @memberof WizardPage
*/
constructor(navService, pageCollection, buttonService) {
this.navService = navService;
this.pageCollection = pageCollection;
this.buttonService = buttonService;
/**
* An input value that is used internally to generate the ClrWizardPage ID as
* well as the step nav item ID.
*
* Typed as any because it should be able to accept numbers as well as
* strings. Passing an index for wizard whose pages are created with an
* ngFor loop is a common use case.
*
* @memberof WizardPage
*
*/
this._id = (wizardPageIndex++).toString();
/**
* Overrides all actions from the page level, so you can use an alternate function for
* validation or data-munging with a ClrWizardPage.onCommit (clrWizardPageOnCommit output),
* ClrWizardPage.onCancel (clrWizardPageOnCancel output), or one
* of the granular page-level button click event emitters.
*
* @memberof WizardPage
*
*/
this.preventDefault = false;
/**
* Emits when the value of ClrWizardPage.nextStepDisabled changes.
* Should emit the new value of nextStepDisabled.
*
* @memberof WizardPage
*
*/
this.nextStepDisabledChange = new EventEmitter();
/**
* Emits when the value of ClrWizardPage.previousStepDisabled changes.
* Should emit the new value of previousStepDisabled.
*
* @memberof WizardPage
*
*/
this.previousStepDisabledChange = new EventEmitter();
/**
*
* @memberof WizardPage
*
*/
this.stopCancelChange = new EventEmitter();
/**
* An event emitter carried over from a legacy version of ClrWizardPage.
* Fires an event on ClrWizardPage whenever the next or finish buttons
* are clicked and the page is the current page of the Wizard.
*
* Note that this does not automatically emit an event when a custom
* button is used in place of a next or finish button.
*
* @memberof WizardPage
*
*/
this.onCommit = new EventEmitter(false);
/**
* Emits an event when ClrWizardPage becomes the current page of the
* Wizard.
*
* @memberof WizardPage
*
*/
this.onLoad = new EventEmitter();
/**
* Emits an event when the ClrWizardPage invokes the cancel routine for the wizard.
*
* Can be used in conjunction with the ClrWizardPage.stopCancel
* (clrWizardPagePreventDefaultCancel) or ClrWizardPage.preventDefault
* (clrWizardPagePagePreventDefault) inputs to implement custom cancel
* functionality at the page level. This is useful if you would like to do
* validation, save data, or warn users before cancelling the wizard.
*
* Note that this requires you to call Wizard.close() from the host component.
* This constitues a full replacement of the cancel functionality.
*
* @memberof WizardPage
*
*/
this.pageOnCancel = new EventEmitter();
/**
* Emits an event when the finish button is clicked and the ClrWizardPage is
* the wizard's current page.
*
* Can be used in conjunction with the ClrWizardPage.preventDefault
* (clrWizardPagePagePreventDefault) input to implement custom finish
* functionality at the page level. This is useful if you would like to do
* validation, save data, or warn users before allowing them to complete
* the wizard.
*
* Note that this requires you to call Wizard.finish() or Wizard.forceFinish()
* from the host component. This combination creates a full replacement of
* the finish functionality.
*
* @memberof WizardPage
*
*/
this.finishButtonClicked = new EventEmitter();
/**
* Emits an event when the previous button is clicked and the ClrWizardPage is
* the wizard's current page.
*
* Can be used in conjunction with the ClrWizardPage.preventDefault
* (clrWizardPagePagePreventDefault) input to implement custom backwards
* navigation at the page level. This is useful if you would like to do
* validation, save data, or warn users before allowing them to go
* backwards in the wizard.
*
* Note that this requires you to call Wizard.previous()
* from the host component. This combination creates a full replacement of
* the backwards navigation functionality.
*
* @memberof WizardPage
*
*/
this.previousButtonClicked = new EventEmitter();
/**
* Emits an event when the next button is clicked and the ClrWizardPage is
* the wizard's current page.
*
* Can be used in conjunction with the ClrWizardPage.preventDefault
* (clrWizardPagePagePreventDefault) input to implement custom forwards
* navigation at the page level. This is useful if you would like to do
* validation, save data, or warn users before allowing them to go
* to the next page in the wizard.
*
* Note that this requires you to call Wizard.forceNext() or Wizard.next()
* from the host component. This combination creates a full replacement of
* the forward navigation functionality.
*
* @memberof WizardPage
*
*/
this.nextButtonClicked = new EventEmitter();
/**
* Emits an event when a danger button is clicked and the ClrWizardPage is
* the wizard's current page. By default, a danger button will act as
* either a "next" or "finish" button depending on if the ClrWizardPage is the
* last page or not.
*
* Can be used in conjunction with the ClrWizardPage.preventDefault
* (clrWizardPagePagePreventDefault) input to implement custom forwards
* or finish navigation at the page level when the danger button is clicked.
* This is useful if you would like to do validation, save data, or warn
* users before allowing them to go to the next page in the wizard or
* finish the wizard.
*
* Note that this requires you to call Wizard.finish(), Wizard.forceFinish(),
* Wizard.forceNext() or Wizard.next() from the host component. This
* combination creates a full replacement of the forward navigation and
* finish functionality.
*
* @memberof WizardPage
*
*/
this.dangerButtonClicked = new EventEmitter();
/**
* Emits an event when a next, finish, or danger button is clicked and the
* ClrWizardPage is the wizard's current page.
*
* Can be used in conjunction with the ClrWizardPage.preventDefault
* (clrWizardPagePagePreventDefault) input to implement custom forwards
* or finish navigation at the page level, regardless of the type of
* primary button.
*
* This is useful if you would like to do validation, save data, or warn
* users before allowing them to go to the next page in the wizard or
* finish the wizard.
*
* Note that this requires you to call Wizard.finish(), Wizard.forceFinish(),
* Wizard.forceNext() or Wizard.next() from the host component. This
* combination creates a full replacement of the forward navigation and
* finish functionality.
*
* @memberof WizardPage
*
*/
this.primaryButtonClicked = new EventEmitter();
this.customButtonClicked = new EventEmitter();
/**
*
* @memberof WizardPage
*
*/
this._nextStepDisabled = false;
/**
*
* @memberof WizardPage
*
*/
this._previousStepDisabled = false;
/**
*
* @memberof WizardPage
*
*/
this._hasError = false;
/**
*
* @memberof WizardPage
*
*/
this._stopCancel = false;
/**
*
* @memberof WizardPage
*
*/
this._stopNext = false;
/**
*
* @memberof WizardPage
*
*/
this._complete = false;
}
/**
* A property that tells whether or not the wizard should be allowed
* to move to the next page.
*
* Useful for in-page validation because it prevents forward navigation
* and visibly disables the next button.
*
* Does not require that you re-implement navigation routines like you
* would if you were using ClrWizardPage.preventDefault or
* Wizard.preventDefault.
*
* @memberof WizardPage
*
*/
get nextStepDisabled() {
return this._nextStepDisabled;
}
set nextStepDisabled(val) {
const valBool = !!val;
if (valBool !== this._nextStepDisabled) {
this._nextStepDisabled = valBool;
this.nextStepDisabledChange.emit(valBool);
}
}
/**
* A property that tells whether or not the wizard should be allowed
* to move to the previous page.
*
* Useful for in-page validation because it prevents backward navigation
* and visibly disables the previous button.
*
* Does not require that you re-implement navigation routines like you
* would if you were using ClrWizardPage.preventDefault or
* Wizard.preventDefault.
*
* @memberof WizardPage
*
*/
get previousStepDisabled() {
return this._previousStepDisabled;
}
set previousStepDisabled(val) {
const valBool = !!val;
if (valBool !== this._previousStepDisabled) {
this._previousStepDisabled = valBool;
this.previousStepDisabledChange.emit(valBool);
}
}
/**
* Whether the page has an error and also resolve the "falsy" value. The
* current logic treat a "0" or an empty string as false and likewise will treat any
* "truthy" value as true.
*
* @memberof WizardPage
*
*/
get hasError() {
return this._hasError;
}
set hasError(val) {
const valBool = !!val;
if (valBool !== this._hasError) {
this._hasError = valBool;
}
}
/**
* Overrides the cancel action from the page level. Allows you to use an
* alternate function for validation or data-munging before cancelling the
* wizard when combined with the ClrWizardPage.onCancel
* (the clrWizardPageOnCancel output).
*
* Requires that you manually close the wizard from your host component,
* usually with a call to Wizard.forceNext() or wizard.next();
*
* @memberof ClrWizardPage
*/
get stopCancel() {
return this._stopCancel;
}
set stopCancel(val) {
const valBool = !!val;
if (valBool !== this._stopCancel) {
this._stopCancel = valBool;
this.stopCancelChange.emit(valBool);
}
}
/**
* Overrides forward navigation from the page level. Allows you to use an
* alternate function for validation or data-munging before moving the
* wizard to the next pagewhen combined with the ClrWizardPage.onCommit
* (clrWizardPageOnCommit) or ClrWizardPage.nextButtonClicked
* (clrWizardPageNext) outputs.
*
* Requires that you manually tell the wizard to navigate forward from
* the hostComponent, usually with a call to Wizard.forceNext() or
* wizard.next();
*
* @memberof ClrWizardPage
*/
get stopNext() {
return this._stopNext;
}
set stopNext(val) {
const valBool = !!val;
if (valBool !== this._stopNext) {
this._stopNext = valBool;
}
}
/**
* A read-only getter that generates an ID string for the wizard page from
* either the value passed to the ClrWizardPage "id" input or a wizard page
* counter shared across all wizard pages in the application.
*
* Note that the value passed into the ID input Will be prefixed with
* "clr-wizard-page-".
*
* @readonly
*
* @memberof ClrWizardPage
*/
get id() {
// covers things like null, undefined, false, and empty string
// while allowing zero to pass
const idIsNonZeroFalsy = !this._id && this._id !== 0;
// in addition to non-zero falsy we also want to make sure _id is not a negative
// number.
if (idIsNonZeroFalsy || this._id < 0) {
// guard here in the event that input becomes undefined or null by accident
this._id = (wizardPageIndex++).toString();
}
return `clr-wizard-page-${this._id}`;
}
/**
* A read-only getter that serves as a convenience for those who would rather
* not think in the terms of !ClrWizardPage.nextStepDisabled. For some use cases,
* ClrWizardPage.readyToComplete is more logical and declarative.
*
* @memberof WizardPage
*
*/
get readyToComplete() {
return !this.nextStepDisabled;
}
/**
* A page is marked as completed if it is both readyToComplete and completed,
* as in the next or finish action has been executed while this page was current.
*
* Note there is and open question about how to handle pages that are marked
* complete but who are no longer readyToComplete. This might indicate an error
* state for the ClrWizardPage. Currently, the wizard does not acknowledge this state
* and only returns that the page is incomplete.
*
* @memberof WizardPage
*
*/
get completed() {
return this._complete && this.readyToComplete;
// FOR V2: UNWIND COMPLETED, READYTOCOMPLETE, AND ERRORS
// SUCH THAT ERRORS IS ITS OWN INPUT. IF A STEP IS
// INCOMPLETE AND ERRORED, ERRORED WILL NOT SHOW.
// FIRST QUESTION: AM I GREY OR COLORED?
// SECOND QUESTION: AM I GREEN OR RED?
}
/**
* A ClrWizardPage can be manually set to completed using this boolean setter.
* It is recommended that users rely on the convenience functions in the wizard
* and navigation service instead of manually setting pages’ completion state.
*
* @memberof ClrWizardPage
*/
set completed(value) {
this._complete = value;
}
/**
* Checks with the navigation service to see if it is the current page.
*
* @memberof WizardPage
*
*/
get current() {
return this.navService.currentPage === this;
}
get disabled() {
return !this.enabled;
}
/**
* A read-only getter that returns whether or not the page is navigable
* in the wizard. A wizard page can be navigated to if it is completed
* or the page before it is completed.
*
* This getter handles the logic for enabling or disabling the links in
* the step nav on the left Side of the wizard.
*
* @memberof WizardPage
*
*/
get enabled() {
return this.current || this.completed || this.previousCompleted;
}
/**
* A read-only getter that returns whether or not the page before this
* ClrWizardPage is completed. This is useful for determining whether or not
* a page is navigable if it is not current or already completed.
*
* @memberof WizardPage
*
*/
get previousCompleted() {
const previousPage = this.pageCollection.getPreviousPage(this);
if (!previousPage) {
return true;
}
return previousPage.completed;
}
/**
*
* @memberof WizardPage
*
*/
get title() {
return this.pageTitle.pageTitleTemplateRef;
}
/**
*
* @memberof WizardPage
*
*/
get navTitle() {
if (this.pageNavTitle) {
return this.pageNavTitle.pageNavTitleTemplateRef;
}
return this.pageTitle.pageTitleTemplateRef;
}
/**
*
* @memberof WizardPage
*
*/
get headerActions() {
if (!this._headerActions) {
return undefined;
}
return this._headerActions.pageHeaderActionsTemplateRef;
}
/**
*
* @memberof WizardPage
*
*/
get hasHeaderActions() {
return !!this._headerActions;
}
/**
*
* @memberof WizardPage
*
*/
get buttons() {
if (!this._buttons) {
return undefined;
}
return this._buttons.pageButtonsTemplateRef;
}
/**
* A read-only getter that returns a boolean that says whether or
* not the ClrWizardPage includes buttons. Used to determine if the
* Wizard should override the default button set defined as
* its direct children.
*
* @memberof WizardPage
*
*/
get hasButtons() {
return !!this._buttons;
}
/**
* A read-only getter that returns the id used by the step nav item associated with the page.
*
* ClrWizardPage needs this ID string for aria information.
*
* @memberof WizardPage
*
*/
get stepItemId() {
return this.pageCollection.getStepItemIdForPage(this);
}
/**
* Links the nav service and establishes the current page if one is not defined.
*
* @memberof WizardPage
*
*/
ngOnInit() {
const navService = this.navService;
if (!navService.currentPage && !navService.navServiceLoaded) {
this.makeCurrent();
this.navService.navServiceLoaded = true;
}
}
/**
* Uses the nav service to make the ClrWizardPage the current page in the
* wizard. Bypasses all checks but still emits the ClrWizardPage.onLoad
* (clrWizardPageOnLoad) output.
*
* In most cases, it is better to use the default navigation functions
* in Wizard.
*
* @memberof WizardPage
*
*/
makeCurrent() {
this.navService.currentPage = this;
}
}
ClrWizardPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPage, deps: [{ token: WizardNavigationService }, { token: PageCollectionService }, { token: ButtonHubService }], target: i0.ɵɵFactoryTarget.Component });
ClrWizardPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardPage, selector: "clr-wizard-page", inputs: { _id: ["id", "_id"], preventDefault: ["clrWizardPagePreventDefault", "preventDefault"], nextStepDisabled: ["clrWizardPageNextDisabled", "nextStepDisabled"], previousStepDisabled: ["clrWizardPagePreviousDisabled", "previousStepDisabled"], hasError: ["clrWizardPageHasError", "hasError"], stopCancel: ["clrWizardPagePreventDefaultCancel", "stopCancel"], stopNext: ["clrWizardPagePreventDefaultNext", "stopNext"] }, outputs: { nextStepDisabledChange: "clrWizardPageNextDisabledChange", previousStepDisabledChange: "clrWizardPagePreviousDisabledChange", stopCancelChange: "clrWizardPagePreventDefaultCancelChange", onCommit: "clrWizardPageOnCommit", onLoad: "clrWizardPageOnLoad", pageOnCancel: "clrWizardPageOnCancel", finishButtonClicked: "clrWizardPageFinish", previousButtonClicked: "clrWizardPagePrevious", nextButtonClicked: "clrWizardPageNext", dangerButtonClicked: "clrWizardPageDanger", primaryButtonClicked: "clrWizardPagePrimary", customButtonClicked: "clrWizardPageCustomButton" }, host: { properties: { "id": "id", "attr.aria-hidden": "!current", "attr.aria-labelledby": "stepItemId", "class.active": "current", "class.clr-wizard-page": "true" } }, queries: [{ propertyName: "pageTitle", first: true, predicate: ClrWizardPageTitle, descendants: true, static: true }, { propertyName: "pageNavTitle", first: true, predicate: ClrWizardPageNavTitle, descendants: true, static: true }, { propertyName: "_buttons", first: true, predicate: ClrWizardPageButtons, descendants: true, static: true }, { propertyName: "_headerActions", first: true, predicate: ClrWizardPageHeaderActions, descendants: true, static: true }], ngImport: i0, template: '', isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardPage, decorators: [{
type: Component,
args: [{
selector: 'clr-wizard-page',
template: '',
host: {
'[id]': 'id',
'[attr.aria-hidden]': '!current',
'[attr.aria-labelledby]': 'stepItemId',
'[class.active]': 'current',
'[class.clr-wizard-page]': 'true',
},
}]
}], ctorParameters: function () { return [{ type: WizardNavigationService }, { type: PageCollectionService }, { type: ButtonHubService }]; }, propDecorators: { _id: [{
type: Input,
args: ['id']
}], preventDefault: [{
type: Input,
args: ['clrWizardPagePreventDefault']
}], nextStepDisabledChange: [{
type: Output,
args: ['clrWizardPageNextDisabledChange']
}], previousStepDisabledChange: [{
type: Output,
args: ['clrWizardPagePreviousDisabledChange']
}], stopCancelChange: [{
type: Output,
args: ['clrWizardPagePreventDefaultCancelChange']
}], onCommit: [{
type: Output,
args: ['clrWizardPageOnCommit']
}], onLoad: [{
type: Output,
args: ['clrWizardPageOnLoad']
}], pageOnCancel: [{
type: Output,
args: ['clrWizardPageOnCancel']
}], finishButtonClicked: [{
type: Output,
args: ['clrWizardPageFinish']
}], previousButtonClicked: [{
type: Output,
args: ['clrWizardPagePrevious']
}], nextButtonClicked: [{
type: Output,
args: ['clrWizardPageNext']
}], dangerButtonClicked: [{
type: Output,
args: ['clrWizardPageDanger']
}], primaryButtonClicked: [{
type: Output,
args: ['clrWizardPagePrimary']
}], customButtonClicked: [{
type: Output,
args: ['clrWizardPageCustomButton']
}], pageTitle: [{
type: ContentChild,
args: [ClrWizardPageTitle, { static: true }]
}], pageNavTitle: [{
type: ContentChild,
args: [ClrWizardPageNavTitle, { static: true }]
}], _buttons: [{
type: ContentChild,
args: [ClrWizardPageButtons, { static: true }]
}], _headerActions: [{
type: ContentChild,
args: [ClrWizardPageHeaderActions, { static: true }]
}], nextStepDisabled: [{
type: Input,
args: ['clrWizardPageNextDisabled']
}], previousStepDisabled: [{
type: Input,
args: ['clrWizardPagePreviousDisabled']
}], hasError: [{
type: Input,
args: ['clrWizardPageHasError']
}], stopCancel: [{
type: Input,
args: ['clrWizardPagePreventDefaultCancel']
}], stopNext: [{
type: Input,
args: ['clrWizardPagePreventDefaultNext']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardTitle {
}
ClrWizardTitle.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
ClrWizardTitle.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardTitle, selector: "clr-wizard-title", inputs: { headingLevel: ["clrHeadingLevel", "headingLevel"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardTitle, decorators: [{
type: Directive,
args: [{
selector: 'clr-wizard-title',
}]
}], propDecorators: { headingLevel: [{
type: Input,
args: ['clrHeadingLevel']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardStepnavItem {
constructor(navService, pageCollection, commonStrings) {
this.navService = navService;
this.pageCollection = pageCollection;
this.commonStrings = commonStrings;
}
get id() {
this.pageGuard();
return this.pageCollection.getStepItemIdForPage(this.page);
}
get stepAriaCurrent() {
return this.isCurrent && 'step';
}
get isDisabled() {
this.pageGuard();
return this.page.disabled || this.navService.wizardStopNavigation || this.navService.wizardDisableStepnav;
}
get isCurrent() {
this.pageGuard();
return this.page.current;
}
get isComplete() {
this.pageGuard();
return this.page.completed;
}
get hasError() {
this.pageGuard();
return this.page.hasError && this.isComplete;
}
get canNavigate() {
this.pageGuard();
return this.pageCollection.previousPageIsCompleted(this.page);
}
click() {
this.pageGuard();
// if we click on our own stepnav or a disabled stepnav, we don't want to do anything
if (this.isDisabled || this.isCurrent) {
return;
}
this.navService.goTo(this.page);
}
pageGuard() {
if (!this.page) {
throw new Error('Wizard stepnav item is not associated with a wizard page.');
}
}
}
ClrWizardStepnavItem.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardStepnavItem, deps: [{ token: WizardNavigationService }, { token: PageCollectionService }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
ClrWizardStepnavItem.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardStepnavItem, selector: "[clr-wizard-stepnav-item]", inputs: { page: "page" }, host: { properties: { "id": "id", "attr.aria-current": "stepAriaCurrent", "attr.aria-controls": "page.id", "class.clr-nav-link": "true", "class.nav-item": "true", "class.active": "isCurrent", "class.disabled": "isDisabled", "class.no-click": "!canNavigate", "class.complete": "isComplete", "class.error": "hasError" } }, ngImport: i0, template: `
`, isInline: true, dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: CdsIconCustomTag, selector: "cds-icon" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardStepnavItem, decorators: [{
type: Component,
args: [{
selector: '[clr-wizard-stepnav-item]',
template: `
`,
host: {
'[id]': 'id',
'[attr.aria-current]': 'stepAriaCurrent',
'[attr.aria-controls]': 'page.id',
'[class.clr-nav-link]': 'true',
'[class.nav-item]': 'true',
'[class.active]': 'isCurrent',
'[class.disabled]': 'isDisabled',
'[class.no-click]': '!canNavigate',
'[class.complete]': 'isComplete',
'[class.error]': 'hasError',
},
}]
}], ctorParameters: function () { return [{ type: WizardNavigationService }, { type: PageCollectionService }, { type: ClrCommonStringsService }]; }, propDecorators: { page: [{
type: Input,
args: ['page']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizardStepnav {
constructor(pageService) {
this.pageService = pageService;
}
}
ClrWizardStepnav.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardStepnav, deps: [{ token: PageCollectionService }], target: i0.ɵɵFactoryTarget.Component });
ClrWizardStepnav.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardStepnav, selector: "clr-wizard-stepnav", host: { classAttribute: "clr-wizard-stepnav" }, ngImport: i0, template: `
`,
host: { class: 'clr-wizard-stepnav' },
}]
}], ctorParameters: function () { return [{ type: PageCollectionService }]; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClrWizard {
constructor(platformId, commonStrings, navService, pageCollection, buttonService, headerActionService, elementRef, differs) {
this.platformId = platformId;
this.commonStrings = commonStrings;
this.navService = navService;
this.pageCollection = pageCollection;
this.buttonService = buttonService;
this.headerActionService = headerActionService;
this.elementRef = elementRef;
/**
* Set the aria-label for the stepnav section of the wizard. Set using `[clrWizardStepnavAriaLabel]` input.
*/
this.stepnavAriaLabel = this.commonStrings.keys.wizardStepnavAriaLabel;
/**
* Set the modal size of the wizard. Set using `[clrWizardSize]` input.
*/
this.size = 'xl';
/**
* Tells the modal part of the wizard whether it should have a close "X"
* in the top right corner. Set using `[clrWizardClosable]` input.
*/
this.closable = true;
/**
* Used to communicate to the underlying modal that animations are not
* wanted. Primary use is for the display of static/inline wizards.
* Set using `[clrWizardPreventModalAnimation]` input.
*/
this._stopModalAnimations = false;
/**
* Emits when the wizard is opened or closed.
* Listen via `(clrWizardOpenChange)` event.
*/
this._openChanged = new EventEmitter(false);
/**
* Emits when the wizard is canceled. Listen via `(clrWizardOnCancel)` event.
* Can be combined with the `[clrWizardPreventDefaultCancel]` input to create
* wizard-level custom cancel routines.
*/
this.onCancel = new EventEmitter(false);
/**
* Emits when the wizard is completed. Listen via `(clrWizardOnFinish)` event.
* Can be combined with the `[clrWizardPreventDefaultNext]` input to create
* wizard-level custom completion routines.
*/
this.wizardFinished = new EventEmitter(false);
/**
* Emits when the wizard is reset. Listen via `(clrWizardOnReset)` event.
*/
this.onReset = new EventEmitter(false);
/**
* Emits when the current page has changed. Listen via `(clrWizardCurrentPageChanged)` event.
* output. Useful for non-blocking validation.
*/
this.currentPageChanged = new EventEmitter(false);
/**
* Emits when the wizard moves to the next page. Listen via `(clrWizardOnNext)` event.
* Can be combined with the `[clrWizardPreventDefaultNext]` input to create
* wizard-level custom navigation routines, which are useful for validation.
*/
this.onMoveNext = new EventEmitter(false);
/**
* Emits when the wizard moves to the previous page. Can be useful for validation.
* Listen via `(clrWizardOnPrevious)` event.
*/
this.onMovePrevious = new EventEmitter(false);
this._open = false;
this.wizardId = uniqueIdFactory();
this._forceForward = false;
this._stopNext = false;
this._stopCancel = false;
this._stopNavigation = false;
this._disableStepnav = false;
this.subscriptions = [];
this.subscriptions.push(this.listenForNextPageChanges(), this.listenForPreviousPageChanges(), this.listenForCancelChanges(), this.listenForFinishedChanges(), this.listenForPageChanges());
this.differ = differs.find([]).create(null);
}
/**
* Resets page completed states when navigating backwards.
* Set using `[clrWizardForceForwardNavigation]` input.
*/
get forceForward() {
return this._forceForward;
}
set forceForward(value) {
this._forceForward = !!value;
this.navService.forceForwardNavigation = value;
}
/**
* Toggles open/close of the wizard component.
* Set using the `[clrWizardOpen]` input.
*/
set clrWizardOpen(open) {
if (open) {
this.buttonService.buttonsReady = true;
}
this._open = open;
}
/**
* Prevents ClrWizard from moving to the next page or closing itself on finishing.
* Set using the `[clrWizardPreventDefaultNext]` input. Note that using stopNext
* will require you to create your own calls to .next() and .finish() in your
* host component to make the ClrWizard work as expected.
*/
get stopNext() {
return this._stopNext;
}
set stopNext(value) {
this._stopNext = !!value;
this.navService.wizardHasAltNext = value;
}
/**
* Prevents ClrWizard from closing when the cancel button or close "X" is clicked.
* Set using the `[clrWizardPreventDefaultCancel]` input.
*
* Note that using stopCancel will require you to create your own calls to `close()` in your host compone`nt
* to make the ClrWizard work as expected. Useful for doing checks or prompts
* before closing a ClrWizard.
*/
get stopCancel() {
return this._stopCancel;
}
set stopCancel(value) {
this._stopCancel = !!value;
this.navService.wizardHasAltCancel = value;
}
/**
* Prevents ClrWizard from performing any form of navigation away from the current
* page. Set using the `[clrWizardPreventNavigation]` input.
* Note that stopNavigation is meant to freeze the wizard in place, typically
* during a long validation or background action where you want the wizard to
* display loading content but not allow the user to execute navigation in
* the stepnav, close X, or the back, finish, or next buttons.
*/
get stopNavigation() {
return this._stopNavigation;
}
set stopNavigation(value) {
this._stopNavigation = !!value;
this.navService.wizardStopNavigation = value;
}
/**
* Prevents clicks on the links in the stepnav from working.
* Set using `[clrWizardDisableStepnav]` input.
* A more granular bypassing of navigation which can be useful when your
* ClrWizard is in a state of completion and you don't want users to be
* able to jump backwards and change things.
*/
get disableStepnav() {
return this._disableStepnav;
}
set disableStepnav(value) {
this._disableStepnav = !!value;
this.navService.wizardDisableStepnav = value;
}
get currentPage() {
return this.navService.currentPage;
}
set currentPage(page) {
this.navService.goTo(page, true);
}
get isLast() {
return this.navService.currentPageIsLast;
}
get isFirst() {
return this.navService.currentPageIsFirst;
}
get isInline() {
return this.elementRef.nativeElement.classList.contains('clr-wizard--inline');
}
get stopModalAnimations() {
return this._stopModalAnimations ? 'true' : 'false';
}
ngAfterContentInit() {
this.pageCollection.pages = this.pages;
this.headerActionService.wizardHeaderActions = this.headerActions;
this.initializeButtons();
}
ngDoCheck() {
this.updateNavOnPageChanges();
}
ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe());
}
/**
* Marks Wizard as finished. By default it does not execute event
* emissions or checks before completing and closing. This method is commonly
* used as part of an alternative navigation with `[clrWizardPreventDefaultNext]`.
*
* If `skipChecksAndEmits` is true, the wizard will complete and close
* regardless of the state of its current page. This is useful for alternative
* navigation where event emissions have already been done and firing them again
* may cause an event loop.
*/
finish(skipChecksAndEmits = true) {
if (skipChecksAndEmits) {
this.forceFinish();
}
else {
this.navService.finish();
}
}
/**
* Marks the wizard as finished but does run checks and emissions.
* Good for a last step in an alternate workflow. Does the same thing as
* calling `ClrWizard.finish(true)` or `ClrWizard.finish()` without a parameter.
*/
forceFinish() {
if (this.stopNavigation) {
return;
}
this.close();
}
/**
* Opens the wizard. If there is no current page defined, sets the first page in the wizard to be current.
*/
open() {
this._open = true;
if (!this.currentPage) {
this.navService.setFirstPageCurrent();
}
// Only render buttons when wizard is opened, to avoid chocolate errors
this.buttonService.buttonsReady = true;
this._openChanged.emit(true);
}
/**
* Closes the wizard. Call this directly instead of `cancel()` to implement alternative cancel functionality.
*/
close() {
if (this.stopNavigation) {
return;
}
this._open = false;
this._openChanged.emit(false);
}
/**
* Used to open and close the wizard. By default the wizard will
* close if invoked with no parameter. If parameter is true wizard will open
* else if false will close.
*/
toggle(open) {
if (open) {
this.open();
}
else {
this.close();
}
}
/**
* Moves the wizard to the previous page.
*/
previous() {
this.navService.previous();
}
/**
* By default, `next()` does not execute event emissions.
* This method is commonly called as part of an alternative navigation
* with `[clrWizardPreventDefaultNext]`. The wizard will move to the next page
* regardless of the state of its current page. This is useful for alternative
* navigation where event emissions have already been done and firing them again
* may cause an event loop.
*
* If `skipChecksAndEmits` is false, the wizard will execute default checks
* and emit events as normal. This is useful for custom buttons or programmatic
* workflows that are not executing the wizards default checks and emissions.
* It is another way to navigate without having to rewrite the wizard’s default
* functionality from scratch.
*/
next(skipChecksAndEmits = true) {
if (skipChecksAndEmits) {
this.forceNext();
}
else {
this.navService.next();
}
}
/**
* Moves the wizard to the next page without the checks and emissions.
* Good for a last step in an alternate workflow.
* Alias for `ClrWizard.next(true)` or `ClrWizard.next()`
*/
forceNext() {
this.navService.forceNext();
}
/**
* Cancels and closes the wizard. Do not use this for an override of the cancel
* the functionality with `[clrWizardPreventDefaultCancel]`, `[clrWizardPreventPageDefaultCancel]`,
* or `[clrWizardPagePreventDefault]` because it will initiate the same checks
* and event emissions that invoked your event handler. Use `ClrWizard.close()` instead.
*/
cancel() {
this.navService.cancel();
}
/**
* Overrides behavior of the underlying modal to avoid collisions with
* alternative cancel functionality. In most cases, use `ClrWizard.cancel()` instead.
*/
modalCancel() {
if (this.closable) {
this.checkAndCancel();
}
}
/**
* Checks for alternative cancel flows defined at the current page or
* wizard level. Performs a canceled if not. Emits events that initiate
* the alternative cancel outputs `(clrWizardPageOnCancel)` and `(clrWizardOnCancel)`.
*/
checkAndCancel() {
const currentPage = this.currentPage;
const currentPageHasOverrides = currentPage.stopCancel || currentPage.preventDefault;
if (this.stopNavigation) {
return;
}
currentPage.pageOnCancel.emit();
if (!currentPageHasOverrides) {
this.onCancel.emit();
}
if (!this.stopCancel && !currentPageHasOverrides) {
this.close();
}
}
/**
* Navigates to a given page in the Wizard. Navigation will invoke the wizard’s default
* checks and event emissions.
*
* The format of the expected ID parameter can be found in the return of the
* ClrWizardPage.id getter, usually prefixed with `clr-wizard-page-` and then either a
* numeric ID or the ID specified for the `ClrWizardPage` component’s `id` input.
*/
goTo(pageId) {
if (!pageId) {
return;
}
this.navService.goTo(pageId);
}
/**
* Reset sets all WizardPages to incomplete and sets the first page in the `ClrWizard` to
* be the current page, resetting the wizard navigation.
* Use `(clrWizardOnReset)` event to reset the data or model of your wizard.
*/
reset() {
this.pageCollection.reset();
this.onReset.next();
}
listenForNextPageChanges() {
return this.navService.movedToNextPage.pipe(filter(() => isPlatformBrowser(this.platformId))).subscribe(() => {
this.onMoveNext.emit();
this.pageTitle?.nativeElement.focus();
});
}
listenForPreviousPageChanges() {
return this.navService.movedToPreviousPage.pipe(filter(() => isPlatformBrowser(this.platformId))).subscribe(() => {
this.onMovePrevious.emit();
this.pageTitle?.nativeElement.focus();
});
}
listenForCancelChanges() {
return this.navService.notifyWizardCancel.subscribe(() => this.checkAndCancel());
}
listenForFinishedChanges() {
return this.navService.wizardFinished.subscribe(() => this.emitWizardFinished());
}
listenForPageChanges() {
return this.navService.currentPageChanged.subscribe(() => {
// Added to address VPAT-749:
// When clicking on a wizard tab, focus should move to that
// tabs content to make the wizard more accessible.
this.pageTitle?.nativeElement.focus();
this.currentPageChanged.emit();
});
}
updateNavOnPageChanges() {
const changes = this.differ.diff(this.pages);
if (changes) {
changes.forEachAddedItem(() => this.navService.updateNavigation());
changes.forEachRemovedItem(() => this.navService.updateNavigation());
}
}
initializeButtons() {
// Only trigger buttons ready if default is open (inlined)
if (this._open) {
this.buttonService.buttonsReady = true;
}
}
emitWizardFinished() {
if (!this.stopNext) {
this.forceFinish();
}
this.wizardFinished.emit();
}
}
ClrWizard.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizard, deps: [{ token: PLATFORM_ID }, { token: ClrCommonStringsService }, { token: WizardNavigationService }, { token: PageCollectionService }, { token: ButtonHubService }, { token: HeaderActionService }, { token: i0.ElementRef }, { token: i0.IterableDiffers }], target: i0.ɵɵFactoryTarget.Component });
ClrWizard.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizard, selector: "clr-wizard", inputs: { stepnavAriaLabel: ["clrWizardStepnavAriaLabel", "stepnavAriaLabel"], size: ["clrWizardSize", "size"], closable: ["clrWizardClosable", "closable"], _stopModalAnimations: ["clrWizardPreventModalAnimation", "_stopModalAnimations"], forceForward: ["clrWizardForceForwardNavigation", "forceForward"], clrWizardOpen: "clrWizardOpen", stopNext: ["clrWizardPreventDefaultNext", "stopNext"], stopCancel: ["clrWizardPreventDefaultCancel", "stopCancel"], stopNavigation: ["clrWizardPreventNavigation", "stopNavigation"], disableStepnav: ["clrWizardDisableStepnav", "disableStepnav"] }, outputs: { _openChanged: "clrWizardOpenChange", onCancel: "clrWizardOnCancel", wizardFinished: "clrWizardOnFinish", onReset: "clrWizardOnReset", currentPageChanged: "clrWizardCurrentPageChanged", onMoveNext: "clrWizardOnNext", onMovePrevious: "clrWizardOnPrevious" }, host: { properties: { "class.clr-wizard": "true", "class.wizard-md": "size == 'md'", "class.wizard-lg": "size == 'lg'", "class.wizard-xl": "size == 'xl'", "class.lastPage": "navService.currentPageIsLast" } }, providers: [WizardNavigationService, PageCollectionService, ButtonHubService, HeaderActionService], queries: [{ propertyName: "wizardTitle", first: true, predicate: ClrWizardTitle, descendants: true }, { propertyName: "pages", predicate: ClrWizardPage, descendants: true }, { propertyName: "headerActions", predicate: ClrWizardHeaderAction }], viewQueries: [{ propertyName: "pageTitle", first: true, predicate: ["pageTitle"], descendants: true }], ngImport: i0, template: "\n\n\n
\n \n\n" }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: ClrCommonStringsService }, { type: WizardNavigationService }, { type: PageCollectionService }, { type: ButtonHubService }, { type: HeaderActionService }, { type: i0.ElementRef }, { type: i0.IterableDiffers }]; }, propDecorators: { stepnavAriaLabel: [{
type: Input,
args: ['clrWizardStepnavAriaLabel']
}], size: [{
type: Input,
args: ['clrWizardSize']
}], closable: [{
type: Input,
args: ['clrWizardClosable']
}], _stopModalAnimations: [{
type: Input,
args: ['clrWizardPreventModalAnimation']
}], _openChanged: [{
type: Output,
args: ['clrWizardOpenChange']
}], onCancel: [{
type: Output,
args: ['clrWizardOnCancel']
}], wizardFinished: [{
type: Output,
args: ['clrWizardOnFinish']
}], onReset: [{
type: Output,
args: ['clrWizardOnReset']
}], currentPageChanged: [{
type: Output,
args: ['clrWizardCurrentPageChanged']
}], onMoveNext: [{
type: Output,
args: ['clrWizardOnNext']
}], onMovePrevious: [{
type: Output,
args: ['clrWizardOnPrevious']
}], pageTitle: [{
type: ViewChild,
args: ['pageTitle']
}], pages: [{
type: ContentChildren,
args: [ClrWizardPage, { descendants: true }]
}], headerActions: [{
type: ContentChildren,
args: [ClrWizardHeaderAction]
}], wizardTitle: [{
type: ContentChild,
args: [ClrWizardTitle]
}], forceForward: [{
type: Input,
args: ['clrWizardForceForwardNavigation']
}], clrWizardOpen: [{
type: Input,
args: ['clrWizardOpen']
}], stopNext: [{
type: Input,
args: ['clrWizardPreventDefaultNext']
}], stopCancel: [{
type: Input,
args: ['clrWizardPreventDefaultCancel']
}], stopNavigation: [{
type: Input,
args: ['clrWizardPreventNavigation']
}], disableStepnav: [{
type: Input,
args: ['clrWizardDisableStepnav']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const DEFAULT_BUTTON_TYPES = {
cancel: 'cancel',
previous: 'previous',
next: 'next',
finish: 'finish',
danger: 'danger',
};
const CUSTOM_BUTTON_TYPES = {
cancel: 'custom-cancel',
previous: 'custom-previous',
next: 'custom-next',
finish: 'custom-finish',
danger: 'custom-danger',
};
class ClrWizardButton {
constructor(navService, buttonService) {
this.navService = navService;
this.buttonService = buttonService;
this.type = '';
this.disabled = false;
this.hidden = false;
// EventEmitter which is emitted when a button is clicked.
this.wasClicked = new EventEmitter(false);
}
get isCancel() {
return this.checkDefaultAndCustomType(this.type, 'cancel');
}
get isNext() {
return this.checkDefaultAndCustomType(this.type, 'next');
}
get isPrevious() {
return this.checkDefaultAndCustomType(this.type, 'previous');
}
get isFinish() {
return this.checkDefaultAndCustomType(this.type, 'finish');
}
get isDanger() {
return this.checkDefaultAndCustomType(this.type, 'danger');
}
get isPrimaryAction() {
return this.isNext || this.isDanger || this.isFinish;
}
get _disabledAttribute() {
if (this.isDisabled) {
return '';
}
return null;
}
get isDisabled() {
// dealing with negatives here. cognitively easier to think of it like this...
const disabled = true;
const nav = this.navService;
const page = this.navService.currentPage;
// Ensure we don't change the response until buttons are ready to avoid chocolate
if (!this.buttonService.buttonsReady) {
return !disabled;
}
if (this.disabled || nav.wizardStopNavigation || !page) {
return true;
}
if (this.isCancel) {
return !disabled;
}
if (this.isPrevious && (nav.currentPageIsFirst || page.previousStepDisabled)) {
return disabled;
}
if (this.isDanger && !page.readyToComplete) {
return disabled;
}
if (this.isNext && (nav.currentPageIsLast || !page.readyToComplete)) {
return disabled;
}
if (this.isFinish && (!nav.currentPageIsLast || !page.readyToComplete)) {
return disabled;
}
return !disabled;
}
get isHidden() {
// dealing with negatives here. cognitively easier to think of it like this...
const hidden = true;
const nav = this.navService;
// Ensure we don't change the response until buttons are ready to avoid chocolate
if (!this.buttonService.buttonsReady) {
return !hidden;
}
if (this.hidden) {
return true;
}
if (this.isCancel) {
return !hidden;
}
if (this.isPrevious && nav.currentPageIsFirst) {
return hidden;
}
if (this.isNext && nav.currentPageIsLast) {
return hidden;
}
if (this.isFinish && !nav.currentPageIsLast) {
return hidden;
}
return !hidden;
}
click() {
if (this.isDisabled) {
return;
}
this.wasClicked.emit(this.type);
this.buttonService.buttonClicked(this.type);
}
checkDefaultAndCustomType(valueToCheck = '', typeToLookUp) {
if (DEFAULT_BUTTON_TYPES[typeToLookUp] === valueToCheck) {
return true;
}
if (CUSTOM_BUTTON_TYPES[typeToLookUp] === valueToCheck) {
return true;
}
return false;
}
}
ClrWizardButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardButton, deps: [{ token: WizardNavigationService }, { token: ButtonHubService }], target: i0.ɵɵFactoryTarget.Component });
ClrWizardButton.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrWizardButton, selector: "clr-wizard-button", inputs: { type: "type", disabled: ["clrWizardButtonDisabled", "disabled"], hidden: ["clrWizardButtonHidden", "hidden"] }, outputs: { wasClicked: "clrWizardButtonClicked" }, host: { properties: { "attr.aria-hidden": "isHidden" }, classAttribute: "clr-wizard-btn-wrapper" }, ngImport: i0, template: `
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardButton, decorators: [{
type: Component,
args: [{
selector: 'clr-wizard-button',
template: `
`,
host: { class: 'clr-wizard-btn-wrapper', '[attr.aria-hidden]': 'isHidden' },
}]
}], ctorParameters: function () { return [{ type: WizardNavigationService }, { type: ButtonHubService }]; }, propDecorators: { type: [{
type: Input,
args: ['type']
}], disabled: [{
type: Input,
args: ['clrWizardButtonDisabled']
}], hidden: [{
type: Input,
args: ['clrWizardButtonHidden']
}], wasClicked: [{
type: Output,
args: ['clrWizardButtonClicked']
}] } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CLR_WIZARD_DIRECTIVES = [
ClrWizard,
ClrWizardPage,
ClrWizardStepnav,
ClrWizardStepnavItem,
ClrWizardButton,
ClrWizardHeaderAction,
ClrWizardTitle,
ClrWizardPageTitle,
ClrWizardPageNavTitle,
ClrWizardPageButtons,
ClrWizardPageHeaderActions,
];
class ClrWizardModule {
constructor() {
ClarityIcons.addIcons(errorStandardIcon);
}
}
ClrWizardModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClrWizardModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardModule, declarations: [ClrWizard,
ClrWizardPage,
ClrWizardStepnav,
ClrWizardStepnavItem,
ClrWizardButton,
ClrWizardHeaderAction,
ClrWizardTitle,
ClrWizardPageTitle,
ClrWizardPageNavTitle,
ClrWizardPageButtons,
ClrWizardPageHeaderActions], imports: [CommonModule, ClrModalModule, ClrAlertModule], exports: [ClrWizard,
ClrWizardPage,
ClrWizardStepnav,
ClrWizardStepnavItem,
ClrWizardButton,
ClrWizardHeaderAction,
ClrWizardTitle,
ClrWizardPageTitle,
ClrWizardPageNavTitle,
ClrWizardPageButtons,
ClrWizardPageHeaderActions] });
ClrWizardModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardModule, imports: [CommonModule, ClrModalModule, ClrAlertModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrWizardModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, ClrModalModule, ClrAlertModule],
declarations: [CLR_WIZARD_DIRECTIVES],
exports: [CLR_WIZARD_DIRECTIVES],
}]
}], ctorParameters: function () { return []; } });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
class ClarityModule {
}
ClarityModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClarityModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClarityModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.2", ngImport: i0, type: ClarityModule, exports: [ClrEmphasisModule,
ClrDataModule,
ClrIconModule,
ClrModalModule,
ClrLoadingModule,
ClrConditionalModule,
ClrFocusOnViewInitModule,
ClrButtonModule,
ClrFormsModule,
ClrLayoutModule,
ClrPopoverModule,
ClrWizardModule,
ClrStepperModule,
ClrSpinnerModule,
ClrProgressBarModule,
ClrPopoverModuleNext,
ClrTimelineModule] });
ClarityModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClarityModule, imports: [ClrEmphasisModule,
ClrDataModule,
ClrIconModule,
ClrModalModule,
ClrLoadingModule,
ClrConditionalModule,
ClrFocusOnViewInitModule,
ClrButtonModule,
ClrFormsModule,
ClrLayoutModule,
ClrPopoverModule,
ClrWizardModule,
ClrStepperModule,
ClrSpinnerModule,
ClrProgressBarModule,
ClrPopoverModuleNext,
ClrTimelineModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClarityModule, decorators: [{
type: NgModule,
args: [{
exports: [
ClrEmphasisModule,
ClrDataModule,
ClrIconModule,
ClrModalModule,
ClrLoadingModule,
ClrConditionalModule,
ClrFocusOnViewInitModule,
ClrButtonModule,
ClrFormsModule,
ClrLayoutModule,
ClrPopoverModule,
ClrWizardModule,
ClrStepperModule,
ClrSpinnerModule,
ClrProgressBarModule,
ClrPopoverModuleNext,
ClrTimelineModule,
],
}]
}] });
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
const CLR_MENU_POSITIONS = [
'bottom-left',
'bottom-right',
'top-left',
'top-right',
'left-bottom',
'left-top',
'right-bottom',
'right-top',
];
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/*
* Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
/**
* Generated bundle index. Do not edit.
*/
export { CHANGE_KEYS, CLR_ALERT_DIRECTIVES, CLR_BUTTON_GROUP_DIRECTIVES, CLR_DATAGRID_DIRECTIVES, CLR_DATEPICKER_DIRECTIVES, CLR_DROPDOWN_DIRECTIVES, CLR_ICON_DIRECTIVES, CLR_LAYOUT_DIRECTIVES, CLR_LOADING_BUTTON_DIRECTIVES, CLR_LOADING_DIRECTIVES, CLR_MENU_POSITIONS, CLR_MODAL_DIRECTIVES, CLR_NAVIGATION_DIRECTIVES, CLR_PROGRESS_BAR_DIRECTIVES, CLR_SIGNPOST_DIRECTIVES, CLR_SPINNER_DIRECTIVES, CLR_STACK_VIEW_DIRECTIVES, CLR_TABS_DIRECTIVES, CLR_TOOLTIP_DIRECTIVES, CLR_TREE_VIEW_DIRECTIVES, CLR_VERTICAL_NAV_DIRECTIVES, CLR_WIZARD_DIRECTIVES, CONDITIONAL_DIRECTIVES, CUSTOM_BUTTON_TYPES, CdsIconCustomTag, ClarityModule, ClrAbstractContainer, ClrAccordion, ClrAccordionContent, ClrAccordionDescription, ClrAccordionModule, ClrAccordionPanel, ClrAccordionTitle, ClrAlert, ClrAlertItem, ClrAlertModule, ClrAlertText, ClrAlerts, ClrAlertsPager, ClrAlignment, ClrAriaCurrentLink, ClrAxis, ClrButton, ClrButtonGroup, ClrButtonGroupModule, ClrButtonModule, ClrCalendar, ClrCheckbox, ClrCheckboxContainer, ClrCheckboxModule, ClrCheckboxWrapper, ClrCombobox, ClrComboboxContainer, ClrComboboxModule, ClrCommonFormsModule, ClrCommonStringsService, ClrConditionalModule, ClrControl, ClrControlContainer, ClrControlError, ClrControlHelper, ClrControlSuccess, ClrDataModule, ClrDatagrid, ClrDatagridActionBar, ClrDatagridActionOverflow, ClrDatagridCell, ClrDatagridColumn, ClrDatagridColumnSeparator, ClrDatagridColumnToggle, ClrDatagridColumnToggleButton, ClrDatagridDetail, ClrDatagridDetailBody, ClrDatagridDetailHeader, ClrDatagridFilter, ClrDatagridFooter, ClrDatagridHideableColumn, ClrDatagridItems, ClrDatagridItemsTrackBy, ClrDatagridModule, ClrDatagridPageSize, ClrDatagridPagination, ClrDatagridPlaceholder, ClrDatagridRow, ClrDatagridRowDetail, ClrDatagridSortOrder, ClrDatalist, ClrDatalistContainer, ClrDatalistInput, ClrDatalistModule, ClrDateContainer, ClrDateInput, ClrDateInputValidator, ClrDatepickerModule, ClrDatepickerViewManager, ClrDay, ClrDaypicker, ClrDestroyService, ClrDropdown, ClrDropdownItem, ClrDropdownMenu, ClrDropdownModule, ClrDropdownTrigger, ClrEmphasisModule, ClrExpandableAnimation, ClrFocusOnViewInit, ClrFocusOnViewInitModule, ClrForm, ClrFormLayout, ClrFormsModule, ClrHeader, ClrIconCustomTag, ClrIconModule, ClrIfActive, ClrIfDetail, ClrIfError, ClrIfExpanded, ClrIfOpen, ClrIfSuccess, ClrInput, ClrInputContainer, ClrInputModule, ClrLabel, ClrLayout, ClrLayoutModule, ClrLoading, ClrLoadingButton, ClrLoadingButtonModule, ClrLoadingModule, ClrLoadingState, ClrMainContainer, ClrMainContainerModule, ClrModal, ClrModalBody, ClrModalModule, ClrMonthpicker, ClrNavLevel, ClrNavigationModule, ClrOption, ClrOptionItems, ClrOptionSelected, ClrOptions, ClrPassword, ClrPasswordContainer, ClrPasswordModule, ClrPopoverAnchor, ClrPopoverContent, ClrPopoverEventsService, ClrPopoverHostDirective, ClrPopoverModule, ClrPopoverPositionService, ClrPopoverToggleService, ClrProgressBar, ClrProgressBarModule, ClrRadio, ClrRadioContainer, ClrRadioModule, ClrRadioWrapper, ClrRange, ClrRangeContainer, ClrRangeModule, ClrRecursiveForOf, ClrSelect, ClrSelectContainer, ClrSelectModule, ClrSelectedState, ClrSide, ClrSignpost, ClrSignpostContent, ClrSignpostModule, ClrSignpostTrigger, ClrSpinner, ClrSpinnerModule, ClrStackBlock, ClrStackContentInput, ClrStackHeader, ClrStackView, ClrStackViewCustomTags, ClrStackViewLabel, ClrStackViewModule, ClrStandaloneCdkTrapFocus, ClrStepButton, ClrStepButtonType, ClrStepper, ClrStepperModule, ClrStepperPanel, ClrStopEscapePropagationDirective, ClrTab, ClrTabContent, ClrTabLink, ClrTabOverflowContent, ClrTabs, ClrTabsModule, ClrTextarea, ClrTextareaContainer, ClrTextareaModule, ClrTimeline, ClrTimelineLayout, ClrTimelineModule, ClrTimelineStep, ClrTimelineStepDescription, ClrTimelineStepHeader, ClrTimelineStepState, ClrTimelineStepTitle, ClrTooltip, ClrTooltipContent, ClrTooltipModule, ClrTooltipTrigger, ClrTree, ClrTreeNode, ClrTreeNodeLink, ClrTreeViewModule, ClrVerticalNav, ClrVerticalNavGroup, ClrVerticalNavGroupChildren, ClrVerticalNavIcon, ClrVerticalNavLink, ClrVerticalNavModule, ClrWizard, ClrWizardButton, ClrWizardHeaderAction, ClrWizardModule, ClrWizardPage, ClrWizardPageButtons, ClrWizardPageHeaderActions, ClrWizardPageNavTitle, ClrWizardPageTitle, ClrWizardStepnav, ClrWizardStepnavItem, ClrWizardTitle, ClrYearpicker, DEFAULT_BUTTON_TYPES, DatagridNumericFilter, DatagridPropertyComparator, DatagridPropertyNumericFilter, DatagridPropertyStringFilter, DatagridStringFilter, EXPANDABLE_ANIMATION_DIRECTIVES, FOCUS_ON_VIEW_INIT, FOCUS_ON_VIEW_INIT_DIRECTIVES, IS_TOGGLE, IS_TOGGLE_PROVIDER, LoadingListener, MainContainerWillyWonka, NavDetectionOompaLoompa, TOGGLE_SERVICE, TOGGLE_SERVICE_PROVIDER, ToggleServiceFactory, WrappedFormControl, collapse, commonStringsDefault, fade, fadeSlide, isToggleFactory, slide, AccordionOompaLoompa as ÇlrAccordionOompaLoompa, AccordionWillyWonka as ÇlrAccordionWillyWonka, ActionableOompaLoompa as ÇlrActionableOompaLoompa, ActiveOompaLoompa as ÇlrActiveOompaLoompa, ClrPopoverCloseButton as ÇlrClrPopoverCloseButton, ClrPopoverModuleNext as ÇlrClrPopoverModuleNext, ClrPopoverOpenCloseButton as ÇlrClrPopoverOpenCloseButton, DatagridCellRenderer as ÇlrDatagridCellRenderer, DatagridDetailRegisterer as ÇlrDatagridDetailRegisterer, DatagridHeaderRenderer as ÇlrDatagridHeaderRenderer, DatagridMainRenderer as ÇlrDatagridMainRenderer, DatagridRowRenderer as ÇlrDatagridRowRenderer, ClrDatagridSelectionCellDirective as ÇlrDatagridSelectionCellDirective, DatagridWillyWonka as ÇlrDatagridWillyWonka, ExpandableOompaLoompa as ÇlrExpandableOompaLoompa, StepperOompaLoompa as ÇlrStepperOompaLoompa, StepperWillyWonka as ÇlrStepperWillyWonka, TabsWillyWonka as ÇlrTabsWillyWonka, WrappedCell as ÇlrWrappedCell, WrappedColumn as ÇlrWrappedColumn, WrappedRow as ÇlrWrappedRow };
//# sourceMappingURL=clr-angular.mjs.map