Tldraw/packages/core/src/hooks/useSelection.tsx

93 wiersze
2.6 KiB
TypeScript

import * as React from 'react'
import type { TLPage, TLPageState, TLShape, TLBounds, TLBinding } from '../types'
import Utils from '../utils'
import { useTLContext } from './useTLContext'
import type { TLShapeUtil, TLShapeUtilsMap } from '../TLShapeUtil'
function canvasToScreen(point: number[], camera: TLPageState['camera']): number[] {
return [(point[0] + camera.point[0]) * camera.zoom, (point[1] + camera.point[1]) * camera.zoom]
}
function getShapeUtils<T extends TLShape>(shapeUtils: TLShapeUtilsMap<T>, shape: T) {
return shapeUtils[shape.type as T['type']] as unknown as TLShapeUtil<T>
}
export function useSelection<T extends TLShape>(
page: TLPage<T, TLBinding>,
pageState: TLPageState,
shapeUtils: TLShapeUtilsMap<T>
) {
const { rSelectionBounds } = useTLContext()
const { selectedIds } = pageState
const rPrevBounds = React.useRef<TLBounds>()
let bounds: TLBounds | undefined = undefined
let rotation = 0
let isLocked = false
let isLinked = false
if (selectedIds.length === 1) {
const id = selectedIds[0]
const shape = page.shapes[id]
rotation = shape.rotation || 0
isLocked = shape.isLocked || false
const utils = getShapeUtils(shapeUtils, shape)
bounds = utils.hideBounds ? undefined : utils.getBounds(shape)
} else if (selectedIds.length > 1) {
const selectedShapes = selectedIds.map((id) => page.shapes[id])
rotation = 0
isLocked = selectedShapes.every((shape) => shape.isLocked)
bounds = selectedShapes.reduce((acc, shape, i) => {
if (i === 0) {
return getShapeUtils(shapeUtils, shape).getRotatedBounds(shape)
}
return Utils.getExpandedBounds(acc, getShapeUtils(shapeUtils, shape).getRotatedBounds(shape))
}, {} as TLBounds)
}
if (bounds) {
const [minX, minY] = canvasToScreen([bounds.minX, bounds.minY], pageState.camera)
const [maxX, maxY] = canvasToScreen([bounds.maxX, bounds.maxY], pageState.camera)
isLinked = !!Object.values(page.bindings).find(
(binding) => selectedIds.includes(binding.toId) || selectedIds.includes(binding.fromId)
)
rSelectionBounds.current = {
minX,
minY,
maxX,
maxY,
width: maxX - minX,
height: maxY - minY,
}
} else {
rSelectionBounds.current = null
}
const prevBounds = rPrevBounds.current
if (!prevBounds || !bounds) {
rPrevBounds.current = bounds
} else if (bounds) {
if (
prevBounds.minX === bounds.minX &&
prevBounds.minY === bounds.minY &&
prevBounds.maxX === bounds.maxX &&
prevBounds.maxY === bounds.maxY
) {
bounds = rPrevBounds.current
}
}
return { bounds, rotation, isLocked, isLinked }
}