You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React/Material UI项目如何让Panel响应式充满屏幕剩余宽度

问题背景

现有基于React+Material UI开发的应用,Portal组件负责展示Panel和Widget两个子组件,主应用代码如下:

export default function App() {
  return (
    <div
      style={{
        backgroundColor: "blue"
      }}
    >
      <Portal></Portal>
    </div>
  );
}

核心逻辑集中在Portal.js和Layout.js两个文件中:

  • Layout定义各断点响应式适配参数,实现Portal对窗口尺寸的响应式适配
  • Portal将Widget固定在左上角,xs、sm断点下Panel排布在Widget下方,md、lg、xl断点下排布在Widget右侧
  • Widget保持固定宽高比,示例中宽为400、高为800,支持动态修改
核心代码

Layout断点适配逻辑

const Layout = (aspect_ratio) => {
  const theme = useTheme();
  const xsMatches = useMediaQuery(theme.breakpoints.up("xs"));
  const smMatches = useMediaQuery(theme.breakpoints.up("sm"));
  const mdMatches = useMediaQuery(theme.breakpoints.up("md"));
  const lgMatches = useMediaQuery(theme.breakpoints.up("lg"));
  const xlMatches = useMediaQuery(theme.breakpoints.up("xl"));

  var sizingDetails = {
    xl: {
      widgetGridCells: 10,
      bmkGridCells: 2
    },
    lg: {
      widgetGridCells: 8,
      bmkGridCells: 4
    },
    md: {
      widgetGridCells: 9,
      bmkGridCells: 3
    },
    sm: {
      widgetGridCells: 12,
      bmkGridCells: 12
    },
    xs: {
      widgetGridCells: 12,
      bmkGridCells: 12
    }
  };

  if (xlMatches) {
    sizingDetails.selWidgetWidth = (theme.breakpoints.values.xl * 10.0) / 12.0;
    sizingDetails.viewWidth = theme.breakpoints.values.xl;
  } else if (lgMatches) {
    sizingDetails.selWidgetWidth = (theme.breakpoints.values.lg * 8.0) / 12.0;
    sizingDetails.viewWidth = theme.breakpoints.values.lg;
  } else if (mdMatches) {
    sizingDetails.selWidgetWidth = (theme.breakpoints.values.md * 9) / 12.0;
    sizingDetails.viewWidth = theme.breakpoints.values.md;
  } else if (smMatches) {
    sizingDetails.selWidgetWidth = theme.breakpoints.values.sm;
    sizingDetails.viewWidth = theme.breakpoints.values.sm;
  } else if (xsMatches) {
    sizingDetails.selWidgetWidth = theme.breakpoints.values.xs;
    sizingDetails.viewWidth = theme.breakpoints.values.xs;
  } else {
    sizingDetails.selWidgetWidth = 0;
    sizingDetails.viewWidth = 0;
  }
  sizingDetails.selWidgetHeight = sizingDetails.selWidgetWidth * aspect_ratio;

  return sizingDetails;
};

export default Layout;

Portal组件

函数逻辑

export default function Portal() {
  const selWidHeight = 400;
  const selWidWidth = 800;

  const layout = Layout(selWidHeight / selWidWidth);
  const sizes = {
    containerWidth: layout.selWidgetWidth,
    containerHeight: layout.selWidgetHeight,
    bookmarkPanelContainerHeight: "600px",
    widgetWidth: "90%",
    widgetHeight: "90%",
    viewWidth: layout.viewWidth
  };
  const classes = useStyles(sizes);

  return (
    <>
      {layout.viewWidth > 0 ? (
        <Grid
          container
          direction="row"
          alignItems="flex-start"
          alignContent="flex-start"
          className={classes.root}
        >
          <Grid
            item
            xs={layout.xs.widgetGridCells}
            sm={layout.sm.widgetGridCells}
            md={layout.md.widgetGridCells}
            lg={layout.lg.widgetGridCells}
            xl={layout.xl.widgetGridCells}
          >
            <Container
              className={classes.widgetContainer}
              disableGutters={true}
              maxWidth={false}
            >
              <Widget
                className={classes.widget}
                width={sizes.widgetWidth}
                height={sizes.widgetHeight}
              />
            </Container>
          </Grid>

          <Grid
            item
            xs={layout.xs.bmkGridCells}
            sm={layout.sm.bmkGridCells}
            md={layout.md.bmkGridCells}
            lg={layout.lg.bmkGridCells}
            xl={layout.xl.bmkGridCells}
          >
            <Container
              className={classes.bookmarkPanel}
              disableGutters={true}
            >
              <Panel />
            </Container>
          </Grid>
        </Grid>
      ) : (
        <div>窗口尺寸过小</div>
      )}
    </>
  );
}

