Skip to content

Commit

Permalink
fix(layout): non-fixed AppBar mini layouts
Browse files Browse the repository at this point in the history
Closes #1101
  • Loading branch information
mlaursen committed May 14, 2021
1 parent 4c6239f commit 996c597
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 52 deletions.
87 changes: 51 additions & 36 deletions packages/layout/src/LayoutChildren.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { ReactElement, ReactNode, useEffect, useState } from "react";
import React, {
HTMLAttributes,
ReactElement,
ReactNode,
useEffect,
useState,
} from "react";
import { SkipToMainContent, SkipToMainContentProps } from "@react-md/link";
import { BaseTreeItem, TreeData } from "@react-md/tree";
import { PropsWithRef } from "@react-md/utils";
Expand All @@ -8,6 +14,7 @@ import { LayoutAppBar } from "./LayoutAppBar";
import { LayoutMain, LayoutMainProps } from "./LayoutMain";
import { LayoutNavigation } from "./LayoutNavigation";
import { useLayoutConfig } from "./LayoutProvider";
import { MiniLayoutWrapper } from "./MiniLayoutWrapper";
import { LayoutNavigationItem } from "./types";
import { isMiniLayout } from "./utils";

Expand Down Expand Up @@ -60,6 +67,23 @@ export interface LayoutChildrenProps<
*/
miniNavItems?: TreeData<T>;

/**
* This prop allows you to provide additional props to the `<div>` surrounding
* the `LayoutMain` and mini `LayoutNavigation` components.
*
* Note: This additional `<div>` will only be rendered if:
* - the current layout is one of the `mini` types
* - the layout is not using a fixed app bar
* - the `miniNav` prop has not been defined
* - `treeProps` have been provided
*
* @remarks \@since 2.8.3
*/
miniWrapperProps?: PropsWithRef<
HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>;

/**
* The children to display within the layout. This is pretty much required
* since you'll have an empty app otherwise, but it's left as optional just
Expand Down Expand Up @@ -88,8 +112,9 @@ export function LayoutChildren({
navToggleProps,
navAfterAppBar = false,
nav: propNav,
miniNav: propMiniNav,
miniNav,
miniNavItems,
miniWrapperProps,
navHeader,
navHeaderProps,
navHeaderTitle,
Expand Down Expand Up @@ -152,48 +177,38 @@ export function LayoutChildren({
);
}

let miniNav = propMiniNav;
if (mini && treeProps && typeof miniNav === "undefined") {
let miniTreeProps = treeProps;
if (miniNavItems) {
miniTreeProps = {
...miniTreeProps,
navItems: miniNavItems,
};
}

miniNav = (
<LayoutNavigation
header={navHeader}
headerProps={navHeaderProps}
headerTitle={navHeaderTitle}
headerTitleProps={navHeaderTitleProps}
closeNav={closeNav}
closeNavProps={closeNavProps}
treeProps={miniTreeProps}
{...navProps}
mini
hidden={miniHidden}
/>
);
}

return (
<>
<SkipToMainContent {...skipProps} mainId={mainId} />
{navAfterAppBar && appBar}
{nav}
{!navAfterAppBar && appBar}
{/* mini nav should always be in tab index after app bar */}
{miniNav}
<LayoutMain
headerOffset={fixedAppBar}
{...mainProps}
id={mainId}
<MiniLayoutWrapper
mini={mini}
miniNav={miniNav}
miniHidden={miniHidden}
fixedAppBar={fixedAppBar}
containerProps={miniWrapperProps}
miniNavItems={miniNavItems}
treeProps={treeProps}
header={navHeader}
headerProps={navHeaderProps}
headerTitle={navHeaderTitle}
headerTitleProps={navHeaderTitleProps}
closeNav={closeNav}
closeNavProps={closeNavProps}
>
{children}
</LayoutMain>
<LayoutMain
headerOffset={fixedAppBar}
mini={mini}
miniHidden={miniHidden}
fixedAppBar={fixedAppBar}
{...mainProps}
id={mainId}
>
{children}
</LayoutMain>
</MiniLayoutWrapper>
</>
);
}
27 changes: 25 additions & 2 deletions packages/layout/src/LayoutMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ export interface LayoutMainProps extends HTMLAttributes<HTMLDivElement> {
*/
mini?: boolean;

/**
* Boolean if the mini layout is currently hidden to help determine if
* specific mini styles should be applied when the {@link fixedAppBar} prop is
* `false`.
*
* @internal
* @remarks \@since 2.8.3
*/
miniHidden?: boolean;

/**
* Boolean if the `AppBar` used by the layout is currently fixed to the top of
* the page.
*
* @internal
* @remarks \@since 2.8.3
*/
fixedAppBar?: boolean;

/**
* The transition timeout to use for the toggleable `LayoutNavigation` either
* comes into view or expands from mini to full-width. The transition can be
Expand Down Expand Up @@ -69,6 +88,8 @@ export const LayoutMain = forwardRef<HTMLDivElement, LayoutMainProps>(
timeout: propTimeout = DEFAULT_SHEET_TIMEOUT,
classNames = DEFAULT_LAYOUT_MAIN_CLASSNAMES,
mini = false,
miniHidden = false,
fixedAppBar = true,
...props
},
forwardedRef
Expand Down Expand Up @@ -120,15 +141,17 @@ export const LayoutMain = forwardRef<HTMLDivElement, LayoutMainProps>(
},
});

const isMini = mini && (fixedAppBar || miniHidden);

return (
<Component
{...props}
ref={ref}
tabIndex={tabIndex}
className={cn(
styles({
mini: mini && (isTemporaryLayout(layout) || !visible),
"nav-offset": mini,
mini: isMini && (isTemporaryLayout(layout) || !visible),
"nav-offset": isMini,
"header-offset": headerOffset,
}),
className
Expand Down
27 changes: 25 additions & 2 deletions packages/layout/src/LayoutNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { forwardRef, ReactNode } from "react";
import cn from "classnames";
import { Sheet, SheetProps } from "@react-md/sheet";
import { BaseTreeItem } from "@react-md/tree";
import { BaseTreeItem, TreeItemRenderer } from "@react-md/tree";
import { bem, PropsWithRef } from "@react-md/utils";

import { LayoutCloseNavigationButtonProps } from "./LayoutCloseNavigationButton";
Expand All @@ -17,6 +17,7 @@ import {
isTemporaryLayout,
isToggleableLayout,
} from "./utils";
import { defaultMiniNavigationItemRenderer } from "./defaultMiniNavigationItemRenderer";

export type LayoutNavigationSheetProps = Omit<
SheetProps,
Expand Down Expand Up @@ -94,6 +95,18 @@ export interface LayoutNavigationProps<
* @remarks \@since 2.7.0
*/
mini?: boolean;

/**
* Boolean if the mini navigation should be treated as a "sticky" element.
* This should really only be `true` if disabling the fixed `AppBar` behavior
* in the `Layout`.
*
* @remarks \@since 2.8.3
*/
sticky?: boolean;

/** @remarks \@since 2.8.3 */
miniNavItemRenderer?: TreeItemRenderer<T>;
}

