@nextcloud/vue
Version:
Nextcloud vue components
173 lines (172 loc) • 5.65 kB
JavaScript
import '../assets/NcAppNavigation-CJj8AvqM.css';
import { createFocusTrap } from "focus-trap";
import { subscribe, emit, unsubscribe } from "@nextcloud/event-bus";
import { tabbable } from "tabbable";
import Vue from "vue";
import { g as getTrapStack } from "./focusTrap-Cecv_gjR.mjs";
import { l as logger } from "./logger-D3RVzcfQ.mjs";
import { useHotKey } from "../Composables/useHotKey.mjs";
import { useIsMobile } from "../Composables/useIsMobile.mjs";
import NcAppNavigationList from "../Components/NcAppNavigationList.mjs";
import { N as NcAppNavigationToggle } from "./NcAppNavigationToggle-Pr8bEpDs.mjs";
import { n as normalizeComponent } from "./_plugin-vue2_normalizer-DU4iP6Vu.mjs";
const _sfc_main = {
name: "NcAppNavigation",
components: {
NcAppNavigationList,
NcAppNavigationToggle
},
// Injected from NcContent
inject: {
setHasAppNavigation: {
default: () => () => Vue.util.warn("NcAppNavigation is not mounted inside NcContent, this is probably an error."),
from: "NcContent:setHasAppNavigation"
}
},
props: {
/**
* The aria label to describe the navigation
*/
ariaLabel: {
type: String,
default: ""
},
/**
* aria-labelledby attribute to describe the navigation
*/
ariaLabelledby: {
type: String,
default: ""
}
},
setup() {
return {
isMobile: useIsMobile()
};
},
data() {
return {
open: !this.isMobile,
focusTrap: null
};
},
watch: {
isMobile() {
this.open = !this.isMobile;
this.toggleFocusTrap();
},
open() {
this.toggleFocusTrap();
}
},
mounted() {
this.setHasAppNavigation(true);
subscribe("toggle-navigation", this.toggleNavigationByEventBus);
emit("navigation-toggled", {
open: this.open
});
this.focusTrap = createFocusTrap(this.$refs.appNavigationContainer, {
allowOutsideClick: true,
fallbackFocus: this.$refs.appNavigationContainer,
trapStack: getTrapStack(),
escapeDeactivates: false
});
this.toggleFocusTrap();
useHotKey("n", this.onKeyDown, {
prevent: true,
stop: true
});
},
unmounted() {
this.setHasAppNavigation(false);
unsubscribe("toggle-navigation", this.toggleNavigationByEventBus);
this.focusTrap.deactivate();
},
methods: {
/**
* Toggle the navigation
*
* @param {boolean} [state] set the state instead of inverting the current one
*/
async toggleNavigation(state) {
if (this.open === state) {
emit("navigation-toggled", {
open: this.open
});
return;
}
this.open = typeof state === "undefined" ? !this.open : state;
const bodyStyles = getComputedStyle(document.body);
const animationLength = parseInt(bodyStyles.getPropertyValue("--animation-quick")) || 100;
if (this.open) {
await this.$nextTick();
this.focusFirstElement();
}
setTimeout(() => {
emit("navigation-toggled", {
open: this.open
});
}, 1.5 * animationLength);
},
toggleNavigationByEventBus({ open }) {
this.toggleNavigation(open);
},
/**
* Activate focus trap if it is currently needed, otherwise deactivate
*/
toggleFocusTrap() {
if (this.isMobile && this.open) {
this.focusTrap.activate();
} else {
this.focusTrap.deactivate();
}
},
handleEsc() {
if (this.isMobile && this.open) {
this.toggleNavigation(false);
}
},
focusFirstElement() {
const element = tabbable(this.$refs.appNavigationContainer)[0];
if (element) {
element.focus();
logger.debug("Focusing first element in the navigation", { element });
}
},
onKeyDown(event) {
if (event.key === "n") {
if (!this.open) {
this.toggleNavigation(true);
return;
}
if (this.isFocusWithinNavigation()) {
this.toggleNavigation(false);
}
}
},
isFocusWithinNavigation() {
const activeElement = document.activeElement;
return this.$refs.appNavigationContainer.contains(activeElement);
}
}
};
var _sfc_render = function render() {
var _vm = this, _c = _vm._self._c;
return _c("div", { ref: "appNavigationContainer", staticClass: "app-navigation", class: { "app-navigation--close": !_vm.open } }, [_c("nav", { staticClass: "app-navigation__content", attrs: { "id": "app-navigation-vue", "aria-hidden": _vm.open ? "false" : "true", "aria-label": _vm.ariaLabel || void 0, "aria-labelledby": _vm.ariaLabelledby || void 0, "inert": !_vm.open || void 0 }, on: { "keydown": function($event) {
if (!$event.type.indexOf("key") && _vm._k($event.keyCode, "esc", 27, $event.key, ["Esc", "Escape"])) return null;
return _vm.handleEsc.apply(null, arguments);
} } }, [_c("div", { staticClass: "app-navigation__search" }, [_vm._t("search")], 2), _c("div", { staticClass: "app-navigation__body", class: { "app-navigation__body--no-list": !_vm.$scopedSlots.list } }, [_vm._t("default")], 2), _vm.$scopedSlots.list ? _c("NcAppNavigationList", { staticClass: "app-navigation__list" }, [_vm._t("list")], 2) : _vm._e(), _vm._t("footer")], 2), _c("NcAppNavigationToggle", { attrs: { "open": _vm.open }, on: { "update:open": _vm.toggleNavigation } })], 1);
};
var _sfc_staticRenderFns = [];
var __component__ = /* @__PURE__ */ normalizeComponent(
_sfc_main,
_sfc_render,
_sfc_staticRenderFns,
false,
null,
"1329aac9"
);
const NcAppNavigation = __component__.exports;
export {
NcAppNavigation as N
};