@atlaskit/navigation-system
Version:
The latest navigation system for Atlassian apps.
314 lines (312 loc) • 13 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { bind, bindAll } from 'bind-event-listener';
import { defaultMainMinWidth } from './constants';
import { LayoutAreaSizingContext } from './layout-area-sizing-context';
import { layoutAreaStateContexts } from './layout-area-state-contexts';
import { useCssLayoutAreaStates } from './use-css-layout-area-states';
/**
* Stable, synchronous boundary for SSR content. The drag/cascade implementation
* is a sibling, not a replacement wrapper, so loading it cannot remount the app.
* CSS handles initial and responsive geometry; only splitter interaction requests
* the asynchronous chunk. The eager observer reads modes without changing widths.
*/
export function LayoutAreaSizingProvider(_ref) {
var _states$sideNav$mode, _states$sideNav, _states$chatPanel$mo, _states$chatPanel;
var children = _ref.children,
layoutRef = _ref.layoutRef;
var _useState = useState(null),
_useState2 = _slicedToArray(_useState, 2),
Runtime = _useState2[0],
setRuntime = _useState2[1];
var _useState3 = useState({}),
_useState4 = _slicedToArray(_useState3, 2),
states = _useState4[0],
setStates = _useState4[1];
var actions = useRef(null);
var configs = useRef({});
var _useState5 = useState(null),
_useState6 = _slicedToArray(_useState5, 2),
lastOpenedOverlay = _useState6[0],
setLastOpenedOverlay = _useState6[1];
var _useState7 = useState(0),
_useState8 = _slicedToArray(_useState7, 2),
configRevision = _useState8[0],
setConfigRevision = _useState8[1];
var cssModes = useCssLayoutAreaStates(layoutRef, configRevision, Runtime === null);
var sideNavMode = (_states$sideNav$mode = (_states$sideNav = states['side-nav']) === null || _states$sideNav === void 0 ? void 0 : _states$sideNav.mode) !== null && _states$sideNav$mode !== void 0 ? _states$sideNav$mode : cssModes['side-nav'];
var chatPanelMode = (_states$chatPanel$mo = (_states$chatPanel = states['chat-panel']) === null || _states$chatPanel === void 0 ? void 0 : _states$chatPanel.mode) !== null && _states$chatPanel$mo !== void 0 ? _states$chatPanel$mo : cssModes['chat-panel'];
// Overlay dismissal is available before the sizing chunk loads. Keep the open
// order here too: replaying registrations into the runtime loses that order.
useEffect(function () {
var inlineChatQuery = window.matchMedia('(min-width: 40rem)');
var dismissOlderOverlay = function dismissOlderOverlay() {
var _olderOverlay$onReque;
var sideNav = configs.current['side-nav'];
var chatPanel = configs.current['chat-panel'];
if (!(sideNav !== null && sideNav !== void 0 && sideNav.isOpen) || !(chatPanel !== null && chatPanel !== void 0 && chatPanel.isOpen) || !lastOpenedOverlay) {
return;
}
// Before allocation, CSS puts both areas in overlay below 40rem. Once
// sizing is active, also honour overlays caused by custom widths/slots.
if (inlineChatQuery.matches && (sideNavMode !== 'overlay' || chatPanelMode !== 'overlay')) {
return;
}
var olderOverlay = lastOpenedOverlay === 'side-nav' ? chatPanel : sideNav;
(_olderOverlay$onReque = olderOverlay.onRequestClose) === null || _olderOverlay$onReque === void 0 || _olderOverlay$onReque.call(olderOverlay);
};
dismissOlderOverlay();
return bind(inlineChatQuery, {
type: 'change',
listener: dismissOlderOverlay
});
}, [chatPanelMode, lastOpenedOverlay, sideNavMode]);
var splitters = useRef(new Set());
var registerSplitter = useCallback(function (element) {
splitters.current.add(element);
return function () {
splitters.current.delete(element);
};
}, []);
var mainMinWidth = useRef(defaultMainMinWidth);
var loadRef = useRef(function () {});
var ready = useRef(false);
var waiting = useRef([]);
var pending = useRef([]);
var prepareResize = useCallback(function () {
if (ready.current) {
return Promise.resolve(true);
}
return new Promise(function (resolve) {
waiting.current.push(resolve);
loadRef.current();
});
}, []);
useEffect(function () {
var root = layoutRef === null || layoutRef === void 0 ? void 0 : layoutRef.current;
if (!root) {
return;
}
var cancelled = false;
var loading = false;
var waitingForRuntime = waiting.current;
var load = function load() {
if (loading) {
return;
}
loading = true;
void import( /* webpackChunkName: "@atlaskit-internal_navigation-layout-sizing" */'./layout-area-sizing-runtime').then(function (_ref2) {
var LayoutAreaSizingRuntime = _ref2.LayoutAreaSizingRuntime;
if (!cancelled) {
setRuntime(function () {
return LayoutAreaSizingRuntime;
});
}
}, function () {
loading = false;
pending.current = [];
var _iterator = _createForOfIteratorHelper(waitingForRuntime.splice(0)),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var resolve = _step.value;
resolve(false);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
});
};
loadRef.current = load;
var loadForSplitter = function loadForSplitter(event) {
if (!(event.target instanceof Node)) {
return;
}
var _iterator2 = _createForOfIteratorHelper(splitters.current),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var splitter = _step2.value;
if (splitter.contains(event.target)) {
load();
return;
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
};
var unbindRoot = bindAll(root, [{
type: 'pointerover',
listener: loadForSplitter,
options: {
passive: true
}
}, {
type: 'focusin',
listener: loadForSplitter
}, {
type: 'pointerdown',
listener: loadForSplitter,
options: {
passive: true
}
}]);
return function () {
cancelled = true;
ready.current = false;
pending.current = [];
var _iterator3 = _createForOfIteratorHelper(waitingForRuntime.splice(0)),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var resolve = _step3.value;
resolve(false);
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
loadRef.current = function () {};
unbindRoot();
};
}, [layoutRef]);
var onReady = useCallback(function (runtime) {
actions.current = runtime;
if (runtime) {
runtime.setMainMinWidth(mainMinWidth.current);
var _iterator4 = _createForOfIteratorHelper(['side-nav', 'panel', 'chat-panel']),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var area = _step4.value;
var config = configs.current[area];
if (config) {
runtime.registerArea(area, config);
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
}
}, []);
var onUpdate = useCallback(function (next) {
setStates(next);
var runtime = actions.current;
if (runtime && Object.values(next).some(Boolean)) {
ready.current = true;
var _iterator5 = _createForOfIteratorHelper(pending.current.splice(0)),
_step5;
try {
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
var action = _step5.value;
action(runtime);
}
} catch (err) {
_iterator5.e(err);
} finally {
_iterator5.f();
}
var _iterator6 = _createForOfIteratorHelper(waiting.current.splice(0)),
_step6;
try {
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
var resolve = _step6.value;
resolve(true);
}
} catch (err) {
_iterator6.e(err);
} finally {
_iterator6.f();
}
}
}, []);
var dispatch = useCallback(function (action) {
if (ready.current && actions.current) {
action(actions.current);
} else {
pending.current.push(action);
loadRef.current();
}
}, []);
var value = useMemo(function () {
return {
prepareResize: prepareResize,
registerSplitter: registerSplitter,
registerArea: function registerArea(area, config) {
var _configs$current$area, _actions$current;
if (area !== 'panel' && config.isOpen && !((_configs$current$area = configs.current[area]) !== null && _configs$current$area !== void 0 && _configs$current$area.isOpen)) {
setLastOpenedOverlay(area);
}
configs.current[area] = config;
setConfigRevision(function (revision) {
return revision + 1;
});
(_actions$current = actions.current) === null || _actions$current === void 0 || _actions$current.registerArea(area, config);
},
unregisterArea: function unregisterArea(area) {
var _actions$current2;
delete configs.current[area];
setConfigRevision(function (revision) {
return revision + 1;
});
(_actions$current2 = actions.current) === null || _actions$current2 === void 0 || _actions$current2.unregisterArea(area);
},
setMainMinWidth: function setMainMinWidth(width) {
var _actions$current3;
if (mainMinWidth.current !== width) {
setConfigRevision(function (revision) {
return revision + 1;
});
}
mainMinWidth.current = width;
(_actions$current3 = actions.current) === null || _actions$current3 === void 0 || _actions$current3.setMainMinWidth(width);
},
getResizeBounds: function getResizeBounds(area) {
var _actions$current4;
return (_actions$current4 = actions.current) === null || _actions$current4 === void 0 ? void 0 : _actions$current4.getResizeBounds(area);
},
startResize: function startResize(area, mode) {
return dispatch(function (runtime) {
return runtime.startResize(area, mode);
});
},
resize: function resize(area, mode, width) {
return dispatch(function (runtime) {
return runtime.resize(area, mode, width);
});
},
completeResize: function completeResize(area, mode, width) {
return dispatch(function (runtime) {
return runtime.completeResize(area, mode, width);
});
}
};
}, [dispatch, prepareResize, registerSplitter]);
var SideNavStateProvider = layoutAreaStateContexts['side-nav'].Provider;
var PanelStateProvider = layoutAreaStateContexts.panel.Provider;
var ChatPanelStateProvider = layoutAreaStateContexts['chat-panel'].Provider;
return /*#__PURE__*/React.createElement(LayoutAreaSizingContext.Provider, {
value: value
}, /*#__PURE__*/React.createElement(SideNavStateProvider, {
value: states['side-nav']
}, /*#__PURE__*/React.createElement(PanelStateProvider, {
value: states.panel
}, /*#__PURE__*/React.createElement(ChatPanelStateProvider, {
value: states['chat-panel']
}, children))), Runtime && /*#__PURE__*/React.createElement(Runtime, {
layoutRef: layoutRef,
onReady: onReady,
onUpdate: onUpdate
}));
}