Skip to content

Commit

Permalink
Trigger sidebar animations only on cross-route navigations
Browse files Browse the repository at this point in the history
  • Loading branch information
jsnajdr committed May 6, 2024
1 parent ccf9d33 commit bd386d7
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function SidebarNavigationItem( {
...props
} ) {
const history = useHistory();
const navigate = useContext( SidebarNavigationContext );
const { navigate } = useContext( SidebarNavigationContext );

// If there is no custom click handler, create one that navigates to `path`.
function handleClick( e ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default function SidebarNavigationScreen( {
);
const location = useLocation();
const history = useHistory();
const navigate = useContext( SidebarNavigationContext );
const { navigate } = useContext( SidebarNavigationContext );
const icon = isRTL() ? chevronRight : chevronLeft;
return (
<>
Expand Down
90 changes: 57 additions & 33 deletions packages/edit-site/src/components/sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,86 @@ import clsx from 'clsx';
* WordPress dependencies
*/
import {
useCallback,
createContext,
useContext,
useState,
useRef,
useEffect,
} from '@wordpress/element';
import { focus } from '@wordpress/dom';

export const SidebarNavigationContext = createContext( () => {} );
// Focus a sidebar element after a navigation. The element to focus is either
// specified by `focusSelector` (when navigating back) or it is the first
// tabbable element (usually the "Back" button).
function focusSidebarElement( el, direction, focusSelector ) {
let elementToFocus;
if ( direction === 'back' && focusSelector ) {
elementToFocus = el.querySelector( focusSelector );
}
if ( direction !== null && ! elementToFocus ) {
const [ firstTabbable ] = focus.tabbable.find( el );
elementToFocus = firstTabbable ?? el;
}
elementToFocus?.focus();
}

export default function SidebarContent( { routeKey, children } ) {
const [ navState, setNavState ] = useState( {
// Navigation state that is updated when navigating back or forward. Helps us
// manage the animations and also focus.
function createNavState() {
let state = {
direction: null,
focusSelector: null,
} );
};

const navigate = useCallback( ( direction, focusSelector = null ) => {
setNavState( ( prevState ) => ( {
direction,
focusSelector:
direction === 'forward' && focusSelector
? focusSelector
: prevState.focusSelector,
} ) );
}, [] );
return {
get() {
return state;
},
navigate( direction, focusSelector = null ) {
state = {
direction,
focusSelector:
direction === 'forward' && focusSelector
? focusSelector
: state.focusSelector,
};
},
};
}

function SidebarContentWrapper( { children } ) {
const navState = useContext( SidebarNavigationContext );
const wrapperRef = useRef();
const [ navAnimation, setNavAnimation ] = useState( null );

useEffect( () => {
let elementToFocus;
if ( navState.direction === 'back' && navState.focusSelector ) {
elementToFocus = wrapperRef.current.querySelector(
navState.focusSelector
);
}
if ( navState.direction !== null && ! elementToFocus ) {
const [ firstTabbable ] = focus.tabbable.find( wrapperRef.current );
elementToFocus = firstTabbable ?? wrapperRef.current;
}
elementToFocus?.focus();
const { direction, focusSelector } = navState.get();
focusSidebarElement( wrapperRef.current, direction, focusSelector );
setNavAnimation( direction );
}, [ navState ] );

const wrapperCls = clsx( 'edit-site-sidebar__screen-wrapper', {
'slide-from-left': navState.direction === 'back',
'slide-from-right': navState.direction === 'forward',
'slide-from-left': navAnimation === 'back',
'slide-from-right': navAnimation === 'forward',
} );

return (
<SidebarNavigationContext.Provider value={ navigate }>
<div ref={ wrapperRef } className={ wrapperCls }>
{ children }
</div>
);
}

export default function SidebarContent( { routeKey, children } ) {
const [ navState ] = useState( createNavState );

return (
<SidebarNavigationContext.Provider value={ navState }>
<div className="edit-site-sidebar__content">
<div
ref={ wrapperRef }
key={ routeKey }
className={ wrapperCls }
>
<SidebarContentWrapper key={ routeKey }>
{ children }
</div>
</SidebarContentWrapper>
</div>
</SidebarNavigationContext.Provider>
);
Expand Down

0 comments on commit bd386d7

Please sign in to comment.