[feature] Add isStateful
flag for shape definitions (#130)
* Add `isStateful` flag for shape definitions * Update useShapeTree.tsx
This commit is contained in:
parent
7e74522256
commit
22a9668b5c
5 changed files with 44 additions and 9 deletions
|
@ -10,6 +10,7 @@ import type {
|
||||||
TLCallbacks,
|
TLCallbacks,
|
||||||
TLBinding,
|
TLBinding,
|
||||||
TLBounds,
|
TLBounds,
|
||||||
|
TLShapeUtil,
|
||||||
} from '+types'
|
} from '+types'
|
||||||
import { Utils } from '+utils'
|
import { Utils } from '+utils'
|
||||||
import { Vec } from '@tldraw/vec'
|
import { Vec } from '@tldraw/vec'
|
||||||
|
@ -69,6 +70,13 @@ function shapeIsInViewport(bounds: TLBounds, viewport: TLBounds) {
|
||||||
return Utils.boundsContain(viewport, bounds) || Utils.boundsCollide(viewport, bounds)
|
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<
|
export function useShapeTree<
|
||||||
T extends TLShape,
|
T extends TLShape,
|
||||||
E extends Element,
|
E extends Element,
|
||||||
|
@ -111,18 +119,29 @@ export function useShapeTree<
|
||||||
shapesIdsToRender.clear()
|
shapesIdsToRender.clear()
|
||||||
|
|
||||||
Object.values(page.shapes)
|
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)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
.forEach((shape) => {
|
.forEach((shape) => {
|
||||||
// Don't hide selected shapes (this breaks certain drag interactions)
|
// If the shape's parent is the page, add it to our sets of shapes to render
|
||||||
if (
|
if (shape.parentId === page.id) {
|
||||||
selectedIds.includes(shape.id) ||
|
shapesIdsToRender.add(shape.id)
|
||||||
shapeIsInViewport(shapeUtils[shape.type as T['type']].getBounds(shape), viewport)
|
shapesToRender.add(shape)
|
||||||
) {
|
return
|
||||||
if (shape.parentId === page.id) {
|
|
||||||
shapesIdsToRender.add(shape.id)
|
|
||||||
shapesToRender.add(shape)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Call onChange callback when number of rendering shapes changes
|
||||||
|
|
|
@ -22,6 +22,8 @@ export const ShapeUtil = function <T extends TLShape, E extends Element, M = any
|
||||||
|
|
||||||
canBind: false,
|
canBind: false,
|
||||||
|
|
||||||
|
isStateful: false,
|
||||||
|
|
||||||
isAspectRatioLocked: false,
|
isAspectRatioLocked: false,
|
||||||
|
|
||||||
create: (props) => {
|
create: (props) => {
|
||||||
|
|
|
@ -324,6 +324,8 @@ export type TLShapeUtil<
|
||||||
|
|
||||||
canBind: boolean
|
canBind: boolean
|
||||||
|
|
||||||
|
isStateful: boolean
|
||||||
|
|
||||||
getRotatedBounds(this: TLShapeUtil<T, E, M>, shape: T): TLBounds
|
getRotatedBounds(this: TLShapeUtil<T, E, M>, shape: T): TLBounds
|
||||||
|
|
||||||
hitTest(this: TLShapeUtil<T, E, M>, shape: T, point: number[]): boolean
|
hitTest(this: TLShapeUtil<T, E, M>, shape: T, point: number[]): boolean
|
||||||
|
|
|
@ -17,6 +17,8 @@ export interface LabelShape extends TLShape {
|
||||||
export const Label = new ShapeUtil<LabelShape, HTMLDivElement, { isDarkMode: boolean }>(() => ({
|
export const Label = new ShapeUtil<LabelShape, HTMLDivElement, { isDarkMode: boolean }>(() => ({
|
||||||
type: 'label',
|
type: 'label',
|
||||||
|
|
||||||
|
isStateful: true,
|
||||||
|
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
id: 'example1',
|
id: 'example1',
|
||||||
type: 'label',
|
type: 'label',
|
||||||
|
|
|
@ -122,6 +122,16 @@ export const appState = new AppState({
|
||||||
name: 'Label',
|
name: 'Label',
|
||||||
childIndex: 2,
|
childIndex: 2,
|
||||||
type: 'label',
|
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],
|
point: [200, 200],
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
text: 'Hello world!',
|
text: 'Hello world!',
|
||||||
|
|
Loading…
Reference in a new issue