@eui/core 17.0.0-next.5 → 17.0.0-next.7

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.
Files changed (37) hide show
  1. package/esm2022/lib/interceptors/openid/openid-connect.interceptor.mjs +1 -1
  2. package/esm2022/lib/services/dynamic-component/dynamic-component.service.mjs +63 -0
  3. package/esm2022/lib/services/dynamic-menu/dynamic-menu.service.mjs +7 -7
  4. package/esm2022/lib/services/errors/http-error-handler-callback-functions.mjs +4 -5
  5. package/esm2022/lib/services/eui-app-shell.service.mjs +575 -0
  6. package/esm2022/lib/services/growl/eui-growl.service.mjs +79 -0
  7. package/esm2022/lib/services/growl/index.mjs +2 -0
  8. package/esm2022/lib/services/index.mjs +4 -5
  9. package/esm2022/lib/services/store/effects/app.effects.mjs +8 -18
  10. package/fesm2022/eui-core.mjs +2007 -2059
  11. package/fesm2022/eui-core.mjs.map +1 -1
  12. package/lib/services/{ux-dynamic-component/ux-dynamic-component.service.d.ts → dynamic-component/dynamic-component.service.d.ts} +4 -4
  13. package/lib/services/dynamic-component/dynamic-component.service.d.ts.map +1 -0
  14. package/lib/services/dynamic-menu/dynamic-menu.service.d.ts +2 -2
  15. package/lib/services/dynamic-menu/dynamic-menu.service.d.ts.map +1 -1
  16. package/lib/services/errors/http-error-handler-callback-functions.d.ts.map +1 -1
  17. package/lib/services/{ux-app-shell.service.d.ts → eui-app-shell.service.d.ts} +4 -4
  18. package/lib/services/eui-app-shell.service.d.ts.map +1 -0
  19. package/lib/services/growl/eui-growl.service.d.ts +22 -0
  20. package/lib/services/growl/eui-growl.service.d.ts.map +1 -0
  21. package/lib/services/growl/index.d.ts +2 -0
  22. package/lib/services/growl/index.d.ts.map +1 -0
  23. package/lib/services/index.d.ts +3 -4
  24. package/lib/services/index.d.ts.map +1 -1
  25. package/lib/services/store/effects/app.effects.d.ts +3 -3
  26. package/lib/services/store/effects/app.effects.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/esm2022/lib/services/ux-app-shell.service.mjs +0 -577
  29. package/esm2022/lib/services/ux-dynamic-component/ux-dynamic-component.service.mjs +0 -63
  30. package/esm2022/lib/services/ux-error-feedback/ux-error-feedback.service.mjs +0 -108
  31. package/esm2022/lib/services/ux-error-feedback/ux-error-handler.operator.mjs +0 -16
  32. package/lib/services/ux-app-shell.service.d.ts.map +0 -1
  33. package/lib/services/ux-dynamic-component/ux-dynamic-component.service.d.ts.map +0 -1
  34. package/lib/services/ux-error-feedback/ux-error-feedback.service.d.ts +0 -36
  35. package/lib/services/ux-error-feedback/ux-error-feedback.service.d.ts.map +0 -1
  36. package/lib/services/ux-error-feedback/ux-error-handler.operator.d.ts +0 -4
  37. package/lib/services/ux-error-feedback/ux-error-handler.operator.d.ts.map +0 -1
@@ -1,29 +1,29 @@
1
1
  import * as i1 from '@eui/base';
2
- import { LogLevel, ConsoleAppender, Logger, LoggerMock, mergeAll, getI18nLoaderConfig, EuiLazyService, getI18nServiceConfigFromBase, getI18nState, getLastAddedModule, getBrowserDefaultLanguage, EuiEuLanguages, getActiveLang, initialAppState, initialUserState, initialNotificationsState, initialI18nState, initialLocaleState, xhr, getApiQueue, getApiQueueItem, EuiService, getUserRights, getUserState, UxPublishErrorFeedbackEvent, UxClearErrorFeedbackEvent, getLocaleServiceConfigFromBase, getLocaleState, transformToUxHttpResponse } from '@eui/base';
2
+ import { LogLevel, ConsoleAppender, Logger, LoggerMock, initialAppState, initialUserState, initialNotificationsState, initialI18nState, initialLocaleState, xhr, getApiQueue, getApiQueueItem, mergeAll, getI18nLoaderConfig, EuiLazyService, getI18nServiceConfigFromBase, getI18nState, getLastAddedModule, getBrowserDefaultLanguage, EuiEuLanguages, getActiveLang, EuiService, getUserRights, getUserState, getLocaleServiceConfigFromBase, getLocaleState, transformToUxHttpResponse } from '@eui/base';
3
3
  export * from '@eui/base';
4
4
  import * as i0 from '@angular/core';
5
- import { InjectionToken, Injectable, Inject, Injector, NgModule, Optional, APP_INITIALIZER, EventEmitter, PLATFORM_ID, SkipSelf, ErrorHandler, LOCALE_ID } from '@angular/core';
5
+ import { InjectionToken, Injectable, Inject, Injector, NgModule, Optional, APP_INITIALIZER, SkipSelf, ErrorHandler, EventEmitter, PLATFORM_ID, LOCALE_ID } from '@angular/core';
6
6
  import * as extendProxy from 'extend';
7
7
  import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
8
8
  import { MomentDateAdapter } from '@angular/material-moment-adapter';
9
9
  import { LOCATION_INITIALIZED, DOCUMENT, isPlatformBrowser, getLocaleId, registerLocaleData } from '@angular/common';
10
- import { throwError, forkJoin, of, BehaviorSubject, defer, merge, fromEvent, Observable, Subject, from } from 'rxjs';
11
- import { debounceTime, distinctUntilChanged, map, catchError, switchMap, tap, filter, take, mapTo, mergeMap, concatMap, takeUntil, finalize } from 'rxjs/operators';
10
+ import { throwError, merge, fromEvent, of, forkJoin, BehaviorSubject, defer, from, Subject, Observable } from 'rxjs';
11
+ import { debounceTime, distinctUntilChanged, mapTo, map, mergeMap, tap, take, switchMap, catchError, concatMap, filter, takeUntil, finalize } from 'rxjs/operators';
12
12
  import * as i1$1 from '@ngrx/store';
13
13
  import { createSelector } from '@ngrx/store';
14
- import * as i1$3 from '@ngrx/effects';
14
+ import * as i1$2 from '@ngrx/effects';
15
15
  import { createEffect, ofType } from '@ngrx/effects';
16
- import * as i2 from '@ngx-translate/core';
16
+ import * as i1$3 from '@ngx-translate/core';
17
17
  import { TranslateLoader } from '@ngx-translate/core';
18
- import * as i2$1 from '@angular/router';
19
- import { NavigationEnd } from '@angular/router';
20
- import isEqual from 'lodash-es/isEqual';
21
- import get from 'lodash-es/get';
22
- import * as i1$2 from '@angular/common/http';
18
+ import * as i1$5 from '@angular/common/http';
23
19
  import { HttpErrorResponse, HttpResponse, HttpHeaders, HttpEventType } from '@angular/common/http';
20
+ import * as i1$4 from '@angular/router';
21
+ import { NavigationEnd } from '@angular/router';
24
22
  import { By } from '@angular/platform-browser';
25
23
  import { FormGroup, FormArray } from '@angular/forms';
26
24
  import { DomPortalOutlet, ComponentPortal, PortalInjector } from '@angular/cdk/portal';
25
+ import isEqual from 'lodash-es/isEqual';
26
+ import get from 'lodash-es/get';
27
27
  import * as lf from 'localforage';
28
28
  import localeFr from '@angular/common/locales/fr';
29
29
  import localeEl from '@angular/common/locales/el';
@@ -615,2296 +615,2244 @@ class UpdateLocaleStateAction {
615
615
  }
616
616
  }
617
617
 
618
- class I18nResourceImpl {
619
- /**
620
- * Resource constructor
621
- *
622
- * @param prefix the prefix of the resource
623
- * @param suffix the suffix of the resource, optional parameter
624
- * @param compileTranslations compiler function, optional parameter
625
- */
626
- constructor(prefix, suffix = '', compileTranslationsFn = (translations) => translations) {
627
- this.prefix = prefix;
628
- this.suffix = suffix;
629
- this.compileTranslationsFn = compileTranslationsFn;
630
- }
631
- /**
632
- * Translations pre-processor
633
- *
634
- * @param translations the object to be pre-processed
635
- * @param lang the translations language
636
- * @returns the pre-processed translations
637
- */
638
- compileTranslations(translations, lang) {
639
- return this.compileTranslationsFn(translations, lang);
640
- }
641
- /**
642
- * Indicates whether other object is equal-to this one.
643
- *
644
- * @param obj the reference object with which to compare
645
- * @returns true if this object is the same as obj, false otherwise
646
- */
647
- equals(obj) {
648
- return this.prefix === obj.prefix && this.suffix === obj.suffix;
649
- }
650
- /**
651
- * The path to the resource
652
- *
653
- * @param lang the resource language
654
- * @returns the path
655
- */
656
- getPath(lang) {
657
- return `${this.prefix}${lang}${this.suffix}`;
658
- }
659
- }
660
-
661
- class I18nLoader {
662
- constructor(http, logService, euiAppConfig) {
663
- this.http = http;
664
- this.logService = logService;
665
- this.euiAppConfig = euiAppConfig;
666
- this.resources = [];
667
- this.failedResources = [];
668
- // create the resources from the config object
669
- const i18nConfig = this.euiAppConfig.global && this.euiAppConfig.global.i18n;
670
- const i18nLoaderConfig = i18nConfig && i18nConfig.i18nLoader;
671
- this.resources.push(...this.createResources(i18nLoaderConfig));
672
- }
673
- /**
674
- * Gets the translations from the server
675
- *
676
- * @param lang
677
- * @returns Observable<any>
678
- */
679
- getTranslation(lang) {
680
- return this.loadResources(this.resources, lang).pipe(map((loadedResources) => {
681
- this.failedResources = loadedResources.hasError ? loadedResources.errors : [];
682
- return loadedResources.translations;
683
- }));
684
- }
685
- /**
686
- * Adds resources
687
- *
688
- * @param config loader configuration
689
- * @returns Observable<any> | false
690
- */
691
- addResources(config) {
692
- // create the resources
693
- let resources = this.createResources(config);
694
- // filter the resources first, to avoid duplicates
695
- resources = resources.filter((resource) => !this.resources.some((res) => res.equals(resource)));
696
- // add the new resources to the list of existing resources
697
- this.resources.push(...resources);
698
- // return the filtered resources
699
- return resources;
700
- }
701
- /**
702
- * Removes resources from the loader instance
703
- */
704
- removeResources(removedResources) {
705
- this.resources = this.resources.filter((res) => !removedResources.some((removedResource) => removedResource.equals(res)));
618
+ class EuiGrowlService {
619
+ constructor() {
620
+ this.growlMessages = [];
621
+ this.isGrowlSticky = false;
622
+ this.isCloseAllSticky = false;
623
+ this.growlLife = 3000;
624
+ this.growlPosition = 'bottom-right';
625
+ this.growlCallback = null;
706
626
  }
707
627
  /**
708
- * Loads an array of resources, by language
709
- *
710
- * @param resources the definition of the resources
711
- * @param lang the resource language to load
712
- * @returns Observable<any>
628
+ * displays a growl UxMessage item, isSticky
713
629
  */
714
- loadResources(resources, lang) {
715
- // if there are resources to load
716
- if (Array.isArray(resources) && resources.length > 0) {
717
- // load all the resources in an array of Observable
718
- const requests = resources.map((resource) => this.loadResource(resource, lang));
719
- // group all the observables with forkJoin and deep merge them
720
- return forkJoin(requests).pipe(map((response) => {
721
- const successResp = [];
722
- const errResp = [];
723
- response.forEach((item) => {
724
- if (item.isError) {
725
- errResp.push({ resource: item.resource, error: item.error });
726
- }
727
- else {
728
- successResp.push(item);
729
- }
730
- });
731
- if (successResp.length === response.length) {
732
- return { hasError: false, translations: mergeAll(successResp) };
630
+ growl(msg, isSticky, isMultiple, life, position, callback) {
631
+ if (msg.severity !== 'info' && msg.severity !== 'warning' && msg.severity !== 'success' && msg.severity !== 'danger') {
632
+ throw new Error('EuiGrowlService.growl() ERROR : message severity must be either : success, warning, info, danger');
633
+ }
634
+ else {
635
+ if (isMultiple === undefined || !isMultiple) {
636
+ this.growlMessages = [];
637
+ }
638
+ this.growlMessages = this.growlMessages.concat(msg);
639
+ msg.life = life || msg.life;
640
+ msg.sticky = isSticky || msg.sticky;
641
+ if (life === undefined || isNaN(life)) {
642
+ if (msg.severity === 'danger') {
643
+ isSticky = true;
733
644
  }
734
645
  else {
735
- return { hasError: true, translations: mergeAll(successResp), errors: errResp };
646
+ this.growlLife = 3000;
736
647
  }
737
- }));
738
- }
739
- else {
740
- // no resources to load
741
- return of({ hasError: false, translations: {} });
648
+ }
649
+ else {
650
+ this.growlLife = life;
651
+ }
652
+ if (isSticky) {
653
+ this.isGrowlSticky = isSticky;
654
+ }
655
+ else {
656
+ this.isGrowlSticky = false;
657
+ }
658
+ if (position) {
659
+ this.growlPosition = position;
660
+ }
661
+ if (callback) {
662
+ this.growlCallback = callback;
663
+ }
664
+ else {
665
+ this.growlCallback = () => { };
666
+ }
742
667
  }
743
668
  }
744
- /**
745
- * Returns resources that failed
746
- */
747
- getFailedResources() {
748
- return this.failedResources;
669
+ clearGrowl() {
670
+ this.growlMessages = [];
749
671
  }
750
- /**
751
- * Create resources from a config file
752
- *
753
- * @param config loader configuration
754
- * @returns I18nResourceImpl[] resources
755
- */
756
- createResources(config) {
757
- // use the default config as a reference base
758
- config = getI18nLoaderConfig(config);
759
- const resources = [];
760
- // extract the i18n folders from config
761
- const i18nFolders = Array.isArray(config.i18nFolders)
762
- ? config.i18nFolders
763
- : config.i18nFolders
764
- ? [config.i18nFolders]
765
- : [];
766
- if (i18nFolders) {
767
- // add the i18n folders to resources
768
- resources.push(...i18nFolders.map((folder) => new I18nResourceImpl(`assets/${folder}/`, '.json')));
769
- }
770
- // extract the i18n services from config
771
- const i18nServices = Array.isArray(config.i18nServices)
772
- ? config.i18nServices
773
- : config.i18nServices
774
- ? [config.i18nServices]
775
- : [];
776
- if (i18nServices) {
777
- // add the i18n services to resources
778
- resources.push(...i18nServices.map((service) => new I18nResourceImpl(service)));
779
- }
780
- // extract the i18n resources from config
781
- const i18nResources = Array.isArray(config.i18nResources)
782
- ? config.i18nResources
783
- : config.i18nResources
784
- ? [config.i18nResources]
785
- : [];
786
- if (i18nResources) {
787
- // add the i18n resources to resources
788
- resources.push(...i18nResources.map((resource) => new I18nResourceImpl(resource.prefix, resource.suffix, this.getResourceCompileFunction(resource.compileTranslations))));
789
- }
790
- return resources;
672
+ growlSuccess(msg, position) {
673
+ this.growl({ severity: 'success', summary: 'SUCCESS', detail: msg }, false, false, undefined, position);
791
674
  }
792
- /**
793
- * Loads a resource, by language
794
- *
795
- * @param resource the definition of the resource
796
- * @param lang the resource language to load
797
- * @returns Observable<any>
798
- */
799
- loadResource(resource, lang) {
800
- // the path to the resource
801
- const path = resource.getPath(lang);
802
- // load the translations from the path
803
- return this.http.get(path).pipe(
804
- // preprocess the translations using the resource compiler
805
- map((translations) => resource.compileTranslations(translations, lang)), map((translations) => {
806
- // resource loaded properly, send a info message
807
- if (this.logService) {
808
- this.logService.info(`I18n resource loaded from path ${path}`);
809
- }
810
- // return the translations
811
- return translations;
812
- }), catchError((err) => {
813
- // remove failed resource from loaded resource array
814
- this.removeResources([resource]);
815
- // resource not loaded properly, send a warning message
816
- if (this.logService) {
817
- this.logService.warn(`I18n resource NOT loaded from path ${path}`, err);
818
- }
819
- const resourceError = { error: `I18n resource NOT loaded from path ${path}`, resource, isError: true };
820
- return of(resourceError);
821
- }));
675
+ growlError(msg, position) {
676
+ this.growl({ severity: 'danger', summary: 'ERROR', detail: msg }, false, false, undefined, position);
822
677
  }
823
- getResourceCompileFunction(id) {
824
- const customHandlers = this.euiAppConfig.customHandler;
825
- if (customHandlers && typeof customHandlers[id] === 'function') {
826
- return customHandlers[id];
827
- }
828
- return undefined;
678
+ // date helpers
679
+ growlWarning(msg, position) {
680
+ this.growl({ severity: 'warning', summary: 'WARNING', detail: msg }, false, false, undefined, position);
829
681
  }
830
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nLoader, deps: [{ token: i1$2.HttpClient }, { token: LogService, optional: true }, { token: CONFIG_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
831
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nLoader }); }
682
+ growlInfo(msg, position) {
683
+ this.growl({ severity: 'info', summary: 'INFO', detail: msg }, false, false, undefined, position);
684
+ }
685
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiGrowlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
686
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiGrowlService, providedIn: 'root' }); }
832
687
  }
833
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nLoader, decorators: [{
834
- type: Injectable
835
- }], ctorParameters: function () { return [{ type: i1$2.HttpClient }, { type: LogService, decorators: [{
836
- type: Optional
837
- }] }, { type: undefined, decorators: [{
838
- type: Inject,
839
- args: [CONFIG_TOKEN]
840
- }] }]; } });
841
- const translateConfig = {
842
- loader: {
843
- provide: TranslateLoader,
844
- useClass: I18nLoader,
845
- },
846
- };
688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiGrowlService, decorators: [{
689
+ type: Injectable,
690
+ args: [{
691
+ providedIn: 'root',
692
+ }]
693
+ }] });
847
694
 
848
- class I18nService extends EuiLazyService {
849
- constructor(baseGlobalConfig, translateService, logService, store, document) {
850
- super({ activeLang: 'en' });
851
- this.baseGlobalConfig = baseGlobalConfig;
695
+ class CoreAppEffects {
696
+ constructor(actions,
697
+ // TODO: remove on eUI 14
698
+ showConnectionStatus, translateService, asService) {
699
+ this.actions = actions;
700
+ this.showConnectionStatus = showConnectionStatus;
852
701
  this.translateService = translateService;
853
- this.logService = logService;
854
- this.store = store;
855
- this.document = document;
856
- this.config = getI18nServiceConfigFromBase(this.baseGlobalConfig);
857
- this.onModuleLoad = new BehaviorSubject({ ready: false, name: null });
858
- }
859
- getState(keyOrMapFn) {
860
- let selector = getI18nState;
861
- let dotNotationToObject = (obj, key) => key.split('.').reduce((slice, i) => slice[i], obj);
862
- if (keyOrMapFn) {
863
- switch (typeof keyOrMapFn) {
864
- case 'function':
865
- selector = createSelector(getI18nState, keyOrMapFn);
866
- break;
867
- case 'string':
868
- selector = createSelector(getI18nState, (state) => dotNotationToObject(state, keyOrMapFn));
869
- break;
870
- }
871
- }
872
- return this.store.select(selector);
873
- }
874
- updateState(langState) {
875
- this.updateHTMLDOMLang(langState.activeLang);
876
- this.store.dispatch(new UpdateI18nStateAction(langState));
877
- }
878
- init(langState) {
879
- const initLang = langState && langState.activeLang ? langState.activeLang : this.preparedDefaultLanguage();
880
- const initState = {
881
- ...langState,
882
- activeLang: initLang,
883
- };
884
- if (typeof initLang === 'string') {
885
- super.initEuiService(this.store);
886
- this.updateState(initState);
887
- return this.setup(initLang);
888
- }
889
- return of({ success: false, error: 'Initial active lang should be string' });
890
- }
891
- /**
892
- * A pipe function that could serve as a wrapper of another observable
893
- * e.g. ngx-translate i18n.onReady('my_module').pipe(switchMap(()=>translate.get('eui.KEY')));
894
- * WARNING: onReady will emit only once for a loaded Module, going to another and then going back will not result
895
- * into another emission.
896
- *
897
- * @param moduleName the name of the module that has been given through the eUI globalConfig. In case non provided
898
- * fetch module name from the state.
899
- */
900
- onReady(moduleName) {
901
- return this.onModuleLoad.pipe(switchMap((evt) => moduleName
902
- ? of(evt)
903
- : this.store.select(getLastAddedModule).pipe(tap((m) => (moduleName = m)), map(() => evt))),
904
- // emit only if event emitted matches the module name and is ready
905
- filter((evt) => evt.ready === true && evt.name === moduleName),
906
- // make sure that observable completes
907
- take(1));
908
- }
909
- lazyLoad(config) {
910
- const moduleId = Math.floor(Math.random() * 100000 + 1).toLocaleString();
911
- return this.addResources(config, moduleId);
912
- }
913
- lazyLoadInit(moduleConfig, moduleName) {
914
- const i18nLoaderConfig = moduleConfig;
915
- // add resources
916
- return this.addResources(i18nLoaderConfig, moduleName);
917
- }
918
- /**
919
- * Add resources based of a config loader
920
- *
921
- * @param config I18nLoaderConfig
922
- * @return Promise<EuiServiceStatus> an EuiServiceStatus when translation has fully loaded
923
- */
924
- addResources(config, moduleName) {
925
- // emit that module is loading
926
- this.onModuleLoad.next({ ready: false, name: moduleName });
927
- const loader = this.translateService.currentLoader;
928
- if (loader instanceof I18nLoader) {
929
- const langSubscription = [];
930
- const i18nLoader = loader;
931
- // add the resources to the loader
932
- const resources = i18nLoader.addResources(config);
933
- // manually load the resources for the CURRENT language and add them to the translate service
934
- langSubscription.push(i18nLoader.loadResources(resources, this.translateService.currentLang).pipe(take(1), tap((loadedResources) => {
935
- // add the new set of translations to the current language
936
- this.translateService.setTranslation(this.translateService.currentLang, loadedResources.translations, true);
937
- })));
938
- // if the current language is different than the DEFAULT language
939
- const defaultLang = this.config.defaultLanguage || this.translateService.defaultLang;
940
- if (this.translateService.currentLang !== defaultLang) {
941
- // manually load the resources for the default language and add them to the translate service
942
- langSubscription.push(i18nLoader.loadResources(resources, defaultLang).pipe(take(1), tap((loadedResources) => {
943
- this.translateService.setTranslation(defaultLang, loadedResources.translations, true);
944
- })));
945
- }
946
- return forkJoin(langSubscription).pipe(map((loadedResourcesArr) => !loadedResourcesArr[0].hasError ? { success: true } : { success: false, error: loadedResourcesArr[0].errors }), tap(() => {
947
- // emit status of module loading progress
948
- this.onModuleLoad.next({ ready: true, name: moduleName });
949
- }), catchError((error) => {
950
- // emit status of module loading progress
951
- this.onModuleLoad.next({ ready: true, name: moduleName, error });
952
- return of({ success: false, error });
953
- }));
954
- }
955
- else {
956
- return of({ success: false, error: 'currentLoader is not an I18nLoader.' });
957
- }
958
- }
959
- /**
960
- * Prepares the default language
961
- */
962
- preparedDefaultLanguage() {
963
- const browserPref = getBrowserDefaultLanguage();
964
- // If user has browser preferred lang, and that language is part of the languages array
965
- if (browserPref && this.config.languages && EuiEuLanguages.getLanguageCodes(this.config.languages).includes(browserPref)) {
966
- return browserPref;
967
- }
968
- return this.config.defaultLanguage;
969
- }
970
- /**
971
- * @param default_language if given default language to override default language coming from the
972
- * configuration token.
973
- */
974
- setup(initLanguage) {
975
- // use the default config as a reference base
976
- this.config = Object.assign({}, DEFAULT_I18N_SERVICE_CONFIG, this.config);
977
- // configure the translate config service
978
- if (this.config.languages) {
979
- this.translateService.addLangs(EuiEuLanguages.getLanguageCodes(this.config.languages));
980
- if (this.logService) {
981
- this.logService.info(`I18n accepted languages set to ${EuiEuLanguages.getLanguageCodes(this.config.languages)}`);
982
- }
983
- }
984
- // set the current language, causing to load translations
985
- return this.translateService.use(initLanguage).pipe(tap(() => {
986
- this.bindActiveLangStateToTranslateService();
987
- this.bindTranslateServiceLangChangeToState();
988
- if (this.config.defaultLanguage) {
989
- this.setDefaultLanguage(this.config.defaultLanguage);
990
- }
991
- }), map(() => {
992
- if (this.translateService.currentLoader instanceof I18nLoader) {
993
- const failedResources = this.translateService.currentLoader.getFailedResources();
994
- if (failedResources.length > 0) {
995
- return { success: false, error: failedResources };
702
+ this.asService = asService;
703
+ this.getAppConnectionInformation = createEffect(() => merge(fromEvent(window, 'online').pipe(mapTo(true)), fromEvent(window, 'offline').pipe(mapTo(false))).pipe(map((connected) => new UpdateAppConnectionAction(connected))));
704
+ this.activatedRoute = createEffect(() => this.actions.pipe(ofType(CoreAppActionTypes.ACTIVATED_ROUTE), map((action) => action.payload), mergeMap((route) => {
705
+ // extract the module name, if defined
706
+ const module = route.snapshot.data['module'] || 'undefined';
707
+ return [new UpdateCurrentModuleAction(module)];
708
+ })));
709
+ this.updateAppConnection = createEffect(() => this.actions.pipe(ofType(CoreAppActionTypes.UPDATE_APP_CONNECTION), map((action) => action.payload), tap((connected) => {
710
+ // TODO: for eUI 14, drop boolean support and refactor
711
+ // check if feature is enabled
712
+ const isFeatureEnabled = typeof this.showConnectionStatus !== 'boolean' ? this.showConnectionStatus?.enabled : this.showConnectionStatus;
713
+ // extract lifespan if any
714
+ const lifespan = typeof this.showConnectionStatus !== 'boolean' ? this.showConnectionStatus?.messageBox?.lifespan || 0 : 0;
715
+ // if the user needs to be informed about the connection status
716
+ if (isFeatureEnabled) {
717
+ if (connected) {
718
+ this.translateService.get('eui.CONNECTED').subscribe((translation) => {
719
+ this.asService.growl({ severity: 'success', summary: 'MESSAGE', detail: translation, life: lifespan }, lifespan < 1, false);
720
+ });
721
+ }
722
+ else {
723
+ this.translateService.get('eui.NOTCONNECTED').subscribe((translation) => {
724
+ this.asService.growl({ severity: 'danger', summary: 'ERROR', detail: translation, life: lifespan }, lifespan < 1, false);
725
+ });
996
726
  }
997
727
  }
998
- return { success: true };
999
- }), catchError((error) => of({ success: false, error })));
1000
- }
1001
- bindActiveLangStateToTranslateService() {
1002
- this.store.select(getActiveLang).subscribe((lang) => {
1003
- if (lang !== null && this.translateService.currentLang !== lang) {
1004
- this.use(lang);
1005
- }
1006
- });
1007
- }
1008
- bindTranslateServiceLangChangeToState() {
1009
- this.translateService.onLangChange.subscribe((event) => {
1010
- if (this.stateInstance.activeLang !== event.lang) {
1011
- this.updateState({ activeLang: event.lang });
1012
- }
1013
- if (this.logService) {
1014
- this.logService.info(`I18n current language set to ${event.lang}`);
1015
- }
1016
- });
1017
- }
1018
- setDefaultLanguage(defaultLanguage) {
1019
- // removed, current language is already calculated in init, setup function fill use it directly, not default
1020
- // this.translateService.currentLang = default_language || this.config.defaultLanguage;
1021
- this.translateService.setDefaultLang(defaultLanguage);
1022
- if (this.logService) {
1023
- this.logService.info(`I18n default language set to ${defaultLanguage}`);
1024
- }
1025
- }
1026
- /**
1027
- * change the currently used language
1028
- *
1029
- * @param lang is the language code based on the convention you used e.g. (en or en-EN)
1030
- * @returns Observable that returns an object contain the object translation for given language after language
1031
- * fully loaded.
1032
- */
1033
- use(lang) {
1034
- return this.translateService.use(lang);
1035
- }
1036
- /**
1037
- * updates the HTML element lang attribute
1038
- *
1039
- * @private
1040
- */
1041
- updateHTMLDOMLang(lang) {
1042
- this.document.documentElement.lang = lang;
728
+ })), { dispatch: false });
1043
729
  }
1044
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nService, deps: [{ token: GLOBAL_CONFIG_TOKEN }, { token: i2.TranslateService }, { token: LogService, optional: true }, { token: StoreService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); }
1045
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nService, providedIn: 'root' }); }
730
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreAppEffects, deps: [{ token: i1$2.Actions }, { token: SHOW_CONNECTION_STATUS_TOKEN }, { token: i1$3.TranslateService }, { token: EuiGrowlService }], target: i0.ɵɵFactoryTarget.Injectable }); }
731
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreAppEffects }); }
1046
732
  }
