-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement basic version of DOMRectList
Summary: This implements a basic version of DOMRectList that's close to the spec but diverges in some things (e.g.: methods could be called with an instance created through `Object.create`, etc.). This will be used soon to implement `ReadOnlyelement.getClientRects()` (behind a flag). See: react-native-community/discussions-and-proposals#607 Changelog: [internal] Reviewed By: yungsters Differential Revision: D44060540 fbshipit-source-id: ad29b5c41f2778864e7dd7ece9223dcf73cd5d6c
- Loading branch information
1 parent
c4b84ba
commit a8b5ff8
Showing
3 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
76 changes: 76 additions & 0 deletions
76
packages/react-native/Libraries/DOM/OldStyleCollections/DOMRectList.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/** | ||
* 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. | ||
* | ||
* @format | ||
* @flow strict | ||
*/ | ||
|
||
// flowlint unsafe-getters-setters:off | ||
|
||
import type DOMRectReadOnly from '../Geometry/DOMRectReadOnly'; | ||
import type {ArrayLike} from './ArrayLikeUtils'; | ||
|
||
import {createValueIterator} from './ArrayLikeUtils'; | ||
|
||
// IMPORTANT: The Flow type definition for this module is defined in `DOMRectList.js.flow` | ||
// because Flow only supports indexers in classes in declaration files. | ||
|
||
// $FlowIssue[prop-missing] Flow doesn't understand [Symbol.iterator]() {} and thinks this class doesn't implement the Iterable interface. | ||
export default class DOMRectList implements Iterable<DOMRectReadOnly> { | ||
_length: number; | ||
|
||
/** | ||
* Use `createDOMRectList` to create instances of this class. | ||
* | ||
* @private This is not defined in the declaration file, so users will not see | ||
* the signature of the constructor. | ||
*/ | ||
constructor(elements: $ReadOnlyArray<DOMRectReadOnly>) { | ||
for (let i = 0; i < elements.length; i++) { | ||
Object.defineProperty(this, i, { | ||
value: elements[i], | ||
enumerable: true, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
} | ||
|
||
this._length = elements.length; | ||
} | ||
|
||
get length(): number { | ||
return this._length; | ||
} | ||
|
||
item(index: number): DOMRectReadOnly | null { | ||
if (index < 0 || index >= this._length) { | ||
return null; | ||
} | ||
|
||
// assigning to the interface allows us to access the indexer property in a | ||
// type-safe way. | ||
// eslint-disable-next-line consistent-this | ||
const arrayLike: ArrayLike<DOMRectReadOnly> = this; | ||
return arrayLike[index]; | ||
} | ||
|
||
// $FlowIssue[unsupported-syntax] Flow does not support computed properties in classes. | ||
[Symbol.iterator](): Iterator<DOMRectReadOnly> { | ||
return createValueIterator(this); | ||
} | ||
} | ||
|
||
/** | ||
* This is an internal method to create instances of `DOMRectList`, | ||
* which avoids leaking its constructor to end users. | ||
* We can do that because the external definition of `DOMRectList` lives in | ||
* `DOMRectList.js.flow`, not here. | ||
*/ | ||
export function createDOMRectList( | ||
elements: $ReadOnlyArray<DOMRectReadOnly>, | ||
): DOMRectList { | ||
return new DOMRectList(elements); | ||
} |
27 changes: 27 additions & 0 deletions
27
packages/react-native/Libraries/DOM/OldStyleCollections/DOMRectList.js.flow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* 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. | ||
* | ||
* @format | ||
* @flow strict | ||
*/ | ||
|
||
import type {ArrayLike} from './ArrayLikeUtils'; | ||
import type DOMRectReadOnly from '../Geometry/DOMRectReadOnly'; | ||
|
||
declare export default class DOMRectList | ||
implements Iterable<DOMRectReadOnly>, ArrayLike<DOMRectReadOnly> | ||
{ | ||
// This property should've been read-only as well, but Flow doesn't handle | ||
// read-only indexers correctly (thinks reads are writes and fails). | ||
[index: number]: DOMRectReadOnly; | ||
+length: number; | ||
item(index: number): DOMRectReadOnly | null; | ||
@@iterator(): Iterator<DOMRectReadOnly>; | ||
} | ||
|
||
declare export function createDOMRectList( | ||
domRects: $ReadOnlyArray<DOMRectReadOnly>, | ||
): DOMRectList; |
85 changes: 85 additions & 0 deletions
85
packages/react-native/Libraries/DOM/OldStyleCollections/__tests__/DOMRectList-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/** | ||
* 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-local | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
|
||
import DOMRectReadOnly from '../../Geometry/DOMRectReadOnly'; | ||
import {createDOMRectList} from '../DOMRectList'; | ||
|
||
const domRectA = new DOMRectReadOnly(); | ||
const domRectB = new DOMRectReadOnly(); | ||
const domRectC = new DOMRectReadOnly(); | ||
|
||
describe('DOMRectList', () => { | ||
it('provides an array-like interface', () => { | ||
const collection = createDOMRectList([domRectA, domRectB, domRectC]); | ||
|
||
expect(collection[0]).toBe(domRectA); | ||
expect(collection[1]).toBe(domRectB); | ||
expect(collection[2]).toBe(domRectC); | ||
expect(collection[3]).toBe(undefined); | ||
expect(collection.length).toBe(3); | ||
}); | ||
|
||
it('is immutable (loose mode)', () => { | ||
const collection = createDOMRectList([domRectA, domRectB, domRectC]); | ||
|
||
collection[0] = new DOMRectReadOnly(); | ||
expect(collection[0]).toBe(domRectA); | ||
|
||
// $FlowExpectedError[cannot-write] | ||
collection.length = 100; | ||
expect(collection.length).toBe(3); | ||
}); | ||
|
||
it('is immutable (strict mode)', () => { | ||
'use strict'; | ||
|
||
const collection = createDOMRectList([domRectA, domRectB, domRectC]); | ||
|
||
expect(() => { | ||
collection[0] = new DOMRectReadOnly(); | ||
}).toThrow(TypeError); | ||
expect(collection[0]).toBe(domRectA); | ||
|
||
expect(() => { | ||
// $FlowExpectedError[cannot-write] | ||
collection.length = 100; | ||
}).toThrow(TypeError); | ||
expect(collection.length).toBe(3); | ||
}); | ||
|
||
it('can be converted to an array through common methods', () => { | ||
const collection = createDOMRectList([domRectA, domRectB, domRectC]); | ||
|
||
expect(Array.from(collection)).toEqual([domRectA, domRectB, domRectC]); | ||
expect([...collection]).toEqual([domRectA, domRectB, domRectC]); | ||
}); | ||
|
||
it('can be traversed with for-of', () => { | ||
const collection = createDOMRectList([domRectA, domRectB, domRectC]); | ||
|
||
let i = 0; | ||
for (const value of collection) { | ||
expect(value).toBe(collection[i]); | ||
i++; | ||
} | ||
}); | ||
|
||
describe('item()', () => { | ||
it('returns elements at the specified position, or null', () => { | ||
const collection = createDOMRectList([domRectA, domRectB, domRectC]); | ||
|
||
expect(collection.item(0)).toBe(domRectA); | ||
expect(collection.item(1)).toBe(domRectB); | ||
expect(collection.item(2)).toBe(domRectC); | ||
expect(collection.item(3)).toBe(null); | ||
}); | ||
}); | ||
}); |