tldraw
Version:
A tiny little drawing editor.
100 lines (88 loc) • 3.59 kB
text/typescript
import { Editor, TLClickEventInfo, TLPointerEventInfo, TLShape, isShapeId } from '@tldraw/editor'
export function selectOnCanvasPointerUp(
editor: Editor,
info: TLPointerEventInfo | TLClickEventInfo
) {
const selectedShapeIds = editor.getSelectedShapeIds()
const currentPagePoint = editor.inputs.getCurrentPagePoint()
const { shiftKey, altKey, accelKey } = info
const additiveSelectionKey = shiftKey || accelKey
const hitShape = editor.getShapeAtPoint(currentPagePoint, {
hitInside: false,
margin: editor.options.hitTestMargin / editor.getZoomLevel(),
hitLabels: true,
renderingOnly: true,
filter: (shape) => !shape.isLocked,
})
// Note at the start: if we select a shape that is inside of a group,
// the editor will automatically adjust the selection to the outermost
// selectable shape (the group)
// If the shape's outermost selected id (e.g. the group that contains
// the shape) is not the same as the editor's only selected shape, then
// we want to select the outermost selected shape instead of the shape
if (hitShape) {
const outermostSelectableShape = editor.getOutermostSelectableShape(hitShape)
// If the user is holding shift, they're either adding to or removing from
// their selection.
if (additiveSelectionKey && !altKey) {
editor.cancelDoubleClick() // fuckin eh
if (selectedShapeIds.includes(outermostSelectableShape.id)) {
// Remove it from selected shapes
editor.markHistoryStoppingPoint('deselecting shape')
editor.deselect(outermostSelectableShape)
} else {
// Add it to selected shapes
editor.markHistoryStoppingPoint('shift selecting shape')
editor.setSelectedShapes([...selectedShapeIds, outermostSelectableShape.id])
}
} else {
let shapeToSelect: TLShape | undefined = undefined
if (outermostSelectableShape === hitShape) {
// There's no group around the shape, so we can select it.
shapeToSelect = hitShape
} else {
// There's a group around the hit shape.
// If the group is the current focus layer, OR if the group is
// already selected, then we can select the shape inside the group.
// Otherwise, if the group isn't selected and isn't our current
// focus layer, then we need to select the group instead.
if (
outermostSelectableShape.id === editor.getFocusedGroupId() ||
selectedShapeIds.includes(outermostSelectableShape.id)
) {
shapeToSelect = hitShape
} else {
shapeToSelect = outermostSelectableShape
}
}
if (shapeToSelect && !selectedShapeIds.includes(shapeToSelect.id)) {
editor.markHistoryStoppingPoint('selecting shape')
editor.select(shapeToSelect.id)
}
}
} else {
// We didn't hit anything...
if (additiveSelectionKey) {
// If we were holding shift, then it's a noop. We keep the
// current selection because we didn't add anything to it
return
} else {
// Otherwise, we clear the selction because the user selected
// nothing instead of their current selection.
if (selectedShapeIds.length > 0) {
editor.markHistoryStoppingPoint('selecting none')
editor.selectNone()
}
// If the click was inside of the current focused group, then
// we keep that focused group; otherwise we clear the focused
// group (reset it to the page)
const focusedGroupId = editor.getFocusedGroupId()
if (isShapeId(focusedGroupId)) {
const groupShape = editor.getShape(focusedGroupId)!
if (!editor.isPointInShape(groupShape, currentPagePoint, { margin: 0, hitInside: true })) {
editor.setFocusedGroup(null)
}
}
}
}
}