portal-vue
Version:
> A Portal Component for Vue 3, to render DOM outside of a component, anywhere in the document.
1 lines • 15.7 kB
Source Map (JSON)
{"version":3,"file":"portal-vue.mjs","sources":["../src/composables/wormhole.ts","../src/utils/index.ts","../src/components/portal.ts","../src/components/portal-target.ts","../src/wormhole.ts","../src/utils/mountPortalTarget.ts","../src/index.ts"],"sourcesContent":["import { type InjectionKey, inject, provide } from 'vue'\nimport type { Wormhole } from '../types'\n\nexport const wormholeSymbol = Symbol('wormhole') as InjectionKey<Wormhole>\n\nexport function useWormhole() {\n const wh = inject(wormholeSymbol)\n\n if (!wh) {\n throw new Error(`\n [portal-vue]: Necessary Injection not found. Make sur you installed the plugin properly.`)\n }\n\n return wh\n}\n\nexport function provideWormhole(wormhole: Wormhole) {\n provide(wormholeSymbol, wormhole)\n}\n","import { watch } from 'vue'\n\nexport const inBrowser = typeof window !== 'undefined'\n\nexport const __DEV__ = __NODE_ENV__ === 'development'\n\nexport function warn(msg: string) {\n console.log('[portal-vue]: ' + msg)\n}\n\nexport function assertStaticProps(\n component: string,\n props: Record<string, any>,\n propNames: string[]\n) {\n propNames.forEach(\n (name) => {\n watch(\n () => props[name],\n () => {\n warn(\n `Prop '${name}' of component ${component} is static, but was dynamically changed by the parent.\n This change will not have any effect.`\n )\n }\n )\n },\n { flush: 'post' }\n )\n}\n\nexport function stableSort<T>(array: T[], compareFn: Function) {\n return array\n .map((v: T, idx: number) => {\n return [idx, v] as [number, T]\n })\n .sort(function (a, b) {\n return compareFn(a[1], b[1]) || a[0] - b[0]\n })\n .map((c) => c[1])\n}\n","import {\n type Slots,\n defineComponent,\n onBeforeUnmount,\n onMounted,\n onUpdated,\n watch,\n} from 'vue'\nimport { useWormhole } from '../composables/wormhole'\nimport type { Name, PortalProps } from '../types'\nimport { __DEV__, assertStaticProps, inBrowser } from '../utils'\n\nexport function usePortal(props: PortalProps, slots: Slots) {\n const wormhole = useWormhole()\n\n function sendUpdate() {\n if (!inBrowser) return\n const { to, name: from, order } = props\n if (slots.default) {\n wormhole.open({\n to,\n from: from!,\n order,\n content: slots.default,\n })\n } else {\n clear()\n }\n }\n\n function clear(target?: Name) {\n wormhole.close({\n to: target ?? props.to,\n from: props.name,\n })\n }\n onMounted(() => {\n if (!props.disabled) {\n sendUpdate()\n }\n })\n\n onUpdated(() => {\n if (props.disabled) {\n clear()\n } else {\n sendUpdate()\n }\n })\n\n onBeforeUnmount(() => {\n clear()\n })\n\n watch(\n () => props.to,\n (newTo, oldTo) => {\n if (props.disabled) return\n if (oldTo && oldTo !== newTo) {\n clear(oldTo)\n }\n sendUpdate()\n }\n )\n}\n\nexport default defineComponent({\n compatConfig: { MODE: 3 },\n name: 'portal',\n props: {\n disabled: { type: Boolean },\n name: { type: [String, Symbol], default: () => Symbol() },\n order: { type: Number },\n slotProps: { type: Object, default: () => ({}) },\n to: {\n type: String,\n default: () => String(Math.round(Math.random() * 10000000)),\n },\n },\n setup(props, { slots }) {\n __DEV__ && assertStaticProps('Portal', props, ['order', 'name'])\n usePortal(props, slots)\n\n return () => {\n if (props.disabled && slots.default) {\n return slots.default(props.slotProps)\n } else {\n return null\n }\n }\n },\n})\n","import {\n type FunctionalComponent,\n type VNode,\n computed,\n defineComponent,\n h,\n watch,\n} from 'vue'\nimport { useWormhole } from '../composables/wormhole'\n\nconst PortalTargetContent: FunctionalComponent = (_, { slots }) => {\n return slots.default?.()\n}\n\nexport default defineComponent({\n compatConfig: { MODE: 3 },\n name: 'portalTarget',\n props: {\n multiple: { type: Boolean, default: false },\n name: { type: String, required: true },\n slotProps: { type: Object, default: () => ({}) },\n },\n emits: ['change'],\n setup(props, { emit, slots }) {\n const wormhole = useWormhole()\n\n const slotVnodes = computed<{ vnodes: VNode[]; vnodesFn: () => VNode[] }>(\n () => {\n const transports = wormhole.getContentForTarget(\n props.name,\n props.multiple\n )\n const wrapperSlot = slots.wrapper\n const rawNodes = transports.map((t) => t.content(props.slotProps))\n const vnodes = wrapperSlot\n ? rawNodes.flatMap((nodes) =>\n nodes.length ? wrapperSlot(nodes) : []\n )\n : rawNodes.flat(1)\n return {\n vnodes,\n vnodesFn: () => vnodes, // just to make Vue happy. raw vnodes in a slot give a DEV warning\n }\n }\n )\n\n watch(\n slotVnodes,\n ({ vnodes }) => {\n const hasContent = vnodes.length > 0\n const content = wormhole.transports.get(props.name)\n const sources = content ? [...content.keys()] : []\n emit('change', { hasContent, sources })\n },\n { flush: 'post' }\n )\n return () => {\n const hasContent = !!slotVnodes.value.vnodes.length\n if (hasContent) {\n return [\n // this node is a necessary hack to force Vue to change the scoped-styles boundary\n // TODO: find less hacky solution\n h('div', {\n style: 'display: none',\n key: '__portal-vue-hacky-scoped-slot-repair__',\n }),\n // we wrap the slot content in a functional component\n // so that transitions in the slot can properly determine first render\n // for `appear` behavior to work properly\n h(PortalTargetContent, slotVnodes.value.vnodesFn),\n ]\n } else {\n return slots.default?.()\n }\n }\n },\n})\n","import { reactive, readonly } from 'vue'\nimport type {\n Name,\n Transport,\n TransportCloser,\n TransportInput,\n TransportsHub,\n Wormhole,\n} from './types'\nimport { inBrowser, stableSort } from './utils'\n\nexport function createWormhole(asReadonly = true): Wormhole {\n const transports: TransportsHub = reactive(new Map())\n\n function open(transport: TransportInput) {\n if (!inBrowser) return\n\n const { to, from, content, order = Infinity } = transport\n if (!to || !from || !content) return\n\n if (!transports.has(to)) {\n transports.set(to, new Map())\n }\n const transportsForTarget = transports.get(to)!\n\n const newTransport = {\n to,\n from,\n content,\n order,\n } as Transport\n\n transportsForTarget.set(from, newTransport)\n }\n\n function close(transport: TransportCloser) {\n const { to, from } = transport\n if (!to || !from) return\n const transportsForTarget = transports.get(to)\n if (!transportsForTarget) {\n return\n }\n transportsForTarget.delete(from)\n if (!transportsForTarget.size) {\n transports.delete(to)\n }\n }\n\n function getContentForTarget(target: Name, returnAll?: boolean) {\n const transportsForTarget = transports.get(target)\n if (!transportsForTarget) return []\n\n const content = Array.from(transportsForTarget?.values() || [])\n\n if (!returnAll) {\n // return Transport that was added last\n return [content.pop()] as Transport[]\n }\n // return all Transports, sorted by their order property\n return stableSort(\n content,\n (a: Transport, b: Transport) => a.order - b.order\n )\n }\n\n const wh: Wormhole = {\n open,\n close,\n transports,\n getContentForTarget,\n }\n return asReadonly ? (readonly(wh) as Wormhole) : wh\n}\n\nexport const wormhole = createWormhole()\n","import type { PortalTargetProps } from '../types'\nimport {\n type ComponentInternalInstance,\n createApp,\n getCurrentInstance,\n h,\n onBeforeUnmount,\n onMounted,\n} from 'vue'\nimport PortalTarget from '../components/portal-target'\n\nexport function mountPortalTarget(\n targetProps: PortalTargetProps,\n el: HTMLElement | string\n) {\n const app = createApp({\n // @ts-expect-error no idea why h() doesn't like this import\n render: () => h(PortalTarget, targetProps),\n })\n\n if (!targetProps.multiple) {\n // this is hacky as it relies on internals, but works.\n // TODO: can we get rid of this by somehow properly replacing the target's .parent?\n const provides =\n (\n getCurrentInstance() as ComponentInternalInstance & {\n provides: Record<string, any>\n }\n ).provides ?? {}\n app._context.provides = Object.create(provides)\n //Object.assign(app._context.provides, Object.create(provides))\n }\n onMounted(() => {\n app.mount(el)\n })\n onBeforeUnmount(() => {\n app.unmount()\n })\n}\n","import type { App } from 'vue'\nimport Portal from './components/portal'\nimport PortalTarget from './components/portal-target'\nimport {\n provideWormhole,\n useWormhole,\n wormholeSymbol,\n} from './composables/wormhole'\nimport type { Wormhole as TWormhole } from './types'\nimport { createWormhole, wormhole as defaultWormhole } from './wormhole'\nexport { mountPortalTarget } from './utils/mountPortalTarget'\nexport interface PluginOptions {\n portalName?: string | false\n portalTargetName?: string | false\n MountingPortalName?: string\n wormhole?: TWormhole\n}\n\nexport default function install(app: App, options: PluginOptions = {}) {\n options.portalName !== false &&\n app.component(options.portalName || 'Portal', Portal)\n options.portalTargetName !== false &&\n app.component(options.portalTargetName || 'PortalTarget', PortalTarget)\n\n const wormhole = options.wormhole ?? defaultWormhole\n app.provide(wormholeSymbol, wormhole)\n}\n\nexport const Wormhole = defaultWormhole\n\nexport const version = __PORTAL_VUE_VERSION__\n\nexport {\n install,\n Portal,\n PortalTarget,\n useWormhole,\n provideWormhole,\n TWormhole,\n createWormhole,\n}\n"],"names":["wormholeSymbol","useWormhole","wh","inject","provideWormhole","wormhole","provide","inBrowser","stableSort","array","compareFn","v","idx","a","b","c","usePortal","props","slots","sendUpdate","to","from","order","clear","target","onMounted","onUpdated","onBeforeUnmount","watch","newTo","oldTo","Portal","defineComponent","PortalTargetContent","_","_a","PortalTarget","emit","slotVnodes","computed","transports","wrapperSlot","rawNodes","t","vnodes","nodes","hasContent","content","sources","h","createWormhole","asReadonly","reactive","open","transport","transportsForTarget","newTransport","close","getContentForTarget","returnAll","readonly","mountPortalTarget","targetProps","el","app","createApp","provides","getCurrentInstance","install","options","defaultWormhole","Wormhole","version"],"mappings":"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"}