: 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
}
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 {
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'
}
}
-// 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?
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
}
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)
} 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