-
-
Notifications
You must be signed in to change notification settings - Fork 303
/
Combined.tsx
121 lines (110 loc) · 3.56 KB
/
Combined.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import React, { Fragment, FC, useEffect } from "react";
import cn from "classnames";
import { useRouter } from "next/router";
import { AppBar, AppBarTitle, AppBarAction } from "@react-md/app-bar";
import { ArrowBackSVGIcon } from "@react-md/material-icons";
import { Sheet } from "@react-md/sheet";
import {
bem,
useToggle,
useScrollLock,
useInteractionModeContext,
} from "@react-md/utils";
import TableOfContents from "components/TableOfContents";
import useAppSizeContext from "./useAppSizeContext";
import Header from "./Header";
import NavigationTree from "./NavigationTree";
export interface CombinedProps {
title: string;
}
const block = bem("layout");
const Combined: FC<CombinedProps> = ({ title: propTitle, children }) => {
const { isPhone, isTablet, isDesktop, isLandscape } = useAppSizeContext();
const isLandscapeTablet = isLandscape && isTablet;
const inline = isDesktop || isLandscapeTablet;
const [visible, , disable, toggle, setVisible] = useToggle(isDesktop);
const router = useRouter();
useEffect(() => {
if (visible !== isDesktop) {
setVisible(isDesktop);
}
// disabled since only want to update on media changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDesktop, isTablet, isPhone]);
useEffect(() => {
if (visible && !inline) {
setVisible(false);
}
// disabled since only want to run on pathname changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [router.pathname]);
useScrollLock(visible && isPhone);
let title = propTitle;
if (propTitle.includes(" - ")) {
title = title.substring(title.indexOf(" - ") + 3);
if (isPhone && !router.pathname.includes("/packages/")) {
const i = title.lastIndexOf("- ");
if (i !== -1) {
title = title.substring(i + 2);
}
}
}
// this makes it so that the SkipToMainContent button can still
// focus the `<main>` element, but the `<main>` will no longer be
// focused if the user clicks inside. This is super nice since one
// of my bigger patterns is to click somewhere then press tab to
// focus a specific element. Without this fix, the first element in
// the `<main>` tag would be focused instead of the closest focusable
// element to the click area.
let mainTabIndex: number | undefined;
if (useInteractionModeContext() === "keyboard") {
mainTabIndex = -1;
}
return (
<Fragment>
<Header
title={title}
toggle={toggle}
isPhone={isPhone}
isDesktop={isDesktop}
isSheetVisible={visible}
/>
<Sheet
id="main-navigation"
aria-label="Navigation"
visible={visible}
onRequestClose={disable}
position="left"
portal={!inline}
overlay={!inline}
className={cn(block("nav", { inline }))}
disableScrollLock
>
{isLandscapeTablet && (
<AppBar theme="clear" className={block("nav-header")}>
<AppBarTitle id="main-navigation-title" className={block("title")}>
{title}
</AppBarTitle>
<AppBarAction first onClick={toggle} aria-label="Hide Navigation">
<ArrowBackSVGIcon />
</AppBarAction>
</AppBar>
)}
<NavigationTree />
</Sheet>
<main
id="main-content"
className={cn(
block("main", {
offset: inline && visible,
})
)}
tabIndex={mainTabIndex}
>
<TableOfContents pathname={router.pathname} />
{children}
</main>
</Fragment>
);
};
export default Combined;