UNPKG

@staszek998/v-html

Version:

Simple Vue component capable of rendering the passed-in HTML string without wrapping it within a redundant HTML tag.

99 lines (85 loc) 2.66 kB
/******************************************************************************* * @copyright 2021 IDEALIGN Stanisław Gregor ******************************************************************************/ import Vue, { VNode } from 'vue' /** * Extracts the contents from the passed-in `<slot>`. * * @param slot - The `<slot>` which contents we wont to resolve. * * @author Stanisław Gregor, Vitim.us * * @see https://stackoverflow.com/a/52749314/11869579 */ const getSlotContents = (slot: VNode[]): string => { if (typeof slot === 'undefined' || !Array.isArray(slot) || slot.length === 0) { return '' } /** * The contents from all the VNodes in the `<slot>`. */ const textContents: string[] = slot.map(vnode => { if (typeof vnode.text === 'string') { return vnode.text } if (typeof vnode.elm !== 'undefined' && typeof vnode.elm.textContent === 'string') { return vnode.elm.textContent } return '' }) return textContents.join('') } /** * Simple functional component that accepts HTML string and renders it as-is. * Why? Because `v-html` directive is not always an option 😉 * * @author Stanisław Gregor, Thorsten Lünborg * * @see https://forum.vuejs.org/t/raw-html-without-a-parent-element-via-v-html/87160 * @see https://jsfiddle.net/Linusborg/mfqjk5hm/ */ export const VHTML = Vue.extend({ functional: true, name: 'VHTML', props: { /** * The HTML string to render. */ html: { type: String, required: false } }, render (h, ctx) { /** * Determines whether the `[html]` prop has been specified. */ const hasHtmlProp: boolean = typeof ctx.props.html === 'string' /** * Determines whether the default `<slot>` has been populated. */ const hasDefaultSlot: boolean = typeof ctx.slots().default !== 'undefined' && Array.isArray(ctx.slots().default) && ctx.slots().default.length > 0 /** * The HTML that is to be rendered. */ let content: string if (hasHtmlProp && hasDefaultSlot) { console.warn( 'VHTML: Received both [html] prop AND default <slot> - returning only the HTML from the <slot>.' ) content = '' } else if (hasHtmlProp) { content = ctx.props.html } else if (hasDefaultSlot) { content = getSlotContents(ctx.slots().default) } else { console.warn( 'VHTML: Expected to find the HTML string in [html] prop OR inside the default <slot>, but none was present!' ) content = '' } return new Vue({ template: `<div>${content}</div>` }).$mount()._vnode.children as VNode[] } })