| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
- import { type Block, type BlockFn, DynamicFragment, insert } from './block'
- import { rawPropsProxyHandlers } from './componentProps'
- import { currentInstance, isRef } from '@vue/runtime-dom'
- import type { LooseRawProps, VaporComponentInstance } from './component'
- import { renderEffect } from './renderEffect'
- import {
- insertionAnchor,
- insertionParent,
- resetInsertionState,
- } from './insertionState'
- import { isHydrating, locateHydrationNode } from './dom/hydration'
- export type RawSlots = Record<string, VaporSlot> & {
- $?: DynamicSlotSource[]
- }
- export type StaticSlots = Record<string, VaporSlot>
- export type VaporSlot = BlockFn
- export type DynamicSlot = { name: string; fn: VaporSlot }
- export type DynamicSlotFn = () => DynamicSlot | DynamicSlot[]
- export type DynamicSlotSource = StaticSlots | DynamicSlotFn
- export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
- get: getSlot,
- has: (target, key: string) => !!getSlot(target, key),
- getOwnPropertyDescriptor(target, key: string) {
- const slot = getSlot(target, key)
- if (slot) {
- return {
- configurable: true,
- enumerable: true,
- value: slot,
- }
- }
- },
- ownKeys(target) {
- let keys = Object.keys(target)
- const dynamicSources = target.$
- if (dynamicSources) {
- keys = keys.filter(k => k !== '$')
- for (const source of dynamicSources) {
- if (isFunction(source)) {
- const slot = source()
- if (isArray(slot)) {
- for (const s of slot) keys.push(String(s.name))
- } else {
- keys.push(String(slot.name))
- }
- } else {
- keys.push(...Object.keys(source))
- }
- }
- }
- return keys
- },
- set: NO,
- deleteProperty: NO,
- }
- export function getSlot(
- target: RawSlots,
- key: string,
- ): (VaporSlot & { _bound?: VaporSlot }) | undefined {
- if (key === '$') return
- const dynamicSources = target.$
- if (dynamicSources) {
- let i = dynamicSources.length
- let source
- while (i--) {
- source = dynamicSources[i]
- if (isFunction(source)) {
- const slot = source()
- if (slot) {
- if (isArray(slot)) {
- for (const s of slot) {
- if (String(s.name) === key) return s.fn
- }
- } else if (String(slot.name) === key) {
- return slot.fn
- }
- }
- } else if (hasOwn(source, key)) {
- return source[key]
- }
- }
- }
- if (hasOwn(target, key)) {
- return target[key]
- }
- }
- export function createSlot(
- name: string | (() => string),
- rawProps?: LooseRawProps | null,
- fallback?: VaporSlot,
- ): Block {
- const _insertionParent = insertionParent
- const _insertionAnchor = insertionAnchor
- if (isHydrating) {
- locateHydrationNode()
- } else {
- resetInsertionState()
- }
- const instance = currentInstance as VaporComponentInstance
- const rawSlots = instance.rawSlots
- const slotProps = rawProps
- ? new Proxy(rawProps, rawPropsProxyHandlers)
- : EMPTY_OBJ
- let fragment: DynamicFragment
- if (isRef(rawSlots._)) {
- fragment = instance.appContext.vapor!.vdomSlot(
- rawSlots._,
- name,
- slotProps,
- instance,
- fallback,
- )
- } else {
- fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment()
- const isDynamicName = isFunction(name)
- const renderSlot = () => {
- const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
- if (slot) {
- fragment.fallback = fallback
- // create and cache bound version of the slot to make it stable
- // so that we avoid unnecessary updates if it resolves to the same slot
- fragment.update(slot._bound || (slot._bound = () => slot(slotProps)))
- } else {
- fragment.update(fallback)
- }
- }
- // dynamic slot name or has dynamicSlots
- if (isDynamicName || rawSlots.$) {
- renderEffect(renderSlot)
- } else {
- renderSlot()
- }
- }
- if (!isHydrating && _insertionParent) {
- insert(fragment, _insertionParent, _insertionAnchor)
- }
- return fragment
- }
|