w-funnel/src/components/funnel/templates/layouts/TemplateLayout.tsx
dev.daminik00 b28d22967f story
2025-09-28 16:35:41 +02:00

132 lines
4.2 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.

"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";
};
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";
};
actionButtonOptions?: {
defaultText: string;
disabled: boolean;
onClick: () => void;
};
// Дополнительные props для BottomActionButton
childrenUnderButton?: React.ReactNode;
// Контент 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,
childrenUnderButton,
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}>
{children}
</LayoutQuestion>
{bottomActionButtonProps && (
<BottomActionButton
{...bottomActionButtonProps}
ref={bottomActionButtonRef}
childrenUnderButton={finalChildrenUnderButton}
/>
)}
</div>
);
}