Skip to content

Commit

Permalink
Only render Menu on open
Browse files Browse the repository at this point in the history
  • Loading branch information
felixhabib committed Nov 26, 2024
1 parent 209d69a commit 3070296
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 131 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
// Action type IDs (allows action type names to be minified)
export const actionTypes = {
CLIENT_ENVIRONMENT: 0,
MENU_TRIGGER_UP: 1,
MENU_ITEM_UP: 2,
MENU_TRIGGER_DOWN: 3,
MENU_ITEM_DOWN: 4,
MENU_ITEM_ESCAPE: 5,
MENU_ITEM_TAB: 6,
MENU_ITEM_ENTER: 7,
MENU_ITEM_SPACE: 8,
MENU_ITEM_CLICK: 9,
MENU_ITEM_HOVER: 10,
MENU_TRIGGER_ENTER: 11,
MENU_TRIGGER_SPACE: 12,
MENU_TRIGGER_CLICK: 13,
MENU_TRIGGER_TAB: 14,
MENU_TRIGGER_ESCAPE: 15,
BACKDROP_CLICK: 16,
WINDOW_RESIZE: 17,
MENU_TRIGGER_UP: 0,
MENU_ITEM_UP: 1,
MENU_TRIGGER_DOWN: 2,
MENU_ITEM_DOWN: 3,
MENU_ITEM_ESCAPE: 4,
MENU_ITEM_TAB: 5,
MENU_ITEM_ENTER: 6,
MENU_ITEM_SPACE: 7,
MENU_ITEM_CLICK: 8,
MENU_ITEM_HOVER: 9,
MENU_TRIGGER_ENTER: 10,
MENU_TRIGGER_SPACE: 11,
MENU_TRIGGER_CLICK: 12,
MENU_TRIGGER_TAB: 13,
MENU_TRIGGER_ESCAPE: 14,
BACKDROP_CLICK: 15,
WINDOW_RESIZE: 16,
MENU_CLOSE: 18,
} as const;

export type Action =
| { type: typeof actionTypes.CLIENT_ENVIRONMENT }
| { type: typeof actionTypes.MENU_TRIGGER_UP }
| { type: typeof actionTypes.MENU_ITEM_UP }
| { type: typeof actionTypes.MENU_TRIGGER_DOWN }
Expand Down Expand Up @@ -53,4 +52,5 @@ export type Action =
| { type: typeof actionTypes.MENU_TRIGGER_TAB }
| { type: typeof actionTypes.MENU_TRIGGER_ESCAPE }
| { type: typeof actionTypes.BACKDROP_CLICK }
| { type: typeof actionTypes.WINDOW_RESIZE };
| { type: typeof actionTypes.WINDOW_RESIZE }
| { type: typeof actionTypes.MENU_CLOSE };
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const menuPosition = style({
right: triggerVars.right,
});