样式逻辑

const useStyles = makeStyles((theme) => {
  return {
    root: {
      width: (props) => props.viewWidth,
      height: "100%",
      margin: "0px"
    },
    rootContainer: {
      display: "flex",
      justifyContent: "flex-start",
      flexDirection: "row"
    },
    widgetContainer: {
      width: (props) => props.containerWidth,
      height: (props) => props.containerHeight,
      minWidth: (props) => props.containerWidth,
      backgroundColor: "orange",
      display: "flex",
      flexDirection: "row",
      flexWrap: "wrap",
      justifyContent: "center",
      alignItems: "center",
      position: "relative"
    },
    bookmarkPanel: {
      overflowY: "scroll",
      border: "1px solid green",
      float: "left",
      height: (props) => props.bookmarkPanelContainerHeight,
      position: "relative",
      background: "repeating-linear-gradient(17deg,  #ebf8e1 15%, #f69d3c 20%)"
    },
    widget: {
      width: (props) => {
        return props.containerWidth;
      },
      height: (props) => {
        return props.widgetHeight;
      },
      background:
        "repeating-linear-gradient(45deg, #3f87a6, #ebf8e1 15%, #f69d3c 20%)",
      position: "absolute"
    }
  };
});

Panel组件

const useStyles = makeStyles((theme) => ({
  root: {
    backgroundColor: "red"
  }
}));

const Panel = () => {
  const classes = useStyles();
  return <div className={classes.root}>Panel </div>;
};

Widget组件

const useStyles = makeStyles((theme) => ({
  root: {
    width: (props) => props.width,
    height: (props) => props.height,
    background:
      "repeating-linear-gradient(45deg, #3f87a6, #ebf8e1 15%, #f69d3c 20%)"
  }
}));

const Widget = ({ width, height }) => {
  const props = { width: width, height: height };
  const classes = useStyles(props);
  return <div className={classes.root}>Widget </div>;
};
问题描述
  • xs、sm断点下表现正常:Panel排布在Widget下方,宽度符合预期
  • md、lg、xl断点下Widget展示正常,Panel位置和高度符合预期,但无法填满蓝色父容器的剩余宽度,固定为240px宽
  • 需求:同一断点下,浏览器窗口宽度增加时,Panel宽度动态扩展,填满蓝色父容器的剩余空间
解决方案

问题出在两处样式配置错误,修改即可实现需求:

  1. 修改Grid容器的宽度配置,将固定断点宽度改为占满父容器100%宽度
// 修改前root样式
root: {
  width: (props) => props.viewWidth,
  height: "100%",
  margin: "0px"
}
// 修改后root样式
root: {
  width: "100%",
  height: "100%",
  margin: "0px"
}
  1. 给Panel外层容器添加100%宽度配置,确保占满所在Grid单元格的全部宽度
// 修改前bookmarkPanel样式
bookmarkPanel: {
  overflowY: "scroll",
  border: "1px solid green",
  float: "left",
  height: (props) => props.bookmarkPanelContainerHeight,
  position: "relative",
  background: "repeating-linear-gradient(17deg,  #ebf8e1 15%, #f69d3c 20%)"
}
// 修改后bookmarkPanel样式
bookmarkPanel: {
  width: "100%",
  overflowY: "scroll",
  border: "1px solid green",
  float: "left",
  height: (props) => props.bookmarkPanelContainerHeight,
  position: "relative",
  background: "repeating-linear-gradient(17deg,  #ebf8e1 15%, #f69d3c 20%)"
}

内容的提问来源于stack exchange,提问作者David Sargrad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 18:06:00