mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[DevTools] Show the Suspense boundary name in the rect if there's no overlap (#34918)
This shows the title in the top corner of the rect if there's enough space. The complex bit here is that it can be noisy if too many boundaries occupy the same space to overlap or partially overlap. This uses an R-tree to store all the rects to find overlapping boundaries to cut the available space to draw inside the rect. We use this to compute the rectangle within the rect which doesn't have any overlapping boundaries. The roots don't count as overlapping. Similarly, a parent rect is not consider overlapping a child. However, if two sibling boundaries occupy the same space, no title will be drawn. <img width="734" height="813" alt="Screenshot 2025-10-19 at 5 34 49 PM" src="https://github.com/user-attachments/assets/2b848b9c-3b78-48e5-9476-dd59a7baf6bf" /> We might also consider drawing the "Initial Paint" title at the root but that's less interesting. It's interesting in the beginning before you know about the special case at the root but after that it's just always the same value so just adds noise.
This commit is contained in:
parent
b485f7cf64
commit
f6a4882859
|
|
@ -23,6 +23,7 @@
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"local-storage-fallback": "^4.1.1",
|
"local-storage-fallback": "^4.1.1",
|
||||||
"react-virtualized-auto-sizer": "^1.0.23",
|
"react-virtualized-auto-sizer": "^1.0.23",
|
||||||
"react-window": "^1.8.10"
|
"react-window": "^1.8.10",
|
||||||
|
"rbush": "4.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,31 @@ import type {
|
||||||
import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError';
|
import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError';
|
||||||
import type {DevToolsHookSettings} from '../backend/types';
|
import type {DevToolsHookSettings} from '../backend/types';
|
||||||
|
|
||||||
|
import RBush from 'rbush';
|
||||||
|
|
||||||
|
// Custom version which works with our Rect data structure.
|
||||||
|
class RectRBush extends RBush<Rect> {
|
||||||
|
toBBox(rect: Rect): {
|
||||||
|
minX: number,
|
||||||
|
minY: number,
|
||||||
|
maxX: number,
|
||||||
|
maxY: number,
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
minX: rect.x,
|
||||||
|
minY: rect.y,
|
||||||
|
maxX: rect.x + rect.width,
|
||||||
|
maxY: rect.y + rect.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
compareMinX(a: Rect, b: Rect): number {
|
||||||
|
return a.x - b.x;
|
||||||
|
}
|
||||||
|
compareMinY(a: Rect, b: Rect): number {
|
||||||
|
return a.y - b.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const debug = (methodName: string, ...args: Array<string>) => {
|
const debug = (methodName: string, ...args: Array<string>) => {
|
||||||
if (__DEBUG__) {
|
if (__DEBUG__) {
|
||||||
console.log(
|
console.log(
|
||||||
|
|
@ -194,6 +219,9 @@ export default class Store extends EventEmitter<{
|
||||||
// Renderer ID is needed to support inspection fiber props, state, and hooks.
|
// Renderer ID is needed to support inspection fiber props, state, and hooks.
|
||||||
_rootIDToRendererID: Map<Element['id'], number> = new Map();
|
_rootIDToRendererID: Map<Element['id'], number> = new Map();
|
||||||
|
|
||||||
|
// Stores all the SuspenseNode rects in an R-tree to make it fast to find overlaps.
|
||||||
|
_rtree: RBush<Rect> = new RectRBush();
|
||||||
|
|
||||||
// These options may be initially set by a configuration option when constructing the Store.
|
// These options may be initially set by a configuration option when constructing the Store.
|
||||||
_supportsInspectMatchingDOMElement: boolean = false;
|
_supportsInspectMatchingDOMElement: boolean = false;
|
||||||
_supportsClickToInspect: boolean = false;
|
_supportsClickToInspect: boolean = false;
|
||||||
|
|
@ -1622,7 +1650,12 @@ export default class Store extends EventEmitter<{
|
||||||
const y = operations[i + 1] / 1000;
|
const y = operations[i + 1] / 1000;
|
||||||
const width = operations[i + 2] / 1000;
|
const width = operations[i + 2] / 1000;
|
||||||
const height = operations[i + 3] / 1000;
|
const height = operations[i + 3] / 1000;
|
||||||
rects.push({x, y, width, height});
|
const rect = {x, y, width, height};
|
||||||
|
if (parentID !== 0) {
|
||||||
|
// Track all rects except the root.
|
||||||
|
this._rtree.insert(rect);
|
||||||
|
}
|
||||||
|
rects.push(rect);
|
||||||
i += 4;
|
i += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1680,13 +1713,20 @@ export default class Store extends EventEmitter<{
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
const {children, parentID} = suspense;
|
const {children, parentID, rects} = suspense;
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
this._throwAndEmitError(
|
this._throwAndEmitError(
|
||||||
Error(`Suspense node "${id}" was removed before its children.`),
|
Error(`Suspense node "${id}" was removed before its children.`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rects !== null && parentID !== 0) {
|
||||||
|
// Delete all the existing rects from the R-tree
|
||||||
|
for (let j = 0; j < rects.length; j++) {
|
||||||
|
this._rtree.remove(rects[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._idToSuspense.delete(id);
|
this._idToSuspense.delete(id);
|
||||||
removedSuspenseIDs.set(id, parentID);
|
removedSuspenseIDs.set(id, parentID);
|
||||||
|
|
||||||
|
|
@ -1785,6 +1825,14 @@ export default class Store extends EventEmitter<{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevRects = suspense.rects;
|
||||||
|
if (prevRects !== null && suspense.parentID !== 0) {
|
||||||
|
// Delete all the existing rects from the R-tree
|
||||||
|
for (let j = 0; j < prevRects.length; j++) {
|
||||||
|
this._rtree.remove(prevRects[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let nextRects: SuspenseNode['rects'];
|
let nextRects: SuspenseNode['rects'];
|
||||||
if (numRects === -1) {
|
if (numRects === -1) {
|
||||||
nextRects = null;
|
nextRects = null;
|
||||||
|
|
@ -1796,7 +1844,12 @@ export default class Store extends EventEmitter<{
|
||||||
const width = operations[i + 2] / 1000;
|
const width = operations[i + 2] / 1000;
|
||||||
const height = operations[i + 3] / 1000;
|
const height = operations[i + 3] / 1000;
|
||||||
|
|
||||||
nextRects.push({x, y, width, height});
|
const rect = {x, y, width, height};
|
||||||
|
if (suspense.parentID !== 0) {
|
||||||
|
// Track all rects except the root.
|
||||||
|
this._rtree.insert(rect);
|
||||||
|
}
|
||||||
|
nextRects.push(rect);
|
||||||
|
|
||||||
i += 4;
|
i += 4;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,24 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.SuspenseRectsTitle {
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--color-text);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: var(--font-size-sans-small);
|
||||||
|
line-height: var(--font-size-sans-small);
|
||||||
|
padding: .25rem;
|
||||||
|
container-type: size;
|
||||||
|
container-name: title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container title (width < 30px) or (height < 12px) {
|
||||||
|
.SuspenseRectsTitle > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.SuspenseRectsScaledRect[data-visible='false'] > .SuspenseRectsBoundaryChildren {
|
.SuspenseRectsScaledRect[data-visible='false'] > .SuspenseRectsBoundaryChildren {
|
||||||
overflow: initial;
|
overflow: initial;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +93,7 @@
|
||||||
transition: background-color 0.2s ease-out;
|
transition: background-color 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.SuspenseRectsBoundary[data-selected='true'] {
|
.SuspenseRectsBoundary[data-selected='true'][data-visible='true'] {
|
||||||
box-shadow: var(--elevation-4);
|
box-shadow: var(--elevation-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import {
|
||||||
SuspenseTreeDispatcherContext,
|
SuspenseTreeDispatcherContext,
|
||||||
} from './SuspenseTreeContext';
|
} from './SuspenseTreeContext';
|
||||||
import {getClassNameForEnvironment} from './SuspenseEnvironmentColors.js';
|
import {getClassNameForEnvironment} from './SuspenseEnvironmentColors.js';
|
||||||
|
import type RBush from 'rbush';
|
||||||
|
|
||||||
function ScaledRect({
|
function ScaledRect({
|
||||||
className,
|
className,
|
||||||
|
|
@ -78,8 +79,10 @@ function ScaledRect({
|
||||||
|
|
||||||
function SuspenseRects({
|
function SuspenseRects({
|
||||||
suspenseID,
|
suspenseID,
|
||||||
|
parentRects,
|
||||||
}: {
|
}: {
|
||||||
suspenseID: SuspenseNode['id'],
|
suspenseID: SuspenseNode['id'],
|
||||||
|
parentRects: null | Array<Rect>,
|
||||||
}): React$Node {
|
}): React$Node {
|
||||||
const store = useContext(StoreContext);
|
const store = useContext(StoreContext);
|
||||||
const treeDispatch = useContext(TreeDispatcherContext);
|
const treeDispatch = useContext(TreeDispatcherContext);
|
||||||
|
|
@ -167,7 +170,20 @@ function SuspenseRects({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const boundingBox = getBoundingBox(suspense.rects);
|
const rects = suspense.rects;
|
||||||
|
const boundingBox = getBoundingBox(rects);
|
||||||
|
|
||||||
|
// Next we'll try to find a rect within one of our rects that isn't intersecting with
|
||||||
|
// other rects.
|
||||||
|
// TODO: This should probably be memoized based on if any changes to the rtree has been made.
|
||||||
|
const titleBox: null | Rect =
|
||||||
|
rects === null ? null : findTitleBox(store._rtree, rects, parentRects);
|
||||||
|
const nextRects =
|
||||||
|
rects === null || rects.length === 0
|
||||||
|
? parentRects
|
||||||
|
: parentRects === null || parentRects.length === 0
|
||||||
|
? rects
|
||||||
|
: parentRects.concat(rects);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScaledRect
|
<ScaledRect
|
||||||
|
|
@ -205,11 +221,22 @@ function SuspenseRects({
|
||||||
className={styles.SuspenseRectsBoundaryChildren}
|
className={styles.SuspenseRectsBoundaryChildren}
|
||||||
rect={boundingBox}>
|
rect={boundingBox}>
|
||||||
{suspense.children.map(childID => {
|
{suspense.children.map(childID => {
|
||||||
return <SuspenseRects key={childID} suspenseID={childID} />;
|
return (
|
||||||
|
<SuspenseRects
|
||||||
|
key={childID}
|
||||||
|
suspenseID={childID}
|
||||||
|
parentRects={nextRects}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</ScaledRect>
|
</ScaledRect>
|
||||||
)}
|
)}
|
||||||
{selected ? (
|
{titleBox && suspense.name && visible ? (
|
||||||
|
<ScaledRect className={styles.SuspenseRectsTitle} rect={titleBox}>
|
||||||
|
<span>{suspense.name}</span>
|
||||||
|
</ScaledRect>
|
||||||
|
) : null}
|
||||||
|
{selected && visible ? (
|
||||||
<ScaledRect
|
<ScaledRect
|
||||||
className={styles.SuspenseRectOutline}
|
className={styles.SuspenseRectOutline}
|
||||||
rect={boundingBox}
|
rect={boundingBox}
|
||||||
|
|
@ -320,6 +347,77 @@ function getDocumentBoundingRect(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findTitleBox(
|
||||||
|
rtree: RBush<Rect>,
|
||||||
|
rects: Array<Rect>,
|
||||||
|
parentRects: null | Array<Rect>,
|
||||||
|
): null | Rect {
|
||||||
|
for (let i = 0; i < rects.length; i++) {
|
||||||
|
const rect = rects[i];
|
||||||
|
if (rect.width < 20 || rect.height < 10) {
|
||||||
|
// Skip small rects. They're likely not able to be contain anything useful anyway.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Find all overlapping rects elsewhere in the tree to limit our rect.
|
||||||
|
const overlappingRects = rtree.search({
|
||||||
|
minX: rect.x,
|
||||||
|
minY: rect.y,
|
||||||
|
maxX: rect.x + rect.width,
|
||||||
|
maxY: rect.y + rect.height,
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
overlappingRects.length === 0 ||
|
||||||
|
(overlappingRects.length === 1 && overlappingRects[0] === rect)
|
||||||
|
) {
|
||||||
|
// There are no overlapping rects that isn't our own rect, so we can just use
|
||||||
|
// the full space of the rect.
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
// We have some overlapping rects but they might not overlap everything. Let's
|
||||||
|
// shrink it up toward the top left corner until it has no more overlap.
|
||||||
|
const minX = rect.x;
|
||||||
|
const minY = rect.y;
|
||||||
|
let maxX = rect.x + rect.width;
|
||||||
|
let maxY = rect.y + rect.height;
|
||||||
|
for (let j = 0; j < overlappingRects.length; j++) {
|
||||||
|
const overlappingRect = overlappingRects[j];
|
||||||
|
if (overlappingRect === rect) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const x = overlappingRect.x;
|
||||||
|
const y = overlappingRect.y;
|
||||||
|
if (y < maxY && x < maxX) {
|
||||||
|
if (
|
||||||
|
parentRects !== null &&
|
||||||
|
parentRects.indexOf(overlappingRect) !== -1
|
||||||
|
) {
|
||||||
|
// This rect overlaps but it's part of a parent boundary. We let
|
||||||
|
// title content render if it's on top and not a sibling.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// This rect cuts into the remaining space. Let's figure out if we're
|
||||||
|
// better off cutting on the x or y axis to maximize remaining space.
|
||||||
|
const remainderX = x - minX;
|
||||||
|
const remainderY = y - minY;
|
||||||
|
if (remainderX > remainderY) {
|
||||||
|
maxX = x;
|
||||||
|
} else {
|
||||||
|
maxY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxX > minX && maxY > minY) {
|
||||||
|
return {
|
||||||
|
x: minX,
|
||||||
|
y: minY,
|
||||||
|
width: maxX - minX,
|
||||||
|
height: maxY - minY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function SuspenseRectsRoot({rootID}: {rootID: SuspenseNode['id']}): React$Node {
|
function SuspenseRectsRoot({rootID}: {rootID: SuspenseNode['id']}): React$Node {
|
||||||
const store = useContext(StoreContext);
|
const store = useContext(StoreContext);
|
||||||
const root = store.getSuspenseByID(rootID);
|
const root = store.getSuspenseByID(rootID);
|
||||||
|
|
@ -329,7 +427,9 @@ function SuspenseRectsRoot({rootID}: {rootID: SuspenseNode['id']}): React$Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
return root.children.map(childID => {
|
return root.children.map(childID => {
|
||||||
return <SuspenseRects key={childID} suspenseID={childID} />;
|
return (
|
||||||
|
<SuspenseRects key={childID} suspenseID={childID} parentRects={null} />
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -456,3 +456,191 @@ declare class NavigationDestination {
|
||||||
|
|
||||||
getState(): mixed;
|
getState(): mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ported from definitely-typed
|
||||||
|
declare module 'rbush' {
|
||||||
|
declare interface BBox {
|
||||||
|
minX: number;
|
||||||
|
minY: number;
|
||||||
|
maxX: number;
|
||||||
|
maxY: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare export default class RBush<T> {
|
||||||
|
/**
|
||||||
|
* Constructs an `RBush`, a high-performance 2D spatial index for points and
|
||||||
|
* rectangles. Based on an optimized __R-tree__ data structure with
|
||||||
|
* __bulk-insertion__ support.
|
||||||
|
*
|
||||||
|
* @param maxEntries An optional argument to RBush defines the maximum
|
||||||
|
* number of entries in a tree node. `9` (used by default)
|
||||||
|
* is a reasonable choice for most applications. Higher
|
||||||
|
* value means faster insertion and slower search, and
|
||||||
|
* vice versa.
|
||||||
|
*/
|
||||||
|
constructor(maxEntries?: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts an item. To insert many items at once, use `load()`.
|
||||||
|
*
|
||||||
|
* @param item The item to insert.
|
||||||
|
*/
|
||||||
|
insert(item: T): RBush<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk-inserts the given items into the tree.
|
||||||
|
*
|
||||||
|
* Bulk insertion is usually ~2-3 times faster than inserting items one by
|
||||||
|
* one. After bulk loading (bulk insertion into an empty tree), subsequent
|
||||||
|
* query performance is also ~20-30% better.
|
||||||
|
*
|
||||||
|
* Note that when you do bulk insertion into an existing tree, it bulk-loads
|
||||||
|
* the given data into a separate tree and inserts the smaller tree into the
|
||||||
|
* larger tree. This means that bulk insertion works very well for clustered
|
||||||
|
* data (where items in one update are close to each other), but makes query
|
||||||
|
* performance worse if the data is scattered.
|
||||||
|
*
|
||||||
|
* @param items The items to load.
|
||||||
|
*/
|
||||||
|
load(items: $ReadOnlyArray<T>): RBush<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a previously inserted item, comparing by reference.
|
||||||
|
*
|
||||||
|
* To remove all items, use `clear()`.
|
||||||
|
*
|
||||||
|
* @param item The item to remove.
|
||||||
|
* @param equals A custom function that allows comparing by value instead.
|
||||||
|
* Useful when you have only a copy of the object you need
|
||||||
|
* removed (e.g. loaded from server).
|
||||||
|
*/
|
||||||
|
remove(item: T, equals?: (a: T, b: T) => boolean): RBush<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all items.
|
||||||
|
*/
|
||||||
|
clear(): RBush<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of data items (points or rectangles) that the given
|
||||||
|
* bounding box intersects.
|
||||||
|
*
|
||||||
|
* Note that the search method accepts a bounding box in `{minX, minY, maxX,
|
||||||
|
* maxY}` format regardless of the data format.
|
||||||
|
*
|
||||||
|
* @param box The bounding box in which to search.
|
||||||
|
*/
|
||||||
|
search(box: BBox): T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all items contained in the tree.
|
||||||
|
*/
|
||||||
|
all(): T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if there are any items intersecting the given bounding
|
||||||
|
* box, otherwise `false`.
|
||||||
|
*
|
||||||
|
* @param box The bounding box in which to search.
|
||||||
|
*/
|
||||||
|
collides(box: BBox): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bounding box for the provided item.
|
||||||
|
*
|
||||||
|
* By default, `RBush` assumes the format of data points to be an object
|
||||||
|
* with `minX`, `minY`, `maxX`, and `maxY`. However, you can specify a
|
||||||
|
* custom item format by overriding `toBBox()`, `compareMinX()`, and
|
||||||
|
* `compareMinY()`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* class MyRBush<T> extends RBush<T> {
|
||||||
|
* toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; }
|
||||||
|
* compareMinX(a, b) { return a.x - b.x; }
|
||||||
|
* compareMinY(a, b) { return a.y - b.y; }
|
||||||
|
* }
|
||||||
|
* const tree = new MyRBush<[number, number]>();
|
||||||
|
* tree.insert([20, 50]); // accepts [x, y] points
|
||||||
|
*
|
||||||
|
* @param item The item whose bounding box should be returned.
|
||||||
|
*/
|
||||||
|
toBBox(item: T): BBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the minimum x coordinate of two items. Returns -1 if `a`'s
|
||||||
|
* x-coordinate is smaller, 1 if `b`'s x coordinate is smaller, or 0 if
|
||||||
|
* they're equal.
|
||||||
|
*
|
||||||
|
* By default, `RBush` assumes the format of data points to be an object
|
||||||
|
* with `minX`, `minY`, `maxX`, and `maxY`. However, you can specify a
|
||||||
|
* custom item format by overriding `toBBox()`, `compareMinX()`, and
|
||||||
|
* `compareMinY()`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* class MyRBush<T> extends RBush<T> {
|
||||||
|
* toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; }
|
||||||
|
* compareMinX(a, b) { return a.x - b.x; }
|
||||||
|
* compareMinY(a, b) { return a.y - b.y; }
|
||||||
|
* }
|
||||||
|
* const tree = new MyRBush<[number, number]>();
|
||||||
|
* tree.insert([20, 50]); // accepts [x, y] points
|
||||||
|
*
|
||||||
|
* @param a The first item to compare.
|
||||||
|
* @param b The second item to compare.
|
||||||
|
*/
|
||||||
|
compareMinX(a: T, b: T): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the minimum y coordinate of two items. Returns -1 if `a`'s
|
||||||
|
* x-coordinate is smaller, 1 if `b`'s x coordinate is smaller, or 0 if
|
||||||
|
* they're equal.
|
||||||
|
*
|
||||||
|
* By default, `RBush` assumes the format of data points to be an object
|
||||||
|
* with `minX`, `minY`, `maxX`, and `maxY`. However, you can specify a
|
||||||
|
* custom item format by overriding `toBBox()`, `compareMinX()`, and
|
||||||
|
* `compareMinY()`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* class MyRBush<T> extends RBush<T> {
|
||||||
|
* toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; }
|
||||||
|
* compareMinX(a, b) { return a.x - b.x; }
|
||||||
|
* compareMinY(a, b) { return a.y - b.y; }
|
||||||
|
* }
|
||||||
|
* const tree = new MyRBush<[number, number]>();
|
||||||
|
* tree.insert([20, 50]); // accepts [x, y] points
|
||||||
|
*
|
||||||
|
* @param a The first item to compare.
|
||||||
|
* @param b The second item to compare.
|
||||||
|
*/
|
||||||
|
compareMinY(a: T, b: T): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the tree's contents as a JSON object.
|
||||||
|
*
|
||||||
|
* Importing and exporting as JSON allows you to use RBush on both the
|
||||||
|
* server (using Node.js) and the browser combined, e.g. first indexing the
|
||||||
|
* data on the server and and then importing the resulting tree data on the
|
||||||
|
* client for searching.
|
||||||
|
*
|
||||||
|
* Note that the `maxEntries` option from the constructor must be the same
|
||||||
|
* in both trees for export/import to work properly.
|
||||||
|
*/
|
||||||
|
toJSON(): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports previously exported data into the tree (i.e., data that was
|
||||||
|
* emitted by `toJSON()`).
|
||||||
|
*
|
||||||
|
* Importing and exporting as JSON allows you to use RBush on both the
|
||||||
|
* server (using Node.js) and the browser combined, e.g. first indexing the
|
||||||
|
* data on the server and and then importing the resulting tree data on the
|
||||||
|
* client for searching.
|
||||||
|
*
|
||||||
|
* Note that the `maxEntries` option from the constructor must be the same
|
||||||
|
* in both trees for export/import to work properly.
|
||||||
|
*
|
||||||
|
* @param data The previously exported JSON data.
|
||||||
|
*/
|
||||||
|
fromJSON(data: any): RBush<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ module.exports = Object.assign({}, baseConfig, {
|
||||||
testPathIgnorePatterns: ['/node_modules/', '-test.internal.js$'],
|
testPathIgnorePatterns: ['/node_modules/', '-test.internal.js$'],
|
||||||
// Exclude the build output from transforms
|
// Exclude the build output from transforms
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'/node_modules/',
|
'/node_modules/(?!(rbush|quickselect)/)',
|
||||||
'<rootDir>/build/',
|
'<rootDir>/build/',
|
||||||
'/__compiled__/',
|
'/__compiled__/',
|
||||||
'/__untransformed__/',
|
'/__untransformed__/',
|
||||||
|
|
|
||||||
98
yarn.lock
98
yarn.lock
|
|
@ -8271,7 +8271,7 @@ eslint-utils@^2.0.0, eslint-utils@^2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint-visitor-keys "^1.1.0"
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
"eslint-v7@npm:eslint@^7.7.0":
|
"eslint-v7@npm:eslint@^7.7.0", eslint@^7.7.0:
|
||||||
version "7.32.0"
|
version "7.32.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
|
||||||
integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
|
integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
|
||||||
|
|
@ -8470,52 +8470,6 @@ eslint@8.57.0:
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
|
|
||||||
eslint@^7.7.0:
|
|
||||||
version "7.32.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
|
|
||||||
integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame" "7.12.11"
|
|
||||||
"@eslint/eslintrc" "^0.4.3"
|
|
||||||
"@humanwhocodes/config-array" "^0.5.0"
|
|
||||||
ajv "^6.10.0"
|
|
||||||
chalk "^4.0.0"
|
|
||||||
cross-spawn "^7.0.2"
|
|
||||||
debug "^4.0.1"
|
|
||||||
doctrine "^3.0.0"
|
|
||||||
enquirer "^2.3.5"
|
|
||||||
escape-string-regexp "^4.0.0"
|
|
||||||
eslint-scope "^5.1.1"
|
|
||||||
eslint-utils "^2.1.0"
|
|
||||||
eslint-visitor-keys "^2.0.0"
|
|
||||||
espree "^7.3.1"
|
|
||||||
esquery "^1.4.0"
|
|
||||||
esutils "^2.0.2"
|
|
||||||
fast-deep-equal "^3.1.3"
|
|
||||||
file-entry-cache "^6.0.1"
|
|
||||||
functional-red-black-tree "^1.0.1"
|
|
||||||
glob-parent "^5.1.2"
|
|
||||||
globals "^13.6.0"
|
|
||||||
ignore "^4.0.6"
|
|
||||||
import-fresh "^3.0.0"
|
|
||||||
imurmurhash "^0.1.4"
|
|
||||||
is-glob "^4.0.0"
|
|
||||||
js-yaml "^3.13.1"
|
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
|
||||||
levn "^0.4.1"
|
|
||||||
lodash.merge "^4.6.2"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
natural-compare "^1.4.0"
|
|
||||||
optionator "^0.9.1"
|
|
||||||
progress "^2.0.0"
|
|
||||||
regexpp "^3.1.0"
|
|
||||||
semver "^7.2.1"
|
|
||||||
strip-ansi "^6.0.0"
|
|
||||||
strip-json-comments "^3.1.0"
|
|
||||||
table "^6.0.9"
|
|
||||||
text-table "^0.2.0"
|
|
||||||
v8-compile-cache "^2.0.3"
|
|
||||||
|
|
||||||
espree@10.0.1, espree@^10.0.1:
|
espree@10.0.1, espree@^10.0.1:
|
||||||
version "10.0.1"
|
version "10.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f"
|
||||||
|
|
@ -14317,7 +14271,7 @@ prepend-http@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||||
|
|
||||||
"prettier-2@npm:prettier@^2":
|
"prettier-2@npm:prettier@^2", prettier@^2.5.1:
|
||||||
version "2.8.8"
|
version "2.8.8"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
||||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||||
|
|
@ -14332,11 +14286,6 @@ prettier@^1.19.1:
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||||
|
|
||||||
prettier@^2.5.1:
|
|
||||||
version "2.8.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
|
||||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
|
||||||
|
|
||||||
pretty-format@^29.4.1:
|
pretty-format@^29.4.1:
|
||||||
version "29.4.1"
|
version "29.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.1.tgz#0da99b532559097b8254298da7c75a0785b1751c"
|
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.1.tgz#0da99b532559097b8254298da7c75a0785b1751c"
|
||||||
|
|
@ -14602,6 +14551,11 @@ quick-tmp@0.0.0:
|
||||||
first-match "0.0.1"
|
first-match "0.0.1"
|
||||||
osenv "0.0.3"
|
osenv "0.0.3"
|
||||||
|
|
||||||
|
quickselect@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-3.0.0.tgz#a37fc953867d56f095a20ac71c6d27063d2de603"
|
||||||
|
integrity sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==
|
||||||
|
|
||||||
random-seed@^0.3.0:
|
random-seed@^0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/random-seed/-/random-seed-0.3.0.tgz#d945f2e1f38f49e8d58913431b8bf6bb937556cd"
|
resolved "https://registry.yarnpkg.com/random-seed/-/random-seed-0.3.0.tgz#d945f2e1f38f49e8d58913431b8bf6bb937556cd"
|
||||||
|
|
@ -14639,6 +14593,13 @@ raw-loader@^3.1.0:
|
||||||
loader-utils "^1.1.0"
|
loader-utils "^1.1.0"
|
||||||
schema-utils "^2.0.1"
|
schema-utils "^2.0.1"
|
||||||
|
|
||||||
|
rbush@4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rbush/-/rbush-4.0.1.tgz#1f55afa64a978f71bf9e9a99bc14ff84f3cb0d6d"
|
||||||
|
integrity sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==
|
||||||
|
dependencies:
|
||||||
|
quickselect "^3.0.0"
|
||||||
|
|
||||||
rc@1.2.8, rc@^1.0.1, rc@^1.1.6, rc@^1.2.8:
|
rc@1.2.8, rc@^1.0.1, rc@^1.1.6, rc@^1.2.8:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||||
|
|
@ -16211,7 +16172,7 @@ string-natural-compare@^3.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
|
|
@ -16246,15 +16207,6 @@ string-width@^4.0.0:
|
||||||
is-fullwidth-code-point "^3.0.0"
|
is-fullwidth-code-point "^3.0.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
|
||||||
version "4.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
|
||||||
dependencies:
|
|
||||||
emoji-regex "^8.0.0"
|
|
||||||
is-fullwidth-code-point "^3.0.0"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
|
|
||||||
string-width@^5.0.1, string-width@^5.1.2:
|
string-width@^5.0.1, string-width@^5.1.2:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
|
||||||
|
|
@ -16315,7 +16267,7 @@ string_decoder@~1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.0"
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
|
|
@ -16343,13 +16295,6 @@ strip-ansi@^5.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^4.1.0"
|
ansi-regex "^4.1.0"
|
||||||
|
|
||||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|
||||||
version "6.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
|
||||||
dependencies:
|
|
||||||
ansi-regex "^5.0.1"
|
|
||||||
|
|
||||||
strip-ansi@^7.0.1:
|
strip-ansi@^7.0.1:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||||
|
|
@ -17958,7 +17903,7 @@ workerize-loader@^2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
loader-utils "^2.0.0"
|
loader-utils "^2.0.0"
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
|
|
@ -17976,15 +17921,6 @@ wrap-ansi@^6.2.0:
|
||||||
string-width "^4.1.0"
|
string-width "^4.1.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
wrap-ansi@^7.0.0:
|
|
||||||
version "7.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
|
||||||
dependencies:
|
|
||||||
ansi-styles "^4.0.0"
|
|
||||||
string-width "^4.1.0"
|
|
||||||
strip-ansi "^6.0.0"
|
|
||||||
|
|
||||||
wrap-ansi@^8.1.0:
|
wrap-ansi@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user