1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. import Transform from 'art/core/transform';
    
  9. import Mode from 'art/modes/current';
    
  10. 
    
  11. import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
    
  12. 
    
  13. import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
    
  14. 
    
  15. const pooledTransform = new Transform();
    
  16. 
    
  17. const NO_CONTEXT = {};
    
  18. if (__DEV__) {
    
  19.   Object.freeze(NO_CONTEXT);
    
  20. }
    
  21. 
    
  22. /** Helper Methods */
    
  23. 
    
  24. function addEventListeners(instance, type, listener) {
    
  25.   // We need to explicitly unregister before unmount.
    
  26.   // For this reason we need to track subscriptions.
    
  27.   if (!instance._listeners) {
    
  28.     instance._listeners = {};
    
  29.     instance._subscriptions = {};
    
  30.   }
    
  31. 
    
  32.   instance._listeners[type] = listener;
    
  33. 
    
  34.   if (listener) {
    
  35.     if (!instance._subscriptions[type]) {
    
  36.       instance._subscriptions[type] = instance.subscribe(
    
  37.         type,
    
  38.         createEventHandler(instance),
    
  39.         instance,
    
  40.       );
    
  41.     }
    
  42.   } else {
    
  43.     if (instance._subscriptions[type]) {
    
  44.       instance._subscriptions[type]();
    
  45.       delete instance._subscriptions[type];
    
  46.     }
    
  47.   }
    
  48. }
    
  49. 
    
  50. function createEventHandler(instance) {
    
  51.   return function handleEvent(event) {
    
  52.     const listener = instance._listeners[event.type];
    
  53. 
    
  54.     if (!listener) {
    
  55.       // Noop
    
  56.     } else if (typeof listener === 'function') {
    
  57.       listener.call(instance, event);
    
  58.     } else if (listener.handleEvent) {
    
  59.       listener.handleEvent(event);
    
  60.     }
    
  61.   };
    
  62. }
    
  63. 
    
  64. function destroyEventListeners(instance) {
    
  65.   if (instance._subscriptions) {
    
  66.     for (const type in instance._subscriptions) {
    
  67.       instance._subscriptions[type]();
    
  68.     }
    
  69.   }
    
  70. 
    
  71.   instance._subscriptions = null;
    
  72.   instance._listeners = null;
    
  73. }
    
  74. 
    
  75. function getScaleX(props) {
    
  76.   if (props.scaleX != null) {
    
  77.     return props.scaleX;
    
  78.   } else if (props.scale != null) {
    
  79.     return props.scale;
    
  80.   } else {
    
  81.     return 1;
    
  82.   }
    
  83. }
    
  84. 
    
  85. function getScaleY(props) {
    
  86.   if (props.scaleY != null) {
    
  87.     return props.scaleY;
    
  88.   } else if (props.scale != null) {
    
  89.     return props.scale;
    
  90.   } else {
    
  91.     return 1;
    
  92.   }
    
  93. }
    
  94. 
    
  95. function isSameFont(oldFont, newFont) {
    
  96.   if (oldFont === newFont) {
    
  97.     return true;
    
  98.   } else if (typeof newFont === 'string' || typeof oldFont === 'string') {
    
  99.     return false;
    
  100.   } else {
    
  101.     return (
    
  102.       newFont.fontSize === oldFont.fontSize &&
    
  103.       newFont.fontStyle === oldFont.fontStyle &&
    
  104.       newFont.fontVariant === oldFont.fontVariant &&
    
  105.       newFont.fontWeight === oldFont.fontWeight &&
    
  106.       newFont.fontFamily === oldFont.fontFamily
    
  107.     );
    
  108.   }
    
  109. }
    
  110. 
    
  111. /** Render Methods */
    
  112. 
    
  113. function applyClippingRectangleProps(instance, props, prevProps = {}) {
    
  114.   applyNodeProps(instance, props, prevProps);
    
  115. 
    
  116.   instance.width = props.width;
    
  117.   instance.height = props.height;
    
  118. }
    
  119. 
    
  120. function applyGroupProps(instance, props, prevProps = {}) {
    
  121.   applyNodeProps(instance, props, prevProps);
    
  122. 
    
  123.   instance.width = props.width;
    
  124.   instance.height = props.height;
    
  125. }
    
  126. 
    
  127. function applyNodeProps(instance, props, prevProps = {}) {
    
  128.   const scaleX = getScaleX(props);
    
  129.   const scaleY = getScaleY(props);
    
  130. 
    
  131.   pooledTransform
    
  132.     .transformTo(1, 0, 0, 1, 0, 0)
    
  133.     .move(props.x || 0, props.y || 0)
    
  134.     .rotate(props.rotation || 0, props.originX, props.originY)
    
  135.     .scale(scaleX, scaleY, props.originX, props.originY);
    
  136. 
    
  137.   if (props.transform != null) {
    
  138.     pooledTransform.transform(props.transform);
    
  139.   }
    
  140. 
    
  141.   if (
    
  142.     instance.xx !== pooledTransform.xx ||
    
  143.     instance.yx !== pooledTransform.yx ||
    
  144.     instance.xy !== pooledTransform.xy ||
    
  145.     instance.yy !== pooledTransform.yy ||
    
  146.     instance.x !== pooledTransform.x ||
    
  147.     instance.y !== pooledTransform.y
    
  148.   ) {
    
  149.     instance.transformTo(pooledTransform);
    
  150.   }
    
  151. 
    
  152.   if (props.cursor !== prevProps.cursor || props.title !== prevProps.title) {
    
  153.     instance.indicate(props.cursor, props.title);
    
  154.   }
    
  155. 
    
  156.   if (instance.blend && props.opacity !== prevProps.opacity) {
    
  157.     instance.blend(props.opacity == null ? 1 : props.opacity);
    
  158.   }
    
  159. 
    
  160.   if (props.visible !== prevProps.visible) {
    
  161.     if (props.visible == null || props.visible) {
    
  162.       instance.show();
    
  163.     } else {
    
  164.       instance.hide();
    
  165.     }
    
  166.   }
    
  167. 
    
  168.   for (const type in EVENT_TYPES) {
    
  169.     addEventListeners(instance, EVENT_TYPES[type], props[type]);
    
  170.   }
    
  171. }
    
  172. 
    
  173. function applyRenderableNodeProps(instance, props, prevProps = {}) {
    
  174.   applyNodeProps(instance, props, prevProps);
    
  175. 
    
  176.   if (prevProps.fill !== props.fill) {
    
  177.     if (props.fill && props.fill.applyFill) {
    
  178.       props.fill.applyFill(instance);
    
  179.     } else {
    
  180.       instance.fill(props.fill);
    
  181.     }
    
  182.   }
    
  183.   if (
    
  184.     prevProps.stroke !== props.stroke ||
    
  185.     prevProps.strokeWidth !== props.strokeWidth ||
    
  186.     prevProps.strokeCap !== props.strokeCap ||
    
  187.     prevProps.strokeJoin !== props.strokeJoin ||
    
  188.     // TODO: Consider deep check of stokeDash; may benefit VML in IE.
    
  189.     prevProps.strokeDash !== props.strokeDash
    
  190.   ) {
    
  191.     instance.stroke(
    
  192.       props.stroke,
    
  193.       props.strokeWidth,
    
  194.       props.strokeCap,
    
  195.       props.strokeJoin,
    
  196.       props.strokeDash,
    
  197.     );
    
  198.   }
    
  199. }
    
  200. 
    
  201. function applyShapeProps(instance, props, prevProps = {}) {
    
  202.   applyRenderableNodeProps(instance, props, prevProps);
    
  203. 
    
  204.   const path = props.d || childrenAsString(props.children);
    
  205. 
    
  206.   const prevDelta = instance._prevDelta;
    
  207.   const prevPath = instance._prevPath;
    
  208. 
    
  209.   if (
    
  210.     path !== prevPath ||
    
  211.     path.delta !== prevDelta ||
    
  212.     prevProps.height !== props.height ||
    
  213.     prevProps.width !== props.width
    
  214.   ) {
    
  215.     instance.draw(path, props.width, props.height);
    
  216. 
    
  217.     instance._prevDelta = path.delta;
    
  218.     instance._prevPath = path;
    
  219.   }
    
  220. }
    
  221. 
    
  222. function applyTextProps(instance, props, prevProps = {}) {
    
  223.   applyRenderableNodeProps(instance, props, prevProps);
    
  224. 
    
  225.   const string = props.children;
    
  226. 
    
  227.   if (
    
  228.     instance._currentString !== string ||
    
  229.     !isSameFont(props.font, prevProps.font) ||
    
  230.     props.alignment !== prevProps.alignment ||
    
  231.     props.path !== prevProps.path
    
  232.   ) {
    
  233.     instance.draw(string, props.font, props.alignment, props.path);
    
  234. 
    
  235.     instance._currentString = string;
    
  236.   }
    
  237. }
    
  238. 
    
  239. export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';
    
  240. export * from 'react-reconciler/src/ReactFiberConfigWithNoHydration';
    
  241. export * from 'react-reconciler/src/ReactFiberConfigWithNoScopes';
    
  242. export * from 'react-reconciler/src/ReactFiberConfigWithNoTestSelectors';
    
  243. export * from 'react-reconciler/src/ReactFiberConfigWithNoMicrotasks';
    
  244. export * from 'react-reconciler/src/ReactFiberConfigWithNoResources';
    
  245. export * from 'react-reconciler/src/ReactFiberConfigWithNoSingletons';
    
  246. 
    
  247. export function appendInitialChild(parentInstance, child) {
    
  248.   if (typeof child === 'string') {
    
  249.     // Noop for string children of Text (eg <Text>{'foo'}{'bar'}</Text>)
    
  250.     throw new Error('Text children should already be flattened.');
    
  251.   }
    
  252. 
    
  253.   child.inject(parentInstance);
    
  254. }
    
  255. 
    
  256. export function createInstance(type, props, internalInstanceHandle) {
    
  257.   let instance;
    
  258. 
    
  259.   switch (type) {
    
  260.     case TYPES.CLIPPING_RECTANGLE:
    
  261.       instance = Mode.ClippingRectangle();
    
  262.       instance._applyProps = applyClippingRectangleProps;
    
  263.       break;
    
  264.     case TYPES.GROUP:
    
  265.       instance = Mode.Group();
    
  266.       instance._applyProps = applyGroupProps;
    
  267.       break;
    
  268.     case TYPES.SHAPE:
    
  269.       instance = Mode.Shape();
    
  270.       instance._applyProps = applyShapeProps;
    
  271.       break;
    
  272.     case TYPES.TEXT:
    
  273.       instance = Mode.Text(
    
  274.         props.children,
    
  275.         props.font,
    
  276.         props.alignment,
    
  277.         props.path,
    
  278.       );
    
  279.       instance._applyProps = applyTextProps;
    
  280.       break;
    
  281.   }
    
  282. 
    
  283.   if (!instance) {
    
  284.     throw new Error(`ReactART does not support the type "${type}"`);
    
  285.   }
    
  286. 
    
  287.   instance._applyProps(instance, props);
    
  288. 
    
  289.   return instance;
    
  290. }
    
  291. 
    
  292. export function createTextInstance(
    
  293.   text,
    
  294.   rootContainerInstance,
    
  295.   internalInstanceHandle,
    
  296. ) {
    
  297.   return text;
    
  298. }
    
  299. 
    
  300. export function finalizeInitialChildren(domElement, type, props) {
    
  301.   return false;
    
  302. }
    
  303. 
    
  304. export function getPublicInstance(instance) {
    
  305.   return instance;
    
  306. }
    
  307. 
    
  308. export function prepareForCommit() {
    
  309.   // Noop
    
  310.   return null;
    
  311. }
    
  312. 
    
  313. export function resetAfterCommit() {
    
  314.   // Noop
    
  315. }
    
  316. 
    
  317. export function resetTextContent(domElement) {
    
  318.   // Noop
    
  319. }
    
  320. 
    
  321. export function getRootHostContext() {
    
  322.   return NO_CONTEXT;
    
  323. }
    
  324. 
    
  325. export function getChildHostContext() {
    
  326.   return NO_CONTEXT;
    
  327. }
    
  328. 
    
  329. export const scheduleTimeout = setTimeout;
    
  330. export const cancelTimeout = clearTimeout;
    
  331. export const noTimeout = -1;
    
  332. 
    
  333. export function shouldSetTextContent(type, props) {
    
  334.   return (
    
  335.     typeof props.children === 'string' || typeof props.children === 'number'
    
  336.   );
    
  337. }
    
  338. 
    
  339. export function getCurrentEventPriority() {
    
  340.   return DefaultEventPriority;
    
  341. }
    
  342. 
    
  343. export function shouldAttemptEagerTransition() {
    
  344.   return false;
    
  345. }
    
  346. 
    
  347. // The ART renderer is secondary to the React DOM renderer.
    
  348. export const isPrimaryRenderer = false;
    
  349. 
    
  350. // The ART renderer shouldn't trigger missing act() warnings
    
  351. export const warnsIfNotActing = false;
    
  352. 
    
  353. export const supportsMutation = true;
    
  354. 
    
  355. export function appendChild(parentInstance, child) {
    
  356.   if (child.parentNode === parentInstance) {
    
  357.     child.eject();
    
  358.   }
    
  359.   child.inject(parentInstance);
    
  360. }
    
  361. 
    
  362. export function appendChildToContainer(parentInstance, child) {
    
  363.   if (child.parentNode === parentInstance) {
    
  364.     child.eject();
    
  365.   }
    
  366.   child.inject(parentInstance);
    
  367. }
    
  368. 
    
  369. export function insertBefore(parentInstance, child, beforeChild) {
    
  370.   if (child === beforeChild) {
    
  371.     throw new Error('ReactART: Can not insert node before itself');
    
  372.   }
    
  373. 
    
  374.   child.injectBefore(beforeChild);
    
  375. }
    
  376. 
    
  377. export function insertInContainerBefore(parentInstance, child, beforeChild) {
    
  378.   if (child === beforeChild) {
    
  379.     throw new Error('ReactART: Can not insert node before itself');
    
  380.   }
    
  381. 
    
  382.   child.injectBefore(beforeChild);
    
  383. }
    
  384. 
    
  385. export function removeChild(parentInstance, child) {
    
  386.   destroyEventListeners(child);
    
  387.   child.eject();
    
  388. }
    
  389. 
    
  390. export function removeChildFromContainer(parentInstance, child) {
    
  391.   destroyEventListeners(child);
    
  392.   child.eject();
    
  393. }
    
  394. 
    
  395. export function commitTextUpdate(textInstance, oldText, newText) {
    
  396.   // Noop
    
  397. }
    
  398. 
    
  399. export function commitMount(instance, type, newProps) {
    
  400.   // Noop
    
  401. }
    
  402. 
    
  403. export function commitUpdate(
    
  404.   instance,
    
  405.   updatePayload,
    
  406.   type,
    
  407.   oldProps,
    
  408.   newProps,
    
  409. ) {
    
  410.   instance._applyProps(instance, newProps, oldProps);
    
  411. }
    
  412. 
    
  413. export function hideInstance(instance) {
    
  414.   instance.hide();
    
  415. }
    
  416. 
    
  417. export function hideTextInstance(textInstance) {
    
  418.   // Noop
    
  419. }
    
  420. 
    
  421. export function unhideInstance(instance, props) {
    
  422.   if (props.visible == null || props.visible) {
    
  423.     instance.show();
    
  424.   }
    
  425. }
    
  426. 
    
  427. export function unhideTextInstance(textInstance, text): void {
    
  428.   // Noop
    
  429. }
    
  430. 
    
  431. export function clearContainer(container) {
    
  432.   // TODO Implement this
    
  433. }
    
  434. 
    
  435. export function getInstanceFromNode(node) {
    
  436.   throw new Error('Not implemented.');
    
  437. }
    
  438. 
    
  439. export function beforeActiveInstanceBlur(internalInstanceHandle: Object) {
    
  440.   // noop
    
  441. }
    
  442. 
    
  443. export function afterActiveInstanceBlur() {
    
  444.   // noop
    
  445. }
    
  446. 
    
  447. export function preparePortalMount(portalInstance: any): void {
    
  448.   // noop
    
  449. }
    
  450. 
    
  451. // eslint-disable-next-line no-undef
    
  452. export function detachDeletedInstance(node: Instance): void {
    
  453.   // noop
    
  454. }
    
  455. 
    
  456. export function requestPostPaintCallback(callback: (time: number) => void) {
    
  457.   // noop
    
  458. }
    
  459. 
    
  460. export function maySuspendCommit(type, props) {
    
  461.   return false;
    
  462. }
    
  463. 
    
  464. export function preloadInstance(type, props) {
    
  465.   // Return true to indicate it's already loaded
    
  466.   return true;
    
  467. }
    
  468. 
    
  469. export function startSuspendingCommit() {}
    
  470. 
    
  471. export function suspendInstance(type, props) {}
    
  472. 
    
  473. export function waitForCommitToBeReady() {
    
  474.   return null;
    
  475. }
    
  476. 
    
  477. export const NotPendingTransition = null;