wip: slots
This commit is contained in:
parent
8331aa43c4
commit
4b6100623f
|
@ -1,17 +1,25 @@
|
|||
import { isArray } from '@vue/shared'
|
||||
import { type VaporComponentInstance, isVaporComponent } from './component'
|
||||
|
||||
export const fragmentKey: unique symbol = Symbol(__DEV__ ? `fragmentKey` : ``)
|
||||
import { createComment } from './dom/element'
|
||||
|
||||
export type Block = Node | Fragment | VaporComponentInstance | Block[]
|
||||
export type Fragment = {
|
||||
|
||||
export class Fragment {
|
||||
nodes: Block
|
||||
anchor?: Node
|
||||
[fragmentKey]: true
|
||||
constructor(nodes: Block, anchorLabel?: string) {
|
||||
this.nodes = nodes
|
||||
if (anchorLabel) {
|
||||
this.anchor = __DEV__
|
||||
? createComment(anchorLabel)
|
||||
: // eslint-disable-next-line no-restricted-globals
|
||||
document.createTextNode('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isFragment(val: NonNullable<unknown>): val is Fragment {
|
||||
return fragmentKey in val
|
||||
return val instanceof Fragment
|
||||
}
|
||||
|
||||
export function isBlock(val: NonNullable<unknown>): val is Block {
|
||||
|
@ -59,6 +67,7 @@ export function getFirstNode(block: Block | null): Node | undefined {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
export function isValidBlock(block: Block): boolean {
|
||||
return (
|
||||
normalizeBlock(block).filter(node => !(node instanceof Comment)).length > 0
|
||||
|
|
|
@ -36,7 +36,9 @@ import {
|
|||
type RawSlots,
|
||||
type StaticSlots,
|
||||
dynamicSlotsProxyHandlers,
|
||||
getSlot,
|
||||
} from './componentSlots'
|
||||
import { insert } from './dom/element'
|
||||
|
||||
export { currentInstance } from '@vue/runtime-dom'
|
||||
|
||||
|
@ -84,7 +86,8 @@ interface SharedInternalOptions {
|
|||
|
||||
export function createComponent(
|
||||
component: VaporComponent,
|
||||
rawProps?: RawProps,
|
||||
rawProps?: RawProps | null,
|
||||
rawSlots?: RawSlots | null,
|
||||
isSingleRoot?: boolean,
|
||||
): VaporComponentInstance {
|
||||
// check if we are the single root of the parent
|
||||
|
@ -102,7 +105,7 @@ export function createComponent(
|
|||
}
|
||||
}
|
||||
|
||||
const instance = new VaporComponentInstance(component, rawProps)
|
||||
const instance = new VaporComponentInstance(component, rawProps, rawSlots)
|
||||
const resetCurrentInstance = setCurrentInstance(instance)
|
||||
|
||||
pauseTracking()
|
||||
|
@ -175,12 +178,14 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
|
||||
block: Block
|
||||
scope: EffectScope
|
||||
rawProps: RawProps
|
||||
props: Record<string, any>
|
||||
attrs: Record<string, any>
|
||||
slots: StaticSlots
|
||||
exposed: Record<string, any> | null
|
||||
|
||||
rawProps: RawProps
|
||||
rawSlots: RawSlots
|
||||
|
||||
emitted: Record<string, boolean> | null
|
||||
propsDefaults: Record<string, any> | null
|
||||
|
||||
|
@ -221,7 +226,11 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
propsOptions?: NormalizedPropsOptions
|
||||
emitsOptions?: ObjectEmitsOptions | null
|
||||
|
||||
constructor(comp: VaporComponent, rawProps?: RawProps, rawSlots?: RawSlots) {
|
||||
constructor(
|
||||
comp: VaporComponent,
|
||||
rawProps?: RawProps | null,
|
||||
rawSlots?: RawSlots | null,
|
||||
) {
|
||||
this.vapor = true
|
||||
this.uid = nextUid()
|
||||
this.type = comp
|
||||
|
@ -257,6 +266,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
}
|
||||
|
||||
// init slots
|
||||
this.rawSlots = rawSlots || EMPTY_OBJ
|
||||
this.slots = rawSlots
|
||||
? rawSlots.$
|
||||
? new Proxy(rawSlots, dynamicSlotsProxyHandlers)
|
||||
|
@ -304,12 +314,12 @@ export class SetupContext<E = EmitsOptions> {
|
|||
*/
|
||||
export function createComponentWithFallback(
|
||||
comp: VaporComponent | string,
|
||||
rawProps: RawProps | undefined,
|
||||
// TODO slots: RawSlots | null
|
||||
rawProps: RawProps | null | undefined,
|
||||
rawSlots: RawSlots | null | undefined,
|
||||
isSingleRoot?: boolean,
|
||||
): HTMLElement | VaporComponentInstance {
|
||||
if (!isString(comp)) {
|
||||
return createComponent(comp, rawProps, isSingleRoot)
|
||||
return createComponent(comp, rawProps, rawSlots, isSingleRoot)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
|
@ -331,17 +341,11 @@ export function createComponentWithFallback(
|
|||
})
|
||||
}
|
||||
|
||||
// TODO
|
||||
// if (slots) {
|
||||
// if (!Array.isArray(slots)) slots = [slots]
|
||||
// for (let i = 0; i < slots.length; i++) {
|
||||
// const slot = slots[i]
|
||||
// if (!isDynamicSlotFn(slot) && slot.default) {
|
||||
// const block = slot.default && slot.default()
|
||||
// if (block) el.append(...normalizeBlock(block))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
const defaultSlot = rawSlots && getSlot(rawSlots, 'default')
|
||||
if (defaultSlot) {
|
||||
const res = defaultSlot()
|
||||
insert(res, el)
|
||||
}
|
||||
|
||||
return el
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ function resolveDefault(
|
|||
|
||||
export function hasFallthroughAttrs(
|
||||
comp: VaporComponent,
|
||||
rawProps: RawProps | undefined,
|
||||
rawProps: RawProps | null | undefined,
|
||||
): boolean {
|
||||
if (rawProps) {
|
||||
// determine fallthrough
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { NO, hasOwn, isArray, isFunction } from '@vue/shared'
|
||||
import type { Block } from './block'
|
||||
import { type Block, Fragment, isValidBlock } from './block'
|
||||
import { type RawProps, resolveDynamicProps } from './componentProps'
|
||||
import { currentInstance } from '@vue/runtime-core'
|
||||
import type { VaporComponentInstance } from './component'
|
||||
import { renderEffect } from './renderEffect'
|
||||
|
||||
export type RawSlots = Record<string, Slot> & {
|
||||
$?: (StaticSlots | DynamicSlotFn)[]
|
||||
|
@ -47,7 +51,8 @@ export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
|
|||
deleteProperty: NO,
|
||||
}
|
||||
|
||||
function getSlot(target: RawSlots, key: string) {
|
||||
export function getSlot(target: RawSlots, key: string): Slot | undefined {
|
||||
if (key === '$') return
|
||||
const dynamicSources = target.$
|
||||
if (dynamicSources) {
|
||||
let i = dynamicSources.length
|
||||
|
@ -72,3 +77,31 @@ function getSlot(target: RawSlots, key: string) {
|
|||
return target[key]
|
||||
}
|
||||
}
|
||||
|
||||
export function createSlot(
|
||||
name: string | (() => string),
|
||||
props?: RawProps,
|
||||
fallback?: Slot,
|
||||
): Block {
|
||||
const slots = (currentInstance as VaporComponentInstance)!.rawSlots
|
||||
if (isFunction(name) || slots.$) {
|
||||
// dynamic slot name, or dynamic slot sources
|
||||
// TODO togglable fragment class
|
||||
const fragment = new Fragment([], 'slot')
|
||||
return fragment
|
||||
} else {
|
||||
// static
|
||||
return renderSlot(name)
|
||||
}
|
||||
|
||||
function renderSlot(name: string) {
|
||||
const slot = getSlot(slots, name)
|
||||
if (slot) {
|
||||
const block = slot(props ? resolveDynamicProps(props) : {})
|
||||
if (isValidBlock(block)) {
|
||||
return block
|
||||
}
|
||||
}
|
||||
return fallback ? fallback() : []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export function insert(
|
|||
} else {
|
||||
// fragment
|
||||
insert(block.nodes, parent, anchor)
|
||||
if (block.anchor) parent.insertBefore(block.anchor, anchor)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,12 +48,13 @@ export function remove(block: Block, parent: ParentNode): void {
|
|||
export function createTextNode(values?: any[] | (() => any[])): Text {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const node = document.createTextNode('')
|
||||
if (values)
|
||||
if (values) {
|
||||
if (isArray(values)) {
|
||||
setText(node, ...values)
|
||||
} else {
|
||||
renderEffect(() => setText(node, ...values()))
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ export { createComponent, createComponentWithFallback } from './component'
|
|||
export { renderEffect } from './renderEffect'
|
||||
export { createVaporApp } from './apiCreateApp'
|
||||
export { defineComponent } from './apiDefineComponent'
|
||||
export { createSlot } from './componentSlots'
|
||||
|
||||
// DOM
|
||||
export { template, children, next } from './dom/template'
|
||||
|
|
Loading…
Reference in New Issue