next.js 响应式容器内的网格MUI

vc6uscn9  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(79)

我试图实现类似于YouTube在其网站上所做的事情,那就是:
100d 1xx 1c 1d 1x的字符串
基本上,YT(似乎要做的)是根据容器大小增加/减少网格计数,基于抽屉是否打开/关闭。
底线是,我的代码没有反映布局的变化(我怀疑MUI监听的是窗口的大小而不是父窗口的大小)。
版本:
“@mui/material”:“^5.15.0”,“next”:“^14.0.4”,“react”:“18.2.0”,
谢谢
我试过使用MUI + NextJS做类似的事情,但它不起作用。
相关片段:

import { Box, Container } from '@mui/material'
import Header from '../components/Layout/Header'
import { ServerAuthProvider } from '../../auth/server-auth-provider'

export default function Layout(props: {
    children: React.ReactNode,
    body: React.ReactNode,
    filters: React.ReactNode,
}) {
    return (
        <ServerAuthProvider>
            <Header maxWidth={false}>
                <Container maxWidth={false}>
                    <Box ml={2} mt={10}>
                        {props.filters}
                        {props.body}
                        {props.children}
                    </Box>
                </Container>
            </Header>
        </ServerAuthProvider>
    )
}

字符串
身体(相关部分)

<Box sx={{ mt: 2, mb: 2, ml: -1, mr: -1 }}>
            <Unstable_Grid2 container spacing={2}>
                {events?.map((event: EventSummary) => (
                    <Unstable_Grid2 xs={12} sm={6} md={6} lg={4} xl={3} xxl={12 / 5} key={event.id + event.starts_at}>
                        <EventCard event={event} />
                    </Unstable_Grid2>
                ))}
            </Unstable_Grid2 >
        </Box >
const drawerWidth = 240;

const openedMixin = (theme: Theme): CSSObject => ({
    width: drawerWidth,
    transition: theme.transitions.create('width', {
        easing: theme.transitions.easing.sharp,
        duration: theme.transitions.duration.enteringScreen,
    }),
    overflowX: 'hidden',
    border: 'none',
    borderWidth: 0,
});

const closedMixin = (theme: Theme): CSSObject => ({
    transition: theme.transitions.create('width', {
        easing: theme.transitions.easing.sharp,
        duration: theme.transitions.duration.leavingScreen,
    }),
    overflowX: 'hidden',
    border: 'none',
    borderWidth: 0,
    width: `calc(${theme.spacing(7)} + 1px)`,
    [theme.breakpoints.up('sm')]: {
        width: `calc(${theme.spacing(8)} + 1px)`,
    },
});

const DrawerHeader = styled('div')(({ theme }) => ({
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'flex-end',
    padding: theme.spacing(0, 1),
    // necessary for content to be below app bar
    ...theme.mixins.toolbar,
}));

const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
    ({ theme, open }) => ({
        width: drawerWidth,
        flexShrink: 0,
        whiteSpace: 'nowrap',
        boxSizing: 'unset',
        ...(open && {
            ...openedMixin(theme),
            '& .MuiDrawer-paper': openedMixin(theme),
        }),
        ...(!open && {
            ...closedMixin(theme),
            '& .MuiDrawer-paper': closedMixin(theme),
        }),
    }),
);

const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{
    open?: boolean;
}>(({ theme, open }) => ({
    flexGrow: 1,
    // padding: theme.spacing(3),
    // transition: theme.transitions.create('margin', {
    //     easing: theme.transitions.easing.sharp,
    //     duration: theme.transitions.duration.leavingScreen,
    // }),
    // width: `calc(100% - ${drawerWidth}px)`,
    ...(open && {
        transition: theme.transitions.create('margin', {
            easing: theme.transitions.easing.easeOut,
            duration: theme.transitions.duration.enteringScreen,
        }),
        marginLeft: 0,
        overflow: 'auto'
    }),
}));

export default function PrimaryHeader({ maxWidth, children }: { maxWidth?: false | undefined, children?: ReactNode }) {
    const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
    const [open, setOpen] = useState(false)

    const { user } = useAuth();
    const { getFirebaseAuth } = useFirebaseAuth()
    const router = useRouter()

    const theme = useTheme()
    const isXl = useMediaQuery(theme.breakpoints.only('xl'))
    const isMd = useMediaQuery(theme.breakpoints.up('md'))

    const logout = async () => {
        const auth = getFirebaseAuth()
        await signOut(auth)
        await fetch('/api/logout')
        window.location.reload()
    }

    const loggedOptions = [{ title: 'Logout', callback: logout }]
    const notLoggedOptions = [{ title: 'Entrar', callback: () => router.push('/login') }]
    return (
        <Box sx={{ display: 'flex' }}>
            <CssBaseline />
            <AppBar position="fixed" sx={{ bgcolor: 'white', zIndex: theme.zIndex.drawer + 1, }} elevation={0}>
                <Container disableGutters maxWidth={maxWidth}>
                    <Toolbar>
                        {!!children && <IconButton
                            color="inherit"
                            aria-label="open drawer"
                            onClick={() => { setOpen((prev) => !prev) }}
                            edge="start"
                            sx={{ mr: 2 }}
                        >
                            <MenuIcon sx={{ color: 'black' }} />
                        </IconButton>}
                    </Toolbar>
                </Container>
            </AppBar>
            {!!children && <>
                <Drawer
                    variant="permanent"
                    open={open}
                >
                    <DrawerHeader />
                    <List>
                        {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
                            <ListItem key={text} disablePadding sx={{ display: 'block' }}>
                                <ListItemButton
                                    sx={{
                                        minHeight: 48,
                                        justifyContent: open ? 'initial' : 'center',
                                        px: 2.5,
                                    }}
                                >
                                    <ListItemIcon
                                        sx={{
                                            minWidth: 0,
                                            mr: open ? 3 : 'auto',
                                            justifyContent: 'center',
                                        }}
                                    >
                                        {index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
                                    </ListItemIcon>
                                    <ListItemText primary={text} sx={{ opacity: open ? 1 : 0 }} />
                                </ListItemButton>
                            </ListItem>
                        ))}
                    </List>
                </Drawer>
                <Main open={open}><Container maxWidth={maxWidth}>{children}</Container></Main>
            </>}
        </Box>
    )
}

