WhiteNightsAdminPanel/src/preview/components/AttractionWidget/AttractionWidget.tsx
2025-04-15 21:12:43 +03:00

115 lines
3.4 KiB
TypeScript

import React, { HTMLAttributes, useEffect, useState } from "react";
import { useServerLocalization } from "@mt/i18n";
import cn from "classnames";
import { useSwipeable } from "react-swipeable";
import { ArticleBase } from "@mt/common-types";
import "./AttractionWidget.css";
import { usePrevious } from "@mt/utils";
import { AttractionMedia } from "./media/AttractionMedia";
import { TouchScrollWrapper } from "../TouchScrollWrapper/TouchScrollWrapper";
export interface AttractionsWidgetProps extends HTMLAttributes<HTMLElement> {
articles: ArticleBase[];
isIdleMode: boolean;
isPreviewOnly?: boolean;
}
export function AttractionWidget({
articles,
isIdleMode,
isPreviewOnly = false,
...props
}: AttractionsWidgetProps) {
const [activeIndex, setActiveIndex] = useState(0);
const prevArticles = usePrevious<ArticleBase[]>(articles) || [];
const localizeText = useServerLocalization();
const swipeHandlers = useSwipeable({
onSwipedLeft: ({ event }) => {
event.preventDefault();
setActiveIndex((activeIndex) => (activeIndex + 1) % articles.length);
},
onSwipedRight: ({ event }) => {
event.preventDefault();
setActiveIndex(
(activeIndex) => (activeIndex - 1 + articles.length) % articles.length
);
},
swipeDuration: 500,
preventScrollOnSwipe: true,
trackMouse: true,
});
const handleClick = (index: number) => {
setActiveIndex(index);
document.querySelector(".widget-text.active")!.scrollTop = 0;
};
useEffect(() => setActiveIndex(activeIndex), [activeIndex]);
useEffect(() => {
if (
!isPreviewOnly &&
(isIdleMode || JSON.stringify(prevArticles) !== JSON.stringify(articles))
) {
setActiveIndex(0);
}
// admin specific case: during edit we removed active article
if (prevArticles?.length > articles?.length) {
setActiveIndex(0);
}
}, [isPreviewOnly, isIdleMode, articles]);
return (
<div className="widget-container g-flex-column__item-fixed" {...props}>
<div className="widget-content">
{articles?.map((article, index) => (
<div
key={index}
className={`widget-slide ${index === activeIndex ? "active" : ""}`}
onPointerUp={() => handleClick(index)}
>
<div className="widget-media">
<AttractionMedia media={article.media} />
</div>
{index !== 0 && (
<div className="widget-header">
{localizeText(articles[0].text)}
</div>
)}
<TouchScrollWrapper
className={cn("widget-text", {
active: index === activeIndex,
preview: article.isPreview,
})}
>
<div
dangerouslySetInnerHTML={{ __html: localizeText(article.text) }}
{...swipeHandlers}
/>
</TouchScrollWrapper>
</div>
))}
<div className="widget-titles">
{articles?.map((article, index) => (
<div
key={`title-${index}`}
className={cn("widget-title", {
active: index === activeIndex,
preview: article.isPreview,
})}
onPointerUp={() => handleClick(index)}
>
{localizeText(article.name)}
</div>
))}
</div>
</div>
</div>
);
}