@atlaskit/atlassian-navigation
Version:
A horizontal navigation component for Atlassian products.
155 lines (149 loc) • 7.51 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useOverflowStatus = exports.useOverflowController = exports.default = exports.OverflowProvider = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _throttle = _interopRequireDefault(require("lodash/throttle"));
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
// Prevent width detector from triggering too many re-renders
var THROTTLE_INTERVAL = 16 * 4;
// Approx min width of items (based of "More" size)
var ITEM_APPROX_MINWIDTH = 70;
var calculateHash = function calculateHash(w, n) {
return w + '#' + n;
};
var updateHashRef = function updateHashRef(currentRef, value) {
currentRef.unshift(value);
currentRef.length = 3;
};
var OverflowContext = /*#__PURE__*/(0, _react.createContext)({
isVisible: true,
openOverflowMenu: _noop.default,
closeOverflowMenu: _noop.default
});
// eslint-disable-next-line @repo/internal/react/require-jsdoc
var OverflowProvider = function OverflowProvider(_ref) {
var children = _ref.children,
isVisible = _ref.isVisible,
openOverflowMenu = _ref.openOverflowMenu,
closeOverflowMenu = _ref.closeOverflowMenu;
var Provider = OverflowContext.Provider;
var value = (0, _react.useMemo)(function () {
return {
isVisible: isVisible,
openOverflowMenu: openOverflowMenu,
closeOverflowMenu: closeOverflowMenu
};
}, [isVisible, openOverflowMenu, closeOverflowMenu]);
return /*#__PURE__*/_react.default.createElement(Provider, {
value: value
}, children);
};
/**
* __useOverFlowStatus__
*
* Returns the current context value for the nearest OverflowProvider.
*
* - [Example](https://atlassian.design/components/atlassian-navigation/examples#responsive)
*/
exports.OverflowProvider = OverflowProvider;
var useOverflowStatus = function useOverflowStatus() {
return (0, _react.useContext)(OverflowContext);
};
exports.useOverflowStatus = useOverflowStatus;
var useOverflowController = function useOverflowController(nodes) {
var items = _react.default.Children.toArray(nodes);
var _useState = (0, _react.useState)(9999),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
width = _useState2[0],
setWidth = _useState2[1];
var _useState3 = (0, _react.useState)(items.length),
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
itemsLimit = _useState4[0],
setItemsLimit = _useState4[1];
var _useState5 = (0, _react.useState)({}),
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
forceEffectValue = _useState6[0],
triggerForceEffect = _useState6[1];
// Storing items approximate width so we can try expanding when there is enough room
var itemsWidths = (0, _react.useRef)([]).current;
// Storing a couple of width + items count in order to stabilize
var hashRef = (0, _react.useRef)([]);
// AFP-2511 TODO: Fix automatic suppressions below
// eslint-disable-next-line react-hooks/exhaustive-deps
var throttleSetWidth = (0, _react.useCallback)((0, _throttle.default)(setWidth, THROTTLE_INTERVAL), [setWidth]);
(0, _react.useEffect)(function () {
var lastItemWidth = itemsWidths[itemsLimit] || 0;
var wasJustLimited = lastItemWidth < 0;
var currentHash = calculateHash(width, itemsLimit);
if (hashRef.current[0] === currentHash) {
// After removing an item, if width has not changed yet we schedule a force update
// to handle case where removing an item does not actually trigger width change
var t = setTimeout(function () {
updateHashRef(hashRef.current, '');
triggerForceEffect({});
}, THROTTLE_INTERVAL * 1.5);
return function () {
return clearTimeout(t);
};
}
if (wasJustLimited) {
// Width was updated either via resize or after changing the limit
// we cap the width between ITEM_APPROX_MINWIDTH and 2*ITEM_APPROX_MINWIDTH
// because width is throttled as when fast expanding/resizing partialWidth
// will not be reliable (edge case)
var partialWidth = Math.max(Math.min(width + lastItemWidth, ITEM_APPROX_MINWIDTH * 2), ITEM_APPROX_MINWIDTH);
itemsWidths[itemsLimit] = partialWidth;
}
if (width < ITEM_APPROX_MINWIDTH * 0.9 && itemsLimit) {
// If current width is less than an item approx width we remove an item
// marking the width as negative so we will calculate it on width update
// plus we set the hash to stabilise and not removing more than one element
// until we are sure width was updated
var nextHash = calculateHash(width, itemsLimit - 1);
if (hashRef.current.indexOf(nextHash) === -1) {
setItemsLimit(itemsLimit - 1);
itemsWidths[itemsLimit - 1] = -(width || 1);
updateHashRef(hashRef.current, nextHash);
}
return;
}
/**
* This is not necessarily equal to `lastItemWidth` because the
* ```js
* if (wasJustLimited) {}
* ```
* branch above modifies `itemsWidths`.
*
* Using `lastItemWidth` here can cause collapsing behavior to fail,
* such as the issue reported in DSP-7329.
*/
var currentLastItemWidth = itemsWidths[itemsLimit] || 0;
if (width - currentLastItemWidth > ITEM_APPROX_MINWIDTH * 1.1 && itemsLimit < items.length) {
// If we have enough room to accomodate next item width we increase the limit
// unless it has been recently removed
var _nextHash = calculateHash(width, itemsLimit + 1);
if (hashRef.current.indexOf(_nextHash) === -1) {
setItemsLimit(itemsLimit + 1);
updateHashRef(hashRef.current, _nextHash);
}
return;
}
}, [width, hashRef, itemsLimit, itemsWidths, forceEffectValue, items.length]);
return {
visibleItems: items.slice(0, itemsLimit),
overflowItems: items.slice(itemsLimit),
updateWidth: throttleSetWidth
};
};
// Used to extract props for useOverflowStatus();
// eslint-disable-next-line @repo/internal/react/use-noop
exports.useOverflowController = useOverflowController;
var _default = function _default(props) {};
exports.default = _default;