w-funnel/src/components/funnel/templates/ListTemplate/ListTemplate.tsx
2025-10-09 00:45:32 +02:00

137 lines
4.3 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 { useMemo } from "react";
import { RadioAnswersList } from "@/components/widgets/RadioAnswersList/RadioAnswersList";
import { SelectAnswersList } from "@/components/widgets/SelectAnswersList/SelectAnswersList";
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 { mapListOptionsToButtons } from "@/lib/funnel/mappers";
import type { ListScreenDefinition } from "@/lib/funnel/types";
import { TemplateLayout } from "../layouts/TemplateLayout";
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
export interface ListTemplateProps {
screen: ListScreenDefinition;
selectedOptionIds: string[];
onSelectionChange: (
selectedIds: string[],
skipCheckChanges?: boolean
) => 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 handleRadioAnswerClick: RadioAnswersListProps["onAnswerClick"] = (
answer
) => {
const id = stringId(answer?.id);
onSelectionChange(id ? [id] : [], true);
};
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 чтобы избежать двойного вызова при клике
// onAnswerClick достаточно для обработки выбора
onAnswerClick: handleRadioAnswerClick,
};
const selectContent: SelectAnswersListProps = {
answers: buttons,
activeAnswers,
onChangeSelectedAnswers: handleSelectChange,
};
const actionButtonOptions = actionButtonProps
? {
defaultText: (actionButtonProps.children as string) || "Next",
// Кнопка неактивна если: 1) disabled из props ИЛИ 2) ничего не выбрано
disabled: actionButtonProps.disabled || selectedOptionIds.length === 0,
onClick: () => {
if (actionButtonProps.onClick) {
actionButtonProps.onClick(
{} as React.MouseEvent<HTMLButtonElement>
);
}
},
}
: undefined;
const layoutProps = createTemplateLayoutProps(
screen,
{ canGoBack, onBack },
screenProgress,
{
preset: "left",
actionButton: actionButtonOptions,
}
);
return (
<TemplateLayout {...layoutProps}>
<div className="w-full mt-[22px]">
{contentType === "radio-answers-list" ? (
<RadioAnswersList {...radioContent} />
) : (
<SelectAnswersList {...selectContent} />
)}
</div>
</TemplateLayout>
);
}