ngx-gem-spaas
Version:
This library contains services, components, images and styles to provide a unified look and way-of-working throughout GEM SPaaS.
2,239 lines • 145 kB
JavaScript
import * as i0 from '@angular/core';
import { Component, Injectable, Optional, Inject, Input, EventEmitter, Output, NgModule, ViewChild, Directive, HostListener, APP_INITIALIZER } from '@angular/core';
import * as i6 from '@angular/common';
import { CommonModule } from '@angular/common';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Subject, ReplaySubject, BehaviorSubject, timer, filter, fromEvent } from 'rxjs';
import { takeUntil, take, map } from 'rxjs/operators';
import * as i3 from '@angular/router';
import { NavigationStart, RouterModule } from '@angular/router';
import { OktaAuth } from '@okta/okta-auth-js';
import * as i4 from '@angular/material/progress-bar';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import * as i4$1 from '@angular/forms';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i5 from '@angular/material/radio';
import { MatRadioModule, MAT_RADIO_DEFAULT_OPTIONS } from '@angular/material/radio';
import * as i2 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i1$1 from '@angular/material/snack-bar';
import { MatSnackBarModule, MatSnackBarConfig } from '@angular/material/snack-bar';
import * as i4$2 from '@angular/material/tooltip';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
import * as i1 from '@angular/material/bottom-sheet';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import * as i3$2 from '@angular/cdk/drag-drop';
import { DragDropModule } from '@angular/cdk/drag-drop';
import * as i2$1 from '@angular/service-worker';
import { ServiceWorkerModule } from '@angular/service-worker';
import * as i3$1 from '@angular/cdk/platform';
import * as i2$2 from '@angular/platform-browser';
class BaseComponent {
constructor() {
this.onDestroy$ = new Subject();
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: BaseComponent, selector: "spaas-base", ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BaseComponent, decorators: [{
type: Component,
args: [{
selector: 'spaas-base',
template: '',
}]
}] });
/**
* Okta configuration object to be provided via "forRoot" method of the SpaasModule.
*
* @property {string[]} additionalScopes the scopes needed for your particular back-end
* @property {string} clientId the client id of your SPA
* @property {string[]} interceptUrls the urls to intercept and add an Authorization - Bearer header to
* @property {string} tenantId the tenant id of your Okta issuer
* @property {string} url the url of your Okta issuer
* @property {boolean} withPath false for subdomains, true for context (optional, default is true)
*/
class OktaConfigModel {
constructor(objIn) {
this.additionalScopes = [];
this.clientId = '';
this.interceptUrls = [];
this.tenantId = '';
this.url = '';
this.withPath = true;
this.additionalScopes = objIn.additionalScopes || [];
this.clientId = objIn.clientId || '';
this.interceptUrls = objIn.interceptUrls || [];
this.tenantId = objIn.tenantId || '';
this.url = objIn.url || '';
this.withPath = objIn.withPath || true;
}
}
class OktaUserModel {
constructor(props) {
const nameArr = OktaUserModel.stripMail(props.email).split('.');
this.email = props.email || '';
this.firstName = nameArr.length ? nameArr[0] : '';
this.groups = props.groups || [];
this.lastName = nameArr.length ? nameArr[1] : '';
this.userId = props.userid || props.userId || ''; // props.userId is for when OktaUserModel is extended and fed itself as props
}
static stripMail(strIn) {
return strIn ? strIn.replace('@engie.com', '').replace('@external.engie.com', '') : '';
}
checkGroup(group) {
return this.groups.some((g) => g.toLowerCase().startsWith((group || '').toLowerCase()));
}
}
/**
* New version configuration object to be provided via "forRoot" method of the SpaasModule.
*
* @property {number} reminder default is zero. If not zero and hasBackdrop === true, this sets the interval
* (in minutes) at which to prompt the user to load the newly deployed version. Will be floored to a 5-minute minimum.
* @property {boolean} hasBackdrop default is true. If false, no backdrop is shown for the bottom-sheet pop-up (and
* so the pop-up can only be dismissed by accepting the new version).
*/
class NewVersionConfigModel {
constructor(objIn) {
this.reminder = 0;
this.hasBackdrop = true;
this.reminder = objIn.reminder ? Math.max(objIn.reminder, 5) : 0;
this.hasBackdrop = objIn.hasOwnProperty('hasBackdrop') ? objIn.hasBackdrop : true;
}
}
/**
* Library configuration object to be provided via "forRoot" method of the SpaasModule.
*
* @property {string} appName the application name. Will be shown in browser tab and will be used as storage prefix
* @property {string} environment will be used as storage prefix, in combination with appName
* @property {OktaConfigModel} oktaConfig the Okta configuration for this project
* @property {NewVersionConfigModel} newVersionConfig the configuration for the new version handling
*/
class SpaasConfigModel {
constructor(objIn) {
this.appName = '';
this.environment = '';
this.oktaConfig = new OktaConfigModel({});
this.newVersionConfig = new NewVersionConfigModel({});
this.appName = objIn.appName || '';
this.environment = objIn.environment || '';
this.oktaConfig = new OktaConfigModel(objIn.oktaConfig || {});
this.newVersionConfig = new NewVersionConfigModel(objIn.newVersionConfig || {});
}
}
class SpaasExtConfigModel extends SpaasConfigModel {
constructor(conf) {
super(conf);
this.cleanAppName = this.appName.charAt(0).toUpperCase() + this.appName.substring(1, this.appName.length).toLowerCase();
this.storagePrefix = (this.appName || this.oktaConfig?.clientId) + '-' + this.environment + '-';
}
}
class ConfigService {
constructor(spaasConfig) {
this.spaasConfig = spaasConfig;
if (!spaasConfig) {
throw new Error('please provide a SpaasConfigModel using the forRoot method of the SpaasModule');
}
this.config = new SpaasExtConfigModel(this.spaasConfig);
}
getConfig() {
return this.config;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConfigService, deps: [{ token: SpaasConfigModel, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConfigService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConfigService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: SpaasConfigModel, decorators: [{
type: Optional
}, {
type: Inject,
args: [SpaasConfigModel]
}] }] });
var EmbedModeEnum;
(function (EmbedModeEnum) {
EmbedModeEnum["none"] = "";
EmbedModeEnum["iframe"] = "iframe";
})(EmbedModeEnum || (EmbedModeEnum = {}));
class EmbedModeModel {
constructor() {
this.parent = '';
this.pwd = '';
this.type = EmbedModeEnum.none;
this.usr = '';
}
isIframe() {
return this.type === EmbedModeEnum.iframe;
}
}
class EmbedModeService {
constructor() {
this.POST_MESSAGE_GETTHEME = 'getTheme';
this.embedMode = new EmbedModeModel();
this.embedMode$ = new ReplaySubject(1);
this.embeddedWindow = null;
}
static getIframeParent() {
const ref = document.referrer || '';
if (ref) {
const url = new URL(ref);
if (url.pathname !== '/') {
return url.pathname.replace(/\//g, '');
}
else {
const subdomain = url.hostname.split('.')[0] || '';
if (!subdomain.includes('cylon')) {
return subdomain;
}
}
}
return '';
}
// setEmbedMode is called from the app-init.service
setEmbedMode() {
if (window.location !== window.parent.location) {
// iframe mode
this.embedMode.type = EmbedModeEnum.iframe;
this.embedMode.parent = EmbedModeService.getIframeParent();
}
this.newEmbedMode();
}
// ********************************************************************************************************
// LOAD DATA
// ********************************************************************************************************
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newEmbedMode() {
// console.log('embed mode set: ' + (this.embedMode.type || 'none'));
this.embedMode$.next(this.embedMode);
}
onNewEmbedMode() {
return this.embedMode$.asObservable();
}
getEmbedMode() {
return this.embedMode;
}
getEmbeddedWindow() {
return this.embeddedWindow;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbedModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbedModeService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbedModeService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
class UtilsService {
static copy(objIn) {
return JSON.parse(JSON.stringify(objIn));
}
// DISTINCT SORTED LIST WITHOUT NULL/UNDEFINED VALUES
static distinct(listIn, prefix = '', sortAsc = true) {
const sortCorr = sortAsc ? 1 : -1;
let listOut = listIn
.filter((value, index, self) => value && self.indexOf(value) === index)
.sort((a, b) => a < b ? -1 * sortCorr : 1 * sortCorr);
if (prefix) {
listOut = listOut.map((c) => prefix + c);
}
return listOut;
}
static round(num, dec) {
if (num === null || num === undefined) {
return null;
}
return Number((num).toFixed(dec));
}
static checkJSON(itemToCheck) {
let item = {};
try {
item = JSON.parse(itemToCheck);
}
catch (e) {
return false;
}
return typeof item === 'object' && item !== null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: UtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: UtilsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: UtilsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
class LocalStorageService {
constructor(configService) {
this.configService = configService;
this.storagePrefix = this.configService.getConfig().storagePrefix;
}
getItem(lsKey) {
const lsVal = localStorage.getItem(this.storagePrefix + lsKey) || '';
return UtilsService.checkJSON(lsVal) ? JSON.parse(lsVal) : Number.isNaN(Number(lsVal)) ? lsVal : Number(lsVal);
}
getItems(searchString) {
const tempResults = [];
for (const key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
if (key.indexOf(searchString) > -1) {
if (UtilsService.checkJSON(localStorage[key])) {
tempResults.push(JSON.parse(localStorage[key]));
}
else {
tempResults.push(localStorage[key]);
}
}
}
}
return tempResults;
}
setItem(lsKey, lsValue) {
if (typeof lsValue === 'object') {
localStorage.setItem(this.storagePrefix + lsKey, JSON.stringify(lsValue));
}
else {
localStorage.setItem(this.storagePrefix + lsKey, lsValue);
}
}
removeItem(lsKey) {
localStorage.removeItem(this.storagePrefix + lsKey);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LocalStorageService, deps: [{ token: ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LocalStorageService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LocalStorageService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: ConfigService }] });
class SessionStorageService {
constructor(configService) {
this.configService = configService;
this.storagePrefix = this.configService.getConfig().storagePrefix;
}
getItem(lsKey) {
const lsVal = sessionStorage.getItem(this.storagePrefix + lsKey) || '';
return UtilsService.checkJSON(lsVal) ? JSON.parse(lsVal) : Number.isNaN(Number(lsVal)) ? lsVal : Number(lsVal);
}
getItems(searchString) {
const tempResults = [];
for (const key in sessionStorage) {
if (sessionStorage.hasOwnProperty(key)) {
if (key.indexOf(searchString) > -1) {
if (UtilsService.checkJSON(sessionStorage[key])) {
tempResults.push(JSON.parse(sessionStorage[key]));
}
else {
tempResults.push(sessionStorage[key]);
}
}
}
}
return tempResults;
}
setItem(lsKey, lsValue) {
if (typeof lsValue === 'object') {
sessionStorage.setItem(this.storagePrefix + lsKey, JSON.stringify(lsValue));
}
else {
sessionStorage.setItem(this.storagePrefix + lsKey, lsValue);
}
}
removeItem(lsKey) {
sessionStorage.removeItem(this.storagePrefix + lsKey);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SessionStorageService, deps: [{ token: ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SessionStorageService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SessionStorageService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: ConfigService }] });
class OktaService {
constructor(configService, lsService, router, ssService) {
this.configService = configService;
this.lsService = lsService;
this.router = router;
this.ssService = ssService;
this.LS_ACTIVEROUTE = 'activeRoute';
this.SS_ERROR = 'oktaError';
this.oktaError = null;
this.oktaError$ = new BehaviorSubject(null);
this.oktaUserInfo = new OktaUserModel({});
this.oktaUserInfo$ = new ReplaySubject(1);
this.onDestroy$ = new Subject();
this.oktaClientConfig = this.setConfig();
// CLIENT, TOKENPARAMS AND TOKENMANAGER
this.oktaClient = new OktaAuth(this.oktaClientConfig);
this.oktaClient.start(); // as of v5, needs to be started as a service to auto-renew
this.tokenParams = { scopes: this.oktaClientConfig.scopes };
this.tokenManager = this.oktaClient.tokenManager;
this.tokenManager.on('error', (error) => {
// auto-renew failed, usually cuz user deleted cookies or something (no more valid okta session), so sign out
console.log(error);
this.oktaClient.signOut({ postLogoutRedirectUri: null }).catch((err) => console.log(err));
});
// SUBSCRIBE TO ROUTING
this.onRouting();
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
// ********************************************************************************************************
// GENERIC STUFF
// ********************************************************************************************************
setConfig() {
const spaasConfig = this.configService.getConfig();
if (!spaasConfig?.oktaConfig) {
throw new Error('please provide a valid Okta configuration via the "forRoot" method of the SpaasModule');
}
if (spaasConfig?.oktaConfig?.url?.slice(-1) === '/') {
throw new Error('the okta url should not have a trailing slash');
}
if (spaasConfig?.oktaConfig?.tenantId.includes('/')) {
throw new Error('the okta tenant id should not contain slashes');
}
return {
clientId: spaasConfig.oktaConfig.clientId,
issuer: spaasConfig.oktaConfig.url + '/oauth2/' + spaasConfig.oktaConfig.tenantId,
pkce: true,
redirectUri: spaasConfig.oktaConfig.withPath ? window.location.origin + window.location.pathname : window.location.origin,
responseType: ['id_token', 'token'],
scopes: ['openid', 'email'].concat(spaasConfig.oktaConfig.additionalScopes || []),
tokenManager: {
storage: 'sessionStorage',
storageKey: spaasConfig.storagePrefix + 'tokens',
expireEarlySeconds: 300
},
transactionManager: {
saveNonceCookie: false,
saveParamsCookie: false,
saveStateCookie: false,
},
};
}
onRouting() {
this.router.events
.pipe(takeUntil(this.onDestroy$))
.subscribe((event) => {
if (event instanceof NavigationStart) {
// set active route here, so we know where to route to after token redirection
if (event.url !== '/') {
this.setActiveRoute(event.url);
}
// BECAUSE WE HANDLE THE CALLBACK BEFORE ROUTING, THE HIDDEN IFRAME REMAINS EMPTY
this.handleRedirect();
}
});
}
// ********************************************************************************************************
// OKTA STUFF
// ********************************************************************************************************
handleRedirect() {
if (this.oktaClient.token.isLoginRedirect() || window.location.search.includes('error=')) {
// IS REDIRECT OR OKTA ERROR
this.updateOktaError(null);
this.parseFromUrl();
}
else if (!this.oktaError) {
// IS REGULAR NAVIGATION: CHECK USER INFO
this.getUserInfoFromToken();
}
}
parseFromUrl() {
this.oktaClient.token.parseFromUrl()
.then((tokenResponse) => {
this.tokenManager.setTokens(tokenResponse.tokens);
// NOW SET USER INFO FROM THE TOKEN
// IT'S THE USER INFO OBSERVABLE THAT OTHER COMPONENTS OR SERVICES SHOULD USE AS INDICATION THAT ALL IS GOOD TO GO
this.getUserInfoFromToken();
})
.catch((err) => {
this.updateOktaError(err);
});
}
getUserInfoFromToken() {
if (this.oktaUserInfo.email) {
return;
}
this.tokenManager.getTokens()
.then((tokens) => {
this.newUserInfo(tokens?.accessToken?.claims);
// set a default url. If it exists, fine, if not, it will force the configured redirects to kick in
this.router.navigate([this.getActiveRoute() || '/home']);
})
.catch(() => {
this.getTokenWithRedirect();
});
}
getTokenWithRedirect() {
this.oktaClient.token.getWithRedirect(this.tokenParams)
.catch((e) => console.log(e));
}
getCachedAccessToken() {
// TO BE USED IN CANACTIVE AND/OR CANLOAD AND/OR HTTP INTERCEPTOR
if (this.oktaClient && this.tokenManager) {
return this.oktaClient.getAccessToken() || '';
}
else {
console.log('should never happen: tokenmanager is null! Nothing left to do but redirect');
this.getTokenWithRedirect();
return '';
}
}
updateOktaError(newState) {
this.oktaError = newState;
this.ssService.setItem(this.SS_ERROR, this.oktaError);
this.newOktaError();
}
forceRetry() {
// METHOD THAT CAN BE CALLED BY WHICHEVER COMPONENT YOU HAVE TO HANDLE/DISPLAY OKTA ERRORS
// FOR EXAMPLE ON A BUTTON CLICK 'TRY AGAIN' SO USER CAN FORCE AN OKTA RETRY
this.updateOktaError(null);
this.getTokenWithRedirect();
}
setActiveRoute(url) {
this.lsService.setItem(this.LS_ACTIVEROUTE, url);
}
getActiveRoute() {
return this.lsService.getItem(this.LS_ACTIVEROUTE) || '';
}
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newUserInfo(userInfo) {
this.oktaUserInfo = new OktaUserModel(userInfo);
this.oktaUserInfo$.next(this.oktaUserInfo);
}
onNewUserInfo() {
return this.oktaUserInfo$.asObservable();
}
getUserInfo() {
return UtilsService.copy(this.oktaUserInfo);
}
newOktaError() {
this.oktaError$.next(this.oktaError);
}
onNewOktaError() {
return this.oktaError$.asObservable();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaService, deps: [{ token: ConfigService }, { token: LocalStorageService }, { token: i3.Router }, { token: SessionStorageService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: ConfigService }, { type: LocalStorageService }, { type: i3.Router }, { type: SessionStorageService }] });
const BG_LIST = {
employee_solar: { url: './assets/images/bgs/employee_solar', alt: 'Employee at a solar plant' },
kathu_mirrors: {
url: './assets/images/bgs/kathu_mirrors',
alt: 'Cylindro-parabolic mirrors of Kathu Solar Park in South Africa'
},
outeiro_ccgt: {
url: './assets/images/bgs/outeiro_ccgt',
alt: 'Portugal Tapada do Outeiro combined cycle gas turbine power station'
},
shem_mareges: { url: './assets/images/bgs/shem_mareges', alt: 'SHEM Mareges hydro station' },
shem_thues: { url: './assets/images/bgs/shem_thues', alt: 'SHEM Thues hydro dam' },
tarfaya_windfarm: { url: './assets/images/bgs/tarfaya_windfarm', alt: 'The Tarfaya wind farm' },
};
class BgComponent extends BaseComponent {
constructor() {
super(...arguments);
this.imgExt = '.webp';
this.imgList = BG_LIST;
// LEFT: ALIGN BG IMG LEFT INSTEAD OF DEFAULT RIGHT
// PORTRAIT: SWITCHES BACKGROUND-SIZE FROM COVER TO AUTO (SO PORTRAIT IMGS ARE NOT STRETCHED OVER FULL WIDTH OF SCREEN)
this.bgX = 100;
this.bgY = 50;
this.bgPortrait = false;
this.bgImg = 'employee_solar';
this.bgAnimate = true;
}
ngOnInit() {
this.bg = this.imgList[this.bgImg];
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BgComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: BgComponent, selector: "spaas-bg", inputs: { bgX: "bgX", bgY: "bgY", bgPortrait: "bgPortrait", bgImg: "bgImg", bgAnimate: "bgAnimate" }, usesInheritance: true, ngImport: i0, template: "<div class=\"bg\">\r\n <div class=\"bg__img-wrapper\"\r\n [title]=\"bg.alt\"\r\n [class.with-anim]=\"bgAnimate\"\r\n [style.background-position-x.%]=\"bgX\"\r\n [style.background-position-y.%]=\"bgY\"\r\n [class.portrait]=\"bgPortrait\"\r\n [style.background-image]=\"'url(' + bg.url + imgExt + ')'\">\r\n </div>\r\n</div>\r\n", styles: [".bg{height:100%;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-1}.bg__img-wrapper{background-repeat:no-repeat;background-size:cover;height:100%;overflow:hidden;width:100%}.bg__img-wrapper.with-anim{animation:kenburns-bottom 4s ease-out both}.bg__img-wrapper.visible{opacity:1}.bg__img-wrapper.invisible{opacity:0}.bg__img-wrapper.portrait{background-size:auto;left:0;right:auto;width:60%}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.bg__img-wrapper.portrait{width:100%}}@keyframes kenburns-bottom{0%{transform:scale(1) translate(0);transform-origin:14% 14%}to{transform:scale(1.04) translate(-14px,-4px);transform-origin:top left}}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BgComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-bg', template: "<div class=\"bg\">\r\n <div class=\"bg__img-wrapper\"\r\n [title]=\"bg.alt\"\r\n [class.with-anim]=\"bgAnimate\"\r\n [style.background-position-x.%]=\"bgX\"\r\n [style.background-position-y.%]=\"bgY\"\r\n [class.portrait]=\"bgPortrait\"\r\n [style.background-image]=\"'url(' + bg.url + imgExt + ')'\">\r\n </div>\r\n</div>\r\n", styles: [".bg{height:100%;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-1}.bg__img-wrapper{background-repeat:no-repeat;background-size:cover;height:100%;overflow:hidden;width:100%}.bg__img-wrapper.with-anim{animation:kenburns-bottom 4s ease-out both}.bg__img-wrapper.visible{opacity:1}.bg__img-wrapper.invisible{opacity:0}.bg__img-wrapper.portrait{background-size:auto;left:0;right:auto;width:60%}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.bg__img-wrapper.portrait{width:100%}}@keyframes kenburns-bottom{0%{transform:scale(1) translate(0);transform-origin:14% 14%}to{transform:scale(1.04) translate(-14px,-4px);transform-origin:top left}}\n"] }]
}], propDecorators: { bgX: [{
type: Input
}], bgY: [{
type: Input
}], bgPortrait: [{
type: Input
}], bgImg: [{
type: Input
}], bgAnimate: [{
type: Input
}] } });
class IntroComponent extends BaseComponent {
constructor(configService, embedModeService, oktaService) {
super();
this.configService = configService;
this.embedModeService = embedModeService;
this.oktaService = oktaService;
this.introText = 'all good, loading your data';
this.checkingAccessText = 'checking your access';
this.tryAgainButtonTitle = 'try again';
this.contactGuardButtonTitle = 'contact guard';
this.goToSnowButtonTitle = 'request access in Snow';
this.bgImg = 'employee_solar';
this.isDone = false;
this.introDone = new EventEmitter();
this.oktaError = null;
this.userReceived = false;
this.isIframe = false;
this.config = this.configService.getConfig();
this.isIframe = this.embedModeService.getEmbedMode().isIframe();
this.onUserInfo();
this.onOktaError();
}
onUserInfo() {
this.oktaService.onNewUserInfo()
.pipe(takeUntil(this.onDestroy$))
.subscribe((user) => {
if (user?.email) {
this.userReceived = true;
// emit that all is well, so the screen can already start rendering
this.introDone.emit(true);
// and only hide the okta itself after n ms
timer(1400)
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => {
this.isDone = true;
});
}
});
}
onOktaError() {
this.oktaService.onNewOktaError()
.pipe(takeUntil(this.onDestroy$))
.subscribe((error) => {
this.oktaError = error;
});
}
oktaTryAgain() {
this.oktaService.forceRetry();
}
contactGuard() {
const body = 'Following error was received trying to sign in to ' + this.config?.cleanAppName + ':\r\n\r\n' +
JSON.stringify(this.oktaError);
window.location.href = `mailto:5939@engie.com?subject=Okta sign-in error for ${this.config?.cleanAppName}&body=${encodeURIComponent(body)}`;
}
goToSnow() {
window.open('https://gemprod.service-now.com/gem', '_blank');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IntroComponent, deps: [{ token: ConfigService }, { token: EmbedModeService }, { token: OktaService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: IntroComponent, selector: "spaas-okta-intro", inputs: { introText: "introText", checkingAccessText: "checkingAccessText", tryAgainButtonTitle: "tryAgainButtonTitle", contactGuardButtonTitle: "contactGuardButtonTitle", goToSnowButtonTitle: "goToSnowButtonTitle", bgImg: "bgImg" }, outputs: { introDone: "introDone" }, usesInheritance: true, ngImport: i0, template: "@if (!isDone) {\r\n <div [class.in-iframe]=\"isIframe\"\r\n class=\"intro\">\r\n @if (!isIframe) {\r\n <div class=\"half-screen flex col center\">\r\n <!-- LOGO -->\r\n <div class=\"half-screen__logo flex center\">\r\n <svg viewBox=\"0 0 512 256\">\r\n <use [attr.xlink:href]=\"'../../assets/images/logo-engie.svg#svg1'\"></use>\r\n </svg>\r\n </div>\r\n @if (oktaError) {\r\n <!-- HAS OKTA ERROR -->\r\n <div class=\"txt-center\">\r\n <div class=\"txt-bold\">Bumped into an error:</div>\r\n <div>{{ oktaError.message }}</div>\r\n <div class=\"flex center pad-big-top column-gap-10\">\r\n <button (click)=\"oktaTryAgain()\" class=\"primary\">\r\n {{ tryAgainButtonTitle }}\r\n </button>\r\n @if ((oktaError.errorCode | lowercase) === 'access_denied') {\r\n <button (click)=\"goToSnow()\" class=\"error\">\r\n {{ goToSnowButtonTitle }}\r\n </button>\r\n } @else {\r\n <button (click)=\"contactGuard()\" class=\"error\">\r\n {{ contactGuardButtonTitle }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- NO OKTA ERROR -->\r\n <div class=\"txt-center pad-big-bottom\">\r\n <div class=\"pad-big-bottom\">\r\n {{ userReceived ? introText : checkingAccessText }}\r\n </div>\r\n <mat-progress-bar [mode]=\"'indeterminate'\"></mat-progress-bar>\r\n </div>\r\n }\r\n </div>\r\n <spaas-bg [bgImg]=\"bgImg\"></spaas-bg>\r\n } @else {\r\n <div>\r\n loading embedded {{ config.cleanAppName }}\r\n <mat-progress-bar [mode]=\"'indeterminate'\"></mat-progress-bar>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".intro{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);height:100vh;left:0;position:fixed;top:0;width:100%;z-index:9999}.intro.in-iframe{display:flex;flex-flow:column;font-family:BebasNeue,Lato,Arial,sans-serif;font-size:18px;justify-content:center;text-align:center}.intro.in-iframe>div{margin:auto;width:344px}\n"], dependencies: [{ kind: "component", type: i4.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: BgComponent, selector: "spaas-bg", inputs: ["bgX", "bgY", "bgPortrait", "bgImg", "bgAnimate"] }, { kind: "pipe", type: i6.LowerCasePipe, name: "lowercase" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IntroComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-okta-intro', template: "@if (!isDone) {\r\n <div [class.in-iframe]=\"isIframe\"\r\n class=\"intro\">\r\n @if (!isIframe) {\r\n <div class=\"half-screen flex col center\">\r\n <!-- LOGO -->\r\n <div class=\"half-screen__logo flex center\">\r\n <svg viewBox=\"0 0 512 256\">\r\n <use [attr.xlink:href]=\"'../../assets/images/logo-engie.svg#svg1'\"></use>\r\n </svg>\r\n </div>\r\n @if (oktaError) {\r\n <!-- HAS OKTA ERROR -->\r\n <div class=\"txt-center\">\r\n <div class=\"txt-bold\">Bumped into an error:</div>\r\n <div>{{ oktaError.message }}</div>\r\n <div class=\"flex center pad-big-top column-gap-10\">\r\n <button (click)=\"oktaTryAgain()\" class=\"primary\">\r\n {{ tryAgainButtonTitle }}\r\n </button>\r\n @if ((oktaError.errorCode | lowercase) === 'access_denied') {\r\n <button (click)=\"goToSnow()\" class=\"error\">\r\n {{ goToSnowButtonTitle }}\r\n </button>\r\n } @else {\r\n <button (click)=\"contactGuard()\" class=\"error\">\r\n {{ contactGuardButtonTitle }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- NO OKTA ERROR -->\r\n <div class=\"txt-center pad-big-bottom\">\r\n <div class=\"pad-big-bottom\">\r\n {{ userReceived ? introText : checkingAccessText }}\r\n </div>\r\n <mat-progress-bar [mode]=\"'indeterminate'\"></mat-progress-bar>\r\n </div>\r\n }\r\n </div>\r\n <spaas-bg [bgImg]=\"bgImg\"></spaas-bg>\r\n } @else {\r\n <div>\r\n loading embedded {{ config.cleanAppName }}\r\n <mat-progress-bar [mode]=\"'indeterminate'\"></mat-progress-bar>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".intro{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);height:100vh;left:0;position:fixed;top:0;width:100%;z-index:9999}.intro.in-iframe{display:flex;flex-flow:column;font-family:BebasNeue,Lato,Arial,sans-serif;font-size:18px;justify-content:center;text-align:center}.intro.in-iframe>div{margin:auto;width:344px}\n"] }]
}], ctorParameters: () => [{ type: ConfigService }, { type: EmbedModeService }, { type: OktaService }], propDecorators: { introText: [{
type: Input
}], checkingAccessText: [{
type: Input
}], tryAgainButtonTitle: [{
type: Input
}], contactGuardButtonTitle: [{
type: Input
}], goToSnowButtonTitle: [{
type: Input
}], bgImg: [{
type: Input
}], introDone: [{
type: Output
}] } });
class OktaAuthInterceptor {
static checkUrlsToIntercept(urlsToIntercept, url) {
for (const curUrl of urlsToIntercept) {
if (url.startsWith(curUrl)) {
return true;
}
}
return false;
}
constructor(spaasConfig, oktaService) {
this.spaasConfig = spaasConfig;
this.oktaService = oktaService;
}
intercept(req, next) {
let headers = req.headers;
if (req.method !== 'GET') {
headers = headers.set('Content-type', 'application/json');
}
if (req.url.startsWith(this.spaasConfig.oktaConfig.url) ||
OktaAuthInterceptor.checkUrlsToIntercept(this.spaasConfig.oktaConfig.interceptUrls, req.url)) {
const token = this.oktaService.getCachedAccessToken();
headers = headers.set('Authorization', `Bearer ${token}`);
const authRequest = req.clone({ headers: headers });
return next.handle(authRequest);
}
else {
const authRequest = req.clone();
return next.handle(authRequest);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaAuthInterceptor, deps: [{ token: SpaasConfigModel }, { token: OktaService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaAuthInterceptor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaAuthInterceptor, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: SpaasConfigModel, decorators: [{
type: Inject,
args: [SpaasConfigModel]
}] }, { type: OktaService }] });
/**
* Settings model, used by the settings component to store data on user settings and preferences
*
* @property {boolean} dark the application theme, defaults to light
* @property {ExportExtensionType} exportExtension the preferred extension for data exports, either xlsx or csv
* @property {DecimalSeparatorType} decimalSeparator the user's decimal separator in Excel, used for exporting and
* importing data
* @property {any} extraData any extra data that you want the user to set and store via the settings component
*/
class SettingsModel {
constructor(objIn) {
this.dark = objIn.hasOwnProperty('dark') ? objIn.dark : false; // if you update the default, also update material.scss default!!
this.decimalSeparator = objIn.decimalSeparator || 'dot';
this.exportExtension = objIn.exportExtension || 'xlsx';
this.extraData = objIn.extraData || {};
}
getCsvDelimiter() {
return this.decimalSeparator === 'comma' ? ';' : ',';
}
}
class SettingsService {
constructor(lsService) {
this.lsService = lsService;
this.LS_SETTINGS = 'settings';
this.settings$ = new BehaviorSubject(new SettingsModel(this.lsService.getItem(this.LS_SETTINGS) || {}));
}
// ********************************************************************************************************
// LOAD DATA
// ********************************************************************************************************
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newSettings(settings) {
this.lsService.setItem(this.LS_SETTINGS, settings);
this.settings$.next(settings);
}
onNewSettings() {
return this.settings$.asObservable();
}
getSettings() {
return this.settings$.getValue();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SettingsService, deps: [{ token: LocalStorageService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SettingsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SettingsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: LocalStorageService }] });
const DATA__HAS_MENU = { menu: 'yes' };
const DATA__THEME_DARK = { theme: 'dark' };
const DATA__THEME_LIGHT = { theme: 'light' };
class ThemeModel {
constructor(name, props) {
this.name = name;
this.properties = props;
}
getRgb(color, alpha = 1) {
const h = Number(this.properties['--' + color + '-h']);
let s = Number(this.properties['--' + color + '-s'].replace('%', ''));
let l = Number(this.properties['--' + color + '-l'].replace('%', ''));
// Must be fractions of 1
s /= 100;
l /= 100;
const c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs((h / 60) % 2 - 1)), m = l - c / 2;
let r = 0, g = 0, b = 0;
if (0 <= h && h < 60) {
r = c;
g = x;
b = 0;
}
else if (60 <= h && h < 120) {
r = x;
g = c;
b = 0;
}
else if (120 <= h && h < 180) {
r = 0;
g = c;
b = x;
}
else if (180 <= h && h < 240) {
r = 0;
g = x;
b = c;
}
else if (240 <= h && h < 300) {
r = x;
g = 0;
b = c;
}
else if (300 <= h && h < 360) {
r = c;
g = 0;
b = x;
}
r = Math.round((r + m) * 255);
g = Math.round((g + m) * 255);
b = Math.round((b + m) * 255);
return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
}
}
// THEMES
// IMPORTANT: IF YOU UPDATE THE SETTINGS FOR PRIMARY, ACCENT OR ERROR HERE,
// ALSO UPDATE THEM IN MATERIAL.SCSS !
const light = new ThemeModel('light', {
'--primary-h': 204,
'--primary-s': '100%',
'--primary-l': '40%',
'--primary-op-min': 0.8,
'--primary-op-max': 0.4,
'--secondary-h': 27,
'--secondary-s': '93%',
'--secondary-l': '58%',
'--secondary-op-min': 0.8,
'--secondary-op-max': 0.4,
'--accent-h': 144,
'--accent-s': '100%',
'--accent-l': '27%',
'--accent-op-min': 0.95,
'--accent-op-max': 0.4,
'--error-h': 1,
'--error-s': '70%',
'--error-l': '53%',
'--error-op-min': 0.6,
'--error-op-max': 0.4,
'--bg-h': 0,
'--bg-s': '0%',
'--bg-l': '98%',
'--bg-accent': '-6%',
'--bg-lighten': '6%',
'--bg-darken': '-14%',
'--bg-op-overlay': 0.4,
'--bg-op-min': 0.84,
'--bg-op-max': 0.2,
'--color-h': 0,
'--color-s': '0%',
'--color-l': '13.4%',
'--color-op-min': 0.8,
'--color-op-max': 0.4,
'--disabled-h': 0,
'--disabled-s': '0%',
'--disabled-l': '62%',
'--disabled-op-min': 0.8,
'--disabled-op-max': 0.4,
'--table-even': '-8%',
'--table-odd': '-3%',
'--hover-lighten': '-12%',
});
// IMPORTANT: IF YOU UPDATE THE SETTINGS FOR PRIMARY, ACCENT OR ERROR HERE,
// ALSO UPDATE THEM IN MATERIAL.SCSS !
const dark = new ThemeModel('dark', {
'--primary-h': 200,
'--primary-s': '75%',
'--primary-l': '55%',
'--primary-op-min': 0.8,
'--primary-op-max': 0.4,
'--secondary-h': 27,
'--secondary-s': '93%',
'--secondary-l': '58%',
'--secondary-op-min': 0.8,
'--secondary-op-max': 0.4,
'--accent-h': 136,
'--accent-s': '80%',
'--accent-l': '38%',
'--accent-op-min': 0.95,
'--accent-op-max': 0.4,
'--error-h': 1,
'--error-s': '70%',
'--error-l': '56%',
'--error-op-min': 0.6,
'--error-op-max': 0.4,
'--bg-h': 210,
'--bg-s': '16%',
'--bg-l': '10%',
'--bg-accent': '10%',
'--bg-lighten': '4%',
'--bg-darken': '-8%',
'--bg-op-overlay': 0.4,
'--bg-op-min': 0.84,
'--bg-op-max': 0.2,
'--color-h': 0,
'--color-s': '0%',
'--color-l': '98%',
'--color-op-min': 0.8,
'--color-op-max': 0.4,
'--disabled-h': 0,
'--disabled-s': '0%',
'--disabled-l': '49.4%',
'--disabled-op-min': 0.8,
'--disabled-op-max': 0.4,
'--table-even': '10%',
'--table-odd': '3%',
'--hover-lighten': '22%',
});
class ThemeService {
constructor(settingsService) {
this.settingsService = settingsService;
this.activeTheme = new ThemeModel('', {});
this.activeTheme$ = new ReplaySubject(1);
}
// initTheme is called from the app-init.service
initTheme() {
if (!this.activeTheme?.name) {
this.setActiveTheme(this.settingsService.getSettings().dark ? 'dark' : 'light');
}
}
toggleTheme() {
if (this.activeTheme?.name === 'light') {
this.setActiveTheme('dark');
}
else {
this.setActiveTheme('light');
}
}
setActiveTheme(theme) {
// console.log('setting theme to ' + theme);
this.activeTheme = theme === 'dark' ? dark : light;
this.setCssVariables(this.activeTheme.properties);
this.setBodyData(theme === 'dark' ? DATA__THEME_DARK : DATA__THEME_LIGHT);
this.newTheme();
}
setCssVariables(props) {
for (const prop in props) {
if (props.hasOwnProperty(prop)) {
document.documentElement.style.setProperty(prop, props[prop]);
}
}
}
setBodyData(props) {
for (const prop in props) {
if (props.hasOwnProperty(prop)) {
document.body.dataset[prop] = props[prop];
}
}
}
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newTheme() {
this.activeTheme$.next(this.activeTheme);
}
onNewActiveTheme() {
return this.activeTheme$.asObservable();
}
getActiveTheme() {
return this.activeTheme;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThemeService, deps: [{ token: SettingsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThemeService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThemeService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: SettingsService }] });
/**
* The toggle component provides a custom toggle-look for checkboxes,
* in line with the Fluid UI theming.
*/
class ToggleComponent extends BaseComponent {
constructor() {
super();
/** The boolean form control to bind to the toggle (checkbox) */
this.ctrl = new FormControl(false, { nonNullable: true });
/**
* The unique id to reference the input with (necessary for label binding). If you have multiple
* toggles, like in each row of a table, you'd typically use the for-index for this
*/
this.id = '';
/** The label to display next to the toggle (clickable) */
this.label = '';
/** The label position, to the left or right of the toggle */
this.labelPosition = 'right';
/** Smaller subtext for underneath the label, usually to provide a more detailed description */
this.subText = '';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ToggleComponent, selector: "spaas-toggle", inputs: { ctrl: "ctrl", id: "id", label: "label", labelPosition: "labelPosition", subText: "subText" }, usesInheritance: true, ngImport: i0, template: "<div class=\"flex column-gap-7 width-fit-content\"\r\n [ngClass]=\"labelPosition === 'right' ? 'row' : 'row-reverse txt-right'\">\r\n\r\n <input type=\"checkbox\"\r\n class=\"tgl tgl-flat\"\r\n [formControl]=\"ctrl\"\r\n [id]=\"'toggle_' + id\">\r\n <label [for]=\"'toggle_' + id\" class=\"tgl-btn\"></label>\r\n\r\n @if (label) {\r\n <label [for]=\"'toggle_' + id\">\r\n {{ label }}\r\n @if (subText) {\r\n <div class=\"tgl-subtxt\">\r\n {{ subText }}\r\n </div>\r\n }\r\n </label>\r\n }\r\n\r\n</div>\r\n", dependencies: [{ kind: "directive", type: i6.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ToggleComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-toggle', template: "<div class=\"flex column-gap-7 width-fit-content\"\r\n [ngClass]=\"labelPosition === 'right' ? 'row' : 'row-reverse txt-right'\">\r\n\r\n <input type=\"checkbox\"\r\n class=\"tgl tgl-flat\"\r\n [formControl]=\"ctrl\"\r\n [id]=\"'toggle_' + id\">\r\n <label [for]=\"'toggle_' + id\" class=\"tgl-btn\"></label>\r\n\r\n @if (label) {\r\n <label [for]=\"'toggle_' + id\">\r\n {{ label }}\r\n @if (subText) {\r\n <div class=\"tgl-subtxt\">\r\n {{ subText }}\r\n </div>\r\n }\r\n </label>\r\n }\r\n\r\n</div>\r\n" }]
}], ctorParameters: () => [], propDecorators: { ctrl: [{
type: Input,
args: [{ required: true }]
}], id: [{
type: Input,
args: [{ required: true }]
}], label: [{
type: Input
}], labelPosition: [{
type: Input
}], subText: [{
type: Input
}] } });
class SettingsFormModel {
constructor(settings) {
this.dark = new FormControl(settings.dark, { nonNullable: true });
this.decimalSeparator = new FormControl(settings.decimalSeparator, { nonNullable: true });
this.exportExtension = new FormControl(settings.exportExtension, { nonNullable: true });
}
}
/**
* Settings component that you can simply inject in an otherwise empty 'settings' screen.
* To let the user choose some basic preferences like dark or light theme.
* The component uses content projection to allow you to add any app-specific settings you want to persist.
*/
class SettingsComponent extends BaseComponent {
constructor(oktaService, settingsService, themeService) {
super();
this.oktaService = oktaService;
this.settingsService = settingsService;
this.themeService = themeService;
this.settingTitle = 'These are your settings';
this.darkModeButtonTitle = 'dark mode';
this.darkModeHintText = ' rest your eyes a bit, switch to dark mode';
this.exportDescriptionText = 'When downloading data, save as';
this.excelExportRadioText = 'Excel Workbook (.xlsx)';
this.csvExportRadioText = 'CSV file (.csv)';
this.decimalSeparatorText = 'Your decimal separator in Excel';
this.dotRadioText = 'dot';
this.commaRadioText = 'comma';
this.user = new OktaUserModel({});
this.onNewUser();
this.settings = this.settingsService.getSettings();
this.settingsForm = new FormGroup(new SettingsFormModel(this.settings));
}
onNewUser() {
this.oktaService.onNewUserInfo()
.pipe(takeUntil(this.onDestroy$))
.subscribe((user) => {
this.user = user;
});
}
onToggleTheme() {
this.updateSettings();
this.themeService.toggleTheme();
}
updateSettings() {
// update settings with the form values. Leave extraData untouched, not handled by this component
this.settings.dark = this.settingsForm.controls.dark.value;
this.settings.decimalSeparator = this.settingsForm.controls.decimalSeparator.value;
this.settings.exportExtension = this.settingsForm.controls.exportExtension.value;
// broadcast the new settings
this.settingsService.newSettings(this.settings);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SettingsComponent, deps: [{ token: OktaService }, { token: SettingsService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SettingsComponent, selector: "spaas-settings", inputs: { settingTitle: "settingTitle", darkModeButtonTitle: "darkModeButtonTitle", darkModeHintText: "darkModeHintText", exportDescriptionText: "exportDescriptionText", excelExportRadioText: "excelExportRadioText", csvExportRadioText: "csvExportRadioText", decimalSeparatorText: "decimalSeparatorText", dotRadioText: "dotRadioText", commaRadioText: "commaRadioText" }, usesInheritance: true, ngImport: i0, template: "<div class=\"settings\">\r\n\r\n <div class=\"half-screen\"\r\n [formGroup]=\"settingsForm\">\r\n\r\n <h3>{{ settingTitle }} {{ user.firstName ? ', ' + (user.firstName | titlecase) : '' }}</h3>\r\n\r\n <!-- dark mode -->\r\n <div>\r\n <spaas-toggle id=\"dark\"\r\n [ctrl]=\"settingsForm.controls.dark\"\r\n [label]=\"darkModeButtonTitle\"\r\n [subText]=\"darkModeHintText\"\r\n (change)=\"onToggleTheme()\">\r\n </spaas-toggle>\r\n </div>\r\n\r\n <!-- export type -->\r\n <div>\r\n {{ exportDescriptionText }}\r\n <mat-radio-group aria-label=\"select an export type\"\r\n class=\"flex col\"\r\n [formControl]=\"settingsForm.controls.exportExtension\"\r\n (change)=\"updateSettings()\">\r\n <mat-radio-button value=\"xlsx\">{{ excelExportRadioText }}</mat-radio-button>\r\n <mat-radio-button value=\"csv\">{{ csvExportRadioText }}</mat-radio-button>\r\n </mat-radio-group>\r\n </div>\r\n\r\n <!-- decimal separator -->\r\n <div>\r\n {{ decimalSeparatorText }}\r\n <mat-radio-group aria-label=\"select a decimal separator\"\r\n class=\"flex col\"\r\n [formControl]=\"settingsForm.controls.decimalSeparator\"\r\n (change)=\"updateSettings()\">\r\n <mat-radio-button value=\"dot\">{{ dotRadioText }}</mat-radio-button>\r\n <mat-radio-button value=\"comma\">{{ commaRadioText }}</mat-radio-button>\r\n </mat-radio-group>\r\n </div>\r\n\r\n <!-- projected content -->\r\n <div>\r\n <ng-content></ng-content>\r\n </div>\r\n\r\n </div>\r\n\r\n <spaas-bg bgImg=\"employee_solar\"></spaas-bg>\r\n\r\n</div>\r\n", styles: [".settings{animation:anim-fade-in 1s ease;position:relative}\n"], dependencies: [{ kind: "directive", type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i5.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "directive", type: i4$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i4$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: BgComponent, selector: "spaas-bg", inputs: ["bgX", "bgY", "bgPortrait", "bgImg", "bgAnimate"] }, { kind: "component", type: ToggleComponent, selector: "spaas-toggle", inputs: ["ctrl", "id", "label", "labelPosition", "subText"] }, { kind: "pipe", type: i6.TitleCasePipe, name: "titlecase" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SettingsComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-settings', template: "<div class=\"settings\">\r\n\r\n <div class=\"half-screen\"\r\n [formGroup]=\"settingsForm\">\r\n\r\n <h3>{{ settingTitle }} {{ user.firstName ? ', ' + (user.firstName | titlecase) : '' }}</h3>\r\n\r\n <!-- dark mode -->\r\n <div>\r\n <spaas-toggle id=\"dark\"\r\n [ctrl]=\"settingsForm.controls.dark\"\r\n [label]=\"darkModeButtonTitle\"\r\n [subText]=\"darkModeHintText\"\r\n (change)=\"onToggleTheme()\">\r\n </spaas-toggle>\r\n </div>\r\n\r\n <!-- export type -->\r\n <div>\r\n {{ exportDescriptionText }}\r\n <mat-radio-group aria-label=\"select an export type\"\r\n class=\"flex col\"\r\n [formControl]=\"settingsForm.controls.exportExtension\"\r\n (change)=\"updateSettings()\">\r\n <mat-radio-button value=\"xlsx\">{{ excelExportRadioText }}</mat-radio-button>\r\n <mat-radio-button value=\"csv\">{{ csvExportRadioText }}</mat-radio-button>\r\n </mat-radio-group>\r\n </div>\r\n\r\n <!-- decimal separator -->\r\n <div>\r\n {{ decimalSeparatorText }}\r\n <mat-radio-group aria-label=\"select a decimal separator\"\r\n class=\"flex col\"\r\n [formControl]=\"settingsForm.controls.decimalSeparator\"\r\n (change)=\"updateSettings()\">\r\n <mat-radio-button value=\"dot\">{{ dotRadioText }}</mat-radio-button>\r\n <mat-radio-button value=\"comma\">{{ commaRadioText }}</mat-radio-button>\r\n </mat-radio-group>\r\n </div>\r\n\r\n <!-- projected content -->\r\n <div>\r\n <ng-content></ng-content>\r\n </div>\r\n\r\n </div>\r\n\r\n <spaas-bg bgImg=\"employee_solar\"></spaas-bg>\r\n\r\n</div>\r\n", styles: [".settings{animation:anim-fade-in 1s ease;position:relative}\n"] }]
}], ctorParameters: () => [{ type: OktaService }, { type: SettingsService }, { type: ThemeService }], propDecorators: { settingTitle: [{
type: Input
}], darkModeButtonTitle: [{
type: Input
}], darkModeHintText: [{
type: Input
}], exportDescriptionText: [{
type: Input
}], excelExportRadioText: [{
type: Input
}], csvExportRadioText: [{
type: Input
}], decimalSeparatorText: [{
type: Input
}], dotRadioText: [{
type: Input
}], commaRadioText: [{
type: Input
}] } });
class PreloaderService {
constructor() {
this.preloaderMsgs = [];
this.preloaderMsgs$ = new BehaviorSubject([]);
}
// ********************************************************************************************************
// SAVE DATA
// ********************************************************************************************************
/**
* Activates the preloader overlay ("<spaas-preloader>" to be added to your app.component.html).
*
* @param preloaderMsg will be added to the list of messages, to be shown as feedback to the user.
*/
start(preloaderMsg) {
this.preloaderMsgs.push(preloaderMsg);
this.newPreloaderMsg();
}
/**
* Removes the "preloaderMsg" from the list of preloader messages.
*
* @param preloaderMsg message to be removed from the list. If the "preloaderMsg" was the last one on the list,
* the preloader overlay will be hidden.
*/
stop(preloaderMsg) {
this.preloaderMsgs = this.preloaderMsgs.filter((l) => l !== preloaderMsg);
this.newPreloaderMsg();
}
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newPreloaderMsg() {
this.preloaderMsgs$.next(this.preloaderMsgs);
}
onNewPreloaderMsg() {
return this.preloaderMsgs$.asObservable();
}
getPreloaderMsgs() {
return JSON.parse(JSON.stringify(this.preloaderMsgs$.getValue()));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreloaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreloaderService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreloaderService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
class PreloaderComponent extends BaseComponent {
constructor(preloaderService) {
super();
this.preloaderService = preloaderService;
this.isActive = false;
this.mostRecent = '';
this.preloaderService.onNewPreloaderMsg()
.pipe(takeUntil(this.onDestroy$))
.subscribe((msgs) => {
this.isActive = msgs.length > 0;
this.mostRecent = msgs[msgs.length - 1] || '';
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreloaderComponent, deps: [{ token: PreloaderService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: PreloaderComponent, selector: "spaas-preloader", usesInheritance: true, ngImport: i0, template: "@if (isActive) {\r\n <div class=\"preloader\">\r\n <mat-progress-bar [mode]=\"'indeterminate'\" class=\"preloader__progbar\">\r\n </mat-progress-bar>\r\n <div class=\"preloader__text\">\r\n {{ mostRecent }}\r\n </div>\r\n </div>\r\n}\r\n", styles: [".preloader{animation:anim-visible 0s ease forwards;background:hsla(var(--bg-h),var(--bg-s),var(--bg-l),var(--bg-op-overlay));cursor:wait;height:100%;left:0;opacity:0;visibility:hidden;position:fixed;top:0;width:100vw;z-index:9996}.preloader__text{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);color:hsla(var(--color-h),var(--color-s),var(--color-l),1);font-family:BebasNeue,Lato,Arial,sans-serif;font-size:24px;margin:14px auto;padding:14px;text-align:center;width:fit-content}.preloader__progbar{left:0;position:fixed;top:0;width:100%}\n"], dependencies: [{ kind: "component", type: i4.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreloaderComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-preloader', template: "@if (isActive) {\r\n <div class=\"preloader\">\r\n <mat-progress-bar [mode]=\"'indeterminate'\" class=\"preloader__progbar\">\r\n </mat-progress-bar>\r\n <div class=\"preloader__text\">\r\n {{ mostRecent }}\r\n </div>\r\n </div>\r\n}\r\n", styles: [".preloader{animation:anim-visible 0s ease forwards;background:hsla(var(--bg-h),var(--bg-s),var(--bg-l),var(--bg-op-overlay));cursor:wait;height:100%;left:0;opacity:0;visibility:hidden;position:fixed;top:0;width:100vw;z-index:9996}.preloader__text{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);color:hsla(var(--color-h),var(--color-s),var(--color-l),1);font-family:BebasNeue,Lato,Arial,sans-serif;font-size:24px;margin:14px auto;padding:14px;text-align:center;width:fit-content}.preloader__progbar{left:0;position:fixed;top:0;width:100%}\n"] }]
}], ctorParameters: () => [{ type: PreloaderService }] });
/*
const SLIDE_OUTS = {
dagResults: 'dag-results',
taskLogs: 'task-logs'
} as const;
export type SlideOutType = typeof SLIDE_OUTS[keyof typeof SLIDE_OUTS];
*/
class SlideOutService {
constructor() {
this.soActive$ = new BehaviorSubject('');
}
newSlideOut(slideOut) {
if (slideOut === this.getSlideOut()) {
slideOut = '';
}
this.soActive$.next(slideOut);
}
onNewSlideOut() {
return this.soActive$.asObservable();
}
getSlideOut() {
return this.soActive$.getValue();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
class SlideOutComponent extends BaseComponent {
constructor(slideOutService) {
super();
this.slideOutService = slideOutService;
this.soActive = '';
this.getSmActive();
}
ngOnDestroy() {
this.slideOutService.newSlideOut('');
super.ngOnDestroy();
}
// ********************************************************************************************************
// LOAD DATA
// ********************************************************************************************************
getSmActive() {
this.slideOutService.onNewSlideOut()
.pipe(takeUntil(this.onDestroy$))
.subscribe((sm) => {
this.soActive = sm;
});
}
// ********************************************************************************************************
// UI
// ********************************************************************************************************
onClose() {
this.slideOutService.newSlideOut('');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutComponent, deps: [{ token: SlideOutService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SlideOutComponent, selector: "spaas-slide-out", usesInheritance: true, ngImport: i0, template: "<div class=\"slide-out\"\r\n [class.visible]=\"soActive\">\r\n <div class=\"slide-out__close\"\r\n [class.visible]=\"soActive\">\r\n <mat-icon (click)=\"onClose()\">clear</mat-icon>\r\n </div>\r\n <ng-content select=\"spaas-slide-out-content\"></ng-content>\r\n</div>\r\n\r\n", styles: [".slide-out{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),.94);height:100vh;position:fixed;left:0;top:0;transform:translate3d(-100%,0,0);transition:transform .2s;width:100%;z-index:9004}.slide-out.visible{transform:translateZ(0)}.slide-out__close{color:hsla(var(--error-h),var(--error-s),var(--error-l),1);opacity:0;position:fixed;right:54px;top:20px;z-index:9999}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__close{right:10px}}.slide-out__close.visible{animation:rotate .6s;opacity:1}@keyframes rotate{0%{transform:rotate(0)}to{transform:rotate(90deg)}}.slide-out__close .mat-icon{font-size:28px!important;height:28px!important;line-height:28px!important;width:28px!important}.slide-out__content{animation:anim-fade-in .4s .2s ease forwards;display:flex;height:calc(100vh - 28px);justify-content:flex-start;left:88px;opacity:0;overflow-y:auto;overscroll-behavior:contain;position:fixed;top:14px;width:calc(100% - 176px);z-index:9005}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__content{left:44px;width:calc(100% - 88px)}}\n"], dependencies: [{ kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-slide-out', template: "<div class=\"slide-out\"\r\n [class.visible]=\"soActive\">\r\n <div class=\"slide-out__close\"\r\n [class.visible]=\"soActive\">\r\n <mat-icon (click)=\"onClose()\">clear</mat-icon>\r\n </div>\r\n <ng-content select=\"spaas-slide-out-content\"></ng-content>\r\n</div>\r\n\r\n", styles: [".slide-out{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),.94);height:100vh;position:fixed;left:0;top:0;transform:translate3d(-100%,0,0);transition:transform .2s;width:100%;z-index:9004}.slide-out.visible{transform:translateZ(0)}.slide-out__close{color:hsla(var(--error-h),var(--error-s),var(--error-l),1);opacity:0;position:fixed;right:54px;top:20px;z-index:9999}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__close{right:10px}}.slide-out__close.visible{animation:rotate .6s;opacity:1}@keyframes rotate{0%{transform:rotate(0)}to{transform:rotate(90deg)}}.slide-out__close .mat-icon{font-size:28px!important;height:28px!important;line-height:28px!important;width:28px!important}.slide-out__content{animation:anim-fade-in .4s .2s ease forwards;display:flex;height:calc(100vh - 28px);justify-content:flex-start;left:88px;opacity:0;overflow-y:auto;overscroll-behavior:contain;position:fixed;top:14px;width:calc(100% - 176px);z-index:9005}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__content{left:44px;width:calc(100% - 88px)}}\n"] }]
}], ctorParameters: () => [{ type: SlideOutService }] });
class SlideOutContentComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SlideOutContentComponent, selector: "spaas-slide-out-content", ngImport: i0, template: "<div class=\"slide-out__content\">\r\n <ng-content></ng-content>\r\n</div>\r\n", styles: [".slide-out{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),.94);height:100vh;position:fixed;left:0;top:0;transform:translate3d(-100%,0,0);transition:transform .2s;width:100%;z-index:9004}.slide-out.visible{transform:translateZ(0)}.slide-out__close{color:hsla(var(--error-h),var(--error-s),var(--error-l),1);opacity:0;position:fixed;right:54px;top:20px;z-index:9999}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__close{right:10px}}.slide-out__close.visible{animation:rotate .6s;opacity:1}@keyframes rotate{0%{transform:rotate(0)}to{transform:rotate(90deg)}}.slide-out__close .mat-icon{font-size:28px!important;height:28px!important;line-height:28px!important;width:28px!important}.slide-out__content{animation:anim-fade-in .4s .2s ease forwards;display:flex;height:calc(100vh - 28px);justify-content:flex-start;left:88px;opacity:0;overflow-y:auto;overscroll-behavior:contain;position:fixed;top:14px;width:calc(100% - 176px);z-index:9005}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__content{left:44px;width:calc(100% - 88px)}}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SlideOutContentComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-slide-out-content', template: "<div class=\"slide-out__content\">\r\n <ng-content></ng-content>\r\n</div>\r\n", styles: [".slide-out{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),.94);height:100vh;position:fixed;left:0;top:0;transform:translate3d(-100%,0,0);transition:transform .2s;width:100%;z-index:9004}.slide-out.visible{transform:translateZ(0)}.slide-out__close{color:hsla(var(--error-h),var(--error-s),var(--error-l),1);opacity:0;position:fixed;right:54px;top:20px;z-index:9999}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__close{right:10px}}.slide-out__close.visible{animation:rotate .6s;opacity:1}@keyframes rotate{0%{transform:rotate(0)}to{transform:rotate(90deg)}}.slide-out__close .mat-icon{font-size:28px!important;height:28px!important;line-height:28px!important;width:28px!important}.slide-out__content{animation:anim-fade-in .4s .2s ease forwards;display:flex;height:calc(100vh - 28px);justify-content:flex-start;left:88px;opacity:0;overflow-y:auto;overscroll-behavior:contain;position:fixed;top:14px;width:calc(100% - 176px);z-index:9005}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.slide-out__content{left:44px;width:calc(100% - 88px)}}\n"] }]
}] });
class MaterialModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: MaterialModule, imports: [DragDropModule,
MatBottomSheetModule,
MatIconModule,
MatProgressBarModule,
MatRadioModule,
MatSnackBarModule,
MatTooltipModule], exports: [DragDropModule,
MatBottomSheetModule,
MatIconModule,
MatProgressBarModule,
MatRadioModule,
MatSnackBarModule,
MatTooltipModule] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MaterialModule, providers: [
{
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: {
subscriptSizing: 'dynamic'
}
},
{
provide: MAT_RADIO_DEFAULT_OPTIONS,
useValue: { color: 'primary' },
}
], imports: [DragDropModule,
MatBottomSheetModule,
MatIconModule,
MatProgressBarModule,
MatRadioModule,
MatSnackBarModule,
MatTooltipModule, DragDropModule,
MatBottomSheetModule,
MatIconModule,
MatProgressBarModule,
MatRadioModule,
MatSnackBarModule,
MatTooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MaterialModule, decorators: [{
type: NgModule,
args: [{
exports: [
DragDropModule,
MatBottomSheetModule,
MatIconModule,
MatProgressBarModule,
MatRadioModule,
MatSnackBarModule,
MatTooltipModule,
],
imports: [
DragDropModule,
MatBottomSheetModule,
MatIconModule,
MatProgressBarModule,
MatRadioModule,
MatSnackBarModule,
MatTooltipModule,
],
providers: [
{
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: {
subscriptSizing: 'dynamic'
}
},
{
provide: MAT_RADIO_DEFAULT_OPTIONS,
useValue: { color: 'primary' },
}
]
}]
}] });
class NewVersionComponent extends BaseComponent {
constructor(bottomSheetRef) {
super();
this.bottomSheetRef = bottomSheetRef;
}
reload() {
this.bottomSheetRef.dismiss();
window.location.reload();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NewVersionComponent, deps: [{ token: i1.MatBottomSheetRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: NewVersionComponent, selector: "spaas-new-version", usesInheritance: true, ngImport: i0, template: "<div class=\"txt-center\">\r\n new version available\r\n <div class=\"flex center pad-big-top\">\r\n <button class=\"primary bold\" (click)=\"reload()\">\r\n load it now\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [""] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NewVersionComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-new-version', template: "<div class=\"txt-center\">\r\n new version available\r\n <div class=\"flex center pad-big-top\">\r\n <button class=\"primary bold\" (click)=\"reload()\">\r\n load it now\r\n </button>\r\n </div>\r\n</div>\r\n" }]
}], ctorParameters: () => [{ type: i1.MatBottomSheetRef }] });
class NewVersionService {
constructor(spaasConfig, bottomSheet, swUpdate) {
this.spaasConfig = spaasConfig;
this.bottomSheet = bottomSheet;
this.swUpdate = swUpdate;
// we get the raw spaasConfig provided by the client app, not the one from the ConfigService (because
// this is called from appInit, so we cannot rely on the NewVersionConfigModel constructor making things clean.
// Hence, always do a new NewVersionConfigModel
}
getNewVersion() {
// LISTEN FOR NEW VERSIONS
if (!this.swUpdate?.isEnabled) {
return;
}
this.swUpdate.versionUpdates
.pipe(filter((evt) => evt.type === 'VERSION_READY'))
.subscribe(() => {
const cfg = new NewVersionConfigModel(this.spaasConfig?.newVersionConfig || {});
this.bottomSheet.open(NewVersionComponent, { hasBackdrop: cfg.hasBackdrop });
this.startTimer();
});
}
startTimer() {
const cfg = new NewVersionConfigModel(this.spaasConfig?.newVersionConfig || {});
if (cfg.reminder && cfg.hasBackdrop) {
// if reminder is set and hasBackdrop is true, just start the timer (if no backdrop, the pop-up will stay open, no need for a reminder)
// if the user has accepted the new version anyway, the page will reload
// and after page reload, the timer will not be started again, because there will no longer be a new version
timer(cfg.reminder * 60000, cfg.reminder * 60000)
.subscribe(() => {
this.bottomSheet.open(NewVersionComponent, { hasBackdrop: cfg.hasBackdrop });
});
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NewVersionService, deps: [{ token: SpaasConfigModel, optional: true }, { token: i1.MatBottomSheet }, { token: i2$1.SwUpdate }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NewVersionService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NewVersionService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: SpaasConfigModel, decorators: [{
type: Optional
}, {
type: Inject,
args: [SpaasConfigModel]
}] }, { type: i1.MatBottomSheet }, { type: i2$1.SwUpdate }] });
class PwaInstallComponent extends BaseComponent {
constructor(bottomSheetRef, pwaInstallService) {
super();
this.bottomSheetRef = bottomSheetRef;
this.pwaInstallService = pwaInstallService;
}
doInstall() {
this.pwaInstallService.doInstall();
this.bottomSheetRef.dismiss();
}
ignoreInstall() {
this.pwaInstallService.ignoreInstall();
this.bottomSheetRef.dismiss();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PwaInstallComponent, deps: [{ token: i1.MatBottomSheetRef }, { token: PwaInstallService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PwaInstallComponent, selector: "spaas-pwa-install", usesInheritance: true, ngImport: i0, template: "<div class=\"txt-center\">\n install as an app?\n <div class=\"flex center column-gap-10 pad-big-top\">\n <button class=\"error bold\" (click)=\"ignoreInstall()\">\n no thanx\n </button>\n <button class=\"primary bold\" (click)=\"doInstall()\">\n yes please\n </button>\n </div>\n</div>\n", styles: [""] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PwaInstallComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-pwa-install', template: "<div class=\"txt-center\">\n install as an app?\n <div class=\"flex center column-gap-10 pad-big-top\">\n <button class=\"error bold\" (click)=\"ignoreInstall()\">\n no thanx\n </button>\n <button class=\"primary bold\" (click)=\"doInstall()\">\n yes please\n </button>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.MatBottomSheetRef }, { type: PwaInstallService }] });
class PwaInstallService {
constructor(bottomSheet, lsService, platform) {
this.bottomSheet = bottomSheet;
this.lsService = lsService;
this.platform = platform;
this.LS_ALREADY_PROMPTED = 'installPrompted';
}
installPrompt() {
if (!this.platform.IOS) {
fromEvent(window, 'beforeinstallprompt')
.subscribe((e) => {
e.preventDefault();
this.promptEvent = e;
this.openBottomSheet();
});
}
else {
const isInStandaloneMode = ('standalone' in window.navigator) && (window.navigator['standalone']);
if (!isInStandaloneMode) {
this.openBottomSheet();
}
}
}
openBottomSheet() {
console.log('will prompt install');
if (this.lsService.getItem(this.LS_ALREADY_PROMPTED) === 'true') {
return;
}
timer(3000)
.pipe(take(1))
.subscribe(() => {
this.bottomSheet.open(PwaInstallComponent);
});
}
doInstall() {
this.promptEvent.prompt();
}
ignoreInstall() {
this.lsService.setItem(this.LS_ALREADY_PROMPTED, true);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PwaInstallService, deps: [{ token: i1.MatBottomSheet }, { token: LocalStorageService }, { token: i3$1.Platform }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PwaInstallService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PwaInstallService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1.MatBottomSheet }, { type: LocalStorageService }, { type: i3$1.Platform }] });
class AppInitService {
constructor(embedModeService, newVersionService, pwaInstallService, themeService) {
this.embedModeService = embedModeService;
this.newVersionService = newVersionService;
this.pwaInstallService = pwaInstallService;
this.themeService = themeService;
}
// initApp is called from the spaas.module using APP_INITIALIZER
initApp() {
this.embedModeService.setEmbedMode();
this.themeService.initTheme();
this.pwaInstallService.installPrompt();
this.newVersionService.getNewVersion();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AppInitService, deps: [{ token: EmbedModeService }, { token: NewVersionService }, { token: PwaInstallService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AppInitService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AppInitService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: EmbedModeService }, { type: NewVersionService }, { type: PwaInstallService }, { type: ThemeService }] });
class MenuComponent extends BaseComponent {
constructor(embedModeService, slideOutService, themeService) {
super();
this.embedModeService = embedModeService;
this.slideOutService = slideOutService;
this.themeService = themeService;
/** The menu items to render in the DOM. */
this.menuItems = [];
this.mmActive = false;
this.so = '';
this.isIframe = false;
this.isIframe = this.embedModeService.getEmbedMode().isIframe();
if (!this.isIframe) {
this.themeService.setBodyData(DATA__HAS_MENU);
}
this.onSo();
}
// ********************************************************************************************************
// LOAD
// ********************************************************************************************************
onSo() {
this.slideOutService.onNewSlideOut()
.pipe(takeUntil(this.onDestroy$))
.subscribe((so) => {
if (so) {
this.mmActive = false;
}
});
}
// ********************************************************************************************************
// UI
// ********************************************************************************************************
// EVENT LISTENERS
onToggleMenu() {
this.mmActive = !this.mmActive;
if (this.mmActive) {
// menu active: close open slide-outs
this.slideOutService.newSlideOut('');
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuComponent, deps: [{ token: EmbedModeService }, { token: SlideOutService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MenuComponent, selector: "spaas-menu", inputs: { menuItems: "menuItems" }, usesInheritance: true, ngImport: i0, template: "@if (!isIframe) {\r\n <div class=\"mm\">\r\n\r\n <div (click)=\"onToggleMenu()\"\r\n [class.active]=\"mmActive\"\r\n class=\"mm__burger\">\r\n <span></span>\r\n <span></span>\r\n </div>\r\n <div [class.visible]=\"mmActive\"\r\n class=\"mm__slide-out\">\r\n @if (mmActive) {\r\n <div class=\"mm__slide-out__items\">\r\n @for (item of menuItems; track item) {\r\n <div (click)=\"onToggleMenu()\"\r\n [routerLink]=\"item.rtrLink\"\r\n routerLinkActive=\"accent\">\r\n {{ item.label }}\r\n </div>\r\n }\r\n </div>\r\n <div class=\"mm__slide-out__logo\">\r\n <svg viewBox=\"0 0 512 256\">\r\n <use [attr.xlink:href]=\"'assets/images/logo-engie.svg#svg1'\"></use>\r\n </svg>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".mm__burger{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);color:hsla(var(--color-h),var(--color-s),var(--color-l),1);cursor:pointer;height:44px;left:0;position:fixed;top:2px;transition:background-color .4s;width:44px;z-index:9996}.mm__burger span{background-color:hsla(var(--color-h),var(--color-s),var(--color-l),1);border-radius:9px;display:block;height:3px;opacity:1;position:absolute;right:10px;transform:rotate(0);transition:.25s ease-in-out}.mm__burger span:nth-child(1){top:19px;width:24px}.mm__burger span:nth-child(2){top:24px;width:18px}.mm__burger.active span{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);height:3px}.mm__burger.active span:nth-child(1){top:20px;transform:rotate(45deg);width:24px}.mm__burger.active span:nth-child(2){top:24px;transform:rotate(-45deg);width:24px}.mm__slide-out{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),.94);height:100vh;padding:28px 88px;position:fixed;left:0;top:0;transform:translate3d(-100%,0,0);transition:transform .2s;width:100%;z-index:9995}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.mm__slide-out{padding:28px 44px}}.mm__slide-out.visible{transform:translateZ(0)}.mm__slide-out__items{animation:anim-fade-in .2s .2s forwards;opacity:0}.mm__slide-out__items>div{cursor:pointer;font-size:28px;font-family:BebasNeue,Lato,Arial,sans-serif;padding:7px;transition:transform .2s}.mm__slide-out__items>div:hover{transform:translate(7px)}.mm__slide-out__logo{bottom:14px;color:hsla(var(--bg-h),var(--bg-s),calc(var(--bg-l) + -4%),1);height:40vh;position:absolute;right:88px;width:80vh}.mm__slide-out__logo svg{height:100%;width:100%}\n"], dependencies: [{ kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i3.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-menu', template: "@if (!isIframe) {\r\n <div class=\"mm\">\r\n\r\n <div (click)=\"onToggleMenu()\"\r\n [class.active]=\"mmActive\"\r\n class=\"mm__burger\">\r\n <span></span>\r\n <span></span>\r\n </div>\r\n <div [class.visible]=\"mmActive\"\r\n class=\"mm__slide-out\">\r\n @if (mmActive) {\r\n <div class=\"mm__slide-out__items\">\r\n @for (item of menuItems; track item) {\r\n <div (click)=\"onToggleMenu()\"\r\n [routerLink]=\"item.rtrLink\"\r\n routerLinkActive=\"accent\">\r\n {{ item.label }}\r\n </div>\r\n }\r\n </div>\r\n <div class=\"mm__slide-out__logo\">\r\n <svg viewBox=\"0 0 512 256\">\r\n <use [attr.xlink:href]=\"'assets/images/logo-engie.svg#svg1'\"></use>\r\n </svg>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".mm__burger{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);color:hsla(var(--color-h),var(--color-s),var(--color-l),1);cursor:pointer;height:44px;left:0;position:fixed;top:2px;transition:background-color .4s;width:44px;z-index:9996}.mm__burger span{background-color:hsla(var(--color-h),var(--color-s),var(--color-l),1);border-radius:9px;display:block;height:3px;opacity:1;position:absolute;right:10px;transform:rotate(0);transition:.25s ease-in-out}.mm__burger span:nth-child(1){top:19px;width:24px}.mm__burger span:nth-child(2){top:24px;width:18px}.mm__burger.active span{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);height:3px}.mm__burger.active span:nth-child(1){top:20px;transform:rotate(45deg);width:24px}.mm__burger.active span:nth-child(2){top:24px;transform:rotate(-45deg);width:24px}.mm__slide-out{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),.94);height:100vh;padding:28px 88px;position:fixed;left:0;top:0;transform:translate3d(-100%,0,0);transition:transform .2s;width:100%;z-index:9995}@media screen and (min-width: 768px) and (max-width: 1024px) and (max-height: 1024px) and (orientation: portrait),screen and (max-width: 767px) and (orientation: portrait),screen and (max-height: 420px) and (orientation: landscape){.mm__slide-out{padding:28px 44px}}.mm__slide-out.visible{transform:translateZ(0)}.mm__slide-out__items{animation:anim-fade-in .2s .2s forwards;opacity:0}.mm__slide-out__items>div{cursor:pointer;font-size:28px;font-family:BebasNeue,Lato,Arial,sans-serif;padding:7px;transition:transform .2s}.mm__slide-out__items>div:hover{transform:translate(7px)}.mm__slide-out__logo{bottom:14px;color:hsla(var(--bg-h),var(--bg-s),calc(var(--bg-l) + -4%),1);height:40vh;position:absolute;right:88px;width:80vh}.mm__slide-out__logo svg{height:100%;width:100%}\n"] }]
}], ctorParameters: () => [{ type: EmbedModeService }, { type: SlideOutService }, { type: ThemeService }], propDecorators: { menuItems: [{
type: Input
}] } });
class ActiveRouteModel {
constructor(url, queryParams, params) {
this.params = params || {};
this.queryParams = queryParams || {};
this.url = url && url.startsWith('/') ? url.replace(/\//g, ' ') : '';
}
}
class ActiveRouteService {
constructor(configService, titleService) {
this.configService = configService;
this.titleService = titleService;
this.activeRoute = new ActiveRouteModel('', {}, {});
this.activeRoute$ = new ReplaySubject(1);
}
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newActiveRoute(url, queryParams, params) {
this.activeRoute = new ActiveRouteModel(url, queryParams, params);
this.titleService.setTitle(this.configService.getConfig().cleanAppName + ' - ' + this.activeRoute.url + ' | Engie');
this.activeRoute$.next(this.activeRoute);
}
onNewActiveRoute() {
return this.activeRoute$.asObservable();
}
getActiveRoute() {
return this.activeRoute;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ActiveRouteService, deps: [{ token: ConfigService }, { token: i2$2.Title }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ActiveRouteService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ActiveRouteService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: ConfigService }, { type: i2$2.Title }] });
class MenuSideBarComponent extends BaseComponent {
constructor(activeRouteService, configService, themeService) {
super();
this.activeRouteService = activeRouteService;
this.configService = configService;
this.themeService = themeService;
this.titleText = '';
this.env = '';
this.fullEnv = '';
// enable menu gutter and notify parent that iframe has menu
this.themeService.setBodyData(DATA__HAS_MENU);
this.setEnv();
}
ngOnInit() {
if (!this.titleText) {
this.titleText = this.activeRouteService.getActiveRoute()?.url;
}
}
setEnv() {
const config = this.configService.getConfig();
if (config.environment &&
!config.environment.startsWith('local') &&
!config.environment.startsWith('prod')) {
this.env = config.environment.slice(0, 3);
this.fullEnv = config.environment;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuSideBarComponent, deps: [{ token: ActiveRouteService }, { token: ConfigService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MenuSideBarComponent, selector: "spaas-menu-side-bar", inputs: { titleText: "titleText" }, usesInheritance: true, ngImport: i0, template: "<div class=\"mm-side-bar\">\r\n\r\n @if (env) {\r\n <div class=\"mm-side-bar__env {{env}}\"\r\n matTooltipPosition=\"right\"\r\n matTooltipClass=\"bg-error\"\r\n matTooltip=\"you are on the {{fullEnv | uppercase}} environment\">\r\n {{ env }}\r\n </div>\r\n }\r\n\r\n <div class=\"mm-side-bar__title\">\r\n {{ titleText }}\r\n </div>\r\n\r\n <ng-content select=\"spaas-menu-side-bar-icon\"></ng-content>\r\n\r\n</div>\r\n", styles: [".mm-side-bar{animation:anim-fade-in .4s .2s ease forwards;height:calc(100vh - 88px);left:0;opacity:0;overflow-x:hidden;overflow-y:auto;position:fixed;top:44px;width:44px;z-index:9994}.mm-side-bar__title,.mm-side-bar__env{font-family:BebasNeue,Lato,Arial,sans-serif;font-size:28px;text-align:right;text-orientation:sideways;transform:rotate(180deg);-webkit-user-select:none;user-select:none;width:44px;writing-mode:vertical-lr}.mm-side-bar__title{min-height:174px;padding:44px 0 7px 2px}@media screen and (max-height: 544px){.mm-side-bar__title{display:none}}.mm-side-bar__env{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);color:#fff;margin-bottom:7px;padding:14px 0 14px 2px}.mm-side-bar__env.acc,.mm-side-bar__env.hom{background-color:orange}.mm-side-bar__icon{cursor:pointer;font-family:BebasNeue,Lato,Arial,sans-serif;font-size:84%;height:34px;line-height:26px;padding:4px;position:relative;text-align:center;transition:color .4s;width:44px}.mm-side-bar__icon.with-hover:hover{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.with-divider{border-top:1px solid hsla(var(--bg-h),var(--bg-s),calc(var(--bg-l) + var(--table-even)),1);height:calc(34px + var(--margin-top) * .5);margin-top:calc(var(--margin-top) * .5);padding-top:calc(4px + var(--margin-top) * .5)}.mm-side-bar__icon:not(.with-divider){margin-top:var(--margin-top)}.mm-side-bar__icon.active{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.active:before,.mm-side-bar__icon.active:after{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);content:\"\";height:2px;position:absolute;right:2px;top:7px;width:10px;z-index:900}.mm-side-bar__icon.active:before{transform:rotate(45deg)}.mm-side-bar__icon.active:after{transform:rotate(-45deg)}.mm-side-bar__icon.disabled:before,.mm-side-bar__icon.disabled:after{background-color:hsla(var(--disabled-h),var(--disabled-s),var(--disabled-l),1)}\n"], dependencies: [{ kind: "directive", type: i4$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i6.UpperCasePipe, name: "uppercase" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuSideBarComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-menu-side-bar', template: "<div class=\"mm-side-bar\">\r\n\r\n @if (env) {\r\n <div class=\"mm-side-bar__env {{env}}\"\r\n matTooltipPosition=\"right\"\r\n matTooltipClass=\"bg-error\"\r\n matTooltip=\"you are on the {{fullEnv | uppercase}} environment\">\r\n {{ env }}\r\n </div>\r\n }\r\n\r\n <div class=\"mm-side-bar__title\">\r\n {{ titleText }}\r\n </div>\r\n\r\n <ng-content select=\"spaas-menu-side-bar-icon\"></ng-content>\r\n\r\n</div>\r\n", styles: [".mm-side-bar{animation:anim-fade-in .4s .2s ease forwards;height:calc(100vh - 88px);left:0;opacity:0;overflow-x:hidden;overflow-y:auto;position:fixed;top:44px;width:44px;z-index:9994}.mm-side-bar__title,.mm-side-bar__env{font-family:BebasNeue,Lato,Arial,sans-serif;font-size:28px;text-align:right;text-orientation:sideways;transform:rotate(180deg);-webkit-user-select:none;user-select:none;width:44px;writing-mode:vertical-lr}.mm-side-bar__title{min-height:174px;padding:44px 0 7px 2px}@media screen and (max-height: 544px){.mm-side-bar__title{display:none}}.mm-side-bar__env{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);color:#fff;margin-bottom:7px;padding:14px 0 14px 2px}.mm-side-bar__env.acc,.mm-side-bar__env.hom{background-color:orange}.mm-side-bar__icon{cursor:pointer;font-family:BebasNeue,Lato,Arial,sans-serif;font-size:84%;height:34px;line-height:26px;padding:4px;position:relative;text-align:center;transition:color .4s;width:44px}.mm-side-bar__icon.with-hover:hover{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.with-divider{border-top:1px solid hsla(var(--bg-h),var(--bg-s),calc(var(--bg-l) + var(--table-even)),1);height:calc(34px + var(--margin-top) * .5);margin-top:calc(var(--margin-top) * .5);padding-top:calc(4px + var(--margin-top) * .5)}.mm-side-bar__icon:not(.with-divider){margin-top:var(--margin-top)}.mm-side-bar__icon.active{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.active:before,.mm-side-bar__icon.active:after{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);content:\"\";height:2px;position:absolute;right:2px;top:7px;width:10px;z-index:900}.mm-side-bar__icon.active:before{transform:rotate(45deg)}.mm-side-bar__icon.active:after{transform:rotate(-45deg)}.mm-side-bar__icon.disabled:before,.mm-side-bar__icon.disabled:after{background-color:hsla(var(--disabled-h),var(--disabled-s),var(--disabled-l),1)}\n"] }]
}], ctorParameters: () => [{ type: ActiveRouteService }, { type: ConfigService }, { type: ThemeService }], propDecorators: { titleText: [{
type: Input
}] } });
class MenuSideBarIconComponent extends BaseComponent {
constructor(slideOutService) {
super();
this.slideOutService = slideOutService;
this.tooltipMsg = '';
this.withTopMargin = false;
this.withHover = true;
this.withDivider = false;
// GENERIC STATE INDICATORS FROM PARENT
this.isDisabled = false;
// TYPE TO INDICATE WHAT THIS ICON SHOULD DO WHEN CLICKED
this.soType = '';
// MOUSE-EVENT EMITTER IF NOT SO-TYPE
this.clicked = new EventEmitter();
this.activeSo = '';
this.isActive = false;
this.getSoActive();
}
getSoActive() {
this.slideOutService.onNewSlideOut()
.pipe(takeUntil(this.onDestroy$))
.subscribe((so) => {
this.activeSo = so;
this.isActive = this.soType !== '' && this.activeSo === this.soType;
});
}
onClick(e) {
if (this.soType) {
this.slideOutService.newSlideOut(this.soType);
}
else if (!this.isDisabled) {
this.clicked.emit(e);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuSideBarIconComponent, deps: [{ token: SlideOutService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: MenuSideBarIconComponent, selector: "spaas-menu-side-bar-icon", inputs: { tooltipMsg: "tooltipMsg", withTopMargin: "withTopMargin", withHover: "withHover", withDivider: "withDivider", isDisabled: "isDisabled", soType: "soType" }, outputs: { clicked: "clicked" }, usesInheritance: true, ngImport: i0, template: "<div class=\"mm-side-bar__icon\"\r\n [style.--margin-top]=\"withTopMargin || withDivider ? '24px' : '0px'\"\r\n [class.active]=\"isActive\"\r\n [class.disabled]=\"isDisabled\"\r\n [class.with-hover]=\"withHover\"\r\n [class.with-divider]=\"withDivider\"\r\n [matTooltip]=\"tooltipMsg\"\r\n matTooltipPosition=\"right\"\r\n (click)=\"onClick($event)\">\r\n <ng-content></ng-content>\r\n</div>\r\n", styles: [".mm-side-bar{animation:anim-fade-in .4s .2s ease forwards;height:calc(100vh - 88px);left:0;opacity:0;overflow-x:hidden;overflow-y:auto;position:fixed;top:44px;width:44px;z-index:9994}.mm-side-bar__title,.mm-side-bar__env{font-family:BebasNeue,Lato,Arial,sans-serif;font-size:28px;text-align:right;text-orientation:sideways;transform:rotate(180deg);-webkit-user-select:none;user-select:none;width:44px;writing-mode:vertical-lr}.mm-side-bar__title{min-height:174px;padding:44px 0 7px 2px}@media screen and (max-height: 544px){.mm-side-bar__title{display:none}}.mm-side-bar__env{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);color:#fff;margin-bottom:7px;padding:14px 0 14px 2px}.mm-side-bar__env.acc,.mm-side-bar__env.hom{background-color:orange}.mm-side-bar__icon{cursor:pointer;font-family:BebasNeue,Lato,Arial,sans-serif;font-size:84%;height:34px;line-height:26px;padding:4px;position:relative;text-align:center;transition:color .4s;width:44px}.mm-side-bar__icon.with-hover:hover{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.with-divider{border-top:1px solid hsla(var(--bg-h),var(--bg-s),calc(var(--bg-l) + var(--table-even)),1);height:calc(34px + var(--margin-top) * .5);margin-top:calc(var(--margin-top) * .5);padding-top:calc(4px + var(--margin-top) * .5)}.mm-side-bar__icon:not(.with-divider){margin-top:var(--margin-top)}.mm-side-bar__icon.active{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.active:before,.mm-side-bar__icon.active:after{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);content:\"\";height:2px;position:absolute;right:2px;top:7px;width:10px;z-index:900}.mm-side-bar__icon.active:before{transform:rotate(45deg)}.mm-side-bar__icon.active:after{transform:rotate(-45deg)}.mm-side-bar__icon.disabled:before,.mm-side-bar__icon.disabled:after{background-color:hsla(var(--disabled-h),var(--disabled-s),var(--disabled-l),1)}\n"], dependencies: [{ kind: "directive", type: i4$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuSideBarIconComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-menu-side-bar-icon', template: "<div class=\"mm-side-bar__icon\"\r\n [style.--margin-top]=\"withTopMargin || withDivider ? '24px' : '0px'\"\r\n [class.active]=\"isActive\"\r\n [class.disabled]=\"isDisabled\"\r\n [class.with-hover]=\"withHover\"\r\n [class.with-divider]=\"withDivider\"\r\n [matTooltip]=\"tooltipMsg\"\r\n matTooltipPosition=\"right\"\r\n (click)=\"onClick($event)\">\r\n <ng-content></ng-content>\r\n</div>\r\n", styles: [".mm-side-bar{animation:anim-fade-in .4s .2s ease forwards;height:calc(100vh - 88px);left:0;opacity:0;overflow-x:hidden;overflow-y:auto;position:fixed;top:44px;width:44px;z-index:9994}.mm-side-bar__title,.mm-side-bar__env{font-family:BebasNeue,Lato,Arial,sans-serif;font-size:28px;text-align:right;text-orientation:sideways;transform:rotate(180deg);-webkit-user-select:none;user-select:none;width:44px;writing-mode:vertical-lr}.mm-side-bar__title{min-height:174px;padding:44px 0 7px 2px}@media screen and (max-height: 544px){.mm-side-bar__title{display:none}}.mm-side-bar__env{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);color:#fff;margin-bottom:7px;padding:14px 0 14px 2px}.mm-side-bar__env.acc,.mm-side-bar__env.hom{background-color:orange}.mm-side-bar__icon{cursor:pointer;font-family:BebasNeue,Lato,Arial,sans-serif;font-size:84%;height:34px;line-height:26px;padding:4px;position:relative;text-align:center;transition:color .4s;width:44px}.mm-side-bar__icon.with-hover:hover{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.with-divider{border-top:1px solid hsla(var(--bg-h),var(--bg-s),calc(var(--bg-l) + var(--table-even)),1);height:calc(34px + var(--margin-top) * .5);margin-top:calc(var(--margin-top) * .5);padding-top:calc(4px + var(--margin-top) * .5)}.mm-side-bar__icon:not(.with-divider){margin-top:var(--margin-top)}.mm-side-bar__icon.active{color:hsla(var(--primary-h),var(--primary-s),var(--primary-l),1)}.mm-side-bar__icon.active:before,.mm-side-bar__icon.active:after{background-color:hsla(var(--error-h),var(--error-s),var(--error-l),1);content:\"\";height:2px;position:absolute;right:2px;top:7px;width:10px;z-index:900}.mm-side-bar__icon.active:before{transform:rotate(45deg)}.mm-side-bar__icon.active:after{transform:rotate(-45deg)}.mm-side-bar__icon.disabled:before,.mm-side-bar__icon.disabled:after{background-color:hsla(var(--disabled-h),var(--disabled-s),var(--disabled-l),1)}\n"] }]
}], ctorParameters: () => [{ type: SlideOutService }], propDecorators: { tooltipMsg: [{
type: Input
}], withTopMargin: [{
type: Input
}], withHover: [{
type: Input
}], withDivider: [{
type: Input
}], isDisabled: [{
type: Input
}], soType: [{
type: Input
}], clicked: [{
type: Output
}] } });
class EmbedComponent extends BaseComponent {
constructor(embedModeService, sanitizer) {
super();
this.embedModeService = embedModeService;
this.sanitizer = sanitizer;
/** The url of the application to embed */
this.url = '';
/** Use as fullscreen component, or within parent div */
this.takeOver = false;
}
ngOnInit() {
if (this.url) {
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.url);
}
}
ngAfterViewInit() {
this.embedModeService.embeddedWindow = this.iframeDiv?.nativeElement.contentWindow || null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbedComponent, deps: [{ token: EmbedModeService }, { token: i2$2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EmbedComponent, selector: "spaas-embed", inputs: { url: "url", takeOver: "takeOver" }, viewQueries: [{ propertyName: "iframeDiv", first: true, predicate: ["iframe_div"], descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (safeUrl) {\r\n <div [class.fullscreen]=\"takeOver\"\r\n class=\"embed flex\">\r\n <iframe #iframe_div [src]=\"safeUrl\"></iframe>\r\n </div>\r\n}\r\n", styles: [".embed{height:100%;width:100%}.embed.fullscreen{height:100vh;width:100vw;left:0;position:fixed;top:0;z-index:9994}.embed iframe{border:none;height:100%;width:100%}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbedComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-embed', template: "@if (safeUrl) {\r\n <div [class.fullscreen]=\"takeOver\"\r\n class=\"embed flex\">\r\n <iframe #iframe_div [src]=\"safeUrl\"></iframe>\r\n </div>\r\n}\r\n", styles: [".embed{height:100%;width:100%}.embed.fullscreen{height:100vh;width:100vw;left:0;position:fixed;top:0;z-index:9994}.embed iframe{border:none;height:100%;width:100%}\n"] }]
}], ctorParameters: () => [{ type: EmbedModeService }, { type: i2$2.DomSanitizer }], propDecorators: { url: [{
type: Input
}], takeOver: [{
type: Input
}], iframeDiv: [{
type: ViewChild,
args: ['iframe_div']
}] } });
class SyncScrollModel {
constructor() {
this.absX = 0;
this.ratioVisible = 0;
this.relX = 0;
}
}
class SyncScrollService {
constructor() {
this.scrollX$ = new BehaviorSubject({ absX: 0, relX: 0, ratioVisible: 1 });
}
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newScrollX(x) {
// console.log(x);
this.scrollX$.next(x);
}
onNewScrollX() {
return this.scrollX$.asObservable();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SyncScrollService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SyncScrollService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SyncScrollService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
class SyncScrollDirective {
constructor(el, syncScrollService) {
this.el = el;
this.syncScrollService = syncScrollService;
this.curSync = new SyncScrollModel();
this.onDestroy$ = new Subject();
this.syncScrollService.onNewScrollX()
.pipe(takeUntil(this.onDestroy$))
// .pipe(debounceTime(40)) // TODO: evaluate whether to debounce or not
.subscribe((x) => {
this.curSync = x;
this.el.nativeElement.scrollLeft = x.absX;
});
}
onScroll(e) {
// only broadcast event if different scroll (to prevent slave components from also firing an update)
if (e.target.scrollLeft !== this.curSync?.absX) {
this.syncScrollService.newScrollX({
absX: e.target.scrollLeft,
relX: e.target.scrollLeft / e.target.scrollWidth,
ratioVisible: e.target.clientWidth / e.target.scrollWidth,
});
}
}
ngOnDestroy() {
this.onDestroy$.next();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SyncScrollDirective, deps: [{ token: i0.ElementRef }, { token: SyncScrollService }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: SyncScrollDirective, selector: "[spaasSyncScroll]", host: { listeners: { "scroll": "onScroll($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SyncScrollDirective, decorators: [{
type: Directive,
args: [{
selector: '[spaasSyncScroll]'
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: SyncScrollService }], propDecorators: { onScroll: [{
type: HostListener,
args: ['scroll', ['$event']]
}] } });
class ResizerComponent {
constructor(localStorageService, sessionStorageService) {
this.localStorageService = localStorageService;
this.sessionStorageService = sessionStorageService;
this.sizePx = 324;
this.sizePxChange = new EventEmitter();
this.minSizePx = 144;
this.maxSizePx = 9999;
this.resizeAxis = 'y';
this.storage = 'local';
this.storageId = '';
}
ngOnInit() {
if (Number.isNaN(Number(this.sizePx))) {
throw new Error('the value you provided for sizePx is not a number');
}
}
onDragResizeEnd(e) {
e.source.reset();
const inc = this.resizeAxis === 'y' ? e.distance.y : e.distance.x;
this.sizePx += inc;
this.sizePx = Math.min(Math.max(this.sizePx, this.minSizePx), this.maxSizePx);
this.sizePxChange.emit(this.sizePx);
if (this.storageId) {
if (this.storage === 'local') {
this.localStorageService.setItem(this.storageId, this.sizePx);
}
else {
this.sessionStorageService.setItem(this.storageId, this.sizePx);
}
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ResizerComponent, deps: [{ token: LocalStorageService }, { token: SessionStorageService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ResizerComponent, selector: "spaas-resizer", inputs: { sizePx: "sizePx", minSizePx: "minSizePx", maxSizePx: "maxSizePx", resizeAxis: "resizeAxis", storage: "storage", storageId: "storageId" }, outputs: { sizePxChange: "sizePxChange" }, ngImport: i0, template: "<div (cdkDragEnded)=\"onDragResizeEnd($event)\"\r\n [cdkDragLockAxis]=\"resizeAxis\"\r\n [class.resize-x]=\"resizeAxis === 'x'\"\r\n cdkDrag\r\n class=\"resizer\">\r\n</div>\r\n", styles: [".resizer{bottom:0;cursor:ns-resize;height:8px;left:0;position:absolute;transition:background-color .4s;width:100%;z-index:9000}.resizer:before{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);border-color:hsla(var(--disabled-h),var(--disabled-s),var(--disabled-l),1);border-style:solid;border-width:1px 0 1px 0;content:\"\";height:2px;left:calc(50% - 22px);position:absolute;top:2px;width:44px}.resizer:hover,.resizer.cdk-drag-dragging{background-color:hsla(var(--color-h),var(--color-s),var(--color-l),.2)}.resizer.resize-x{cursor:ew-resize;height:100%;left:auto;right:0;width:8px}.resizer.resize-x:before{border-width:0 1px 0 1px;width:2px;height:44px;left:2px;top:calc(50% - 22px)}\n"], dependencies: [{ kind: "directive", type: i3$2.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ResizerComponent, decorators: [{
type: Component,
args: [{ selector: 'spaas-resizer', template: "<div (cdkDragEnded)=\"onDragResizeEnd($event)\"\r\n [cdkDragLockAxis]=\"resizeAxis\"\r\n [class.resize-x]=\"resizeAxis === 'x'\"\r\n cdkDrag\r\n class=\"resizer\">\r\n</div>\r\n", styles: [".resizer{bottom:0;cursor:ns-resize;height:8px;left:0;position:absolute;transition:background-color .4s;width:100%;z-index:9000}.resizer:before{background-color:hsla(var(--bg-h),var(--bg-s),var(--bg-l),1);border-color:hsla(var(--disabled-h),var(--disabled-s),var(--disabled-l),1);border-style:solid;border-width:1px 0 1px 0;content:\"\";height:2px;left:calc(50% - 22px);position:absolute;top:2px;width:44px}.resizer:hover,.resizer.cdk-drag-dragging{background-color:hsla(var(--color-h),var(--color-s),var(--color-l),.2)}.resizer.resize-x{cursor:ew-resize;height:100%;left:auto;right:0;width:8px}.resizer.resize-x:before{border-width:0 1px 0 1px;width:2px;height:44px;left:2px;top:calc(50% - 22px)}\n"] }]
}], ctorParameters: () => [{ type: LocalStorageService }, { type: SessionStorageService }], propDecorators: { sizePx: [{
type: Input
}], sizePxChange: [{
type: Output
}], minSizePx: [{
type: Input
}], maxSizePx: [{
type: Input
}], resizeAxis: [{
type: Input
}], storage: [{
type: Input
}], storageId: [{
type: Input
}] } });
/**
* The SpaasModule offers all building blocks needed for a bespoke SPaaS SPA.
*
* <spaas-okta-intro> to display an intro for okta handling
* <spaas-preloader> to add a preloader to your app
* OktaAuthGuard to protect your routing
* OktaAuthInterceptor to automatically include the token in your http requests
*/
class SpaasModule {
static forRoot(config) {
return {
ngModule: SpaasModule,
providers: [{ provide: SpaasConfigModel, useValue: config }]
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SpaasModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: SpaasModule, declarations: [BaseComponent,
BgComponent,
EmbedComponent,
IntroComponent,
MenuComponent,
MenuSideBarComponent,
MenuSideBarIconComponent,
NewVersionComponent,
PreloaderComponent,
PwaInstallComponent,
ResizerComponent,
SettingsComponent,
SlideOutComponent,
SlideOutContentComponent,
SyncScrollDirective,
ToggleComponent], imports: [CommonModule,
FormsModule,
HttpClientModule,
MaterialModule,
ReactiveFormsModule,
RouterModule, i2$1.ServiceWorkerModule], exports: [BaseComponent,
BgComponent,
EmbedComponent,
IntroComponent,
MenuComponent,
MenuSideBarComponent,
MenuSideBarIconComponent,
PreloaderComponent,
ResizerComponent,
SettingsComponent,
SlideOutComponent,
SlideOutContentComponent,
SyncScrollDirective,
ToggleComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SpaasModule, providers: [
{
provide: APP_INITIALIZER,
useFactory: (appInitService) => () => {
return appInitService.initApp();
},
deps: [AppInitService],
multi: true
},
{ provide: HTTP_INTERCEPTORS, useClass: OktaAuthInterceptor, multi: true },
], imports: [CommonModule,
FormsModule,
HttpClientModule,
MaterialModule,
ReactiveFormsModule,
RouterModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: !window.location.host.includes('localhost') })] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SpaasModule, decorators: [{
type: NgModule,
args: [{
declarations: [
BaseComponent,
BgComponent,
EmbedComponent,
IntroComponent,
MenuComponent,
MenuSideBarComponent,
MenuSideBarIconComponent,
NewVersionComponent,
PreloaderComponent,
PwaInstallComponent,
ResizerComponent,
SettingsComponent,
SlideOutComponent,
SlideOutContentComponent,
SyncScrollDirective,
ToggleComponent,
],
exports: [
BaseComponent,
BgComponent,
EmbedComponent,
IntroComponent,
MenuComponent,
MenuSideBarComponent,
MenuSideBarIconComponent,
PreloaderComponent,
ResizerComponent,
SettingsComponent,
SlideOutComponent,
SlideOutContentComponent,
SyncScrollDirective,
ToggleComponent,
],
imports: [
CommonModule,
FormsModule,
HttpClientModule,
MaterialModule,
ReactiveFormsModule,
RouterModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: !window.location.host.includes('localhost') }),
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: (appInitService) => () => {
return appInitService.initApp();
},
deps: [AppInitService],
multi: true
},
{ provide: HTTP_INTERCEPTORS, useClass: OktaAuthInterceptor, multi: true },
]
}]
}] });
class EmbeddedOnlyGuard {
constructor(embedModeService, router) {
this.embedModeService = embedModeService;
this.router = router;
}
isIframe() {
const isIframe = this.embedModeService.getEmbedMode().isIframe();
if (!isIframe) {
// set default url to "home". If "home" does not exist, router will fall back to redirect config
return this.router.parseUrl('/home');
}
return isIframe;
}
// CAN LOAD: FOR FEATURE MODULES SECURITY
canLoad(route) {
return this.isIframe();
}
// CAN ACTIVATE: FOR ROUTES SECURITY
canActivate(route, state) {
return this.isIframe();
}
canActivateChild(route, state) {
return this.canActivate(route, state);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbeddedOnlyGuard, deps: [{ token: EmbedModeService }, { token: i3.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbeddedOnlyGuard, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EmbeddedOnlyGuard, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: EmbedModeService }, { type: i3.Router }] });
class OktaAuthGuard {
constructor(activeRouteService, oktaService) {
this.activeRouteService = activeRouteService;
this.oktaService = oktaService;
}
// CAN LOAD: FOR FEATURE MODULES SECURITY
canLoad(route) {
return !!this.oktaService.getCachedAccessToken();
}
// CAN ACTIVATE: FOR ROUTES SECURITY
canActivate(route, state) {
this.activeRouteService.newActiveRoute(state.url, route.queryParams, route.params);
return !!this.oktaService.getCachedAccessToken();
}
canActivateChild(route, state) {
return this.canActivate(route, state);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaAuthGuard, deps: [{ token: ActiveRouteService }, { token: OktaService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaAuthGuard, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OktaAuthGuard, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: ActiveRouteService }, { type: OktaService }] });
/**
* Enum used as msgType in the PostMessageModel. Will be used to know how to respond to a certain message.
*/
var PostMessageEnum;
(function (PostMessageEnum) {
PostMessageEnum["getTheme"] = "1_getTheme";
PostMessageEnum["setTheme"] = "2_setTheme";
})(PostMessageEnum || (PostMessageEnum = {}));
/**
* Model used to post messages from and to an iframe child app.
*
* @property {PostMessageEnum} msgType the type defined by PostMessageEnum. Will be used to know how
* to respond to a certain message
* @property {any} msgData any data you want to include in the PostMessage
*/
class PostMessageModel {
constructor(type, data = '') {
this.msgType = type;
this.msgData = data;
}
}
const REGEX_DEC = '^-?(0|[1-9]\\d*)(\\.\\d+)?$';
const REGEX_DEC_OR_HASH = '^[#]{1}$|^-?(0|[1-9]\\d*)(\\.\\d+)?$';
const REGEX_INT = '^[-]?\\d*$'; // only integers, also negative
const REGEX_INT_OR_1DEC = '^-?\\d+([.]\\d)?$';
const REGEX_POSITIVE_INT = '^\\d+$';
class PostMessageService {
constructor(embedModeService, settingsService, themeService) {
this.embedModeService = embedModeService;
this.settingsService = settingsService;
this.themeService = themeService;
this.onEmbedMode();
this.startListening();
}
onEmbedMode() {
this.embedModeService.onNewEmbedMode()
.subscribe((mode) => {
if (mode?.isIframe()) {
// ask for theme from parent
this.postMessage(window.parent, new PostMessageModel(PostMessageEnum.getTheme));
}
});
}
startListening() {
this.listenToPostMessages()
.subscribe((msg) => {
// **************************************
// MESSAGES TO BE PROCESSED BY PARENT
// request for theme
if (msg.msgType === PostMessageEnum.getTheme) {
console.log('parent received request for theme');
this.postMessage(this.embedModeService.embeddedWindow, new PostMessageModel(PostMessageEnum.setTheme, this.themeService.getActiveTheme().name));
}
// **************************************
// MESSAGES TO BE PROCESSED BY CHILD
// set theme received from parent
if (msg.msgType === PostMessageEnum.setTheme) {
console.log('child received theme from parent: ' + msg.msgData);
const settings = this.settingsService.getSettings();
settings.dark = msg.msgData === 'dark';
this.settingsService.newSettings(settings);
this.themeService.setActiveTheme(msg.msgData);
}
});
}
/**
* When you've embedded a SPaaS app using an iframe, you can use this method to listen to
* the messages it will post. Some messages are automatically processed, check the doc.
*/
listenToPostMessages() {
return fromEvent(window, 'message')
.pipe(map((m) => new PostMessageModel(m?.data?.msgType || '', m?.data?.msgData || '')));
}
/**
* When you've embedded a SPaaS app in an iframe and want to reply to some messages it will post,
* you can use this method to do so.
*
* @param source the MessageEventSource of the iframe. You can retrieve its source by using
* '@ViewChild('your_iframe_div') iframeDiv: ElementRef;' and then get 'this.iframeDiv.nativeElement.contentWindow'
* @param msg the actual message to send to the child app in the iframe
*/
postMessage(source, msg) {
if (source) {
source.postMessage(msg, { targetOrigin: '*' });
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PostMessageService, deps: [{ token: EmbedModeService }, { token: SettingsService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PostMessageService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PostMessageService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: EmbedModeService }, { type: SettingsService }, { type: ThemeService }] });
class SnackbarService {
static getConfirmId(type) {
return type + '_' + performance.now();
}
constructor(snackbar, zone) {
this.snackbar = snackbar;
this.zone = zone;
this.DEF_DURATION = 6000;
this.duration = this.DEF_DURATION;
this.confirm$ = new BehaviorSubject('');
this.sbConfig = new MatSnackBarConfig();
}
/**
* Shows a Material snackbar.
*
* @param message the message to show in the snackbar
* @param action (optional) the message to show in the snackbar action button. When no action is provided, the button
* will not show
* @param append (optional, default true) specifies whether to append the new snackbar message to the previous one
* if a previous snackbar is still active
* @param duration (optional) set a one-time specific duration in milliseconds, just for this snackbar instance
*/
message(message, action = '', append = true, duration = this.duration) {
this.open(message, action, append, duration, false);
}
/**
* Shows a Material snackbar as an error (red background).
*
* @param message the message to show in the snackbar
* @param action (optional) the message to show in the snackbar action button. When no action is provided, the button
* will not show
* @param append (optional, default true) specifies whether to append the new snackbar message to the previous one
* if a previous snackbar is still active
* @param duration (optional) set a one-time specific duration in milliseconds, just for this snackbar instance
*/
error(message, action = '', append = true, duration = this.duration) {
this.open(message, action, append, duration, true);
}
open(message, action, append, duration, isError) {
this.zone.run(() => {
if (append && this.snackbar._openedSnackBarRef?.instance?.data?.message) {
message = this.snackbar._openedSnackBarRef.instance.data.message + '\n------\n' + message;
}
// reset panelClass in case the previous snackbar was an error
this.sbConfig.panelClass = isError ? 'bg-error' : '';
this.sbConfig.duration = duration;
this.snackbar.open(message, action, this.sbConfig);
});
}
/**
* Shows a Material snackbar which awaits confirmation.
*
* @param message the message to show in the snackbar
* @param action the message to show in the snackbar action button. When no action is provided, the button
* will show "Got it"
* @param confirmId a unique identifier by which you can recognise the confirmation response
* that you get when listening to "onNewConfirm". Use static method "SnackbarService.getConfirmId" to get a unique id.
* @param duration (optional) set a one-time specific duration in milliseconds, just for this snackbar instance
*/
confirm(message, action = 'Got it', confirmId, duration = this.duration) {
this.zone.run(() => {
// reset panelClass in case the previous snackbar was an error
this.sbConfig.panelClass = '';
this.sbConfig.duration = duration;
const snack = this.snackbar.open(message, action, this.sbConfig);
snack.onAction()
.subscribe(() => {
this.newConfirm(confirmId);
});
});
}
/**
* Set the global duration for all snackbars
*
* @param ms duration in milliseconds
*/
setDuration(ms) {
this.duration = ms;
}
/**
* Reset the global duration for all snackbars to default (6000)
*/
resetDuration() {
this.duration = this.DEF_DURATION;
}
// ********************************************************************************************************
// BROADCAST DATA
// ********************************************************************************************************
newConfirm(newConfirm) {
this.confirm$.next(newConfirm);
}
onNewConfirm() {
return this.confirm$.asObservable();
}
close() {
this.zone.run(() => {
this.snackbar.dismiss();
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SnackbarService, deps: [{ token: i1$1.MatSnackBar }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SnackbarService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SnackbarService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1$1.MatSnackBar }, { type: i0.NgZone }] });
// MODULE
/**
* Generated bundle index. Do not edit.
*/
export { ActiveRouteService, BaseComponent, BgComponent, ConfigService, DATA__HAS_MENU, DATA__THEME_DARK, DATA__THEME_LIGHT, EmbedComponent, EmbeddedOnlyGuard, IntroComponent, LocalStorageService, MenuComponent, MenuSideBarComponent, MenuSideBarIconComponent, NewVersionConfigModel, NewVersionService, OktaAuthGuard, OktaConfigModel, OktaService, OktaUserModel, PostMessageEnum, PostMessageModel, PostMessageService, PreloaderComponent, PreloaderService, REGEX_DEC, REGEX_DEC_OR_HASH, REGEX_INT, REGEX_INT_OR_1DEC, REGEX_POSITIVE_INT, ResizerComponent, SessionStorageService, SettingsComponent, SettingsModel, SettingsService, SlideOutComponent, SlideOutContentComponent, SlideOutService, SnackbarService, SpaasConfigModel, SpaasExtConfigModel, SpaasModule, SyncScrollDirective, SyncScrollService, ThemeModel, ThemeService, ToggleComponent, UtilsService, dark, light };
//# sourceMappingURL=ngx-gem-spaas.mjs.map