Skip to content

Commit

Permalink
Add animation to Submenu Tray when navigating between levels (#5984)
Browse files Browse the repository at this point in the history
  • Loading branch information
reidbarber authored Apr 6, 2024
1 parent 24d931b commit e6923d9
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 11 deletions.
7 changes: 7 additions & 0 deletions packages/@adobe/spectrum-css-temp/components/menu/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -261,18 +261,25 @@ governing permissions and limitations under the License.
display: flex;
flex-direction: column;
overflow: auto;

&.spectrum-Submenu-wrapper--isMobile {
overflow-x: hidden; /* Prevents scrollbar from appearing during submenu transition */
}
}

&.spectrum-Menu-wrapper--isMobile {
width: 100%;
border: none;
background-color: unset;
opacity: 1;
transition: opacity var(--spectrum-global-animation-duration-600) var(--spectrum-global-animation-ease-out);

&.is-expanded {
position: absolute;
left: 100%;
height: 0;
border: 0;
opacity: 0;
}

.spectrum-Submenu-headingWrapper {
Expand Down
56 changes: 56 additions & 0 deletions packages/@adobe/spectrum-css-temp/components/menu/skin.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,62 @@ governing permissions and limitations under the License.
color: var(--spectrum-heading-subtitle3-text-color);
}

@keyframes slideInFromRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}

@keyframes slideOutToRight {
from {
transform: translateX(0)
}
to {
transform: translateX(100%);
}
}

@keyframes slideInFromLeft {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}

@keyframes slideOutToLeft {
from {
transform: translateX(0)
}
to {
transform: translateX(-100%);
}
}

.spectrum-TraySubmenu-enter {
animation-name: slideInFromRight;
animation-duration: var(--spectrum-global-animation-duration-400);
animation-fill-mode: forwards;
animation-timing-function: var(--spectrum-global-animation-ease-out);
&:dir(rtl) {
animation-name: slideInFromLeft;
}
}

.spectrum-TraySubmenu-exit {
animation-name: slideOutToRight;
animation-duration: var(--spectrum-global-animation-duration-400);
animation-fill-mode: forwards;
animation-timing-function: var(--spectrum-global-animation-ease-in);
&:dir(rtl) {
animation-name: slideOutToLeft;
}
}

