Open kevineaton603 opened 2 years ago
This would be cool to implement, not seeing many downsides.
Would love to see this implemented as well.
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>
)
}
I have created a wrapper for the Divider using logic similar to the MUI breakpoint utilities(https://github.com/mui/material-ui/issues/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} />;
}
Still looking forward to this!
Any updates on this issue? Its still not fixed.
waiting for this, also for the calendar ahahhahaha
Any updates on this, to have different orientation in different breakpoints
How is this not a thing yet?
Duplicates
Latest version
Summary 💡
Currently the
Divider
component can only consumehorizontal | vertical
.I would like to see it use the
ResponsiveStyleValue
as a possible option for theorientation
property.It should work something like this...
Examples 🌈
No response
Motivation 🔦
I want to use a responsive
Divider
in conjunction with theStack
component as theStack
component can change directions responsively.Using the components together would look something like this...
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.