UNPKG

@atlaskit/side-navigation

Version:

A highly composable side navigation component that supports nested views.

169 lines (163 loc) • 6.76 kB
/* 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))))))); };