[feature] Add isStateful flag for shape definitions (#130)

* Add `isStateful` flag for shape definitions

* Update useShapeTree.tsx
This commit is contained in:
Steve Ruiz 2021-09-28 11:12:36 +01:00 committed by GitHub
parent 7e74522256
commit 22a9668b5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 9 deletions

View file

@ -10,6 +10,7 @@ import type {
TLCallbacks,
TLBinding,
TLBounds,
TLShapeUtil,
} from '+types'
import { Utils } from '+utils'
import { Vec } from '@tldraw/vec'
@ -69,6 +70,13 @@ function shapeIsInViewport(bounds: TLBounds, viewport: TLBounds) {
return Utils.boundsContain(viewport, bounds) || Utils.boundsCollide(viewport, bounds)
}
function getShapeUtils<T extends TLShape, E extends Element>(
shape: T,
shapeUtils: TLShapeUtils<T, E>
) {
return shapeUtils[shape.type] as TLShapeUtil<T, E>
}
export function useShapeTree<
T extends TLShape,
E extends Element,
@ -111,18 +119,29 @@ export function useShapeTree<
shapesIdsToRender.clear()
Object.values(page.shapes)
.filter(
(shape) =>
// Always render shapes that are flagged as stateful
getShapeUtils(shape, shapeUtils).isStateful ||
// Always render selected shapes (this preserves certain drag interactions)
selectedIds.includes(shape.id) ||
// Otherwise, only render shapes that are in view
shapeIsInViewport(shapeUtils[shape.type as T['type']].getBounds(shape), viewport)
)
.sort((a, b) => a.childIndex - b.childIndex)
.forEach((shape) => {
// Don't hide selected shapes (this breaks certain drag interactions)
if (
selectedIds.includes(shape.id) ||
shapeIsInViewport(shapeUtils[shape.type as T['type']].getBounds(shape), viewport)
) {
if (shape.parentId === page.id) {
shapesIdsToRender.add(shape.id)
shapesToRender.add(shape)
}
// If the shape's parent is the page, add it to our sets of shapes to render
if (shape.parentId === page.id) {
shapesIdsToRender.add(shape.id)
shapesToRender.add(shape)
return
}
// If the shape's parent is a different shape (e.g. a group),
// add the parent to the sets of shapes to render. The parent's
// children will all be rendered.
shapesIdsToRender.add(shape.parentId)
shapesToRender.add(page.shapes[shape.parentId])
})
// Call onChange callback when number of rendering shapes changes

View file

@ -22,6 +22,8 @@ export const ShapeUtil = function <T extends TLShape, E extends Element, M = any
canBind: false,
isStateful: false,
isAspectRatioLocked: false,
create: (props) => {

View file

@ -324,6 +324,8 @@ export type TLShapeUtil<
canBind: boolean
isStateful: boolean
getRotatedBounds(this: TLShapeUtil<T, E, M>, shape: T): TLBounds
hitTest(this: TLShapeUtil<T, E, M>, shape: T, point: number[]): boolean

View file

@ -17,6 +17,8 @@ export interface LabelShape extends TLShape {
export const Label = new ShapeUtil<LabelShape, HTMLDivElement, { isDarkMode: boolean }>(() => ({
type: 'label',
isStateful: true,
defaultProps: {
id: 'example1',
type: 'label',

View file

@ -122,6 +122,16 @@ export const appState = new AppState({
name: 'Label',
childIndex: 2,
type: 'label',
point: [-200, -200],
rotation: 0,
text: 'My shape is stateful, I should still render while off screen!',
},
label2: {
id: 'label2',
parentId: 'page1',
name: 'Label',
childIndex: 2,
type: 'label',
point: [200, 200],
rotation: 0,
text: 'Hello world!',