cps-ui-kit 22.0.1 → 22.1.0

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.
@@ -1,7 +1,7 @@
1
1
  import * as i2 from '@angular/common';
2
2
  import { DOCUMENT, isPlatformBrowser, CommonModule, isPlatformServer, NgTemplateOutlet } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { inject, PLATFORM_ID, signal, Injectable, InjectionToken, ElementRef, Renderer2, HostAttributeToken, Input, ChangeDetectionStrategy, Component, EventEmitter, Output, input, computed, SecurityContext, Directive, Pipe, NgModule, effect, untracked, provideAppInitializer, makeEnvironmentProviders, ViewChild, Inject, ViewEncapsulation, Injector, ChangeDetectorRef, booleanAttribute, ContentChildren, ContentChild, HostBinding, Self, Optional, NgZone, contentChild, numberAttribute, HostListener, output, afterRenderEffect, forwardRef, ViewChildren, TemplateRef, viewChildren, viewChild, Host, model, RendererStyleFlags2, createComponent, createEnvironmentInjector, SkipSelf } from '@angular/core';
4
+ import { inject, PLATFORM_ID, signal, Injectable, InjectionToken, ElementRef, Renderer2, HostAttributeToken, Input, ChangeDetectionStrategy, Component, EventEmitter, Output, input, computed, NgZone, SecurityContext, Directive, Pipe, NgModule, effect, untracked, provideAppInitializer, makeEnvironmentProviders, ViewChild, Inject, ViewEncapsulation, Injector, ChangeDetectorRef, booleanAttribute, ContentChildren, ContentChild, HostBinding, Self, Optional, contentChild, numberAttribute, HostListener, output, afterRenderEffect, forwardRef, ViewChildren, TemplateRef, viewChildren, viewChild, Host, model, RendererStyleFlags2, createComponent, createEnvironmentInjector, SkipSelf } from '@angular/core';
5
5
  import * as i1$1 from '@angular/forms';
6
6
  import { Validators, FormsModule, NgControl, NG_VALUE_ACCESSOR, FormBuilder, ReactiveFormsModule } from '@angular/forms';
7
7
  import { Subject, takeUntil, debounceTime, distinctUntilChanged, take, catchError, of, Subscription, fromEvent } from 'rxjs';
@@ -661,6 +661,116 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
661
661
  }, template: "<div\n aria-hidden=\"true\"\n class=\"cps-progress-linear\"\n data-testid=\"cps-progress-linear\"\n [style.max-width]=\"cvtWidth()\"\n [style.height]=\"cvtHeight()\"\n [style.border-radius]=\"cvtRadius()\"\n [style.background]=\"cssBgColor()\">\n <div\n class=\"cps-progress-linear-line inc\"\n data-testid=\"cps-progress-linear-inc\"\n [style.background]=\"cssColor()\"\n [style.opacity]=\"opacity()\"></div>\n <div\n class=\"cps-progress-linear-line dec\"\n data-testid=\"cps-progress-linear-dec\"\n [style.background]=\"cssColor()\"\n [style.opacity]=\"opacity()\"></div>\n</div>\n", styles: [":host{width:100%}:host .cps-progress-linear{position:relative;overflow-x:hidden}:host .cps-progress-linear-line{position:absolute;height:inherit;left:-5%;top:0}:host .cps-progress-linear .inc{animation:increase 2s infinite}:host .cps-progress-linear .dec{animation:decrease 2s .5s infinite}@media(prefers-reduced-motion:reduce){:host .cps-progress-linear .inc{animation-duration:6s!important;animation-iteration-count:infinite!important}:host .cps-progress-linear .dec{animation-duration:6s!important;animation-iteration-count:infinite!important}}@keyframes increase{0%{left:-5%;width:5%}to{left:130%;width:100%}}@keyframes decrease{0%{left:-80%;width:80%}to{left:110%;width:10%}}\n"] }]
662
662
  }], propDecorators: { width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], bgColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "bgColor", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
663
663
 
