UNPKG

@quick-tv/react

Version:

QuickTV react framework

203 lines (182 loc) 5.24 kB
/* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Fiber } from '@hippy/react-reconciler'; import ElementNode from '../dom/element-node'; type RootContainer = any; // Single root instance let rootContainer: RootContainer; let rootViewId: number; const fiberNodeCache = new Map(); function setRootContainer(rootId: number, root: RootContainer) { rootViewId = rootId; rootContainer = root; } function getRootContainer(): RootContainer { return rootContainer; } function getRootViewId() { if (!rootViewId) { throw new Error('getRootViewId must execute after setRootContainer'); } return rootViewId; } function findNodeByCondition(condition: (node: Fiber) => boolean): null | Fiber { if (!rootContainer) { return null; } const { current: root } = rootContainer; const queue: Fiber[] = [root]; while (queue.length) { const targetNode = queue.shift(); if (!targetNode) { break; } if (condition(targetNode)) { return targetNode; } if (targetNode.child) { queue.push(targetNode.child); } if (targetNode.sibling) { queue.push(targetNode.sibling); } } return null; } function findNodeById(nodeId: number) { return findNodeByCondition(node => node.stateNode && node.stateNode.nodeId === nodeId); } /** * preCacheFiberNode - cache FiberNode * @param {Fiber} targetNode * @param {number} nodeId */ function preCacheFiberNode(targetNode: Fiber, nodeId: number): void { fiberNodeCache.set(nodeId, targetNode); } /** * unCacheFiberNode - delete Fiber Node from cache * @param {number} nodeId */ function unCacheFiberNode(nodeId: number): void { fiberNodeCache.delete(nodeId); } /** * getElementFromFiber - get ElementNode by Fiber * @param {number} fiberNode */ function getElementFromFiber(fiberNode: Fiber) { return fiberNode?.stateNode || null; } /** * getFiberNodeFromId - get FiberNode by nodeId * @param {number} nodeId */ function getFiberNodeFromId(nodeId: number) { return fiberNodeCache.get(nodeId) || null; } /** * unCacheFiberNodeOnIdle - recursively delete FiberNode cache on idle * @param {ElementNode|number} node */ function unCacheFiberNodeOnIdle(node: ElementNode | number) { requestIdleCallback((deadline: { timeRemaining: Function, didTimeout: boolean }) => { // if idle time exists or callback invoked when timeout if (deadline.timeRemaining() > 0 || deadline.didTimeout) { recursivelyUnCacheFiberNode(node); } }, { timeout: 50 }); // 50ms to avoid blocking user operation } /** * recursivelyUnCacheFiberNode - delete ViewNode cache recursively * @param {ElementNode|number} node */ function recursivelyUnCacheFiberNode(node: ElementNode | number): void { if (typeof node === 'number') { // if leaf node (e.g. text node) unCacheFiberNode(node); } else if (node) { unCacheFiberNode(node.nodeId); if (Array.isArray(node.childNodes)) { node.childNodes.forEach(node => recursivelyUnCacheFiberNode(node as ElementNode)); } } } /** * requestIdleCallback polyfill * @param {Function} cb * @param {{timeout: number}} [options] */ function requestIdleCallback( cb: IdleRequestCallback, options?: { timeout: number }, ): ReturnType<typeof setTimeout> | number { if (!global.requestIdleCallback) { return setTimeout(() => { cb({ didTimeout: false, timeRemaining() { return Infinity; }, }); }, 1); } return global.requestIdleCallback(cb, options); } /** * cancelIdleCallback polyfill * @param {ReturnType<typeof requestIdleCallback>} id */ function cancelIdleCallback(id: ReturnType<typeof requestIdleCallback>): void { if (!global.cancelIdleCallback) { clearTimeout(id as ReturnType<typeof setTimeout>); } else { global.cancelIdleCallback(id as number); } } interface EventNamesMap { [propName: string]: string[]; } // Event Names map const NATIVE_EVENT = 1; const eventNamesMap: EventNamesMap = { // TODO pressIn, pressOut will be deprecated in future // onPressIn: ['onPressIn', 'onTouchDown'], // onPressOut: ['onPressOut', 'onTouchEnd'], onTouchStart: ['onTouchStart', 'onTouchDown'], onPress: ['onPress', 'onClick'], }; export { NATIVE_EVENT, eventNamesMap, requestIdleCallback, cancelIdleCallback, setRootContainer, getRootContainer, getRootViewId, findNodeByCondition, findNodeById, preCacheFiberNode, unCacheFiberNode, getFiberNodeFromId, getElementFromFiber, unCacheFiberNodeOnIdle, recursivelyUnCacheFiberNode, };