178 lines
5.0 KiB
TypeScript
178 lines
5.0 KiB
TypeScript
"use client";
|
||
|
||
import React from "react";
|
||
import { LayoutQuestion } from "@/components/layout/LayoutQuestion/LayoutQuestion";
|
||
import { BottomActionButton } from "@/components/widgets/BottomActionButton/BottomActionButton";
|
||
import PrivacyTermsConsent from "@/components/widgets/PrivacyTermsConsent/PrivacyTermsConsent";
|
||
import { useDynamicSize } from "@/hooks/DOM/useDynamicSize";
|
||
import {
|
||
buildLayoutQuestionProps,
|
||
buildTemplateBottomActionButtonProps,
|
||
} from "@/lib/funnel/mappers";
|
||
import type { ScreenDefinition } from "@/lib/funnel/types";
|
||
|
||
interface TemplateLayoutProps {
|
||
screen: ScreenDefinition;
|
||
canGoBack: boolean;
|
||
onBack: () => void;
|
||
screenProgress?: { current: number; total: number };
|
||
|
||
// Настройки template
|
||
titleDefaults?: {
|
||
font?: "manrope" | "inter" | "geistSans" | "geistMono";
|
||
weight?: "regular" | "medium" | "semiBold" | "bold" | "extraBold" | "black";
|
||
align?: "left" | "center" | "right";
|
||
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
|
||
color?:
|
||
| "default"
|
||
| "primary"
|
||
| "secondary"
|
||
| "destructive"
|
||
| "success"
|
||
| "card"
|
||
| "accent"
|
||
| "muted";
|
||
className?: string;
|
||
};
|
||
subtitleDefaults?: {
|
||
font?: "manrope" | "inter" | "geistSans" | "geistMono";
|
||
weight?: "regular" | "medium" | "semiBold" | "bold" | "extraBold" | "black";
|
||
color?:
|
||
| "default"
|
||
| "primary"
|
||
| "secondary"
|
||
| "destructive"
|
||
| "success"
|
||
| "card"
|
||
| "accent"
|
||
| "muted";
|
||
align?: "left" | "center" | "right";
|
||
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
|
||
className?: string;
|
||
};
|
||
actionButtonOptions?: {
|
||
defaultText: string;
|
||
disabled: boolean;
|
||
onClick: () => void;
|
||
};
|
||
|
||
// Дополнительные props для BottomActionButton
|
||
childrenAboveButton?: React.ReactNode;
|
||
childrenUnderButton?: React.ReactNode;
|
||
|
||
// Дополнительные props для Title
|
||
childrenAboveTitle?: React.ReactNode;
|
||
|
||
// Переопределения стилей LayoutQuestion (контент и обертка контента)
|
||
contentProps?: React.ComponentProps<"div">;
|
||
childrenWrapperProps?: React.ComponentProps<"div">;
|
||
|
||
// Контент template
|
||
children: React.ReactNode;
|
||
}
|
||
|
||
/**
|
||
* Централизованный layout wrapper для всех templates
|
||
* Устраняет дублирование логики Header и BottomActionButton
|
||
*/
|
||
export function TemplateLayout({
|
||
screen,
|
||
canGoBack,
|
||
onBack,
|
||
screenProgress,
|
||
titleDefaults = {
|
||
font: "manrope",
|
||
weight: "bold",
|
||
align: "left",
|
||
size: "2xl",
|
||
color: "default",
|
||
},
|
||
subtitleDefaults = {
|
||
font: "manrope",
|
||
weight: "medium",
|
||
color: "default",
|
||
align: "left",
|
||
size: "lg",
|
||
},
|
||
actionButtonOptions,
|
||
childrenAboveButton,
|
||
childrenUnderButton,
|
||
childrenAboveTitle,
|
||
contentProps,
|
||
childrenWrapperProps,
|
||
children,
|
||
}: TemplateLayoutProps) {
|
||
// 🎛️ ЦЕНТРАЛИЗОВАННАЯ ЛОГИКА BOTTOM BUTTON
|
||
const { elementRef: bottomActionButtonRef } = useDynamicSize<HTMLDivElement>({
|
||
defaultHeight: 132,
|
||
});
|
||
|
||
// 🎯 ЦЕНТРАЛИЗОВАННАЯ ЛОГИКА HEADER
|
||
const layoutQuestionProps = buildLayoutQuestionProps({
|
||
screen,
|
||
titleDefaults,
|
||
subtitleDefaults,
|
||
canGoBack,
|
||
onBack,
|
||
screenProgress,
|
||
});
|
||
|
||
// 🎯 ЦЕНТРАЛИЗОВАННАЯ ЛОГИКА BOTTOM BUTTON PROPS
|
||
const bottomActionButtonProps = actionButtonOptions
|
||
? buildTemplateBottomActionButtonProps({
|
||
screen,
|
||
actionButtonOptions,
|
||
})
|
||
: undefined;
|
||
|
||
// 🎯 Автоматически создаем PrivacyTermsConsent с фиксированными настройками
|
||
const shouldShowPrivacyTermsConsent =
|
||
"bottomActionButton" in screen &&
|
||
screen.bottomActionButton?.showPrivacyTermsConsent === true;
|
||
|
||
const autoPrivacyTermsConsent = shouldShowPrivacyTermsConsent ? (
|
||
<PrivacyTermsConsent
|
||
className="mt-5"
|
||
privacyPolicy={{
|
||
href: "/privacy",
|
||
children: "Privacy Policy",
|
||
}}
|
||
termsOfUse={{
|
||
href: "/terms",
|
||
children: "Terms of use",
|
||
}}
|
||
/>
|
||
) : null;
|
||
|
||
// Комбинируем переданный childrenUnderButton с автоматическим PrivacyTermsConsent
|
||
const finalChildrenUnderButton = (
|
||
<>
|
||
{childrenUnderButton}
|
||
{autoPrivacyTermsConsent}
|
||
</>
|
||
);
|
||
|
||
// 🎨 ЦЕНТРАЛИЗОВАННЫЙ РЕНДЕРИНГ
|
||
return (
|
||
<div className="w-full">
|
||
<LayoutQuestion
|
||
{...layoutQuestionProps}
|
||
childrenAboveTitle={childrenAboveTitle}
|
||
contentProps={contentProps}
|
||
childrenWrapperProps={childrenWrapperProps}
|
||
>
|
||
{children}
|
||
</LayoutQuestion>
|
||
|
||
{bottomActionButtonProps && (
|
||
<BottomActionButton
|
||
{...bottomActionButtonProps}
|
||
ref={bottomActionButtonRef}
|
||
childrenAboveButton={childrenAboveButton}
|
||
childrenUnderButton={finalChildrenUnderButton}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|