main
js 181 lines 4.74 KB
Raw
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 * as React from 'react';
9 import ReactVersion from 'shared/ReactVersion';
10 import {LegacyRoot, ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';
11 import {
12 createContainer,
13 updateContainerSync,
14 injectIntoDevTools,
15 flushSyncWork,
16 defaultOnUncaughtError,
17 defaultOnCaughtError,
18 defaultOnRecoverableError,
19 } from 'react-reconciler/src/ReactFiberReconciler';
20
21 import Transform from 'art/core/transform';
22 import Mode from 'art/modes/current';
23 import FastNoSideEffects from 'art/modes/fast-noSideEffects';
24 import {disableLegacyMode} from 'shared/ReactFeatureFlags';
25
26 import {TYPES, childrenAsString} from './ReactARTInternals';
27
28 function defaultOnDefaultTransitionIndicator() {
29 // Noop
30 }
31
32 Mode.setCurrent(
33 // Change to 'art/modes/dom' for easier debugging via SVG
34 FastNoSideEffects,
35 );
36
37 /** Declarative fill-type objects; API design not finalized */
38
39 const slice = Array.prototype.slice;
40
41 class LinearGradient {
42 constructor(stops, x1, y1, x2, y2) {
43 this._args = slice.call(arguments);
44 }
45
46 applyFill(node) {
47 node.fillLinear.apply(node, this._args);
48 }
49 }
50
51 class RadialGradient {
52 constructor(stops, fx, fy, rx, ry, cx, cy) {
53 this._args = slice.call(arguments);
54 }
55
56 applyFill(node) {
57 node.fillRadial.apply(node, this._args);
58 }
59 }
60
61 class Pattern {
62 constructor(url, width, height, left, top) {
63 this._args = slice.call(arguments);
64 }
65
66 applyFill(node) {
67 node.fillImage.apply(node, this._args);
68 }
69 }
70
71 /** React Components */
72
73 class Surface extends React.Component {
74 componentDidMount() {
75 const {height, width} = this.props;
76
77 this._surface = Mode.Surface(+width, +height, this._tagRef);
78
79 this._mountNode = createContainer(
80 this._surface,
81 disableLegacyMode ? ConcurrentRoot : LegacyRoot,
82 null,
83 false,
84 false,
85 '',
86 defaultOnUncaughtError,
87 defaultOnCaughtError,
88 defaultOnRecoverableError,
89 defaultOnDefaultTransitionIndicator,
90 null,
91 );
92 // We synchronously flush updates coming from above so that they commit together
93 // and so that refs resolve before the parent life cycles.
94 updateContainerSync(this.props.children, this._mountNode, this);
95 flushSyncWork();
96 }
97
98 componentDidUpdate(prevProps, prevState) {
99 const props = this.props;
100
101 if (props.height !== prevProps.height || props.width !== prevProps.width) {
102 this._surface.resize(+props.width, +props.height);
103 }
104
105 // We synchronously flush updates coming from above so that they commit together
106 // and so that refs resolve before the parent life cycles.
107 updateContainerSync(this.props.children, this._mountNode, this);
108 flushSyncWork();
109
110 if (this._surface.render) {
111 this._surface.render();
112 }
113 }
114
115 componentWillUnmount() {
116 // We synchronously flush updates coming from above so that they commit together
117 // and so that refs resolve before the parent life cycles.
118 updateContainerSync(null, this._mountNode, this);
119 flushSyncWork();
120 }
121
122 render() {
123 // This is going to be a placeholder because we don't know what it will
124 // actually resolve to because ART may render canvas, vml or svg tags here.
125 // We only allow a subset of properties since others might conflict with
126 // ART's properties.
127 const props = this.props;
128
129 // TODO: ART's Canvas Mode overrides surface title and cursor
130 const Tag = Mode.Surface.tagName;
131
132 return (
133 <Tag
134 ref={ref => (this._tagRef = ref)}
135 accessKey={props.accessKey}
136 className={props.className}
137 draggable={props.draggable}
138 role={props.role}
139 style={props.style}
140 tabIndex={props.tabIndex}
141 title={props.title}
142 />
143 );
144 }
145 }
146
147 class Text extends React.Component {
148 constructor(props) {
149 super(props);
150 // We allow reading these props. Ideally we could expose the Text node as
151 // ref directly.
152 ['height', 'width', 'x', 'y'].forEach(key => {
153 Object.defineProperty(this, key, {
154 get: function () {
155 return this._text ? this._text[key] : undefined;
156 },
157 });
158 });
159 }
160 render() {
161 // This means you can't have children that render into strings...
162 const T = TYPES.TEXT;
163 return (
164 <T {...this.props} ref={t => (this._text = t)}>
165 {childrenAsString(this.props.children)}
166 </T>
167 );
168 }
169 }
170
171 injectIntoDevTools();
172
173 /** API */
174
175 export const ClippingRectangle = TYPES.CLIPPING_RECTANGLE;
176 export const Group = TYPES.GROUP;
177 export const Shape = TYPES.SHAPE;
178 export const Path = Mode.Path;
179 export {LinearGradient, Pattern, RadialGradient, Surface, Text, Transform};
180
181 export {ReactVersion as version};