115 lines
3.4 KiB
TypeScript
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>
|
|
);
|
|
}
|