diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index eb83463c7..8aa9e0cea 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -112,39 +112,14 @@ export function getPropsProxyHandlers( : null const getAttr = (target: RawProps, key: string) => { - if (isProp(key) || isEmitListener(emitsOptions, key)) { - return - } - const dynamicSources = target.$ - if (dynamicSources) { - let i = dynamicSources.length - let source, isDynamic - while (i--) { - source = dynamicSources[i] - isDynamic = isFunction(source) - source = isDynamic ? (source as Function)() : source - if (hasOwn(source, key)) { - return isDynamic ? source[key] : source[key]() - } - } - } - if (hasOwn(target, key)) { - return target[key]() + if (!isProp(key) && !isEmitListener(emitsOptions, key)) { + return getAttrFromRawProps(target, key) } } const hasAttr = (target: RawProps, key: string) => { if (isAttr(key)) { - const dynamicSources = target.$ - if (dynamicSources) { - let i = dynamicSources.length - while (i--) { - if (hasOwn(resolveSource(dynamicSources[i]), key)) { - return true - } - } - } - return hasOwn(target, key) + return hasAttrFromRawProps(target, key) } else { return false } @@ -188,6 +163,40 @@ export function getPropsProxyHandlers( return (comp.__propsHandlers = [propsHandlers, attrsHandlers]) } +export function getAttrFromRawProps(rawProps: RawProps, key: string): unknown { + if (key === '$') return + const dynamicSources = rawProps.$ + if (dynamicSources) { + let i = dynamicSources.length + let source, isDynamic + while (i--) { + source = dynamicSources[i] + isDynamic = isFunction(source) + source = isDynamic ? (source as Function)() : source + if (hasOwn(source, key)) { + return isDynamic ? source[key] : source[key]() + } + } + } + if (hasOwn(rawProps, key)) { + return rawProps[key]() + } +} + +export function hasAttrFromRawProps(rawProps: RawProps, key: string): boolean { + if (key === '$') return false + const dynamicSources = rawProps.$ + if (dynamicSources) { + let i = dynamicSources.length + while (i--) { + if (hasOwn(resolveSource(dynamicSources[i]), key)) { + return true + } + } + } + return hasOwn(rawProps, key) +} + export function normalizePropsOptions( comp: VaporComponent, ): NormalizedPropsOptions { diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index cadbb6422..d33d76bce 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -1,6 +1,10 @@ import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared' import { type Block, type BlockFn, DynamicFragment } from './block' -import type { RawProps } from './componentProps' +import { + type RawProps, + getAttrFromRawProps, + hasAttrFromRawProps, +} from './componentProps' import { currentInstance } from '@vue/runtime-core' import type { VaporComponentInstance } from './component' import { renderEffect } from './renderEffect' @@ -78,14 +82,10 @@ export function getSlot(target: RawSlots, key: string): Slot | undefined { } } -// TODO const dynamicSlotsPropsProxyHandlers: ProxyHandler = { - get(target, key: string) { - return target[key] - }, - has(target, key) { - return key in target - }, + get: getAttrFromRawProps, + has: hasAttrFromRawProps, + ownKeys: target => Object.keys(target).filter(k => k !== '$'), } // TODO how to handle empty slot return blocks? @@ -97,38 +97,27 @@ export function createSlot( rawProps?: RawProps, fallback?: Slot, ): Block { + const fragment = new DynamicFragment('slot') const rawSlots = (currentInstance as VaporComponentInstance)!.rawSlots - const resolveSlot = () => getSlot(rawSlots, isFunction(name) ? name() : name) const slotProps = rawProps - ? rawProps.$ - ? new Proxy(rawProps, dynamicSlotsPropsProxyHandlers) - : rawProps + ? new Proxy(rawProps, dynamicSlotsPropsProxyHandlers) : EMPTY_OBJ - if (isFunction(name) || rawSlots.$) { - // dynamic slot name, or dynamic slot sources - const fragment = new DynamicFragment('slot') - renderEffect(() => { - const slot = resolveSlot() - if (slot) { - fragment.update( - () => slot(slotProps) || (fallback && fallback()), - // pass the stable slot fn as key to avoid toggling when resolving - // to the same slot - slot, - ) - } else { - fragment.update(fallback) - } - }) - return fragment - } else { - // static - const slot = resolveSlot() + // always create effect because a slot may contain dynamic root inside + // which affects fallback + renderEffect(() => { + const slot = getSlot(rawSlots, isFunction(name) ? name() : name) if (slot) { - const block = slot(slotProps) - if (block) return block + fragment.update( + () => slot(slotProps) || (fallback && fallback()), + // TODO this key needs to account for possible fallback (v-if) + // inside the slot + slot, + ) + } else { + fragment.update(fallback) } - return fallback ? fallback() : [] - } + }) + + return fragment } diff --git a/packages/runtime-vapor/src/dom/node.ts b/packages/runtime-vapor/src/dom/node.ts index 5a0c530ba..246fdf8e3 100644 --- a/packages/runtime-vapor/src/dom/node.ts +++ b/packages/runtime-vapor/src/dom/node.ts @@ -7,10 +7,10 @@ import { isVaporComponent } from '../component' export function insert( block: Block, parent: ParentNode, - anchor: Node | null = null, + anchor: Node | null | 0 = null, ): void { if (block instanceof Node) { - parent.insertBefore(block, anchor) + parent.insertBefore(block, anchor === 0 ? parent.firstChild : anchor) } else if (isVaporComponent(block)) { if (!block.isMounted) { if (block.bm) invokeArrayFns(block.bm) @@ -27,13 +27,12 @@ export function insert( } else { // fragment insert(block.nodes, parent, anchor) - if (block.anchor) parent.insertBefore(block.anchor, anchor) + if (block.anchor) insert(block.anchor, parent, anchor) } } export function prepend(parent: ParentNode, ...blocks: Block[]): void { - const anchor = parent.firstChild - for (const b of blocks) insert(b, parent, anchor) + for (const b of blocks) insert(b, parent, 0) } // TODO optimize