feat(portal): support disabled prop
This commit is contained in:
		
							parent
							
								
									039d024b7f
								
							
						
					
					
						commit
						8ce3da0104
					
				@ -8,7 +8,7 @@ import {
 | 
			
		||||
  ref,
 | 
			
		||||
  nextTick
 | 
			
		||||
} from '@vue/runtime-test'
 | 
			
		||||
import { createVNode } from '../../src/vnode'
 | 
			
		||||
import { createVNode, Fragment } from '../../src/vnode'
 | 
			
		||||
 | 
			
		||||
describe('renderer: portal', () => {
 | 
			
		||||
  test('should work', () => {
 | 
			
		||||
@ -24,7 +24,7 @@ describe('renderer: portal', () => {
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal--><div>root</div>"`
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
@ -46,7 +46,7 @@ describe('renderer: portal', () => {
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal--><div>root</div>"`
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(targetA)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
@ -57,7 +57,7 @@ describe('renderer: portal', () => {
 | 
			
		||||
    await nextTick()
 | 
			
		||||
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal--><div>root</div>"`
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(targetA)).toMatchInlineSnapshot(`""`)
 | 
			
		||||
    expect(serializeInner(targetB)).toMatchInlineSnapshot(
 | 
			
		||||
@ -122,7 +122,7 @@ describe('renderer: portal', () => {
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div><!--portal--><!--portal--></div>"`
 | 
			
		||||
      `"<div><!--portal start--><!--portal end--><!--portal start--><!--portal end--></div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(`"<div>one</div>two"`)
 | 
			
		||||
 | 
			
		||||
@ -141,7 +141,7 @@ describe('renderer: portal', () => {
 | 
			
		||||
    // toggling
 | 
			
		||||
    render(h('div', [null, h(Portal, { target }, 'three')]), root)
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div><!----><!--portal--></div>"`
 | 
			
		||||
      `"<div><!----><!--portal start--><!--portal end--></div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(`"three"`)
 | 
			
		||||
 | 
			
		||||
@ -154,7 +154,7 @@ describe('renderer: portal', () => {
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div><!--portal--><!--portal--></div>"`
 | 
			
		||||
      `"<div><!--portal start--><!--portal end--><!--portal start--><!--portal end--></div>"`
 | 
			
		||||
    )
 | 
			
		||||
    // should append
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
@ -170,10 +170,133 @@ describe('renderer: portal', () => {
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div><!--portal--><!----></div>"`
 | 
			
		||||
      `"<div><!--portal start--><!--portal end--><!----></div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>one</div><div>two</div>"`
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test('disabled', () => {
 | 
			
		||||
    const target = nodeOps.createElement('div')
 | 
			
		||||
    const root = nodeOps.createElement('div')
 | 
			
		||||
 | 
			
		||||
    const renderWithDisabled = (disabled: boolean) => {
 | 
			
		||||
      return h(Fragment, [
 | 
			
		||||
        h(Portal, { target, disabled }, h('div', 'teleported')),
 | 
			
		||||
        h('div', 'root')
 | 
			
		||||
      ])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(renderWithDisabled(false), root)
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    render(renderWithDisabled(true), root)
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><div>teleported</div><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toBe(``)
 | 
			
		||||
 | 
			
		||||
    // toggle back
 | 
			
		||||
    render(renderWithDisabled(false), root)
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test('moving portal while enabled', () => {
 | 
			
		||||
    const target = nodeOps.createElement('div')
 | 
			
		||||
    const root = nodeOps.createElement('div')
 | 
			
		||||
 | 
			
		||||
    render(
 | 
			
		||||
      h(Fragment, [
 | 
			
		||||
        h(Portal, { target }, h('div', 'teleported')),
 | 
			
		||||
        h('div', 'root')
 | 
			
		||||
      ]),
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    render(
 | 
			
		||||
      h(Fragment, [
 | 
			
		||||
        h('div', 'root'),
 | 
			
		||||
        h(Portal, { target }, h('div', 'teleported'))
 | 
			
		||||
      ]),
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>root</div><!--portal start--><!--portal end-->"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    render(
 | 
			
		||||
      h(Fragment, [
 | 
			
		||||
        h(Portal, { target }, h('div', 'teleported')),
 | 
			
		||||
        h('div', 'root')
 | 
			
		||||
      ]),
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>teleported</div>"`
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test('moving portal while disabled', () => {
 | 
			
		||||
    const target = nodeOps.createElement('div')
 | 
			
		||||
    const root = nodeOps.createElement('div')
 | 
			
		||||
 | 
			
		||||
    render(
 | 
			
		||||
      h(Fragment, [
 | 
			
		||||
        h(Portal, { target, disabled: true }, h('div', 'teleported')),
 | 
			
		||||
        h('div', 'root')
 | 
			
		||||
      ]),
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><div>teleported</div><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toBe('')
 | 
			
		||||
 | 
			
		||||
    render(
 | 
			
		||||
      h(Fragment, [
 | 
			
		||||
        h('div', 'root'),
 | 
			
		||||
        h(Portal, { target, disabled: true }, h('div', 'teleported'))
 | 
			
		||||
      ]),
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<div>root</div><!--portal start--><div>teleported</div><!--portal end-->"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toBe('')
 | 
			
		||||
 | 
			
		||||
    render(
 | 
			
		||||
      h(Fragment, [
 | 
			
		||||
        h(Portal, { target, disabled: true }, h('div', 'teleported')),
 | 
			
		||||
        h('div', 'root')
 | 
			
		||||
      ]),
 | 
			
		||||
      root
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(root)).toMatchInlineSnapshot(
 | 
			
		||||
      `"<!--portal start--><div>teleported</div><!--portal end--><div>root</div>"`
 | 
			
		||||
    )
 | 
			
		||||
    expect(serializeInner(target)).toBe('')
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -206,7 +206,6 @@ const KeepAliveImpl = {
 | 
			
		||||
      if (cachedVNode) {
 | 
			
		||||
        // copy over mounted state
 | 
			
		||||
        vnode.el = cachedVNode.el
 | 
			
		||||
        vnode.anchor = cachedVNode.anchor
 | 
			
		||||
        vnode.component = cachedVNode.component
 | 
			
		||||
        if (vnode.transition) {
 | 
			
		||||
          // recursively update transition hooks on subTree
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,7 @@ import {
 | 
			
		||||
  RendererInternals,
 | 
			
		||||
  MoveType,
 | 
			
		||||
  RendererElement,
 | 
			
		||||
  RendererNode,
 | 
			
		||||
  RendererOptions
 | 
			
		||||
  RendererNode
 | 
			
		||||
} from '../renderer'
 | 
			
		||||
import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode'
 | 
			
		||||
import { isString, ShapeFlags } from '@vue/shared'
 | 
			
		||||
@ -15,6 +14,52 @@ export const isPortal = (type: any): boolean => type.__isPortal
 | 
			
		||||
 | 
			
		||||
export interface PortalProps {
 | 
			
		||||
  target: string | object
 | 
			
		||||
  disabled?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const enum PortalMoveTypes {
 | 
			
		||||
  TARGET_CHANGE,
 | 
			
		||||
  TOGGLE, // enable / disable
 | 
			
		||||
  REORDER // moved in the main view
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const movePortal = (
 | 
			
		||||
  vnode: VNode,
 | 
			
		||||
  container: RendererElement,
 | 
			
		||||
  parentAnchor: RendererNode | null,
 | 
			
		||||
  { o: { insert }, m: move }: RendererInternals,
 | 
			
		||||
  moveType: PortalMoveTypes = PortalMoveTypes.REORDER
 | 
			
		||||
) => {
 | 
			
		||||
  // move target anchor if this is a target change.
 | 
			
		||||
  if (moveType === PortalMoveTypes.TARGET_CHANGE) {
 | 
			
		||||
    insert(vnode.targetAnchor!, container, parentAnchor)
 | 
			
		||||
  }
 | 
			
		||||
  const { el, anchor, shapeFlag, children, props } = vnode
 | 
			
		||||
  const isReorder = moveType === PortalMoveTypes.REORDER
 | 
			
		||||
  // move main view anchor if this is a re-order.
 | 
			
		||||
  if (isReorder) {
 | 
			
		||||
    insert(el!, container, parentAnchor)
 | 
			
		||||
  }
 | 
			
		||||
  // if this is a re-order and portal is enabled (content is in target)
 | 
			
		||||
  // do not move children. So the opposite is: only move children if this
 | 
			
		||||
  // is not a reorder, or the portal is disabled
 | 
			
		||||
  if (!isReorder || (props && props.disabled)) {
 | 
			
		||||
    // Portal has either Array children or no children.
 | 
			
		||||
    if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
 | 
			
		||||
      for (let i = 0; i < (children as VNode[]).length; i++) {
 | 
			
		||||
        move(
 | 
			
		||||
          (children as VNode[])[i],
 | 
			
		||||
          container,
 | 
			
		||||
          parentAnchor,
 | 
			
		||||
          MoveType.REORDER
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // move main view anchor if this is a re-order.
 | 
			
		||||
  if (isReorder) {
 | 
			
		||||
    insert(anchor!, container, parentAnchor)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const PortalImpl = {
 | 
			
		||||
@ -28,60 +73,83 @@ export const PortalImpl = {
 | 
			
		||||
    parentSuspense: SuspenseBoundary | null,
 | 
			
		||||
    isSVG: boolean,
 | 
			
		||||
    optimized: boolean,
 | 
			
		||||
    {
 | 
			
		||||
    internals: RendererInternals
 | 
			
		||||
  ) {
 | 
			
		||||
    const {
 | 
			
		||||
      mc: mountChildren,
 | 
			
		||||
      pc: patchChildren,
 | 
			
		||||
      pbc: patchBlockChildren,
 | 
			
		||||
      m: move,
 | 
			
		||||
      o: { insert, querySelector, createText, createComment }
 | 
			
		||||
    }: RendererInternals
 | 
			
		||||
  ) {
 | 
			
		||||
    } = internals
 | 
			
		||||
 | 
			
		||||
    const targetSelector = n2.props && n2.props.target
 | 
			
		||||
    const disabled = n2.props && n2.props.disabled
 | 
			
		||||
    const { shapeFlag, children } = n2
 | 
			
		||||
    if (n1 == null) {
 | 
			
		||||
      // insert an empty node as the placeholder for the portal
 | 
			
		||||
      insert((n2.el = createComment(`portal`)), container, anchor)
 | 
			
		||||
      if (__DEV__ && isString(targetSelector) && !querySelector) {
 | 
			
		||||
        warn(
 | 
			
		||||
          `Current renderer does not support string target for Portals. ` +
 | 
			
		||||
            `(missing querySelector renderer option)`
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      // insert anchors in the main view
 | 
			
		||||
      const placeholder = (n2.el = __DEV__
 | 
			
		||||
        ? createComment('portal start')
 | 
			
		||||
        : createText(''))
 | 
			
		||||
      const mainAnchor = (n2.anchor = __DEV__
 | 
			
		||||
        ? createComment('portal end')
 | 
			
		||||
        : createText(''))
 | 
			
		||||
      insert(placeholder, container, anchor)
 | 
			
		||||
      insert(mainAnchor, container, anchor)
 | 
			
		||||
      // portal content needs an anchor to support patching multiple portals
 | 
			
		||||
      // appending to the same target element.
 | 
			
		||||
      const target = (n2.target = isString(targetSelector)
 | 
			
		||||
        ? querySelector!(targetSelector)
 | 
			
		||||
        : targetSelector)
 | 
			
		||||
      // portal content needs an anchor to support patching multiple portals
 | 
			
		||||
      // appending to the same target element.
 | 
			
		||||
      const portalAnchor = (n2.anchor = createText(''))
 | 
			
		||||
      const targetAnchor = (n2.targetAnchor = createText(''))
 | 
			
		||||
      if (target) {
 | 
			
		||||
        insert(portalAnchor, target)
 | 
			
		||||
        insert(targetAnchor, target)
 | 
			
		||||
      } else if (__DEV__) {
 | 
			
		||||
        warn('Invalid Portal target on mount:', target, `(${typeof target})`)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const mount = (container: RendererElement, anchor: RendererNode) => {
 | 
			
		||||
        // Portal *always* has Array children. This is enforced in both the
 | 
			
		||||
        // compiler and vnode children normalization.
 | 
			
		||||
        if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
 | 
			
		||||
          mountChildren(
 | 
			
		||||
            children as VNodeArrayChildren,
 | 
			
		||||
            target,
 | 
			
		||||
            portalAnchor,
 | 
			
		||||
            container,
 | 
			
		||||
            anchor,
 | 
			
		||||
            parentComponent,
 | 
			
		||||
            parentSuspense,
 | 
			
		||||
            isSVG,
 | 
			
		||||
            optimized
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      } else if (__DEV__) {
 | 
			
		||||
        warn('Invalid Portal target on mount:', target, `(${typeof target})`)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (disabled) {
 | 
			
		||||
        mount(container, mainAnchor)
 | 
			
		||||
      } else if (target) {
 | 
			
		||||
        mount(target, targetAnchor)
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // update content
 | 
			
		||||
      n2.el = n1.el
 | 
			
		||||
      const mainAnchor = (n2.anchor = n1.anchor)!
 | 
			
		||||
      const target = (n2.target = n1.target)!
 | 
			
		||||
      const portalAnchor = (n2.anchor = n1.anchor)!
 | 
			
		||||
      const targetAnchor = (n2.targetAnchor = n1.targetAnchor)!
 | 
			
		||||
      const wasDisabled = n1.props && n1.props.disabled
 | 
			
		||||
      const currentContainer = wasDisabled ? container : target
 | 
			
		||||
      const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
 | 
			
		||||
 | 
			
		||||
      if (n2.dynamicChildren) {
 | 
			
		||||
        // fast path when the portal happens to be a block root
 | 
			
		||||
        patchBlockChildren(
 | 
			
		||||
          n1.dynamicChildren!,
 | 
			
		||||
          n2.dynamicChildren,
 | 
			
		||||
          container,
 | 
			
		||||
          currentContainer,
 | 
			
		||||
          parentComponent,
 | 
			
		||||
          parentSuspense,
 | 
			
		||||
          isSVG
 | 
			
		||||
@ -90,23 +158,57 @@ export const PortalImpl = {
 | 
			
		||||
        patchChildren(
 | 
			
		||||
          n1,
 | 
			
		||||
          n2,
 | 
			
		||||
          target,
 | 
			
		||||
          portalAnchor,
 | 
			
		||||
          currentContainer,
 | 
			
		||||
          currentAnchor,
 | 
			
		||||
          parentComponent,
 | 
			
		||||
          parentSuspense,
 | 
			
		||||
          isSVG
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (disabled) {
 | 
			
		||||
        if (!wasDisabled) {
 | 
			
		||||
          // enabled -> disabled
 | 
			
		||||
          // move into main container
 | 
			
		||||
          movePortal(
 | 
			
		||||
            n2,
 | 
			
		||||
            container,
 | 
			
		||||
            mainAnchor,
 | 
			
		||||
            internals,
 | 
			
		||||
            PortalMoveTypes.TOGGLE
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        // target changed
 | 
			
		||||
        if (targetSelector !== (n1.props && n1.props.target)) {
 | 
			
		||||
          const nextTarget = (n2.target = isString(targetSelector)
 | 
			
		||||
            ? querySelector!(targetSelector)
 | 
			
		||||
            : targetSelector)
 | 
			
		||||
          if (nextTarget) {
 | 
			
		||||
          movePortal(n2, nextTarget, null, insert, move)
 | 
			
		||||
            movePortal(
 | 
			
		||||
              n2,
 | 
			
		||||
              nextTarget,
 | 
			
		||||
              null,
 | 
			
		||||
              internals,
 | 
			
		||||
              PortalMoveTypes.TARGET_CHANGE
 | 
			
		||||
            )
 | 
			
		||||
          } else if (__DEV__) {
 | 
			
		||||
          warn('Invalid Portal target on update:', target, `(${typeof target})`)
 | 
			
		||||
            warn(
 | 
			
		||||
              'Invalid Portal target on update:',
 | 
			
		||||
              target,
 | 
			
		||||
              `(${typeof target})`
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        } else if (wasDisabled) {
 | 
			
		||||
          // disabled -> enabled
 | 
			
		||||
          // move into portal target
 | 
			
		||||
          movePortal(
 | 
			
		||||
            n2,
 | 
			
		||||
            target,
 | 
			
		||||
            targetAnchor,
 | 
			
		||||
            internals,
 | 
			
		||||
            PortalMoveTypes.TOGGLE
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -123,25 +225,9 @@ export const PortalImpl = {
 | 
			
		||||
        remove((children as VNode[])[i])
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
const movePortal = (
 | 
			
		||||
  vnode: VNode,
 | 
			
		||||
  nextTarget: RendererElement,
 | 
			
		||||
  anchor: RendererNode | null,
 | 
			
		||||
  insert: RendererOptions['insert'],
 | 
			
		||||
  move: RendererInternals['m']
 | 
			
		||||
) => {
 | 
			
		||||
  const { anchor: portalAnchor, shapeFlag, children } = vnode
 | 
			
		||||
  // move content.
 | 
			
		||||
  // Portal has either Array children or no children.
 | 
			
		||||
  insert(portalAnchor!, nextTarget, anchor)
 | 
			
		||||
  if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
 | 
			
		||||
    for (let i = 0; i < (children as VNode[]).length; i++) {
 | 
			
		||||
      move((children as VNode[])[i], nextTarget, portalAnchor, MoveType.REORDER)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  move: movePortal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Force-casted public typing for h and TSX props inference
 | 
			
		||||
 | 
			
		||||
@ -1606,33 +1606,41 @@ function baseCreateRenderer(
 | 
			
		||||
    vnode,
 | 
			
		||||
    container,
 | 
			
		||||
    anchor,
 | 
			
		||||
    type,
 | 
			
		||||
    moveType,
 | 
			
		||||
    parentSuspense = null
 | 
			
		||||
  ) => {
 | 
			
		||||
    if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
 | 
			
		||||
      move(vnode.component!.subTree, container, anchor, type)
 | 
			
		||||
    const { el, type, transition, children, shapeFlag } = vnode
 | 
			
		||||
    if (shapeFlag & ShapeFlags.COMPONENT) {
 | 
			
		||||
      move(vnode.component!.subTree, container, anchor, moveType)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
 | 
			
		||||
      vnode.suspense!.move(container, anchor, type)
 | 
			
		||||
 | 
			
		||||
    if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
 | 
			
		||||
      vnode.suspense!.move(container, anchor, moveType)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    if (vnode.type === Fragment) {
 | 
			
		||||
      hostInsert(vnode.el!, container, anchor)
 | 
			
		||||
      const children = vnode.children as VNode[]
 | 
			
		||||
      for (let i = 0; i < children.length; i++) {
 | 
			
		||||
        move(children[i], container, anchor, type)
 | 
			
		||||
 | 
			
		||||
    if (shapeFlag & ShapeFlags.PORTAL) {
 | 
			
		||||
      ;(type as typeof PortalImpl).move(vnode, container, anchor, internals)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (type === Fragment) {
 | 
			
		||||
      hostInsert(el!, container, anchor)
 | 
			
		||||
      for (let i = 0; i < (children as VNode[]).length; i++) {
 | 
			
		||||
        move((children as VNode[])[i], container, anchor, moveType)
 | 
			
		||||
      }
 | 
			
		||||
      hostInsert(vnode.anchor!, container, anchor)
 | 
			
		||||
    } else {
 | 
			
		||||
      // Plain element
 | 
			
		||||
      const { el, transition, shapeFlag } = vnode
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // single nodes
 | 
			
		||||
    const needTransition =
 | 
			
		||||
        type !== MoveType.REORDER &&
 | 
			
		||||
      moveType !== MoveType.REORDER &&
 | 
			
		||||
      shapeFlag & ShapeFlags.ELEMENT &&
 | 
			
		||||
      transition
 | 
			
		||||
    if (needTransition) {
 | 
			
		||||
        if (type === MoveType.ENTER) {
 | 
			
		||||
      if (moveType === MoveType.ENTER) {
 | 
			
		||||
        transition!.beforeEnter(el!)
 | 
			
		||||
        hostInsert(el!, container, anchor)
 | 
			
		||||
        queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
 | 
			
		||||
@ -1655,7 +1663,6 @@ function baseCreateRenderer(
 | 
			
		||||
      hostInsert(el!, container, anchor)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const unmount: UnmountFn = (
 | 
			
		||||
    vnode,
 | 
			
		||||
@ -1839,7 +1846,7 @@ function baseCreateRenderer(
 | 
			
		||||
    if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
 | 
			
		||||
      return vnode.suspense!.next()
 | 
			
		||||
    }
 | 
			
		||||
    return hostNextSibling((vnode.type === Fragment ? vnode.anchor : vnode.el)!)
 | 
			
		||||
    return hostNextSibling((vnode.anchor || vnode.el)!)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const setRef = (
 | 
			
		||||
 | 
			
		||||
@ -114,6 +114,7 @@ export interface VNode<HostNode = RendererNode, HostElement = RendererElement> {
 | 
			
		||||
  el: HostNode | null
 | 
			
		||||
  anchor: HostNode | null // fragment anchor
 | 
			
		||||
  target: HostElement | null // portal target
 | 
			
		||||
  targetAnchor: HostNode | null // portal target anchor
 | 
			
		||||
 | 
			
		||||
  // optimization only
 | 
			
		||||
  shapeFlag: number
 | 
			
		||||
@ -308,6 +309,7 @@ function _createVNode(
 | 
			
		||||
    el: null,
 | 
			
		||||
    anchor: null,
 | 
			
		||||
    target: null,
 | 
			
		||||
    targetAnchor: null,
 | 
			
		||||
    shapeFlag,
 | 
			
		||||
    patchFlag,
 | 
			
		||||
    dynamicProps,
 | 
			
		||||
@ -357,6 +359,7 @@ export function cloneVNode<T, U>(
 | 
			
		||||
    scopeId: vnode.scopeId,
 | 
			
		||||
    children: vnode.children,
 | 
			
		||||
    target: vnode.target,
 | 
			
		||||
    targetAnchor: vnode.targetAnchor,
 | 
			
		||||
    shapeFlag: vnode.shapeFlag,
 | 
			
		||||
    patchFlag: vnode.patchFlag,
 | 
			
		||||
    dynamicProps: vnode.dynamicProps,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user