132 lines
3.8 KiB
TypeScript
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}
|
|
/>
|
|
);
|
|
}
|