Skip to content

Commit 6e4a06d

Browse files
committed
feat(layout): added prop to control toggleable layouts default visibility
This adds the new prop `defaultToggleableVisible` and some more documentation around controlling the visibility of the layout. Closes #1066
1 parent 7fa6b0c commit 6e4a06d

File tree

9 files changed

+333
-12
lines changed

9 files changed

+333
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Since there might be times where it is useful to update the temporary and
2+
toggleable layouts' visibility, this package also exports a `useLayoutConfig`
3+
hook to help out that returns the current configuration and controls.
4+
5+
The example below will give a quick example using this hook to control the
6+
visibility of the navigation panel for non-persistent layouts. This example will
7+
also show how to make toggleable layouts default to being visible with a new
8+
`defaultToggleableVisible` prop introduced in `react-md@2.6.0`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.container {
2+
margin: 1rem;
3+
}
4+
5+
.center {
6+
margin: 0 auto;
7+
}
8+
9+
.select {
10+
min-width: 15rem;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React, { ReactElement, useState } from "react";
2+
import cn from "classnames";
3+
import { Checkbox, Select } from "@react-md/form";
4+
import { Layout, SupportedWideLayout } from "@react-md/layout";
5+
import { Grid } from "@react-md/utils";
6+
7+
import styles from "./ControllingTheLayout.module.scss";
8+
import LayoutVisibility from "./LayoutVisibility";
9+
import CloseButton from "./CloseButton";
10+
11+
const options: SupportedWideLayout[] = [
12+
"temporary",
13+
// 'temporary-mini', // not supported yet
14+
"toggleable",
15+
// 'toggleable-mini', // not supported yet
16+
"clipped",
17+
"floating",
18+
"full-height",
19+
];
20+
21+
export default function ControllingTheLayout(): ReactElement {
22+
const [defaultVisible, setDefaultVisible] = useState(false);
23+
const [desktopLayout, setDesktopLayout] = useState<SupportedWideLayout>(
24+
"full-height"
25+
);
26+
27+
return (
28+
<Layout
29+
id="custom-layout"
30+
title="Toggleable Layout"
31+
navHeaderTitle="Another Title"
32+
tabletLayout="toggleable"
33+
landscapeTabletLayout="toggleable"
34+
desktopLayout={desktopLayout}
35+
largeDesktopLayout={desktopLayout}
36+
defaultToggleableVisible={defaultVisible}
37+
// this is only required since I already have a main element due to the
38+
// documentation site's Layout component
39+
mainProps={{ component: "div" }}
40+
navProps={{
41+
// added a button since there **has** to be something focusable in the
42+
// nav when the temporary layout is chosen
43+
children: <CloseButton />,
44+
}}
45+
>
46+
<Grid columns={1} className={styles.container}>
47+
<Checkbox
48+
id="visibility"
49+
label="Toggleable default visible?"
50+
checked={defaultVisible}
51+
onChange={(event) => setDefaultVisible(event.currentTarget.checked)}
52+
className={styles.center}
53+
/>
54+
<Select
55+
id="desktop-layout"
56+
label="Desktop Layout"
57+
value={desktopLayout}
58+
options={options}
59+
onChange={(nextValue) => {
60+
if (options.includes(nextValue as SupportedWideLayout)) {
61+
setDesktopLayout(nextValue as SupportedWideLayout);
62+
}
63+
}}
64+
className={cn(styles.center, styles.select)}
65+
/>
66+
<LayoutVisibility />
67+
</Grid>
68+
</Layout>
69+
);
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { ReactElement } from "react";
2+
import { Button } from "@react-md/button";
3+
import { isPersistentLayout, useLayoutConfig } from "@react-md/layout";
4+
5+
import CodeBlock from "components/Code/CodeBlock";
6+
import Blockquote from "components/Blockquote";
7+
8+
export default function LayoutVisibility(): ReactElement {
9+
const { showNav, hideNav, ...remaining } = useLayoutConfig();
10+
const code = `const config = ${JSON.stringify(
11+
{
12+
showNav: "function",
13+
hideNav: "function",
14+
...remaining,
15+
},
16+
null,
17+
2
18+
)}`;
19+
20+
return (
21+
<div>
22+
<CodeBlock language="typescript">{code}</CodeBlock>
23+
{isPersistentLayout(remaining.layout) && (
24+
<Blockquote>
25+
The visibility cannot be changed for persistent layouts so the buttons
26+
will do nothing.
27+
</Blockquote>
28+
)}
29+
<Button onClick={showNav}>Show</Button>
30+
<Button onClick={hideNav} theme="secondary">
31+
Hide
32+
</Button>
33+
</div>
34+
);
35+
}

packages/documentation/src/components/Demos/Layout/index.tsx

+20-7
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,31 @@ import README from "./README.md";
66
import ConfigurableLayout from "./ConfigurableLayout";
77
import configurableLayout from "./ConfigurableLayout.md";
88

9+
import ControllingTheLayout from "./ControllingTheLayout";
10+
import controllingTheLayout from "./ControllingTheLayout.md";
11+
12+
const modalProps = {
13+
fullPage: true,
14+
fullPageFAB: true,
15+
fullPageProps: {
16+
defaultFocus: "button",
17+
disableAppBar: true,
18+
disableContent: true,
19+
},
20+
};
21+
922
const demos = [
1023
{
24+
...modalProps,
1125
name: "Configurable Layout",
1226
description: configurableLayout,
1327
children: <ConfigurableLayout />,
14-
fullPage: true,
15-
fullPageFAB: true,
16-
fullPageProps: {
17-
defaultFocus: "button",
18-
disableAppBar: true,
19-
disableContent: true,
20-
},
28+
},
29+
{
30+
...modalProps,
31+
name: "Controlling the Layout",
32+
description: controllingTheLayout,
33+
children: <ControllingTheLayout />,
2134
},
2235
];
2336

packages/layout/src/Layout.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export function Layout({
267267
landscapeTabletLayout = DEFAULT_LANDSCAPE_TABLET_LAYOUT,
268268
desktopLayout = DEFAULT_DESKTOP_LAYOUT,
269269
largeDesktopLayout,
270+
defaultToggleableVisible = false,
270271
customTitle,
271272
title,
272273
titleProps,
@@ -323,6 +324,7 @@ export function Layout({
323324
landscapeTabletLayout={landscapeTabletLayout}
324325
desktopLayout={desktopLayout}
325326
largeDesktopLayout={largeDesktopLayout}
327+
defaultToggleableVisible={defaultToggleableVisible}
326328
>
327329
<SkipToMainContent {...skipProps} mainId={mainId} />
328330
{navAfterAppBar && appBar}

packages/layout/src/LayoutProvider.tsx

+24-4
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import {
1717
DEFAULT_TABLET_LAYOUT,
1818
} from "./constants";
1919
import { LayoutConfiguration, SupportedWideLayout } from "./types";
20-
import { getLayoutType, isPersistentLayout } from "./utils";
20+
import { getLayoutType, isPersistentLayout, isToggleableLayout } from "./utils";
2121

2222
/**
2323
* @private
2424
*/
2525
const notInitialized = (name: string) => (): void => {
26+
/* istanbul ignore next */
2627
if (process.env.NODE_ENV !== "production") {
2728
/* eslint-disable no-console */
2829
console.warn(
@@ -91,6 +92,19 @@ export interface LayoutProviderProps extends LayoutConfiguration {
9192
children: ReactNode;
9293
}
9394

95+
/**
96+
* @since 2.6.0
97+
* @private
98+
*/
99+
function isToggleableVisible(
100+
behavior: boolean | "toggleable" | "toggleable-mini",
101+
layout: SupportedWideLayout
102+
): boolean {
103+
return typeof behavior === "string"
104+
? behavior === layout
105+
: behavior && isToggleableLayout(layout);
106+
}
107+
94108
/**
95109
* Determines the current layout based on the `LayoutConfiguration` and hooks
96110
* into the `AppSizeListener` to update on resize. This also initializes the
@@ -104,6 +118,7 @@ export function LayoutProvider({
104118
landscapeTabletLayout = DEFAULT_LANDSCAPE_TABLET_LAYOUT,
105119
desktopLayout = DEFAULT_DESKTOP_LAYOUT,
106120
largeDesktopLayout,
121+
defaultToggleableVisible = false,
107122
children,
108123
}: LayoutProviderProps): ReactElement {
109124
const appSize = useAppSize();
@@ -119,12 +134,17 @@ export function LayoutProvider({
119134
const isPersistent = isPersistentLayout(layout);
120135

121136
const { isDesktop } = appSize;
122-
const [visible, setVisible] = useState(isPersistent && isDesktop);
137+
const [visible, setVisible] = useState(
138+
(isPersistent && isDesktop) ||
139+
isToggleableVisible(defaultToggleableVisible, layout)
140+
);
123141
const prevLayout = useRef(layout);
124142
if (prevLayout.current !== layout) {
125143
prevLayout.current = layout;
126-
if (visible !== isPersistent) {
127-
setVisible(isPersistent);
144+
const nextVisible =
145+
isPersistent || isToggleableVisible(defaultToggleableVisible, layout);
146+
if (visible !== nextVisible) {
147+
setVisible(nextVisible);
128148
}
129149
}
130150

0 commit comments

Comments
 (0)