UNPKG

@nextcloud/vue

Version:
293 lines (292 loc) 11.9 kB
import '../assets/NcReferenceWidget.css'; import { defineComponent, inject, ref, useTemplateRef, nextTick, computed, watch, onBeforeUnmount, openBlock, createElementBlock, normalizeClass, normalizeStyle, createElementVNode, withKeys, withModifiers, unref, createCommentVNode, createBlock, resolveDynamicComponent, mergeProps, withCtx, toDisplayString, createTextVNode } from "vue"; import { useElementSize, useIntersectionObserver } from "@vueuse/core"; import { routerKey, RouterLink } from "vue-router"; import { N as NcButton } from "./NcButton.mjs"; import { r as register, m as t26, a as t } from "./_l10n.mjs"; import { c as createElementId } from "./createElementId.mjs"; import { b as hasFullWidth, c as isResizable, i as isWidgetRegistered, h as hasInteractiveView, a as renderWidget, d as destroyWidget } from "./widgets.mjs"; import { g as getRoute } from "./autolink.mjs"; import { _ as _export_sfc } from "./_plugin-vue_export-helper.mjs"; register(t26); const _hoisted_1 = ["aria-label", "aria-valuenow", "aria-valuemin", "aria-valuemax"]; const _hoisted_2 = ["src"]; const _hoisted_3 = { class: "widget-default--details" }; const _hoisted_4 = { class: "widget-default--name" }; const _hoisted_5 = { class: "widget-default--link" }; const IDLE_TIMEOUT = 3 * 60 * 1e3; const RESIZE_KEYBOARD_STEP = 10; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "NcReferenceWidget", props: { reference: {}, interactive: { type: Boolean, default: true }, interactiveOptIn: { type: Boolean, default: false } }, setup(__props) { const props = __props; const router = inject(routerKey, null); const widgetId = `nc-reference-widget-${createElementId()}`; const isVisible = ref(false); const customWidget = useTemplateRef("customWidget"); const customWidgetContent = useTemplateRef("customWidgetContent"); const widgetRoot = useTemplateRef("widgetRoot"); const { width } = useElementSize(widgetRoot); const { height: customWidgetHeight } = useElementSize(customWidget); useIntersectionObserver(widgetRoot, ([entry]) => { nextTick(() => { isVisible.value = entry.isIntersecting; }); }); const showInteractive = ref(false); const rendered = ref(false); let idleTimeout = null; const isResizing = ref(false); const resizedHeight = ref(null); const resizeStartY = ref(0); const resizeStartHeight = ref(0); const resizeMinHeight = ref(100); const resizeMaxHeight = ref(window.innerHeight - 120); const isInteractive = computed(() => { return !props.interactiveOptIn && props.interactive || showInteractive.value; }); const referenceHasFullWidth = computed(() => { return hasFullWidth(props.reference.richObjectType); }); const isWidgetResizable = computed(() => { return isResizable(props.reference.richObjectType); }); const hasCustomWidget = computed(() => { return isWidgetRegistered(props.reference.richObjectType); }); const customWidgetStyle = computed(() => { if (!resizedHeight.value) { return null; } return { height: `${resizedHeight.value}px !important` }; }); const ariaValueNow = computed(() => { return Math.round(resizedHeight.value ?? customWidgetHeight.value); }); const referenceHasInteractiveView = computed(() => { return hasCustomWidget.value && hasInteractiveView(props.reference.richObjectType); }); const noAccess = computed(() => { return !props.reference.accessible; }); const numberOfLines = computed(() => { const lineCountOffsets = [450, 550, 650, Infinity]; return lineCountOffsets.findIndex((max) => width.value < max); }); const descriptionStyle = computed(() => { if (numberOfLines.value === 0) { return { display: "none" }; } const lineClamp = numberOfLines.value; return { lineClamp, webkitLineClamp: lineClamp }; }); const compactLink = computed(() => { const link = props.reference.openGraphObject.link; if (!link) { return ""; } if (link.startsWith("https://")) { return link.substring(8); } if (link.startsWith("http://")) { return link.substring(7); } return link; }); const route = computed(() => { return getRoute(router, props.reference.openGraphObject.link); }); const referenceWidgetLinkComponent = computed(() => { return route.value ? RouterLink : "a"; }); const referenceWidgetLinkProps = computed(() => { return route.value ? { to: route.value } : { href: props.reference.openGraphObject.link, target: "_blank" }; }); watch(isVisible, (val) => { if (!val) { idleTimeout = setTimeout(() => { if (!isVisible.value) { destroyReferenceWidget(); } }, IDLE_TIMEOUT); return; } if (idleTimeout) { clearTimeout(idleTimeout); idleTimeout = null; } if (!rendered.value) { renderReferenceWidget(); } }, { immediate: true }); onBeforeUnmount(() => { if (idleTimeout) { clearTimeout(idleTimeout); idleTimeout = null; } stopResize(); destroyReferenceWidget(); }); function enableInteractive() { showInteractive.value = true; renderReferenceWidget(); } function renderReferenceWidget() { if (!customWidgetContent.value) { return; } if (props.reference.richObjectType === "open-graph") { return; } customWidgetContent.value.innerHTML = ""; const widget = document.createElement("div"); widget.style.width = "100%"; customWidgetContent.value.appendChild(widget); nextTick(() => { renderWidget(widget, { ...props.reference, interactive: isInteractive.value }); rendered.value = true; }); } function destroyReferenceWidget() { if (rendered.value && widgetRoot.value) { destroyWidget(props.reference.richObjectType, widgetRoot.value); rendered.value = false; } } function initResizeLimits() { const widgetEl = customWidgetContent.value?.firstElementChild; if (widgetEl) { const computedStyle = window.getComputedStyle(widgetEl); const parsedMin = parseFloat(computedStyle.minHeight); const parsedMax = parseFloat(computedStyle.maxHeight); resizeMinHeight.value = parsedMin > 0 ? parsedMin : 100; resizeMaxHeight.value = Number.isFinite(parsedMax) && parsedMax > 0 ? parsedMax : window.innerHeight - 120; } else { resizeMinHeight.value = 100; resizeMaxHeight.value = window.innerHeight - 120; } } function startResize(event) { if (!isWidgetResizable.value || !customWidget.value) { return; } initResizeLimits(); isResizing.value = true; resizeStartY.value = event.clientY; resizeStartHeight.value = customWidget.value.getBoundingClientRect().height; window.addEventListener("pointermove", onResize); window.addEventListener("pointerup", stopResize); } function onResizeKeydown(delta) { if (!isWidgetResizable.value || !customWidget.value) { return; } initResizeLimits(); const currentHeight = resizedHeight.value ?? resizeStartHeight.value; const next = currentHeight + delta; resizedHeight.value = Math.min(resizeMaxHeight.value, Math.max(resizeMinHeight.value, next)); } function onResize(event) { if (!isResizing.value || !customWidget.value) { return; } const deltaY = event.clientY - resizeStartY.value; resizedHeight.value = Math.min(resizeMaxHeight.value, Math.max(resizeMinHeight.value, resizeStartHeight.value + deltaY)); } function stopResize() { if (!isResizing.value) { return; } isResizing.value = false; window.removeEventListener("pointermove", onResize); window.removeEventListener("pointerup", stopResize); } return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { ref_key: "widgetRoot", ref: widgetRoot, class: normalizeClass({ "toggle-interactive": referenceHasInteractiveView.value && !isInteractive.value }) }, [ __props.reference && hasCustomWidget.value ? (openBlock(), createElementBlock("div", { key: 0, id: widgetId, ref_key: "customWidget", ref: customWidget, class: normalizeClass(["widget-custom", { "full-width": referenceHasFullWidth.value }]), style: normalizeStyle(customWidgetStyle.value) }, [ createElementVNode("div", { ref_key: "customWidgetContent", ref: customWidgetContent, class: "widget-custom__content" }, null, 512), isWidgetResizable.value ? (openBlock(), createElementBlock("div", { key: 0, class: "widget-custom__resize-handle", role: "slider", tabindex: "0", "aria-orientation": "vertical", "aria-label": unref(t)("Widget height"), "aria-valuenow": ariaValueNow.value, "aria-valuemin": resizeMinHeight.value, "aria-valuemax": resizeMaxHeight.value, "aria-controls": widgetId, onPointerdown: withModifiers(startResize, ["stop", "prevent"]), onKeydown: [ _cache[0] || (_cache[0] = withKeys(withModifiers(($event) => onResizeKeydown(-RESIZE_KEYBOARD_STEP), ["stop", "prevent"]), ["up"])), _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => onResizeKeydown(RESIZE_KEYBOARD_STEP), ["stop", "prevent"]), ["down"])) ] }, null, 40, _hoisted_1)) : createCommentVNode("", true) ], 6)) : !noAccess.value && __props.reference && __props.reference.openGraphObject && !hasCustomWidget.value ? (openBlock(), createBlock(resolveDynamicComponent(referenceWidgetLinkComponent.value), mergeProps({ key: 1 }, referenceWidgetLinkProps.value, { rel: "noopener noreferrer", class: "widget-default" }), { default: withCtx(() => [ __props.reference.openGraphObject.thumb ? (openBlock(), createElementBlock("img", { key: 0, class: "widget-default--image", src: __props.reference.openGraphObject.thumb }, null, 8, _hoisted_2)) : createCommentVNode("", true), createElementVNode("div", _hoisted_3, [ createElementVNode("p", _hoisted_4, toDisplayString(__props.reference.openGraphObject.name), 1), createElementVNode("p", { class: "widget-default--description", style: normalizeStyle(descriptionStyle.value) }, toDisplayString(__props.reference.openGraphObject.description), 5), createElementVNode("p", _hoisted_5, toDisplayString(compactLink.value), 1) ]) ]), _: 1 }, 16)) : createCommentVNode("", true), __props.interactiveOptIn && referenceHasInteractiveView.value && !isInteractive.value ? (openBlock(), createBlock(NcButton, { key: 2, class: "toggle-interactive--button", onClick: enableInteractive }, { default: withCtx(() => [ createTextVNode(toDisplayString(unref(t)("Enable interactive view")), 1) ]), _: 1 })) : createCommentVNode("", true) ], 2); }; } }); const NcReferenceWidget = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4b144346"]]); export { NcReferenceWidget as N }; //# sourceMappingURL=NcReferenceWidget.mjs.map