From 980477ea845dfd6ed1bb9606a364cd383ac93e79 Mon Sep 17 00:00:00 2001 From: Martin Bittnerm <martin.bittner@yeebase.com> Date: Thu, 16 Jan 2025 08:15:50 +0100 Subject: [PATCH 1/2] feat: allow icon button in carousel --- .../components/src/Carousel/Carousel.d.ts | 1 + packages/components/src/Carousel/Carousel.tsx | 62 +++++++++++++++-- .../components/content/carousel.stories.d.ts | 2 + .../components/content/carousel.stories.tsx | 66 +++++++++++++++++++ 4 files changed, 125 insertions(+), 6 deletions(-) diff --git a/packages/components/src/Carousel/Carousel.d.ts b/packages/components/src/Carousel/Carousel.d.ts index 59184596..9bad6146 100644 --- a/packages/components/src/Carousel/Carousel.d.ts +++ b/packages/components/src/Carousel/Carousel.d.ts @@ -15,6 +15,7 @@ export interface CarouselProps { hideNextButton?: boolean; hidePrevButton?: boolean; onChange?: (currentIndex: number) => void; + isIconButton?: boolean; children?: ReactNode; } declare const Carousel: React.FC<CarouselProps>; diff --git a/packages/components/src/Carousel/Carousel.tsx b/packages/components/src/Carousel/Carousel.tsx index 9f1ea0da..07d30c24 100644 --- a/packages/components/src/Carousel/Carousel.tsx +++ b/packages/components/src/Carousel/Carousel.tsx @@ -4,8 +4,15 @@ import { default as SlickSlider, ResponsiveObject } from 'react-slick'; import styled from 'styled-components'; import { display, layout, space } from 'styled-system'; +import { + MaterialCheck, + MaterialChevronLeft, + MaterialChevronRight, +} from '@t3n/icons'; + import Box from '../Box'; import Button from '../Button'; +import IconButton from '../IconButton'; export interface CarouselProps { slidesToShow?: number; @@ -22,6 +29,7 @@ export interface CarouselProps { hideNextButton?: boolean; hidePrevButton?: boolean; onChange?: (currentIndex: number) => void; + isIconButton?: boolean; children?: ReactNode; } @@ -68,17 +76,43 @@ const StyledPrevButton = styled(Button)` ${({ theme }) => display({ theme, display: ['none', 'block'] })} `; +const StyledIconButtonWrapper = styled.div<{ position: 'left' | 'right' }>` + position: absolute; + bottom: 0; + ${({ position }) => (position === 'left' ? 'left: 0;' : 'right: 0;')} + z-index: 1; +`; + const NextButton: React.FC<{ onClick?: () => void; show: boolean; - label: string; + label?: string; customOnClick?: () => void; -}> = ({ onClick, show = true, label, customOnClick }) => { + isIconButton?: boolean; + isLastSlide?: boolean; +}> = ({ + onClick, + show = true, + label, + customOnClick, + isIconButton, + isLastSlide, +}) => { if (!show) return null; + const icon = isLastSlide ? MaterialCheck : MaterialChevronRight; + + if (isIconButton) { + return ( + <StyledIconButtonWrapper position="right"> + <IconButton onClick={customOnClick || onClick} icon={icon} /> + </StyledIconButtonWrapper> + ); + } + return ( <StyledNextButton onClick={customOnClick || onClick}> - {label} + {isLastSlide ? 'Done' : label} </StyledNextButton> ); }; @@ -86,11 +120,23 @@ const NextButton: React.FC<{ const PrevButton: React.FC<{ onClick?: () => void; show: boolean; - label: string; + label?: string; customOnClick?: () => void; -}> = ({ onClick, show = true, label, customOnClick }) => { + isIconButton?: boolean; +}> = ({ onClick, show = true, label, customOnClick, isIconButton }) => { if (!show) return null; + if (isIconButton) { + return ( + <StyledIconButtonWrapper position="left"> + <IconButton + onClick={customOnClick || onClick} + icon={MaterialChevronLeft} + /> + </StyledIconButtonWrapper> + ); + } + return ( <StyledPrevButton onClick={customOnClick || onClick} variant="secondary"> {label} @@ -112,6 +158,7 @@ const Carousel: React.FC<CarouselProps> = ({ onPrevClick, hideNextButton, hidePrevButton, + isIconButton, onChange, children, }) => { @@ -123,7 +170,7 @@ const Carousel: React.FC<CarouselProps> = ({ onChange(currentIndex); } }, [currentIndex, onChange]); - + const isLastSlide = currentIndex === slidesAmount - slidesToShow; return ( <Box> <StyledSlider @@ -141,6 +188,8 @@ const Carousel: React.FC<CarouselProps> = ({ ? !hideNextButton : currentIndex < slidesAmount - 1 || infinite } + isLastSlide={isLastSlide} + isIconButton={isIconButton} label={nextLabel} customOnClick={onNextClick} /> @@ -152,6 +201,7 @@ const Carousel: React.FC<CarouselProps> = ({ ? !hidePrevButton : currentIndex > 0 || infinite } + isIconButton={isIconButton} label={prevLabel} customOnClick={onPrevClick} /> diff --git a/packages/storybook/src/stories/components/content/carousel.stories.d.ts b/packages/storybook/src/stories/components/content/carousel.stories.d.ts index e6c67d62..a94d279f 100644 --- a/packages/storybook/src/stories/components/content/carousel.stories.d.ts +++ b/packages/storybook/src/stories/components/content/carousel.stories.d.ts @@ -8,3 +8,5 @@ export declare const infinite: Story; export declare const autoplay: Story; export declare const responsive: Story; export declare const sliderInModal: Story; +export declare const carouselWithChevronButtons: Story; +export declare const sliderWithChevronButtonsInModal: Story; diff --git a/packages/storybook/src/stories/components/content/carousel.stories.tsx b/packages/storybook/src/stories/components/content/carousel.stories.tsx index 6279e018..e8c6d430 100644 --- a/packages/storybook/src/stories/components/content/carousel.stories.tsx +++ b/packages/storybook/src/stories/components/content/carousel.stories.tsx @@ -99,6 +99,7 @@ const meta: Meta<typeof Carousel> = { nextLabel: 'Nächste', prevLabel: 'Zurück', speed: 500, + isIconButton: false, children: defaultData.map((el) => ( <Box key={el.id} mb={8} overflow="hidden"> <Image @@ -150,6 +151,12 @@ const StyledBox = styled(Box)` export const carousel: Story = {}; +export const carouselWithChevronButtons: Story = { + args: { + isIconButton: true, + }, +}; + export const infinite: Story = { args: { infinite: true, @@ -257,3 +264,62 @@ export const sliderInModal: Story = { ); }, }; + +export const sliderWithChevronButtonsInModal: Story = { + decorators: [storyContainerDecorator], + render: () => { + const [showOnboardingModal, setShowOnboardingModal] = useState(true); + const [currentIndex, setCurrentIndex] = useState(0); + + return ( + <> + <Button onClick={() => setShowOnboardingModal(true)}> + Slider im Modal anzeigen + </Button> + + {showOnboardingModal && ( + <StyledBox> + <Modal headline="" onClose={() => setShowOnboardingModal(false)}> + <Carousel + onNextClick={ + currentIndex === defaultData.length - 1 + ? () => setShowOnboardingModal(false) + : undefined + } + isIconButton + hideNextButton={false} + nextLabel={ + currentIndex === defaultData.length - 1 + ? 'Schließen' + : undefined + } + onChange={setCurrentIndex} + > + {defaultData.map((el) => ( + <Box key={el.id} mt={2} mb={8} overflow="hidden"> + <Image + m="0 auto" + height={['165px', '180px', '150px', '200px', '250px']} + src={el.imageSrc} + alt={el.headline} + title={el.headline} + lazy={false} + /> + + <Text bold mt={3} mb={2}> + {el.name} + </Text> + <Heading as="h5" my={0}> + {el.headline} + </Heading> + <Text>{el.description}</Text> + </Box> + ))} + </Carousel> + </Modal> + </StyledBox> + )} + </> + ); + }, +}; From d82269abf4e36ce438cee0c68896984c0d0dff70 Mon Sep 17 00:00:00 2001 From: Martin Bittnerm <martin.bittner@yeebase.com> Date: Thu, 16 Jan 2025 08:18:39 +0100 Subject: [PATCH 2/2] fix: remove testing code --- packages/components/src/Carousel/Carousel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/Carousel/Carousel.tsx b/packages/components/src/Carousel/Carousel.tsx index 07d30c24..8f4f2b57 100644 --- a/packages/components/src/Carousel/Carousel.tsx +++ b/packages/components/src/Carousel/Carousel.tsx @@ -112,7 +112,7 @@ const NextButton: React.FC<{ return ( <StyledNextButton onClick={customOnClick || onClick}> - {isLastSlide ? 'Done' : label} + {label} </StyledNextButton> ); };