|
|
@@ -3,41 +3,57 @@ import {
|
|
|
type ConcreteComponent,
|
|
|
type Plugin,
|
|
|
type RendererInternals,
|
|
|
+ type ShallowRef,
|
|
|
+ type Slots,
|
|
|
+ type VNode,
|
|
|
type VaporInteropInterface,
|
|
|
createVNode,
|
|
|
currentInstance,
|
|
|
ensureRenderer,
|
|
|
+ renderSlot,
|
|
|
shallowRef,
|
|
|
simpleSetCurrentInstance,
|
|
|
} from '@vue/runtime-dom'
|
|
|
import {
|
|
|
type LooseRawProps,
|
|
|
type LooseRawSlots,
|
|
|
+ type VaporComponent,
|
|
|
VaporComponentInstance,
|
|
|
createComponent,
|
|
|
mountComponent,
|
|
|
unmountComponent,
|
|
|
} from './component'
|
|
|
-import { VaporFragment, insert } from './block'
|
|
|
-import { extend, remove } from '@vue/shared'
|
|
|
+import { type Block, VaporFragment, insert, remove } from './block'
|
|
|
+import { extend, isFunction, remove as removeItem } from '@vue/shared'
|
|
|
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
|
|
|
-import type { RawSlots } from './componentSlots'
|
|
|
+import type { RawSlots, VaporSlot } from './componentSlots'
|
|
|
+import { renderEffect } from './renderEffect'
|
|
|
|
|
|
const vaporInteropImpl: Omit<
|
|
|
VaporInteropInterface,
|
|
|
- 'vdomMount' | 'vdomUnmount'
|
|
|
+ 'vdomMount' | 'vdomUnmount' | 'vdomSlot'
|
|
|
> = {
|
|
|
mount(vnode, container, anchor, parentComponent) {
|
|
|
const selfAnchor = (vnode.anchor = document.createComment('vapor'))
|
|
|
container.insertBefore(selfAnchor, anchor)
|
|
|
const prev = currentInstance
|
|
|
simpleSetCurrentInstance(parentComponent)
|
|
|
+
|
|
|
const propsRef = shallowRef(vnode.props)
|
|
|
+ const slotsRef = shallowRef(vnode.children)
|
|
|
+
|
|
|
// @ts-expect-error
|
|
|
- const instance = (vnode.component = createComponent(vnode.type, {
|
|
|
- $: [() => propsRef.value],
|
|
|
- }))
|
|
|
+ const instance = (vnode.component = createComponent(
|
|
|
+ vnode.type as any as VaporComponent,
|
|
|
+ {
|
|
|
+ $: [() => propsRef.value],
|
|
|
+ } as RawProps,
|
|
|
+ {
|
|
|
+ _: slotsRef, // pass the slots ref
|
|
|
+ } as any as RawSlots,
|
|
|
+ ))
|
|
|
instance.rawPropsRef = propsRef
|
|
|
+ instance.rawSlotsRef = slotsRef
|
|
|
mountComponent(instance, container, selfAnchor)
|
|
|
simpleSetCurrentInstance(prev)
|
|
|
return instance
|
|
|
@@ -46,8 +62,9 @@ const vaporInteropImpl: Omit<
|
|
|
update(n1, n2, shouldUpdate) {
|
|
|
n2.component = n1.component
|
|
|
if (shouldUpdate) {
|
|
|
- ;(n2.component as any as VaporComponentInstance).rawPropsRef!.value =
|
|
|
- n2.props
|
|
|
+ const instance = n2.component as any as VaporComponentInstance
|
|
|
+ instance.rawPropsRef!.value = n2.props
|
|
|
+ instance.rawSlotsRef!.value = n2.children
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -109,8 +126,66 @@ function createVDOMComponent(
|
|
|
}
|
|
|
frag.remove = () => {
|
|
|
internals.umt(vnode.component!, null, true)
|
|
|
- remove(parentInstance.vdomChildren!, vnode.component)
|
|
|
- isMounted = false
|
|
|
+ removeItem(parentInstance.vdomChildren!, vnode.component)
|
|
|
+ }
|
|
|
+
|
|
|
+ return frag
|
|
|
+}
|
|
|
+
|
|
|
+function renderVDOMSlot(
|
|
|
+ internals: RendererInternals,
|
|
|
+ slotsRef: ShallowRef<Slots>,
|
|
|
+ name: string | (() => string),
|
|
|
+ props: Record<string, any>,
|
|
|
+ parentComponent: VaporComponentInstance,
|
|
|
+ fallback?: VaporSlot,
|
|
|
+): VaporFragment {
|
|
|
+ const frag = new VaporFragment([])
|
|
|
+
|
|
|
+ let isMounted = false
|
|
|
+ let fallbackNodes: Block | undefined
|
|
|
+ let parentNode: ParentNode
|
|
|
+ let oldVNode: VNode | null = null
|
|
|
+
|
|
|
+ frag.insert = (parent, anchor) => {
|
|
|
+ parentNode = parent
|
|
|
+ if (!isMounted) {
|
|
|
+ renderEffect(() => {
|
|
|
+ const vnode = renderSlot(
|
|
|
+ slotsRef.value,
|
|
|
+ isFunction(name) ? name() : name,
|
|
|
+ props,
|
|
|
+ )
|
|
|
+ if ((vnode.children as any[]).length) {
|
|
|
+ if (fallbackNodes) {
|
|
|
+ remove(fallbackNodes, parentNode)
|
|
|
+ fallbackNodes = undefined
|
|
|
+ }
|
|
|
+ internals.p(oldVNode, vnode, parent, anchor, parentComponent as any)
|
|
|
+ oldVNode = vnode
|
|
|
+ } else {
|
|
|
+ if (fallback && !fallbackNodes) {
|
|
|
+ // mount fallback
|
|
|
+ if (oldVNode) {
|
|
|
+ internals.um(oldVNode, parentComponent as any, null, true)
|
|
|
+ }
|
|
|
+ insert((fallbackNodes = fallback(props)), parent, anchor)
|
|
|
+ }
|
|
|
+ oldVNode = null
|
|
|
+ }
|
|
|
+ })
|
|
|
+ isMounted = true
|
|
|
+ } else {
|
|
|
+ // TODO move
|
|
|
+ }
|
|
|
+
|
|
|
+ frag.remove = () => {
|
|
|
+ if (fallbackNodes) {
|
|
|
+ remove(fallbackNodes, parentNode)
|
|
|
+ } else if (oldVNode) {
|
|
|
+ internals.um(oldVNode, parentComponent as any, null)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return frag
|
|
|
@@ -121,5 +196,6 @@ export const vaporInteropPlugin: Plugin = app => {
|
|
|
app._context.vapor = extend(vaporInteropImpl, {
|
|
|
vdomMount: createVDOMComponent.bind(null, internals),
|
|
|
vdomUnmount: internals.umt,
|
|
|
+ vdomSlot: renderVDOMSlot.bind(null, internals),
|
|
|
})
|
|
|
}
|