UNPKG

@nextcloud/vue

Version:
421 lines (420 loc) 13 kB
import '../assets/NcRichText-zt8ALFHi.css'; import { ref } from "vue"; import { N as NcReferenceWidget } from "./referencePickerModal-CGG57Dy_.mjs"; import { U as URL_PATTERN, p as parseUrl, r as remarkAutolink, g as getRoute } from "./autolink-cbuFALXr.mjs"; import axios from "@nextcloud/axios"; import { getSharingToken } from "@nextcloud/sharing/public"; import { getCurrentUser } from "@nextcloud/auth"; import { generateOcsUrl } from "@nextcloud/router"; import { n as normalizeComponent } from "./_plugin-vue2_normalizer-DU4iP6Vu.mjs"; import { N as NcCheckboxRadioSwitch } from "./NcCheckboxRadioSwitch-CCuKA55c.mjs"; import { visit, SKIP } from "unist-util-visit"; import { u } from "unist-builder"; import { G as GenRandomId } from "./GenRandomId-CMooMQt0.mjs"; import { unified } from "unified"; import remarkParse from "remark-parse"; import remarkGfm from "remark-gfm"; import breaks from "remark-breaks"; import remark2rehype from "remark-rehype"; import rehype2react from "rehype-react"; import rehypeExternalLinks from "rehype-external-links"; import { RouterLink } from "vue-router"; const _sfc_main$1 = { name: "NcReferenceList", components: { NcReferenceWidget }, props: { text: { type: String, default: "" }, referenceData: { type: Object, default: null }, limit: { type: Number, default: 1 }, displayFallback: { type: Boolean, default: false }, interactive: { type: Boolean, default: true }, interactiveOptIn: { type: Boolean, default: false } }, data() { return { references: null, loading: true }; }, computed: { isVisible() { return this.loading || this.displayedReferences.length !== 0; }, values() { if (this.referenceData) { return this.referenceData; } if (this.displayFallback && !this.loading && !this.references) { return [this.fallbackReference]; } return this.references ? Object.values(this.references) : []; }, firstReference() { return this.values[0] ?? null; }, displayedReferences() { return this.values.slice(0, this.limit); }, fallbackReference() { return { accessible: true, openGraphObject: { id: this.text, link: this.text, name: this.text }, richObjectType: "open-graph" }; } }, watch: { text: "fetch" }, mounted() { this.fetch(); }, methods: { fetch() { this.loading = true; if (this.referenceData) { this.references = null; this.loading = false; return; } if (!new RegExp(URL_PATTERN).exec(this.text)) { this.references = null; this.loading = false; return; } this.resolve().then((response) => { this.references = response.data.ocs.data.references; this.loading = false; this.$emit("loaded"); }).catch((error) => { console.error("Failed to extract references", error); this.loading = false; this.$emit("loaded"); }); }, resolve() { const match = new RegExp(URL_PATTERN).exec(this.text.trim()); const isPublic = getCurrentUser() === null; if (this.limit === 1 && match) { return isPublic ? axios.get(generateOcsUrl("references/resolvePublic") + `?reference=${encodeURIComponent(match[0])}&sharingToken=${getSharingToken()}`) : axios.get(generateOcsUrl("references/resolve") + `?reference=${encodeURIComponent(match[0])}`); } return isPublic ? axios.post(generateOcsUrl("references/extractPublic"), { text: this.text, resolve: true, limit: this.limit, sharingToken: getSharingToken() }) : axios.post(generateOcsUrl("references/extract"), { text: this.text, resolve: true, limit: this.limit }); } } }; var _sfc_render$1 = function render() { var _vm = this, _c = _vm._self._c; return _vm.isVisible ? _c("div", { staticClass: "widgets--list", class: { "icon-loading": _vm.loading } }, _vm._l(_vm.displayedReferences, function(reference) { return _c("NcReferenceWidget", { key: reference?.openGraphObject?.id, attrs: { "reference": reference, "interactive": _vm.interactive, "interactive-opt-in": _vm.interactiveOptIn } }); }), 1) : _vm._e(); }; var _sfc_staticRenderFns$1 = []; var __component__$1 = /* @__PURE__ */ normalizeComponent( _sfc_main$1, _sfc_render$1, _sfc_staticRenderFns$1, false, null, "2677cf0e" ); const NcReferenceList = __component__$1.exports; const remarkPlaceholder = function() { return function(ast) { visit(ast, (node) => node.type === "text", visitor); function visitor(node, index, parent) { const placeholders = node.value.split(/(\{[a-z\-_.0-9]+\})/ig).map((entry, index2, list) => { const matches = entry.match(/^\{([a-z\-_.0-9]+)\}$/i); if (!matches) { return u("text", entry); } const [, component] = matches; return u("element", { tagName: `#${component}` }); }); parent.children.splice(index, 1, ...placeholders); } }; }; const prepareTextNode = ({ h, context }, text) => { if (context.autolink) { text = parseUrl(text); } if (Array.isArray(text)) { return text.map((entry) => { if (typeof entry === "string") { return entry; } const { component, props } = entry; const componentClass = component.name === "NcLink" ? void 0 : "rich-text--component"; return h(component, { props, class: componentClass }); }); } return text; }; const remarkUnescape = function() { return function(tree) { visit( tree, (node) => ["text", "code", "inlineCode"].includes(node.type), (node, index, parent) => { parent.children.splice(index, 1, { ...node, value: node.value.replace(/&lt;/gmi, "<").replace(/&gt;/gmi, ">") }); return [SKIP, index + 1]; } ); }; }; const rehypeHighlight = ref(null); async function importRehypeHighlightLibrary() { const module = await import("rehype-highlight"); rehypeHighlight.value = module.default; } const _sfc_main = { name: "NcRichText", components: { NcReferenceList }, props: { text: { type: String, default: "" }, arguments: { type: Object, default: () => { return {}; } }, referenceLimit: { type: Number, default: 0 }, referenceInteractive: { type: Boolean, default: true }, referenceInteractiveOptIn: { type: Boolean, default: false }, /** Provide data upfront to avoid extra http request */ references: { type: Object, default: null }, /** Provide basic Markdown syntax */ useMarkdown: { type: Boolean, default: false }, /** Provide GitHub Flavored Markdown syntax */ useExtendedMarkdown: { type: Boolean, default: false }, /** Provide event from rendered markdown inputs */ interactive: { type: Boolean, default: false }, autolink: { type: Boolean, default: true } }, emits: ["interact:todo"], data() { return { parentId: GenRandomId(5) }; }, methods: { renderPlaintext(h) { const context = this; const placeholders = this.text.split(/(\{[a-z\-_.0-9]+\})/ig).map(function(entry, index, list) { const matches = entry.match(/^\{([a-z\-_.0-9]+)\}$/i); if (!matches) { return prepareTextNode({ h, context }, entry); } const argumentId = matches[1]; const argument = context.arguments[argumentId]; if (typeof argument === "object") { const { component, props } = argument; return h(component, { props, class: "rich-text--component" }); } if (argument) { return h("span", { class: "rich-text--fallback" }, argument); } return entry; }); return h("div", { class: "rich-text--wrapper" }, [ h("div", {}, placeholders.flat()), this.referenceLimit > 0 ? h("div", { class: "rich-text--reference-widget" }, [ h(NcReferenceList, { props: { text: this.text, referenceData: this.references, interactive: this.referenceInteractive, interactiveOptIn: this.referenceInteractiveOptIn } }) ]) : null ]); }, renderMarkdown(h) { const renderedMarkdown = unified().use(remarkParse).use(remarkAutolink, { autolink: this.autolink, useMarkdown: this.useMarkdown, useExtendedMarkdown: this.useExtendedMarkdown }).use(remarkUnescape).use(this.useExtendedMarkdown ? remarkGfm : void 0).use(breaks).use(remark2rehype, { handlers: { component(toHast, node) { return toHast(node, node.component, { value: node.value }); } } }).use(this.useExtendedMarkdown ? rehypeHighlight.value : void 0).use(remarkPlaceholder).use(rehypeExternalLinks, { target: "_blank", rel: ["noopener noreferrer"] }).use(rehype2react, { createElement: (tag, attrs, children) => { if (!tag.startsWith("#")) { if (this.useExtendedMarkdown) { if (tag === "code" && !rehypeHighlight.value && attrs?.attrs?.class?.includes("language")) { importRehypeHighlightLibrary(); } let nestedNode = null; if (tag === "li" && Array.isArray(children) && children[0].tag === "input" && children[0].data.attrs.type === "checkbox") { const [inputNode, ...labelParts] = children; const nestedNodeIndex = labelParts.findIndex((child) => ["ul", "ol", "li", "blockquote", "pre"].includes(child.tag)); if (nestedNodeIndex !== -1) { nestedNode = labelParts[nestedNodeIndex]; labelParts.splice(nestedNodeIndex); } const id = this.parentId + "-markdown-input-" + GenRandomId(5); const inputComponent = h(NcCheckboxRadioSwitch, { attrs: { ...inputNode.data.attrs, id, disabled: !this.interactive }, on: { "update:checked": () => { this.$emit("interact:todo", id); } } }, labelParts); return h(tag, attrs, [inputComponent, nestedNode]); } } if (tag === "a") { const route = getRoute(this.$router, attrs.attrs.href); if (route) { delete attrs.attrs.href; delete attrs.attrs.target; return h(RouterLink, { ...attrs, props: { to: route } }, children); } } return h(tag, attrs, children); } const placeholder = this.arguments[tag.slice(1)]; if (!placeholder) { return h("span", { ...{ attrs }, ...{ class: "rich-text--fallback" } }, [`{${tag.slice(1)}}`]); } if (!placeholder.component) { return h("span", attrs, [placeholder]); } return h( placeholder.component, { attrs, props: placeholder.props, class: "rich-text--component" }, children ); }, prefix: false }).processSync( this.text.replace(/<[^>]+>/g, (match) => match.replace(/</g, "&lt;")).replace(/&gt;/gmi, ">") ).result; return h("div", { class: "rich-text--wrapper rich-text--wrapper-markdown" }, [ renderedMarkdown, this.referenceLimit > 0 ? h("div", { class: "rich-text--reference-widget" }, [ h(NcReferenceList, { props: { text: this.text, referenceData: this.references, interactive: this.referenceInteractive, interactiveOptIn: this.referenceInteractiveOptIn } }) ]) : null ]); } }, render(h) { return this.useMarkdown || this.useExtendedMarkdown ? this.renderMarkdown(h) : this.renderPlaintext(h); } }; const _sfc_render = null; const _sfc_staticRenderFns = null; var __component__ = /* @__PURE__ */ normalizeComponent( _sfc_main, _sfc_render, _sfc_staticRenderFns, false, null, "1de82f09" ); const NcRichText = __component__.exports; export { NcRichText as N, NcReferenceList as a };