From 673c7617bcf90a892a0afc2c0d9cf9c0493fdf27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 9 Feb 2023 09:36:08 -0800 Subject: [PATCH] Implement DOMRect and DOMRectReadOnly matching Web Summary: This adds the `DOMRect` and `DOMRectReadOnly` classes to React Native, mostly following the Web spec. This is a requirement for `node.getBoundingClientRect()`, which we'll implement in React (in https://github.com/facebook/react/blob/main/packages/react-native-renderer/src/ReactFabricHostConfig.js#L134-L323). Changelog: [General][Added] - Added Web-compatible `DOMRect` and `DOMRectReadOnly` classes to the global scope. Reviewed By: ryancat Differential Revision: D42963222 fbshipit-source-id: bf2ed15bfbfd71822cb6f969f8cc0a67c7834333 --- Libraries/Core/InitializeCore.js | 1 + Libraries/Core/setUpDOM.js | 18 +++ Libraries/DOM/Geometry/DOMRect.js | 82 ++++++++++ Libraries/DOM/Geometry/DOMRectReadOnly.js | 188 ++++++++++++++++++++++ flow/global.js | 4 + 5 files changed, 293 insertions(+) create mode 100644 Libraries/Core/setUpDOM.js create mode 100644 Libraries/DOM/Geometry/DOMRect.js create mode 100644 Libraries/DOM/Geometry/DOMRectReadOnly.js diff --git a/Libraries/Core/InitializeCore.js b/Libraries/Core/InitializeCore.js index 827edefeade6ad..25377f6890cb90 100644 --- a/Libraries/Core/InitializeCore.js +++ b/Libraries/Core/InitializeCore.js @@ -27,6 +27,7 @@ const start = Date.now(); require('./setUpGlobals'); +require('./setUpDOM'); require('./setUpPerformance'); require('./setUpErrorHandling'); require('./polyfillPromise'); diff --git a/Libraries/Core/setUpDOM.js b/Libraries/Core/setUpDOM.js new file mode 100644 index 00000000000000..e25aa01371329c --- /dev/null +++ b/Libraries/Core/setUpDOM.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +import DOMRect from '../DOM/Geometry/DOMRect'; +import DOMRectReadOnly from '../DOM/Geometry/DOMRectReadOnly'; + +// $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it +global.DOMRect = DOMRect; + +// $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it +global.DOMRectReadOnly = DOMRectReadOnly; diff --git a/Libraries/DOM/Geometry/DOMRect.js b/Libraries/DOM/Geometry/DOMRect.js new file mode 100644 index 00000000000000..07af232a399891 --- /dev/null +++ b/Libraries/DOM/Geometry/DOMRect.js @@ -0,0 +1,82 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +/** + * The JSDoc comments in this file have been extracted from [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect). + * Content by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect/contributors.txt), + * licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). + */ + +import DOMRectReadOnly, {type DOMRectLike} from './DOMRectReadOnly'; + +// flowlint unsafe-getters-setters:off + +/** + * A `DOMRect` describes the size and position of a rectangle. + * The type of box represented by the `DOMRect` is specified by the method or property that returned it. + * + * This is a (mostly) spec-compliant version of `DOMRect` (https://developer.mozilla.org/en-US/docs/Web/API/DOMRect). + */ +export default class DOMRect extends DOMRectReadOnly { + /** + * The x coordinate of the `DOMRect`'s origin. + */ + get x(): number { + return this.__getInternalX(); + } + + set x(x: ?number) { + this.__setInternalX(x); + } + + /** + * The y coordinate of the `DOMRect`'s origin. + */ + get y(): number { + return this.__getInternalY(); + } + + set y(y: ?number) { + this.__setInternalY(y); + } + + /** + * The width of the `DOMRect`. + */ + get width(): number { + return this.__getInternalWidth(); + } + + set width(width: ?number) { + this.__setInternalWidth(width); + } + + /** + * The height of the `DOMRect`. + */ + get height(): number { + return this.__getInternalHeight(); + } + + set height(height: ?number) { + this.__setInternalHeight(height); + } + + /** + * Creates a new `DOMRect` object with a given location and dimensions. + */ + static fromRect(rect?: ?DOMRectLike): DOMRect { + if (!rect) { + return new DOMRect(); + } + + return new DOMRect(rect.x, rect.y, rect.width, rect.height); + } +} diff --git a/Libraries/DOM/Geometry/DOMRectReadOnly.js b/Libraries/DOM/Geometry/DOMRectReadOnly.js new file mode 100644 index 00000000000000..ce5df6767b486a --- /dev/null +++ b/Libraries/DOM/Geometry/DOMRectReadOnly.js @@ -0,0 +1,188 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +/** + * The JSDoc comments in this file have been extracted from [DOMRectReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly). + * Content by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/contributors.txt), + * licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). + */ + +// flowlint sketchy-null:off, unsafe-getters-setters:off + +export interface DOMRectLike { + x?: ?number; + y?: ?number; + width?: ?number; + height?: ?number; +} + +function castToNumber(value: mixed): number { + return value ? Number(value) : 0; +} + +/** + * The `DOMRectReadOnly` interface specifies the standard properties used by `DOMRect` to define a rectangle whose properties are immutable. + * + * This is a (mostly) spec-compliant version of `DOMRectReadOnly` (https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly). + */ +export default class DOMRectReadOnly { + _x: number; + _y: number; + _width: number; + _height: number; + + constructor(x: ?number, y: ?number, width: ?number, height: ?number) { + this.__setInternalX(x); + this.__setInternalY(y); + this.__setInternalWidth(width); + this.__setInternalHeight(height); + } + + /** + * The x coordinate of the `DOMRectReadOnly`'s origin. + */ + get x(): number { + return this._x; + } + + /** + * The y coordinate of the `DOMRectReadOnly`'s origin. + */ + get y(): number { + return this._y; + } + + /** + * The width of the `DOMRectReadOnly`. + */ + get width(): number { + return this._width; + } + + /** + * The height of the `DOMRectReadOnly`. + */ + get height(): number { + return this._height; + } + + /** + * Returns the top coordinate value of the `DOMRect` (has the same value as `y`, or `y + height` if `height` is negative). + */ + get top(): number { + const height = this._height; + const y = this._y; + + if (height < 0) { + return y + height; + } + + return y; + } + + /** + * Returns the right coordinate value of the `DOMRect` (has the same value as ``x + width`, or `x` if `width` is negative). + */ + get right(): number { + const width = this._width; + const x = this._x; + + if (width < 0) { + return x; + } + + return x + width; + } + + /** + * Returns the bottom coordinate value of the `DOMRect` (has the same value as `y + height`, or `y` if `height` is negative). + */ + get bottom(): number { + const height = this._height; + const y = this._y; + + if (height < 0) { + return y; + } + + return y + height; + } + + /** + * Returns the left coordinate value of the `DOMRect` (has the same value as `x`, or `x + width` if `width` is negative). + */ + get left(): number { + const width = this._width; + const x = this._x; + + if (width < 0) { + return x + width; + } + + return x; + } + + toJSON(): { + x: number, + y: number, + width: number, + height: number, + top: number, + left: number, + bottom: number, + right: number, + } { + const {x, y, width, height, top, left, bottom, right} = this; + return {x, y, width, height, top, left, bottom, right}; + } + + /** + * Creates a new `DOMRectReadOnly` object with a given location and dimensions. + */ + static fromRect(rect?: ?DOMRectLike): DOMRectReadOnly { + if (!rect) { + return new DOMRectReadOnly(); + } + + return new DOMRectReadOnly(rect.x, rect.y, rect.width, rect.height); + } + + __getInternalX(): number { + return this._x; + } + + __getInternalY(): number { + return this._y; + } + + __getInternalWidth(): number { + return this._width; + } + + __getInternalHeight(): number { + return this._height; + } + + __setInternalX(x: ?number) { + this._x = castToNumber(x); + } + + __setInternalY(y: ?number) { + this._y = castToNumber(y); + } + + __setInternalWidth(width: ?number) { + this._width = castToNumber(width); + } + + __setInternalHeight(height: ?number) { + this._height = castToNumber(height); + } +} diff --git a/flow/global.js b/flow/global.js index 63624ca3d2dc16..e7f9814671a5a6 100644 --- a/flow/global.js +++ b/flow/global.js @@ -80,6 +80,10 @@ declare var global: { +__DEV__?: boolean, +RN$Bridgeless?: boolean, + // setupDOM + +DOMRect: typeof DOMRect, + +DOMRectReadOnly: typeof DOMRectReadOnly, + // Undeclared properties are implicitly `any`. [string | symbol]: any, };