@nextcloud/vue
Version:
Nextcloud vue components
423 lines (422 loc) • 16.9 kB
JavaScript
import '../assets/NcModal-DLWvQ8EA.css';
import { getCurrentInstance, warn, defineComponent, mergeModels, useCssVars, computed, useModel, useTemplateRef, onMounted, onUnmounted, watch, toRef, ref, watchEffect, nextTick, createBlock, openBlock, Teleport, createVNode, Transition, withCtx, withDirectives, createElementVNode, mergeProps, unref, createElementBlock, createCommentVNode, toDisplayString, normalizeClass, renderSlot, withModifiers, vShow } from "vue";
import { C as mdiPause, D as mdiPlay, b as mdiClose, x as mdiChevronLeft, c as mdiChevronRight } from "./mdi-XFJRiRqJ.mjs";
import { useIntervalFn, useSwipe } from "@vueuse/core";
import { createFocusTrap } from "focus-trap";
import { N as NcActions } from "./NcActions-DWmvh7-Y.mjs";
import { N as NcButton } from "./NcButton-Dc8V4Urj.mjs";
import { N as NcIconSvgWrapper } from "./NcIconSvgWrapper-BvLanNaW.mjs";
import "../composables/useFormatDateTime/index.mjs";
import { useHotKey } from "../composables/useHotKey/index.mjs";
import "../composables/useIsDarkTheme/index.mjs";
import "../composables/useIsFullscreen/index.mjs";
import "../composables/useIsMobile/index.mjs";
import { r as register, a as t } from "./_l10n-DrTiip5c.mjs";
import { c as createElementId } from "./createElementId-DhjFt1I9.mjs";
import { g as getTrapStack } from "./focusTrap-HJQ4pqHV.mjs";
import { i as isRtl } from "./rtl-v0UOPAM7.mjs";
import { _ as _export_sfc } from "./_plugin-vue_export-helper-1tPrXgE0.mjs";
/*!
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
function getSameNodeParent(instance) {
if (!instance.parent) {
return null;
}
if ("vapor" in instance || "vapor" in instance.parent) {
warn("Vapor instances are not supported in useScopeIdAttrs :(");
return null;
}
if (instance.parent.subTree !== instance.vnode) {
return null;
}
return instance.parent;
}
function getSameNodeAncestors(instance) {
const ancestors = [instance];
let parent = getSameNodeParent(instance);
while (parent) {
ancestors.push(parent);
parent = getSameNodeParent(parent);
}
return ancestors;
}
function useScopeIdAttrs() {
const instance = getCurrentInstance();
if (!instance) {
throw new Error("useScopeId must be called within a setup context");
}
const sameNodeAncestors = getSameNodeAncestors(instance);
const scopeIds = sameNodeAncestors.map((instance2) => instance2.vnode.scopeId).filter(Boolean);
const scopeIdAttrs = Object.fromEntries(scopeIds.map((scopeId) => [scopeId, ""]));
return scopeIdAttrs;
}
register();
const _hoisted_1 = ["aria-labelledby", "aria-describedby"];
const _hoisted_2 = ["data-theme-light", "data-theme-dark"];
const _hoisted_3 = ["id"];
const _hoisted_4 = { class: "icons-menu" };
const _hoisted_5 = ["title"];
const _hoisted_6 = ["id"];
const _hoisted_7 = { class: "modal-container__content" };
const _sfc_main = /* @__PURE__ */ defineComponent({
...{ inheritAttrs: false },
__name: "NcModal",
props: /* @__PURE__ */ mergeModels({
name: { default: "" },
hasPrevious: { type: Boolean },
hasNext: { type: Boolean },
outTransition: { type: Boolean },
enableSlideshow: { type: Boolean },
slideshowDelay: { default: 5e3 },
slideshowPaused: { type: Boolean },
disableSwipe: { type: Boolean },
spreadNavigation: { type: Boolean },
size: { default: "normal" },
noClose: { type: Boolean },
closeOnClickOutside: { type: Boolean },
dark: { type: Boolean },
lightBackdrop: { type: Boolean },
container: { default: "body" },
closeButtonOutside: { type: Boolean },
additionalTrapElements: { default: () => [] },
inlineActions: { default: 0 },
labelId: { default: "" },
setReturnFocus: { default: void 0 }
}, {
"show": { type: Boolean, ...{ default: true } },
"showModifiers": {}
}),
emits: /* @__PURE__ */ mergeModels(["next", "previous", "close", "update:show"], ["update:show"]),
setup(__props, { emit: __emit }) {
useCssVars((_ctx) => ({
"3caa6a4b": cssSlideshowDelay.value
}));
const showModal = useModel(__props, "show");
const props = __props;
const emit = __emit;
const scopeIdAttrs = useScopeIdAttrs();
const modalId = createElementId();
const maskElement = useTemplateRef("mask");
let focusTrap;
onMounted(() => useFocusTrap());
onUnmounted(() => clearFocusTrap());
watch(() => props.additionalTrapElements, (elements) => {
if (focusTrap) {
focusTrap.updateContainerElements([maskElement.value, ...elements]);
}
});
const {
isActive: isPlaying,
pause: stopSlideshow,
resume: startSlideshow
} = useIntervalFn(nextSlide, toRef(() => props.slideshowDelay), { immediate: false });
const animationKey = ref(0);
const runSlideshow = ref(false);
watchEffect(() => {
if (runSlideshow.value && !props.slideshowPaused) {
startSlideshow();
} else if (isPlaying.value) {
stopSlideshow();
}
});
const cssSlideshowDelay = computed(() => `${props.slideshowDelay}ms`);
const { stop: stopSwipe } = useSwipe(maskElement, {
onSwipeEnd: handleSwipe
});
onUnmounted(stopSwipe);
useHotKey("Escape", () => {
const trapStack = getTrapStack();
if (trapStack.at(-1) === focusTrap) {
close();
}
}, { allowInModal: true });
useHotKey(["ArrowLeft", "ArrowRight"], (event) => {
if (document.activeElement && !maskElement.value.contains(document.activeElement)) {
return;
}
if (event.key === "ArrowLeft" !== isRtl) {
previousSlide();
} else {
nextSlide();
}
}, { allowInModal: true });
onMounted(() => {
if (!props.name && !props.labelId) {
warn("[NcModal] You need either set the name or set a `labelId` for accessibility.");
}
});
function nextSlide(event) {
if (!props.hasNext) {
runSlideshow.value = false;
return;
}
if (event && isPlaying.value) {
restartSlideshow();
}
emit("next", event);
}
function previousSlide(event) {
if (!props.hasPrevious) {
return;
}
if (event && isPlaying.value) {
restartSlideshow();
}
emit("previous", event);
}
function handleSwipe(e, direction) {
if (!props.disableSwipe) {
if (direction !== "left" && direction !== "right") {
return;
}
if (direction === "left" !== isRtl) {
nextSlide(e);
} else {
previousSlide(e);
}
}
}
function restartSlideshow() {
stopSlideshow();
startSlideshow();
animationKey.value++;
}
function close(event) {
if (props.noClose) {
return;
}
showModal.value = false;
setTimeout(() => {
emit("close", event);
}, 300);
}
function handleClickModalWrapper(event) {
if (props.closeOnClickOutside) {
close(event);
}
}
async function useFocusTrap() {
if (!showModal.value || focusTrap) {
return;
}
await nextTick();
const options = {
allowOutsideClick: true,
fallbackFocus: maskElement.value,
trapStack: getTrapStack(),
// Esc can be used without stop in content or additionalTrapElements where it should not deactivate modal's focus trap.
// Focus trap is deactivated on modal close anyway.
escapeDeactivates: false,
setReturnFocus: props.setReturnFocus
};
focusTrap = createFocusTrap([maskElement.value, ...props.additionalTrapElements], options);
focusTrap.activate();
}
function clearFocusTrap() {
if (!focusTrap) {
return;
}
focusTrap?.deactivate();
focusTrap = void 0;
}
return (_ctx, _cache) => {
return openBlock(), createBlock(Teleport, {
disabled: _ctx.container === null,
to: _ctx.container
}, [
createVNode(Transition, {
name: "fade",
appear: "",
onAfterEnter: useFocusTrap,
onBeforeLeave: clearFocusTrap
}, {
default: withCtx(() => [
withDirectives(createElementVNode("div", mergeProps({ ..._ctx.$attrs, ...unref(scopeIdAttrs) }, {
ref: "mask",
class: ["modal-mask", {
"modal-mask--opaque": _ctx.dark || _ctx.closeButtonOutside || _ctx.hasPrevious || _ctx.hasNext,
"modal-mask--light": _ctx.lightBackdrop
}],
role: "dialog",
"aria-modal": "true",
"aria-labelledby": _ctx.labelId || `modal-name-${unref(modalId)}`,
"aria-describedby": "modal-description-" + unref(modalId),
tabindex: "-1"
}), [
createVNode(Transition, {
name: "fade-visibility",
appear: ""
}, {
default: withCtx(() => [
createElementVNode("div", {
class: "modal-header",
"data-theme-light": _ctx.lightBackdrop,
"data-theme-dark": !_ctx.lightBackdrop
}, [
_ctx.name.trim() !== "" ? (openBlock(), createElementBlock("h2", {
key: 0,
id: "modal-name-" + unref(modalId),
class: "modal-header__name"
}, toDisplayString(_ctx.name), 9, _hoisted_3)) : createCommentVNode("", true),
createElementVNode("div", _hoisted_4, [
_ctx.hasNext && _ctx.enableSlideshow ? (openBlock(), createElementBlock("button", {
key: 0,
class: normalizeClass(["play-pause-icons", { "play-pause-icons--paused": _ctx.slideshowPaused }]),
title: unref(isPlaying) ? unref(t)("Pause slideshow") : unref(t)("Start slideshow"),
type: "button",
onClick: _cache[0] || (_cache[0] = ($event) => runSlideshow.value = !runSlideshow.value)
}, [
createVNode(NcIconSvgWrapper, {
class: "play-pause-icons__icon",
inline: "",
name: unref(isPlaying) ? unref(t)("Pause slideshow") : unref(t)("Start slideshow"),
path: unref(isPlaying) ? unref(mdiPause) : unref(mdiPlay)
}, null, 8, ["name", "path"]),
unref(isPlaying) ? (openBlock(), createElementBlock("svg", {
key: `${unref(modalId)}-animation-${animationKey.value}`,
class: "progress-ring",
height: "50",
width: "50"
}, [..._cache[1] || (_cache[1] = [
createElementVNode("circle", {
class: "progress-ring__circle",
stroke: "white",
"stroke-width": "2",
fill: "transparent",
r: "15",
cx: "25",
cy: "25"
}, null, -1)
])])) : createCommentVNode("", true)
], 10, _hoisted_5)) : createCommentVNode("", true),
createVNode(NcActions, {
class: "header-actions",
inline: _ctx.inlineActions
}, {
default: withCtx(() => [
renderSlot(_ctx.$slots, "actions", {}, void 0, true)
]),
_: 3
}, 8, ["inline"]),
!_ctx.noClose && _ctx.closeButtonOutside ? (openBlock(), createBlock(NcButton, {
key: 1,
"aria-label": unref(t)("Close"),
class: "header-close",
variant: "tertiary",
onClick: close
}, {
icon: withCtx(() => [
createVNode(NcIconSvgWrapper, { path: unref(mdiClose) }, null, 8, ["path"])
]),
_: 1
}, 8, ["aria-label"])) : createCommentVNode("", true)
])
], 8, _hoisted_2)
]),
_: 3
}),
createVNode(Transition, {
name: `modal-${_ctx.outTransition ? "out" : "in"}`,
appear: ""
}, {
default: withCtx(() => [
withDirectives(createElementVNode("div", {
class: normalizeClass(["modal-wrapper", [
`modal-wrapper--${_ctx.size}`,
{ "modal-wrapper--spread-navigation": _ctx.spreadNavigation }
]]),
onMousedown: withModifiers(handleClickModalWrapper, ["self"])
}, [
createVNode(Transition, {
name: "fade-visibility",
appear: ""
}, {
default: withCtx(() => [
withDirectives(createVNode(NcButton, {
"aria-label": unref(t)("Previous"),
class: "prev",
variant: "tertiary-no-background",
onClick: previousSlide
}, {
icon: withCtx(() => [
createVNode(NcIconSvgWrapper, {
directional: "",
path: unref(mdiChevronLeft),
size: 40
}, null, 8, ["path"])
]),
_: 1
}, 8, ["aria-label"]), [
[vShow, _ctx.hasPrevious]
])
]),
_: 1
}),
createElementVNode("div", {
id: "modal-description-" + unref(modalId),
class: "modal-container"
}, [
createElementVNode("div", _hoisted_7, [
renderSlot(_ctx.$slots, "default", {}, void 0, true)
]),
!_ctx.noClose && !_ctx.closeButtonOutside ? (openBlock(), createBlock(NcButton, {
key: 0,
"aria-label": unref(t)("Close"),
class: "modal-container__close",
variant: "tertiary",
onClick: close
}, {
icon: withCtx(() => [
createVNode(NcIconSvgWrapper, { path: unref(mdiClose) }, null, 8, ["path"])
]),
_: 1
}, 8, ["aria-label"])) : createCommentVNode("", true)
], 8, _hoisted_6),
createVNode(Transition, {
name: "fade-visibility",
appear: ""
}, {
default: withCtx(() => [
withDirectives(createVNode(NcButton, {
"aria-label": unref(t)("Next"),
class: "next",
variant: "tertiary-no-background",
onClick: nextSlide
}, {
icon: withCtx(() => [
createVNode(NcIconSvgWrapper, {
directional: "",
path: unref(mdiChevronRight),
size: 40
}, null, 8, ["path"])
]),
_: 1
}, 8, ["aria-label"]), [
[vShow, _ctx.hasNext]
])
]),
_: 1
})
], 34), [
[vShow, showModal.value]
])
]),
_: 3
}, 8, ["name"])
], 16, _hoisted_1), [
[vShow, showModal.value]
])
]),
_: 3
})
], 8, ["disabled", "to"]);
};
}
});
const NcModal = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-1639aad0"]]);
export {
NcModal as N
};
//# sourceMappingURL=NcModal-MC_HktJd.mjs.map