1047
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nService, decorators: [{
1048
- type: Injectable,
1049
- args: [{
1050
- providedIn: 'root',
1051
- }]
1052
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
1053
- type: Inject,
1054
- args: [GLOBAL_CONFIG_TOKEN]
1055
- }] }, { type: i2.TranslateService }, { type: LogService, decorators: [{
1056
- type: Optional
1057
- }] }, { type: StoreService }, { type: Document, decorators: [{
733
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreAppEffects, decorators: [{
734
+ type: Injectable
735
+ }], ctorParameters: function () { return [{ type: i1$2.Actions }, { type: undefined, decorators: [{
1058
736
  type: Inject,
1059
- args: [DOCUMENT]
1060
- }] }]; } });
737
+ args: [SHOW_CONNECTION_STATUS_TOKEN]
738
+ }] }, { type: i1$3.TranslateService }, { type: EuiGrowlService }]; } });
1061
739
 
1062
- class I18nServiceMock extends I18nService {
1063
- constructor() {
1064
- super(null, null, null, null, null);
1065
- this.DEFAULT_LANGUAGE = 'en';
1066
- }
1067
- get onLangChange() {
1068
- return new EventEmitter();
1069
- }
1070
- addResources() {
1071
- return of({ success: true });
1072
- }
1073
- get currentLanguage() {
1074
- return this.DEFAULT_LANGUAGE;
1075
- }
1076
- getState() {
1077
- return of({ activeLang: this.DEFAULT_LANGUAGE });
740
+ const CoreModuleEffects = [CoreAppEffects];
741
+
742
+ const updateAppVersion = (state, action) => ({ ...state, version: action.payload });
743
+ const updateAppConnection = (state, action) => ({ ...state, connected: action.payload });
744
+ const AddAppLoadedConfigModules = (state, action) => ({
745
+ ...state,
746
+ loadedConfigModules: {
747
+ lastAddedModule: action.payload.moduleName,
748
+ modulesConfig: {
749
+ ...state.loadedConfigModules.modulesConfig,
750
+ [action.payload.moduleName]: {
751
+ ...action.payload.moduleConfig,
752
+ },
753
+ },
754
+ },
755
+ });
756
+ const updateAppStatus = (state, action) => ({ ...state, status: action.payload });
757
+ const updateCurrentModule = (state, action) => Object.assign({}, state, { currentModule: action.payload });
758
+ const addApiQueueItem = (state, action) => {
759
+ // apply the timestamp to the queue item
760
+ const apiQueueItem = Object.assign({}, action.payload.item, { timestamp: new Date().getTime() });
761
+ // update the queue
762
+ const apiQueue = Object.assign({}, state.apiQueue, { [action.payload.id]: apiQueueItem });
763
+ return Object.assign({}, state, { apiQueue });
764
+ };
765
+ const removeApiQueueItem = (state, action) => {
766
+ // remove the specified id from the list of item ids and recreate the apiQueue from the remaining ids
767
+ const apiQueue = Object.keys(state.apiQueue)
768
+ .filter((id) => id !== action.payload)
769
+ .reduce((queue, key) => {
770
+ queue[key] = state.apiQueue[key];
771
+ return queue;
772
+ }, {});
773
+ return Object.assign({}, state, { apiQueue });
774
+ };
775
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
776
+ const emptyApiQueue = (state, action) =>
777
+ // remove all items in the Api Queue
778
+ Object.assign({}, state, { apiQueue: {} });
779
+ const actionToReducerMap$4 = {
780
+ [CoreAppActionTypes.UPDATE_APP_VERSION]: updateAppVersion,
781
+ [CoreAppActionTypes.UPDATE_APP_CONNECTION]: updateAppConnection,
782
+ [CoreAppActionTypes.ADD_APP_LOADED_CONFIG_MODULES]: AddAppLoadedConfigModules,
783
+ [CoreAppActionTypes.UPDATE_APP_STATUS]: updateAppStatus,
784
+ [CoreAppActionTypes.UPDATE_CURRENT_MODULE]: updateCurrentModule,
785
+ [CoreAppActionTypes.ADD_API_QUEUE_ITEM]: addApiQueueItem,
786
+ [CoreAppActionTypes.REMOVE_API_QUEUE_ITEM]: removeApiQueueItem,
787
+ [CoreAppActionTypes.EMPTY_API_QUEUE]: emptyApiQueue,
788
+ };
789
+ const coreAppReducers = (state = initialAppState, action) => {
790
+ if (actionToReducerMap$4[action.type]) {
791
+ return actionToReducerMap$4[action.type](state, action);
1078
792
  }
1079
- updateState(i18nState) {
1080
- this.DEFAULT_LANGUAGE = i18nState.activeLang || 'en';
793
+ // if the action type is not in the map, call the base reducers method
794
+ return state;
795
+ };
796
+
797
+ const updateUser = (state, action) => {
798
+ let fullName = action.payload.fullName;
799
+ if (!fullName) {
800
+ fullName = `${action.payload.firstName || ''} ${action.payload.lastName || ''}`.trim();
1081
801
  }
1082
- init(i18nState) {
1083
- this.DEFAULT_LANGUAGE = i18nState.activeLang || 'en';
1084
- return of({ success: true });
802
+ return Object.assign({}, state, action.payload, { fullName });
803
+ };
804
+ const updateUserDetails = (state, action) => {
805
+ let fullName = action.payload.fullName;
806
+ if (!fullName) {
807
+ fullName = `${action.payload.firstName || ''} ${action.payload.lastName || ''}`.trim();
1085
808
  }
1086
- lazyLoadInit() {
1087
- return of({ success: true });
809
+ // todo should be checked if state.details is really there
810
+ const details = Object.assign({}, state.details, action.payload, { fullName });
811
+ return Object.assign({}, state, details);
812
+ };
813
+ const updateUserPreferences = (state, action) => {
814
+ const preferences = Object.assign({}, state.preferences, action.payload);
815
+ return Object.assign({}, state, { preferences });
816
+ };
817
+ const updateUserRights = (state, action) => Object.assign({}, state, { rights: [...action.payload] });
818
+ const updateUserDashboard = (state, action) => {
819
+ // apply the dashboard to preferences
820
+ const preferences = Object.assign({}, state.preferences, { dashboard: action.payload });
821
+ // update the state
822
+ return { ...state, preferences };
823
+ };
824
+ const actionToReducerMap$3 = {
825
+ [CoreUserActionTypes.UPDATE_USER_DETAILS]: updateUserDetails,
826
+ [CoreUserActionTypes.UPDATE_USER_PREFERENCES]: updateUserPreferences,
827
+ [CoreUserActionTypes.UPDATE_USER_STATE]: updateUser,
828
+ [CoreUserActionTypes.UPDATE_USER_RIGHTS]: updateUserRights,
829
+ [CoreUserActionTypes.UPDATE_USER_DASHBOARD]: updateUserDashboard,
830
+ };
831
+ const coreUserReducers = (state = initialUserState, action) => {
832
+ if (actionToReducerMap$3[action.type]) {
833
+ return actionToReducerMap$3[action.type](state, action);
1088
834
  }
1089
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nServiceMock, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1090
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nServiceMock }); }
1091
- }
1092
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nServiceMock, decorators: [{
1093
- type: Injectable
1094
- }], ctorParameters: function () { return []; } });
835
+ return state;
836
+ };
1095
837
 
