w-funnel/src/components/funnel/templates/layouts/TemplateLayout.tsx
dev.daminik00 fdb98c0322 fix funnel
2025-10-09 13:19:10 +02:00

186 lines
5.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";
import { cn } from "@/lib/utils";
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-2"
privacyPolicy={{
href: "https://witlab.us/privacy",
children: "Privacy Policy",
}}
termsOfUse={{
href: "https://witlab.us/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}
gradientBlurProps={
shouldShowPrivacyTermsConsent
? {
className: cn(shouldShowPrivacyTermsConsent && "pb-1"),
}
: undefined
}
/>
)}
</div>
);
}