mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.73k stars 32.24k forks source link

[Slider] Setting it as relative to a container is not working #31977

Closed Tanmay2711 closed 2 years ago

Tanmay2711 commented 2 years ago

I am building my own image carousel component. I am using Slide component to slide in/out images. Setting it's container property using react ref to carousel container div so that images will start transitioning from edge of that reference element, however its not working and images always start transitioning from edge of computer screen (right/left)

here is my code - AppCarousel.js

`import React, { useReducer, useEffect, useRef, cloneElement } from 'react'; import styled from 'styled-components'; import { ChevronLeft, ChevronRight } from 'components/global/Icons'; import { Slide } from '@material-ui/core';

/* TODO:

const reducer = (itemCount) => (state, action) => { switch (action.type) { case 'exact': if (state.currentItem !== action.payload) { return { currentItem: action.payload }; } return state; case 'next': return state.currentItem === itemCount - 1 ? { currentItem: 0, direction: 'left' } : { currentItem: state.currentItem + 1, direction: 'left' }; case 'previous': return state.currentItem === 0 ? { currentItem: itemCount - 1, direction: 'right' } : { currentItem: state.currentItem - 1, direction: 'right' }; default: console.error('Failed to load item.'); break; } };

const AppCarousel = ({ children, className, options = {} }) => { const { disableControls, activeIndex = 0 } = options; const itemCount = React.Children.count(children); const containerRef = useRef(null); const childItems = React.Children.toArray(children); const [state, dispatch] = useReducer(reducer(itemCount), { currentItem: activeIndex < itemCount && activeIndex > -1 ? activeIndex : 0, direction: 'right' });

const loadPrevItem = () => {
    if (!disableControls) {
        dispatch({ type: 'previous' });
    }
};

const loadNextItem = () => {
    if (!disableControls) {
        dispatch({ type: 'next' });
    }
};

const loadTargetItem = (index) => {
    if (!disableControls) {
        dispatch({ type: 'exact', payload: index });
    }
};

const handleKeys = (key) => {
    switch (key) {
        case 'ArrowLeft':
            loadPrevItem();
            break;
        case 'ArrowRight':
            loadNextItem();
            break;
        default:
            break;
    }
};

return (
    <StyledCarouselWrapper
        aria-label='Carousel Control'
        tabIndex={0}
        onKeyDown={(e) => handleKeys(e.key)}
        onClick={childItems[state.currentItem]?.onClick}
        className={className}
    >
        <StyledFlexRow>
            <StyledLeftArrowButton
                id='prev-button'
                aria-label='Show previous item'
                aria-controls='content-area'
                onClick={loadPrevItem}
                disabled={disableControls}
            >
                <ChevronLeft />
            </StyledLeftArrowButton>
            <StyledContentArea
                id='content-area'
                aria-label='Content display area'
                role='presentation'
                itemCount={itemCount}
                currentItem={state.currentItem}
                ref={containerRef}
            >
                {/* <Slide direction='right' in timeout={1000}>
                    {childItems[state.currentItem]}
                </Slide> */}
                {cloneElement(childItems[state.currentItem], {
                    ref: containerRef,
                    direction: state.direction
                })}
                {/* {childItems[state.currentItem]} */}
            </StyledContentArea>
            <StyledRightArrowButton
                id='next-button'
                aria-label='Show next item'
                aria-controls='content-area'
                onClick={loadNextItem}
                disabled={disableControls}
            >
                <ChevronRight />
            </StyledRightArrowButton>
        </StyledFlexRow>
        <BubbleNav
            childItems={childItems}
            onClick={loadTargetItem}
            currentItem={state.currentItem}
            disabled={disableControls}
        />
    </StyledCarouselWrapper>
);

};

export const AppCarouselItem = React.forwardRef( ({ children, direction }, ref) => { console.log('Ref ', ref); return ( <Slide direction={direction} in //timeout={1000} container={ref.current}** // mountOnEnter // unmountOnExit

{children}

** ); } );

const ChildContainer = React.forwardRef((props, ref) => { return ( <div ref={ref} {...props}> {props.children}

); });

const BubbleNav = ({ childItems, onClick, currentItem, disabled }) => ( <StyledBubbleWrapper aria-label='Item navigation' aria-controls='content-area'

{childItems.map((item, index) => { return ( <StyledDot key={index} index={index} id={carousel-item-${index}} aria-label={ item?.ariaLabel ? item.ariaLabel : Item ${index} } onClick={() => onClick(index)} currentItem={currentItem} disabled={disabled} ); })} );

export default AppCarousel;

const StyledDot = styled.div background-color: ${({ index, currentItem, theme }) => index === currentItem ? theme.carousel.bubbleColorSelected : theme.carousel.bubbleColor}; border-radius: 0.3rem; width: 0.6rem; height: 0.6rem; margin: 0 0.2rem 0; opacity: ${({ disabled }) => (disabled ? 0.5 : 1)}; :hover { cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')}; } ;

const StyledBubbleWrapper = styled.div display: flex; align-self: center; margin-top: 0.5rem; ;

const StyledCarouselWrapper = styled.div display: flex; flex-direction: column; justify-content: space-around; ;

const StyledFlexRow = styled.div display: flex; flex-direction: row; align-self: center; ;

const StyledContentArea = styled.div display: flex; background-color: ${({ theme }) => theme.carousel.contentBackgroundColor}; min-width: ${({ itemCount }) =>${1 * itemCount}rem`};

const StyledArrowButton = styled.button color: ${({ theme }) => theme.carousel.navigationArrowColor}; z-index: 1; background: none; border: none; align-self: center; height: fit-content; font-size: ${({ theme }) => theme.iconSizeSmall}; opacity: ${({ disabled }) => (disabled ? 0.5 : 1)}; :hover, :focus { cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')}; } & i::before { font-weight: bolder; } ;

const StyledLeftArrowButton = styled(StyledArrowButton) margin-right: 1rem; ; const StyledRightArrowButton = styled(StyledArrowButton) margin-left: 1rem; ; `

danilo-leal commented 2 years ago

Hey @Tanmay2711, thanks for opening the issue up! To better assist you and go through your problem, could you provide a CodeSanbox that contains the above code, so we're able to reproduce it?

Tanmay2711 commented 2 years ago

@danilo-leal - Sorry for not able to provide codebox link earlier - here is code box link where you can see this issue is reproducible.

https://codesandbox.io/s/distracted-sanderson-bj7thn?file=/src/AppCarousel.js

https://bj7thn.csb.app/

danilo-leal commented 2 years ago

Just noticed that you're using an older version of Material UI. Have you tried using the latest one to see if the problem persists?

Tanmay2711 commented 2 years ago

I have updated Material UI version, seems issue is partially resolved. I have 4 child items in my Carousel component and for some children - its transitioning from reference container but for some child it still transitions from edge of the screen. you can refer to below updated URL - https://codesandbox.io/s/vigilant-jackson-1pmwc5?file=/src/App.js https://1pmwc5.csb.app/

Expected - transition should start before Next and Prev buttons - sometimes it starts after those buttons, because container reference is the give div element -

image

github-actions[bot] commented 2 years ago

Since the issue is missing key information, and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.

Tanmay2711 commented 2 years ago

I have provided all detailed information with sandbox link to reproduce this issue.

Tanmay2711 commented 2 years ago

@danilo-leal - Is there any updates on this issue from your end ?

github-actions[bot] commented 2 years ago

Since the issue is missing key information, and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.

ghost commented 2 years ago

@Tanmay2711 I also faced the same trouble and it took my day.

I must be too late for you but put the solution for others.


Please add the style overflow: hidden to the container DOM.

Document makes it sound as all you need to do is only specifying container props, but in fact you also need to set overflow style to the container DOM.

ref) https://stackoverflow.com/a/69787498


@danilo-leal I think the documentation is misleading and should be improved so that it is clear that overflow: hidden is required.

And, this issue is not about Slider but Slide.

EmilianoMorghen commented 8 months ago

Slide gave me more troubles than what it solves. This is a custom implementation of a slide-able div. The container must have position: relative set. Also the SlideDiv component should have a starting top set.

Customize it as you want.

import { Box, BoxProps, CSSObject, Theme, styled } from '@mui/material';

const slideInMixin = (theme: Theme): CSSObject => ({
  transition: theme.transitions.create(['top', 'opacity'], {
    easing: theme.transitions.easing.easeInOut,
    duration: theme.transitions.duration.enteringScreen,
  }),
  top: '0px',
  opacity: 1,
});

const slideOutMixin = (theme: Theme): CSSObject => ({
  transition: theme.transitions.create(['top', 'opacity'], {
    easing: theme.transitions.easing.easeInOut,
    duration: theme.transitions.duration.enteringScreen,
  }),
  top: '100px',
  opacity: 0,
});

interface SlideDivProps extends BoxProps {
  slide?: boolean;
}

export const SlideDiv = styled(Box, {
  shouldForwardProp: (prop) => prop !== 'slide',
})<SlideDivProps>(({ theme, slide }) => ({
  ...(slide && {
    '&': slideInMixin(theme),
  }),
  ...(!slide && {
    '&': slideOutMixin(theme),
  }),
}));