Merge remote-tracking branch 'upstream/main' into w2p-84367_Various-small-changes

This commit is contained in:
Yura Bondarenko
2022-02-09 16:43:14 +01:00
804 changed files with 50248 additions and 26437 deletions

View File

@@ -11,9 +11,8 @@ import {
OnChanges
} from '@angular/core';
import { hasValue, isNotEmpty } from '../empty.util';
import { Subscription } from 'rxjs';
import { from as fromPromise, Observable, of as observableOf, Subscription } from 'rxjs';
import { ThemeService } from './theme.service';
import { fromPromise } from 'rxjs/internal-compatibility';
import { catchError, switchMap, map } from 'rxjs/operators';
import { GenericConstructor } from '../../core/shared/generic-constructor';
@@ -72,31 +71,27 @@ export abstract class ThemedComponent<T> implements OnInit, OnDestroy, OnChanges
this.lazyLoadSub.unsubscribe();
}
this.lazyLoadSub =
fromPromise(this.importThemedComponent(this.themeService.getThemeName())).pipe(
// if there is no themed version of the component an exception is thrown,
// catch it and return null instead
catchError(() => [null]),
switchMap((themedFile: any) => {
if (hasValue(themedFile) && hasValue(themedFile[this.getComponentName()])) {
// if the file is not null, and exports a component with the specified name,
// return that component
return [themedFile[this.getComponentName()]];
} else {
// otherwise import and return the default component
return fromPromise(this.importUnthemedComponent()).pipe(
map((unthemedFile: any) => {
return unthemedFile[this.getComponentName()];
})
);
}
}),
).subscribe((constructor: GenericConstructor<T>) => {
const factory = this.resolver.resolveComponentFactory(constructor);
this.compRef = this.vcr.createComponent(factory);
this.connectInputsAndOutputs();
this.cdr.markForCheck();
});
this.lazyLoadSub = this.resolveThemedComponent(this.themeService.getThemeName()).pipe(
switchMap((themedFile: any) => {
if (hasValue(themedFile) && hasValue(themedFile[this.getComponentName()])) {
// if the file is not null, and exports a component with the specified name,
// return that component
return [themedFile[this.getComponentName()]];
} else {
// otherwise import and return the default component
return fromPromise(this.importUnthemedComponent()).pipe(
map((unthemedFile: any) => {
return unthemedFile[this.getComponentName()];
})
);
}
}),
).subscribe((constructor: GenericConstructor<T>) => {
const factory = this.resolver.resolveComponentFactory(constructor);
this.compRef = this.vcr.createComponent(factory);
this.connectInputsAndOutputs();
this.cdr.markForCheck();
});
}
protected destroyComponentInstance(): void {
@@ -116,4 +111,32 @@ export abstract class ThemedComponent<T> implements OnInit, OnDestroy, OnChanges
});
}
}
/**
* Attempt to import this component from the current theme or a theme it {@link NamedThemeConfig.extends}.
* Recurse until we succeed or when until we run out of themes to fall back to.
*
* @param themeName The name of the theme to check
* @param checkedThemeNames The list of theme names that are already checked
* @private
*/
private resolveThemedComponent(themeName?: string, checkedThemeNames: string[] = []): Observable<any> {
if (isNotEmpty(themeName)) {
return fromPromise(this.importThemedComponent(themeName)).pipe(
catchError(() => {
// Try the next ancestor theme instead
const nextTheme = this.themeService.getThemeConfigFor(themeName)?.extends;
const nextCheckedThemeNames = [...checkedThemeNames, themeName];
if (checkedThemeNames.includes(nextTheme)) {
throw new Error('Theme extension cycle detected: ' + [...nextCheckedThemeNames, nextTheme].join(' -> '));
} else {
return this.resolveThemedComponent(nextTheme, nextCheckedThemeNames);
}
}),
);
} else {
// If we got here, we've failed to import this component from any ancestor theme → fall back to unthemed
return observableOf(null);
}
}
}