83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
import { matchesNavigationConditions, type UnleashChecker } from "./navigation";
|
||
import type {
|
||
FunnelAnswers,
|
||
ScreenDefinition,
|
||
ScreenVariantDefinition,
|
||
} from "./types";
|
||
|
||
function cloneScreen<T>(screen: T): T {
|
||
if (typeof globalThis.structuredClone === "function") {
|
||
return globalThis.structuredClone(screen);
|
||
}
|
||
|
||
return JSON.parse(JSON.stringify(screen)) as T;
|
||
}
|
||
|
||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||
}
|
||
|
||
function deepMerge<T>(target: T, source: Partial<T> | undefined): T {
|
||
if (!source) {
|
||
return target;
|
||
}
|
||
|
||
const result: T = Array.isArray(target)
|
||
? ([...target] as unknown as T)
|
||
: ({ ...(target as Record<string, unknown>) } as T);
|
||
|
||
for (const key of Object.keys(source) as (keyof T)[]) {
|
||
const sourceValue = source[key];
|
||
|
||
if (sourceValue === undefined) {
|
||
continue;
|
||
}
|
||
|
||
const targetValue = (result as Record<string, unknown>)[key as unknown as string];
|
||
|
||
if (isPlainObject(sourceValue)) {
|
||
const baseValue = isPlainObject(targetValue) ? targetValue : {};
|
||
(result as Record<string, unknown>)[key as unknown as string] = deepMerge(
|
||
baseValue,
|
||
sourceValue as Record<string, unknown>
|
||
) as unknown as T[keyof T];
|
||
continue;
|
||
}
|
||
|
||
(result as Record<string, unknown>)[key as unknown as string] = sourceValue as unknown as T[keyof T];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
function applyScreenOverrides<T extends ScreenDefinition>(
|
||
screen: T,
|
||
overrides: ScreenVariantDefinition<T>["overrides"]
|
||
): T {
|
||
const cloned = cloneScreen(screen);
|
||
// DeepPartial совместим с deepMerge, так как deepMerge обрабатывает вложенные объекты
|
||
return deepMerge(cloned, overrides as unknown as Partial<T>);
|
||
}
|
||
|
||
export function resolveScreenVariant<T extends ScreenDefinition>(
|
||
screen: T,
|
||
answers: FunnelAnswers,
|
||
allScreens?: ScreenDefinition[],
|
||
unleashChecker?: UnleashChecker
|
||
): T {
|
||
const variants = (screen as T & { variants?: ScreenVariantDefinition<T>[] }).variants;
|
||
|
||
if (!variants || variants.length === 0) {
|
||
return screen;
|
||
}
|
||
|
||
for (const variant of variants) {
|
||
// Передаем allScreens и unleashChecker для правильной проверки условий
|
||
if (matchesNavigationConditions(variant.conditions, answers, allScreens, unleashChecker)) {
|
||
return applyScreenOverrides(screen, variant.overrides);
|
||
}
|
||
}
|
||
|
||
return screen;
|
||
}
|