@clr/angular 15.14.1 → 15.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { Directive, NgModule, Optional, Input, Injectable, Component, ChangeDete
3
3
  import * as i5 from '@angular/common';
4
4
  import { CommonModule, isPlatformBrowser, DOCUMENT, NgForOf, getLocaleDayNames, FormStyle, TranslationWidth, getLocaleMonthNames, getLocaleFirstDayOfWeek, getLocaleDateFormat, FormatWidth } from '@angular/common';
5
5
  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';
6
- import { map, tap, startWith, distinctUntilChanged, pairwise, filter, debounceTime, takeUntil, take, first, switchMap } from 'rxjs/operators';
6
+ import { map, tap, startWith, distinctUntilChanged, filter, skipUntil, debounceTime, takeUntil, take, first, switchMap } from 'rxjs/operators';
7
7
  import * as i3 from 'rxjs';
8
8
  import { Subject, BehaviorSubject, Observable, fromEvent, isObservable, of, ReplaySubject, combineLatest, merge } from 'rxjs';
9
9
  import { trigger, transition, style, animate, state, keyframes } from '@angular/animations';
@@ -361,8 +361,8 @@ const commonStringsDefault = {
361
361
  * Password Input
362
362
  * Screen-reader text for the hide/show password field button
363
363
  */
364
- passwordHide: 'Hide password',
365
- passwordShow: 'Show password',
364
+ passwordHide: 'Hide password for {LABEL}',
365
+ passwordShow: 'Show password for {LABEL}',
366
366
  /**
367
367
  * Datagrid footer; sr-only text after the number of selected rows.
368
368
  */
@@ -941,6 +941,12 @@ class StepperModel extends AccordionModel {
941
941
  }
942
942
  });
943
943
  }
944
+ setPanelValid(panelId) {
945
+ this._panels[panelId].status = AccordionStatus.Complete;
946
+ }
947
+ setPanelInvalid(panelId) {
948
+ this._panels[panelId].status = AccordionStatus.Error;
949
+ }
944
950
  setPanelsWithErrors(ids) {
945
951
  ids.forEach(id => this.setPanelError(id));
946
952
  }
@@ -1021,6 +1027,14 @@ class StepperService extends AccordionService {
1021
1027
  this.accordion.resetPanels();
1022
1028
  this.emitUpdatedPanels();
1023
1029
  }
