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:
parent
bf27743595
commit
e4829f6702
4 changed files with 246 additions and 947 deletions
|
@ -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;
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue