Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Divider] Responsive orientation #29908

Open
2 tasks done
kevineaton603 opened this issue Nov 26, 2021 · 12 comments
Open
2 tasks done

[Divider] Responsive orientation #29908

kevineaton603 opened this issue Nov 26, 2021 · 12 comments
Labels
component: divider This is the name of the generic UI component, not the React module! new feature New feature or request waiting for 👍 Waiting for upvotes

Comments

@kevineaton603
Copy link

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Summary 💡

Currently the Divider component can only consume horizontal | vertical.

I would like to see it use the ResponsiveStyleValue as a possible option for the orientation property.

It should work something like this...

<Divider
  orientation={{ xs: 'horizontal', sm: 'vertical' }}
/>

Examples 🌈

No response

Motivation 🔦

I want to use a responsive Divider in conjunction with the Stack component as the Stack component can change directions responsively.

Using the components together would look something like this...

<Stack
  direction={{ xs: 'column', sm: 'row' }}
  alignItems={'center'}
  justifyContent={'center'}
  spacing={2}
  divider={(
    <Divider
      orientation={{ xs: 'horizontal', sm: 'vertical' }}
      flexItem={true}
    />
)}
>
{children}
</Stack>

I think that this features would be very useful not just to me but others developers as they start using the Stack component. I am sure that I won't be the only one to run into these problem in the future.

@kevineaton603 kevineaton603 added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Nov 26, 2021
@mbrookes mbrookes changed the title Responsive Divider Orientation [Divider] Responsive orientation Nov 28, 2021
@mbrookes mbrookes added component: divider This is the name of the generic UI component, not the React module! new feature New feature or request and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Nov 28, 2021
@hbjORbj hbjORbj added the waiting for 👍 Waiting for upvotes label Apr 20, 2022
@aleccaputo
Copy link
Contributor

This would be cool to implement, not seeing many downsides.

@dwgr8ergolfer
Copy link

Would love to see this implemented as well.

@bjornpijnacker
Copy link

bjornpijnacker commented May 11, 2022

This would be very nice to see indeed. Until then, the snippet below works to achieve similar results

const Component = ({children}) => {
  const theme = useTheme();

  return (
    <Stack
      direction={{ xs: 'column', sm: 'row' }}
      alignItems={'center'}
      justifyContent={'center'}
      spacing={2}
      divider={(
        <Divider
          orientation={useMediaQuery(theme.breakpoints.down("md")) ? "horizontal" : "vertical"}
          flexItem={true}
        />
    )}
    >
    {children}
    </Stack>
  )
}

@Zach-Jaensch
Copy link

