UNPKG

@nextcloud/vue

Version:
1,372 lines (1,371 loc) 47.1 kB
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