summaryrefslogtreecommitdiffstats
path: root/wp-includes/blocks/navigation/view.js
diff options
context:
space:
mode:
Diffstat (limited to 'wp-includes/blocks/navigation/view.js')
-rw-r--r--wp-includes/blocks/navigation/view.js213
1 files changed, 213 insertions, 0 deletions
diff --git a/wp-includes/blocks/navigation/view.js b/wp-includes/blocks/navigation/view.js
new file mode 100644
index 0000000..46b2c68
--- /dev/null
+++ b/wp-includes/blocks/navigation/view.js
@@ -0,0 +1,213 @@
+"use strict";
+(self["__WordPressPrivateInteractivityAPI__"] = self["__WordPressPrivateInteractivityAPI__"] || []).push([[3],{
+
+/***/ 932:
+/***/ (function(__unused_webpack_module, __unused_webpack___webpack_exports__, __webpack_require__) {
+
+/* harmony import */ var _wordpress_interactivity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(754);
+/**
+ * WordPress dependencies
+ */
+
+const focusableSelectors = ['a[href]', 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 'select:not([disabled]):not([aria-hidden])', 'textarea:not([disabled]):not([aria-hidden])', 'button:not([disabled]):not([aria-hidden])', '[contenteditable]', '[tabindex]:not([tabindex^="-"])'];
+
+// This is a fix for Safari in iOS/iPadOS. Without it, Safari doesn't focus out
+// when the user taps in the body. It can be removed once we add an overlay to
+// capture the clicks, instead of relying on the focusout event.
+document.addEventListener('click', () => {});
+const openMenu = (store, menuOpenedOn) => {
+ const {
+ context,
+ selectors
+ } = store;
+ selectors.core.navigation.menuOpenedBy(store)[menuOpenedOn] = true;
+ if (context.core.navigation.type === 'overlay') {
+ // Add a `has-modal-open` class to the <html> root.
+ document.documentElement.classList.add('has-modal-open');
+ }
+};
+const closeMenu = (store, menuClosedOn) => {
+ const {
+ context,
+ selectors
+ } = store;
+ selectors.core.navigation.menuOpenedBy(store)[menuClosedOn] = false;
+ // Check if the menu is still open or not.
+ if (!selectors.core.navigation.isMenuOpen(store)) {
+ if (context.core.navigation.modal?.contains(window.document.activeElement)) {
+ context.core.navigation.previousFocus?.focus();
+ }
+ context.core.navigation.modal = null;
+ context.core.navigation.previousFocus = null;
+ if (context.core.navigation.type === 'overlay') {
+ document.documentElement.classList.remove('has-modal-open');
+ }
+ }
+};
+(0,_wordpress_interactivity__WEBPACK_IMPORTED_MODULE_0__/* .store */ .h)({
+ effects: {
+ core: {
+ navigation: {
+ initMenu: store => {
+ const {
+ context,
+ selectors,
+ ref
+ } = store;
+ if (selectors.core.navigation.isMenuOpen(store)) {
+ const focusableElements = ref.querySelectorAll(focusableSelectors);
+ context.core.navigation.modal = ref;
+ context.core.navigation.firstFocusableElement = focusableElements[0];
+ context.core.navigation.lastFocusableElement = focusableElements[focusableElements.length - 1];
+ }
+ },
+ focusFirstElement: store => {
+ const {
+ selectors,
+ ref
+ } = store;
+ if (selectors.core.navigation.isMenuOpen(store)) {
+ ref.querySelector('.wp-block-navigation-item > *:first-child').focus();
+ }
+ }
+ }
+ }
+ },
+ selectors: {
+ core: {
+ navigation: {
+ roleAttribute: store => {
+ const {
+ context,
+ selectors
+ } = store;
+ return context.core.navigation.type === 'overlay' && selectors.core.navigation.isMenuOpen(store) ? 'dialog' : null;
+ },
+ ariaModal: store => {
+ const {
+ context,
+ selectors
+ } = store;
+ return context.core.navigation.type === 'overlay' && selectors.core.navigation.isMenuOpen(store) ? 'true' : null;
+ },
+ ariaLabel: store => {
+ const {
+ context,
+ selectors
+ } = store;
+ return context.core.navigation.type === 'overlay' && selectors.core.navigation.isMenuOpen(store) ? context.core.navigation.ariaLabel : null;
+ },
+ isMenuOpen: ({
+ context
+ }) =>
+ // The menu is opened if either `click`, `hover` or `focus` is true.
+ Object.values(context.core.navigation[context.core.navigation.type === 'overlay' ? 'overlayOpenedBy' : 'submenuOpenedBy']).filter(Boolean).length > 0,
+ menuOpenedBy: ({
+ context
+ }) => context.core.navigation[context.core.navigation.type === 'overlay' ? 'overlayOpenedBy' : 'submenuOpenedBy']
+ }
+ }
+ },
+ actions: {
+ core: {
+ navigation: {
+ openMenuOnHover(store) {
+ const {
+ navigation
+ } = store.context.core;
+ if (navigation.type === 'submenu' &&
+ // Only open on hover if the overlay is closed.
+ Object.values(navigation.overlayOpenedBy || {}).filter(Boolean).length === 0) openMenu(store, 'hover');
+ },
+ closeMenuOnHover(store) {
+ closeMenu(store, 'hover');
+ },
+ openMenuOnClick(store) {
+ const {
+ context,
+ ref
+ } = store;
+ context.core.navigation.previousFocus = ref;
+ openMenu(store, 'click');
+ },
+ closeMenuOnClick(store) {
+ closeMenu(store, 'click');
+ closeMenu(store, 'focus');
+ },
+ openMenuOnFocus(store) {
+ openMenu(store, 'focus');
+ },
+ toggleMenuOnClick: store => {
+ const {
+ selectors,
+ context,
+ ref
+ } = store;
+ // Safari won't send focus to the clicked element, so we need to manually place it: https://bugs.webkit.org/show_bug.cgi?id=22261
+ if (window.document.activeElement !== ref) ref.focus();
+ const menuOpenedBy = selectors.core.navigation.menuOpenedBy(store);
+ if (menuOpenedBy.click || menuOpenedBy.focus) {
+ closeMenu(store, 'click');
+ closeMenu(store, 'focus');
+ } else {
+ context.core.navigation.previousFocus = ref;
+ openMenu(store, 'click');
+ }
+ },
+ handleMenuKeydown: store => {
+ const {
+ context,
+ selectors,
+ event
+ } = store;
+ if (selectors.core.navigation.menuOpenedBy(store).click) {
+ // If Escape close the menu.
+ if (event?.key === 'Escape') {
+ closeMenu(store, 'click');
+ closeMenu(store, 'focus');
+ return;
+ }
+
+ // Trap focus if it is an overlay (main menu).
+ if (context.core.navigation.type === 'overlay' && event.key === 'Tab') {
+ // If shift + tab it change the direction.
+ if (event.shiftKey && window.document.activeElement === context.core.navigation.firstFocusableElement) {
+ event.preventDefault();
+ context.core.navigation.lastFocusableElement.focus();
+ } else if (!event.shiftKey && window.document.activeElement === context.core.navigation.lastFocusableElement) {
+ event.preventDefault();
+ context.core.navigation.firstFocusableElement.focus();
+ }
+ }
+ }
+ },
+ handleMenuFocusout: store => {
+ const {
+ context,
+ event
+ } = store;
+ // If focus is outside modal, and in the document, close menu
+ // event.target === The element losing focus
+ // event.relatedTarget === The element receiving focus (if any)
+ // When focusout is outsite the document,
+ // `window.document.activeElement` doesn't change.
+
+ // The event.relatedTarget is null when something outside the navigation menu is clicked. This is only necessary for Safari.
+ if (event.relatedTarget === null || !context.core.navigation.modal?.contains(event.relatedTarget) && event.target !== window.document.activeElement) {
+ closeMenu(store, 'click');
+ closeMenu(store, 'focus');
+ }
+ }
+ }
+ }
+ }
+});
+
+/***/ })
+
+},
+/******/ function(__webpack_require__) { // webpackRuntimeModules
+/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
+/******/ var __webpack_exports__ = (__webpack_exec__(932));
+/******/ }
+]); \ No newline at end of file