664
+ /**
665
+ * Service for making accessible live-region announcements without relying on a
666
+ * specific component's live region. Creates two persistent hidden elements in
667
+ * document.body (one polite, one assertive) that screen readers monitor for
668
+ * content changes.
669
+ */
670
+ class CpsLiveAnnouncerService {
671
+ constructor() {
672
+ this._document = inject(DOCUMENT);
673
+ this._ngZone = inject(NgZone);
674
+ this._platformId = inject(PLATFORM_ID);
675
+ this._isBrowser = isPlatformBrowser(this._platformId);
676
+ this._regions = {
677
+ polite: {
678
+ el: this._createElement('polite'),
679
+ writeTimer: undefined,
680
+ clearTimer: undefined
681
+ },
682
+ assertive: {
683
+ el: this._createElement('assertive'),
684
+ writeTimer: undefined,
685
+ clearTimer: undefined
686
+ }
687
+ };
688
+ }
689
+ /**
690
+ * Announces a message through the appropriate live region. Clears any
691
+ * existing text first so the same message can be re-announced, then writes
692
+ * the new text after `delayMs` so screen readers observe the content change.
693
+ * After `durationMs` the region is cleared automatically; pass `0` to keep
694
+ * the message indefinitely. Defaults to a next-tick write and 5000ms duration.
695
+ */
696
+ announce(message, politeness = 'polite', durationMs = 5000, delayMs = 0) {
697
+ if (!this._isBrowser)
698
+ return;
699
+ this.clear(politeness);
700
+ const region = this._regions[politeness];
701
+ this._ngZone.runOutsideAngular(() => {
702
+ region.writeTimer = setTimeout(() => {
703
+ region.writeTimer = undefined;
704
+ region.el.textContent = message;
705
+ if (durationMs > 0) {
706
+ region.clearTimer = setTimeout(() => {
707
+ region.clearTimer = undefined;
708
+ region.el.textContent = '';
709
+ }, durationMs);
710
+ }
711
+ }, delayMs);
712
+ });
713
+ }
714
+ /**
715
+ * Clears the live region without making a new announcement.
716
+ */
717
+ clear(politeness = 'polite') {
718
+ if (!this._isBrowser)
719
+ return;
720
+ const region = this._regions[politeness];
721
+ clearTimeout(region.writeTimer);
722
+ clearTimeout(region.clearTimer);
723
+ region.writeTimer = undefined;
724
+ region.clearTimer = undefined;
725
+ region.el.textContent = '';
726
+ }
727
+ ngOnDestroy() {
728
+ for (const region of Object.values(this._regions)) {
729
+ clearTimeout(region.writeTimer);
730
+ clearTimeout(region.clearTimer);
731
+ region.el.remove();
732
+ }
733
+ }
734
+ _createElement(politeness) {
735
+ const cls = `cps-${politeness}-live-announcer-element`;
736
+ if (this._isBrowser) {
737
+ const existing = this._document.body.querySelector(`.${cls}`);
738
+ if (existing)
739
+ return existing;
740
+ }
741
+ const el = this._document.createElement('div');
742
+ if (!this._isBrowser)
743
+ return el;
744
+ el.setAttribute('aria-live', politeness);
745
+ el.setAttribute('aria-atomic', 'true');
746
+ el.className = `cps-sr-only ${cls}`;
747
+ this._document.body.appendChild(el);
748
+ return el;
749
+ }
750
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsLiveAnnouncerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
751
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsLiveAnnouncerService, providedIn: 'root' }); }
752
+ }
753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsLiveAnnouncerService, decorators: [{
754
+ type: Injectable,
755
+ args: [{ providedIn: 'root' }]
756
+ }] });
757
+ /**
758
+ * Injection token for the live-announcer service.
759
+ *
760
+ * By default it resolves to the singleton {@link CpsLiveAnnouncerService}.
761
+ * Consumer applications can override it to supply a custom implementation or
762
+ * provide `null` to disable all live-region announcements.
763
+ *
764
+ * @example Disable announcements:
765
+ * ```typescript
766
+ * providers: [{ provide: CPS_LIVE_ANNOUNCER_SERVICE, useValue: null }]
767
+ * ```
768
+ */
769
+ const CPS_LIVE_ANNOUNCER_SERVICE = new InjectionToken('CpsLiveAnnouncerService', {
770
+ providedIn: 'root',
771
+ factory: () => inject(CpsLiveAnnouncerService)
772
+ });
773
+
664
774
  /**
665
775
  * CpsTooltipDirective provides advisory information for a target element.
666
776
  * @group Components
@@ -745,6 +855,7 @@ class CpsTooltipDirective {
745
855
  this._document = inject(DOCUMENT);
746
856
  this._platformId = inject(PLATFORM_ID);
747
857
  this._domSanitizer = inject(DomSanitizer);
858
+ this._liveAnnouncer = inject(CPS_LIVE_ANNOUNCER_SERVICE);
748
859
  this._onScrollDestroy = () => {
749
860
  this._destroyTooltip(true);
750
861
  };
@@ -890,7 +1001,7 @@ class CpsTooltipDirective {
890
1001
  clearTimeout(this._hideTimeout);
891
1002
  this._createTooltip();
892
1003
  this._positionAndShow();
893
- this._announce(this._popup?.textContent ?? '');
1004
+ this._liveAnnouncer?.announce(this._popup?.textContent ?? '', 'assertive', 1000, 100);
894
1005
  }
895
1006
  }
896
1007
  onDocumentClick(target) {
@@ -1028,20 +1139,6 @@ class CpsTooltipDirective {
1028
1139
  _resolveAriaTarget() {
1029
1140
  return this._document.activeElement ?? undefined;
1030
1141
  }
1031
- // aria-describedby is not read by SR on click trigger.
1032
- // Use a self-removing live region. It must exist in DOM
1033
- // before content is set, hence the 100ms delay.
1034
- _announce(text) {
1035
- const el = this._document.createElement('div');
1036
- el.setAttribute('aria-live', 'assertive');
1037
- el.setAttribute('aria-atomic', 'true');
1038
- el.className = 'cps-sr-only';
1039
- this._document.body.appendChild(el);
1040
- setTimeout(() => {
1041
- el.textContent = text;
1042
- setTimeout(() => el.remove(), 1000);
1043
- }, 100);
1044
- }
1045
1142
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1046
1143
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: CpsTooltipDirective, isStandalone: true, selector: "[cpsTooltip]", inputs: { tooltip: { classPropertyName: "tooltip", publicName: "cpsTooltip", isSignal: true, isRequired: true, transformFunction: null }, tooltipOpenDelay: { classPropertyName: "tooltipOpenDelay", publicName: "tooltipOpenDelay", isSignal: true, isRequired: false, transformFunction: null }, tooltipCloseDelay: { classPropertyName: "tooltipCloseDelay", publicName: "tooltipCloseDelay", isSignal: true, isRequired: false, transformFunction: null }, tooltipOpenOn: { classPropertyName: "tooltipOpenOn", publicName: "tooltipOpenOn", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, tooltipPersistent: { classPropertyName: "tooltipPersistent", publicName: "tooltipPersistent", isSignal: true, isRequired: false, transformFunction: null }, tooltipDisabled: { classPropertyName: "tooltipDisabled", publicName: "tooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, tooltipMaxWidth: { classPropertyName: "tooltipMaxWidth", publicName: "tooltipMaxWidth", isSignal: true, isRequired: false, transformFunction: null }, tooltipContentClass: { classPropertyName: "tooltipContentClass", publicName: "tooltipContentClass", isSignal: true, isRequired: false, transformFunction: null }, tooltipOffset: { classPropertyName: "tooltipOffset", publicName: "tooltipOffset", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focusin": "onFocus()", "focusout": "onFocusOut($event)", "keydown.tab": "onTabFromTrigger($event)", "click": "onClick()", "document:click": "onDocumentClick($event.target)", "window:resize": "onPageResize()" } }, ngImport: i0 }); }
1047
1144
  }
@@ -5682,17 +5779,30 @@ class CpsFocusService {
5682
5779
  el.addEventListener('blur', () => el.classList.remove('suppress-focus-visible'), { once: true });
5683
5780
  }
5684
5781
  /**
5685
- * Focuses `el`, conditionally suppressing the focus-visible ring.
5782
+ * Focuses `el`, optionally suppressing its focus ring and passing native
5783
+ * focus options through to `HTMLElement.focus`.
5686
5784
  *
5687
- * When `showFocusRing` is `false`, adds the `suppress-focus-visible` CSS
5688
- * class to the element before focusing and removes it on the next `blur`
5689
- * event. This keeps the UI clean for pointer-initiated closes.
5785
+ * @param el - The element to focus.
5786
+ * @param showFocusRing - Whether the focus ring should be shown. When false, adds
5787
+ * the suppress-focus-visible CSS class to the element before focusing and removes
5788
+ * it on the next blur event to keep the UI clean for pointer-initiated closes.
5789
+ * @param options - Native focus options, e.g. `{ preventScroll: true }`.
5790
+ *
5791
+ * @example
5792
+ * ```ts
5793
+ * focusService.focusElement(button, true, { preventScroll: true });
5794
+ * ```
5690
5795
  */
