GreenSaiko
GreenSaiko

Reputation: 345

React - Mapping variables with a consistent layout

My goal is to have a container, something like @mui/Box, that displays rows of an icon and two texts. The icon and the first text are a unit and the second text is its own. Both the first, and the second texts of each row are dynamic texts, they can vary in length. I want that all 3 elements always start at the same position no matter the content of the texts. So something like this:

Desired outcome

And even when a texts changes in length, each of the 3 elements start at the same position

Desired outcome with longer label

But I just can't get it to work. I tried mapping each iconText into their own Box, but then the layouts always differ. Probably because they are each in their own container, so I thought maybe I can just map the variables of each iconText as an element and have them all positioned by a grid. And that does work (that's the layout of the images above), but then I have the issue that those elements are only wrapped in a react fragment and you can't set the key property on that.

I tried so many different layouts and looked for anything about dynamic text layouts, but nothing worked right.

The layout with grid looks like this

<Box sx={{ display: "grid", gridTemplateColumns: "1fr auto", columnGap: 3 }}>
    {iconTexts.map((iconText, index) => (
        <>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
              {iconText.icon}
              <Typography variant="h6" component={"h3"} sx={{ color: "#394454" }}>
                {iconText.label}
              </Typography>
            </Box>
            <Typography variant="h4" component={"h2"}>
              {iconText.value}
            </Typography>
        </>
    ))}
</Box>

Which would work so well, but I can't set the keys.

My question is how can I achieve the desired dynamic text layout with either grid or flex, or actually any styling.

You can look at the example in this codesandbox.

Upvotes: 1

Views: 57

Answers (2)

bluestar
bluestar

Reputation: 100

Grid is good solution. But since you show the list of Box, please wrap the card with Box instead of Fragment, and use key attribute.

like this.

<Box sx={{ display: "grid", gridTemplateColumns: "1fr auto", columnGap: 3 }}>
    {iconTexts.map((iconText, index) => (
        <Box key={index} sx={{ display: "contents" }}>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
                {iconText.icon}
                <Typography variant="h6" component="h3" sx={{ color: "#394454" }}>
                    {iconText.label}
                </Typography>
            </Box>
            <Typography variant="h4" component="h2">
                {iconText.value}
            </Typography>
        </Box>
    ))}
</Box>

Upvotes: 2

Shailesh
Shailesh

Reputation: 196

You can try this method; I believe it will resolve your problem.

          <Box
            key={index}
            sx={{
              display: "flex",
              alignItems: "center",
              justifyContent: "space-between",
              gap: 6,
            }}
          >
            <Box
              sx={{
                display: "flex",
                alignItems: "center",
                gap: 1,
                width: "100%",
              }}
            >
              {iconText.icon}
              <Typography
                variant="h6"
                component={"h3"}
                sx={{ color: "#394454" }}
              >
                {iconText.label}
              </Typography>
            </Box>
            <Box sx={{ textAlign: "start", width: "100%" }}>
              <Typography variant="h4" component={"h2"}>
                {iconText.value}
              </Typography>
            </Box>
          </Box>

Upvotes: 3

Related Questions