New source found from dndbeyond.com
This commit is contained in:
Generated
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
'use client';
|
||||
import ChevronLeft from '@dndbeyond/fontawesome-cache/svgs/regular/chevron-left.svg';
|
||||
import ChevronRight from '@dndbeyond/fontawesome-cache/svgs/regular/chevron-right.svg';
|
||||
import clsx from 'clsx';
|
||||
import useEmblaCarousel from 'embla-carousel-react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import { getItemAttributes, getSubElementAttributes } from '../../../bi/helpers';
|
||||
import styles from './PromoCarousel.module.css';
|
||||
|
||||
type PromoCarouselType = {
|
||||
promotions: {
|
||||
text: string;
|
||||
color?: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export const PromoCarousel = ({ promotions }: PromoCarouselType) => {
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel();
|
||||
const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);
|
||||
const [nextBtnDisabled, setNextBtnDisabled] = useState(true);
|
||||
const [activeSlide, setActiveSlide] = useState(0);
|
||||
|
||||
const onPrevButtonClick = () => {
|
||||
emblaApi?.scrollPrev();
|
||||
determineSliderPosition();
|
||||
if (emblaApi) setActiveSlide(emblaApi.selectedScrollSnap());
|
||||
};
|
||||
|
||||
const onNextButtonClick = () => {
|
||||
emblaApi?.scrollNext();
|
||||
determineSliderPosition();
|
||||
if (emblaApi) setActiveSlide(emblaApi.selectedScrollSnap());
|
||||
};
|
||||
|
||||
const determineSliderPosition = useCallback(() => {
|
||||
setPrevBtnDisabled(!emblaApi?.canScrollPrev());
|
||||
setNextBtnDisabled(!emblaApi?.canScrollNext());
|
||||
}, [emblaApi]);
|
||||
|
||||
useEffect(() => {
|
||||
if (emblaApi) setActiveSlide(emblaApi.selectedScrollSnap());
|
||||
determineSliderPosition();
|
||||
}, [determineSliderPosition]);
|
||||
|
||||
return (
|
||||
<div className={styles.carouselContainer}>
|
||||
<button
|
||||
onClick={onPrevButtonClick}
|
||||
className={clsx(
|
||||
styles.navigationButton,
|
||||
prevBtnDisabled && styles.navigationButtonDisabled,
|
||||
promotions.length === 1 && styles.navigationButtonHidden,
|
||||
)}
|
||||
disabled={prevBtnDisabled}
|
||||
{...getItemAttributes('Previous', 'button', 'scroll', undefined, undefined)}
|
||||
>
|
||||
<ChevronLeft />
|
||||
</button>
|
||||
<div
|
||||
className={styles.carousel}
|
||||
ref={emblaRef}
|
||||
{...getSubElementAttributes('Promo Carousel', 'main tile', undefined)}
|
||||
>
|
||||
<div className={styles.carouselScrollContainer}>
|
||||
{promotions.map((promo, index) => (
|
||||
// React versions below 19 remove the 'inert' attribute when provided as a boolean. Passing it as a string "true" as a workaround.
|
||||
<div
|
||||
inert={activeSlide !== index && ('inert' as unknown as boolean)}
|
||||
key={index + promo.text}
|
||||
className={styles.slide}
|
||||
>
|
||||
<Markdown>{promo.text}</Markdown>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onNextButtonClick}
|
||||
className={clsx(
|
||||
styles.navigationButton,
|
||||
nextBtnDisabled && styles.navigationButtonDisabled,
|
||||
promotions.length === 1 && styles.navigationButtonHidden,
|
||||
)}
|
||||
disabled={nextBtnDisabled}
|
||||
{...getItemAttributes('Next', 'button', 'scroll', undefined, undefined)}
|
||||
>
|
||||
<ChevronRight />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Generated
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
'use client';
|
||||
import { Button } from '@dndbeyond/ttui/components/Button';
|
||||
import clsx from 'clsx';
|
||||
import { forwardRef } from 'react';
|
||||
import { getItemAttributes, getSubElementAttributes } from '../../bi/helpers';
|
||||
import type { Meta, Title as PromoCarouselAdapterSchema } from '../../schemas/promotion-message-schema';
|
||||
import type { LanguageType, TopNavigationMessages } from '../../types';
|
||||
import { AmplienceComponent } from '../AmplienceComponent';
|
||||
import styles from './TopNavigation.module.css';
|
||||
|
||||
type TopNavigationType = {
|
||||
className?: string;
|
||||
promoSlotData?: {
|
||||
content: PromoCarouselAdapterSchema;
|
||||
_meta: Meta;
|
||||
};
|
||||
messages: TopNavigationMessages;
|
||||
locale: LanguageType;
|
||||
onDismissBanner?: () => void;
|
||||
};
|
||||
|
||||
export const TopNavigation = forwardRef<HTMLDivElement, TopNavigationType>(
|
||||
({ className, promoSlotData, messages, locale, onDismissBanner }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={clsx(styles.promoBannerWrapper, className)}
|
||||
data-testid="topNavigation-wrapper"
|
||||
{...getSubElementAttributes('Top Navigation', 'main tile', undefined)}
|
||||
>
|
||||
<div className={styles.promoBanner}>
|
||||
<div></div>
|
||||
<div>{promoSlotData && <AmplienceComponent item={promoSlotData} locale={locale} />}</div>
|
||||
{!!onDismissBanner && (
|
||||
<Button
|
||||
variant="outline"
|
||||
color="primary"
|
||||
size="x-small"
|
||||
className={styles.dismissButton}
|
||||
onClick={onDismissBanner}
|
||||
data-testid="dismiss-button"
|
||||
{...getItemAttributes('Dismiss Promo Banner', 'button', 'close', undefined, undefined)}
|
||||
>
|
||||
{messages.dismiss}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
TopNavigation.displayName = 'TopNavigation';
|
||||
Reference in New Issue
Block a user