Custom rendering margin / don't cull selected shapes (#1788)

This PR:
- supports client configuration of the rendering bounds via
`Editor.renderingBoundsMargin`
- no longer culls selected shapes
- restores rendering shape tests accidentally removed in #1786 

### Change Type

- [x] `patch` — Bug fix

### Test Plan

1. Select shapes, scroll quickly to see if they get culled

- [x] Unit Tests

### Release Notes

- [editor] add `Editor.renderingBoundsMargin`
This commit is contained in:
Steve Ruiz 2023-08-03 08:37:15 +01:00 committed by GitHub
parent bf27743595
commit e4829f6702
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 246 additions and 947 deletions

View file

@ -869,6 +869,7 @@ export class Editor extends EventEmitter<TLEventMap> {
renamePage(id: TLPageId, name: string, squashing?: boolean): this;
get renderingBounds(): Box2d;
get renderingBoundsExpanded(): Box2d;
renderingBoundsMargin: number;
get renderingShapes(): {
id: TLShapeId;
shape: TLShape;

View file

@ -92,8 +92,6 @@ export const Shape = track(function Shape({
backgroundContainerRef.current?.style.setProperty('z-index', backgroundIndex + '')
}, [opacity, index, backgroundIndex, setProperty])
// const shape = editor.getShape(id)
const annotateError = React.useCallback(
(error: any) => {
editor.annotateError(error, { origin: 'react.shape', willCrashApp: false })
@ -120,7 +118,7 @@ export const Shape = track(function Shape({
</div>
)}
<div ref={containerRef} className="tl-shape" data-shape-type={shape.type} draggable={false}>
{isCulled && util.canUnmount(shape) ? (
{isCulled ? (
<CulledShape shape={shape} />
) : (
<OptionalErrorBoundary fallback={ShapeErrorFallback as any} onError={annotateError}>

View file

@ -2869,18 +2869,20 @@ export class Editor extends EventEmitter<TLEventMap> {
}
}
private computeUnorderedRenderingShapes(
private getUnorderedRenderingShapes(
ids: TLParentId[],
{
renderingBounds,
renderingBoundsExpanded,
erasingShapeIdsSet,
editingShapeId,
selectedShapeIds,
}: {
renderingBounds?: Box2d
renderingBoundsExpanded?: Box2d
erasingShapeIdsSet?: Set<TLShapeId>
editingShapeId?: TLShapeId | null
selectedShapeIds?: TLShapeId[]
} = {}
) {
// Here we get the shape as well as any of its children, as well as their
@ -2935,15 +2937,21 @@ export class Editor extends EventEmitter<TLEventMap> {
? renderingBounds?.includes(maskedPageBounds) ?? true
: false
// Whether the shape should actually be culled / unmounted.
// - Use the "expanded" rendering viewport to include shapes that are just off-screen.
// - Editing shapes should never be culled.
const isCulled = maskedPageBounds
? (editingShapeId !== id && !renderingBoundsExpanded?.includes(maskedPageBounds)) ?? true
: true
const util = this.getShapeUtil(shape)
const isCulled =
// shapes completely clipped by parent are always culled
maskedPageBounds === undefined
? true
: // some shapes can't be unmounted / culled
util.canUnmount(shape) &&
// editing shapes can't be culled
editingShapeId !== id &&
// selected shapes can't be culled
!selectedShapeIds?.includes(id) &&
// shapes outside of the viewport are culled
!!(renderingBoundsExpanded && !renderingBoundsExpanded.includes(maskedPageBounds))
renderingShapes.push({
id,
shape,
@ -2991,11 +2999,12 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public
*/
@computed get renderingShapes() {
const renderingShapes = this.computeUnorderedRenderingShapes([this.currentPageId], {
const renderingShapes = this.getUnorderedRenderingShapes([this.currentPageId], {
renderingBounds: this.renderingBounds,
renderingBoundsExpanded: this.renderingBoundsExpanded,
erasingShapeIdsSet: this.erasingShapeIdsSet,
editingShapeId: this.editingShapeId,
selectedShapeIds: this.selectedShapeIds,
})
// Its IMPORTANT that the result be sorted by id AND include the index
@ -3052,10 +3061,20 @@ export class Editor extends EventEmitter<TLEventMap> {
const { viewportPageBounds } = this
if (viewportPageBounds.equals(this._renderingBounds.__unsafe__getWithoutCapture())) return this
this._renderingBounds.set(viewportPageBounds.clone())
this._renderingBoundsExpanded.set(viewportPageBounds.clone().expandBy(100 / this.zoomLevel))
this._renderingBoundsExpanded.set(
viewportPageBounds.clone().expandBy(this.renderingBoundsMargin / this.zoomLevel)
)
return this
}
/**
* The distance to expand the viewport when measuring culling. A larger distance will
* mean that shapes near to the viewport (but still outside of it) will not be culled.
*
* @public
*/
renderingBoundsMargin = 100
/* --------------------- Pages ---------------------- */
/** @internal */
@ -7907,7 +7926,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// ---Figure out which shapes we need to include
const shapeIdsToInclude = this.getShapeAndDescendantIds(ids)
const renderingShapes = this.computeUnorderedRenderingShapes([this.currentPageId]).filter(
const renderingShapes = this.getUnorderedRenderingShapes([this.currentPageId]).filter(
({ id }) => shapeIdsToInclude.has(id)
)

File diff suppressed because it is too large Load diff