@eui/ecl 22.0.0-alpha.2 → 22.0.0-alpha.21
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.
- package/docs/changelog.html +618 -0
- package/docs/components/EclDateBlockComponent.html +0 -9
- package/docs/components/EclDescriptionListDefinitionComponent.html +6 -44
- package/docs/components/EclIconComponent.html +1 -1
- package/docs/components/EclListIllustrationComponent.html +0 -171
- package/docs/components/EclListIllustrationItemComponent.html +77 -83
- package/docs/components/EclLoadingIndicatorComponent.html +4 -91
- package/docs/components/EclMegaMenuComponent.html +5 -203
- package/docs/components/EclMegaMenuFeaturedComponent.html +10 -126
- package/docs/components/EclMegaMenuInfoComponent.html +0 -91
- package/docs/components/EclMegaMenuItemComponent.html +38 -115
- package/docs/components/EclMegaMenuSubitemComponent.html +4 -68
- package/docs/components/EclMenuItemComponent.html +46 -0
- package/docs/components/EclModalBodyComponent.html +4 -45
- package/docs/components/EclModalComponent.html +5 -178
- package/docs/components/EclModalFooterComponent.html +4 -45
- package/docs/components/EclModalHeaderComponent.html +9 -76
- package/docs/components/EclSiteFooterRowCommonComponent.html +1 -1
- package/docs/components/EclSiteHeaderLanguageComponent.html +5 -627
- package/docs/components/EclSiteHeaderLanguagePopoverComponent.html +99 -440
- package/docs/components/EclSiteHeaderLoginComponent.html +9 -453
- package/docs/components/EclSiteHeaderSearchComponent.html +18 -470
- package/docs/components/EclSocialMediaFollowComponent.html +28 -39
- package/docs/components/EclSocialMediaFollowItemComponent.html +26 -129
- package/docs/components/EclStoryCardComponent.html +1 -1
- package/docs/directives/ECLClickOutsideDirective.html +3 -0
- package/docs/directives/EclDescriptionListDefinitionItemDirective.html +6 -47
- package/docs/directives/EclDescriptionListDefinitionListDirective.html +0 -116
- package/docs/directives/EclDividerDirective.html +0 -116
- package/docs/directives/EclFileUploadDirective.html +2 -390
- package/docs/directives/EclLabelDirective.html +0 -46
- package/docs/directives/EclLinkDirective.html +0 -39
- package/docs/directives/EclLoadingIndicatorLabelDirective.html +0 -61
- package/docs/directives/EclLoadingIndicatorOverlayDirective.html +0 -45
- package/docs/directives/EclMediaContainerCaptionDirective.html +0 -39
- package/docs/directives/EclMediaContainerCreditDirective.html +0 -39
- package/docs/directives/EclMediaContainerIframeDirective.html +13 -19
- package/docs/directives/EclMediaContainerItemAudioDescriptionDirective.html +0 -39
- package/docs/directives/EclMediaContainerItemDirective.html +0 -39
- package/docs/directives/EclMediaContainerPictureDirective.html +0 -39
- package/docs/directives/EclMegaMenuContainerDirective.html +0 -104
- package/docs/directives/EclMegaMenuFeaturedImageDirective.html +0 -104
- package/docs/directives/EclMegaMenuFeaturedListDirective.html +0 -104
- package/docs/directives/EclMegaMenuFeaturedListItemDescriptionDirective.html +0 -104
- package/docs/directives/EclMegaMenuFeaturedListItemDirective.html +0 -104
- package/docs/directives/EclMegaMenuFeaturedPictureDirective.html +0 -39
- package/docs/directives/EclMegaMenuListDirective.html +0 -104
- package/docs/directives/EclMegaMenuSpacerDirective.html +0 -104
- package/docs/directives/EclMegaMenuSublistDirective.html +0 -165
- package/docs/directives/EclModalBodyFixedContentDirective.html +0 -61
- package/docs/directives/EclModalCloseDirective.html +1 -90
- package/docs/directives/EclModalTriggerDirective.html +1 -90
- package/docs/js/search/search_index.js +2 -2
- package/docs/json/EclIconComponent.md +1 -1
- package/docs/json/EclMediaContainerIframeDirective.md +0 -1
- package/docs/json/EclSiteHeaderLanguagePopoverComponent.md +6 -6
- package/docs/json/EclSiteHeaderSearchComponent.md +1 -0
- package/docs/json/EclSocialMediaFollowComponent.md +1 -0
- package/docs/json/documentation.json +1261 -2880
- package/docs/llms.txt +31 -84
- package/docs/miscellaneous/variables.html +222 -41
- package/docs/properties.html +1 -1
- package/fesm2022/eui-ecl-components-ecl-date-block.mjs +2 -2
- package/fesm2022/eui-ecl-components-ecl-date-block.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-divider.mjs +6 -13
- package/fesm2022/eui-ecl-components-ecl-divider.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-file-upload.mjs +13 -27
- package/fesm2022/eui-ecl-components-ecl-file-upload.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-icon.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-inpage-navigation.mjs +55 -9
- package/fesm2022/eui-ecl-components-ecl-inpage-navigation.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-label.mjs +6 -6
- package/fesm2022/eui-ecl-components-ecl-label.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-link.mjs +10 -13
- package/fesm2022/eui-ecl-components-ecl-link.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-list-illustration.mjs +19 -30
- package/fesm2022/eui-ecl-components-ecl-list-illustration.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-list.mjs +44 -41
- package/fesm2022/eui-ecl-components-ecl-list.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-loading-indicator.mjs +17 -20
- package/fesm2022/eui-ecl-components-ecl-loading-indicator.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-media-container.mjs +36 -33
- package/fesm2022/eui-ecl-components-ecl-media-container.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-mega-menu.mjs +100 -159
- package/fesm2022/eui-ecl-components-ecl-mega-menu.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-menu.mjs +16 -3
- package/fesm2022/eui-ecl-components-ecl-menu.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-modal.mjs +94 -88
- package/fesm2022/eui-ecl-components-ecl-modal.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-site-footer.mjs +2 -2
- package/fesm2022/eui-ecl-components-ecl-site-footer.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-site-header.mjs +326 -261
- package/fesm2022/eui-ecl-components-ecl-site-header.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-social-media-follow.mjs +17 -17
- package/fesm2022/eui-ecl-components-ecl-social-media-follow.mjs.map +1 -1
- package/fesm2022/eui-ecl-components-ecl-story-card.mjs +2 -2
- package/fesm2022/eui-ecl-components-ecl-story-card.mjs.map +1 -1
- package/package.json +1 -1
- package/types/eui-ecl-components-ecl-date-block.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-divider.d.ts +0 -4
- package/types/eui-ecl-components-ecl-divider.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-file-upload.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-inpage-navigation.d.ts +25 -0
- package/types/eui-ecl-components-ecl-inpage-navigation.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-label.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-link.d.ts +2 -4
- package/types/eui-ecl-components-ecl-link.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-list-illustration.d.ts +3 -3
- package/types/eui-ecl-components-ecl-list-illustration.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-list.d.ts +9 -12
- package/types/eui-ecl-components-ecl-list.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-loading-indicator.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-media-container.d.ts +7 -2
- package/types/eui-ecl-components-ecl-media-container.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-mega-menu.d.ts +12 -22
- package/types/eui-ecl-components-ecl-mega-menu.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-menu.d.ts +12 -1
- package/types/eui-ecl-components-ecl-menu.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-modal.d.ts +20 -10
- package/types/eui-ecl-components-ecl-modal.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-site-header.d.ts +240 -216
- package/types/eui-ecl-components-ecl-site-header.d.ts.map +1 -1
- package/types/eui-ecl-components-ecl-social-media-follow.d.ts +5 -3
- package/types/eui-ecl-components-ecl-social-media-follow.d.ts.map +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { HostBinding, Component, inject, PLATFORM_ID, Directive, EventEmitter, contentChild, forwardRef, HostListener, Output, Input, ElementRef, Renderer2, afterNextRender, viewChild, input, booleanAttribute, output, NgZone, DOCUMENT,
|
|
2
|
+
import { HostBinding, Component, inject, PLATFORM_ID, Directive, EventEmitter, contentChild, forwardRef, HostListener, Output, Input, ElementRef, Renderer2, afterNextRender, viewChild, input, booleanAttribute, output, NgZone, DOCUMENT, signal, Injectable } from '@angular/core';
|
|
3
3
|
import * as i1 from '@eui/ecl/core';
|
|
4
4
|
import { ECLBaseDirective, ECLClickOutsideDirective, EclBaseEvent, EclThemeService, EclRtlService, logoLanguageCode } from '@eui/ecl/core';
|
|
5
5
|
import { isPlatformBrowser, UpperCasePipe, AsyncPipe } from '@angular/common';
|
|
@@ -21,9 +21,9 @@ import { EclMenuComponent } from '@eui/ecl/components/ecl-menu';
|
|
|
21
21
|
import { EclHeaderMenuComunicationService } from '@eui/ecl/shared';
|
|
22
22
|
import { TranslatePipe } from '@ngx-translate/core';
|
|
23
23
|
import { ScrollDispatcher } from '@angular/cdk/scrolling';
|
|
24
|
-
import * as
|
|
24
|
+
import * as i2$2 from '@angular/forms';
|
|
25
25
|
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
|
|
26
|
-
import * as
|
|
26
|
+
import * as i3$2 from '@eui/ecl/components/ecl-search-form';
|
|
27
27
|
import { EUI_ECL_SEARCH_FORM } from '@eui/ecl/components/ecl-search-form';
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -404,9 +404,6 @@ class EclAreaChangeEvent {
|
|
|
404
404
|
* Emits events when language is selected and when dialog is closed.
|
|
405
405
|
*/
|
|
406
406
|
class EclSiteHeaderLanguagePopoverComponent extends ECLBaseDirective {
|
|
407
|
-
get display() {
|
|
408
|
-
return this.isHidden() ? 'none' : 'block';
|
|
409
|
-
}
|
|
410
407
|
constructor() {
|
|
411
408
|
super();
|
|
412
409
|
this.el = inject(ElementRef);
|
|
@@ -416,8 +413,15 @@ class EclSiteHeaderLanguagePopoverComponent extends ECLBaseDirective {
|
|
|
416
413
|
*/
|
|
417
414
|
this.languageContent = viewChild('languageContent', /* @ts-ignore */
|
|
418
415
|
...(ngDevMode ? [{ debugName: "languageContent" }] : /* istanbul ignore next */ []));
|
|
419
|
-
|
|
420
|
-
|
|
416
|
+
/**
|
|
417
|
+
* Current language
|
|
418
|
+
*/
|
|
419
|
+
this.languageCode = input(undefined, /* @ts-ignore */
|
|
420
|
+
...(ngDevMode ? [{ debugName: "languageCode" }] : /* istanbul ignore next */ []));
|
|
421
|
+
this.euLanguages = input([], /* @ts-ignore */
|
|
422
|
+
...(ngDevMode ? [{ debugName: "euLanguages" }] : /* istanbul ignore next */ []));
|
|
423
|
+
this.nonEuLanguages = input([], /* @ts-ignore */
|
|
424
|
+
...(ngDevMode ? [{ debugName: "nonEuLanguages" }] : /* istanbul ignore next */ []));
|
|
421
425
|
/**
|
|
422
426
|
* Number of columns for eu languages. Calculated by the parent component 'ecl-site-header-language'.
|
|
423
427
|
*/
|
|
@@ -448,13 +452,23 @@ class EclSiteHeaderLanguagePopoverComponent extends ECLBaseDirective {
|
|
|
448
452
|
* Property to position the dialog correctly. Calculated by the parent component 'ecl-site-header-language'.
|
|
449
453
|
*/
|
|
450
454
|
this.isFull = input(false, { ...(ngDevMode ? { debugName: "isFull" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
451
|
-
this.languageClosed = output();
|
|
452
|
-
this.languageSelected = output();
|
|
453
455
|
/**
|
|
454
|
-
*
|
|
456
|
+
* Property to position the dialog correctly. Calculated by the parent component 'ecl-site-header-language'.
|
|
457
|
+
*/
|
|
458
|
+
this.rightPosition = input(undefined, /* @ts-ignore */
|
|
459
|
+
...(ngDevMode ? [{ debugName: "rightPosition" }] : /* istanbul ignore next */ []));
|
|
460
|
+
/**
|
|
461
|
+
* Property to position the dialog correctly. Calculated by the parent component 'ecl-site-header-language'.
|
|
462
|
+
*/
|
|
463
|
+
this.leftPosition = input(undefined, /* @ts-ignore */
|
|
464
|
+
...(ngDevMode ? [{ debugName: "leftPosition" }] : /* istanbul ignore next */ []));
|
|
465
|
+
/**
|
|
466
|
+
* Property to position the arrow of the dialog correctly. Calculated by the parent component 'ecl-site-header-language'.
|
|
455
467
|
*/
|
|
456
|
-
this.
|
|
457
|
-
|
|
468
|
+
this.languageArrowPosition = input(undefined, /* @ts-ignore */
|
|
469
|
+
...(ngDevMode ? [{ debugName: "languageArrowPosition" }] : /* istanbul ignore next */ []));
|
|
470
|
+
this.languageClosed = output();
|
|
471
|
+
this.languageSelected = output();
|
|
458
472
|
this.destroy$ = new Subject();
|
|
459
473
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
460
474
|
this.zone = inject(NgZone);
|
|
@@ -478,6 +492,9 @@ class EclSiteHeaderLanguagePopoverComponent extends ECLBaseDirective {
|
|
|
478
492
|
.join(' ')
|
|
479
493
|
.trim();
|
|
480
494
|
}
|
|
495
|
+
get display() {
|
|
496
|
+
return this.isHidden() ? 'none' : 'block';
|
|
497
|
+
}
|
|
481
498
|
get isModal() {
|
|
482
499
|
return !this.isHidden() || undefined;
|
|
483
500
|
}
|
|
@@ -492,7 +509,6 @@ class EclSiteHeaderLanguagePopoverComponent extends ECLBaseDirective {
|
|
|
492
509
|
this.languageClosed.emit(new EclSiteHeaderLanguageCloseEvent());
|
|
493
510
|
}
|
|
494
511
|
onLanguageClick(language, evt) {
|
|
495
|
-
this.languageCode = language.code;
|
|
496
512
|
this.languageSelected.emit(new EclSiteHeaderLanguageSelectedEvent(language));
|
|
497
513
|
evt.preventDefault();
|
|
498
514
|
}
|
|
@@ -518,76 +534,26 @@ class EclSiteHeaderLanguagePopoverComponent extends ECLBaseDirective {
|
|
|
518
534
|
});
|
|
519
535
|
}
|
|
520
536
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderLanguagePopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
521
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: EclSiteHeaderLanguagePopoverComponent, isStandalone: true, selector: "ecl-site-header-language-popover", inputs: { languageCode: { classPropertyName: "languageCode", publicName: "languageCode", isSignal:
|
|
537
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: EclSiteHeaderLanguagePopoverComponent, isStandalone: true, selector: "ecl-site-header-language-popover", inputs: { languageCode: { classPropertyName: "languageCode", publicName: "languageCode", isSignal: true, isRequired: false, transformFunction: null }, euLanguages: { classPropertyName: "euLanguages", publicName: "euLanguages", isSignal: true, isRequired: false, transformFunction: null }, nonEuLanguages: { classPropertyName: "nonEuLanguages", publicName: "nonEuLanguages", isSignal: true, isRequired: false, transformFunction: null }, euColumnsCount: { classPropertyName: "euColumnsCount", publicName: "euColumnsCount", isSignal: true, isRequired: false, transformFunction: null }, nonEuColumnsCount: { classPropertyName: "nonEuColumnsCount", publicName: "nonEuColumnsCount", isSignal: true, isRequired: false, transformFunction: null }, isHidden: { classPropertyName: "isHidden", publicName: "isHidden", isSignal: true, isRequired: false, transformFunction: null }, isPushRight: { classPropertyName: "isPushRight", publicName: "isPushRight", isSignal: true, isRequired: false, transformFunction: null }, isPushLeft: { classPropertyName: "isPushLeft", publicName: "isPushLeft", isSignal: true, isRequired: false, transformFunction: null }, isStack: { classPropertyName: "isStack", publicName: "isStack", isSignal: true, isRequired: false, transformFunction: null }, isFull: { classPropertyName: "isFull", publicName: "isFull", isSignal: true, isRequired: false, transformFunction: null }, rightPosition: { classPropertyName: "rightPosition", publicName: "rightPosition", isSignal: true, isRequired: false, transformFunction: null }, leftPosition: { classPropertyName: "leftPosition", publicName: "leftPosition", isSignal: true, isRequired: false, transformFunction: null }, languageArrowPosition: { classPropertyName: "languageArrowPosition", publicName: "languageArrowPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { languageClosed: "languageClosed", languageSelected: "languageSelected" }, host: { attributes: { "role": "dialog", "aria-labelledby": "ecl-site-header__language-title" }, properties: { "class": "cssClasses", "style.display": "display", "style.right.px": "rightPosition()", "style.left.px": "leftPosition()", "style.--ecl-overlay-arrow-position": "languageArrowPosition()", "attr.aria-modal": "isModal", "attr.hidden": "isDropdownHidden" } }, viewQueries: [{ propertyName: "languageContent", first: true, predicate: ["languageContent"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ecl-site-header__language-header\">\n <div class=\"ecl-site-header__language-title\">\n {{ 'ecl.language-list.SELECT-LANGUAGE' | translate }}\n </div>\n <button eclButton variant=\"tertiary\" eclStyle=\"neutral\" isIconOnly class=\"ecl-site-header__language-close\" (click)=\"onCloseClick()\">\n <span class=\"ecl-button__container\">\n <span class=\"ecl-button__label\">\n {{ 'ecl.common.CLOSE' | translate }}\n </span>\n <ecl-icon icon=\"close\" size=\"m\"></ecl-icon>\n </span>\n </button>\n</div>\n\n<div #languageContent class=\"ecl-site-header__language-content\" [style.max-height.px]=\"languageContentMaxHeight\"\n [class.ecl-site-header__language-content--stack]=\"isStack()\">\n @if (euLanguages().length) {\n <div class=\"ecl-site-header__language-category ecl-site-header__language-category--{{ euColumnsCount() }}-col\">\n <div class=\"ecl-site-header__language-category-title\">\n {{ 'ecl.language-list.EU-LANGUAGES' | translate }}\n </div>\n <ul class=\"ecl-site-header__language-list\" [attr.translate]=\"'no'\">\n @for (item of euLanguages(); track item.code) {\n <li class=\"ecl-site-header__language-item\">\n <a\n eclLink\n href=\"#\"\n variant=\"standalone\"\n [lang]=\"item.code\"\n [hreflang]=\"item.code\"\n class=\"ecl-site-header__language-link\"\n [class.ecl-site-header__language-link--active]=\"item.code === languageCode()\"\n (click)=\"onLanguageClick(item, $event)\">\n <span class=\"ecl-site-header__language-link-code\">{{ item.code }}</span>\n <span class=\"ecl-site-header__language-link-label\">{{ item.label }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n }\n @if (nonEuLanguages().length) {\n <div class=\"ecl-site-header__language-category ecl-site-header__language-category--{{ nonEuColumnsCount() }}-col\">\n <div class=\"ecl-site-header__language-category-title\">\n {{ 'ecl.language-list.NON-EU-LANGUAGES' | translate }}\n </div>\n <ul class=\"ecl-site-header__language-list\" [attr.translate]=\"'no'\">\n @for (item of nonEuLanguages(); track item.code) {\n <li class=\"ecl-site-header__language-item\">\n <a\n eclLink\n href=\"#\"\n [lang]=\"item.code\"\n [hreflang]=\"item.code\"\n variant=\"standalone\"\n class=\"ecl-site-header__language-link\"\n [class.ecl-site-header__language-link--active]=\"item.code === languageCode()\"\n (click)=\"onLanguageClick(item, $event)\">\n <span class=\"ecl-site-header__language-link-code\">{{ item.code }}</span>\n <span class=\"ecl-site-header__language-link-label\">{{ item.label }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: i3.EclButtonComponent, selector: "button[eclButton], button[ecl-button], a[eclButton]", inputs: ["isIconOnly", "variant", "eclStyle", "size", "containerStyleClass"] }, { kind: "component", type: i2$1.EclIconComponent, selector: "ecl-icon", inputs: ["iconSet", "icon", "size", "color", "transform", "ariaLabelledby", "role", "title", "ariaHidden", "focusable", "isFlipHorizontal"] }, { kind: "directive", type: i3$1.EclLinkDirective, selector: "[eclLink]", inputs: ["extraAttributes", "isInverted", "isIconOnly", "isHighlight", "isNoVisited", "isBranded", "variant"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
522
538
|
}
|
|
523
539
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderLanguagePopoverComponent, decorators: [{
|
|
524
540
|
type: Component,
|
|
525
|
-
args: [{ selector: 'ecl-site-header-language-popover', imports: [TranslatePipe, ...EUI_ECL_BUTTON, ...EUI_ECL_ICON, ...EUI_ECL_LINK],
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
type: Input
|
|
539
|
-
}, {
|
|
540
|
-
type: HostBinding,
|
|
541
|
-
args: ['style.left.px']
|
|
542
|
-
}], languageArrowPosition: [{
|
|
543
|
-
type: Input
|
|
544
|
-
}, {
|
|
545
|
-
type: HostBinding,
|
|
546
|
-
args: ['style.--ecl-language-arrow-position']
|
|
547
|
-
}], languageClosed: [{ type: i0.Output, args: ["languageClosed"] }], languageSelected: [{ type: i0.Output, args: ["languageSelected"] }], role: [{
|
|
548
|
-
type: HostBinding,
|
|
549
|
-
args: ['attr.role']
|
|
550
|
-
}], labelledBy: [{
|
|
551
|
-
type: HostBinding,
|
|
552
|
-
args: ['attr.aria-labelledby']
|
|
553
|
-
}], display: [{
|
|
554
|
-
type: HostBinding,
|
|
555
|
-
args: ['style.display']
|
|
556
|
-
}], cssClasses: [{
|
|
557
|
-
type: HostBinding,
|
|
558
|
-
args: ['class']
|
|
559
|
-
}], isModal: [{
|
|
560
|
-
type: HostBinding,
|
|
561
|
-
args: ['attr.aria-modal']
|
|
562
|
-
}], isDropdownHidden: [{
|
|
563
|
-
type: HostBinding,
|
|
564
|
-
args: ['attr.hidden']
|
|
565
|
-
}] } });
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* This class is used for methods, that serve for communication between the components in ecl-side-header.
|
|
569
|
-
*/
|
|
570
|
-
class EclAreaDataService {
|
|
571
|
-
constructor() {
|
|
572
|
-
this.dataSubject = new Subject();
|
|
573
|
-
this.dataSubject$ = this.dataSubject.asObservable();
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* When header is resized, some of it's chidren parameters for positioning on the screen need to be recalculated.
|
|
577
|
-
* This event publishes the new site header position, so that the chidren can adjust their position accordingly.
|
|
578
|
-
* @param event
|
|
579
|
-
*/
|
|
580
|
-
publishEvent(event) {
|
|
581
|
-
this.dataSubject.next(event);
|
|
582
|
-
}
|
|
583
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclAreaDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
584
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclAreaDataService, providedIn: 'root' }); }
|
|
585
|
-
}
|
|
586
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclAreaDataService, decorators: [{
|
|
587
|
-
type: Injectable,
|
|
588
|
-
args: [{ providedIn: 'root' }]
|
|
589
|
-
}], ctorParameters: () => [] });
|
|
541
|
+
args: [{ selector: 'ecl-site-header-language-popover', imports: [TranslatePipe, ...EUI_ECL_BUTTON, ...EUI_ECL_ICON, ...EUI_ECL_LINK], host: {
|
|
542
|
+
// The role of the dialog is 'dialog'. Should not be changed.
|
|
543
|
+
role: 'dialog',
|
|
544
|
+
'aria-labelledby': 'ecl-site-header__language-title',
|
|
545
|
+
'[class]': 'cssClasses',
|
|
546
|
+
'[style.display]': 'display',
|
|
547
|
+
'[style.right.px]': 'rightPosition()',
|
|
548
|
+
'[style.left.px]': 'leftPosition()',
|
|
549
|
+
'[style.--ecl-overlay-arrow-position]': 'languageArrowPosition()',
|
|
550
|
+
'[attr.aria-modal]': 'isModal',
|
|
551
|
+
'[attr.hidden]': 'isDropdownHidden',
|
|
552
|
+
}, template: "<div class=\"ecl-site-header__language-header\">\n <div class=\"ecl-site-header__language-title\">\n {{ 'ecl.language-list.SELECT-LANGUAGE' | translate }}\n </div>\n <button eclButton variant=\"tertiary\" eclStyle=\"neutral\" isIconOnly class=\"ecl-site-header__language-close\" (click)=\"onCloseClick()\">\n <span class=\"ecl-button__container\">\n <span class=\"ecl-button__label\">\n {{ 'ecl.common.CLOSE' | translate }}\n </span>\n <ecl-icon icon=\"close\" size=\"m\"></ecl-icon>\n </span>\n </button>\n</div>\n\n<div #languageContent class=\"ecl-site-header__language-content\" [style.max-height.px]=\"languageContentMaxHeight\"\n [class.ecl-site-header__language-content--stack]=\"isStack()\">\n @if (euLanguages().length) {\n <div class=\"ecl-site-header__language-category ecl-site-header__language-category--{{ euColumnsCount() }}-col\">\n <div class=\"ecl-site-header__language-category-title\">\n {{ 'ecl.language-list.EU-LANGUAGES' | translate }}\n </div>\n <ul class=\"ecl-site-header__language-list\" [attr.translate]=\"'no'\">\n @for (item of euLanguages(); track item.code) {\n <li class=\"ecl-site-header__language-item\">\n <a\n eclLink\n href=\"#\"\n variant=\"standalone\"\n [lang]=\"item.code\"\n [hreflang]=\"item.code\"\n class=\"ecl-site-header__language-link\"\n [class.ecl-site-header__language-link--active]=\"item.code === languageCode()\"\n (click)=\"onLanguageClick(item, $event)\">\n <span class=\"ecl-site-header__language-link-code\">{{ item.code }}</span>\n <span class=\"ecl-site-header__language-link-label\">{{ item.label }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n }\n @if (nonEuLanguages().length) {\n <div class=\"ecl-site-header__language-category ecl-site-header__language-category--{{ nonEuColumnsCount() }}-col\">\n <div class=\"ecl-site-header__language-category-title\">\n {{ 'ecl.language-list.NON-EU-LANGUAGES' | translate }}\n </div>\n <ul class=\"ecl-site-header__language-list\" [attr.translate]=\"'no'\">\n @for (item of nonEuLanguages(); track item.code) {\n <li class=\"ecl-site-header__language-item\">\n <a\n eclLink\n href=\"#\"\n [lang]=\"item.code\"\n [hreflang]=\"item.code\"\n variant=\"standalone\"\n class=\"ecl-site-header__language-link\"\n [class.ecl-site-header__language-link--active]=\"item.code === languageCode()\"\n (click)=\"onLanguageClick(item, $event)\">\n <span class=\"ecl-site-header__language-link-code\">{{ item.code }}</span>\n <span class=\"ecl-site-header__language-link-label\">{{ item.label }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n }\n</div>\n" }]
|
|
553
|
+
}], ctorParameters: () => [], propDecorators: { languageContent: [{ type: i0.ViewChild, args: ['languageContent', { isSignal: true }] }], languageCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "languageCode", required: false }] }], euLanguages: [{ type: i0.Input, args: [{ isSignal: true, alias: "euLanguages", required: false }] }], nonEuLanguages: [{ type: i0.Input, args: [{ isSignal: true, alias: "nonEuLanguages", required: false }] }], euColumnsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "euColumnsCount", required: false }] }], nonEuColumnsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "nonEuColumnsCount", required: false }] }], isHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "isHidden", required: false }] }], isPushRight: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPushRight", required: false }] }], isPushLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPushLeft", required: false }] }], isStack: [{ type: i0.Input, args: [{ isSignal: true, alias: "isStack", required: false }] }], isFull: [{ type: i0.Input, args: [{ isSignal: true, alias: "isFull", required: false }] }], rightPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightPosition", required: false }] }], leftPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftPosition", required: false }] }], languageArrowPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "languageArrowPosition", required: false }] }], languageClosed: [{ type: i0.Output, args: ["languageClosed"] }], languageSelected: [{ type: i0.Output, args: ["languageSelected"] }] } });
|
|
590
554
|
|
|
555
|
+
/** Half the arrow's own width, matching ECL's `arrowSize`. */
|
|
556
|
+
const ARROW_SIZE$2 = '0.5rem';
|
|
591
557
|
/**
|
|
592
558
|
* This component is used to switch between the languages of the application. Shows the current language, when clicked,
|
|
593
559
|
* opens a language selector.
|
|
@@ -598,15 +564,6 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
598
564
|
this.eclThemeService = inject(EclThemeService);
|
|
599
565
|
this.config = inject(GLOBAL_CONFIG_TOKEN, { optional: true });
|
|
600
566
|
this.i18nService = inject(I18nService);
|
|
601
|
-
/**
|
|
602
|
-
* The language popover, that shows all available languages (configured in the config file of the current site).
|
|
603
|
-
*/
|
|
604
|
-
this.languagePopover = viewChild(EclSiteHeaderLanguagePopoverComponent, /* @ts-ignore */
|
|
605
|
-
...(ngDevMode ? [{ debugName: "languagePopover" }] : /* istanbul ignore next */ []));
|
|
606
|
-
/**
|
|
607
|
-
* The link to open the popover dialog.
|
|
608
|
-
*/
|
|
609
|
-
this.languageLinkElement = viewChild('languageLink', { ...(ngDevMode ? { debugName: "languageLinkElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
610
567
|
/**
|
|
611
568
|
* Thie event is emited, when language link, showing the current language is clicked.
|
|
612
569
|
*/
|
|
@@ -615,8 +572,6 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
615
572
|
* Thie event is emited, when user selects a new language from the dropdown.
|
|
616
573
|
*/
|
|
617
574
|
this.languageSelected = new EventEmitter();
|
|
618
|
-
this.MOBILE_BREAKPOINT = 768;
|
|
619
|
-
this.LANGUAGE_MAX_COLUMNS = 8;
|
|
620
575
|
this.isLanguagesDropdownVisible = false;
|
|
621
576
|
this.languageCode = 'en';
|
|
622
577
|
/**
|
|
@@ -629,11 +584,23 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
629
584
|
this.nonEuLanguages = [];
|
|
630
585
|
this.euColumnsCount = 1;
|
|
631
586
|
this.nonEuColumnsCount = 1;
|
|
587
|
+
/**
|
|
588
|
+
* The language popover, that shows all available languages (configured in the config file of the current site).
|
|
589
|
+
*/
|
|
590
|
+
this.languagePopover = viewChild(EclSiteHeaderLanguagePopoverComponent, /* @ts-ignore */
|
|
591
|
+
...(ngDevMode ? [{ debugName: "languagePopover" }] : /* istanbul ignore next */ []));
|
|
592
|
+
/**
|
|
593
|
+
* The link to open the popover dialog.
|
|
594
|
+
*/
|
|
595
|
+
this.languageLinkElement = viewChild('languageLink', { ...(ngDevMode ? { debugName: "languageLinkElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
596
|
+
this.MOBILE_BREAKPOINT = 768;
|
|
597
|
+
this.LANGUAGE_MAX_COLUMNS = 8;
|
|
598
|
+
this.RESIZE_DEBOUNCE = 200;
|
|
632
599
|
this.destroy$ = new Subject();
|
|
633
600
|
this._languages = [];
|
|
634
601
|
this.isRtl = false;
|
|
635
|
-
this.eclAreaDataService = inject(EclAreaDataService);
|
|
636
602
|
this.eclRtlService = inject(EclRtlService);
|
|
603
|
+
this.el = inject(ElementRef);
|
|
637
604
|
this.document = inject(DOCUMENT);
|
|
638
605
|
this.window = this.document.defaultView; // Window | null
|
|
639
606
|
}
|
|
@@ -644,13 +611,11 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
644
611
|
this.handleRtlChangeState();
|
|
645
612
|
}
|
|
646
613
|
/**
|
|
647
|
-
* After view is initialized ->
|
|
614
|
+
* After view is initialized -> set up resize repositioning and initialize languages, that are configured in the config file
|
|
648
615
|
* of the current site.
|
|
649
616
|
*/
|
|
650
617
|
ngAfterViewInit() {
|
|
651
|
-
this.
|
|
652
|
-
this.calculatePopoverPosition(parentHeaderRect.right, parentHeaderRect.left);
|
|
653
|
-
});
|
|
618
|
+
this.handleResize();
|
|
654
619
|
this.handleInitialConfiguredLanguages();
|
|
655
620
|
}
|
|
656
621
|
ngOnDestroy() {
|
|
@@ -667,18 +632,14 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
667
632
|
onLanguageClick(evt) {
|
|
668
633
|
this.isLanguagesDropdownVisible = !this.isLanguagesDropdownVisible;
|
|
669
634
|
if (!this.isLanguagesDropdownVisible) {
|
|
670
|
-
setTimeout(() =>
|
|
671
|
-
this.isFull = false;
|
|
672
|
-
this.isStack = false;
|
|
673
|
-
this.isPushRight = false;
|
|
674
|
-
this.rightPosition = undefined;
|
|
675
|
-
this.leftPosition = undefined;
|
|
676
|
-
this.languageArrowPosition = undefined;
|
|
677
|
-
});
|
|
635
|
+
setTimeout(() => this.resetPositioning());
|
|
678
636
|
}
|
|
679
637
|
// If the dropdown is visible, we set the max-height of the dropdown, so that it can be scrolled.
|
|
680
638
|
if (this.isLanguagesDropdownVisible) {
|
|
681
639
|
setTimeout(() => this.languagePopover().setLanguageContentMaxHeight());
|
|
640
|
+
// Position the popover and its arrow after the popover is rendered and reflowed.
|
|
641
|
+
// Matches ECL's updateOverlayPosition call in toggleOverlay.
|
|
642
|
+
requestAnimationFrame(() => this.calculatePopoverPosition());
|
|
682
643
|
}
|
|
683
644
|
this.languageClick.next(new EclSiteHeaderLanguageClickEvent());
|
|
684
645
|
evt.preventDefault();
|
|
@@ -710,6 +671,9 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
710
671
|
handleRtlChangeState() {
|
|
711
672
|
this.eclRtlService.currentRtlState$.pipe(takeUntil(this.destroy$)).subscribe((rtlState) => {
|
|
712
673
|
this.isRtl = rtlState;
|
|
674
|
+
if (this.isLanguagesDropdownVisible) {
|
|
675
|
+
this.calculatePopoverPosition();
|
|
676
|
+
}
|
|
713
677
|
});
|
|
714
678
|
}
|
|
715
679
|
/**
|
|
@@ -721,44 +685,80 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
721
685
|
this.euColumnsCount = Math.ceil(this.euLanguages.length / this.LANGUAGE_MAX_COLUMNS);
|
|
722
686
|
this.nonEuColumnsCount = Math.ceil(this.nonEuLanguages.length / this.LANGUAGE_MAX_COLUMNS);
|
|
723
687
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
688
|
+
/**
|
|
689
|
+
* Clears every positioning flag/offset so a fresh calculation (or a close) starts from a known state.
|
|
690
|
+
*/
|
|
691
|
+
resetPositioning() {
|
|
692
|
+
this.isFull = false;
|
|
693
|
+
this.isStack = false;
|
|
694
|
+
this.isPushRight = false;
|
|
695
|
+
this.isPushLeft = false;
|
|
696
|
+
this.rightPosition = undefined;
|
|
697
|
+
this.leftPosition = undefined;
|
|
698
|
+
this.languageArrowPosition = undefined;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Positions the language popover and its arrow, matching ECL's updateOverlayPosition.
|
|
702
|
+
* The container is the site-header top area (ECL's '[data-ecl-site-header-top]'), resolved from
|
|
703
|
+
* the DOM rather than a shared service so the measurement is taken at the right time (on open/resize).
|
|
704
|
+
*/
|
|
705
|
+
calculatePopoverPosition() {
|
|
706
|
+
if (!this.isLanguagesDropdownVisible || !this.hasLanguageCodes())
|
|
707
|
+
return;
|
|
708
|
+
const container = this.el.nativeElement.closest('.ecl-site-header__top');
|
|
709
|
+
const languagePopover = this.languagePopover();
|
|
710
|
+
if (!container || !languagePopover)
|
|
711
|
+
return;
|
|
712
|
+
const toggleButtonRect = this.languageLinkElement().nativeElement.getBoundingClientRect();
|
|
713
|
+
const popoverRect = languagePopover.el.nativeElement.getBoundingClientRect();
|
|
714
|
+
const containerRect = container.getBoundingClientRect();
|
|
715
|
+
const screenWidth = this.window.innerWidth;
|
|
716
|
+
const clientWidth = Math.max(this.document.documentElement.clientWidth || 0, this.window.innerWidth || 0);
|
|
717
|
+
this.resetPositioning();
|
|
718
|
+
// Stack the EU / non-EU lists when the popover is wider than the container.
|
|
719
|
+
if (popoverRect.width > containerRect.width) {
|
|
720
|
+
this.isStack = true;
|
|
721
|
+
if (this.nonEuLanguages.length) {
|
|
722
|
+
this.nonEuColumnsCount = Math.max(this.euColumnsCount, this.nonEuColumnsCount);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
// Mobile: the popover is full width. The arrow is anchored from the box's right edge,
|
|
726
|
+
// so the offset is measured against popoverRect.right. Exclusive with push-right/left,
|
|
727
|
+
// matching ECL's updateOverlayPosition (mobile branch returns early).
|
|
728
|
+
if (clientWidth < this.MOBILE_BREAKPOINT) {
|
|
729
|
+
const arrowOffset = popoverRect.right - toggleButtonRect.right + toggleButtonRect.width / 2;
|
|
730
|
+
this.isFull = true;
|
|
731
|
+
this.languageArrowPosition = `calc(${arrowOffset}px - ${ARROW_SIZE$2})`;
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
// RTL: popover overflows the left edge -> push it right against the container's left edge.
|
|
735
|
+
if (this.isRtl && popoverRect.left < 0) {
|
|
736
|
+
const arrowOffset = toggleButtonRect.right - containerRect.left - toggleButtonRect.width / 2;
|
|
737
|
+
this.isPushLeft = true;
|
|
738
|
+
this.leftPosition = -toggleButtonRect.left + containerRect.left;
|
|
739
|
+
this.languageArrowPosition = `${arrowOffset}px`;
|
|
740
|
+
}
|
|
741
|
+
else if (!this.isRtl && popoverRect.right > screenWidth) {
|
|
742
|
+
// LTR: popover overflows the right edge -> push it left against the container's right edge.
|
|
743
|
+
const arrowOffset = containerRect.right - toggleButtonRect.right + toggleButtonRect.width / 2;
|
|
744
|
+
this.isPushRight = true;
|
|
745
|
+
this.rightPosition = -containerRect.right + toggleButtonRect.right;
|
|
746
|
+
this.languageArrowPosition = `calc(${arrowOffset}px - ${ARROW_SIZE$2})`;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* On window resize -> reposition the popover, but only while it is open.
|
|
751
|
+
* Debounced for performance, matching ECL's handleResize.
|
|
752
|
+
*/
|
|
753
|
+
handleResize() {
|
|
754
|
+
const win = this.window;
|
|
755
|
+
if (!win)
|
|
756
|
+
return;
|
|
757
|
+
fromEvent(win, 'resize')
|
|
758
|
+
.pipe(debounceTime(this.RESIZE_DEBOUNCE), takeUntil(this.destroy$))
|
|
759
|
+
.subscribe(() => {
|
|
760
|
+
if (this.isLanguagesDropdownVisible) {
|
|
761
|
+
this.calculatePopoverPosition();
|
|
762
762
|
}
|
|
763
763
|
});
|
|
764
764
|
}
|
|
@@ -790,7 +790,7 @@ class EclSiteHeaderLanguageComponent extends ECLBaseDirective {
|
|
|
790
790
|
});
|
|
791
791
|
}
|
|
792
792
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderLanguageComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
793
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: EclSiteHeaderLanguageComponent, isStandalone: true, selector: "ecl-site-header-language", outputs: { languageClick: "languageClick", languageSelected: "languageSelected" }, host: { listeners: { "eclClickOutside": "onClickOutside()", "document:keydown.escape": "onEscapeKeydownHandler()" }, properties: { "class": "
|
|
793
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: EclSiteHeaderLanguageComponent, isStandalone: true, selector: "ecl-site-header-language", outputs: { languageClick: "languageClick", languageSelected: "languageSelected" }, host: { listeners: { "eclClickOutside": "onClickOutside()", "document:keydown.escape": "onEscapeKeydownHandler()" }, properties: { "class": "cssClasses" } }, viewQueries: [{ propertyName: "languagePopover", first: true, predicate: EclSiteHeaderLanguagePopoverComponent, descendants: true, isSignal: true }, { propertyName: "languageLinkElement", first: true, predicate: ["languageLink"], descendants: true, read: ElementRef, isSignal: true }], usesInheritance: true, hostDirectives: [{ directive: i1.ECLClickOutsideDirective, outputs: ["eclClickOutside", "eclClickOutside"] }], ngImport: i0, template: "<a\n href=\"#\"\n #languageLink\n class=\"ecl-button ecl-button--tertiary ecl-button--neutral ecl-site-header__language-selector\"\n aria-controls=\"language-list-overlay\"\n role=\"button\"\n [attr.aria-label]=\"'ecl.language-list.SELECT-LANGUAGE-EXT' | translate: { currentLang: getLanguageLabel() }\"\n [attr.aria-expanded]=\"isLanguagesDropdownVisible\"\n (click)=\"onLanguageClick($event)\"\n (keydown.space)=\"onLanguageClick($event)\">\n <span class=\"ecl-site-header__language-icon\">\n <ecl-icon icon=\"global\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n </span>\n {{ languageCode | uppercase }}\n</a>\n@if(hasLanguageCodes() && isLanguagesDropdownVisible) {\n<ecl-site-header-language-popover\n #languagePopover\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n id=\"language-list-overlay\"\n [languageCode]=\"languageCode\"\n [euLanguages]=\"euLanguages\"\n [nonEuLanguages]=\"nonEuLanguages\"\n [euColumnsCount]=\"euColumnsCount\"\n [nonEuColumnsCount]=\"nonEuColumnsCount\"\n [isStack]=\"isStack\"\n [isPushRight]=\"isPushRight\"\n [isPushLeft] = \"isPushLeft\"\n [isFull]=\"isFull\"\n [rightPosition]=\"rightPosition\"\n [leftPosition]=\"leftPosition\"\n [languageArrowPosition]=\"languageArrowPosition\"\n (languageSelected)=\"onLanguageSelected($event)\"\n (languageClosed)=\"onLanguageClosed($event)\">\n</ecl-site-header-language-popover>\n}", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: i2$1.EclIconComponent, selector: "ecl-icon", inputs: ["iconSet", "icon", "size", "color", "transform", "ariaLabelledby", "role", "title", "ariaHidden", "focusable", "isFlipHorizontal"] }, { kind: "component", type: EclSiteHeaderLanguagePopoverComponent, selector: "ecl-site-header-language-popover", inputs: ["languageCode", "euLanguages", "nonEuLanguages", "euColumnsCount", "nonEuColumnsCount", "isHidden", "isPushRight", "isPushLeft", "isStack", "isFull", "rightPosition", "leftPosition", "languageArrowPosition"], outputs: ["languageClosed", "languageSelected"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: UpperCasePipe, name: "uppercase" }] }); }
|
|
794
794
|
}
|
|
795
795
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderLanguageComponent, decorators: [{
|
|
796
796
|
type: Component,
|
|
@@ -799,22 +799,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
799
799
|
directive: ECLClickOutsideDirective,
|
|
800
800
|
outputs: ['eclClickOutside'],
|
|
801
801
|
},
|
|
802
|
-
],
|
|
803
|
-
|
|
802
|
+
], host: {
|
|
803
|
+
'[class]': 'cssClasses',
|
|
804
|
+
'(eclClickOutside)': 'onClickOutside()',
|
|
805
|
+
'(document:keydown.escape)': 'onEscapeKeydownHandler()',
|
|
806
|
+
}, template: "<a\n href=\"#\"\n #languageLink\n class=\"ecl-button ecl-button--tertiary ecl-button--neutral ecl-site-header__language-selector\"\n aria-controls=\"language-list-overlay\"\n role=\"button\"\n [attr.aria-label]=\"'ecl.language-list.SELECT-LANGUAGE-EXT' | translate: { currentLang: getLanguageLabel() }\"\n [attr.aria-expanded]=\"isLanguagesDropdownVisible\"\n (click)=\"onLanguageClick($event)\"\n (keydown.space)=\"onLanguageClick($event)\">\n <span class=\"ecl-site-header__language-icon\">\n <ecl-icon icon=\"global\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n </span>\n {{ languageCode | uppercase }}\n</a>\n@if(hasLanguageCodes() && isLanguagesDropdownVisible) {\n<ecl-site-header-language-popover\n #languagePopover\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n id=\"language-list-overlay\"\n [languageCode]=\"languageCode\"\n [euLanguages]=\"euLanguages\"\n [nonEuLanguages]=\"nonEuLanguages\"\n [euColumnsCount]=\"euColumnsCount\"\n [nonEuColumnsCount]=\"nonEuColumnsCount\"\n [isStack]=\"isStack\"\n [isPushRight]=\"isPushRight\"\n [isPushLeft] = \"isPushLeft\"\n [isFull]=\"isFull\"\n [rightPosition]=\"rightPosition\"\n [leftPosition]=\"leftPosition\"\n [languageArrowPosition]=\"languageArrowPosition\"\n (languageSelected)=\"onLanguageSelected($event)\"\n (languageClosed)=\"onLanguageClosed($event)\">\n</ecl-site-header-language-popover>\n}", styles: [":host{display:block}\n"] }]
|
|
807
|
+
}], propDecorators: { languageClick: [{
|
|
804
808
|
type: Output
|
|
805
809
|
}], languageSelected: [{
|
|
806
810
|
type: Output
|
|
807
|
-
}],
|
|
808
|
-
type: HostBinding,
|
|
809
|
-
args: ['class']
|
|
810
|
-
}], onClickOutside: [{
|
|
811
|
-
type: HostListener,
|
|
812
|
-
args: ['eclClickOutside']
|
|
813
|
-
}], onEscapeKeydownHandler: [{
|
|
814
|
-
type: HostListener,
|
|
815
|
-
args: ['document:keydown.escape']
|
|
816
|
-
}] } });
|
|
811
|
+
}], languagePopover: [{ type: i0.ViewChild, args: [i0.forwardRef(() => EclSiteHeaderLanguagePopoverComponent), { isSignal: true }] }], languageLinkElement: [{ type: i0.ViewChild, args: ['languageLink', { ...{ read: ElementRef }, isSignal: true }] }] } });
|
|
817
812
|
|
|
813
|
+
/** Half the arrow's own width, matching ECL's `arrowSize`. */
|
|
814
|
+
const ARROW_SIZE$1 = '0.5rem';
|
|
815
|
+
/** Debounce for the resize recomputation, matching ECL's `handleResize`. */
|
|
816
|
+
const RESIZE_DEBOUNCE$1 = 200;
|
|
818
817
|
/**
|
|
819
818
|
* Component for login/logout as part of the harmonised site header. Emits events when user logs in and when user opens the
|
|
820
819
|
* login dropdown.
|
|
@@ -823,22 +822,42 @@ class EclSiteHeaderLoginComponent extends ECLBaseDirective {
|
|
|
823
822
|
constructor() {
|
|
824
823
|
super();
|
|
825
824
|
this.isLoggedIn = input(false, { ...(ngDevMode ? { debugName: "isLoggedIn" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
826
|
-
this.class = 'ecl-site-header__login-container';
|
|
827
|
-
this.loginLinkElement = viewChild('loginLink', { ...(ngDevMode ? { debugName: "loginLinkElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
828
825
|
this.login = output();
|
|
829
826
|
this.loginBoxToggle = new EventEmitter();
|
|
830
827
|
/**
|
|
831
|
-
* If login popover is open (true) or closed (false).
|
|
828
|
+
* If login popover is open (true) or closed (false). Kept public because the parent
|
|
829
|
+
* `ecl-site-header` resets it directly when the search box opens.
|
|
832
830
|
*/
|
|
833
831
|
this.isLoginBoxActive = false;
|
|
834
|
-
|
|
832
|
+
/**
|
|
833
|
+
* The position of the arrow of the popover, when in mobile (full-width) mode.
|
|
834
|
+
*/
|
|
835
|
+
this.loginArrowPosition = signal(undefined, /* @ts-ignore */
|
|
836
|
+
...(ngDevMode ? [{ debugName: "loginArrowPosition" }] : /* istanbul ignore next */ []));
|
|
837
|
+
/**
|
|
838
|
+
* The login link element from the view, i.e. the login button (the toggle).
|
|
839
|
+
*/
|
|
840
|
+
this.loginLinkElement = viewChild('loginLink', { ...(ngDevMode ? { debugName: "loginLinkElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
841
|
+
/**
|
|
842
|
+
* The login box element from the view, i.e. the dropdown the arrow points at.
|
|
843
|
+
*/
|
|
844
|
+
this.loginBoxElement = viewChild('loginBox', { ...(ngDevMode ? { debugName: "loginBoxElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
835
845
|
this.destroy$ = new Subject();
|
|
836
|
-
this.eclAreaDataService = inject(EclAreaDataService);
|
|
837
846
|
this.eclRtlService = inject(EclRtlService);
|
|
838
847
|
this.document = inject(DOCUMENT);
|
|
848
|
+
this.window = this.document.defaultView;
|
|
839
849
|
afterNextRender(() => {
|
|
840
|
-
|
|
841
|
-
this.
|
|
850
|
+
// Recompute the arrow only while the box is open.
|
|
851
|
+
this.eclRtlService.currentRtlState$.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => {
|
|
852
|
+
if (this.isLoginBoxActive)
|
|
853
|
+
this.setLoginArrow();
|
|
854
|
+
});
|
|
855
|
+
if (this.window) {
|
|
856
|
+
fromEvent(this.window, 'resize').pipe(debounceTime(RESIZE_DEBOUNCE$1), takeUntil(this.destroy$)).subscribe(() => {
|
|
857
|
+
if (this.isLoginBoxActive)
|
|
858
|
+
this.setLoginArrow();
|
|
859
|
+
});
|
|
860
|
+
}
|
|
842
861
|
});
|
|
843
862
|
}
|
|
844
863
|
ngOnChanges(changes) {
|
|
@@ -854,7 +873,7 @@ class EclSiteHeaderLoginComponent extends ECLBaseDirective {
|
|
|
854
873
|
/**
|
|
855
874
|
* Close the login popover on click outside.
|
|
856
875
|
*/
|
|
857
|
-
|
|
876
|
+
onClickOutside() {
|
|
858
877
|
this.onLoginBoxClose();
|
|
859
878
|
}
|
|
860
879
|
/**
|
|
@@ -873,66 +892,56 @@ class EclSiteHeaderLoginComponent extends ECLBaseDirective {
|
|
|
873
892
|
const event = new EclSiteHeaderLoginBoxToggleEvent(this.isLoginBoxActive);
|
|
874
893
|
this.loginBoxToggle.next(event);
|
|
875
894
|
evt.preventDefault();
|
|
895
|
+
if (this.isLoginBoxActive) {
|
|
896
|
+
// Measure after the '--active' class is applied and the box reflows to full width.
|
|
897
|
+
this.window?.requestAnimationFrame(() => this.setLoginArrow());
|
|
898
|
+
}
|
|
876
899
|
}
|
|
877
900
|
onLoginBoxClose() {
|
|
878
901
|
if (this.isLoginBoxActive) {
|
|
879
902
|
this.isLoginBoxActive = false;
|
|
880
903
|
const event = new EclSiteHeaderLoginBoxToggleEvent(this.isLoginBoxActive);
|
|
881
904
|
this.loginBoxToggle.next(event);
|
|
905
|
+
this.loginArrowPosition.set(undefined);
|
|
882
906
|
}
|
|
883
907
|
}
|
|
884
|
-
handleRtlChangeState() {
|
|
885
|
-
this.eclRtlService.currentRtlState$.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => {
|
|
886
|
-
this.calculateLoginArrowPosition(this.parentHeaderRight);
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
908
|
/**
|
|
890
|
-
*
|
|
909
|
+
* Position the dropdown arrow so it points at the toggle button.
|
|
910
|
+
* The arrow only exists on the mobile full-width dropdown, so the calculation is gated on the box
|
|
911
|
+
* spanning (almost) the full viewport width. The offset is relative to the box's own left edge.
|
|
912
|
+
* Mirrors ECL's 'setLoginArrow' in site-header.js.
|
|
891
913
|
*/
|
|
892
|
-
|
|
893
|
-
this.
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
calculateLoginArrowPosition(parentHeaderRight) {
|
|
899
|
-
const vw = this.document.documentElement.clientWidth;
|
|
900
|
-
const arrowSize = '0.5rem';
|
|
901
|
-
const loginLink = this.loginLinkElement()?.nativeElement.getBoundingClientRect();
|
|
902
|
-
const arrowPosition = parentHeaderRight - loginLink?.right + loginLink?.width / 2;
|
|
903
|
-
if (parentHeaderRight === undefined) {
|
|
914
|
+
setLoginArrow() {
|
|
915
|
+
const loginBoxRect = this.loginBoxElement()?.nativeElement.getBoundingClientRect();
|
|
916
|
+
const toggleButtonRect = this.loginLinkElement()?.nativeElement.getBoundingClientRect();
|
|
917
|
+
if (!loginBoxRect || !toggleButtonRect)
|
|
904
918
|
return;
|
|
919
|
+
const clientWidth = this.document.documentElement.clientWidth;
|
|
920
|
+
if (loginBoxRect.width >= clientWidth - 1) {
|
|
921
|
+
const arrowOffset = toggleButtonRect.left - loginBoxRect.left + toggleButtonRect.width / 2;
|
|
922
|
+
this.loginArrowPosition.set(`calc(${arrowOffset}px - ${ARROW_SIZE$1})`);
|
|
905
923
|
}
|
|
906
|
-
|
|
907
|
-
this.loginArrowPosition
|
|
908
|
-
}
|
|
909
|
-
else if (this.isLoginBoxActive) {
|
|
910
|
-
this.loginArrowPosition = null;
|
|
924
|
+
else {
|
|
925
|
+
this.loginArrowPosition.set(undefined);
|
|
911
926
|
}
|
|
912
927
|
}
|
|
913
928
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderLoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
914
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: EclSiteHeaderLoginComponent, isStandalone: true, selector: "ecl-site-header-login", inputs: { isLoggedIn: { classPropertyName: "isLoggedIn", publicName: "isLoggedIn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", loginBoxToggle: "loginBoxToggle" }, host: { listeners: { "eclClickOutside": "
|
|
929
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: EclSiteHeaderLoginComponent, isStandalone: true, selector: "ecl-site-header-login", inputs: { isLoggedIn: { classPropertyName: "isLoggedIn", publicName: "isLoggedIn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", loginBoxToggle: "loginBoxToggle" }, host: { listeners: { "eclClickOutside": "onClickOutside()" }, classAttribute: "ecl-site-header__login-container" }, viewQueries: [{ propertyName: "loginLinkElement", first: true, predicate: ["loginLink"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "loginBoxElement", first: true, predicate: ["loginBox"], descendants: true, read: ElementRef, isSignal: true }], usesInheritance: true, usesOnChanges: true, hostDirectives: [{ directive: i1.ECLClickOutsideDirective, outputs: ["eclClickOutside", "eclClickOutside"] }], ngImport: i0, template: "@if (isLoggedIn()) {\n <a\n #loginLink\n href=\"#\"\n class=\"ecl-button ecl-button--tertiary ecl-button--neutral ecl-site-header__login-toggle\"\n aria-controls=\"login-box-id\"\n [attr.aria-expanded]=\"isLoginBoxActive\"\n (click)=\"onLoginBoxToggle($event)\">\n <ecl-icon icon=\"logged-in\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n {{ 'ecl.site-header.LOGGED-IN' | translate }}\n </a>\n <div\n #loginBox\n id=\"login-box-id\"\n [style]=\"{'--ecl-login-arrow-position': loginArrowPosition()}\"\n class=\"{{ getLoginBoxCssClasses() }}\">\n <ng-content></ng-content>\n </div>\n} @else {\n <a\n href=\"#\"\n class=\"ecl-button ecl-button--tertiary ecl-button--neutral ecl-site-header__login-toggle\"\n [attr.aria-expanded]=\"isLoginBoxActive\"\n (click)=\"onLoginClick($event)\">\n <ecl-icon icon=\"log-in\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n {{ 'ecl.site-header.LOGIN' | translate }}\n </a>\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: i2$1.EclIconComponent, selector: "ecl-icon", inputs: ["iconSet", "icon", "size", "color", "transform", "ariaLabelledby", "role", "title", "ariaHidden", "focusable", "isFlipHorizontal"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
915
930
|
}
|
|
916
931
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderLoginComponent, decorators: [{
|
|
917
932
|
type: Component,
|
|
918
933
|
args: [{ selector: 'ecl-site-header-login', imports: [TranslatePipe, ...EUI_ECL_ICON], hostDirectives: [
|
|
919
|
-
{
|
|
920
|
-
directive: ECLBaseDirective,
|
|
921
|
-
},
|
|
922
934
|
{
|
|
923
935
|
directive: ECLClickOutsideDirective,
|
|
924
936
|
outputs: ['eclClickOutside'],
|
|
925
937
|
},
|
|
926
|
-
],
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
938
|
+
], host: {
|
|
939
|
+
class: 'ecl-site-header__login-container',
|
|
940
|
+
'(eclClickOutside)': 'onClickOutside()',
|
|
941
|
+
}, template: "@if (isLoggedIn()) {\n <a\n #loginLink\n href=\"#\"\n class=\"ecl-button ecl-button--tertiary ecl-button--neutral ecl-site-header__login-toggle\"\n aria-controls=\"login-box-id\"\n [attr.aria-expanded]=\"isLoginBoxActive\"\n (click)=\"onLoginBoxToggle($event)\">\n <ecl-icon icon=\"logged-in\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n {{ 'ecl.site-header.LOGGED-IN' | translate }}\n </a>\n <div\n #loginBox\n id=\"login-box-id\"\n [style]=\"{'--ecl-login-arrow-position': loginArrowPosition()}\"\n class=\"{{ getLoginBoxCssClasses() }}\">\n <ng-content></ng-content>\n </div>\n} @else {\n <a\n href=\"#\"\n class=\"ecl-button ecl-button--tertiary ecl-button--neutral ecl-site-header__login-toggle\"\n [attr.aria-expanded]=\"isLoginBoxActive\"\n (click)=\"onLoginClick($event)\">\n <ecl-icon icon=\"log-in\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n {{ 'ecl.site-header.LOGIN' | translate }}\n </a>\n}\n", styles: [":host{display:block}\n"] }]
|
|
942
|
+
}], ctorParameters: () => [], propDecorators: { isLoggedIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoggedIn", required: false }] }], login: [{ type: i0.Output, args: ["login"] }], loginBoxToggle: [{
|
|
931
943
|
type: Output
|
|
932
|
-
}],
|
|
933
|
-
type: HostListener,
|
|
934
|
-
args: ['eclClickOutside']
|
|
935
|
-
}] } });
|
|
944
|
+
}], loginLinkElement: [{ type: i0.ViewChild, args: ['loginLink', { ...{ read: ElementRef }, isSignal: true }] }], loginBoxElement: [{ type: i0.ViewChild, args: ['loginBox', { ...{ read: ElementRef }, isSignal: true }] }] } });
|
|
936
945
|
|
|
937
946
|
/**
|
|
938
947
|
* This directive is used to add specific class for the login description element, within the login dropdown.
|
|
@@ -1079,6 +1088,10 @@ const ECL_SITE_HEADER_SEARCH_VALUE_ACCESSOR = {
|
|
|
1079
1088
|
const noop = () => {
|
|
1080
1089
|
/** empty*/
|
|
1081
1090
|
};
|
|
1091
|
+
/** Half the arrow's own width, matching ECL's `arrowSize`. */
|
|
1092
|
+
const ARROW_SIZE = '0.5rem';
|
|
1093
|
+
/** Debounce for the resize recomputation, matching ECL's `handleResize`. */
|
|
1094
|
+
const RESIZE_DEBOUNCE = 200;
|
|
1082
1095
|
/**
|
|
1083
1096
|
* This component builds the search box in the ecl-site-header component. Adds specific style and functionality, emits
|
|
1084
1097
|
* events on search and search toggle.
|
|
@@ -1086,51 +1099,74 @@ const noop = () => {
|
|
|
1086
1099
|
class EclSiteHeaderSearchComponent extends ECLBaseDirective {
|
|
1087
1100
|
constructor() {
|
|
1088
1101
|
super(...arguments);
|
|
1089
|
-
this.class = 'ecl-site-header__search-container';
|
|
1090
|
-
/**
|
|
1091
|
-
* The default role of the component is 'search', should not be changed.
|
|
1092
|
-
*/
|
|
1093
|
-
this.role = 'search';
|
|
1094
1102
|
this.searchInputPlaceholder = input(undefined, /* @ts-ignore */
|
|
1095
1103
|
...(ngDevMode ? [{ debugName: "searchInputPlaceholder" }] : /* istanbul ignore next */ []));
|
|
1096
1104
|
this.searchButtonLabel = input(undefined, /* @ts-ignore */
|
|
1097
1105
|
...(ngDevMode ? [{ debugName: "searchButtonLabel" }] : /* istanbul ignore next */ []));
|
|
1098
|
-
/**
|
|
1099
|
-
* The search link element from the view, i.e. the search button.
|
|
1100
|
-
*/
|
|
1101
|
-
this.searchLinkElement = viewChild('searchLink', { ...(ngDevMode ? { debugName: "searchLinkElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
1102
1106
|
this.searchEvent = output();
|
|
1103
1107
|
this.searchToggle = new EventEmitter();
|
|
1108
|
+
/**
|
|
1109
|
+
* Whether the search dropdown is open. Kept public because the parent
|
|
1110
|
+
* `ecl-site-header` resets it directly when the login box opens.
|
|
1111
|
+
*/
|
|
1104
1112
|
this.isSearchBoxActive = false;
|
|
1113
|
+
/**
|
|
1114
|
+
* The arrow position of the search dropdown, when in mobile (full-width) mode.
|
|
1115
|
+
*/
|
|
1116
|
+
this.searchArrowPosition = signal(undefined, /* @ts-ignore */
|
|
1117
|
+
...(ngDevMode ? [{ debugName: "searchArrowPosition" }] : /* istanbul ignore next */ []));
|
|
1118
|
+
/**
|
|
1119
|
+
* The search link element from the view, i.e. the search button (the toggle).
|
|
1120
|
+
*/
|
|
1121
|
+
this.searchLinkElement = viewChild('searchLink', { ...(ngDevMode ? { debugName: "searchLinkElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
1122
|
+
/**
|
|
1123
|
+
* The search form element from the view, i.e. the dropdown box the arrow points from.
|
|
1124
|
+
*/
|
|
1125
|
+
this.searchFormElement = viewChild('searchForm', { ...(ngDevMode ? { debugName: "searchFormElement" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
1105
1126
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1106
1127
|
this.onChange = noop;
|
|
1107
1128
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1108
1129
|
this.onTouched = noop;
|
|
1109
1130
|
this.destroy$ = new Subject();
|
|
1110
|
-
this.eclAreaDataService = inject(EclAreaDataService);
|
|
1111
1131
|
this.eclRtlService = inject(EclRtlService);
|
|
1112
|
-
this.cdr = inject(ChangeDetectorRef);
|
|
1113
1132
|
this.platformId = inject(PLATFORM_ID);
|
|
1114
1133
|
this.document = inject(DOCUMENT);
|
|
1134
|
+
this.window = this.document.defaultView; // Window | null
|
|
1115
1135
|
}
|
|
1116
1136
|
ngAfterViewInit() {
|
|
1117
1137
|
if (!isPlatformBrowser(this.platformId))
|
|
1118
1138
|
return;
|
|
1119
|
-
|
|
1120
|
-
this.
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
this.ro.observe(el);
|
|
1139
|
+
// Recompute while open on resize (debounced, matching ECL's handleResize).
|
|
1140
|
+
if (this.window) {
|
|
1141
|
+
fromEvent(this.window, 'resize')
|
|
1142
|
+
.pipe(debounceTime(RESIZE_DEBOUNCE), takeUntil(this.destroy$))
|
|
1143
|
+
.subscribe(() => this.setSearchArrow());
|
|
1144
|
+
}
|
|
1145
|
+
// Recompute while open on RTL direction change.
|
|
1146
|
+
this.eclRtlService.currentRtlState$.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => this.setSearchArrow());
|
|
1128
1147
|
}
|
|
1129
1148
|
ngOnDestroy() {
|
|
1130
|
-
this.ro?.disconnect();
|
|
1131
1149
|
this.destroy$.next(true);
|
|
1132
1150
|
this.destroy$.complete();
|
|
1133
1151
|
}
|
|
1152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1153
|
+
writeValue(value) {
|
|
1154
|
+
this.searchInputText = value;
|
|
1155
|
+
}
|
|
1156
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1157
|
+
registerOnChange(fn) {
|
|
1158
|
+
this.onChange = fn;
|
|
1159
|
+
}
|
|
1160
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1161
|
+
registerOnTouched(fn) {
|
|
1162
|
+
this.onTouched = fn;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Close the search dropdown on click outside, matching ECL's `handleClickGlobal`.
|
|
1166
|
+
*/
|
|
1167
|
+
onClickOutside() {
|
|
1168
|
+
this.closeSearchBox();
|
|
1169
|
+
}
|
|
1134
1170
|
onSearch(evt) {
|
|
1135
1171
|
this.searchEvent.emit(new EclSiteHeaderSearchEvent(evt.keyword));
|
|
1136
1172
|
this.onChange(evt.keyword);
|
|
@@ -1141,6 +1177,11 @@ class EclSiteHeaderSearchComponent extends ECLBaseDirective {
|
|
|
1141
1177
|
const event = new EclSiteHeaderSearchToggleEvent(this.isSearchBoxActive);
|
|
1142
1178
|
this.searchToggle.next(event);
|
|
1143
1179
|
evt.preventDefault();
|
|
1180
|
+
// Measure after the `--active` class is applied and the box reflows to
|
|
1181
|
+
// full width (matching ECL's toggleSearch -> setSearchArrow order).
|
|
1182
|
+
if (this.isSearchBoxActive) {
|
|
1183
|
+
this.window?.requestAnimationFrame(() => this.setSearchArrow());
|
|
1184
|
+
}
|
|
1144
1185
|
}
|
|
1145
1186
|
/**
|
|
1146
1187
|
*
|
|
@@ -1153,56 +1194,80 @@ class EclSiteHeaderSearchComponent extends ECLBaseDirective {
|
|
|
1153
1194
|
this.searchInputText = newValue;
|
|
1154
1195
|
this.onChange(newValue);
|
|
1155
1196
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
this.onChange = fn;
|
|
1163
|
-
}
|
|
1164
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1165
|
-
registerOnTouched(fn) {
|
|
1166
|
-
this.onTouched = fn;
|
|
1167
|
-
}
|
|
1168
|
-
handleRtlChangeState() {
|
|
1169
|
-
this.eclRtlService.currentRtlState$.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => {
|
|
1170
|
-
this.calculateSearchFormArrowPosition(this.parentHeaderRight);
|
|
1171
|
-
});
|
|
1172
|
-
}
|
|
1173
|
-
handleSiteHeaderAreaChangeEvent() {
|
|
1174
|
-
this.eclAreaDataService.dataSubject$.pipe(takeUntil(this.destroy$)).subscribe(parentHeaderRect => {
|
|
1175
|
-
this.parentHeaderRight = parentHeaderRect.right;
|
|
1176
|
-
this.calculateSearchFormArrowPosition(this.parentHeaderRight);
|
|
1177
|
-
});
|
|
1197
|
+
closeSearchBox() {
|
|
1198
|
+
if (!this.isSearchBoxActive)
|
|
1199
|
+
return;
|
|
1200
|
+
this.isSearchBoxActive = false;
|
|
1201
|
+
this.searchToggle.next(new EclSiteHeaderSearchToggleEvent(this.isSearchBoxActive));
|
|
1202
|
+
this.searchArrowPosition.set(undefined);
|
|
1178
1203
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1204
|
+
/**
|
|
1205
|
+
* Positions the dropdown arrow so it points at the center of the search toggle button.
|
|
1206
|
+
* Mirrors ECL's `setSearchArrow`: the arrow position is an offset relative to the
|
|
1207
|
+
* search form's own left edge, and it only applies to the mobile full-width dropdown
|
|
1208
|
+
* (`searchFormRect.width >= clientWidth - 1`, the `- 1` absorbing subpixel rounding).
|
|
1209
|
+
*/
|
|
1210
|
+
setSearchArrow() {
|
|
1211
|
+
if (!this.isSearchBoxActive)
|
|
1212
|
+
return;
|
|
1213
|
+
const searchFormRect = this.searchFormElement()?.nativeElement.getBoundingClientRect();
|
|
1214
|
+
const toggleButtonRect = this.searchLinkElement()?.nativeElement.getBoundingClientRect();
|
|
1215
|
+
if (!searchFormRect || !toggleButtonRect)
|
|
1216
|
+
return;
|
|
1217
|
+
const clientWidth = this.document.documentElement.clientWidth;
|
|
1218
|
+
if (searchFormRect.width >= clientWidth - 1) {
|
|
1219
|
+
const arrowOffset = toggleButtonRect.left - searchFormRect.left + toggleButtonRect.width / 2;
|
|
1220
|
+
this.searchArrowPosition.set(`calc(${arrowOffset}px - ${ARROW_SIZE})`);
|
|
1221
|
+
}
|
|
1222
|
+
else {
|
|
1223
|
+
this.searchArrowPosition.set(undefined);
|
|
1187
1224
|
}
|
|
1188
1225
|
}
|
|
1189
1226
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderSearchComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1190
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.2", type: EclSiteHeaderSearchComponent, isStandalone: true, selector: "ecl-site-header-search", inputs: { searchInputText: { classPropertyName: "searchInputText", publicName: "searchInputText", isSignal: false, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, searchButtonLabel: { classPropertyName: "searchButtonLabel", publicName: "searchButtonLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchEvent: "searchEvent", searchToggle: "searchToggle" }, host: {
|
|
1227
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.2", type: EclSiteHeaderSearchComponent, isStandalone: true, selector: "ecl-site-header-search", inputs: { searchInputText: { classPropertyName: "searchInputText", publicName: "searchInputText", isSignal: false, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, searchButtonLabel: { classPropertyName: "searchButtonLabel", publicName: "searchButtonLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchEvent: "searchEvent", searchToggle: "searchToggle" }, host: { attributes: { "role": "search" }, listeners: { "eclClickOutside": "onClickOutside()" }, classAttribute: "ecl-site-header__search-container" }, providers: [ECL_SITE_HEADER_SEARCH_VALUE_ACCESSOR], viewQueries: [{ propertyName: "searchLinkElement", first: true, predicate: ["searchLink"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "searchFormElement", first: true, predicate: ["searchForm"], descendants: true, read: ElementRef, isSignal: true }], usesInheritance: true, hostDirectives: [{ directive: i1.ECLClickOutsideDirective, outputs: ["eclClickOutside", "eclClickOutside"] }], ngImport: i0, template: "<a\n #searchLink\n href=\"#\"\n class=\"ecl-button ecl-button--tertiary ecl-site-header__search-toggle ecl-button--neutral\"\n data-ecl-search-toggle=\"true\"\n aria-controls=\"search-form-id\"\n [attr.aria-expanded]=\"isSearchBoxActive\"\n (click)=\"onSearchBoxToggle($event)\">\n <ecl-icon icon=\"search\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n {{ 'ecl.common.SEARCH' | translate }}\n</a>\n\n<ecl-search-form\n #searchForm\n [style]=\"{'--ecl-search-arrow-position': searchArrowPosition()}\"\n (searchEvent)=\"onSearch($event)\"\n [placeholder]=\"searchInputPlaceholder()\"\n (ngModelChange)=\"onModelChange($event)\"\n [(ngModel)]=\"searchInputText\"\n [searchButtonLabel]=\"searchButtonLabel()\"\n [class]=\"getSearchBoxCssClasses()\">\n</ecl-search-form>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3$2.EclSearchFormComponent, selector: "ecl-search-form", inputs: ["searchInputText", "placeholder", "searchButtonLabel", "searchButtonIcon", "buttonExtraClassses", "inputExtraClassses"], outputs: ["searchInputTextChange", "searchEvent"] }, { kind: "component", type: i2$1.EclIconComponent, selector: "ecl-icon", inputs: ["iconSet", "icon", "size", "color", "transform", "ariaLabelledby", "role", "title", "ariaHidden", "focusable", "isFlipHorizontal"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
1191
1228
|
}
|
|
1192
1229
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclSiteHeaderSearchComponent, decorators: [{
|
|
1193
1230
|
type: Component,
|
|
1194
|
-
args: [{ selector: 'ecl-site-header-search', imports: [FormsModule, TranslatePipe, ...EUI_ECL_SEARCH_FORM, ...EUI_ECL_ICON], providers: [ECL_SITE_HEADER_SEARCH_VALUE_ACCESSOR],
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1231
|
+
args: [{ selector: 'ecl-site-header-search', imports: [FormsModule, TranslatePipe, ...EUI_ECL_SEARCH_FORM, ...EUI_ECL_ICON], providers: [ECL_SITE_HEADER_SEARCH_VALUE_ACCESSOR], hostDirectives: [
|
|
1232
|
+
{
|
|
1233
|
+
directive: ECLClickOutsideDirective,
|
|
1234
|
+
outputs: ['eclClickOutside'],
|
|
1235
|
+
},
|
|
1236
|
+
], host: {
|
|
1237
|
+
class: 'ecl-site-header__search-container',
|
|
1238
|
+
// The default role of the component is 'search', should not be changed.
|
|
1239
|
+
role: 'search',
|
|
1240
|
+
'(eclClickOutside)': 'onClickOutside()',
|
|
1241
|
+
}, template: "<a\n #searchLink\n href=\"#\"\n class=\"ecl-button ecl-button--tertiary ecl-site-header__search-toggle ecl-button--neutral\"\n data-ecl-search-toggle=\"true\"\n aria-controls=\"search-form-id\"\n [attr.aria-expanded]=\"isSearchBoxActive\"\n (click)=\"onSearchBoxToggle($event)\">\n <ecl-icon icon=\"search\" size=\"m\" class=\"ecl-site-header__icon\"></ecl-icon>\n {{ 'ecl.common.SEARCH' | translate }}\n</a>\n\n<ecl-search-form\n #searchForm\n [style]=\"{'--ecl-search-arrow-position': searchArrowPosition()}\"\n (searchEvent)=\"onSearch($event)\"\n [placeholder]=\"searchInputPlaceholder()\"\n (ngModelChange)=\"onModelChange($event)\"\n [(ngModel)]=\"searchInputText\"\n [searchButtonLabel]=\"searchButtonLabel()\"\n [class]=\"getSearchBoxCssClasses()\">\n</ecl-search-form>\n", styles: [":host{display:block}\n"] }]
|
|
1242
|
+
}], propDecorators: { searchInputText: [{
|
|
1202
1243
|
type: Input
|
|
1203
|
-
}], searchInputPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchInputPlaceholder", required: false }] }], searchButtonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchButtonLabel", required: false }] }],
|
|
1244
|
+
}], searchInputPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchInputPlaceholder", required: false }] }], searchButtonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchButtonLabel", required: false }] }], searchEvent: [{ type: i0.Output, args: ["searchEvent"] }], searchToggle: [{
|
|
1204
1245
|
type: Output
|
|
1205
|
-
}] } });
|
|
1246
|
+
}], searchLinkElement: [{ type: i0.ViewChild, args: ['searchLink', { ...{ read: ElementRef }, isSignal: true }] }], searchFormElement: [{ type: i0.ViewChild, args: ['searchForm', { ...{ read: ElementRef }, isSignal: true }] }] } });
|
|
1247
|
+
|
|
1248
|
+
/**
|
|
1249
|
+
* This class is used for methods, that serve for communication between the components in ecl-side-header.
|
|
1250
|
+
*/
|
|
1251
|
+
class EclAreaDataService {
|
|
1252
|
+
constructor() {
|
|
1253
|
+
this.dataSubject = new Subject();
|
|
1254
|
+
this.dataSubject$ = this.dataSubject.asObservable();
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* When header is resized, some of it's chidren parameters for positioning on the screen need to be recalculated.
|
|
1258
|
+
* This event publishes the new site header position, so that the chidren can adjust their position accordingly.
|
|
1259
|
+
* @param event
|
|
1260
|
+
*/
|
|
1261
|
+
publishEvent(event) {
|
|
1262
|
+
this.dataSubject.next(event);
|
|
1263
|
+
}
|
|
1264
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclAreaDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1265
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclAreaDataService, providedIn: 'root' }); }
|
|
1266
|
+
}
|
|
1267
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: EclAreaDataService, decorators: [{
|
|
1268
|
+
type: Injectable,
|
|
1269
|
+
args: [{ providedIn: 'root' }]
|
|
1270
|
+
}], ctorParameters: () => [] });
|
|
1206
1271
|
|
|
1207
1272
|
/**
|
|
1208
1273
|
* The Site header offers easily recognisable elements (such as the logo) and the most essential functionality of
|