@atlaskit/navigation-system
Version:
The latest navigation system for Atlassian apps.
245 lines (236 loc) • 11.7 kB
JavaScript
/* panel.tsx generated by @compiled/babel-plugin v3.0.2 */
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import "./panel.compiled.css";
import * as React from 'react';
import { ax, ix } from "@compiled/react/runtime";
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
import { useMotion } from '@atlaskit/motion/entering/use-motion';
import ExitingPersistence from '@atlaskit/motion/exiting-persistence';
import { Reanimate } from '@atlaskit/motion/reanimate';
import { fg } from '@atlaskit/platform-feature-flags/fg';
// eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- TODO: migrate to @atlaskit/primitives/compiled
import { useSkipLinkInternal } from '../../context/skip-links/use-skip-link-internal';
import { UNSAFE_panelLayoutVar, panelPanelSplitterId, panelVar, sideNavLiveWidthVar } from './constants';
import { DangerouslyHoistCssVarToDocumentRoot } from './dangerously-hoist-css-var-to-document-root';
import { DangerouslyHoistSlotSizes } from './hoist-slot-sizes-context';
import { PanelSplitterProvider } from './panel-splitter/provider';
import { useSideNavRef } from './side-nav/use-side-nav-ref';
import { useLayoutId } from './use-layout-id';
import { useResizingWidthCssVarOnRootElement } from './use-resizing-width-css-var-on-root-element';
import { useSafeDefaultWidth } from './use-safe-default-width';
var panelSplitterResizingVar = '--n_pnlRsz';
var fallbackDefaultWidth = 365;
/**
* We typically use the `defaultWidth` as the minimum resizing width,
* but for large default widths we fallback to a standard value.
*
* This standard value of `400px` is to align with the Global Preview Panels.
*/
var fallbackResizeMinWidth = 400;
var styles = {
root: "_nd5l1b6c _10fph9n0 _vchhusvi _1pbykb7n _4t3ieqxy _kqsw1if8 _152timx3 _bfhk1bhr _1bsb1dxx _14b54rrg _4ap3vuon _1dhy1c6w _9sns1wug",
border: "_191wglyw _16qs130s _1w6eia51 _scbpglyw",
scrollContainer: "_1reo1wug _18m91wug _4t3i1osq",
hidden: "_1e0cglyw",
entering: "_k1af1nn1 _10t81ia4 _xrrp147i _1wsa4so3 _1vrh1a5r _1kmh1n9t _105abamb",
exiting: "_k1af14vv _10t81ia4 _xrrp147i _1wsa1jkh _1vrh1a5r _bq641n9t _105aeydr",
contentEntering: "_y44v1mph",
contentExiting: "_y44vf1qi",
contentChangeContainer: "_4t3i1osq"
};
var PanelContentMotion = function PanelContentMotion(_ref) {
var children = _ref.children;
var _useMotion = useMotion(),
state = _useMotion.state,
ref = _useMotion.ref;
return /*#__PURE__*/React.createElement("div", {
ref: ref,
className: ax([styles.contentChangeContainer, state === 'entering' && styles.contentEntering, state === 'exiting' && styles.contentExiting])
}, children);
};
/**
* The Panel layout area is rendered to the right (inline end) of the Main area, or the Aside area if it is present.
*
* On small viewports (below 64rem, or 1024px), the Panel slot will become an overlay.
*
* You can optionally render a `PanelSplitter` as a child to make the panel area resizable.
*/
export function Panel(_ref2) {
var children = _ref2.children,
_ref2$defaultWidth = _ref2.defaultWidth,
defaultWidthProp = _ref2$defaultWidth === void 0 ? fallbackDefaultWidth : _ref2$defaultWidth,
_ref2$label = _ref2.label,
label = _ref2$label === void 0 ? 'Panel' : _ref2$label,
_ref2$skipLinkLabel = _ref2.skipLinkLabel,
skipLinkLabel = _ref2$skipLinkLabel === void 0 ? label : _ref2$skipLinkLabel,
testId = _ref2.testId,
providedId = _ref2.id,
xcss = _ref2.xcss,
_ref2$hasBorder = _ref2.hasBorder,
hasBorder = _ref2$hasBorder === void 0 ? true : _ref2$hasBorder,
maxWidth = _ref2.maxWidth,
contentKey = _ref2.contentKey;
var dangerouslyHoistSlotSizes = useContext(DangerouslyHoistSlotSizes);
var id = useLayoutId({
providedId: providedId
});
var isMotionUpliftEnabled = fg('platform-dst-motion-uplift-panel');
var isContentMotionEnabled = fg('platform-dst-motion-uplift-panel-content');
var prevContentKeyRef = useRef(contentKey);
useEffect(function () {
if (isContentMotionEnabled && contentKey !== undefined) {
prevContentKeyRef.current = contentKey;
}
}, [contentKey, isContentMotionEnabled]);
var defaultWidth = useSafeDefaultWidth({
defaultWidthProp: defaultWidthProp,
fallbackDefaultWidth: fallbackDefaultWidth,
slotName: 'Panel'
});
// useMotion is applied only when the `platform-dst-motion-uplift-panel` gate is enabled.
var _useMotion2 = useMotion({
onFinish: function onFinish(state) {
if (state === 'exiting' && defaultWidthRef.current === 0) {
// Set width to 0 as animation is complete
setWidth(0);
}
},
initialState: defaultWidth === 0 ? 'hidden' : undefined
}),
state = _useMotion2.state,
motionRef = _useMotion2.ref,
reanimate = _useMotion2.reanimate;
/**
* Don't show the skip link if the slot has 0 width.
*
* Remove `isHidden` usage after https://jplat.atlassian.net/browse/BLU-3951
*
* TODO: when cleaning up 'platform_dst_nav4_skip_link_a11y_1' remove this call entirely
*/
useSkipLinkInternal({
id: id,
label: skipLinkLabel,
isHidden: defaultWidth === 0 || fg('platform_dst_nav4_skip_link_a11y_1')
});
var ref = useRef(null);
var _useState = useState(defaultWidth),
_useState2 = _slicedToArray(_useState, 2),
width = _useState2[0],
setWidth = _useState2[1];
// Used to track the previous value of the `defaultWidth` prop, for logging dev warnings when it changes.
var defaultWidthRef = useRef(defaultWidth);
// `mergeRefs` returns a new callback ref on every call, and combined with a new array literal
// this would cause React to detach/re-attach the ref on every render. Memoise it so the merged
// ref is stable across renders.
var mergedRef = useMemo(function () {
return isMotionUpliftEnabled ? mergeRefs([ref, motionRef]) : ref;
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[isMotionUpliftEnabled, motionRef]);
/**
* TODO: Remove this useEffect once the `width: 0` usage is removed from Jira.
* It updates the width state based on changes to `defaultWidth`, as a temporary stopgap to support Jira's current usage.
* https://jplat.atlassian.net/browse/BLU-3951
*/
useEffect(function () {
if (defaultWidthRef.current === defaultWidth) {
return;
}
if (isMotionUpliftEnabled) {
if (defaultWidth === 0) {
reanimate(Reanimate.exit);
defaultWidthRef.current = defaultWidth;
// Set width to 0 once the exit animation is complete
return;
} else if (defaultWidthRef.current === 0) {
reanimate(Reanimate.enter);
}
}
defaultWidthRef.current = defaultWidth;
setWidth(defaultWidth);
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn('Page Layout warning\n\n', 'The value of the `defaultWidth` prop on the `Panel` layout slot component has changed. This should not be changed after the component has been mounted.\n\n', 'In the future, changes to the `defaultWidth` prop will not be respected. It is only supported as a stopgap to enable migration from Nav3 to Nav4.\n\n');
}
}, [defaultWidth, isMotionUpliftEnabled, reanimate]);
var sideNavRef = useSideNavRef();
/**
* The minimum width that the panel can be resized to by the user.
*
* We only use the `defaultWidth` up to a point to avoid huge panels.
*/
var minWidth = Math.min(defaultWidth, fallbackResizeMinWidth);
/**
* Returns the bounds for resizing, evaluated lazily when needed.
*
* Defined separately to the slot bounds because the resizing bounds need to be resolvable to a pixel value,
* and the panel's slot bounds use a complex CSS expression.
*/
var getResizeBounds = useCallback(function () {
var _sideNavRef$current$o, _sideNavRef$current;
var sideNavWidth = (_sideNavRef$current$o = (_sideNavRef$current = sideNavRef.current) === null || _sideNavRef$current === void 0 ? void 0 : _sideNavRef$current.offsetWidth) !== null && _sideNavRef$current$o !== void 0 ? _sideNavRef$current$o : 0;
/**
* By default the panel should not resize larger than the page content, equivalent to the `Main` + `Aside` slots.
*
* This maximum width is equivalent to half the viewport width, after removing the sidebar width.
*
* Consumers can opt in to a larger maximum via the `maxWidth` prop.
*/
var defaultMaxWidth = "".concat(Math.round((window.innerWidth - sideNavWidth) / 2), "px");
return {
min: "".concat(minWidth, "px"),
max: maxWidth !== null && maxWidth !== void 0 ? maxWidth : defaultMaxWidth
};
}, [maxWidth, minWidth, sideNavRef]);
var panelWidthSlotBounds = {
min: "".concat(minWidth, "px"),
// `sideNavLiveWidthVar` is not defined if the `SideNav` is not mounted, so we fallback to `0px`.
// Consumers can opt in to a larger maximum via the `maxWidth` prop.
max: maxWidth !== null && maxWidth !== void 0 ? maxWidth : "round(nearest, calc((100vw - var(".concat(sideNavLiveWidthVar, ", 0px)) / 2), 1px)")
};
var panelVariableWidth = "clamp(".concat(panelWidthSlotBounds.min, ", ").concat(width, "px, ").concat(panelWidthSlotBounds.max, ")");
useResizingWidthCssVarOnRootElement({
isEnabled: dangerouslyHoistSlotSizes,
cssVar: panelSplitterResizingVar,
panelId: panelPanelSplitterId
});
return /*#__PURE__*/React.createElement("section", {
id: id,
"data-layout-slot": true,
"aria-label": label
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
,
className: ax([styles.root, isMotionUpliftEnabled && state === 'hidden' && styles.hidden, isMotionUpliftEnabled && state === 'entering' && styles.entering, isMotionUpliftEnabled && state === 'exiting' && styles.exiting, !isMotionUpliftEnabled && defaultWidth === 0 && styles.hidden, hasBorder && styles.border, xcss]),
style: _defineProperty(_defineProperty({}, panelVar, panelVariableWidth), '--minWidth', "".concat(minWidth, "px")),
"data-testid": testId,
ref: mergedRef
}, dangerouslyHoistSlotSizes &&
/*#__PURE__*/
// ------ START UNSAFE STYLES ------
// These styles are only needed for the UNSAFE legacy use case for Jira + Confluence.
// When they aren't needed anymore we can delete them wholesale.
React.createElement(DangerouslyHoistCssVarToDocumentRoot, {
variableName: UNSAFE_panelLayoutVar,
value: "0px",
mediaQuery: '@media (min-width: 90rem)',
responsiveValue: "var(".concat(panelSplitterResizingVar, ", ").concat(panelVariableWidth, ")")
})
// ------ END UNSAFE STYLES ------
, /*#__PURE__*/React.createElement(PanelSplitterProvider, {
panelId: panelPanelSplitterId,
panelRef: ref,
panelWidth: width,
onCompleteResize: setWidth,
getResizeBounds: getResizeBounds,
resizingCssVar: panelSplitterResizingVar,
position: "start"
}, /*#__PURE__*/React.createElement("div", {
className: ax([styles.scrollContainer, isMotionUpliftEnabled && state === 'entering' && styles.contentEntering, isMotionUpliftEnabled && state === 'exiting' && styles.contentExiting])
}, isContentMotionEnabled ? /*#__PURE__*/React.createElement(ExitingPersistence, {
exitThenEnter: contentKey !== undefined
}, /*#__PURE__*/React.createElement(PanelContentMotion, {
key: contentKey === undefined ? prevContentKeyRef.current : contentKey
}, children)) : children)));
}