Skip to content

Commit 3c0dd96

Browse files
authored
[TreeView] Redesign UX for narrow windows (#3816)
1 parent 266cb4f commit 3c0dd96

File tree

10 files changed

+390
-233
lines changed

10 files changed

+390
-233
lines changed

src/components/TreeView/TreeDepiction/ChildrenRow.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { ImageList, ImageListItem } from "@mui/material";
1+
import { Grid2, ImageList, ImageListItem } from "@mui/material";
22
import { ReactElement } from "react";
33

44
import DomainTileButton from "components/TreeView/TreeDepiction/DomainTileButton";
5+
import DownBrace from "components/TreeView/TreeDepiction/DownBrace";
56
import {
67
Direction,
78
RATIO_TILE_TO_GAP,
@@ -102,14 +103,37 @@ export default function ChildrenRow(props: TreeRowProps): ReactElement {
102103
return subdomains;
103104
};
104105

105-
const numCols = getNumCols(props.currentDomain.children.length);
106-
107-
return (
106+
const numKids = props.currentDomain.children.length;
107+
const numCols = getNumCols(numKids);
108+
109+
return props.small ? (
110+
<Grid2 container justifyContent="center">
111+
<DownBrace
112+
height={48}
113+
width={(window.innerWidth * Math.min(numKids, 3)) / 3}
114+
/>
115+
<Grid2
116+
container
117+
justifyContent={numKids < 3 ? "center" : "flex-start"}
118+
spacing={2}
119+
sx={{ px: 2, width: window.innerWidth }}
120+
>
121+
{props.currentDomain.children.map((child) => (
122+
<Grid2 key={child.id} size={4}>
123+
<DomainTileButton
124+
direction={Direction.Down}
125+
domain={child}
126+
onClick={(d) => props.animate(d)}
127+
/>
128+
</Grid2>
129+
))}
130+
</Grid2>
131+
</Grid2>
132+
) : (
108133
<ImageList
109134
cols={numCols}
110135
gap={0}
111-
rowHeight={"auto"}
112-
style={{ overflow: "visible", width: numCols * props.colWidth }}
136+
sx={{ m: 0, width: numCols * props.colWidth }}
113137
>
114138
{joistRow()}
115139
{domainRow()}
Lines changed: 110 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,143 @@
1-
import { Button, ImageList, ImageListItem } from "@mui/material";
1+
import { Box, Button, Grid2, ImageList, ImageListItem } from "@mui/material";
22
import { ReactElement } from "react";
33

44
import DomainTileButton, {
55
DomainText,
66
} from "components/TreeView/TreeDepiction/DomainTileButton";
77
import {
88
Direction,
9-
TreeDepictionProps,
9+
TreeRowProps,
1010
} from "components/TreeView/TreeDepiction/TreeDepictionTypes";
11+
import { parent as parentSvg } from "resources/tree";
1112

12-
export default function CurrentRow(props: TreeDepictionProps): ReactElement {
13-
const { next, previous } = props.currentDomain;
13+
const currentDomainButtonId = "current-domain";
1414

15-
const currentTile = (
15+
export default function CurrentRow(props: TreeRowProps): ReactElement {
16+
return props.small ? (
17+
<CurrentRowSm {...props} />
18+
) : (
19+
<CurrentRowLg {...props} />
20+
);
21+
}
22+
23+
function CurrentTile(props: TreeRowProps): ReactElement {
24+
const { animate, currentDomain } = props;
25+
26+
return (
1627
<Button
17-
color="primary"
18-
disabled={!props.currentDomain.parent}
28+
data-testid={currentDomainButtonId}
29+
disabled={!currentDomain.parent}
1930
fullWidth
20-
id="current-domain"
21-
onClick={() => props.animate(props.currentDomain)}
22-
size="large"
23-
style={{ height: "95%" }}
31+
id={currentDomainButtonId}
32+
onClick={() => animate(currentDomain)}
33+
sx={{ height: "100%", p: 1 }}
2434
variant="contained"
2535
>
26-
<DomainText domain={props.currentDomain} extraProps={{ minWidth: 200 }} />
36+
<DomainText domain={currentDomain} />
2737
</Button>
2838
);
39+
}
2940

30-
return props.small ? (
31-
currentTile
32-
) : (
33-
<ImageList cols={7} gap={20} rowHeight={"auto"}>
34-
<ImageListItem cols={2}>
41+
function CurrentRowLg(props: TreeRowProps): ReactElement {
42+
const { next, parent, previous } = props.currentDomain;
43+
44+
return (
45+
<>
46+
{parent && (
47+
<>
48+
<Box>
49+
<DomainTileButton
50+
direction={Direction.Up}
51+
domain={parent}
52+
onClick={props.animate}
53+
/>
54+
</Box>
55+
<img
56+
src={parentSvg}
57+
style={{ transform: "scaleY(-1)" }}
58+
width={props.colWidth}
59+
/>
60+
</>
61+
)}
62+
<ImageList
63+
cols={7}
64+
gap={20}
65+
// Specify overflowY and zIndex for the current button bounce.
66+
sx={{ overflowY: "visible", mx: 1, my: 0, zIndex: 1 }}
67+
>
68+
<ImageListItem cols={2}>
69+
{previous && (
70+
<DomainTileButton
71+
direction={Direction.Prev}
72+
domain={previous}
73+
onClick={props.animate}
74+
/>
75+
)}
76+
</ImageListItem>
77+
<ImageListItem cols={3}>
78+
<CurrentTile {...props} />
79+
</ImageListItem>
80+
<ImageListItem cols={2}>
81+
{next && (
82+
<DomainTileButton
83+
direction={Direction.Next}
84+
domain={next}
85+
onClick={props.animate}
86+
/>
87+
)}
88+
</ImageListItem>
89+
</ImageList>
90+
</>
91+
);
92+
}
93+
94+
function CurrentRowSm(props: TreeRowProps): ReactElement {
95+
const { next, parent, previous } = props.currentDomain;
96+
97+
return (
98+
<Grid2 container columnSpacing={2} sx={{ px: 2, width: window.innerWidth }}>
99+
{parent && (
100+
<>
101+
<Grid2 size={4} />
102+
<Grid2 size={4}>
103+
<DomainTileButton
104+
direction={Direction.Up}
105+
domain={parent}
106+
onClick={props.animate}
107+
/>
108+
</Grid2>
109+
<Grid2 size={4} />
110+
<Grid2
111+
container
112+
justifyContent="center"
113+
size={12}
114+
sx={{ minHeight: 2 }}
115+
>
116+
<img src={parentSvg} style={{ transform: "scaleX(1.7)" }} />
117+
</Grid2>
118+
</>
119+
)}
120+
<Grid2 size={4}>
35121
{previous && (
36122
<DomainTileButton
37123
direction={Direction.Prev}
38124
domain={previous}
39125
onClick={props.animate}
40126
/>
41127
)}
42-
</ImageListItem>
43-
<ImageListItem cols={3}>{currentTile}</ImageListItem>
44-
<ImageListItem cols={2}>
128+
</Grid2>
129+
<Grid2 size={4}>
130+
<CurrentTile {...props} />
131+
</Grid2>
132+
<Grid2 size={4}>
45133
{next && (
46134
<DomainTileButton
47135
direction={Direction.Next}
48136
domain={next}
49137
onClick={props.animate}
50138
/>
51139
)}
52-
</ImageListItem>
53-
</ImageList>
140+
</Grid2>
141+
</Grid2>
54142
);
55143
}

src/components/TreeView/TreeDepiction/DomainTileButton.tsx

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import {
44
KeyboardArrowDown,
55
KeyboardArrowUp,
66
} from "@mui/icons-material";
7-
import { Button, Grid, Typography } from "@mui/material";
8-
import { CSSProperties, ReactElement } from "react";
7+
import { Button, Stack, Typography } from "@mui/material";
8+
import { ReactElement } from "react";
99
import { useTranslation } from "react-i18next";
1010

1111
import { SemanticDomain } from "api/models";
@@ -14,19 +14,15 @@ import { rootId } from "types/semanticDomain";
1414

1515
interface DomainTextProps {
1616
domain: SemanticDomain;
17-
extraProps?: CSSProperties;
1817
}
1918

2019
export function DomainText(props: DomainTextProps): ReactElement {
2120
const { t } = useTranslation();
21+
const { id, name } = props.domain;
2222
return (
23-
<div style={{ ...props.extraProps, textTransform: "capitalize" }}>
24-
<Typography variant={"overline"}>
25-
{props.domain.id !== rootId ? props.domain.id : ""}
26-
</Typography>
27-
<Typography variant={"body1"}>
28-
{props.domain.id !== rootId ? props.domain.name : t("addWords.domain")}
29-
</Typography>
23+
<div style={{ textTransform: "capitalize" }}>
24+
{id !== rootId && <Typography variant="overline">{id}</Typography>}
25+
<Typography>{id !== rootId ? name : t("addWords.domain")}</Typography>
3026
</div>
3127
);
3228
}
@@ -48,31 +44,25 @@ function DomainTile(props: DomainTileProps): ReactElement {
4844
);
4945
case Direction.Prev:
5046
return (
51-
<Grid
52-
container
47+
<Stack
5348
alignItems="center"
49+
direction="row"
5450
justifyContent="space-around"
55-
wrap="nowrap"
5651
>
57-
<Grid item>{rtl ? <ChevronRight /> : <ChevronLeft />}</Grid>
58-
<Grid item>
59-
<DomainText domain={props.domain} />
60-
</Grid>
61-
</Grid>
52+
{rtl ? <ChevronRight /> : <ChevronLeft />}
53+
<DomainText domain={props.domain} />
54+
</Stack>
6255
);
6356
case Direction.Next:
6457
return (
65-
<Grid
66-
container
58+
<Stack
6759
alignItems="center"
60+
direction="row"
6861
justifyContent="space-around"
69-
wrap="nowrap"
7062
>
71-
<Grid item>
72-
<DomainText domain={props.domain} />
73-
</Grid>
74-
<Grid item>{rtl ? <ChevronLeft /> : <ChevronRight />}</Grid>
75-
</Grid>
63+
<DomainText domain={props.domain} />
64+
{rtl ? <ChevronLeft /> : <ChevronRight />}
65+
</Stack>
7666
);
7767
case Direction.Up:
7868
return (
@@ -92,23 +82,17 @@ interface DomainTileButtonProps extends DomainTileProps {
9282
export default function DomainTileButton(
9383
props: DomainTileButtonProps
9484
): ReactElement {
85+
const { onClick, ...domainTileProps } = props;
9586
return (
9687
<Button
97-
color="primary"
9888
id={props.domain.id}
99-
onClick={() => props.onClick(props.domain)}
100-
style={{
101-
insetInlineStart: 0,
102-
bottom: 0,
103-
width: "95%",
104-
height: "95%",
105-
margin: "2.5%",
106-
padding: "5px",
107-
}}
89+
fullWidth
90+
onClick={() => onClick(props.domain)}
91+
sx={{ height: "100%" }}
10892
tabIndex={-1}
109-
variant={"outlined"}
93+
variant="outlined"
11094
>
111-
<DomainTile {...props} />
95+
<DomainTile {...domainTileProps} />
11296
</Button>
11397
);
11498
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { ReactElement } from "react";
2+
3+
interface DownBraceProps {
4+
backgroundColor?: string;
5+
color?: string;
6+
height: number;
7+
width: number;
8+
}
9+
10+
export default function DownBrace(props: DownBraceProps): ReactElement {
11+
const backgroundColor = props.backgroundColor ?? "white";
12+
const color = props.color ?? "#C6CACC";
13+
14+
const height = 16;
15+
const wScaled = props.width * (height / props.height);
16+
const wEnd = 9;
17+
const wMid = 16;
18+
const wRequired = 2 * wEnd + wMid;
19+
const wBar = Math.max(0, wScaled - wRequired) / 2;
20+
const width = wRequired + 2 * wBar;
21+
22+
return (
23+
<svg
24+
height={props.height}
25+
preserveAspectRatio="none" // In case wRequired > wScaled
26+
viewBox={`0 0 ${width} ${height}`}
27+
width={props.width}
28+
xmlns="http://www.w3.org/2000/svg"
29+
>
30+
{/* Background */}
31+
<rect fill={backgroundColor} height={height} width={width} />
32+
33+
{/* Endcap */}
34+
<g fill={color}>
35+
<path d="M2 16C2.03757 10.5664 4.2842 8.95611 9 9.00001L9 7C2.08822 7.03228 0.71149 8.74186 0 16L2 16Z" />
36+
</g>
37+
38+
{/* Bar */}
39+
<g fill={color} transform={`translate(${wEnd})`}>
40+
<rect height={2} width={wBar} y={7} />
41+
</g>
42+
43+
{/* Middle */}
44+
<g fill={color} transform={`translate(${wEnd + wBar})`}>
45+
<path d="M10 0C10.0376 5.4336 11.2842 7.04389 16 6.99999L16 9C9.08822 8.96772 6.7115 7.25814 6 0L10 0Z" />
46+
<path d="M6 0C5.96243 5.4336 4.71577 7.04389 0 6.99999L0 9C6.91178 8.96772 9.2885 7.25814 10 0L6 0Z" />
47+
</g>
48+
49+
{/* Bar */}
50+
<g fill={color} transform={`translate(${wEnd + wBar + wMid})`}>
51+
<rect height={2} width={wBar} y={7} />
52+
</g>
53+
54+
{/* Endcap */}
55+
<g fill={color} transform={`translate(${wEnd + wBar + wMid + wBar})`}>
56+
<path d="M7 16C6.96243 10.5664 4.71577 8.95611 0 9.00001L0 7C6.91178 7.03228 8.28851 8.74186 9 16L7 16Z" />
57+
</g>
58+
</svg>
59+
);
60+
}

0 commit comments

Comments
 (0)