tldraw/components/canvas/page.tsx

133 lines
2.9 KiB
TypeScript
Raw Normal View History

2021-06-21 21:35:28 +00:00
import { useSelector } from 'state'
2021-07-09 16:15:27 +00:00
import tld from 'utils/tld'
2021-07-09 20:04:41 +00:00
import { Data, Shape, ShapeType } from 'types'
2021-07-09 16:15:27 +00:00
import { getShapeUtils } from 'state/shape-utils'
2021-07-09 20:04:41 +00:00
import { boundsCollide, boundsContain } from 'utils'
import ShapeComponent from './shape'
2021-05-09 21:22:25 +00:00
/*
2021-07-09 20:04:41 +00:00
On each state change, populate a tree structure with all of
the shapes that we need to render..
2021-05-09 21:22:25 +00:00
*/
2021-07-09 20:04:41 +00:00
interface Node {
shape: Shape
children: Node[]
isEditing: boolean
isHovered: boolean
isSelected: boolean
2021-07-10 20:39:29 +00:00
isDarkMode: boolean
2021-07-09 20:04:41 +00:00
isCurrentParent: boolean
}
2021-06-21 21:35:28 +00:00
export default function Page(): JSX.Element {
2021-07-09 20:04:41 +00:00
// Get a tree of shapes to render
2021-07-09 16:15:27 +00:00
const shapeTree = useSelector((s) => {
2021-07-09 20:04:41 +00:00
// Get the shapes that fit into the current viewport
2021-07-09 16:15:27 +00:00
const viewport = tld.getViewport(s.data)
const shapesToShow = s.values.currentShapes.filter((shape) => {
const shapeBounds = getShapeUtils(shape).getBounds(shape)
2021-05-28 14:37:23 +00:00
2021-07-09 16:15:27 +00:00
return (
2021-07-09 20:04:41 +00:00
shape.type === ShapeType.Ray ||
shape.type === ShapeType.Line ||
2021-07-09 16:15:27 +00:00
boundsContain(viewport, shapeBounds) ||
boundsCollide(viewport, shapeBounds)
)
})
2021-07-09 20:04:41 +00:00
// Should we allow shapes to be hovered?
const allowHovers = s.isInAny('selecting', 'text', 'editingShape')
// Populate the shape tree
2021-07-09 16:15:27 +00:00
const tree: Node[] = []
shapesToShow.forEach((shape) =>
addToTree(s.data, s.values.selectedIds, allowHovers, tree, shape)
)
return tree
})
2021-05-09 21:22:25 +00:00
return (
2021-07-09 16:15:27 +00:00
<>
{shapeTree.map((node) => (
<ShapeNode key={node.shape.id} node={node} />
))}
</>
)
}
2021-07-09 20:04:41 +00:00
interface ShapeNodeProps {
node: Node
parentPoint?: number[]
2021-07-09 16:15:27 +00:00
}
2021-07-09 20:04:41 +00:00
const ShapeNode = ({
2021-07-10 20:39:29 +00:00
node: {
shape,
children,
isEditing,
isHovered,
isDarkMode,
isSelected,
isCurrentParent,
},
2021-07-09 20:04:41 +00:00
}: ShapeNodeProps) => {
return (
<>
<ShapeComponent
shape={shape}
isEditing={isEditing}
isHovered={isHovered}
isSelected={isSelected}
2021-07-10 20:39:29 +00:00
isDarkMode={isDarkMode}
2021-07-09 20:04:41 +00:00
isCurrentParent={isCurrentParent}
/>
{children.map((childNode) => (
<ShapeNode key={childNode.shape.id} node={childNode} />
))}
</>
)
}
/**
* Populate the shape tree. This helper is recursive and only one call is needed.
*
* ### Example
*
*```ts
* addDataToTree(data, selectedIds, allowHovers, branch, shape)
*```
*/
2021-07-09 16:15:27 +00:00
function addToTree(
data: Data,
selectedIds: string[],
allowHovers: boolean,
branch: Node[],
shape: Shape
): void {
const node = {
shape,
children: [],
isHovered: data.hoveredId === shape.id,
isCurrentParent: data.currentParentId === shape.id,
isEditing: data.editingId === shape.id,
2021-07-10 20:39:29 +00:00
isDarkMode: data.settings.isDarkMode,
2021-07-09 16:15:27 +00:00
isSelected: selectedIds.includes(shape.id),
}
branch.push(node)
if (shape.children) {
shape.children
.map((id) => tld.getShape(data, id))
.sort((a, b) => a.childIndex - b.childIndex)
2021-07-09 22:35:38 +00:00
.forEach((childShape) => {
addToTree(data, selectedIds, allowHovers, node.children, childShape)
2021-07-09 16:15:27 +00:00
})
}
}