@wordpress/components
Version:
UI components for WordPress.
136 lines (126 loc) • 5.71 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.NavigatorScreen = void 0;
var _dom = require("@wordpress/dom");
var _element = require("@wordpress/element");
var _compose = require("@wordpress/compose");
var _escapeHtml = require("@wordpress/escape-html");
var _warning = _interopRequireDefault(require("@wordpress/warning"));
var _context = require("../../context");
var _useCx = require("../../utils/hooks/use-cx");
var _view = require("../../view");
var _context2 = require("../context");
var styles = _interopRequireWildcard(require("../styles"));
var _useScreenAnimatePresence = require("./use-screen-animate-presence");
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
function UnconnectedNavigatorScreen(props, forwardedRef) {
if (!/^\//.test(props.path)) {
globalThis.SCRIPT_DEBUG === true ? (0, _warning.default)('wp.components.Navigator.Screen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.') : void 0;
}
const screenId = (0, _element.useId)();
const {
children,
className,
path,
onAnimationEnd: onAnimationEndProp,
...otherProps
} = (0, _context.useContextSystem)(props, 'Navigator.Screen');
const {
location,
match,
addScreen,
removeScreen
} = (0, _element.useContext)(_context2.NavigatorContext);
const {
isInitial,
isBack,
focusTargetSelector,
skipFocus
} = location;
const isMatch = match === screenId;
const wrapperRef = (0, _element.useRef)(null);
const skipAnimationAndFocusRestoration = !!isInitial && !isBack;
// Register / unregister screen with the navigator context.
(0, _element.useEffect)(() => {
const screen = {
id: screenId,
path: (0, _escapeHtml.escapeAttribute)(path)
};
addScreen(screen);
return () => removeScreen(screen);
}, [screenId, path, addScreen, removeScreen]);
// Animation.
const {
animationStyles,
shouldRenderScreen,
screenProps
} = (0, _useScreenAnimatePresence.useScreenAnimatePresence)({
isMatch,
isBack,
onAnimationEnd: onAnimationEndProp,
skipAnimation: skipAnimationAndFocusRestoration
});
const cx = (0, _useCx.useCx)();
const classes = (0, _element.useMemo)(() => cx(styles.navigatorScreen, animationStyles, className), [className, cx, animationStyles]);
// Focus restoration
const locationRef = (0, _element.useRef)(location);
(0, _element.useEffect)(() => {
locationRef.current = location;
}, [location]);
(0, _element.useEffect)(() => {
const wrapperEl = wrapperRef.current;
// Only attempt to restore focus:
// - if the current location is not the initial one (to avoid moving focus on page load)
// - when the screen becomes visible
// - if the wrapper ref has been assigned
// - if focus hasn't already been restored for the current location
// - if the `skipFocus` option is not set to `true`. This is useful when we trigger the navigation outside of NavigatorScreen.
if (skipAnimationAndFocusRestoration || !isMatch || !wrapperEl || locationRef.current.hasRestoredFocus || skipFocus) {
return;
}
const activeElement = wrapperEl.ownerDocument.activeElement;
// If an element is already focused within the wrapper do not focus the
// element. This prevents inputs or buttons from losing focus unnecessarily.
if (wrapperEl.contains(activeElement)) {
return;
}
let elementToFocus = null;
// When navigating back, if a selector is provided, use it to look for the
// target element (assumed to be a node inside the current NavigatorScreen)
if (isBack && focusTargetSelector) {
elementToFocus = wrapperEl.querySelector(focusTargetSelector);
}
// If the previous query didn't run or find any element to focus, fallback
// to the first tabbable element in the screen (or the screen itself).
if (!elementToFocus) {
const [firstTabbable] = _dom.focus.tabbable.find(wrapperEl);
elementToFocus = firstTabbable !== null && firstTabbable !== void 0 ? firstTabbable : wrapperEl;
}
locationRef.current.hasRestoredFocus = true;
elementToFocus.focus();
}, [skipAnimationAndFocusRestoration, isMatch, isBack, focusTargetSelector, skipFocus]);
const mergedWrapperRef = (0, _compose.useMergeRefs)([forwardedRef, wrapperRef]);
return shouldRenderScreen ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_view.View, {
ref: mergedWrapperRef,
className: classes,
...screenProps,
...otherProps,
children: children
}) : null;
}
const NavigatorScreen = exports.NavigatorScreen = (0, _context.contextConnect)(UnconnectedNavigatorScreen, 'Navigator.Screen');
//# sourceMappingURL=component.js.map