]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: slot props
authorEvan You <evan@vuejs.org>
Sun, 8 Dec 2024 02:16:25 +0000 (10:16 +0800)
committerEvan You <evan@vuejs.org>
Sun, 8 Dec 2024 02:16:25 +0000 (10:16 +0800)
packages/runtime-vapor/src/componentProps.ts
packages/runtime-vapor/src/componentSlots.ts
packages/runtime-vapor/src/dom/node.ts

index eb83463c746e4a81c00281f767d98a0c9a943dd2..8aa9e0cea4447c408a121787bc3949d6787a8f14 100644 (file)
@@ -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 {
index cadbb642230460bb162015bb4cc05859ec4ed21e..d33d76bce9e3281982aa8a01ae1c8dbf6e6cda4f 100644 (file)
@@ -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<RawProps> = {
-  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
 }
index 5a0c530ba34c1cd9fe29699855a6a3e16192ca41..246fdf8e30dce83be12f997d71565ee1adfb1a2d 100644 (file)
@@ -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