export const menuIsClosed = style({
export const entrance = style({
transform: `translateY(${calc(vars.grid).negate().multiply(2)})`,
visibility: 'hidden',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React, {
useRef,
useReducer,
useEffect,
useState,
} from 'react';
import flattenChildren from '../../utils/flattenChildren';
import { Box } from '../Box/Box';
Expand Down Expand Up @@ -61,7 +62,6 @@ export interface MenuRendererProps {
}

const {
CLIENT_ENVIRONMENT,
MENU_TRIGGER_UP,
MENU_ITEM_UP,
MENU_TRIGGER_DOWN,
Expand Down Expand Up @@ -100,7 +100,6 @@ const getPosition = (element: HTMLElement | null): position | undefined => {
};

interface State {
onClient: boolean;
open: boolean;
highlightIndex: number;
closeReason: CloseReason;
Expand All @@ -110,7 +109,6 @@ interface State {
const CLOSED_INDEX = -1;
const CLOSE_REASON_EXIT: CloseReasonExit = { reason: 'exit' };
const initialState: State = {
onClient: false,
open: false,
highlightIndex: CLOSED_INDEX,
closeReason: CLOSE_REASON_EXIT,
Expand Down Expand Up @@ -146,109 +144,94 @@ export const MenuRenderer = ({
'All child nodes within a menu component must be a MenuItem, MenuItemLink, MenuItemCheckbox or MenuItemDivider: https://seek-oss.github.io/braid-design-system/components/MenuRenderer',
);

const [
{
onClient: hasRenderedOnClient,
open,
highlightIndex,
closeReason,
triggerPosition,
},
dispatch,
] = useReducer((state: State, action: Action): State => {
switch (action.type) {
case CLIENT_ENVIRONMENT: {
return { ...state, onClient: true };
}
case MENU_TRIGGER_UP:
case MENU_ITEM_UP: {
return {
...state,
open: true,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: getNextIndex(-1, state.highlightIndex, itemCount),
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case MENU_TRIGGER_DOWN:
case MENU_ITEM_DOWN: {
return {
...state,
open: true,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: getNextIndex(1, state.highlightIndex, itemCount),
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case BACKDROP_CLICK:
case MENU_TRIGGER_ESCAPE:
case MENU_TRIGGER_TAB:
case MENU_ITEM_ESCAPE:
case MENU_ITEM_TAB: {
return {
...state,
open: false,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: CLOSED_INDEX,
};
}
case MENU_ITEM_ENTER:
case MENU_ITEM_SPACE:
case MENU_ITEM_CLICK: {
// Don't close the menu if the user clicked a "form element" item, e.g. checkbox
if ('formElement' in action && action.formElement) {
return state;
const [{ open, highlightIndex, closeReason, triggerPosition }, dispatch] =
useReducer((state: State, action: Action): State => {
switch (action.type) {
case MENU_TRIGGER_UP:
case MENU_ITEM_UP: {
return {
...state,
open: true,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: getNextIndex(-1, state.highlightIndex, itemCount),
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}

return {
...state,
open: false,
closeReason: {
reason: 'selection',
index: action.index,
id: action.id,
},
highlightIndex: CLOSED_INDEX,
};
}
case MENU_ITEM_HOVER: {
return { ...state, highlightIndex: action.value };
}
case MENU_TRIGGER_ENTER:
case MENU_TRIGGER_SPACE: {
const nextOpen = !state.open;
return {
...state,
open: nextOpen,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: nextOpen ? 0 : CLOSED_INDEX,
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case MENU_TRIGGER_CLICK: {
const nextOpen = !state.open;

return {
...state,
open: nextOpen,
closeReason: CLOSE_REASON_EXIT,
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case WINDOW_RESIZE: {
return {
...state,
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
case MENU_TRIGGER_DOWN:
case MENU_ITEM_DOWN: {
return {
...state,
open: true,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: getNextIndex(1, state.highlightIndex, itemCount),
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case BACKDROP_CLICK:
case MENU_TRIGGER_ESCAPE:
case MENU_TRIGGER_TAB:
case MENU_ITEM_ESCAPE:
case MENU_ITEM_TAB: {
return {
...state,
open: false,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: CLOSED_INDEX,
};
}
case MENU_ITEM_ENTER:
case MENU_ITEM_SPACE:
case MENU_ITEM_CLICK: {
// Don't close the menu if the user clicked a "form element" item, e.g. checkbox
if ('formElement' in action && action.formElement) {
return state;
}

return {
...state,
open: false,
closeReason: {
reason: 'selection',
index: action.index,
id: action.id,
},
highlightIndex: CLOSED_INDEX,
};
}
case MENU_ITEM_HOVER: {
return { ...state, highlightIndex: action.value };
}
case MENU_TRIGGER_ENTER:
case MENU_TRIGGER_SPACE: {
const nextOpen = !state.open;
return {
...state,
open: nextOpen,
closeReason: CLOSE_REASON_EXIT,
highlightIndex: nextOpen ? 0 : CLOSED_INDEX,
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case MENU_TRIGGER_CLICK: {
const nextOpen = !state.open;

return {
...state,
open: nextOpen,
closeReason: CLOSE_REASON_EXIT,
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
case WINDOW_RESIZE: {
return {
...state,
triggerPosition: buttonRef && getPosition(buttonRef.current),
};
}
default:
return state;
}
default:
return state;
}
}, initialState);

useEffect(() => {
dispatch({ type: CLIENT_ENVIRONMENT });
}, []);
}, initialState);

useEffect(() => {
if (lastOpen.current === open) {
Expand Down Expand Up @@ -354,10 +337,9 @@ export const MenuRenderer = ({
<Box position="relative">
{trigger(triggerProps, { open })}

{hasRenderedOnClient && (
{open && (
<BraidPortal>
<Menu
open={open}
align={align}
width={width}
placement={placement}
Expand Down Expand Up @@ -409,18 +391,18 @@ interface MenuProps {
dispatch: (action: Action) => void;
focusTrigger: () => void;
highlightIndex: number;
open: boolean;
children: ReactNode[];
triggerPosition?: position;
}

const RENDER_DURATION = 1;

export function Menu({
offsetSpace,
align,
width,
placement,
children,
open,
dispatch,
focusTrigger,
highlightIndex,
Expand All @@ -429,6 +411,20 @@ export function Menu({
}: MenuProps) {
let dividerCount = 0;

const [opening, setOpening] = useState(true);

useEffect(() => {
if (opening) {
const timer = setTimeout(() => {
setOpening(false);
}, RENDER_DURATION);

return () => {
clearTimeout(timer);
};
}
}, [opening]);

const inlineVars =
triggerPosition &&
assignInlineVars({
Expand All @@ -449,11 +445,11 @@ export function Menu({
marginTop={placement === 'bottom' ? offsetSpace : undefined}
marginBottom={placement === 'top' ? offsetSpace : undefined}
transition="fast"
opacity={!open ? 0 : undefined}
opacity={opening ? 0 : undefined}
overflow="hidden"
className={[
styles.menuPosition,
!open && styles.menuIsClosed,
opening && styles.entrance,
width !== 'content' && styles.width[width],
]}
>
Expand Down

0 comments on commit 3070296

Please sign in to comment.