@nextcloud/vue
Version:
Nextcloud vue components
265 lines (264 loc) • 8.03 kB
JavaScript
require('../assets/NcPopover-BDlL00qZ.css');
;
const Vue = require("vue");
const floatingVue = require("floating-vue");
const focusTrap = require("focus-trap");
const focusTrap$1 = require("./focusTrap-EeXFmjdI.cjs");
const _pluginVue2_normalizer = require("./_plugin-vue2_normalizer-V0q-tHlQ.cjs");
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
const Vue__default = /* @__PURE__ */ _interopDefault(Vue);
const _sfc_main$1 = Vue.defineComponent({
name: "NcPopoverTriggerProvider",
provide() {
return {
"NcPopover:trigger:shown": () => this.shown,
"NcPopover:trigger:attrs": () => this.triggerAttrs
};
},
props: {
shown: {
type: Boolean,
required: true
},
popupRole: {
type: String,
default: void 0
}
},
computed: {
triggerAttrs() {
return {
"aria-haspopup": this.popupRole,
"aria-expanded": this.shown.toString()
};
}
},
render() {
var _a, _b;
return (_b = (_a = this.$scopedSlots).default) == null ? void 0 : _b.call(_a, {
attrs: this.triggerAttrs
});
}
});
const _sfc_render$1 = null;
const _sfc_staticRenderFns$1 = null;
var __component__$1 = /* @__PURE__ */ _pluginVue2_normalizer.normalizeComponent(
_sfc_main$1,
_sfc_render$1,
_sfc_staticRenderFns$1,
false,
null,
null
);
const NcPopoverTriggerProvider = __component__$1.exports;
const _sfc_main = {
name: "NcPopover",
components: {
Dropdown: floatingVue.Dropdown,
NcPopoverTriggerProvider
},
inheritAttrs: false,
props: {
/**
* Show or hide the popper
* @see https://floating-vue.starpad.dev/api/#shown
*/
shown: {
type: Boolean,
default: false
},
/**
* Popup role
* @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup#values
*/
popupRole: {
type: String,
default: void 0,
validator: (value) => ["menu", "listbox", "tree", "grid", "dialog", "true"].includes(value)
},
popoverBaseClass: {
type: String,
default: ""
},
/**
* Enable popover focus trap
*/
focusTrap: {
type: Boolean,
default: true
},
/**
* Set element to return focus to after focus trap deactivation
*
* @type {import('focus-trap').FocusTargetValueOrFalse}
*/
setReturnFocus: {
default: void 0,
type: [HTMLElement, SVGElement, String, Boolean]
}
},
emits: [
"after-show",
"after-hide",
/**
* @see https://floating-vue.starpad.dev/api/#update-shown
*/
"update:shown"
],
data() {
return {
internalShown: this.shown
};
},
watch: {
shown(value) {
this.internalShown = value;
},
internalShown(value) {
this.$emit("update:shown", value);
}
},
mounted() {
this.checkTriggerA11y();
},
beforeDestroy() {
this.clearFocusTrap();
this.clearEscapeStopPropagation();
},
methods: {
/**
* Check if the trigger has all required a11y attributes.
* Important to check custom trigger button.
*/
checkTriggerA11y() {
var _a;
if ((_a = window.OC) == null ? void 0 : _a.debug) {
const triggerContainer = this.getPopoverTriggerContainerElement();
const requiredTriggerButton = triggerContainer.querySelector("[aria-expanded]");
if (!requiredTriggerButton) {
Vue__default.default.util.warn("It looks like you are using a custom button as a <NcPopover> or other popover #trigger. If you are not using <NcButton> as a trigger, you need to bind attrs from the #trigger slot props to your custom button. See <NcPopover> docs for an example.");
}
}
},
/**
* Remove incorrect aria-describedby attribute from the trigger.
* @see https://github.com/Akryum/floating-vue/blob/8d4f7125aae0e3ea00ba4093d6d2001ab15058f1/packages/floating-vue/src/components/Popper.ts#L734
*/
removeFloatingVueAriaDescribedBy() {
const triggerContainer = this.getPopoverTriggerContainerElement();
const triggerElements = triggerContainer.querySelectorAll("[data-popper-shown]");
for (const el of triggerElements) {
el.removeAttribute("aria-describedby");
}
},
/**
* @return {HTMLElement|undefined}
*/
getPopoverContentElement() {
var _a, _b;
return (_b = (_a = this.$refs.popover) == null ? void 0 : _a.$refs.popperContent) == null ? void 0 : _b.$el;
},
/**
* @return {HTMLElement|undefined}
*/
getPopoverTriggerContainerElement() {
return this.$refs.popover.$refs.reference;
},
/**
* Add focus trap for accessibility.
*/
async useFocusTrap() {
await this.$nextTick();
if (!this.focusTrap) {
return;
}
const el = this.getPopoverContentElement();
if (!el) {
return;
}
this.$focusTrap = focusTrap.createFocusTrap(el, {
// Prevents to lose focus using esc key
// Focus will be release when popover be hide
escapeDeactivates: false,
allowOutsideClick: true,
setReturnFocus: this.setReturnFocus,
trapStack: focusTrap$1.getTrapStack()
});
this.$focusTrap.activate();
},
/**
* Remove focus trap
*
* @param {object} options The configuration options for focusTrap
*/
clearFocusTrap(options = {}) {
var _a;
try {
(_a = this.$focusTrap) == null ? void 0 : _a.deactivate(options);
this.$focusTrap = null;
} catch (err) {
console.warn(err);
}
},
/**
* Add stopPropagation for Escape.
* It prevents global Escape handling after closing popover.
*
* Manual event handling is used here instead of v-on because there is no direct access to the node.
* Alternative - wrap <template #popover> in a div wrapper.
*/
addEscapeStopPropagation() {
const el = this.getPopoverContentElement();
el == null ? void 0 : el.addEventListener("keydown", this.stopKeydownEscapeHandler);
},
/**
* Remove stop Escape handler
*/
clearEscapeStopPropagation() {
const el = this.getPopoverContentElement();
el == null ? void 0 : el.removeEventListener("keydown", this.stopKeydownEscapeHandler);
},
/**
* @param {KeyboardEvent} event - native keydown event
*/
stopKeydownEscapeHandler(event) {
if (event.type === "keydown" && event.key === "Escape") {
event.stopPropagation();
}
},
afterShow() {
this.removeFloatingVueAriaDescribedBy();
this.$nextTick(() => {
this.$emit("after-show");
this.useFocusTrap();
this.addEscapeStopPropagation();
});
},
afterHide() {
this.$emit("after-hide");
this.clearFocusTrap();
this.clearEscapeStopPropagation();
}
}
};
var _sfc_render = function render() {
var _vm = this, _c = _vm._self._c;
return _c("Dropdown", _vm._g(_vm._b({ ref: "popover", attrs: { "distance": 10, "arrow-padding": 10, "no-auto-focus": true, "popper-class": _vm.popoverBaseClass, "shown": _vm.internalShown }, on: { "update:shown": function($event) {
_vm.internalShown = $event;
}, "apply-show": _vm.afterShow, "apply-hide": _vm.afterHide }, scopedSlots: _vm._u([{ key: "popper", fn: function() {
return [_vm._t("default")];
}, proxy: true }], null, true) }, "Dropdown", _vm.$attrs, false), _vm.$listeners), [_c("NcPopoverTriggerProvider", { attrs: { "shown": _vm.internalShown, "popup-role": _vm.popupRole }, scopedSlots: _vm._u([{ key: "default", fn: function(slotProps) {
return [_vm._t("trigger", null, null, slotProps)];
} }], null, true) })], 1);
};
var _sfc_staticRenderFns = [];
var __component__ = /* @__PURE__ */ _pluginVue2_normalizer.normalizeComponent(
_sfc_main,
_sfc_render,
_sfc_staticRenderFns,
false,
null,
null
);
const NcPopover = __component__.exports;
exports.NcPopover = NcPopover;