@nextcloud/vue
Version:
Nextcloud vue components
1,372 lines (1,371 loc) • 47.1 kB
JavaScript
import '../assets/referencePickerModal-DWMAMaU3.css';
import { createElementBlock, openBlock, mergeProps, createElementVNode, createCommentVNode, toDisplayString, resolveComponent, createVNode, withCtx, defineComponent, inject, ref, useTemplateRef, nextTick, computed, watch, onBeforeUnmount, normalizeClass, createBlock, resolveDynamicComponent, normalizeStyle, createTextVNode, unref, withKeys, withModifiers, createApp } from "vue";
import { emit } from "@nextcloud/event-bus";
import { _ as _export_sfc } from "./_plugin-vue_export-helper-1tPrXgE0.mjs";
import { I as IconClose } from "./Close-D6ngJ4t9.mjs";
import { f as renderCustomPickerElement, b as destroyCustomPickerElement, c as isCustomPickerElementRegistered, j as hasFullWidth, i as isWidgetRegistered, h as hasInteractiveView, a as renderWidget, d as destroyWidget, g as getCustomPickerElementSize } from "./customPickerElements-4pQTZUnk.mjs";
import axios from "@nextcloud/axios";
import { loadState } from "@nextcloud/initial-state";
import { imagePath, generateOcsUrl } from "@nextcloud/router";
import { r as register, D as t8, a as t, E as t45, F as t40, G as t25, j as t42, H as t32, I as t19, J as t12 } from "./_l10n-DrTiip5c.mjs";
import { l as logger } from "./logger-D3RVzcfQ.mjs";
import { N as NcEmptyContent } from "./NcEmptyContent-B8-90BSI.mjs";
import { _ as _sfc_main$a } from "./NcHighlight.vue_vue_type_script_lang-DnWQDM_2.mjs";
import { N as NcSelect } from "./NcSelect-Czzsi3P_.mjs";
import debounce from "debounce";
import { useElementSize, useIntersectionObserver } from "@vueuse/core";
import { routerKey, RouterLink } from "vue-router";
import { N as NcButton } from "./NcButton-Dc8V4Urj.mjs";
import { g as getRoute } from "./autolink-U5pBzLgI.mjs";
import { N as NcLoadingIcon } from "./NcLoadingIcon-b_ajZ_nQ.mjs";
import { _ as _sfc_main$b } from "./NcTextField.vue_vue_type_script_setup_true_lang-D1y_LfGJ.mjs";
import { I as IconDotsHorizontal } from "./NcActions-DWmvh7-Y.mjs";
import { N as NcModal } from "./NcModal-MC_HktJd.mjs";
const _sfc_main$9 = {
name: "ArrowLeftIcon",
emits: ["click"],
props: {
title: {
type: String
},
fillColor: {
type: String,
default: "currentColor"
},
size: {
type: Number,
default: 24
}
}
};
const _hoisted_1$9 = ["aria-hidden", "aria-label"];
const _hoisted_2$7 = ["fill", "width", "height"];
const _hoisted_3$6 = { d: "M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" };
const _hoisted_4$5 = { key: 0 };
function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("span", mergeProps(_ctx.$attrs, {
"aria-hidden": $props.title ? null : "true",
"aria-label": $props.title,
class: "material-design-icon arrow-left-icon",
role: "img",
onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("click", $event))
}), [
(openBlock(), createElementBlock("svg", {
fill: $props.fillColor,
class: "material-design-icon__svg",
width: $props.size,
height: $props.size,
viewBox: "0 0 24 24"
}, [
createElementVNode("path", _hoisted_3$6, [
$props.title ? (openBlock(), createElementBlock("title", _hoisted_4$5, toDisplayString($props.title), 1)) : createCommentVNode("", true)
])
], 8, _hoisted_2$7))
], 16, _hoisted_1$9);
}
const ArrowLeftIcon = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render$8]]);
const _sfc_main$8 = {
name: "NcCustomPickerElement",
props: {
/**
* The reference provider
*/
provider: {
type: Object,
required: true
}
},
emits: [
"cancel",
"submit"
],
data() {
return {
isRegistered: isCustomPickerElementRegistered(this.provider.id),
renderResult: null
};
},
mounted() {
if (this.isRegistered) {
this.renderElement();
}
},
beforeUnmount() {
if (this.isRegistered) {
destroyCustomPickerElement(this.provider.id, this.$el, this.renderResult);
}
},
methods: {
renderElement() {
if (this.$refs.domElement) {
this.$refs.domElement.innerHTML = "";
}
const renderFunctionResult = renderCustomPickerElement(this.$refs.domElement, { providerId: this.provider.id, accessible: false });
Promise.resolve(renderFunctionResult).then((result) => {
this.renderResult = result;
if (this.renderResult.object?._isVue && this.renderResult.object?.$on) {
this.renderResult.object.$on("submit", this.onSubmit);
this.renderResult.object.$on("cancel", this.onCancel);
}
this.renderResult.element.addEventListener("submit", (e) => {
this.onSubmit(e.detail);
});
this.renderResult.element.addEventListener("cancel", this.onCancel);
});
},
onSubmit(value) {
this.$emit("submit", value);
},
onCancel() {
this.$emit("cancel");
}
}
};
const _hoisted_1$8 = { ref: "domElement" };
function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("div", _hoisted_1$8, null, 512);
}
const NcCustomPickerElement = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$7], ["__scopeId", "data-v-e408867a"]]);
const _sfc_main$7 = {
name: "LinkVariantIcon",
emits: ["click"],
props: {
title: {
type: String
},
fillColor: {
type: String,
default: "currentColor"
},
size: {
type: Number,
default: 24
}
}
};
const _hoisted_1$7 = ["aria-hidden", "aria-label"];
const _hoisted_2$6 = ["fill", "width", "height"];
const _hoisted_3$5 = { d: "M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" };
const _hoisted_4$4 = { key: 0 };
function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("span", mergeProps(_ctx.$attrs, {
"aria-hidden": $props.title ? null : "true",
"aria-label": $props.title,
class: "material-design-icon link-variant-icon",
role: "img",
onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("click", $event))
}), [
(openBlock(), createElementBlock("svg", {
fill: $props.fillColor,
class: "material-design-icon__svg",
width: $props.size,
height: $props.size,
viewBox: "0 0 24 24"
}, [
createElementVNode("path", _hoisted_3$5, [
$props.title ? (openBlock(), createElementBlock("title", _hoisted_4$4, toDisplayString($props.title), 1)) : createCommentVNode("", true)
])
], 8, _hoisted_2$6))
], 16, _hoisted_1$7);
}
const LinkVariantIcon = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["render", _sfc_render$6]]);
register(t8);
const anyLinkProviderId = "any-link";
const anyLinkProvider = {
id: anyLinkProviderId,
title: t("Any link"),
order: 0,
icon_url: imagePath("core", "filetypes/link.svg")
};
window._vue_richtext_reference_providers ??= loadState("core", "reference-provider-list", []);
window._vue_richtext_reference_provider_timestamps ??= loadState("core", "reference-provider-timestamps", {});
function getProvider(providerId) {
if (providerId === anyLinkProviderId) {
return anyLinkProvider;
}
return getProviders().find((p) => p.id === providerId);
}
function getProviders() {
return window._vue_richtext_reference_providers.filter((p) => {
const keep = !!p.search_providers_ids && p.search_providers_ids.length > 0 || isCustomPickerElementRegistered(p.id);
if (!keep) {
logger.debug(`[smart picker] ${p.id} reference provider is discoverable but does not have any related search provider or custom picker component registered`);
}
return keep;
});
}
function sortProviders(providerList) {
const timestamps = window._vue_richtext_reference_provider_timestamps;
return providerList.sort((a, b) => {
return a.order === b.order ? 0 : a.order > b.order ? 1 : -1;
}).sort((a, b) => {
const ta = timestamps[a.id];
const tb = timestamps[b.id];
return ta === tb ? 0 : tb === void 0 ? -1 : ta === void 0 ? 1 : ta > tb ? -1 : 1;
});
}
function searchProvider(query, limit) {
const providers = getProviders();
const escapedQuery = query.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
const regexp = new RegExp(escapedQuery, "i");
const sortedProviders = sortProviders(providers);
const filteredSortedProviders = sortedProviders.filter((p) => {
return p.title.match(regexp);
});
const searchResult = limit ? filteredSortedProviders.slice(0, limit) : filteredSortedProviders;
if (query === "" || searchResult.length === 0) {
searchResult.push(anyLinkProvider);
}
return searchResult;
}
async function touchProvider(providerId) {
const timestamp = Math.floor(Date.now() / 1e3);
const url = generateOcsUrl("references/provider/{providerId}", { providerId });
await axios.put(url, { timestamp });
window._vue_richtext_reference_provider_timestamps[providerId] = timestamp;
}
register(t40, t45);
/*!
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
function isUrl(str) {
try {
return Boolean(new URL(str));
} catch {
return false;
}
}
const _sfc_main$6 = {
name: "NcProviderList",
components: {
NcSelect,
NcHighlight: _sfc_main$a,
NcEmptyContent,
LinkVariantIcon
},
emits: [
"selectProvider",
"submit"
],
data() {
return {
selectedProvider: null,
query: "",
multiselectPlaceholder: t("Select provider"),
providerIconAlt: t("Provider icon")
};
},
computed: {
options() {
const result = [];
if (this.query !== "" && isUrl(this.query)) {
result.push({
id: this.query,
title: this.query,
isLink: true
});
}
result.push(...searchProvider(this.query));
return result;
}
},
methods: {
focus() {
setTimeout(() => {
this.$refs["provider-select"]?.$el?.querySelector("#provider-select-input")?.focus();
}, 300);
},
onProviderSelected(p) {
if (p !== null) {
if (p.isLink) {
this.$emit("submit", p.title);
} else {
this.$emit("selectProvider", p);
}
this.selectedProvider = null;
}
},
onSearch(query) {
this.query = query;
}
}
};
const _hoisted_1$6 = { class: "provider-list" };
const _hoisted_2$5 = {
key: 0,
class: "provider"
};
const _hoisted_3$4 = {
key: 1,
class: "provider"
};
const _hoisted_4$3 = ["src", "alt"];
function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
const _component_LinkVariantIcon = resolveComponent("LinkVariantIcon");
const _component_NcHighlight = resolveComponent("NcHighlight");
const _component_NcSelect = resolveComponent("NcSelect");
const _component_NcEmptyContent = resolveComponent("NcEmptyContent");
return openBlock(), createElementBlock("div", _hoisted_1$6, [
createVNode(_component_NcSelect, {
ref: "provider-select",
modelValue: $data.selectedProvider,
"onUpdate:modelValue": [
_cache[0] || (_cache[0] = ($event) => $data.selectedProvider = $event),
$options.onProviderSelected
],
class: "provider-list--select",
"input-id": "provider-select-input",
label: "title",
placeholder: $data.multiselectPlaceholder,
options: $options.options,
"append-to-body": false,
"clear-search-on-select": true,
"clear-search-on-blur": () => false,
filterable: false,
onSearch: $options.onSearch
}, {
option: withCtx((option) => [
option.isLink ? (openBlock(), createElementBlock("div", _hoisted_2$5, [
createVNode(_component_LinkVariantIcon, {
class: "link-icon",
size: 20
}),
createElementVNode("span", null, toDisplayString(option.title), 1)
])) : (openBlock(), createElementBlock("div", _hoisted_3$4, [
createElementVNode("img", {
class: "provider-icon",
src: option.icon_url,
alt: $data.providerIconAlt
}, null, 8, _hoisted_4$3),
createVNode(_component_NcHighlight, {
class: "option-text",
search: $data.query,
text: option.title
}, null, 8, ["search", "text"])
]))
]),
_: 1
}, 8, ["modelValue", "placeholder", "options", "onSearch", "onUpdate:modelValue"]),
createVNode(_component_NcEmptyContent, { class: "provider-list--empty-content" }, {
icon: withCtx(() => [
createVNode(_component_LinkVariantIcon)
]),
_: 1
})
]);
}
const NcProviderList = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$5], ["__scopeId", "data-v-90c6aa3b"]]);
register();
const _hoisted_1$5 = ["src"];
const _hoisted_2$4 = { class: "widget-default--details" };
const _hoisted_3$3 = { class: "widget-default--name" };
const _hoisted_4$2 = { class: "widget-default--link" };
const IDLE_TIMEOUT = 3 * 60 * 1e3;
const _sfc_main$5 = /* @__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 isVisible = ref(false);
const customWidget = useTemplateRef("customWidget");
const widgetRoot = useTemplateRef("widgetRoot");
const { width } = useElementSize(widgetRoot);
useIntersectionObserver(widgetRoot, ([entry]) => {
nextTick(() => {
isVisible.value = entry.isIntersecting;
});
});
const showInteractive = ref(false);
const rendered = ref(false);
let idleTimeout = null;
const isInteractive = computed(() => {
return !props.interactiveOptIn && props.interactive || showInteractive.value;
});
const referenceHasFullWidth = computed(() => {
return hasFullWidth(props.reference.richObjectType);
});
const hasCustomWidget = computed(() => {
return isWidgetRegistered(props.reference.richObjectType);
});
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(() => {
destroyReferenceWidget();
});
function enableInteractive() {
showInteractive.value = true;
renderReferenceWidget();
}
function renderReferenceWidget() {
if (!customWidget.value) {
return;
}
if (props.reference.richObjectType === "open-graph") {
return;
}
customWidget.value.innerHTML = "";
const widget = document.createElement("div");
widget.style.width = "100%";
customWidget.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;
}
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "widgetRoot",
ref: widgetRoot,
class: normalizeClass({ "toggle-interactive": referenceHasInteractiveView.value && !isInteractive.value })
}, [
_ctx.reference && hasCustomWidget.value ? (openBlock(), createElementBlock("div", {
key: 0,
ref_key: "customWidget",
ref: customWidget,
class: normalizeClass(["widget-custom", { "full-width": referenceHasFullWidth.value }])
}, null, 2)) : !noAccess.value && _ctx.reference && _ctx.reference.openGraphObject && !hasCustomWidget.value ? (openBlock(), createBlock(resolveDynamicComponent(referenceWidgetLinkComponent.value), mergeProps({ key: 1 }, referenceWidgetLinkProps.value, {
rel: "noopener noreferrer",
class: "widget-default"
}), {
default: withCtx(() => [
_ctx.reference.openGraphObject.thumb ? (openBlock(), createElementBlock("img", {
key: 0,
class: "widget-default--image",
src: _ctx.reference.openGraphObject.thumb
}, null, 8, _hoisted_1$5)) : createCommentVNode("", true),
createElementVNode("div", _hoisted_2$4, [
createElementVNode("p", _hoisted_3$3, toDisplayString(_ctx.reference.openGraphObject.name), 1),
createElementVNode("p", {
class: "widget-default--description",
style: normalizeStyle(descriptionStyle.value)
}, toDisplayString(_ctx.reference.openGraphObject.description), 5),
createElementVNode("p", _hoisted_4$2, toDisplayString(compactLink.value), 1)
])
]),
_: 1
}, 16)) : createCommentVNode("", true),
_ctx.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$5, [["__scopeId", "data-v-8ce33442"]]);
register(t25);
const _sfc_main$4 = {
name: "NcRawLinkInput",
components: {
LinkVariantIcon,
NcEmptyContent,
NcLoadingIcon,
NcReferenceWidget,
NcTextField: _sfc_main$b
},
props: {
/**
* The reference provider
*/
provider: {
type: Object,
required: true
}
},
emits: [
"submit"
],
data() {
return {
inputValue: "",
loading: false,
reference: null,
abortController: null,
inputPlaceholder: t("Enter link")
};
},
computed: {
isLinkValid() {
return isUrl(this.inputValue);
},
debouncedUpdateReference() {
return debounce(this.updateReference, 500);
}
},
methods: {
focus() {
this.$refs["url-input"].$el.getElementsByTagName("input")[0]?.focus();
},
onSubmit(e) {
const value = e.target.value;
if (this.isLinkValid) {
this.$emit("submit", value);
}
},
onClear() {
this.inputValue = "";
this.reference = null;
},
onInput() {
this.reference = null;
if (this.abortController) {
this.abortController.abort();
}
if (this.isLinkValid) {
this.debouncedUpdateReference();
}
},
updateReference() {
this.loading = true;
this.abortController = new AbortController();
axios.get(generateOcsUrl("references/resolve", 2) + "?reference=" + encodeURIComponent(this.inputValue), {
signal: this.abortController.signal
}).then((response) => {
this.reference = response.data.ocs.data.references[this.inputValue];
}).catch((error) => {
logger.error("[NcRawLinkInput] Failed to update reference", { error });
}).then(() => {
this.loading = false;
});
}
}
};
const _hoisted_1$4 = { class: "raw-link" };
const _hoisted_2$3 = { class: "input-wrapper" };
const _hoisted_3$2 = ["src"];
function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
const _component_LinkVariantIcon = resolveComponent("LinkVariantIcon");
const _component_NcTextField = resolveComponent("NcTextField");
const _component_NcReferenceWidget = resolveComponent("NcReferenceWidget");
const _component_NcEmptyContent = resolveComponent("NcEmptyContent");
return openBlock(), createElementBlock("div", _hoisted_1$4, [
createElementVNode("div", _hoisted_2$3, [
createVNode(_component_NcTextField, {
ref: "url-input",
modelValue: $data.inputValue,
"onUpdate:modelValue": [
_cache[0] || (_cache[0] = ($event) => $data.inputValue = $event),
$options.onInput
],
"show-trailing-button": $data.inputValue !== "",
label: $data.inputPlaceholder,
onTrailingButtonClick: $options.onClear,
onKeyup: withKeys($options.onSubmit, ["enter"])
}, {
default: withCtx(() => [
$data.loading ? (openBlock(), createBlock(_component_NcLoadingIcon, {
key: 0,
size: 16
})) : (openBlock(), createBlock(_component_LinkVariantIcon, {
key: 1,
size: 16
}))
]),
_: 1
}, 8, ["modelValue", "show-trailing-button", "label", "onTrailingButtonClick", "onUpdate:modelValue", "onKeyup"])
]),
$data.reference !== null ? (openBlock(), createBlock(_component_NcReferenceWidget, {
key: 0,
class: "reference-widget",
reference: $data.reference
}, null, 8, ["reference"])) : (openBlock(), createBlock(_component_NcEmptyContent, {
key: 1,
class: "raw-link--empty-content"
}, {
icon: withCtx(() => [
$props.provider.icon_url ? (openBlock(), createElementBlock("img", {
key: 0,
class: "provider-icon",
src: $props.provider.icon_url
}, null, 8, _hoisted_3$2)) : (openBlock(), createBlock(_component_LinkVariantIcon, { key: 1 }))
]),
_: 1
}))
]);
}
const NcRawLinkInput = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$4], ["__scopeId", "data-v-a0658f2a"]]);
const _sfc_main$3 = {
name: "NcSearchResult",
components: {
NcHighlight: _sfc_main$a
},
props: {
/**
* Unified search result entry
*/
entry: {
type: Object,
required: true
},
/**
* The query that led to getting this result
* Used to highlight the entry text
*/
query: {
type: String,
required: true
}
}
};
const _hoisted_1$3 = { class: "result" };
const _hoisted_2$2 = ["src"];
const _hoisted_3$1 = { class: "result--content" };
const _hoisted_4$1 = { class: "result--content--name" };
const _hoisted_5$1 = { class: "result--content--subline" };
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
const _component_NcHighlight = resolveComponent("NcHighlight");
return openBlock(), createElementBlock("div", _hoisted_1$3, [
$props.entry.icon ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass([{ [$props.entry.icon]: true, rounded: $props.entry.rounded }, "result--icon-class"])
}, null, 2)) : (openBlock(), createElementBlock("img", {
key: 1,
class: normalizeClass(["result--image", { rounded: $props.entry.rounded }]),
src: $props.entry.thumbnailUrl
}, null, 10, _hoisted_2$2)),
createElementVNode("div", _hoisted_3$1, [
createElementVNode("span", _hoisted_4$1, [
createVNode(_component_NcHighlight, {
search: $props.query,
text: $props.entry.title
}, null, 8, ["search", "text"])
]),
createElementVNode("span", _hoisted_5$1, [
createVNode(_component_NcHighlight, {
search: $props.query,
text: $props.entry.subline
}, null, 8, ["search", "text"])
])
])
]);
}
const NcSearchResult = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__scopeId", "data-v-059edcfb"]]);
register(t32, t40, t42);
const LIMIT = 5;
const _sfc_main$2 = {
name: "NcSearch",
components: {
LinkVariantIcon,
DotsHorizontalIcon: IconDotsHorizontal,
NcEmptyContent,
NcSelect,
NcSearchResult
},
/* eslint vue/require-prop-comment: warn -- TODO: Add a proper doc block about what this props do */
props: {
/**
* The selected reference provider
*/
provider: {
type: Object,
required: true
},
showEmptyContent: {
type: Boolean,
default: true
},
/**
* Placeholder of the search
*/
searchPlaceholder: {
type: String,
default: null
}
},
emits: [
"submit"
],
data() {
return {
searchQuery: "",
selectedResult: null,
resultsBySearchProvider: {},
searching: false,
searchingMoreOf: null,
abortController: null,
noOptionsText: t("Start typing to search"),
providerIconAlt: t("Provider icon")
};
},
computed: {
mySearchPlaceholder() {
return this.searchPlaceholder || t("Search");
},
searchProviderIds() {
return this.provider.search_providers_ids;
},
options() {
if (this.searchQuery === "") {
return [];
}
const options = [];
if (isUrl(this.searchQuery)) {
options.push(this.rawLinkEntry);
}
options.push(...this.formattedSearchResults);
return options;
},
rawLinkEntry() {
return {
id: "rawLinkEntry",
resourceUrl: this.searchQuery,
isRawLink: true
};
},
formattedSearchResults() {
const results = [];
this.searchProviderIds.forEach((pid) => {
if (this.resultsBySearchProvider[pid].entries.length > 0) {
if (this.searchProviderIds.length > 1 || this.resultsBySearchProvider[pid].entries.length > 1) {
results.push({
id: "groupTitle-" + pid,
name: this.resultsBySearchProvider[pid].name,
isCustomGroupTitle: true,
providerId: pid
});
}
const providerEntriesWithId = this.resultsBySearchProvider[pid].entries.map((entry, index) => {
return {
id: "provider-" + pid + "-entry-" + index,
...entry
};
});
results.push(...providerEntriesWithId);
if (this.resultsBySearchProvider[pid].isPaginated) {
results.push({
id: "moreOf-" + pid,
name: this.resultsBySearchProvider[pid].name,
isMore: true,
providerId: pid,
isLoading: this.searchingMoreOf === pid
});
}
}
});
return results;
},
debouncedUpdateSearch() {
return debounce(this.updateSearch, 500);
}
},
mounted() {
this.resetResults();
},
beforeUnmount() {
this.cancelSearchRequests();
},
methods: {
t,
resetResults() {
const resultsBySearchProvider = {};
this.searchProviderIds.forEach((pid) => {
resultsBySearchProvider[pid] = {
entries: []
};
});
this.resultsBySearchProvider = resultsBySearchProvider;
},
focus() {
setTimeout(() => {
this.$refs["search-select"]?.$el?.querySelector("#search-select-input")?.focus();
}, 300);
},
cancelSearchRequests() {
if (this.abortController) {
this.abortController.abort();
}
},
onSearchInput(query) {
this.searchQuery = query;
this.debouncedUpdateSearch();
},
onSelectResultSelected(item) {
if (item !== null) {
if (item.resourceUrl) {
this.cancelSearchRequests();
this.$emit("submit", item.resourceUrl);
} else if (item.isMore) {
this.searchMoreOf(item.providerId).then(() => {
this.selectedResult = null;
});
}
}
},
searchMoreOf(searchProviderId) {
this.searchingMoreOf = searchProviderId;
this.cancelSearchRequests();
return this.searchProviders(searchProviderId);
},
updateSearch() {
this.cancelSearchRequests();
this.resetResults();
if (this.searchQuery === "") {
this.searching = false;
return;
}
return this.searchProviders();
},
searchProviders(searchProviderId = null) {
this.abortController = new AbortController();
this.searching = true;
const searchPromises = searchProviderId === null ? [...this.searchProviderIds].map((pid) => {
return this.searchOneProvider(pid);
}) : [this.searchOneProvider(searchProviderId, this.resultsBySearchProvider[searchProviderId]?.cursor ?? null)];
return Promise.allSettled(searchPromises).then((promises) => {
const isOneCanceled = !!promises.find((p) => {
return p.status === "rejected" && (p.reason.name === "CanceledError" || p.reason.code === "ERR_CANCELED");
});
if (!isOneCanceled) {
this.searching = false;
this.searchingMoreOf = null;
}
});
},
searchOneProvider(providerId, cursor = null) {
const url = cursor === null ? generateOcsUrl("search/providers/{providerId}/search?term={term}&limit={limit}", { providerId, term: this.searchQuery, limit: LIMIT }) : generateOcsUrl("search/providers/{providerId}/search?term={term}&limit={limit}&cursor={cursor}", { providerId, term: this.searchQuery, limit: LIMIT, cursor });
return axios.get(url, {
signal: this.abortController.signal
}).then((response) => {
const data = response.data.ocs.data;
this.resultsBySearchProvider[providerId].name = data.name;
this.resultsBySearchProvider[providerId].cursor = data.cursor;
this.resultsBySearchProvider[providerId].isPaginated = data.isPaginated;
this.resultsBySearchProvider[providerId].entries.push(...data.entries);
});
}
}
};
const _hoisted_1$2 = {
key: 0,
class: "custom-option"
};
const _hoisted_2$1 = { class: "option-text" };
const _hoisted_3 = {
key: 2,
class: "custom-option group-name"
};
const _hoisted_4 = ["src"];
const _hoisted_5 = { class: "option-text" };
const _hoisted_6 = {
key: 3,
class: "custom-option"
};
const _hoisted_7 = {
key: 0,
class: "option-simple-icon icon-loading-small"
};
const _hoisted_8 = { class: "option-text" };
const _hoisted_9 = ["alt", "src"];
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
const _component_LinkVariantIcon = resolveComponent("LinkVariantIcon");
const _component_NcSearchResult = resolveComponent("NcSearchResult");
const _component_DotsHorizontalIcon = resolveComponent("DotsHorizontalIcon");
const _component_NcSelect = resolveComponent("NcSelect");
const _component_NcEmptyContent = resolveComponent("NcEmptyContent");
return openBlock(), createElementBlock("div", {
class: normalizeClass(["smart-picker-search", { "with-empty-content": $props.showEmptyContent }])
}, [
createVNode(_component_NcSelect, {
ref: "search-select",
modelValue: $data.selectedResult,
"onUpdate:modelValue": [
_cache[0] || (_cache[0] = ($event) => $data.selectedResult = $event),
$options.onSelectResultSelected
],
class: "smart-picker-search--select",
"input-id": "search-select-input",
label: "name",
placeholder: $options.mySearchPlaceholder,
options: $options.options,
"append-to-body": false,
"close-on-select": false,
"clear-search-on-select": false,
"clear-search-on-blur": () => false,
"reset-focus-on-options-change": false,
filterable: false,
autoscroll: true,
"reset-on-options-change": false,
loading: $data.searching,
onSearch: $options.onSearchInput
}, {
option: withCtx((option) => [
option.isRawLink ? (openBlock(), createElementBlock("div", _hoisted_1$2, [
createVNode(_component_LinkVariantIcon, {
class: "option-simple-icon",
size: 20
}),
createElementVNode("span", _hoisted_2$1, toDisplayString($options.t("Raw link {options}", { options: option.resourceUrl })), 1)
])) : option.resourceUrl ? (openBlock(), createBlock(_component_NcSearchResult, {
key: 1,
class: "search-result",
entry: option,
query: $data.searchQuery
}, null, 8, ["entry", "query"])) : option.isCustomGroupTitle ? (openBlock(), createElementBlock("span", _hoisted_3, [
$props.provider.icon_url ? (openBlock(), createElementBlock("img", {
key: 0,
class: "provider-icon group-name-icon",
src: $props.provider.icon_url
}, null, 8, _hoisted_4)) : createCommentVNode("", true),
createElementVNode("span", _hoisted_5, [
createElementVNode("strong", null, toDisplayString(option.name), 1)
])
])) : option.isMore ? (openBlock(), createElementBlock("span", _hoisted_6, [
option.isLoading ? (openBlock(), createElementBlock("span", _hoisted_7)) : (openBlock(), createBlock(_component_DotsHorizontalIcon, {
key: 1,
class: "option-simple-icon",
size: 20
})),
createElementVNode("span", _hoisted_8, toDisplayString($options.t('Load more "{options}"', { options: option.name })), 1)
])) : createCommentVNode("", true)
]),
"no-options": withCtx(() => [
createTextVNode(toDisplayString($data.noOptionsText), 1)
]),
_: 1
}, 8, ["modelValue", "placeholder", "options", "loading", "onSearch", "onUpdate:modelValue"]),
$props.showEmptyContent ? (openBlock(), createBlock(_component_NcEmptyContent, {
key: 0,
class: "smart-picker-search--empty-content"
}, {
icon: withCtx(() => [
$props.provider.icon_url ? (openBlock(), createElementBlock("img", {
key: 0,
class: "provider-icon",
alt: $data.providerIconAlt,
src: $props.provider.icon_url
}, null, 8, _hoisted_9)) : (openBlock(), createBlock(_component_LinkVariantIcon, { key: 1 }))
]),
_: 1
})) : createCommentVNode("", true)
], 2);
}
const NcSearch = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-e8abf1d4"]]);
const MODES = {
providerList: 1,
standardLinkInput: 2,
searchInput: 3,
customElement: 4
};
const _sfc_main$1 = {
name: "NcReferencePicker",
components: {
NcCustomPickerElement,
NcProviderList,
NcRawLinkInput,
NcSearch
},
props: {
/**
* Provider to select on creation
* Default: null. Show the provider list
*/
initialProvider: {
type: Object,
default: () => null
},
/**
* Optional width in pixels
* Default: 100%
*/
width: {
type: Number,
default: null
},
/**
* Focus on the provider list select input on creation
* Default: true
*/
focusOnCreate: {
type: Boolean,
default: true
}
},
emits: [
"cancel",
"cancelRawLink",
"cancelSearch",
"providerSelected",
"submit"
],
data() {
return {
MODES,
selectedProvider: this.initialProvider
};
},
computed: {
mode() {
return this.selectedProvider === null ? MODES.providerList : isCustomPickerElementRegistered(this.selectedProvider.id) ? MODES.customElement : this.selectedProvider.search_providers_ids ? MODES.searchInput : MODES.standardLinkInput;
},
pickerWrapperStyle() {
return {
width: this.width ? this.width + "px" : void 0
};
}
},
mounted() {
if (this.focusOnCreate) {
if (this.initialProvider) {
setTimeout(() => {
this.$refs["url-input"]?.focus();
}, 300);
} else {
this.$nextTick(() => {
this.$refs["provider-list"]?.focus();
});
}
}
},
methods: {
onEscapePressed() {
if (this.selectedProvider !== null) {
this.deselectProvider();
} else {
this.cancelProviderSelection();
}
},
onProviderSelected(provider) {
this.selectedProvider = provider;
this.$emit("providerSelected", provider);
this.$nextTick(() => {
this.$refs["url-input"]?.focus();
});
},
cancelCustomElement() {
this.deselectProvider();
},
cancelSearch() {
this.$emit("cancelSearch", this.selectedProvider?.title);
this.deselectProvider();
},
cancelRawLinkInput() {
this.$emit("cancelRawLink", this.selectedProvider?.title);
this.deselectProvider();
},
cancelProviderSelection() {
this.$emit("cancel");
},
submitLink(link) {
if (this.selectedProvider !== null) {
touchProvider(this.selectedProvider.id);
}
this.$emit("submit", link);
this.deselectProvider();
},
deselectProvider() {
this.selectedProvider = null;
this.$emit("providerSelected", null);
setTimeout(() => {
this.$refs["provider-list"]?.focus();
}, 300);
}
}
};
const _hoisted_1$1 = {
key: 3,
class: "custom-element-wrapper"
};
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
const _component_NcProviderList = resolveComponent("NcProviderList");
const _component_NcRawLinkInput = resolveComponent("NcRawLinkInput");
const _component_NcSearch = resolveComponent("NcSearch");
const _component_NcCustomPickerElement = resolveComponent("NcCustomPickerElement");
return openBlock(), createElementBlock("div", {
class: "reference-picker",
style: normalizeStyle($options.pickerWrapperStyle),
tabindex: "-1",
onKeydown: _cache[0] || (_cache[0] = withKeys(withModifiers((...args) => $options.onEscapePressed && $options.onEscapePressed(...args), ["stop", "prevent"]), ["esc"]))
}, [
$options.mode === $data.MODES.providerList ? (openBlock(), createBlock(_component_NcProviderList, {
key: 0,
ref: "provider-list",
onSelectProvider: $options.onProviderSelected,
onSubmit: $options.submitLink,
onCancel: $options.cancelProviderSelection
}, null, 8, ["onSelectProvider", "onSubmit", "onCancel"])) : $options.mode === $data.MODES.standardLinkInput ? (openBlock(), createBlock(_component_NcRawLinkInput, {
key: 1,
ref: "url-input",
provider: $data.selectedProvider,
onSubmit: $options.submitLink,
onCancel: $options.cancelRawLinkInput
}, null, 8, ["provider", "onSubmit", "onCancel"])) : $options.mode === $data.MODES.searchInput ? (openBlock(), createBlock(_component_NcSearch, {
key: 2,
ref: "url-input",
provider: $data.selectedProvider,
onCancel: $options.cancelSearch,
onSubmit: $options.submitLink
}, null, 8, ["provider", "onCancel", "onSubmit"])) : $options.mode === $data.MODES.customElement ? (openBlock(), createElementBlock("div", _hoisted_1$1, [
createVNode(_component_NcCustomPickerElement, {
provider: $data.selectedProvider,
class: "custom-element",
onSubmit: $options.submitLink,
onCancel: $options.cancelCustomElement
}, null, 8, ["provider", "onSubmit", "onCancel"])
])) : createCommentVNode("", true)
], 36);
}
const NcReferencePicker = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1], ["__scopeId", "data-v-b193005a"]]);
register(t12, t19);
const _sfc_main = {
name: "NcReferencePickerModal",
components: {
NcReferencePicker,
NcModal,
NcButton,
ArrowLeftIcon,
CloseIcon: IconClose
},
props: {
/**
* Provider to select on creation
* Show the provider list if no initial one is provided
*/
initialProvider: {
type: Object,
default: () => null
},
/**
* Focus on the input item on create
*/
focusOnCreate: {
type: Boolean,
default: true
},
/**
* If true, add the modal content to the Viewer trap elements via the event-bus
*/
isInsideViewer: {
type: Boolean,
default: false
}
},
emits: [
"cancel",
"submit"
],
data() {
return {
show: true,
selectedProvider: this.initialProvider,
backButtonTitle: t("Back to provider selection"),
closeButtonTitle: t("Close"),
closeButtonLabel: t("Close Smart Picker")
};
},
computed: {
isProviderSelected() {
return this.selectedProvider !== null;
},
showBackButton() {
return this.initialProvider === null && this.isProviderSelected;
},
modalSize() {
return this.isProviderSelected && isCustomPickerElementRegistered(this.selectedProvider.id) ? getCustomPickerElementSize(this.selectedProvider.id) ?? "large" : "normal";
},
showModalName() {
return !this.isProviderSelected || !isCustomPickerElementRegistered(this.selectedProvider.id);
},
modalName() {
return this.isProviderSelected ? this.selectedProvider.title : t("Smart Picker");
}
},
mounted() {
if (this.isInsideViewer) {
const elem = this.$refs.modal_content;
emit("viewer:trapElements:changed", elem);
}
},
methods: {
onCancel() {
this.show = false;
this.$emit("cancel");
},
onSubmit(value) {
this.show = false;
this.$emit("submit", value);
},
onProviderSelect(provider) {
this.selectedProvider = provider;
if (provider === null && this.initialProvider !== null) {
this.onCancel();
}
},
onBackClicked() {
this.$refs.referencePicker.deselectProvider();
}
}
};
const _hoisted_1 = {
ref: "modal_content",
class: "reference-picker-modal--content"
};
const _hoisted_2 = { key: 1 };
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_ArrowLeftIcon = resolveComponent("ArrowLeftIcon");
const _component_NcButton = resolveComponent("NcButton");
const _component_CloseIcon = resolveComponent("CloseIcon");
const _component_NcReferencePicker = resolveComponent("NcReferencePicker");
const _component_NcModal = resolveComponent("NcModal");
return $data.show ? (openBlock(), createBlock(_component_NcModal, {
key: 0,
size: $options.modalSize,
class: "reference-picker-modal",
onClose: $options.onCancel
}, {
default: withCtx(() => [
createElementVNode("div", _hoisted_1, [
$options.showBackButton ? (openBlock(), createBlock(_component_NcButton, {
key: 0,
"aria-label": $data.backButtonTitle,
title: $data.backButtonTitle,
class: "back-button",
onClick: $options.onBackClicked
}, {
icon: withCtx(() => [
createVNode(_component_ArrowLeftIcon)
]),
_: 1
}, 8, ["aria-label", "title", "onClick"])) : createCommentVNode("", true),
createVNode(_component_NcButton, {
class: "close-button",
"aria-label": $data.closeButtonLabel,
title: $data.closeButtonTitle,
variant: "tertiary",
onClick: $options.onCancel
}, {
icon: withCtx(() => [
createVNode(_component_CloseIcon)
]),
_: 1
}, 8, ["aria-label", "title", "onClick"]),
$options.showModalName ? (openBlock(), createElementBlock("h2", _hoisted_2, toDisplayString($options.modalName), 1)) : createCommentVNode("", true),
createVNode(_component_NcReferencePicker, {
ref: "referencePicker",
"initial-provider": $props.initialProvider,
"focus-on-create": $props.focusOnCreate,
onProviderSelected: $options.onProviderSelect,
onSubmit: $options.onSubmit,
onCancel: $options.onCancel
}, null, 8, ["initial-provider", "focus-on-create", "onProviderSelected", "onSubmit", "onCancel"])
], 512)
]),
_: 1
}, 8, ["size", "onClose"])) : createCommentVNode("", true);
}
const NcReferencePickerModal = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-15018516"]]);
async function getLinkWithPicker(providerId, isInsideViewer) {
const modalId = "referencePickerModal";
const modalElement = document.createElement("div");
modalElement.id = modalId;
document.body.append(modalElement);
const { promise, reject, resolve } = Promise.withResolvers();
const initialProvider = providerId && getProvider(providerId) || null;
const view = createApp(NcReferencePickerModal, {
initialProvider,
isInsideViewer,
onCancel() {
view.unmount();
reject(new Error("User cancellation"));
},
onSubmit(link) {
view.unmount();
resolve(link);
}
});
view.mount(modalElement);
return promise;
}
export {
NcReferencePicker as N,
anyLinkProviderId as a,
getProvider as b,
getProviders as c,
sortProviders as d,
NcReferencePickerModal as e,
NcReferenceWidget as f,
getLinkWithPicker as g,
NcSearch as h,
searchProvider as s
};
//# sourceMappingURL=referencePickerModal-DmD3-xYB.mjs.map