const styles = bem("rmd-layout-navigation");
Expand All @@ -120,6 +133,8 @@ export const LayoutNavigation = forwardRef<
closeNav,
closeNavProps,
treeProps,
sticky = false,
miniNavItemRenderer = defaultMiniNavigationItemRenderer,
...props
},
ref
Expand Down Expand Up @@ -172,14 +187,22 @@ export const LayoutNavigation = forwardRef<
className={cn(
styles({
mini,
sticky,
floating,
"header-offset": layout === "clipped" || floating,
}),
className
)}
>
{header}
{treeProps && <LayoutTree {...treeProps} mini={mini} />}
{treeProps && (
<LayoutTree
miniItemRenderer={miniNavItemRenderer}
sticky={mini && sticky}
{...treeProps}
mini={mini}
/>
)}
{children}
</Sheet>
);
Expand Down
39 changes: 30 additions & 9 deletions packages/layout/src/LayoutTree.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import React, { CSSProperties, forwardRef, useEffect, useRef } from "react";
import cn from "classnames";
import { BaseTreeItem, Tree, TreeData, TreeProps } from "@react-md/tree";
import {
BaseTreeItem,
Tree,
TreeData,
TreeItemRenderer,
TreeProps,
} from "@react-md/tree";
import { bem } from "@react-md/utils";

import { defaultMiniNavigationItemRenderer } from "./defaultMiniNavigationItemRenderer";
Expand All @@ -11,9 +17,8 @@ import { isTemporaryLayout } from "./utils";

const styles = bem("rmd-layout-nav");

export type BaseLayoutTreeProps<
T extends BaseTreeItem = LayoutNavigationItem
> = Omit<TreeProps<T>, "id" | "data" | "aria-label" | "aria-labelledby">;
export type BaseLayoutTreeProps<T extends BaseTreeItem = LayoutNavigationItem> =
Omit<TreeProps<T>, "id" | "data" | "aria-label" | "aria-labelledby">;

export interface LayoutTreeProps<T extends BaseTreeItem = LayoutNavigationItem>
extends BaseLayoutTreeProps<T> {
Expand Down Expand Up @@ -47,6 +52,22 @@ export interface LayoutTreeProps<T extends BaseTreeItem = LayoutNavigationItem>
*/
mini?: boolean;

/**
* Boolean if the mini navigation should be treated as a "sticky" element.
* This should really only be `true` if disabling the fixed `AppBar` behavior
* in the `Layout`.
*
* @remarks \@since 2.8.3
*/
sticky?: boolean;

/**
* The {@link TreeItemRenderer} to use if the `mini` prop is enabled.
*
* @remarks \@since 2.8.3
*/
miniItemRenderer?: TreeItemRenderer<T>;

/**
* Optional style to provide to the `<nav>` element surrounding the tree
*/
Expand Down Expand Up @@ -84,14 +105,14 @@ export const LayoutTree = forwardRef<HTMLUListElement, LayoutTreeProps>(
"aria-label": ariaLabel = ariaLabelledBy ? undefined : "Navigation",
className,
mini = false,
sticky = false,
navStyle,
navClassName,
navItems,
labelKey = "children",
valueKey = "children",
itemRenderer = mini
? defaultMiniNavigationItemRenderer
: defaultNavigationItemRenderer,
itemRenderer = defaultNavigationItemRenderer,
miniItemRenderer = defaultMiniNavigationItemRenderer,
selectedIds,
disableTemporaryAutoclose = false,
...props
Expand Down Expand Up @@ -127,7 +148,7 @@ export const LayoutTree = forwardRef<HTMLUListElement, LayoutTreeProps>(
<nav
id={`${id}-nav`}
style={navStyle}
className={cn(styles({ mini }), navClassName)}
className={cn(styles({ sticky, grow: !sticky }), navClassName)}
>
<Tree
{...props}
Expand All @@ -139,7 +160,7 @@ export const LayoutTree = forwardRef<HTMLUListElement, LayoutTreeProps>(
labelKey={labelKey}
valueKey={valueKey}
selectedIds={selectedIds}
itemRenderer={itemRenderer}
itemRenderer={mini ? miniItemRenderer : itemRenderer}
className={cn("rmd-layout-tree", className)}
/>
</nav>
Expand Down
Loading

0 comments on commit 996c597

Please sign in to comment.