@media (forced-colors: active) {
.spectrum-Menu-divider {
background-color: CanvasText;
Expand Down
22 changes: 16 additions & 6 deletions packages/@react-spectrum/menu/src/ContextualHelpTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {getInteractionModality} from '@react-aria/interactions';
import helpStyles from '@adobe/spectrum-css-temp/components/contextualhelp/vars.css';
import {ItemProps, Key} from '@react-types/shared';
import {Popover} from '@react-spectrum/overlays';
import React, {JSX, ReactElement, useRef, useState} from 'react';
import React, {JSX, ReactElement, useEffect, useRef, useState} from 'react';
import ReactDOM from 'react-dom';
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
import {SubmenuTriggerContext, useMenuStateContext} from './context';
Expand Down Expand Up @@ -55,6 +55,12 @@ function ContextualHelpTrigger(props: InternalMenuDialogTriggerProps): ReactElem
isDisabled: !isUnavailable
}, submenuTriggerState, triggerRef);
let isMobile = useIsMobileDevice();
let [traySubmenuAnimation, setTraySubmenuAnimation] = useState('');
useEffect(() => {
if (submenuTriggerState.isOpen) {
setTraySubmenuAnimation('spectrum-TraySubmenu-enter');
}
}, [submenuTriggerState.isOpen]);
let slots = {};
if (isUnavailable) {
slots = {
Expand All @@ -68,7 +74,8 @@ function ContextualHelpTrigger(props: InternalMenuDialogTriggerProps): ReactElem
classNames(
styles,
{
'spectrum-Menu-subdialog': !isMobile
'spectrum-Menu-subdialog': !isMobile,
[traySubmenuAnimation]: isMobile
}
)
)
Expand All @@ -91,10 +98,13 @@ function ContextualHelpTrigger(props: InternalMenuDialogTriggerProps): ReactElem
let overlay;
let tray;
let onBackButtonPress = () => {
submenuTriggerState.close();
if (parentMenuRef.current && !parentMenuRef.current.contains(document.activeElement)) {
parentMenuRef.current.focus();
}
setTraySubmenuAnimation('spectrum-TraySubmenu-exit');
setTimeout(() => {
submenuTriggerState.close();
if (parentMenuRef.current && !parentMenuRef.current.contains(document.activeElement)) {
parentMenuRef.current.focus();
}
}, 220); // Matches transition duration
};
let [offset, setOffset] = useState(0);
useLayoutEffect(() => {
Expand Down
33 changes: 28 additions & 5 deletions packages/@react-spectrum/menu/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import intlMessages from '../intl/*.json';
import {MenuContext, MenuStateContext, useMenuStateContext} from './context';
import {MenuItem} from './MenuItem';
import {MenuSection} from './MenuSection';
import {mergeProps, useSlotId, useSyncRef} from '@react-aria/utils';
import {mergeProps, useLayoutEffect, useSlotId, useSyncRef} from '@react-aria/utils';
import React, {ReactElement, useContext, useEffect, useRef, useState} from 'react';
import {SpectrumMenuProps} from '@react-types/menu';
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
Expand Down Expand Up @@ -70,7 +70,6 @@ function Menu<T extends object>(props: SpectrumMenuProps<T>, ref: DOMRef<HTMLDiv
isDisabled: isSubmenu || !hasOpenSubmenu
});

// TODO: add slide transition
return (
<MenuStateContext.Provider value={{popoverContainer, trayContainerRef, menu: domRef, submenu: submenuRef, rootMenuTriggerState, state}}>
<div style={{height: hasOpenSubmenu ? '100%' : undefined}} ref={trayContainerRef} />
Expand Down Expand Up @@ -136,6 +135,29 @@ export function TrayHeaderWrapper(props) {
let isMobile = useIsMobileDevice();
let {direction} = useLocale();

let [traySubmenuAnimation, setTraySubmenuAnimation] = useState('');
useLayoutEffect(() => {
if (!hasOpenSubmenu) {
setTraySubmenuAnimation('spectrum-TraySubmenu-enter');
}
}, [hasOpenSubmenu, isMobile]);

let timeoutRef = useRef(null);
let handleBackButtonPress = () => {
setTraySubmenuAnimation('spectrum-TraySubmenu-exit');
timeoutRef.current = setTimeout(() => {
onBackButtonPress();
}, 220); // Matches transition duration
};

useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);

return (
<>
<div
Expand All @@ -149,17 +171,18 @@ export function TrayHeaderWrapper(props) {
'spectrum-Menu-wrapper',
{
'spectrum-Menu-wrapper--isMobile': isMobile,
'is-expanded': hasOpenSubmenu
'is-expanded': hasOpenSubmenu,
[traySubmenuAnimation]: isMobile
}
)
}>
<div role="presentation" className={classNames(styles, 'spectrum-Submenu-wrapper')} onKeyDown={wrapperKeyDown}>
<div role="presentation" className={classNames(styles, 'spectrum-Submenu-wrapper', {'spectrum-Submenu-wrapper--isMobile': isMobile})} onKeyDown={wrapperKeyDown}>
{isMobile && isSubmenu && !hasOpenSubmenu && (
<div className={classNames(styles, 'spectrum-Submenu-headingWrapper')}>
<ActionButton
aria-label={backButtonLabel}
isQuiet
onPress={onBackButtonPress}>
onPress={handleBackButtonPress}>
{/* We don't have a ArrowLeftSmall so make due with ArrowDownSmall and transforms */}
{direction === 'rtl' ? <ArrowDownSmall UNSAFE_style={{rotate: '270deg'}} /> : <ArrowDownSmall UNSAFE_style={{rotate: '90deg'}} />}
</ActionButton>
Expand Down

1 comment on commit e6923d9

@rspbot
Copy link

@rspbot rspbot commented on e6923d9 Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.