5691
- focusElement(el, showFocusRing) {
5796
+ focusElement(el, showFocusRing, options) {
5692
5797
  if (!showFocusRing) {
5693
5798
  this.suppressNextFocusRing(el);
5694
5799
  }
5695
- el.focus();
5800
+ if (!options) {
5801
+ el.focus();
5802
+ }
5803
+ else {
5804
+ el.focus(options);
5805
+ }
5696
5806
  }
5697
5807
  /**
5698
5808
  * Installs a Tab key focus trap on `container`.
@@ -22783,116 +22893,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
22783
22893
  args: ['dropzoneButton']
22784
22894
  }] } });
22785
22895
 
22786
- /**
22787
- * Service for making accessible live-region announcements without relying on a
22788
- * specific component's live region. Creates two persistent hidden elements in
22789
- * document.body (one polite, one assertive) that screen readers monitor for
22790
- * content changes.
22791
- */
22792
- class CpsLiveAnnouncerService {
22793
- constructor() {
22794
- this._document = inject(DOCUMENT);
22795
- this._ngZone = inject(NgZone);
22796
- this._platformId = inject(PLATFORM_ID);
22797
- this._isBrowser = isPlatformBrowser(this._platformId);
22798
- this._regions = {
22799
- polite: {
22800
- el: this._createElement('polite'),
22801
- writeTimer: undefined,
22802
- clearTimer: undefined
22803
- },
22804
- assertive: {
22805
- el: this._createElement('assertive'),
22806
- writeTimer: undefined,
22807
- clearTimer: undefined
22808
- }
22809
- };
22810
- }
22811
- /**
22812
- * Announces a message through the appropriate live region. Clears any
22813
- * existing text first so the same message can be re-announced, then writes
22814
- * the new text on the next tick so screen readers observe the content change.
22815
- * After `duration` ms the region is cleared automatically; pass `0` to keep
22816
- * the message indefinitely. Defaults to 5000ms.
22817
- */
22818
- announce(message, politeness = 'polite', durationMs = 5000) {
22819
- if (!this._isBrowser)
22820
- return;
22821
- this.clear(politeness);
22822
- const region = this._regions[politeness];
22823
- this._ngZone.runOutsideAngular(() => {
22824
- region.writeTimer = setTimeout(() => {
22825
- region.writeTimer = undefined;
22826
- region.el.textContent = message;
22827
- if (durationMs > 0) {
22828
- region.clearTimer = setTimeout(() => {
22829
- region.clearTimer = undefined;
22830
- region.el.textContent = '';
22831
- }, durationMs);
22832
- }
22833
- });
22834
- });
22835
- }
22836
- /**
22837
- * Clears the live region without making a new announcement.
22838
- */
22839
- clear(politeness = 'polite') {
22840
- if (!this._isBrowser)
22841
- return;
22842
- const region = this._regions[politeness];
22843
- clearTimeout(region.writeTimer);
22844
- clearTimeout(region.clearTimer);
22845
- region.writeTimer = undefined;
22846
- region.clearTimer = undefined;
22847
- region.el.textContent = '';
22848
- }
22849
- ngOnDestroy() {
22850
- for (const region of Object.values(this._regions)) {
22851
- clearTimeout(region.writeTimer);
22852
- clearTimeout(region.clearTimer);
22853
- region.el.remove();
22854
- }
22855
- }
22856
- _createElement(politeness) {
22857
- const cls = `cps-${politeness}-live-announcer-element`;
22858
- if (this._isBrowser) {
22859
- const existing = this._document.body.querySelector(`.${cls}`);
22860
- if (existing)
22861
- return existing;
22862
- }
22863
- const el = this._document.createElement('div');
22864
- if (!this._isBrowser)
22865
- return el;
22866
- el.setAttribute('aria-live', politeness);
22867
- el.setAttribute('aria-atomic', 'true');
22868
- el.className = `cps-sr-only ${cls}`;
22869
- this._document.body.appendChild(el);
22870
- return el;
22871
- }
22872
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsLiveAnnouncerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
22873
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsLiveAnnouncerService, providedIn: 'root' }); }
22874
- }
22875
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: CpsLiveAnnouncerService, decorators: [{
22876
- type: Injectable,
22877
- args: [{ providedIn: 'root' }]
22878
- }] });
22879
- /**
22880
- * Injection token for the live-announcer service.
22881
- *
22882
- * By default it resolves to the singleton {@link CpsLiveAnnouncerService}.
22883
- * Consumer applications can override it to supply a custom implementation or
22884
- * provide `null` to disable all live-region announcements.
22885
- *
22886
- * @example Disable announcements:
22887
- * ```typescript
22888
- * providers: [{ provide: CPS_LIVE_ANNOUNCER_SERVICE, useValue: null }]
22889
- * ```
22890
- */
22891
- const CPS_LIVE_ANNOUNCER_SERVICE = new InjectionToken('CpsLiveAnnouncerService', {
22892
- providedIn: 'root',
22893
- factory: () => inject(CpsLiveAnnouncerService)
22894
- });
22895
-
22896
22896
  /**
22897
22897
  * CpsLoaderComponent is a fetch data indicator.
22898
22898
  * @group Components