1096
- const initialState = {
1097
- appName: '',
1098
- appShortName: '',
1099
- appSubTitle: '',
1100
- appBaseFontSize: '',
1101
- userInfos: null,
1102
- userSubInfos: null,
1103
- impersonatedUserInfos: null,
1104
- isSidebarOpen: true,
1105
- isSidebarHover: false,
1106
- isSidebarActive: false,
1107
- isSidebarInnerActive: false,
1108
- isSidebarStateCloseWithIcons: false,
1109
- isSidebarExpandOnHover: true,
1110
- hasSidebar: false,
1111
- hasHeader: false,
1112
- hasBreadcrumb: false,
1113
- hasHeaderLogo: false,
1114
- hasHeaderEnvironment: false,
1115
- hasToolbar: false,
1116
- hasToolbarMenu: false,
1117
- environmentValue: '',
1118
- isSidebarHidden: false,
1119
- hasTopMessage: false,
1120
- windowWidth: 0,
1121
- windowHeight: 0,
1122
- mainContentHeight: 0,
1123
- pageHeaderHeight: 0,
1124
- wrapperClasses: '',
1125
- breakpoint: '',
1126
- breakpoints: {
1127
- isMobile: false,
1128
- isTablet: false,
1129
- isLtDesktop: false,
1130
- isDesktop: false,
1131
- isXL: false,
1132
- isXXL: false,
1133
- isFHD: false,
1134
- is2K: false,
1135
- is4K: false,
1136
- },
1137
- menuLinks: [],
1138
- sidebarLinks: [],
1139
- combinedLinks: [],
1140
- isBlockDocumentActive: false,
1141
- deviceInfo: null,
1142
- activeLanguage: 'en',
1143
- languages: EuiEuLanguages.getLanguages(),
1144
- appMetadata: null,
1145
- hasModalActive: false,
1146
- isDimmerActive: false,
838
+ const updateNotificationsList = (state, action) => Object.assign({}, state, { list: [...action.payload] });
839
+ const actionToReducerMap$2 = {
840
+ [CoreNotificationsActionTypes.UPDATE_NOTIFICATIONS_LIST]: updateNotificationsList,
1147
841
  };
1148
- // TODO: this service needs to be transferred in the respective component
1149
- class UxAppShellService {
1150
- // -------------------
1151
- get state$() {
1152
- return this._state$.asObservable();
1153
- }
1154
- // -------------------
1155
- // exposed observables
1156
- get breakpoint$() {
1157
- return this._breakpoint$.asObservable();
1158
- }
1159
- get breakpoints$() {
1160
- return this._breakpoints$.asObservable();
842
+ const coreNotificationsReducers = (state = initialNotificationsState, action) => {
843
+ if (actionToReducerMap$2[action.type]) {
844
+ return actionToReducerMap$2[action.type](state, action);
1161
845
  }
1162
- // ----------------
1163
- // state operations
1164
- // ----------------
1165
- get state() {
1166
- return this._state$.getValue();
846
+ return state;
847
+ };
848
+
849
+ const updateI18nState = (state, action) => ({
850
+ ...state,
851
+ ...action.payload,
852
+ });
853
+ const actionToReducerMap$1 = {
854
+ [CoreI18nActionTypes.UPDATE_I18N_STATE]: updateI18nState,
855
+ };
856
+ const corI18nReducers = (state = initialI18nState, action) => {
857
+ if (actionToReducerMap$1[action.type]) {
858
+ return actionToReducerMap$1[action.type](state, action);
1167
859
  }
1168
- // ----------------------------
1169
- // public setters and functions
1170
- // ----------------------------
1171
- set isSidebarOpen(isOpen) {
1172
- this.setState({
1173
- ...this.state,
1174
- isSidebarOpen: isOpen,
1175
- });
860
+ return state;
861
+ };
862
+
863
+ const updateLocaleState = (state, action) => ({
864
+ ...state,
865
+ ...action.payload,
866
+ });
867
+ const actionToReducerMap = {
868
+ [CoreLocaleActionTypes.UPDATE_LOCALE_STATE]: updateLocaleState,
869
+ };
870
+ const coreLocaleReducers = (state = initialLocaleState, action) => {
871
+ if (actionToReducerMap[action.type]) {
872
+ return actionToReducerMap[action.type](state, action);
1176
873
  }
1177
- get isSidebarOpen() {
1178
- return this.state.isSidebarOpen;
874
+ return state;
875
+ };
876
+
877
+ const coreReducers = Object.assign({}, {
878
+ app: coreAppReducers,
879
+ user: coreUserReducers,
880
+ notifications: coreNotificationsReducers,
881
+ i18n: corI18nReducers,
882
+ locale: coreLocaleReducers,
883
+ });
884
+
885
+ const extend = extendProxy.default || extendProxy;
886
+ /**
887
+ * Utility meta-reducer to load the state from the local storage
888
+ * @param reducer the action reducer
889
+ */
890
+ function localStorageSync(reducer) {
891
+ return cb(reducer, localStorage);
892
+ }
893
+ /**
894
+ * Utility meta-reducer to load the state from the session storage
895
+ * @param reducer the action reducer
896
+ */
897
+ function sessionStorageSync(reducer) {
898
+ return cb(reducer, sessionStorage);
899
+ }
900
+ // Keys that are allowed to be synced with NgRx state from BrowserStorage
901
+ const disallowedKeys = ['loadedConfigModules', 'connect', 'currentModule', 'status', 'apiQueue'];
902
+ /**
903
+ * filters the AppState by removing those keys from the state and return a new state Object
904
+ * @param app the AppState
905
+ */
906
+ const filterAppState = (app) => {
907
+ // in case app is undefined or null it should return empty object
908
+ if (!app) {
909
+ return {};
1179
910
  }
1180
- set isSidebarHover(isHover) {
1181
- if (this.state.isSidebarExpandOnHover) {
1182
- this.setState({
1183
- ...this.state,
1184
- isSidebarHover: isHover,
1185
- });
911
+ return (Object.keys(app)
912
+ // select keys that are present in the allowed list
913
+ .filter((key) => !disallowedKeys.includes(key))
914
+ // build a new object with only the allowed properties
915
+ .reduce((obj, key) => {
916
+ obj[key] = app[key];
917
+ return obj;
918
+ }, {}));
919
+ };
920
+ /**
921
+ *
922
+ * @param storageType
923
+ */
924
+ const loadState = (storage = localStorage) => {
925
+ try {
926
+ // by default falls back to localStorage
927
+ const serializedState = storage.getItem('state');
928
+ if (serializedState === null) {
929
+ return undefined;
1186
930
  }
931
+ // deserialize state
932
+ let state = JSON.parse(serializedState);
933
+ // filter AppState and replace
934
+ state = { ...state, app: filterAppState(state?.app) };
935
+ return state;
1187
936
  }
1188
- get isSidebarHover() {
1189
- return this.state.isSidebarHover;
937
+ catch (err) {
938
+ return undefined;
1190
939
  }
1191
- set isSidebarActive(isActive) {
1192
- this.setState({
1193
- ...this.state,
1194
- isSidebarActive: isActive,
1195
- });
940
+ };
941
+ const cb = (reducer, storage) => (state, action) => {
942
+ if (action.type === CoreAppActionTypes.INIT_STORE) {
943
+ // This action must be dispatched from an app initializer
944
+ // We load the state from a storage and compare the version and userId with the provided ones.
945
+ // If they are the same, we merge the local state into the initial state.
946
+ // If there is a mismatch, we don't merge the local state, but update only the initial state.
947
+ // load the state from the localStorage
948
+ const localState = loadState(storage);
949
+ // extract the details about user and app version from the action payload
950
+ const payload = action.payload;
951
+ const payloadVersion = payload?.version;
952
+ if (localState) {
953
+ // TODO: Why all these checks here? What is the purpose of this code?
954
+ // extract the details about user and app version from the storage state
955
+ const localVersion = localState?.app?.version;
956
+ // const localUserId = localState.user && localState.user.userId;
957
+ const isVersionOK = !localVersion || !payloadVersion || localVersion === payloadVersion;
958
+ // const isUserIdOK = (localUserId === payloadUserId);
959
+ // update the entire state with deep merge of states, if the version and user are the same
960
+ if (isVersionOK) {
961
+ state = extend(true, {}, state, localState);
962
+ }
963
+ }
964
+ // update the app version, if defined
965
+ if (payloadVersion) {
966
+ state = { ...state, app: { ...state?.app, version: payloadVersion } };
967
+ }
1196
968
  }
1197
- set isSidebarInnerActive(isOpen) {
1198
- this.setState({
1199
- ...this.state,
1200
- isSidebarInnerActive: isOpen,
1201
- });
969
+ return reducer(state, action);
970
+ };
971
+
972
+ class StoreServiceMock extends StoreService {
973
+ constructor() {
974
+ super(null, null);
1202
975
  }
1203
- set sidebarLinks(links) {
1204
- this.setState({
1205
- ...this.state,
1206
- sidebarLinks: links,
1207
- });
976
+ init() { }
977
+ addAutoSaveHandler() { }
978
+ handleAutoSave() { }
979
+ dispatch() { }
980
+ select() {
981
+ return of({});
1208
982
  }
1209
- set menuLinks(links) {
1210
- this.setState({
1211
- ...this.state,
1212
- menuLinks: links,
1213
- });
983
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: StoreServiceMock, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
984
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: StoreServiceMock }); }
985
+ }
986
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: StoreServiceMock, decorators: [{
987
+ type: Injectable
988
+ }], ctorParameters: function () { return []; } });
989
+
990
+ const CORE_ROOT_GUARD = new InjectionToken('Internal Theme ForRoot Guard');
991
+ class CoreModule {
992
+ /**
993
+ * No more providing config from forRoot, you have to use EUI_CONFIG_TOKEN to pass config via providers.
994
+ */
995
+ static forRoot() {
996
+ return {
997
+ ngModule: CoreModule,
998
+ providers: [
999
+ {
1000
+ provide: EuiCoreRootGuardClass,
1001
+ useFactory: euiCoreRootGuardClass,
1002
+ deps: [],
1003
+ },
1004
+ {
1005
+ provide: CORE_ROOT_GUARD,
1006
+ useFactory: createEuiCoreRootGuard,
1007
+ deps: [[EuiCoreRootGuardClass, new Optional(), new SkipSelf()]],
1008
+ },
1009
+ ...getCoreProviders(),
1010
+ ],
1011
+ };
1214
1012
  }
1215
- set isBlockDocumentActive(isActive) {
1216
- this.setState({
1217
- ...this.state,
1218
- isBlockDocumentActive: isActive,
1219
- });
1013
+ static forChild(moduleName) {
1014
+ return {
1015
+ ngModule: CoreModule,
1016
+ providers: getCoreChildProviders(moduleName),
1017
+ };
1220
1018
  }
1221
- get hasHeader() {
1222
- return this.state.hasHeader;
1019
+ constructor(guard, parentModule, injector) {
1020
+ // if there is a parentModule, a new instance is created in a lazy loaded module
1021
+ if (parentModule) {
1022
+ // extract the i18n service and the i18n loader config
1023
+ // todo, this thing should be achieved by store, and localization service dependency should be removed
1024
+ const storeService = injector.get(StoreService);
1025
+ const moduleName = injector.get(MODULE_NAME_TOKEN);
1026
+ const moduleConfig = injector.get(MODULE_CONFIG_TOKEN);
1027
+ storeService.dispatch(new AddAppLoadedConfigModulesAction({ moduleName, moduleConfig }));
1028
+ }
1223
1029
  }
1224
- // Edit mode
1225
- get isDimmerActive() {
1226
- return this.state.isDimmerActive;
1030
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule, deps: [{ token: CORE_ROOT_GUARD, optional: true }, { token: CoreModule, optional: true, skipSelf: true }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.NgModule }); }
1031
+ /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule }); }
1032
+ /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule }); }
1033
+ }
1034
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule, decorators: [{
1035
+ type: NgModule,
1036
+ args: [{
1037
+ imports: [
1038
+ // HttpClientModule, no more..
1039
+ ],
1040
+ }]
1041
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
1042
+ type: Optional
1043
+ }, {
1044
+ type: Inject,
1045
+ args: [CORE_ROOT_GUARD]
1046
+ }] }, { type: CoreModule, decorators: [{
1047
+ type: Optional
1048
+ }, {
1049
+ type: SkipSelf
1050
+ }] }, { type: i0.Injector }]; } });
1051
+ const createEuiCoreRootGuard = (core) => {
1052
+ if (core) {
1053
+ throw new TypeError(`CoreModule.forRoot() called twice. Feature modules should use ThemeModule.forChild() instead.`);
1227
1054
  }
1228
- set isDimmerActive(isActive) {
1229
- this.setState({
1230
- ...this.state,
1231
- isDimmerActive: isActive,
1232
- });
1055
+ return 'guarded';
1056
+ };
1057
+ class EuiCoreRootGuardClass {
1058
+ constructor() { }
1059
+ }
1060
+ const euiCoreRootGuardClass = () => new EuiCoreRootGuardClass();
1061
+
1062
+ /**
1063
+ * Function to be used before the AppModule is bootstrapped. It is currently used to load dynamic configurations.
1064
+ * It needs to be added in your application main.ts file.
1065
+ * @param envConfig the current environment configuration
1066
+ * @returns the updated environment configuration, as a promise
1067
+ */
1068
+ async function preInitApp(envConfig) {
1069
+ const envDynamicConfig = envConfig && envConfig.envDynamicConfig;
1070
+ if (envDynamicConfig) {
1071
+ // load the local configuration file, if defined
1072
+ const euiAppJsonConfig = await loadEuiDynamicEnvironmentConfig(envDynamicConfig.uri, envDynamicConfig.configTimeout || 2000);
1073
+ envConfig.loadedEnvDynamicConfig = euiAppJsonConfig;
1074
+ return envConfig;
1233
1075
  }
1234
- constructor(config, http, platformId, router, storeService, i18nService) {
1235
- this.config = config;
1236
- this.http = http;
1237
- this.platformId = platformId;
1238
- this.router = router;
1239
- this.storeService = storeService;
1240
- this.i18nService = i18nService;
1241
- this.cssVars = [];
1242
- let stateWithConfig = initialState;
1243
- const languages = config?.i18n?.i18nService?.languages || initialState.languages;
1244
- const defaultLanguage = config?.i18n?.i18nService?.defaultLanguage || initialState.activeLanguage;
1245
- stateWithConfig = {
1246
- ...stateWithConfig,
1247
- ...{
1248
- languages,
1249
- activeLanguage: defaultLanguage,
1250
- },
1251
- };
1252
- this._state$ = new BehaviorSubject(stateWithConfig);
1253
- this._breakpoint$ = new BehaviorSubject('');
1254
- this._breakpoints$ = new BehaviorSubject({});
1255
- this.bindActiveLanguageToAppShellState();
1256
- if (isPlatformBrowser(this.platformId)) {
1257
- router.events.subscribe((event) => {
1258
- if (event instanceof NavigationEnd) {
1259
- window.scrollTo(0, 0);
1260
- }
1076
+ return envConfig;
1077
+ }
1078
+ /**
1079
+ * Function to load asynchronously a dynamic configuration (from a local path or a web service)
1080
+ *
1081
+ * @param url the path to the configuration
1082
+ * @param timeout possible timeout
1083
+ * @returns EuiAppJsonConfig promise
1084
+ */
1085
+ async function loadEuiDynamicEnvironmentConfig(url, timeout) {
1086
+ // if the url is specified
1087
+ if (url) {
1088
+ // fetch the config
1089
+ try {
1090
+ return await xhr({
1091
+ url,
1092
+ timeout,
1261
1093
  });
1262
1094
  }
1263
- }
1264
- setState(nextState, updateI18 = true) {
1265
- let breakpoint, breakpoints, wrapperClasses;
1266
- let combinedLinks;
1267
- const state = this.state;
1268
- // check if window width has been updated from previous state
1269
- if (this.state.windowWidth !== nextState.windowWidth) {
1270
- breakpoint = this.getBreakpoint(nextState.windowWidth);
1271
- breakpoints = this.getBreakpoints(breakpoint);
1272
- this._breakpoint$.next(breakpoint);
1273
- this._breakpoints$.next(breakpoints);
1274
- // if not propagate the old ones without doing any calculations
1275
- }
1276
- else {
1277
- breakpoint = state.breakpoint;
1278
- breakpoints = state.breakpoints;
1279
- }
1280
- // finally get the wrapper classes when both the state and breakpoint are known
1281
- wrapperClasses = this.getWrapperClasses(nextState, breakpoint);
1282
- // check if the menuLinks or sidebarLinks have changed from previous state
1283
- if (this.state.menuLinks !== nextState.menuLinks || this.state.sidebarLinks !== nextState.sidebarLinks) {
1284
- combinedLinks = [...nextState.menuLinks, ...nextState.sidebarLinks];
1095
+ catch (error) {
1096
+ // send a warning to the console
1097
+ console.warn('Dynamic configuration cannot be loaded from url', url, error);
1285
1098
  }
1286
- else {
1287
- combinedLinks = this.state.combinedLinks;
1099
+ }
1100
+ // the dynamic config is not loaded
1101
+ return null;
1102
+ }
1103
+
1104
+ const errorCodes = {
1105
+ ERR_GENERIC: 'ERR_GENERIC',
1106
+ };
1107
+ const messages = {
1108
+ ERR_GENERIC: 'An error occured',
1109
+ };
1110
+ // Intermediate "hacky" subclass taken from
1111
+ // https://www.bennadel.com/blog/3226-experimenting-with-error-sub-classing-using-es5-and-typescript-2-1-5.htm
1112
+ class ErrorSubClass {
1113
+ constructor(code, msg, args) {
1114
+ if (!msg && code && code in messages) {
1115
+ if (typeof messages[code] === 'function') {
1116
+ msg = messages[code](...args.messageParams);
1117
+ }
1118
+ else {
1119
+ msg = messages[code];
1120
+ }
1288
1121
  }
1289
- const stateBeforeUpdate = { ...this.state };
1290
- // we put it all together with the calculated properties
1291
- this._state$.next({
1292
- ...nextState,
1293
- wrapperClasses,
1294
- breakpoint,
1295
- breakpoints,
1296
- combinedLinks,
1297
- appBaseFontSize: this.getBaseFontSize(),
1298
- });
1299
- // update the Store Language
1300
- if (updateI18 && nextState.activeLanguage !== stateBeforeUpdate.activeLanguage) {
1301
- this.i18nService.updateState({ activeLang: nextState.activeLanguage });
1122
+ this.name = 'ErrorSubClass';
1123
+ this.message = msg;
1124
+ this.code = code;
1125
+ this.stack = new Error(msg).stack;
1126
+ if (args) {
1127
+ Object.keys(args).forEach((k) => {
1128
+ if (k !== 'messageParams') {
1129
+ this[k] = args[k];
1130
+ }
1131
+ });
1302
1132
  }
1303
1133
  }
1134
+ }
1135
+ // This replaces the "extends" on the Class
1136
+ // ErrorSubClass.prototype = <any>Object.create(Error.prototype);
1137
+ class EuiError extends ErrorSubClass {
1138
+ constructor(code = errorCodes.ERR_GENERIC, msg, args) {
1139
+ super(code, msg, args);
1140
+ this.name = 'EuiError';
1141
+ }
1142
+ }
1143
+
1144
+ class GlobalErrorHandler extends ErrorHandler {
1145
+ constructor(logService) {
1146
+ super();
1147
+ this.logService = logService;
1148
+ }
1149
+ handleError(error) {
1150
+ // log error
1151
+ this.logService.error(error.message || error.toString());
1152
+ // throw error;
1153
+ super.handleError(error);
1154
+ }
1155
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: GlobalErrorHandler, deps: [{ token: LogService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1156
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: GlobalErrorHandler, providedIn: 'root' }); }
1157
+ }
1158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: GlobalErrorHandler, decorators: [{
1159
+ type: Injectable,
1160
+ args: [{
1161
+ providedIn: 'root',
1162
+ }]
1163
+ }], ctorParameters: function () { return [{ type: LogService }]; } });
1164
+
1165
+ class HttpErrorHandlerInterceptor {
1166
+ constructor(config = DEFAULT_HTTP_ERROR_HANDLER_CONFIG, injector, router) {
1167
+ this.config = config;
1168
+ this.injector = injector;
1169
+ this.router = router;
1170
+ }
1171
+ intercept(req, next) {
1172
+ return next.handle(req).pipe(tap(() => { }, (error) => {
1173
+ // in case of http error
1174
+ if (error instanceof HttpErrorResponse) {
1175
+ const routeConfig = this.getRouteConfig(error);
1176
+ const rule = routeConfig && (routeConfig[error.status] || routeConfig.default);
1177
+ if (typeof rule === 'string') {
1178
+ // perform a redirect
1179
+ this.router.navigate([rule]);
1180
+ }
1181
+ else if (typeof rule === 'function') {
1182
+ // callback function
1183
+ rule.call(null, error, this.injector);
1184
+ }
1185
+ }
1186
+ }));
1187
+ }
1304
1188
  /**
1305
- * Emits a slice from the state whether that changes
1189
+ * Search in the configuration for the first matching route
1306
1190
  *
1307
- * @param key can be 'key' or 'key.sub.sub'
1191
+ * @param error http error response
1192
+ * @returns HttpErrorRouteConfig or null
1308
1193
  */
1309
- getState(key) {
1310
- return defer(() =>
1311
- // check if key exists
1312
- key
1313
- ? this.state$.pipe(map((state) => get(state, key)),
1314
- // filter((state) => state),
1315
- distinctUntilChanged((x, y) => isEqual(x, y)))
1316
- : this.state$);
1317
- }
1318
- sidebarToggle() {
1319
- this.isSidebarOpen = !this.state.isSidebarOpen;
1320
- }
1321
- // Edit mode
1322
- dimmerActiveToggle() {
1323
- const isActive = this.isDimmerActive;
1324
- this.setState({
1325
- ...this.state,
1326
- isDimmerActive: !isActive,
1194
+ getRouteConfig(error) {
1195
+ // filter the routes
1196
+ const routes = this.config.routes.filter((route) => {
1197
+ const regex = new RegExp('^' + route.path.split('*').join('.*') + '$');
1198
+ return regex.test(error.url) && (typeof route[error.status] !== 'undefined' || typeof route.default !== 'undefined');
1327
1199
  });
1328
- this.activateEditModeCssVars(!isActive);
1200
+ // return the first matching route, if any
1201
+ return routes.length > 0 ? routes[0] : null;
1329
1202
  }
1330
- setDimmerActiveState(activeState) {
1331
- this.setState({
1332
- ...this.state,
1333
- isDimmerActive: activeState,
1334
- });
1335
- this.activateEditModeCssVars(activeState);
1203
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: HttpErrorHandlerInterceptor, deps: [{ token: HTTP_ERROR_HANDLER_CONFIG_TOKEN }, { token: i0.Injector }, { token: i1$4.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
1204
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: HttpErrorHandlerInterceptor }); }
1205
+ }
1206
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: HttpErrorHandlerInterceptor, decorators: [{
1207
+ type: Injectable
1208
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
1209
+ type: Inject,
1210
+ args: [HTTP_ERROR_HANDLER_CONFIG_TOKEN]
1211
+ }] }, { type: i0.Injector }, { type: i1$4.Router }]; } });
1212
+
1213
+ function AlertHttpErrorCallbackFn(error) {
1214
+ alert(error.statusText);
1215
+ }
1216
+ function ConsoleHttpErrorCallbackFn(error) {
1217
+ console.error('HttpError', error);
1218
+ }
1219
+ function LogHttpErrorCallbackFn(error, injector) {
1220
+ const logService = injector.get(LogService);
1221
+ logService.error('HttpError', error);
1222
+ }
1223
+ function GrowlHttpErrorCallbackFn(error, injector) {
1224
+ const asService = injector.get(EuiGrowlService);
1225
+ asService.growlError(error.statusText);
1226
+ }
1227
+
1228
+ class ApiQueueService {
1229
+ constructor(store, http, logService) {
1230
+ this.store = store;
1231
+ this.http = http;
1232
+ this.logService = logService;
1336
1233
  }
1337
- activateEditModeCssVars(isActive) {
1338
- if (isActive) {
1339
- this._setCssVarValue('--eui-docpage-navigation-z-index', 'inherit'); // Reset doc-page-navigation-column z-index
1340
- this._setCssVarValue('--eui-z-index-sidebar', 'inherit'); // Reset eui-app-sidebar z-index
1341
- this._setCssVarValue('--eui-z-index-root', 'inherit'); // Reset eui-app-main-content z-index
1342
- }
1343
- else {
1344
- this._setCssVarValue('--eui-docpage-navigation-z-index', '2'); // Restore doc-page-navigation-column z-index
1345
- this._setCssVarValue('--eui-z-index-sidebar', '1044'); // Restore eui-app-sidebar z-index
1346
- this._setCssVarValue('--eui-z-index-root', '1'); // Restore eui-app-main-content z-index
1234
+ /**
1235
+ * Adds an item in the queue by dispatching an action to the store.
1236
+ * @param id - The id of the item inside the queue.
1237
+ * WARNING if matches any other queue item id will overwrite it.
1238
+ * @param item - an item for Queue
1239
+ */
1240
+ addQueueItem(id, item) {
1241
+ const allowedMethods = ['post', 'put', 'get'];
1242
+ if (allowedMethods.indexOf(item.method) < 0) {
1243
+ throw new Error(`[ApiQueue] method "${item.method}" is not allowed`);
1347
1244
  }
1245
+ this.store.dispatch(new AddApiQueueItemAction({ id, item }));
1348
1246
  }
1349
- // --------------
1350
- // public methods
1351
- // --------------
1352
- fetchAppMetadata(metadataFilePath = 'assets/app-metadata.json') {
1353
- this.getJson(metadataFilePath).then((data) => {
1354
- this.setState({
1355
- ...this.state,
1356
- appMetadata: data,
1357
- });
1358
- });
1247
+ /**
1248
+ * Subscribes to the store to retrieve queue and then converts the queue object to an array of items.
1249
+ * @return An Observable of array of all items inside the current queue (ApiQueueItem[])
1250
+ */
1251
+ getQueue() {
1252
+ return this.store.select(getApiQueue).pipe(take(1), map((queue) => {
1253
+ const queueArray = queue && Object.values(queue);
1254
+ if (!queueArray || queueArray.length < 1) {
1255
+ this.logService.warn('No items in the queue exist.');
1256
+ }
1257
+ return queueArray;
1258
+ }));
1359
1259
  }
1360
- activateSidebar() {
1361
- this.setState({
1362
- ...this.state,
1363
- hasSidebar: true,
1364
- });
1365
- if (!this.state.isSidebarHidden) {
1366
- this.activateSidebarCssVars();
1367
- }
1260
+ /**
1261
+ * Subscribes to the store to retrieve an item from the queue.
1262
+ * @param id - id of the item inside the queue
1263
+ * @return An Observable of ApiQueueItem type
1264
+ */
1265
+ getQueueItem(id) {
1266
+ return this.store.select(getApiQueueItem(id)).pipe(take(1), map((queue) => {
1267
+ if (!queue) {
1268
+ this.logService.warn(`Queue item with id "${id}" does not exist`);
1269
+ return null;
1270
+ }
1271
+ return queue;
1272
+ }));
1368
1273
  }
1369
- activateSidebarHeader() {
1370
- this.activateSidebarHeaderCssVars();
1274
+ /**
1275
+ * Removes a given item from the queue.
1276
+ * @param id - id of the item inside the queue
1277
+ */
1278
+ removeQueueItem(id) {
1279
+ this.store.dispatch(new RemoveApiQueueItemAction(id));
1371
1280
  }
1372
- activateSidebarFooter() {
1373
- this.activateSidebarFooterCssVars();
1281
+ /**
1282
+ * Removes all items placed in the queue.
1283
+ */
1284
+ removeAllQueueItem() {
1285
+ this.store.dispatch(new EmptyApiQueueAction(null));
1374
1286
  }
1375
- activateHeader() {
1376
- this.setState({
1377
- ...this.state,
1378
- hasHeader: true,
1379
- });
1380
- this.activateHeaderCssVars();
1287
+ /**
1288
+ * Process an item from the queue.
1289
+ * @param id - id of the item inside the queue
1290
+ * @return An Observable of type any
1291
+ */
1292
+ processQueueItem(id) {
1293
+ return this.store.select(getApiQueueItem(id)).pipe(take(1), switchMap((queue) => {
1294
+ if (!queue) {
1295
+ this.logService.warn(`Queue item with id "${id}" does not exist`);
1296
+ return of(null);
1297
+ }
1298
+ return this.buildHttpRequest(id, queue);
1299
+ }));
1381
1300
  }
1382
- activateBreadcrumb() {
1383
- this.setState({
1384
- ...this.state,
1385
- hasBreadcrumb: true,
1386
- });
1387
- this.activateBreadcrumbCssVars();
1301
+ /**
1302
+ * Process all the items inside the queue.
1303
+ * @param [continueOnError=true] - in case of an item request fails continue with the rest in queue
1304
+ * @return An Observable with an array which contains all responses from all items in queue.
1305
+ */
1306
+ processAllQueueItems(continueOnError = true) {
1307
+ return this.store.select(getApiQueue).pipe(map((queue) => Object.entries(queue).map(([key, value]) => this.buildHttpRequest(key, value).pipe(catchError((error) => {
1308
+ this.logService.error(`Queue Item with id ${key} failed.`, error.message, error.stack);
1309
+ if (!continueOnError) {
1310
+ throw error;
1311
+ }
1312
+ return of(error);
1313
+ })))), switchMap((obsQueue) => forkJoin(Array.from(obsQueue))));
1388
1314
  }
1389
- activateTopMessage(height) {
1390
- this.setState({
1391
- ...this.state,
1392
- hasTopMessage: true,
1393
- });
1394
- this.activateTopMessageCssVars(height);
1315
+ /**
1316
+ * Process all items in queue after it order them based on their timestamp. Beware that order
1317
+ * matter in execution of Http Request in queue. When a request completes only then next will begin. Emission
1318
+ * or Http response follows order.
1319
+ * @param [ascending=true] - sets the order of queue items based on timestamp
1320
+ */
1321
+ processAllQueueItemsSequential(ascending = true) {
1322
+ return this.store.select(getApiQueue).pipe(concatMap((queue) => Object.entries(queue)
1323
+ .sort((a, b) => (ascending ? this.sortOnTimestamp(a, b) : this.sortOnTimestamp(b, a)))
1324
+ .map(([id, item]) => this.buildHttpRequest(id, item))));
1395
1325
  }
1396
- activateToolbar() {
1397
- this.setState({
1398
- ...this.state,
1399
- hasToolbar: true,
1400
- });
1401
- this.activateToolbarCssVars();
1326
+ /**
1327
+ * Short dates ascending based on input
1328
+ * @param a - Date one
1329
+ * @param b - Date two
1330
+ *
1331
+ * @beta
1332
+ */
1333
+ sortOnTimestamp(a, b) {
1334
+ return +new Date(a[1].timestamp) - +new Date(b[1].timestamp);
1402
1335
  }
1403
- activateToolbarMenu() {
1404
- this.setState({
1405
- ...this.state,
1406
- hasToolbarMenu: true,
1407
- });
1336
+ /**
1337
+ * build and HttpRequest Observable based on the given queue.
1338
+ * After successful request completion removes the item from the queue.
1339
+ * @param id - id of the item inside the queue
1340
+ * @param item - the item of the queue that matches the id.
1341
+ * @return An Observable of HttpClient Request
1342
+ */
1343
+ buildHttpRequest(id, item) {
1344
+ return this.http[item.method.toLowerCase()](item.uri, item.payload).pipe(tap(() => this.removeQueueItem(id)));
1408
1345
  }
1409
- fetchCssVars() {
1410
- const cssVarNames = [
1411
- '--eui-app-header-height-default',
1412
- '--eui-app-header-height-active',
1413
- '--eui-app-header-height-shrink',
1414
- '--eui-app-breadcrumb-height-default',
1415
- '--eui-app-breadcrumb-height-active',
1416
- '--eui-app-top-message-height-default',
1417
- '--eui-app-top-message-height-active',
1418
- '--eui-app-top-message-height-shrink',
1419
- '--eui-app-toolbar-height-default',
1420
- '--eui-app-toolbar-height-active',
1421
- '--eui-app-sidebar-width-default',
1422
- '--eui-app-sidebar-width-active',
1423
- '--eui-app-sidebar-width-close-default',
1424
- '--eui-app-sidebar-width-close-active',
1425
- '--eui-app-sidebar-header-height-default',
1426
- '--eui-app-sidebar-header-height-active',
1427
- '--eui-app-sidebar-footer-height-default',
1428
- '--eui-app-sidebar-footer-height-active',
1429
- ];
1430
- this.cssVars = cssVarNames.map((v) => ({ key: v, value: this.getCssVarValue(v) }));
1346
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: ApiQueueService, deps: [{ token: StoreService }, { token: i1$5.HttpClient }, { token: LogService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1347
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: ApiQueueService, providedIn: 'root' }); }
1348
+ }
1349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: ApiQueueService, decorators: [{
1350
+ type: Injectable,
1351
+ args: [{
1352
+ providedIn: 'root',
1353
+ }]
1354
+ }], ctorParameters: function () { return [{ type: StoreService }, { type: i1$5.HttpClient }, { type: LogService }]; } });
1355
+
1356
+ class ApiQueueServiceMock extends ApiQueueService {
1357
+ constructor() {
1358
+ super(null, null, null);
1431
1359
  }
1432
- initCssVars() {
1433
- this.setCssVar('--eui-app-header-height-default', '--eui-app-header-height');
1434
- this.setCssVar('--eui-app-breadcrumb-height-default', '--eui-app-breadcrumb-height');
1435
- this.setCssVar('--eui-app-top-message-height-default', '--eui-app-top-message-height');
1436
- this.setCssVar('--eui-app-toolbar-height-default', '--eui-app-toolbar-height');
1437
- this.setCssVar('--eui-app-sidebar-width-default', '--eui-app-sidebar-width');
1438
- this.setCssVar('--eui-app-sidebar-width-close-default', '--eui-app-sidebar-width-close');
1439
- this.setCssVar('--eui-app-sidebar-header-height-default', '--eui-app-sidebar-header-height');
1440
- this.setCssVar('--eui-app-sidebar-footer-height-default', '--eui-app-sidebar-footer-height');
1360
+ getQueue() {
1361
+ return of([]);
1441
1362
  }
1442
- activateHeaderCssVars() {
1443
- this.setCssVar('--eui-app-header-height-active', '--eui-app-header-height');
1363
+ getQueueItem(id) {
1364
+ return of({ uri: id, method: 'GET' });
1444
1365
  }
1445
- activateBreadcrumbCssVars() {
1446
- this.setCssVar('--eui-app-breadcrumb-height-active', '--eui-app-breadcrumb-height');
1366
+ removeAllQueueItem() { }
1367
+ processAllQueueItems() {
1368
+ return of({});
1447
1369
  }
1448
- activateTopMessageCssVars(height) {
1449
- this._setCssVarValue('--eui-app-top-message-height', `${height}px`);
1370
+ addQueueItem() { }
1371
+ removeQueueItem() { }
1372
+ processQueueItem() {
1373
+ return of({});
1450
1374
  }
1451
- activateToolbarCssVars() {
1452
- this.setCssVar('--eui-app-toolbar-height-active', '--eui-app-toolbar-height');
1375
+ }
1376
+
1377
+ class I18nResourceImpl {
1378
+ /**
1379
+ * Resource constructor
1380
+ *
1381
+ * @param prefix the prefix of the resource
1382
+ * @param suffix the suffix of the resource, optional parameter
1383
+ * @param compileTranslations compiler function, optional parameter
1384
+ */
1385
+ constructor(prefix, suffix = '', compileTranslationsFn = (translations) => translations) {
1386
+ this.prefix = prefix;
1387
+ this.suffix = suffix;
1388
+ this.compileTranslationsFn = compileTranslationsFn;
1453
1389
  }
1454
- activateFooterCssVars() {
1455
- this.setCssVar('--eui-app-footer-height-active', '--eui-app-footer-height');
1390
+ /**
1391
+ * Translations pre-processor
1392
+ *
1393
+ * @param translations the object to be pre-processed
1394
+ * @param lang the translations language
1395
+ * @returns the pre-processed translations
1396
+ */
1397
+ compileTranslations(translations, lang) {
1398
+ return this.compileTranslationsFn(translations, lang);
1456
1399
  }
1457
- setHeaderShrinkCssVar(active) {
1458
- if (active) {
1459
- this.setCssVar('--eui-app-header-height-shrink', '--eui-app-header-height');
1460
- }
1461
- else {
1462
- this.setCssVar('--eui-app-header-height-active', '--eui-app-header-height');
1463
- }
1400
+ /**
1401
+ * Indicates whether other object is equal-to this one.
1402
+ *
1403
+ * @param obj the reference object with which to compare
1404
+ * @returns true if this object is the same as obj, false otherwise
1405
+ */
1406
+ equals(obj) {
1407
+ return this.prefix === obj.prefix && this.suffix === obj.suffix;
1464
1408
  }
1465
- activateSidebarCssVars() {
1466
- this.setCssVar('--eui-app-sidebar-width-active', '--eui-app-sidebar-width');
1467
- this.setCssVar('--eui-app-sidebar-width-close-active', '--eui-app-sidebar-width-close');
1409
+ /**
1410
+ * The path to the resource
1411
+ *
1412
+ * @param lang the resource language
1413
+ * @returns the path
1414
+ */
1415
+ getPath(lang) {
1416
+ return `${this.prefix}${lang}${this.suffix}`;
1468
1417
  }
1469
- removeSidebarCssVars() {
1470
- document.documentElement.style.removeProperty('--eui-app-sidebar-header-height');
1471
- document.documentElement.style.removeProperty('--eui-app-sidebar-footer-height');
1472
- document.documentElement.style.removeProperty('--eui-app-sidebar-width');
1473
- document.documentElement.style.removeProperty('--eui-app-sidebar-width-close');
1418
+ }
1419
+
1420
+ class I18nLoader {
1421
+ constructor(http, logService, euiAppConfig) {
1422
+ this.http = http;
1423
+ this.logService = logService;
1424
+ this.euiAppConfig = euiAppConfig;
1425
+ this.resources = [];
1426
+ this.failedResources = [];
1427
+ // create the resources from the config object
1428
+ const i18nConfig = this.euiAppConfig.global && this.euiAppConfig.global.i18n;
1429
+ const i18nLoaderConfig = i18nConfig && i18nConfig.i18nLoader;
1430
+ this.resources.push(...this.createResources(i18nLoaderConfig));
1474
1431
  }
1475
- activateSidebarHeaderCssVars() {
1476
- this.setCssVar('--eui-app-sidebar-header-height-active', '--eui-app-sidebar-header-height');
1432
+ /**
1433
+ * Gets the translations from the server
1434
+ *
1435
+ * @param lang
1436
+ * @returns Observable<any>
1437
+ */
1438
+ getTranslation(lang) {
1439
+ return this.loadResources(this.resources, lang).pipe(map((loadedResources) => {
1440
+ this.failedResources = loadedResources.hasError ? loadedResources.errors : [];
1441
+ return loadedResources.translations;
1442
+ }));
1477
1443
  }
1478
- activateSidebarFooterCssVars() {
1479
- this.setCssVar('--eui-app-sidebar-footer-height-active', '--eui-app-sidebar-footer-height');
1444
+ /**
1445
+ * Adds resources
1446
+ *
1447
+ * @param config loader configuration
1448
+ * @returns Observable<any> | false
1449
+ */
1450
+ addResources(config) {
1451
+ // create the resources
1452
+ let resources = this.createResources(config);
1453
+ // filter the resources first, to avoid duplicates
1454
+ resources = resources.filter((resource) => !this.resources.some((res) => res.equals(resource)));
1455
+ // add the new resources to the list of existing resources
1456
+ this.resources.push(...resources);
1457
+ // return the filtered resources
1458
+ return resources;
1480
1459
  }
1481
1460
  /**
1482
- * Gets the viewport height and width and multiple it by 1% to get a value for a vh/vw unit
1483
- * Sets the value in the --eui-app-vh custom property to the root of the document
1484
- * Sets the value in the --eui-app-vw custom property to the root of the document
1485
- * Example for 100% height: calc(var(--eui-app-vh, 1vh) * 100);
1486
- * Example for 50% height: calc(var(--eui-app-vh, 1vh) * 50);
1487
- * Example for 50% width: calc(var(--eui-app-vw, 1vw) * 50);
1461
+ * Removes resources from the loader instance
1488
1462
  */
1489
- setAppViewportCssVars() {
1490
- let vh = window.innerHeight * 0.01;
1491
- let vw = window.innerWidth * 0.01;
1492
- this._setCssVarValue('--eui-app-vh', `${vh}px`);
1493
- this._setCssVarValue('--eui-app-vw', `${vw}px`);
1463
+ removeResources(removedResources) {
1464
+ this.resources = this.resources.filter((res) => !removedResources.some((removedResource) => removedResource.equals(res)));
1465
+ }
1466
+ /**
1467
+ * Loads an array of resources, by language
1468
+ *
1469
+ * @param resources the definition of the resources
1470
+ * @param lang the resource language to load
1471
+ * @returns Observable<any>
1472
+ */
1473
+ loadResources(resources, lang) {
1474
+ // if there are resources to load
1475
+ if (Array.isArray(resources) && resources.length > 0) {
1476
+ // load all the resources in an array of Observable
1477
+ const requests = resources.map((resource) => this.loadResource(resource, lang));
1478
+ // group all the observables with forkJoin and deep merge them
1479
+ return forkJoin(requests).pipe(map((response) => {
1480
+ const successResp = [];
1481
+ const errResp = [];
1482
+ response.forEach((item) => {
1483
+ if (item.isError) {
1484
+ errResp.push({ resource: item.resource, error: item.error });
1485
+ }
1486
+ else {
1487
+ successResp.push(item);
1488
+ }
1489
+ });
1490
+ if (successResp.length === response.length) {
1491
+ return { hasError: false, translations: mergeAll(successResp) };
1492
+ }
1493
+ else {
1494
+ return { hasError: true, translations: mergeAll(successResp), errors: errResp };
1495
+ }
1496
+ }));
1497
+ }
1498
+ else {
1499
+ // no resources to load
1500
+ return of({ hasError: false, translations: {} });
1501
+ }
1494
1502
  }
1495
1503
  /**
1496
- * Returns the current value of --eui-base-font-size CSS variable
1504
+ * Returns resources that failed
1497
1505
  */
1498
- getBaseFontSize() {
1499
- return this.state.appBaseFontSize || this.getCssVarValue('--eui-base-font-size');
1506
+ getFailedResources() {
1507
+ return this.failedResources;
1500
1508
  }
1501
1509
  /**
1502
- * Updates the current value of --eui-base-font-size CSS variable and the UIState appBaseFontSize
1510
+ * Create resources from a config file
1511
+ *
1512
+ * @param config loader configuration
1513
+ * @returns I18nResourceImpl[] resources
1503
1514
  */
1504
- setBaseFontSize(newsize) {
1505
- this.setState({
1506
- ...this.state,
1507
- appBaseFontSize: newsize,
1508
- }, false);
1509
- this._setCssVarValue('--eui-base-font-size', newsize);
1510
- }
1511
- // ---------------
1512
- // private getters
1513
- // ---------------
1514
- getCssVarValue(inputProperty) {
1515
- return getComputedStyle(document.documentElement).getPropertyValue(inputProperty);
1516
- }
1517
- setCssVar(inputProperty, outputProperty) {
1518
- const cssVarValue = this.cssVars.filter((v) => v.key === inputProperty)[0].value;
1519
- this._setCssVarValue(outputProperty, cssVarValue);
1520
- // console.log(inputProperty, outputProperty, cssVarValue);
1521
- }
1522
- _setCssVarValue(varName, varValue) {
1523
- document.documentElement.style.setProperty(varName, varValue);
1524
- }
1525
- getWrapperClasses(state, breakpoint) {
1526
- let classes = [];
1527
- classes.push(breakpoint);
1528
- if (state.hasSidebar) {
1529
- if (state.isSidebarOpen && (state.isSidebarActive || state.isSidebarInnerActive)) {
1530
- classes.push('sidebar-state-open');
1531
- }
1532
- if (!state.isSidebarOpen && (state.isSidebarActive || state.isSidebarInnerActive)) {
1533
- classes.push('sidebar-state-close');
1534
- }
1535
- if (state.isSidebarActive) {
1536
- classes.push('sidebar-active');
1537
- }
1538
- if (state.isSidebarInnerActive) {
1539
- classes.push('sidebar-inner-active');
1540
- }
1541
- if (state.isSidebarStateCloseWithIcons) {
1542
- classes.push('sidebar-state-close-with-icons');
1543
- }
1544
- // NEW APP-SHELL props
1545
- if (state.isSidebarHidden) {
1546
- classes.push('sidebar--hidden');
1547
- }
1548
- if (state.isSidebarOpen) {
1549
- classes.push('sidebar--open');
1550
- }
1551
- else {
1552
- classes.push('sidebar--close');
1553
- }
1554
- if (state.isSidebarHover) {
1555
- classes.push('sidebar--hover');
1556
- }
1557
- }
1558
- if (state.deviceInfo && state.deviceInfo.isFF) {
1559
- classes.push('ff');
1560
- }
1561
- if (state.deviceInfo && state.deviceInfo.isIE) {
1562
- classes.push('ie');
1563
- }
1564
- if (state.deviceInfo && state.deviceInfo.isIE11) {
1565
- classes.push('ie11');
1515
+ createResources(config) {
1516
+ // use the default config as a reference base
1517
+ config = getI18nLoaderConfig(config);
1518
+ const resources = [];
1519
+ // extract the i18n folders from config
1520
+ const i18nFolders = Array.isArray(config.i18nFolders)
1521
+ ? config.i18nFolders
1522
+ : config.i18nFolders
1523
+ ? [config.i18nFolders]
1524
+ : [];
1525
+ if (i18nFolders) {
1526
+ // add the i18n folders to resources
1527
+ resources.push(...i18nFolders.map((folder) => new I18nResourceImpl(`assets/${folder}/`, '.json')));
1566
1528
  }
1567
- if (state.deviceInfo && state.deviceInfo.isChrome) {
1568
- classes.push('chrome');
1529
+ // extract the i18n services from config
1530
+ const i18nServices = Array.isArray(config.i18nServices)
1531
+ ? config.i18nServices
1532
+ : config.i18nServices
1533
+ ? [config.i18nServices]
1534
+ : [];
1535
+ if (i18nServices) {
1536
+ // add the i18n services to resources
1537
+ resources.push(...i18nServices.map((service) => new I18nResourceImpl(service)));
1569
1538
  }
1570
- return classes.join(' ');
1571
- }
1572
- getBreakpoint(windowWidth) {
1573
- let bkp = '';
1574
- switch (true) {
1575
- case windowWidth >= 0 && windowWidth < 576:
1576
- bkp = 'xs';
1577
- break;
1578
- case windowWidth >= 576 && windowWidth < 768:
1579
- bkp = 'sm';
1580
- break;
1581
- case windowWidth >= 768 && windowWidth < 992:
1582
- bkp = 'md';
1583
- break;
1584
- case windowWidth >= 992 && windowWidth < 1200:
1585
- bkp = 'lg';
1586
- break;
1587
- case windowWidth >= 1200 && windowWidth < 1400:
1588
- bkp = 'xl';
1589
- break;
1590
- case windowWidth >= 1400 && windowWidth < 1920:
1591
- bkp = 'xxl';
1592
- break;
1593
- case windowWidth >= 1920 && windowWidth < 2560:
1594
- bkp = 'fhd';
1595
- break;
1596
- case windowWidth >= 2560 && windowWidth < 3840:
1597
- bkp = '2k';
1598
- break;
1599
- case windowWidth >= 3840:
1600
- bkp = '4k';
1601
- break;
1539
+ // extract the i18n resources from config
1540
+ const i18nResources = Array.isArray(config.i18nResources)
1541
+ ? config.i18nResources
1542
+ : config.i18nResources
1543
+ ? [config.i18nResources]
1544
+ : [];
1545
+ if (i18nResources) {
1546
+ // add the i18n resources to resources
1547
+ resources.push(...i18nResources.map((resource) => new I18nResourceImpl(resource.prefix, resource.suffix, this.getResourceCompileFunction(resource.compileTranslations))));
1602
1548
  }
1603
- return bkp;
1604
- }
1605
- getBreakpoints(bkp) {
1606
- return {
1607
- isMobile: bkp === 'xs' || bkp === 'sm',
1608
- isTablet: bkp === 'md',
1609
- isLtDesktop: bkp === 'xs' || bkp === 'sm' || bkp === 'md',
1610
- isDesktop: bkp === 'lg',
1611
- isXL: bkp === 'xl',
1612
- isXXL: bkp === 'xxl',
1613
- isFHD: bkp === 'fhd',
1614
- is2K: bkp === '2k',
1615
- is4K: bkp === '4k',
1616
- };
1617
- }
1618
- getJson(url) {
1619
- return this.http.get(url).toPromise().then(this.extractData).catch(this.handleError);
1620
- }
1621
- extractData(res) {
1622
- const body = res;
1623
- return body || {};
1624
- }
1625
- handleError(error) {
1626
- console.error('An error occurred', error);
1627
- return Promise.reject(error.message || error);
1549
+ return resources;
1628
1550
  }
1629
- bindActiveLanguageToAppShellState() {
1630
- this.storeService.select(getActiveLang).subscribe((activeLang) => {
1631
- if (activeLang !== this.state.activeLanguage) {
1632
- this.setState({
1633
- ...this.state,
1634
- activeLanguage: activeLang,
1635
- }, false);
1551
+ /**
1552
+ * Loads a resource, by language
1553
+ *
1554
+ * @param resource the definition of the resource
1555
+ * @param lang the resource language to load
1556
+ * @returns Observable<any>
1557
+ */
1558
+ loadResource(resource, lang) {
1559
+ // the path to the resource
1560
+ const path = resource.getPath(lang);
1561
+ // load the translations from the path
1562
+ return this.http.get(path).pipe(
1563
+ // preprocess the translations using the resource compiler
1564
+ map((translations) => resource.compileTranslations(translations, lang)), map((translations) => {
1565
+ // resource loaded properly, send a info message
1566
+ if (this.logService) {
1567
+ this.logService.info(`I18n resource loaded from path ${path}`);
1636
1568
  }
1637
- });
1638
- }
1639
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxAppShellService, deps: [{ token: GLOBAL_CONFIG_TOKEN, optional: true }, { token: i1$2.HttpClient }, { token: PLATFORM_ID }, { token: i2$1.Router }, { token: StoreService }, { token: I18nService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1640
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxAppShellService, providedIn: 'root' }); }
1641
- }
1642
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxAppShellService, decorators: [{
1643
- type: Injectable,
1644
- args: [{
1645
- providedIn: 'root',
1646
- }]
1647
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
1648
- type: Optional
1649
- }, {
1650
- type: Inject,
1651
- args: [GLOBAL_CONFIG_TOKEN]
1652
- }] }, { type: i1$2.HttpClient }, { type: undefined, decorators: [{
1653
- type: Inject,
1654
- args: [PLATFORM_ID]
1655
- }] }, { type: i2$1.Router }, { type: StoreService }, { type: I18nService, decorators: [{
1656
- type: Optional
1657
- }] }]; } });
1658
-
1659
- class CoreAppEffects {
1660
- constructor(actions,
1661
- // TODO: remove on eUI 14
1662
- showConnectionStatus, translateService, asService) {
1663
- this.actions = actions;
1664
- this.showConnectionStatus = showConnectionStatus;
1665
- this.translateService = translateService;
1666
- this.asService = asService;
1667
- this.getAppConnectionInformation = createEffect(() => merge(fromEvent(window, 'online').pipe(mapTo(true)), fromEvent(window, 'offline').pipe(mapTo(false))).pipe(map((connected) => new UpdateAppConnectionAction(connected))));
1668
- this.activatedRoute = createEffect(() => this.actions.pipe(ofType(CoreAppActionTypes.ACTIVATED_ROUTE), map((action) => action.payload), mergeMap((route) => {
1669
- // extract the module name, if defined
1670
- const module = route.snapshot.data['module'] || 'undefined';
1671
- return [new UpdateCurrentModuleAction(module)];
1672
- })));
1673
- this.updateAppConnection = createEffect(() => this.actions.pipe(ofType(CoreAppActionTypes.UPDATE_APP_CONNECTION), map((action) => action.payload), tap((connected) => {
1674
- // TODO: for eUI 14, drop boolean support and refactor
1675
- // check if feature is enabled
1676
- let isFeatureEnabled = typeof this.showConnectionStatus !== 'boolean' ? this.showConnectionStatus?.enabled : this.showConnectionStatus;
1677
- // extract lifespan if any
1678
- let lifespan = typeof this.showConnectionStatus !== 'boolean' ? this.showConnectionStatus?.messageBox?.lifespan || 0 : 0;
1679
- // if the user needs to be informed about the connection status
1680
- if (isFeatureEnabled) {
1681
- if (connected) {
1682
- this.translateService.get('eui.CONNECTED').subscribe((translation) => {
1683
- // TODO v17 - should be subscribed from the eui-app to display growl
1684
- // this.asService.growl(
1685
- // { severity: 'success', summary: 'MESSAGE', detail: translation, life: lifespan },
1686
- // lifespan < 1,
1687
- // false
1688
- // );
1689
- });
1690
- }
1691
- else {
1692
- this.translateService.get('eui.NOTCONNECTED').subscribe((translation) => {
1693
- // TODO v17 - should be subscribed from the eui-app to display growl
1694
- // this.asService.growl(
1695
- // { severity: 'danger', summary: 'ERROR', detail: translation, life: lifespan },
1696
- // lifespan < 1,
1697
- // false
1698
- // );
1699
- });
1700
- }
1569
+ // return the translations
1570
+ return translations;
1571
+ }), catchError((err) => {
1572
+ // remove failed resource from loaded resource array
1573
+ this.removeResources([resource]);
1574
+ // resource not loaded properly, send a warning message
1575
+ if (this.logService) {
1576
+ this.logService.warn(`I18n resource NOT loaded from path ${path}`, err);
1701
1577
  }
1702
- })), { dispatch: false });
1578
+ const resourceError = { error: `I18n resource NOT loaded from path ${path}`, resource, isError: true };
1579
+ return of(resourceError);
1580
+ }));
1703
1581
  }
1704
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreAppEffects, deps: [{ token: i1$3.Actions }, { token: SHOW_CONNECTION_STATUS_TOKEN }, { token: i2.TranslateService }, { token: UxAppShellService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1705
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreAppEffects }); }
1582
+ getResourceCompileFunction(id) {
1583
+ const customHandlers = this.euiAppConfig.customHandler;
1584
+ if (customHandlers && typeof customHandlers[id] === 'function') {
1585
+ return customHandlers[id];
1586
+ }
1587
+ return undefined;
1588
+ }
1589
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nLoader, deps: [{ token: i1$5.HttpClient }, { token: LogService, optional: true }, { token: CONFIG_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
1590
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nLoader }); }
1706
1591
  }
1707
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreAppEffects, decorators: [{
1592
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nLoader, decorators: [{
1708
1593
  type: Injectable
1709
- }], ctorParameters: function () { return [{ type: i1$3.Actions }, { type: undefined, decorators: [{
1594
+ }], ctorParameters: function () { return [{ type: i1$5.HttpClient }, { type: LogService, decorators: [{
1595
+ type: Optional
1596
+ }] }, { type: undefined, decorators: [{
1710
1597
  type: Inject,
1711
- args: [SHOW_CONNECTION_STATUS_TOKEN]
1712
- }] }, { type: i2.TranslateService }, { type: UxAppShellService }]; } });
1713
-
1714
- const CoreModuleEffects = [CoreAppEffects];
1715
-
1716
- const updateAppVersion = (state, action) => ({ ...state, version: action.payload });
1717
- const updateAppConnection = (state, action) => ({ ...state, connected: action.payload });
1718
- const AddAppLoadedConfigModules = (state, action) => ({
1719
- ...state,
1720
- loadedConfigModules: {
1721
- lastAddedModule: action.payload.moduleName,
1722
- modulesConfig: {
1723
- ...state.loadedConfigModules.modulesConfig,
1724
- [action.payload.moduleName]: {
1725
- ...action.payload.moduleConfig,
1726
- },
1727
- },
1598
+ args: [CONFIG_TOKEN]
1599
+ }] }]; } });
1600
+ const translateConfig = {
1601
+ loader: {
1602
+ provide: TranslateLoader,
1603
+ useClass: I18nLoader,
1728
1604
  },
1729
- });
1730
- const updateAppStatus = (state, action) => ({ ...state, status: action.payload });
1731
- const updateCurrentModule = (state, action) => Object.assign({}, state, { currentModule: action.payload });
1732
- const addApiQueueItem = (state, action) => {
1733
- // apply the timestamp to the queue item
1734
- const apiQueueItem = Object.assign({}, action.payload.item, { timestamp: new Date().getTime() });
1735
- // update the queue
1736
- const apiQueue = Object.assign({}, state.apiQueue, { [action.payload.id]: apiQueueItem });
1737
- return Object.assign({}, state, { apiQueue });
1738
- };
1739
- const removeApiQueueItem = (state, action) => {
1740
- // remove the specified id from the list of item ids and recreate the apiQueue from the remaining ids
1741
- const apiQueue = Object.keys(state.apiQueue)
1742
- .filter((id) => id !== action.payload)
1743
- .reduce((queue, key) => {
1744
- queue[key] = state.apiQueue[key];
1745
- return queue;
1746
- }, {});
1747
- return Object.assign({}, state, { apiQueue });
1748
- };
1749
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1750
- const emptyApiQueue = (state, action) =>
1751
- // remove all items in the Api Queue
1752
- Object.assign({}, state, { apiQueue: {} });
1753
- const actionToReducerMap$4 = {
1754
- [CoreAppActionTypes.UPDATE_APP_VERSION]: updateAppVersion,
1755
- [CoreAppActionTypes.UPDATE_APP_CONNECTION]: updateAppConnection,
1756
- [CoreAppActionTypes.ADD_APP_LOADED_CONFIG_MODULES]: AddAppLoadedConfigModules,
1757
- [CoreAppActionTypes.UPDATE_APP_STATUS]: updateAppStatus,
1758
- [CoreAppActionTypes.UPDATE_CURRENT_MODULE]: updateCurrentModule,
1759
- [CoreAppActionTypes.ADD_API_QUEUE_ITEM]: addApiQueueItem,
1760
- [CoreAppActionTypes.REMOVE_API_QUEUE_ITEM]: removeApiQueueItem,
1761
- [CoreAppActionTypes.EMPTY_API_QUEUE]: emptyApiQueue,
1762
- };
1763
- const coreAppReducers = (state = initialAppState, action) => {
1764
- if (actionToReducerMap$4[action.type]) {
1765
- return actionToReducerMap$4[action.type](state, action);
1766
- }
1767
- // if the action type is not in the map, call the base reducers method
1768
- return state;
1769
1605
  };
1770
1606
 
1771
- const updateUser = (state, action) => {
1772
- let fullName = action.payload.fullName;
1773
- if (!fullName) {
1774
- fullName = `${action.payload.firstName || ''} ${action.payload.lastName || ''}`.trim();
1775
- }
1776
- return Object.assign({}, state, action.payload, { fullName });
1777
- };
1778
- const updateUserDetails = (state, action) => {
1779
- let fullName = action.payload.fullName;
1780
- if (!fullName) {
1781
- fullName = `${action.payload.firstName || ''} ${action.payload.lastName || ''}`.trim();
1782
- }
1783
- // todo should be checked if state.details is really there
1784
- const details = Object.assign({}, state.details, action.payload, { fullName });
1785
- return Object.assign({}, state, details);
1786
- };
1787
- const updateUserPreferences = (state, action) => {
1788
- const preferences = Object.assign({}, state.preferences, action.payload);
1789
- return Object.assign({}, state, { preferences });
1790
- };
1791
- const updateUserRights = (state, action) => Object.assign({}, state, { rights: [...action.payload] });
1792
- const updateUserDashboard = (state, action) => {
1793
- // apply the dashboard to preferences
1794
- const preferences = Object.assign({}, state.preferences, { dashboard: action.payload });
1795
- // update the state
1796
- return { ...state, preferences };
1797
- };
1798
- const actionToReducerMap$3 = {
1799
- [CoreUserActionTypes.UPDATE_USER_DETAILS]: updateUserDetails,
1800
- [CoreUserActionTypes.UPDATE_USER_PREFERENCES]: updateUserPreferences,
1801
- [CoreUserActionTypes.UPDATE_USER_STATE]: updateUser,
1802
- [CoreUserActionTypes.UPDATE_USER_RIGHTS]: updateUserRights,
1803
- [CoreUserActionTypes.UPDATE_USER_DASHBOARD]: updateUserDashboard,
1804
- };
1805
- const coreUserReducers = (state = initialUserState, action) => {
1806
- if (actionToReducerMap$3[action.type]) {
1807
- return actionToReducerMap$3[action.type](state, action);
1607
+ class I18nService extends EuiLazyService {
1608
+ constructor(baseGlobalConfig, translateService, logService, store, document) {
1609
+ super({ activeLang: 'en' });
1610
+ this.baseGlobalConfig = baseGlobalConfig;
1611
+ this.translateService = translateService;
1612
+ this.logService = logService;
1613
+ this.store = store;
1614
+ this.document = document;
1615
+ this.config = getI18nServiceConfigFromBase(this.baseGlobalConfig);
1616
+ this.onModuleLoad = new BehaviorSubject({ ready: false, name: null });
1808
1617
  }
1809
- return state;
1810
- };
1811
-
1812
- const updateNotificationsList = (state, action) => Object.assign({}, state, { list: [...action.payload] });
1813
- const actionToReducerMap$2 = {
1814
- [CoreNotificationsActionTypes.UPDATE_NOTIFICATIONS_LIST]: updateNotificationsList,
1815
- };
1816
- const coreNotificationsReducers = (state = initialNotificationsState, action) => {
1817
- if (actionToReducerMap$2[action.type]) {
1818
- return actionToReducerMap$2[action.type](state, action);
1618
+ getState(keyOrMapFn) {
1619
+ let selector = getI18nState;
1620
+ let dotNotationToObject = (obj, key) => key.split('.').reduce((slice, i) => slice[i], obj);
1621
+ if (keyOrMapFn) {
1622
+ switch (typeof keyOrMapFn) {
1623
+ case 'function':
1624
+ selector = createSelector(getI18nState, keyOrMapFn);
1625
+ break;
1626
+ case 'string':
1627
+ selector = createSelector(getI18nState, (state) => dotNotationToObject(state, keyOrMapFn));
1628
+ break;
1629
+ }
1630
+ }
1631
+ return this.store.select(selector);
1819
1632
  }
1820
- return state;
1821
- };
1822
-
1823
- const updateI18nState = (state, action) => ({
1824
- ...state,
1825
- ...action.payload,
1826
- });
1827
- const actionToReducerMap$1 = {
1828
- [CoreI18nActionTypes.UPDATE_I18N_STATE]: updateI18nState,
1829
- };
1830
- const corI18nReducers = (state = initialI18nState, action) => {
1831
- if (actionToReducerMap$1[action.type]) {
1832
- return actionToReducerMap$1[action.type](state, action);
1633
+ updateState(langState) {
1634
+ this.updateHTMLDOMLang(langState.activeLang);
1635
+ this.store.dispatch(new UpdateI18nStateAction(langState));
1833
1636
  }
1834
- return state;
1835
- };
1836
-
1837
- const updateLocaleState = (state, action) => ({
1838
- ...state,
1839
- ...action.payload,
1840
- });
1841
- const actionToReducerMap = {
1842
- [CoreLocaleActionTypes.UPDATE_LOCALE_STATE]: updateLocaleState,
1843
- };
1844
- const coreLocaleReducers = (state = initialLocaleState, action) => {
1845
- if (actionToReducerMap[action.type]) {
1846
- return actionToReducerMap[action.type](state, action);
1637
+ init(langState) {
1638
+ const initLang = langState && langState.activeLang ? langState.activeLang : this.preparedDefaultLanguage();
1639
+ const initState = {
1640
+ ...langState,
1641
+ activeLang: initLang,
1642
+ };
1643
+ if (typeof initLang === 'string') {
1644
+ super.initEuiService(this.store);
1645
+ this.updateState(initState);
1646
+ return this.setup(initLang);
1647
+ }
1648
+ return of({ success: false, error: 'Initial active lang should be string' });
1847
1649
  }
1848
- return state;
1849
- };
1850
-
1851
- const coreReducers = Object.assign({}, {
1852
- app: coreAppReducers,
1853
- user: coreUserReducers,
1854
- notifications: coreNotificationsReducers,
1855
- i18n: corI18nReducers,
1856
- locale: coreLocaleReducers,
1857
- });
1858
-
1859
- const extend = extendProxy.default || extendProxy;
1860
- /**
1861
- * Utility meta-reducer to load the state from the local storage
1862
- * @param reducer the action reducer
1863
- */
1864
- function localStorageSync(reducer) {
1865
- return cb(reducer, localStorage);
1866
- }
1867
- /**
1868
- * Utility meta-reducer to load the state from the session storage
1869
- * @param reducer the action reducer
1870
- */
1871
- function sessionStorageSync(reducer) {
1872
- return cb(reducer, sessionStorage);
1873
- }
1874
- // Keys that are allowed to be synced with NgRx state from BrowserStorage
1875
- const disallowedKeys = ['loadedConfigModules', 'connect', 'currentModule', 'status', 'apiQueue'];
1876
- /**
1877
- * filters the AppState by removing those keys from the state and return a new state Object
1878
- * @param app the AppState
1879
- */
1880
- const filterAppState = (app) => {
1881
- // in case app is undefined or null it should return empty object
1882
- if (!app) {
1883
- return {};
1650
+ /**
1651
+ * A pipe function that could serve as a wrapper of another observable
1652
+ * e.g. ngx-translate i18n.onReady('my_module').pipe(switchMap(()=>translate.get('eui.KEY')));
1653
+ * WARNING: onReady will emit only once for a loaded Module, going to another and then going back will not result
1654
+ * into another emission.
1655
+ *
1656
+ * @param moduleName the name of the module that has been given through the eUI globalConfig. In case non provided
1657
+ * fetch module name from the state.
1658
+ */
1659
+ onReady(moduleName) {
1660
+ return this.onModuleLoad.pipe(switchMap((evt) => moduleName
1661
+ ? of(evt)
1662
+ : this.store.select(getLastAddedModule).pipe(tap((m) => (moduleName = m)), map(() => evt))),
1663
+ // emit only if event emitted matches the module name and is ready
1664
+ filter((evt) => evt.ready === true && evt.name === moduleName),
1665
+ // make sure that observable completes
1666
+ take(1));
1884
1667
  }
1885
- return (Object.keys(app)
1886
- // select keys that are present in the allowed list
1887
- .filter((key) => !disallowedKeys.includes(key))
1888
- // build a new object with only the allowed properties
1889
- .reduce((obj, key) => {
1890
- obj[key] = app[key];
1891
- return obj;
1892
- }, {}));
1893
- };
1894
- /**
1895
- *
1896
- * @param storageType
1897
- */
1898
- const loadState = (storage = localStorage) => {
1899
- try {
1900
- // by default falls back to localStorage
1901
- const serializedState = storage.getItem('state');
1902
- if (serializedState === null) {
1903
- return undefined;
1904
- }
1905
- // deserialize state
1906
- let state = JSON.parse(serializedState);
1907
- // filter AppState and replace
1908
- state = { ...state, app: filterAppState(state?.app) };
1909
- return state;
1668
+ lazyLoad(config) {
1669
+ const moduleId = Math.floor(Math.random() * 100000 + 1).toLocaleString();
1670
+ return this.addResources(config, moduleId);
1910
1671
  }
1911
- catch (err) {
1912
- return undefined;
1672
+ lazyLoadInit(moduleConfig, moduleName) {
1673
+ const i18nLoaderConfig = moduleConfig;
1674
+ // add resources
1675
+ return this.addResources(i18nLoaderConfig, moduleName);
1913
1676
  }
1914
- };
1915
- const cb = (reducer, storage) => (state, action) => {
1916
- if (action.type === CoreAppActionTypes.INIT_STORE) {
1917
- // This action must be dispatched from an app initializer
1918
- // We load the state from a storage and compare the version and userId with the provided ones.
1919
- // If they are the same, we merge the local state into the initial state.
1920
- // If there is a mismatch, we don't merge the local state, but update only the initial state.
1921
- // load the state from the localStorage
1922
- const localState = loadState(storage);
1923
- // extract the details about user and app version from the action payload
1924
- const payload = action.payload;
1925
- const payloadVersion = payload?.version;
1926
- if (localState) {
1927
- // TODO: Why all these checks here? What is the purpose of this code?
1928
- // extract the details about user and app version from the storage state
1929
- const localVersion = localState?.app?.version;
1930
- // const localUserId = localState.user && localState.user.userId;
1931
- const isVersionOK = !localVersion || !payloadVersion || localVersion === payloadVersion;
1932
- // const isUserIdOK = (localUserId === payloadUserId);
1933
- // update the entire state with deep merge of states, if the version and user are the same
1934
- if (isVersionOK) {
1935
- state = extend(true, {}, state, localState);
1677
+ /**
1678
+ * Add resources based of a config loader
1679
+ *
1680
+ * @param config I18nLoaderConfig
1681
+ * @return Promise<EuiServiceStatus> an EuiServiceStatus when translation has fully loaded
1682
+ */
1683
+ addResources(config, moduleName) {
1684
+ // emit that module is loading
1685
+ this.onModuleLoad.next({ ready: false, name: moduleName });
1686
+ const loader = this.translateService.currentLoader;
1687
+ if (loader instanceof I18nLoader) {
1688
+ const langSubscription = [];
1689
+ const i18nLoader = loader;
1690
+ // add the resources to the loader
1691
+ const resources = i18nLoader.addResources(config);
1692
+ // manually load the resources for the CURRENT language and add them to the translate service
1693
+ langSubscription.push(i18nLoader.loadResources(resources, this.translateService.currentLang).pipe(take(1), tap((loadedResources) => {
1694
+ // add the new set of translations to the current language
1695
+ this.translateService.setTranslation(this.translateService.currentLang, loadedResources.translations, true);
1696
+ })));
1697
+ // if the current language is different than the DEFAULT language
1698
+ const defaultLang = this.config.defaultLanguage || this.translateService.defaultLang;
1699
+ if (this.translateService.currentLang !== defaultLang) {
1700
+ // manually load the resources for the default language and add them to the translate service
1701
+ langSubscription.push(i18nLoader.loadResources(resources, defaultLang).pipe(take(1), tap((loadedResources) => {
1702
+ this.translateService.setTranslation(defaultLang, loadedResources.translations, true);
1703
+ })));
1936
1704
  }
1705
+ return forkJoin(langSubscription).pipe(map((loadedResourcesArr) => !loadedResourcesArr[0].hasError ? { success: true } : { success: false, error: loadedResourcesArr[0].errors }), tap(() => {
1706
+ // emit status of module loading progress
1707
+ this.onModuleLoad.next({ ready: true, name: moduleName });
1708
+ }), catchError((error) => {
1709
+ // emit status of module loading progress
1710
+ this.onModuleLoad.next({ ready: true, name: moduleName, error });
1711
+ return of({ success: false, error });
1712
+ }));
1937
1713
  }
1938
- // update the app version, if defined
1939
- if (payloadVersion) {
1940
- state = { ...state, app: { ...state?.app, version: payloadVersion } };
1714
+ else {
1715
+ return of({ success: false, error: 'currentLoader is not an I18nLoader.' });
1941
1716
  }
1942
1717
  }
1943
- return reducer(state, action);
1944
- };
1945
-
1946
- class StoreServiceMock extends StoreService {
1947
- constructor() {
1948
- super(null, null);
1949
- }
1950
- init() { }
1951
- addAutoSaveHandler() { }
1952
- handleAutoSave() { }
1953
- dispatch() { }
1954
- select() {
1955
- return of({});
1718
+ /**
1719
+ * Prepares the default language
1720
+ */
1721
+ preparedDefaultLanguage() {
1722
+ const browserPref = getBrowserDefaultLanguage();
1723
+ // If user has browser preferred lang, and that language is part of the languages array
1724
+ if (browserPref && this.config.languages && EuiEuLanguages.getLanguageCodes(this.config.languages).includes(browserPref)) {
1725
+ return browserPref;
1726
+ }
1727
+ return this.config.defaultLanguage;
1956
1728
  }
1957
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: StoreServiceMock, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1958
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: StoreServiceMock }); }
1959
- }
1960
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: StoreServiceMock, decorators: [{
1961
- type: Injectable
1962
- }], ctorParameters: function () { return []; } });
1963
-
1964
- const CORE_ROOT_GUARD = new InjectionToken('Internal Theme ForRoot Guard');
1965
- class CoreModule {
1966
1729
  /**
1967
- * No more providing config from forRoot, you have to use EUI_CONFIG_TOKEN to pass config via providers.
1730
+ * @param default_language if given default language to override default language coming from the
1731
+ * configuration token.
1968
1732
  */
1969
- static forRoot() {
1970
- return {
1971
- ngModule: CoreModule,
1972
- providers: [
1973
- {
1974
- provide: EuiCoreRootGuardClass,
1975
- useFactory: euiCoreRootGuardClass,
1976
- deps: [],
1977
- },
1978
- {
1979
- provide: CORE_ROOT_GUARD,
1980
- useFactory: createEuiCoreRootGuard,
1981
- deps: [[EuiCoreRootGuardClass, new Optional(), new SkipSelf()]],
1982
- },
1983
- ...getCoreProviders(),
1984
- ],
1985
- };
1733
+ setup(initLanguage) {
1734
+ // use the default config as a reference base
1735
+ this.config = Object.assign({}, DEFAULT_I18N_SERVICE_CONFIG, this.config);
1736
+ // configure the translate config service
1737
+ if (this.config.languages) {
1738
+ this.translateService.addLangs(EuiEuLanguages.getLanguageCodes(this.config.languages));
1739
+ if (this.logService) {
1740
+ this.logService.info(`I18n accepted languages set to ${EuiEuLanguages.getLanguageCodes(this.config.languages)}`);
1741
+ }
1742
+ }
1743
+ // set the current language, causing to load translations
1744
+ return this.translateService.use(initLanguage).pipe(tap(() => {
1745
+ this.bindActiveLangStateToTranslateService();
1746
+ this.bindTranslateServiceLangChangeToState();
1747
+ if (this.config.defaultLanguage) {
1748
+ this.setDefaultLanguage(this.config.defaultLanguage);
1749
+ }
1750
+ }), map(() => {
1751
+ if (this.translateService.currentLoader instanceof I18nLoader) {
1752
+ const failedResources = this.translateService.currentLoader.getFailedResources();
1753
+ if (failedResources.length > 0) {
1754
+ return { success: false, error: failedResources };
1755
+ }
1756
+ }
1757
+ return { success: true };
1758
+ }), catchError((error) => of({ success: false, error })));
1986
1759
  }
1987
- static forChild(moduleName) {
1988
- return {
1989
- ngModule: CoreModule,
1990
- providers: getCoreChildProviders(moduleName),
1991
- };
1760
+ bindActiveLangStateToTranslateService() {
1761
+ this.store.select(getActiveLang).subscribe((lang) => {
1762
+ if (lang !== null && this.translateService.currentLang !== lang) {
1763
+ this.use(lang);
1764
+ }
1765
+ });
1992
1766
  }
1993
- constructor(guard, parentModule, injector) {
1994
- // if there is a parentModule, a new instance is created in a lazy loaded module
1995
- if (parentModule) {
1996
- // extract the i18n service and the i18n loader config
1997
- // todo, this thing should be achieved by store, and localization service dependency should be removed
1998
- const storeService = injector.get(StoreService);
1999
- const moduleName = injector.get(MODULE_NAME_TOKEN);
2000
- const moduleConfig = injector.get(MODULE_CONFIG_TOKEN);
2001
- storeService.dispatch(new AddAppLoadedConfigModulesAction({ moduleName, moduleConfig }));
1767
+ bindTranslateServiceLangChangeToState() {
1768
+ this.translateService.onLangChange.subscribe((event) => {
1769
+ if (this.stateInstance.activeLang !== event.lang) {
1770
+ this.updateState({ activeLang: event.lang });
1771
+ }
1772
+ if (this.logService) {
1773
+ this.logService.info(`I18n current language set to ${event.lang}`);
1774
+ }
1775
+ });
1776
+ }
1777
+ setDefaultLanguage(defaultLanguage) {
1778
+ // removed, current language is already calculated in init, setup function fill use it directly, not default
1779
+ // this.translateService.currentLang = default_language || this.config.defaultLanguage;
1780
+ this.translateService.setDefaultLang(defaultLanguage);
1781
+ if (this.logService) {
1782
+ this.logService.info(`I18n default language set to ${defaultLanguage}`);
2002
1783
  }
2003
1784
  }
2004
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule, deps: [{ token: CORE_ROOT_GUARD, optional: true }, { token: CoreModule, optional: true, skipSelf: true }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.NgModule }); }
2005
- /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule }); }
2006
- /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule }); }
1785
+ /**
1786
+ * change the currently used language
1787
+ *
1788
+ * @param lang is the language code based on the convention you used e.g. (en or en-EN)
1789
+ * @returns Observable that returns an object contain the object translation for given language after language
1790
+ * fully loaded.
1791
+ */
1792
+ use(lang) {
1793
+ return this.translateService.use(lang);
1794
+ }
1795
+ /**
1796
+ * updates the HTML element lang attribute
1797
+ *
1798
+ * @private
1799
+ */
1800
+ updateHTMLDOMLang(lang) {
1801
+ this.document.documentElement.lang = lang;
1802
+ }
1803
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nService, deps: [{ token: GLOBAL_CONFIG_TOKEN }, { token: i1$3.TranslateService }, { token: LogService, optional: true }, { token: StoreService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); }
1804
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nService, providedIn: 'root' }); }
2007
1805
  }
2008
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: CoreModule, decorators: [{
2009
- type: NgModule,
1806
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nService, decorators: [{
1807
+ type: Injectable,
2010
1808
  args: [{
2011
- imports: [
2012
- // HttpClientModule, no more..
2013
- ],
1809
+ providedIn: 'root',
2014
1810
  }]
2015
1811
  }], ctorParameters: function () { return [{ type: undefined, decorators: [{
2016
- type: Optional
2017
- }, {
2018
1812
  type: Inject,
2019
- args: [CORE_ROOT_GUARD]
2020
- }] }, { type: CoreModule, decorators: [{
1813
+ args: [GLOBAL_CONFIG_TOKEN]
1814
+ }] }, { type: i1$3.TranslateService }, { type: LogService, decorators: [{
2021
1815
  type: Optional
2022
- }, {
2023
- type: SkipSelf
2024
- }] }, { type: i0.Injector }]; } });
2025
- const createEuiCoreRootGuard = (core) => {
2026
- if (core) {
2027
- throw new TypeError(`CoreModule.forRoot() called twice. Feature modules should use ThemeModule.forChild() instead.`);
1816
+ }] }, { type: StoreService }, { type: Document, decorators: [{
1817
+ type: Inject,
1818
+ args: [DOCUMENT]
1819
+ }] }]; } });
1820
+
1821
+ class I18nServiceMock extends I18nService {
1822
+ constructor() {
1823
+ super(null, null, null, null, null);
1824
+ this.DEFAULT_LANGUAGE = 'en';
1825
+ }
1826
+ get onLangChange() {
1827
+ return new EventEmitter();
1828
+ }
1829
+ addResources() {
1830
+ return of({ success: true });
1831
+ }
1832
+ get currentLanguage() {
1833
+ return this.DEFAULT_LANGUAGE;
1834
+ }
1835
+ getState() {
1836
+ return of({ activeLang: this.DEFAULT_LANGUAGE });
1837
+ }
1838
+ updateState(i18nState) {
1839
+ this.DEFAULT_LANGUAGE = i18nState.activeLang || 'en';
1840
+ }
1841
+ init(i18nState) {
1842
+ this.DEFAULT_LANGUAGE = i18nState.activeLang || 'en';
1843
+ return of({ success: true });
1844
+ }
1845
+ lazyLoadInit() {
1846
+ return of({ success: true });
2028
1847
  }
2029
- return 'guarded';
2030
- };
2031
- class EuiCoreRootGuardClass {
2032
- constructor() { }
1848
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nServiceMock, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1849
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nServiceMock }); }
2033
1850
  }
2034
- const euiCoreRootGuardClass = () => new EuiCoreRootGuardClass();
1851
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: I18nServiceMock, decorators: [{
1852
+ type: Injectable
1853
+ }], ctorParameters: function () { return []; } });
1854
+
1855
+ const diffDays = (dateStart, dateEnd) => Math.round((dateEnd.getTime() - dateStart.getTime()) / 1000 / 60 / 60 / 24);
1856
+ const diffDaysFromToday = (date) => diffDays(new Date(), date);
2035
1857
 
2036
1858
  /**
2037
- * Function to be used before the AppModule is bootstrapped. It is currently used to load dynamic configurations.
2038
- * It needs to be added in your application main.ts file.
2039
- * @param envConfig the current environment configuration
2040
- * @returns the updated environment configuration, as a promise
1859
+ * Provides read-only equivalent of jQuery's position function:
1860
+ * http://api.jquery.com/position/
2041
1861
  */
2042
- async function preInitApp(envConfig) {
2043
- const envDynamicConfig = envConfig && envConfig.envDynamicConfig;
2044
- if (envDynamicConfig) {
2045
- // load the local configuration file, if defined
2046
- const euiAppJsonConfig = await loadEuiDynamicEnvironmentConfig(envDynamicConfig.uri, envDynamicConfig.configTimeout || 2000);
2047
- envConfig.loadedEnvDynamicConfig = euiAppJsonConfig;
2048
- return envConfig;
1862
+ const position = (nativeEl) => {
1863
+ let elBCR = offset(nativeEl);
1864
+ let offsetParentBCR = { top: 0, left: 0 };
1865
+ let offsetParentEl = parentOffsetEl(nativeEl);
1866
+ if (offsetParentEl !== document) {
1867
+ offsetParentBCR = offset(offsetParentEl);
1868
+ offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
1869
+ offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
2049
1870
  }
2050
- return envConfig;
2051
- }
1871
+ let boundingClientRect = nativeEl.getBoundingClientRect();
1872
+ return {
1873
+ width: boundingClientRect.width || nativeEl.offsetWidth,
1874
+ height: boundingClientRect.height || nativeEl.offsetHeight,
1875
+ top: elBCR.top - offsetParentBCR.top,
1876
+ left: elBCR.left - offsetParentBCR.left,
1877
+ };
1878
+ };
2052
1879
  /**
2053
- * Function to load asynchronously a dynamic configuration (from a local path or a web service)
2054
- *
2055
- * @param url the path to the configuration
2056
- * @param timeout possible timeout
2057
- * @returns EuiAppJsonConfig promise
1880
+ * Provides read-only equivalent of jQuery's offset function:
1881
+ * http://api.jquery.com/offset/
2058
1882
  */
2059
- async function loadEuiDynamicEnvironmentConfig(url, timeout) {
2060
- // if the url is specified
2061
- if (url) {
2062
- // fetch the config
2063
- try {
2064
- return await xhr({
2065
- url,
2066
- timeout,
2067
- });
1883
+ const offset = (nativeEl) => {
1884
+ let boundingClientRect = nativeEl.getBoundingClientRect();
1885
+ return {
1886
+ width: boundingClientRect.width || nativeEl.offsetWidth,
1887
+ height: boundingClientRect.height || nativeEl.offsetHeight,
1888
+ top: boundingClientRect.top + (window.pageYOffset || document.documentElement.scrollTop),
1889
+ left: boundingClientRect.left + (window.pageXOffset || document.documentElement.scrollLeft),
1890
+ };
1891
+ };
1892
+ // PRIVATE FUNCTIONS
1893
+ const getStyle = (nativeEl, cssProp) => {
1894
+ // IE
1895
+ if (nativeEl.currentStyle) {
1896
+ return nativeEl.currentStyle[cssProp];
1897
+ }
1898
+ if (window.getComputedStyle) {
1899
+ return window.getComputedStyle(nativeEl)[cssProp];
1900
+ }
1901
+ // finally try and get inline style
1902
+ return nativeEl.style[cssProp];
1903
+ };
1904
+ /**
1905
+ * Checks if a given element is statically positioned
1906
+ */
1907
+ const isStaticPositioned = (nativeEl) => (getStyle(nativeEl, 'position') || 'static') === 'static';
1908
+ /**
1909
+ * returns the closest, non-statically positioned parentOffset of a given element
1910
+ * @param nativeEl
1911
+ */
1912
+ const parentOffsetEl = (nativeEl) => {
1913
+ let offsetParent = nativeEl.offsetParent || document;
1914
+ while (offsetParent && offsetParent !== document && isStaticPositioned(offsetParent)) {
1915
+ offsetParent = offsetParent.offsetParent;
1916
+ }
1917
+ return offsetParent || document;
1918
+ };
1919
+ const getViewElement = (fixture, componentClass, klass) => {
1920
+ let de, el, domElement;
1921
+ de = fixture.debugElement.query(By.css(componentClass));
1922
+ if (de) {
1923
+ el = de.nativeElement;
1924
+ if (el && klass) {
1925
+ domElement = el.querySelectorAll(klass);
1926
+ if (domElement.length <= 1) {
1927
+ domElement = el.querySelector(klass);
1928
+ }
2068
1929
  }
2069
- catch (error) {
2070
- // send a warning to the console
2071
- console.warn('Dynamic configuration cannot be loaded from url', url, error);
1930
+ }
1931
+ return { de, el, domElement };
1932
+ };
1933
+ const closestMatchingParent = (target, className) => {
1934
+ let element = target;
1935
+ while (element && element.nodeType === Node.ELEMENT_NODE) {
1936
+ if (element.classList.contains(className)) {
1937
+ return element;
2072
1938
  }
1939
+ element = element.parentElement;
2073
1940
  }
2074
- // the dynamic config is not loaded
2075
1941
  return null;
2076
- }
1942
+ };
2077
1943
 
2078
- const errorCodes = {
2079
- ERR_GENERIC: 'ERR_GENERIC',
1944
+ const consumeEvent = (event) => {
1945
+ if (event) {
1946
+ event.preventDefault();
1947
+ event.stopPropagation();
1948
+ event.cancelBubble = true;
1949
+ }
1950
+ return false;
2080
1951
  };
2081
- const messages = {
2082
- ERR_GENERIC: 'An error occured',
1952
+
1953
+ const forOwn = (object, iteratee) => {
1954
+ object = Object(object);
1955
+ Object.keys(object).forEach((key) => iteratee(object[key], key, object));
2083
1956
  };
2084
- // Intermediate "hacky" subclass taken from
2085
- // https://www.bennadel.com/blog/3226-experimenting-with-error-sub-classing-using-es5-and-typescript-2-1-5.htm
2086
- class ErrorSubClass {
2087
- constructor(code, msg, args) {
2088
- if (!msg && code && code in messages) {
2089
- if (typeof messages[code] === 'function') {
2090
- msg = messages[code](...args.messageParams);
1957
+ const markFormGroupTouched = (FormControls) => {
1958
+ const markFormGroupTouchedRecursive = (controls) => {
1959
+ forOwn(controls, (c) => {
1960
+ if (c instanceof FormGroup || c instanceof FormArray) {
1961
+ markFormGroupTouchedRecursive(c.controls);
2091
1962
  }
2092
1963
  else {
2093
- msg = messages[code];
1964
+ c.markAsTouched();
1965
+ c.updateValueAndValidity();
2094
1966
  }
1967
+ });
1968
+ };
1969
+ markFormGroupTouchedRecursive(FormControls);
1970
+ };
1971
+ const markControlsTouched = (group) => {
1972
+ Object.keys(group.controls).forEach((key) => {
1973
+ const abstractControl = group.controls[key];
1974
+ if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
1975
+ markControlsTouched(abstractControl);
2095
1976
  }
2096
- this.name = 'ErrorSubClass';
2097
- this.message = msg;
2098
- this.code = code;
2099
- this.stack = new Error(msg).stack;
2100
- if (args) {
2101
- Object.keys(args).forEach((k) => {
2102
- if (k !== 'messageParams') {
2103
- this[k] = args[k];
2104
- }
2105
- });
1977
+ else {
1978
+ abstractControl.markAsTouched();
2106
1979
  }
2107
- }
2108
- }
2109
- // This replaces the "extends" on the Class
2110
- // ErrorSubClass.prototype = <any>Object.create(Error.prototype);
2111
- class EuiError extends ErrorSubClass {
2112
- constructor(code = errorCodes.ERR_GENERIC, msg, args) {
2113
- super(code, msg, args);
2114
- this.name = 'EuiError';
2115
- }
2116
- }
1980
+ });
1981
+ };
1982
+ const validateEmail = (c) => {
1983
+ const EMAIL_REGEXP =
1984
+ // eslint-disable-next-line max-len, no-useless-escape
1985
+ /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1986
+ const error = {
1987
+ validateEmail: {
1988
+ valid: false,
1989
+ },
1990
+ };
1991
+ return EMAIL_REGEXP.test(c.value) ? null : error;
1992
+ };
2117
1993
 
2118
- class GlobalErrorHandler extends ErrorHandler {
2119
- constructor(logService) {
2120
- super();
2121
- this.logService = logService;
2122
- }
2123
- handleError(error) {
2124
- // log error
2125
- this.logService.error(error.message || error.toString());
2126
- // throw error;
2127
- super.handleError(error);
1994
+ /**
1995
+ * returns a random string with radix=36
1996
+ */
1997
+ const uniqueId = () => Math.random().toString(36).substr(2, 9);
1998
+ /**
1999
+ * Its job is to parse number and format it based on given input.
2000
+ * @param value can be a number or a string number
2001
+ * @param fractionSize size of fraction of a number e.g. 1000.88 fraction is 2 (88) default is 2
2002
+ * @param inDecimalSeparator separator for decimal part of the number default is ','
2003
+ * @param inThousandSeparator separator for the whole part of the number default is ''
2004
+ */
2005
+ const formatNumber = (value, fractionSize = 2, inDecimalSeparator = ',', inThousandSeparator = '') => {
2006
+ if (inDecimalSeparator || inThousandSeparator) {
2007
+ console.warn('The inDecimalSeparator and inThousandSeparator are deprecated. They will be a ' +
2008
+ 'BREAKING CHANGE for eUI 9 and be replaced by localization strategy.');
2128
2009
  }
2129
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: GlobalErrorHandler, deps: [{ token: LogService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2130
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: GlobalErrorHandler, providedIn: 'root' }); }
2131
- }
2132
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: GlobalErrorHandler, decorators: [{
2133
- type: Injectable,
2134
- args: [{
2135
- providedIn: 'root',
2136
- }]
2137
- }], ctorParameters: function () { return [{ type: LogService }]; } });
2138
-
2139
- class HttpErrorHandlerInterceptor {
2140
- constructor(config = DEFAULT_HTTP_ERROR_HANDLER_CONFIG, injector, router) {
2141
- this.config = config;
2142
- this.injector = injector;
2143
- this.router = router;
2010
+ let decimalSeparator = '';
2011
+ let thousandsSeparator = '';
2012
+ if (value === null || value === undefined) {
2013
+ return null;
2144
2014
  }
2145
- intercept(req, next) {
2146
- return next.handle(req).pipe(tap(() => { }, (error) => {
2147
- // in case of http error
2148
- if (error instanceof HttpErrorResponse) {
2149
- const routeConfig = this.getRouteConfig(error);
2150
- const rule = routeConfig && (routeConfig[error.status] || routeConfig.default);
2151
- if (typeof rule === 'string') {
2152
- // perform a redirect
2153
- this.router.navigate([rule]);
2154
- }
2155
- else if (typeof rule === 'function') {
2156
- // callback function
2157
- rule.call(null, error, this.injector);
2158
- }
2015
+ if (typeof value === 'string') {
2016
+ // get all non characters
2017
+ let nonChars = value.match(/[^0-9]+/g) || [];
2018
+ // get all unique chars
2019
+ let uniqueChars = nonChars.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
2020
+ // get separators
2021
+ let separators = Array.from(uniqueChars.keys());
2022
+ // sense where decimal separator and where thousand separator is
2023
+ if (separators.length > 1) {
2024
+ // left detected separator will be thousand and second will be decimal
2025
+ thousandsSeparator = separators[0];
2026
+ decimalSeparator = separators[1];
2027
+ }
2028
+ else if (separators.length > 0) {
2029
+ // in case there is only one separator you don't know exactly who this separator is.
2030
+ // in case there are more than two occurrences means we have a thousand otherwise we'll
2031
+ // agree by conventions that is decimal
2032
+ if (nonChars.length > 1) {
2033
+ return value
2034
+ .split(separators[0])
2035
+ .join('')
2036
+ .replace(/\B(?=(\d{3})+(?!\d))/g, inThousandSeparator);
2159
2037
  }
2160
- }));
2161
- }
2162
- /**
2163
- * Search in the configuration for the first matching route
2164
- *
2165
- * @param error http error response
2166
- * @returns HttpErrorRouteConfig or null
2167
- */
2168
- getRouteConfig(error) {
2169
- // filter the routes
2170
- const routes = this.config.routes.filter((route) => {
2171
- const regex = new RegExp('^' + route.path.split('*').join('.*') + '$');
2172
- return regex.test(error.url) && (typeof route[error.status] !== 'undefined' || typeof route.default !== 'undefined');
2173
- });
2174
- // return the first matching route, if any
2175
- return routes.length > 0 ? routes[0] : null;
2038
+ else {
2039
+ return value.replace(separators[0], inDecimalSeparator).replace(/\B(?=(\d{3})+(?!\d))/g, inThousandSeparator);
2040
+ }
2041
+ }
2042
+ else {
2043
+ // in case there are no separators then only format based on thousand one
2044
+ return value.replace(/\B(?=(\d{3})+(?!\d))/g, inThousandSeparator);
2045
+ }
2046
+ // do the replacement of separators and parseInt
2047
+ // Beware! Always replace thousand separator first and then decimal
2048
+ value = value.split(thousandsSeparator).join('').replace(decimalSeparator, '.');
2176
2049
  }
2177
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: HttpErrorHandlerInterceptor, deps: [{ token: HTTP_ERROR_HANDLER_CONFIG_TOKEN }, { token: i0.Injector }, { token: i2$1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
2178
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: HttpErrorHandlerInterceptor }); }
2179
- }
2180
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: HttpErrorHandlerInterceptor, decorators: [{
2181
- type: Injectable
2182
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
2183
- type: Inject,
2184
- args: [HTTP_ERROR_HANDLER_CONFIG_TOKEN]
2185
- }] }, { type: i0.Injector }, { type: i2$1.Router }]; } });
2050
+ decimalSeparator = inDecimalSeparator;
2051
+ thousandsSeparator = inThousandSeparator;
2052
+ const maxFraction = '0'.repeat(fractionSize);
2053
+ let [integer, fraction = maxFraction] = (value || '0').toString().split('.');
2054
+ fraction = fractionSize > 0 ? (fraction + maxFraction).substring(0, fractionSize) : '';
2055
+ // this rule makes sense only in number in case fraction is all zero
2056
+ fraction = typeof value === 'number' && fraction === maxFraction ? '' : decimalSeparator + fraction;
2057
+ return integer.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator) + fraction;
2058
+ };
2186
2059
 
2187
- // import { UxAppShellService } from '../ux-app-shell.service';
2188
- function AlertHttpErrorCallbackFn(error) {
2189
- alert(error.statusText);
2190
- }
2191
- function ConsoleHttpErrorCallbackFn(error) {
2192
- console.error('HttpError', error);
2193
- }
2194
- function LogHttpErrorCallbackFn(error, injector) {
2195
- const logService = injector.get(LogService);
2196
- logService.error('HttpError', error);
2197
- }
2198
- function GrowlHttpErrorCallbackFn(error, injector) {
2199
- // TODO v17 should not display growl from here but from where it's called
2200
- // const asService = injector.get(UxAppShellService);
2201
- // asService.growlError(error.statusText);
2202
- }
2060
+ const handleError = (error) => {
2061
+ let errMsg = error.message ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';
2062
+ // console.error(errMsg);
2063
+ return throwError(errMsg);
2064
+ };
2203
2065
 
2204
- class ApiQueueService {
2205
- constructor(store, http, logService) {
2066
+ /**
2067
+ * check if a value is null or undefined. If it's not both returns true otherwise false
2068
+ *
2069
+ * @param value
2070
+ */
2071
+ const isDefined = (value) => value !== undefined && value !== null;
2072
+
2073
+ class EuiPermissionService extends EuiService {
2074
+ constructor(log, store) {
2075
+ super([]);
2076
+ this.log = log;
2206
2077
  this.store = store;
2207
- this.http = http;
2208
- this.logService = logService;
2209
- }
2210
- /**
2211
- * Adds an item in the queue by dispatching an action to the store.
2212
- * @param id - The id of the item inside the queue.
2213
- * WARNING if matches any other queue item id will overwrite it.
2214
- * @param item - an item for Queue
2215
- */
2216
- addQueueItem(id, item) {
2217
- const allowedMethods = ['post', 'put', 'get'];
2218
- if (allowedMethods.indexOf(item.method) < 0) {
2219
- throw new Error(`[ApiQueue] method "${item.method}" is not allowed`);
2078
+ if (log) {
2079
+ this.logger = log.getLogger('core.PermissionService');
2220
2080
  }
2221
- this.store.dispatch(new AddApiQueueItemAction({ id, item }));
2222
2081
  }
2223
2082
  /**
2224
- * Subscribes to the store to retrieve queue and then converts the queue object to an array of items.
2225
- * @return An Observable of array of all items inside the current queue (ApiQueueItem[])
2083
+ * initialize the service by providing a state
2084
+ * @param rights
2226
2085
  */
2227
- getQueue() {
2228
- return this.store.select(getApiQueue).pipe(take(1), map((queue) => {
2229
- const queueArray = queue && Object.values(queue);
2230
- if (!queueArray || queueArray.length < 1) {
2231
- this.logService.warn('No items in the queue exist.');
2232
- }
2233
- return queueArray;
2234
- }));
2086
+ init(rights) {
2087
+ super.initEuiService();
2088
+ if (Array.isArray(rights)) {
2089
+ this.updateState(rights);
2090
+ return of({ success: true });
2091
+ }
2092
+ if (this.logger) {
2093
+ this.logger.error('Init object should be instance of BaseUserState');
2094
+ }
2095
+ return of({ success: false, error: 'Init object should be instance of BaseUserState' });
2235
2096
  }
2236
- /**
2237
- * Subscribes to the store to retrieve an item from the queue.
2238
- * @param id - id of the item inside the queue
2239
- * @return An Observable of ApiQueueItem type
2240
- */
2241
- getQueueItem(id) {
2242
- return this.store.select(getApiQueueItem(id)).pipe(take(1), map((queue) => {
2243
- if (!queue) {
2244
- this.logService.warn(`Queue item with id "${id}" does not exist`);
2245
- return null;
2246
- }
2247
- return queue;
2248
- }));
2097
+ get userRights() {
2098
+ return this.stateInstance;
2249
2099
  }
2250
2100
  /**
2251
- * Removes a given item from the queue.
2252
- * @param id - id of the item inside the queue
2101
+ * get the user's Rights from the state
2253
2102
  */
2254
- removeQueueItem(id) {
2255
- this.store.dispatch(new RemoveApiQueueItemAction(id));
2103
+ getState() {
2104
+ return this.store.select(getUserRights);
2256
2105
  }
2257
2106
  /**
2258
- * Removes all items placed in the queue.
2107
+ * update the state with the given user's rights
2108
+ * @param rights
2259
2109
  */
2260
- removeAllQueueItem() {
2261
- this.store.dispatch(new EmptyApiQueueAction(null));
2110
+ updateState(rights) {
2111
+ this.store.dispatch(new UpdateUserRightsAction(rights || []));
2262
2112
  }
2263
2113
  /**
2264
- * Process an item from the queue.
2265
- * @param id - id of the item inside the queue
2266
- * @return An Observable of type any
2114
+ * accepts a string of right and or a permission. In string first comes the right and or followed by permission in
2115
+ * a dot separated way e.g. 'RIGHT.PERMISSION' or 'RIGHT'. The string can contain more that 2 separated fields.
2116
+ * In that case the last field should be the permission and all the others a Right. e.g. 'RIGHT1.RIGHT2.PERMISSION'
2117
+ * The last part is useful where you want a user to have multiple rights and the permission of the last right.
2118
+ * @param rightsAndPermission
2267
2119
  */
2268
- processQueueItem(id) {
2269
- return this.store.select(getApiQueueItem(id)).pipe(take(1), switchMap((queue) => {
2270
- if (!queue) {
2271
- this.logService.warn(`Queue item with id "${id}" does not exist`);
2272
- return of(null);
2120
+ checkAttributePermission(rightsAndPermission) {
2121
+ const permProps = rightsAndPermission.split('.');
2122
+ const rightId = permProps[0];
2123
+ const hasRight = this.checkRight(rightId);
2124
+ if (hasRight) {
2125
+ if (permProps.length === 2) {
2126
+ // in case of 'RIGHT.PERMISSION'
2127
+ const permission = permProps[1];
2128
+ return this.checkPermission(rightId, permission);
2273
2129
  }
2274
- return this.buildHttpRequest(id, queue);
2275
- }));
2130
+ else if (permProps.length > 2) {
2131
+ // in case of 'RIGHT1.RIGHT2.....PERMISSION' string call recursively
2132
+ return this.checkAttributePermission(rightsAndPermission.slice(rightId.length + 1));
2133
+ }
2134
+ }
2135
+ return hasRight;
2276
2136
  }
2277
2137
  /**
2278
- * Process all the items inside the queue.
2279
- * @param [continueOnError=true] - in case of an item request fails continue with the rest in queue
2280
- * @return An Observable with an array which contains all responses from all items in queue.
2138
+ * check if a user has the right.
2139
+ * @param rightId the id of Right
2281
2140
  */
2282
- processAllQueueItems(continueOnError = true) {
2283
- return this.store.select(getApiQueue).pipe(map((queue) => Object.entries(queue).map(([key, value]) => this.buildHttpRequest(key, value).pipe(catchError((error) => {
2284
- this.logService.error(`Queue Item with id ${key} failed.`, error.message, error.stack);
2285
- if (!continueOnError) {
2286
- throw error;
2287
- }
2288
- return of(error);
2289
- })))), switchMap((obsQueue) => forkJoin(Array.from(obsQueue))));
2141
+ checkRight(rightId) {
2142
+ return this.userRights.some((right) => right && right.id === rightId);
2290
2143
  }
2291
2144
  /**
2292
- * Process all items in queue after it order them based on their timestamp. Beware that order
2293
- * matter in execution of Http Request in queue. When a request completes only then next will begin. Emission
2294
- * or Http response follows order.
2295
- * @param [ascending=true] - sets the order of queue items based on timestamp
2145
+ * check if a user has that right and that right has the permission given.
2146
+ * @param rightId the right of the user
2147
+ * @param permission the permission of the right
2296
2148
  */
2297
- processAllQueueItemsSequential(ascending = true) {
2298
- return this.store.select(getApiQueue).pipe(concatMap((queue) => Object.entries(queue)
2299
- .sort((a, b) => (ascending ? this.sortOnTimestamp(a, b) : this.sortOnTimestamp(b, a)))
2300
- .map(([id, item]) => this.buildHttpRequest(id, item))));
2149
+ checkPermission(rightId, permission) {
2150
+ // the user has to have that right and that right has to have the given permission
2151
+ return this.userRights.some((right) => right && right.id === rightId && right.permissions && right.permissions.includes(permission));
2301
2152
  }
2302
2153
  /**
2303
- * Short dates ascending based on input
2304
- * @param a - Date one
2305
- * @param b - Date two
2306
- *
2307
- * @beta
2154
+ * checks whether a user have all rights passed as an argument
2155
+ * @param rights an array of EuiUserRight
2308
2156
  */
2309
- sortOnTimestamp(a, b) {
2310
- return +new Date(a[1].timestamp) - +new Date(b[1].timestamp);
2157
+ haveAllRights(rights) {
2158
+ if (!isDefined(rights)) {
2159
+ throw Error(`The rights should be an array but instead it's not defined!`);
2160
+ }
2161
+ return rights.every((right) => this.userRights.find((uRight) => uRight.id === right.id));
2162
+ }
2163
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiPermissionService, deps: [{ token: LogService, optional: true }, { token: StoreService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2164
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiPermissionService, providedIn: 'root' }); }
2165
+ }
2166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiPermissionService, decorators: [{
2167
+ type: Injectable,
2168
+ args: [{
2169
+ providedIn: 'root',
2170
+ }]
2171
+ }], ctorParameters: function () { return [{ type: LogService, decorators: [{
2172
+ type: Optional
2173
+ }] }, { type: StoreService }]; } });
2174
+
2175
+ class EuiDynamicMenuService {
2176
+ // TODO: it should be moved euiservice, or it should be static
2177
+ // TODO: can we moved that service inside the module of the component that's using it?
2178
+ constructor(euiPermission, store) {
2179
+ this.euiPermission = euiPermission;
2180
+ this.store = store;
2181
+ this.menuLinks = [];
2182
+ this.store.select(getUserState).subscribe((user) => {
2183
+ if (user.menuLinks) {
2184
+ this.menuLinks = user.menuLinks;
2185
+ }
2186
+ });
2187
+ }
2188
+ getMenuLinks() {
2189
+ return this.menuLinks;
2311
2190
  }
2312
- /**
2313
- * build and HttpRequest Observable based on the given queue.
2314
- * After successful request completion removes the item from the queue.
2315
- * @param id - id of the item inside the queue
2316
- * @param item - the item of the queue that matches the id.
2317
- * @return An Observable of HttpClient Request
2318
- */
2319
- buildHttpRequest(id, item) {
2320
- return this.http[item.method.toLowerCase()](item.uri, item.payload).pipe(tap(() => this.removeQueueItem(id)));
2191
+ filterEuiMenuItemsWithRights(links) {
2192
+ return links.filter((link) => {
2193
+ if (link.hasChildren) {
2194
+ link.children = this.filterEuiMenuItemsWithRights(link.children);
2195
+ }
2196
+ // First check if the user is denied access:
2197
+ if (link.deniedRightId) {
2198
+ if (this.euiPermission.checkRight(link.deniedRightId)) {
2199
+ return false;
2200
+ }
2201
+ }
2202
+ if (link.deniedRightIds) {
2203
+ for (const deniedRightId of link.deniedRightIds) {
2204
+ if (this.euiPermission.checkRight(deniedRightId)) {
2205
+ return false;
2206
+ }
2207
+ }
2208
+ }
2209
+ // Then check if the user needs to be allowed access explicitly:
2210
+ if (link.allowedRightId) {
2211
+ return this.euiPermission.checkRight(link.allowedRightId);
2212
+ }
2213
+ if (link.allowedRightIds) {
2214
+ for (const allowedRightId of link.allowedRightIds) {
2215
+ if (this.euiPermission.checkRight(allowedRightId)) {
2216
+ return true;
2217
+ }
2218
+ }
2219
+ // User doesn't have one of the allowed rights. Deny access:
2220
+ return false;
2221
+ }
2222
+ // Access to the menu item is not denied, therefore allow it:
2223
+ return true;
2224
+ });
2321
2225
  }
2322
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: ApiQueueService, deps: [{ token: StoreService }, { token: i1$2.HttpClient }, { token: LogService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2323
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: ApiQueueService, providedIn: 'root' }); }
2226
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicMenuService, deps: [{ token: EuiPermissionService }, { token: StoreService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2227
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicMenuService, providedIn: 'root' }); }
2324
2228
  }
2325
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: ApiQueueService, decorators: [{
2229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicMenuService, decorators: [{
2326
2230
  type: Injectable,
2327
2231
  args: [{
2328
2232
  providedIn: 'root',
2329
2233
  }]
2330
- }], ctorParameters: function () { return [{ type: StoreService }, { type: i1$2.HttpClient }, { type: LogService }]; } });
2234
+ }], ctorParameters: function () { return [{ type: EuiPermissionService }, { type: StoreService }]; } });
2331
2235
 
2332
- class ApiQueueServiceMock extends ApiQueueService {
2333
- constructor() {
2334
- super(null, null, null);
2335
- }
2336
- getQueue() {
2337
- return of([]);
2236
+ const DYNAMIC_COMPONENT_CONFIG = new InjectionToken('DYNAMIC_COMPONENT_CONFIG');
2237
+ class EuiDynamicComponentService {
2238
+ constructor(componentFactoryResolver, injector, appRef) {
2239
+ this.componentFactoryResolver = componentFactoryResolver;
2240
+ this.injector = injector;
2241
+ this.appRef = appRef;
2338
2242
  }
2339
- getQueueItem(id) {
2340
- return of({ uri: id, method: 'GET' });
2243
+ /**
2244
+ * Adds a component to portal host dynamically
2245
+ * @param component The component to be injected
2246
+ * @param el The element reference for the component to be injected
2247
+ * @param config The data to be passed (config) for the DYNAMIC_COMPONENT_CONFIG token
2248
+ */
2249
+ add(component, el, config) {
2250
+ // TODO: take a look and replace deprecated componentFactoryResolver https://github.com/angular/components/issues/24334
2251
+ // Create a portalHost from a DOM element
2252
+ this.portalHost = new DomPortalOutlet(el ? el.nativeElement : document.body, this.componentFactoryResolver, this.appRef, this.injector);
2253
+ // Locate the component factory for the ComponentToIncludeDynamicallyComponent
2254
+ this.portal = new ComponentPortal(component, null, config ? this.createInjector(config) : null);
2255
+ // Attach portal to host
2256
+ this.portalComponentInstance = this.portalHost.attach(this.portal);
2257
+ if (config) {
2258
+ Object.assign(config, {
2259
+ portalHostRef: this.portalHost,
2260
+ portalRef: this.portal,
2261
+ portalComponentInstanceRef: this.portalComponentInstance,
2262
+ });
2263
+ }
2264
+ return {
2265
+ portalHost: this.portalHost,
2266
+ portal: this.portal,
2267
+ portalComponentInstance: this.portalComponentInstance,
2268
+ };
2341
2269
  }
2342
- removeAllQueueItem() { }
2343
- processAllQueueItems() {
2344
- return of({});
2270
+ /**
2271
+ * detaches a portalHost
2272
+ * @param portalHostRef reference of portal host to be detached
2273
+ */
2274
+ remove(portalHostRef) {
2275
+ portalHostRef.detach();
2276
+ this.portalHost = null;
2277
+ this.portal = null;
2278
+ this.portalComponentInstance = null;
2345
2279
  }
2346
- addQueueItem() { }
2347
- removeQueueItem() { }
2348
- processQueueItem() {
2349
- return of({});
2280
+ /**
2281
+ * creates a custom injector to be used when providing custom injection tokens to components inside a portal
2282
+ * @param data to be used by DYNAMIC_COMPONENT_CONFIG token
2283
+ */
2284
+ createInjector(data) {
2285
+ const injectorTokens = new WeakMap();
2286
+ injectorTokens.set(DYNAMIC_COMPONENT_CONFIG, data);
2287
+ return new PortalInjector(this.injector, injectorTokens);
2350
2288
  }
2289
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicComponentService, deps: [{ token: i0.ComponentFactoryResolver }, { token: i0.Injector }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Injectable }); }
2290
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicComponentService }); }
2351
2291
  }
2292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicComponentService, decorators: [{
2293
+ type: Injectable
2294
+ }], ctorParameters: function () { return [{ type: i0.ComponentFactoryResolver }, { type: i0.Injector }, { type: i0.ApplicationRef }]; } });
2352
2295
 
2353
- const diffDays = (dateStart, dateEnd) => Math.round((dateEnd.getTime() - dateStart.getTime()) / 1000 / 60 / 60 / 24);
2354
- const diffDaysFromToday = (date) => diffDays(new Date(), date);
2355
-
2356
- /**
2357
- * Provides read-only equivalent of jQuery's position function:
2358
- * http://api.jquery.com/position/
2359
- */
2360
- const position = (nativeEl) => {
2361
- let elBCR = offset(nativeEl);
2362
- let offsetParentBCR = { top: 0, left: 0 };
2363
- let offsetParentEl = parentOffsetEl(nativeEl);
2364
- if (offsetParentEl !== document) {
2365
- offsetParentBCR = offset(offsetParentEl);
2366
- offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
2367
- offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
2368
- }
2369
- let boundingClientRect = nativeEl.getBoundingClientRect();
2370
- return {
2371
- width: boundingClientRect.width || nativeEl.offsetWidth,
2372
- height: boundingClientRect.height || nativeEl.offsetHeight,
2373
- top: elBCR.top - offsetParentBCR.top,
2374
- left: elBCR.left - offsetParentBCR.left,
2375
- };
2376
- };
2377
- /**
2378
- * Provides read-only equivalent of jQuery's offset function:
2379
- * http://api.jquery.com/offset/
2380
- */
2381
- const offset = (nativeEl) => {
2382
- let boundingClientRect = nativeEl.getBoundingClientRect();
2383
- return {
2384
- width: boundingClientRect.width || nativeEl.offsetWidth,
2385
- height: boundingClientRect.height || nativeEl.offsetHeight,
2386
- top: boundingClientRect.top + (window.pageYOffset || document.documentElement.scrollTop),
2387
- left: boundingClientRect.left + (window.pageXOffset || document.documentElement.scrollLeft),
2388
- };
2296
+ const initialState = {
2297
+ appName: '',
2298
+ appShortName: '',
2299
+ appSubTitle: '',
2300
+ appBaseFontSize: '',
2301
+ userInfos: null,
2302
+ userSubInfos: null,
2303
+ impersonatedUserInfos: null,
2304
+ isSidebarOpen: true,
2305
+ isSidebarHover: false,
2306
+ isSidebarActive: false,
2307
+ isSidebarInnerActive: false,
2308
+ isSidebarStateCloseWithIcons: false,
2309
+ isSidebarExpandOnHover: true,
2310
+ hasSidebar: false,
2311
+ hasHeader: false,
2312
+ hasBreadcrumb: false,
2313
+ hasHeaderLogo: false,
2314
+ hasHeaderEnvironment: false,
2315
+ hasToolbar: false,
2316
+ hasToolbarMenu: false,
2317
+ environmentValue: '',
2318
+ isSidebarHidden: false,
2319
+ hasTopMessage: false,
2320
+ windowWidth: 0,
2321
+ windowHeight: 0,
2322
+ mainContentHeight: 0,
2323
+ pageHeaderHeight: 0,
2324
+ wrapperClasses: '',
2325
+ breakpoint: '',
2326
+ breakpoints: {
2327
+ isMobile: false,
2328
+ isTablet: false,
2329
+ isLtDesktop: false,
2330
+ isDesktop: false,
2331
+ isXL: false,
2332
+ isXXL: false,
2333
+ isFHD: false,
2334
+ is2K: false,
2335
+ is4K: false,
2336
+ },
2337
+ menuLinks: [],
2338
+ sidebarLinks: [],
2339
+ combinedLinks: [],
2340
+ isBlockDocumentActive: false,
2341
+ deviceInfo: null,
2342
+ activeLanguage: 'en',
2343
+ languages: EuiEuLanguages.getLanguages(),
2344
+ appMetadata: null,
2345
+ hasModalActive: false,
2346
+ isDimmerActive: false,
2389
2347
  };
2390
- // PRIVATE FUNCTIONS
2391
- const getStyle = (nativeEl, cssProp) => {
2392
- // IE
2393
- if (nativeEl.currentStyle) {
2394
- return nativeEl.currentStyle[cssProp];
2348
+ class EuiAppShellService {
2349
+ // -------------------
2350
+ get state$() {
2351
+ return this._state$.asObservable();
2395
2352
  }
2396
- if (window.getComputedStyle) {
2397
- return window.getComputedStyle(nativeEl)[cssProp];
2353
+ // -------------------
2354
+ // exposed observables
2355
+ get breakpoint$() {
2356
+ return this._breakpoint$.asObservable();
2398
2357
  }
2399
- // finally try and get inline style
2400
- return nativeEl.style[cssProp];
2401
- };
2402
- /**
2403
- * Checks if a given element is statically positioned
2404
- */
2405
- const isStaticPositioned = (nativeEl) => (getStyle(nativeEl, 'position') || 'static') === 'static';
2406
- /**
2407
- * returns the closest, non-statically positioned parentOffset of a given element
2408
- * @param nativeEl
2409
- */
2410
- const parentOffsetEl = (nativeEl) => {
2411
- let offsetParent = nativeEl.offsetParent || document;
2412
- while (offsetParent && offsetParent !== document && isStaticPositioned(offsetParent)) {
2413
- offsetParent = offsetParent.offsetParent;
2358
+ get breakpoints$() {
2359
+ return this._breakpoints$.asObservable();
2414
2360
  }
2415
- return offsetParent || document;
2416
- };
2417
- const getViewElement = (fixture, componentClass, klass) => {
2418
- let de, el, domElement;
2419
- de = fixture.debugElement.query(By.css(componentClass));
2420
- if (de) {
2421
- el = de.nativeElement;
2422
- if (el && klass) {
2423
- domElement = el.querySelectorAll(klass);
2424
- if (domElement.length <= 1) {
2425
- domElement = el.querySelector(klass);
2426
- }
2427
- }
2361
+ // ----------------
2362
+ // state operations
2363
+ // ----------------
2364
+ get state() {
2365
+ return this._state$.getValue();
2366
+ }
2367
+ // ----------------------------
2368
+ // public setters and functions
2369
+ // ----------------------------
2370
+ set isSidebarOpen(isOpen) {
2371
+ this.setState({
2372
+ ...this.state,
2373
+ isSidebarOpen: isOpen,
2374
+ });
2428
2375
  }
2429
- return { de, el, domElement };
2430
- };
2431
- const closestMatchingParent = (target, className) => {
2432
- let element = target;
2433
- while (element && element.nodeType === Node.ELEMENT_NODE) {
2434
- if (element.classList.contains(className)) {
2435
- return element;
2376
+ get isSidebarOpen() {
2377
+ return this.state.isSidebarOpen;
2378
+ }
2379
+ set isSidebarHover(isHover) {
2380
+ if (this.state.isSidebarExpandOnHover) {
2381
+ this.setState({
2382
+ ...this.state,
2383
+ isSidebarHover: isHover,
2384
+ });
2436
2385
  }
2437
- element = element.parentElement;
2438
2386
  }
2439
- return null;
2440
- };
2441
-
2442
- const consumeEvent = (event) => {
2443
- if (event) {
2444
- event.preventDefault();
2445
- event.stopPropagation();
2446
- event.cancelBubble = true;
2387
+ get isSidebarHover() {
2388
+ return this.state.isSidebarHover;
2447
2389
  }
2448
- return false;
2449
- };
2450
-
2451
- const forOwn = (object, iteratee) => {
2452
- object = Object(object);
2453
- Object.keys(object).forEach((key) => iteratee(object[key], key, object));
2454
- };
2455
- const markFormGroupTouched = (FormControls) => {
2456
- const markFormGroupTouchedRecursive = (controls) => {
2457
- forOwn(controls, (c) => {
2458
- if (c instanceof FormGroup || c instanceof FormArray) {
2459
- markFormGroupTouchedRecursive(c.controls);
2460
- }
2461
- else {
2462
- c.markAsTouched();
2463
- c.updateValueAndValidity();
2464
- }
2390
+ set isSidebarActive(isActive) {
2391
+ this.setState({
2392
+ ...this.state,
2393
+ isSidebarActive: isActive,
2465
2394
  });
2466
- };
2467
- markFormGroupTouchedRecursive(FormControls);
2468
- };
2469
- const markControlsTouched = (group) => {
2470
- Object.keys(group.controls).forEach((key) => {
2471
- const abstractControl = group.controls[key];
2472
- if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
2473
- markControlsTouched(abstractControl);
2474
- }
2475
- else {
2476
- abstractControl.markAsTouched();
2477
- }
2478
- });
2479
- };
2480
- const validateEmail = (c) => {
2481
- const EMAIL_REGEXP =
2482
- // eslint-disable-next-line max-len, no-useless-escape
2483
- /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
2484
- const error = {
2485
- validateEmail: {
2486
- valid: false,
2487
- },
2488
- };
2489
- return EMAIL_REGEXP.test(c.value) ? null : error;
2490
- };
2491
-
2492
- /**
2493
- * returns a random string with radix=36
2494
- */
2495
- const uniqueId = () => Math.random().toString(36).substr(2, 9);
2496
- /**
2497
- * Its job is to parse number and format it based on given input.
2498
- * @param value can be a number or a string number
2499
- * @param fractionSize size of fraction of a number e.g. 1000.88 fraction is 2 (88) default is 2
2500
- * @param inDecimalSeparator separator for decimal part of the number default is ','
2501
- * @param inThousandSeparator separator for the whole part of the number default is ''
2502
- */
2503
- const formatNumber = (value, fractionSize = 2, inDecimalSeparator = ',', inThousandSeparator = '') => {
2504
- if (inDecimalSeparator || inThousandSeparator) {
2505
- console.warn('The inDecimalSeparator and inThousandSeparator are deprecated. They will be a ' +
2506
- 'BREAKING CHANGE for eUI 9 and be replaced by localization strategy.');
2507
2395
  }
2508
- let decimalSeparator = '';
2509
- let thousandsSeparator = '';
2510
- if (value === null || value === undefined) {
2511
- return null;
2396
+ set isSidebarInnerActive(isOpen) {
2397
+ this.setState({
2398
+ ...this.state,
2399
+ isSidebarInnerActive: isOpen,
2400
+ });
2512
2401
  }
2513
- if (typeof value === 'string') {
2514
- // get all non characters
2515
- let nonChars = value.match(/[^0-9]+/g) || [];
2516
- // get all unique chars
2517
- let uniqueChars = nonChars.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
2518
- // get separators
2519
- let separators = Array.from(uniqueChars.keys());
2520
- // sense where decimal separator and where thousand separator is
2521
- if (separators.length > 1) {
2522
- // left detected separator will be thousand and second will be decimal
2523
- thousandsSeparator = separators[0];
2524
- decimalSeparator = separators[1];
2402
+ set sidebarLinks(links) {
2403
+ this.setState({
2404
+ ...this.state,
2405
+ sidebarLinks: links,
2406
+ });
2407
+ }
2408
+ set menuLinks(links) {
2409
+ this.setState({
2410
+ ...this.state,
2411
+ menuLinks: links,
2412
+ });
2413
+ }
2414
+ set isBlockDocumentActive(isActive) {
2415
+ this.setState({
2416
+ ...this.state,
2417
+ isBlockDocumentActive: isActive,
2418
+ });
2419
+ }
2420
+ get hasHeader() {
2421
+ return this.state.hasHeader;
2422
+ }
2423
+ // Edit mode
2424
+ get isDimmerActive() {
2425
+ return this.state.isDimmerActive;
2426
+ }
2427
+ set isDimmerActive(isActive) {
2428
+ this.setState({
2429
+ ...this.state,
2430
+ isDimmerActive: isActive,
2431
+ });
2432
+ }
2433
+ constructor(config, http, platformId, router, storeService, i18nService) {
2434
+ this.config = config;
2435
+ this.http = http;
2436
+ this.platformId = platformId;
2437
+ this.router = router;
2438
+ this.storeService = storeService;
2439
+ this.i18nService = i18nService;
2440
+ this.cssVars = [];
2441
+ let stateWithConfig = initialState;
2442
+ const languages = config?.i18n?.i18nService?.languages || initialState.languages;
2443
+ const defaultLanguage = config?.i18n?.i18nService?.defaultLanguage || initialState.activeLanguage;
2444
+ stateWithConfig = {
2445
+ ...stateWithConfig,
2446
+ ...{
2447
+ languages,
2448
+ activeLanguage: defaultLanguage,
2449
+ },
2450
+ };
2451
+ this._state$ = new BehaviorSubject(stateWithConfig);
2452
+ this._breakpoint$ = new BehaviorSubject('');
2453
+ this._breakpoints$ = new BehaviorSubject({});
2454
+ this.bindActiveLanguageToAppShellState();
2455
+ if (isPlatformBrowser(this.platformId)) {
2456
+ router.events.subscribe((event) => {
2457
+ if (event instanceof NavigationEnd) {
2458
+ window.scrollTo(0, 0);
2459
+ }
2460
+ });
2525
2461
  }
2526
- else if (separators.length > 0) {
2527
- // in case there is only one separator you don't know exactly who this separator is.
2528
- // in case there are more than two occurrences means we have a thousand otherwise we'll
2529
- // agree by conventions that is decimal
2530
- if (nonChars.length > 1) {
2531
- return value
2532
- .split(separators[0])
2533
- .join('')
2534
- .replace(/\B(?=(\d{3})+(?!\d))/g, inThousandSeparator);
2535
- }
2536
- else {
2537
- return value.replace(separators[0], inDecimalSeparator).replace(/\B(?=(\d{3})+(?!\d))/g, inThousandSeparator);
2538
- }
2462
+ }
2463
+ setState(nextState, updateI18 = true) {
2464
+ let breakpoint, breakpoints, wrapperClasses;
2465
+ let combinedLinks;
2466
+ const state = this.state;
2467
+ // check if window width has been updated from previous state
2468
+ if (this.state.windowWidth !== nextState.windowWidth) {
2469
+ breakpoint = this.getBreakpoint(nextState.windowWidth);
2470
+ breakpoints = this.getBreakpoints(breakpoint);
2471
+ this._breakpoint$.next(breakpoint);
2472
+ this._breakpoints$.next(breakpoints);
2473
+ // if not propagate the old ones without doing any calculations
2539
2474
  }
2540
2475
  else {
2541
- // in case there are no separators then only format based on thousand one
2542
- return value.replace(/\B(?=(\d{3})+(?!\d))/g, inThousandSeparator);
2476
+ breakpoint = state.breakpoint;
2477
+ breakpoints = state.breakpoints;
2543
2478
  }
2544
- // do the replacement of separators and parseInt
2545
- // Beware! Always replace thousand separator first and then decimal
2546
- value = value.split(thousandsSeparator).join('').replace(decimalSeparator, '.');
2547
- }
2548
- decimalSeparator = inDecimalSeparator;
2549
- thousandsSeparator = inThousandSeparator;
2550
- const maxFraction = '0'.repeat(fractionSize);
2551
- let [integer, fraction = maxFraction] = (value || '0').toString().split('.');
2552
- fraction = fractionSize > 0 ? (fraction + maxFraction).substring(0, fractionSize) : '';
2553
- // this rule makes sense only in number in case fraction is all zero
2554
- fraction = typeof value === 'number' && fraction === maxFraction ? '' : decimalSeparator + fraction;
2555
- return integer.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator) + fraction;
2556
- };
2557
-
2558
- const handleError = (error) => {
2559
- let errMsg = error.message ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';
2560
- // console.error(errMsg);
2561
- return throwError(errMsg);
2562
- };
2563
-
2564
- /**
2565
- * check if a value is null or undefined. If it's not both returns true otherwise false
2566
- *
2567
- * @param value
2568
- */
2569
- const isDefined = (value) => value !== undefined && value !== null;
2570
-
2571
- class EuiPermissionService extends EuiService {
2572
- constructor(log, store) {
2573
- super([]);
2574
- this.log = log;
2575
- this.store = store;
2576
- if (log) {
2577
- this.logger = log.getLogger('core.PermissionService');
2479
+ // finally get the wrapper classes when both the state and breakpoint are known
2480
+ wrapperClasses = this.getWrapperClasses(nextState, breakpoint);
2481
+ // check if the menuLinks or sidebarLinks have changed from previous state
2482
+ if (this.state.menuLinks !== nextState.menuLinks || this.state.sidebarLinks !== nextState.sidebarLinks) {
2483
+ combinedLinks = [...nextState.menuLinks, ...nextState.sidebarLinks];
2578
2484
  }
2579
- }
2580
- /**
2581
- * initialize the service by providing a state
2582
- * @param rights
2583
- */
2584
- init(rights) {
2585
- super.initEuiService();
2586
- if (Array.isArray(rights)) {
2587
- this.updateState(rights);
2588
- return of({ success: true });
2485
+ else {
2486
+ combinedLinks = this.state.combinedLinks;
2589
2487
  }
2590
- if (this.logger) {
2591
- this.logger.error('Init object should be instance of BaseUserState');
2488
+ const stateBeforeUpdate = { ...this.state };
2489
+ // we put it all together with the calculated properties
2490
+ this._state$.next({
2491
+ ...nextState,
2492
+ wrapperClasses,
2493
+ breakpoint,
2494
+ breakpoints,
2495
+ combinedLinks,
2496
+ });
2497
+ // update the Store Language
2498
+ if (updateI18 && nextState.activeLanguage !== stateBeforeUpdate.activeLanguage) {
2499
+ this.i18nService.updateState({ activeLang: nextState.activeLanguage });
2592
2500
  }
2593
- return of({ success: false, error: 'Init object should be instance of BaseUserState' });
2594
- }
2595
- get userRights() {
2596
- return this.stateInstance;
2597
2501
  }
2598
2502
  /**
2599
- * get the user's Rights from the state
2503
+ * Emits a slice from the state whether that changes
2504
+ *
2505
+ * @param key can be 'key' or 'key.sub.sub'
2600
2506
  */
2601
- getState() {
2602
- return this.store.select(getUserRights);
2507
+ getState(key) {
2508
+ return defer(() =>
2509
+ // check if key exists
2510
+ key
2511
+ ? this.state$.pipe(map((state) => get(state, key)),
2512
+ // filter((state) => state),
2513
+ distinctUntilChanged((x, y) => isEqual(x, y)))
2514
+ : this.state$);
2603
2515
  }
2604
- /**
2605
- * update the state with the given user's rights
2606
- * @param rights
2607
- */
2608
- updateState(rights) {
2609
- this.store.dispatch(new UpdateUserRightsAction(rights || []));
2516
+ sidebarToggle() {
2517
+ this.isSidebarOpen = !this.state.isSidebarOpen;
2610
2518
  }
2611
- /**
2612
- * accepts a string of right and or a permission. In string first comes the right and or followed by permission in
2613
- * a dot separated way e.g. 'RIGHT.PERMISSION' or 'RIGHT'. The string can contain more that 2 separated fields.
2614
- * In that case the last field should be the permission and all the others a Right. e.g. 'RIGHT1.RIGHT2.PERMISSION'
2615
- * The last part is useful where you want a user to have multiple rights and the permission of the last right.
2616
- * @param rightsAndPermission
2617
- */
2618
- checkAttributePermission(rightsAndPermission) {
2619
- const permProps = rightsAndPermission.split('.');
2620
- const rightId = permProps[0];
2621
- const hasRight = this.checkRight(rightId);
2622
- if (hasRight) {
2623
- if (permProps.length === 2) {
2624
- // in case of 'RIGHT.PERMISSION'
2625
- const permission = permProps[1];
2626
- return this.checkPermission(rightId, permission);
2627
- }
2628
- else if (permProps.length > 2) {
2629
- // in case of 'RIGHT1.RIGHT2.....PERMISSION' string call recursively
2630
- return this.checkAttributePermission(rightsAndPermission.slice(rightId.length + 1));
2631
- }
2632
- }
2633
- return hasRight;
2519
+ // Edit mode
2520
+ dimmerActiveToggle() {
2521
+ const isActive = this.isDimmerActive;
2522
+ this.setState({
2523
+ ...this.state,
2524
+ isDimmerActive: !isActive,
2525
+ });
2526
+ this.activateEditModeCssVars(!isActive);
2634
2527
  }
2635
- /**
2636
- * check if a user has the right.
2637
- * @param rightId the id of Right
2638
- */
2639
- checkRight(rightId) {
2640
- return this.userRights.some((right) => right && right.id === rightId);
2528
+ setDimmerActiveState(activeState) {
2529
+ this.setState({
2530
+ ...this.state,
2531
+ isDimmerActive: activeState,
2532
+ });
2533
+ this.activateEditModeCssVars(activeState);
2641
2534
  }
2642
- /**
2643
- * check if a user has that right and that right has the permission given.
2644
- * @param rightId the right of the user
2645
- * @param permission the permission of the right
2646
- */
2647
- checkPermission(rightId, permission) {
2648
- // the user has to have that right and that right has to have the given permission
2649
- return this.userRights.some((right) => right && right.id === rightId && right.permissions && right.permissions.includes(permission));
2535
+ activateEditModeCssVars(isActive) {
2536
+ if (isActive) {
2537
+ this._setCssVarValue('--eui-docpage-navigation-z-index', 'inherit'); // Reset doc-page-navigation-column z-index
2538
+ this._setCssVarValue('--eui-z-index-sidebar', 'inherit'); // Reset eui-app-sidebar z-index
2539
+ this._setCssVarValue('--eui-z-index-root', 'inherit'); // Reset eui-app-main-content z-index
2540
+ }
2541
+ else {
2542
+ this._setCssVarValue('--eui-docpage-navigation-z-index', '2'); // Restore doc-page-navigation-column z-index
2543
+ this._setCssVarValue('--eui-z-index-sidebar', '1044'); // Restore eui-app-sidebar z-index
2544
+ this._setCssVarValue('--eui-z-index-root', '1'); // Restore eui-app-main-content z-index
2545
+ }
2650
2546
  }
2651
- /**
2652
- * checks whether a user have all rights passed as an argument
2653
- * @param rights an array of EuiUserRight
2654
- */
2655
- haveAllRights(rights) {
2656
- if (!isDefined(rights)) {
2657
- throw Error(`The rights should be an array but instead it's not defined!`);
2547
+ // --------------
2548
+ // public methods
2549
+ // --------------
2550
+ fetchAppMetadata(metadataFilePath = 'assets/app-metadata.json') {
2551
+ this.getJson(metadataFilePath).then((data) => {
2552
+ this.setState({
2553
+ ...this.state,
2554
+ appMetadata: data,
2555
+ });
2556
+ });
2557
+ }
2558
+ activateSidebar() {
2559
+ this.setState({
2560
+ ...this.state,
2561
+ hasSidebar: true,
2562
+ });
2563
+ if (!this.state.isSidebarHidden) {
2564
+ this.activateSidebarCssVars();
2658
2565
  }
2659
- return rights.every((right) => this.userRights.find((uRight) => uRight.id === right.id));
2660
2566
  }
2661
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiPermissionService, deps: [{ token: LogService, optional: true }, { token: StoreService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2662
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiPermissionService, providedIn: 'root' }); }
2663
- }
2664
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiPermissionService, decorators: [{
2665
- type: Injectable,
2666
- args: [{
2667
- providedIn: 'root',
2668
- }]
2669
- }], ctorParameters: function () { return [{ type: LogService, decorators: [{
2670
- type: Optional
2671
- }] }, { type: StoreService }]; } });
2672
-
2673
- class EuiDynamicMenuService {
2674
- // TODO: it should be moved euiservice, or it should be static
2675
- // TODO: can we moved that service inside the module of the component that's using it?
2676
- constructor(uxPermission, store) {
2677
- this.uxPermission = uxPermission;
2678
- this.store = store;
2679
- this.menuLinks = [];
2680
- this.store.select(getUserState).subscribe((user) => {
2681
- if (user.menuLinks) {
2682
- this.menuLinks = user.menuLinks;
2683
- }
2567
+ activateSidebarHeader() {
2568
+ this.activateSidebarHeaderCssVars();
2569
+ }
2570
+ activateSidebarFooter() {
2571
+ this.activateSidebarFooterCssVars();
2572
+ }
2573
+ activateHeader() {
2574
+ this.setState({
2575
+ ...this.state,
2576
+ hasHeader: true,
2684
2577
  });
2578
+ this.activateHeaderCssVars();
2685
2579
  }
2686
- getMenuLinks() {
2687
- return this.menuLinks;
2580
+ activateBreadcrumb() {
2581
+ this.setState({
2582
+ ...this.state,
2583
+ hasBreadcrumb: true,
2584
+ });
2585
+ this.activateBreadcrumbCssVars();
2688
2586
  }
2689
- filterEuiMenuItemsWithRights(links) {
2690
- return links.filter((link) => {
2691
- if (link.hasChildren) {
2692
- link.children = this.filterEuiMenuItemsWithRights(link.children);
2693
- }
2694
- // First check if the user is denied access:
2695
- if (link.deniedRightId) {
2696
- if (this.uxPermission.checkRight(link.deniedRightId)) {
2697
- return false;
2698
- }
2699
- }
2700
- if (link.deniedRightIds) {
2701
- for (const deniedRightId of link.deniedRightIds) {
2702
- if (this.uxPermission.checkRight(deniedRightId)) {
2703
- return false;
2704
- }
2705
- }
2706
- }
2707
- // Then check if the user needs to be allowed access explicitly:
2708
- if (link.allowedRightId) {
2709
- return this.uxPermission.checkRight(link.allowedRightId);
2710
- }
2711
- if (link.allowedRightIds) {
2712
- for (const allowedRightId of link.allowedRightIds) {
2713
- if (this.uxPermission.checkRight(allowedRightId)) {
2714
- return true;
2715
- }
2716
- }
2717
- // User doesn't have one of the allowed rights. Deny access:
2718
- return false;
2719
- }
2720
- // Access to the menu item is not denied, therefore allow it:
2721
- return true;
2587
+ activateTopMessage(height) {
2588
+ this.setState({
2589
+ ...this.state,
2590
+ hasTopMessage: true,
2722
2591
  });
2592
+ this.activateTopMessageCssVars(height);
2723
2593
  }
2724
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicMenuService, deps: [{ token: EuiPermissionService }, { token: StoreService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2725
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicMenuService, providedIn: 'root' }); }
2726
- }
2727
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiDynamicMenuService, decorators: [{
2728
- type: Injectable,
2729
- args: [{
2730
- providedIn: 'root',
2731
- }]
2732
- }], ctorParameters: function () { return [{ type: EuiPermissionService }, { type: StoreService }]; } });
2733
-
2734
- function withUxErrorManager(errorManager) {
2735
- return (source$) => new Observable((observer) => {
2736
- const wrapper = {
2737
- next: (value) => {
2738
- errorManager.clearError();
2739
- observer.next(value);
2740
- },
2741
- error: (err) => {
2742
- errorManager.publishError(err);
2743
- },
2744
- };
2745
- return source$.subscribe(wrapper);
2746
- });
2747
- }
2748
-
2749
- class UxErrorFeedbackService {
2750
- constructor(asService, translateService) {
2751
- this.asService = asService;
2752
- this.translateService = translateService;
2753
- this.errors = new Subject();
2594
+ activateToolbar() {
2595
+ this.setState({
2596
+ ...this.state,
2597
+ hasToolbar: true,
2598
+ });
2599
+ this.activateToolbarCssVars();
2600
+ }
2601
+ activateToolbarMenu() {
2602
+ this.setState({
2603
+ ...this.state,
2604
+ hasToolbarMenu: true,
2605
+ });
2606
+ }
2607
+ fetchCssVars() {
2608
+ const cssVarNames = [
2609
+ '--eui-app-header-height-default',
2610
+ '--eui-app-header-height-active',
2611
+ '--eui-app-header-height-shrink',
2612
+ '--eui-app-breadcrumb-height-default',
2613
+ '--eui-app-breadcrumb-height-active',
2614
+ '--eui-app-top-message-height-default',
2615
+ '--eui-app-top-message-height-active',
2616
+ '--eui-app-top-message-height-shrink',
2617
+ '--eui-app-toolbar-height-default',
2618
+ '--eui-app-toolbar-height-active',
2619
+ '--eui-app-sidebar-width-default',
2620
+ '--eui-app-sidebar-width-active',
2621
+ '--eui-app-sidebar-width-close-default',
2622
+ '--eui-app-sidebar-width-close-active',
2623
+ '--eui-app-sidebar-header-height-default',
2624
+ '--eui-app-sidebar-header-height-active',
2625
+ '--eui-app-sidebar-footer-height-default',
2626
+ '--eui-app-sidebar-footer-height-active',
2627
+ ];
2628
+ this.cssVars = cssVarNames.map((v) => ({ key: v, value: this.getCssVarValue(v) }));
2629
+ }
2630
+ initCssVars() {
2631
+ this.setCssVar('--eui-app-header-height-default', '--eui-app-header-height');
2632
+ this.setCssVar('--eui-app-breadcrumb-height-default', '--eui-app-breadcrumb-height');
2633
+ this.setCssVar('--eui-app-top-message-height-default', '--eui-app-top-message-height');
2634
+ this.setCssVar('--eui-app-toolbar-height-default', '--eui-app-toolbar-height');
2635
+ this.setCssVar('--eui-app-sidebar-width-default', '--eui-app-sidebar-width');
2636
+ this.setCssVar('--eui-app-sidebar-width-close-default', '--eui-app-sidebar-width-close');
2637
+ this.setCssVar('--eui-app-sidebar-header-height-default', '--eui-app-sidebar-header-height');
2638
+ this.setCssVar('--eui-app-sidebar-footer-height-default', '--eui-app-sidebar-footer-height');
2754
2639
  }
2755
- /**
2756
- * Publishes errors to targeted components
2757
- */
2758
- publishError(err, instanceId, groupId, accumulate) {
2759
- if (instanceId) {
2760
- this.errors.next(new UxPublishErrorFeedbackEvent(err.uxHttpErrorOutput, instanceId, groupId, accumulate));
2761
- }
2762
- else {
2763
- this.errors.next(new UxPublishErrorFeedbackEvent(err.uxHttpErrorOutput, undefined, groupId, accumulate));
2764
- }
2640
+ activateHeaderCssVars() {
2641
+ this.setCssVar('--eui-app-header-height-active', '--eui-app-header-height');
2765
2642
  }
2766
- /**
2767
- * Growl errors
2768
- */
2769
- growlError(err) {
2770
- // TODO v17 - should not display growl from here, but where it's called
2771
- // const feedBacks = err.uxHttpErrorOutput.getFeedBacks();
2772
- // const isGrowlSticky = false;
2773
- // const isGrowlMultiple = true;
2774
- // const growlLife = 10000;
2775
- // const position = 'bottom-right';
2776
- // feedBacks.forEach((feedback: UxErrorFeedback) =>
2777
- // this.translateService
2778
- // .get(feedback.msgId)
2779
- // .pipe(take(1))
2780
- // .subscribe((translate) => {
2781
- // this.asService.growl(
2782
- // {
2783
- // severity: feedback.severity || 'danger',
2784
- // summary: 'ERROR',
2785
- // detail: translate,
2786
- // },
2787
- // isGrowlSticky,
2788
- // isGrowlMultiple,
2789
- // growlLife,
2790
- // position
2791
- // );
2792
- // })
2793
- // );
2643
+ activateBreadcrumbCssVars() {
2644
+ this.setCssVar('--eui-app-breadcrumb-height-active', '--eui-app-breadcrumb-height');
2794
2645
  }
2795
- clearError(id, groupId) {
2796
- this.errors.next(new UxClearErrorFeedbackEvent(id, groupId));
2646
+ activateTopMessageCssVars(height) {
2647
+ this._setCssVarValue('--eui-app-top-message-height', `${height}px`);
2797
2648
  }
2798
- createLocalErrorManager() {
2799
- return new UxErrorManager(this);
2649
+ activateToolbarCssVars() {
2650
+ this.setCssVar('--eui-app-toolbar-height-active', '--eui-app-toolbar-height');
2800
2651
  }
2801
- createGlobalErrorManager() {
2802
- return new UxErrorManager(this, true);
2652
+ activateFooterCssVars() {
2653
+ this.setCssVar('--eui-app-footer-height-active', '--eui-app-footer-height');
2803
2654
  }
2804
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxErrorFeedbackService, deps: [{ token: UxAppShellService }, { token: i2.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2805
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxErrorFeedbackService, providedIn: 'root' }); }
2806
- }
2807
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxErrorFeedbackService, decorators: [{
2808
- type: Injectable,
2809
- args: [{
2810
- providedIn: 'root',
2811
- }]
2812
- }], ctorParameters: function () { return [{ type: UxAppShellService }, { type: i2.TranslateService }]; } });
2813
- class UxErrorManager {
2814
- constructor(uxErrorFeedbackService, isGlobal) {
2815
- this.uxErrorFeedbackService = uxErrorFeedbackService;
2816
- this.isGlobal = isGlobal;
2817
- this.httpErrorOperator = withUxErrorManager(this);
2818
- this.id = Math.random().toString();
2819
- }
2820
- publishError(err, groupId, accumulate) {
2821
- if (!this.isGlobal) {
2822
- this.uxErrorFeedbackService.publishError(err, this.id, groupId, accumulate);
2655
+ setHeaderShrinkCssVar(active) {
2656
+ if (active) {
2657
+ this.setCssVar('--eui-app-header-height-shrink', '--eui-app-header-height');
2823
2658
  }
2824
2659
  else {
2825
- this.uxErrorFeedbackService.publishError(err, undefined, groupId, accumulate);
2660
+ this.setCssVar('--eui-app-header-height-active', '--eui-app-header-height');
2826
2661
  }
2827
2662
  }
2828
- clearError(groupId) {
2829
- if (!this.isGlobal) {
2830
- this.uxErrorFeedbackService.clearError(this.id, groupId);
2831
- }
2832
- else {
2833
- this.uxErrorFeedbackService.clearError(undefined, groupId);
2834
- }
2663
+ activateSidebarCssVars() {
2664
+ this.setCssVar('--eui-app-sidebar-width-active', '--eui-app-sidebar-width');
2665
+ this.setCssVar('--eui-app-sidebar-width-close-active', '--eui-app-sidebar-width-close');
2835
2666
  }
2836
- growlError(err) {
2837
- this.uxErrorFeedbackService.growlError(err);
2667
+ removeSidebarCssVars() {
2668
+ document.documentElement.style.removeProperty('--eui-app-sidebar-header-height');
2669
+ document.documentElement.style.removeProperty('--eui-app-sidebar-footer-height');
2670
+ document.documentElement.style.removeProperty('--eui-app-sidebar-width');
2671
+ document.documentElement.style.removeProperty('--eui-app-sidebar-width-close');
2838
2672
  }
2839
- errors() {
2840
- if (!this.isGlobal) {
2841
- return this.uxErrorFeedbackService.errors.pipe(filter((evt) => evt.id === this.id));
2842
- }
2843
- else {
2844
- return this.uxErrorFeedbackService.errors.pipe(filter((evt) => !evt.id));
2845
- }
2673
+ activateSidebarHeaderCssVars() {
2674
+ this.setCssVar('--eui-app-sidebar-header-height-active', '--eui-app-sidebar-header-height');
2846
2675
  }
2847
- }
2848
-
2849
- const DYNAMIC_COMPONENT_CONFIG = new InjectionToken('DYNAMIC_COMPONENT_CONFIG');
2850
- class UxDynamicComponentService {
2851
- constructor(componentFactoryResolver, injector, appRef) {
2852
- this.componentFactoryResolver = componentFactoryResolver;
2853
- this.injector = injector;
2854
- this.appRef = appRef;
2676
+ activateSidebarFooterCssVars() {
2677
+ this.setCssVar('--eui-app-sidebar-footer-height-active', '--eui-app-sidebar-footer-height');
2855
2678
  }
2856
2679
  /**
2857
- * Adds a component to portal host dynamically
2858
- * @param component The component to be injected
2859
- * @param el The element reference for the component to be injected
2860
- * @param config The data to be passed (config) for the DYNAMIC_COMPONENT_CONFIG token
2680
+ * Gets the viewport height and width and multiple it by 1% to get a value for a vh/vw unit
2681
+ * Sets the value in the --eui-app-vh custom property to the root of the document
2682
+ * Sets the value in the --eui-app-vw custom property to the root of the document
2683
+ * Example for 100% height: calc(var(--eui-app-vh, 1vh) * 100);
2684
+ * Example for 50% height: calc(var(--eui-app-vh, 1vh) * 50);
2685
+ * Example for 50% width: calc(var(--eui-app-vw, 1vw) * 50);
2861
2686
  */
2862
- add(component, el, config) {
2863
- // TODO: take a look and replace deprecated componentFactoryResolver https://github.com/angular/components/issues/24334
2864
- // Create a portalHost from a DOM element
2865
- this.portalHost = new DomPortalOutlet(el ? el.nativeElement : document.body, this.componentFactoryResolver, this.appRef, this.injector);
2866
- // Locate the component factory for the ComponentToIncludeDynamicallyComponent
2867
- this.portal = new ComponentPortal(component, null, config ? this.createInjector(config) : null);
2868
- // Attach portal to host
2869
- this.portalComponentInstance = this.portalHost.attach(this.portal);
2870
- if (config) {
2871
- Object.assign(config, {
2872
- portalHostRef: this.portalHost,
2873
- portalRef: this.portal,
2874
- portalComponentInstanceRef: this.portalComponentInstance,
2875
- });
2876
- }
2877
- return {
2878
- portalHost: this.portalHost,
2879
- portal: this.portal,
2880
- portalComponentInstance: this.portalComponentInstance,
2881
- };
2687
+ setAppViewportCssVars() {
2688
+ let vh = window.innerHeight * 0.01;
2689
+ let vw = window.innerWidth * 0.01;
2690
+ this._setCssVarValue('--eui-app-vh', `${vh}px`);
2691
+ this._setCssVarValue('--eui-app-vw', `${vw}px`);
2882
2692
  }
2883
2693
  /**
2884
- * detaches a portalHost
2885
- * @param portalHostRef reference of portal host to be detached
2694
+ * Returns the current value of --eui-base-font-size CSS variable
2886
2695
  */
2887
- remove(portalHostRef) {
2888
- portalHostRef.detach();
2889
- this.portalHost = null;
2890
- this.portal = null;
2891
- this.portalComponentInstance = null;
2696
+ getBaseFontSize() {
2697
+ return this.state.appBaseFontSize || this.getCssVarValue('--eui-base-font-size');
2892
2698
  }
2893
2699
  /**
2894
- * creates a custom injector to be used when providing custom injection tokens to components inside a portal
2895
- * @param data to be used by DYNAMIC_COMPONENT_CONFIG token
2700
+ * Updates the current value of --eui-base-font-size CSS variable and the UIState appBaseFontSize
2896
2701
  */
2897
- createInjector(data) {
2898
- const injectorTokens = new WeakMap();
2899
- injectorTokens.set(DYNAMIC_COMPONENT_CONFIG, data);
2900
- return new PortalInjector(this.injector, injectorTokens);
2702
+ setBaseFontSize(newsize) {
2703
+ this.setState({
2704
+ ...this.state,
2705
+ appBaseFontSize: newsize,
2706
+ }, false);
2707
+ this._setCssVarValue('--eui-base-font-size', newsize);
2708
+ }
2709
+ // ---------------
2710
+ // private getters
2711
+ // ---------------
2712
+ getCssVarValue(inputProperty) {
2713
+ return getComputedStyle(document.documentElement).getPropertyValue(inputProperty);
2714
+ }
2715
+ setCssVar(inputProperty, outputProperty) {
2716
+ const cssVarValue = this.cssVars.filter((v) => v.key === inputProperty)[0].value;
2717
+ this._setCssVarValue(outputProperty, cssVarValue);
2718
+ // console.log(inputProperty, outputProperty, cssVarValue);
2719
+ }
2720
+ _setCssVarValue(varName, varValue) {
2721
+ document.documentElement.style.setProperty(varName, varValue);
2722
+ }
2723
+ getWrapperClasses(state, breakpoint) {
2724
+ let classes = [];
2725
+ classes.push(breakpoint);
2726
+ if (state.hasSidebar) {
2727
+ if (state.isSidebarOpen && (state.isSidebarActive || state.isSidebarInnerActive)) {
2728
+ classes.push('sidebar-state-open');
2729
+ }
2730
+ if (!state.isSidebarOpen && (state.isSidebarActive || state.isSidebarInnerActive)) {
2731
+ classes.push('sidebar-state-close');
2732
+ }
2733
+ if (state.isSidebarActive) {
2734
+ classes.push('sidebar-active');
2735
+ }
2736
+ if (state.isSidebarInnerActive) {
2737
+ classes.push('sidebar-inner-active');
2738
+ }
2739
+ if (state.isSidebarStateCloseWithIcons) {
2740
+ classes.push('sidebar-state-close-with-icons');
2741
+ }
2742
+ // NEW APP-SHELL props
2743
+ if (state.isSidebarHidden) {
2744
+ classes.push('sidebar--hidden');
2745
+ }
2746
+ if (state.isSidebarOpen) {
2747
+ classes.push('sidebar--open');
2748
+ }
2749
+ else {
2750
+ classes.push('sidebar--close');
2751
+ }
2752
+ if (state.isSidebarHover) {
2753
+ classes.push('sidebar--hover');
2754
+ }
2755
+ }
2756
+ if (state.deviceInfo && state.deviceInfo.isFF) {
2757
+ classes.push('ff');
2758
+ }
2759
+ if (state.deviceInfo && state.deviceInfo.isIE) {
2760
+ classes.push('ie');
2761
+ }
2762
+ if (state.deviceInfo && state.deviceInfo.isIE11) {
2763
+ classes.push('ie11');
2764
+ }
2765
+ if (state.deviceInfo && state.deviceInfo.isChrome) {
2766
+ classes.push('chrome');
2767
+ }
2768
+ return classes.join(' ');
2769
+ }
2770
+ getBreakpoint(windowWidth) {
2771
+ let bkp = '';
2772
+ switch (true) {
2773
+ case windowWidth >= 0 && windowWidth < 576:
2774
+ bkp = 'xs';
2775
+ break;
2776
+ case windowWidth >= 576 && windowWidth < 768:
2777
+ bkp = 'sm';
2778
+ break;
2779
+ case windowWidth >= 768 && windowWidth < 992:
2780
+ bkp = 'md';
2781
+ break;
2782
+ case windowWidth >= 992 && windowWidth < 1200:
2783
+ bkp = 'lg';
2784
+ break;
2785
+ case windowWidth >= 1200 && windowWidth < 1400:
2786
+ bkp = 'xl';
2787
+ break;
2788
+ case windowWidth >= 1400 && windowWidth < 1920:
2789
+ bkp = 'xxl';
2790
+ break;
2791
+ case windowWidth >= 1920 && windowWidth < 2560:
2792
+ bkp = 'fhd';
2793
+ break;
2794
+ case windowWidth >= 2560 && windowWidth < 3840:
2795
+ bkp = '2k';
2796
+ break;
2797
+ case windowWidth >= 3840:
2798
+ bkp = '4k';
2799
+ break;
2800
+ }
2801
+ return bkp;
2802
+ }
2803
+ getBreakpoints(bkp) {
2804
+ return {
2805
+ isMobile: bkp === 'xs' || bkp === 'sm',
2806
+ isTablet: bkp === 'md',
2807
+ isLtDesktop: bkp === 'xs' || bkp === 'sm' || bkp === 'md',
2808
+ isDesktop: bkp === 'lg',
2809
+ isXL: bkp === 'xl',
2810
+ isXXL: bkp === 'xxl',
2811
+ isFHD: bkp === 'fhd',
2812
+ is2K: bkp === '2k',
2813
+ is4K: bkp === '4k',
2814
+ };
2815
+ }
2816
+ getJson(url) {
2817
+ return this.http.get(url).toPromise().then(this.extractData).catch(this.handleError);
2818
+ }
2819
+ extractData(res) {
2820
+ const body = res;
2821
+ return body || {};
2822
+ }
2823
+ handleError(error) {
2824
+ console.error('An error occurred', error);
2825
+ return Promise.reject(error.message || error);
2826
+ }
2827
+ bindActiveLanguageToAppShellState() {
2828
+ this.storeService.select(getActiveLang).subscribe((activeLang) => {
2829
+ if (activeLang !== this.state.activeLanguage) {
2830
+ this.setState({
2831
+ ...this.state,
2832
+ activeLanguage: activeLang,
2833
+ }, false);
2834
+ }
2835
+ });
2901
2836
  }
2902
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxDynamicComponentService, deps: [{ token: i0.ComponentFactoryResolver }, { token: i0.Injector }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Injectable }); }
2903
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxDynamicComponentService }); }
2837
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiAppShellService, deps: [{ token: GLOBAL_CONFIG_TOKEN, optional: true }, { token: i1$5.HttpClient }, { token: PLATFORM_ID }, { token: i1$4.Router }, { token: StoreService }, { token: I18nService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
2838
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiAppShellService, providedIn: 'root' }); }
2904
2839
  }
2905
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: UxDynamicComponentService, decorators: [{
2906
- type: Injectable
2907
- }], ctorParameters: function () { return [{ type: i0.ComponentFactoryResolver }, { type: i0.Injector }, { type: i0.ApplicationRef }]; } });
2840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: EuiAppShellService, decorators: [{
2841
+ type: Injectable,
2842
+ args: [{
2843
+ providedIn: 'root',
2844
+ }]
2845
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
2846
+ type: Optional
2847
+ }, {
2848
+ type: Inject,
2849
+ args: [GLOBAL_CONFIG_TOKEN]
2850
+ }] }, { type: i1$5.HttpClient }, { type: undefined, decorators: [{
2851
+ type: Inject,
2852
+ args: [PLATFORM_ID]
2853
+ }] }, { type: i1$4.Router }, { type: StoreService }, { type: I18nService, decorators: [{
2854
+ type: Optional
2855
+ }] }]; } });
2908
2856
 
2909
2857
  /* eslint-disable */
2910
2858
  const EUI_COUNTRIES = {
@@ -4268,12 +4216,12 @@ class AddLangParamInterceptor {
4268
4216
  params: req.params.set(langParam, langValue),
4269
4217
  }));
4270
4218
  }
4271
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: AddLangParamInterceptor, deps: [{ token: i2.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4219
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: AddLangParamInterceptor, deps: [{ token: i1$3.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4272
4220
  /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: AddLangParamInterceptor }); }
4273
4221
  }
4274
4222
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.0-next.4", ngImport: i0, type: AddLangParamInterceptor, decorators: [{
4275
4223
  type: Injectable
4276
- }], ctorParameters: function () { return [{ type: i2.TranslateService }]; } });
4224
+ }], ctorParameters: function () { return [{ type: i1$3.TranslateService }]; } });
4277
4225
 
4278
4226
  class CachePreventionInterceptor {
4279
4227
  intercept(req, next) {
@@ -4627,5 +4575,5 @@ class OpenIdConnectServiceMock extends OpenIdConnectService {
4627
4575
  * Generated bundle index. Do not edit.
4628
4576
  */
4629
4577
 
4630
- export { ActivatedRouteAction, AddApiQueueItemAction, AddAppLoadedConfigModulesAction, AddLangParamInterceptor, AlertHttpErrorCallbackFn, ApiQueueService, ApiQueueServiceMock, AsyncStorageService, AsyncStorageServiceMock, BASE_LOGGER_NAME_TOKEN, BASE_NAME_TOKEN, BrowserStorageType, CONFIG_TOKEN, CORE_ROOT_GUARD, CachePreventionInterceptor, ConsoleHttpErrorCallbackFn, CoreAppActionTypes, CoreAppEffects, CoreI18nActionTypes, CoreLocaleActionTypes, CoreModule, CoreModuleEffects, CoreNotificationsActionTypes, CoreUserActionTypes, CorsSecurityInterceptor, CsrfPreventionInterceptor, DYNAMIC_COMPONENT_CONFIG, EUI_CONFIG_TOKEN, EUI_COUNTRIES, EUI_TIMEZONES, EmptyApiQueueAction, ErrorSubClass, EuLoginSessionTimeoutHandlingInterceptor, EuiCoreRootGuardClass, EuiDynamicMenuService, EuiError, EuiPermissionService, EuiTimezoneService, GLOBAL_CONFIG_TOKEN, GlobalErrorHandler, GrowlHttpErrorCallbackFn, HTTP_ERROR_HANDLER_CONFIG_TOKEN, HttpErrorHandlerInterceptor, I18nLoader, I18nResourceImpl, I18nService, I18nServiceMock, InitStoreAction, LANG_PARAM_KEY, LOCALE_ID_MAPPER, LOCAL_FORAGE_SERVICE_CONFIG_TOKEN, LOG_APPENDERS_TOKEN, LOG_LEVEL_TOKEN, LocalForageService, LocalStorageService, LocaleService, LocaleServiceMock, LogHttpErrorCallbackFn, LogModule, LogService, LogServiceMock, MODULE_CONFIG_TOKEN, MODULE_NAME_TOKEN, OpenIdConnectInterceptor, OpenIdConnectService, OpenIdConnectServiceMock, ROOT_LOG_CONFIG_TOKEN, RemoveApiQueueItemAction, SHOW_CONNECTION_STATUS_TOKEN, SessionStorageService, StorageService, StorageServiceMock, StoreService, StoreServiceMock, UX_ERROR_MAPPING_HANDLER_TOKEN, UpdateAppConnectionAction, UpdateAppStatusAction, UpdateAppVersionAction, UpdateCurrentModuleAction, UpdateI18nStateAction, UpdateLocaleStateAction, UpdateNotificationsListAction, UpdateUserDashboardAction, UpdateUserDetailsAction, UpdateUserPreferencesAction, UpdateUserRightsAction, UpdateUserStateAction, UserService, UserServiceMock, UxAppShellService, UxDynamicComponentService, UxErrorFeedbackService, UxErrorManager, UxRequestErrorModelInterceptor, closestMatchingParent, consumeEvent, corI18nReducers, coreAppReducers, coreLocaleReducers, coreNotificationsReducers, coreUserReducers, createEuiCoreRootGuard, diffDays, diffDaysFromToday, errorCodes, euiCoreRootGuardClass, formatNumber, getGlobalConfig, getHttpErrorHandlingConfig, getModuleConfig, getRootLogConfig, getShowConnectionStatus, getViewElement, handleError, isDefined, loadEuiDynamicEnvironmentConfig, loadState, localForage, localStorageSync, markControlsTouched, markFormGroupTouched, mergeAppHandlerConfigToAppConfig, mergeAppJsonConfigToAppConfig, offset, position, preInitApp, prepareEuiAppConfigToken, coreReducers as reducers, sessionStorageSync, translateConfig, uniqueId, validateEmail, withUxErrorManager };
4578
+ export { ActivatedRouteAction, AddApiQueueItemAction, AddAppLoadedConfigModulesAction, AddLangParamInterceptor, AlertHttpErrorCallbackFn, ApiQueueService, ApiQueueServiceMock, AsyncStorageService, AsyncStorageServiceMock, BASE_LOGGER_NAME_TOKEN, BASE_NAME_TOKEN, BrowserStorageType, CONFIG_TOKEN, CORE_ROOT_GUARD, CachePreventionInterceptor, ConsoleHttpErrorCallbackFn, CoreAppActionTypes, CoreAppEffects, CoreI18nActionTypes, CoreLocaleActionTypes, CoreModule, CoreModuleEffects, CoreNotificationsActionTypes, CoreUserActionTypes, CorsSecurityInterceptor, CsrfPreventionInterceptor, DYNAMIC_COMPONENT_CONFIG, EUI_CONFIG_TOKEN, EUI_COUNTRIES, EUI_TIMEZONES, EmptyApiQueueAction, ErrorSubClass, EuLoginSessionTimeoutHandlingInterceptor, EuiAppShellService, EuiCoreRootGuardClass, EuiDynamicComponentService, EuiDynamicMenuService, EuiError, EuiGrowlService, EuiPermissionService, EuiTimezoneService, GLOBAL_CONFIG_TOKEN, GlobalErrorHandler, GrowlHttpErrorCallbackFn, HTTP_ERROR_HANDLER_CONFIG_TOKEN, HttpErrorHandlerInterceptor, I18nLoader, I18nResourceImpl, I18nService, I18nServiceMock, InitStoreAction, LANG_PARAM_KEY, LOCALE_ID_MAPPER, LOCAL_FORAGE_SERVICE_CONFIG_TOKEN, LOG_APPENDERS_TOKEN, LOG_LEVEL_TOKEN, LocalForageService, LocalStorageService, LocaleService, LocaleServiceMock, LogHttpErrorCallbackFn, LogModule, LogService, LogServiceMock, MODULE_CONFIG_TOKEN, MODULE_NAME_TOKEN, OpenIdConnectInterceptor, OpenIdConnectService, OpenIdConnectServiceMock, ROOT_LOG_CONFIG_TOKEN, RemoveApiQueueItemAction, SHOW_CONNECTION_STATUS_TOKEN, SessionStorageService, StorageService, StorageServiceMock, StoreService, StoreServiceMock, UX_ERROR_MAPPING_HANDLER_TOKEN, UpdateAppConnectionAction, UpdateAppStatusAction, UpdateAppVersionAction, UpdateCurrentModuleAction, UpdateI18nStateAction, UpdateLocaleStateAction, UpdateNotificationsListAction, UpdateUserDashboardAction, UpdateUserDetailsAction, UpdateUserPreferencesAction, UpdateUserRightsAction, UpdateUserStateAction, UserService, UserServiceMock, UxRequestErrorModelInterceptor, closestMatchingParent, consumeEvent, corI18nReducers, coreAppReducers, coreLocaleReducers, coreNotificationsReducers, coreUserReducers, createEuiCoreRootGuard, diffDays, diffDaysFromToday, errorCodes, euiCoreRootGuardClass, formatNumber, getGlobalConfig, getHttpErrorHandlingConfig, getModuleConfig, getRootLogConfig, getShowConnectionStatus, getViewElement, handleError, isDefined, loadEuiDynamicEnvironmentConfig, loadState, localForage, localStorageSync, markControlsTouched, markFormGroupTouched, mergeAppHandlerConfigToAppConfig, mergeAppJsonConfigToAppConfig, offset, position, preInitApp, prepareEuiAppConfigToken, coreReducers as reducers, sessionStorageSync, translateConfig, uniqueId, validateEmail };
4631
4579
  //# sourceMappingURL=eui-core.mjs.map