@atlaskit/side-navigation
Version:
A highly composable side navigation component that supports nested views.
169 lines (163 loc) • 6.76 kB
JavaScript
/* index.tsx generated by @compiled/babel-plugin v3.0.2 */
import _extends from "@babel/runtime/helpers/extends";
import "./index.compiled.css";
import { ax, ix } from "@compiled/react/runtime";
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import ExitingPersistence from '@atlaskit/motion/exiting-persistence';
import { fg } from '@atlaskit/platform-feature-flags/fg';
import useChildIds from '../../common/use-child-ids';
import { GoBackItem as GoBackButton } from '../Item/go-back-item';
import { NestingItem } from '../NestingItem';
import { NestedContext } from './nested-context';
import { NestingMotion } from './nesting-motion';
// Named so ERT doesn't pick up the override name as a type.
export const ROOT_ID = 'ATLASKIT_NESTED_ROOT';
/**
* @deprecated `@atlaskit/side-navigation` is deprecated. Use `@atlaskit/navigation-system` instead.
*/
const nestableNavigationContentStyles = null;
const nestingRootStyles = null;
/**
* __Nestable navigation content__
*
* The container for navigation items with nested views
*
* - [Examples](https://atlassian.design/components/side-navigation/examples#nested-navigation)
* - [Code](https://atlassian.design/components/side-navigation/code)
*
* @deprecated `@atlaskit/side-navigation` is deprecated. Use `@atlaskit/navigation-system` instead.
*/
export const NestableNavigationContent = props => {
const containerRef = useRef(null);
const {
children,
testId,
overrides,
initialStack,
onChange,
onUnknownNest,
stack,
showTopScrollIndicator,
isDefaultFocusControl = true
} = props;
const goBackButtonRef = useRef(null);
const [committedStack, setCommittedStack] = useState(stack || initialStack || []);
const controlledStack = stack || undefined;
const currentStackId = committedStack[committedStack.length - 1] || ROOT_ID;
const [transition, setTransition] = useState('nesting');
const backTestId = testId && `${testId}--go-back-item`;
const [activeParentId, setActiveParentId] = useState('');
const [focusGoBackButton, setFocusGoBackButton] = useState(false);
const shouldFocus = isDefaultFocusControl && fg('platform-side-navigation-keyboard-focus');
const renderGoBackItem = overrides && overrides.GoBackItem && overrides.GoBackItem.render ? overrides.GoBackItem.render : props => /*#__PURE__*/React.createElement(GoBackButton, props, "Go back");
const {
childIdsRef
} = useChildIds(currentStackId, committedStack, onUnknownNest);
const onNestHandler = useCallback(layerId => {
onChange && onChange(committedStack.concat(layerId));
setFocusGoBackButton(true);
if (controlledStack) {
// We are in controlled mode - ignore the steps.
return;
}
// We need to split the state update into to parts.
// First: Updating the direction of the motions.
// Second: Actually updating the stack (which will cause elements to enter & leave).
setTransition('nesting');
requestAnimationFrame(() => {
setCommittedStack(prev => {
const newStack = prev.concat(layerId);
return newStack;
});
});
}, [controlledStack, onChange, committedStack]);
const onUnNestHandler = useCallback(() => {
onChange && onChange(committedStack.slice(0, committedStack.length - 1));
setActiveParentId(currentStackId);
setFocusGoBackButton(false);
if (controlledStack) {
// We are in controlled mode - ignore the steps.
return;
}
// We need to split the state update into to parts.
// First: Updating the direction of the motions.
// Second: Actually updating the stack (which will cause elements to enter & leave).
setTransition('unnesting');
requestAnimationFrame(() => {
setCommittedStack(prev => {
const newStack = prev.slice(0, prev.length - 1);
return newStack;
});
});
}, [controlledStack, onChange, committedStack, setActiveParentId, currentStackId]);
useEffect(() => {
if (!controlledStack) {
// We aren't in controlled mode - bail out.
return;
}
if (JSON.stringify(committedStack) === JSON.stringify(controlledStack)) {
// stacks are equal - do nothing!
return;
}
// Controlled prop updated, let's figure out if we're nesting or unnesting.
if (controlledStack.length < committedStack.length) {
// We are unnesting (removing from the stack)
setTransition('unnesting');
} else {
// We are nesting (adding to the stack)
setTransition('nesting');
}
requestAnimationFrame(() => {
setCommittedStack(controlledStack);
});
}, [committedStack, controlledStack]);
const backButton = renderGoBackItem({
onClick: onUnNestHandler,
testId: backTestId,
ref: shouldFocus ? goBackButtonRef : null
});
const context = useMemo(() => ({
currentStackId,
backButton,
stack: committedStack,
onNest: onNestHandler,
onUnNest: onUnNestHandler,
parentId: ROOT_ID,
childIds: childIdsRef,
forceShowTopScrollIndicator: showTopScrollIndicator,
activeParentId,
goBackButtonRef,
isDefaultFocusControl,
focusGoBackButton
}), [currentStackId, backButton, committedStack, onNestHandler, onUnNestHandler, childIdsRef, showTopScrollIndicator, activeParentId, goBackButtonRef, isDefaultFocusControl, focusGoBackButton]);
const manageFocus = event => {
const triggeredByKeyboard = event.nativeEvent.detail === 0;
if (triggeredByKeyboard) {
containerRef.current && containerRef.current.focus();
}
};
return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", {
"data-testid": testId,
ref: containerRef,
tabIndex: -1,
onClick: manageFocus,
className: ax(["_12ji1r31 _1qu2glyw _12y31o36 _4t3i1osq _kqswh2mm"])
}, /*#__PURE__*/React.createElement(ExitingPersistence, null, /*#__PURE__*/React.createElement(NestingMotion
// Key is needed to have a unique react instance per stack name.
// This enables us to easily animate it in & out with exiting persistence.
, {
key: currentStackId,
enterFrom: transition === 'nesting' ? 'right' : 'left',
exitTo: transition === 'nesting' ? 'left' : 'right',
testId: testId && `${testId}-anim`
}, motion => /*#__PURE__*/React.createElement("div", _extends({}, motion, {
className: ax(["_1e0c1txw _1bsb1osq _4t3i1osq _kqswstnw _2lx21bp4", motion.className])
}), /*#__PURE__*/React.createElement(NestedContext.Provider, {
// This provider is inside the NestingMotion to ensure it keeps a stale
// reference to the previous value.
value: context
}, /*#__PURE__*/React.createElement(NestingItem, {
title: "",
id: ROOT_ID
}, children)))))));
};