的数据

oyjwcjzk

oyjwcjzk1#

你是对的,断点是基于视口宽度,而不是当前容器宽度。
但你所寻找的可以使用基本的CSS网格实现:

const { useState } = React
const App = () => {
  const [isSidebarOpen, setIsSidebarOpen] = useState(false)
  return (
    <div className={'App' + (isSidebarOpen ? ' sidebar-open' : '')}>
      <header className="header">Header</header>
      <div className="sidebar" onClick={() => setIsSidebarOpen(!isSidebarOpen)}>
        {isSidebarOpen ? '<' : '>'}
      </div>
      <main>
        {Array.from({ length: 24 }).map((_, key) => (
          <div className="box" key={key}>{key + 1}</div>
        ))}
      </main>
    </div>
  )
}

ReactDOM.createRoot(root).render(<App />)
body { 
  margin: 0;
}
.App {
  min-height: 100vh;
  background-color: #f5f5f5;
  display: grid;
  grid-template: 'header header' auto 'sidebar main' 1fr / 3rem 1fr;
  transition: grid-template-columns .42s cubic-bezier(.4, 0, .2, 1);
}
.App.sidebar-open {
  grid-template-columns: 16rem 1fr;
}
.App > * {
  padding: 1rem;
}
header {
  background-color: lightcoral;
  grid-area: header;
  text-align: center;
}

.sidebar {
  grid-area: sidebar;
  background-color: lightblue;
}

main {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  grid-gap: 1rem;
  height: min-content;
}
.box {
  aspect-ratio: 1/0.7;
  border: 1px solid #eee;
  font-size: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #fff;
  box-shadow: 0 2px 4px -1px rgba(0,0,0,.1), 0 4px 5px 0 rgba(0,0,0,.07), 0 1px 10px 0 rgba(0,0,0,.06)
}
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

点击侧边栏进行切换。
关键在于

main {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))
}


...它试图在可用宽度内容纳尽可能多的150px列。任何额外的宽度然后在列之间平均分配。
150px更改为对您的情况有意义的任何内容。
其他内容都不是特别相关;我添加它是为了使示例看起来更像一个 “布局”
如果您不希望列具有可变宽度,而是希望将额外的空间分布在两侧,请使用:用途:

main {
  grid-template-columns: repeat(auto-fill, 150px);
  justify-content: center;    
}


演示:

const { useState } = React
const App = () => {
  const [isSidebarOpen, setIsSidebarOpen] = useState(false)
  return (
    <div className={'App' + (isSidebarOpen ? ' sidebar-open' : '')}>
      <header className="header">Header</header>
      <div className="sidebar" onClick={() => setIsSidebarOpen(!isSidebarOpen)}>
        {isSidebarOpen ? '<' : '>'}
      </div>
      <main>
        {Array.from({ length: 24 }).map((_, key) => (
          <div className="box" key={key}>{key + 1}</div>
        ))}
      </main>
    </div>
  )
}

ReactDOM.createRoot(root).render(<App />)

x

body { 
  margin: 0;
}
.App {
  min-height: 100vh;
  background-color: #f5f5f5;
  display: grid;
  grid-template: 'header header' auto 'sidebar main' 1fr / 3rem 1fr;
  transition: grid-template-columns .35s cubic-bezier(.5, 0, .3, 1);
}
.App.sidebar-open {
  grid-template-columns: 16rem 1fr;
}
.App > * {
  padding: 1rem;
}
header {
  background-color: lightcoral;
  grid-area: header;
  text-align: center;
}

.sidebar {
  grid-area: sidebar;
  background-color: lightblue;
}

main {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, 150px);
  justify-content: center;
  grid-gap: 1rem;
  height: min-content;
}
.box {
  aspect-ratio: 1/0.7;
  border: 1px solid #eee;
  font-size: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #fff;
  box-shadow: 0 2px 4px -1px rgba(0,0,0,.1), 0 4px 5px 0 rgba(0,0,0,.07), 0 1px 10px 0 rgba(0,0,0,.06)
}
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

同样,将150px更改为适合您需要的任何值。

相关问题