import { Fragment } from '@/wDom';
import {
WDom,
TagFunction,
TagFunctionResolver,
FragmentFunction,
WDomType,
} from '@/types';
import { isObject } from '@/utils';
type WDomParam =
| string
| WDom
| TagFunction
| TagFunctionResolver
| FragmentFunction;
/**
* Predicator
*/
const checkSameCustomComponent = (
newWDom: WDom | TagFunction | TagFunctionResolver,
originalWDom?: WDom
): boolean =>
'ctor' in newWDom
? newWDom.ctor === (originalWDom && originalWDom.ctor)
: newWDom === (originalWDom && originalWDom.ctor);
// The checks below are reached through checkSameWDomWithOriginal only: with
// a plain node of that type (getWDomType) and an original.
const checkSameFragment = (newWDom: WDom, originalWDom: WDom): boolean =>
originalWDom.type === 'f' &&
// A component's node is a one-child fragment too: never update it in
// place as a plain fragment, or the component is never unmounted, nor
// update a plain fragment with one (a hoisted first drawn by a
// redraw), or it is never mounted.
!originalWDom.ctor &&
!newWDom.ctor &&
!!originalWDom.children &&
originalWDom.children.length ===
(newWDom.children && newWDom.children.length);
const checkSameTagElement = (newWDom: WDom, originalWDom: WDom): boolean =>
originalWDom.type === 'e' &&
originalWDom.tag === newWDom.tag &&
!!originalWDom.children &&
originalWDom.children.length ===
(newWDom.children && newWDom.children.length);
const checkNormalTypeElement = (newWDom: WDom, originalWDom: WDom): boolean =>
originalWDom.type === newWDom.type;
const checkLoopTypeElement = (newWDom: WDom, originalWDom: WDom): boolean =>
originalWDom.type === newWDom.type &&
((checkKeyedChildren(newWDom) && checkKeyedChildren(originalWDom)) ||
(!!originalWDom.children &&
originalWDom.children.length ===
(newWDom.children && newWDom.children.length)));
export const getKey = (target: WDom) =>
(target && target.compProps && target.compProps.key) ??
(target && target.props && target.props.key);
/**
* Check if the type is virtual (fragment or loop)
* Virtual types don't create real DOM elements themselves
*/
export const checkVirtualType = (
type?: string | null // 'f': fragment, 'l': loop
) => type === 'f' || type === 'l';
export const checkCustemComponentFunction = (
target: WDomParam
): target is TagFunction | TagFunctionResolver =>
(typeof target === 'function' && !checkFragmentFunction(target)) ||
(isObject(target) && 'resolve' in target);
export const checkFragmentFunction = (
target: unknown
): target is FragmentFunction => target === Fragment;
// A diffed node (resolved already): empty when it has no type.
export const checkEmptyElement = (wDom: WDom) => !wDom.type;
// Whether the node's element is a portal target, which lies outside the
// parent the portal sits in: a portal's own element, or the one a node
// replacing or deleting a portal still holds, with that portal's props (op),
// until it is drawn.
export const checkPortalElement = ({ el, op, props }: WDom) =>
!!el && (el === (op && op.portal) || el === (props && props.portal));
export const checkEventKey = (key: string) => key[0] === 'o' && key[1] === 'n';
export const checkExistyKey = (target: WDom) => checkExisty(getKey(target));
// A list is keyed when any child has a key: a leading null or false (a hidden
// row) must not turn key matching off for the rest.
export const checkKeyedChildren = (target?: WDom) =>
!!target && !!target.children && target.children.some(checkExistyKey);
export const checkExisty = (value: unknown) =>
value !== null && value !== undefined;
export const checkStyleData = (
dataKey: string,
dataValue: unknown
): dataValue is Record =>
dataKey === 'style' && isObject(dataValue);
export const checkRefData = (
dataKey: string,
dataValue: unknown
): dataValue is {
value: HTMLElement | Element | DocumentFragment | Text | undefined;
} => dataKey === 'ref' && isObject(dataValue);
// Descriptor lookups are cached per prototype+key. Keying by prototype
// rather than nodeName matters for custom elements: one rendered before its
// definition is upgraded later and gets a new prototype, which must be looked
// up again instead of reusing the "no accessor" answer for its tag name.
const accessorCache = new WeakMap