w-funnel/src/lib/funnel/mappers.tsx
dev.daminik00 0fc1dc756e admin
2025-09-27 05:48:42 +02:00

214 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { TypographyProps } from "@/components/ui/Typography/Typography";
import type { MainButtonProps } from "@/components/ui/MainButton/MainButton";
import type {
HeaderDefinition,
HeaderProgressDefinition,
ListOptionDefinition,
SelectionType,
TypographyVariant,
BottomActionButtonDefinition,
ScreenDefinition,
} from "./types";
import type { LayoutQuestionProps } from "@/components/layout/LayoutQuestion/LayoutQuestion";
import type { ActionButtonProps } from "@/components/ui/ActionButton/ActionButton";
import type { BottomActionButtonProps } from "@/components/widgets/BottomActionButton/BottomActionButton";
type TypographyAs = "span" | "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "div";
interface TypographyDefaults {
font?: TypographyVariant["font"];
weight?: TypographyVariant["weight"];
size?: TypographyVariant["size"];
align?: TypographyVariant["align"];
color?: TypographyVariant["color"];
}
interface BuildTypographyOptions<T extends TypographyAs> {
as: T;
defaults?: TypographyDefaults;
}
export function buildTypographyProps<T extends TypographyAs>(
variant: TypographyVariant | undefined,
options: BuildTypographyOptions<T>
): TypographyProps<T> | undefined {
if (!variant) {
return undefined;
}
const { as, defaults } = options;
return {
as,
children: variant.text,
font: variant.font ?? defaults?.font,
weight: variant.weight ?? defaults?.weight,
size: variant.size ?? defaults?.size,
align: variant.align ?? defaults?.align,
color: variant.color ?? defaults?.color,
className: variant.className,
} as TypographyProps<T>;
}
export function buildHeaderProgress(progress?: HeaderProgressDefinition) {
if (!progress) {
return undefined;
}
const { current, total, value, label, className } = progress;
const computedValue =
value ?? (current !== undefined && total ? (current / total) * 100 : undefined);
return {
value: computedValue,
label,
className,
};
}
export function buildAutoHeaderProgress(
currentScreenId: string,
totalScreens: number,
currentPosition: number,
explicitProgress?: HeaderProgressDefinition
) {
// If explicit progress is provided, use it
if (explicitProgress) {
return buildHeaderProgress(explicitProgress);
}
// Otherwise, auto-calculate
const autoProgress: HeaderProgressDefinition = {
current: currentPosition,
total: totalScreens,
label: `${currentPosition} of ${totalScreens}`,
};
return buildHeaderProgress(autoProgress);
}
export function mapListOptionsToButtons(
options: ListOptionDefinition[],
selectionType: SelectionType
): MainButtonProps[] {
return options.map((option) => ({
id: option.id,
children: option.label,
emoji: option.emoji,
isCheckbox: selectionType === "multi",
disabled: option.disabled,
}));
}
export function shouldShowBackButton(header?: HeaderDefinition, canGoBack?: boolean) {
if (header?.showBackButton === false) {
return false;
}
return Boolean(canGoBack);
}
export function shouldShowHeader(header?: HeaderDefinition) {
return header?.show !== false;
}
interface BuildActionButtonOptions {
defaultText?: string;
disabled?: boolean;
onClick: () => void;
}
export function buildActionButtonProps(
options: BuildActionButtonOptions,
buttonDef?: BottomActionButtonDefinition
): ActionButtonProps {
const { defaultText = "Continue", disabled = false, onClick } = options;
return {
children: buttonDef?.text ?? defaultText,
cornerRadius: buttonDef?.cornerRadius,
disabled: disabled, // disabled управляется только логикой экрана, не админкой
onClick: disabled ? undefined : onClick,
};
}
export function buildBottomActionButtonProps(
options: BuildActionButtonOptions,
buttonDef?: BottomActionButtonDefinition
): BottomActionButtonProps | undefined {
// Если кнопка отключена (show: false) - не показывать ничего
if (buttonDef?.show === false) {
return undefined;
}
// В остальных случаях показать кнопку с градиентом
const actionButtonProps = buildActionButtonProps(options, buttonDef);
return {
actionButtonProps,
showGradientBlur: true, // Градиент всегда включен (как требовалось)
};
}
interface BuildLayoutQuestionOptions {
screen: ScreenDefinition;
titleDefaults?: TypographyDefaults;
subtitleDefaults?: TypographyDefaults;
canGoBack: boolean;
onBack: () => void;
actionButtonOptions?: BuildActionButtonOptions;
screenProgress?: { current: number; total: number };
}
export function buildLayoutQuestionProps(
options: BuildLayoutQuestionOptions
): Omit<LayoutQuestionProps, "children"> {
const {
screen,
titleDefaults = { font: "manrope", weight: "bold", align: "left" },
subtitleDefaults = { font: "inter", weight: "medium", color: "muted", align: "left" },
canGoBack,
onBack,
actionButtonOptions,
screenProgress
} = options;
const showBackButton = shouldShowBackButton(screen.header, canGoBack);
const showHeader = shouldShowHeader(screen.header);
const bottomActionButtonProps = actionButtonOptions ? buildBottomActionButtonProps(
actionButtonOptions,
// Если передаются actionButtonOptions, это означает что кнопка должна показываться
// Если кнопка отключена (show: false), принудительно включаем её
'bottomActionButton' in screen ?
(screen.bottomActionButton?.show === false ? { ...screen.bottomActionButton, show: true } : screen.bottomActionButton)
: undefined
) : undefined;
return {
headerProps: showHeader ? {
progressProps: screenProgress ? buildHeaderProgress({
current: screenProgress.current,
total: screenProgress.total,
label: `${screenProgress.current} of ${screenProgress.total}`
}) : buildHeaderProgress(screen.header?.progress),
onBack: showBackButton ? onBack : undefined,
showBackButton,
} : undefined,
title: buildTypographyProps(screen.title, {
as: "h2",
defaults: titleDefaults,
}) ?? {
as: "h2",
children: screen.title.text,
},
subtitle: 'subtitle' in screen ? buildTypographyProps(screen.subtitle, {
as: "p",
defaults: subtitleDefaults,
}) : undefined,
bottomActionButtonProps,
};
}