Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface CollaboratorIndicatorData {

interface RenderData {
idsToDisplay: Set<TLShapeId>
hoveredOnlyId: TLShapeId | null
isBrushing: boolean
renderingShapeIds: Set<TLShapeId>
hintingShapeIds: TLShapeId[]
collaboratorIndicators: CollaboratorIndicatorData[]
Expand Down Expand Up @@ -51,6 +53,8 @@ function collaboratorIndicatorsEqual(

function renderDataEqual(a: RenderData, b: RenderData): boolean {
return (
a.hoveredOnlyId === b.hoveredOnlyId &&
a.isBrushing === b.isBrushing &&
setsEqual(a.idsToDisplay, b.idsToDisplay) &&
setsEqual(a.renderingShapeIds, b.renderingShapeIds) &&
arraysEqual(a.hintingShapeIds, b.hintingShapeIds) &&
Expand Down Expand Up @@ -154,21 +158,26 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() {
const instanceState = editor.getInstanceState()
const isChangingStyle = instanceState.isChangingStyle
const isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape')
const isInSelectState = editor.isInAny(
'select.brushing',
'select.scribble_brushing',
'select.pointing_shape',
'select.pointing_selection',
'select.pointing_handle'
)
const isBrushing = editor.isInAny('select.brushing', 'select.scribble_brushing')
const isInSelectState =
isBrushing ||
editor.isInAny(
'select.pointing_shape',
'select.pointing_selection',
'select.pointing_handle'
)

let hoveredOnlyId: TLShapeId | null = null
if (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {
for (const id of editor.getSelectedShapeIds()) {
idsToDisplay.add(id)
}
if (isIdleOrEditing && instanceState.isHoveringCanvas && !instanceState.isCoarsePointer) {
const hovered = editor.getHoveredShapeId()
if (hovered) idsToDisplay.add(hovered)
if (hovered) {
if (!idsToDisplay.has(hovered)) hoveredOnlyId = hovered
idsToDisplay.add(hovered)
}
}
}

Expand Down Expand Up @@ -201,6 +210,8 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() {

return {
idsToDisplay,
hoveredOnlyId,
isBrushing,
renderingShapeIds,
hintingShapeIds,
collaboratorIndicators,
Expand All @@ -219,7 +230,7 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() {
const ctx = canvas.getContext('2d')
if (!ctx) return

const { idsToDisplay, renderingShapeIds, hintingShapeIds, collaboratorIndicators } =
const { idsToDisplay, hoveredOnlyId, isBrushing, renderingShapeIds, hintingShapeIds, collaboratorIndicators } =
$renderData.get()

const { w, h } = editor.getViewportScreenBounds()
Expand Down Expand Up @@ -269,9 +280,16 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() {

// Draw selected/hovered indicators (1.5px stroke)
ctx.lineWidth = 1.5 / zoom
ctx.globalAlpha = isBrushing ? 0.6 : 1.0
for (const shapeId of idsToDisplay) {
if (shapeId === hoveredOnlyId) continue
renderShapeIndicator(ctx, editor, shapeId, renderingShapeIds)
}
if (hoveredOnlyId) {
ctx.globalAlpha = 0.6
renderShapeIndicator(ctx, editor, hoveredOnlyId, renderingShapeIds)
}
ctx.globalAlpha = 1.0

// Draw hinted indicators with a thicker stroke (2.5px)
if (hintingShapeIds.length > 0) {
Expand Down
8 changes: 5 additions & 3 deletions packages/editor/src/lib/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5336,11 +5336,13 @@ export class Editor extends EventEmitter<TLEventMap> {

// If the hit is within the frame's outer margin, then select the frame
const distance = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside)
const isGrid = (shape as any).type === 'grid'
if (
hitFrameInside
!isGrid &&
(hitFrameInside
? (distance > 0 && distance <= outerMargin) ||
(distance <= 0 && distance > -innerMargin)
: distance > 0 && distance <= outerMargin
: distance > 0 && distance <= outerMargin)
) {
return inMarginClosestToEdgeHit || shape
}
Expand All @@ -5355,7 +5357,7 @@ export class Editor extends EventEmitter<TLEventMap> {
return (
inMarginClosestToEdgeHit ||
inHollowSmallestAreaHit ||
(hitFrameInside ? shape : undefined)
(hitFrameInside || isGrid ? shape : undefined)
)
}
continue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StateNode, TLPointerEventInfo, TLShape } from '@tldraw/editor'
import { Group2d, StateNode, TLPointerEventInfo, TLShape } from '@tldraw/editor'
import { isOverArrowLabel } from '../../../shapes/arrow/arrowLabel'
import { getTextLabels } from '../../../utils/shapes/shapes'

Expand All @@ -11,6 +11,7 @@ export class PointingShape extends StateNode {

didCtrlOnEnter = false
didSelectOnEnter = false
didHitGridLabel = false

override onEnter(info: TLPointerEventInfo & { target: 'shape' }) {
const selectedShapeIds = this.editor.getSelectedShapeIds()
Expand All @@ -22,6 +23,19 @@ export class PointingShape extends StateNode {
this.hitShape = info.shape
this.isDoubleClick = false
this.didCtrlOnEnter = accelKey
this.didHitGridLabel = false
if ((info.shape as any).type === 'grid') {
const geometry = this.editor.getShapeGeometry(info.shape)
if (geometry instanceof Group2d) {
const pointInShapeSpace = this.editor.getPointInShapeSpace(info.shape, currentPagePoint)
for (const child of geometry.children) {
if (child.isLabel && child.isPointInBounds(pointInShapeSpace)) {
this.didHitGridLabel = true
break
}
}
}
}
const outermostSelectingShape = this.editor.getOutermostSelectableShape(info.shape)
const selectedAncestor = this.editor.findShapeAncestor(outermostSelectingShape, (parent) =>
selectedShapeIds.includes(parent.id)
Expand Down Expand Up @@ -217,6 +231,9 @@ export class PointingShape extends StateNode {

if (this.didCtrlOnEnter) {
this.parent.transition('brushing', info)
} else if (this.didSelectOnEnter && (this.hitShape as any).type === 'grid' && !this.didHitGridLabel) {
this.editor.setSelectedShapes([])
this.parent.transition('brushing', { ...info, target: 'canvas' as const })
Comment thread
cursor[bot] marked this conversation as resolved.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shift+drag from grid body loses prior selection

Medium Severity

setSelectedShapes([]) unconditionally clears the entire selection before transitioning to brushing. When shift+click+dragging from an unselected grid body, any previously selected shapes are lost. Before this change, the same interaction hit the canvas path (PointingCanvas), which preserves selection when shift is held and passes it to Brushing as initialSelectedShapeIds. The new grid body path always zeroes out the selection, so Brushing.initialSelectedShapeIds becomes empty and shift-based additive brush selection no longer works from grid bodies.

Additional Locations (1)

Fix in Cursor Fix in Web

} else {
this.startTranslating(info)
}
Expand Down
Loading