1030
+ setPanelValid(panelId) {
1031
+ this.accordion.setPanelValid(panelId);
1032
+ this.emitUpdatedPanels();
1033
+ }
1034
+ setPanelInvalid(panelId) {
1035
+ this.accordion.setPanelInvalid(panelId);
1036
+ this.emitUpdatedPanels();
1037
+ }
1024
1038
  setPanelsWithErrors(ids) {
1025
1039
  this.accordion.setPanelsWithErrors(ids);
1026
1040
  this.emitUpdatedPanels();
@@ -1083,11 +1097,16 @@ class ClrStepperPanel extends ClrAccordionPanel {
1083
1097
  this.panel = this.panel.pipe(tap(panel => this.triggerAllFormControlValidationIfError(panel)));
1084
1098
  this.stepperService.disablePanel(this.id, true);
1085
1099
  this.listenToFocusChanges();
1100
+ // not all stepper panels are guaranteed to have a form (i.e. empty template-driven)
1086
1101
  if (this.formGroup) {
1087
- // not all stepper panels are guaranteed to have a form (i.e. empty template-driven)
1088
- this.subscriptions.push(this.formGroup.statusChanges.pipe(pairwise()).subscribe(([prevStatus, newStatus]) => {
1089
- if ('VALID' === prevStatus && 'INVALID' === newStatus) {
1090
- this.stepperService.navigateToNextPanel(this.id, this.formGroup.valid);
1102
+ // set panel status on form status change only after the form becomes invalid
1103
+ const invalidStatusTrigger = this.formGroup.statusChanges.pipe(filter(status => status === 'INVALID'));
1104
+ this.subscriptions.push(this.formGroup.statusChanges.pipe(skipUntil(invalidStatusTrigger), distinctUntilChanged()).subscribe(status => {
1105
+ if (status === 'VALID') {
1106
+ this.stepperService.setPanelValid(this.id);
1107
+ }
1108
+ else if (status === 'INVALID') {
1109
+ this.stepperService.setPanelInvalid(this.id);
1091
1110
  }
1092
1111
  }));
1093
1112
  }
@@ -1933,12 +1952,12 @@ class ClrPopoverPositionService {
1933
1952
  *
1934
1953
  * Note, more than 3 viewport violations and there isn't anything we can do to help. Also when there are two
1935
1954
  * violations, we can't help if the violations are TOP+BOTTOM || LEFT+RIGHT => There is no transformation we
1936
- * can make to the postion that will help.
1955
+ * can make to the position that will help.
1937
1956
  *
1938
1957
  * Some examples:
1939
1958
  * There is only one error and Primary axis is VERTICAL
1940
1959
  * - this.handleVerticalAxisOneViolation has a switch that will use the error sum to apply the correct
1941
- * transform to the postion based on the reduction of visibilityViolations.
1960
+ * transform to the position based on the reduction of visibilityViolations.
1942
1961
  *
1943
1962
  * There are two errors and Primary axis is HORIZONTAL
1944
1963
  * - handleHorizontalAxisTwoViolations has a switch that uses the error sum to apply both transforms needed to
@@ -1970,6 +1989,14 @@ class ClrPopoverPositionService {
1970
1989
  if (this.currentContentCoords.top < 0) {
1971
1990
  this.contentOffsets.yOffset += Math.abs(this.currentContentCoords.top);
1972
1991
  }
1992
+ /**
1993
+ * This detects the condition where the popover is flipped because it would violate the bottom of the viewport, but flipping it results in the
1994
+ * popover rendering above the top of the body (y coordinate outside the body). In that event, it should be rendered within the body
1995
+ * as much as possible, so this logic sets the top of popover to render touching the top of the body.
1996
+ */
1997
+ if (this.contentOffsets.yOffset + this.currentAnchorCoords.y < 0) {
1998
+ this.contentOffsets.yOffset = 0 - this.currentContentCoords.top;
1999
+ }
1973
2000
  return this.contentOffsets;
1974
2001
  }
1975
2002
  handleVerticalAxisOneViolation(errorSum) {
@@ -3795,16 +3822,14 @@ class ClrButtonGroup {
3795
3822
  this.menuButtons = tempInMenuButtons;
3796
3823
  }
3797
3824
  handleFocusOnMenuOpen() {
3798
- if (this.menuButtons.length) {
3799
- this.toggleService.popoverVisible.pipe(takeUntil(this.destroy$)).subscribe(visible => {
3800
- if (visible) {
3801
- this.focusHandler.initialize({
3802
- menu: this.menu.nativeElement,
3803
- menuToggle: this.menuToggle.nativeElement,
3804
- });
3805
- }
3806
- });
3807
- }
3825
+ this.toggleService.popoverVisible.pipe(takeUntil(this.destroy$)).subscribe(visible => {
3826
+ if (visible) {
3827
+ this.focusHandler.initialize({
3828
+ menu: this.menu.nativeElement,
3829
+ menuToggle: this.menuToggle.nativeElement,
3830
+ });
3831
+ }
3832
+ });
3808
3833
  }
3809
3834
  }
3810
3835
  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 });
@@ -3903,7 +3928,7 @@ class ClrLoadingButton {
3903
3928
  }
3904
3929
  ClrLoadingButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrLoadingButton, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
3905
3930
  ClrLoadingButton.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrLoadingButton, selector: "button[clrLoading]", inputs: { disabled: "disabled" }, outputs: { clrLoadingChange: "clrLoadingChange" }, host: { properties: { "attr.disabled": "disabled? '' : null" } }, providers: [{ provide: LoadingListener, useExisting: ClrLoadingButton }], ngImport: i0, template: `
3906
- <ng-container [ngSwitch]="state">
3931
+ <span @parent [ngSwitch]="state">
3907
3932
  <span *ngSwitchCase="buttonState.LOADING">
3908
3933
  <span @spinner class="spinner spinner-inline"></span>
3909
3934
  </span>
@@ -3917,8 +3942,13 @@ ClrLoadingButton.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", versi
3917
3942
  <span *ngSwitchCase="buttonState.DEFAULT" @defaultButton>
3918
3943
  <ng-content></ng-content>
3919
3944
  </span>
3920
- </ng-container>
3945
+ </span>
3921
3946
  `, isInline: true, dependencies: [{ kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }], animations: [
3947
+ trigger('parent', [
3948
+ // Skip :enter animation on first render.
3949
+ // The button text/content should only be faded when transitioning to or from a non-default state.
3950
+ transition(':enter', []),
3951
+ ]),
3922
3952
  trigger('defaultButton', [
3923
3953
  transition(':enter', [style({ opacity: 0 }), animate('200ms 100ms ease-in', style({ opacity: 1 }))]),
3924
3954
  // TODO: see if we can get leave animation to work before spinner's enter animation
@@ -3946,7 +3976,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImpor
3946
3976
  args: [{
3947
3977
  selector: 'button[clrLoading]',
3948
3978
  template: `
3949
- <ng-container [ngSwitch]="state">
3979
+ <span @parent [ngSwitch]="state">
3950
3980
  <span *ngSwitchCase="buttonState.LOADING">
3951
3981
  <span @spinner class="spinner spinner-inline"></span>
3952
3982
  </span>
@@ -3960,10 +3990,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImpor
3960
3990
  <span *ngSwitchCase="buttonState.DEFAULT" @defaultButton>
3961
3991
  <ng-content></ng-content>
3962
3992
  </span>
3963
- </ng-container>
3993
+ </span>
3964
3994
  `,
3965
3995
  providers: [{ provide: LoadingListener, useExisting: ClrLoadingButton }],
3966
3996
  animations: [
3997
+ trigger('parent', [
3998
+ // Skip :enter animation on first render.
3999
+ // The button text/content should only be faded when transitioning to or from a non-default state.
4000
+ transition(':enter', []),
4001
+ ]),
3967
4002
  trigger('defaultButton', [
3968
4003
  transition(':enter', [style({ opacity: 0 }), animate('200ms 100ms ease-in', style({ opacity: 1 }))]),
3969
4004
  // TODO: see if we can get leave animation to work before spinner's enter animation
@@ -7126,25 +7161,24 @@ class ClrOptionItems {
7126
7161
  if (!this._rawItems || this.filter === undefined || this.filter === null) {
7127
7162
  return;
7128
7163
  }
7164
+ const normalizedFilterValue = normalizeValue(this.filter);
7129
7165
  if (this.optionService.showAllOptions) {
7130
7166
  this.filteredItems = this._rawItems;
7131
7167
  }
7132
7168
  else if (this._filterField) {
7133
7169
  this.filteredItems = this._rawItems.filter(item => {
7134
7170
  const objValue = item[this._filterField];
7135
- return objValue ? objValue.toString().toLowerCase().indexOf(this.filter.toLowerCase().toString()) > -1 : false;
7171
+ return objValue ? normalizeValue(objValue).includes(normalizedFilterValue) : false;
7136
7172
  });
7137
7173
  }
7138
7174
  else {
7139
7175
  // Filter by all item object values
7140
7176
  this.filteredItems = this._rawItems.filter(item => {
7141
7177
  if (typeof item !== 'object') {
7142
- return item.toString().toLowerCase().indexOf(this.filter.toString().toLowerCase()) > -1;
7178
+ return normalizeValue(item).includes(normalizedFilterValue);
7143
7179
  }
7144
7180
  const objValues = Object.values(item).filter(value => {
7145
- return value !== null && value !== undefined
7146
- ? value.toString().toLowerCase().indexOf(this.filter.toString().toLowerCase()) > -1
7147
- : false;
7181
+ return value !== null && value !== undefined ? normalizeValue(value).includes(normalizedFilterValue) : false;
7148
7182
  });
7149
7183
  return objValues.length > 0;
7150
7184
  });
@@ -7169,6 +7203,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImpor
7169
7203
  type: Input,
7170
7204
  args: ['clrOptionItemsField']
7171
7205
  }] } });
7206
+ function normalizeValue(value) {
7207
+ return value
7208
+ .toString()
7209
+ .normalize('NFD')
7210
+ .replace(/\p{Diacritic}/gu, '')
7211
+ .toLowerCase();
7212
+ }
7172
7213
 
7173
7214
  /*
7174
7215
  * Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
@@ -8373,7 +8414,7 @@ class DateIOService {
8373
8414
  return '';
8374
8415
  }
8375
8416
  getDateValueFromDateString(date) {
8376
- if (!date) {
8417
+ if (!date || typeof date !== 'string') {
8377
8418
  return null;
8378
8419
  }
8379
8420
  const dateParts = date.match(USER_INPUT_REGEX);
@@ -9579,9 +9620,11 @@ class ClrDateInput extends WrappedFormControl {
9579
9620
  }
9580
9621
  set min(dateString) {
9581
9622
  this.dateIOService.setMinDate(dateString);
9623
+ this.triggerControlValidation();
9582
9624
  }
9583
9625
  set max(dateString) {
9584
9626
  this.dateIOService.setMaxDate(dateString);
9627
+ this.triggerControlValidation();
9585
9628
  }
9586
9629
  get disabled() {
9587
9630
  if (this.dateFormControlService) {
@@ -9646,6 +9689,13 @@ class ClrDateInput extends WrappedFormControl {
9646
9689
  this.focusService.focused = focus;
9647
9690
  }
9648
9691
  }
9692
+ triggerControlValidation() {
9693
+ if (this.datepickerHasFormControl()) {
9694
+ // Set `emitEvent` to false to prevent unnecessary value change event. Status change event will be emitted by `setErrors` below.
9695
+ this.control.control?.updateValueAndValidity({ emitEvent: false });
9696
+ this.control.control?.setErrors(this.control.control.errors);
9697
+ }
9698
+ }
9649
9699
  populateServicesFromContainerComponent() {
9650
9700
  if (!this.container) {
9651
9701
  this.dateIOService = this.getProviderFromContainer(DateIOService);
@@ -9951,6 +10001,12 @@ class ClrPasswordContainer extends ClrAbstractContainer {
9951
10001
  this.show = !this.show;
9952
10002
  this.toggleService.next(this.show);
9953
10003
  }
10004
+ showPasswordText(label) {
10005
+ return this.commonStrings.parse(this.commonStrings.keys.passwordShow, { LABEL: label });
10006
+ }
10007
+ hidePasswordText(label) {
10008
+ return this.commonStrings.parse(this.commonStrings.keys.passwordHide, { LABEL: label });
10009
+ }
9954
10010
  }
9955
10011
  ClrPasswordContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: ClrPasswordContainer, deps: [{ token: IfControlStateService }, { token: LayoutService, optional: true }, { token: ControlClassService }, { token: NgControlService }, { token: FocusService }, { token: TOGGLE_SERVICE }, { token: ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component });
9956
10012
  ClrPasswordContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: ClrPasswordContainer, selector: "clr-password-container", inputs: { clrToggle: "clrToggle" }, host: { properties: { "class.clr-form-control": "true", "class.clr-form-control-disabled": "control?.disabled", "class.clr-row": "addGrid()" } }, providers: [
@@ -9976,7 +10032,7 @@ ClrPasswordContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", v
9976
10032
  >
9977
10033
  <cds-icon status="info" class="clr-password-eye-icon" [attr.shape]="show ? 'eye-hide' : 'eye'"></cds-icon>
9978
10034
  <span class="clr-sr-only">
9979
- {{ show ? commonStrings.keys.passwordHide : commonStrings.keys.passwordShow }}
10035
+ {{ show ? hidePasswordText(label?.labelText) : showPasswordText(label?.labelText) }}
9980
10036
  </span>
9981
10037
  </button>
9982
10038
  </div>
@@ -10020,7 +10076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImpor
10020
10076
  >
10021
10077
  <cds-icon status="info" class="clr-password-eye-icon" [attr.shape]="show ? 'eye-hide' : 'eye'"></cds-icon>
10022
10078
  <span class="clr-sr-only">
10023
- {{ show ? commonStrings.keys.passwordHide : commonStrings.keys.passwordShow }}
10079
+ {{ show ? hidePasswordText(label?.labelText) : showPasswordText(label?.labelText) }}
10024
10080
  </span>
10025
10081
  </button>
10026
10082
  </div>
@@ -13524,6 +13580,7 @@ class ClrSignpostTrigger {
13524
13580
  this.signpostIdService = signpostIdService;
13525
13581
  this.signpostFocusManager = signpostFocusManager;
13526
13582
  this.platformId = platformId;
13583
+ this.ariaExpanded = false;
13527
13584
  this.subscriptions = [];
13528
13585
  this.document = document;
13529
13586
  }
@@ -19909,7 +19966,7 @@ class ClrAlert {
19909
19966
  }
19910
19967
  const isCurrentAlert = this.multiAlertService?.currentAlert === this;
19911
19968
  this._closed = true;
19912
- if (this.multiAlertService) {
19969
+ if (this.multiAlertService?.activeAlerts) {
19913
19970
  this.multiAlertService.close(isCurrentAlert);
19914
19971
  }
19915
19972
  this._closedChanged.emit(true);
@@ -22420,7 +22477,7 @@ ClrSignpostContent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ver
22420
22477
  <cds-icon shape="window-close" [attr.title]="commonStrings.keys.close"></cds-icon>
22421
22478
  </button>
22422
22479
  </div>
22423
- <div class="signpost-content-body">
22480
+ <div class="signpost-content-body" tabindex="0">
22424
22481
  <ng-content></ng-content>
22425
22482
  </div>
22426
22483
  </div>
@@ -22443,7 +22500,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImpor
22443
22500
  <cds-icon shape="window-close" [attr.title]="commonStrings.keys.close"></cds-icon>
22444
22501
  </button>
22445
22502
  </div>
22446
- <div class="signpost-content-body">
22503
+ <div class="signpost-content-body" tabindex="0">
22447
22504
  <ng-content></ng-content>
22448
22505
  </div>
22449
22506
  </div>