214 lines
6.4 KiB
TypeScript
214 lines
6.4 KiB
TypeScript
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,
|
||
};
|
||
}
|
||
|