|
@@ -17,6 +17,8 @@ export interface ModuleRenderContext {
|
|
|
key: number;
|
|
|
slim: boolean;
|
|
|
expandingOrCollapsing: boolean;
|
|
|
+ onAccountExpand: () => void;
|
|
|
+ onSearchClick: () => void;
|
|
|
currentPath: string;
|
|
|
strings: Strings;
|
|
|
navigate(url: string): Promise<void>;
|
|
@@ -46,7 +48,6 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
// 'collapsed' is a persistent state that is controlled by the arrow icon at the top
|
|
|
// It records the user's general preference for a collapsed/uncollapsed menu
|
|
|
// This is just a hint though, and we may still collapse the menu if the screen is too small
|
|
|
- // Also, we may display the full menu temporarily in collapsed mode (see 'peeking' below)
|
|
|
const [collapsed, setCollapsed] = React.useState(collapsedOnLoad);
|
|
|
|
|
|
// Call onExpandCollapse(true) if menu is initialised in collapsed state
|
|
@@ -56,11 +57,6 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
}
|
|
|
}, []);
|
|
|
|
|
|
- // 'peeking' is a temporary state to allow the user to peek in the menu while it is collapsed, or hidden.
|
|
|
- // When peeking is true, the menu renders as if it's not collapsed, but as an overlay instead of occupying
|
|
|
- // space next to the content
|
|
|
- const [peeking, setPeeking] = React.useState(false);
|
|
|
-
|
|
|
// 'visibleOnMobile' indicates whether the sidebar is currently visible on mobile
|
|
|
// On mobile, the sidebar is completely hidden by default and must be opened manually
|
|
|
const [visibleOnMobile, setVisibleOnMobile] = React.useState(false);
|
|
@@ -80,6 +76,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
setVisibleOnMobile(false);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
handleResize();
|
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
@@ -88,7 +85,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
// Whether or not to display the menu with slim layout.
|
|
|
// Separate from 'collapsed' as the menu can still be displayed with an expanded
|
|
|
// layout while in 'collapsed' mode if the user is 'peeking' into it (see above)
|
|
|
- const slim = collapsed && !peeking && !isMobile;
|
|
|
+ const slim = collapsed && !isMobile;
|
|
|
|
|
|
// 'expandingOrCollapsing' is set to true whilst the the menu is transitioning between slim and expanded layouts
|
|
|
const [expandingOrCollapsing, setExpandingOrCollapsing] =
|
|
@@ -124,40 +121,33 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
};
|
|
|
};
|
|
|
|
|
|
- // Switch peeking on/off when the mouse cursor hovers the sidebar or focus is on the sidebar
|
|
|
- const [mouseHover, setMouseHover] = React.useState(false);
|
|
|
const [focused, setFocused] = React.useState(false);
|
|
|
|
|
|
- const onMouseEnterHandler = () => {
|
|
|
- setMouseHover(true);
|
|
|
- };
|
|
|
-
|
|
|
- const onMouseLeaveHandler = () => {
|
|
|
- setMouseHover(false);
|
|
|
+ const onBlurHandler = () => {
|
|
|
+ if (focused) {
|
|
|
+ setFocused(false);
|
|
|
+ setCollapsed(true);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const onFocusHandler = () => {
|
|
|
- setFocused(true);
|
|
|
+ if (focused) {
|
|
|
+ setCollapsed(false);
|
|
|
+ setFocused(true);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- const onBlurHandler = () => {
|
|
|
- setFocused(false);
|
|
|
+ const onSearchClick = () => {
|
|
|
+ if (slim) {
|
|
|
+ onClickCollapseToggle();
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- // We need a stop peeking timeout to stop the sidebar moving as someone tab's though the menu
|
|
|
- const stopPeekingTimeout = React.useRef<any>(null);
|
|
|
-
|
|
|
- React.useEffect(() => {
|
|
|
- if (mouseHover || focused) {
|
|
|
- clearTimeout(stopPeekingTimeout.current);
|
|
|
- setPeeking(true);
|
|
|
- } else {
|
|
|
- clearTimeout(stopPeekingTimeout.current);
|
|
|
- stopPeekingTimeout.current = setTimeout(() => {
|
|
|
- setPeeking(false);
|
|
|
- }, SIDEBAR_TRANSITION_DURATION);
|
|
|
+ const onAccountExpand = () => {
|
|
|
+ if (slim) {
|
|
|
+ onClickCollapseToggle();
|
|
|
}
|
|
|
- }, [mouseHover, focused]);
|
|
|
+ };
|
|
|
|
|
|
// Render modules
|
|
|
const renderedModules = modules.map((module, index) =>
|
|
@@ -165,6 +155,8 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
key: index,
|
|
|
slim,
|
|
|
expandingOrCollapsing,
|
|
|
+ onAccountExpand,
|
|
|
+ onSearchClick,
|
|
|
currentPath,
|
|
|
strings,
|
|
|
navigate,
|
|
@@ -181,31 +173,43 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|
|
(isMobile && !visibleOnMobile ? ' sidebar--hidden' : '')
|
|
|
}
|
|
|
>
|
|
|
- <div className="sidebar__inner">
|
|
|
- <button
|
|
|
- onClick={onClickCollapseToggle}
|
|
|
- aria-label={strings.TOGGLE_SIDEBAR}
|
|
|
- aria-expanded={slim ? 'false' : 'true'}
|
|
|
- type="button"
|
|
|
- className="button sidebar__collapse-toggle hover:w-bg-primary-200 hover:text-white hover:opacity-100"
|
|
|
+ <div
|
|
|
+ className="sidebar__inner"
|
|
|
+ onFocus={onFocusHandler}
|
|
|
+ onBlur={onBlurHandler}
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ className={`${
|
|
|
+ slim ? 'w-justify-center' : 'w-justify-end'
|
|
|
+ } w-flex w-items-center`}
|
|
|
>
|
|
|
- <Icon
|
|
|
- name="expand-right"
|
|
|
- className={`w-transition motion-reduce:w-transition-none
|
|
|
+ <button
|
|
|
+ onClick={onClickCollapseToggle}
|
|
|
+ aria-label={strings.TOGGLE_SIDEBAR}
|
|
|
+ aria-expanded={slim ? 'false' : 'true'}
|
|
|
+ type="button"
|
|
|
+ className={`
|
|
|
+ ${!slim ? 'w-mr-4' : ''}
|
|
|
+ button
|
|
|
+ sidebar__collapse-toggle
|
|
|
+ w-flex
|
|
|
+ w-justify-center
|
|
|
+ w-items-center
|
|
|
+ sm:w-mt-4
|
|
|
+ hover:w-bg-primary-200
|
|
|
+ hover:text-white
|
|
|
+ hover:opacity-100`}
|
|
|
+ >
|
|
|
+ <Icon
|
|
|
+ name="expand-right"
|
|
|
+ className={`w-transition motion-reduce:w-transition-none
|
|
|
${!collapsed ? '-w-rotate-180' : ''}
|
|
|
`}
|
|
|
- />
|
|
|
- </button>
|
|
|
-
|
|
|
- <div
|
|
|
- className="sidebar__peek-hover-area"
|
|
|
- onMouseEnter={onMouseEnterHandler}
|
|
|
- onMouseLeave={onMouseLeaveHandler}
|
|
|
- onFocus={onFocusHandler}
|
|
|
- onBlur={onBlurHandler}
|
|
|
- >
|
|
|
- {renderedModules}
|
|
|
+ />
|
|
|
+ </button>
|
|
|
</div>
|
|
|
+
|
|
|
+ {renderedModules}
|
|
|
</div>
|
|
|
</div>
|
|
|
<button
|