From dea1c6bde7b3484247f4a6e46df0862bcfefe9de Mon Sep 17 00:00:00 2001 From: Borghild Date: Fri, 14 Feb 2025 13:05:18 +0100 Subject: [PATCH] :art: update --- .../objects/ImageWithLinkAndOrOverlay.tsx | 2 +- .../objects/carousel/sharedCarouselFields.tsx | 34 --- sanityv3/schemas/objects/iframeCarousel.tsx | 7 +- sanityv3/schemas/objects/imageCarousel.tsx | 3 +- .../schemas/objects/videoPlayerCarousel.tsx | 13 +- .../src/Backgrounds/BackgroundContainer.tsx | 8 +- .../src/Backgrounds/ColouredContainer.tsx | 16 +- .../Backgrounds/ImageBackgroundContainer.tsx | 16 +- web/core/Carousel/Carousel.tsx | 231 ++++++++---------- web/core/Carousel/CarouselImageItem.tsx | 24 +- web/core/Carousel/CarouselItem.tsx | 71 +++--- web/core/Image/ImageWithOverlay.tsx | 39 +-- web/lib/queries/videoPlayerCarouselFields.ts | 3 +- .../IframeCarousel/IframeCarousel.tsx | 13 +- web/sections/ImageCarousel/ImageCarousel.tsx | 9 +- .../VideoPlayerCarousel.tsx | 14 +- web/tailwind.config.cjs | 3 + web/types/imageTypes.ts | 2 - web/types/types.ts | 2 - web/types/videoTypes.ts | 3 +- 20 files changed, 244 insertions(+), 269 deletions(-) diff --git a/sanityv3/schemas/objects/ImageWithLinkAndOrOverlay.tsx b/sanityv3/schemas/objects/ImageWithLinkAndOrOverlay.tsx index 90b669f55..80678144e 100644 --- a/sanityv3/schemas/objects/ImageWithLinkAndOrOverlay.tsx +++ b/sanityv3/schemas/objects/ImageWithLinkAndOrOverlay.tsx @@ -66,7 +66,7 @@ export default { of: [blockContentType], validation: (Rule: Rule) => Rule.custom((value: PortableTextBlock[]) => { - return validateCharCounterEditor(value, 600, true) + return validateCharCounterEditor(value, 200, true) }).error(), }, { diff --git a/sanityv3/schemas/objects/carousel/sharedCarouselFields.tsx b/sanityv3/schemas/objects/carousel/sharedCarouselFields.tsx index 615a7b099..918d5a4da 100644 --- a/sanityv3/schemas/objects/carousel/sharedCarouselFields.tsx +++ b/sanityv3/schemas/objects/carousel/sharedCarouselFields.tsx @@ -1,37 +1,3 @@ -import type { Rule } from 'sanity' - -export const singleMode = { - type: 'boolean', - name: 'singleMode', - title: 'Single mode', - description: 'Displays the carousel as one item at the time. Default is scroll container', - initialValue: false, - validation: (Rule: Rule) => - Rule.custom((value: any, context) => { - if (!value) { - return true - } - //@ts-ignore:todo - return value && context?.parent?.items?.length >= 3 ? true : 'Single mode requires at least 3 items' - }), -} -export const carouselWidth = { - type: 'boolean', - name: 'useFullWidthScroll', - title: 'Use scroll fullwidth container', - description: - 'Scroll: Select this if you want the carusel go full viewport width. Default is within large content column', - initialValue: false, - validation: (Rule: Rule) => - Rule.custom((value: any, context) => { - if (!value) { - return true - } - //@ts-ignore:todo - return value && context?.parent?.singleMode ? 'Single mode is set, turn off single mode to use this' : true - }), -} - export const autoPlay = { type: 'boolean', name: 'autoplay', diff --git a/sanityv3/schemas/objects/iframeCarousel.tsx b/sanityv3/schemas/objects/iframeCarousel.tsx index 107b06d2b..4507929f6 100644 --- a/sanityv3/schemas/objects/iframeCarousel.tsx +++ b/sanityv3/schemas/objects/iframeCarousel.tsx @@ -12,7 +12,6 @@ import { height, action, } from './iframe/sharedIframeFields' -import { carouselWidth, singleMode } from './carousel/sharedCarouselFields' const carouselItemFields = [title, frameTitle, description, cookiePolicy, aspectRatio, url, height, action] @@ -43,7 +42,7 @@ export default { type: 'array', name: 'items', description: 'Add more iframes', - title: 'Scrollable iframe items', + title: 'Iframe items', of: [ { title: 'Iframe item', @@ -70,10 +69,8 @@ export default { fields: [...carouselItemFields], }, ], - validation: (Rule: Rule) => Rule.required().min(2), + validation: (Rule: Rule) => Rule.required().min(3), }, - singleMode, - carouselWidth, { title: 'Background', description: 'Pick a colour for the background. Default is white.', diff --git a/sanityv3/schemas/objects/imageCarousel.tsx b/sanityv3/schemas/objects/imageCarousel.tsx index bb34cfc1f..efbf61022 100644 --- a/sanityv3/schemas/objects/imageCarousel.tsx +++ b/sanityv3/schemas/objects/imageCarousel.tsx @@ -77,9 +77,8 @@ export default { { type: 'imageWithRichTextBelow' }, { type: 'imageWithLinkAndOrOverlay' }, ], - validation: (Rule: Rule) => Rule.required().min(2), + validation: (Rule: Rule) => Rule.required().min(3), }, - singleMode, autoPlay, { title: 'Background', diff --git a/sanityv3/schemas/objects/videoPlayerCarousel.tsx b/sanityv3/schemas/objects/videoPlayerCarousel.tsx index d3cae6012..5eb530345 100644 --- a/sanityv3/schemas/objects/videoPlayerCarousel.tsx +++ b/sanityv3/schemas/objects/videoPlayerCarousel.tsx @@ -100,10 +100,8 @@ export default { }, }, ], - validation: (Rule: Rule) => Rule.required().min(2), + validation: (Rule: Rule) => Rule.required().min(3), }, - singleMode, - carouselWidth, { name: 'aspectRatio', type: 'string', @@ -120,6 +118,15 @@ export default { fieldset: 'design', validation: (Rule: Rule) => Rule.required(), }, + { + type: 'boolean', + name: 'scrollMode', + title: 'Scroll mode', + description: 'Displays the carousel as scroll container', + initialValue: false, + fieldset: 'design', + hidden: ({ parent }: { parent: any }) => parent?.aspectRatio !== '9:16', + }, { title: 'Background', description: 'Pick a colour for the background. Default is white.', diff --git a/web/components/src/Backgrounds/BackgroundContainer.tsx b/web/components/src/Backgrounds/BackgroundContainer.tsx index e5023b6b4..c5e303bb6 100644 --- a/web/components/src/Backgrounds/BackgroundContainer.tsx +++ b/web/components/src/Backgrounds/BackgroundContainer.tsx @@ -30,6 +30,8 @@ export type BackgroundContainerProps = { scrimClassName?: string /* On mobile dont split background image and content */ dontSplit?: boolean + /** Set return element as section */ + asSection?: boolean } & HTMLAttributes export const BackgroundContainer = forwardRef(function BackgroundContainer( @@ -43,6 +45,7 @@ export const BackgroundContainer = forwardRef {children} )} {(type === 'backgroundColor' || !type) && ( <> - {renderFragmentWhenPossible && + {!asSection && + renderFragmentWhenPossible && (restBackground?.backgroundColor === 'White' || restBackground?.backgroundUtility === 'white-100') && className === '' && !id ? ( @@ -76,6 +81,7 @@ export const BackgroundContainer = forwardRef type ColourContainerProps = { @@ -23,9 +25,15 @@ const ColourContainer = styled.div` ${({ $isInverted }) => ($isInverted ? inverted : normal)} ${({ $hasUtility }) => ($hasUtility ? '' : 'background-color: var(--background-color);')} ` +const ColourSectionContainer = styled.section` + container: size; + color: var(--color-on-background); + ${({ $isInverted }) => ($isInverted ? inverted : normal)} + ${({ $hasUtility }) => ($hasUtility ? '' : 'background-color: var(--background-color);')} +` export const ColouredContainer = forwardRef(function BackgroundContainer( - { backgroundColor = 'White', backgroundUtility, dark, style, children, className = '', ...rest }, + { backgroundColor = 'White', backgroundUtility, dark, style, children, className = '', asSection = false, ...rest }, ref, ) { const styleVariant = getContainerColor(backgroundColor) @@ -35,8 +43,10 @@ export const ColouredContainer = forwardRef {children} - + ) }) diff --git a/web/components/src/Backgrounds/ImageBackgroundContainer.tsx b/web/components/src/Backgrounds/ImageBackgroundContainer.tsx index ddb272ac1..7c9cf44ee 100644 --- a/web/components/src/Backgrounds/ImageBackgroundContainer.tsx +++ b/web/components/src/Backgrounds/ImageBackgroundContainer.tsx @@ -11,6 +11,8 @@ type ImageBackgroundContainerProps = { /* Provide gradient in scrimClassname and disable default */ overrideGradient?: boolean aspectRatio?: number + /** Set return element as section */ + asSection?: boolean } & ImageBackground & HTMLAttributes const DEFAULT_MAX_WIDTH = 1920 @@ -28,6 +30,7 @@ export const ImageBackgroundContainer = forwardRef {children} - + ) : isMobile && !dontSplit ? ( -
+
{children} -
+
) : ( -
{children}
-
+ ) }, ) diff --git a/web/core/Carousel/Carousel.tsx b/web/core/Carousel/Carousel.tsx index 4b712406a..95602bdb4 100644 --- a/web/core/Carousel/Carousel.tsx +++ b/web/core/Carousel/Carousel.tsx @@ -51,41 +51,6 @@ export const getUtilityForAspectRatio = (aspectRatio: string) => { } } -export const getWidthsForAspectRatio = (aspectRatio: string, itemLength: number, displayMode: DisplayModes) => { - if (displayMode === 'single') { - return - } - if (itemLength <= 3) { - switch (aspectRatio) { - case '16:9': - return 'w-[75vw] md:w-[70vw] lg:w-[62vw] max-w-[1030px]' - case '4:3': - return 'w-[65vw] md:w-[42vw] lg:w-[43vw]' - case '9:16': - return 'w-[55vw] md:w-[33vw] lg:w-[30vw]' - case '1:1': - return 'w-[55vw] md:w-[33vw] lg:w-[30vw]' - - default: - return 'w-[45vw] md:w-[33vw] lg:w-[30vw]' - } - } else { - switch (aspectRatio) { - case '16:9': - return 'w-[55vw] md:w-[33vw] lg:w-[30vw]' - case '4:3': - return 'w-[55vw] md:w-[33vw] lg:w-[30vw]' - case '9:16': - return 'w-[55vw] md:w-[33vw] lg:w-[30vw]' - case '1:1': - return 'w-[55vw] md:w-[33vw] lg:w-[30vw]' - - default: - return 'w-[45vw] md:w-[33vw] lg:w-[30vw]' - } - } -} - type CarouselItemTypes = | VideoPlayerCarouselItem | ImageCarouselItem @@ -99,8 +64,6 @@ type CarouselProps = { items: CarouselItemTypes[] variant?: Variants displayMode?: DisplayModes - //For scroll container - layout?: Layouts /* If carousel has a title/sectionTitle over it */ labelledbyId?: string className?: string @@ -125,7 +88,6 @@ export const Carousel = forwardRef(function Carousel className = '', listClassName = '', hasSectionTitle = false, - layout = 'default', }, ref, ) { @@ -149,6 +111,9 @@ export const Carousel = forwardRef(function Carousel const isLarge = useMediaQuery(`(min-width: 1024px)`) const [scrollPosition, setScrollPosition] = useState<'start' | 'middle' | 'end'>('start') + const carouselGridTop = `col-start-1 col-end-1 row-start-1 row-end-1` + const carouselGridBottom = `col-start-1 col-end-1 row-start-2 row-end-2` + if (isMedium) { TRANSLATE_X_AMOUNT = TRANSLATE_X_AMOUNT_MD } @@ -209,7 +174,7 @@ export const Carousel = forwardRef(function Carousel }) const calculatedYPos = Math.abs(sliderRef?.current.scrollLeft + calculatedOffset) const maxW = sliderRef?.current.scrollWidth - sliderRef?.current.clientWidth - const isAtEnd = calculatedYPos >= maxW - itemWidth / 2 + const isAtEnd = calculatedYPos >= maxW - itemWidth / 2 + 5 //+ 5 for outlines and some overflow if (scrollPosition === 'start') { setScrollPosition('middle') } @@ -237,7 +202,6 @@ export const Carousel = forwardRef(function Carousel } //move the ul slider const newTranslateX = currentListTranslateX - TRANSLATE_X_AMOUNT - console.log('move slider', newTranslateX) setCurrentListTranslateX(newTranslateX) setCurrentIndex(newIndex) setCurrentXPosition(newXPosition) @@ -303,26 +267,6 @@ export const Carousel = forwardRef(function Carousel timeoutRef.current && clearTimeout(timeoutRef.current) } - // no-scrollbar - const commonScrollListContainerClassName = ` - ${layout === 'default' ? `max-w-smContainer horizontal-scrollbar` : 'w-full scroll-pl-lg no-scrollbar'} - flex - gap-3 - lg:gap-6 - h-max - overflow-x-auto - snap-x - pb-6` - const commonSingleListContainerClassName = `grid transition-transform ease-scroll delay-0 duration-[800ms]` - - const customListVariantClassName = { - video: '', - image: '', - event: 'p-[2px]', - keyNumber: '', - iframe: '', - } - const getCarouselItemVariant = () => { if (displayMode === 'single') { if (variant === 'iframe') { @@ -348,7 +292,7 @@ export const Carousel = forwardRef(function Carousel frameTitle={iframeData?.frameTitle} url={iframeData?.url} cookiePolicy={iframeData?.cookiePolicy} - aspectRatio={iframeData?.aspectRatio ?? '16:9'} + aspectRatio={'16:9'} height={iframeData?.height} hasSectionTitle={!!title} /> @@ -364,27 +308,26 @@ export const Carousel = forwardRef(function Carousel //@ts-ignore:TODO- didnt work with type assertion as PortableTextBlock[] content={description} action={action} - layout={layout} + overrideHeights={true} aspectRatio={iframeData?.aspectRatio as CarouselAspectRatios} {...(displayMode === 'single' && { style: { transform: `translate3d(${itemsXPositions[index]}px, 0px, 0px)`, }, })} - className={getWidthsForAspectRatio(iframeData?.aspectRatio as string, items?.length, displayMode)} > {element} ) } const getVideoVariantBody = (itemData: VideoPlayerCarouselItem, index: number) => { - const { title, video, aspectRatio, id, ...videoData } = itemData + const { title, video, id } = itemData const element = ( (function Carousel controls: true, }} className={` - ${getUtilityForAspectRatio(aspectRatio as string)} + ${displayMode === 'single' ? '' : 'aspect-9/16'} object-cover `} /> @@ -404,12 +347,9 @@ export const Carousel = forwardRef(function Carousel displayMode={displayMode} active={index === currentIndex} variant={title ? 'richTextBelow' : 'default'} - aspectRatio={aspectRatio} //@ts-ignore:TODO- didnt work with type assertion as PortableTextBlock[] title={title} - //@ts-ignore:TODO- didnt work with type assertion as PortableTextBlock[] - layout={layout} - className={getWidthsForAspectRatio(aspectRatio as string, items?.length, displayMode)} + className={`${displayMode === 'scroll' ? 'h-full w-[260px] md:w-[372px] lg:w-[405px]' : ''}`} {...(displayMode === 'single' && { style: { transform: `translate3d(${itemsXPositions[index]}px, 0px, 0px)`, @@ -472,16 +412,15 @@ export const Carousel = forwardRef(function Carousel displayMode={displayMode} aria-label={ariaLabel} active={i === currentIndex} - {...(variant === 'image' && - displayMode === 'single' && { - style: { - transform: `translate3d(${itemsXPositions[i]}px, 0px, 0px)`, - }, - wasUserPress: wasUserInteraction, - onFocus: () => { - cancelTimeout() - }, - })} + {...(displayMode === 'single' && { + style: { + transform: `translate3d(${itemsXPositions[i]}px, 0px, 0px)`, + }, + wasUserPress: wasUserInteraction, + onFocus: () => { + cancelTimeout() + }, + })} /> ) case 'event': @@ -493,48 +432,80 @@ export const Carousel = forwardRef(function Carousel } } + const customListVariantClassName = { + video: '', + image: '', + event: 'p-[2px]', + keyNumber: '', + iframe: '', + } + + // no-scrollbar + const commonScrollListContainerClassName = ` + no-scrollbar + flex + gap-3 + lg:gap-6 + h-full + overflow-x-auto + snap-x + pb-6` + + const commonSingleListContainerClassName = ` + grid + transition-transform + ease-scroll + delay-0 + duration-[800ms] + ${getCarouselItemVariant() === 'richTextBelow' ? carouselGridBottom : carouselGridTop} + ` + return ( - {/* Controls - should be before slide in DOM but not visually - */} - {displayMode === 'single' && ( +
+ {/* Controls - should be before slide in DOM but not visually + */}
@@ -558,6 +529,7 @@ export const Carousel = forwardRef(function Carousel title={`Go to previous`} aria-controls={carouselItemsId} mode="previous" + disabled={(displayMode === 'scroll' && scrollPosition === 'start') ?? false} onClick={() => { if (displayMode === 'single') { loopSlidePrev() @@ -574,6 +546,7 @@ export const Carousel = forwardRef(function Carousel title={`Go to next`} mode="next" aria-controls={carouselItemsId} + disabled={(displayMode === 'scroll' && scrollPosition === 'end') ?? false} onClick={() => { if (displayMode === 'single') { loopSlideNext() @@ -588,35 +561,35 @@ export const Carousel = forwardRef(function Carousel />
- )} -
    - {items?.map((item, i) => { - return getCarouselItem(item, i) - })} -
+ listClassName, + )} + {...(displayMode === 'single' && { + style: { + transform: `translate3d(${currentListTranslateX}px, 0px, 0px)`, + }, + 'aria-live': pauseAutoRotation ? 'polite' : 'off', + onKeyDown: handleKeyDown, + })} + > + {items?.map((item, i) => { + return getCarouselItem(item, i) + })} + +
) }) diff --git a/web/core/Carousel/CarouselImageItem.tsx b/web/core/Carousel/CarouselImageItem.tsx index 50d46af94..4693e58d1 100644 --- a/web/core/Carousel/CarouselImageItem.tsx +++ b/web/core/Carousel/CarouselImageItem.tsx @@ -57,6 +57,10 @@ export const CarouselImageItem = forwardRef { if (active && itemRef?.current && wasUserPress) { itemRef?.current?.focus() @@ -66,12 +70,7 @@ export const CarouselImageItem = forwardRef { if (isJustImage) { return ( -
+
) @@ -79,7 +78,7 @@ export const CarouselImageItem = forwardRef - +
- +
-
+
{caption && ( /* Carousel item @@ -47,8 +47,8 @@ export const CarouselItem = forwardRef(functio { variant = 'richTextBelow', displayMode = 'scroll', - layout = 'default', wasUserPress = false, + overrideHeights = false, children, title, content, @@ -56,8 +56,6 @@ export const CarouselItem = forwardRef(functio action, attribution, className = '', - aspectRatio = '16:9', - customListItemWidth, ...rest }, ref, @@ -69,13 +67,13 @@ export const CarouselItem = forwardRef(functio const getTitleElement = () => { if (title && (title === 'string' || typeof title === 'string')) { return ( - + {title} ) } if (title && Array.isArray(title)) { - return + return } return null } @@ -90,12 +88,14 @@ export const CarouselItem = forwardRef(functio return null } - const getElementAspectRatio = () => { - if (customListItemWidth || displayMode === 'single') { - return - } - return getUtilityForAspectRatio(aspectRatio) - } + const singleWidths = ` + w-single-carousel-card-w-sm + md:w-single-carousel-card-w-md + lg:w-single-carousel-card-w-lg` + const singleHeigths = ` + min-h-single-carousel-card-h-sm + md:min-h-single-carousel-card-h-md + lg:min-h-single-carousel-card-h-lg` const scrollVariantClassNames = ` group @@ -105,14 +105,15 @@ export const CarouselItem = forwardRef(functio relative snap-center pb-6 - ${layout === 'full' ? 'first:ml-lg lg:first:ml-layout-sm last:mr-lg lg:last:mr-layout-sm' : ''}` + last:mr-lg + lg:last:mr-layout-sm + max-h-[500px] + lg:max-h-[740px]` const singleVariantClassNames = ` relative h-full - w-single-carousel-card-w-sm - md:w-single-carousel-card-w-md - lg:w-single-carousel-card-w-lg + ${variant === 'richTextBelow' ? '' : `${singleWidths} ${singleHeigths}`} ms-2 me-2 col-start-1 @@ -128,7 +129,6 @@ export const CarouselItem = forwardRef(functio duration-1000 ease-[ease] ${!active ? 'opacity-30' : ''} - ${variant !== 'richTextBelow' ? 'aspect-4/5 md:aspect-video' : ''} ` const getVariantBody = () => { @@ -138,25 +138,33 @@ export const CarouselItem = forwardRef(functio const singleClassNames = `${active ? 'block' : 'hidden'}` return ( -
-
{children}
+
+
+ {children} +
{getTitleElement()} {getContentElement()} @@ -178,7 +186,7 @@ export const CarouselItem = forwardRef(functio ) } default: - return
{children}
+ return <>{children} } } @@ -201,8 +209,7 @@ export const CarouselItem = forwardRef(functio tabIndex: active ? 0 : -1, })} className={envisTwMerge( - `${displayMode === 'single' ? singleVariantClassNames : scrollVariantClassNames} - `, + `${displayMode === 'single' ? singleVariantClassNames : scrollVariantClassNames}`, className, )} > diff --git a/web/core/Image/ImageWithOverlay.tsx b/web/core/Image/ImageWithOverlay.tsx index c020ba773..a25b38a6b 100644 --- a/web/core/Image/ImageWithOverlay.tsx +++ b/web/core/Image/ImageWithOverlay.tsx @@ -48,18 +48,18 @@ export const ImageWithOverlay = forwardRef {typeof title === 'string' ? ( - + {title} ) : ( //@ts-ignore: Checked earlier for undefined title - + )} ) return ( -
+
-
{title && titleElement}
- {text && } +
{title && titleElement}
+ {text && } {action && ( Show me more @@ -135,9 +138,11 @@ export const ImageWithOverlay = forwardRefthumbnail, }, }, - singleMode, - useFullWidthScroll, + scrollMode, "designOptions": { "aspectRatio": coalesce(aspectRatio, '16:9'), ${background} diff --git a/web/sections/IframeCarousel/IframeCarousel.tsx b/web/sections/IframeCarousel/IframeCarousel.tsx index 83ec7275e..16d863ca1 100644 --- a/web/sections/IframeCarousel/IframeCarousel.tsx +++ b/web/sections/IframeCarousel/IframeCarousel.tsx @@ -11,11 +11,11 @@ type IframeCarouselProps = { } const IframeCarousel = ({ data, anchor, className, ...rest }: IframeCarouselProps) => { - const { title, hideTitle, items, singleMode, useFullWidthScroll, designOptions } = data + const { title, hideTitle, items, designOptions } = data const { background } = designOptions return ( - +
{title && ( )} - +
) diff --git a/web/sections/ImageCarousel/ImageCarousel.tsx b/web/sections/ImageCarousel/ImageCarousel.tsx index 4e1cd190c..d09766a4c 100644 --- a/web/sections/ImageCarousel/ImageCarousel.tsx +++ b/web/sections/ImageCarousel/ImageCarousel.tsx @@ -20,7 +20,12 @@ const ImageCarousel = forwardRef(function const headingId = useId() return ( - + {((title && !hideTitle) || ingress) && (
{title && !hideTitle && ( @@ -37,7 +42,7 @@ const ImageCarousel = forwardRef(function +
{title && (