I have created a wrapper for the Divider using logic similar to the MUI breakpoint utilities(#29864). This enables me to use the prop exactly like the direction prop. As you can see I have used "react-singleton-hook. If you have a custom theme (specifically custom breakpoints), you must ensure you add <SingletonHooksContainer /> inside the context of your theme provider.

// src/hooks/useCurrentBreakpoint/index.ts

import { useTheme } from "@mui/material";
 import { Breakpoint } from "@mui/system";
 import { useEffect, useState } from "react";
 import { singletonHook } from "react-singleton-hook";

 // https://github.com/Light-Keeper/react-singleton-hook/issues/406#issuecomment-962282765
 // eslint-disable-next-line no-underscore-dangle
 export function _useCurrentBreakpoint(): Breakpoint {
   const globalTheme = useTheme();

   const mqs: [Breakpoint, string][] = globalTheme.breakpoints.keys.map(
     (key, index, breakpoints) => {
       let mq = "";
       if (index === breakpoints.length - 1) {
         mq = globalTheme.breakpoints.up(key);
       } else {
         mq = globalTheme.breakpoints.between(key, breakpoints[index + 1]);
       }
       return [key, mq.replace(/^@media( ?)/m, "")];
     }
   );
   const [currentBreakpoint, setCurrentBreakpoint] = useState<Breakpoint>(() => {
     const bp = mqs.find(([, mq]) => window.matchMedia(mq).matches);
     return bp ? bp[0] : "xs";
   });

   useEffect(() => {
     function handleCurrentBreakpointChange(
       key: Breakpoint,
       e: MediaQueryListEvent
     ) {
       if (e.matches) {
         setCurrentBreakpoint(key);
       }
     }

     const handlers: [string, (e: MediaQueryListEvent) => void][] = mqs.map(
       ([key, mq]) => {
         const handler = (e: MediaQueryListEvent) =>
           handleCurrentBreakpointChange(key, e);
         return [mq, handler];
       }
     );

     handlers.forEach(([mq, handler]) => {
       window.matchMedia(mq).addEventListener("change", handler);
     });

     return () => {
       handlers.forEach(([mq, handler]) => {
         window.matchMedia(mq).removeEventListener("change", handler);
       });
     };
   }, [mqs]);

   return currentBreakpoint;
 }

 const useCurrentBreakpoint = singletonHook("xs", _useCurrentBreakpoint);

 export { useCurrentBreakpoint };
// src/components/ResponsiveDivider/index.tsx

import { useTheme } from "@mui/material";
 import { Breakpoint, ResponsiveStyleValue } from "@mui/system";

 function isBPValueAnObject<T>(
   breakpointValues: ResponsiveStyleValue<T>
 ): breakpointValues is Record<Breakpoint, T | null> {
   return (
     typeof breakpointValues === "object" && !Array.isArray(breakpointValues)
   );
 }

 export function useResolveAllBreakpoints<T>(
   breakpointValues: ResponsiveStyleValue<T>
 ): Record<Breakpoint, T> {
   const bpKeys = useTheme().breakpoints.keys;
   const bpsProvided = (() => {
     if (typeof breakpointValues !== "object") {
       return [];
     }

     let b: Breakpoint[] = [];

     if (Array.isArray(breakpointValues)) {
       b = breakpointValues.map((_, i) => bpKeys[i]);
     } else {
       b = Object.entries(breakpointValues as Record<Breakpoint, T | null>)
         .filter(([k, v]) => bpKeys.includes(k as Breakpoint) && v != null)
         .map(([k]) => k as Breakpoint);
     }
     return b;
   })();
   if (bpsProvided.length === 0) {
     return Object.fromEntries(
       bpKeys.map((k) => [k, breakpointValues as T])
     ) as Record<Breakpoint, T>;
   }

   let previous: Breakpoint | number;

   return bpKeys.reduce(
     (acc: Partial<Record<Breakpoint, unknown>>, breakpoint, i) => {
       if (Array.isArray(breakpointValues)) {
         if (breakpointValues[i] != null) {
           acc[breakpoint] = breakpointValues[i];
           previous = i;
         } else {
           acc[breakpoint] = breakpointValues[previous as number];
         }
       } else if (isBPValueAnObject(breakpointValues)) {
         if (breakpointValues[breakpoint] != null) {
           acc[breakpoint] = breakpointValues[breakpoint];
           previous = breakpoint;
         } else {
           acc[breakpoint] = breakpointValues[previous as Breakpoint];
         }
       } else {
         acc[breakpoint] = breakpointValues;
       }
       return acc;
     },
     {}
   ) as Record<Breakpoint, T>;
 }
// src/hooks/useResolveAllBreakpoints/index.ts

import { Divider, DividerProps } from "@mui/material";
import { ResponsiveStyleValue } from "@mui/system";

import { useCurrentBreakpoint } from "src/hooks/useCurrentBreakpoint";
import { useResolveAllBreakpoints } from "src/hooks/useResolveAllBreakpoints";

export function ResponsiveDivider({
 orientation,
 ...props
}: {
 orientation: ResponsiveStyleValue<"horizontal" | "vertical">;
} & Omit<DividerProps, "orientation">): JSX.Element {
 const currentBreakpoint = useCurrentBreakpoint();

 const bpValues = useResolveAllBreakpoints(orientation);

 const currentOrientation = bpValues[currentBreakpoint];

 return <Divider {...props} orientation={currentOrientation} />;
}

@Melancholism
Copy link

Still looking forward to this!

@TheBongStack
Copy link

Any updates on this issue? Its still not fixed.

@pablojsx
Copy link

pablojsx commented Apr 4, 2024

waiting for this, also for the calendar ahahhahaha

@Linuhusainnk
Copy link

Any updates on this, to have different orientation in different breakpoints

@korydondzila
Copy link

How is this not a thing yet?

@mleister97
Copy link

Would love to see this

@pablojsx
Copy link

Crap I thought this was the Nextui page haha

@yash49
Copy link
Contributor

yash49 commented Dec 24, 2024

Hey maintainers
is this issue ready to take ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: divider This is the name of the generic UI component, not the React module! new feature New feature or request waiting for 👍 Waiting for upvotes
Projects
None yet
Development

No branches or pull requests