UNPKG

@atlaskit/navigation-system

Version:

The latest navigation system for Atlassian apps.

146 lines (141 loc) • 6.53 kB
/* skip-links-popup.tsx generated by @compiled/babel-plugin v3.0.2 */ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.SkipLinksPopup = SkipLinksPopup; require("./skip-links-popup.compiled.css"); var _react = _interopRequireWildcard(require("react")); var React = _react; var _runtime = require("@compiled/react/runtime"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _reactDom = require("react-dom"); var _button = _interopRequireDefault(require("@atlaskit/button/default/button")); var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs")); var _popup = require("@atlaskit/popup/popup"); var _focusElement = require("./focus-element"); var _skipLink = require("./skip-link"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } var contentStyles = { root: "_1rjcu2gc _18zrpxbi _1e0c1txw _2lx21bp4 _1pbyjh3g", skipLinkList: "_1e0c1txw _2lx21bp4 _1x8w11lx _2mzuglyw _1pfhze3t _bozgze3t", label: "_11c8i4vh _1rjcu2gc _syazazsu" }; var triggerStyles = { root: "_2rko12b0 _1dqoglyw _kqsw1n9t _1e02utpp _152tutpp _1pby1mrw _tzy4idpf _bfhk1bhr _4ojojh3g _c8j0kb7n", isOpen: "_tzy4idpf _t9ecz6y5" }; var popupOffset = [0, 0]; /** * Skip links shown behind `platform_dst_nav4_skip_link_a11y_1`: a single focus-revealed * control opens a dialog listing skip targets. */ function SkipLinksPopup(_ref) { var title = _ref.label, triggerLabel = _ref.triggerLabel, testId = _ref.testId, links = _ref.links; var titleId = (0, _react.useId)(); var _useState = (0, _react.useState)(false), _useState2 = (0, _slicedToArray2.default)(_useState, 2), isOpen = _useState2[0], setIsOpen = _useState2[1]; var triggerRef = (0, _react.useRef)(null); var openPopup = (0, _react.useCallback)(function () { setIsOpen(true); }, []); /** * Closes the popup. * * Uses `flushSync` to avoid issues with focus management. * This ensures the popup's focus lock is fully released by the time skip links or our `onClose` try to move focus. */ var closePopup = (0, _react.useCallback)(function () { (0, _reactDom.flushSync)(function () { setIsOpen(false); }); }, []); /** * Restores focus to the trigger on close, if it hasn't moved to another element, such as when Esc is pressed instead of a skip link. * * We need this because using the standard `shouldReturnFocus` on the popup would break the skip link focus behaviour, * as focus would always move back to the trigger. */ var onClose = (0, _react.useCallback)(function () { closePopup(); // Focus on the body implies a skip link was not clicked if (document.activeElement === document.body) { var _triggerRef$current; (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 || _triggerRef$current.focus(); } }, [closePopup]); return /*#__PURE__*/React.createElement(_popup.Popup, { isOpen: isOpen, onClose: onClose, placement: "bottom-start", shouldRenderToParent: true, shouldReturnFocus: false, role: "dialog", titleId: titleId, testId: testId ? "".concat(testId, "--skip-links-container") : undefined, offset: popupOffset, content: function content() { return /*#__PURE__*/React.createElement("div", { className: (0, _runtime.ax)([contentStyles.root]) }, /*#__PURE__*/React.createElement("div", { "data-testid": testId ? "".concat(testId, "--skip-links-container--label") : undefined, id: titleId, className: (0, _runtime.ax)([contentStyles.label]) }, title), /*#__PURE__*/React.createElement("ol", { className: (0, _runtime.ax)([contentStyles.skipLinkList]) }, links.map(function (_ref2) { var id = _ref2.id, label = _ref2.label, _navigate = _ref2.navigate; return /*#__PURE__*/React.createElement(_skipLink.SkipLink, { key: id, id: id /** * The popup always owns the navigation effect under the * `platform_dst_nav4_skip_link_a11y_1` gate (the only path * that renders `SkipLinksPopup`). It first closes itself * synchronously so its focus lock is released, then either: * * - delegates to the consumer's `navigate` (e.g. SideNav * expanding and focusing its first nav item), or * - falls back to focusing the slot element with `id`. * * This means `SkipLink`'s `onBeforeNavigate` hook is never * needed under the gate and can be removed on cleanup. */, navigate: function navigate() { closePopup(); if (_navigate) { _navigate(); return; } // Intentionally not using `document.querySelector` because many valid IDs are not valid selectors. var target = document.getElementById(id); if (target) { (0, _focusElement.focusElement)(target); } } }, label); }))); }, trigger: function trigger(triggerProps) { return /*#__PURE__*/React.createElement("div", { className: (0, _runtime.ax)([triggerStyles.root, isOpen && triggerStyles.isOpen]) }, /*#__PURE__*/React.createElement(_button.default, (0, _extends2.default)({}, triggerProps, { ref: (0, _mergeRefs.default)([triggerProps.ref, triggerRef]), appearance: "primary", isSelected: isOpen, testId: testId ? "".concat(testId, "--skip-links-trigger") : undefined, onClick: openPopup }), triggerLabel)); } }); }