Match-id-34cc8d2e71fcba103088bb87b33f8df28ae8c31d
This commit is contained in:
parent
34abe28056
commit
19a39513e6
|
@ -3,19 +3,17 @@ import {
|
|||
syncUpdates, startUpdate,
|
||||
} from '../renderer/Renderer';
|
||||
import {createPortal} from '../renderer/components/CreatePortal';
|
||||
import {
|
||||
clearContainer, saveContainer,
|
||||
} from './DOMInternalKeys';
|
||||
import type {Container} from './DOMOperator';
|
||||
import {isElement} from './utils/Common';
|
||||
import {listenDelegatedEvents} from '../event/EventBinding';
|
||||
import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils';
|
||||
import {TreeRoot} from '../renderer/vnode/VNodeTags';
|
||||
import {Callback} from '../renderer/UpdateHandler';
|
||||
|
||||
function executeRender(
|
||||
children: any,
|
||||
container: Container,
|
||||
callback?: Function,
|
||||
callback?: Callback,
|
||||
) {
|
||||
let treeRoot = container._treeRoot;
|
||||
|
||||
|
@ -36,7 +34,7 @@ function executeRender(
|
|||
return getFirstCustomDom(treeRoot);
|
||||
}
|
||||
|
||||
function createRoot(children: any, container: Container, callback?: Function) {
|
||||
function createRoot(children: any, container: Container, callback?: Callback) {
|
||||
// 清空容器
|
||||
let child = container.lastChild;
|
||||
while (child) {
|
||||
|
@ -46,11 +44,10 @@ function createRoot(children: any, container: Container, callback?: Function) {
|
|||
|
||||
// 调度器创建根节点,并给容器dom赋vNode结构体
|
||||
const treeRoot = createVNode(TreeRoot, container);
|
||||
saveContainer(container, treeRoot._domRoot);
|
||||
container._treeRoot = treeRoot;
|
||||
|
||||
// 根节点挂接全量事件
|
||||
listenDelegatedEvents(container);
|
||||
listenDelegatedEvents(container as Element);
|
||||
|
||||
// 执行回调
|
||||
if (typeof callback === 'function') {
|
||||
|
@ -89,7 +86,6 @@ function destroy(container: Container) {
|
|||
syncUpdates(() => {
|
||||
executeRender(null, container, () => {
|
||||
container._treeRoot = null;
|
||||
clearContainer(container);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import type {
|
|||
import {
|
||||
DomComponent,
|
||||
DomText,
|
||||
DomRoot,
|
||||
TreeRoot,
|
||||
} from '../renderer/vnode/VNodeTags';
|
||||
|
||||
const suffixKey = new Date().getTime().toString();
|
||||
|
@ -20,7 +20,6 @@ const prefix = '_horizon';
|
|||
const internalKeys = {
|
||||
VNode: `${prefix}VNode@${suffixKey}`,
|
||||
props: `${prefix}Props@${suffixKey}`,
|
||||
container: `${prefix}Container@${suffixKey}`,
|
||||
events: `${prefix}Events@${suffixKey}`,
|
||||
nonDelegatedEvents: `${prefix}NonDelegatedEvents@${suffixKey}`,
|
||||
};
|
||||
|
@ -42,33 +41,33 @@ export function saveVNode(
|
|||
}
|
||||
|
||||
// 用 DOM 节点,来找其对应的 VNode 实例
|
||||
export function getVNode(dom: Node): VNode | null {
|
||||
const vNode = dom[internalKeys.VNode] || dom[internalKeys.container];
|
||||
export function getVNode(dom: Node|Container): VNode | null {
|
||||
const vNode = dom[internalKeys.VNode] || (dom as Container)._treeRoot;
|
||||
if (vNode) {
|
||||
const {tag} = vNode;
|
||||
if (tag === DomComponent || tag === DomText || tag === DomRoot) {
|
||||
if (tag === DomComponent || tag === DomText || tag === TreeRoot) {
|
||||
return vNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 用 DOM 对象,来寻找其对应或者说是最近的 VNode 实例
|
||||
// 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode
|
||||
export function getNearestVNode(dom: Node): null | VNode {
|
||||
let vNode = dom[internalKeys.VNode];
|
||||
if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例
|
||||
return vNode;
|
||||
}
|
||||
// 下面处理的是为被框架标记过的 DOM 节点,向上找其父节点是否被框架标记过
|
||||
let parent = dom.parentNode;
|
||||
let parentDom = dom.parentNode;
|
||||
let nearVNode = null;
|
||||
while (parent) {
|
||||
vNode = parent[internalKeys.VNode];
|
||||
while (parentDom) {
|
||||
vNode = parentDom[internalKeys.VNode];
|
||||
if (vNode) {
|
||||
nearVNode = vNode;
|
||||
break;
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
parentDom = parentDom.parentNode;
|
||||
}
|
||||
return nearVNode;
|
||||
}
|
||||
|
@ -99,11 +98,3 @@ export function getEventToListenerMap(target: EventTarget): Map<string, EventLis
|
|||
}
|
||||
return eventsMap;
|
||||
}
|
||||
|
||||
export function saveContainer(dom: Container, domRoot: VNode): void {
|
||||
dom[internalKeys.container] = domRoot;
|
||||
}
|
||||
|
||||
export function clearContainer(dom: Container): void {
|
||||
dom[internalKeys.container] = null;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
/**
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
|
||||
*/
|
||||
|
||||
import {
|
||||
saveVNode,
|
||||
updateVNodeProps,
|
||||
|
@ -10,7 +6,7 @@ import {
|
|||
createDom,
|
||||
} from './utils/DomCreator';
|
||||
import {getSelectionInfo, resetSelectionRange, selectionData} from './SelectionRangeHandler';
|
||||
import {isElement, isComment, isDocument, isDocumentFragment} from './utils/Common';
|
||||
import {isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus} from './utils/Common';
|
||||
import {NSS} from './utils/DomCreator';
|
||||
import {adjustStyleValue} from './DOMPropertiesHandler/StyleHandler';
|
||||
|
||||
|
@ -20,7 +16,7 @@ import {
|
|||
setInitValue,
|
||||
getPropsWithoutValue,
|
||||
updateValue,
|
||||
} from './valueHandler/ValueHandler';
|
||||
} from './valueHandler';
|
||||
import {
|
||||
compareProps,
|
||||
setDomProps, updateDomProps
|
||||
|
@ -31,76 +27,39 @@ import {DomComponent, DomText} from '../renderer/vnode/VNodeTags';
|
|||
import {updateCommonProp} from './DOMPropertiesHandler/UpdateCommonProp';
|
||||
|
||||
export type Props = {
|
||||
autoFocus?: boolean,
|
||||
children?: any,
|
||||
dangerouslySetInnerHTML?: any,
|
||||
disabled?: boolean,
|
||||
hidden?: boolean,
|
||||
style?: { display?: string },
|
||||
autoFocus?: boolean;
|
||||
children?: any;
|
||||
dangerouslySetInnerHTML?: any;
|
||||
disabled?: boolean;
|
||||
hidden?: boolean;
|
||||
style?: { display?: string };
|
||||
};
|
||||
|
||||
export type Container = (Element & { _treeRoot?: VNode }) | (Document & { _treeRoot?: VNode });
|
||||
|
||||
let selectionInfo: null | selectionData = null;
|
||||
|
||||
const types = ['button', 'input', 'select', 'textarea'];
|
||||
|
||||
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||
function shouldAutoFocus(type: string, props: Props): boolean {
|
||||
return types.includes(type) ? Boolean(props.autoFocus) : false;
|
||||
}
|
||||
|
||||
function getChildNS(parent: string | null, type: string,): string {
|
||||
if (parent === NSS.svg && type === 'foreignObject') {
|
||||
function getChildNS(parentNS: string | null, tagName: string): string {
|
||||
if (parentNS === NSS.svg && tagName === 'foreignObject') {
|
||||
return NSS.html;
|
||||
}
|
||||
if (parent == null || parent === NSS.html) {
|
||||
// 没有父命名空间
|
||||
return Object.keys(NSS).includes(type) ? NSS[type] : NSS.html;
|
||||
|
||||
if (parentNS == null || parentNS === NSS.html) {
|
||||
// 没有父命名空间,或父命名空间为xhtml
|
||||
return NSS[tagName] ?? NSS.html;
|
||||
}
|
||||
|
||||
// 默认返回parentNamespace.
|
||||
return parent;
|
||||
}
|
||||
|
||||
function getRootNS(dom, root, nextRoot) {
|
||||
let namespace;
|
||||
let tag;
|
||||
let container, ownNamespace;
|
||||
|
||||
if (isDocument(dom)) {
|
||||
tag = '#document';
|
||||
namespace = root ? root.namespaceURI : getChildNS(null, '');
|
||||
} else if (isDocumentFragment(dom)) {
|
||||
tag = '#fragment';
|
||||
namespace = root ? root.namespaceURI : getChildNS(null, '');
|
||||
} else if (isComment(dom)) {
|
||||
container = nextRoot.parentNode;
|
||||
ownNamespace = container.namespaceURI || null;
|
||||
tag = container.tagName;
|
||||
namespace = getChildNS(ownNamespace, tag);
|
||||
} else {
|
||||
container = nextRoot;
|
||||
ownNamespace = container.namespaceURI || null;
|
||||
tag = container.tagName;
|
||||
namespace = getChildNS(ownNamespace, tag);
|
||||
}
|
||||
|
||||
return namespace;
|
||||
return parentNS;
|
||||
}
|
||||
|
||||
// 获取容器
|
||||
export function getNSCtx(
|
||||
nextRoot: Container,
|
||||
ctxNamespace: string,
|
||||
type: string): string {
|
||||
export function getNSCtx(dom: Container, parentNS: string, type: string): string {
|
||||
let namespace;
|
||||
if (nextRoot) {
|
||||
// 获取并解析根节点容器
|
||||
const root = nextRoot.documentElement;
|
||||
namespace = getRootNS(nextRoot, root, nextRoot);
|
||||
if (dom) {
|
||||
namespace = getChildNS(dom.namespaceURI ?? null, dom.nodeName);
|
||||
} else {
|
||||
// 获取子节点容器
|
||||
namespace = getChildNS(ctxNamespace, type);
|
||||
namespace = getChildNS(parentNS, type);
|
||||
}
|
||||
return namespace;
|
||||
}
|
||||
|
@ -114,21 +73,14 @@ export function resetAfterSubmit(): void {
|
|||
selectionInfo = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在内存中创建 DOM 对象
|
||||
* @param tagName 元素的类型
|
||||
* @param props 属性
|
||||
* @param parentNamespace 当前上下文
|
||||
* @param vNode 当前元素对应的 VNode
|
||||
* @returns DOM 对象
|
||||
*/
|
||||
// 创建 DOM 对象
|
||||
export function newDom(
|
||||
tagName: string,
|
||||
props: Props,
|
||||
parentNamespace: string,
|
||||
vNode: VNode,
|
||||
): Element {
|
||||
const dom: Element = createDom(tagName, props, parentNamespace);
|
||||
const dom: Element = createDom(tagName, parentNamespace);
|
||||
// 将 vNode 节点挂到 DOM 对象上
|
||||
saveVNode(vNode, dom);
|
||||
// 将属性挂到 DOM 对象上
|
||||
|
@ -164,7 +116,7 @@ export function getPropChangeList(
|
|||
type: string,
|
||||
lastRawProps: Props,
|
||||
nextRawProps: Props,
|
||||
): null | Array<any> {
|
||||
): Array<any> {
|
||||
// 校验两个对象的不同
|
||||
validateProps(type, nextRawProps);
|
||||
|
||||
|
@ -177,15 +129,11 @@ export function getPropChangeList(
|
|||
}
|
||||
|
||||
export function isTextChild(type: string, props: Props): boolean {
|
||||
if (type === 'textarea') {
|
||||
const typeArray = ['textarea', 'option', 'noscript'];
|
||||
const typeOfPropsChild = ['string', 'number'];
|
||||
if (typeArray.indexOf(type) >= 0) {
|
||||
return true;
|
||||
} else if (type === 'option') {
|
||||
return true;
|
||||
} else if (type === 'noscript') {
|
||||
return true;
|
||||
} else if (typeof props.children === 'string') {
|
||||
return true;
|
||||
} else if (typeof props.children === 'number') {
|
||||
} else if (typeOfPropsChild.indexOf(typeof props.children) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return (
|
||||
|
@ -205,19 +153,8 @@ export function newTextDom(
|
|||
return textNode;
|
||||
}
|
||||
|
||||
export function submitMount(
|
||||
dom: Element,
|
||||
type: string,
|
||||
newProps: Props,
|
||||
): void {
|
||||
if (shouldAutoFocus(type, newProps)) {
|
||||
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||
dom.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// 提交vNode的类型为Component或者Text的更新
|
||||
export function submitDomUpdate(tag: number, vNode: VNode) {
|
||||
export function submitDomUpdate(tag: string, vNode: VNode) {
|
||||
const newProps = vNode.props;
|
||||
const element: Element = vNode.realNode;
|
||||
|
||||
|
@ -251,71 +188,44 @@ export function clearText(dom: Element): void {
|
|||
}
|
||||
|
||||
// 添加child元素
|
||||
export function appendChildElement(isContainer: boolean,
|
||||
export function appendChildElement(
|
||||
parent: Element | Container,
|
||||
child: Element | Text): void {
|
||||
if (isContainer && isComment(parent)) {
|
||||
parent.parentNode.insertBefore(child, parent);
|
||||
} else {
|
||||
child: Element | Text
|
||||
): void {
|
||||
parent.appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
// 插入dom元素
|
||||
export function insertDomBefore(
|
||||
isContainer: boolean,
|
||||
parent: Element | Container,
|
||||
child: Element | Text,
|
||||
beforeChild: Element | Text,
|
||||
) {
|
||||
if (isContainer && isComment(parent)) {
|
||||
parent.parentNode.insertBefore(child, beforeChild);
|
||||
} else {
|
||||
parent.insertBefore(child, beforeChild);
|
||||
}
|
||||
}
|
||||
|
||||
export function removeChildDom(
|
||||
isContainer: boolean,
|
||||
parent: Element | Container,
|
||||
child: Element | Text
|
||||
) {
|
||||
if (isContainer && isComment(parent)) {
|
||||
parent.parentNode.removeChild(child);
|
||||
} else {
|
||||
parent.removeChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏元素
|
||||
export function hideDom(tag: number, element: Element | Text) {
|
||||
export function hideDom(tag: string, dom: Element | Text) {
|
||||
if (tag === DomComponent) {
|
||||
// DomComponent类型
|
||||
const {style} = element;
|
||||
if (style.setProperty && typeof style.setProperty === 'function') {
|
||||
style.setProperty('display', 'none', 'important');
|
||||
} else {
|
||||
style.display = 'none';
|
||||
}
|
||||
dom.style.display = 'none';
|
||||
} else if (tag === DomText) {
|
||||
// text类型
|
||||
element.textContent = '';
|
||||
dom.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 不隐藏元素
|
||||
export function unHideDom(tag: number, element: Element | Text, props: Props) {
|
||||
export function unHideDom(tag: string, dom: Element | Text, props: Props) {
|
||||
if (tag === DomComponent) {
|
||||
// DomComponent类型
|
||||
const style = props.style;
|
||||
let display = null;
|
||||
if (style !== undefined && style !== null && style.hasOwnProperty('display')) {
|
||||
display = style.display;
|
||||
}
|
||||
element.style.display = adjustStyleValue('display', display);
|
||||
dom.style.display = adjustStyleValue('display', props?.style?.display ?? '');
|
||||
} else if (tag === DomText) {
|
||||
// text类型
|
||||
element.textContent = props;
|
||||
dom.textContent = props;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ function updateOneProp(dom, propName, propVal, isNativeTag, isInit = false) {
|
|||
export function compareProps(
|
||||
oldProps: Object,
|
||||
newProps: Object,
|
||||
): null | Array<any> {
|
||||
): Array<any> {
|
||||
let updatesForStyle = {};
|
||||
const toBeDeletedProps = [];
|
||||
const toBeUpdatedProps = [];
|
||||
|
|
|
@ -7,29 +7,44 @@ export function setStyles(dom, styles) {
|
|||
}
|
||||
|
||||
const style = dom.style;
|
||||
const styleKeys = Object.keys(styles);
|
||||
Object.keys(styles).forEach((name) => {
|
||||
const styleVal = styles[name];
|
||||
|
||||
for (let i = 0; i < styleKeys.length; i++) {
|
||||
const styleKey = styleKeys[i];
|
||||
const styleVal = styles[styleKey];
|
||||
const validStyleValue = adjustStyleValue(name, styleVal);
|
||||
|
||||
const validStyleValue = adjustStyleValue(styleKey, styleVal);
|
||||
|
||||
style[styleKey] = validStyleValue;
|
||||
}
|
||||
style[name] = validStyleValue;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. 对空值或布尔值进行适配,转为空字符串
|
||||
* 2. 去掉多余空字符
|
||||
* 不需要加长度单位的 css 属性
|
||||
*/
|
||||
const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp',
|
||||
'lineHeight', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom'];
|
||||
|
||||
function isNeedUnitCSS(propName: string) {
|
||||
return !(noUnitCSS.includes(propName)
|
||||
|| propName.startsWith('borderImage')
|
||||
|| propName.startsWith('flex')
|
||||
|| propName.startsWith('gridRow')
|
||||
|| propName.startsWith('gridColumn')
|
||||
|| propName.startsWith('stroke')
|
||||
|| propName.startsWith('box')
|
||||
|| propName.endsWith('Opacity'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一些没有写单位的样式进行适配,例如:width: 10 => width: 10px
|
||||
* 对空值或布尔值进行适配,转为空字符串
|
||||
* 去掉多余空字符
|
||||
*/
|
||||
export function adjustStyleValue(name, value) {
|
||||
let validValue;
|
||||
let validValue = value;
|
||||
|
||||
if (value === '' || value == null || typeof value === 'boolean') {
|
||||
if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) {
|
||||
validValue = `${value}px`;
|
||||
} else if (value === '' || value == null || typeof value === 'boolean') {
|
||||
validValue = '';
|
||||
} else {
|
||||
validValue = String(value).trim();
|
||||
}
|
||||
|
||||
return validValue;
|
||||
|
|
|
@ -5,6 +5,18 @@ import {
|
|||
import {isInvalidValue} from '../validators/ValidateProps';
|
||||
import {getNamespaceCtx} from '../../renderer/ContextSaver';
|
||||
import {NSS} from '../utils/DomCreator';
|
||||
import {getDomTag} from '../utils/Common';
|
||||
|
||||
const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'baseProfile', 'calcMode', 'clipPathUnits',
|
||||
'contentScriptType', 'contentStyleType', 'diffuseConstant', 'edgeMode', 'externalResourcesRequired', 'filterRes',
|
||||
'filterUnits', 'glyphRef', 'gradientTransform', 'gradientUnits', 'kernelMatrix', 'kernelUnitLength', 'keyPoints',
|
||||
'keySplines', 'keyTimes', 'lengthAdjust', 'limitingConeAngle', 'markerHeight', 'markerUnits', 'markerWidth',
|
||||
'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,',
|
||||
'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits',
|
||||
'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
|
||||
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'stitchTiles', 'surfaceScale',
|
||||
'systemLanguage', 'tableValues', 'targetX', 'targetY', 'textLength', 'viewBox', 'viewTarget', 'xChannelSelector',
|
||||
'yChannelSelector', 'zoomAndPan']);
|
||||
|
||||
/**
|
||||
* 给 dom 设置属性
|
||||
|
@ -20,9 +32,11 @@ export function updateCommonProp(dom: Element, attrName: string, value: any, isN
|
|||
|
||||
if (!isNativeTag || propDetails === null) {
|
||||
// 特殊处理svg的属性,把驼峰式的属性名称转成'-'
|
||||
if (dom.tagName.toLowerCase() === 'svg' || getNamespaceCtx() === NSS.svg) {
|
||||
if (getDomTag(dom) === 'svg' || getNamespaceCtx() === NSS.svg) {
|
||||
if (!svgHumpAttr.has(attrName)) {
|
||||
attrName = convertToLowerCase(attrName);
|
||||
}
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
dom.removeAttribute(attrName);
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import {HorizonDom} from './Interface';
|
||||
import {Props} from '../DOMOperator';
|
||||
|
||||
/**
|
||||
* 获取当前聚焦的 input 或者 textarea 元素
|
||||
* @param currentDoc 指定 document
|
||||
* @param doc 指定 document
|
||||
*/
|
||||
export function getFocusedDom(currentDoc?: Document): HorizonDom | void {
|
||||
export function getFocusedDom(doc?: Document): HorizonDom | void {
|
||||
let currentDocument;
|
||||
if (currentDoc) {
|
||||
currentDocument = currentDoc;
|
||||
if (doc) {
|
||||
currentDocument = doc;
|
||||
} else {
|
||||
if (document) {
|
||||
currentDocument = document;
|
||||
|
@ -63,13 +64,13 @@ export function isDocumentFragment(dom) {
|
|||
return dom.nodeType === 11;
|
||||
}
|
||||
|
||||
export function getRootElement(dom: HorizonDom): HorizonDom {
|
||||
let rootElement = dom;
|
||||
|
||||
while (rootElement.parentNode) {
|
||||
// @ts-ignore
|
||||
rootElement = rootElement.parentNode;
|
||||
export function getDomTag(dom) {
|
||||
return dom.nodeName.toLowerCase();
|
||||
}
|
||||
|
||||
return rootElement;
|
||||
const types = ['button', 'input', 'select', 'textarea'];
|
||||
|
||||
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||
export function shouldAutoFocus(tagName: string, props: Props): boolean {
|
||||
return types.includes(tagName) ? Boolean(props.autoFocus) : false;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ export const NSS = {
|
|||
// 创建DOM元素
|
||||
export function createDom(
|
||||
tagName: string,
|
||||
props: Object,
|
||||
parentNamespace: string,
|
||||
): Element {
|
||||
let dom: Element;
|
||||
|
@ -20,6 +19,5 @@ export function createDom(
|
|||
} else {
|
||||
dom = document.createElement(tagName);
|
||||
}
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ export function validateProps(type, props) {
|
|||
throw new Error('style should be a object.');
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (isDev) {
|
||||
// 校验属性
|
||||
const invalidProps = Object.keys(props).filter(key => !isValidProp(type, key, props[key]));
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {updateCommonProp} from '../DOMPropertiesHandler/UpdateCommonProp';
|
||||
import {getVNodeProps} from '../DOMInternalKeys';
|
||||
import {IProperty} from '../utils/Interface';
|
||||
import {getRootElement} from '../utils/Common';
|
||||
import {isInputValueChanged} from './ValueChangeHandler';
|
||||
|
||||
function getInitValue(dom: HTMLInputElement, properties: IProperty) {
|
||||
|
@ -33,12 +32,12 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, properties: IPr
|
|||
export function updateInputValue(dom: HTMLInputElement, properties: IProperty) {
|
||||
const {value, checked} = properties;
|
||||
|
||||
if (checked != null) {
|
||||
updateCommonProp(dom, 'checked', checked);
|
||||
} else if (value != null) { // 处理 dom.value 逻辑
|
||||
if (value != null) { // 处理 dom.value 逻辑
|
||||
if (dom.value !== String(value)) {
|
||||
dom.value = String(value);
|
||||
}
|
||||
} else if (checked != null) {
|
||||
updateCommonProp(dom, 'checked', checked);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,17 +63,10 @@ export function resetInputValue(dom: HTMLInputElement, properties: IProperty) {
|
|||
const {name, type} = properties;
|
||||
// 如果是 radio,先更新相同 name 的 radio
|
||||
if (type === 'radio' && name != null) {
|
||||
// radio 的根节点
|
||||
const radioRoot = getRootElement(dom);
|
||||
|
||||
const radioList = radioRoot.querySelectorAll(`input[type="radio"]`);
|
||||
const radioList = document.querySelectorAll(`input[type="radio"][name="${name}"]`);
|
||||
|
||||
for (let i = 0; i < radioList.length; i++) {
|
||||
const radio = radioList[i];
|
||||
// @ts-ignore
|
||||
if (radio.name !== name) {
|
||||
continue;
|
||||
}
|
||||
if (radio === dom) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -12,11 +12,7 @@ function getInitValue(properties: IProperty) {
|
|||
// children content存在时,会覆盖defaultValue
|
||||
if (children != null) {
|
||||
// 子节点不是纯文本,则取第一个子节点
|
||||
if (children instanceof Array) {
|
||||
initValue = children[0];
|
||||
} else {
|
||||
initValue = children;
|
||||
}
|
||||
initValue = children instanceof Array ? children[0] : children;
|
||||
}
|
||||
|
||||
// defaultValue 属性未配置,置为空字符串
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
import {throwIfTrue} from '../renderer/utils/throwIfTrue';
|
||||
import {TYPE_ELEMENT, TYPE_PORTAL} from '../renderer/utils/elementType';
|
||||
|
||||
import {isValidElement, HorizonElement} from './HorizonElement';
|
||||
|
||||
// 生成key
|
||||
function getItemKey(item: any, index: number): string {
|
||||
if (typeof item === 'object' && item !== null && item.key != null) {
|
||||
return '.$' + item.key;
|
||||
}
|
||||
// 使用36进制减少生成字符串的长度以节省空间
|
||||
return '.' + index.toString(36);
|
||||
}
|
||||
|
||||
function mapChildrenToArray(
|
||||
children: any,
|
||||
arr: Array<any>,
|
||||
prefix: string,
|
||||
callback?: Function,
|
||||
): number | void {
|
||||
const type = typeof children;
|
||||
switch (type) {
|
||||
// 继承原有规格,undefined和boolean类型按照null处理
|
||||
case 'undefined':
|
||||
case 'boolean':
|
||||
callMapFun(null, arr, prefix, callback);
|
||||
return;
|
||||
case 'number':
|
||||
case 'string':
|
||||
callMapFun(children, arr, prefix, callback);
|
||||
return;
|
||||
case 'object':
|
||||
if (children === null) {
|
||||
callMapFun(null, arr, prefix, callback);
|
||||
return;
|
||||
}
|
||||
const vtype = children.vtype;
|
||||
if (vtype === TYPE_ELEMENT || vtype === TYPE_PORTAL) {
|
||||
callMapFun(children, arr, prefix, callback) ;
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(children)) {
|
||||
processArrayChildren(children, arr, prefix, callback);
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
'Object is invalid as a Horizon child. '
|
||||
);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function processArrayChildren(
|
||||
children: any,
|
||||
arr: Array<any>,
|
||||
prefix: string,
|
||||
callback: Function,
|
||||
) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const childItem = children[i];
|
||||
const nextPrefix = prefix + getItemKey(childItem, i);
|
||||
mapChildrenToArray(
|
||||
childItem,
|
||||
arr,
|
||||
nextPrefix,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function callMapFun(
|
||||
children: any,
|
||||
arr: Array<any>,
|
||||
prefix: string,
|
||||
callback: Function,
|
||||
) {
|
||||
let mappedChild = callback(children);
|
||||
if (Array.isArray(mappedChild)) {
|
||||
// 维持原有规格,如果callback返回结果是数组,处理函数修改为返回数组item
|
||||
processArrayChildren(mappedChild, arr, prefix + '/', subChild => subChild);
|
||||
} else if (mappedChild !== null && mappedChild !== undefined) {
|
||||
// 给一个key值,确保返回的对象一定带有key
|
||||
if (isValidElement(mappedChild)) {
|
||||
const childKey = prefix === '' ? getItemKey(children, 0) : '';
|
||||
const mappedKey = getItemKey(mappedChild, 0);
|
||||
const newKey = prefix + childKey + (mappedChild.key && mappedKey !== getItemKey(children, 0)
|
||||
? '.$' + mappedChild.key
|
||||
: '');
|
||||
// 返回一个修改key的children
|
||||
mappedChild = HorizonElement(
|
||||
mappedChild.type,
|
||||
newKey,
|
||||
mappedChild.ref,
|
||||
mappedChild._vNode,
|
||||
mappedChild.props,
|
||||
);
|
||||
}
|
||||
arr.push(mappedChild);
|
||||
}
|
||||
}
|
||||
|
||||
// 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg
|
||||
function mapChildren(
|
||||
children: any,
|
||||
func: Function,
|
||||
context?: any,
|
||||
): Array<any> {
|
||||
if (children === null || children === undefined) {
|
||||
return children;
|
||||
}
|
||||
let count = 0;
|
||||
const result = [];
|
||||
mapChildrenToArray(children, result, '', (child) => {
|
||||
return func.call(context, child, count++);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const Children = {
|
||||
forEach: (children, func, context?: any) => {
|
||||
// 不返回数组即可
|
||||
mapChildren(children, func, context);
|
||||
},
|
||||
map: mapChildren,
|
||||
// 并非所有元素都会计数,只计数调用callMapFun函数次数
|
||||
count: (children) => {
|
||||
let n = 0;
|
||||
mapChildren(children, () => {
|
||||
n++;
|
||||
});
|
||||
return n;
|
||||
},
|
||||
only: (children) => {
|
||||
throwIfTrue(
|
||||
!isValidElement(children),
|
||||
'Horizon.Children.only function received invalid element.'
|
||||
);
|
||||
return children;
|
||||
},
|
||||
toArray: (children) => {
|
||||
const result = [];
|
||||
mapChildrenToArray(children, result, '', child => child);
|
||||
return result;
|
||||
},
|
||||
}
|
||||
|
||||
export {
|
||||
Children
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
import {
|
||||
TYPE_FRAGMENT,
|
||||
TYPE_PROFILER,
|
||||
TYPE_STRICT_MODE,
|
||||
TYPE_SUSPENSE,
|
||||
} from '../renderer/utils/elementType';
|
||||
|
||||
import {Component, PureComponent} from '../renderer/components/BaseClassComponent';
|
||||
import {createRef} from '../renderer/components/CreateRef';
|
||||
import {Children} from './ChildrenUtil';
|
||||
import {
|
||||
createElement,
|
||||
cloneElement,
|
||||
isValidElement,
|
||||
} from './HorizonElement';
|
||||
import {createContext} from '../renderer/components/context/CreateContext';
|
||||
import {lazy} from '../renderer/components/Lazy';
|
||||
import {forwardRef} from '../renderer/components/ForwardRef';
|
||||
import {memo} from '../renderer/components/Memo';
|
||||
import hookMapping from '../renderer/hooks/HookMapping';
|
||||
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useRef,
|
||||
useState,
|
||||
} from '../renderer/hooks/HookExternal';
|
||||
|
||||
export {
|
||||
Children,
|
||||
createRef,
|
||||
Component,
|
||||
PureComponent,
|
||||
createContext,
|
||||
forwardRef,
|
||||
lazy,
|
||||
memo,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useRef,
|
||||
useState,
|
||||
TYPE_FRAGMENT as Fragment,
|
||||
TYPE_PROFILER as Profiler,
|
||||
TYPE_STRICT_MODE as StrictMode,
|
||||
TYPE_SUSPENSE as Suspense,
|
||||
createElement,
|
||||
cloneElement,
|
||||
isValidElement,
|
||||
hookMapping,
|
||||
};
|
|
@ -0,0 +1,82 @@
|
|||
import { TYPE_ELEMENT } from '../renderer/utils/elementType';
|
||||
import ProcessingVNode from '../renderer/vnode/ProcessingVNode';
|
||||
|
||||
|
||||
/**
|
||||
* vtype, 节点的类型,这里固定是element
|
||||
* type,保存dom节点的名称或者组件的函数地址
|
||||
* key key属性
|
||||
* ref ref属性
|
||||
* props 其他常规属性
|
||||
*/
|
||||
export function HorizonElement(type, key, ref, vNode, props) {
|
||||
return {
|
||||
// Horizon元素标识符
|
||||
vtype: TYPE_ELEMENT,
|
||||
|
||||
// 属于元素的内置属性
|
||||
type: type,
|
||||
key: key,
|
||||
ref: ref,
|
||||
props: props,
|
||||
|
||||
// 记录负责创建此元素的组件。
|
||||
_vNode: vNode,
|
||||
};
|
||||
};
|
||||
|
||||
function isValidKey(key) {
|
||||
return key !== 'key' && key !== 'ref' && key !== '__source';
|
||||
}
|
||||
|
||||
function buildElement(isClone, type, setting, ...children) {
|
||||
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null
|
||||
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
|
||||
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
|
||||
const props = isClone ? {...type.props} : {};
|
||||
let vNode = isClone ? type._vNode : ProcessingVNode.val;
|
||||
|
||||
if (setting != null) {
|
||||
Object.keys(setting).forEach(k => {
|
||||
if (isValidKey(k)) {
|
||||
props[k] = setting[k];
|
||||
}
|
||||
});
|
||||
if (setting.ref !== undefined && isClone) {
|
||||
vNode = ProcessingVNode.val;
|
||||
}
|
||||
}
|
||||
|
||||
if (children.length) {
|
||||
props.children = children.length === 1 ? children[0] : children;
|
||||
}
|
||||
const element = isClone ? type.type : type;
|
||||
//合并默认属性
|
||||
if (element && element.defaultProps) {
|
||||
mergeDefault(props, element.defaultProps);
|
||||
}
|
||||
|
||||
return HorizonElement(element, key, ref, vNode, props);
|
||||
}
|
||||
|
||||
//创建Element结构体,供JSX编译时调用
|
||||
export function createElement(type, setting, ...children) {
|
||||
return buildElement(false, type, setting, ...children);
|
||||
}
|
||||
|
||||
function mergeDefault(sourceObj, defaultObj) {
|
||||
Object.keys(defaultObj).forEach((key) => {
|
||||
if (sourceObj[key] === undefined) {
|
||||
sourceObj[key] = defaultObj[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function cloneElement(element, setting, ...children) {
|
||||
return buildElement(true, element, setting, ...children);
|
||||
}
|
||||
|
||||
// 检测结构体是否为合法的Element
|
||||
export function isValidElement(element) {
|
||||
return !!(element && element.vtype === TYPE_ELEMENT);
|
||||
}
|
Loading…
Reference in New Issue