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

132 lines
3.8 KiB
TypeScript

"use client";
import { useMemo } from "react";
import { Question } from "@/components/templates/Question/Question";
import type { ActionButtonProps } from "@/components/ui/ActionButton/ActionButton";
import type { MainButtonProps } from "@/components/ui/MainButton/MainButton";
import type { RadioAnswersListProps } from "@/components/widgets/RadioAnswersList/RadioAnswersList";
import type { SelectAnswersListProps } from "@/components/widgets/SelectAnswersList/SelectAnswersList";
import {
buildLayoutQuestionProps,
mapListOptionsToButtons,
} from "@/lib/funnel/mappers";
import type { ListScreenDefinition } from "@/lib/funnel/types";
interface ListTemplateProps {
screen: ListScreenDefinition;
selectedOptionIds: string[];
onSelectionChange: (selectedIds: string[]) => void;
actionButtonProps?: ActionButtonProps;
canGoBack: boolean;
onBack: () => void;
screenProgress?: { current: number; total: number };
}
function stringId(value: MainButtonProps["id"]): string | null {
if (value === undefined || value === null) {
return null;
}
return String(value);
}
export function ListTemplate({
screen,
selectedOptionIds,
onSelectionChange,
actionButtonProps,
canGoBack,
onBack,
screenProgress,
}: ListTemplateProps) {
const buttons = useMemo(
() => mapListOptionsToButtons(screen.list.options, screen.list.selectionType),
[screen.list.options, screen.list.selectionType]
);
const selectionSet = useMemo(
() => new Set(selectedOptionIds.map((id) => String(id))),
[selectedOptionIds]
);
const contentType: "radio-answers-list" | "select-answers-list" =
screen.list.selectionType === "multi"
? "select-answers-list"
: "radio-answers-list";
const activeAnswer: MainButtonProps | null =
contentType === "radio-answers-list"
? buttons.find((button) => selectionSet.has(String(button.id))) ?? null
: null;
const activeAnswers: MainButtonProps[] | null =
contentType === "select-answers-list"
? buttons.filter((button) => selectionSet.has(String(button.id)))
: null;
const handleRadioChange: RadioAnswersListProps["onChangeSelectedAnswer"] = (
answer
) => {
const id = stringId(answer?.id);
onSelectionChange(id ? [id] : []);
};
const handleSelectChange: SelectAnswersListProps["onChangeSelectedAnswers"] = (
answers
) => {
const ids = answers
?.map((answer) => stringId(answer.id))
.filter((value): value is string => Boolean(value));
onSelectionChange(ids ?? []);
};
const radioContent: RadioAnswersListProps = {
answers: buttons,
activeAnswer,
onChangeSelectedAnswer: handleRadioChange,
};
const selectContent: SelectAnswersListProps = {
answers: buttons,
activeAnswers,
onChangeSelectedAnswers: handleSelectChange,
};
// Определяем action button options для centralized логики только если кнопка нужна
const actionButtonOptions = actionButtonProps ? {
defaultText: actionButtonProps.children as string || "Next",
disabled: actionButtonProps.disabled || false,
onClick: () => {
if (actionButtonProps.onClick) {
actionButtonProps.onClick({} as React.MouseEvent<HTMLButtonElement>);
}
},
} : undefined;
const layoutQuestionProps = buildLayoutQuestionProps({
screen,
titleDefaults: { font: "manrope", weight: "bold", align: "left" },
subtitleDefaults: { font: "inter", weight: "medium", color: "muted", align: "left" },
canGoBack,
onBack,
actionButtonOptions: actionButtonOptions,
screenProgress,
});
const contentProps =
contentType === "radio-answers-list" ? radioContent : selectContent;
return (
<Question
layoutQuestionProps={layoutQuestionProps}
contentType={contentType}
content={contentProps}
/>
);
}